提示词保存

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

129
frontend/api/README.md Normal file
View File

@@ -0,0 +1,129 @@
# Mono 级别 API 架构
## 📁 目录结构
```
frontend/api/
├── axios/
│ └── client.js # Mono 级别的 C 端 Axios 实例
├── services/
│ ├── tikhub.js # TikHub API 服务
│ └── index.js # 服务统一导出
└── README.md # 本文档
```
## 🎯 设计理念
### 1. 分层清晰
- **Mono 级别** (`frontend/api/`): 可在 monorepo 中所有应用复用的代码
- **应用级别** (`frontend/app/web-gold/src/api/`): 应用特定的 API 封装
### 2. 模块化设计
- **Axios 实例**: 统一的 HTTP 客户端配置
- **API 服务**: 按功能模块组织(如 `TikHubService`
- **Hooks**: 直接使用服务,无需全局注入
### 3. 人类可读
- 清晰的命名和注释
- 直观的导入路径
- 易于理解和维护
## 🚀 使用方式
### 在 Mono 级别使用
```javascript
// 直接使用服务
import { TikHubService } from '@gold/api/services'
const result = await TikHubService.videoToCharacters({
fileLinkList: ['https://example.com/audio.mp3']
})
```
### 在应用层使用
```javascript
// 使用应用层封装的服务
import { CommonService } from '@/api/common'
const result = await CommonService.videoToCharacters({
fileLinkList: ['https://example.com/audio.mp3']
})
```
### 在 Hooks 中使用
```typescript
// useVoiceText 直接使用 TikHubService无需配置
import useVoiceText from '@gold/hooks/web/useVoiceText'
const { getVoiceText } = useVoiceText()
const transcriptions = await getVoiceText([
{ audio_url: 'https://example.com/audio.mp3' }
])
```
## 📦 添加新的 API 服务
### 1. 创建服务文件
```javascript
// frontend/api/services/my-service.js
import { clientAxios } from '@gold/api/axios/client'
import { API_BASE } from '@gold/config/api'
const BASE_URL = API_BASE.MY_SERVICE || ''
export const MyService = {
async getData(params) {
return await clientAxios.get(`${BASE_URL}/data`, { params })
},
async createData(data) {
return await clientAxios.post(`${BASE_URL}/data`, data)
},
}
export default MyService
```
### 2. 导出服务
```javascript
// frontend/api/services/index.js
export { TikHubService } from './tikhub'
export { MyService } from './my-service'
```
### 3. 使用服务
```javascript
import { MyService } from '@gold/api/services'
const data = await MyService.getData({ id: 1 })
```
## 🔧 自定义 Axios 实例
如果需要创建自定义的 Axios 实例:
```javascript
import { createClientAxios } from '@gold/api/axios/client'
const customAxios = createClientAxios({
baseURL: '/custom-api',
timeout: 60000,
on401: () => {
// 自定义 401 处理
}
})
```
## 📝 注意事项
1. **Mono 级别代码**应该保持通用,不依赖特定应用的逻辑
2. **应用层代码**可以依赖应用特定的 store、组件等
3. **服务模块**应该按功能划分,保持单一职责
4. **Axios 实例**统一管理,避免重复配置

View File

@@ -0,0 +1,152 @@
/**
* Mono 级别的 C 端 Axios 实例
* 供 monorepo 中所有应用使用的统一 HTTP 客户端
*/
import axios from 'axios'
import { getAuthHeader, clearAllTokens } from '@gold/utils/token-manager'
/**
* 不需要 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',
]
/**
* 检查 URL 是否在白名单中
*/
function isInWhiteList(url) {
if (!url) return false
return WHITE_LIST.some((path) => url.includes(path))
}
/**
* 处理 401 未授权错误
*/
let isHandling401 = false
function handle401Error() {
if (isHandling401) return
isHandling401 = true
try {
clearAllTokens()
} catch (e) {
console.error('清空 token 失败:', e)
}
// 延迟重置标志
setTimeout(() => {
isHandling401 = false
}, 2000)
}
/**
* 创建 C 端 Axios 实例
* @param {Object} options - 配置选项
* @param {string} options.baseURL - 基础 URL
* @param {number} options.timeout - 超时时间(毫秒)
* @param {Function} options.on401 - 401 错误处理函数
* @param {Function} options.on403 - 403 错误处理函数
* @returns {AxiosInstance} Axios 实例
*/
export function createClientAxios(options = {}) {
const {
baseURL = '/',
timeout = 180000,
on401 = handle401Error,
on403 = null,
} = options
const client = axios.create({
baseURL,
timeout,
})
// 请求拦截器
client.interceptors.request.use((config) => {
// 添加 tenant-id
const tenantId =
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_TENANT_ID) ||
(typeof process !== 'undefined' && process.env?.VITE_TENANT_ID) ||
'1'
if (tenantId) {
config.headers['tenant-id'] = tenantId
}
// 添加 Authorization header
const needToken = config.headers?.isToken !== false && !isInWhiteList(config.url || '')
if (needToken) {
const authHeader = getAuthHeader()
if (authHeader) {
config.headers.Authorization = authHeader
}
}
return config
})
// 响应拦截器
client.interceptors.response.use(
(response) => {
const data = response.data
// 检查业务状态码
if (data && typeof data.code === 'number') {
if (data.code === 0 || data.code === 200) {
return data
}
// 处理 401
if (data.code === 401 && typeof on401 === 'function') {
on401()
}
// 处理 403业务状态码
if (data.code === 403 && typeof on403 === 'function') {
on403()
}
// 抛出业务错误
const error = new Error(data?.message || data?.msg || '请求失败')
error.code = data?.code
error.data = data
return Promise.reject(error)
}
return data
},
(error) => {
// 处理 HTTP 401
if (error.response?.status === 401 && typeof on401 === 'function') {
on401()
}
// 处理 HTTP 403
if (error.response?.status === 403 && typeof on403 === 'function') {
on403()
}
return Promise.reject(error)
}
)
return client
}
/**
* 默认导出的 C 端 Axios 实例
* 可在应用层覆盖配置
*/
export const clientAxios = createClientAxios()
export default clientAxios

