Sfoglia il codice sorgente

docs(readme): 补全四大板块——技术栈/目录结构与用途/编码规范/构建与校验

- 技术栈表对齐 go.mod 实际依赖(补 ULID/argon2/JWT v5/zap,配置改 JSON 12-factor)
- 新增目录用途表(cmd/config/internal 七域逐层 + 依赖方向)
- 编码规范拆为 §3.1~3.9:分层架构/分包铁律/接口契约/GORM 纪律/GoDoc 三段式注释/异常处理/日志配置/异步任务/测试门禁/Git 纪律
- 构建与校验补本地启动与部署形态摘要
Q 4 giorni fa
parent
commit
92821233ce
1 ha cambiato i file con 178 aggiunte e 57 eliminazioni
  1. 178 57
      README.md

+ 178 - 57
README.md

@@ -1,84 +1,205 @@
 # 统一 Go 后端(模块化单体)
 
-> 两个 PHP 后端(教务 ysw-finance + 教学 yuansiweimarking_v2)收敛为**一个 Go 后端**,模块化单体、单二进制(ADR-011)。本文档描述工程结构与域分包约定;架构决策见 `../unified/00-planning/用户系统合并规划.md`。
+> 两个 PHP 后端(教务 ysw-finance + 教学 yuansiweimarking_v2)收敛为**一个 Go 后端**,模块化单体、单二进制(ADR-011)。
+> 本文档覆盖四块:**技术栈 / 目录结构与用途 / 编码规范 / 构建与校验**。架构决策 ADR-001~016 全文见工作区文档 `unified/00-planning/用户系统合并规划.md`(本仓库不复制 ADR 原文,仅引用编号)。
 
 ## 定位
 
-单一 Go 服务承载全部业务域:身份/SSO、多租户组织、教务域、教学域、AI 编排、异步任务。对外经 **nginx 统一入口**(TLS + HTTP/2 终结,ADR-010)暴露;数据库为 **PolarDB MySQL 单 schema**(ADR-008)。
+单一 Go 服务承载全部业务域:身份/SSO、多租户组织、教务域、教学域、AI 编排、异步任务。对外经 **nginx 统一入口**(TLS + HTTP/2 终结,ADR-010)暴露;数据库为 **PolarDB MySQL 单 schema**(ADR-008,过渡态允许按域多库)。部署形态为**单机双实例·预留 K8s**(ADR-015)。
 
-## 技术栈(与规划对齐)
+## 1. 技术栈
 
-| 关注点 | 选型 | 依据 |
+与 `go.mod` 实际依赖一一对应(2026-11-05 核实):
+
+| 关注点 | 选型(版本) | 说明 / 依据 |
 |---|---|---|
