Commit Graph

17 Commits

Author SHA1 Message Date
7635802a42 feat(audit): 新增操作审计日志(表/中间件/查询接口)+ 角色常量收敛
背景:系统此前没有操作审计。task_logs 的 task_id 是 NOT NULL 外键,只能挂在
任务上,且全项目仅 4 处写入点 —— 登录、导出、产品增删改、收编完全不留痕。
需求方整理的问题清单里「无审计日志查看页」正源于此:不是没有页面,是没数据。

设计参考 MOM(KCGL) 的 audit_logs / audit_listener,但按 Track 栈做了取舍:

1) 写入时机:MOM 用 SQLAlchemy event listener + 同事务写入,优点是零侵入,
   缺点是**业务回滚时审计一起消失**,而失败/被拒的操作(越权尝试、参数错误)
   恰恰最需要留痕。Track 改为响应生成后用**独立 session** 写入:
   - 业务回滚不影响审计(已验证 422/401 失败操作同样落库)
   - 审计写入失败也不影响业务(全包裹 try/except)
   - 代价:非原子提交,响应后进程立即被 kill 可能丢一条(已注释说明取舍)

2) 采集方式:中间件自动采集写操作 + 导出/下载/打印这类「读但敏感」的 GET。
   路径段推导 module/action/target_id。不做手写埋点,因为手写必然漏 ——
   task_logs 只有 4 处写入点就是前车之鉴。

3) 增量价值:新增 request_id 字段,与 core/logging.py 的结构化日志打通,
   凭一个 ID 就能从审计记录直接跳到那一次接口日志。MOM 无此字段。

4) 敏感信息:details 经 sanitize_details 递归剔除 password/token/secret 等键;
   中间件不读请求体,登录明文密码不会落库(已断言表内无密码痕迹)。

配套改动:
- core/roles.py:角色常量与 is_admin 收敛为单一事实来源。此前同一份
  「管理员角色」规则散在 task_service、products.py 内联判断和前端
  constants/task.ts 三处,已因此发生过「移动端漏判 SUPERVISOR 误挡主管」。
  task_service 改为从 core.roles 导入同名常量,保持既有引用可用。
- core/deps.py:抽出 require_roles/require_admin 可复用依赖,替代内联判断。
- main.py:500 响应显式补 X-Request-ID 头 —— 该响应由 ServerErrorMiddleware
  生成,位于 RequestContextMiddleware 外层,中间件没机会写头。
- auth.py:登录校验前把「尝试的账号」写入 request.state,使登录事件
  (含失败登录)可归属到人,可用于追踪暴力破解。

验证:本地起 PostgreSQL 17 + 迁移后跑端到端测试,32/32 通过
(TestClient 每个请求新建事件循环,与模块级 asyncpg 连接池冲突会报
 "got Future attached to a different loop",故改用 httpx.AsyncClient +
 ASGITransport 单循环;生产 uvicorn 单循环无此问题)。
2026-09-21 02:23:19 +00:00
b9f9b897a1 feat: 新增产品生命周期阶段字段lifecycle_phase与售后工序词表 2026-09-14 14:49:34 +08:00
3019653dfa refactor: 宏观状态统一(待仓库收货/已入库)废弃"在库"; WIP矩阵返回product_name并新增下钻明细 2026-09-02 10:38:49 +08:00
ad4b55dece feat(工作日): 新增工作小时计算工具 + 节假日表与管理接口
- time_utils 新增 to_beijing(统一naive按UTC转北京时间) + working_duration_hours(排除周末/节假日)
- Holiday 模型 + alembic 迁移建 holidays 表
- GET/POST/DELETE /holidays 节假日管理接口,可随时配置放假日期
2026-08-28 15:08:05 +08:00
ca195192b6 fix: 协同留言时间改为北京时间
根因: ProductMessage.created_at 使用 datetime.utcnow (UTC),
      与其他所有模型使用的 get_beijing_time 不一致。
      看板留言抽屉显示的时间比实际晚8小时。

修复:
  1. message.py: DateTime → DateTime(timezone=True)
     default 从 datetime.utcnow → get_beijing_time
  2. dashboard_service.py: 兜底转换已有数据
     naive UTC → replace(tzinfo=UTC) → astimezone(Beijing)
