- git-workflow.md v1.1: scope-enum 对齐实际服务名(identity→iam 等) - git-workflow.md: 新增 §4.7 模块 Owner 与 CODEOWNERS 章节 - git-workflow.md: husky hooks 与实际文件对齐,新增 pre-push 说明 - .github/CODEOWNERS: 定义各模块 owner team 与 PR 合并职责 - .github/pull_request_template.md: PR 模板
36 KiB
Edu Git 工作流规范
版本:1.1 日期:2026-07-08 状态:基线发布(v1.1:scope-enum 对齐 + CODEOWNERS) 适用范围:Edu 多语言 monorepo(pnpm workspace + go.work + pyproject.toml) 关联文档:
目录
- 分支策略
- Conventional Commits 规范
- commitlint + husky 配置
- PR 与 Code Review
- 文档同步规则
- 多语言 monorepo 提交规则
- 版本与发布
- 紧急回滚
- 附录:CICD 与 Edu Git 工作流差异
一、分支策略
1.1 主干开发(Trunk-Based Development)
本项目采用主干开发模式,所有变更最终合并至 main 分支。
核心原则:
main分支始终保持可发布状态- 短生命周期特性分支(通常 ≤ 3 天)
- 频繁集成,每天至少一次 rebase/merge 至最新
main - 通过特性开关(Feature Flag)控制未完成功能的暴露
1.2 分支模型
gitGraph
commit id: "init"
commit id: "P1-foundation"
branch feat/identity-service
checkout feat/identity-service
commit id: "scaffold"
commit id: "implement-login"
commit id: "add-tests"
checkout main
merge feat/identity-service tag: "v0.2.0"
branch fix/jwt-expiry
checkout fix/jwt-expiry
commit id: "fix-token-refresh"
checkout main
merge fix/jwt-expiry tag: "v0.2.1"
branch release/v0.3
checkout release/v0.3
commit id: "freeze"
commit id: "hotfix"
checkout main
merge release/v0.3 tag: "v0.3.0"
1.3 分支命名规范
| 分支类型 | 前缀 | 示例 | 生命周期 |
|---|---|---|---|
| 主干 | main |
main |
永久 |
| 特性 | feat/ |
feat/identity-service |
≤ 3 天 |
| 修复 | fix/ |
fix/jwt-expiry |
≤ 1 天 |
| 重构 | refactor/ |
refactor/split-data-access |
≤ 5 天 |
| 性能 | perf/ |
perf/query-optimization |
≤ 3 天 |
| 文档 | docs/ |
docs/api-specification |
≤ 2 天 |
| 发布 | release/v |
release/v0.3.0 |
发布周期内 |
| 热修复 | hotfix/ |
hotfix/v0.3.1 |
≤ 1 天 |
规则:
- 分支名使用 kebab-case
- 一个分支只做一件事,禁止在一个分支内混合多个无关变更
- 特性分支命名包含服务/模块名(
feat/identity-service而非feat/login)
1.4 分支保护规则
main 分支保护:
- 禁止直接 push,必须通过 PR
- 至少 1 名 Reviewer 审批通过(核心模块需 2 名)
- 所有 CI 检查通过(lint + typecheck + test + build)
- 分支必须与
main保持最新(无冲突或已 rebase) - 禁止 force push
release/* 分支保护:
- 禁止直接 push,仅接受 cherry-pick 或特定 hotfix PR
- 至少 2 名 Reviewer 审批
- 发布完成后打 tag 并归档
二、Conventional Commits 规范
2.1 提交信息格式
所有提交信息必须遵循 Conventional Commits 1.0.0:
<type>(<scope>): <subject>
<body>
<footer>
2.2 类型(type)
| 类型 | 含义 | 是否触发发布 |
|---|---|---|
feat |
新功能 | 是(MINOR) |
fix |
Bug 修复 | 是(PATCH) |
perf |
性能优化 | 是(PATCH) |
refactor |
重构(不改变行为) | 否 |
style |
代码风格(格式化、空白) | 否 |
test |
新增/修改测试 | 否 |
docs |
文档变更 | 否 |
build |
构建系统或依赖变更 | 否 |
ci |
CI 配置变更 | 否 |
chore |
杂项(不修改 src 或 test) | 否 |
revert |
回滚某次提交 | 是 |
2.3 范围(scope)
scope 必须是服务名或包名,详见 §3.3 scope-enum。
示例:
feat(identity): 实现用户注册接口fix(gateway): 修复路由匹配优先级perf(teaching): 优化课表查询 N+1 问题docs(contracts): 补充 identity.proto 字段说明
2.4 主题(subject)
规则:
- 使用中文简短描述
- 不超过 50 个字符
- 不以句号结尾
- 使用祈使句("实现用户注册"而非"实现了用户注册")
- 不包含 type/scope(已在前面体现)
2.5 正文(body)
规则:
- 解释"为什么"而非"做了什么"(代码已说明做了什么)
- 每行不超过 72 个字符
- 使用无序列表列出关键变更点
- 涉及 breaking change 必须在正文开头说明
示例:
feat(teaching): 作业提交支持附件上传
- 新增 Attachment 聚合根,支持多文件关联
- Outbox 事件 HomeworkSubmitted 新增 attachments 字段
- BFF 接口新增 multipart/form-data 支持
BREAKING CHANGE: HomeworkSubmitted 事件 schema 变更,
消费方需升级 proto 至 v2 才能消费新事件
2.6 脚注(footer)
用于关联 Issue、PR、Jira 卡片:
fix(content): 修复题库导入时重复题目标题去重逻辑
Closes #123
Refs EDU-456
Reviewed-by: @reviewer-name
三、commitlint + husky 配置
3.1 安装与初始化
# 在 monorepo 根目录
pnpm add -Dw @commitlint/cli @commitlint/config-conventional husky lint-staged
# 初始化 husky
pnpm exec husky init
3.2 commitlint 配置
实际生效配置文件:仓库根 .commitlintrc.js(CommonJS)。
/** @type {import('@commitlint/types').UserConfig} */
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
// type 枚举
"type-enum": [
2,
"always",
[
"feat",
"fix",
"perf",
"refactor",
"style",
"test",
"docs",
"build",
"ci",
"chore",
"revert",
],
],
// type 小写
"type-case": [2, "always", "lower-case"],
// type 不能为空
"type-empty": [2, "never"],
// scope 枚举(见 3.3,与 .commitlintrc.js 保持同步)
"scope-enum": [
2,
"always",
[
// 网关层(Go)
"api-gateway",
"push-gateway",
// 业务微服务(NestJS / FastAPI)
"iam",
"core-edu",
"classes",
"content",
"data-ana",
"msg",
"ai",
// BFF 聚合层(NestJS)
"teacher-bff",
"student-bff",
"parent-bff",
// 微前端(Next.js)
"teacher-portal",
"student-portal",
"parent-portal",
"admin-portal",
// 共享包(packages/)
"shared-proto",
"shared-ts",
"shared-go",
"shared-py",
"shared-tokens",
// 工具与平台级
"arch-scan",
"infra",
"docs",
"deps",
"release",
],
],
// scope 小写
"scope-case": [2, "always", "lower-case"],
// subject 不能为空
"subject-empty": [2, "never"],
// subject 不超过 50 字符
"subject-max-length": [2, "always", 72],
// subject 不以句号结尾
"subject-full-stop": [2, "never", "."],
// body 每行不超过 100 字符
"body-max-line-length": [1, "always", 100],
// footer 每行不超过 100 字符
"footer-max-line-length": [1, "always", 100],
// header 不超过 100 字符
"header-max-length": [2, "always", 100],
},
};
单一事实源:实际生效的配置在仓库根
.commitlintrc.js,本节示例仅作说明。修改 scope 必须同步更新.commitlintrc.js与本节,并在 PR 中说明原因。
3.3 scope-enum 完整清单
与仓库根
.commitlintrc.js保持同步;修改 scope 必须同时更新此处与.commitlintrc.js。
| 分类 | scope | 对应目录 | 说明 |
|---|---|---|---|
| 网关层 | api-gateway |
services/api-gateway/ |
API 网关(Go + Gin) |
| 网关层 | push-gateway |
services/push-gateway/ |
WebSocket 推送网关(Go) |
| 业务微服务 | iam |
services/iam/ |
身份与访问管理(NestJS) |
| 业务微服务 | core-edu |
services/core-edu/ |
教学核心服务(NestJS,Outbox + Kafka) |
| 业务微服务 | classes |
services/classes/ |
班级服务(P1 黄金模板,P3 并入 core-edu) |
| 业务微服务 | content |
services/content/ |
内容资源服务(NestJS + Neo4j) |
| 业务微服务 | data-ana |
services/data-ana/ |
数据分析服务(Python + FastAPI + ClickHouse) |
| 业务微服务 | msg |
services/msg/ |
消息通知服务(NestJS + ES) |
| 业务微服务 | ai |
services/ai/ |
AI 网关服务(Python + FastAPI + LLM) |
| BFF 聚合层 | teacher-bff |
services/teacher-bff/ |
教师端 BFF(NestJS) |
| BFF 聚合层 | student-bff |
services/student-bff/ |
学生端 BFF(待建立) |
| BFF 聚合层 | parent-bff |
services/parent-bff/ |
家长端 BFF(待建立) |
| 微前端 | teacher-portal |
apps/teacher-portal/ |
教师端 Portal(Next.js) |
| 微前端 | student-portal |
apps/student-portal/ |
学生端 Portal(待建立) |
| 微前端 | parent-portal |
apps/parent-portal/ |
家长端 Portal(待建立) |
| 微前端 | admin-portal |
apps/admin-portal/ |
管理端 Portal(待建立) |
| 共享包 | shared-proto |
packages/shared-proto/ |
protobuf 契约定义 |
| 共享包 | shared-ts |
packages/shared-ts/ |
TS 共享类型与工具(待建立) |
| 共享包 | shared-go |
packages/shared-go/ |
Go 共享工具(待建立) |
| 共享包 | shared-py |
packages/shared-py/ |
Python 共享工具(待建立) |
| 共享包 | shared-tokens |
packages/shared-tokens/ |
设计令牌(待建立) |
| 工具 | arch-scan |
scripts/arch-scan/ |
架构元数据库扫描器 |
| 平台级 | infra |
infra/ |
基础设施(K8s / docker-compose / backup / monitoring) |
| 平台级 | docs |
docs/ |
平台级文档(跨多模块) |
| 平台级 | deps |
- | 依赖升级 |
| 平台级 | release |
- | 发布相关 |
chore/ci/build等 Conventional Commits 标准 type 不需要 scope,可直接使用chore: xxx、ci: xxx。
3.4 husky hooks
实际生效的 hook 文件在仓库根 .husky/ 目录。使用 npx --no-install 确保使用本地依赖。
.husky/commit-msg:
npx --no-install commitlint --edit $1
.husky/pre-commit:
npx --no-install lint-staged
.husky/pre-push(推送前类型检查,TS 服务 typecheck 通过后才允许推送):
#!/usr/bin/env sh
# 推送前运行类型检查(TS 服务)
pnpm -r run typecheck 2>/dev/null || echo "[pre-push] typecheck 跳过或不可用"
# Go 服务编译检查
for d in services/api-gateway services/push-gateway; do
if [ -d "$d" ]; then (cd "$d" && go build ./... ) || exit 1; fi
done
注意:
pre-push中的 typecheck 在 TS 服务 ESLint 9 flat config 迁移完成前为可选(当前pnpm -r run typecheck缺失脚本,会 fallback 到 echo 提示)。Gogo build检查必须通过。
3.5 lint-staged 配置
实际生效配置文件:仓库根 lint-staged.config.js(CommonJS)。
module.exports = {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{go,mod,sum}": ["gofmt -w", "golangci-lint run --fix"],
"*.{py}": ["ruff check --fix", "ruff format"],
"*.proto": ["buf format --write"],
"*.md": ["prettier --write"],
};
JSON / YAML 文件由 prettier 在
*.md规则外按全局配置处理,如需显式规则可在lint-staged.config.js追加'*.{json,yaml,yml}': ['prettier --write']。
四、PR 与 Code Review
4.1 PR 流程
flowchart TD
A[开发者创建特性分支] --> B[提交代码]
B --> C[本地通过 lint + typecheck + test]
C --> D[rebase 至最新 main]
D --> E[推送至远程]
E --> F[创建 PR]
F --> G[CI 自动检查]
G --> H{CI 通过?}
H -->|否| I[修复问题]
I --> B
H -->|是| J[Reviewer 审查]
J --> K{审查通过?}
K -->|需修改| L[根据反馈修改]
L --> B
K -->|通过| M[Squash Merge]
M --> N[删除特性分支]
N --> O[CI 部署至 staging]
4.2 PR 标题
PR 标题必须符合 Conventional Commits 规范(与最终 squash merge 的 commit message 一致):
feat(identity): 实现用户注册接口
4.3 PR 模板
.github/pull_request_template.md(或 Gitea 等价路径):
## 变更说明
<!-- 简述本次变更的目的和实现方式 -->
## 变更类型
- [ ] feat: 新功能
- [ ] fix: Bug 修复
- [ ] perf: 性能优化
- [ ] refactor: 重构
- [ ] test: 测试
- [ ] docs: 文档
- [ ] build/ci: 构建/CI
## 影响范围
<!-- 列出受影响的服务/包 -->
- 服务:
- 包:
- 数据库迁移:是 / 否
- protobuf 契约变更:是 / 否
- Kafka topic 变更:是 / 否
## 测试情况
- [ ] 单元测试通过
- [ ] 集成测试通过
- [ ] 本地手动测试通过
- [ ] 新增测试覆盖新功能
## 文档同步
- [ ] 已更新服务 README(如涉及服务结构变更)
- [ ] 已更新架构文档(如涉及架构变更)
- [ ] 已运行 `pnpm run arch:scan` 更新 arch.db
- [ ] 已更新 known-issues.md(如遇到新问题)
## Breaking Change
- [ ] 否
- [ ] 是(请在下方说明影响和迁移路径)
## 关联 Issue
Closes #
4.4 Reviewer 要求
| 变更类型 | 最少 Reviewer | 备注 |
|---|---|---|
| 普通业务变更 | 1 | 默认 |
| 跨服务变更 | 2 | 涉及 ≥ 2 个服务 |
| protobuf 契约变更 | 2 | 需包含架构组成员 |
| 数据库 schema 变更 | 2 | 需包含 DBA 或架构组 |
| 安全相关变更 | 2 | 需包含安全负责人 |
| 核心模块(auth/identity) | 2 | 核心模块强制 2 人 |
4.5 Code Review 清单
通用检查:
- 代码是否符合 编码规范
- 是否有明显的逻辑错误
- 错误处理是否完整(不忽略 error/err/exception)
- 日志是否包含足够的上下文(request_id、user_id 等)
- 是否存在硬编码的密钥、token、连接字符串
架构检查:
- 是否违反限界上下文边界(跨服务直接查 DB)
- 是否违反依赖方向(shared 反向依赖 services)
- protobuf 变更是否向后兼容
- 事件 schema 变更是否向后兼容
性能检查:
- 是否有 N+1 查询
- 是否有未加索引的查询
- 是否有不必要的大对象拷贝
- 是否有阻塞事件循环的同步操作(Python/Node)
安全检查:
- 所有入口是否经过权限校验
- 用户输入是否经过验证
- SQL 是否使用参数化查询
- 是否有 SQL 注入、XSS、SSRF 风险
4.6 合并策略
默认使用 Squash Merge:
- 保留 PR 的完整变更作为一个 commit
- commit message 使用 PR 标题
- 删除特性分支
禁止使用 Merge Commit(除非是发布分支合并回 main):
- 避免历史中充斥 "Merge branch" 噪音
- 保持线性历史
Rebase Merge:
- 仅用于需要保留多个有意义 commit 的特性分支
- 需在 PR 中说明原因
4.7 模块 Owner 与 CODEOWNERS
实际生效文件:仓库根 .github/CODEOWNERS(GitHub/Gitea 原生支持,自动为 PR 分配 reviewer)。
设计原则:
- 每个模块至少 1 名 owner,核心模块 2 名
- 跨模块变更(如 proto 契约、arch.db、project_rules)由架构组 review
- 基础设施变更(K8s/Helm/backup)由 SRE review
- owner 名单变更需走 PR,由架构组审批
模块 Owner 分配矩阵:
| 模块分类 | 路径 | Owner Team | 最少 Reviewer | 备注 |
|---|---|---|---|---|
| 架构与规则 | .trae/rules/、docs/architecture/、docs/standards/ |
@edu-platform/arch |
2 | 架构组强制 review |
| 架构工具 | scripts/arch-scan/ |
@edu-platform/arch |
1 | |
| 根配置 | .commitlintrc.js、lint-staged.config.js、package.json、pnpm-workspace.yaml、go.work、pyproject.toml、tsconfig.base.json |
@edu-platform/arch |
2 | 影响全局 |
| 共享包 | packages/shared-proto/ |
@edu-platform/arch |
2 | 契约变更影响所有服务 |
| 网关层 | services/api-gateway/、services/push-gateway/ |
@edu-platform/gateway |
1 | |
| IAM 服务 | services/iam/ |
@edu-platform/iam |
2 | 核心模块强制 2 人 |
| 教学核心 | services/core-edu/、services/classes/ |
@edu-platform/edu-core |
1 | |
| 内容资源 | services/content/ |
@edu-platform/content |
1 | |
| 消息通知 | services/msg/ |
@edu-platform/messaging |
1 | |
| 数据分析 | services/data-ana/ |
@edu-platform/data |
1 | |
| AI 服务 | services/ai/ |
@edu-platform/ai |
1 | |
| BFF 层 | services/teacher-bff/、services/student-bff/、services/parent-bff/ |
@edu-platform/edu-core |
1 | |
| 微前端 | apps/teacher-portal/、apps/student-portal/、apps/parent-portal/、apps/admin-portal/ |
@edu-platform/frontend |
1 | |
| 基础设施 | infra/k8s/、infra/backup/、infra/security/、infra/monitoring/ |
@edu-platform/sre |
2 | 生产环境变更强制 2 人 |
| CI/CD | .github/、.husky/ |
@edu-platform/sre |
1 | |
| 文档 | docs/troubleshooting/、docs/standards/ |
@edu-platform/arch |
1 | known-issues 更新 |
Team handle 占位符:上表
@edu-platform/*为 team handle 模板。实际团队 handle 需在 GitHub/Gitea Organization 中创建对应 team 后,同步更新.github/CODEOWNERS。
CODEOWNERS 文件维护规则:
- 新增服务/包时,必须在同一 PR 中更新
.github/CODEOWNERS - owner 变更(人员调动)需开独立 PR,由架构组审批
- CODEOWNERS 与本节表格保持同步,单一事实源为
.github/CODEOWNERS文件
五、文档同步规则
5.1 文档同步矩阵
| 代码变更类型 | 需同步的文档 | 同步时机 |
|---|---|---|
| 新增/删除服务 | 001_architecture_overview.md + 服务 README |
PR 内同步 |
| 新增/删除模块 | 服务 README + arch:scan |
PR 内同步 |
| 新增/删除导出函数 | pnpm run arch:scan |
提交前 |
| 修改函数签名 | pnpm run arch:scan |
提交前 |
| 修改权限点 | project_rules.md + 权限文档 |
PR 内同步 |
| 新增/删除数据库表 | 架构文档 + 服务 README | PR 内同步 |
| 新增/删除路由 | 服务 README + OpenAPI | PR 内同步 |
| 修改模块间依赖 | arch:scan + 架构文档 |
PR 内同步 |
| 新增 protobuf message | proto/ README + arch:scan |
PR 内同步 |
| 新增 Kafka topic | 架构文档 + 服务 README | PR 内同步 |
| 遇到新问题/经验 | known-issues.md |
PR 内同步 |
5.2 arch.db 同步规则
强制规则:任何代码变更提交前必须运行 pnpm run arch:scan 更新 arch.db。
# 提交前流程
pnpm run arch:scan # 更新 arch.db
pnpm run arch:query -- violations # 检查是否有违规
git add arch.db # 将 arch.db 纳入提交
git commit -m "feat(identity): 实现用户注册接口"
违规检查:
- 长文件(> 1000 行)
- 未校验权限的 Handler
- 循环依赖
- 跨限界上下文直接 DB 访问
5.3 服务 README 要求
每个服务必须包含 README.md,结构如下:
# [服务名] 服务
> 版本:1.0
> 限界上下文:[上下文名]
> 技术栈:[语言 + 框架]
## 职责
[一段话描述]
## 架构
[mermaid 架构图]
## 核心流程
[mermaid 时序图]
## 目录结构
[树形结构 + 说明]
## 依赖
- 上游服务:[列表]
- 下游服务:[列表]
- 共享包:[列表]
## 约束
[业务规则、技术约束]
## 架构决策
[关键设计决策记录]
5.4 known-issues.md 格式
docs/troubleshooting/known-issues.md 采用索引式速查手册格式:
### X.X 主题分区
| 场景 | 技术/规则 |
| -------- | ------------------ |
| 简述场景 | 正确做法(一句话) |
规则:
- 索引式:场景→技术/规则映射
- 不写代码示例和错误示范
- 同类问题在原条目补充,不重复创建
- 在"工作经验日志"区按时间倒序追加记录
六、多语言 monorepo 提交规则
6.1 单服务单提交原则
规则:一个 commit 只涉及一个服务或一个包的变更。
原因:
- 便于回滚(按服务粒度回滚)
- 便于追踪(changelog 清晰)
- 便于 review(聚焦单一职责)
例外:跨服务重构或契约变更可在同一 commit 中涉及多个服务,但需在 body 中说明。
6.2 生成代码单独提交
规则:protobuf 生成的代码必须与手写代码分开提交。
# 正确流程
# 1. 修改 proto 定义
git commit -m "feat(proto): 新增 UserRegistered 事件 schema"
# 2. 生成代码
pnpm run proto:generate
# 3. 单独提交生成代码
git commit -m "build(contracts): 重新生成 proto 代码"
6.3 跨语言变更拆分
当一个功能涉及多个语言(如 TS 服务 + Go 网关 + Python AI),按以下顺序拆分提交:
- 契约先行:
feat(proto): ... - 生成代码:
build(contracts): ... - 后端服务:
feat(teaching): ... - 网关层:
feat(gateway): ... - 前端:
feat(teacher-shell): ...
6.4 依赖升级规则
规则:
- 依赖升级使用
build(deps):类型 - 必须说明升级原因(安全、功能、兼容性)
- 安全漏洞修复必须包含 CVE 编号
- 大版本升级需单独 PR 并完整测试
示例:
build(deps): 升级 nestjs 至 10.3.0
- 修复 CVE-2024-1234 漏洞
- 升级至最新 patch 版本
- 全部测试通过
6.5 跨服务依赖更新
当一个服务的 protobuf 变更影响其他服务:
# 1. 提交 proto 变更
git commit -m "feat(proto): UserRegistered 事件新增 attachments 字段"
# 2. 生成新契约
pnpm run proto:generate
git commit -m "build(contracts): 生成 UserRegistered v2"
# 3. 更新消费方服务
git commit -m "feat(notification): 适配 UserRegistered v2 事件"
七、版本与发布
7.1 版本号体系
本项目采用双层版本号:
| 层级 | 格式 | 说明 |
|---|---|---|
| 平台版本 | v{阶段}.{迭代}.{patch} |
如 v0.3.1(P3 阶段第 1 次迭代 patch 1) |
| 服务版本 | {service}:{semver} |
如 identity:1.2.0 |
7.2 阶段版本范围
| 阶段 | 平台版本范围 | 说明 |
|---|---|---|
| P1 地基 | v0.1.x |
monorepo 初始化、CI/CD、arch.db |
| P2 身份 | v0.2.x |
identity + auth + notification |
| P3 核心教学 | v0.3.x |
org + teaching + content |
| P4 内容分析 | v0.4.x |
insight + CQRS 读模型 |
| P5 沟通AI | v0.5.x |
comm + AI 增强 |
| P6 硬化 | v1.0.x |
正式发布版 |
7.3 Docker 镜像标签
格式:{registry}/edu/{service}:{tag}
| tag 类型 | 格式 | 示例 | 用途 |
|---|---|---|---|
| 版本号 | v{version} |
v0.3.1 |
正式发布 |
| 服务版本 | {service}-{semver} |
identity-1.2.0 |
服务独立版本 |
| Git SHA | sha-{short} |
sha-a1b2c3d |
精确追溯 |
| 最新 | latest |
latest |
开发环境 |
| 阶段 | {stage}-{sha} |
staging-a1b2c3d |
阶段环境 |
7.4 发布流程
flowchart TD
A[main 分支达到发布标准] --> B[创建 release/v0.3.x 分支]
B --> C[运行完整测试套件]
C --> D{测试通过?}
D -->|否| E[修复问题]
E --> C
D -->|是| F[更新 CHANGELOG.md]
F --> G[打 tag v0.3.0]
G --> H[CI 自动构建镜像]
H --> I[部署至 staging]
I --> J[冒烟测试]
J --> K{冒烟通过?}
K -->|否| L[修复并重新发布]
L --> C
K -->|是| M[审批发布至 production]
M --> N[monitoring 观察指标]
N --> O{指标正常?}
O -->|否| P[紧急回滚]
O -->|是| Q[发布完成]
Q --> R[合并 release 分支回 main]
7.5 CHANGELOG 格式
CHANGELOG.md 按 Keep a Changelog 格式维护:
## [v0.3.0] - 2026-08-15
### Added
- 教学核心服务新增作业管理功能
- 内容服务支持题库导入
- 教师端 Shell 新增作业批改界面
### Changed
- identity 服务升级至 NestJS 10.3
- gateway 路由匹配算法优化
### Fixed
- 修复 JWT 刷新 token 过期判断错误
- 修复课表查询时区问题
### Breaking Changes
- UserRegistered 事件 schema 变更至 v2,消费方需升级
7.6 服务独立版本
每个服务维护独立的 VERSION 文件,内容为 semver:
# services/identity/VERSION
1.2.0
版本号升级规则:
- MAJOR:Breaking Change(protobuf 不兼容变更、API 破坏性修改)
- MINOR:新增功能,向后兼容
- PATCH:Bug 修复,向后兼容
八、紧急回滚
8.1 回滚策略
| 场景 | 回滚方式 | 耗时 |
|---|---|---|
| 代码缺陷 | git revert + 重新部署 |
5-10 分钟 |
| 镜像问题 | kubectl rollout undo |
1-2 分钟 |
| 数据库迁移问题 | 执行迁移 down 脚本 | 5-30 分钟 |
| 配置错误 | 回滚 ConfigMap/Secret | 1-2 分钟 |
| 全站故障 | 回滚至上一稳定 tag | 10-30 分钟 |
8.2 代码回滚
# 1. 确认要回滚的 commit
git log --oneline -10
# 2. 创建回滚分支
git checkout -b hotfix/v0.3.1
# 3. 回滚指定 commit
git revert <commit-sha>
# 4. 推送并创建 PR
git push origin hotfix/v0.3.1
# 5. 紧急审批合并后部署
8.3 K8s 部署回滚
# 查看部署历史
kubectl rollout history deployment/identity -n edu-prod
# 回滚至上一版本
kubectl rollout undo deployment/identity -n edu-prod
# 回滚至指定版本
kubectl rollout undo deployment/identity -n edu-prod --to-revision=3
# 监控回滚状态
kubectl rollout status deployment/identity -n edu-prod
8.4 数据库迁移回滚
规则:
- 所有迁移必须提供
up和down脚本 down脚本必须在 CI 中测试- 回滚前必须备份生产数据
# 回滚最近一次迁移
pnpm --filter identity run migrate:down
# 回滚至指定版本
pnpm --filter identity run migrate:down -- --to <version>
8.5 回滚原则
- 优先保证可用性:回滚决策应在 5 分钟内做出,无需 root cause
- 保留现场:回滚前保存日志、metrics、错误堆栈
- 通知相关方:回滚后立即通知运维、产品、相关开发
- 复盘改进:回滚后 24 小时内完成事故复盘,记录至
docs/troubleshooting/incidents/
8.6 回滚记录格式
docs/troubleshooting/incidents/YYYY-MM-DD-incident.md:
# 事故记录:[简述]
> 日期:YYYY-MM-DD
> 影响范围:[服务/用户]
> 持续时间:[开始-结束]
> 严重等级:P0/P1/P2/P3
## 时间线
- HH:MM 告警触发
- HH:MM 确认问题
- HH:MM 决定回滚
- HH:MM 回滚完成
- HH:MM 服务恢复
## 影响分析
[受影响的功能、用户数、业务损失]
## 根本原因
[技术原因 + 流程原因]
## 回滚过程
[执行的操作]
## 改进措施
- [ ] 短期:[立即修复项]
- [ ] 中期:[流程改进项]
- [ ] 长期:[架构改进项]
九、附录:CICD 与 Edu Git 工作流差异
| 维度 | CICD(Next.js 单应用) | Edu(微服务 monorepo) |
|---|---|---|
| 仓库结构 | 单一 Next.js 应用 | 多语言 monorepo(pnpm + go.work + uv) |
| 分支策略 | trunk-based | trunk-based(沿用) |
| 提交规范 | Conventional Commits | Conventional Commits(沿用,scope 扩展至服务/包) |
| scope 范围 | 模块名(如 exams、homework) |
服务/包名(如 iam、api-gateway、shared-proto) |
| commitlint scope-enum | 35 个模块 | 26 个 scope(含服务/包/工具/平台级,与 .commitlintrc.js 同步) |
| PR Reviewer | 1 人 | 1-2 人(核心模块/跨服务 2 人) |
| 合并策略 | Squash Merge | Squash Merge(沿用) |
| 版本号 | 单一应用版本 | 双层(平台版本 + 服务独立版本) |
| 发布粒度 | 整体发布 | 按服务独立发布 |
| Docker 镜像 | 单一镜像 | 每服务一镜像 |
| 回滚粒度 | 整体回滚 | 按服务回滚 |
| 数据库迁移 | Drizzle 单库 | 每服务独立库 + 独立迁移 |
| 文档同步 | npm run arch:scan |
pnpm run arch:scan(多语言扫描) |
| CI 检查 | lint + tsc + test | lint + typecheck + test(按语言分别执行) |
| 紧急回滚 | git revert + 重新部署 |
git revert + kubectl rollout undo |
| 事故复盘 | known-issues.md | incidents/ 目录独立记录 |
变更记录
| 版本 | 日期 | 变更内容 |
|---|---|---|
| 1.0 | 2026-07-07 | 基线发布,从 CICD 单应用规范迁移至微服务多语言 monorepo |
| 1.1 | 2026-07-08 | scope-enum 对齐实际服务名(identity→iam、teaching→core-edu 等);新增 §4.7 模块 Owner 与 CODEOWNERS;husky hooks 与实际文件对齐;新增 pre-push hook 说明 |