topfans/backend/docs/资产服务设计文档.md
2026-04-07 22:29:48 +08:00

51 KiB
Raw Blame History

资产服务Asset Service设计文档

📊 文档状态总结

已完成

  • 数据库模型设计(pkg/models/asset.go 已实现)
  • 核心功能设计确认Q1-Q17, Q23-Q25 已确认)
  • 数据库表结构设计assets、mint_orders、asset_likes 表结构已确定)
  • 服务职责边界定义
  • 配置设计(参考 socialService 的配置模式)

⚠️ 需要补充和确认

🔴 高优先级(阻塞开发)

  1. User Service 接口补充

    • UpdateCrystalBalance RPC 接口(需要在 proto/user.proto 中添加)
    • ⚠️ UpdateAssetsCount RPC 接口Repository 层已实现,需要添加 RPC 接口)
  2. Asset Service Proto 定义

    • 需要创建 proto/asset.proto 文件
    • 定义资产服务的所有 RPC 接口
  3. Social Service 接口补充

    • 资产点赞相关的 RPC 接口(需要在 proto/social.proto 中添加)

🟡 中优先级(建议确认)

  1. 事件驱动机制 已确认:不使用消息队列

    • 不使用消息队列RocketMQ/Kafka/RabbitMQ直接通过 RPC 调用或数据库记录
    • ⚠️ 事件格式规范(如需事件记录,可在数据库中记录)
    • ⚠️ 事件重试机制(如需重试,可在应用层实现)
  2. 缓存策略 已确认:不使用 Redis 缓存

    • 不使用 Redis 缓存,直接查询数据库
    • 所有数据查询都直接访问数据库

🟢 低优先级(可后续优化)

  1. 错误处理国际化
    • ⚠️ 是否需要多语言支持

📝 待实现内容

1. 服务实现(未开始)

  • 创建 services/assetService/ 目录结构
  • 实现 Repository 层asset_repository.go、mint_order_repository.go、asset_like_repository.go
  • 实现 Service 层asset_service.go、mint_service.go
  • 实现 Provider 层asset_provider.go
  • 实现 RPC Clientuser_rpc_client.go

2. Gateway 集成(未开始)

  • 创建 gateway/controller/asset_controller.go
  • 创建 gateway/dto/asset_converter.go
  • gateway/router/router.go 中添加路由

3. 数据库迁移(未开始)

  • 创建表结构迁移脚本
  • 创建索引

4. 测试(未开始)

  • 单元测试
  • 集成测试
  • HTTP 完整测试流程

📋 快速参考

数据库表设计(基于提供设计 + 服务交互字段)

assets 表

  • 基础字段(来自提供设计):id, owner_uid, star_id, name, cover_url, status, tx_hash, block_number, like_count
  • 新增字段(需求确认):material_url(用户上传素材),description, rarity, tags, visibility(预留)
  • 新增字段(服务交互):created_at, updated_at, minted_at, deleted_at, is_active

mint_orders 表

  • 基础字段(来自提供设计):order_idUUID, user_id, asset_id, star_id, status, created_at
  • 新增字段(服务交互):updated_at, minted_at, cost_crystal, error_message, retry_count

服务间交互字段映射

交互服务 使用的字段 用途
User Service mint_orders.cost_crystal 扣除/退款水晶
User Service assets.owner_uid, assets.star_id 更新资产数量
Social Service assets.like_count 更新点赞数
Gallery Service - 暂不考虑,后续再实现
Task Service assets.created_at, assets.minted_at 事件发布

需要确认的关键问题

🔴 高优先级(必须确认):

  1. 铸造费用和支付方式Q1
  2. 区块链集成方式Q11, Q23
  3. User Service 接口可用性Q18

🟡 中优先级(建议确认): 4. 资产属性字段Q13 5. 订单表设计细节Q14

🟢 低优先级(可后续优化): 6. 其他功能细节Q5-Q10, Q15-Q17, Q19-Q30


一、服务概述

1.1 服务定位

Asset Service资产服务 是负责数字藏品NFT全生命周期管理的核心微服务包括

  • 数字藏品的铸造(链下记录 + 链上确权)
  • 资产查询与列表管理
  • 资产详情展示
  • 素材管理(平台素材库)
  • 图片上传与审核

1.2 服务职责边界

Asset Service 负责:

  • 资产的创建、查询、更新
  • 铸造订单的管理
  • 素材库管理
  • 图片上传处理
  • 资产上链状态跟踪

Asset Service 不负责:

  • 用户余额管理CoinBalance, CrystalBalance- 由 User Service 管理
  • 资产数量统计AssetsCount- 由 User Service 管理(通过 RPC 调用更新)
  • 资产展示(展馆)- 由 Gallery Service 管理
  • 资产点赞 - 由 Social Service 管理

二、核心功能设计

2.1 资产铸造流程

2.1.1 铸造流程概述

用户发起铸造请求
    ↓
1. 验证用户权限和余额
    ↓
2. 创建资产记录(链下)
    ↓
3. 创建铸造订单(异步)
    ↓
4. 调用区块链服务上链
    ↓
5. 更新资产状态和链上信息
    ↓