2026-08-12 13:55:33 +08:00
991d713777 feat(backend): 新增留言通知 — add_task_record 自动推送给任务负责人
触发条件:
- 有人对任务添加流转记录(留言/备注)
- 任务有 assignee_id
- 留言人 != 任务负责人 (不给自己发通知)

通知内容:
- title: 💬 收到新留言
- content: 产品[SN]的「任务名」有新留言:{前30字摘要}
- type: COMMENT

额外:
- Notification 模型新增 NOTIFY_COMMENT 常量
- 前端 notify 页新增 COMMENT 图标(💬)和标题(收到新留言)
2026-08-11 18:17:52 +08:00
b14773f81b feat: 产品协同留言板 — ProductMessage 模型 + API + 迁移
- 新增 ProductMessage 模型,挂载在 products.id(ON DELETE CASCADE)
- GET/POST /products/{id}/messages 接口
- 注册到 Alembic 自动发现
2026-08-10 17:37:01 +08:00
bff8787e62 feat: OTA热更新 — 版本检测API + App端WGT下载安装 + app_versions表 2026-08-07 11:49:59 +08:00
b71c5a2d07 feat: 双Token认证(Access 2h/Refresh 7d) + 通知系统(转交/驳回自动推送) 2026-08-07 11:44:04 +08:00
0d323e8181 血缘锚定法: task_type字段+迁移+三大接口写入+TreeCanvas基于task_type布局 2026-08-06 13:03:25 +08:00
616638a6c8 撤回改为CANCELED状态标记+TaskRecord留痕+TreeCanvas灰度渲染+全部状态映射更新 2026-08-06 12:51:40 +08:00
22f7bb2f79 全栈: Task模型+remark字段 + 前端任务卡片初始说明展示区 2026-08-05 15:22:50 +08:00
e0c01a562f feat(models): Task/Product 模型扩充 + 4个Alembic迁移
Task 模型:
- 新增 received_at, completed_at, reject_reason, is_rework
- 新增 TaskRecord 模型 (remark + images JSON)
- 状态常量 PENDING/WIP/COMPLETED/REJECTED/ARCHIVED
- 全部时间字段改用北京时间 (get_beijing_time)

Product 模型:
- order_id → 可选; 新增 external_serial, current_location_id
- 新增 material_name/spec_model/category/material_type 快照字段
- 新增 overall_status (备货/生产/测试/维修/在库)

迁移:
- b2c3: HEX计数器Sequence + external_serial + order_id nullable
- c3d4: material快照4列
- d4e5: overall_status
- e5f6: task_records表
2026-08-05 14:00:23 +08:00
2b967afbf6 chore: 更新后端依赖,初始化数据库迁移与 MOM 外部系统连接 2026-08-04 17:09:33 +08:00
817be6b036 feat(models): 扩充 Task 和 Product 数据库模型
Task 模型新增:
- received_at: DateTime (操作员确认接收时间)
- completed_at: DateTime (任务完工转交时间)
- reject_reason: String(500) (驳回原因)
- is_rework: Boolean (是否为返工任务,默认 False)
- 状态常量: PENDING/WIP/COMPLETED/REJECTED/ARCHIVED
- 默认状态由 'pending' 升为大写 'PENDING'

Product 模型新增:
- current_location_id: String(64) (当前持有者ID 或 'virtual_warehouse')
2026-08-04 17:03:23 +08:00
895fed6ac3 chore: 添加 .gitignore 并移除敏感/缓存文件
- 添加 .gitignore (Python __pycache__, .env, .idea, node_modules 等)
- 从追踪中移除 backend/.env, frontend/.env (密钥文件)
- 从追踪中移除 __pycache__/ 目录 (编译缓存)
- 从追踪中移除 .idea/ (IDE 配置)
2026-08-04 17:03:17 +08:00
17105dc9c2 初始提交:项目基础结构
- backend: FastAPI 后端服务 (Python)
- frontend: React + Tauri 前端应用
- docker-compose.yml: 容器编排配置
2026-08-04 10:05:59 +08:00