feat: harden downstream requeue tasks
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
# 下游投递后台批量重投任务设计与实现符合性审计
|
||||
|
||||
> 版本:V1.0<br>
|
||||
> 需求确认日期:2026-08-12<br>
|
||||
> 文档整理日期:2026-08-13<br>
|
||||
> 适用页面:运营端 → 下游投递记录<br>
|
||||
> 审计基线:当前工作区 `HEAD=67fee216162e638ba21004fcf87e7711facefb91`;本功能相关文件相对 HEAD 无未提交修改<br>
|
||||
> 本文目的:还原 2026-08-12 已确认的设计口径,并将当前实现逐条映射到设计,不能以“已有代码”代替“符合设计”的结论。
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
下游投递记录原有两种人工操作:单条重投、勾选当前页后批量重投。它们适合少量记录,但不适合事故期间处理跨页、跨应用的大批量积压。
|
||||
|
||||
新增“按当前筛选条件创建后台重投任务”的目标是:
|
||||
|
||||
1. 后端按当前真实筛选条件确定范围,不受页面分页影响;
|
||||
2. 创建时冻结任务快照,避免执行期间不断卷入新记录;
|
||||
3. 后台限速执行,客户离线或 ACK 异常时不得形成重投洪峰;
|
||||
4. 每条任务项可追踪、可恢复、可审计,成功确认后不得重复投递;
|
||||
5. 运营人员能够预检、创建、暂停、继续、终止和查看完整结果。
|
||||
|
||||
本功能不替代单条重投和当前页勾选重投,三种入口应同时保留。
|
||||
|
||||
## 2. 已确认的第一版业务口径
|
||||
|
||||
以下七条是 2026-08-12 已写入正式需求文档和测试用例的确认口径:
|
||||
|
||||
1. 保留单条重投、当前页勾选批量重投,新增“按筛选条件重投”;投递记录分页支持每页 10/25/50 条。
|
||||
2. 后台任务使用企业、应用、投递类型、状态、创建日期、关键词组成的筛选快照;页码和每页条数不属于任务范围。预检生成 `snapshotAt`,创建任务后产生的新记录不进入该任务。
|
||||
3. 第一版后台任务只允许 `pending`、`failed`、`unconfirmed`、`rejected`。不得批量重投客户端已确认的 `delivered`;处于 `awaiting_ack` 的记录不得并发重投。创建前展示真实命中数、可重投数、规则跳过数、状态分布,任务原因必填。
|
||||
4. 任务按企业应用分批执行,默认每个应用 10 条/秒。单条失败不阻断整批;连续失败达到 10 条,或 ACK 超时/拒绝达到安全阈值时,自动暂停。客户离线、已有链路等待 ACK 属于“等待”,不能记作“跳过”。
|
||||
5. “跳过”严格表示本任务没有调用 Gateway。第一版跳过原因包括:执行前状态变化、已被客户确认、已被其他任务处理、本任务已成功处理、不属于任务快照、应用或投递能力已停用、投递数据不完整、缺少原 Submit 映射。
|
||||
6. 任务支持列表、详情、暂停、继续、终止。终止只影响尚未发送的任务项。任务项以 `taskId + deliveryId` 幂等;执行前原子认领并复核当前状态;API 重启后继续执行;已获得成功 ACK 的任务项不得再次发送。
|
||||
7. 创建、暂停、继续、终止、自动暂停都必须写操作日志。任务必须使用真实 PostgreSQL、Gateway 和客户 ACK,不得使用 mock、静态数据或 localStorage。
|
||||
|
||||
## 3. 用户交互设计
|
||||
|
||||
### 3.1 投递记录筛选区
|
||||
|
||||
筛选条件应包含:
|
||||
|
||||
| 条件 | 说明 |
|
||||
| --- | --- |
|
||||
| 企业 | 全部企业或指定企业 |
|
||||
| 企业应用 | 受企业条件联动;全部应用或指定应用 |
|
||||
| 投递类型 | 全部、状态回执、上行短信 |
|
||||
| 状态 | 全部或单一状态 |
|
||||
| 创建日期 | 开始日期、结束日期,按北京时间自然日 |
|
||||
| 关键词 | 消息 ID、客户账号、手机号、最后错误、企业名称、应用名称 |
|
||||
|
||||
“按筛选条件重投”使用上表条件,但明确排除页码、每页条数和当前页勾选状态。
|
||||
|
||||
### 3.2 创建任务流程
|
||||
|
||||
1. 用户设置筛选条件,点击“按筛选条件重投”。
|
||||
2. 后端在同一时点生成预检快照,返回:筛选条件、`snapshotAt`、筛选命中数、可重投数、规则跳过数、状态分布、涉及应用数、最早记录时间。
|
||||
3. 弹窗明确告知第一版允许和禁止的状态。
|
||||
4. 用户选择执行速度,填写不少于 5 个字的事故原因、工单号或处理说明。
|
||||
5. 用户确认后,后端必须重新按预检的筛选快照和 `snapshotAt` 物化任务项,而不是使用前端传入的记录 ID 列表。
|
||||
6. 创建成功后关闭弹窗,任务出现在任务列表,状态为“排队中”。
|
||||
|
||||
预检结果必须严格遵守当前筛选条件。例如当前状态选择“待投递”,命中数、可重投数和状态分布都只能基于“待投递”记录,不能擅自扩展为全部状态。
|
||||
|
||||
### 3.3 任务列表
|
||||
|
||||
任务列表至少展示:任务号、创建时间、企业/应用范围、任务原因、中文状态、总数、成功数、失败数、跳过数、等待数、创建人。
|
||||
|
||||
列表应支持分页和状态筛选,保证历史任务可查询,不能只展示固定数量的最近任务。
|
||||
|
||||
可执行操作:
|
||||
|
||||
| 当前状态 | 可执行操作 |
|
||||
| --- | --- |
|
||||
| 排队中、执行中 | 查看详情、暂停、终止 |
|
||||
| 已暂停 | 查看详情、继续、终止 |
|
||||
| 已完成、部分完成、已终止 | 查看详情 |
|
||||
|
||||
### 3.4 任务详情
|
||||
|
||||
任务详情分为三部分:
|
||||
|
||||
1. 任务信息:筛选快照、快照时间、原因、创建人、速度、安全阈值、开始/暂停/完成时间;
|
||||
2. 结果汇总:排队、处理中、等待连接、等待 ACK、成功、失败、跳过、未处理;
|
||||
3. 完整任务项:支持分页及按结果状态、消息 ID、错误/跳过原因查询。
|
||||
|
||||
任务项必须使用中文状态和明确原因,不能只返回内部英文枚举,也不能只展示最近 50 项而使其余项目不可查询。
|
||||
|
||||
## 4. 后端数据设计
|
||||
|
||||
### 4.1 任务主表
|
||||
|
||||
`DownstreamRequeueTask` 保存:
|
||||
|
||||
- 任务号、企业范围、应用范围;
|
||||
- 完整筛选快照及 `snapshotAt`;
|
||||
- 原因、执行速度、失败安全阈值;
|
||||
- 任务状态和各结果计数;
|
||||
- 创建人、开始时间、暂停时间、完成时间、最近错误;
|
||||
- 创建时间、更新时间。
|
||||
|
||||
### 4.2 任务项表
|
||||
|
||||
`DownstreamRequeueTaskItem` 保存:
|
||||
|
||||
- `taskId`、`deliveryId`、`applicationId`;
|
||||
- 创建任务时的原状态;
|
||||
- 当前任务项状态;
|
||||
- 跳过原因、失败信息;
|
||||
- 认领时间、完成时间、创建时间、更新时间。
|
||||
|
||||
数据库必须有 `taskId + deliveryId` 唯一约束。任务项不能级联删除原下游投递记录,历史下游投递和 ACK 证据必须保留。
|
||||
|
||||
## 5. 状态机设计
|
||||
|
||||
### 5.1 任务状态
|
||||
|
||||
| 状态 | 含义 | 进入条件 |
|
||||
| --- | --- | --- |
|
||||
| `queued` | 排队中 | 创建成功或人工继续 |
|
||||
| `running` | 执行中 | 扫描器开始处理 |
|
||||
| `paused` | 已暂停 | 人工暂停或触发安全阈值 |
|
||||
| `completed` | 已完成 | 所有任务项完成且没有失败 |
|
||||
| `partial_completed` | 部分完成 | 所有任务项结束但存在失败 |
|
||||
| `terminated` | 已终止 | 人工终止;未认领项不再执行 |
|
||||
|
||||
### 5.2 任务项状态
|
||||
|
||||
| 状态 | 是否调用过 Gateway | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `queued` | 否 | 等待认领 |
|
||||
| `processing` | 尚不确定 | 已原子认领,正在复核并准备调用 |
|
||||
| `waiting_connection` | 否或尚未写出 | 客户离线,等待可用连接,不算失败、不算跳过 |
|
||||
| `waiting_external_ack` | 否 | 其他链路已写出,等待其 ACK 结论 |
|
||||
| `waiting_ack` | 是 | 本任务已写出,等待客户 ACK |
|
||||
| `success` | 是 | 客户返回可关联原消息的成功 ACK |
|
||||
| `failed` | 是或调用失败 | Gateway 调用失败、ACK 超时或 ACK 拒绝 |
|
||||
| `skipped` | 否 | 复核后确认本任务不应调用 Gateway |
|
||||
| `unprocessed` | 否 | 任务终止时尚未开始 |
|
||||
|
||||
“写出成功”不能直接记为 `success`,只有客户有效 ACK 才是成功。
|
||||
|
||||
## 6. 执行与并发控制
|
||||
|
||||
1. 扫描器只处理 `queued/running` 任务。
|
||||
2. 多实例扫描时必须先原子认领任务项;同一任务项只能有一个执行者。
|
||||
3. 执行前重新读取下游投递及应用状态,再判断等待、跳过或调用 Gateway。
|
||||
4. 限速维度是“企业应用”,不是任务总量。一个跨 3 个应用的任务配置 10 条/秒时,每个应用各自最多 10 条/秒,并且各应用互不阻塞。
|
||||
5. 限速必须使用时间窗口或分布式令牌,不能依赖“定时器大约每秒执行一次 + 每轮取 N 条”,否则扫描重叠或执行耗时变化会突破或降低速率。
|
||||
6. 客户离线时任务项进入等待连接;客户恢复后继续,不应消耗连续失败阈值。
|
||||
7. 本任务写出后进入 `waiting_ack`;成功 ACK 转 `success`;ACK 超时、拒绝、无效 Msg_Id 转 `failed` 并计入安全阈值。
|
||||
8. 连续失败只能在真正成功 ACK 后清零,不能在“写出并开始等待 ACK”时提前清零。
|
||||
9. `processing` 必须有租约/超时恢复。API 进程中断后,超过认领租约的项目恢复为 `queued` 并重新复核,才能满足重启续跑。
|
||||
10. 暂停或终止与执行器并发时,执行器在认领下一项和调用 Gateway 前都必须复核任务状态。
|
||||
|
||||
## 7. 跳过、等待和失败判定
|
||||
|
||||
### 7.1 跳过
|
||||
|
||||
跳过意味着本任务没有调用 Gateway:
|
||||
|
||||
| 原因 | 判定 |
|
||||
| --- | --- |
|
||||
| 执行前状态已变化 | 已不属于允许重投状态,且不是等待 ACK/已确认 |
|
||||
| 已被客户确认 | 当前投递已是 `delivered` 且有效 ACK |
|
||||
| 已被其他任务处理 | 其他任务已认领、等待 ACK 或成功 |
|
||||
| 本任务已成功处理 | 同任务项已有成功结果,重复扫描不得再调用 |
|
||||
| 不属于任务快照 | 创建时间晚于 `snapshotAt` 或不再满足冻结范围 |
|
||||
| 应用或投递能力已停用 | 应用状态或接口能力不允许投递 |
|
||||
| 投递数据不完整 | 缺少可重放 payload 或投递类型非法 |
|
||||
| 缺少原 Submit 映射 | 无法形成可关联原短信的安全回执 |
|
||||
|
||||
### 7.2 等待
|
||||
|
||||
- 客户离线、没有可用连接:`waiting_connection`;
|
||||
- 其他链路已经写出且仍在 ACK 窗口:`waiting_external_ack`;
|
||||
- 本任务已经写出:`waiting_ack`。
|
||||
|
||||
等待项不计入跳过数或失败数。等待结束后根据真实状态继续、成功或失败。
|
||||
|
||||
### 7.3 失败
|
||||
|
||||
- 调用 Gateway 发生非等待型错误;
|
||||
- Gateway 明确拒绝或无法安全投递;
|
||||
- 本任务写出后 ACK 超时;
|
||||
- 客户 ACK 非零;
|
||||
- 客户 ACK 无法关联原消息。
|
||||
|
||||
失败原因必须保留原始错误,同时归一为可统计的失败类别。
|
||||
|
||||
## 8. 安全阈值与自动暂停
|
||||
|
||||
默认安全策略:
|
||||
|
||||
- 每应用默认 10 条/秒;
|
||||
- 连续失败阈值默认 10 条;
|
||||
- ACK 超时和 ACK 拒绝纳入失败阈值;
|
||||
- 达到阈值时,在认领下一条之前将任务原子改为 `paused`;
|
||||
- 写操作日志,记录任务号、失败类别、连续失败数、阈值和暂停时间;
|
||||
- 页面展示明确的自动暂停原因;
|
||||
- 人工继续后从待处理/等待项继续,不重放已成功项。
|
||||
|
||||
如果多个应用同时执行,连续失败计数至少应按应用隔离,避免一个客户应用故障暂停其他正常应用;第一版若选择整任务暂停,也必须在设计和页面中明确,且仍要保存触发暂停的应用。
|
||||
|
||||
## 9. API 设计
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| POST | `/admin/operations/downstream-requeue-tasks/preview` | 按当前筛选条件生成预检快照 |
|
||||
| POST | `/admin/operations/downstream-requeue-tasks` | 使用预检快照创建任务 |
|
||||
| GET | `/admin/operations/downstream-requeue-tasks` | 分页查询任务列表,支持状态筛选 |
|
||||
| GET | `/admin/operations/downstream-requeue-tasks/:id` | 查询任务汇总 |
|
||||
| GET | `/admin/operations/downstream-requeue-tasks/:id/items` | 分页查询完整任务项及原因 |
|
||||
| POST | `/admin/operations/downstream-requeue-tasks/:id/pause` | 暂停 |
|
||||
| POST | `/admin/operations/downstream-requeue-tasks/:id/resume` | 继续 |
|
||||
| POST | `/admin/operations/downstream-requeue-tasks/:id/terminate` | 终止 |
|
||||
|
||||
创建接口不得仅信任前端回传的筛选条件和时间。建议预检生成短期有效、服务端签名的 `previewToken`,绑定筛选条件、`snapshotAt` 和操作人;创建时校验令牌,防止绕过页面篡改范围。
|
||||
|
||||
## 10. 验收用例
|
||||
|
||||
| 编号 | 验收点 | 预期 |
|
||||
| --- | --- | --- |
|
||||
| DRQ-001 | 筛选快照 | 企业、应用、类型、状态、日期、关键词均生效;分页无关;快照后新记录不进入 |
|
||||
| DRQ-002 | 预检口径 | 命中、可重投、跳过和状态分布严格基于当前筛选条件 |
|
||||
| DRQ-003 | 状态白名单 | 只物化 `pending/failed/unconfirmed/rejected`;拒绝 `delivered/awaiting_ack` |
|
||||
| DRQ-004 | 每应用限速 | 多应用任务中每个应用独立达到配置速度,任意扫描重叠都不超速 |
|
||||
| DRQ-005 | 客户离线 | 进入等待连接,不记失败或跳过,恢复连接后继续 |
|
||||
| DRQ-006 | ACK 闭环 | 写出只进入等待;有效 ACK 成功;超时、拒绝、无效 Msg_Id 失败 |
|
||||
| DRQ-007 | 自动暂停 | 立即失败与 ACK 失败均计入阈值;达到阈值自动暂停并审计 |
|
||||
| DRQ-008 | 并发幂等 | 多实例同时扫描,同一投递最多调用一次 Gateway |
|
||||
| DRQ-009 | 重启恢复 | 在 `processing`、等待连接、等待 ACK 三种阶段重启 API,任务均可继续且不重复成功项 |
|
||||
| DRQ-010 | 人工控制 | 暂停、继续、终止与执行并发时状态正确;终止不撤回已写出项 |
|
||||
| DRQ-011 | 完整可查 | 任务列表和任务项均可分页查询全部历史数据,不限最近 10/50 条 |
|
||||
| DRQ-012 | 操作审计 | 创建、暂停、继续、终止、自动暂停均有操作人/触发源和完整上下文 |
|
||||
|
||||
## 11. 当前实现符合性审计
|
||||
|
||||
当前实现的主要文件:
|
||||
|
||||
- `api/src/send-chain/send-downstream-requeue-task.service.ts`
|
||||
- `api/src/send-chain/send-downstream-state.service.ts`
|
||||
- `api/src/operations/admin-operations.controller.ts`
|
||||
- `api/prisma/schema.prisma`
|
||||
- `src/apps/admin/AdminDownstreamDeliveriesPage.tsx`
|
||||
- `api/src/send-chain/send-downstream-requeue-task.service.spec.ts`
|
||||
|
||||
### 11.1 已符合或基本符合
|
||||
|
||||
| 设计项 | 结论 | 当前证据 |
|
||||
| --- | --- | --- |
|
||||
| 真实持久化 | 符合 | 已有任务表、任务项表和 migration,不使用前端本地状态代替任务 |
|
||||
| 快照时间上限 | 基本符合 | 创建时按 `snapshotAt` 限制 `createdAt`,快照后记录不物化 |
|
||||
| 后台状态白名单 | 符合 | `REPLAYABLE_STATUSES` 为 `pending/failed/unconfirmed/rejected` |
|
||||
| 禁止后台任务处理已确认/等待 ACK 筛选 | 符合 | 创建接口明确拒绝 `delivered/awaiting_ack` |
|
||||
| 原因校验 | 符合 | 少于 5 个字拒绝 |
|
||||
| 任务项幂等 | 符合 | 数据库唯一约束 `taskId + deliveryId` |
|
||||
| 执行前任务项认领 | 基本符合 | 通过 `status=queued` 的条件更新认领为 `processing` |
|
||||
| 执行前复核 | 基本符合 | 重新查询投递、应用、payload 和其他任务状态 |
|
||||
| 成功 ACK 不再发送 | 基本符合 | 已确认投递会跳过,其他任务 `success` 也会阻止调用 |
|
||||
| 人工控制 | 基本符合 | 已有暂停、继续、终止接口和页面按钮 |
|
||||
| 核心操作日志 | 基本符合 | 创建、暂停、继续、终止、自动暂停均写日志 |
|
||||
| 投递记录分页 | 符合 | 页面支持每页 10/25/50 条并回到第一页 |
|
||||
|
||||
### 11.2 明确偏差与缺口
|
||||
|
||||
| 优先级 | 偏差 | 当前实现 | 与设计冲突及风险 |
|
||||
| --- | --- | --- | --- |
|
||||
| P0 | API 重启后 `processing` 项无法恢复 | 只扫描 `queued`,没有 `processing` 认领租约或超时回收 | 进程在认领后中断会使任务项永久卡住,任务永久 `running`,不满足重启续跑 |
|
||||
| P0 | ACK 失败不参与自动暂停阈值 | `reconcileWaiting`把 ACK 超时/拒绝改为 `failed`,但不增加`consecutiveFailures`;进入 `waiting_ack` 时反而立即清零 | 客户持续拒绝或 ACK 超时不会触发安全暂停,可能持续向故障客户重投 |
|
||||
| P0 | 限速不是按应用,且可能被并发扫描突破 | 每个任务每轮最多取 `min(10, ratePerSecond)`;没有按`applicationId`分组,也没有分布式速率令牌;定时器可能重叠执行 | 不符合“每应用10条/秒”;配置20实际单轮最多10,多扫描器/多实例又可能超过配置 |
|
||||
| P0 | 客户离线被记为任务项失败 | Gateway未写出时,底层通常返回`pending/failed`,任务层将非`awaiting_ack/delivered`结果直接记为`failed` | 不符合“客户离线属于等待”;会错误增加失败数并可能自动暂停 |
|
||||
| P1 | 预检不严格遵守当前状态筛选 | 计算`matchedCount/statusCounts`时强制把状态改成`all` | 用户选择“待投递”时,命中数和状态分布仍可能包含其他状态;预检范围表达失真 |
|
||||
| P1 | 页面缺少企业筛选 | 后端类型支持`tenantId`,但页面只有应用筛选,`currentTaskFilter`不传企业 | 未完整实现已确认的“企业 + 应用”筛选范围 |
|
||||
| P1 | 任务列表不可完整查询 | 页面固定请求第1页、每页10条,没有任务分页和状态筛选 | 第11个以后历史任务在页面不可达,不满足任务列表要求 |
|
||||
| P1 | 任务详情不可完整查询 | 详情接口固定返回最近50个任务项,没有任务项分页接口 | 大任务无法核对全部失败、跳过和等待项,难以验收和审计 |
|
||||
| P1 | 扫描器缺少任务级/应用级分布式锁 | `setInterval`直接调用扫描,`runScan`可被重叠触发,多实例也会同时处理同一任务 | 虽有任务项条件认领可降低单项重复,但无法保证整体限速和连续失败统计一致 |
|
||||
| P1 | 连续失败清零时点错误 | Gateway写出进入`waiting_ack`即把连续失败清零 | 写出不是业务成功;应等有效 ACK 后再清零 |
|
||||
| P1 | 预检与创建没有不可篡改绑定 | 创建接口信任前端回传的`filter + snapshotAt`,没有预检令牌或服务端预检记录 | 可绕过页面修改筛选范围;虽仍受状态白名单和10万条上限保护,但不等于复用原预检结果 |
|
||||
| P1 | 自动化覆盖远低于设计风险 | 专项仅4个测试:预检计数、参数拒绝、活动任务冲突、已确认跳过 | 未覆盖限速、多应用、离线等待、ACK阈值、重启恢复、并发扫描、完整分页及所有审计动作 |
|
||||
| P2 | 状态和详情表达偏内部化 | 任务列表/详情直接展示英文状态;任务详情只展示汇总和最近项 | 运营人员不易区分等待连接、等待外部ACK、任务写出等待ACK等状态 |
|
||||
| P2 | 终止后的未处理数未进入常规汇总 | 终止把`queued`改为`unprocessed`,但任务汇总只保存成功/失败/跳过/等待 | 任务进度分子可能小于总数,页面没有单独解释未处理数量 |
|
||||
| P2 | 已确认跳过原因存在口径混用 | `waiting_external_ack`最终由其他链路成功后记为“跳过:已由其他投递链路完成” | 可以接受为“本任务未调用Gateway”,但详情必须明确这是外部链路成功,不应让用户误以为业务未处理 |
|
||||
|
||||
### 11.3 综合结论
|
||||
|
||||
当前实现完成了数据库模型、基本预检、任务创建、任务项认领、Gateway调用、ACK结果回看和人工控制的骨架,但不能判定为严格按照 2026-08-12 方案完成。
|
||||
|
||||
尤其以下四项属于设计中的安全核心,当前存在实质缺口:
|
||||
|
||||
1. `processing` 任务项缺少重启恢复;
|
||||
2. ACK 超时/拒绝未进入自动暂停阈值;
|
||||
3. 每应用限速没有实现,且扫描重叠可能突破限速;
|
||||
4. 客户离线没有作为等待状态处理。
|
||||
|
||||
在上述 P0 修复并通过真实 PostgreSQL、Gateway、客户 ACK 链路测试前,不应把该后台任务认定为完整满足事故批量恢复方案。
|
||||
|
||||
## 12. 建议整改顺序
|
||||
|
||||
1. 先补 `processing` 租约恢复、扫描分布式锁和每应用速率令牌;
|
||||
2. 重构任务项等待/失败状态,让离线、外部 ACK、本任务 ACK 三类等待分开;
|
||||
3. 将 ACK 超时、拒绝、无效 ACK 纳入按应用安全阈值,并把清零时点改为有效 ACK;
|
||||
4. 修正预检状态口径,增加企业筛选,并用服务端令牌绑定预检与创建;
|
||||
5. 增加任务列表和任务项分页、中文状态及完整原因查询;
|
||||
6. 补齐 DRQ-001~DRQ-012 自动化,并使用真实 PostgreSQL、Redis、Gateway 与本地客户连接完成专项验收。
|
||||
|
||||
整改应作为独立需求进行,不在未经授权的情况下直接修改或发布现有批量重投逻辑。
|
||||
Reference in New Issue
Block a user