后端规范
依赖注入与分层
- 使用构造器注入(
@AllArgsConstructor+private final),字段上不打@Autowired;单构造器不需要@Autowired注解。 - Controller 只做参数绑定、权限声明与调用编排;业务逻辑与事务边界在
service/;数据访问在mapper/。 - Controller 统一
@RequestMapping("/api/..."),返回业务对象,由GlobalResponseBodyAdvice包Result;异常交给GlobalExceptionHandler。 - 分页统一 MyBatis-Plus
Page/IPage,禁止手写limit拼 SQL。 - 业务模块之间不互相依赖,跨域能力下沉
asone-server-system。
禁用 record
本项目不允许使用 Java record(顶层、嵌套、局部都不行),值对象统一用 Lombok 普通类:
| record 场景 | 改成的普通类 |
|---|---|
| 需要被 Jackson 反序列化的 DTO | @Data @NoArgsConstructor @AllArgsConstructor |
| 不可变值对象(无归一化逻辑) | @Getter @ToString @EqualsAndHashCode @AllArgsConstructor + private final 字段 |
| 不可变值对象(构造时校验 / 归一化) | 同上,但手写全参构造器完成归一化,不用 @AllArgsConstructor |
| 只读的内部结果载体 | @Getter @AllArgsConstructor + private final 字段 |
@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor
public class ApprovalOutcome {
/** 业务动作是否执行成功 */
private final boolean approved;
/** 未成功时的原因 */
private final String reason;
public static ApprovalOutcome succeeded() {
return new ApprovalOutcome(true, null);
}
}
配套注意:
- record 头部的
@param注释要拆成字段级 Javadoc,@param放在类上编译器不认。 boolean字段经 Lombok 生成的 getter 是isXxx()而非getXxx(),改调用点时注意。- 派生方法保留业务命名,不硬套
get前缀,例如approverIds()、usable()。 - 自查(命中即不合规):
grep -rnE '(^|\s)record\s+[A-Za-z_]' --include='*.java' asone-server/*/src
禁止过期 API
| 过期写法 | 应写 |
|---|---|
javax.servlet.*、javax.validation.*、javax.persistence.* | jakarta.* 同名包 |
WebSecurityConfigurerAdapter、authorizeRequests()、antMatchers() | SecurityFilterChain Bean + authorizeHttpRequests() + requestMatchers() |
新的出站调用用 RestTemplate | RestClient(同步)/ WebClient(异步、流式) |
springfox 与 @Api、@ApiOperation | springdoc-openapi 与 @Tag、@Operation |
MyBatis-Plus PaginationInterceptor、SqlInjector | MybatisPlusInterceptor + PaginationInnerInterceptor |
java.util.Date、Calendar、SimpleDateFormat | java.time(LocalDateTime、DateTimeFormatter) |
字段或 setter 上 @Autowired | 构造器注入 |
finalize()、Thread.stop/suspend/resume() | AutoCloseable + try-with-resources、interrupt() |
编译期自查:
cd asone-server && mvn -q -DskipTests -Dmaven.compiler.showDeprecation=true compile
cd asone-server && mvn versions:display-dependency-updates
上游确实未提供替代 API 时(如 xxl-job-core 个别兼容接口),只在调用处保留并加一行「上游未提供替代写法」说明,不扩散到新代码。
接口与安全约定
- 读接口
hasAuthority('xxx:read'),写接口xxx:manage并叠加@authenticationType.isSession(authentication)。 - 写操作必须
@OperationLog,summaryTemplate用 SpEL 生成可读描述。 - 开放给 API Key 的接口显式加
@ApiKeyAllowed,免鉴权路径改security.permit-all-paths配置,不用注解绕过。 - 接口文案国际化:新文案进
messages*.properties,后端优先读X-Locale。
数据库操作约束
排查问题、验证数据、探索库表结构时对数据库只允许只读查询。任何增、改、删、结构变更默认禁止,只有用户在当前对话中明确授权并确认后才可执行。
- 允许:
SELECT、SHOW、DESC、information_schema、只读EXPLAIN、SET SESSION TRANSACTION READ ONLY。 - 禁止:DML、DDL、
GRANT/REVOKE/FLUSH/KILL、CALL不确定是否只读的例程、通过事务「先写后回滚」试探行为、清理测试脏数据或「顺手改一下」。 - 迁移 / 初始化 / 数据修复脚本可以写进文件供人工评审,但不由开发者或调试链路自动执行。
- 表结构唯一来源是全量 DDL,见数据库。
- 发现现有代码或测试在调试链路里隐式写库,主动报告,不掩盖。
其他约束
- 注释只描述当前功能与作用,就地改写受影响注释;不写「新增 / 修改 / 优化后 / 2026-xx-xx」这类流水账,版本演进交给 git log。
- 注释与代码不一致且无法判断原意时,按当前实现重写。
- 上线顺序注意:DDL 必须先于依赖它列的后端版本上线,否则查询因缺列失败。