-| 语言 | Go 1.27(go.mod 最低版本锁定;2026-11-05 由 1.23 升级,依赖零变化全绿实证) | 统一后端重写 |
-| Web 框架 | **Fiber** | ADR-009(用户拍板) |
-| ORM / 数据访问 | GORM + **Repository(强制作用域注入)** | ADR-006/007(多租户 institution/campus 过滤,应用层强制) |
-| 数据库 | PolarDB MySQL 单 schema | ADR-008(源库在阿里云,DTS 迁移) |
-| 身份 / SSO | OIDC(RS256),**identity 模块 = Provider + client 一体** | ADR-004 / ADR-011 |
-| 角色 | **7 类**:超管/教务/财务/老师/学生/家长/市场 | 三项拍板(市场扩入第 7 角色) |
-| 异步任务 | 统一 DB 任务表 + Go 工作池 | 替代两套旧队列 |
-| 配置 / 日志 | viper + zap | |
-| 外部入口 | nginx 统一七层入口(`proxy_buffering off` 支持 SSE) | ADR-010 |
-| 对象存储 | 阿里云 OSS | 文件资产迁移 |
-| AI 能力 | 消费 Python FastAPI Agent(契约 OpenAPI 化) | 跨系统综合分析 |
-
-## 目录结构约定
+| 语言 | **Go 1.27** | `go.mod` 锁 `go 1.27`(语言级别 + 最低工具链);2026-11-05 由 1.23 升级,依赖零变化全绿实证 |
+| Web 框架 | **Fiber v2**(v2.52.16) | ADR-009 拍板;fasthttp 内核,吞吐高/内存低,契合 ~325 接口高频只读;SSE 支持好 |
+| ORM / 数据访问 | **GORM**(v1.31.2)+ Repository 分层 | 多租户 institution/campus 作用域由 Repository 强制注入(ADR-006/007);科目分表用动态表名 |
+| 数据库 | PolarDB MySQL 8.0(单 schema) | ADR-008;源库同引擎,阿里云 DTS 平滑迁移 |
+| 缓存 / 锁 | Redis(阿里云,6/7) | 热点缓存 + 限流计数 + 任务表分布式锁;登录态由 token 承载,Redis 不存会话 |
+| 身份 / SSO | Go 自研 identity 模块(Provider + client 一体) | ADR-014 **最小可行**:统一 token 签发 + 本地验签 + 用户中心 API,预留 OIDC 端点(`/auth/oidc/*` 占位 501) |
+| JWT | **golang-jwt/jwt/v5**(v5.3.1) | RS256 签发/验签;access 30min + refresh 30 天;jti 吊销走 Redis 黑名单 |
+| 身份主键 | **ULID**(github.com/oklog/ulid v1.3.1) | ADR-013:canonical_user_id = VARCHAR(26) Crockford,全项目统一;域外键禁引用旧 int user_id |
+| 密码哈希 | **argon2id**(golang.org/x/crypto v0.33.0) | ADR-016:登录成功**惰性升级**(hash_version 路由 md5_salt/sha_salt/argon2),不批量不强制重置 |
+| 入参校验 | **validator/v10**(v10.27.0) | 结构体 tag 校验;`Validate(c, dst) bool` 语义(铁律 7) |
+| 日志 | **zap**(go.uber.org/zap v1.28.0) | 结构化 JSON(ts/level/caller/msg/trace_id);trace_id 经 X-Request-ID 贯穿 |
+| 配置 | **JSON 模板 + 环境变量**(12-factor) | `config/{env}.json`(入库模板,只含非敏感默认值)← `CONFIG_*` 环境变量覆盖;敏感字段全走环境变量/KMS |
+| 角色 | **7 类**:超管/教务/财务/老师/学生/家长/市场 | 市场登录入口暂禁用(不进登录白名单 `1,2,3,4,5,8`) |
+| 异步任务 | 统一 DB 任务表 `t_task` + Go 工作池 | 替代旧 `t_queue` + `t_agent_queue` 两套队列;乐观锁认领 + 指数退避 + 幂等键 |
+| 对象存储 | 阿里云 OSS(按域隔离 bucket) | 文件资产迁移 |
+| AI 能力 | 消费 Python FastAPI Agent(OpenAPI 契约) | Agent 核心计算独立保留;Go 侧为消费方 + 任务派发,不可达时 fire-and-forget 不阻断主链路 |
+| 外部入口 | nginx(TLS + HTTP/2 终结) | ADR-010;`proxy_buffering off` 支持 SSE;按 location 分发 |
+| 监控 | Prometheus 指标 + `/healthz` 存活探针 | 登录成功率/接口错误率/队列积压(兼容期监控项) |
+
+## 2. 目录结构与用途
 
 ```
-backend/
-├── README.md            ← 本文件
-├── EXAMPLES.md          ← 可编译样例索引 + GoDoc 注释标准(铁律 11 的落地模板)
-├── go.mod               ← Go 模块(go 1.27;依赖版本锁定,见"构建与校验")
-├── cmd/api/             # 单二进制入口:装配 Fiber app + 各域模块 + 任务工作池(规划待建)
-├── config/              # viper 配置:环境、多租户、DB/Redis/OSS、SSO 签发/验签(config.{env}.json 模板)
+.
+├── README.md                  ← 本文件(技术栈/目录/规范/构建)
+├── EXAMPLES.md                ← 可编译样例索引 + GoDoc 注释标准模板(编码规范 §3.4 的落地清单)
+├── go.mod / go.sum            ← Go 模块(go 1.27;依赖版本锁定,见 §4)
+├── .gitignore                 ← 排除 config/*.json(敏感)、/log、/tmp、IDE 文件
+├── cmd/
+│   └── api/main.go            ← 单二进制入口:配置加载 → 运行目录幂等 → zap → Fiber 装配
+│                                  (Recover→RequestID 中间件链 + /healthz + 优雅退出);业务域路由挂载点
+├── config/
+│   └── dev.example.json       ← 配置模板(**唯一允许入库的配置文件**;实际 config/dev.json 等走 .gitignore)
 └── internal/
-    ├── identity/        # 身份与 SSO(已有样例:id 生成 ULID / credential argon2 惰性升级 / token RS256 签发验签吊销)
-    ├── org/             # 多租户组织:Institution / Campus / Membership / Role(7) / GuardianLink(规划待建)
-    ├── academic/        # 教务域:排班 / 费用 / 打卡 / 充值(SP 重写) / 报课 / 同步(规划待建)
-    ├── teaching/        # 教学域:备课 / 练习分析 / 题库 / 组卷 / 科目分表 / 共享标志(规划待建)
-    ├── ai/              # AI 能力编排:作为 OpenAPI 消费方调用 Python Agent(规划待建)
-    ├── task/            # 统一 DB 任务表 + 工作池(已有样例:t_task 状态机/乐观锁认领/指数退避)
-    └── infra/           # 横切(已有样例:response 统一信封 / errors 三层错误 / scope 多租户作用域 / validator 入参校验 / config 12-factor 配置)
+    ├── identity/              ← 身份与 SSO 域(被所有域依赖,不依赖业务域)
+    │   ├── id/                ←    canonical_user_id 发放与校验(ULID,ADR-013)【已有样例】
+    │   ├── credential/        ←    密码凭证:argon2id 哈希 + hash_version 路由 + 惰性升级(ADR-016)【已有样例】
+    │   └── token/             ←    RS256 签发/验签/refresh 轮换/jti 吊销(ADR-014)【已有样例】
+    ├── org/                   ← 多租户组织域:Institution/Campus/Membership/Role(7)/GuardianLink(规划待建)
+    ├── academic/              ← 教务域:排班/费用/打卡/充值(SP Go 事务重写)/报课/跨系统同步(规划待建)
+    ├── teaching/              ← 教学域:备课/练习分析/题库/组卷/科目分表路由/共享标志(规划待建)
+    ├── ai/                    ← AI 编排域:OpenAPI 消费方 → Python Agent + 任务派发(规划待建)
+    ├── task/                  ← 统一任务表仓储:t_task 状态机/乐观锁认领/指数退避/僵死回收【已有样例】
+    └── infra/                 ← 横切基础设施(被所有域依赖,**不依赖任何业务域**)
+        ├── response/          ←    统一响应信封 R + OK/Page/Fail + ParsePage【已有样例】
+        ├── errors/            ←    三层错误模型 AppError + 1xxx~5xxx 分段 sentinel【已有样例】
+        ├── scope/             ←    多租户作用域 Scope + BaseRepo.scoping 强制过滤【已有样例】
+        ├── validator/         ←    入参校验 Validate(c,dst) bool(铁律 7 语义)【已有样例】
+        └── config/            ←    Config 结构 + Load(JSON+环境变量+必填 fail-fast)+ 运行目录幂等【已有样例】
+```
+
+**目录用途约定**:
+
+| 目录 | 用途 | 依赖方向 |
+|---|---|---|
+| `cmd/api` | 装配与进程生命周期(不写业务逻辑) | → 所有 internal 域 |
+| `config/` | 环境配置模板(12-factor) | — |
+| `internal/identity` | 身份地基(ULID/凭证/token) | 被所有域依赖;不依赖业务域 |
+| `internal/org` | 多租户 + 7 角色 + 作用域解析 | 被业务域依赖;不依赖业务域 |
+| `internal/academic` `internal/teaching` | 两个业务域 | 依赖 identity/org/infra;域间经接口 |
+| `internal/ai` | Agent 消费方 | 依赖 infra/task |
+| `internal/task` | 任务表横切 | 依赖 infra;不依赖业务域 |
+| `internal/infra` | 横切基类(响应/错误/作用域/校验/配置) | 被所有域依赖;**严禁反向依赖** |
+
+> **样例即规范**:标【已有样例】的包均为可编译参考实现(`go build` + `go vet` 全绿),
+> 每个导出符号带三段式 GoDoc 中文注释。新增业务域代码须对齐其风格(编码规范 §3.4),
+> 索引与模板见 [EXAMPLES.md](./EXAMPLES.md)。
+
+## 3. 编码规范
+
+### 3.1 分层架构(调用拓扑,禁止反向/跨层)
+
 ```
+nginx → HTTP 接入层(cmd/api:Recover→RequestID→CORS→Throttle→Log→[Auth])
+      → Handler(internal/{域}/handler:参数绑定/校验 → 调 Service → 统一响应 R)
+      → Service(internal/{域}/service:业务编排/事务/领域规则;返回 sentinel 错误)
+      → Repository(internal/{域}/repo:唯一数据访问入口;强制作用域注入)
+      → Domain/Model + infra(实体 + 横切基础设施)
+```
+
+| 层 | 职责 | 禁止 |
+|---|---|---|
+| Handler | 绑定入参、调 Service、映射统一 `R` | 直接碰 DB/Repository;写 SQL;吞错误 |
+| Service | 业务规则、事务边界、跨域编排(经接口) | 处理 HTTP;返回 gorm 原错误 |
+| Repository | 唯一数据访问;作用域注入;GORM 纪律 | 业务判断;跨域 JOIN(跨库只"取 ID 回查") |
+| Infra | 横切基础设施 | 依赖任何业务域(单向) |
+
+**分包铁律**:
+
+1. 域间不直接互调内部实现,跨域走 `internal/` 内定义的接口(接口在 `org`/`identity` 侧,消费方在业务域)。
+2. 所有数据访问必须经 Repository 层,作用域由中间件从 token 解析后强制注入,禁止绕过。
+3. `infra` 不依赖任何业务域;`org`/`identity` 不得反向依赖业务域。
+4. SP 行为(充值/审批/同步)在 `academic` 域内用 Go 事务重写,旧存储过程仅留档对照,不迁移。
+5. 硬编码身份/魔法值(如旧系统 `user->id==393`)一律进配置或权限判断,消灭目标为 0。
+
+### 3.2 接口规范
+
+- **统一响应契约**:HTTP 恒 200,真实状态在 body `R.code`(成功 200;`{code, data, message, total?, success}` 信封,顶层 `total` 解决旧 Success/SuccessPage 不一致);e2e 断言 **body code** 而非 HTTP status。
+- **错误码分段**:`1xxx` 认证/权限(1001 未登录/1002 失效/1003 无权限/1004 作用域越界)· `2xxx` 参数/校验 · `3xxx` 数据(3001 不存在/3002 冲突/3003 唯一约束)· `4xxx` 依赖(Agent/支付/第三方)· `5xxx` 系统(5001 内部错误/5003 限流)。
+- **入参校验**:`validator.Validate(c, &req) bool`——失败时内部已写 body(code=2001)并返 false,调用方 `if !ok { return nil }`;**禁用 `err != nil` 判**(误判通过继续落库——教务踩坑)。
+- **路由纪律**:`/{端}/{模块}/{资源}` RESTful;**字面路径先于 `/:id`** 注册;同 method+path 不重复注册;新增端点先全局 grep 同路径;公开路由(登录/SSO)在 Auth Group **之前**注册。
+- **分页**:`page`(默认 1)/`pageSize`(默认 10,上限 200);列表"先 pluck 分页 id → 批量取详情"防 N+1。
+- **命名**:入参/出参 `camelCase`,DB 列 `snake_case`;外键统一 `_id` 后缀(消灭旧 `userid/schoolid` 混用)。
+- **流式(SSE/AI)**:错误也走 `text/event-stream`,不得返 JSON 让前端 SSE 解析失败。
+
+### 3.3 数据访问纪律(GORM,源自教务重构踩坑)
+
+- 可空列(`IS_NULLABLE=YES`)一律 `*string`/`*int`,写边界 nil→""/0 归一(防 `converting NULL` → 500)。
+- 聚合别名/无下划线列/SP 返 camelCase:行结构体显式 `gorm:"column:xxx"`。
+- 链式调用三查各自 `Model()`,base 不带 Select;`Count` 收 `*int64`;存在性判断用 `Take`。
+- `Updates` 的 `RowsAffected==0` 可能是"值未变"非"失败":对齐旧系统"行存在即成功",`Take` 预检再更新。
+- 事务内全部用 `tx` 句柄;动态表名(科目分表)收敛一处——`Table(subjectTable(subjectID))`,体育(10) 已对齐补齐。
+- 跨库过渡态只"取 ID 回查"(`whereIn`),禁 ORM 跨库 join;`only_full_group_by` 用 `Distinct().Order()`;保留字反引号。
 
