docs: add task management system design document

包含:
- 整体架构设计
- 数据库 ER 图和表结构
- 移动端 API 和 Dubbo 内部 RPC 定义
- 业务流程(每日任务、引导任务、展示收益)
- Worker 设计(Advisory Lock 每日重置 + 收益自动发放)
- activity-admin 页面规划
- 项目目录结构

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zheng020 2026-04-08 18:04:16 +08:00
parent 6f51cead6c
commit 770ca0e512

View File

@ -0,0 +1,732 @@
# 任务管理系统设计文档
> **创建日期:** 2026-04-08
> **项目:** TopFans 任务管理系统
> **服务:** activity-admin (Python FastAPI) + taskService (Go Dubbo-go) + 共享 PostgreSQL
---
## 一、整体架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 移动端 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 每日任务页面 │ │ 引导任务页面 │ │ 展示收益/奖励领取 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
└─────────┼─────────────────┼─────────────────────┼───────────────┘
│ │ │
│ Triple/HTTP │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ taskService (新服务,端口 20005) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Mobile API Handler (HTTP/Triple) │ │
│ │ /api/tasks/daily/* /api/tasks/guide/* /api/tasks/* │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ DailyTaskService│ │OnboardingService│ │ RevenueService │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ ┌────────▼─────────────────────▼─────────────────────▼────────┐ │
│ │ Repository Layer (GORM) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────▼─────────────┐ │
│ │ DailyResetWorker │ │
│ │ + RevenueAutoClaimWorker │ │
│ │ (PostgreSQL Advisory │ │
│ │ Lock05:00 Asia/Shanghai)│ │
│ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ ▲ ▲
│ Triple RPC │ │
▼ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ galleryService │ │ userService │ │ activity-admin │
│ (OnExhibition │ │ (UpdateCrystal │ │ (任务定义管理) │
│ Completed RPC)│ │ Balance + │ │ 直接操作DB │
│ │ │ AddExperience)│ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ ▲ ▲
└────────────────────▼────────────────────┘
PostgreSQL
```
---
## 二、设计决策汇总
### 2.1 服务职责划分
| 服务 | 职责 |
|------|------|
| activity-admin (Python) | 任务定义管理、进度查询、统计、手动重置 |
| taskService (Go) | 移动端 API、内部 RPC、奖励发放、定时任务 |
| PostgreSQL | 共用数据库 |
### 2.2 核心设计决策
| 决策项 | 选择 |
|--------|------|
| 移动端 API 提供方 | 新建独立 taskService 微服务 |
| 引导完成上报 | 前端每步完成立即调用 `/api/tasks/guide/complete` |
| 每日任务重置 | PostgreSQL Advisory Lock每天 05:00 Asia/Shanghai 执行 |
| 引导阶段推进 | 前端主动调用 `/api/tasks/onboarding/advance-stage` |
| 每日任务奖励 | 手动领取(单个或一键领取) |
| 引导任务奖励 | 阶段切换时统一发放 |
| 展示收益发放 | 统一到次日 05:00 自动发放,支持手动领取 |
| 展示收益计算 | 由调用方galleryService计算后传入 |
| 每日任务 scope | 每个 (user_id, star_id) 各自独立 |
| InitUserTasks | 只打新用户标记,不预创建任务进度 |
| 重置日志 | 记录到 task_reset_log 表 |
| 旧字段处理 | is_first_login_bonus_claimed、has_friend_display_bonus 标记废弃 |
| 奖励发放失败 | 记录日志 + 本地重试 N 次(可配置),仍失败标记 failed |
| 重置时区判断 | 按自然天,每天 05:00 执行一次 |
| 重置范围 | 全局所有用户所有 star_id |
| 引导上报 key | 动态配置,数据库存什么 key前端就上报什么 key |
| 阶段切换检查 | 后端检查当前阶段所有任务是否已完成,未完成拒绝切换 |
| 引导任务 | 不允许重复完成,不重置 |
| claimed 状态 | 每日重置时也重置回 pending |
| 收益自动发放 | 分批发放,每条记录结果保存,失败重试 N 次(可配置) |
| 统计页面 | 多指标完成率、领取率等、多维度star_id、时间范围 |
| Worker 设计 | 共用一个 Worker05:00 先重置再发收益 |
| 日志监控 | 接入现有日志和监控体系 |
| 健康检查 | 不实现,复用 Dubbo-go 框架自带能力 |
| 错误码 | 复用现有 BaseResponse.code 体系 |
| 分页规范 | page + page_size + total |
| 引导状态返回 | POST `/api/tasks/guide/complete` 返回当前引导完整状态 |
| 锁 key 设计 | pg_advisory_lock(YYYYMMDD) |
| taskService 端口 | 20005 |
| taskService 调用 userService | 直接调用UpdateCrystalBalance + AddExperience |
| activity-admin | 直接操作数据库,不通过 taskService API |
---
## 三、数据库设计
### 3.1 ER 图
```
┌─────────────────────────┐ ┌─────────────────────────────┐
│ task_definitions │ │ onboarding_stage_config │
├─────────────────────────┤ ├─────────────────────────────┤
│ id (PK) │ │ id (PK) │
│ star_id │ │ stage │
│ task_key │────▶│ required_task_keys (TEXT[]) │
│ task_type │ │ crystal_reward │
│ name │ │ exp_reward │
│ description │ │ ... │
│ crystal_reward │ └─────────────────────────────┘
│ exp_reward │ ▲
│ ... │ │
└─────────────────────────┘ │
│ │
│ 1:N 1:N │
▼ │
┌─────────────────────────┐ ┌─────────────────────────────┐
│ user_daily_task_progress│ │ user_onboarding_progress │
├─────────────────────────┤ ├─────────────────────────────┤
│ id (PK) │ │ id (PK) │
│ user_id │ │ user_id │
│ star_id │ │ task_key ───────────────────┘
│ task_key │◀────│
│ status │ │ ┌─────────────────────────┐
│ completed_at │ │ │user_onboarding_status │
│ claimed_at │ │ ├─────────────────────────┤
│ ... │ │ │ user_id (PK) │
└─────────────────────────┘ │ │ current_stage │
│ │ status │
│ │ is_first_login_bonus_ │
│ │ claimed (废弃) │
│ │ has_friend_display_bonus│
│ │ (废弃) │
│ └─────────────────────────┘
┌─────────────────────────────┐ │
│ exhibition_revenue_records │ │
├─────────────────────────────┤ │
│ id (PK) │ │
│ user_id │ │
│ star_id │ │
│ exhibition_id │ │
│ asset_id │ │
│ slot_id │ │
│ slot_owner_uid │ │
│ slot_type ('own'/'friend') │ │
│ crystal_amount │ │
│ cycle_start_time │ │
│ cycle_end_time │ │
│ status ('claimable'/'...') │ │
│ claimed_at │ │
│ created_at │ │
└─────────────────────────────┘
┌─────────────────────────────┐
│ task_reset_log │
├─────────────────────────────┤
│ id (PK) │
│ reset_type │
│ last_reset_at │
│ created_at │
└─────────────────────────────┘
```
### 3.2 表结构
#### task_definitions任务定义表
```sql
CREATE TABLE task_definitions (
id BIGSERIAL PRIMARY KEY,
star_id BIGINT, -- NULL=全局默认
task_key VARCHAR(50) NOT NULL,
task_type VARCHAR(20) NOT NULL, -- 'daily' | 'onboarding'
name VARCHAR(100) NOT NULL,
description TEXT,
crystal_reward BIGINT DEFAULT 0,
exp_reward BIGINT DEFAULT 0,
sort_order INT DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at BIGINT,
updated_at BIGINT
);
CREATE UNIQUE INDEX ix_task_def_star_key ON task_definitions(star_id, task_key);
```
#### user_daily_task_progress每日任务进度表
```sql
CREATE TABLE user_daily_task_progress (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
star_id BIGINT NOT NULL,
task_key VARCHAR(50) NOT NULL,
status VARCHAR(20) DEFAULT 'pending', -- pending/completed/claimed
completed_at BIGINT,
claimed_at BIGINT,
created_at BIGINT,
updated_at BIGINT
);
CREATE UNIQUE INDEX ix_daily_progress_user_star_key
ON user_daily_task_progress(user_id, star_id, task_key);
```
#### user_onboarding_progress引导任务进度表
```sql
CREATE TABLE user_onboarding_progress (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
task_key VARCHAR(50) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
completed_at BIGINT,
claimed_at BIGINT,
created_at BIGINT,
updated_at BIGINT
);
CREATE UNIQUE INDEX ix_onboard_progress_user_key
ON user_onboarding_progress(user_id, task_key);
```
#### user_onboarding_status引导流程状态表
```sql
CREATE TABLE user_onboarding_status (
user_id BIGINT PRIMARY KEY,
current_stage INT DEFAULT 0, -- 0=未开始1~N=阶段
status VARCHAR(20) DEFAULT 'pending',
-- 以下字段标记废弃,但仍保留
is_first_login_bonus_claimed BOOLEAN DEFAULT false,
has_friend_display_bonus BOOLEAN DEFAULT false,
completed_at BIGINT,
claimed_at BIGINT,
created_at BIGINT,
updated_at BIGINT
);
```
#### onboarding_stage_config引导阶段配置表
```sql
CREATE TABLE onboarding_stage_config (
id BIGSERIAL PRIMARY KEY,
stage INT NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
required_task_keys TEXT[], -- PostgreSQL 数组类型
crystal_reward BIGINT DEFAULT 0,
exp_reward BIGINT DEFAULT 0,
sort_order INT DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at BIGINT,
updated_at BIGINT
);
```
#### exhibition_revenue_records展示收益记录表
```sql
CREATE TABLE exhibition_revenue_records (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
star_id BIGINT NOT NULL,
exhibition_id BIGINT NOT NULL,
asset_id BIGINT NOT NULL,
slot_id BIGINT NOT NULL,
slot_owner_uid BIGINT NOT NULL,
slot_type VARCHAR(20) NOT NULL, -- 'own' | 'friend'
crystal_amount BIGINT NOT NULL,
cycle_start_time BIGINT NOT NULL,
cycle_end_time BIGINT NOT NULL,
status VARCHAR(20) DEFAULT 'claimable',
claimed_at BIGINT,
created_at BIGINT
);
CREATE INDEX ix_revenue_user_star_status
ON exhibition_revenue_records(user_id, star_id, status);
CREATE INDEX ix_revenue_star_status
ON exhibition_revenue_records(star_id, status);
```
#### task_reset_log重置日志表
```sql
CREATE TABLE task_reset_log (
id BIGSERIAL PRIMARY KEY,
reset_type VARCHAR(20) NOT NULL, -- 'daily'
last_reset_at BIGINT NOT NULL,
created_at BIGINT
);
```
---
## 四、API 设计
### 4.1 移动端 APItaskService HTTP/Triple
#### 每日任务
| 方法 | 路径 | 请求 | 说明 |
|------|------|------|------|
| GET | `/api/tasks/daily` | `star_id` | 获取每日任务列表及进度 |
| POST | `/api/tasks/daily/claim` | `{ task_key, star_id }` | 领取单个任务奖励 |
| POST | `/api/tasks/daily/claim-all` | `star_id` | 一键领取所有已完成任务奖励 |
**GET /api/tasks/daily 响应:**
```json
{
"tasks": [
{
"task_key": "daily_login",
"name": "每日首次登录",
"description": "每日首次登录 App",
"crystal_reward": 20,
"exp_reward": 20,
"status": "pending",
"can_claim": false
}
]
}
```
#### 引导任务
| 方法 | 路径 | 请求 | 说明 |
|------|------|------|------|
| POST | `/api/tasks/guide/complete` | `{ task_key }` | 上报引导步骤完成 |
| GET | `/api/tasks/onboarding/status` | - | 获取引导状态 |
| POST | `/api/tasks/onboarding/advance-stage` | `{ target_stage }` | 切换阶段 |
| POST | `/api/tasks/onboarding/claim-reward` | `{ stage }` | 领取当前阶段奖励 |
**POST /api/tasks/guide/complete 响应:**
```json
{
"user_id": 123,
"current_stage": 1,
"status": "in_progress",
"stages": [
{
"stage": 1,
"name": "入门引导",
"required_task_keys": ["square_home", "profile_edit"],
"crystal_reward": 0,
"exp_reward": 0
}
]
}
```
**GET /api/tasks/onboarding/status 响应:**
```json
{
"user_id": 123,
"current_stage": 1,
"status": "in_progress",
"stages": [
{
"stage": 1,
"name": "入门引导",
"required_task_keys": ["square_home", "profile_edit"],
"crystal_reward": 0,
"exp_reward": 0
}
]
}
```
#### 展示收益
| 方法 | 路径 | 请求 | 说明 |
|------|------|------|------|
| GET | `/api/tasks/exhibition-revenue` | `star_id`, `status`, `page`, `page_size` | 获取收益记录列表 |
| POST | `/api/tasks/exhibition-revenue/claim` | `{ revenue_id, star_id }` | 领取单个收益 |
| POST | `/api/tasks/exhibition-revenue/claim-all` | `star_id` | 一键领取所有可领取收益 |
**GET /api/tasks/exhibition-revenue 响应:**
```json
{
"items": [
{
"id": 123,
"exhibition_id": 456,
"asset_id": 789,
"slot_id": 1,
"slot_type": "own",
"crystal_amount": 100,
"cycle_start_time": 1712607600000,
"cycle_end_time": 1712694000000,
"status": "claimable",
"can_claim": true
}
],
"total": 10,
"page": 1,
"page_size": 20
}
```
### 4.2 Dubbo 内部 RPC
#### taskService 作为服务端(被调用)
**TaskInternalService**
```protobuf
service TaskInternalService {
// 用户注册时调用,只打新用户标记
rpc InitUserTasks(InitUserTasksRequest) returns (InitUserTasksResponse);
// 任务事件上报
rpc ReportTaskEvent(ReportTaskEventRequest) returns (ReportTaskEventResponse);
// 展品到期完成galleryService 调用)
rpc OnExhibitionCompleted(OnExhibitionCompletedRequest) returns (OnExhibitionCompletedResponse);
}
```
**消息定义:**
```protobuf
message InitUserTasksRequest {
int64 user_id = 1;
}
message InitUserTasksResponse {
topfans.common.BaseResponse base = 1;
bool success = 2;
}
message ReportTaskEventRequest {
int64 user_id = 1;
int64 star_id = 2;
string event_type = 3;
int64 timestamp = 4;
}
message ReportTaskEventResponse {
topfans.common.BaseResponse base = 1;
bool task_completed = 2;
string task_key = 3;
}
message OnExhibitionCompletedRequest {
int64 exhibition_id = 1;
int64 asset_id = 2;
int64 slot_id = 3;
int64 occupier_uid = 4;
int64 occupier_star_id = 5;
int64 slot_owner_uid = 6;
int64 crystal_amount = 7;
int64 start_time = 8;
int64 expire_at = 9;
}
message OnExhibitionCompletedResponse {
topfans.common.BaseResponse base = 1;
int64 revenue_record_id = 2;
}
```
#### taskService 作为客户端(调用 userService
**userService 新增 RPC**
```protobuf
// 更新经验值请求内部RPC调用用于taskService发放经验奖励
message AddExperienceRequest {
int64 user_id = 1;
int64 star_id = 2;
int64 delta = 3;
}
message AddExperienceResponse {
topfans.common.BaseResponse base = 1;
int64 new_experience = 2;
}
// 在 service UserSocialService 中添加:
rpc AddExperience(AddExperienceRequest) returns (AddExperienceResponse);
```
---
## 五、业务流程
### 5.1 每日任务流程
```
1. 用户登录 → 前端调用 GET /api/tasks/daily?star_id=xxx
2. 后端返回该用户的每日任务列表(含 status
3. 用户完成行为(如浏览展品)→ 前端调用 ReportTaskEvent
4. 后端自动标记任务完成status=completed
5. 用户点击"领取" → POST /api/tasks/daily/claim
6. 后端验证任务已完成 → 调用 userService.UpdateCrystalBalance → 调用 AddExperience → 更新状态为 claimed
7. 每日 05:00 Asia/Shanghai
a. Advisory Lock 获取锁pg_advisory_lock(YYYYMMDD)
b. 查询 task_reset_log如今天已有记录则跳过
c. 所有非 pending 状态的记录恢复为 pending
d. 记录 task_reset_log
e. 释放锁
```
### 5.2 引导任务流程
```
1. 用户首次登录 → 前端调用 GET /api/tasks/onboarding/status
2. 返回 { current_stage: 0, status: 'pending', stages: [...] }
3. 前端根据 stage 配置显示引导步骤
4. 用户完成引导步骤(如 square_home
5. → POST /api/tasks/guide/complete { task_key: "square_home" }
6. 后端更新 user_onboarding_progress.status = 'completed'
7. 前端引导流程中,用户点击"进入下一阶段"
8. → POST /api/tasks/onboarding/advance-stage { target_stage: 2 }
9. 后端检查 stage 1 所有 task_key 是否都已 completed
- 如未全部完成,返回错误
- 如全部完成current_stage++,返回最新状态
10. 前端调用 POST /api/tasks/onboarding/claim-reward 领取阶段奖励
11. 重复 3-10直到所有引导完成
```
### 5.3 展示收益流程
```
1. galleryService 的 CleanupWorker 扫描到过期展品
2. → 调用 taskService.OnExhibitionCompleted RPC
3. taskService 创建 exhibition_revenue_recordsstatus=claimable
4. 每日 05:00紧接重置后
a. 查询所有 status=claimable 的记录
b. 分批处理(每批 N 条,可配置)
c. 调用 userService.UpdateCrystalBalance 发放水晶
d. 成功则更新 status=claimed失败则重试 N 次
e. 仍失败则标记 status=failed
5. 用户打开移动端 → GET /api/tasks/exhibition-revenue
6. 用户点击领取 → POST /api/tasks/exhibition-revenue/claim
7. 后端调用 userService.UpdateCrystalBalance → 更新状态
```
---
## 六、分页规范
所有列表查询统一分页参数:
| 参数 | 类型 | 说明 |
|------|------|------|
| page | int | 页码,从 1 开始 |
| page_size | int | 每页条数,最大 100 |
响应结构:
```json
{
"items": [...],
"total": 100,
"page": 1,
"page_size": 20
}
```
---
## 七、Worker 设计
### 7.1 DailyResetWorker + RevenueAutoClaimWorker
**共用一个 Worker每天 05:00 Asia/Shanghai 执行。**
```go
// 伪代码
func (w *Worker) run() {
// 尝试获取锁日期作为锁ID
lockID := time.Now().Format("20060102") // 如 "20230409"
if !w.acquireAdvisoryLock(lockID) {
return // 其他实例在执行
}
defer w.releaseAdvisoryLock(lockID)
// 1. 执行每日任务重置
w.resetDailyTasks()
// 2. 执行展示收益自动发放
w.autoClaimExhibitionRevenue()
}
func (w *Worker) resetDailyTasks() {
// 查询 task_reset_log如今天已有记录则跳过
// 更新所有 status != 'pending' 的记录为 'pending'
// 记录 task_reset_log
}
func (w *Worker) autoClaimExhibitionRevenue() {
// 查询所有 status='claimable' 的记录
// 分批处理batch_size 可配置)
// 每批:调用 userService.UpdateCrystalBalance
// - 成功:更新 status='claimed'
// - 失败:重试 N 次(可配置),仍失败标记 status='failed'
// 每条记录处理结果保存到日志
}
```
### 7.2 Advisory Lock 设计
- **锁 key** 日期格式 `YYYYMMDD`(如 `20230409`
- **获取锁:** `pg_try_advisory_lock(lockID)`
- **释放锁:** `pg_advisory_unlock(lockID)`
- **作用:** 保证多实例环境下每天只执行一次重置和自动发放
---
## 八、activity-admin 页面
| 页面 | 路由 | 功能 |
|------|------|------|
| 任务定义列表 | `/tasks/definitions` | 查看、新建、编辑、删除、启用/禁用任务定义 |
| 每日任务进度 | `/tasks/daily-progress` | 查询各用户的每日任务完成情况 |
| 引导任务进度 | `/tasks/onboarding-progress` | 查询各用户的引导任务完成情况 |
| 任务统计 | `/tasks/stats` | 任务完成率/领取率统计图表,支持 star_id、时间范围筛选 |
| 手动重置 | `/tasks/reset` | 手动触发每日任务重置 |
| 收益记录 | `/tasks/revenue` | 查看展示收益记录 |
**统计页面指标:**
- 每日任务:完成人数、领取人数、完成率、领取率
- 引导任务:各 stage 完成人数、总体完成率
- 收益统计:总发放水晶数、待领取水晶数、已领取水晶数
**筛选维度:**
- star_id
- 时间范围近7天、近30天、自定义
- 任务类型daily/onboarding
---
## 九、项目结构
### 9.1 目录结构
```
backend/
├── proto/
│ ├── user.proto # 修改:新增 AddExperience RPC
│ └── task.proto # 新增TaskInternalService
├── services/
│ ├── taskService/ # 新增
│ │ ├── main.go
│ │ ├── config/
│ │ │ └── task_config.go
│ │ ├── repository/
│ │ │ └── task_repository.go
│ │ ├── service/
│ │ │ ├── daily_task_service.go
│ │ │ ├── onboarding_service.go
│ │ │ └── exhibition_revenue_service.go
│ │ ├── provider/
│ │ │ ├── task_mobile_provider.go # 移动端 API
│ │ │ └── task_internal_provider.go # 内部 RPC
│ │ ├── worker/
│ │ │ └── daily_reset_worker.go
│ │ └── client/
│ │ └── user_rpc_client.go
│ │
│ └── userService/ # 修改
│ ├── repository/
│ │ └── fan_profile_repository.go # 新增 UpdateExperience
│ ├── service/
│ │ └── user_service.go # 新增 AddExperience
│ └── provider/
│ ├── user_provider.go # 新增 AddExperience
│ └── unified_provider.go # 新增 AddExperience
├── pkg/
│ └── proto/
│ ├── user/
│ │ └── *.go # 重新生成
│ └── task/
│ └── *.go # 新生成
└── scripts/
└── v001_init_task_tables.sql
```
### 9.2 前端文件activity-admin
```
frontend/src/
├── api/
│ └── admin.js # 修改:新增任务相关 API
├── views/
│ ├── TaskDefinitionList.vue
│ ├── TaskProgress.vue
│ ├── TaskStats.vue
│ ├── TaskReset.vue
│ └── RevenueList.vue
└── router/
└── index.js # 修改:新增路由
```
---
## 十、后续迭代项
以下内容在当前设计中预留扩展点,后续可根据业务需求添加:
1. **引导任务细分** - `square_home` 等 key 是否需要与 star_id 关联
2. **任务事件扩展** - ReportTaskEvent 的 event_type 可继续扩展
3. **收益周期细分** - 当前 cycle_start/end_time 为基础字段,可扩展 cycle_name 等
4. **统计图表扩展** - 当前预留 ECharts 位置,可扩展更多图表类型
5. **手动重置扩展** - 当前支持全局重置,可扩展按 star_id 重置
---
## 十一、错误码规范
复用现有 `BaseResponse.code` 体系,不新增自定义业务错误码。
| 场景 | 处理方式 |
|------|----------|
| 领取未完成任务 | 静默返回成功,前端根据 status 阻止 |
| 领取已领取任务 | 静默返回成功,前端根据 status 阻止 |
| 阶段切换但任务未完成 | 返回错误,前端提示 |
| RPC 超时 | 记录日志,不自动重试(可手动补发) |
| 重置时其他实例在执行 | 跳过本次执行 |