14 KiB
14 KiB
积分系统与OSS额度系统实施计划
创建日期:2026-02-25 状态:待审批
一、需求概述
1.1 业务需求
| 模块 | 需求描述 |
|---|---|
| 前端积分系统 | 前端启动时加载 muye_ai_model_config,根据 model_code 获取积分消耗,显示在对标分析、热点趋势、智能体、数字人模块 |
| OSS额度系统 | 后端统一用字节计算,仅统计已上传未删除文件,上传前校验,删除后释放,每日凌晨自动对账 |
| OSS存储管理 | 素材列表和个人中心显示用户正确的额度 |
1.2 验收标准
- 前端各业务模块正确显示积分消耗数值
- OSS 上传前校验额度,超额拒绝上传
- OSS 文件删除后实时释放额度
- 每日凌晨自动对账,确保数据库记录与实际一致
二、现有架构分析
2.1 已有基础设施
| 组件 | 位置 | 状态 |
|---|---|---|
| 积分服务 | PointsService |
✅ 已实现预检、扣减、预扣逻辑 |
| AI模型配置 | muye_ai_model_config 表 |
✅ 已有 consume_points 字段 |
| 用户档案服务 | MemberUserProfileService |
⚠️ 需增强存储校验和更新 |
| 用户档案表 | muye_member_user_profile |
✅ 已有存储和积分字段(GB单位) |
| 用户Store | stores/user.js |
✅ 已有积分和存储计算属性 |
| 个人中心 | views/user/Profile.vue |
✅ 已显示基本额度信息 |
2.2 需要新增的组件
| 组件 | 类型 | 说明 |
|---|---|---|
ModelConfigApi |
前端API | 获取模型配置列表 |
PointsConfigStore |
前端Store | 管理积分配置状态 |
PointsTag |
前端组件 | 积分消耗标签组件 |
OssQuotaReconcileJob |
后端Job | 每日对账任务 |
StorageQuotaMixin |
前端Composable | 存储额度显示逻辑复用 |
三、详细设计
3.1 前端积分系统
3.1.1 数据流
前端启动 → 调用 API 获取模型配置 → 存入 Store → 各模块从 Store 读取显示
3.1.2 新增文件
frontend/app/web-gold/src/
├── api/
│ └── modelConfig.js # 模型配置 API
├── stores/
│ └── pointsConfig.js # 积分配置 Store
└── components/
└── common/
└── PointsTag.vue # 积分消耗标签组件
3.1.3 API 设计
// api/modelConfig.js
export function getModelConfigList() {
return request({
url: `${BASE_URL}/aimodelconfig/list-enabled`,
method: 'get'
})
}
// 返回格式
{
"dify": {
"agent_chat_pro": { "consumePoints": 10, "modelName": "深度版" },
"agent_chat_standard": { "consumePoints": 5, "modelName": "标准版" }
},
"digital_human": {
"latentsync": { "consumePoints": 50, "modelName": "数字人" }
},
// ...
}
3.1.4 Store 设计
// stores/pointsConfig.js
export const usePointsConfigStore = defineStore('pointsConfig', () => {
const configMap = ref({}) // 按平台+modelCode组织的配置
const isLoaded = ref(false)
// 根据 platform 和 modelCode 获取积分消耗
const getConsumePoints = (platform, modelCode) => {
return configMap.value[platform]?.[modelCode]?.consumePoints ?? 0
}
// 初始化加载配置
const loadConfig = async () => {
if (isLoaded.value) return
const data = await getModelConfigList()
configMap.value = data
isLoaded.value = true
}
return { configMap, isLoaded, getConsumePoints, loadConfig }
})
3.1.5 积分标签组件
<!-- components/common/PointsTag.vue -->
<template>
<span class="points-tag" :class="{ 'is-loading': loading }">
<ThunderboltOutlined />
<span class="points-value">{{ displayPoints }}</span>
<span class="points-unit">积分</span>
</span>
</template>
<script setup>
import { computed } from 'vue'
import { usePointsConfigStore } from '@/stores/pointsConfig'
const props = defineProps({
platform: { type: String, required: true },
modelCode: { type: String, required: true },
loading: { type: Boolean, default: false }
})
const pointsConfigStore = usePointsConfigStore()
const displayPoints = computed(() =>
pointsConfigStore.getConsumePoints(props.platform, props.modelCode)
)
</script>
3.1.6 各模块集成点
| 模块 | 文件位置 | 集成方式 |
|---|---|---|
| 智能体 | ChatDrawer.vue |
在发送按钮旁显示积分消耗 |
| 数字人 | Video.vue |
在生成按钮旁显示积分消耗 |
| 对标分析 | Benchmark.vue |
在分析按钮旁显示积分消耗 |
| 热点趋势 | Forecast.vue |
在文案生成按钮旁显示积分消耗 |
3.2 OSS 额度系统
3.2.1 数据模型
MemberUserProfileDO 字段(muye_member_user_profile 表):
private String userId; // 用户ID
private BigDecimal totalStorage; // 云空间总容量 (GB)
private BigDecimal usedStorage; // 云空间已用容量 (GB)
private BigDecimal remainingStorage;// 云空间剩余容量 (GB)
private Integer totalPoints; // 账户总积分
private Integer usedPoints; // 账户消耗积分
private Integer remainingPoints; // 账户剩余积分
设计决策:保持 GB 单位存储(兼容现有数据),后端逻辑统一用字节计算
存储文件记录表(muye_material_file):
-- 文件大小字段(字节)
file_size BIGINT NOT NULL COMMENT '文件大小(字节)'
单位转换常量:
// 1 GB = 1024 * 1024 * 1024 字节
public static final long BYTES_PER_GB = 1073741824L;
3.2.2 后端增强设计
修改/新增文件:
yudao-module-tik/src/main/java/cn/iocoder/yudao/module/tik/
├── muye/
│ └── memberuserprofile/
│ ├── service/
│ │ ├── MemberUserProfileService.java # 增强:添加存储额度方法
│ │ └── MemberUserProfileServiceImpl.java
│ ├── mapper/
│ │ └── MemberUserProfileMapper.java # 增强:添加原子更新方法
│ └── job/
│ └── OssQuotaReconcileJob.java # 新增:每日对账Job
3.2.3 额度校验流程
用户上传文件
↓
检查文件大小(字节)
↓
调用 MemberUserProfileService.validateStorage(userId, fileSizeBytes)
↓
内部逻辑:将 fileSizeBytes 转换为 GB,与 remainingStorage 比较
↓
├── 余额充足 → 允许上传
│ ↓
│ 文件上传成功 → 调用 increaseUsedStorage(userId, fileSizeBytes)
│
└── 余额不足 → 抛出异常,拒绝上传
3.2.4 额度释放流程
用户删除文件
↓
获取文件大小 fileSizeBytes(字节)
↓
调用 MemberUserProfileService.decreaseUsedStorage(userId, fileSizeBytes)
↓
内部逻辑:字节转GB,原子更新 usedStorage/remainingStorage
3.2.5 MemberUserProfileService 增强
// 新增方法
/**
* 校验存储空间是否足够
* @param userId 用户ID
* @param fileSizeBytes 文件大小(字节)
*/
void validateStorage(String userId, long fileSizeBytes);
/**
* 增加已使用存储空间
* @param userId 用户ID
* @param fileSizeBytes 文件大小(字节)
*/
void increaseUsedStorage(String userId, long fileSizeBytes);
/**
* 减少已使用存储空间
* @param userId 用户ID
* @param fileSizeBytes 文件大小(字节)
*/
void decreaseUsedStorage(String userId, long fileSizeBytes);
3.2.6 Mapper 原子更新
// MemberUserProfileMapper.java 新增
/**
* 原子增加已用存储(乐观锁)
* @param userId 用户ID
* @param storageGb 增加的存储量(GB,BigDecimal转String)
* @return 影响行数,0表示余额不足
*/
@Update("UPDATE muye_member_user_profile " +
"SET used_storage = used_storage + #{storageGb}, " +
" remaining_storage = remaining_storage - #{storageGb}, " +
" update_time = NOW() " +
"WHERE user_id = #{userId} AND remaining_storage >= #{storageGb}")
int updateStorageIncrease(@Param("userId") String userId, @Param("storageGb") String storageGb);
/**
* 原子减少已用存储
*/
@Update("UPDATE muye_member_user_profile " +
"SET used_storage = used_storage - #{storageGb}, " +
" remaining_storage = remaining_storage + #{storageGb}, " +
" update_time = NOW() " +
"WHERE user_id = #{userId} AND used_storage >= #{storageGb}")
int updateStorageDecrease(@Param("userId") String userId, @Param("storageGb") String storageGb);
3.2.7 每日对账Job
@Component
public class OssQuotaReconcileJob {
@Scheduled(cron = "0 0 3 * * ?") // 每日凌晨3点
public void reconcile() {
// 1. 查询所有用户的档案
// 2. 统计每个用户 muye_material_file 表中文件总大小(字节)
// 3. 转换为 GB,与 usedStorage 对比
// 4. 不一致则修正并记录日志
}
}
3.2.8 API 设计
使用现有的 /webApi/api/tik/member-profile/get 接口,返回数据已包含存储额度信息。
---
### 3.3 OSS 存储管理
#### 3.3.1 素材列表集成
在 `MaterialListNew.vue` 顶部工具栏显示存储额度:
```vue
<div class="storage-quota-info">
<DatabaseOutlined />
<span>{{ formatStorage(usedStorage) }} / {{ formatStorage(totalStorage) }}</span>
<a-progress :percent="storagePercent" size="small" />
</div>
3.3.2 个人中心优化
Profile.vue 已有存储空间显示,需要确保数据来源正确:
- 从
userStore.remainingStorage读取 - 确保
getUserProfile()API 返回正确的字节数据
四、任务分解
Phase 1: 后端基础(优先级:高)
| # | 任务 | 文件 | 说明 |
|---|---|---|---|
| 1.1 | 新增模型配置列表API | AppAiModelConfigController.java |
添加 /list-enabled 接口 |
| 1.2 | 增强用户档案服务 | MemberUserProfileServiceImpl.java |
添加存储校验和更新方法 |
| 1.3 | 新增Mapper原子更新 | MemberUserProfileMapper.java |
添加存储增减的原子操作 |
| 1.4 | 新增每日对账Job | OssQuotaReconcileJob.java |
实现自动对账 |
Phase 2: 前端积分系统(优先级:高)
| # | 任务 | 文件 | 说明 |
|---|---|---|---|
| 2.1 | 创建模型配置API | api/modelConfig.js |
封装配置获取接口 |
| 2.2 | 创建积分配置Store | stores/pointsConfig.js |
管理配置状态 |
| 2.3 | 创建积分标签组件 | components/common/PointsTag.vue |
可复用的积分显示组件 |
| 2.4 | 应用启动时加载配置 | App.vue 或入口文件 |
初始化积分配置 |
Phase 3: 业务模块集成(优先级:中)
| # | 任务 | 文件 | 说明 |
|---|---|---|---|
| 3.1 | 智能体模块集成 | ChatDrawer.vue |
显示对话积分消耗 |
| 3.2 | 数字人模块集成 | dh/Video.vue |
显示生成积分消耗 |
| 3.3 | 对标分析集成 | Benchmark.vue |
显示分析积分消耗 |
| 3.4 | 热点趋势集成 | Forecast.vue |
显示文案生成积分消耗 |
Phase 4: OSS存储管理(优先级:中)
| # | 任务 | 文件 | 说明 |
|---|---|---|---|
| 4.1 | 素材列表显示额度 | MaterialListNew.vue |
工具栏显示存储额度 |
| 4.2 | 上传前额度校验 | useUpload.js |
上传前检查额度 |
| 4.3 | 删除后释放额度 | 后端文件删除接口 | 调用 decreaseUsedStorage |
| 4.4 | 个人中心优化 | Profile.vue |
确保显示正确字节值 |
五、风险分析
| 风险 | 影响 | 对策 |
|---|---|---|
| 积分配置加载失败 | 前端显示0积分 | 添加默认值和重试机制 |
| 并发上传导致额度超限 | 超出配额 | 使用数据库乐观锁或原子操作 |
| 对账Job执行时间过长 | 影响系统性能 | 分批处理,添加超时控制 |
| 历史数据不一致 | 对账修正幅度大 | 先做数据盘点,再逐步修正 |
六、测试计划
6.1 单元测试
PointsConfigStore状态管理测试TikUserQuotaService额度计算测试- 对账Job逻辑测试
6.2 集成测试
- 前端积分显示正确性
- OSS上传额度校验
- OSS删除额度释放
- 每日对账执行
6.3 验收测试
Benchark Checklist:
1. ✅ 智能体对话页面显示"消耗 X 积分"
2. ✅ 数字人生成页面显示"消耗 X 积分"
3. ✅ 对标分析页面显示"消耗 X 积分"
4. ✅ 热点趋势页面显示"消耗 X 积分"
5. ✅ 素材列表显示存储额度进度条
6. ✅ 上传大文件超出额度时提示错误
7. ✅ 删除文件后额度正确释放
8. ✅ 个人中心显示正确的存储额度
七、实施顺序建议
Week 1: Phase 1 (后端基础) + Phase 2 (前端积分系统)
Week 2: Phase 3 (业务模块集成) + Phase 4 (OSS存储管理)
八、相关文件清单
后端文件
| 操作 | 文件路径 |
|---|---|
| 修改 | yudao-module-tik/.../aimodelconfig/AiModelConfigController.java |
| 修改 | yudao-module-tik/.../memberuserprofile/service/MemberUserProfileService.java |
| 修改 | yudao-module-tik/.../memberuserprofile/service/MemberUserProfileServiceImpl.java |
| 修改 | yudao-module-tik/.../memberuserprofile/mapper/MemberUserProfileMapper.java |
| 新增 | yudao-module-tik/.../memberuserprofile/job/OssQuotaReconcileJob.java |
前端文件
| 操作 | 文件路径 |
|---|---|
| 新增 | frontend/app/web-gold/src/api/modelConfig.js |
| 新增 | frontend/app/web-gold/src/stores/pointsConfig.js |
| 新增 | frontend/app/web-gold/src/components/common/PointsTag.vue |
| 修改 | frontend/app/web-gold/src/views/agents/ChatDrawer.vue |
| 修改 | frontend/app/web-gold/src/views/dh/Video.vue |
| 修改 | frontend/app/web-gold/src/views/content-style/Benchmark.vue |
| 修改 | frontend/app/web-gold/src/views/trends/Forecast.vue |
| 修改 | frontend/app/web-gold/src/views/material/MaterialListNew.vue |
| 修改 | frontend/app/web-gold/src/composables/useUpload.js |