View File

@@ -1,113 +0,0 @@
/**
* 公共 API 服务
* 封装可在 monorepo 各个应用中复用的 API 调用
*
* 使用方式:
* import { createApiService } from '@gold/config/api/services'
*
* const apiService = createApiService({
* http: axiosInstance,
* getAuthHeader: () => 'Bearer token',
* baseUrl: API_BASE.TIKHUB_APP
* })
*
* await apiService.videoToCharacters({ fileLinkList: [...] })
*/
import { API_BASE } from '@gold/config/api'
/**
* 创建 API 服务实例
* @param {Object} options - 配置选项
* @param {Object} options.http - HTTP 客户端实例(如 axios
* @param {Function} options.getAuthHeader - 获取 Authorization header 的函数
* @param {string} options.baseUrl - API 基础 URL可选默认使用 TIKHUB_APP
* @returns {Object} API 服务对象
*/
export function createApiService(options = {}) {
const { http, getAuthHeader, baseUrl } = options
if (!http) {
throw new Error('createApiService: http 实例是必需的')
}
// 确定 API 基础路径
// 如果没有提供 baseUrl尝试使用 TIKHUB_APP 或 TIKHUB
const apiBaseUrl = baseUrl || API_BASE.TIKHUB_APP || API_BASE.TIKHUB || ''
/**
* 视频转字符(音频转文字)
* @param {Object} data - 请求数据
* @param {string[]} data.fileLinkList - 音频文件链接列表
* @returns {Promise<Object>} 响应数据
*/
async function videoToCharacters(data) {
const url = `${apiBaseUrl}/videoToCharacters2`
const headers = {
'Content-Type': 'application/json',
}
// 添加 Authorization header如果提供了 getAuthHeader 函数)
if (getAuthHeader) {
const authHeader = getAuthHeader()
if (authHeader) {
headers.Authorization = authHeader
}
}
// 获取 tenant-id从环境变量或默认值
const tenantId =
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_TENANT_ID) ||
(typeof process !== 'undefined' && process.env?.VITE_TENANT_ID) ||
'1'
if (tenantId) {
headers['tenant-id'] = tenantId
}
return await http.post(url, data, { headers })
}
/**
* 调用工作流
* @param {Object} data - 请求数据
* @returns {Promise<Object>} 响应数据
*/
async function callWorkflow(data) {
const url = `${apiBaseUrl}/callWorkflow`
const headers = {
'Content-Type': 'application/json',
}
if (getAuthHeader) {
const authHeader = getAuthHeader()
if (authHeader) {
headers.Authorization = authHeader
}
}
const tenantId =
(typeof import.meta !== 'undefined' && import.meta.env?.VITE_TENANT_ID) ||
(typeof process !== 'undefined' && process.env?.VITE_TENANT_ID) ||
'1'
if (tenantId) {
headers['tenant-id'] = tenantId
}
return await http.post(url, data, { headers })
}
return {
videoToCharacters,
callWorkflow,
}
}
/**
* 默认导出(便于直接使用)
*/
export default createApiService

View File

@@ -0,0 +1,11 @@
/**
* API 服务统一导出
* 按功能模块组织,便于维护和扩展
*/
export { TikHubService } from './tikhub'
// 可以继续添加其他服务模块
// export { UserService } from './user'
// export { ChatService } from './chat'

View File

@@ -0,0 +1,43 @@
/**
* TikHub API 服务
* 封装 TikHub 相关的 API 调用
*/
import { clientAxios } from '@gold/api/axios/client'
import { API_BASE } from '@gold/config/api'
/**
* TikHub API 基础路径
*/
const BASE_URL = API_BASE.TIKHUB_APP || API_BASE.TIKHUB || ''
/**
* TikHub API 服务
*/
export const TikHubService = {
/**
* 视频转字符(音频转文字)
* @param {Object} params - 请求参数
* @param {string[]} params.fileLinkList - 音频文件链接列表
* @returns {Promise<{ data: string }>} 响应数据
*/
async videoToCharacters(params) {
const { fileLinkList } = params
return await clientAxios.post(`${BASE_URL}/videoToCharacters2`, {
fileLinkList,
})
},
/**
* 调用工作流
* @param {Object} data - 请求数据
* @returns {Promise<Object>} 响应数据
*/
async callWorkflow(data) {
return await clientAxios.post(`${BASE_URL}/callWorkflow`, data)
},
}
export default TikHubService