# 积分系统与OSS额度系统实施计划 > 创建日期:2026-02-25 > 状态:待审批 --- ## 一、需求概述 ### 1.1 业务需求 | 模块 | 需求描述 | |------|----------| | **前端积分系统** | 前端启动时加载 `muye_ai_model_config`,根据 model_code 获取积分消耗,显示在对标分析、热点趋势、智能体、数字人模块 | | **OSS额度系统** | 后端统一用字节计算,仅统计已上传未删除文件,上传前校验,删除后释放,每日凌晨自动对账 | | **OSS存储管理** | 素材列表和个人中心显示用户正确的额度 | ### 1.2 验收标准 1. 前端各业务模块正确显示积分消耗数值 2. OSS 上传前校验额度,超额拒绝上传 3. OSS 文件删除后实时释放额度 4. 每日凌晨自动对账,确保数据库记录与实际一致 --- ## 二、现有架构分析 ### 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 设计 ```javascript // 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 设计 ```javascript // 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 积分标签组件 ```vue ``` #### 3.1.6 各模块集成点 | 模块 | 文件位置 | 集成方式 | |------|----------|----------| | **智能体** | `ChatDrawer.vue` | 在发送按钮旁显示积分消耗 | | **数字人** | `Video.vue` | 在生成按钮旁显示积分消耗 | | **对标分析** | `Benchmark.vue` | 在分析按钮旁显示积分消耗 | | **热点趋势** | `Forecast.vue` | 在文案生成按钮旁显示积分消耗 | --- ### 3.2 OSS 额度系统 #### 3.2.1 数据模型 **MemberUserProfileDO 字段(muye_member_user_profile 表):** ```java 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):** ```sql -- 文件大小字段(字节) file_size BIGINT NOT NULL COMMENT '文件大小(字节)' ``` **单位转换常量:** ```java // 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 增强 ```java // 新增方法 /** * 校验存储空间是否足够 * @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 原子更新 ```java // 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 ```java @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
{{ formatStorage(usedStorage) }} / {{ formatStorage(totalStorage) }}
``` #### 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` |