包含: - 整体架构设计 - 数据库 ER 图和表结构 - 移动端 API 和 Dubbo 内部 RPC 定义 - 业务流程(每日任务、引导任务、展示收益) - Worker 设计(Advisory Lock 每日重置 + 收益自动发放) - activity-admin 页面规划 - 项目目录结构 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
733 lines
28 KiB
Markdown
733 lines
28 KiB
Markdown
# 任务管理系统设计文档
|
||
|
||
> **创建日期:** 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 │ │
|
||
│ │ Lock,05: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 设计 | 共用一个 Worker,05: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 移动端 API(taskService 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_records(status=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 超时 | 记录日志,不自动重试(可手动补发) |
|
||
| 重置时其他实例在执行 | 跳过本次执行 |
|