前端
This commit is contained in:
237
frontend/app/web-gold/src/api/auth.js
Normal file
237
frontend/app/web-gold/src/api/auth.js
Normal file
@@ -0,0 +1,237 @@
|
||||
|
||||
import api from '@/api/http'
|
||||
import { setToken, getRefreshToken } from '@/utils/auth'
|
||||
|
||||
const SERVER_BASE = import.meta.env.VITE_BASE_URL + '/app-api/member'
|
||||
|
||||
/**
|
||||
* 保存 token 的辅助函数
|
||||
* @param {Object} info - 包含 accessToken 和 refreshToken 的对象
|
||||
*/
|
||||
function saveTokens(info) {
|
||||
if (info?.accessToken || info?.refreshToken) {
|
||||
setToken({
|
||||
accessToken: info.accessToken || '',
|
||||
refreshToken: info.refreshToken || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截(可选):统一错误处理
|
||||
*/
|
||||
// api.interceptors.response.use(
|
||||
// (resp) => resp,
|
||||
// (err) => {
|
||||
// // 统一错误日志/提示
|
||||
// return Promise.reject(err);
|
||||
// }
|
||||
// );
|
||||
|
||||
/**
|
||||
* 短信场景枚举(请与后端配置保持一致)
|
||||
* - MEMBER_LOGIN: 会员短信登录场景
|
||||
* - MEMBER_UPDATE_PASSWORD: 已登录用户修改密码(短信校验)场景
|
||||
* - MEMBER_RESET_PASSWORD: 未登录用户忘记密码重置(短信校验)场景
|
||||
* 如有"注册"独立场景,可在此添加:MEMBER_REGISTER: 13
|
||||
*/
|
||||
export const SMS_SCENE = {
|
||||
MEMBER_LOGIN: 1,
|
||||
MEMBER_UPDATE_PASSWORD: 3,
|
||||
MEMBER_RESET_PASSWORD: 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* 短信模板编码常量
|
||||
*/
|
||||
export const SMS_TEMPLATE_CODE = {
|
||||
USER_REGISTER: 'muye-user-code', // 用户注册模板编码
|
||||
};
|
||||
|
||||
/**
|
||||
* 账号密码登录
|
||||
* POST /member/auth/login
|
||||
*
|
||||
* @param {string} mobile - 手机号(必填)
|
||||
* @param {string} password - 密码(必填,长度 4-16)
|
||||
* @returns {Promise<Object>} data.data: { accessToken, refreshToken, expiresTime, userInfo }
|
||||
*/
|
||||
export async function loginByPassword(mobile, password) {
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/login`, { mobile, password });
|
||||
const info = data || {};
|
||||
saveTokens(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
* POST /member/auth/send-sms-code
|
||||
*
|
||||
* @param {string} mobile - 手机号(登录/忘记密码等需要;修改密码场景可不传)
|
||||
* @param {number} scene - 短信场景(见 SMS_SCENE)
|
||||
* @param {string} templateCode - 模板编码(可选,如 'muye-user-code')
|
||||
* @returns {Promise<Object>} 后端通用响应结构
|
||||
*/
|
||||
export async function sendSmsCode(mobile, scene, templateCode) {
|
||||
const body = { scene };
|
||||
if (mobile) body.mobile = mobile; // 修改密码场景后端会根据当前登录用户自动填充手机号
|
||||
if (templateCode) body.templateCode = templateCode; // 添加模板编码参数
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, body);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验短信验证码(可选前置校验,一般直接在业务接口 use)
|
||||
* POST /member/auth/validate-sms-code
|
||||
*
|
||||
* @param {string} mobile - 手机号(必填)
|
||||
* @param {string} code - 验证码(必填,4-6 位数字)
|
||||
* @param {number} scene - 短信场景(必填,见 SMS_SCENE)
|
||||
* @returns {Promise<Object>} 后端通用响应结构
|
||||
*/
|
||||
export async function validateSmsCode(mobile, code, scene) {
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/validate-sms-code`, { mobile, code, scene });
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机+验证码登录(首次即自动注册)
|
||||
* POST /member/auth/sms-login
|
||||
*
|
||||
* @param {string} mobile - 手机号(必填)
|
||||
* @param {string} code - 短信验证码(必填)
|
||||
* @returns {Promise<Object>} data.data: { accessToken, refreshToken, expiresTime, userInfo }
|
||||
*/
|
||||
export async function loginBySms(mobile, code) {
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/sms-login`, { mobile, code });
|
||||
const info = data || {};
|
||||
saveTokens(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* POST /member/auth/refresh-token?refreshToken=xxx
|
||||
*
|
||||
* @returns {Promise<Object>} data.data: { accessToken, refreshToken, expiresTime, userInfo }
|
||||
*/
|
||||
export async function refreshToken() {
|
||||
const rt = getRefreshToken();
|
||||
if (!rt) throw new Error('缺少 refresh_token');
|
||||
// refreshToken 作为 URL 查询参数传递
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/refresh-token`, null, { params: { refreshToken: rt } });
|
||||
const info = data || {};
|
||||
saveTokens(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录态下:发送“修改密码”验证码
|
||||
* - 场景:SMS_SCENE.MEMBER_UPDATE_PASSWORD
|
||||
* - 特性:后端会以当前登录用户的手机号为准,无需传 mobile
|
||||
* POST /member/auth/send-sms-code
|
||||
*
|
||||
* @returns {Promise<Object>} 后端通用响应结构
|
||||
*/
|
||||
export async function sendUpdatePasswordCode() {
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, {
|
||||
scene: SMS_SCENE.MEMBER_UPDATE_PASSWORD,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录态下:通过短信验证码修改密码
|
||||
* PUT /member/user/update-password
|
||||
*
|
||||
* @param {string} newPassword - 新密码(必填,4-16 位)
|
||||
* @param {string} smsCode - 短信验证码(必填,4-6 位数字)
|
||||
* @returns {Promise<Object>} 后端通用响应结构
|
||||
*/
|
||||
export async function updatePasswordBySmsCode(newPassword, smsCode) {
|
||||
const { data } = await api.put(`${SERVER_BASE}/user/update-password`, {
|
||||
password: newPassword,
|
||||
code: smsCode,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 未登录:发送“忘记密码”验证码
|
||||
* - 场景:SMS_SCENE.MEMBER_RESET_PASSWORD
|
||||
* POST /member/auth/send-sms-code
|
||||
*
|
||||
* @param {string} mobile - 手机号(必填)
|
||||
* @returns {Promise<Object>} 后端通用响应结构
|
||||
*/
|
||||
export async function sendResetPasswordCode(mobile) {
|
||||
const { data } = await api.post(`${SERVER_BASE}/auth/send-sms-code`, {
|
||||
mobile,
|
||||
scene: SMS_SCENE.MEMBER_RESET_PASSWORD,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 未登录:通过手机验证码重置密码(忘记密码)
|
||||
* PUT /member/user/reset-password
|
||||
*
|
||||
* @param {string} mobile - 手机号(必填)
|
||||
* @param {string} newPassword - 新密码(必填,4-16 位)
|
||||
* @param {string} smsCode - 短信验证码(必填,4-6 位数字)
|
||||
* @returns {Promise<Object>} 后端通用响应结构
|
||||
*/
|
||||
export async function resetPasswordBySms(mobile, newPassword, smsCode) {
|
||||
const { data } = await api.put(`${SERVER_BASE}/user/reset-password`, {
|
||||
mobile,
|
||||
password: newPassword,
|
||||
code: smsCode,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* “手机+验证码+密码注册”组合流程(基于短信登录即注册 + 设置密码)
|
||||
* 说明:
|
||||
* - 1) 发送登录场景验证码(可选:若已拿到 code 可跳过)
|
||||
* - 2) 短信登录:首次会自动注册并返回 token
|
||||
* - 3) 登录态下发送“修改密码”验证码
|
||||
* - 4) 用短信验证码设置密码
|
||||
*
|
||||
* @param {string} mobile - 手机号(必填)
|
||||
* @param {string} loginCode - 第一次登录使用的短信验证码(必填)
|
||||
* @param {string} newPassword - 注册后设置的新密码(必填)
|
||||
* @param {string} updatePwdCode - 设置密码用到的短信验证码(必填)
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function registerWithMobileCodePassword(mobile, loginCode, newPassword, updatePwdCode) {
|
||||
// 1) 可选:发送登录场景验证码(若已拿到 loginCode 可跳过)
|
||||
// await sendSmsCode(mobile, SMS_SCENE.MEMBER_LOGIN);
|
||||
|
||||
// 2) 短信登录(首次即注册)
|
||||
await loginBySms(mobile, loginCode);
|
||||
|
||||
// 3) 登录态下发送“修改密码”验证码(短信会发到当前账号绑定的手机号)
|
||||
// await sendUpdatePasswordCode();
|
||||
|
||||
// 4) 用短信验证码设置密码
|
||||
await updatePasswordBySmsCode(newPassword, updatePwdCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出一个默认对象,便于统一引入
|
||||
*/
|
||||
export default {
|
||||
SMS_SCENE,
|
||||
SMS_TEMPLATE_CODE,
|
||||
loginByPassword,
|
||||
sendSmsCode,
|
||||
validateSmsCode,
|
||||
loginBySms,
|
||||
refreshToken,
|
||||
sendUpdatePasswordCode,
|
||||
updatePasswordBySmsCode,
|
||||
sendResetPasswordCode,
|
||||
resetPasswordBySms,
|
||||
registerWithMobileCodePassword,
|
||||
};
|
||||
74
frontend/app/web-gold/src/api/chat.js
Normal file
74
frontend/app/web-gold/src/api/chat.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import request from '@/api/http'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
const SERVER_BASE_AI= import.meta.env.VITE_BASE_URL + '/admin-api/ai'
|
||||
|
||||
|
||||
|
||||
// AI chat 聊天
|
||||
export const ChatMessageApi = {
|
||||
// 创建【我的】聊天对话
|
||||
createChatConversationMy: async (data) => {
|
||||
return await request.post(`${SERVER_BASE_AI}/chat/conversation/create-my`, data)
|
||||
},
|
||||
|
||||
|
||||
|
||||
// 发送 Stream 消息(对象入参,便于维护)
|
||||
// 为什么不用 axios 呢?因为它不支持 SSE 调用
|
||||
sendChatMessageStream: async (options) => {
|
||||
const {
|
||||
conversationId,
|
||||
content,
|
||||
ctrl,
|
||||
enableContext = true,
|
||||
enableWebSearch = false,
|
||||
onMessage,
|
||||
onError,
|
||||
onClose,
|
||||
attachmentUrls = []
|
||||
} = options || {}
|
||||
const token = getAccessToken()
|
||||
|
||||
let retryCount = 0
|
||||
const maxRetries = 0 // 禁用自动重试
|
||||
|
||||
return fetchEventSource(`${SERVER_BASE_AI}/chat/message/send-stream`, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
openWhenHidden: true,
|
||||
body: JSON.stringify({
|
||||
conversationId,
|
||||
content,
|
||||
useContext: enableContext,
|
||||
useSearch: enableWebSearch,
|
||||
attachmentUrls: attachmentUrls || []
|
||||
}),
|
||||
onmessage: onMessage,
|
||||
onerror: (err) => {
|
||||
retryCount++
|
||||
console.error('SSE错误,重试次数:', retryCount, err)
|
||||
|
||||
// 调用自定义错误处理
|
||||
if (typeof onError === 'function') {
|
||||
onError(err)
|
||||
}
|
||||
|
||||
// 超过最大重试次数,停止重连
|
||||
if (retryCount > maxRetries) {
|
||||
throw err // 抛出错误,停止自动重连
|
||||
}
|
||||
},
|
||||
onclose: () => {
|
||||
// 调用自定义关闭处理
|
||||
if (typeof onClose === 'function') {
|
||||
onClose()
|
||||
}
|
||||
},
|
||||
signal: ctrl ? ctrl.signal : undefined
|
||||
})
|
||||
},
|
||||
}
|
||||
67
frontend/app/web-gold/src/api/common.js
Normal file
67
frontend/app/web-gold/src/api/common.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import http from '@/api/http'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { getAuthHeader } from '@/utils/token-manager'
|
||||
|
||||
// 使用本地代理前缀 /tikhub,开发环境通过 Vite 代理到 https://api.tikhub.io
|
||||
const SERVER_BASE = '/webApi/admin-api/ai/tikHup'
|
||||
|
||||
export const CommonService = {
|
||||
videoToCharacters(data) {
|
||||
return http.post(`${SERVER_BASE}/videoToCharacters2`, data)
|
||||
},
|
||||
callWorkflow(data) {
|
||||
return http.post(`${SERVER_BASE}/callWorkflow`, data)
|
||||
},
|
||||
|
||||
// 流式调用 workflow
|
||||
callWorkflowStream: async (options) => {
|
||||
const {
|
||||
data,
|
||||
ctrl,
|
||||
onMessage,
|
||||
onError,
|
||||
onClose
|
||||
} = options || {}
|
||||
|
||||
const authHeader = getAuthHeader()
|
||||
|
||||
let retryCount = 0
|
||||
const maxRetries = 0 // 禁用自动重试
|
||||
|
||||
return fetchEventSource(`${SERVER_BASE}/callWorkflow`, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(authHeader ? { Authorization: authHeader } : {})
|
||||
},
|
||||
openWhenHidden: true,
|
||||
body: JSON.stringify(data),
|
||||
onmessage: onMessage,
|
||||
onerror: (err) => {
|
||||
retryCount++
|
||||
console.error('SSE错误,重试次数:', retryCount, err)
|
||||
|
||||
// 调用自定义错误处理
|
||||
if (typeof onError === 'function') {
|
||||
onError(err)
|
||||
}
|
||||
|
||||
// 超过最大重试次数,停止重连
|
||||
if (retryCount > maxRetries) {
|
||||
throw err // 抛出错误,停止自动重连
|
||||
}
|
||||
},
|
||||
onclose: () => {
|
||||
// 调用自定义关闭处理
|
||||
if (typeof onClose === 'function') {
|
||||
onClose()
|
||||
}
|
||||
},
|
||||
signal: ctrl ? ctrl.signal : undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default CommonService
|
||||
|
||||
|
||||
80
frontend/app/web-gold/src/api/http.js
Normal file
80
frontend/app/web-gold/src/api/http.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import axios from 'axios'
|
||||
import { getAuthHeader } from '@/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', // 刷新token(可选,根据后端要求)
|
||||
]
|
||||
|
||||
/**
|
||||
* 检查 URL 是否在白名单中
|
||||
* @param {string} url - 请求 URL
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isInWhiteList(url) {
|
||||
if (!url) return false
|
||||
return WHITE_LIST.some((path) => url.includes(path))
|
||||
}
|
||||
|
||||
/**
|
||||
* 可选:多租户场景可在此处统一注入 tenant-id
|
||||
* api.interceptors.request.use((config) => {
|
||||
* config.headers['tenant-id'] = '1';
|
||||
* return config;
|
||||
* });
|
||||
*/
|
||||
// 创建 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 || '')
|
||||
|
||||
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 时,抛出错误
|
||||
const error = new Error(data?.message || data?.msg || '请求失败')
|
||||
error.code = data?.code
|
||||
error.data = data
|
||||
return Promise.reject(error)
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
// 统一错误处理:输出关键信息,便于排查 403 等问题
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default http
|
||||
|
||||
|
||||
5
frontend/app/web-gold/src/api/tikhub.js
Normal file
5
frontend/app/web-gold/src/api/tikhub.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// TikHub API 统一导出入口
|
||||
export { TikhubService } from './tikhub/tikhub.js'
|
||||
export { default } from './tikhub/tikhub.js'
|
||||
export { InterfaceType, MethodType, InterfaceUrlMap, ParamType } from './tikhub/types.js'
|
||||
|
||||
180
frontend/app/web-gold/src/api/tikhub/README.md
Normal file
180
frontend/app/web-gold/src/api/tikhub/README.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# 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`
|
||||
|
||||
7
frontend/app/web-gold/src/api/tikhub/index.js
Normal file
7
frontend/app/web-gold/src/api/tikhub/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* TikHub API 统一导出
|
||||
*/
|
||||
export { TikhubService } from './tikhub.js'
|
||||
export { default } from './tikhub.js'
|
||||
export { InterfaceType, MethodType, InterfaceUrlMap, ParamType } from './types.js'
|
||||
|
||||
55
frontend/app/web-gold/src/api/tikhub/tikhub.js
Normal file
55
frontend/app/web-gold/src/api/tikhub/tikhub.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import http from '@/api/http'
|
||||
import { InterfaceType, MethodType, InterfaceUrlMap, ParamType } from './types'
|
||||
import qs from 'qs'
|
||||
// 使用本地代理前缀 /tikhub,开发环境通过 Vite 代理到 https://api.tikhub.io
|
||||
const SERVER_TIKHUB = '/webApi/admin-api/ai/tikHup'
|
||||
|
||||
/**
|
||||
* TikHub API 服务类
|
||||
*/
|
||||
export const TikhubService = {
|
||||
/**
|
||||
* 统一调用 TikHub 接口的中间层方法
|
||||
* @param {String} type - 接口类型,使用 InterfaceType 枚举
|
||||
* @param {String} methodType - HTTP 方法类型,使用 MethodType 枚举
|
||||
* @param {Object|String} urlParams - 实际接口的参数
|
||||
* @returns {Promise} axios 响应
|
||||
*
|
||||
* @example
|
||||
* // 调用抖音热门搜索接口
|
||||
* TikhubService.postTikHup(
|
||||
* InterfaceType.DOUYIN_WEB_HOT_SEARCH,
|
||||
* MethodType.GET,
|
||||
* { keyword: '测试' }
|
||||
* )
|
||||
*/
|
||||
postTikHup({
|
||||
type,
|
||||
methodType,
|
||||
urlParams,
|
||||
paramType = '',
|
||||
}) {
|
||||
// 验证接口类型是否存在
|
||||
if (!InterfaceUrlMap[type]) {
|
||||
throw new Error(`无效的接口类型: ${type}`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 构造请求参数
|
||||
const requestData = {
|
||||
type: type,
|
||||
methodType: methodType,
|
||||
paramType,
|
||||
urlParams: paramType === ParamType.JSON ? JSON.stringify(urlParams) : qs.stringify(urlParams),
|
||||
}
|
||||
|
||||
return http.post(`${SERVER_TIKHUB}/postTikHup`, qs.stringify(requestData))
|
||||
},
|
||||
}
|
||||
|
||||
export default TikhubService
|
||||
|
||||
// 导出枚举,方便外部使用
|
||||
export { InterfaceType, MethodType, InterfaceUrlMap }
|
||||
|
||||
87
frontend/app/web-gold/src/api/tikhub/types.js
Normal file
87
frontend/app/web-gold/src/api/tikhub/types.js
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* TikHub 接口类型枚举
|
||||
* 对应数据库 tik_token 表中的 interface_type 字段
|
||||
*/
|
||||
export const InterfaceType = {
|
||||
/** 小红书 - 获取用户信息 */
|
||||
XIAOHONGSHU_USER_INFO: '1',
|
||||
|
||||
/** 抖音 - 网页端获取用户发布的视频 */
|
||||
DOUYIN_WEB_USER_POST_VIDEOS: '2',
|
||||
|
||||
/** 抖音 - APP端V3获取用户发布的视频 */
|
||||
DOUYIN_APP_USER_POST_VIDEOS: '3',
|
||||
|
||||
/** 抖音 - APP端V3通用搜索结果 */
|
||||
DOUYIN_APP_GENERAL_SEARCH: '4',
|
||||
|
||||
/** 抖音 - 搜索建议 */
|
||||
DOUYIN_SEARCH_SUGGEST: '5',
|
||||
|
||||
/** 抖音 - 通用搜索V4 */
|
||||
DOUYIN_GENERAL_SEARCH_V4: '6',
|
||||
|
||||
/** 小红书 - 网页端V2热门榜单 */
|
||||
XIAOHONGSHU_WEB_HOT_LIST: '7',
|
||||
|
||||
/** 抖音 - 网页端热门搜索结果 */
|
||||
DOUYIN_WEB_HOT_SEARCH: '8',
|
||||
|
||||
/** 快手 - 网页端热门榜单V2 */
|
||||
KUAISHOU_WEB_HOT_LIST: '9',
|
||||
|
||||
/** B站 - 网页端流行内容 */
|
||||
BILIBILI_WEB_POPULAR: '10',
|
||||
|
||||
/** 微博 - 网页端V2热门搜索指数 */
|
||||
WEIBO_WEB_HOT_SEARCH: '11',
|
||||
|
||||
/** 抖音 - 网页端通用搜索结果 */
|
||||
DOUYIN_WEB_GENERAL_SEARCH: '12',
|
||||
/** 抖音 - APP通用搜索结果 */
|
||||
DOUYIN_SEARCH_GENERAL_SEARCH: '14',
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP 请求方法枚举
|
||||
*/
|
||||
export const MethodType = {
|
||||
GET: 'GET',
|
||||
POST: 'POST',
|
||||
PUT: 'PUT',
|
||||
DELETE: 'DELETE',
|
||||
PATCH: 'PATCH',
|
||||
}
|
||||
/**
|
||||
* 请求参数类型枚举
|
||||
*/
|
||||
export const ParamType = {
|
||||
JSON: 'json',
|
||||
FORM: 'form'
|
||||
}
|
||||
/**
|
||||
* 接口类型与平台 URL 的映射
|
||||
* 根据 tik_token 表中的数据生成
|
||||
*/
|
||||
export const InterfaceUrlMap = {
|
||||
[InterfaceType.XIAOHONGSHU_USER_INFO]: 'https://api.tikhub.io/api/v1/xiaohongshu/app/get_user_info',
|
||||
[InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS]: 'https://api.tikhub.io/api/v1/douyin/web/fetch_user_post_videos',
|
||||
[InterfaceType.DOUYIN_APP_USER_POST_VIDEOS]: 'https://api.tikhub.io/api/v1/douyin/app/v3/fetch_user_post_videos',
|
||||
[InterfaceType.DOUYIN_APP_GENERAL_SEARCH]: 'https://api.tikhub.io/api/v1/douyin/app/v3/fetch_general_search_result',
|
||||
[InterfaceType.DOUYIN_SEARCH_GENERAL_SEARCH]: 'https://api.tikhub.io/api/v1/douyin/search/fetch_general_search_v4',
|
||||
[InterfaceType.DOUYIN_SEARCH_SUGGEST]: 'https://api.tikhub.io/api/v1/douyin/search/fetch_search_suggest',
|
||||
[InterfaceType.DOUYIN_GENERAL_SEARCH_V4]: 'https://api.tikhub.io/api/v1/douyin/search/fetch_general_search_v4',
|
||||
[InterfaceType.XIAOHONGSHU_WEB_HOT_LIST]: 'https://api.tikhub.io/api/v1/xiaohongshu/web_v2/fetch_hot_list',
|
||||
[InterfaceType.DOUYIN_WEB_HOT_SEARCH]: 'https://api.tikhub.io/api/v1/douyin/web/fetch_hot_search_result',
|
||||
[InterfaceType.KUAISHOU_WEB_HOT_LIST]: 'https://api.tikhub.io/api/v1/kuaishou/web/fetch_kuaishou_hot_list_v2',
|
||||
[InterfaceType.BILIBILI_WEB_POPULAR]: 'https://api.tikhub.io/api/v1/bilibili/web/fetch_com_popular',
|
||||
[InterfaceType.WEIBO_WEB_HOT_SEARCH]: 'https://api.tikhub.io/api/v1/weibo/web_v2/fetch_hot_search_index',
|
||||
[InterfaceType.DOUYIN_WEB_GENERAL_SEARCH]: 'https://api.tikhub.io/api/v1/douyin/web/fetch_general_search_result',
|
||||
}
|
||||
|
||||
export default {
|
||||
InterfaceType,
|
||||
MethodType,
|
||||
InterfaceUrlMap,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user