6. 更新用户资产数量(调用 User Service
    ↓
7. 发布事件asset.mint_complete

2.1.2 已确认

Q1: 铸造费用和支付方式

  • 铸造需要消耗水晶CrystalBalance
  • 费用后续同样会引入规则表进行定义,现在直接和 socialService 一致,统一定义在 config/asset_config.go 文件中(CostCrystal
  • 支付是在创建订单的时候扣除
  • 上链失败会进行退款

Q2: 铸造材料来源

  • 目前可以暂不考虑,随机生成一个图片的 URL 即可
  • 后续再进行拓展

Q3: 铸造限制

  • 次数限制同样和 Q1 一致,后续同样会引入规则表进行定义
  • 现在直接和 socialService 一致,统一定义在 config/asset_config.go 文件中(MaxMintCountPerDay, MaxMintCountPerUser

Q4: 异步处理机制

  • 上链为异步机制
  • 但是目前不引入上链功能,所以只需要做简单的模拟
  • 后续引入后再进行优化

2.2 资产查询功能

2.2.1 我的藏品列表

接口设计:

GET /api/v1/assets/me
Query Parameters:
  - page: 页码默认1
  - page_size: 每页数量默认20最大100
  - status: 状态筛选all, pending, minted, failed
  - keyword: 关键词搜索(资产名称)
  - sort: 排序方式created_at_desc, created_at_asc, name_asc

响应结构:

{
  "code": 200,
  "message": "ok",
  "data": {
    "items": [
      {
        "asset_id": 123,
        "name": "藏品名称",
        "cover_url": "https://...",
        "status": "minted",  // pending, minting, minted, failed
        "token_id": "0x123...",
        "tx_hash": "0xabc...",
        "created_at": 1704067200000,
        "minted_at": 1704067300000
      }
    ],
    "total": 100,
    "page": 1,
    "page_size": 20,
    "has_more": true
  }
}

2.2.2 资产详情

接口设计:

GET /api/v1/assets/{asset_id}

响应结构:

{
  "code": 200,
  "message": "ok",
  "data": {
    "asset_id": 123,
    "name": "藏品名称",
    "description": "藏品描述",
    "cover_url": "https://...",
    "owner": {
      "user_id": 1,
      "nickname": "张三"
    },
    "star_id": 1,
    "status": "minted",
    "token_id": "0x123...",
    "tx_hash": "0xabc...",
    "materials": [
      {
        "material_id": 1,
        "type": "platform",
        "url": "https://..."
      }
    ],
    "created_at": 1704067200000,
    "minted_at": 1704067300000,
    "like_count": 10,
    "is_liked": false  // 当前用户是否点赞
  }
}

2.2.3 已确认

Q5: 资产可见性

  • 用户不能看到别人的资产
  • 目前不需要考虑分享等功能
  • 但是在表格设计的时候可以留一个权限控制的字段接口出来(visibility 字段)

Q6: 资产属性

  • 名称name和封面图cover_url是目前设计的必须字段
  • 同样可以预留描述description、稀有度rarity、标签tags的字段接口以便后续功能添加

2.3 素材管理功能

2.3.1 平台素材列表

接口设计:

GET /api/v1/materials/platform
Query Parameters:
  - page: 页码
  - page_size: 每页数量
  - category: 分类筛选(可选)

响应结构:

{
  "code": 200,
  "message": "ok",
  "data": {
    "items": [
      {
        "material_id": 1,
        "name": "素材名称",
        "url": "https://...",
        "category": "背景",
        "star_id": 1
      }
    ],
    "total": 50,
    "page": 1,
    "page_size": 20
  }
}

2.3.2 已确认

Q7: 素材分类和管理

  • 素材需要根据 star_id 进行隔离

Q8: 用户上传素材

  • 用户可以上传自己的素材
  • 目前暂不需要审核功能
  • 不需要 materials 表,直接在 assets 表里面加入一个 material_url 字段代表素材的 URL 即可
  • 后续会定期清理这个字段

2.4 图片上传功能

2.4.1 上传接口设计

接口设计:

POST /api/v1/assets/upload
Content-Type: multipart/form-data

Form Data:
  - file: 图片文件
  - type: 上传类型cover, material

响应结构:

{
  "code": 200,
  "message": "ok",
  "data": {
    "url": "https://minio.example.com/bucket/path/to/image.jpg",
    "upload_id": "upload_123"
  }
}

2.4.2 已确认

Q9: 图片存储和审核

  • 暂不考虑图片存储的细节问题,存一个 URL 即可

Q10: 上传任务管理

  • 不需要上传任务表
  • 直接阻塞上传,上传成功返回后才会允许前端用户进行下一步操作

2.5 上链状态查询

2.5.1 状态查询接口

接口设计:

GET /api/v1/assets/{asset_id}/status

响应结构:

{
  "code": 200,
  "message": "ok",
  "data": {
    "asset_id": 123,
    "status": "minting",  // pending, minting, minted, failed
    "tx_hash": "0xabc...",
    "token_id": "0x123...",
    "block_number": 12345,
    "minted_at": 1704067300000,
    "error_message": ""  // 如果失败,错误信息
  }
}

2.5.2 已确认

Q11: 区块链集成

  • 区块链暂不考虑,后续才会引入相应的函数功能
  • 目前只需要模拟已经上链成功结束即可

Q12: 上链信息存储

  • 只需要存储 tx_hashblock_number 即可
  • 不需要 token_id

三、数据库设计

3.1 核心表结构

3.1.1 assets 表(资产表)

基于提供的数据库设计,完善后的表结构:

CREATE TABLE assets (
    -- 主键和基础字段
    id BIGSERIAL PRIMARY KEY,                    -- asset_id (Asset ID)
    owner_uid BIGINT NOT NULL,                  -- 当前持有者 (Current Owner)
    star_id BIGINT NOT NULL,                     -- 所属星球 (Belonging Planet)
    name VARCHAR(100) NOT NULL,                  -- 藏品名称 (Collection Name)
    cover_url VARCHAR(500) NOT NULL,            -- 封面图URL (Cover Image URL)
    
    -- 用户上传的素材URL后续会定期清理
    material_url VARCHAR(500),                   -- 用户上传的素材URL可选
    
    -- 预留字段(后续功能扩展)
    description TEXT,                            -- 藏品描述(预留)
    rarity INT,                                  -- 稀有度(预留)
    tags JSONB,                                  -- 标签预留JSON数组
    
    -- 权限控制字段(预留)
    visibility VARCHAR(20) DEFAULT 'private',    -- 可见性private, friends, public预留
    
    -- 状态字段
    status INT NOT NULL DEFAULT 0,              -- 0:Pending, 1:Active (状态枚举)
    
    -- 链上信息(目前模拟,后续引入区块链功能)
    tx_hash VARCHAR(100),                        -- Web3:交易哈希
    block_number BIGINT,                         -- 区块号
    
    -- 社交相关(与 Social Service 交互)
    like_count INT NOT NULL DEFAULT 0,          -- 点赞数 (Like Count)
    
    -- 时间戳字段(用于排序、查询、事件追踪)
    created_at BIGINT NOT NULL,                  -- 创建时间(毫秒时间戳)
    updated_at BIGINT NOT NULL,                  -- 更新时间(毫秒时间戳)
    minted_at BIGINT,                            -- 上链成功时间(毫秒时间戳,用于 Task Service 事件)
    
    -- 软删除(与现有服务保持一致)
    deleted_at BIGINT,                           -- 软删除时间戳NULL 表示未删除)
    is_active BOOLEAN NOT NULL DEFAULT true,     -- 是否激活(用于快速查询过滤)
    
    -- 索引
    INDEX idx_assets_owner_star (owner_uid, star_id),
    INDEX idx_assets_status (status),
    INDEX idx_assets_created_at (created_at DESC),
    INDEX idx_assets_tx_hash (tx_hash),
    INDEX idx_assets_star_active (star_id, is_active),
    INDEX idx_assets_deleted_at (deleted_at)
);

完整字段列表(基于提供的设计 + 服务交互所需字段):

字段名 类型 约束 默认值 说明 来源
id BIGINT PK, AUTO_INCREMENT - 资产ID 提供设计
owner_uid BIGINT NOT NULL, FK - 当前持有者 提供设计
star_id BIGINT NOT NULL, FK - 所属星球 提供设计
name VARCHAR(100) NOT NULL - 藏品名称 提供设计
cover_url VARCHAR(500) NOT NULL - MinIO地址 提供设计
status INT NOT NULL 0 状态0:Pending, 1:Active 提供设计
tx_hash VARCHAR(100) NULL NULL Web3交易哈希 提供设计
token_id VARCHAR(100) NULL NULL Web3链上ID 提供设计
like_count INT NOT NULL 0 点赞数 提供设计
created_at BIGINT NOT NULL - 创建时间(毫秒时间戳) 新增
updated_at BIGINT NOT NULL - 更新时间(毫秒时间戳) 新增
minted_at BIGINT NULL NULL 上链成功时间(毫秒时间戳) 新增
deleted_at BIGINT NULL NULL 软删除时间戳 新增
is_active BOOLEAN NOT NULL true 是否激活 新增

字段说明:

字段 类型 说明 用途
id bigint 资产ID主键 唯一标识
owner_uid bigint 当前持有者 资产归属,与 User Service 交互
star_id bigint 所属星球 数据隔离,与现有服务保持一致
name varchar 藏品名称 显示和搜索
cover_url varchar 封面图URL 图片展示
material_url varchar 用户上传的素材URL 用户自定义素材(后续会清理)
status int 状态0:Pending, 1:Active 状态管理
tx_hash varchar 交易哈希 链上查询(目前模拟)
block_number bigint 区块号 链上查询(目前模拟)
like_count int 点赞数 与 Social Service 交互
created_at bigint 创建时间 排序、查询、Task Service 事件
updated_at bigint 更新时间 追踪变更
minted_at bigint 上链成功时间 事件发布、统计
deleted_at bigint 软删除时间 数据恢复、审计
is_active boolean 是否激活 快速查询过滤
description text 藏品描述 🔵 预留字段
rarity int 稀有度 🔵 预留字段
tags jsonb 标签JSON数组 🔵 预留字段
visibility varchar 可见性private/friends/public 🔵 预留字段

状态枚举值:

const (
    AssetStatusPending = 0  // 待处理(创建中)
    AssetStatusActive  = 1  // 已激活(上链成功)
    // 预留AssetStatusMinting = 2  // 上链中
    // 预留AssetStatusFailed = 3    // 上链失败
)

索引设计说明:

索引名 字段 用途
idx_assets_owner_star (owner_uid, star_id) 查询用户的资产列表
idx_assets_status (status) 按状态筛选资产
idx_assets_created_at (created_at DESC) 按创建时间排序
idx_assets_tx_hash (tx_hash) 通过交易哈希查询
idx_assets_star_active (star_id, is_active) 按明星和激活状态查询
idx_assets_deleted_at (deleted_at) 软删除查询过滤

Q13: 资产表字段补充

  • description(描述)字段:已添加为预留字段
  • block_number(区块号)字段:已添加
  • token_id链上ID字段不需要已移除
  • contract_address(合约地址)字段:不需要
  • chain_id链ID字段不需要
  • material_url用户上传素材URL字段已添加后续会定期清理
  • 不需要合约地址和链ID其他都需要description, block_number等

3.1.2 mint_orders 表(铸造订单表)

基于提供的数据库设计,完善后的表结构:

CREATE TABLE mint_orders (
    -- 主键
    order_id VARCHAR(100) PRIMARY KEY,          -- 铸造订单号 (Mint Order Number)
    
    -- 关联字段
    user_id BIGINT NOT NULL,                    -- 用户ID
    asset_id BIGINT,                            -- 关联资产(生成后回填) (Associated Asset)
    star_id BIGINT NOT NULL,                    -- 明星ID用于数据隔离
    
    -- 状态字段
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING',  -- PENDING/PROCESSING/SUCCESS/FAILED
    
    -- 费用相关(与 User Service 交互)
    cost_crystal BIGINT DEFAULT 0,              -- 消耗的水晶数量(用于记录和审计)
    
    -- 错误处理
    error_message TEXT,                          -- 错误信息(如果失败)
    retry_count INT DEFAULT 0,                  -- 重试次数(用于上链失败重试)
    
    -- 时间戳字段
    created_at BIGINT NOT NULL,                 -- 创建时间(毫秒时间戳)
    updated_at BIGINT NOT NULL,                 -- 更新时间(毫秒时间戳)
    minted_at BIGINT,                           -- 上链成功时间(用于 Task Service 事件)
    
    -- 外键约束
    FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE SET NULL,
    
    -- 索引
    INDEX idx_mint_orders_user_star (user_id, star_id),
    INDEX idx_mint_orders_asset (asset_id),
    INDEX idx_mint_orders_status (status),
    INDEX idx_mint_orders_created_at (created_at DESC)
);

字段说明:

字段 类型 说明 用途
order_id varchar 铸造订单号(主键) 唯一标识,使用 UUID 生成
user_id bigint 用户ID 与 User Service 交互
asset_id bigint 关联资产ID 关联 assets 表(创建资产后回填)
star_id bigint 明星ID 数据隔离
status varchar 订单状态 PENDING/PROCESSING/SUCCESS/FAILED
cost_crystal bigint 消耗的水晶数量 记录费用,用于审计和退款
error_message text 错误信息 失败原因记录
retry_count int 重试次数 上链失败重试机制
created_at bigint 创建时间 排序、查询
updated_at bigint 更新时间 追踪变更
minted_at bigint 上链成功时间 Task Service 事件发布

状态枚举值:

const (
    MintOrderStatusPending   = "PENDING"    // 待处理
    MintOrderStatusProcessing = "PROCESSING" // 处理中(上链中,目前模拟)
    MintOrderStatusSuccess   = "SUCCESS"    // 成功
    MintOrderStatusFailed    = "FAILED"     // 失败
    // 注意:不需要 CANCELLED 状态,不支持订单取消功能
)

字段说明:

字段 类型 说明 状态
order_id varchar 铸造订单号UUID 必需
user_id bigint 用户ID 必需
asset_id bigint 关联资产ID 必需
star_id bigint 明星ID 必需
status varchar 订单状态 必需
cost_crystal bigint 消耗的水晶数量 必需
error_message text 错误信息 必需
retry_count int 重试次数 必需
created_at bigint 创建时间 必需
updated_at bigint 更新时间 必需
minted_at bigint 上链成功时间 必需

3.2 与现有服务交互所需的额外字段

3.2.1 与 User Service 交互

需要添加的字段:

  1. assets.created_at / mint_orders.created_at

    • 用途:记录创建时间,用于:
      • 资产列表排序(按创建时间)
      • Task Service 事件发布(asset.mint 事件需要时间戳)
      • 统计和报表(每日/每月新增资产数)
  2. assets.minted_at / mint_orders.minted_at

    • 用途:记录上链成功时间,用于:
      • Task Service 事件发布(asset.mint_complete 事件)
      • 统计上链成功率
      • 计算上链耗时
  3. mint_orders.cost_crystal

    • 用途:记录消耗的水晶数量,用于:
      • 调用 User Service 扣除余额时的参数
      • 上链失败时的退款计算
      • 费用审计和统计
  4. assets.updated_at / mint_orders.updated_at

    • 用途:追踪数据变更,用于:
      • 数据同步和缓存更新
      • 审计日志

3.2.2 与 Social Service 交互

已包含的字段:

  1. assets.like_count
    • 用途:存储点赞数,避免每次查询都 JOIN asset_likes
    • 更新方式Social Service 点赞/取消点赞时,通过 RPC 调用更新

已确认:

  • 暂不考虑 Gallery Service不需要 is_exhibited 字段

已确认:

  • 目前不考虑 Gallery Service 的实现
  • 后续引入 Gallery Service 时,再添加相应的字段和接口
  • 可能的字段:assets.is_exhibited 或通过关联表实现

3.2.4 与 Task Service 交互

需要添加的字段:

  1. assets.created_at

    • 用途:发布 asset.mint 事件时的时间戳
  2. assets.minted_at

    • 用途:发布 asset.mint_complete 事件时的时间戳

事件发布格式:

{
  "event_type": "asset.mint_complete",
  "user_id": 1,
  "star_id": 1,
  "asset_id": 123,
  "minted_at": 1704067300000,
  "timestamp": 1704067300000
}

3.2.5 数据隔离和软删除

需要添加的字段:

  1. assets.deleted_at / mint_orders.deleted_at

    • 类型BIGINT NULL
    • 用途软删除与现有服务User Service保持一致
    • 查询过滤WHERE deleted_at IS NULL
  2. assets.is_active

    • 类型BOOLEAN DEFAULT true
    • 用途:快速查询过滤,避免每次都检查 deleted_at
    • 索引(star_id, is_active) 复合索引

3.2.6 字段添加总结

assets 表需要添加的字段:

字段名 类型 默认值 说明 交互服务
created_at BIGINT NOT NULL 创建时间(毫秒时间戳) Task Service
updated_at BIGINT NOT NULL 更新时间(毫秒时间戳) 通用
minted_at BIGINT NULL 上链成功时间(毫秒时间戳) Task Service
deleted_at BIGINT NULL 软删除时间戳 通用
is_active BOOLEAN true 是否激活 通用

mint_orders 表需要添加的字段:

字段名 类型 默认值 说明 交互服务
updated_at BIGINT NOT NULL 更新时间(毫秒时间戳) 通用
minted_at BIGINT NULL 上链成功时间(毫秒时间戳) Task Service
cost_crystal BIGINT 0 消耗的水晶数量 User Service
error_message TEXT NULL 错误信息 错误处理
retry_count INT 0 重试次数 错误处理

已确认:

  • 不需要 materials 表:用户上传的素材直接存储在 assets.material_url 字段中
  • 不需要 asset_materials 表:资产和素材的关联关系不需要单独维护
  • 不需要 upload_tasks 表:上传采用阻塞式,上传成功返回后才会允许前端用户进行下一步操作
  • 素材隔离:根据 star_id 进行隔离(在 assets 表中已有 star_id 字段)

3.1.3 asset_likes 表(点赞记录表)

用于存储用户对资产的点赞记录,支持高并发场景:

CREATE TABLE asset_likes (
    id BIGSERIAL PRIMARY KEY,
    asset_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    star_id BIGINT NOT NULL,  -- 用于数据隔离和查询优化
    created_at BIGINT NOT NULL,
    
    -- 唯一索引:防止重复点赞(数据库层面保护)
    UNIQUE KEY uk_asset_likes_user_asset (asset_id, user_id),
    
    -- 索引:用于查询用户点赞列表
    INDEX idx_asset_likes_user_star (user_id, star_id, created_at DESC),
    
    -- 索引:用于查询资产点赞用户列表(可选功能)
    INDEX idx_asset_likes_asset (asset_id, created_at DESC),
    
    -- 外键约束
    FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE CASCADE
);

字段说明:

字段 类型 说明 用途
id bigint 主键 唯一标识
asset_id bigint 资产ID 关联 assets 表
user_id bigint 用户ID 点赞用户
star_id bigint 明星ID 数据隔离,查询优化
created_at bigint 创建时间 排序、统计

设计要点:

  • 唯一索引uk_asset_likes_user_asset 防止重复点赞
  • 复合索引idx_asset_likes_user_star 支持查询用户点赞列表
  • 外键约束:资产删除时自动删除点赞记录
  • 第一阶段设计:直接数据库查询,不使用 Redis 缓存(后续可根据性能需求再引入)

详细设计文档: 资产点赞功能高性能设计.md


四、服务间交互设计

4.1 与 User Service 的交互

4.1.1 需要调用的接口

验证用户和粉丝档案:

RPC: ValidateUser(user_id) -> bool
RPC: ValidateFanProfile(user_id, star_id) -> bool
  • 使用场景:创建铸造订单前验证用户有效性
  • 使用字段mint_orders.user_id, mint_orders.star_id

扣除/增加余额:

RPC: UpdateCrystalBalance(user_id, star_id, delta) -> new_balance
RPC: UpdateCoinBalance(user_id, star_id, delta) -> new_balance
  • 使用场景
    • 创建铸造订单时扣除水晶(mint_orders.cost_crystal
    • 上链失败时退款
  • 使用字段mint_orders.cost_crystal, mint_orders.user_id, mint_orders.star_id

更新资产数量:

RPC: UpdateAssetsCount(user_id, star_id, delta) -> new_count
  • 使用场景:资产上链成功后,更新用户的资产数量
  • 使用字段assets.owner_uid, assets.star_id
  • 触发时机assets.status0 (Pending) 变为 1 (Active) 时

4.1.2 需要提供的接口

查询用户信息(用于资产详情展示):

RPC: GetUserInfo(user_id) -> UserInfo
  • 使用场景:资产详情中显示持有者信息
  • 使用字段assets.owner_uid

需要确认的问题 ⚠️

Q18: User Service 接口

  • ValidateUser(user_id) -> bool - 已实现(通过 GetUser RPC 接口实现Social Service 中已有使用)
  • ValidateFanProfile(user_id, star_id) -> bool - 已实现(通过 GetFanProfile RPC 接口实现Social Service 中已有使用)
  • UpdateCrystalBalance(user_id, star_id, delta) -> new_balance - 未实现,需要在 User Service 中添加 RPC 接口
  • ⚠️ UpdateAssetsCount(user_id, star_id, delta) -> new_count - Repository 层已实现IncrementAssetsCount/DecrementAssetsCount),但缺少 RPC 接口,需要在 User Service 中添加

补充说明:

  • ValidateUserValidateFanProfile 可以通过现有的 GetUserGetFanProfile 接口实现,返回错误即表示不存在
  • UpdateCrystalBalance 需要新增 RPC 接口,用于扣除/增加水晶余额
  • UpdateAssetsCount 需要在 User Service 的 Proto 中新增 RPC 接口,调用现有的 Repository 方法

Q19: 接口提供

  • Asset Service 需要提供 RPC 接口给其他服务
  • 需要调用的服务:
    • Social Service:需要 GetAsset(asset_id) 接口用于验证资产是否存在(点赞功能)
    • Gallery Service(后续):需要 ValidateAssetGetAsset 接口
    • Task Service(后续):可能需要查询资产信息用于事件处理

4.2 与 Social Service 的交互

4.2.1 点赞功能交互

Social Service 需要提供的接口:

更新点赞数:

RPC: UpdateAssetLikeCount(asset_id, delta) -> new_count
  • 使用场景:用户点赞/取消点赞时,更新 assets.like_count
  • 使用字段assets.like_count
  • delta 值+1点赞或 -1取消点赞

查询用户是否点赞:

RPC: CheckAssetLike(user_id, star_id, asset_id) -> bool
  • 使用场景:资产详情中显示 is_liked 状态
  • 使用字段assets.id

Asset Service 需要提供的接口:

获取资产信息:

RPC: GetAsset(asset_id) -> AssetInfo
  • 使用场景Social Service 点赞时验证资产是否存在
  • 使用字段assets.id, assets.status, assets.is_active

简化设计方案(第一阶段,直接数据库交互):

核心设计要点:

  1. 数据库直接交互

    • 点赞操作直接写入数据库
    • 使用数据库原子更新保证一致性
    • 使用唯一索引防止重复点赞
  2. 原子操作

    • 使用 UPDATE assets SET like_count = like_count + 1 原子更新
    • 使用数据库唯一索引 uk_asset_likes_user_asset 防止重复
  3. 防重复点赞

    • 数据库唯一索引 uk_asset_likes_user_asset 保护
    • 应用层先查询再插入(双重保护)
  4. 性能优化

    • 数据库索引优化(uk_asset_likes_user_asset, idx_asset_likes_user_star
    • 原子更新减少锁竞争
    • 不使用 Redis 缓存,直接查询数据库(后续可根据性能需求再引入)

实现方式:

// 点赞操作(直接数据库)
1. 检查是否已点赞SELECT COUNT(*) FROM asset_likes WHERE asset_id = ? AND user_id = ?
2. 如果未点赞
   - INSERT INTO asset_likes (asset_id, user_id, star_id, created_at)
   - UPDATE assets SET like_count = like_count + 1 WHERE id = ?
3. 返回成功

后续优化(第二阶段,暂不实现):

需要确认的问题 ⚠️

Q20: 点赞功能实现

  • 资产点赞功能在 Social Service 中实现(参考:资产点赞功能高性能设计.md
  • Asset Service 不需要提供资产列表Social Service 直接查询 assets 表
  • 资产详情中的 like_count 和 is_liked 获取方式:
    • 当前实现:直接查询数据库(assets.like_countasset_likes 表)
    • 后续优化:如需提升性能,可后续引入 Redis 缓存(暂不实现)
  • ⚠️ 需要补充Social Service 的 Proto 中需要添加资产点赞相关的 RPC 接口定义(LikeAsset, UnlikeAsset, CheckAssetLike 等)

4.3.1 资产验证

Asset Service 需要提供的接口:

验证资产是否存在:

RPC: ValidateAsset(asset_id, user_id, star_id) -> bool
  • 使用场景Gallery Service 放置资产前验证资产是否存在且属于当前用户
  • 使用字段assets.id, assets.owner_uid, assets.star_id, assets.status, assets.is_active

获取资产信息:

RPC: GetAsset(asset_id) -> AssetInfo
  • 使用场景Gallery Service 获取资产信息用于展示
  • 使用字段assets.*

4.3.2 展馆状态同步

Gallery Service 需要提供的接口:

更新资产展馆状态:

RPC: UpdateAssetExhibitionStatus(asset_id, is_exhibited) -> void
  • 使用场景:资产放置/下架时,更新 assets.is_exhibited
  • 使用字段assets.is_exhibited
  • 触发时机
    • 放置资产:is_exhibited = true
    • 下架资产:is_exhibited = false

需要确认的问题 ⚠️

Q21: 展馆集成

  • Gallery Service 是否需要验证资产是否存在?
  • Asset Service 是否需要提供资产列表接口给 Gallery Service
  • 资产下架时,是否需要通知 Gallery Service
  • 是否需要 assets.is_exhibited 字段,还是通过关联表查询?

4.4 与 Task Service 的交互

4.4.1 事件发布 已确认:不使用消息队列

实现方式:

  • 不使用消息队列RocketMQ/Kafka/RabbitMQ
  • 如需事件通知,直接通过 RPC 调用 Task Service
  • 如需事件记录,可在数据库中创建事件表进行记录

事件类型(如需实现):

  1. asset.mint - 资产创建事件

    • 触发时机:创建资产记录时(assets.status = 0
    • 使用字段assets.created_at, assets.owner_uid, assets.star_id, assets.id
    • 实现方式:直接 RPC 调用 Task Service 或数据库记录
  2. asset.mint_complete - 资产上链成功事件

    • 触发时机:资产上链成功,assets.status0 变为 1
    • 使用字段assets.minted_at, assets.owner_uid, assets.star_id, assets.id
    • 实现方式:直接 RPC 调用 Task Service 或数据库记录
  3. asset.mint_failed - 资产上链失败事件(可选)

    • 触发时机:资产上链失败时
    • 实现方式:直接 RPC 调用 Task Service 或数据库记录

已确认:

  • 不使用消息队列,直接通过 RPC 调用或数据库记录
  • ⚠️ 事件格式是否需要统一规范?待确认(如需事件记录,可在数据库中创建事件表)
  • ⚠️ 是否需要事件重试机制?待确认(如需重试,可在应用层实现)
  • ⚠️ 是否需要 asset.mint_failed 事件(上链失败时)?待确认(如需记录,可在数据库中记录)

五、配置设计

5.1 配置结构

参考 socialService 的配置设计,在 assetService/config/asset_config.go 中定义配置:

package config

// AssetConfig 资产服务配置
type AssetConfig struct {
    // MintConfig 铸造相关配置
    MintConfig MintConfig
    
    // RetryConfig 重试相关配置
    RetryConfig RetryConfig
}

// MintConfig 铸造配置
type MintConfig struct {
    // CostCrystal 铸造费用(水晶数量)
    // 后续会引入规则表进行定义现在统一定义在config文件中
    CostCrystal int64
    
    // MaxMintCountPerDay 每日最大铸造次数
    // 后续会引入规则表进行定义现在统一定义在config文件中
    MaxMintCountPerDay int
    
    // MaxMintCountPerUser 用户最大铸造次数(总次数)
    // 后续会引入规则表进行定义现在统一定义在config文件中
    MaxMintCountPerUser int
}

// RetryConfig 重试配置
type RetryConfig struct {
    // MaxRetryCount 上链失败最大重试次数
    MaxRetryCount int
    
    // RetryIntervalSeconds 重试间隔(秒)
    RetryIntervalSeconds int
}

// GlobalAssetConfig 全局资产配置实例
var GlobalAssetConfig = &AssetConfig{
    MintConfig: MintConfig{
        CostCrystal:         100,  // 默认100水晶
        MaxMintCountPerDay:  10,   // 默认每日10次
        MaxMintCountPerUser: 100,  // 默认总次数100次
    },
    RetryConfig: RetryConfig{
        MaxRetryCount:        3,   // 默认重试3次
        RetryIntervalSeconds: 5,   // 默认间隔5秒
    },
}

5.2 配置使用场景

5.2.1 铸造费用CostCrystal

使用场景:

  • 创建铸造订单时,从用户账户扣除 CostCrystal 数量的水晶
  • 上链失败时,退还 CostCrystal 数量的水晶

实现位置:

  • service/mint_service.go - CreateMintOrder 方法

5.2.2 次数限制MaxMintCountPerDay / MaxMintCountPerUser

使用场景:

  • 创建铸造订单前,检查用户是否超过每日/总次数限制

实现位置:

  • service/mint_service.go - CreateMintOrder 方法
  • 需要查询 mint_orders 表统计用户今日/总铸造次数

5.2.3 重试配置MaxRetryCount / RetryIntervalSeconds

使用场景:

  • 上链失败时,根据 MaxRetryCount 决定是否重试
  • 重试间隔由 RetryIntervalSeconds 控制

实现位置:

  • service/mint_service.go - 上链失败处理逻辑(目前模拟)

5.3 配置更新方法(预留)

// UpdateMintConfig 更新铸造配置
// 注意:后续可以从规则表或配置中心读取配置并调用此方法更新
func (c *AssetConfig) UpdateMintConfig(costCrystal int64, maxPerDay, maxPerUser int) {
    c.MintConfig.CostCrystal = costCrystal
    c.MintConfig.MaxMintCountPerDay = maxPerDay
    c.MintConfig.MaxMintCountPerUser = maxPerUser
}

// UpdateRetryConfig 更新重试配置
func (c *AssetConfig) UpdateRetryConfig(maxRetryCount, retryIntervalSeconds int) {
    c.RetryConfig.MaxRetryCount = maxRetryCount
    c.RetryConfig.RetryIntervalSeconds = retryIntervalSeconds
}

已确认:

  • 铸造费用:配置在 config/asset_config.go 中,创建订单时扣除,上链失败退款
  • 次数限制:配置在 config/asset_config.go 中,后续会引入规则表
  • 重试次数:配置在 config/asset_config.go默认3次

六、技术架构设计

5.1 服务结构

assetService/
├── main.go                 # 服务入口
├── config/                 # 配置管理
│   └── asset_config.go
├── repository/             # 数据访问层
│   ├── asset_repository.go
│   ├── mint_order_repository.go
│   └── asset_like_repository.go  # 点赞记录(第一阶段:直接数据库)
├── service/                # 业务逻辑层
│   ├── asset_service.go
│   └── mint_service.go
├── provider/               # RPC 提供者
│   └── asset_provider.go
├── client/                 # RPC 客户端(调用其他服务)
│   └── user_rpc_client.go
├── proto/                  # Proto 定义(可选,如果单独定义)
└── start.sh               # 启动脚本

5.2 外部依赖

6.2.1 区块链服务

已确认:

  • 区块链暂不考虑,后续才会引入相应的功能
  • 目前只需要模拟已经上链成功结束即可
  • 上链为异步机制,但目前不引入上链功能,只做简单模拟

实现方式:

  • 创建资产时,随机生成 tx_hashblock_number
  • 模拟异步上链:延迟几秒后更新 assets.status = 1Active
  • 后续引入区块链功能后再进行优化

6.2.2 对象存储服务

已确认:

  • 暂不考虑图片存储的细节问题存一个URL即可
  • 封面图URL直接存储在 assets.cover_url 字段中
  • 用户上传的素材URL存储在 assets.material_url 字段中(后续会定期清理)

实现方式:

  • 前端上传图片后返回图片URL
  • 后端直接存储URL不进行图片处理

七、API 接口设计

7.1 接口列表

功能 方法 路径 说明
创建铸造订单 POST /api/v1/assets/mints 创建资产并发起铸造(包含上传封面图)
获取我的藏品列表 GET /api/v1/assets/me 获取当前用户的藏品列表
获取资产详情 GET /api/v1/assets/{asset_id} 获取资产详细信息(仅限自己的资产)
查询上链状态 GET /api/v1/assets/{asset_id}/status 查询资产上链状态(仅限自己的资产)

已确认:

  • 不需要单独的上传接口:上传采用阻塞式,在创建铸造订单时一起处理
  • 不需要素材管理接口:用户上传的素材直接存储在 assets.material_url 字段中
  • 不需要订单取消接口:不支持订单取消功能
  • 用户不能看到别人的资产:所有查询接口都只返回当前用户的资产

6.2 需要确认的问题 ⚠️

Q26: API 路径规范

  • API 路径前缀使用 /api/v1/assets(与其他服务保持一致)
  • 需要版本控制v1

Q27: 权限控制

  • 所有接口都需要登录(通过 Gateway 的 auth middleware
  • 资产查询接口需要验证资产所有权(只能查询自己的资产)
  • 创建铸造订单接口需要验证用户权限和余额

七、数据隔离设计

7.1 隔离方案

所有表都包含 star_id 字段,实现数据隔离:

-- 所有查询都基于 user_id + star_id
SELECT * FROM assets 
WHERE user_id = ? AND star_id = ?;

7.2 需要确认的问题 ⚠️

Q28: 跨明星资产

  • 资产不能跨明星使用(通过 star_id 字段隔离)
  • 用户切换明星身份后,只能看到当前明星下的资产(通过 star_id 过滤)

八、性能优化设计

8.1 缓存策略

需要确认的问题 ⚠️

Q29: 缓存需求 已确认:不使用 Redis 缓存

  • 不使用 Redis 缓存,所有数据查询都直接访问数据库
  • 所有接口都直接查询数据库,保证数据实时性
  • ⚠️ 后续优化:如需提升性能,可后续引入 Redis 缓存(暂不实现)

8.2 分页和索引

  • 所有列表接口都支持分页(默认 page=1, page_size=20最大 page_size=100
  • 关键字段建立索引user_id, star_id, status, created_at

九、错误处理设计

9.1 错误码定义

需要确认的问题 ⚠️

Q30: 错误处理

  • 使用统一的错误码体系(参考 pkg/errors/errors.go
  • ⚠️ 待确认:错误信息是否需要国际化?(建议第一阶段仅支持中文,后续再扩展)

十、数据库设计总结

10.1 完整表结构(基于提供设计 + 服务交互字段)

assets 表(最终设计)

CREATE TABLE assets (
    -- 主键和基础字段(来自提供设计)
    id BIGSERIAL PRIMARY KEY,
    owner_uid BIGINT NOT NULL,
    star_id BIGINT NOT NULL,
    name VARCHAR(100) NOT NULL,
    cover_url VARCHAR(500) NOT NULL,
    material_url VARCHAR(500),                  -- 用户上传的素材URL后续会定期清理
    description TEXT,                           -- 藏品描述(预留)
    rarity INT,                                 -- 稀有度(预留)
    tags JSONB,                                 -- 标签预留JSON数组
    visibility VARCHAR(20) DEFAULT 'private',   -- 可见性(预留)
    status INT NOT NULL DEFAULT 0,              -- 0:Pending, 1:Active
    tx_hash VARCHAR(100),                       -- 交易哈希(目前模拟)
    block_number BIGINT,                        -- 区块号(目前模拟)
    like_count INT NOT NULL DEFAULT 0,
    
    -- 服务交互所需字段(新增)
    created_at BIGINT NOT NULL,                -- Task Service 事件
    updated_at BIGINT NOT NULL,                -- 通用
    minted_at BIGINT,                          -- Task Service 事件
    deleted_at BIGINT,                         -- 软删除
    is_active BOOLEAN NOT NULL DEFAULT true,   -- 快速查询
    
    -- 索引
    INDEX idx_assets_owner_star (owner_uid, star_id),
    INDEX idx_assets_status (status),
    INDEX idx_assets_created_at (created_at DESC),
    INDEX idx_assets_tx_hash (tx_hash),
    INDEX idx_assets_star_active (star_id, is_active),
    INDEX idx_assets_deleted_at (deleted_at)
);

mint_orders 表(最终设计)

CREATE TABLE mint_orders (
    -- 主键和基础字段(来自提供设计)
    order_id VARCHAR(100) PRIMARY KEY,
    user_id BIGINT NOT NULL,
    asset_id BIGINT,
    star_id BIGINT NOT NULL,
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    created_at BIGINT NOT NULL,
    
    -- 服务交互所需字段(新增)
    updated_at BIGINT NOT NULL,                -- 通用
    minted_at BIGINT,                          -- Task Service 事件
    cost_crystal BIGINT DEFAULT 0,             -- User Service 交互
    error_message TEXT,                        -- 错误处理
    retry_count INT DEFAULT 0,                 -- 重试机制
    
    -- 外键和索引
    FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE SET NULL,
    INDEX idx_mint_orders_user_star (user_id, star_id),
    INDEX idx_mint_orders_asset (asset_id),
    INDEX idx_mint_orders_status (status),
    INDEX idx_mint_orders_created_at (created_at DESC)
);

10.2 字段分类总结

来自提供设计的字段:

  • assets.id, assets.owner_uid, assets.star_id, assets.name, assets.cover_url
  • assets.status, assets.tx_hash, assets.like_count
  • mint_orders.order_id, mint_orders.user_id, mint_orders.asset_id, mint_orders.status, mint_orders.created_at

根据需求新增的字段:

  • assets.material_url - 用户上传的素材URL后续会定期清理
  • assets.description, assets.rarity, assets.tags, assets.visibility - 预留字段
  • assets.block_number - 区块号(目前模拟)
  • assets.created_at, assets.updated_at, assets.minted_at - 时间戳Task Service 事件)
  • assets.deleted_at, assets.is_active - 软删除(与现有服务一致)
  • mint_orders.updated_at, mint_orders.minted_at - 时间戳
  • mint_orders.cost_crystal - 费用记录User Service 交互)
  • mint_orders.error_message, mint_orders.retry_count - 错误处理

已移除的字段:

  • assets.token_id - 不需要根据Q12确认
  • assets.is_exhibited - 暂不需要Gallery Service集成后续再考虑

十一、待确认问题汇总

已确认的问题

Q1-Q4: 铸造流程相关

  • Q1: 铸造费用在config文件中定义创建订单时扣除上链失败退款
  • Q2: 暂不考虑随机生成图片URL即可
  • Q3: 次数限制在config文件中定义
  • Q4: 上链为异步机制,但目前只做简单模拟

Q5-Q6: 资产可见性和属性

  • Q5: 用户不能看到别人的资产,预留权限控制字段
  • Q6: 名称和封面图必须,预留描述、稀有度、标签字段

Q7-Q10: 素材和上传

  • Q7: 素材需要根据star_id隔离
  • Q8: 用户可以上传素材不需要审核不需要materials表在assets表加material_url字段
  • Q9: 暂不考虑图片存储细节存URL即可
  • Q10: 不需要上传任务表,直接阻塞上传

Q11-Q12: 区块链集成

  • Q11: 区块链暂不考虑,模拟上链成功
  • Q12: 只需要tx_hash和block_number不需要token_id

Q13-Q14: 数据库表字段

  • Q13: 不需要合约地址和链ID其他都需要description, block_number等
  • Q14: UUID生成不需要支付状态不需要取消功能重试次数在config中

Q15-Q17: 其他表设计

  • Q15-Q17: 不需要materials表、asset_materials表、upload_tasks表

Q23-Q25: 技术选型

  • Q23: 区块链暂不考虑,后续引入
  • Q24: 暂不考虑图片存储细节
  • Q25: 暂不考虑AI生成功能

⚠️ 仍需确认的问题

Q18: User Service 接口可用性 ⚠️ 部分已实现,部分待补充

  • ValidateUser - 已实现(通过 GetUser 接口)
  • ValidateFanProfile - 已实现(通过 GetFanProfile 接口)
  • UpdateCrystalBalance - 需要在 User Service 中新增 RPC 接口
  • ⚠️ UpdateAssetsCount - Repository 层已实现,需要在 User Service 中新增 RPC 接口

Q19-Q22: 服务间交互细节

  • Q19: Asset Service 需要提供 RPC 接口给 Social Service 和 Gallery Service
  • Q20: 资产点赞功能在 Social Service 中实现,直接查询数据库(不使用 Redis
  • Q21: Gallery Service 集成暂不考虑,后续再实现
  • Q22: 事件驱动已确认不使用消息队列,直接通过 RPC 调用或数据库记录

Q26-Q30: 其他技术细节

  • Q26-Q27: API 路径规范和权限控制已确认
  • Q28: 跨明星资产隔离已确认
  • Q29: 缓存需求已确认(不使用 Redis 缓存,直接查询数据库)
  • ⚠️ Q30: 错误处理国际化待确认

📝 需要补充的内容

1. Proto 接口定义

  • Asset Service Proto:需要创建 proto/asset.proto 文件,定义资产服务的 RPC 接口
  • User Service Proto 补充:需要在 proto/user.proto 中添加:
    • UpdateCrystalBalance RPC 接口
    • UpdateAssetsCount RPC 接口
  • Social Service Proto 补充:需要在 proto/social.proto 中添加资产点赞相关的 RPC 接口

2. 服务实现状态

  • Asset Service 服务目录:需要创建 services/assetService/ 目录结构
  • 配置管理:需要创建 services/assetService/config/asset_config.go(参考 socialService/config/social_config.go
  • Repository 层:需要实现 asset_repository.gomint_order_repository.goasset_like_repository.go
  • Service 层:需要实现 asset_service.gomint_service.go
  • Provider 层:需要实现 asset_provider.go
  • RPC Client:需要实现 user_rpc_client.go(调用 User Service

3. Gateway 集成

  • 路由配置:需要在 gateway/router/router.go 中添加资产服务的路由
  • Controller:需要创建 gateway/controller/asset_controller.go
  • DTO 转换:需要创建 gateway/dto/asset_converter.go

4. 数据库迁移

  • 表结构创建:需要创建 assetsmint_ordersasset_likes 表的迁移脚本
  • 索引创建:确保所有索引都已创建

5. 测试

  • 单元测试Repository 层、Service 层的单元测试
  • 集成测试:服务间交互的集成测试
  • HTTP 测试Gateway 接口的完整测试流程(参考 用户服务HTTP完整测试流程.md

十一、下一步行动

阶段 1: 需求确认(当前阶段) 进行中

  • 与产品/业务确认上述 30 个问题(大部分已确认)
  • 确定核心功能范围(已确定)
  • 确定技术选型(已确定)
  • 待补充:确认 User Service 接口补充需求
  • 待补充:确认 Social Service 资产点赞接口定义

阶段 2: 详细设计 ⚠️ 部分完成

  • 完善数据库表结构(已完成,见 pkg/models/asset.go
  • 待补充:设计 Asset Service Proto 接口定义
  • 待补充:设计 User Service Proto 接口补充(UpdateCrystalBalanceUpdateAssetsCount
  • 待补充:设计 Social Service Proto 接口补充(资产点赞相关)
  • 设计服务间交互协议(已设计)
  • 设计错误处理机制(参考现有服务)

阶段 3: 开发实现 未开始

  • Repository 层实现
  • Service 层实现
  • Provider 层实现
  • Gateway 集成
  • 单元测试

阶段 4: 测试和上线 未开始

  • 集成测试
  • 性能测试
  • 上线部署

附录:参考文档