-> **样例即规范**:`internal/infra/*`、`internal/identity/{id,credential,token}`、`internal/task` 为**可编译参考样例**
-> (`go build ./...` + `go vet ./...` 全绿,基于真实依赖版本),每个结构体/函数/方法均带
-> GoDoc 中文注释(功能说明+典型样例、入参逐项、出参逐项+异常场景)。**新增业务域代码须对齐此注释标准**(铁律 11)。
-> 样例索引与注释模板见 [EXAMPLES.md](./EXAMPLES.md)。
+### 3.4 注释标准(GoDoc 三段式,强制)
 
-## 分包铁律
+每个**导出**的结构体/函数/方法/常量/错误必须带 GoDoc 中文注释:
 
-1. **域间不直接互调内部实现**,跨域走 `internal/` 内定义的接口(接口在 `org`/`identity` 侧,消费方在业务域)。
-2. **所有数据访问必须经 Repository 层**,作用域(institution_id / campus_id)由中间件从 token 解析后强制注入,禁止绕过。
-3. **`infra` 不依赖任何业务域**;业务域可依赖 `infra`、`org`、`identity`,但 `org`/`identity` 不得反向依赖业务域。
-4. **SP 行为(充值/审批/同步)在 `academic` 域内用 Go 事务重写**,旧存储过程仅留档对照,不迁移(见统一规划风险登记册)。
-5. **硬编码身份/魔法值(如 `user->id==393`、`teacherId=354`)一律进配置或权限判断**,阶段 0 硬编码 ID 清单为消灭目标。
-6. **统一响应契约**:HTTP 恒 200,真实状态码在 body `R.code`(成功 code=200,`{code,data,message}` 信封);e2e 断言 body code 而非 HTTP status(源自教务 Go 重构在产验证,ADR 见统一规划)。
-7. **入参校验惯例**:`validateReq(c, req) bool`——校验失败内部已写 400 并返 false,调用方 `if !validateReq { return nil }`(用 `err!=nil` 判会误判通过继续落库)。
-8. **路由注册顺序纪律**:Fiber 字面路径先于 `/:id`(如 `/courses/list-detail` 先于 `/courses/:id`);同 method+path 不重复注册(先注册优先);新增端点全局 grep 同路径。
-9. **GORM 纪律**:`IS_NULLABLE=YES` 列一律 `*string`/`*int`(写边界 nil→""/0 归一);聚合/别名/无下划线列显式 `gorm:"column:xxx"`;链式调用勿就地改接收者(三查各自 `Model()`);事务内全部用 tx;service 返回 sentinel 错误(非 `gorm.ErrRecordNotFound`),handler 用 `errors.Is`。
-10. **e2e id 段独立**:TestMain 建库建 SP→跑→DROP;多 e2e 共用库时用独立业务键/独立 id 段/独占时间窗/专属 token(避开教务已占 97090/97481/98141)。
-11. **注释标准(GoDoc 三段式)**:每个导出的结构体/函数/方法/常量/错误**必须**带 GoDoc 中文注释,含①功能说明(用途+核心逻辑,导出项附"典型使用样例")②入参说明(逐项:名称、类型、含义、约束)③出参说明(逐项:名称、类型、含义;异常场景单列)。代码即文档——注释与实现必须一致,改实现同步改注释;索引与模板见 [EXAMPLES.md](./EXAMPLES.md)。
+1. **功能说明**:用途 + 核心逻辑 + 对应 ADR/铁律;导出项附"典型使用样例";
+2. **入参说明**:逐项(名称、类型、含义、约束/默认值/敏感标注);
+3. **出参说明**:逐项(名称、类型、含义)+ **异常场景单列**(哪些输入/状态出错、调用方怎么处理、降级还是 fail-closed)。
 
