[Claude Workbench] Initial commit - preserving existing code
This commit is contained in:
556
POINTS_AND_MODELS_SUMMARY.md
Normal file
556
POINTS_AND_MODELS_SUMMARY.md
Normal file
@@ -0,0 +1,556 @@
|
||||
# 积分系统与AI模型管理完整功能总结
|
||||
|
||||
## 📋 目录
|
||||
1. [积分消费查询功能](#1-积分消费查询功能)
|
||||
2. [AI模型列表查询功能](#2-ai模型列表查询功能)
|
||||
3. [数据库迁移脚本](#3-数据库迁移脚本)
|
||||
4. [API接口列表](#4-api接口列表)
|
||||
5. [前端调用示例](#5-前端调用示例)
|
||||
|
||||
---
|
||||
|
||||
## 1. 积分消费查询功能
|
||||
|
||||
### 功能概述
|
||||
用户可以查看自己的积分余额、消费明细和统计信息。
|
||||
|
||||
### 核心接口
|
||||
|
||||
#### 1.1 获取积分余额
|
||||
```http
|
||||
GET /user/points/consumption/balance
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"currentPoints": 1500,
|
||||
"pointsExpiresAt": "2025-12-31T23:59:59",
|
||||
"willExpireSoon": false,
|
||||
"daysUntilExpire": 120
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 获取积分消费记录(分页)
|
||||
```http
|
||||
GET /user/points/consumption/logs?page=1&size=10&changeType=consume
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `page`: 页码(默认1)
|
||||
- `size`: 每页数量(默认10,最大100)
|
||||
- `changeType`: 变动类型(可选)
|
||||
- `recharge`: 充值
|
||||
- `consume`: 消费
|
||||
- `refund`: 退款
|
||||
- `admin_adjust`: 管理员调整
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"id": 1,
|
||||
"taskNo": "TASK202510221234567890",
|
||||
"changeType": "consume",
|
||||
"changeTypeName": "消费",
|
||||
"changeAmount": -10,
|
||||
"balanceBefore": 1510,
|
||||
"balanceAfter": 1500,
|
||||
"description": "AI图片生成消费",
|
||||
"createTime": "2025-10-22T10:30:00"
|
||||
}
|
||||
],
|
||||
"total": 100,
|
||||
"page": 1,
|
||||
"size": 10,
|
||||
"totalPages": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3 获取积分统计
|
||||
```http
|
||||
GET /user/points/consumption/stats
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"currentPoints": 1500,
|
||||
"totalRechargePoints": 2000,
|
||||
"totalConsumePoints": 500,
|
||||
"totalRefundPoints": 0,
|
||||
"pointsExpiresAt": "2025-12-31T23:59:59"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. AI模型列表查询功能
|
||||
|
||||
### 功能概述
|
||||
用户可以查看系统中所有可用的AI模型,支持按任务类型、厂商分组查询。
|
||||
|
||||
### 任务类型分类
|
||||
|
||||
#### 细致分类(数据库 task_type 字段)
|
||||
- `text_to_image`: 文生图
|
||||
- `image_to_image`: 图生图
|
||||
- `text_to_video`: 文生视频
|
||||
- `image_to_video`: 图生视频
|
||||
- `llm`: 大语言模型
|
||||
- `text_to_audio`: 文生音频
|
||||
- `image_to_text`: 图生文
|
||||
- `other`: 其他
|
||||
|
||||
#### 粗略分类(兼容旧接口)
|
||||
- `image`: 图片生成(包括 text_to_image 和 image_to_image)
|
||||
- `video`: 视频生成(包括 text_to_video 和 image_to_video)
|
||||
- `audio`: 音频生成
|
||||
- `text`: 文本生成
|
||||
|
||||
### 核心接口
|
||||
|
||||
#### 2.1 获取模型列表(支持筛选)
|
||||
```http
|
||||
GET /user/ai/models?taskType=text_to_image&provider=openai&enabledOnly=true
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `taskType`: 任务类型(可选)
|
||||
- `provider`: 服务提供商(可选:openai/runninghub)
|
||||
- `enabledOnly`: 是否只返回已启用的模型(默认true)
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"modelName": "sora_image",
|
||||
"displayName": "Sora高质量图片生成",
|
||||
"description": "Sora高质量图片生成",
|
||||
"pointsCost": 11,
|
||||
"providerType": "openai",
|
||||
"taskType": "text_to_image",
|
||||
"isEnabled": true,
|
||||
"extendedConfig": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 按任务类型分组获取模型
|
||||
```http
|
||||
GET /user/ai/models/group-by-type?provider=&enabledOnly=true
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"taskType": "text_to_image",
|
||||
"taskTypeName": "文生图",
|
||||
"models": [
|
||||
{
|
||||
"id": 1,
|
||||
"modelName": "sora_image",
|
||||
"displayName": "Sora高质量图片生成",
|
||||
"pointsCost": 11,
|
||||
"providerType": "openai",
|
||||
"taskType": "text_to_image",
|
||||
"isEnabled": true
|
||||
}
|
||||
],
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"taskType": "text_to_video",
|
||||
"taskTypeName": "文生视频",
|
||||
"models": [...],
|
||||
"count": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 按厂商分组获取模型
|
||||
```http
|
||||
GET /user/ai/models/group-by-provider?taskType=&enabledOnly=true
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"providerType": "openai",
|
||||
"providerName": "OpenAI",
|
||||
"models": [...],
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"providerType": "runninghub",
|
||||
"providerName": "RunningHub",
|
||||
"models": [...],
|
||||
"count": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.4 获取模型统计
|
||||
```http
|
||||
GET /user/ai/models/stats
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"totalModels": 10,
|
||||
"enabledModels": 8,
|
||||
"countByType": {
|
||||
"text_to_image": 2,
|
||||
"text_to_video": 4,
|
||||
"image_to_video": 1,
|
||||
"llm": 1
|
||||
},
|
||||
"countByProvider": {
|
||||
"openai": 3,
|
||||
"runninghub": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据库迁移脚本
|
||||
|
||||
### V6: 积分充值系统
|
||||
- 创建 `points_package` 表(积分套餐)
|
||||
- 扩展 `order` 表支持积分订单
|
||||
- 更新 `points_consumption_log` 表支持充值类型
|
||||
|
||||
### V7: 任务类型细分
|
||||
- 为 `points_config` 表添加 `task_type` 字段
|
||||
- 根据现有模型名称更新任务类型
|
||||
- 添加多种模型类型示例数据
|
||||
- 添加索引优化查询性能
|
||||
|
||||
---
|
||||
|
||||
## 4. API接口列表
|
||||
|
||||
### 4.1 积分消费查询(需要登录)
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 获取积分余额 | GET | `/user/points/consumption/balance` | 当前积分和过期时间 |
|
||||
| 获取消费记录 | GET | `/user/points/consumption/logs` | 分页查询消费明细 |
|
||||
| 获取积分统计 | GET | `/user/points/consumption/stats` | 累计充值、消费、退款 |
|
||||
|
||||
### 4.2 AI模型查询(公开访问)
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 获取模型列表 | GET | `/user/ai/models` | 支持筛选和过滤 |
|
||||
| 按类型分组 | GET | `/user/ai/models/group-by-type` | 按任务类型分组 |
|
||||
| 按厂商分组 | GET | `/user/ai/models/group-by-provider` | 按服务提供商分组 |
|
||||
| 获取统计信息 | GET | `/user/ai/models/stats` | 模型数量统计 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 前端调用示例
|
||||
|
||||
### 5.1 Vue 3 + TypeScript 示例
|
||||
|
||||
```typescript
|
||||
// api/points.ts
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取积分余额
|
||||
export function getPointsBalance() {
|
||||
return request({
|
||||
url: '/user/points/consumption/balance',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取积分消费记录
|
||||
export function getConsumptionLogs(params: {
|
||||
page?: number
|
||||
size?: number
|
||||
changeType?: 'recharge' | 'consume' | 'refund' | 'admin_adjust'
|
||||
}) {
|
||||
return request({
|
||||
url: '/user/points/consumption/logs',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取积分统计
|
||||
export function getConsumptionStats() {
|
||||
return request({
|
||||
url: '/user/points/consumption/stats',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// api/models.ts
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取所有模型
|
||||
export function getAllModels(params: {
|
||||
taskType?: string
|
||||
provider?: string
|
||||
enabledOnly?: boolean
|
||||
}) {
|
||||
return request({
|
||||
url: '/user/ai/models',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 按类型分组获取模型
|
||||
export function getModelsByType(params: {
|
||||
provider?: string
|
||||
enabledOnly?: boolean
|
||||
}) {
|
||||
return request({
|
||||
url: '/user/ai/models/group-by-type',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 按厂商分组获取模型
|
||||
export function getModelsByProvider(params: {
|
||||
taskType?: string
|
||||
enabledOnly?: boolean
|
||||
}) {
|
||||
return request({
|
||||
url: '/user/ai/models/group-by-provider',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取模型统计
|
||||
export function getModelStats() {
|
||||
return request({
|
||||
url: '/user/ai/models/stats',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 React 示例
|
||||
|
||||
```typescript
|
||||
// hooks/usePoints.ts
|
||||
import { useState, useEffect } from 'react'
|
||||
import { getPointsBalance, getConsumptionLogs } from '@/api/points'
|
||||
|
||||
export function usePointsBalance() {
|
||||
const [balance, setBalance] = useState(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
getPointsBalance().then(res => {
|
||||
setBalance(res.data)
|
||||
setLoading(false)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return { balance, loading }
|
||||
}
|
||||
|
||||
// hooks/useModels.ts
|
||||
import { useState, useEffect } from 'react'
|
||||
import { getModelsByType } from '@/api/models'
|
||||
|
||||
export function useModelsByType(provider?: string) {
|
||||
const [models, setModels] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
getModelsByType({ provider, enabledOnly: true }).then(res => {
|
||||
setModels(res.data)
|
||||
setLoading(false)
|
||||
})
|
||||
}, [provider])
|
||||
|
||||
return { models, loading }
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 使用场景示例
|
||||
|
||||
```vue
|
||||
<!-- PointsBalance.vue -->
|
||||
<template>
|
||||
<div class="points-balance">
|
||||
<div class="balance-card">
|
||||
<h3>当前积分</h3>
|
||||
<p class="points">{{ balance?.currentPoints || 0 }}</p>
|
||||
<p class="expire-info" v-if="balance?.pointsExpiresAt">
|
||||
{{ balance.willExpireSoon ? '即将过期' : '有效期至' }}:
|
||||
{{ formatDate(balance.pointsExpiresAt) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="consumption-logs">
|
||||
<h3>消费记录</h3>
|
||||
<div v-for="log in logs.records" :key="log.id" class="log-item">
|
||||
<span>{{ log.changeTypeName }}</span>
|
||||
<span>{{ log.description }}</span>
|
||||
<span :class="log.changeAmount > 0 ? 'increase' : 'decrease'">
|
||||
{{ log.changeAmount > 0 ? '+' : '' }}{{ log.changeAmount }}
|
||||
</span>
|
||||
<span>{{ formatDate(log.createTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getPointsBalance, getConsumptionLogs } from '@/api/points'
|
||||
|
||||
const balance = ref(null)
|
||||
const logs = ref({ records: [], total: 0 })
|
||||
|
||||
onMounted(async () => {
|
||||
const [balanceRes, logsRes] = await Promise.all([
|
||||
getPointsBalance(),
|
||||
getConsumptionLogs({ page: 1, size: 10 })
|
||||
])
|
||||
balance.value = balanceRes.data
|
||||
logs.value = logsRes.data
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
```vue
|
||||
<!-- ModelSelector.vue -->
|
||||
<template>
|
||||
<div class="model-selector">
|
||||
<div class="filter">
|
||||
<select v-model="selectedType">
|
||||
<option value="">全部类型</option>
|
||||
<option value="text_to_image">文生图</option>
|
||||
<option value="text_to_video">文生视频</option>
|
||||
<option value="image_to_video">图生视频</option>
|
||||
</select>
|
||||
|
||||
<select v-model="selectedProvider">
|
||||
<option value="">全部厂商</option>
|
||||
<option value="openai">OpenAI</option>
|
||||
<option value="runninghub">RunningHub</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="models-grid">
|
||||
<div v-for="model in filteredModels" :key="model.id" class="model-card">
|
||||
<h4>{{ model.displayName }}</h4>
|
||||
<p>{{ model.description }}</p>
|
||||
<div class="model-footer">
|
||||
<span class="cost">{{ model.pointsCost }} 积分</span>
|
||||
<span class="provider">{{ model.providerType }}</span>
|
||||
</div>
|
||||
<button @click="selectModel(model)">选择</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getAllModels } from '@/api/models'
|
||||
|
||||
const selectedType = ref('')
|
||||
const selectedProvider = ref('')
|
||||
const allModels = ref([])
|
||||
|
||||
const filteredModels = computed(() => {
|
||||
return allModels.value.filter(model => {
|
||||
const typeMatch = !selectedType.value || model.taskType === selectedType.value
|
||||
const providerMatch = !selectedProvider.value || model.providerType === selectedProvider.value
|
||||
return typeMatch && providerMatch
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await getAllModels({ enabledOnly: true })
|
||||
allModels.value = res.data
|
||||
})
|
||||
|
||||
const selectModel = (model) => {
|
||||
// 选择模型逻辑
|
||||
console.log('Selected model:', model)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 总结
|
||||
|
||||
### 已实现功能
|
||||
1. ✅ 用户积分余额查询
|
||||
2. ✅ 用户积分消费记录查询(分页、筛选)
|
||||
3. ✅ 用户积分统计信息
|
||||
4. ✅ AI模型列表查询(支持筛选)
|
||||
5. ✅ 按任务类型分组查询模型
|
||||
6. ✅ 按厂商分组查询模型
|
||||
7. ✅ 模型统计信息
|
||||
|
||||
### 技术特点
|
||||
- 支持细致的任务类型分类(文生图、图生图、图生视频等)
|
||||
- 向后兼容粗略分类(image、video等)
|
||||
- 公开访问的模型列表接口,无需登录
|
||||
- 需要登录的积分查询接口,保护用户隐私
|
||||
- 完整的分页支持
|
||||
- 灵活的筛选和分组功能
|
||||
|
||||
### 数据库优化
|
||||
- 添加 `task_type` 字段用于精确分类
|
||||
- 添加索引提升查询性能
|
||||
- 支持逻辑删除
|
||||
|
||||
### 安全性
|
||||
- 积分相关接口需要用户认证
|
||||
- 模型列表接口公开访问,方便前端展示
|
||||
- 完整的权限控制配置
|
||||
|
||||
---
|
||||
|
||||
**文档版本:** v1.0
|
||||
**最后更新:** 2025-10-22
|
||||
|
||||
Reference in New Issue
Block a user