提示词保存
This commit is contained in:
129
frontend/api/README.md
Normal file
129
frontend/api/README.md
Normal 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 实例**统一管理,避免重复配置
|
||||
|
||||
152
frontend/api/axios/client.js
Normal file
152
frontend/api/axios/client.js
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
11
frontend/api/services/index.js
Normal file
11
frontend/api/services/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* API 服务统一导出
|
||||
* 按功能模块组织,便于维护和扩展
|
||||
*/
|
||||
|
||||
export { TikHubService } from './tikhub'
|
||||
|
||||
// 可以继续添加其他服务模块
|
||||
// export { UserService } from './user'
|
||||
// export { ChatService } from './chat'
|
||||
|
||||
43
frontend/api/services/tikhub.js
Normal file
43
frontend/api/services/tikhub.js
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user