1446 lines
51 KiB
Markdown
1446 lines
51 KiB
Markdown
# 资产服务(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` 中添加)
|
||
|
||
#### 🟡 中优先级(建议确认)
|
||
4. **事件驱动机制** ✅ **已确认:不使用消息队列**
|
||
- ✅ 不使用消息队列(RocketMQ/Kafka/RabbitMQ),直接通过 RPC 调用或数据库记录
|
||
- ⚠️ 事件格式规范(如需事件记录,可在数据库中记录)
|
||
- ⚠️ 事件重试机制(如需重试,可在应用层实现)
|
||
|
||
5. **缓存策略** ✅ **已确认:不使用 Redis 缓存**
|
||
- ✅ 不使用 Redis 缓存,直接查询数据库
|
||
- ✅ 所有数据查询都直接访问数据库
|
||
|
||
#### 🟢 低优先级(可后续优化)
|
||
6. **错误处理国际化**
|
||
- ⚠️ 是否需要多语言支持
|
||
|
||
### 📝 待实现内容
|
||
|
||
#### 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 Client(user_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_id`(UUID), `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)
|
||
```
|
||
|
||
**响应结构:**
|
||
```json
|
||
{
|
||
"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}
|
||
```
|
||
|
||
**响应结构:**
|
||
```json
|
||
{
|
||
"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: 分类筛选(可选)
|
||
```
|
||
|
||
**响应结构:**
|
||
```json
|
||
{
|
||
"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)
|
||
```
|
||
|
||
**响应结构:**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应结构:**
|
||
```json
|
||
{
|
||
"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_hash` 和 `block_number` 即可
|
||
- ✅ 不需要 `token_id`
|
||
|
||
---
|
||
|
||
## 三、数据库设计
|
||
|
||
### 3.1 核心表结构
|
||
|
||
#### 3.1.1 assets 表(资产表)
|
||
|
||
**基于提供的数据库设计,完善后的表结构:**
|
||
|
||
```sql
|
||
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) | 🔵 预留字段 |
|
||
|
||
**状态枚举值:**
|
||
```go
|
||
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 表(铸造订单表)
|
||
|
||
**基于提供的数据库设计,完善后的表结构:**
|
||
|
||
```sql
|
||
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 事件发布 |
|
||
|
||
**状态枚举值:**
|
||
```go
|
||
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` 字段
|
||
|
||
#### 3.2.3 与 Gallery Service 交互(暂不考虑)
|
||
|
||
**✅ 已确认:**
|
||
- ✅ 目前不考虑 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` 事件时的时间戳
|
||
|
||
**事件发布格式:**
|
||
```json
|
||
{
|
||
"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 表(点赞记录表)
|
||
|
||
**用于存储用户对资产的点赞记录,支持高并发场景:**
|
||
|
||
```sql
|
||
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](./资产点赞功能高性能设计.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.status` 从 `0` (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 中添加
|
||
|
||
**补充说明:**
|
||
- `ValidateUser` 和 `ValidateFanProfile` 可以通过现有的 `GetUser` 和 `GetFanProfile` 接口实现,返回错误即表示不存在
|
||
- `UpdateCrystalBalance` 需要新增 RPC 接口,用于扣除/增加水晶余额
|
||
- `UpdateAssetsCount` 需要在 User Service 的 Proto 中新增 RPC 接口,调用现有的 Repository 方法
|
||
|
||
**Q19: 接口提供**
|
||
- ✅ Asset Service 需要提供 RPC 接口给其他服务
|
||
- ✅ 需要调用的服务:
|
||
- **Social Service**:需要 `GetAsset(asset_id)` 接口用于验证资产是否存在(点赞功能)
|
||
- **Gallery Service**(后续):需要 `ValidateAsset` 和 `GetAsset` 接口
|
||
- **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 缓存,直接查询数据库(后续可根据性能需求再引入)
|
||
|
||
**实现方式:**
|
||
```go
|
||
// 点赞操作(直接数据库)
|
||
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. 返回成功
|
||
```
|
||
|
||
**后续优化(第二阶段,暂不实现):**
|
||
- 如需提升性能,可后续引入 Redis 缓存
|
||
- 可考虑异步写入数据库
|
||
- 详细设计参考:[资产点赞功能高性能设计.md](./资产点赞功能高性能设计.md)
|
||
|
||
**需要确认的问题 ⚠️**
|
||
|
||
**Q20: 点赞功能实现**
|
||
- ✅ 资产点赞功能在 Social Service 中实现(参考:[资产点赞功能高性能设计.md](./资产点赞功能高性能设计.md))
|
||
- ✅ Asset Service 不需要提供资产列表,Social Service 直接查询 assets 表
|
||
- ✅ 资产详情中的 like_count 和 is_liked 获取方式:
|
||
- **当前实现**:直接查询数据库(`assets.like_count` 和 `asset_likes` 表)
|
||
- **后续优化**:如需提升性能,可后续引入 Redis 缓存(暂不实现)
|
||
- ⚠️ **需要补充**:Social Service 的 Proto 中需要添加资产点赞相关的 RPC 接口定义(`LikeAsset`, `UnlikeAsset`, `CheckAssetLike` 等)
|
||
|
||
---
|
||
|
||
### 4.3 与 Gallery Service 的交互
|
||
|
||
#### 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.status` 从 `0` 变为 `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` 中定义配置:
|
||
|
||
```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 配置更新方法(预留)
|
||
|
||
```go
|
||
// 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_hash` 和 `block_number`
|
||
- 模拟异步上链:延迟几秒后更新 `assets.status = 1`(Active)
|
||
- 后续引入区块链功能后再进行优化
|
||
|
||
#### 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` 字段,实现数据隔离:**
|
||
|
||
```sql
|
||
-- 所有查询都基于 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 表(最终设计)
|
||
|
||
```sql
|
||
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 表(最终设计)
|
||
|
||
```sql
|
||
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.go`、`mint_order_repository.go`、`asset_like_repository.go`
|
||
- [ ] **Service 层**:需要实现 `asset_service.go`、`mint_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. 数据库迁移
|
||
- [ ] **表结构创建**:需要创建 `assets`、`mint_orders`、`asset_likes` 表的迁移脚本
|
||
- [ ] **索引创建**:确保所有索引都已创建
|
||
|
||
#### 5. 测试
|
||
- [ ] **单元测试**:Repository 层、Service 层的单元测试
|
||
- [ ] **集成测试**:服务间交互的集成测试
|
||
- [ ] **HTTP 测试**:Gateway 接口的完整测试流程(参考 `用户服务HTTP完整测试流程.md`)
|
||
|
||
---
|
||
|
||
## 十一、下一步行动
|
||
|
||
### 阶段 1: 需求确认(当前阶段)✅ **进行中**
|
||
- [x] 与产品/业务确认上述 30 个问题(大部分已确认)
|
||
- [x] 确定核心功能范围(已确定)
|
||
- [x] 确定技术选型(已确定)
|
||
- [ ] **待补充**:确认 User Service 接口补充需求
|
||
- [ ] **待补充**:确认 Social Service 资产点赞接口定义
|
||
|
||
### 阶段 2: 详细设计 ⚠️ **部分完成**
|
||
- [x] 完善数据库表结构(已完成,见 `pkg/models/asset.go`)
|
||
- [ ] **待补充**:设计 Asset Service Proto 接口定义
|
||
- [ ] **待补充**:设计 User Service Proto 接口补充(`UpdateCrystalBalance`、`UpdateAssetsCount`)
|
||
- [ ] **待补充**:设计 Social Service Proto 接口补充(资产点赞相关)
|
||
- [x] 设计服务间交互协议(已设计)
|
||
- [x] 设计错误处理机制(参考现有服务)
|
||
|
||
### 阶段 3: 开发实现 ❌ **未开始**
|
||
- [ ] Repository 层实现
|
||
- [ ] Service 层实现
|
||
- [ ] Provider 层实现
|
||
- [ ] Gateway 集成
|
||
- [ ] 单元测试
|
||
|
||
### 阶段 4: 测试和上线 ❌ **未开始**
|
||
- [ ] 集成测试
|
||
- [ ] 性能测试
|
||
- [ ] 上线部署
|
||
|
||
---
|
||
|
||
## 附录:参考文档
|
||
|
||
- [微服务架构设计文档](./微服务架构设计.md)
|
||
- [用户服务实现文档](../services/userService/README.md)
|
||
- [社交服务实现文档](../services/socialService/IMPLEMENTATION_COMPLETE.md)
|
||
|