Files
track-LICA/AGENTS.md

190 lines
12 KiB
Markdown
Raw Normal View History

# AGENTS.md
本仓库Track 生产流转系统)的工作笔记。仅在验证过之后才写入,避免传谣。
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
> ## ⚠️ 本仓库是 **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_frontend` | `lica_db` / `lica_backend` / `lica_frontend` |
> | compose 项目 | `track` | `track-lica` |
> | PC 管理端 | 8010 | **8030** |
> | 后端 API | 8011 | **8031** |
> | 数据库 | 8012 | **8032** |
> | 数据卷 | `track_pgdata` | `lica_pgdata` |
> | 部门过滤 | `IRIS` | `LICA` |
>
> **两条红线:**
> 1. **永远不要在 `/home/yueli/track` 目录下执行 `docker compose down -v`** ——
> 那会删掉 IRIS 的生产数据。两个卷相互独立,在 `track-lica` 下执行不影响 IRIS。
> 2. 改后端地址时别只改一半PC 端走 `docker-compose.yml` 的 `CORS_ORIGINS`
> 移动端走 `track-uniapp/src/utils/config.js`uni-app 地址的**唯一来源**
> 和 `track-uniapp/.env`。只改一处会出现「接口通了但图片/上传 404」。
## 架构速览
- `backend/` FastAPI + SQLAlchemy 2.x(async) + AlembicPostgreSQL。
- `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 引擎)。
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
- **组织隔离**:本实例只服务 LICA 部门,唯一开关是 `app/core/config.py`
`ORG_DEPARTMENT`(对应 MOM `sys_user.department`)与
`MATERIAL_CATEGORY_PREFIX`(对应 MOM `material_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。
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
- 人员与物料都是**服务端钉死**部门,客户端传什么部门参数都不采纳
(这样 uni-app 里写死的 `dept` 不会造成跨部门影响)。
- 刻意**不做**「查询失败退回全表」的降级 —— 那是跨部门数据泄漏,
宁可查不出,不可查过头。
- ⚠️ 物料必须用前缀 `category LIKE 'LICA/%'`**不能用** `ILIKE '%LICA%'`
MOM 里存在 171 条 `IRIS/成品/LICA/...`(无人机/野外便携/高塔监测等),
模糊匹配会把 IRIS 的物料漏给 LICA。已实测前缀匹配命中 795 条 / 5 个分组。
- **业务分组数据权限**2026-09 新增,是**部门隔离之内**的第二层)
LICA 部门内部再分组(生产大组 / 维修大组,各自可建小组),组对应
`lifecycle_phase`,成员只看本组阶段的数据。组存在 Track 库(`business_groups`
/ `business_group_phases` / `business_group_members`),人在 MOM。
**唯一判定来源**`app/services/data_scope_service.py``DataScope`
- `scope.product_where()` —— 主体是 Product 的语句用
- `scope.task_where()` —— 主体是 Task 的语句用,**前置条件:调用方必须已
`join(Product, Task.product_id == Product.id)`**tasks 表没有 lifecycle_phase
- ⚠️ **业务代码里不准再手写 `lifecycle_phase` 过滤**。评审检查:
`grep -rn "lifecycle_phase.in_" backend/app/` 应只命中 `data_scope_service.py`
- ⚠️ 两条红线:`None`(不限) 与 `frozenset()`(空) 语义相反,不共用哨兵值;
空集合必须显式 `false()` —— SQLAlchemy 对 `in_(())` 生成 `IN (NULL)`
一旦退化成不过滤就是全量泄漏
解析优先级(`resolve_data_scope`
1. `SUPER_ADMIN` → 永远全厂(硬编码,不可被分组覆盖)
2. 被显式分进组 → 该组生效(**分组优先于角色**SUPERVISOR 被分组也会受限)
3. 未分组的 `SUPERVISOR` → 默认全厂
4. 未分组的普通用户 → 按 `DATA_SCOPE_UNGROUPED`(见下)
**过渡开关** `config.DATA_SCOPE_UNGROUPED`
- `"ALL"`(当前默认)= 未分组用户先放行,同时打 WARNING 记录「谁还没分组」
- `"NONE"` = 目标态:未分组 = 看不到任何数据(列表返回 `[]`,不是 403
- 直接上 NONE 会让所有未分组工人当场看不到自己的任务、现场停摆。
推荐节奏ALL 上线 → 看日志收集名单 → 建组配人 → 改 NONE 重启。
**其它约定**
- 扫码 `get_product_by_serial` **刻意不过滤**(现场要能判断设备走没走错流程),
理由写死在该函数 docstring 里;「不能操作」由 `_check_permission` 保证。
- 分组**管理**仅 `SUPER_ADMIN``require_roles(SUPER_ADMIN)`,不能用
`require_admin`)。原因:被分组的主管若能把人移出组,就等于获得提权路径。
- 组支持两级(`parent_id`),子组不配范围则继承父组。
- 删组**仅限空组** —— 级联删是静默的批量权限变更。
- 统计接口dashboard/analytics/screen 共 20 个路由)**已从匿名可访问变为需登录**
并受范围过滤。注意:大屏挂满 7 天后 refresh token 过期会跳登录页。
- **MOM 回执的部门校验**`endpoints/webhooks.py`2026-09 新增)
MOM 同时对接 IRIS 与 LICA 两个 Track 实例,按载荷里的 `company_name` 分流。
本实例LICA采取**严格白名单**
| `company_name` | 本实例行为 |
|---|---|
| `"LICA"`strip 后) | 正常处理 |
| 空白 / 缺失 / `"IRIS"` / 未知值 | 忽略,返回 `reason: "ignored_company"` |
⚠️ **这与 IRIS 实例的策略刻意相反,不要"统一"**
IRIS 对空白值要**放行** —— MOM 判定不出公司时会回落到指向 IRIS 的扁平配置,
不收就彻底丢了LICA 没有兜底角色,空白值本就该由 IRIS 兜。
LICA 的原则是**宁可漏、不可误收**:误收会改掉别的部门的设备状态。
⚠️ 拦截时返回 **200 + `matched=False`** 而非 4xx —— 与"未命中"保持同一契约,
避免 MOM 侧当成故障去重试。`reason: "ignored_company"` 这个取值与 IRIS 实例
保持一致MOM 不解析它,但排查时两边日志要对着看)。
排查口径:**有 `reason` = 被部门校验拦下;无 `reason` = 通过了校验、只是产品没匹配上。**
- **MOM 回执的审计归因**`endpoints/webhooks.py`2026-09 新增)
外部回调走 X-API-Key、没有 JWT审计中间件读到的 `request.state.audit_user`
永远是空 —— 操作审计里会出现一堆没有归属的「外部系统对接」记录,看不出是谁
扫的码。现在用载荷里的 `operator`MOM 侧实际扫码的那位)写进 `request.state`
归因到人;取不到操作人时写 `"MOM系统"`,而不是继续显示「未认证」。
⚠️ 调用位置必须在 X-API-Key 校验**之后** —— 密钥不对说明载荷本身就不可信,
此时写审计等于允许伪造人。放在两处部门校验**之后**同样有意为之:被拦下的
外来消息不该留下任何归属痕迹。
该函数与 IRIS 实例(`~/track`)代码体逐行一致,只差 docstring —— 改动请两边
同步,否则两个实例的审计口径会对不上。
## 本地起环境(关键,踩过的坑都在这)
1. **本机没有 Postgres 时需要先装**(容器内 `sudo` 可用):
`sudo -n apt-get install -y --fix-missing postgresql postgresql-contrib`
然后 `sudo -n pg_ctlcluster 17 main start`
本仓库不使用 pgvector无需额外扩展。
2. **数据库端口与生产默认值不同**,必须用环境变量覆盖:
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
- `DATABASE_URL=postgresql+asyncpg://lica:<密码>@127.0.0.1:8032/lica_production`
- `MOM_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 失败。
3. 迁移:`cd backend && python3 -m alembic upgrade head`(没有全局 `alembic` 命令,
要用 `python3 -m alembic`)。校验纯 SQL 用 `alembic upgrade head --sql`
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
4. 前端 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。
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
注意 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 基建,也没有前端测试脚本。
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
## 已知的待修问题(继承自 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`
feat: LICA 部门独立实例 — 组织隔离与端口/标识改造 派生自 IRIS 实例的 feature/ai-audit-update @ 192c8ee,在同一台机器上独立运行。 隔离机制(开关集中在 app/core/config.py 的 ORG_DEPARTMENT / MATERIAL_CATEGORY_PREFIX): - 登录:sys_user 查询增加 department 条件,非本部门账号一律 401 - 人员列表:服务端钉死部门、忽略客户端传参;删除「异常退回全表」的降级分支 - 物料:groups 与 items 都增加 category LIKE 'LICA/%' 前缀过滤 - 人员操作统计:把硬编码的 department='IRIS' 改为配置项 物料为什么用前缀而不是 LIKE '%LICA%': MOM 里存在 171 条 IRIS/成品/LICA/...(无人机/野外便携/高塔监测等), 模糊匹配会把这些 IRIS 物料漏给 LICA。实测前缀匹配命中 795 条 / 5 个分组。 部署隔离: - 端口 8030/8031/8032,容器名 lica_*,卷 lica_pgdata(与 IRIS 完全独立) - 服务名改为 lica_backend,避免在 projects_default 网络上与 IRIS 的 backend 重名 —— 否则将来任何一方写 http://backend:8000 会随机打到另一个部门 - SECRET_KEY 重新生成:实测两边 token 互不通用(双向 401) 客户端标识(不改会导致两个部门的客户端互相覆盖): - Tauri identifier 改 com.lica.production(否则桌面端互相覆盖安装,且共用 WebView 数据目录会让 track_admin_token 串号) - uni-app appid 改 __UNI__D2F4A19(否则同机 APK 互相覆盖、wgt 热更新串号) - uni-app 地址端口 8011 → 8031(收敛在 utils/config.js 单一来源) - sync-watch.sh 的 DST 指向 LICA 专属 HBuilderX 目录(否则会把源码灌进 IRIS 工程) 排除项:未复制 deploy.sh / deploy_full.sh / docker-compose.prod.yml —— 它们写死了 IRIS 的生产服务器,误跑会覆盖线上系统。
2026-09-21 16:10:52 +08:00
- **对 LICA 的影响面**`/api/v1/users/` 虽无鉴权,但部门过滤在 SQL 层钉死,
实测无 token 也只能拿到 19 个 LICA 人员,**不会泄露 IRIS 的人**
其余匿名接口读的是本实例自己的库,跨不到 IRIS 库。真正的风险是
「本部门内部」的越权(如普通操作员能看管理端统计),不是跨部门。
- `AdminLayout` 只判登录不判角色LICA 的 15 个 `INBOUND` 账号能进 PC 管理端。
- `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`
避免前端再抄一份映射开始漂移。
- 本仓库的提交信息用中文,说明「为什么」而非「改了什么」。