开发规范
本文档定义了 Personal Blog Backend 项目的强制性编码标准和架构约束。
ArchUnit 守护
项目使用 ArchUnit 进行架构测试,违反规范的代码将无法通过 CI/CD。
🏗️ 模块结构
blog-modules/
├── blog-*-api/ # API 定义层(仅 DTO/VO/Interface)
└── blog-*-service/ # 服务实现层(Controller/Service/Entity)
| 模块 | 允许 | 禁止 |
|---|---|---|
*-api | DTO, VO, Interface, Enum | ❌ Entity, 业务逻辑 |
*-service | Controller, Service, Entity, Mapper | - |
blog-application | 启动类, 全局配置 | ❌ Controller, Service |
依赖规则
graph LR
APP["blog-application"] --> SVC["*-service"]
SVC --> API["*-api"]
SVC --> COMMON["blog-common"]
API --> COMMON
SVC -.-x|禁止| SVC2["其他 *-service"]
💻 编码标准
Entity(数据库实体)
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.ASSIGN_ID) // 雪花算法
private Long id;
@Version // 乐观锁
private Integer version;
@TableLogic // 逻辑删除
private Integer isDeleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
禁止直接返回 Entity
API 响应必须使用 DTO/VO,通过 MapStruct 转换。
DTO(数据传输对象)
@Data
@Schema(description = "用户信息")
public class UserDTO implements Serializable, Identifiable<Long> {
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20)
private String username;
@Email(message = "邮箱格式不正确")
private String email;
}
必须要求:
- ✅ 实现
Serializable和Identifiable<T> - ✅ 使用
@Schema注解(API 文档) - ✅ 使用 JSR-303 验证注解
依赖注入
@Service
@RequiredArgsConstructor // ✅ 构造器注入
@Slf4j
public class UserServiceImpl {
private final UserMapper userMapper; // ✅ final 字段
private final UserConverter converter;
// ❌ 避免 @Autowired 字段注入
}
统一响应
@GetMapping("/{id}")
public Result<UserVO> getUser(@PathVariable Long id) {
return userService.getVoById(id)
.map(Result::success)
.orElseThrow(() -> new BusinessException(SystemErrorCode.NOT_FOUND));
}
所有 Controller 方法必须返回 Result<T>
异常处理
// ✅ 抛出业务异常,由 GlobalExceptionHandler 统一处理
if (user == null) {
throw new BusinessException(SystemErrorCode.USER_NOT_FOUND);
}
// ❌ 禁止在 Controller 中使用 try-catch
安全上下文
// ❌ 错误:用户可伪造
@PostMapping("/articles")
public Result<?> create(@RequestParam Long userId) { }
// ✅ 正确:从 Security Context 获取
@PostMapping("/articles")
public Result<?> create(@RequestBody ArticleDTO dto) {
Long userId = SecurityUtils.getCurrentUserId();
}
🗄️ 数据库变更
禁止手动修改 Schema,必须使用 Flyway:
- 创建脚本:
blog-application/src/main/resources/db/ - 命名格式:
V{version}__{description}.sql
-- V1.0.3__add_phone_column.sql
ALTER TABLE sys_user ADD COLUMN phone VARCHAR(20);
CREATE INDEX idx_phone ON sys_user(phone);
🔧 MapStruct 配置
@Mapper(
componentModel = "spring",
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
)
public interface UserConverter extends BaseConverter<UserDTO, User, UserVO> {
// nullValuePropertyMappingStrategy = IGNORE 是关键配置
// 确保更新时不会用 null 覆盖已有值
}
🧪 测试规范
单元测试
@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {
@Mock private UserMapper userMapper;
@InjectMocks private UserServiceImpl userService;
@Test
void should_returnUser_when_validId() {
// Given - When - Then 模式
}
}
集成测试
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired private MockMvc mockMvc;
}
命名规范
- 类名:
{TargetClass}Test - 方法名:
should_expectedBehavior_when_state()
覆盖率目标
| 层级 | 目标 |
|---|---|
| Service | ≥ 80% |
| Controller | ≥ 70% |
| 关键路径 | 100% |
� 延伸阅读
- Base Framework 指南 — 核心框架详解
- Git 提交规范 — Conventional Commits
- ArchUnit 测试 — 架构守护