# 协作规范 本仓库是 M-Z 业务层的唯一主干。以下规则不是风格偏好,每一条都对应过一类实际事故,改动前请先读完。 ## 一、分支模型 | 分支 | 用途 | 说明 | |---|---|---| | `main` | 主干,随时可构建 | 不直接提交。只接受合并 | | `feat/<简短英文>` | 新功能 | 如 `feat/notify-retry` | | `fix/<简短英文>` | 缺陷修复 | 如 `fix/raw-ref-null` | | `docs/<简短英文>` | 仅文档 | 如 `docs/j00-v18` | | `hotfix/<简短英文>` | 生产应急 | 合并后必须回写 `main` | 分支名用英文小写与连字符。不建长期存在的个人分支——长期分支与主干拉开两周以上,合并冲突的代价远高于当时直接改主干。 ## 二、提交信息 格式: ``` <类型>(<范围>): <一句话说明> <可选正文:为什么这么改,以及这样改的代价> ``` 类型只有六种: | 类型 | 用于 | |---|---| | `feat` | 新增能力 | | `fix` | 修缺陷 | | `refactor` | 不改行为的重构 | | `docs` | 文档与契约 | | `chore` | 构建、依赖、配置 | | `revert` | 回退 | 范围写模块名,不写人名。示例: ``` fix(internal): I-12 原文缺失返回 404 而不是 500 rawRef 指向不存在的行时,原先抛 NPE 被全局兜底成 500, 接入侧无法区分"我写错了"和"你这边崩了"。改为显式 404 + 原因码 RAW_REF_NOT_FOUND。 ``` **一条提交只做一件事。** 把"改接口"和"顺手格式化整个文件"混在一条提交里,出问题时无法单独回退其中一件。 **不要写"按某人要求修改"。** 写清楚改了什么、为什么,谁要求的写在会签与议题里,不写在提交信息里。 ## 三、绝不入库的东西 | 类别 | 具体 | |---|---| | 凭据 | 口令、token、AK/SK、私钥、`.pem`、`.key`、`.jks`、证书 | | 本地配置 | `.env`、`application-local.yml`、`*.local.yml` | | 构建产物 | `target/`、`*.jar`、`*.war`、`*.class` | | IDE 与系统 | `.idea/`、`*.iml`、`.vscode/`、`Thumbs.db`、`.DS_Store` | | 运行产物 | 日志、`*.jsonl`、证据 `raw/`、截图 | | 公司无关资料 | 员工绩效、客户名单、采购函、其他项目报价 | **唯一例外是 `.env.example`。** 它是变量清单模板,必须入库——不入库接手的人就不知道要配哪些变量,只能去翻 `application.yml` 逐个猜。模板里只放占位值,真实值填在本地 `.env` 里。新增环境变量时同步更新它,否则清单很快就不准了。 `.gitignore` 已经覆盖上述路径,但**它只挡新增,不挡已提交**。加文件前先 `git status` 看一眼。 判断标准很简单:**这个文件一旦公开,会不会造成损失?** 会,就不要放进来。Git 的历史是追加式的,删除只影响最新版本,历史里那份永远在。 ## 四、迁移脚本(Flyway)硬规则 1. **只增不改。** 已执行过的 `V1.0.x__*.sql` 一个字节都不能动。改动会让校验和不符,已部署环境启动即失败(`Migration checksum mismatch`)。 2. **不重命名、不删除、不调换顺序。** 版本号是执行顺序的唯一依据。 3. **编号连续递增**,命名 `V1.0.__<英文小写下划线>.sql`。当前终态 `1.0.28`。 4. **加列与加索引拆成两条 `ALTER`。** INSTANT 与 INPLACE 混在同一条语句里,MySQL 会整体降级为重建表,大表上表现为长时间锁表。 5. **迁移里不写 `ALGORITHM=INSTANT`。** 本机 MySQL 是 8.0.11,该语法 8.0.12 才有,写了后端起不来;确需指定用 `ALGORITHM=INPLACE, LOCK=NONE`。 6. **校验和不符不要手改 `flyway_schema_history`。** 用 `flyway repair`,它只重写校验和与描述,不改表结构。 7. 一期与二期脚本分目录。二期脚本放 `db/migration-phase2/`,**不要**放进 `spring.flyway.locations` 指向的目录,否则一期启动会建出二期空表。 ## 五、接口契约硬规则 本工程**没有运行时 OpenAPI 生成**(未引入 springdoc)。`docs/contract/` 下的 md 与 yaml 是**手工维护**的唯一机读契约,因此"改了代码但没改文档"就是契约漂移,且没有任何工具会报错。 | 改动 | 必须同步 | |---|---| | 接口增删、字段增删、字段语义变化 | `docs/contract/6_J-00_接入接口规格_V1.8.md` | | 同上,机读部分 | `docs/contract/6_J-00_接入接口规格_V1.8_OpenAPI.yaml` | | 五个内部接口(I-01/I-02/I-05/I-12/I-18) | `docs/contract/内部五接口定义.md` | | 状态、结果码、状态迁移 | `docs/contract/状态机.md` | 契约版本号升级时,文件名、正文、yaml 的 `info.version` 三处必须一致,旧版本文件保留不删。 **以下是不许自己决定的判断题,先会签再改:** 幂等键的构成、`rawRef` 的必填性与指向、时间来源与精度口径、命令状态数量、告警状态与动作的映射。这些一旦出现两套写法,表现是"两边都正常,数据互相不认"。 ## 六、目录约定 - **新增文档进 `docs/`**,按性质放 `handover/`(交付与清理)、`contract/`(接口契约)、`reference/`(指纹与校验)。不要堆在仓库根。 - **目录名用 ASCII**(`docs`、`demo`、`aomin-gateway`),避免跨平台与 URL 编码问题。 - **文件名可用中文**。`docs/` 下的历史文件保留原始文件名,因为会签材料、复核记录按名字索引,改名等于让它失联。 - **新增文档不要覆盖旧版**:同主题改版新建文件、版本号 +1,旧件保留。 ## 七、行尾与编码 | 文件 | 约定 | |---|---| | `.bat` / `.cmd` | **GBK 编码 + CRLF**,脚本内 `chcp 936`。用 UTF-8 编辑器"修正"会立刻让中文提示变乱码 | | 新增源码与文档 | UTF-8(无 BOM)+ LF | | `.jar` / 图片 / 文档 | 二进制,禁止行尾转换 | **`.gitattributes` 里 `* -text` 是刻意的,不要改成 `text=auto`。** 基线按交付字节原样保存,源码树摘要才可复现;一旦开启自动转换,克隆出来的树与交付包不是同一份字节,摘要对不上,复核会误判成"被改过"。归一化行尾属于基线变更,要走会签。 代价是:交付包内 14 个文件本来就是 CRLF,与新增的 LF 文件混在一起,`git diff` 遇到"整文件行尾变化"时看不出实质改动。改老文件前先确认它的行尾,别让编辑器顺手整篇转换。 Windows 上建议开启中文路径正常显示: ```bash git config --global core.quotepath false ``` ## 八、评审 | 改动类型 | 至少几人看 | 谁必须看 | |---|---|---| | 迁移脚本、表结构 | 2 | 数据库负责人 | | 接口契约、内部五接口 | 2 | 架构一致性负责人 | | 权限、鉴权、加解密 | 2 | 架构一致性负责人 | | 其他代码 | 1 | 模块负责人 | | 纯文档 | 1 | — | 评审看三件事,不看代码风格:**改了会不会影响已验收的东西;有没有第二套实现;文档跟上了没有。** ## 九、大文件 单个文件超过 5 MB 不要直接提交。演示素材、证据、构建产物走受控目录或发布附件,仓库只放需要版本追溯的内容。 已经提交进去的大文件,删除也只是"最新版本没有",`.git` 里仍然存在。真要清理得重写全部历史,代价远高于当初不放进来。 ## 十、开工前自检 ```bash git status # 有没有把 target/ 和 .env 带进来 git diff --cached --stat # 改动范围是否符合预期 git grep -nIE 'password|secret|token' --cached -- ':!docs' # 有没有硬编码凭据 ``` 第三条命中不一定是问题(变量名也会命中),但每一条都要能说清为什么不是问题。