13 KiB
API v2 Mission 任务模块接口文档
本文档面向前端,描述 mission 中立任务模块的 API。
基本约定
- Base URL:
/api/v2 - 认证:所有接口都需要登录。
- 人员身份:后端使用
request.user.employee作为当前员工身份。 - 多商户隔离:所有任务、任务分类、参与者、回应都只允许访问当前员工所属商户的数据。
- 删除能力:不提供任务删除接口;需要结束业务时使用
cancel取消任务。 - 状态字段:普通创建/更新接口不允许修改状态字段,状态变化统一走独立接口。
普通 CRUD 禁止提交这些字段:
| 字段 | 说明 |
|---|---|
is_urgent |
是否紧急,使用 set-urgent 接口修改 |
is_completed |
是否完成,由结束回应或 reopen/reject 流程维护 |
is_cancelled |
是否取消,使用 cancel 接口修改 |
cancelled_by |
取消人,由后端写入 |
cancelled_at |
取消时间,由后端写入 |
rejected_by |
撤销人,由后端写入 |
rejected_at |
撤销时间,由后端写入 |
说明:
- 本次新增的“未回复提醒配置字段”不属于状态字段,允许通过普通创建/更新接口维护
content_type 说明
content_type 和 content_id 是可选的"关联业务对象"字段,用于把任务挂靠到系统中的某个具体业务单据或对象上。
content_type 是什么
content_type 是一个整数,对应后端数据库 django_content_type 表的主键(id)。
该表记录了系统中所有 Django 模型的元信息,每一行代表一个模型,格式为 app_label + model。
前端不应硬编码 content_type 整数 ID,因为这个 ID 在不同部署环境中可能不同。
正确做法:调用 /api/v2/content-types/ 接口(见下方"查询可用 content_type"一节)获取当前环境的实际 ID。
content_type_label 字段
响应体中的 content_type_label 字段是人类可读的模型标识,格式为 "app_label.model",例如:
"printing.plateorder"— 开版单"printing.printingjob"— 印刷任务"business.purchaseorder"— 采购单
该字段只读,用于前端展示或调试,不作为提交 content_type 时的值。
当前系统中可关联的主要业务对象
content_type_label |
中文说明 |
|---|---|
printing.plateorder |
开版单 |
printing.printingorder |
印刷单 |
printing.printingjob |
印刷任务 |
business.purchaseorder |
采购单 |
business.presalesorder |
预销售单 |
business.salesorder |
销售单 |
business.prepurchaseorder |
预采购单 |
business.purchasereturnorder |
采购退货单 |
business.salesreturnorder |
销售退货单 |
business.paymentorder |
付款单 |
business.receiptorder |
收款单 |
stock.transferorder |
调拨单 |
shipment.shipment |
发货单 |
stateflow.process |
工艺流程实例 |
上述列表是"有意义的"业务关联对象;系统本身不限制可关联的模型类型,技术上任何有效 ContentType ID 都被接受。
查询可用 content_type
- URL:
/api/v2/content-types/ - Method:
GET
响应示例:
[
{"id": 7, "app_label": "business", "model": "paymentorder", "label": "business.paymentorder", "name": "付款单"},
{"id": 8, "app_label": "business", "model": "presalesorder", "label": "business.presalesorder", "name": "预销售单"},
...
]
前端应在运行时调用此接口获取 id,不应硬编码,因为不同部署环境的 ID 可能不同。
数据结构
MissionCategory
{
"id": 1,
"merchant": 10,
"name": "通用",
"created_at": "2026-04-10T12:00:00+08:00",
"updated_at": "2026-04-10T12:00:00+08:00"
}
Mission
{
"id": 1,
"merchant": 10,
"description": "跟进客户问题",
"category": 1,
"category_name": "通用",
"is_urgent": false,
"is_completed": false,
"is_cancelled": false,
"notify_if_unreplied": false,
"unreplied_notify_interval_minutes": null,
"unreplied_notify_max_count": 5,
"unreplied_notify_sent_count": 0,
"unreplied_last_notified_at": null,
"cancelled_at": null,
"creator": {
"id": 20,
"name": "张三",
"merchant_id": 10
},
"cancelled_by": null,
"participants": [
{
"id": 21,
"name": "李四",
"merchant_id": 10
}
],
"content_type": null,
"content_type_label": null,
"content_id": null,
"has_ending_reply": false,
"can_reply": true,
"created_at": "2026-04-10T12:00:00+08:00",
"updated_at": "2026-04-10T12:00:00+08:00"
}
MissionReply
{
"id": 100,
"mission": 1,
"merchant": 10,
"responder": {
"id": 20,
"name": "张三",
"merchant_id": 10
},
"content": "已处理",
"replied_at": "2026-04-10T12:10:00+08:00",
"ends_task": true,
"is_rejected": false,
"rejected_by": null,
"rejected_at": null,
"created_at": "2026-04-10T12:10:00+08:00",
"updated_at": "2026-04-10T12:10:00+08:00"
}
任务分类列表
- URL:
/api/v2/mission-categories/ - Method:
GET
响应:MissionCategory[]
创建任务分类
- URL:
/api/v2/mission-categories/ - Method:
POST
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 是 | 分类名称,同商户下唯一 |
请求示例:
{
"name": "售后"
}
成功响应:201 Created,返回 MissionCategory
任务分类详情
- URL:
/api/v2/mission-categories/<category_id>/ - Method:
GET
成功响应:MissionCategory
更新任务分类
- URL:
/api/v2/mission-categories/<category_id>/ - Method:
PATCH
允许更新:
| 参数 | 类型 | 说明 |
|---|---|---|
name |
string | 分类名称,同商户下唯一 |
成功响应:MissionCategory
删除任务分类
- URL:
/api/v2/mission-categories/<category_id>/ - Method:
DELETE
说明:
- 如果该分类已经被任务使用,则删除会被拒绝,返回
400
成功响应:204 No Content
任务列表
- URL:
/api/v2/missions/ - Method:
GET
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
category |
int | 任务分类 ID |
is_urgent |
boolean | true / false |
is_completed |
boolean | true / false |
is_cancelled |
boolean | true / false |
content_type |
int | Django ContentType ID |
content_id |
int | 关联业务对象 ID |
响应:Mission[]
创建任务
- URL:
/api/v2/missions/ - Method:
POST
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
description |
string | 是 | 任务描述 |
category |
int | 否 | 任务分类 ID;不传时默认使用当前商户下名称为“通用”的分类,不存在则自动创建 |
content_type |
int/null | 否 | Django ContentType ID;必须与 content_id 同时提供或同时省略 |
content_id |
int/null | 否 | 关联业务对象 ID;必须与 content_type 同时提供或同时省略 |
notify_if_unreplied |
boolean | 否 | 是否开启“未回复持续提醒”,默认 false |
unreplied_notify_interval_minutes |
int/null | 否 | 未回复提醒间隔(分钟);开启未回复提醒时必填 |
unreplied_notify_max_count |
int | 否 | 最大提醒次数,默认 5 |
participant_ids |
int[] | 否 | 参与者员工 ID 列表,必须属于当前商户 |
说明:
creator、merchant由当前登录用户的 employee 自动写入category_name为只读字段,由后端根据分类表返回is_urgent、is_completed、is_cancelled均按默认值创建,不接受请求参数- 若关联对象存在
merchant_id字段,后端会校验它必须属于当前商户 - 若
notify_if_unreplied=true,则必须同时传入unreplied_notify_interval_minutes unreplied_notify_sent_count与unreplied_last_notified_at为只读运行时字段,由后端维护
请求示例:
{
"description": "跟进客户问题",
"category": 1,
"notify_if_unreplied": true,
"unreplied_notify_interval_minutes": 30,
"unreplied_notify_max_count": 5,
"participant_ids": [21, 22]
}
成功响应:201 Created,返回 Mission
任务详情
- URL:
/api/v2/missions/<mission_id>/ - Method:
GET
成功响应:Mission
跨商户访问返回 404
更新任务
- URL:
/api/v2/missions/<mission_id>/ - Method:
PATCH
允许更新:
| 参数 | 类型 | 说明 |
|---|---|---|
description |
string | 任务描述 |
category |
int | 任务分类 ID |
content_type |
int/null | 关联对象类型;必须与 content_id 同时提供 |
content_id |
int/null | 关联对象 ID;必须与 content_type 同时提供 |
notify_if_unreplied |
boolean | 是否开启“未回复持续提醒” |
unreplied_notify_interval_minutes |
int/null | 未回复提醒间隔(分钟) |
unreplied_notify_max_count |
int | 最大提醒次数 |
participant_ids |
int[] | 重置参与者列表 |
禁止更新状态字段,见“基本约定”
补充说明:
- 更新
notify_if_unreplied、unreplied_notify_interval_minutes时,后端会重置当前任务的未回复提醒计数与上次提醒时间 - 更新
unreplied_notify_max_count不会重置已提醒次数
成功响应:Mission
删除任务
- URL:
/api/v2/missions/<mission_id>/ - Method:
DELETE
当前不提供删除能力,固定返回:
{
"detail": "Mission 删除接口未提供,请使用 cancel 接口取消任务"
}
HTTP 状态码:405 Method Not Allowed
获取任务回应列表
- URL:
/api/v2/missions/<mission_id>/replies/ - Method:
GET
查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
ends_task |
boolean | true / false,筛选是否为结束回应 |
is_rejected |
boolean | true / false,筛选是否已被撤销 |
响应:MissionReply[],按 replied_at 升序排列
创建任务回应
- URL:
/api/v2/missions/<mission_id>/replies/ - Method:
POST
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
content |
string | 是 | 回应内容 |
ends_task |
boolean | 否 | 是否结束任务,默认 false |
说明:
responder使用当前登录用户的 employee- 如果
ends_task=true,后端会同步设置Mission.is_completed=true - 已取消任务、或已有有效结束回应的任务不允许继续回应
成功响应:201 Created,返回 MissionReply
重新打开任务
- URL:
/api/v2/missions/<mission_id>/reopen/ - Method:
POST - 额外权限:
mission.reopen_mission
请求体可为空:
{}
前置条件:
- 当前用户必须具备
mission.reopen_mission - 任务未取消
- 任务已完成
- 任务存在有效的
ends_task=true回应
执行效果:
Mission.is_completed=false- 相关结束回应被标记为
ends_task=false - 相关结束回应记录
is_rejected=true、rejected_by=当前员工、rejected_at=当前时间
成功响应:Mission
无权限响应:403 Forbidden
取消任务
- URL:
/api/v2/missions/<mission_id>/cancel/ - Method:
POST
请求体可为空:
{}
执行效果:
Mission.is_cancelled=trueMission.cancelled_by=当前员工Mission.cancelled_at=当前时间- 不强行修改
Mission.is_completed - 后端会停止当前任务后续的未回复提醒,并保留任务上的提醒历史字段供查看
成功响应:Mission
设置紧急状态
- URL:
/api/v2/missions/<mission_id>/set-urgent/ - Method:
POST
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
is_urgent |
boolean | 是 | 新的紧急状态 |
请求示例:
{
"is_urgent": true
}
成功响应:Mission
撤销任务回应
- URL:
/api/v2/mission-replies/<reply_id>/reject/ - Method:
POST - 额外权限:
mission.reject_mission_reply
请求体可为空:
{}
执行效果:
- 当前回应标记为
is_rejected=true - 当前回应的
rejected_by、rejected_at由后端写入 - 如果当前回应原本是唯一有效的结束回应,则对应任务会被重新置为未完成
- 如果任务重新回到“没有任何有效回复”的状态,后端会重置未回复提醒计数,并从新的空窗期重新开始计算后续提醒
成功响应:MissionReply
无权限响应:403 Forbidden
未回复提醒规则
当任务开启 notify_if_unreplied=true 时,系统会通过每分钟一次的后台定时任务检查是否需要发送“未回复提醒”。
判定规则:
- 任务开启了未回复提醒
- 任务未完成
- 任务未取消
- 任务当前没有任何有效回复(
is_rejected=false的回复) unreplied_notify_sent_count < unreplied_notify_max_count- 到达提醒时间:
- 从未提醒过:
created_at + unreplied_notify_interval_minutes - 已提醒过:
unreplied_last_notified_at + unreplied_notify_interval_minutes
发送成功后:
unreplied_notify_sent_count自增 1unreplied_last_notified_at更新为本次成功发送时间
收到有效回复后:
- 当前未回复提醒周期会被停止
unreplied_notify_sent_count与unreplied_last_notified_at会被重置
如果后续因为撤销回复或 reopen 重新回到“无有效回复”状态:
- 系统会把该任务视为一个新的未回复周期重新开始计时