topfans/backend/docs/AI-Chat-Service/status_text标签系统设计方案.md

11 KiB
Raw Blame History

status_text 标签系统设计方案

1. 概述

1.1 需求背景

用户在我的点赞作品页面需要看到每个作品的动态状态标签(如"屠榜顶流"、"火速破圈"等),用于直观展示作品的热度和表现。

1.2 实现方案

采用 后端计算返回 方案:由后端计算每个作品的 status_text 状态标签,前端直接展示。

1.3 优势

  • 后端拥有完整的排行榜数据和用户行为数据,可准确计算
  • 前端无需关心业务逻辑复杂度,保持轻量
  • 状态计算逻辑集中,便于维护和修改
  • 减少前后端字段依赖,减少数据冗余

2. 标签体系定义

2.1 标签分类

优先级 类型 说明
T0 收益型 最高优先级,用户点赞后作品表现极佳
T1 排名型 排行榜相关,作品在榜上表现优秀
T3 状态型 涨粉速度,体现作品热度变化
T4 状态型 涨粉速度,体现作品热度变化

2.2 标签详情

优先级 标签名 显示条件 背后逻辑
T0 眼光拉满 用户点赞后新增点赞≥50 且作品仍在展出 用户点赞后作品持续火热,用户收益已达峰值
T1 屠榜顶流TopX 排行榜排名为 1、2、3、4、5 作品稳居排行榜前五,顶级流量
T1 第Y爆款 排行榜排名为 Y10≥Y>5 作品进入排行榜前10但未进前5
T1 排名破Z 排行榜排名达到 ZZ∈{20,50,100,200} 里程碑式突破,达到特定门槛
T3 火速破圈 过去1小时新增点赞≥20 作品热度急剧上升中
T4 小爆出圈 过去1小时新增点赞20>新增点赞≥10 作品热度稳步上升
T4 热度积累 过去1小时新增点赞10>新增点赞≥5 作品热度温和增长
T4 缓慢涨粉 过去1小时新增点赞5>新增点赞≥0 作品热度缓慢增长
- 潜力待挖 无任何标签满足 默认状态,等待挖掘

2.3 优先级规则

当多个标签条件同时满足时按优先级取最高级别T0>T1>T3>T4

计算流程:

1. 检查 T0「眼光拉满」→ 满足则返回
2. 检查 T1 排名型(屠榜顶流/第Y爆款/排名破Z→ 满足则返回
3. 检查 T3/T4 状态型(火速破圈/小爆出圈/热度积累/缓慢涨粉)→ 满足则返回
4. 默认返回「潜力待挖」

3. 后端接口设计

3.1 修改接口

接口 方法 说明
/api/v1/me/liked-assets GET 获取我点赞的作品列表

3.2 响应新增字段

GetMyLikedAssets 接口的响应 items 数组元素中新增:

{
  "asset_id": 12345,
  "name": "作品名称",
  "cover_url": "https://...",
  "like_count": 1000,
  "liked_at": "2026-05-26T10:00:00Z",
  "earnings": 500.00,
  "hourly_earnings": 10.00,
  "is_lenticular": false,
  "expire_at": "2026-05-27T10:00:00Z",
  "status_text": "屠榜顶流Top3"
}
字段 类型 说明
status_text string 动态状态标签,默认「潜力待挖」

4. 后端实现设计

4.1 数据依赖

字段 来源 说明
用户点赞时间 liked_assets 表 用于计算用户点赞后新增点赞数
用户点赞后新增点赞数 likes 表聚合 用户点赞时刻起到当前时刻,作品累计新增点赞数
排行榜排名 ranking 或 likes 表 当前作品排名
过去1小时新增点赞 likes 表聚合 需要按时间窗口聚合

4.2 Service 层计算逻辑

// pkg/service/social_service.go

func (s *SocialService) GetMyLikedAssets(ctx context.Context, req *pbSocial.GetMyLikedAssetsRequest) (*pbSocial.GetMyLikedAssetsResponse, error) {
    // 1. 获取用户点赞作品列表JOIN exhibition 获取 start_time避免 N+1
    items, total, hasMore := s.getLikedAssetsList(ctx, userID, page, pageSize)
    assetIDs := extractAssetIDs(items)

    // 2. 批量获取用户点赞时间
    likedAtMap := s.batchGetUserLikedAtMap(ctx, userID, assetIDs)

    // 3. 批量获取作品排名(排名数据变化不频繁,可加缓存)
    rankMap := s.batchGetAssetRanks(ctx, assetIDs)

    // 4. 批量获取过去1小时新增点赞
    hourlyNewLikesMap := s.batchGetHourlyNewLikes(ctx, assetIDs)

    // 5. 为每个作品计算 status_text
    for _, item := range items {
        item.UserLikedAt = likedAtMap[item.AssetId]
        item.Rank = rankMap[item.AssetId]
        item.HourlyNewLikes = hourlyNewLikesMap[item.AssetId]
        item.StatusText = computeStatusText(item)
    }

    return resp, nil
}

性能优化说明:

优化项 优化前 优化后 收益
exhibition 查询 循环内逐条查询N 次) JOIN 一次获取 30 次查询 → 1 次
排名查询 - 批量 IN 查询 1 次
小时级新增点赞 - 批量聚合查询 1 次

关键索引建议:

  • likes.asset_id + likes.created_at(联合索引,时间窗口聚合)
  • likes.asset_id + likes.created_at + user_id(用户点赞后新增统计)
  • exhibitions.asset_id + exhibitions.deleted_atJOIN 优化)

4.3 status_text 计算函数

