From 42a67bfa3a046e1edabbe71b7515abeac581e200 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 21 Sep 2026 02:28:47 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=20AGENTS.md=20?= =?UTF-8?q?=E2=80=94=E2=80=94=20=E8=AE=B0=E5=BD=95=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E8=B5=B7=E7=8E=AF=E5=A2=83=E3=80=81=E6=B5=8B=E8=AF=95=E8=B8=A9?= =?UTF-8?q?=E5=9D=91=E4=B8=8E=E5=B7=B2=E7=9F=A5=E5=BE=85=E4=BF=AE=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 刻意只写验证过的事实: - 本机装 PG + 必须覆盖 DATABASE_URL/MOM_DB_*/SECRET_KEY 才能起服务 - TestClient 与异步引擎跨事件循环冲突会伪装成随机 500, 改用 httpx.AsyncClient + ASGITransport 单循环 - 实测确认读接口大面积未鉴权(含匿名可导出个人工时台账) - 「管理员角色」规则的后端唯一事实来源是 app/core/roles.py --- AGENTS.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c1227d9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,63 @@ +# AGENTS.md + +本仓库(Track 生产流转系统)的工作笔记。仅在验证过之后才写入,避免传谣。 + +## 架构速览 + +- `backend/` FastAPI + SQLAlchemy 2.x(async) + Alembic,PostgreSQL。 +- `frontend/` React 19 + Vite + antd + Tailwind。路由见 `src/App.tsx`, + 管理端菜单见 `src/components/layout/AdminLayout.tsx`(`MENU` 数组)。 +- 登录不走 Track 自己的用户表,而是**只读** MOM(KCGL) 的 `sys_user`: + `sys_user.username` 存 `"真实姓名/登录账号"`,`login()` 用 + `WHERE username LIKE '%/<账号>'` 匹配,`display_name` 由 `/` 拆解得到。 + MOM 连接配置在 `app/core/mom_database.py`(同步 psycopg2 引擎)。 + +## 本地起环境(关键,踩过的坑都在这) + +1. **本机没有 Postgres 时需要先装**(容器内 `sudo` 可用): + `sudo -n apt-get install -y --fix-missing postgresql postgresql-contrib` + 然后 `sudo -n pg_ctlcluster 17 main start`。 + 本仓库不使用 pgvector,无需额外扩展。 +2. **数据库端口与生产默认值不同**,必须用环境变量覆盖: + - `DATABASE_URL=postgresql+asyncpg://track:track_prod_2026@127.0.0.1:5432/track_production` + - `MOM_DB_HOST=127.0.0.1`、`MOM_DB_PORT=5432` + - `SECRET_KEY=<≥32 字符>`:`DEBUG=false` 时配置项会**拒绝**默认 SECRET_KEY + (见 `app/core/config.py` 的校验),不设会直接 import 失败。 +3. 迁移:`cd backend && python3 -m alembic upgrade head`(没有全局 `alembic` 命令, + 要用 `python3 -m alembic`)。校验纯 SQL 用 `alembic upgrade head --sql`。 +4. 前端 proxy 指向 Docker 服务名 `backend:8000`。本机跑要么把 + `127.0.0.1 backend` 写进 `/etc/hosts`,要么直接给 + `VITE_API_BASE_URL=http://localhost:/api/v1` 绕过 proxy。 + 注意 dev server 由 `basicSsl` 起 HTTPS,跨域需要后端 + `CORS_ORIGINS` 加上 `https://localhost:1420`。 + +## 测试的坑(重要) + +- **不要用 `starlette.testclient.TestClient` 测异步 SQLAlchemy 应用。** + 它每个请求新建事件循环,而引擎是模块级单例、池里挂着 asyncpg 连接, + 跨循环复用会报 `got Future attached to a different loop`,表现为随机 500。 + 正确做法:`httpx.AsyncClient(transport=httpx.ASGITransport(app=app))` + 并在单个 `asyncio.run()` 里跑完全部请求。生产 uvicorn 单循环无此问题。 +- 仓库目前**没有** pytest 基建,也没有前端测试脚本。 + +## 已知的待修问题(截至 1.0应用 分支) + +- **读接口大面积未鉴权**(已实测,非推测):无 token 直接 200 的包括 + `/api/v1/users/`、全部 `/api/v1/dashboard/*`(含 + `people-history/export` —— 匿名即可批量导出个人工时台账)、 + `/api/v1/analytics/*`、`/api/v1/screen/*`、`/api/v1/orders/`。 + 写操作和 `/api/v1/tasks`、`/api/v1/products` 是有鉴权的。 + 新增接口请统一用 `app/core/deps.py` 的 `require_admin` / `require_roles`。 +- 「管理员角色」这份规则此前散在 4 处(后端 `task_service`、`products.py` 内联、 + 前端 `constants/task.ts`、`AdminProductsPage` 内联),已因此出过事故。 + **后端唯一事实来源是 `app/core/roles.py`,前端用 `constants/task.ts::isAdminRole`。** + 新增判断不要手写 `===` 比较。 +- `task_logs.task_id` 是 NOT NULL 外键,只能挂任务,不是通用审计。通用审计是 + `audit_logs`(本轮新增,由 `app/core/audit_middleware.py` 自动采集)。 + +## 约定 + +- 时间统一北京时间(`app/core/time_utils.py`),库里存 timestamptz。 +- 中文枚举标签尽量由服务端下发(如审计接口的 `module_label`/`action_label`), + 避免前端再抄一份映射开始漂移。 +- 本仓库的提交信息用中文,说明「为什么」而非「改了什么」。