feat: 按创建人隔离采集采购任务 (#127)
This commit is contained in:
@@ -261,12 +261,17 @@ PDD 商品之所以单独一个模块,是因为它在数据上就是**独立
|
||||
**顶部工具条:**
|
||||
|
||||
```text
|
||||
[类型▾ 全部] [状态▾ 全部] 关键词[____________] [搜索] [删除]
|
||||
[类型▾ 全部] [状态▾ 全部] [创建人▾ 全部] 关键词[____________] [搜索] [删除]
|
||||
```
|
||||
|
||||
- **类型**:全部 / 采集 / 采购。
|
||||
- **状态**:全部 + 7 个状态,见 §6.2。
|
||||
- **关键词**:同时匹配任务编号、订单号、PDD 商品 ID。
|
||||
- **创建人**:仅管理员显示,可选全部账号(包含已禁用账号)或“历史任务”。
|
||||
|
||||
`[必须]` 采购员登录后只可查看自己创建的任务;URL 参数不能扩大范围。管理员查看
|
||||
全部任务,`created_by_user_id` 为空的存量记录显示为“历史任务”。列表、统计、分页、
|
||||
详情和删除必须使用同一权限范围,采购员直接访问他人详情统一返回 404。
|
||||
|
||||
`[必须]` 筛选比搜索更常用:这个页面最常被问的问题是"有没有卡住的任务",
|
||||
不是"订单 SO-001 怎么样了",所以类型和状态筛选要放在最前面。
|
||||
@@ -285,6 +290,7 @@ PDD 商品之所以单独一个模块,是因为它在数据上就是**独立
|
||||
| **目标** | 采集:`PDD <pdd_goods_id>`(能 join 到未删除的商品标题时追加显示);采购:`<order_no> · <颜色/尺码> · <数量>件 · ≤<价格上限>` |
|
||||
| 状态 | 中文,7 个取值见 §6.2 |
|
||||
| 客户端 | `assigned_client`;**无主任务显示 `—`**(#17 之后采集任务默认无主,这一列会大量为空) |
|
||||
| 创建人 | 仅管理员列表显示;存量 NULL 显示“历史任务” |
|
||||
| 更新时间 | 本地时区 |
|
||||
|
||||
`[必须]` 目标列所需字段全在 `tasks` 表上(`pdd_goods_id` / `order_no` /
|
||||
@@ -302,7 +308,7 @@ PDD 商品之所以单独一个模块,是因为它在数据上就是**独立
|
||||
|
||||
`[必须]` **只读**。改派 / 重试 / 取消是后续工单的范围,本页不提供入口。
|
||||
|
||||
弹窗显示:类型、状态、分配客户端、领取时间、完成时间;执行参数
|
||||
弹窗显示:类型、状态、创建人、分配客户端、领取时间、完成时间;执行参数
|
||||
(PDD 链接,采购任务额外显示目标规格、数量、价格上限);错误信息
|
||||
(错误码、错误说明,没有错误时不显示这一段)。
|
||||
|
||||
@@ -317,6 +323,9 @@ Admin 本地时区,付款状态只是 Client 核单上报时的快照,Admin
|
||||
`[建议]` `result_data`(Client 提交的完整结果)默认折叠,提供展开查看;
|
||||
过长时截断显示。
|
||||
|
||||
`[必须]` 批量删除是全有或全无:提交中只要混入不存在或不属于当前采购员的任务,
|
||||
整批拒绝且一条也不删除。
|
||||
|
||||
**底部状态条:**
|
||||
|
||||
```text
|
||||
|
||||
@@ -138,6 +138,10 @@ MySQL 迁移 v5(工单 #98)为 `tasks` 追加 `execution_mode`、`live_confi
|
||||
和 `live_confirmed_at`,并增加模式/审计 CHECK 与领取索引。旧任务通过数据库默认值
|
||||
统一成为 `dry_run`;每条 DDL 可重放,列、约束和索引全部通过自检后才记录 v5。
|
||||
|
||||
MySQL 迁移 v6(工单 #127)为 `tasks` 追加可空的 `created_by_user_id` 和创建人列表
|
||||
索引。存量记录不回填,NULL 明确表示“历史任务”;新建采集和采购任务必须写当前
|
||||
网页登录用户。迁移与自检可重放,历史 SQLite migrations 保持冻结。
|
||||
|
||||
## 3. 蝦皮数据
|
||||
|
||||
蝦皮报表**一个文件里混了两层数据**,所以拆成两张表。
|
||||
@@ -693,6 +697,16 @@ CREATE INDEX idx_tasks_order ON tasks(order_no);
|
||||
缺少创建审计信息的真实任务;`idx_tasks_claim_mode` 支持按客户端能力领取。字段名保留
|
||||
历史命名以兼容已发布的 v5 migration。
|
||||
|
||||
生产 MySQL v6 再追加任务创建人字段:
|
||||
|
||||
| 字段 | 含义 |
|
||||
|---|---|
|
||||
| `created_by_user_id` | 创建任务的 Admin 用户 ID;存量 NULL 表示“历史任务” |
|
||||
|
||||
`idx_tasks_creator_list(created_by_user_id, updated_at DESC, task_id DESC)` 支持采购员
|
||||
按本人范围分页,以及管理员按创建人筛选。用户只禁用不删除,因此无需把显示名称冗余
|
||||
写入任务;`fk_tasks_created_by` 指向 `users(user_id)`,页面通过 `users` 关联显示用户名。
|
||||
|
||||
`[必须]` 两条硬约束,来自 [Client 契约](../client/04-admin-api-contract.md) §4:
|
||||
|
||||
1. **`pdd_goods_url` 不能为空**,否则 Client 无法执行(它那边是 `NOT NULL`)。
|
||||
|
||||
@@ -604,12 +604,14 @@ ID 和在线状态;所有可见 Client 都可选择且只有一个时自动选
|
||||
### 7.1 工具条
|
||||
|
||||
```text
|
||||
[类型▾ 全部] [状态▾ 全部] 关键词[___] [搜索] [删除]
|
||||
[类型▾ 全部] [状态▾ 全部] [创建人▾ 全部] 关键词[___] [搜索] [删除]
|
||||
```
|
||||
|
||||
- **类型**:全部 / 采集 / 采购。
|
||||
- **状态**:全部 + 7 个状态,见 [01 需求](01-requirements.md) §6.2。
|
||||
- **关键词**:同时匹配任务编号、订单号、PDD 商品 ID。
|
||||
- **创建人**:仅管理员显示;包含全部账号、已禁用账号和“历史任务”。采购员不显示
|
||||
此控件,服务端固定只查询本人创建的任务。
|
||||
|
||||
`[必须]` 搜索框宽度见 [§3.1](#31-搜索框宽度)。
|
||||
placeholder 写「任务编号 / 订单号 / 商品 ID」,**不要写全「PDD 商品 ID」**——
|
||||
@@ -617,7 +619,7 @@ placeholder 写「任务编号 / 订单号 / 商品 ID」,**不要写全「PDD
|
||||
|
||||
### 7.2 表格列
|
||||
|
||||
☐ / 任务编号 / 类型 / **执行模式** / **目标** / 状态 / 客户端 / 更新时间
|
||||
☐ / 任务编号 / 类型 / **执行模式** / **目标** / 状态 / 客户端 / 创建人(管理员) / 更新时间
|
||||
|
||||
```text
|
||||
☐ │ 任务编号 │ 类型 │ 执行模式 │ 目标 │ 状态 │ 客户端 │ 更新时间
|
||||
@@ -632,6 +634,7 @@ placeholder 写「任务编号 / 订单号 / 商品 ID」,**不要写全「PDD
|
||||
- `[必须]` **客户端列**:无主任务显示 `—`,不是空白或 `<nil>`——#17 之后
|
||||
采集任务默认无主,这一列会大量为空。
|
||||
- `[必须]` 类型和状态都是文字,不能只靠颜色区分。
|
||||
- `[必须]` 管理员显示创建人列,NULL 显示“历史任务”;采购员省略该列。
|
||||
|
||||
### 7.3 详情弹窗(双击行打开)
|
||||
|
||||
@@ -647,6 +650,7 @@ placeholder 写「任务编号 / 订单号 / 商品 ID」,**不要写全「PDD
|
||||
│ 执行模式 采集 │
|
||||
│ 状态 已领取 │
|
||||
│ 分配客户端 办公室-01 │
|
||||
│ 创建人 buyer-a │
|
||||
│ 领取时间 2026-08-07 15:20:31 │
|
||||
│ 完成时间 — │
|
||||
├──────────────────────────────────────────────┤
|
||||
@@ -685,6 +689,9 @@ placeholder 写「任务编号 / 订单号 / 商品 ID」,**不要写全「PDD
|
||||
`[必须]` 批量删除要二次确认,写明"将删除 N 条,不可恢复"——`tasks` 表
|
||||
没有软删除列,删了就是真删了。
|
||||
|
||||
`[必须]` 删除权限在服务端判断。采购员混选本人和他人任务、或提交不存在编号时,
|
||||
整批拒绝并保持所有任务不变;详情越权与任务不存在都显示 404。
|
||||
|
||||
### 7.5 底部状态条
|
||||
|
||||
```text
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
- 最后管理员保护:不能禁用最后一个有效管理员。
|
||||
- 客户端归属:一人多客户端、一台客户端唯一当前负责人、转交/解绑历史完整;
|
||||
- 归属权限:采购员只看到自己的客户端,不能绑定、解绑或删除;禁用采购员不能成为新目标。
|
||||
- 任务创建人隔离:采购员列表/统计/分页只包含本人任务,伪造创建人参数不能扩大范围;
|
||||
- 任务越权:他人详情表现为 404,混合本人/他人/不存在编号的批量删除完整回滚。
|
||||
|
||||
`[必须]` 导入相关的测试用 `admin/testdata/` 下的**小样本**(几十行),
|
||||
不要读完整报表。
|
||||
@@ -77,6 +79,8 @@
|
||||
- Client API 不返回登录页或 302 重定向。
|
||||
- 管理员客户端页显示全量和负责人;采购员客户端页只读且只显示自己的客户端;
|
||||
- 绑定/转交弹窗有明确标签和焦点,解绑有二次确认,操作结果可被辅助技术读到。
|
||||
- 管理员采集采购页可按正常账号、禁用账号和历史任务筛选并显示创建人;采购员不显示
|
||||
创建人筛选和列表列,详情仍显示创建人。
|
||||
|
||||
### 2.4 契约测试
|
||||
|
||||
@@ -124,6 +128,8 @@
|
||||
- `[必须]` 管理员自助修改密码必须验证当前密码,目标账号从当前 Session 取得;成功后撤销该账号全部 Session 并重新登录。
|
||||
- `[必须]` `/setup`、`/login`、`/logout`、`/account/change-password` 和用户管理写操作都保留 CSRF 防护。
|
||||
- `[必须]` 客户端绑定、转交、解绑和删除只能由管理员执行,并保留 CSRF 防护。
|
||||
- `[必须]` 任务列表、统计、分页、详情和删除按同一创建人范围授权;采购员提交 URL 或
|
||||
表单中的其他创建人/任务编号不能扩大范围,混合越权批量删除必须全部回滚。
|
||||
- `[必须]` Web 登录中间件只保护 HTML 路由,不得覆盖 `/api/v1/client/*`。
|
||||
- `[建议]` 对连续登录失败做简单限速;错误提示不区分用户名不存在和密码错误。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user