213
server/model/app/README.md
Normal file
213
server/model/app/README.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# App 前台应用数据模型
|
||||
|
||||
## 📋 模型列表
|
||||
|
||||
本目录包含所有前台用户应用相关的数据模型,与管理后台的 `system` 模块完全独立。
|
||||
|
||||
### 1. 用户相关模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `app_user.go` | `AppUser` | `app_users` | 前台用户表 |
|
||||
| `app_user_session.go` | `AppUserSession` | `app_user_sessions` | 用户会话表 |
|
||||
|
||||
### 2. AI 角色相关模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `ai_character.go` | `AICharacter` | `ai_characters` | AI 角色表 |
|
||||
| `ai_character.go` | `AppUserFavoriteCharacter` | `app_user_favorite_characters` | 用户收藏角色表 |
|
||||
|
||||
### 3. 对话相关模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `ai_chat.go` | `AIChat` | `ai_chats` | 对话表 |
|
||||
| `ai_chat.go` | `AIChatMember` | `ai_chat_members` | 群聊成员表 |
|
||||
| `ai_message.go` | `AIMessage` | `ai_messages` | 消息表 |
|
||||
| `ai_message.go` | `AIMessageSwipe` | `ai_message_swipes` | 消息变体表 |
|
||||
|
||||
### 4. 向量记忆模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `ai_memory.go` | `AIMemoryVector` | `ai_memory_vectors` | 向量记忆表(使用 pgvector) |
|
||||
|
||||
### 5. AI 服务配置模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `ai_provider.go` | `AIProvider` | `ai_providers` | AI 提供商配置表 |
|
||||
| `ai_provider.go` | `AIModel` | `ai_models` | AI 模型配置表 |
|
||||
|
||||
### 6. 文件管理模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `ai_file.go` | `AIFile` | `ai_files` | 文件表 |
|
||||
|
||||
### 7. 其他模型
|
||||
|
||||
| 文件 | 模型 | 表名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `ai_preset.go` | `AIPreset` | `ai_presets` | 对话预设表 |
|
||||
| `ai_world_info.go` | `AIWorldInfo` | `ai_world_info` | 世界书表 |
|
||||
| `ai_usage_stat.go` | `AIUsageStat` | `ai_usage_stats` | 使用统计表 |
|
||||
|
||||
## 🔧 使用说明
|
||||
|
||||
### 1. 数据库自动迁移
|
||||
|
||||
所有模型已在 `initialize/gorm.go` 中注册,启动服务时会自动创建表:
|
||||
|
||||
```go
|
||||
// 在 RegisterTables() 函数中已注册
|
||||
app.AppUser{},
|
||||
app.AppUserSession{},
|
||||
app.AICharacter{},
|
||||
app.AppUserFavoriteCharacter{},
|
||||
app.AIChat{},
|
||||
app.AIChatMember{},
|
||||
app.AIMessage{},
|
||||
app.AIMessageSwipe{},
|
||||
app.AIMemoryVector{},
|
||||
app.AIProvider{},
|
||||
app.AIModel{},
|
||||
app.AIFile{},
|
||||
app.AIPreset{},
|
||||
app.AIWorldInfo{},
|
||||
app.AIUsageStat{},
|
||||
```
|
||||
|
||||
### 2. PostgreSQL 向量扩展
|
||||
|
||||
向量记忆功能依赖 `pgvector` 扩展,已在 `initialize/gorm_pgsql_extension.go` 中自动安装:
|
||||
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
CREATE INDEX idx_memory_vectors_embedding ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops);
|
||||
```
|
||||
|
||||
### 3. 外键关系
|
||||
|
||||
模型之间的关系已通过 GORM 标签定义:
|
||||
|
||||
- `AppUser` ← `AppUserSession`(一对多)
|
||||
- `AppUser` ← `AICharacter`(一对多,创建者)
|
||||
- `AppUser` ← `AIChat`(一对多)
|
||||
- `AppUser` ← `AppUserFavoriteCharacter`(多对多,通过中间表)
|
||||
- `AICharacter` ← `AppUserFavoriteCharacter`(多对多,通过中间表)
|
||||
- `AICharacter` ← `AIChat`(一对多)
|
||||
- `AIChat` ← `AIMessage`(一对多)
|
||||
- `AIChat` ← `AIChatMember`(多对多,通过中间表)
|
||||
- `AICharacter` ← `AIChatMember`(多对多,通过中间表)
|
||||
- `AIMessage` ← `AIMessageSwipe`(一对多)
|
||||
- `AIProvider` ← `AIModel`(一对多)
|
||||
|
||||
### 4. JSONB 字段
|
||||
|
||||
以下字段使用 PostgreSQL 的 JSONB 类型:
|
||||
|
||||
- `AppUser.AISettings` - AI 相关配置
|
||||
- `AppUser.Preferences` - 用户偏好设置
|
||||
- `AICharacter.CardData` - 角色卡片数据
|
||||
- `AICharacter.Tags` - 角色标签
|
||||
- `AICharacter.ExampleMessages` - 消息示例
|
||||
- `AIChat.Settings` - 对话设置
|
||||
- `AIMessage.GenerationParams` - AI 生成参数
|
||||
- `AIMessage.Metadata` - 消息元数据
|
||||
- `AIMemoryVector.Metadata` - 记忆元数据
|
||||
- `AIProvider.APIConfig` - API 配置
|
||||
- `AIModel.Config` - 模型配置
|
||||
- `AIFile.RelatedTo` - 文件关联对象
|
||||
- `AIFile.Metadata` - 文件元数据
|
||||
- `AIPreset.Config` - 预设配置
|
||||
- `AIWorldInfo.TriggerConfig` - 触发条件配置
|
||||
|
||||
### 5. 向量字段
|
||||
|
||||
`AIMemoryVector.Embedding` 使用 `pgvector.Vector` 类型,维度为 1536(OpenAI text-embedding-ada-002)。
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **不要修改 system 包**:所有管理后台相关的模型在 `model/system/` 包中,**不要修改**
|
||||
2. **表名前缀**:
|
||||
- 前台用户相关:`app_*`
|
||||
- AI 功能相关:`ai_*`
|
||||
- 系统管理相关:`sys_*`(不修改)
|
||||
3. **UUID 生成**:`AppUser.UUID` 使用数据库自动生成(PostgreSQL 的 `gen_random_uuid()`)
|
||||
4. **软删除**:所有模型继承 `global.GVA_MODEL`,自动支持软删除
|
||||
5. **时间字段**:`CreatedAt`、`UpdatedAt`、`DeletedAt` 由 GORM 自动管理
|
||||
|
||||
## 📊 ER 图关系
|
||||
|
||||
```
|
||||
AppUser (前台用户)
|
||||
├── AppUserSession (会话)
|
||||
├── AICharacter (创建的角色)
|
||||
├── AIChat (对话)
|
||||
├── AppUserFavoriteCharacter (收藏的角色)
|
||||
├── AIMemoryVector (记忆)
|
||||
├── AIProvider (AI 提供商配置)
|
||||
├── AIFile (文件)
|
||||
├── AIPreset (预设)
|
||||
├── AIWorldInfo (世界书)
|
||||
└── AIUsageStat (使用统计)
|
||||
|
||||
AICharacter (AI 角色)
|
||||
├── AIChat (对话)
|
||||
├── AIChatMember (群聊成员)
|
||||
├── AppUserFavoriteCharacter (被收藏)
|
||||
└── AIMemoryVector (记忆)
|
||||
|
||||
AIChat (对话)
|
||||
├── AIMessage (消息)
|
||||
├── AIChatMember (群聊成员)
|
||||
└── AIMemoryVector (记忆)
|
||||
|
||||
AIMessage (消息)
|
||||
└── AIMessageSwipe (消息变体)
|
||||
|
||||
AIProvider (AI 提供商)
|
||||
└── AIModel (AI 模型)
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
1. 确保 PostgreSQL 已安装 pgvector 扩展
|
||||
2. 配置 `config.yaml` 中的数据库连接
|
||||
3. 启动服务,AutoMigrate 会自动创建所有表
|
||||
4. 检查日志确认表创建成功
|
||||
|
||||
```bash
|
||||
# 启动服务
|
||||
go run main.go
|
||||
|
||||
# 查看日志
|
||||
# [GVA] pgvector extension is ready
|
||||
# [GVA] vector indexes created successfully
|
||||
# [GVA] register table success
|
||||
```
|
||||
|
||||
## 📝 开发建议
|
||||
|
||||
1. 查询时使用预加载避免 N+1 问题:
|
||||
```go
|
||||
db.Preload("User").Preload("Character").Find(&chats)
|
||||
```
|
||||
|
||||
2. 向量搜索示例:
|
||||
```go
|
||||
db.Order("embedding <=> ?", queryVector).Limit(10).Find(&memories)
|
||||
```
|
||||
|
||||
3. JSONB 查询示例:
|
||||
```go
|
||||
db.Where("ai_settings->>'model' = ?", "gpt-4").Find(&users)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**创建日期**: 2026-02-10
|
||||
**维护者**: 开发团队
|
||||
49
server/model/app/ai_character.go
Normal file
49
server/model/app/ai_character.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AICharacter 角色卡模型
|
||||
type AICharacter struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
// 基本信息
|
||||
UserID uint `gorm:"index;not null" json:"userId"` // 所属用户ID
|
||||
Name string `gorm:"type:varchar(100);not null" json:"name"` // 角色名称
|
||||
Avatar string `gorm:"type:text" json:"avatar"` // 头像URL或Base64
|
||||
Creator string `gorm:"type:varchar(100)" json:"creator"` // 创建者
|
||||
Version string `gorm:"type:varchar(50)" json:"version"` // 角色版本
|
||||
IsPublic bool `gorm:"default:false" json:"isPublic"` // 是否公开
|
||||
|
||||
// SillyTavern V2 格式字段
|
||||
Description string `gorm:"type:text" json:"description"` // 角色描述
|
||||
Personality string `gorm:"type:text" json:"personality"` // 性格特征
|
||||
Scenario string `gorm:"type:text" json:"scenario"` // 场景设定
|
||||
FirstMes string `gorm:"type:text" json:"firstMes"` // 第一条消息
|
||||
MesExample string `gorm:"type:text" json:"mesExample"` // 消息示例
|
||||
CreatorNotes string `gorm:"type:text" json:"creatorNotes"` // 创建者备注
|
||||
SystemPrompt string `gorm:"type:text" json:"systemPrompt"` // 系统提示词
|
||||
PostHistoryInstructions string `gorm:"type:text" json:"postHistoryInstructions"` // 历史后指令
|
||||
Tags datatypes.JSON `gorm:"type:jsonb" json:"tags"` // 标签数组
|
||||
AlternateGreetings datatypes.JSON `gorm:"type:jsonb" json:"alternateGreetings"` // 备用问候语
|
||||
CharacterBook datatypes.JSON `gorm:"type:jsonb" json:"characterBook"` // 角色书
|
||||
Extensions datatypes.JSON `gorm:"type:jsonb" json:"extensions"` // 扩展数据
|
||||
Spec string `gorm:"type:varchar(50);default:'chara_card_v2'" json:"spec"` // 规范名称
|
||||
SpecVersion string `gorm:"type:varchar(50);default:'2.0'" json:"specVersion"` // 规范版本
|
||||
|
||||
// 统计信息
|
||||
UseCount int `gorm:"default:0" json:"useCount"` // 使用次数
|
||||
FavoriteCount int `gorm:"default:0" json:"favoriteCount"` // 收藏次数
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (AICharacter) TableName() string {
|
||||
return "ai_characters"
|
||||
}
|
||||
31
server/model/app/ai_config.go
Normal file
31
server/model/app/ai_config.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AIConfig AI配置模型
|
||||
type AIConfig struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
Name string `gorm:"type:varchar(100);not null" json:"name"` // 配置名称
|
||||
Provider string `gorm:"type:varchar(50);not null" json:"provider"` // 提供商 (openai/anthropic/custom)
|
||||
BaseURL string `gorm:"type:varchar(500)" json:"baseUrl"` // API Base URL
|
||||
APIKey string `gorm:"type:varchar(500)" json:"apiKey"` // API Key (加密存储)
|
||||
Models datatypes.JSON `gorm:"type:jsonb" json:"models"` // 可用模型列表
|
||||
DefaultModel string `gorm:"type:varchar(100)" json:"defaultModel"` // 默认模型
|
||||
Settings datatypes.JSON `gorm:"type:jsonb" json:"settings"` // 其他设置 (temperature等)
|
||||
IsActive bool `gorm:"default:false" json:"isActive"` // 是否激活
|
||||
IsDefault bool `gorm:"default:false" json:"isDefault"` // 是否为默认配置
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (AIConfig) TableName() string {
|
||||
return "ai_configs"
|
||||
}
|
||||
33
server/model/app/app_user.go
Normal file
33
server/model/app/app_user.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// AppUser 前台用户模型(与 sys_users 独立)
|
||||
type AppUser struct {
|
||||
global.GVA_MODEL
|
||||
UUID string `json:"uuid" gorm:"type:uuid;uniqueIndex;comment:用户UUID"`
|
||||
Username string `json:"username" gorm:"uniqueIndex;comment:用户登录名"`
|
||||
Password string `json:"-" gorm:"comment:用户登录密码"`
|
||||
NickName string `json:"nickName" gorm:"comment:用户昵称"`
|
||||
Email string `json:"email" gorm:"index;comment:用户邮箱"`
|
||||
Phone string `json:"phone" gorm:"comment:用户手机号"`
|
||||
Avatar string `json:"avatar" gorm:"type:varchar(1024);comment:用户头像"`
|
||||
Status string `json:"status" gorm:"type:varchar(50);default:active;comment:账户状态"`
|
||||
Enable bool `json:"enable" gorm:"default:true;comment:用户是否启用"`
|
||||
IsAdmin bool `json:"isAdmin" gorm:"default:false;comment:是否为管理员"`
|
||||
LastLoginAt *time.Time `json:"lastLoginAt" gorm:"comment:最后登录时间"`
|
||||
LastLoginIP string `json:"lastLoginIp" gorm:"type:varchar(100);comment:最后登录IP"`
|
||||
AISettings datatypes.JSON `json:"aiSettings" gorm:"type:jsonb;comment:AI配置"`
|
||||
Preferences datatypes.JSON `json:"preferences" gorm:"type:jsonb;comment:用户偏好"`
|
||||
ChatCount int `json:"chatCount" gorm:"default:0;comment:对话数量"`
|
||||
MessageCount int `json:"messageCount" gorm:"default:0;comment:消息数量"`
|
||||
}
|
||||
|
||||
func (AppUser) TableName() string {
|
||||
return "app_users"
|
||||
}
|
||||
25
server/model/app/app_user_session.go
Normal file
25
server/model/app/app_user_session.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// AppUserSession 前台用户会话
|
||||
type AppUserSession struct {
|
||||
global.GVA_MODEL
|
||||
UserID uint `json:"userId" gorm:"index;comment:用户ID"`
|
||||
SessionToken string `json:"sessionToken" gorm:"type:varchar(500);uniqueIndex;comment:会话Token"`
|
||||
RefreshToken string `json:"refreshToken" gorm:"type:varchar(500);comment:刷新Token"`
|
||||
ExpiresAt time.Time `json:"expiresAt" gorm:"index;comment:过期时间"`
|
||||
RefreshExpiresAt *time.Time `json:"refreshExpiresAt" gorm:"comment:刷新Token过期时间"`
|
||||
IPAddress string `json:"ipAddress" gorm:"type:varchar(100);comment:IP地址"`
|
||||
UserAgent string `json:"userAgent" gorm:"type:text;comment:用户代理"`
|
||||
DeviceInfo datatypes.JSON `json:"deviceInfo" gorm:"type:jsonb;comment:设备信息"`
|
||||
}
|
||||
|
||||
func (AppUserSession) TableName() string {
|
||||
return "app_user_sessions"
|
||||
}
|
||||
56
server/model/app/conversation.go
Normal file
56
server/model/app/conversation.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Conversation 对话会话模型
|
||||
type Conversation struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
UserID uint `gorm:"index;not null" json:"userId"` // 所属用户ID
|
||||
CharacterID uint `gorm:"index;not null" json:"characterId"` // 角色卡ID
|
||||
Title string `gorm:"type:varchar(200)" json:"title"` // 对话标题
|
||||
|
||||
// 对话配置
|
||||
PresetID *uint `gorm:"index" json:"presetId"` // 使用的预设ID
|
||||
AIProvider string `gorm:"type:varchar(50)" json:"aiProvider"` // AI提供商 (openai/anthropic)
|
||||
Model string `gorm:"type:varchar(100)" json:"model"` // 使用的模型
|
||||
Settings datatypes.JSON `gorm:"type:jsonb" json:"settings"` // 对话设置 (temperature等)
|
||||
|
||||
// 统计信息
|
||||
MessageCount int `gorm:"default:0" json:"messageCount"` // 消息数量
|
||||
TokenCount int `gorm:"default:0" json:"tokenCount"` // Token使用量
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Conversation) TableName() string {
|
||||
return "conversations"
|
||||
}
|
||||
|
||||
// Message 消息模型
|
||||
type Message struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
ConversationID uint `gorm:"index;not null" json:"conversationId"` // 所属对话ID
|
||||
Role string `gorm:"type:varchar(20);not null" json:"role"` // 角色 (user/assistant/system)
|
||||
Content string `gorm:"type:text;not null" json:"content"` // 消息内容
|
||||
|
||||
// 元数据
|
||||
TokenCount int `gorm:"default:0" json:"tokenCount"` // Token数量
|
||||
Metadata datatypes.JSON `gorm:"type:jsonb" json:"metadata"` // 额外元数据
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Message) TableName() string {
|
||||
return "messages"
|
||||
}
|
||||
44
server/model/app/preset.go
Normal file
44
server/model/app/preset.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AIPreset AI预设配置模型
|
||||
type AIPreset struct {
|
||||
gorm.Model
|
||||
UserID uint `json:"userId" gorm:"not null;index:idx_preset_user"`
|
||||
Name string `json:"name" gorm:"not null;size:100"`
|
||||
Description string `json:"description" gorm:"size:500"`
|
||||
IsPublic bool `json:"isPublic" gorm:"default:false;index:idx_preset_public"`
|
||||
IsDefault bool `json:"isDefault" gorm:"default:false"`
|
||||
|
||||
// Sampling Parameters (stored as individual fields for query efficiency)
|
||||
Temperature float64 `json:"temperature" gorm:"default:1.0"`
|
||||
TopP float64 `json:"topP" gorm:"default:1.0"`
|
||||
TopK int `json:"topK" gorm:"default:0"`
|
||||
FrequencyPenalty float64 `json:"frequencyPenalty" gorm:"default:0"`
|
||||
PresencePenalty float64 `json:"presencePenalty" gorm:"default:0"`
|
||||
MaxTokens int `json:"maxTokens" gorm:"default:2000"`
|
||||
RepetitionPenalty float64 `json:"repetitionPenalty" gorm:"default:1.0"`
|
||||
MinP float64 `json:"minP" gorm:"default:0"`
|
||||
TopA float64 `json:"topA" gorm:"default:0"`
|
||||
|
||||
// Prompt Configuration (stored as JSON for flexibility)
|
||||
SystemPrompt string `json:"systemPrompt" gorm:"type:text"`
|
||||
StopSequences datatypes.JSON `json:"stopSequences" gorm:"type:jsonb"`
|
||||
|
||||
// SillyTavern Extensions (for full compatibility)
|
||||
Extensions datatypes.JSON `json:"extensions" gorm:"type:jsonb"`
|
||||
|
||||
// Usage Statistics
|
||||
UseCount int `json:"useCount" gorm:"default:0"`
|
||||
|
||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (AIPreset) TableName() string {
|
||||
return "ai_presets"
|
||||
}
|
||||
37
server/model/app/request/ai_config.go
Normal file
37
server/model/app/request/ai_config.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package request
|
||||
|
||||
// CreateAIConfigRequest 创建AI配置请求
|
||||
type CreateAIConfigRequest struct {
|
||||
Name string `json:"name" binding:"required,max=100"`
|
||||
Provider string `json:"provider" binding:"required,oneof=openai anthropic custom"`
|
||||
BaseURL string `json:"baseUrl" binding:"required,url"`
|
||||
APIKey string `json:"apiKey" binding:"required"`
|
||||
DefaultModel string `json:"defaultModel"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
}
|
||||
|
||||
// UpdateAIConfigRequest 更新AI配置请求
|
||||
type UpdateAIConfigRequest struct {
|
||||
Name string `json:"name" binding:"max=100"`
|
||||
BaseURL string `json:"baseUrl" binding:"omitempty,url"`
|
||||
APIKey string `json:"apiKey"`
|
||||
DefaultModel string `json:"defaultModel"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
IsActive *bool `json:"isActive"`
|
||||
IsDefault *bool `json:"isDefault"`
|
||||
}
|
||||
|
||||
// TestAIConfigRequest 测试AI配置请求
|
||||
type TestAIConfigRequest struct {
|
||||
Provider string `json:"provider" binding:"required,oneof=openai anthropic custom"`
|
||||
BaseURL string `json:"baseUrl" binding:"required,url"`
|
||||
APIKey string `json:"apiKey" binding:"required"`
|
||||
Model string `json:"model"`
|
||||
}
|
||||
|
||||
// GetModelsRequest 获取模型列表请求
|
||||
type GetModelsRequest struct {
|
||||
Provider string `json:"provider" binding:"required,oneof=openai anthropic custom"`
|
||||
BaseURL string `json:"baseUrl" binding:"required,url"`
|
||||
APIKey string `json:"apiKey" binding:"required"`
|
||||
}
|
||||
37
server/model/app/request/auth.go
Normal file
37
server/model/app/request/auth.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package request
|
||||
|
||||
// RegisterRequest 用户注册请求
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=32"`
|
||||
Password string `json:"password" binding:"required,min=6,max=32"`
|
||||
NickName string `json:"nickName" binding:"max=50"`
|
||||
Email string `json:"email" binding:"omitempty,email"`
|
||||
Phone string `json:"phone" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// LoginRequest 用户登录请求
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// RefreshTokenRequest 刷新 Token 请求
|
||||
type RefreshTokenRequest struct {
|
||||
RefreshToken string `json:"refreshToken" binding:"required"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest 修改密码请求
|
||||
type ChangePasswordRequest struct {
|
||||
OldPassword string `json:"oldPassword" binding:"required"`
|
||||
NewPassword string `json:"newPassword" binding:"required,min=6,max=32"`
|
||||
}
|
||||
|
||||
// UpdateProfileRequest 更新用户信息请求
|
||||
type UpdateProfileRequest struct {
|
||||
NickName string `json:"nickName" binding:"max=50"`
|
||||
Email string `json:"email" binding:"omitempty,email"`
|
||||
Phone string `json:"phone"`
|
||||
Avatar string `json:"avatar"`
|
||||
Preferences string `json:"preferences"` // JSON 字符串
|
||||
AISettings string `json:"aiSettings"` // JSON 字符串
|
||||
}
|
||||
52
server/model/app/request/character.go
Normal file
52
server/model/app/request/character.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package request
|
||||
|
||||
// CreateCharacterRequest 创建角色卡请求
|
||||
type CreateCharacterRequest struct {
|
||||
Name string `json:"name" binding:"required,max=100"`
|
||||
Avatar string `json:"avatar"`
|
||||
Creator string `json:"creator" binding:"max=100"`
|
||||
Version string `json:"version" binding:"max=50"`
|
||||
Description string `json:"description"`
|
||||
Personality string `json:"personality"`
|
||||
Scenario string `json:"scenario"`
|
||||
FirstMes string `json:"firstMes"`
|
||||
MesExample string `json:"mesExample"`
|
||||
CreatorNotes string `json:"creatorNotes"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
PostHistoryInstructions string `json:"postHistoryInstructions"`
|
||||
Tags []string `json:"tags"`
|
||||
AlternateGreetings []string `json:"alternateGreetings"`
|
||||
CharacterBook map[string]interface{} `json:"characterBook"`
|
||||
Extensions map[string]interface{} `json:"extensions"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
}
|
||||
|
||||
// UpdateCharacterRequest 更新角色卡请求
|
||||
type UpdateCharacterRequest struct {
|
||||
Name string `json:"name" binding:"max=100"`
|
||||
Avatar string `json:"avatar"`
|
||||
Creator string `json:"creator" binding:"max=100"`
|
||||
Version string `json:"version" binding:"max=50"`
|
||||
Description string `json:"description"`
|
||||
Personality string `json:"personality"`
|
||||
Scenario string `json:"scenario"`
|
||||
FirstMes string `json:"firstMes"`
|
||||
MesExample string `json:"mesExample"`
|
||||
CreatorNotes string `json:"creatorNotes"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
PostHistoryInstructions string `json:"postHistoryInstructions"`
|
||||
Tags []string `json:"tags"`
|
||||
AlternateGreetings []string `json:"alternateGreetings"`
|
||||
CharacterBook map[string]interface{} `json:"characterBook"`
|
||||
Extensions map[string]interface{} `json:"extensions"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
}
|
||||
|
||||
// GetCharacterListRequest 获取角色卡列表请求
|
||||
type GetCharacterListRequest struct {
|
||||
Page int `form:"page" binding:"min=1"`
|
||||
PageSize int `form:"pageSize" binding:"min=1,max=100"`
|
||||
Keyword string `form:"keyword"`
|
||||
Tag string `form:"tag"`
|
||||
IsPublic *bool `form:"isPublic"`
|
||||
}
|
||||
32
server/model/app/request/conversation.go
Normal file
32
server/model/app/request/conversation.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package request
|
||||
|
||||
// CreateConversationRequest 创建对话请求
|
||||
type CreateConversationRequest struct {
|
||||
CharacterID uint `json:"characterId" binding:"required"`
|
||||
Title string `json:"title" binding:"max=200"`
|
||||
PresetID *uint `json:"presetId"`
|
||||
AIProvider string `json:"aiProvider" binding:"omitempty,oneof=openai anthropic"`
|
||||
Model string `json:"model"`
|
||||
}
|
||||
|
||||
// SendMessageRequest 发送消息请求
|
||||
type SendMessageRequest struct {
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
// GetConversationListRequest 获取对话列表请求
|
||||
type GetConversationListRequest struct {
|
||||
Page int `form:"page" binding:"min=1"`
|
||||
PageSize int `form:"pageSize" binding:"min=1,max=100"`
|
||||
}
|
||||
|
||||
// GetMessageListRequest 获取消息列表请求
|
||||
type GetMessageListRequest struct {
|
||||
Page int `form:"page" binding:"min=1"`
|
||||
PageSize int `form:"pageSize" binding:"min=1,max=100"`
|
||||
}
|
||||
|
||||
// UpdateConversationSettingsRequest 更新对话设置请求
|
||||
type UpdateConversationSettingsRequest struct {
|
||||
Settings map[string]interface{} `json:"settings" binding:"required"`
|
||||
}
|
||||
47
server/model/app/request/preset.go
Normal file
47
server/model/app/request/preset.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package request
|
||||
|
||||
// CreatePresetRequest 创建预设请求
|
||||
type CreatePresetRequest struct {
|
||||
Name string `json:"name" binding:"required,min=1,max=100"`
|
||||
Description string `json:"description" binding:"max=500"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
Temperature float64 `json:"temperature" binding:"min=0,max=2"`
|
||||
TopP float64 `json:"topP" binding:"min=0,max=1"`
|
||||
TopK int `json:"topK" binding:"min=0"`
|
||||
FrequencyPenalty float64 `json:"frequencyPenalty" binding:"min=-2,max=2"`
|
||||
PresencePenalty float64 `json:"presencePenalty" binding:"min=-2,max=2"`
|
||||
MaxTokens int `json:"maxTokens" binding:"min=1,max=32000"`
|
||||
RepetitionPenalty float64 `json:"repetitionPenalty"`
|
||||
MinP float64 `json:"minP"`
|
||||
TopA float64 `json:"topA"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
StopSequences []string `json:"stopSequences"`
|
||||
Extensions map[string]interface{} `json:"extensions"`
|
||||
}
|
||||
|
||||
// UpdatePresetRequest 更新预设请求
|
||||
type UpdatePresetRequest struct {
|
||||
Name string `json:"name" binding:"min=1,max=100"`
|
||||
Description string `json:"description" binding:"max=500"`
|
||||
IsPublic *bool `json:"isPublic"`
|
||||
Temperature *float64 `json:"temperature" binding:"omitempty,min=0,max=2"`
|
||||
TopP *float64 `json:"topP" binding:"omitempty,min=0,max=1"`
|
||||
TopK *int `json:"topK" binding:"omitempty,min=0"`
|
||||
FrequencyPenalty *float64 `json:"frequencyPenalty" binding:"omitempty,min=-2,max=2"`
|
||||
PresencePenalty *float64 `json:"presencePenalty" binding:"omitempty,min=-2,max=2"`
|
||||
MaxTokens *int `json:"maxTokens" binding:"omitempty,min=1,max=32000"`
|
||||
RepetitionPenalty *float64 `json:"repetitionPenalty"`
|
||||
MinP *float64 `json:"minP"`
|
||||
TopA *float64 `json:"topA"`
|
||||
SystemPrompt *string `json:"systemPrompt"`
|
||||
StopSequences []string `json:"stopSequences"`
|
||||
Extensions map[string]interface{} `json:"extensions"`
|
||||
}
|
||||
|
||||
// GetPresetListRequest 获取预设列表请求
|
||||
type GetPresetListRequest struct {
|
||||
Page int `form:"page" binding:"min=1"`
|
||||
PageSize int `form:"pageSize" binding:"min=1,max=100"`
|
||||
Keyword string `form:"keyword"`
|
||||
IsPublic *bool `form:"isPublic"`
|
||||
}
|
||||
88
server/model/app/response/ai_config.go
Normal file
88
server/model/app/response/ai_config.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/model/app"
|
||||
)
|
||||
|
||||
// AIConfigResponse AI配置响应
|
||||
type AIConfigResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKey string `json:"apiKey"` // 前端显示时应该脱敏
|
||||
Models []string `json:"models"`
|
||||
DefaultModel string `json:"defaultModel"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
IsActive bool `json:"isActive"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// AIConfigListResponse AI配置列表响应
|
||||
type AIConfigListResponse struct {
|
||||
List []AIConfigResponse `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// TestAIConfigResponse 测试AI配置响应
|
||||
type TestAIConfigResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Latency int64 `json:"latency"` // 响应延迟(ms)
|
||||
}
|
||||
|
||||
// GetModelsResponse 获取模型列表响应
|
||||
type GetModelsResponse struct {
|
||||
Models []ModelInfo `json:"models"`
|
||||
}
|
||||
|
||||
// ModelInfo 模型信息
|
||||
type ModelInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
OwnedBy string `json:"ownedBy"`
|
||||
}
|
||||
|
||||
// ToAIConfigResponse 转换为AI配置响应结构
|
||||
func ToAIConfigResponse(config *app.AIConfig) AIConfigResponse {
|
||||
resp := AIConfigResponse{
|
||||
ID: config.ID,
|
||||
Name: config.Name,
|
||||
Provider: config.Provider,
|
||||
BaseURL: config.BaseURL,
|
||||
APIKey: maskAPIKey(config.APIKey),
|
||||
DefaultModel: config.DefaultModel,
|
||||
IsActive: config.IsActive,
|
||||
IsDefault: config.IsDefault,
|
||||
CreatedAt: config.CreatedAt,
|
||||
UpdatedAt: config.UpdatedAt,
|
||||
}
|
||||
|
||||
// 解析 JSON 字段
|
||||
if len(config.Models) > 0 {
|
||||
var models []string
|
||||
json.Unmarshal(config.Models, &models)
|
||||
resp.Models = models
|
||||
}
|
||||
|
||||
if len(config.Settings) > 0 {
|
||||
var settings map[string]interface{}
|
||||
json.Unmarshal(config.Settings, &settings)
|
||||
resp.Settings = settings
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// maskAPIKey 脱敏API Key
|
||||
func maskAPIKey(apiKey string) string {
|
||||
if len(apiKey) <= 8 {
|
||||
return "****"
|
||||
}
|
||||
return apiKey[:4] + "****" + apiKey[len(apiKey)-4:]
|
||||
}
|
||||
55
server/model/app/response/auth.go
Normal file
55
server/model/app/response/auth.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/model/app"
|
||||
)
|
||||
|
||||
// AppUserResponse 用户信息响应
|
||||
type AppUserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
UUID string `json:"uuid"`
|
||||
Username string `json:"username"`
|
||||
NickName string `json:"nickName"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Avatar string `json:"avatar"`
|
||||
Status string `json:"status"`
|
||||
Enable bool `json:"enable"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
LastLoginAt *time.Time `json:"lastLoginAt"`
|
||||
LastLoginIP string `json:"lastLoginIp"`
|
||||
ChatCount int `json:"chatCount"`
|
||||
MessageCount int `json:"messageCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
// LoginResponse 登录响应
|
||||
type LoginResponse struct {
|
||||
User AppUserResponse `json:"user"`
|
||||
Token string `json:"token"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
ExpiresAt int64 `json:"expiresAt"`
|
||||
}
|
||||
|
||||
// ToAppUserResponse 转换为用户响应结构
|
||||
func ToAppUserResponse(user *app.AppUser) AppUserResponse {
|
||||
return AppUserResponse{
|
||||
ID: user.ID,
|
||||
UUID: user.UUID,
|
||||
Username: user.Username,
|
||||
NickName: user.NickName,
|
||||
Email: user.Email,
|
||||
Phone: user.Phone,
|
||||
Avatar: user.Avatar,
|
||||
Status: user.Status,
|
||||
Enable: user.Enable,
|
||||
IsAdmin: user.IsAdmin,
|
||||
LastLoginAt: user.LastLoginAt,
|
||||
LastLoginIP: user.LastLoginIP,
|
||||
ChatCount: user.ChatCount,
|
||||
MessageCount: user.MessageCount,
|
||||
CreatedAt: user.CreatedAt,
|
||||
}
|
||||
}
|
||||
108
server/model/app/response/character.go
Normal file
108
server/model/app/response/character.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/model/app"
|
||||
)
|
||||
|
||||
// CharacterResponse 角色卡响应
|
||||
type CharacterResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Creator string `json:"creator"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Personality string `json:"personality"`
|
||||
Scenario string `json:"scenario"`
|
||||
FirstMes string `json:"firstMes"`
|
||||
MesExample string `json:"mesExample"`
|
||||
CreatorNotes string `json:"creatorNotes"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
PostHistoryInstructions string `json:"postHistoryInstructions"`
|
||||
Tags []string `json:"tags"`
|
||||
AlternateGreetings []string `json:"alternateGreetings"`
|
||||
CharacterBook map[string]interface{} `json:"characterBook"`
|
||||
Extensions map[string]interface{} `json:"extensions"`
|
||||
Spec string `json:"spec"`
|
||||
SpecVersion string `json:"specVersion"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
UseCount int `json:"useCount"`
|
||||
FavoriteCount int `json:"favoriteCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// CharacterSimpleResponse 角色卡简化响应(用于列表)
|
||||
type CharacterSimpleResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// CharacterListResponse 角色卡列表响应
|
||||
type CharacterListResponse struct {
|
||||
List []CharacterResponse `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// ToCharacterResponse 转换为角色卡响应结构
|
||||
func ToCharacterResponse(character *app.AICharacter) CharacterResponse {
|
||||
resp := CharacterResponse{
|
||||
ID: character.ID,
|
||||
Name: character.Name,
|
||||
Avatar: character.Avatar,
|
||||
Creator: character.Creator,
|
||||
Version: character.Version,
|
||||
Description: character.Description,
|
||||
Personality: character.Personality,
|
||||
Scenario: character.Scenario,
|
||||
FirstMes: character.FirstMes,
|
||||
MesExample: character.MesExample,
|
||||
CreatorNotes: character.CreatorNotes,
|
||||
SystemPrompt: character.SystemPrompt,
|
||||
PostHistoryInstructions: character.PostHistoryInstructions,
|
||||
Spec: character.Spec,
|
||||
SpecVersion: character.SpecVersion,
|
||||
IsPublic: character.IsPublic,
|
||||
UseCount: character.UseCount,
|
||||
FavoriteCount: character.FavoriteCount,
|
||||
CreatedAt: character.CreatedAt,
|
||||
UpdatedAt: character.UpdatedAt,
|
||||
}
|
||||
|
||||
// 解析 JSON 字段
|
||||
if len(character.Tags) > 0 {
|
||||
json.Unmarshal(character.Tags, &resp.Tags)
|
||||
}
|
||||
if len(character.AlternateGreetings) > 0 {
|
||||
json.Unmarshal(character.AlternateGreetings, &resp.AlternateGreetings)
|
||||
}
|
||||
if len(character.CharacterBook) > 0 {
|
||||
json.Unmarshal(character.CharacterBook, &resp.CharacterBook)
|
||||
}
|
||||
if len(character.Extensions) > 0 {
|
||||
json.Unmarshal(character.Extensions, &resp.Extensions)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// ToCharacterSimpleResponse 转换为角色卡简化响应结构(用于列表)
|
||||
func ToCharacterSimpleResponse(character *app.AICharacter) CharacterSimpleResponse {
|
||||
return CharacterSimpleResponse{
|
||||
ID: character.ID,
|
||||
Name: character.Name,
|
||||
Avatar: character.Avatar,
|
||||
Description: character.Description,
|
||||
CreatedAt: character.CreatedAt,
|
||||
UpdatedAt: character.UpdatedAt,
|
||||
}
|
||||
}
|
||||
122
server/model/app/response/conversation.go
Normal file
122
server/model/app/response/conversation.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/model/app"
|
||||
)
|
||||
|
||||
// ConversationResponse 对话响应
|
||||
type ConversationResponse struct {
|
||||
ID uint `json:"id"`
|
||||
CharacterID uint `json:"characterId"`
|
||||
Title string `json:"title"`
|
||||
PresetID *uint `json:"presetId"`
|
||||
AIProvider string `json:"aiProvider"`
|
||||
Model string `json:"model"`
|
||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
||||
MessageCount int `json:"messageCount"`
|
||||
TokenCount int `json:"tokenCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
|
||||
// 关联数据
|
||||
Character *CharacterResponse `json:"character,omitempty"`
|
||||
}
|
||||
|
||||
// ConversationListItemResponse 对话列表项响应(轻量级)
|
||||
type ConversationListItemResponse struct {
|
||||
ID uint `json:"id"`
|
||||
CharacterID uint `json:"characterId"`
|
||||
Title string `json:"title"`
|
||||
MessageCount int `json:"messageCount"`
|
||||
TokenCount int `json:"tokenCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Character *CharacterSimpleResponse `json:"character,omitempty"`
|
||||
}
|
||||
|
||||
// MessageResponse 消息响应
|
||||
type MessageResponse struct {
|
||||
ID uint `json:"id"`
|
||||
ConversationID uint `json:"conversationId"`
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
TokenCount int `json:"tokenCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
// ConversationListResponse 对话列表响应
|
||||
type ConversationListResponse struct {
|
||||
List []ConversationListItemResponse `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// MessageListResponse 消息列表响应
|
||||
type MessageListResponse struct {
|
||||
List []MessageResponse `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// ToConversationResponse 转换为对话响应结构
|
||||
func ToConversationResponse(conv *app.Conversation) ConversationResponse {
|
||||
// 解析 settings JSON
|
||||
var settings map[string]interface{}
|
||||
if len(conv.Settings) > 0 {
|
||||
// 尝试解析 JSON,如果失败则返回 nil
|
||||
if err := json.Unmarshal(conv.Settings, &settings); err != nil {
|
||||
settings = nil
|
||||
}
|
||||
}
|
||||
|
||||
return ConversationResponse{
|
||||
ID: conv.ID,
|
||||
CharacterID: conv.CharacterID,
|
||||
Title: conv.Title,
|
||||
PresetID: conv.PresetID,
|
||||
AIProvider: conv.AIProvider,
|
||||
Model: conv.Model,
|
||||
Settings: settings,
|
||||
MessageCount: conv.MessageCount,
|
||||
TokenCount: conv.TokenCount,
|
||||
CreatedAt: conv.CreatedAt,
|
||||
UpdatedAt: conv.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToMessageResponse 转换为消息响应结构
|
||||
func ToMessageResponse(msg *app.Message) MessageResponse {
|
||||
return MessageResponse{
|
||||
ID: msg.ID,
|
||||
ConversationID: msg.ConversationID,
|
||||
Role: msg.Role,
|
||||
Content: msg.Content,
|
||||
TokenCount: msg.TokenCount,
|
||||
CreatedAt: msg.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToConversationListItemResponse 转换为对话列表项响应结构(轻量级)
|
||||
func ToConversationListItemResponse(conv *app.Conversation, character *app.AICharacter) ConversationListItemResponse {
|
||||
resp := ConversationListItemResponse{
|
||||
ID: conv.ID,
|
||||
CharacterID: conv.CharacterID,
|
||||
Title: conv.Title,
|
||||
MessageCount: conv.MessageCount,
|
||||
TokenCount: conv.TokenCount,
|
||||
CreatedAt: conv.CreatedAt,
|
||||
UpdatedAt: conv.UpdatedAt,
|
||||
}
|
||||
|
||||
if character != nil {
|
||||
simpleChar := ToCharacterSimpleResponse(character)
|
||||
resp.Character = &simpleChar
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
78
server/model/app/response/preset.go
Normal file
78
server/model/app/response/preset.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/model/app"
|
||||
)
|
||||
|
||||
// PresetResponse 预设响应
|
||||
type PresetResponse struct {
|
||||
ID uint `json:"id"`
|
||||
UserID uint `json:"userId"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
TopP float64 `json:"topP"`
|
||||
TopK int `json:"topK"`
|
||||
FrequencyPenalty float64 `json:"frequencyPenalty"`
|
||||
PresencePenalty float64 `json:"presencePenalty"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
RepetitionPenalty float64 `json:"repetitionPenalty"`
|
||||
MinP float64 `json:"minP"`
|
||||
TopA float64 `json:"topA"`
|
||||
SystemPrompt string `json:"systemPrompt"`
|
||||
StopSequences []string `json:"stopSequences"`
|
||||
Extensions map[string]interface{} `json:"extensions"`
|
||||
UseCount int `json:"useCount"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// PresetListResponse 预设列表响应
|
||||
type PresetListResponse struct {
|
||||
List []PresetResponse `json:"list"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// ToPresetResponse 转换为预设响应结构
|
||||
func ToPresetResponse(preset *app.AIPreset) PresetResponse {
|
||||
var stopSequences []string
|
||||
if len(preset.StopSequences) > 0 {
|
||||
json.Unmarshal(preset.StopSequences, &stopSequences)
|
||||
}
|
||||
|
||||
var extensions map[string]interface{}
|
||||
if len(preset.Extensions) > 0 {
|
||||
json.Unmarshal(preset.Extensions, &extensions)
|
||||
}
|
||||
|
||||
return PresetResponse{
|
||||
ID: preset.ID,
|
||||
UserID: preset.UserID,
|
||||
Name: preset.Name,
|
||||
Description: preset.Description,
|
||||
IsPublic: preset.IsPublic,
|
||||
IsDefault: preset.IsDefault,
|
||||
Temperature: preset.Temperature,
|
||||
TopP: preset.TopP,
|
||||
TopK: preset.TopK,
|
||||
FrequencyPenalty: preset.FrequencyPenalty,
|
||||
PresencePenalty: preset.PresencePenalty,
|
||||
MaxTokens: preset.MaxTokens,
|
||||
RepetitionPenalty: preset.RepetitionPenalty,
|
||||
MinP: preset.MinP,
|
||||
TopA: preset.TopA,
|
||||
SystemPrompt: preset.SystemPrompt,
|
||||
StopSequences: stopSequences,
|
||||
Extensions: extensions,
|
||||
UseCount: preset.UseCount,
|
||||
CreatedAt: preset.CreatedAt,
|
||||
UpdatedAt: preset.UpdatedAt,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user