Java 后端开发工作流
工作原则
- 默认处于开发阶段,以 IDEA Run/Debug 联调;除非用户要求,不长期启动服务,不提前生成 JAR、Docker 或部署产物。
- 只修改任务所需内容,保留用户的未提交改动、现有
target/和无关文件。 - 先确认事实再实现;对可能变化的版本、兼容矩阵和官方用法查询当前官方资料,不凭记忆猜测。
- 修改源码时同步维护日志、注释、OpenAPI、测试和相关文档,不把它们留作事后补充。
后端开发与设计契约
- 将项目根目录
BACKEND_DEVELOPMENT.md作为后端范围、架构、接口状态、数据演进、阶段和验证记录的维护入口。 - 将项目根目录
DATABASE_SCHEMA.md作为当前数据库表、字段摘要、隔离边界、关系、索引、初始化数据和迁移版本的维护入口。 - 开始后端设计、开发、修复或审查前,先查找并完整读取该文档,再读取源码、配置、迁移、测试和 Git 状态;文档不得替代事实扫描。
- 文档缺失且任务涉及架构、接口、权限、数据库、配置或外部集成设计时,复制 后端开发契约模板 到项目根目录并按扫描结果填写。
DATABASE_SCHEMA.md缺失且项目已有数据库迁移、建表 SQL 或持久化模型时,根据数据库结构真源生成;不得只凭 Entity、Mapper 或旧文档推断实际表结构。- 设计任务先在文档中记录范围、现状证据、目标边界、接口状态、数据流、迁移、风险和验收;仅完成设计的能力标记“未实现”。
- 实现任务开始时将目标能力标记“开发中”,完成源码、权限、校验、测试、OpenAPI、编译和文档同步后才标记“已实现”。
- 接口、表结构、权限、配置、模块边界、外部集成、实施阶段或关键决策变化时,在同一任务内同步更新文档;简单内部重构且不改变契约时只追加必要验证记录。
- 状态只使用“未确认、未实现、开发中、已实现、已废弃”。存在类、DTO、前端调用或占位路由不等于已实现。
- OpenAPI 是 HTTP 契约真源,Flyway/Liquibase 历史是已执行数据库结构真源;开发文档只维护索引、决策、状态和验收,不复制整份生成文档或完整 DDL。
- 只读诊断或审查不自动改写项目文件,除非用户明确要求维护文档;交付时报告发现的文档偏差。
标准流程
- 检查:读取仓库约束、根目录后端开发契约、数据库表结构文档、构建文件、相关源码、配置、迁移、其他文档、测试和 Git 状态;识别现有日志与注释风格。
- 同步现状:用代码、OpenAPI、迁移和测试校正文档中的模块、接口和阶段状态,区分事实、设计和待确认项。
- 设计:明确接口契约、数据流、权限、事务、异常、兼容性和最小修改范围,并同步开发契约。
- 实现:完成必要源码、配置、测试和新增迁移,不扩大业务范围;持续维护目标能力状态。
- 校验:检查日志、注释、开发契约、OpenAPI 和依赖树,再按风险执行 Maven 验证并记录实际结果。
- 交付:说明源码与契约变化、验证结果、未验证项、风险和 IDEA 联调步骤,并提供规范的建议 Git 提交信息;除非用户明确要求,不执行暂存、提交或推送。
代码与模型
DTO、VO 与 Entity
- 新增的无状态 DTO、查询条件和简单 VO 优先使用 Java
record。 record组件直接承载@Schema和 Jakarta Validation;调用方使用componentName(),不得伪造 Getter、Setter 或可变 Builder。- 对集合、Map、数组等可变成员执行必要的防御性复制;跨字段不变量在紧凑构造器中校验。
- Entity、MyBatis 依赖无参构造或 Setter 的对象、存在合法状态变化的模型和依赖继承的模型使用普通
class。 - 仅在不破坏 Jackson、Spring MVC、MyBatis、模板表达式或第三方框架约定时迁移旧模型;同步替换 Setter 和不适用的
BeanUtils.copyProperties。
JavaDoc 与行内注释
- Service 接口及实现类必须有类级 JavaDoc;接口说明业务能力,实现类说明实现职责。
- Service 接口每个方法及实现类每个公开方法必须有简洁方法级 JavaDoc,说明业务动作和关键约束。
- Repository 类及其公开方法必须有 JavaDoc;私有方法只在存在复杂规则、边界或格式转换时注释。
- JavaDoc 放在 Spring、Lombok、事务等注解之前;仅在参数或返回值存在额外语义时写
@param、@return,禁止空模板。 - 行内注释解释原因、边界和业务规则,不复述代码;禁止流程编号、废弃代码和失效注释。
- 修改逻辑时同步修改注释;完成后扫描 Service 接口、实现类和 Repository,确认类与方法覆盖完整。
后台日志
- 使用 Lombok
@Slf4j和 SLF4J{}占位符;禁止字符串拼接、System.out、System.err和printStackTrace()。 - 沿用现有模块标签;缺失时使用
log.info("[模块或场景] 操作描述,字段={}", value)。 debug记录诊断细节;info记录启动、接口入口和重要状态变化;warn记录可预期异常;error记录未预期故障并传入异常对象。- Controller 记录业务入口和必要关键参数;Service 只记录重要状态变化或外部调用结果,避免跨层重复。
- 不记录密码、JWT、密钥、完整手机号、身份证号、巨大请求体、集合或完整响应。
- 统一异常处理器已记录异常时,业务层不重复记录同一异常堆栈。
依赖版本兼容性
- 新增或升级依赖前确认 Java、Spring Boot、Spring Framework、Spring Cloud、构建插件和相关 Starter 的声明版本与实际解析版本。
- 对 Spring Boot、Spring Cloud、Springdoc、Knife4j、MyBatis、数据库驱动等强耦合组件,优先核对官方兼容矩阵、发布说明或 BOM。
- 优先由 Spring Boot 或组件官方 BOM 管理传递依赖;Starter 已集成的组件不得重复声明。
- 确需覆盖传递版本时说明原因,并确认不存在同一库多版本、重复实现或二进制 API 冲突。
- 修改后运行
mvn dependency:tree,按相关groupId/artifactId检查最终版本和来源;必要时使用mvn help:effective-pom或 Maven Enforcer。 - 兼容性结论至少包含:项目基础版本、目标依赖版本、传递核心版本、官方依据和依赖树结果。
compile只证明编译兼容;涉及自动配置、反射、序列化或 UI Starter 时执行最小运行时或端点验证。- 遇到
NoSuchMethodError、ClassNotFoundException或自动配置失败时,先按依赖树定位冲突,再采用官方支持的版本组合;禁止用放宽业务逻辑或盲目降级核心框架掩盖问题。
OpenAPI 与 Knife4j
UI 与依赖
- 默认只保留 Knife4j UI,入口为
/doc.html;除非用户明确要求,不同时开放多个文档 UI。 - 禁用或拦截
/swagger-ui.html与/swagger-ui/**,但保留/v3/api-docs、/v3/api-docs/**和/v3/api-docs/swagger-config。 - Spring Boot 3 使用兼容的
knife4j-openapi3-jakarta-spring-boot-starter;Starter 已传递 Springdoc,禁止重复声明 UI Starter。 - 启用 Knife4j 增强功能前验证其与 Springdoc 的运行时兼容性;发生二进制冲突时采用官方兼容组合。
Controller 与模型契约
- Controller 类使用
@Tag;每个接口使用@Operation,并声明稳定、唯一、语义化的operationId。 - 路径参数和含义不明显的查询参数使用
@Parameter。 - 使用
@ApiResponses声明真实成功状态及适用的400/401/403/404/409/500;错误响应引用统一 Schema。 - 认证项目声明标准 Bearer JWT;私有操作声明
security,公开操作使用@SecurityRequirements明确取消继承认证。 - DTO、VO 等实际接口模型使用
@Schema;Entity 未直接作为接口模型时不强制添加 Swagger 注解。 @Schema与 Jakarta Validation 必须表达一致的必填、范围、长度和格式;Controller 使用@Valid/@Validated启用运行时校验。- 按语义声明
requiredMode、example、allowableValues、format、完整pattern、minimum和maximum。 - 邮箱使用
format: email;分页满足page >= 1、1 <= size <= 100;跨端 ID 类型保持一致。 - 禁止让稳定结构的裸
JsonNode生成{};动态对象声明additionalProperties,动态数组声明items。 - 配置 API 标题、语义版本和环境无关 Server。
文档验证
- 修改接口或模型后验证实际
/v3/api-docs,不得用旧导出文件代替。 - 至少校验
/doc.html、OpenAPI 数据端点、非目标 UI、路径与方法、全部$ref、安全声明、错误响应、关键 Schema、元信息和唯一operationId。 - 使用非法请求验证 Jakarta Validation 确实生效;源码或配置变化后重启应用再导出 JSON/YAML,并核对生成时间和关键内容。
配置、数据与文档
配置分层
.env只保存本地或部署环境注入的变量值,例如数据库、缓存、密钥、第三方凭据和端口;不得在其中复制 YAML 层级结构、业务默认值、Profile 选择或功能开关说明。.env必须加入.gitignore,真实凭据不得提交;可提交脱敏的.env.example作为变量模板。application.yml只保存所有环境共用的配置结构、非敏感默认值和环境变量占位符。公共配置只能定义一次;必填敏感配置使用无默认值占位符,只有安全且通用的基础参数允许设置默认值。application-{profile}.yml只保存当前 Profile 的差异配置,例如开发、测试和生产的功能模式、日志级别、初始化策略和文档开关,并使用spring.config.activate.on-profile声明适用环境。不得重复复制公共数据库、Redis、JWT、OSS 等完整配置,除非明确是该环境的差异覆盖;生产配置必须显式关闭开发初始化、调试文档和宽松开发选项。一个配置项只能有一个规范键名和一个读取入口;禁止为同一能力维护多个命名空间、环境变量或默认值。优先使用 Spring Boot 标准键,自定义业务配置统一放到
ray.*;环境变量统一使用大写下划线形式,并保持与 YAML 键的可追溯映射。Profile 激活逻辑只能有一个明确来源,默认环境必须显式记录,例如
spring.profiles.active: ${SPRING_PROFILES_ACTIVE:dev}。文档、代码和配置中的键名必须一致;测试配置必须覆盖主配置实际读取的标准键,不能使用未被绑定的同名自定义前缀。审查配置时检查:公共配置是否被 Profile 重复复制;同一功能是否存在多个前缀、环境变量或默认值;
.env是否混入结构化配置;application.yml是否写入密码、密钥、AccessKey 或 AppSecret;必填变量是否错误地默认为空;Profile 行为是否与文档一致;生产是否继承通配跨域、SQL 初始化或调试日志等宽松默认值;同一功能是否存在未说明的重复定义或覆盖。推荐文件结构为:
.env # 本地真实变量,不提交 .env.example # 脱敏变量模板,可提交 application.yml # 公共结构与占位符 application-dev.yml # 开发环境差异 application-test.yml # 测试环境差异 application-prod.yml # 生产环境差异配置加载关系为:
.env / 外部环境变量 -> application.yml 公共结构 -> application-{profile}.yml 环境差异覆盖 -> 最终 Spring Environment。当前项目审查示例:根目录
.env已被.gitignore忽略但包含高敏感凭据,应提供.env.example;application-test.yml的ray.redis.*与主配置使用的spring.data.redis.*不一致;application-dev.yml的微信登录模式与文档中的 dev/test MOCK 约定不一致;application.yml的 WebSocketallowed-origins默认*不应作为生产默认值;专题文档中关于application-dev.yml已忽略且未跟踪的描述需要记录为文档偏差。IDEA 后端连接开发机可达的虚拟机 IP,不写
localhost;数据库、Redis 等开发依赖端口只向开发机开放。README 为虚拟机 Docker 依赖提供最简启动、状态、日志和停止命令。
数据库迁移
- 数据库结构只通过新的有序迁移演进;不修改已执行迁移,不清空、重建或重置数据库。
- 启动应用前检查是否存在自动执行 SQL、清库、重建或不可逆初始化逻辑;存在风险时不擅自启动。
数据库表结构文档
- 在项目根目录维护
DATABASE_SCHEMA.md;首次生成时扫描全部 Flyway/Liquibase 迁移、基线和建表 SQL,以按顺序执行后的最终结构为准。 - 文档至少记录当前结构版本、表清单、用途、隔离范围、关键字段、主键、唯一约束、重要索引、实际外键、主要逻辑关系、初始化数据和待建表。
- 明确区分数据库实际声明的外键与仅由应用维护的逻辑关联,不把 Entity 注解或字段命名误写为数据库约束。
- 合并基线和增量迁移的效果;已被后续迁移删除、改名或替换的字段、索引和约束不得作为当前结构保留。
- 新增或修改迁移时,在同一任务内同步更新受影响表、关系、索引、迁移版本和演进状态;只改业务代码且表结构未变化时不机械重写。
- 多租户项目必须标明每张表的平台级、租户级或租户加门店级隔离范围,并记录临时默认租户值、兼容桥接和后续移除条件。
- 校验文档覆盖全部当前业务表,表数量和表名与迁移结果一致;排除
flyway_schema_history等框架元数据表,除非项目要求记录。 - 文档是便于开发和评审的结构摘要,不复制完整 DDL;实际数据库结构仍以迁移历史为真源。
文档与接口状态
- 优先维护根目录
BACKEND_DEVELOPMENT.md和DATABASE_SCHEMA.md;沿用其他专题文档结构,README 只做导航,不堆接口或表字段明细。 - 接口状态只使用“未确认、未实现、开发中、已实现、已废弃”;仅在实现、权限、校验、测试、OpenAPI、说明和编译全部完成后标记“已实现”。
- 接口、架构、权限、配置、数据库或目录变化时同步开发契约;专题细节放到独立文档并从契约链接。
- 仅完成设计时统一标记“未实现”。
Maven 验证矩阵
- 先运行
mvn -version;不可用时再使用仓库现有 Maven Wrapper,不下载 Maven。 - 小型源码或配置修改:运行相关模块
compile。 - 业务行为修改:运行相关测试,再编译。
- 认证、持久化、迁移或公共基础设施修改:运行相关集成测试,必要时运行
test。 - Controller、DTO 或 OpenAPI 修改:运行实际文档端点契约测试,再编译。
- 依赖升级:运行依赖树、编译及相应的最小运行时验证。
- 仅在用户明确进入发布或部署阶段时执行
package、install、deploy或新增部署产物。
Git 提交信息
- 每次产生文件变更的交付都提供一份可直接复制的 Conventional Commits 建议,不等待用户再次询问。
- 使用
<type>(<scope>): <中文简述>标题;优先选择feat、fix、refactor、docs、test、perf、build、ci、chore。 scope使用稳定的领域或模块名,如saas、auth、tenant、employee、order、db、docs;一次提交涉及同一目标的多层改动时使用业务领域,不罗列目录。- 标题准确描述单一逻辑目标,避免“更新代码”“修复问题”等空泛文字;正文使用短列表总结关键变化与验证结果。
- 存在不兼容变更时在类型后加
!,并在正文末尾添加BREAKING CHANGE: ...。 - 若改动包含多个彼此独立的逻辑主题,建议拆成多条提交信息并说明各自文件范围,不把所有变化强塞进一个提交。
- 只生成建议文本,不执行
git add、git commit、git push,除非用户在当前请求中明确授权。
模板:
type(scope): 简短说明
- 关键变化一
- 关键变化二
- 验证:实际执行的测试或编译结果
交付清单
- 说明源码、配置、迁移、日志、注释、根目录开发契约、数据库表结构文档、OpenAPI 和依赖变化。
- 列出敏感信息与重复日志检查、依赖兼容结论、测试和编译结果。
- 明确未验证项、剩余风险、IDEA 联调步骤,以及是否延后 JAR、Docker 和部署工作。
- 附上与本次实际改动一致的建议 Git 提交信息,并明确未代用户提交。