Files
1818web-hoduan/POINTS_AND_MODELS_SUMMARY.md
2025-11-14 17:41:15 +08:00

557 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 积分系统与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