M2 最后一块:让"该我办了"能出现在系统里,并且允许不经过任务直接开单。 通知(backend/app/services/notify.py 统一出口) - 七类触发:task_assigned、task_status_changed、item_commented、 ticket_assigned、ticket_submitted、ticket_approved/rejected、task_unblocked - 不通知自己:notify() 按 actor_id 跳过,调用方不用各自判断 - 已读:GET /notifications、/unread-count、POST /read、/read-all 手工开单 - POST /tickets:直接指定人、初始 open,与任务升级单共用同一条 kind='ticket' item, ticket_ext.parent_item_id 为空,验收通过不回流任务 - 指派前 _ensure_member 校验在册成员,越界指派返回 1001(避免"挂在看不到的名字上") 前端 - Web:通知页 /w/:wsId/notifications(未读/全部,点开标已读并跳目标); 工单页"新建"面板;AppShell 每 30 秒对未读数,标已读后 dispatch notifications:changed 立刻刷新; 任务与工单支持 ?open= 深链落地即展开 - 安卓:五个 tab 加"通知",未读数 30 秒刷新;工单动作按需求人/办理人/管理员显隐;手工开单对话框 验证 - pytest 85 passed(新增 backend/tests/test_notifications.py 6 个用例) - ruff check / format --check 全 0 - tsc --noEmit 通过;vite build 成功 - web/e2e/notifications.mjs 12 项断言全绿(手工开单 → B 收到 → 点开跳转 → 未读归零) - 安卓:本机 JDK 25 与 Gradle 8.7 / AGP 8.7.3 不兼容,本次未做编译验证, 装回 JDK 17 后第一件事是 assembleDebug
This commit is contained in:
+34
-2
@@ -227,6 +227,20 @@ canceled→ todo (恢复)
|
||||
工单**不是**另一张表里的另一种东西,它是一条 `kind='ticket'` 的 item 加一行 `ticket_ext`。
|
||||
所以详情仍然走 `GET /items/{item_id}`,响应里多一个 `ticket` 字段。
|
||||
|
||||
### 4.0 `POST /tickets`(手工开单)
|
||||
|
||||
```json
|
||||
{ "title": "核对 9 月发票", "body_md": "可选说明",
|
||||
"assignee_id": "uuid", "priority": 2, "due_at": "2026-10-30T10:00:00+08:00" }
|
||||
```
|
||||
|
||||
返回与详情同构的工单对象(item + `ticket`)。与 4.1 的区别只有一处:**没有来源任务**,
|
||||
`ticket.parent_item_id` 为 `null`,因此验收通过后不会回流任何任务(但同样会沉淀一份知识草稿)。
|
||||
|
||||
- `title` 必填、`assignee_id` 必须是工作区内 `active` 成员,否则 `1001`。
|
||||
- 初始状态 `open`,被指派人立刻收到 `ticket_assigned` 通知。
|
||||
- 手工单与升级单共用同一条 `item`,所以看板、评论、验收、知识沉淀全部复用,不需要另一套接口。
|
||||
|
||||
### 4.1 `POST /items/{item_id}/promote`(任务升级成工单)
|
||||
|
||||
```json
|
||||
@@ -286,8 +300,26 @@ canceled→ todo (恢复)
|
||||
| POST | `/notifications/{id}/read` | 标一条 |
|
||||
| POST | `/notifications/read-all` | 全标已读 |
|
||||
|
||||
`kind`:`ticket_assigned` / `ticket_submitted` / `ticket_approved` / `ticket_rejected` / `task_unblocked`。
|
||||
通知只发给当事人,同工作区里其他人看不到——这是"协同"与"广播"的区别。
|
||||
`kind` 一共七种,都是"某人在某件事上做了什么":
|
||||
|
||||
| kind | 什么时候发 | 发给谁 |
|
||||
|---|---|---|
|
||||
| `task_assigned` | 建任务/改任务时指派了人 | 被指派人 |
|
||||
| `task_status_changed` | 任务状态变化 | 创建人 + 办理人(除操作者本人) |
|
||||
| `item_commented` | 有人评论 | 创建人 + 办理人(除评论者本人) |
|
||||
| `ticket_assigned` | 升级成工单 / 手工开单 / 改派 | 办理人 |
|
||||
| `ticket_submitted` | 办理人提交验收 | 需求人 |
|
||||
| `ticket_approved` / `ticket_rejected` | 验收通过 / 退回 | 办理人 |
|
||||
| `task_unblocked` | 工单验收通过、原任务回流待办 | 原任务创建人 |
|
||||
|
||||
三条硬规则:
|
||||
|
||||
1. **不通知自己**:谁操作就跳过谁,否则每个人都会收到自己刚做的事。
|
||||
2. 通知里必须写清"谁 + 做了什么 + 在哪件事上",否则点开等于重新看一遍列表。
|
||||
3. 通知只是提示,不是凭证——点开之后目标接口会重新校验权限、重新取数据。
|
||||
|
||||
Web 端未读数由 `AppShell` 每 30 秒轮询一次;在通知页标已读后立刻派发 `notifications:changed` 事件刷新角标,不必等下一轮。
|
||||
安卓端同样按 30 秒/切页刷新。
|
||||
|
||||
### 4.6 工单的错误码
|
||||
|
||||
|
||||
Reference in New Issue
Block a user