13 KiB
13 KiB
积分系统与AI模型管理完整功能总结
📋 目录
1. 积分消费查询功能
功能概述
用户可以查看自己的积分余额、消费明细和统计信息。
核心接口
1.1 获取积分余额
GET /user/points/consumption/balance
Authorization: Bearer {token}
响应示例:
{
"code": 200,
"message": "success",
"data": {
"currentPoints": 1500,
"pointsExpiresAt": "2025-12-31T23:59:59",
"willExpireSoon": false,
"daysUntilExpire": 120
}
}
1.2 获取积分消费记录(分页)
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: 管理员调整
响应示例:
{
"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 获取积分统计
GET /user/points/consumption/stats
Authorization: Bearer {token}
响应示例:
{
"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 获取模型列表(支持筛选)
GET /user/ai/models?taskType=text_to_image&provider=openai&enabledOnly=true
参数说明:
taskType: 任务类型(可选)provider: 服务提供商(可选:openai/runninghub)enabledOnly: 是否只返回已启用的模型(默认true)
响应示例:
{
"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 按任务类型分组获取模型
GET /user/ai/models/group-by-type?provider=&enabledOnly=true
响应示例:
{
"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 按厂商分组获取模型
GET /user/ai/models/group-by-provider?taskType=&enabledOnly=true
响应示例:
{
"code": 200,
"message": "success",
"data": [
{
"providerType": "openai",
"providerName": "OpenAI",
"models": [...],
"count": 3
},
{
"providerType": "runninghub",
"providerName": "RunningHub",
"models": [...],
"count": 5
}
]
}
2.4 获取模型统计
GET /user/ai/models/stats
响应示例:
{
"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 示例
// 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 示例
// 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 使用场景示例
<!-- 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>
<!-- 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>
📌 总结
已实现功能
- ✅ 用户积分余额查询
- ✅ 用户积分消费记录查询(分页、筛选)
- ✅ 用户积分统计信息
- ✅ AI模型列表查询(支持筛选)
- ✅ 按任务类型分组查询模型
- ✅ 按厂商分组查询模型
- ✅ 模型统计信息
技术特点
- 支持细致的任务类型分类(文生图、图生图、图生视频等)
- 向后兼容粗略分类(image、video等)
- 公开访问的模型列表接口,无需登录
- 需要登录的积分查询接口,保护用户隐私
- 完整的分页支持
- 灵活的筛选和分组功能
数据库优化
- 添加
task_type字段用于精确分类 - 添加索引提升查询性能
- 支持逻辑删除
安全性
- 积分相关接口需要用户认证
- 模型列表接口公开访问,方便前端展示
- 完整的权限控制配置
文档版本: v1.0
最后更新: 2025-10-22