提示词保存

This commit is contained in:
2025-11-13 01:06:28 +08:00
parent fc7d2ccea5
commit c652d0ddf3
49 changed files with 4072 additions and 2452 deletions

View File

@@ -1,118 +0,0 @@
# API 统一管理说明
## 📁 目录结构
```
api/
├── config.js # API 基础配置(统一管理所有基础 URL
├── index.js # 统一导出入口(所有 API 服务从这里导出)
├── http.js # Axios 实例和拦截器
├── auth.js # 认证相关 API
├── chat.js # 聊天相关 API
├── common.js # 通用服务 API
└── tikhub/ # TikHub 相关 API
├── index.js
├── tikhub.js
└── types.js
```
## 🚀 使用方式
### 方式一:从统一入口导入(推荐)
```javascript
// 导入所有 API
import { ChatMessageApi, AuthApi, CommonService, TikhubService } from '@/api'
// 或按需导入
import { ChatMessageApi } from '@/api'
import { AuthApi } from '@/api'
```
### 方式二:从具体文件导入(兼容旧代码)
```javascript
// 仍然支持原有的导入方式
import { ChatMessageApi } from '@/api/chat'
import { CommonService } from '@/api/common'
```
### 方式三:使用配置工具函数
```javascript
import { getApiUrl, API_BASE } from '@/api/config'
// 获取完整 API URL
const url = getApiUrl('ADMIN_AI', '/chat/conversation/create-my')
// 结果: /admin-api/ai/chat/conversation/create-my
// 直接使用配置
const baseUrl = API_BASE.ADMIN_AI
```
## 📝 API 配置说明
### config.js
所有 API 基础 URL 统一在 `config.js` 中管理:
```javascript
export const API_BASE = {
ADMIN: '/admin-api', // 管理后台基础路径
APP: '/app-api', // 会员端基础路径
ADMIN_AI: '/admin-api/ai', // AI 模块(管理后台)
APP_MEMBER: '/app-api/member', // 会员模块
TIKHUB: '/webApi/admin-api/ai/tikHup', // TikHub管理后台
TIKHUB_APP: '/app-api/api/tikHup', // TikHub会员端
}
```
### 添加新的 API 模块
1.`config.js` 中添加新的基础路径:
```javascript
export const API_BASE = {
// ... 现有配置
NEW_MODULE: `${BASE_URL}/admin-api/new-module`,
}
```
2. 创建新的 API 文件(如 `new-module.js`
```javascript
import request from '@/api/http'
import { API_BASE } from '@/api/config'
const BASE = API_BASE.NEW_MODULE
export const NewModuleApi = {
getList: () => request.get(`${BASE}/list`),
create: (data) => request.post(`${BASE}/create`, data),
}
```
3.`index.js` 中导出:
```javascript
export { NewModuleApi } from './new-module'
```
## 🔧 HTTP 实例
所有 API 都使用统一的 HTTP 实例(`http.js`),已配置:
- ✅ 自动 Token 注入
- ✅ 统一错误处理
- ✅ 请求/响应拦截器
- ✅ 白名单机制(无需 Token 的接口)
## 📌 注意事项
1. **基础 URL 配置**:所有 API 的基础 URL 都应该在 `config.js` 中定义,不要在业务文件中硬编码
2. **统一导出**:新增 API 服务后,记得在 `index.js` 中导出
3. **向后兼容**:保持原有的导入方式仍然可用,方便逐步迁移
## 🎯 最佳实践
1. **使用统一入口**:优先使用 `@/api` 统一导入
2. **配置集中管理**:所有 URL 配置都在 `config.js`
3. **类型安全**:使用 TypeScript 时,可以为 API 添加类型定义
4. **错误处理**:利用 HTTP 拦截器统一处理错误

View File

@@ -4,7 +4,8 @@ import { getAccessToken } from '@/utils/auth'
// 使用公共配置
import { API_BASE } from '@gold/config/api'
const SERVER_BASE_AI = API_BASE.ADMIN_AI
// C 端使用 APP_AI如果不存在则回退到 ADMIN_AI
const SERVER_BASE_AI = API_BASE.APP_AI || API_BASE.ADMIN_AI

View File

@@ -1,36 +1,41 @@
import http from '@/api/http'
/**
* 应用层 API 服务
* 封装应用特定的 API 调用,使用 mono 级别的服务
*/
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { getAuthHeader } from '@gold/utils/token-manager'
// 使用公共配置和 API 服务创建器
import { TikHubService } from '@gold/api/services'
import { API_BASE } from '@gold/config/api'
import { createApiService } from '@gold/api/services'
// 初始化公共 hook 的 API 服务
import { setApiService } from '@gold/hooks/web/useVoiceText'
// 使用本地代理前缀 /tikhub开发环境通过 Vite 代理到 https://api.tikhub.io
// 注意API_BASE.TIKHUB 不存在,应该使用 TIKHUB_APP
const SERVER_BASE = API_BASE.TIKHUB_APP || API_BASE.TIKHUB || ''
// 创建公共 API 服务实例
const apiService = createApiService({
http,
getAuthHeader,
baseUrl: SERVER_BASE,
})
// 设置全局 API 服务(供 useVoiceText hook 使用)
setApiService(apiService)
/**
* TikHub API 基础路径
*/
const TIKHUB_BASE = API_BASE.TIKHUB_APP || API_BASE.TIKHUB || ''
/**
* 应用层通用服务
*/
export const CommonService = {
/**
* 视频转字符(音频转文字)
* 直接使用 mono 级别的 TikHub 服务
*/
videoToCharacters(data) {
return apiService.videoToCharacters(data)
return TikHubService.videoToCharacters(data)
},
/**
* 调用工作流
*/
callWorkflow(data) {
return apiService.callWorkflow(data)
return TikHubService.callWorkflow(data)
},
// 流式调用 workflow
callWorkflowStream: async (options) => {
/**
* 流式调用工作流SSE
*/
async callWorkflowStream(options) {
const {
data,
ctrl,
@@ -42,9 +47,9 @@ export const CommonService = {
const authHeader = getAuthHeader()
let retryCount = 0
const maxRetries = 0 // 禁用自动重试
const maxRetries = 0
return fetchEventSource(`${SERVER_BASE}/callWorkflow`, {
return fetchEventSource(`${TIKHUB_BASE}/callWorkflow`, {
method: 'post',
headers: {
'Content-Type': 'application/json',
@@ -57,18 +62,15 @@ export const CommonService = {
retryCount++
console.error('SSE错误重试次数:', retryCount, err)
// 调用自定义错误处理
if (typeof onError === 'function') {
onError(err)
}
// 超过最大重试次数,停止重连
if (retryCount > maxRetries) {
throw err // 抛出错误,停止自动重连
throw err
}
},
onclose: () => {
// 调用自定义关闭处理
if (typeof onClose === 'function') {
onClose()
}

View File

@@ -1,14 +0,0 @@
/**
* API 基础配置
* 统一管理所有 API 的基础 URL
*
* 注意:此文件已迁移到公共模块 @gold/config/api
* 为了保持向后兼容,这里重新导出公共配置
* 新代码建议直接使用 @gold/config/api
*/
// 从公共模块导入
export { API_BASE, getApiUrl } from '@gold/config/api'

View File

@@ -1,72 +0,0 @@
/**
* API 使用示例
* 此文件仅作为参考,展示如何使用统一的 API 管理
*/
// ========== 方式一:从统一入口导入(推荐) ==========
import {
ChatMessageApi,
CommonService,
TikhubService,
API_BASE,
getApiUrl,
http
} from '@/api'
// 使用示例
async function example1() {
// 使用 ChatMessageApi
const conversationId = await ChatMessageApi.createChatConversationMy({
roleId: 1
})
// 使用 CommonService
const result = await CommonService.videoToCharacters({ videoUrl: 'xxx' })
// 使用 TikhubService
await TikhubService.postTikHup({
type: 'DOUYIN_WEB_HOT_SEARCH',
methodType: 'GET',
urlParams: { keyword: '测试' }
})
// 使用配置
const baseUrl = API_BASE.ADMIN_AI
const fullUrl = getApiUrl('ADMIN_AI', '/chat/conversation/create-my')
// 直接使用 http 实例
await http.get('/some-endpoint')
}
// ========== 方式二:从具体文件导入(兼容旧代码) ==========
import { ChatMessageApi } from '@/api/chat'
import { CommonService } from '@/api/common'
// 使用公共配置
import { API_BASE } from '@gold/config/api'
async function example2() {
// 原有方式仍然可用
await ChatMessageApi.createChatConversationMy({ roleId: 1 })
}
// ========== 方式三:按需导入认证 API ==========
import {
loginBySms,
sendSmsCode,
SMS_SCENE
} from '@/api'
async function example3() {
// 发送验证码
await sendSmsCode('13800138000', SMS_SCENE.MEMBER_LOGIN)
// 短信登录
await loginBySms('13800138000', '123456')
}
export default {
example1,
example2,
example3
}

View File

@@ -1,126 +1,168 @@
import axios from 'axios'
/**
* 应用层 HTTP 客户端
* 使用 mono 级别的 axios 实例,添加应用特定的 401 处理
*/
import { message } from 'ant-design-vue'
import { getAuthHeader, clearAllTokens } from '@gold/utils/token-manager'
import { clearAllTokens, getRefreshToken } from '@gold/utils/token-manager'
import { useUserStore } from '@/stores/user'
import { createClientAxios } from '@gold/api/axios/client'
import { refreshToken } from '@/api/auth'
// 刷新 token 的状态管理
let isRefreshing = false
let refreshPromise = null
/**
* 不需要 token 的接口白名单
* 支持完整路径匹配或路径包含匹配
* 处理 403 禁止访问错误(应用层特定逻辑)
* 先尝试刷新 token如果失败或没有 refresh token 才提示用户
*/
const WHITE_LIST = [
'/auth/login', // 密码登录
'/auth/send-sms-code', // 发送验证码
'/auth/sms-login', // 短信登录
'/auth/validate-sms-code', // 验证验证码
'/auth/register', // 注册(如果有)
'/auth/reset-password', // 重置密码
'/auth/refresh-token', // 刷新token可选根据后端要求
]
/**
* 检查 URL 是否在白名单中
* @param {string} url - 请求 URL
* @returns {boolean}
*/
function isInWhiteList(url) {
if (!url) return false
return WHITE_LIST.some((path) => url.includes(path))
}
// 创建 axios 实例
const http = axios.create({
baseURL: '/',
timeout: 180000, // 3分钟
})
// 请求拦截:自动注入 Token
http.interceptors.request.use((config) => {
// 检查是否需要 token不在白名单中且未显式设置 isToken = false
const needToken = config.headers?.isToken !== false && !isInWhiteList(config.url || '')
config.headers['tenant-id'] = import.meta.env.VITE_TENANT_ID
if (needToken) {
// 使用统一的 token 管理器获取 header
const authHeader = getAuthHeader()
if (authHeader) {
config.headers.Authorization = authHeader
}
}
// 允许跨域第三方域名,使用绝对地址时保持原样
return config
})
http.interceptors.response.use(
(resp) => {
const data = resp.data
// 检查响应数据中的 code 字段
if (data && typeof data.code === 'number' && (data.code === 0 || data.code === 200)) {
return data
} else {
// code 不为 0 时检查是否为401
if (data && typeof data.code === 'number' && data.code === 401) {
handle401Error()
}
// code 不为 0 时,抛出错误
const error = new Error(data?.message || data?.msg || '请求失败')
error.code = data?.code
error.data = data
return Promise.reject(error)
}
},
(error) => {
// 处理 HTTP 状态码 401
if (error.response && error.response.status === 401) {
handle401Error()
}
// 统一错误处理:输出关键信息,便于排查 403 等问题
return Promise.reject(error)
}
)
/**
* 处理 401 未授权错误
* 清空 token 并退出登录
*
* 注意使用防抖机制避免多个请求同时401时重复处理
*/
function handle401Error() {
// 避免重复处理防止多个请求同时401导致多次调用
if (handle401Error.processed) {
async function handleApp403Error() {
// 避免重复处理
if (handleApp403Error.processed) {
return
}
handle401Error.processed = true
handleApp403Error.processed = true
// 1. 清空所有 token
try {
clearAllTokens() // 统一使用 token-manager 的清空函数
// 检查是否有 refresh token
const refreshTokenValue = getRefreshToken()
if (refreshTokenValue) {
// 如果有 refresh token尝试刷新
try {
// 如果正在刷新,等待刷新完成
if (isRefreshing && refreshPromise) {
await refreshPromise
handleApp403Error.processed = false
return
}
// 开始刷新 token
isRefreshing = true
refreshPromise = refreshToken()
await refreshPromise
// 刷新成功,重置状态
isRefreshing = false
refreshPromise = null
handleApp403Error.processed = false
// 刷新成功,不提示用户(静默刷新)
return
} catch (refreshError) {
// 刷新失败,清除状态
isRefreshing = false
refreshPromise = null
console.error('刷新 token 失败:', refreshError)
// 刷新失败才提示用户
message.warning('登录状态已过期,请重新登录', 3)
handleApp403Error.processed = false
return
}
} else {
// 没有 refresh token提示用户
message.warning('登录状态已过期,请重新登录', 3)
handleApp403Error.processed = false
}
} catch (e) {
console.error('清空 token 失败:', e)
console.error('处理 403 错误失败:', e)
handleApp403Error.processed = false
}
// 2. 退出登录状态(清空用户信息)
try {
const userStore = useUserStore()
// logout() 会清空用户信息和本地存储
userStore.logout()
} catch (e) {
console.error('退出登录失败:', e)
}
// 3. 提示用户(延迟显示,避免在清空过程中显示)
setTimeout(() => {
message.warning('登录已过期,请重新登录', 3)
}, 100)
// 4. 延迟重置标志,避免短时间内重复处理
setTimeout(() => {
handle401Error.processed = false
}, 2000)
}
/**
* 处理 401 未授权错误(应用层特定逻辑)
* 先尝试刷新 token如果失败或没有 refresh token 才退出登录
*/
async function handleApp401Error() {
// 避免重复处理
if (handleApp401Error.processed) {
return
}
handleApp401Error.processed = true
try {
// 检查是否有 refresh token
const refreshTokenValue = getRefreshToken()
if (refreshTokenValue) {
// 如果有 refresh token尝试刷新
try {
// 如果正在刷新,等待刷新完成
if (isRefreshing && refreshPromise) {
await refreshPromise
handleApp401Error.processed = false
return
}
// 开始刷新 token
isRefreshing = true
refreshPromise = refreshToken()
await refreshPromise
// 刷新成功,重置状态
isRefreshing = false
refreshPromise = null
handleApp401Error.processed = false
// 刷新成功,不提示用户(静默刷新)
return
} catch (refreshError) {
// 刷新失败,清除状态
isRefreshing = false
refreshPromise = null
console.error('刷新 token 失败:', refreshError)
// 继续执行退出登录逻辑
}
}
// 没有 refresh token 或刷新失败,执行退出登录
try {
clearAllTokens()
} catch (e) {
console.error('清空 token 失败:', e)
}
try {
const userStore = useUserStore()
userStore.logout()
} catch (e) {
console.error('退出登录失败:', e)
}
setTimeout(() => {
message.warning('登录已过期,请重新登录', 3)
}, 100)
} catch (e) {
console.error('处理 401 错误失败:', e)
} finally {
setTimeout(() => {
handleApp401Error.processed = false
}, 2000)
}
}
/**
* 创建应用层 HTTP 客户端
* 基于 mono 级别的 axios 实例,添加应用特定的错误处理
*/
const http = createClientAxios({
baseURL: '/',
timeout: 180000,
on401: handleApp401Error,
on403: handleApp403Error,
})
// 注意403 处理已在 createClientAxios 的响应拦截器中通过 on403 回调处理
export default http

View File

@@ -3,9 +3,6 @@
* 所有 API 服务都从这里导出,方便统一管理和使用
*/
// 配置
export { default as API_BASE, getApiUrl } from './config'
// HTTP 实例
export { default as http, default as request } from './http'
@@ -15,6 +12,9 @@ export * from './auth'
// 聊天相关 API
export { ChatMessageApi } from './chat'
// 用户提示词 API
export { UserPromptApi } from './userPrompt'
// 通用服务 API
export { CommonService } from './common'
export { default as CommonServiceDefault } from './common'
@@ -23,20 +23,3 @@ export { default as CommonServiceDefault } from './common'
export { TikhubService, default as TikhubServiceDefault } from './tikhub'
export { InterfaceType, MethodType, InterfaceUrlMap, ParamType } from './tikhub/types'
/**
* 统一导出所有 API 服务(便于按需导入)
*/
export default {
// 配置
config: () => import('./config'),
// HTTP 实例
http: () => import('./http'),
// API 服务
auth: () => import('./auth'),
chat: () => import('./chat'),
common: () => import('./common'),
tikhub: () => import('./tikhub'),
}

View File

@@ -1,5 +0,0 @@
// TikHub API 统一导出入口
export { TikhubService } from './tikhub/tikhub.js'
export { default } from './tikhub/tikhub.js'
export { InterfaceType, MethodType, InterfaceUrlMap, ParamType } from './tikhub/types.js'

View File

@@ -1,180 +0,0 @@
# TikHub API 模块
本模块提供了统一的 TikHub 接口调用中间层,支持多种平台的 API 接口。
## 目录结构
```
tikhub/
├── types.js # 枚举定义InterfaceType、MethodType
├── tikhub.js # 核心服务类 TikhubService
├── index.js # 统一导出入口
├── example.js # 使用示例
└── README.md # 本文档
```
## 快速开始
### 1. 导入模块
```javascript
import TikhubService, { InterfaceType, MethodType } from '@/api/tikhub'
```
### 2. 调用接口
```javascript
// 调用抖音热门搜索接口
const response = await TikhubService.postTikHup(
InterfaceType.DOUYIN_WEB_HOT_SEARCH, // 接口类型
MethodType.GET, // HTTP 方法
{ keyword: '测试关键词' } // 实际接口参数
)
```
## API 说明
### TikhubService.postTikHup(type, methodType, urlParams)
统一调用 TikHub 接口的中间层方法。
**参数:**
- `type` (String) - 接口类型,使用 `InterfaceType` 枚举值
- `methodType` (String) - HTTP 方法类型,使用 `MethodType` 枚举值
- `urlParams` (Object|String) - 实际接口的参数
**返回:**
- Promise - axios 响应对象
**示例:**
```javascript
// 获取小红书热门榜单
await TikhubService.postTikHup(
InterfaceType.XIAOHONGSHU_WEB_HOT_LIST,
MethodType.GET,
{ page: 1, page_size: 20 }
)
// 搜索抖音内容
await TikhubService.postTikHup(
InterfaceType.DOUYIN_GENERAL_SEARCH_V4,
MethodType.POST,
{
keyword: '热门内容',
sort_type: 0,
publish_time: 0,
}
)
```
## 枚举说明
### InterfaceType - 接口类型
支持的所有接口类型:
| 枚举值 | 说明 |
|--------|------|
| `XIAOHONGSHU_USER_INFO` | 小红书 - 获取用户信息 |
| `DOUYIN_WEB_USER_POST_VIDEOS` | 抖音 - 网页端获取用户发布的视频 |
| `DOUYIN_APP_USER_POST_VIDEOS` | 抖音 - APP端V3获取用户发布的视频 |
| `DOUYIN_APP_GENERAL_SEARCH` | 抖音 - APP端V3通用搜索结果 |
| `DOUYIN_SEARCH_SUGGEST` | 抖音 - 搜索建议 |
| `DOUYIN_GENERAL_SEARCH_V4` | 抖音 - 通用搜索V4 |
| `XIAOHONGSHU_WEB_HOT_LIST` | 小红书 - 网页端V2热门榜单 |
| `DOUYIN_WEB_HOT_SEARCH` | 抖音 - 网页端热门搜索结果 |
| `KUAISHOU_WEB_HOT_LIST` | 快手 - 网页端热门榜单V2 |
| `BILIBILI_WEB_POPULAR` | B站 - 网页端流行内容 |
| `WEIBO_WEB_HOT_SEARCH` | 微博 - 网页端V2热门搜索指数 |
| `DOUYIN_WEB_GENERAL_SEARCH` | 抖音 - 网页端通用搜索结果 |
### MethodType - HTTP 方法
| 枚举值 | 说明 |
|--------|------|
| `GET` | GET 请求 |
| `POST` | POST 请求 |
| `PUT` | PUT 请求 |
| `DELETE` | DELETE 请求 |
| `PATCH` | PATCH 请求 |
## 使用示例
详细的使用示例请参考 `example.js` 文件。
### 示例 1获取抖音热门搜索
```javascript
import TikhubService, { InterfaceType, MethodType } from '@/api/tikhub'
async function getDouyinHotSearch() {
try {
const response = await TikhubService.postTikHup(
InterfaceType.DOUYIN_WEB_HOT_SEARCH,
MethodType.GET,
{ keyword: '测试关键词' }
)
return response
} catch (error) {
console.error('调用失败:', error)
throw error
}
}
```
### 示例 2获取用户发布的视频
```javascript
async function getUserVideos() {
return await TikhubService.postTikHup(
InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
MethodType.GET,
{
sec_user_id: 'MS4wLjABAAAxxxxxxxx',
count: 20,
max_cursor: 0,
}
)
}
```
## 错误处理
```javascript
try {
const response = await TikhubService.postTikHup(
InterfaceType.DOUYIN_WEB_HOT_SEARCH,
MethodType.GET,
{ keyword: '测试' }
)
console.log('成功:', response)
} catch (error) {
if (error.message.includes('无效的接口类型')) {
console.error('接口类型错误')
} else {
console.error('请求失败:', error.message)
}
}
```
## 后端接口格式
本模块会调用后端接口 `/webApi/admin-api/ai/tikHup/post_tik_hup`,传递的参数格式为:
```json
{
"interface_type": "8",
"method_type": "GET",
"platform_url": "https://api.tikhub.io/api/v1/douyin/web/fetch_hot_search_result",
"url_params": { "keyword": "测试" }
}
```
## 注意事项
1. 所有接口类型必须使用 `InterfaceType` 枚举,不能使用字符串数字
2. HTTP 方法必须使用 `MethodType` 枚举
3. `urlParams` 可以是对象或字符串,取决于实际接口的要求
4. 后端会根据 `interface_type` 从数据库中获取对应的 `platform_token`

View File

@@ -1,7 +1,6 @@
/**
* TikHub API 统一导出
*/
export { TikhubService } from './tikhub.js'
export { default } from './tikhub.js'
export { TikhubService, default } from './tikhub.js'
export { InterfaceType, MethodType, InterfaceUrlMap, ParamType } from './types.js'

View File

@@ -0,0 +1,68 @@
import http from '@/api/http'
import { API_BASE } from '@gold/config/api'
// C 端使用 APP_AI如果不存在则回退到 ADMIN_AI
// 后端路径是 /app-api/ai/user-prompt参考 chat API 的实现
const SERVER_BASE_AI = API_BASE.APP_AI || API_BASE.ADMIN_AI
/**
* 用户提示词 API
*/
export const UserPromptApi = {
/**
* 创建用户提示词
* @param {Object} data - 提示词数据
* @returns {Promise} 响应数据
*/
createUserPrompt: async (data) => {
return await http.post(`${SERVER_BASE_AI}/user-prompt/create`, data)
},
/**
* 更新用户提示词
* @param {Object} data - 提示词数据
* @returns {Promise} 响应数据
*/
updateUserPrompt: async (data) => {
return await http.put(`${SERVER_BASE_AI}/user-prompt/update`, data)
},
/**
* 分页查询用户提示词
* @param {Object} params - 查询参数
* @returns {Promise} 响应数据
*/
getUserPromptPage: async (params) => {
return await http.get(`${SERVER_BASE_AI}/user-prompt/page`, { params })
},
/**
* 获取单个用户提示词
* @param {Number} id - 提示词ID
* @returns {Promise} 响应数据
*/
getUserPrompt: async (id) => {
return await http.get(`${SERVER_BASE_AI}/user-prompt/get`, { params: { id } })
},
/**
* 删除用户提示词
* @param {Number} id - 提示词ID
* @returns {Promise} 响应数据
*/
deleteUserPrompt: async (id) => {
return await http.delete(`${SERVER_BASE_AI}/user-prompt/delete`, { params: { id } })
},
/**
* 批量删除用户提示词
* @param {Array<Number>} ids - 提示词ID列表
* @returns {Promise} 响应数据
*/
deleteUserPromptList: async (ids) => {
return await http.delete(`${SERVER_BASE_AI}/user-prompt/delete-list`, { params: { ids } })
},
}
export default UserPromptApi