Files
cmautobuy/admin/AGENTS.md
T

159 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Admin 子项目规则
本文件适用于 `admin/` 下的全部代码、模板和测试,并继承仓库根目录 `AGENTS.md` 的
**红线**、工作流、Git、安全、文档和初级程序员维护规则。
## 开始工作前
- 从仓库根目录执行 Admin 任务时,也必须先读取本文件。
- 第一次接手,先看这两份:
- 环境和运行:[00 上手指南](../docs/admin/00-getting-started.md)
- 看不懂的词:[00 术语表](../docs/admin/00-glossary.md)
- 只读取与当前任务有关的长期基线,不必每次加载全部文档:
- 需求边界:[01 产品需求基线](../docs/admin/01-requirements.md)
- 分层和目录:[02 系统架构](../docs/admin/02-architecture.md)
- 数据库表:[03 数据模型](../docs/admin/03-data-model.md)
- 给 Client 的接口:[04 Client 接口实现](../docs/admin/04-client-api.md)
- 页面和交互:[05 界面规范](../docs/admin/05-ui-specification.md)
- 测试和安全:[06 质量与安全](../docs/admin/06-quality-security.md)
- Admin 提供的接口必须满足 Client 侧契约
[docs/client/04-admin-api-contract.md](../docs/client/04-admin-api-contract.md),
两边不一致时以 Client 契约为准,要改先走工单。
## 技术栈
**Go 版本固定 1.23.0。** 依赖版本受它约束,见下面的表。
- 固定使用 **Go + Gin + Go 标准库 `html/template`**。
- 生产数据库固定 **MySQL 8.4**,驱动固定
`github.com/go-sql-driver/mysql` **v1.9.2**(纯 Go,兼容 Go 1.23)。
- Admin 默认与 MySQL 同机或走私有网络;生产默认连接 `127.0.0.1:3307`。
项目负责人已于 2026-08-11 明确批准生产 MySQL 3307 接受所有公网来源,公网业务账号
可以使用 `%`,但必须同时满足:只开放专用业务账号、权限仅限 `autobuy`、
`REQUIRE SSL`、Admin 使用 `verify_ca`,且 root 仍仅限本机。不得把这项例外扩展到
root、其他数据库或关闭 TLS;恢复固定公网 IP 后应优先收回到 `/32` 白名单。
- Admin 公网连接必须使用 `database.tls_mode: verify_ca` 和服务器公开 CA:要求
TLS 1.2+、验证证书链、禁止回退明文。`disabled` 只允许同机或 SSH 隧道连接;
不得使用驱动的 `preferred` 或单纯 `skip-verify`。
- Windows 本地运行允许把 MySQL 账号密码写入已被 Git 忽略的 `admin/config.yaml`;
线上部署仍优先使用权限为 `600` 的环境文件。`CMAUTOBUY_DB_*` 环境变量按字段
覆盖 YAML,真实 `config.yaml` 不得提交、打包、截图或复制到工单和日志。
- `modernc.org/sqlite` 只保留给 SQLite → MySQL 单向迁移工具和历史库回归,
不得用于生产运行时,也不得做 SQLite/MySQL 双写或自动同步。
- 仍然**不得改用 `mattn/go-sqlite3`**,避免引入 cgo 和 gcc。
- Excel 读取固定 `github.com/xuri/excelize/v2`。
### 依赖版本已钉死,不要随手升
| 依赖 | 固定版本 | 为什么不能用更新的 |
|---|---|---|
| `github.com/gin-gonic/gin` | **v1.11.0** | v1.12.0 起要求 Go ≥ 1.25.0 |
| `modernc.org/sqlite` | **v1.38.0** | v1.40.0 起要求 Go ≥ 1.24.0,v1.48.0 起要求 ≥ 1.25.0 |
| `github.com/xuri/excelize/v2` | **v2.9.1** | v2.10.0 要求 Go ≥ 1.24.0,v2.11.0 要求 ≥ 1.25.0 |
| `github.com/go-sql-driver/mysql` | **v1.9.2** | v1.9.3 起要求 Go ≥ 1.24.0 |
> excelize 目前**还不在 `go.mod` 里**——导入功能还没写,没有代码 import 它,
> `go mod tidy` 会把它去掉,这是 Go 的正常行为。写导入功能时用
> `go get github.com/xuri/excelize/v2@v2.9.1` 加进来。
这些版本是**在 Go 1.23.0 下实测能编译通过的固定版本**。
`[必须]` 直接跑 `go get <包名>`(不带版本)会拉到最新版,然后报
`requires go >= 1.25.0`。拉依赖要带版本号,见
[00 上手指南](../docs/admin/00-getting-started.md) §2。
`[必须]` **要升依赖就得先升 Go**,这是一个决定不是两个。
升 Go 属于会影响构建的变更,先过工单。
- 实际版本以 `admin/go.mod` / `go.sum` 为准,这两个文件都要提交 Git。
- 新增或升级依赖前先过工单。
### 前端约束
- **不得引入 React、Vue、Angular 等前端框架,不得引入 npm 构建流程。**
页面一律服务端渲染。
- 搜索、删除、导入这类操作用普通表单提交,**零 JavaScript**。
- 勾选、弹窗、导入进度确实需要 JS,只允许两种做法:
- 原生 JavaScript,直接写在模板里或放 `static/js/` 下的独立文件;
- htmx(单个 js 文件,无构建步骤)。
- **不得引入打包器**(webpack/vite/esbuild)。CSS 手写,放 `static/css/`。
## 代码分层
- `handler` 只负责解析请求、调用 service、渲染模板或返回 JSON,**不写业务逻辑,不拼 SQL**。
- `service` 放业务逻辑(导入解析、创建任务、匹配复用),不认识 Gin 的 `*gin.Context`。
- `repository` 封装 MySQL 读写,迁移命令中的 SQLite 读取也只能放在 repository/cmd 边界内;**只有这一层能写业务 SQL**。
- `model` 只放数据结构,不导入 Gin 和数据库驱动。
- 给 Client 的接口和给浏览器的页面**分开放**(`handler/api/` 和 `handler/web/`),
两者的错误格式、认证方式都不一样,混在一起迟早出事。
## 数据与接口
- Admin 新建采购任务固定为真实下单(不支付),当前账号可见的全部 Client 都允许被指派;创建时不得按 Client 的 `purchase_mode` 禁用候选或拒绝任务。`purchase_mode` 只用于领取期判断 Client 的自动运行就绪能力。
- 表结构以 [03 数据模型](../docs/admin/03-data-model.md) 为准。
- 给 Client 的接口以 [docs/client/04-admin-api-contract.md](../docs/client/04-admin-api-contract.md) 为准。
- `[必须]` **金额一律用整数存**,人民币用分、台币用分,字段名带单位后缀(`_cent`)。
**禁止用 float 存金额。**
- `[必须]` 时间一律带时区 ISO 8601,库里存 UTC,页面上转本地时区显示。
- `[必须]` Excel 导入只做 **upsert(更新或新增)**,
**绝不允许先清空再导入** —— 会把人工填的 PDD 链接全洗掉。
- `[必须]` 蝦皮规格原文(`spec_raw`)永远保留,解析不出来就留空,不要瞎猜。
- `[必须]` Client 提交结果时,**不管任务是否已取消、是否已重派,一律接受**,
理由见 Client 契约 §6.1。这条最容易被顺手违反。
- `[必须]` MySQL 的 `schema_migrations` 只追加,已经发布的版本不得改写。
MySQL DDL 会隐式提交:每条 DDL 必须可重放,整版完成并通过 schema 自检后
才记录版本。历史 SQLite migrations 已冻结,只供迁移工具读取旧库。
## 界面规则
- 五个模块统一使用三段式布局:**顶部工具条 / 中间带勾选的表格 / 底部状态条**。
第一个页面写完,其余四个照抄结构改字段,不要给某个模块搞特殊。
- 表格行的身份用业务主键,**不得用行号**。
- 批量删除必须二次确认,并显示"将删除 N 条"。
- 破坏性操作(删除、导入覆盖)用 POST,**不得用 GET**。
- 页面必须能在 1366×768 上正常使用,表格横向滚动而不是压缩列宽。
## 安全
- 根目录 `AGENTS.md` 的五条红线同样适用。
- `[必须]` 所有写操作(新增/编辑/删除/导入/建任务)加 CSRF 防护。
- `[必须]` SQL 一律用参数化查询,**禁止字符串拼接 SQL**。
- `[必须]` 模板输出走 `html/template` 的自动转义,
**禁止用 `template.HTML` 包裹用户可控内容**。
- `[必须]` 上传的 Excel 限制大小和扩展名,解析失败要有明确报错,不能让整个进程崩掉。
- `[必须]` 日志、页面、导出里不得出现 token、密码、Cookie。
## 验证
全部命令从 `admin/` 目录执行。
```powershell
go vet ./...
go build ./...
go test ./...
go run .
```
`[必须]` **交付前至少跑一次带 `GOTOOLCHAIN=go1.23.0` 的验证**:
```powershell
$env:GOTOOLCHAIN="go1.23.0"; go build ./...; go test ./... -count=1
Remove-Item Env:GOTOOLCHAIN
```
本项目固定 Go 1.23.0(见 [00 上手指南](../docs/admin/00-getting-started.md)),
但开发机上装的可能是更新的版本,Go 会默默用它编译,**测过的不是要交付的那个版本**。
本项目已经发生过:连续六份归档写着「Go 1.23.0 验证通过」,实际全跑在 1.26.5 上。
新加依赖时尤其要跑——依赖的 `go` 指令高于 1.23.0 的话,只有这条命令能发现。
- `go run .` 启动后访问 `http://localhost:8080`,**不会连手机、不会下单**,可随时运行。
- 修改数据库时必须使用独立 MySQL 8.4 测试库验证首次建库、重复迁移和上一版本升级。
测试库名必须以 `_test` 结尾,禁止测试清理连接生产库。
- 修改 SQLite 单向迁移时,同时用各历史 SQLite schema 状态演练;源库只读,
目标库必须为空,逐表计数和关键业务关系必须核对。
- 修改给 Client 的接口时,跑契约测试,确认仍满足 Client 侧 §6.1 的无条件接受。
- 修改 Excel 导入时用 `raw_data/` 下的样本跑一遍,核对导入条数。
该样本含商业数据、**不在仓库里**,需向项目负责人索取;自动化测试用 `testdata/` 下的脱敏小样本。
- 交付时说明已运行的命令、结果和未验证的部分。