func computeStatusText(item *LikedAssetItem) string {
    // T0: 眼光拉满
    if item.UserLikedCountAfter >= 50 && !item.IsExpired {
        return "眼光拉满"
    }

    // T1: 排名型
    switch {
    case item.Rank >= 1 && item.Rank <= 5:
        return fmt.Sprintf("屠榜顶流Top%d", item.Rank)
    case item.Rank > 5 && item.Rank <= 10:
        return fmt.Sprintf("第%d爆款", item.Rank)
    case item.Rank == 20 || item.Rank == 50 || item.Rank == 100 || item.Rank == 200:
        return fmt.Sprintf("排名破%d", item.Rank)
    }

    // T3/T4: 状态型
    switch {
    case item.HourlyNewLikes >= 20:
        return "火速破圈"
    case item.HourlyNewLikes >= 10:
        return "小爆出圈"
    case item.HourlyNewLikes >= 5:
        return "热度积累"
    case item.HourlyNewLikes >= 0:
        return "缓慢涨粉"
    default:
        return "潜力待挖"
    }
}

4.4 新增字段结构

// proto/social.proto

message LikedAssetItem {
    int64 asset_id = 1;
    string name = 2;
    string cover_url = 3;
    int32 like_count = 4;
    int64 liked_at = 5;
    double earnings = 6;
    double hourly_earnings = 7;
    bool is_lenticular = 8;
    int64 expire_at = 9;
    // 新增字段
    string status_text = 10;
}

5. 前端修改设计

5.1 修改文件

frontend/pages/profile/myWorks.vue

5.2 修改点

修改前 (line 919):

status_text: index < 3 ? '排名进榜' : '潜力待挖',

修改后:

status_text: item.status_text || '潜力待挖',

5.3 完整修改的代码块

if (res.data && res.data.items) {
    likedWorks.value = res.data.items.map((item, index) => ({
        id: item.asset_id,
        cover_url: item.cover_url,
        like_count: item.like_count,
        earnings: item.earnings,
        liked_at: item.liked_at,
        expire_at: item.expire_at,
        name: item.name,
        is_lenticular: item.is_lenticular ?? false,
        status_text: item.status_text || '潜力待挖',  // 直接使用后端返回
        score: item.like_count,
        reward: Math.floor(item.earnings || 0),
    }));
    // ...
}

6. 测试用例

6.1 标签测试用例

用例编号 前提条件 输入 预期输出
TC-01 用户点赞后作品新增≥50点赞 status_text 眼光拉满
TC-02 作品排名第1 status_text 屠榜顶流Top1
TC-03 作品排名第3 status_text 屠榜顶流Top3
TC-04 作品排名第7 status_text 第7爆款
TC-05 作品排名第20 status_text 排名破20
TC-06 过去1小时新增点赞=25 status_text 火速破圈
TC-07 过去1小时新增点赞=15 status_text 小爆出圈
TC-08 过去1小时新增点赞=7 status_text 热度积累
TC-09 过去1小时新增点赞=2 status_text 缓慢涨粉
TC-10 无任何标签满足 status_text 潜力待挖

6.2 优先级测试用例

用例编号 前提条件 预期输出 说明
TC-11 排名第2 且 过去1小时新增点赞=25 眼光拉满 T0 > T1
TC-12 排名第5 且 过去1小时新增点赞=30 屠榜顶流Top5 T1 > T3
TC-13 排名第15 且 过去1小时新增点赞=22 火速破圈 无T0/T1满足

7. 里程碑

阶段 任务 负责人 状态
1 后端 proto/social.proto 新增 status_text 字段 后端 -
2 后端 Service 层实现 computeStatusText 逻辑 后端 -
3 后端修改 GetMyLikedAssets 接口返回 status_text 后端 -
4 前端 myWorks.vue 修改 status_text 取值逻辑 前端 -
5 联调测试 + 回归测试 前端+后端 -

8. 高并发性能评估

7.1 单请求成本

查询操作 次数 说明
点赞列表 + exhibition JOIN 1 批量获取 30 条
批量获取排名 1 IN 查询 30 条
批量获取小时级新增 1 IN + 时间聚合
批量获取点赞后新增 1 IN + 时间聚合
总计 4 次

computeStatusText 计算复杂度为 O(1),耗时 < 1μs可忽略。

7.2 并发容量估算

假设:单请求 DB 耗时 20msDB 连接池 500

单个连接处理能力1000ms / 20ms = 50,000 请求/秒/连接
500 连接 × 50 = 25,000 理论上限 QPS

10,000 并发用户 → 约 3,000-5,000 实际 QPS

7.3 必要配置

配置项 推荐值 说明
DB 连接池 300-500 根据 DB 服务器规格调整
排名缓存 TTL 60s 排名数据变化不频繁
索引 必建 参见 4.2 节索引建议

7.4 风险点

风险 缓解方案
点赞时频繁触发查询 点赞后可异步计算 status_text不阻塞主流程
DB 连接池耗尽 限流 + 连接池监控
慢查询 确保索引生效,定期 EXPLAIN 分析

9. 附录

8.1 标签文案汇总

标签名 字数
眼光拉满 4
屠榜顶流Top1~5 6~7
第6~10爆款 4~5
排名破20/50/100/200 5~6
火速破圈 4
小爆出圈 4
热度积累 4
缓慢涨粉 4
潜力待挖 4

8.2 相关文件

文件路径 说明
backend/proto/social.proto Protobuf 定义
backend/pkg/service/social_service.go Service 层实现
backend/gateway/controller/social_controller.go Controller 层(返回格式)
frontend/pages/profile/myWorks.vue 前端页面