-## 构建与校验
+代码即文档:改实现必须同步改注释;注释描述与实现不符 = bug。模板与索引见 [EXAMPLES.md](./EXAMPLES.md)。
+
+### 3.5 异常处理
+
+三层错误模型:`infra/errors.AppError{Code, Msg, Cause}`。
+- **Repository**:`gorm.ErrRecordNotFound` 等转 sentinel(`errors.Is` 判定),不泄漏 ORM 原错;
+- **Service**:只返回 sentinel(`errors.WithCause` 挂底层原因供日志,不进响应体);
+- **Handler**:`response.Fail(c, err)` 统一出口;非 `*AppError` 降级 5001 + 结构化日志(带 stack + trace_id);
+- **全局 Recover**:兜底 panic → 5001,**绝不**把 panic 信息/堆栈/SQL 片段/哈希透传前端。
+
+### 3.6 日志与配置
+
+- **日志**:zap 结构化(ts/level/caller/msg/trace_id);trace_id 经 nginx `X-Request-ID` 生成/透传,跨 Python Agent 调用同值透传;级别生产 Info;**脱敏**(手机号打码、密码/密钥/JWT 不落日志);高频只读访问日志可采样,写操作全量。
+- **配置**:加载顺序 `config/{env}.json` ← `CONFIG_*` 环境变量 ← 启动参数;必填字段缺失 fail-fast 拒绝启动;**敏感字段(DB DSN/JWT 私钥/OSS/微信/Agent 密钥)绝不进代码库/日志**,走环境变量或 KMS;启动时幂等建 `log/{instance}/`、`tmp/{instance}/`(双实例同机隔离)。
+
+### 3.7 异步任务
+
+业务域写 `t_task`(status=pending,幂等键 = type + 业务唯一键)→ 工作池(并发度 `task.workers`)抢占;**认领用条件 UPDATE 乐观锁**(双实例防重复消费);失败指数退避(2^retry 秒,上限 1h)超 `maxRetry` 置 dead 告警;**派发成功 ≠ 任务完成**(AI 批改/导出/PDF),前端轮询任务终态;Agent 不可达 fire-and-forget 不阻断主链路;割接时在途任务先排空或标记重投。
+
+### 3.8 测试与质量门禁
+
+| 门禁 | 命令 / 做法 | 基线 |
+|---|---|---|
+| 编译 | `go build ./... && go vet ./...` | 0 错误 0 告警 |
+| 单测 | `go test -count=1 ./internal/... ./cmd/...` | 表驱动 + 黄金测试,0 失败 |
+| e2e | TestMain 建库建 SP→跑→DROP;**独立业务键/独立 id 段/独占时间窗/专属 token**(避开教务已占 97090/97481/98141) | 多 e2e 共用库不撞 |
+| schema 核对 | 复用 config+gorm 查 `information_schema`(DATA_TYPE/IS_NULLABLE) | 割接前逐表过 |
+| 端点实测 | 真实登录拿 token → curl → **断言 body code + 结构 + 富化字段** | 结构对不上先查路由重复注册 |
+| 并发守门 | `TestE2E_ConcurrentLogin`(后台 goroutine 禁访问 fiber.Ctx) | 0 失败 |
+
+### 3.9 Git 与提交规范
+
+- **工程纪律**:本地 commit 可执行;**脚手架搭建与 `git push` 前必须经用户确认**(逐次确认)。
+- 提交信息:conventional commits(`feat:` / `fix:` / `chore:` / `docs:` / `refactor:`),正文写清影响的域与 ADR 编号。
+- 分支:`master` 主干开发;阶段 1 起按需建 `feat/{域}-{主题}` 分支走 MR。
+- 提交前自检:`go build ./... && go vet ./...` 全绿;新增端点全局 grep 同路径;敏感值未入库(`git diff --cached` 扫一遍)。
+
+## 4. 构建与校验
 
 ```bash
 # go.mod `go 1.27`(语言级别 + 最低工具链),本机 go 1.27.x 直接构建。
-# 未写 toolchain 指令:旧机器(GOTOOLCHAIN=auto)会自动从 goproxy.cn 拉取 go1.27 工具链,
-# 有网即可,离线/内网隔离环境需预装 1.27 或设 GOPROXY 可达。
+# 未写 toolchain 指令:旧机器(GOTOOLCHAIN=auto)自动从 goproxy.cn 拉取 go1.27 工具链,
+# 有网即可;离线/内网隔离环境需预装 1.27 或保证 GOPROXY 可达。
 go build ./...    # 0 错误
-go vet   ./...    # 0 告警(1.27 起 go vet/go test 默认含 stdversion 检查:只用 ≤ go.mod 版本的标准库符号,天然满足)
+go vet   ./...    # 0 告警(1.27 起 go vet/go test 默认含 stdversion 检查)
+go test  ./...    # 测试(域模块就绪后启用)
 ```
 
