feat: 增加客户端采购员归属管理 (#54)

This commit is contained in:
chengma
2026-08-09 16:02:25 +08:00
parent 0181170457
commit e0c0dac7a0
17 changed files with 766 additions and 37 deletions
+34
View File
@@ -96,6 +96,7 @@ SQLite 同一时刻只允许一个写事务,连接放太开会互相抢锁、
| v4 | `pdd_products` 增加可空的 `shop_name`;老数据保持 `NULL`。 |
| v5 | 顺运宝货运单同步(工单 #46):新增 `syb_session`(会话缓存)、`syb_sync_state`(同步进度)两张表;`syb_orders` 增加可空的 `product_spec`(规格原文)。三条都是新增,v1–v4 一个字节没改。 |
| v6(#50) | Admin 网页登录:新增 `users` 和 `web_sessions`,只在迁移末尾追加,未改写 v1–v5。 |
| v7(#54) | 客户端负责人:新增 `client_user_assignments` 和当前归属唯一索引,保留绑定、转交、解绑历史;未改写 v1–v6。 |
**v3 为什么丢弃旧 `sku_mappings` 数据(见 #20):** 新主键需要 `pdd_option_key`,
这是 Go 的 `service.OptionKey()` 用 `json.Marshal` 算出来的规范化键,SQL 语句
@@ -689,6 +690,8 @@ shopee_products ──1:N──→ shopee_skus
(skus_json 里是所有规格和价格)
syb_orders ──创建──→ tasks ──分配──→ clients
↑
users(采购员)──1:N 当前归属──────────┘
```
两条关联都可以变,这是有意的:
@@ -753,3 +756,34 @@ CREATE INDEX idx_web_sessions_expiry ON web_sessions(expires_at);
- Session 使用固定过期时间,MVP 默认 12 小时,不做复杂刷新令牌。
- 退出登录、密码重置或账号禁用时,删除该用户对应的 Session 记录。
- 过期 Session 可以在登录、退出或定期维护时清理,不需要后台常驻线程。
## 13. `client_user_assignments` 客户端负责人历史(v7)
`clients` 是执行任务的软件实例,`users` 是登录 Admin 的人,两者生命周期不同,
因此保持两张独立实体表,用归属历史表连接,不能合并字段。
```sql
CREATE TABLE client_user_assignments (
assignment_id TEXT PRIMARY KEY,
client_id TEXT NOT NULL,
user_id TEXT NOT NULL,
started_at TEXT NOT NULL,
ended_at TEXT,
assigned_by_user_id TEXT NOT NULL,
ended_by_user_id TEXT,
end_reason TEXT CHECK (end_reason IS NULL OR end_reason IN ('unbind', 'transfer')),
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id),
FOREIGN KEY (ended_by_user_id) REFERENCES users(user_id)
);
CREATE UNIQUE INDEX idx_client_assignment_current
ON client_user_assignments(client_id) WHERE ended_at IS NULL;
```
- `ended_at IS NULL` 表示当前归属;部分唯一索引保证一台客户端最多一个当前负责人。
- 首次绑定只新增记录;转交在同一事务结束旧记录并新增记录;解绑只结束旧记录。
- `assigned_by_user_id` / `ended_by_user_id` 都是执行操作的管理员,不是目标采购员。
- `client_id` 故意不设指向 `clients` 的外键:客户端清单允许删除后由同一稳定编号
重新登记,归属和审计历史不能随临时清单记录丢失。
- 归属记录不参与 Client API 的登记和领取判断,也不更新 `tasks.assigned_client`。