7.7 KiB
7.7 KiB
AGENTS.md
本仓库(Track 生产流转系统)的工作笔记。仅在验证过之后才写入,避免传谣。
⚠️ 本仓库是 LICA 部门实例
派生自 IRIS 实例(
git.iris-rs.cn/duxingchen/track.git的feature/ai-audit-update分支 @192c8ee)。两套系统在同一台机器上并行 运行、共用 MOM 主数据,但人员、物料、业务数据完全隔离。
IRIS 实例 LICA 实例(本仓库) 代码目录 /home/yueli/track/home/yueli/track-lica容器 track_db/track_backend/track_frontendlica_db/lica_backend/lica_frontendcompose 项目 tracktrack-licaPC 管理端 8010 8030 后端 API 8011 8031 数据库 8012 8032 数据卷 track_pgdatalica_pgdata部门过滤 IRISLICA两条红线:
- 永远不要在
/home/yueli/track目录下执行docker compose down -v—— 那会删掉 IRIS 的生产数据。两个卷相互独立,在track-lica下执行不影响 IRIS。- 改后端地址时别只改一半:PC 端走
docker-compose.yml的CORS_ORIGINS, 移动端走track-uniapp/src/utils/config.js(uni-app 地址的唯一来源) 和track-uniapp/.env。只改一处会出现「接口通了但图片/上传 404」。
架构速览
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 引擎)。 - 组织隔离:本实例只服务 LICA 部门,唯一开关是
app/core/config.py的ORG_DEPARTMENT(对应 MOMsys_user.department)与MATERIAL_CATEGORY_PREFIX(对应 MOMmaterial_base.category前缀)。 过滤点共 4 处:登录auth_service.login、人员列表endpoints/users.py、 物料endpoints/materials.py的groups/items、 人员操作统计dashboard_service.get_user_operations。- 登录是四个过滤点里唯一区分角色的:普通角色(INBOUND / SUPERVISOR /
WAREHOUSE_MGR / SALES)必须严格属于本部门,否则一律 401;
但
SUPER_ADMIN跨部门放行 —— IRIS 的超管也能登 LICA 实例, 供运维/管理员在两个实例之间切换。角色常量取自app/core/roles.py, 不要写裸字符串'SUPER_ADMIN'。 (这条曾经被改错过一次:把豁免去掉导致 IRIS 超管登不进 LICA。) - 人员与物料都是服务端钉死部门,客户端传什么部门参数都不采纳
(这样 uni-app 里写死的
dept不会造成跨部门影响)。 - 刻意不做「查询失败退回全表」的降级 —— 那是跨部门数据泄漏, 宁可查不出,不可查过头。
- ⚠️ 物料必须用前缀
category LIKE 'LICA/%',不能用ILIKE '%LICA%': MOM 里存在 171 条IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把 IRIS 的物料漏给 LICA。已实测前缀匹配命中 795 条 / 5 个分组。
- 登录是四个过滤点里唯一区分角色的:普通角色(INBOUND / SUPERVISOR /
WAREHOUSE_MGR / SALES)必须严格属于本部门,否则一律 401;
但
本地起环境(关键,踩过的坑都在这)
- 本机没有 Postgres 时需要先装(容器内
sudo可用):sudo -n apt-get install -y --fix-missing postgresql postgresql-contrib然后sudo -n pg_ctlcluster 17 main start。 本仓库不使用 pgvector,无需额外扩展。 - 数据库端口与生产默认值不同,必须用环境变量覆盖:
DATABASE_URL=postgresql+asyncpg://lica:<密码>@127.0.0.1:8032/lica_productionMOM_DB_HOST=inventory_db、MOM_DB_PORT=5432(容器内走 projects_default 网络的服务名;从宿主机连则用127.0.0.1:5435)SECRET_KEY=<≥32 字符>:DEBUG=false时配置项会拒绝默认 SECRET_KEY (见app/core/config.py的校验),不设会直接 import 失败。
- 迁移:
cd backend && python3 -m alembic upgrade head(没有全局alembic命令, 要用python3 -m alembic)。校验纯 SQL 用alembic upgrade head --sql。 - 前端 proxy 指向 Docker 服务名
lica_backend:8000(本实例的服务名刻意不叫backend,避免在projects_default网络上与 IRIS 实例重名,否则将来任何一方 写http://backend:8000会随机打到另一个部门)。本机跑要么把127.0.0.1 lica_backend写进/etc/hosts,要么直接给VITE_API_BASE_URL=http://localhost:<port>/api/v1绕过 proxy。 注意 dev server 由basicSsl起 HTTPS —— 必须用https://访问, 用http://会直接连不上(curl 报 000)。跨域需要后端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 基建,也没有前端测试脚本。
已知的待修问题(继承自 IRIS 侧 192c8ee,本仓库未修)
- 读接口大面积未鉴权(已实测,非推测):无 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。- 对 LICA 的影响面:
/api/v1/users/虽无鉴权,但部门过滤在 SQL 层钉死, 实测无 token 也只能拿到 19 个 LICA 人员,不会泄露 IRIS 的人; 其余匿名接口读的是本实例自己的库,跨不到 IRIS 库。真正的风险是 「本部门内部」的越权(如普通操作员能看管理端统计),不是跨部门。 AdminLayout只判登录不判角色:LICA 的 15 个INBOUND账号能进 PC 管理端。
- 对 LICA 的影响面:
dashboard_service.py查 tasks 时用小写pending/in_progress/completed, 而Task.status实际存大写(PENDING/WIP/COMPLETED), 导致管理端「待接收/进行中/已完成」三个数字恒为 0。 (注意Product.status确实是小写,修的时候别一起改。)- 「管理员角色」这份规则此前散在 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), 避免前端再抄一份映射开始漂移。 - 本仓库的提交信息用中文,说明「为什么」而非「改了什么」。