-**依赖版本锁定**(`go.mod`,升 go 1.27 后版本不变,已实测无意外升级):
+**依赖版本锁定**(`go.mod`,2026-11-05 实测无意外升级):
 `fiber/v2 v2.52.16` / `gorm v1.31.2` / `validator/v10 v10.27.0` /
-`golang-jwt/jwt/v5 v5.3.1` / `oklog/ulid v1.3.1` / `x/crypto v0.33.0`。
+`golang-jwt/jwt/v5 v5.3.1` / `oklog/ulid v1.3.1` / `x/crypto v0.33.0` / `zap v1.28.0`。
 
 > **工具链沿革**:初版锁 go 1.23 + validator v10.27.0(go 1.20 要求),防 tidy 把工具链抬到 1.26;
-> 2026-11-05 工具链升级为 **go 1.27**(泛型方法/encoding-json-v2/小对象分配提速 ~30%/goroutine 泄漏检测 GA),
-> 依赖保持原锁定版本,`go build`+`go vet` 全绿实证兼容。
+> 2026-11-05 升级 **go 1.27**(泛型方法/encoding-json-v2/小对象分配提速 ~30%/goroutine 泄漏检测 GA),
+> 依赖保持原锁定版本,全绿实证兼容。
+
+**本地启动**(dev):
+
+```bash
+cp config/dev.example.json config/dev.json    # 填 DSN 等(或走 CONFIG_* 环境变量)
+go run ./cmd/api -env=dev                     # 监听 config 的 server.addr,/healthz 可探活
+```
+
+**部署形态**(ADR-015 摘要):nginx×2(keepalived VIP,TLS+HTTP/2,SSE `proxy_buffering off`)→ 2× Go 进程(systemd,`:8080`/`:8081`)→ 共享 PolarDB/Redis/OSS;多阶段 Dockerfile 镜像化预留(构建阶段钉 `golang:1.27`),K8s 期 Secret 平移。
 
-## 与文档目录的关系
+## 5. 与文档目录的关系
 
-- 本目录**只放代码与工程配置**;任何架构决策、数据模型、迁移/割接方案放 `../unified/`;单项目事实画像放 `../academic/`、`../teaching/`。
-- 代码内注释如需引用决策,用相对路径指向 `../unified/00-planning/用户系统合并规划.md`,不在代码注释里复述结论。
+- 本仓库**只放代码与工程配置**;架构决策(16 条 ADR)、数据模型、迁移/割接方案在工作区文档库 `unified/`;单项目事实画像在 `academic/`、`teaching/`。
+- 代码注释引用决策时写 ADR 编号(如"ADR-014 SSO 最小可行"),不复述结论。