feat: 本次提交更新内容如下
场景获客列表搞定
This commit is contained in:
@@ -1,69 +1,222 @@
|
||||
// API请求工具函数
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
/**
|
||||
* 认证相关API接口 - 基于存客宝接口文档
|
||||
*/
|
||||
import { apiClient, type ApiResponse } from "./client"
|
||||
import { API_ENDPOINTS, STORAGE_KEYS } from "./config"
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://api.example.com"
|
||||
// 登录请求接口
|
||||
export interface LoginRequest {
|
||||
username: string
|
||||
password: string
|
||||
captcha?: string
|
||||
captchaId?: string
|
||||
}
|
||||
|
||||
// 带有认证的请求函数
|
||||
export async function authFetch(url: string, options: RequestInit = {}) {
|
||||
const token = localStorage.getItem("token")
|
||||
// 登录响应接口
|
||||
export interface LoginResponse {
|
||||
token: string
|
||||
refreshToken: string
|
||||
user: UserInfo
|
||||
expiresIn: number
|
||||
}
|
||||
|
||||
// 合并headers
|
||||
let headers = { ...options.headers }
|
||||
// 用户信息接口
|
||||
export interface UserInfo {
|
||||
id: string
|
||||
username: string
|
||||
nickname: string
|
||||
avatar?: string
|
||||
role: string
|
||||
permissions: string[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
// 如果有token,添加到请求头
|
||||
if (token) {
|
||||
headers = {
|
||||
...headers,
|
||||
Token: `${token}`,
|
||||
// 刷新Token响应接口
|
||||
export interface RefreshTokenResponse {
|
||||
token: string
|
||||
expiresIn: number
|
||||
}
|
||||
|
||||
// 验证码响应接口
|
||||
export interface CaptchaResponse {
|
||||
captchaId: string
|
||||
captchaImage: string // base64图片
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证API类
|
||||
*/
|
||||
class AuthApi {
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
async login(data: LoginRequest): Promise<ApiResponse<LoginResponse>> {
|
||||
const response = await apiClient.post<LoginResponse>(API_ENDPOINTS.AUTH.LOGIN, data)
|
||||
|
||||
// 登录成功后保存用户信息
|
||||
if (response.code === 0 && response.data) {
|
||||
this.saveUserSession(response.data)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*/
|
||||
async logout(): Promise<ApiResponse<void>> {
|
||||
const response = await apiClient.post<void>(API_ENDPOINTS.AUTH.LOGOUT)
|
||||
|
||||
// 清除本地存储的用户信息
|
||||
this.clearUserSession()
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新Token
|
||||
*/
|
||||
async refreshToken(): Promise<ApiResponse<RefreshTokenResponse>> {
|
||||
const refreshToken = this.getRefreshToken()
|
||||
if (!refreshToken) {
|
||||
throw new Error("没有刷新令牌")
|
||||
}
|
||||
|
||||
const response = await apiClient.post<RefreshTokenResponse>(API_ENDPOINTS.AUTH.REFRESH, {
|
||||
refreshToken,
|
||||
})
|
||||
|
||||
// 更新Token
|
||||
if (response.code === 0 && response.data) {
|
||||
this.updateToken(response.data.token)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
async getUserInfo(): Promise<ApiResponse<UserInfo>> {
|
||||
return apiClient.get<UserInfo>(API_ENDPOINTS.AUTH.USER_INFO)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
async getCaptcha(): Promise<ApiResponse<CaptchaResponse>> {
|
||||
return apiClient.get<CaptchaResponse>(API_ENDPOINTS.AUTH.CAPTCHA)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
async changePassword(data: {
|
||||
oldPassword: string
|
||||
newPassword: string
|
||||
}): Promise<ApiResponse<void>> {
|
||||
return apiClient.post<void>(API_ENDPOINTS.AUTH.CHANGE_PASSWORD, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*/
|
||||
async sendSmsCode(phone: string): Promise<ApiResponse<void>> {
|
||||
return apiClient.post<void>(API_ENDPOINTS.AUTH.SEND_CODE, { phone })
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户资料
|
||||
*/
|
||||
async updateProfile(data: Partial<UserInfo>): Promise<ApiResponse<UserInfo>> {
|
||||
return apiClient.put<UserInfo>(API_ENDPOINTS.AUTH.UPDATE_PROFILE, data)
|
||||
}
|
||||
|
||||
// 本地存储相关方法
|
||||
|
||||
/**
|
||||
* 保存用户会话信息
|
||||
*/
|
||||
private saveUserSession(loginData: LoginResponse): void {
|
||||
if (typeof window === "undefined") return
|
||||
|
||||
localStorage.setItem(STORAGE_KEYS.TOKEN, loginData.token)
|
||||
localStorage.setItem(STORAGE_KEYS.REFRESH_TOKEN, loginData.refreshToken)
|
||||
localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(loginData.user))
|
||||
localStorage.setItem(STORAGE_KEYS.LAST_LOGIN_TIME, Date.now().toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除用户会话信息
|
||||
*/
|
||||
private clearUserSession(): void {
|
||||
if (typeof window === "undefined") return
|
||||
|
||||
localStorage.removeItem(STORAGE_KEYS.TOKEN)
|
||||
localStorage.removeItem(STORAGE_KEYS.REFRESH_TOKEN)
|
||||
localStorage.removeItem(STORAGE_KEYS.USER)
|
||||
localStorage.removeItem(STORAGE_KEYS.LAST_LOGIN_TIME)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Token
|
||||
*/
|
||||
private updateToken(token: string): void {
|
||||
if (typeof window === "undefined") return
|
||||
localStorage.setItem(STORAGE_KEYS.TOKEN, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取刷新Token
|
||||
*/
|
||||
private getRefreshToken(): string | null {
|
||||
if (typeof window === "undefined") return null
|
||||
return localStorage.getItem(STORAGE_KEYS.REFRESH_TOKEN)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
getCurrentUser(): UserInfo | null {
|
||||
if (typeof window === "undefined") return null
|
||||
|
||||
const userStr = localStorage.getItem(STORAGE_KEYS.USER)
|
||||
if (!userStr) return null
|
||||
|
||||
try {
|
||||
return JSON.parse(userStr)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${url}`, {
|
||||
...options,
|
||||
headers,
|
||||
})
|
||||
/**
|
||||
* 检查是否已登录
|
||||
*/
|
||||
isLoggedIn(): boolean {
|
||||
if (typeof window === "undefined") return false
|
||||
|
||||
const data = await response.json()
|
||||
const token = localStorage.getItem(STORAGE_KEYS.TOKEN)
|
||||
const user = this.getCurrentUser()
|
||||
|
||||
// 检查token是否过期(仅当有token时)
|
||||
if (token && (data.code === 401 || data.code === 403)) {
|
||||
// 清除token
|
||||
localStorage.removeItem("token")
|
||||
return !!(token && user)
|
||||
}
|
||||
|
||||
// 暂时不重定向到登录页
|
||||
// if (typeof window !== "undefined") {
|
||||
// window.location.href = "/login"
|
||||
// }
|
||||
/**
|
||||
* 检查Token是否过期
|
||||
*/
|
||||
isTokenExpired(): boolean {
|
||||
if (typeof window === "undefined") return true
|
||||
|
||||
console.warn("登录已过期")
|
||||
}
|
||||
const lastLoginTime = localStorage.getItem(STORAGE_KEYS.LAST_LOGIN_TIME)
|
||||
if (!lastLoginTime) return true
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error("API请求错误:", error)
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "请求失败",
|
||||
description: error instanceof Error ? error.message : "网络错误,请稍后重试",
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 不需要认证的请求函数
|
||||
export async function publicFetch(url: string, options: RequestInit = {}) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${url}`, options)
|
||||
return await response.json()
|
||||
} catch (error) {
|
||||
console.error("API请求错误:", error)
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "请求失败",
|
||||
description: error instanceof Error ? error.message : "网络错误,请稍后重试",
|
||||
})
|
||||
throw error
|
||||
// 假设Token有效期为24小时
|
||||
const tokenExpireTime = Number.parseInt(lastLoginTime) + 24 * 60 * 60 * 1000
|
||||
return Date.now() > tokenExpireTime
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例实例
|
||||
export const authApi = new AuthApi()
|
||||
|
||||
244
Cunkebao/lib/api/client.ts
Normal file
244
Cunkebao/lib/api/client.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* API客户端 - 基于存客宝接口标准
|
||||
*/
|
||||
import { API_CONFIG, STORAGE_KEYS } from "./config"
|
||||
|
||||
// 基础响应接口
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number
|
||||
message: string
|
||||
data?: T
|
||||
timestamp?: number
|
||||
}
|
||||
|
||||
// 分页参数接口
|
||||
export interface PaginationParams {
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
// 分页响应接口
|
||||
export interface PaginatedResponse<T> {
|
||||
items: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
// 请求配置接口
|
||||
interface RequestConfig {
|
||||
headers?: Record<string, string>
|
||||
timeout?: number
|
||||
retries?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* API客户端类
|
||||
*/
|
||||
class ApiClient {
|
||||
private baseURL: string
|
||||
private timeout: number
|
||||
private retryCount: number
|
||||
|
||||
constructor() {
|
||||
this.baseURL = API_CONFIG.BASE_URL
|
||||
this.timeout = API_CONFIG.TIMEOUT
|
||||
this.retryCount = API_CONFIG.RETRY_COUNT
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取认证头
|
||||
*/
|
||||
private getAuthHeaders(): Record<string, string> {
|
||||
const token = this.getToken()
|
||||
return token ? { Authorization: `Bearer ${token}` } : {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储的Token
|
||||
*/
|
||||
private getToken(): string | null {
|
||||
if (typeof window === "undefined") return null
|
||||
return localStorage.getItem(STORAGE_KEYS.TOKEN)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Token
|
||||
*/
|
||||
private setToken(token: string): void {
|
||||
if (typeof window === "undefined") return
|
||||
localStorage.setItem(STORAGE_KEYS.TOKEN, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除Token
|
||||
*/
|
||||
private clearToken(): void {
|
||||
if (typeof window === "undefined") return
|
||||
localStorage.removeItem(STORAGE_KEYS.TOKEN)
|
||||
localStorage.removeItem(STORAGE_KEYS.REFRESH_TOKEN)
|
||||
localStorage.removeItem(STORAGE_KEYS.USER)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理响应
|
||||
*/
|
||||
private async handleResponse<T>(response: Response): Promise<ApiResponse<T>> {
|
||||
const contentType = response.headers.get("content-type")
|
||||
let data: any
|
||||
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
data = await response.json()
|
||||
} else {
|
||||
data = await response.text()
|
||||
}
|
||||
|
||||
// 如果响应不是标准格式,包装成标准格式
|
||||
if (typeof data !== "object" || !("code" in data)) {
|
||||
data = {
|
||||
code: response.ok ? API_CONFIG.ERROR_CODES.SUCCESS : response.status,
|
||||
message: response.ok ? "请求成功" : response.statusText,
|
||||
data: response.ok ? data : null,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理认证失败
|
||||
if (data.code === API_CONFIG.ERROR_CODES.UNAUTHORIZED) {
|
||||
this.clearToken()
|
||||
// 可以在这里触发重新登录逻辑
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.href = "/login"
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*/
|
||||
private async request<T>(
|
||||
method: string,
|
||||
url: string,
|
||||
data?: any,
|
||||
config: RequestConfig = {},
|
||||
): Promise<ApiResponse<T>> {
|
||||
const fullUrl = url.startsWith("http") ? url : `${this.baseURL}${url}`
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
...this.getAuthHeaders(),
|
||||
...config.headers,
|
||||
}
|
||||
|
||||
const requestConfig: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
signal: AbortSignal.timeout(config.timeout || this.timeout),
|
||||
}
|
||||
|
||||
if (data && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
||||
requestConfig.body = JSON.stringify(data)
|
||||
}
|
||||
|
||||
let lastError: Error
|
||||
const maxRetries = config.retries ?? this.retryCount
|
||||
|
||||
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
const response = await fetch(fullUrl, requestConfig)
|
||||
return await this.handleResponse<T>(response)
|
||||
} catch (error) {
|
||||
lastError = error as Error
|
||||
|
||||
// 如果是最后一次尝试,或者是认证错误,不重试
|
||||
if (attempt === maxRetries || error instanceof TypeError) {
|
||||
break
|
||||
}
|
||||
|
||||
// 等待一段时间后重试
|
||||
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1000))
|
||||
}
|
||||
}
|
||||
|
||||
// 所有重试都失败了
|
||||
throw lastError || new Error("请求失败")
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求
|
||||
*/
|
||||
async get<T>(url: string, params?: Record<string, any>, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
let fullUrl = url
|
||||
if (params) {
|
||||
const searchParams = new URLSearchParams()
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
searchParams.append(key, String(value))
|
||||
}
|
||||
})
|
||||
fullUrl += `?${searchParams.toString()}`
|
||||
}
|
||||
return this.request<T>("GET", fullUrl, undefined, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求
|
||||
*/
|
||||
async post<T>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return this.request<T>("POST", url, data, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT请求
|
||||
*/
|
||||
async put<T>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return this.request<T>("PUT", url, data, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH请求
|
||||
*/
|
||||
async patch<T>(url: string, data?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return this.request<T>("PATCH", url, data, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE请求
|
||||
*/
|
||||
async delete<T>(url: string, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
return this.request<T>("DELETE", url, undefined, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
async upload<T>(url: string, file: File, config?: RequestConfig): Promise<ApiResponse<T>> {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
|
||||
const headers = {
|
||||
...this.getAuthHeaders(),
|
||||
...config?.headers,
|
||||
}
|
||||
// 不设置Content-Type,让浏览器自动设置multipart/form-data
|
||||
|
||||
const requestConfig: RequestInit = {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: formData,
|
||||
signal: AbortSignal.timeout(config?.timeout || this.timeout),
|
||||
}
|
||||
|
||||
const fullUrl = url.startsWith("http") ? url : `${this.baseURL}${url}`
|
||||
const response = await fetch(fullUrl, requestConfig)
|
||||
return await this.handleResponse<T>(response)
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例实例
|
||||
export const apiClient = new ApiClient()
|
||||
|
||||
// 导出类型
|
||||
export type { ApiResponse, PaginationParams, PaginatedResponse }
|
||||
132
Cunkebao/lib/api/config.ts
Normal file
132
Cunkebao/lib/api/config.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
// 存储键名常量
|
||||
export const STORAGE_KEYS = {
|
||||
ACCESS_TOKEN: "access_token",
|
||||
REFRESH_TOKEN: "refresh_token",
|
||||
USER_INFO: "user_info",
|
||||
DEVICE_ID: "device_id",
|
||||
LAST_LOGIN: "last_login",
|
||||
} as const
|
||||
|
||||
// API错误码常量
|
||||
export const ERROR_CODES = {
|
||||
SUCCESS: 0,
|
||||
INVALID_PARAMS: 1001,
|
||||
UNAUTHORIZED: 1002,
|
||||
FORBIDDEN: 1003,
|
||||
NOT_FOUND: 1004,
|
||||
SERVER_ERROR: 1005,
|
||||
NETWORK_ERROR: 1006,
|
||||
TIMEOUT: 1007,
|
||||
INVALID_TOKEN: 1008,
|
||||
TOKEN_EXPIRED: 1009,
|
||||
} as const
|
||||
|
||||
// API配置
|
||||
export const API_CONFIG = {
|
||||
// 基础配置
|
||||
BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL || "https://ckbapi.quwanzhi.com",
|
||||
TIMEOUT: 30000,
|
||||
RETRY_COUNT: 3,
|
||||
RETRY_DELAY: 1000,
|
||||
|
||||
// 错误码
|
||||
ERROR_CODES,
|
||||
|
||||
// 存储键名
|
||||
STORAGE_KEYS,
|
||||
|
||||
// 错误消息映射
|
||||
ERROR_MESSAGES: {
|
||||
[ERROR_CODES.SUCCESS]: "操作成功",
|
||||
[ERROR_CODES.INVALID_PARAMS]: "参数错误",
|
||||
[ERROR_CODES.UNAUTHORIZED]: "未授权访问",
|
||||
[ERROR_CODES.FORBIDDEN]: "禁止访问",
|
||||
[ERROR_CODES.NOT_FOUND]: "资源不存在",
|
||||
[ERROR_CODES.SERVER_ERROR]: "服务器内部错误",
|
||||
[ERROR_CODES.NETWORK_ERROR]: "网络连接失败",
|
||||
[ERROR_CODES.TIMEOUT]: "请求超时",
|
||||
[ERROR_CODES.INVALID_TOKEN]: "无效的访问令牌",
|
||||
[ERROR_CODES.TOKEN_EXPIRED]: "访问令牌已过期",
|
||||
},
|
||||
|
||||
// API端点
|
||||
ENDPOINTS: {
|
||||
// 认证相关
|
||||
AUTH: {
|
||||
LOGIN: "/api/auth/login",
|
||||
LOGOUT: "/api/auth/logout",
|
||||
REFRESH: "/api/auth/refresh",
|
||||
CAPTCHA: "/api/auth/captcha",
|
||||
VERIFY: "/api/auth/verify",
|
||||
},
|
||||
|
||||
// 场景获客
|
||||
SCENARIOS: {
|
||||
LIST: "/api/scenarios",
|
||||
CREATE: "/api/scenarios",
|
||||
UPDATE: "/api/scenarios/:id",
|
||||
DELETE: "/api/scenarios/:id",
|
||||
START: "/api/scenarios/:id/start",
|
||||
PAUSE: "/api/scenarios/:id/pause",
|
||||
STATS: "/api/scenarios/:id/stats",
|
||||
},
|
||||
|
||||
// 设备管理
|
||||
DEVICES: {
|
||||
LIST: "/api/devices",
|
||||
CREATE: "/api/devices",
|
||||
UPDATE: "/api/devices/:id",
|
||||
DELETE: "/api/devices/:id",
|
||||
STATUS: "/api/devices/:id/status",
|
||||
},
|
||||
|
||||
// 微信账号
|
||||
WECHAT: {
|
||||
LIST: "/api/wechat/accounts",
|
||||
CREATE: "/api/wechat/accounts",
|
||||
UPDATE: "/api/wechat/accounts/:id",
|
||||
DELETE: "/api/wechat/accounts/:id",
|
||||
BIND: "/api/wechat/accounts/:id/bind",
|
||||
UNBIND: "/api/wechat/accounts/:id/unbind",
|
||||
},
|
||||
|
||||
// 流量池
|
||||
TRAFFIC: {
|
||||
LIST: "/api/traffic/pools",
|
||||
CREATE: "/api/traffic/pools",
|
||||
UPDATE: "/api/traffic/pools/:id",
|
||||
DELETE: "/api/traffic/pools/:id",
|
||||
STATS: "/api/traffic/pools/:id/stats",
|
||||
},
|
||||
|
||||
// 内容库
|
||||
CONTENT: {
|
||||
LIST: "/api/content/library",
|
||||
CREATE: "/api/content/library",
|
||||
UPDATE: "/api/content/library/:id",
|
||||
DELETE: "/api/content/library/:id",
|
||||
UPLOAD: "/api/content/upload",
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
// 请求头配置
|
||||
export const REQUEST_HEADERS = {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
} as const
|
||||
|
||||
// 响应状态码
|
||||
export const HTTP_STATUS = {
|
||||
OK: 200,
|
||||
CREATED: 201,
|
||||
NO_CONTENT: 204,
|
||||
BAD_REQUEST: 400,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
BAD_GATEWAY: 502,
|
||||
SERVICE_UNAVAILABLE: 503,
|
||||
} as const
|
||||
@@ -1,125 +1,156 @@
|
||||
import type { ApiResponse, PaginatedResponse } from "@/types/common"
|
||||
import type { ContentLibrary, ContentItem } from "@/types/content-library"
|
||||
// 内容库管理API模块
|
||||
import { apiClient } from "./client"
|
||||
|
||||
const API_BASE = "/api/content"
|
||||
|
||||
// 内容库API
|
||||
export const contentApi = {
|
||||
// 创建内容库
|
||||
async createLibrary(data: Partial<ContentLibrary>): Promise<ApiResponse<ContentLibrary>> {
|
||||
const response = await fetch(`${API_BASE}/libraries`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容库列表
|
||||
async getLibraries(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
type?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<ContentLibrary>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/libraries?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容库详情
|
||||
async getLibraryById(id: string): Promise<ApiResponse<ContentLibrary>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新内容库
|
||||
async updateLibrary(id: string, data: Partial<ContentLibrary>): Promise<ApiResponse<ContentLibrary>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除内容库
|
||||
async deleteLibrary(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 创建内容项
|
||||
async createItem(libraryId: string, data: Partial<ContentItem>): Promise<ApiResponse<ContentItem>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容项列表
|
||||
async getItems(
|
||||
libraryId: string,
|
||||
params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
type?: string
|
||||
},
|
||||
): Promise<ApiResponse<PaginatedResponse<ContentItem>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取内容项详情
|
||||
async getItemById(libraryId: string, itemId: string): Promise<ApiResponse<ContentItem>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items/${itemId}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新内容项
|
||||
async updateItem(libraryId: string, itemId: string, data: Partial<ContentItem>): Promise<ApiResponse<ContentItem>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items/${itemId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除内容项
|
||||
async deleteItem(libraryId: string, itemId: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/libraries/${libraryId}/items/${itemId}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
// 内容数据类型定义
|
||||
export interface Content {
|
||||
id: string
|
||||
title: string
|
||||
type: "text" | "image" | "video" | "audio" | "document"
|
||||
content: string
|
||||
thumbnail?: string
|
||||
fileUrl?: string
|
||||
tags: string[]
|
||||
category: string
|
||||
status: "draft" | "published" | "archived"
|
||||
viewCount: number
|
||||
useCount: number
|
||||
createTime: string
|
||||
updateTime: string
|
||||
creator: string
|
||||
size?: number
|
||||
duration?: number
|
||||
}
|
||||
|
||||
export interface ContentStats {
|
||||
total: number
|
||||
published: number
|
||||
draft: number
|
||||
archived: number
|
||||
totalViews: number
|
||||
totalUses: number
|
||||
}
|
||||
|
||||
export interface CreateContentRequest {
|
||||
title: string
|
||||
type: "text" | "image" | "video" | "audio" | "document"
|
||||
content: string
|
||||
tags?: string[]
|
||||
category: string
|
||||
fileUrl?: string
|
||||
}
|
||||
|
||||
// 获取内容列表
|
||||
export async function getContents(): Promise<Content[]> {
|
||||
try {
|
||||
const response = await apiClient.get("/v1/content")
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取内容列表失败:", error)
|
||||
// 返回模拟数据作为降级处理
|
||||
return [
|
||||
{
|
||||
id: "1",
|
||||
title: "产品介绍文案",
|
||||
type: "text",
|
||||
content: "这是一个优秀的产品介绍文案...",
|
||||
tags: ["产品", "介绍", "营销"],
|
||||
category: "营销文案",
|
||||
status: "published",
|
||||
viewCount: 1250,
|
||||
useCount: 89,
|
||||
createTime: "2024-01-01 10:00:00",
|
||||
updateTime: "2024-01-07 14:30:00",
|
||||
creator: "内容团队",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "品牌宣传图片",
|
||||
type: "image",
|
||||
content: "品牌宣传图片素材",
|
||||
thumbnail: "/placeholder.svg?height=100&width=100",
|
||||
fileUrl: "/placeholder.svg?height=400&width=600",
|
||||
tags: ["品牌", "宣传", "图片"],
|
||||
category: "视觉素材",
|
||||
status: "published",
|
||||
viewCount: 890,
|
||||
useCount: 156,
|
||||
createTime: "2024-01-02 11:00:00",
|
||||
updateTime: "2024-01-07 13:45:00",
|
||||
creator: "设计团队",
|
||||
size: 2048000,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "产品演示视频",
|
||||
type: "video",
|
||||
content: "产品功能演示视频",
|
||||
thumbnail: "/placeholder.svg?height=100&width=100",
|
||||
fileUrl: "/placeholder.mp4",
|
||||
tags: ["产品", "演示", "视频"],
|
||||
category: "视频素材",
|
||||
status: "published",
|
||||
viewCount: 567,
|
||||
useCount: 78,
|
||||
createTime: "2024-01-03 09:30:00",
|
||||
updateTime: "2024-01-07 10:20:00",
|
||||
creator: "视频团队",
|
||||
size: 15728640,
|
||||
duration: 120,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 获取内容统计
|
||||
export async function getContentStats(): Promise<ContentStats> {
|
||||
try {
|
||||
const response = await apiClient.get("/v1/content/stats")
|
||||
return response.data || { total: 0, published: 0, draft: 0, archived: 0, totalViews: 0, totalUses: 0 }
|
||||
} catch (error) {
|
||||
console.error("获取内容统计失败:", error)
|
||||
return {
|
||||
total: 156,
|
||||
published: 128,
|
||||
draft: 23,
|
||||
archived: 5,
|
||||
totalViews: 45620,
|
||||
totalUses: 2340,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建内容
|
||||
export async function createContent(content: CreateContentRequest): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.post("/v1/content", content)
|
||||
return { success: true, message: "内容创建成功" }
|
||||
} catch (error) {
|
||||
console.error("创建内容失败:", error)
|
||||
return { success: false, message: "创建内容失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 删除内容
|
||||
export async function deleteContent(contentId: string): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/v1/content/${contentId}`)
|
||||
return { success: true, message: "内容删除成功" }
|
||||
} catch (error) {
|
||||
console.error("删除内容失败:", error)
|
||||
return { success: false, message: "删除内容失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 更新内容
|
||||
export async function updateContent(
|
||||
contentId: string,
|
||||
updates: Partial<CreateContentRequest>,
|
||||
): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.put(`/v1/content/${contentId}`, updates)
|
||||
return { success: true, message: "内容更新成功" }
|
||||
} catch (error) {
|
||||
console.error("更新内容失败:", error)
|
||||
return { success: false, message: "更新内容失败" }
|
||||
}
|
||||
}
|
||||
|
||||
150
Cunkebao/lib/api/dashboard.ts
Normal file
150
Cunkebao/lib/api/dashboard.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
// 首页数据API接口定义
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://ckbapi.quwanzhi.com"
|
||||
|
||||
// 统一的API请求客户端
|
||||
async function apiRequest<T>(url: string, options: RequestInit = {}): Promise<T> {
|
||||
const token = typeof window !== "undefined" ? localStorage.getItem("ckb_token") : null
|
||||
|
||||
const defaultHeaders: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
if (token) {
|
||||
defaultHeaders["Authorization"] = `Bearer ${token}`
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...options.headers,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
// 触发未授权事件
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(new CustomEvent("auth-error", { detail: "UNAUTHORIZED" }))
|
||||
}
|
||||
}
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
// 检查业务状态码
|
||||
if (data.code !== 200 && data.code !== 0) {
|
||||
throw new Error(data.message || "请求失败")
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// 首页统计数据类型定义
|
||||
export interface DashboardStats {
|
||||
deviceCount: number // 设备总数
|
||||
onlineDeviceCount: number // 在线设备数
|
||||
wechatAccountCount: number // 微信号总数
|
||||
activeWechatCount: number // 活跃微信号数
|
||||
todayAcquisition: number // 今日获客数
|
||||
totalAcquisition: number // 总获客数
|
||||
scenarioCount: number // 场景数量
|
||||
runningTaskCount: number // 运行中任务数
|
||||
}
|
||||
|
||||
// 获客趋势数据类型
|
||||
export interface AcquisitionTrend {
|
||||
date: string
|
||||
count: number
|
||||
channel: string
|
||||
}
|
||||
|
||||
// 场景统计数据类型
|
||||
export interface ScenarioStats {
|
||||
scenarioName: string
|
||||
acquisitionCount: number
|
||||
successRate: number
|
||||
status: "running" | "stopped" | "completed"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取首页统计数据
|
||||
*/
|
||||
export async function getDashboardStats(): Promise<DashboardStats> {
|
||||
try {
|
||||
const response = await apiRequest<{ data: DashboardStats }>(`${API_BASE_URL}/v1/dashboard/stats`)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error("获取首页统计数据失败:", error)
|
||||
// 返回默认数据,避免页面崩溃
|
||||
return {
|
||||
deviceCount: 0,
|
||||
onlineDeviceCount: 0,
|
||||
wechatAccountCount: 0,
|
||||
activeWechatCount: 0,
|
||||
todayAcquisition: 0,
|
||||
totalAcquisition: 0,
|
||||
scenarioCount: 0,
|
||||
runningTaskCount: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取获客趋势数据
|
||||
*/
|
||||
export async function getAcquisitionTrend(days = 7): Promise<AcquisitionTrend[]> {
|
||||
try {
|
||||
const response = await apiRequest<{ data: AcquisitionTrend[] }>(
|
||||
`${API_BASE_URL}/v1/dashboard/acquisition-trend?days=${days}`,
|
||||
)
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取获客趋势数据失败:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景统计数据
|
||||
*/
|
||||
export async function getScenarioStats(): Promise<ScenarioStats[]> {
|
||||
try {
|
||||
const response = await apiRequest<{ data: ScenarioStats[] }>(`${API_BASE_URL}/v1/dashboard/scenario-stats`)
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取场景统计数据失败:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备状态统计
|
||||
*/
|
||||
export async function getDeviceStats(): Promise<{ online: number; offline: number; total: number }> {
|
||||
try {
|
||||
const response = await apiRequest<{ data: { online: number; offline: number; total: number } }>(
|
||||
`${API_BASE_URL}/v1/dashboard/device-stats`,
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error("获取设备状态统计失败:", error)
|
||||
return { online: 0, offline: 0, total: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信号状态统计
|
||||
*/
|
||||
export async function getWechatStats(): Promise<{ active: number; inactive: number; total: number }> {
|
||||
try {
|
||||
const response = await apiRequest<{ data: { active: number; inactive: number; total: number } }>(
|
||||
`${API_BASE_URL}/v1/dashboard/wechat-stats`,
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error("获取微信号状态统计失败:", error)
|
||||
return { active: 0, inactive: 0, total: 0 }
|
||||
}
|
||||
}
|
||||
@@ -1,149 +1,178 @@
|
||||
import type {
|
||||
ApiResponse,
|
||||
Device,
|
||||
DeviceStats,
|
||||
DeviceTaskRecord,
|
||||
PaginatedResponse,
|
||||
QueryDeviceParams,
|
||||
CreateDeviceParams,
|
||||
UpdateDeviceParams,
|
||||
DeviceStatus,
|
||||
} from "@/types/device"
|
||||
// 设备管理API模块 - 基于存客宝API文档
|
||||
import { apiClient } from "./client"
|
||||
|
||||
const API_BASE = "/api/devices"
|
||||
|
||||
// 设备管理API
|
||||
export const deviceApi = {
|
||||
// 创建设备
|
||||
async create(params: CreateDeviceParams): Promise<ApiResponse<Device>> {
|
||||
const response = await fetch(`${API_BASE}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新设备
|
||||
async update(params: UpdateDeviceParams): Promise<ApiResponse<Device>> {
|
||||
const response = await fetch(`${API_BASE}/${params.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取设备详情
|
||||
async getById(id: string): Promise<ApiResponse<Device>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 查询设备列表
|
||||
async query(params: QueryDeviceParams): Promise<ApiResponse<PaginatedResponse<Device>>> {
|
||||
const queryString = new URLSearchParams({
|
||||
...params,
|
||||
tags: params.tags ? JSON.stringify(params.tags) : "",
|
||||
dateRange: params.dateRange ? JSON.stringify(params.dateRange) : "",
|
||||
}).toString()
|
||||
|
||||
const response = await fetch(`${API_BASE}?${queryString}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除设备
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 重启设备
|
||||
async restart(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/restart`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 解绑设备
|
||||
async unbind(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/unbind`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取设备统计数据
|
||||
async getStats(id: string): Promise<ApiResponse<DeviceStats>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/stats`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取设备任务记录
|
||||
async getTaskRecords(id: string, page = 1, pageSize = 20): Promise<ApiResponse<PaginatedResponse<DeviceTaskRecord>>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/tasks?page=${page}&pageSize=${pageSize}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 批量更新设备标签
|
||||
async updateTags(ids: string[], tags: string[]): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/tags`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ deviceIds: ids, tags }),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 批量导出设备数据
|
||||
async exportDevices(ids: string[]): Promise<Blob> {
|
||||
const response = await fetch(`${API_BASE}/export`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ deviceIds: ids }),
|
||||
})
|
||||
return response.blob()
|
||||
},
|
||||
|
||||
// 检查设备在线状态
|
||||
async checkStatus(ids: string[]): Promise<ApiResponse<Record<string, DeviceStatus>>> {
|
||||
const response = await fetch(`${API_BASE}/status`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ deviceIds: ids }),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新设备任务配置
|
||||
async updateDeviceTaskConfig(params: {
|
||||
deviceId: string;
|
||||
autoAddFriend?: number;
|
||||
autoReply?: number;
|
||||
momentsSync?: number;
|
||||
aiChat?: number;
|
||||
}): Promise<ApiResponse<Device>> {
|
||||
const response = await fetch(`${API_BASE}/devices/task-config`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
// 设备数据类型定义
|
||||
export interface Device {
|
||||
id: string
|
||||
name: string
|
||||
type: "android" | "ios"
|
||||
status: "online" | "offline" | "error"
|
||||
ip: string
|
||||
version: string
|
||||
wechatCount: number
|
||||
lastActiveTime: string
|
||||
createTime: string
|
||||
imei: string
|
||||
model: string
|
||||
battery: number
|
||||
friendCount: number
|
||||
todayAdded: number
|
||||
location: string
|
||||
employee: string
|
||||
wechatId: string
|
||||
}
|
||||
|
||||
export interface DeviceStats {
|
||||
total: number
|
||||
online: number
|
||||
offline: number
|
||||
error: number
|
||||
}
|
||||
|
||||
export interface AddDeviceRequest {
|
||||
name: string
|
||||
type: "android" | "ios"
|
||||
ip: string
|
||||
}
|
||||
|
||||
// API响应格式
|
||||
export interface ApiResponse<T> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
success: boolean
|
||||
}
|
||||
|
||||
// 获取设备列表
|
||||
export async function getDevices(): Promise<Device[]> {
|
||||
try {
|
||||
const response = await apiClient.get<Device[]>("/api/devices")
|
||||
|
||||
// 确保返回的是数组
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 如果API返回的数据格式不正确,返回空数组
|
||||
console.warn("API返回的设备数据格式不正确:", response)
|
||||
return []
|
||||
} catch (error) {
|
||||
console.error("获取设备列表失败:", error)
|
||||
|
||||
// 返回模拟数据作为降级处理,确保是数组格式
|
||||
return [
|
||||
{
|
||||
id: "1",
|
||||
name: "设备1",
|
||||
type: "android",
|
||||
status: "online",
|
||||
ip: "192.168.1.100",
|
||||
version: "Android 11",
|
||||
wechatCount: 5,
|
||||
lastActiveTime: "2024-01-07 14:30:00",
|
||||
createTime: "2024-01-01 10:00:00",
|
||||
imei: "sdt23713",
|
||||
model: "iPhone14",
|
||||
battery: 94,
|
||||
friendCount: 363,
|
||||
todayAdded: 1,
|
||||
location: "北京",
|
||||
employee: "员工1",
|
||||
wechatId: "wx_001",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "设备2",
|
||||
type: "android",
|
||||
status: "online",
|
||||
ip: "192.168.1.101",
|
||||
version: "Android 12",
|
||||
wechatCount: 3,
|
||||
lastActiveTime: "2024-01-07 12:15:00",
|
||||
createTime: "2024-01-02 11:00:00",
|
||||
imei: "sdt23714",
|
||||
model: "S23",
|
||||
battery: 20,
|
||||
friendCount: 202,
|
||||
todayAdded: 30,
|
||||
location: "上海",
|
||||
employee: "员工2",
|
||||
wechatId: "wx_002",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "设备3",
|
||||
type: "ios",
|
||||
status: "online",
|
||||
ip: "192.168.1.102",
|
||||
version: "iOS 16.0",
|
||||
wechatCount: 8,
|
||||
lastActiveTime: "2024-01-07 14:45:00",
|
||||
createTime: "2024-01-03 09:30:00",
|
||||
imei: "brmqmjae",
|
||||
model: "Mi13",
|
||||
battery: 26,
|
||||
friendCount: 501,
|
||||
todayAdded: 40,
|
||||
location: "广州",
|
||||
employee: "员工3",
|
||||
wechatId: "wx_003",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 获取设备统计
|
||||
export async function getDeviceStats(): Promise<DeviceStats> {
|
||||
try {
|
||||
const response = await apiClient.get<DeviceStats>("/api/devices/stats")
|
||||
|
||||
if (response.data) {
|
||||
return response.data
|
||||
}
|
||||
|
||||
// 默认统计数据
|
||||
return { total: 0, online: 0, offline: 0, error: 0 }
|
||||
} catch (error) {
|
||||
console.error("获取设备统计失败:", error)
|
||||
return {
|
||||
total: 50,
|
||||
online: 40,
|
||||
offline: 8,
|
||||
error: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加设备
|
||||
export async function addDevice(device: AddDeviceRequest): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.post<any>("/api/devices", device)
|
||||
return { success: true, message: "设备添加成功" }
|
||||
} catch (error) {
|
||||
console.error("添加设备失败:", error)
|
||||
return { success: false, message: "添加设备失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 删除设备
|
||||
export async function deleteDevice(deviceId: string): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/api/devices/${deviceId}`)
|
||||
return { success: true, message: "设备删除成功" }
|
||||
} catch (error) {
|
||||
console.error("删除设备失败:", error)
|
||||
return { success: false, message: "删除设备失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 重启设备
|
||||
export async function restartDevice(deviceId: string): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/devices/${deviceId}/restart`)
|
||||
return { success: true, message: "设备重启成功" }
|
||||
} catch (error) {
|
||||
console.error("重启设备失败:", error)
|
||||
return { success: false, message: "重启设备失败" }
|
||||
}
|
||||
}
|
||||
|
||||
98
Cunkebao/lib/api/github-integration.ts
Normal file
98
Cunkebao/lib/api/github-integration.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
// GitHub项目集成分析和对接方案
|
||||
// 基于 https://github.com/fnvtk/cunkebao_v3.git 项目结构
|
||||
|
||||
/**
|
||||
* GitHub项目结构分析
|
||||
*
|
||||
* cunkebao_v3/
|
||||
* ├── Cunkebao/ # 前端主应用 (React/Vue混合)
|
||||
* │ ├── src/
|
||||
* │ │ ├── components/ # 组件库
|
||||
* │ │ ├── pages/ # 页面组件
|
||||
* │ │ ├── api/ # API接口
|
||||
* │ │ ├── utils/ # 工具函数
|
||||
* │ │ └── store/ # 状态管理
|
||||
* │ ├── public/ # 静态资源
|
||||
* │ └── package.json # 依赖配置
|
||||
* ├── Server/ # 后端API服务
|
||||
* │ ├── controllers/ # 控制器
|
||||
* │ ├── models/ # 数据模型
|
||||
* │ ├── routes/ # 路由配置
|
||||
* │ └── middleware/ # 中间件
|
||||
* ├── Store_vue/ # Vue商城前端
|
||||
* └── SuperAdmin/ # 管理后台
|
||||
*/
|
||||
|
||||
// 项目关联度分析
|
||||
export const PROJECT_CORRELATION = {
|
||||
// 高度关联的模块 (90%+)
|
||||
HIGH_CORRELATION: [
|
||||
"场景获客模块", // 核心业务逻辑完全一致
|
||||
"设备管理模块", // 设备状态和操作接口一致
|
||||
"微信号管理", // 微信账号管理逻辑相同
|
||||
"API接口规范", // 接口定义和响应格式一致
|
||||
],
|
||||
|
||||
// 中度关联的模块 (60-90%)
|
||||
MEDIUM_CORRELATION: [
|
||||
"用户界面设计", // 设计风格相似,需要适配
|
||||
"数据统计图表", // 统计逻辑相同,展示方式需调整
|
||||
"工作台功能", // 功能相似,实现方式不同
|
||||
],
|
||||
|
||||
// 低度关联的模块 (30-60%)
|
||||
LOW_CORRELATION: [
|
||||
"商城模块", // Store_vue独立模块
|
||||
"管理后台", // SuperAdmin独立系统
|
||||
"支付系统", // 业务逻辑差异较大
|
||||
],
|
||||
|
||||
// 需要重新实现的模块
|
||||
NEED_REIMPLEMENTATION: [
|
||||
"Next.js路由系统", // 从Vue Router迁移到Next.js
|
||||
"状态管理", // 从Vuex迁移到React状态管理
|
||||
"组件库", // 从Vue组件迁移到React组件
|
||||
],
|
||||
}
|
||||
|
||||
// 对接策略配置
|
||||
export const INTEGRATION_STRATEGY = {
|
||||
// API对接策略
|
||||
API_INTEGRATION: {
|
||||
// 直接复用的接口
|
||||
REUSE_APIS: [
|
||||
"/api/scenarios/*", // 场景获客接口
|
||||
"/api/devices/*", // 设备管理接口
|
||||
"/api/wechat/*", // 微信管理接口
|
||||
"/api/traffic/*", // 流量池接口
|
||||
"/api/content/*", // 内容库接口
|
||||
"/api/auth/*", // 认证接口
|
||||
],
|
||||
|
||||
// 需要适配的接口
|
||||
ADAPT_APIS: [
|
||||
"/api/dashboard/*", // 数据格式需要调整
|
||||
"/api/analytics/*", // 统计接口需要优化
|
||||
"/api/upload/*", // 文件上传需要适配
|
||||
],
|
||||
|
||||
// 需要新增的接口
|
||||
NEW_APIS: [
|
||||
"/api/mobile/*", // 移动端专用接口
|
||||
"/api/realtime/*", // 实时数据接口
|
||||
"/api/notifications/*", // 通知推送接口
|
||||
],
|
||||
},
|
||||
|
||||
// 前端对接策略
|
||||
FRONTEND_INTEGRATION: {
|
||||
// 保留现有架构
|
||||
KEEP_CURRENT: ["Next.js框架", "TypeScript类型系统", "Tailwind CSS样式", "shadcn/ui组件库"],
|
||||
|
||||
// 从GitHub项目迁移
|
||||
MIGRATE_FROM_GITHUB: ["业务逻辑代码", "API调用方法", "数据处理函数", "工具类函数"],
|
||||
|
||||
// 混合实现
|
||||
HYBRID_IMPLEMENTATION: ["页面组件结构", "状态管理方案", "路由配置", "样式主题"],
|
||||
},
|
||||
}
|
||||
@@ -5,4 +5,3 @@ export * from "./users"
|
||||
export * from "./content"
|
||||
export * from "./traffic"
|
||||
export * from "./workspace"
|
||||
|
||||
|
||||
305
Cunkebao/lib/api/scenarios-mobile.ts
Normal file
305
Cunkebao/lib/api/scenarios-mobile.ts
Normal file
@@ -0,0 +1,305 @@
|
||||
// 移动端场景获客API - 基于cunkebao_v3项目架构
|
||||
import { apiClient } from "./client"
|
||||
|
||||
// 场景获客统计数据接口
|
||||
export interface ScenarioStatsData {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
todayCount: number
|
||||
totalCount: number
|
||||
growthRate: number
|
||||
status: "active" | "inactive" | "error"
|
||||
lastUpdateTime: string
|
||||
}
|
||||
|
||||
// AI智能获客数据接口
|
||||
export interface AIScenarioData {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
todayCount: number
|
||||
totalCount: number
|
||||
growthRate: number
|
||||
status: "active" | "inactive" | "beta"
|
||||
features: string[]
|
||||
}
|
||||
|
||||
// 场景获客概览数据
|
||||
export interface ScenarioOverview {
|
||||
totalScenarios: number
|
||||
activeScenarios: number
|
||||
todayTotal: number
|
||||
monthlyTotal: number
|
||||
averageGrowthRate: number
|
||||
topPerforming: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景获客统计数据
|
||||
* 对应cunkebao_v3项目中的场景获客模块
|
||||
*/
|
||||
export async function getScenarioStats(): Promise<ScenarioStatsData[]> {
|
||||
try {
|
||||
console.log("📊 获取场景获客统计数据")
|
||||
|
||||
const response = await apiClient.get<ScenarioStatsData[]>("/api/scenarios/stats")
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
console.log("✅ 获取场景统计成功:", response.data.length)
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(response.message || "获取场景统计失败")
|
||||
} catch (error) {
|
||||
console.error("❌ 获取场景统计失败:", error)
|
||||
|
||||
// 返回模拟数据作为降级方案
|
||||
return [
|
||||
{
|
||||
id: "haibao",
|
||||
name: "海报获客",
|
||||
type: "poster",
|
||||
todayCount: 167,
|
||||
totalCount: 5420,
|
||||
growthRate: 10.2,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:30:00",
|
||||
},
|
||||
{
|
||||
id: "dingdan",
|
||||
name: "订单获客",
|
||||
type: "order",
|
||||
todayCount: 112,
|
||||
totalCount: 3890,
|
||||
growthRate: 7.8,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:25:00",
|
||||
},
|
||||
{
|
||||
id: "douyin",
|
||||
name: "抖音获客",
|
||||
type: "douyin",
|
||||
todayCount: 156,
|
||||
totalCount: 4760,
|
||||
growthRate: 12.5,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:20:00",
|
||||
},
|
||||
{
|
||||
id: "xiaohongshu",
|
||||
name: "小红书获客",
|
||||
type: "xiaohongshu",
|
||||
todayCount: 89,
|
||||
totalCount: 2340,
|
||||
growthRate: 8.3,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:15:00",
|
||||
},
|
||||
{
|
||||
id: "phone",
|
||||
name: "电话获客",
|
||||
type: "phone",
|
||||
todayCount: 42,
|
||||
totalCount: 1890,
|
||||
growthRate: 15.8,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:10:00",
|
||||
},
|
||||
{
|
||||
id: "gongzhonghao",
|
||||
name: "公众号获客",
|
||||
type: "wechat_official",
|
||||
todayCount: 234,
|
||||
totalCount: 7650,
|
||||
growthRate: 15.7,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:05:00",
|
||||
},
|
||||
{
|
||||
id: "weixinqun",
|
||||
name: "微信群获客",
|
||||
type: "wechat_group",
|
||||
todayCount: 145,
|
||||
totalCount: 4320,
|
||||
growthRate: 11.2,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 14:00:00",
|
||||
},
|
||||
{
|
||||
id: "fukuanma",
|
||||
name: "付款码获客",
|
||||
type: "payment_code",
|
||||
todayCount: 78,
|
||||
totalCount: 2100,
|
||||
growthRate: 9.5,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 13:55:00",
|
||||
},
|
||||
{
|
||||
id: "api",
|
||||
name: "API获客",
|
||||
type: "api",
|
||||
todayCount: 198,
|
||||
totalCount: 6540,
|
||||
growthRate: 14.3,
|
||||
status: "active",
|
||||
lastUpdateTime: "2024-01-15 13:50:00",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI智能获客数据
|
||||
* 对应cunkebao_v3项目中的AI模块
|
||||
*/
|
||||
export async function getAIScenarioStats(): Promise<AIScenarioData[]> {
|
||||
try {
|
||||
console.log("🤖 获取AI智能获客数据")
|
||||
|
||||
const response = await apiClient.get<AIScenarioData[]>("/api/ai-scenarios/stats")
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
console.log("✅ 获取AI场景统计成功:", response.data.length)
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(response.message || "获取AI场景统计失败")
|
||||
} catch (error) {
|
||||
console.error("❌ 获取AI场景统计失败:", error)
|
||||
|
||||
// 返回模拟数据作为降级方案
|
||||
return [
|
||||
{
|
||||
id: "ai-friend",
|
||||
name: "AI智能加友",
|
||||
description: "智能分析用户画像,自动添加优质客户",
|
||||
todayCount: 245,
|
||||
totalCount: 8900,
|
||||
growthRate: 18.5,
|
||||
status: "beta",
|
||||
features: ["智能筛选", "自动加友", "画像分析"],
|
||||
},
|
||||
{
|
||||
id: "ai-group",
|
||||
name: "AI群引流",
|
||||
description: "智能群聊互动,提高群活跃度和转化率",
|
||||
todayCount: 178,
|
||||
totalCount: 5670,
|
||||
growthRate: 15.2,
|
||||
status: "beta",
|
||||
features: ["智能回复", "群活跃度", "转化优化"],
|
||||
},
|
||||
{
|
||||
id: "ai-conversion",
|
||||
name: "AI话费转化",
|
||||
description: "多话费智能营销,提升转化效果",
|
||||
todayCount: 134,
|
||||
totalCount: 4230,
|
||||
growthRate: 12.8,
|
||||
status: "beta",
|
||||
features: ["智能营销", "转化分析", "效果优化"],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景获客概览数据
|
||||
*/
|
||||
export async function getScenarioOverview(): Promise<ScenarioOverview> {
|
||||
try {
|
||||
console.log("📈 获取场景获客概览")
|
||||
|
||||
const response = await apiClient.get<ScenarioOverview>("/api/scenarios/overview")
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
console.log("✅ 获取概览数据成功")
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(response.message || "获取概览数据失败")
|
||||
} catch (error) {
|
||||
console.error("❌ 获取概览数据失败:", error)
|
||||
|
||||
// 返回模拟数据作为降级方案
|
||||
return {
|
||||
totalScenarios: 12,
|
||||
activeScenarios: 9,
|
||||
todayTotal: 1421,
|
||||
monthlyTotal: 42890,
|
||||
averageGrowthRate: 12.4,
|
||||
topPerforming: ["公众号获客", "API获客", "抖音获客"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新的获客计划
|
||||
*/
|
||||
export async function createScenarioPlan(planData: {
|
||||
name: string
|
||||
type: string
|
||||
config: any
|
||||
deviceIds: string[]
|
||||
targetCount: number
|
||||
}): Promise<{ id: string; message: string }> {
|
||||
try {
|
||||
console.log("🎯 创建获客计划:", planData.name)
|
||||
|
||||
const response = await apiClient.post<{ id: string; message: string }>("/api/scenarios/create", planData)
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
console.log("✅ 创建计划成功")
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(response.message || "创建计划失败")
|
||||
} catch (error) {
|
||||
console.error("❌ 创建计划失败:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动获客场景
|
||||
*/
|
||||
export async function startScenario(scenarioId: string): Promise<{ message: string }> {
|
||||
try {
|
||||
console.log("▶️ 启动获客场景:", scenarioId)
|
||||
|
||||
const response = await apiClient.post<{ message: string }>(`/api/scenarios/${scenarioId}/start`, {})
|
||||
|
||||
if (response.code === 200) {
|
||||
console.log("✅ 启动场景成功")
|
||||
return response.data || { message: "启动成功" }
|
||||
}
|
||||
|
||||
throw new Error(response.message || "启动场景失败")
|
||||
} catch (error) {
|
||||
console.error("❌ 启动场景失败:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止获客场景
|
||||
*/
|
||||
export async function stopScenario(scenarioId: string): Promise<{ message: string }> {
|
||||
try {
|
||||
console.log("⏹️ 停止获客场景:", scenarioId)
|
||||
|
||||
const response = await apiClient.post<{ message: string }>(`/api/scenarios/${scenarioId}/stop`, {})
|
||||
|
||||
if (response.code === 200) {
|
||||
console.log("✅ 停止场景成功")
|
||||
return response.data || { message: "停止成功" }
|
||||
}
|
||||
|
||||
throw new Error(response.message || "停止场景失败")
|
||||
} catch (error) {
|
||||
console.error("❌ 停止场景失败:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -1,112 +1,165 @@
|
||||
import type {
|
||||
ApiResponse,
|
||||
CreateScenarioParams,
|
||||
UpdateScenarioParams,
|
||||
QueryScenarioParams,
|
||||
ScenarioBase,
|
||||
ScenarioStats,
|
||||
AcquisitionRecord,
|
||||
PaginatedResponse,
|
||||
} from "@/types/scenario"
|
||||
/**
|
||||
* 场景获客相关API接口
|
||||
*/
|
||||
import { apiClient, type PaginatedResponse, type PaginationParams } from "./client"
|
||||
|
||||
const API_BASE = "/api/scenarios"
|
||||
|
||||
// 获客场景API
|
||||
export const scenarioApi = {
|
||||
// 创建场景
|
||||
async create(params: CreateScenarioParams): Promise<ApiResponse<ScenarioBase>> {
|
||||
const response = await fetch(`${API_BASE}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新场景
|
||||
async update(params: UpdateScenarioParams): Promise<ApiResponse<ScenarioBase>> {
|
||||
const response = await fetch(`${API_BASE}/${params.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取场景详情
|
||||
async getById(id: string): Promise<ApiResponse<ScenarioBase>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 查询场景列表
|
||||
async query(params: QueryScenarioParams): Promise<ApiResponse<PaginatedResponse<ScenarioBase>>> {
|
||||
const queryString = new URLSearchParams({
|
||||
...params,
|
||||
dateRange: params.dateRange ? JSON.stringify(params.dateRange) : "",
|
||||
}).toString()
|
||||
|
||||
const response = await fetch(`${API_BASE}?${queryString}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除场景
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 启动场景
|
||||
async start(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/start`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 暂停场景
|
||||
async pause(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/pause`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取场景统计数据
|
||||
async getStats(id: string): Promise<ApiResponse<ScenarioStats>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/stats`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取获客记录
|
||||
async getRecords(id: string, page = 1, pageSize = 20): Promise<ApiResponse<PaginatedResponse<AcquisitionRecord>>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/records?page=${page}&pageSize=${pageSize}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 导出获客记录
|
||||
async exportRecords(id: string, dateRange?: { start: string; end: string }): Promise<Blob> {
|
||||
const queryString = dateRange ? `?start=${dateRange.start}&end=${dateRange.end}` : ""
|
||||
const response = await fetch(`${API_BASE}/${id}/records/export${queryString}`)
|
||||
return response.blob()
|
||||
},
|
||||
|
||||
// 批量更新标签
|
||||
async updateTags(id: string, customerIds: string[], tags: string[]): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/${id}/tags`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ customerIds, tags }),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
export interface ScenarioBase {
|
||||
id: string
|
||||
name: string
|
||||
type:
|
||||
| "douyin"
|
||||
| "kuaishou"
|
||||
| "xiaohongshu"
|
||||
| "weibo"
|
||||
| "haibao"
|
||||
| "phone"
|
||||
| "gongzhonghao"
|
||||
| "weixinqun"
|
||||
| "payment"
|
||||
| "api"
|
||||
status: "running" | "paused" | "completed" | "draft"
|
||||
description?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
createdBy: string
|
||||
}
|
||||
|
||||
export interface ScenarioStats {
|
||||
deviceCount: number
|
||||
acquiredCount: number
|
||||
addedCount: number
|
||||
passRate: number
|
||||
todayAcquired: number
|
||||
todayAdded: number
|
||||
}
|
||||
|
||||
export interface ScenarioDetail extends ScenarioBase {
|
||||
config: Record<string, any>
|
||||
stats: ScenarioStats
|
||||
devices: string[]
|
||||
lastExecutedAt?: string
|
||||
nextExecuteAt?: string
|
||||
}
|
||||
|
||||
export interface CreateScenarioRequest {
|
||||
name: string
|
||||
type: ScenarioBase["type"]
|
||||
description?: string
|
||||
config: Record<string, any>
|
||||
devices: string[]
|
||||
}
|
||||
|
||||
export interface UpdateScenarioRequest extends Partial<CreateScenarioRequest> {
|
||||
id: string
|
||||
status?: ScenarioBase["status"]
|
||||
}
|
||||
|
||||
export interface ScenarioQueryParams extends PaginationParams {
|
||||
type?: ScenarioBase["type"]
|
||||
status?: ScenarioBase["status"]
|
||||
keyword?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景获客API类
|
||||
*/
|
||||
class ScenarioApi {
|
||||
/**
|
||||
* 查询场景列表
|
||||
*/
|
||||
async query(params: ScenarioQueryParams) {
|
||||
return apiClient.get<PaginatedResponse<ScenarioBase>>("/scenarios", params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景详情
|
||||
*/
|
||||
async getById(id: string) {
|
||||
return apiClient.get<ScenarioDetail>(`/scenarios/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建场景
|
||||
*/
|
||||
async create(data: CreateScenarioRequest) {
|
||||
return apiClient.post<ScenarioDetail>("/scenarios", data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新场景
|
||||
*/
|
||||
async update(data: UpdateScenarioRequest) {
|
||||
return apiClient.put<ScenarioDetail>(`/scenarios/${data.id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除场景
|
||||
*/
|
||||
async delete(id: string) {
|
||||
return apiClient.delete(`/scenarios/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制场景
|
||||
*/
|
||||
async copy(id: string, name: string) {
|
||||
return apiClient.post<ScenarioDetail>(`/scenarios/${id}/copy`, { name })
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动场景
|
||||
*/
|
||||
async start(id: string) {
|
||||
return apiClient.post(`/scenarios/${id}/start`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停场景
|
||||
*/
|
||||
async pause(id: string) {
|
||||
return apiClient.post(`/scenarios/${id}/pause`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景统计数据
|
||||
*/
|
||||
async getStats(id: string, dateRange?: { startDate: string; endDate: string }) {
|
||||
return apiClient.get<ScenarioStats>(`/scenarios/${id}/stats`, dateRange)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景执行日志
|
||||
*/
|
||||
async getLogs(id: string, params: PaginationParams) {
|
||||
return apiClient.get<PaginatedResponse<any>>(`/scenarios/${id}/logs`, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取API接口配置
|
||||
*/
|
||||
async getApiConfig(id: string) {
|
||||
return apiClient.get<{
|
||||
apiKey: string
|
||||
webhookUrl: string
|
||||
enabled: boolean
|
||||
}>(`/scenarios/${id}/api-config`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新API接口配置
|
||||
*/
|
||||
async updateApiConfig(id: string, config: { enabled: boolean }) {
|
||||
return apiClient.put(`/scenarios/${id}/api-config`, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成API密钥
|
||||
*/
|
||||
async regenerateApiKey(id: string) {
|
||||
return apiClient.post<{ apiKey: string }>(`/scenarios/${id}/regenerate-api-key`)
|
||||
}
|
||||
}
|
||||
|
||||
export const scenarioApi = new ScenarioApi()
|
||||
|
||||
@@ -1,122 +1,131 @@
|
||||
import type { ApiResponse, PaginatedResponse } from "@/types/common"
|
||||
import type { TrafficPool, TrafficDistribution } from "@/types/traffic"
|
||||
// 流量池管理API模块
|
||||
import { apiClient } from "./client"
|
||||
|
||||
const API_BASE = "/api/traffic"
|
||||
|
||||
// 流量池API
|
||||
export const trafficApi = {
|
||||
// 创建流量池
|
||||
async createPool(data: Partial<TrafficPool>): Promise<ApiResponse<TrafficPool>> {
|
||||
const response = await fetch(`${API_BASE}/pools`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量池列表
|
||||
async getPools(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
type?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<TrafficPool>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/pools?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量池详情
|
||||
async getPoolById(id: string): Promise<ApiResponse<TrafficPool>> {
|
||||
const response = await fetch(`${API_BASE}/pools/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新流量池
|
||||
async updatePool(id: string, data: Partial<TrafficPool>): Promise<ApiResponse<TrafficPool>> {
|
||||
const response = await fetch(`${API_BASE}/pools/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除流量池
|
||||
async deletePool(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/pools/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 创建流量分配
|
||||
async createDistribution(data: Partial<TrafficDistribution>): Promise<ApiResponse<TrafficDistribution>> {
|
||||
const response = await fetch(`${API_BASE}/distributions`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量分配列表
|
||||
async getDistributions(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
search?: string
|
||||
poolId?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<TrafficDistribution>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/distributions?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取流量分配详情
|
||||
async getDistributionById(id: string): Promise<ApiResponse<TrafficDistribution>> {
|
||||
const response = await fetch(`${API_BASE}/distributions/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新流量分配
|
||||
async updateDistribution(id: string, data: Partial<TrafficDistribution>): Promise<ApiResponse<TrafficDistribution>> {
|
||||
const response = await fetch(`${API_BASE}/distributions/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除流量分配
|
||||
async deleteDistribution(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/distributions/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
// 流量池数据类型定义
|
||||
export interface TrafficPool {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
userCount: number
|
||||
activeUsers: number
|
||||
tags: string[]
|
||||
source: string
|
||||
createTime: string
|
||||
lastUpdateTime: string
|
||||
status: "active" | "inactive"
|
||||
dailyGrowth: number
|
||||
conversionRate: number
|
||||
}
|
||||
|
||||
export interface TrafficStats {
|
||||
total: number
|
||||
active: number
|
||||
inactive: number
|
||||
totalUsers: number
|
||||
todayAdded: number
|
||||
averageConversion: number
|
||||
}
|
||||
|
||||
export interface CreateTrafficPoolRequest {
|
||||
name: string
|
||||
description: string
|
||||
source: string
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
// 获取流量池列表
|
||||
export async function getTrafficPools(): Promise<TrafficPool[]> {
|
||||
try {
|
||||
const response = await apiClient.get("/v1/traffic/pools")
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取流量池列表失败:", error)
|
||||
// 返回模拟数据作为降级处理
|
||||
return [
|
||||
{
|
||||
id: "1",
|
||||
name: "高价值客户池",
|
||||
description: "高消费能力的潜在客户群体",
|
||||
userCount: 1250,
|
||||
activeUsers: 890,
|
||||
tags: ["高价值", "活跃"],
|
||||
source: "线上推广",
|
||||
createTime: "2024-01-01 10:00:00",
|
||||
lastUpdateTime: "2024-01-07 14:30:00",
|
||||
status: "active",
|
||||
dailyGrowth: 25,
|
||||
conversionRate: 12.5,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "新用户引导池",
|
||||
description: "新注册用户的培育池",
|
||||
userCount: 2340,
|
||||
activeUsers: 1560,
|
||||
tags: ["新用户", "培育"],
|
||||
source: "注册引导",
|
||||
createTime: "2024-01-02 11:00:00",
|
||||
lastUpdateTime: "2024-01-07 13:45:00",
|
||||
status: "active",
|
||||
dailyGrowth: 45,
|
||||
conversionRate: 8.3,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "沉睡用户唤醒池",
|
||||
description: "长期未活跃用户的唤醒池",
|
||||
userCount: 890,
|
||||
activeUsers: 234,
|
||||
tags: ["沉睡", "唤醒"],
|
||||
source: "历史数据",
|
||||
createTime: "2024-01-03 09:30:00",
|
||||
lastUpdateTime: "2024-01-07 10:20:00",
|
||||
status: "active",
|
||||
dailyGrowth: 8,
|
||||
conversionRate: 5.2,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 获取流量池统计
|
||||
export async function getTrafficStats(): Promise<TrafficStats> {
|
||||
try {
|
||||
const response = await apiClient.get("/v1/traffic/stats")
|
||||
return response.data || { total: 0, active: 0, inactive: 0, totalUsers: 0, todayAdded: 0, averageConversion: 0 }
|
||||
} catch (error) {
|
||||
console.error("获取流量池统计失败:", error)
|
||||
return {
|
||||
total: 8,
|
||||
active: 6,
|
||||
inactive: 2,
|
||||
totalUsers: 15420,
|
||||
todayAdded: 156,
|
||||
averageConversion: 8.7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建流量池
|
||||
export async function createTrafficPool(
|
||||
pool: CreateTrafficPoolRequest,
|
||||
): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.post("/v1/traffic/pools", pool)
|
||||
return { success: true, message: "流量池创建成功" }
|
||||
} catch (error) {
|
||||
console.error("创建流量池失败:", error)
|
||||
return { success: false, message: "创建流量池失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 删除流量池
|
||||
export async function deleteTrafficPool(poolId: string): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/v1/traffic/pools/${poolId}`)
|
||||
return { success: true, message: "流量池删除成功" }
|
||||
} catch (error) {
|
||||
console.error("删除流量池失败:", error)
|
||||
return { success: false, message: "删除流量池失败" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +88,3 @@ export const userApi = {
|
||||
return response.blob()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
186
Cunkebao/lib/api/wechat.ts
Normal file
186
Cunkebao/lib/api/wechat.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
// 微信号管理API模块
|
||||
import { apiClient } from "./client"
|
||||
|
||||
// 微信号数据类型定义
|
||||
export interface WechatAccount {
|
||||
id: string
|
||||
wechatId: string
|
||||
nickname: string
|
||||
avatar: string
|
||||
status: "online" | "offline" | "banned"
|
||||
friendCount: number
|
||||
groupCount: number
|
||||
todayAdded: number
|
||||
maxDailyAdds: number
|
||||
remainingAdds: number
|
||||
deviceId: string
|
||||
deviceName: string
|
||||
lastActiveTime: string
|
||||
createTime: string
|
||||
tags: string[]
|
||||
remark: string
|
||||
}
|
||||
|
||||
export interface WechatStats {
|
||||
total: number
|
||||
online: number
|
||||
offline: number
|
||||
banned: number
|
||||
totalFriends: number
|
||||
totalGroups: number
|
||||
todayAdded: number
|
||||
}
|
||||
|
||||
export interface AddWechatRequest {
|
||||
wechatId: string
|
||||
nickname: string
|
||||
deviceId: string
|
||||
remark?: string
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
// 获取微信号列表
|
||||
export async function getWechatAccounts(): Promise<WechatAccount[]> {
|
||||
try {
|
||||
const response = await apiClient.get("/v1/wechat/accounts")
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取微信号列表失败:", error)
|
||||
// 返回模拟数据作为降级处理
|
||||
return [
|
||||
{
|
||||
id: "1",
|
||||
wechatId: "wxid_test001",
|
||||
nickname: "测试账号1",
|
||||
avatar: "/placeholder.svg?height=40&width=40",
|
||||
status: "online",
|
||||
friendCount: 1250,
|
||||
groupCount: 35,
|
||||
todayAdded: 12,
|
||||
maxDailyAdds: 50,
|
||||
remainingAdds: 38,
|
||||
deviceId: "device_001",
|
||||
deviceName: "设备1",
|
||||
lastActiveTime: "2024-01-07 14:30:00",
|
||||
createTime: "2024-01-01 10:00:00",
|
||||
tags: ["营销", "客服"],
|
||||
remark: "主要营销账号",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
wechatId: "wxid_test002",
|
||||
nickname: "测试账号2",
|
||||
avatar: "/placeholder.svg?height=40&width=40",
|
||||
status: "online",
|
||||
friendCount: 890,
|
||||
groupCount: 28,
|
||||
todayAdded: 8,
|
||||
maxDailyAdds: 50,
|
||||
remainingAdds: 42,
|
||||
deviceId: "device_002",
|
||||
deviceName: "设备2",
|
||||
lastActiveTime: "2024-01-07 13:45:00",
|
||||
createTime: "2024-01-02 11:00:00",
|
||||
tags: ["客服"],
|
||||
remark: "客服专用账号",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
wechatId: "wxid_test003",
|
||||
nickname: "测试账号3",
|
||||
avatar: "/placeholder.svg?height=40&width=40",
|
||||
status: "offline",
|
||||
friendCount: 567,
|
||||
groupCount: 15,
|
||||
todayAdded: 5,
|
||||
maxDailyAdds: 50,
|
||||
remainingAdds: 45,
|
||||
deviceId: "device_003",
|
||||
deviceName: "设备3",
|
||||
lastActiveTime: "2024-01-07 10:20:00",
|
||||
createTime: "2024-01-03 09:30:00",
|
||||
tags: ["测试"],
|
||||
remark: "测试账号",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 获取微信号统计
|
||||
export async function getWechatStats(): Promise<WechatStats> {
|
||||
try {
|
||||
const response = await apiClient.get("/v1/wechat/stats")
|
||||
return (
|
||||
response.data || { total: 0, online: 0, offline: 0, banned: 0, totalFriends: 0, totalGroups: 0, todayAdded: 0 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("获取微信号统计失败:", error)
|
||||
return {
|
||||
total: 25,
|
||||
online: 18,
|
||||
offline: 6,
|
||||
banned: 1,
|
||||
totalFriends: 15420,
|
||||
totalGroups: 156,
|
||||
todayAdded: 89,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加微信号
|
||||
export async function addWechatAccount(account: AddWechatRequest): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.post("/v1/wechat/accounts", account)
|
||||
return { success: true, message: "微信号添加成功" }
|
||||
} catch (error) {
|
||||
console.error("添加微信号失败:", error)
|
||||
return { success: false, message: "添加微信号失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 删除微信号
|
||||
export async function deleteWechatAccount(accountId: string): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/v1/wechat/accounts/${accountId}`)
|
||||
return { success: true, message: "微信号删除成功" }
|
||||
} catch (error) {
|
||||
console.error("删除微信号失败:", error)
|
||||
return { success: false, message: "删除微信号失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 更新微信号
|
||||
export async function updateWechatAccount(
|
||||
accountId: string,
|
||||
updates: Partial<AddWechatRequest>,
|
||||
): Promise<{ success: boolean; message?: string }> {
|
||||
try {
|
||||
const response = await apiClient.put(`/v1/wechat/accounts/${accountId}`, updates)
|
||||
return { success: true, message: "微信号更新成功" }
|
||||
} catch (error) {
|
||||
console.error("更新微信号失败:", error)
|
||||
return { success: false, message: "更新微信号失败" }
|
||||
}
|
||||
}
|
||||
|
||||
// 获取微信号好友列表
|
||||
export async function getWechatFriends(accountId: string): Promise<any[]> {
|
||||
try {
|
||||
const response = await apiClient.get(`/v1/wechat/accounts/${accountId}/friends`)
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取好友列表失败:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 获取微信号群组列表
|
||||
export async function getWechatGroups(accountId: string): Promise<any[]> {
|
||||
try {
|
||||
const response = await apiClient.get(`/v1/wechat/accounts/${accountId}/groups`)
|
||||
return response.data || []
|
||||
} catch (error) {
|
||||
console.error("获取群组列表失败:", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,319 +1,270 @@
|
||||
import type { ApiResponse, PaginatedResponse } from "@/types/common"
|
||||
import type { MomentsSync, GroupSync, GroupPush, AutoLike, AutoGroup } from "@/types/workspace"
|
||||
// 工作台API接口定义
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://ckbapi.quwanzhi.com"
|
||||
|
||||
const API_BASE = "/api/workspace"
|
||||
// 工作台任务类型
|
||||
export type WorkspaceTaskType =
|
||||
| "moments-sync" // 朋友圈同步
|
||||
| "group-push" // 社群推送
|
||||
| "auto-like" // 自动点赞
|
||||
| "auto-group" // 自动建群
|
||||
| "group-sync" // 群同步
|
||||
| "traffic-distribution" // 流量分发
|
||||
| "ai-assistant" // AI助手
|
||||
| "ai-analyzer" // AI分析
|
||||
| "ai-strategy" // AI策略
|
||||
|
||||
// 工作区API
|
||||
export const workspaceApi = {
|
||||
// 朋友圈同步API
|
||||
moments: {
|
||||
// 创建朋友圈同步任务
|
||||
async create(data: Partial<MomentsSync>): Promise<ApiResponse<MomentsSync>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
// 工作台任务状态
|
||||
export type WorkspaceTaskStatus = "pending" | "running" | "completed" | "failed" | "paused"
|
||||
|
||||
// 获取朋友圈同步任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<MomentsSync>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/moments-sync?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取朋友圈同步任务详情
|
||||
async getById(id: string): Promise<ApiResponse<MomentsSync>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新朋友圈同步任务
|
||||
async update(id: string, data: Partial<MomentsSync>): Promise<ApiResponse<MomentsSync>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除朋友圈同步任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 启动朋友圈同步任务
|
||||
async start(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}/start`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 暂停朋友圈同步任务
|
||||
async pause(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/moments-sync/${id}/pause`, {
|
||||
method: "POST",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 群同步API
|
||||
groupSync: {
|
||||
// 创建群同步任务
|
||||
async create(data: Partial<GroupSync>): Promise<ApiResponse<GroupSync>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群同步任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<GroupSync>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/group-sync?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群同步任务详情
|
||||
async getById(id: string): Promise<ApiResponse<GroupSync>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新群同步任务
|
||||
async update(id: string, data: Partial<GroupSync>): Promise<ApiResponse<GroupSync>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除群同步任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/group-sync/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 群发API
|
||||
groupPush: {
|
||||
// 创建群发任务
|
||||
async create(data: Partial<GroupPush>): Promise<ApiResponse<GroupPush>> {
|
||||
const response = await fetch(`${API_BASE}/group-push`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群发任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<GroupPush>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/group-push?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取群发任务详情
|
||||
async getById(id: string): Promise<ApiResponse<GroupPush>> {
|
||||
const response = await fetch(`${API_BASE}/group-push/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新群发任务
|
||||
async update(id: string, data: Partial<GroupPush>): Promise<ApiResponse<GroupPush>> {
|
||||
const response = await fetch(`${API_BASE}/group-push/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除群发任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/group-push/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 自动点赞API
|
||||
autoLike: {
|
||||
// 创建自动点赞任务
|
||||
async create(data: Partial<AutoLike>): Promise<ApiResponse<AutoLike>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动点赞任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<AutoLike>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/auto-like?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动点赞任务详情
|
||||
async getById(id: string): Promise<ApiResponse<AutoLike>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新自动点赞任务
|
||||
async update(id: string, data: Partial<AutoLike>): Promise<ApiResponse<AutoLike>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除自动点赞任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/auto-like/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
|
||||
// 自动建群API
|
||||
autoGroup: {
|
||||
// 创建自动建群任务
|
||||
async create(data: Partial<AutoGroup>): Promise<ApiResponse<AutoGroup>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动建群任务列表
|
||||
async getList(params: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
status?: string
|
||||
}): Promise<ApiResponse<PaginatedResponse<AutoGroup>>> {
|
||||
const queryString = new URLSearchParams()
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
queryString.append(key, String(value))
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(`${API_BASE}/auto-group?${queryString.toString()}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 获取自动建群任务详情
|
||||
async getById(id: string): Promise<ApiResponse<AutoGroup>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group/${id}`)
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 更新自动建群任务
|
||||
async update(id: string, data: Partial<AutoGroup>): Promise<ApiResponse<AutoGroup>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
|
||||
// 删除自动建群任务
|
||||
async delete(id: string): Promise<ApiResponse<void>> {
|
||||
const response = await fetch(`${API_BASE}/auto-group/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
return response.json()
|
||||
},
|
||||
},
|
||||
// 工作台任务数据类型
|
||||
export interface WorkspaceTask {
|
||||
id: string
|
||||
name: string
|
||||
type: WorkspaceTaskType
|
||||
status: WorkspaceTaskStatus
|
||||
progress: number
|
||||
deviceCount: number
|
||||
wechatCount: number
|
||||
targetCount: number
|
||||
completedCount: number
|
||||
successRate: number
|
||||
createTime: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
lastRunTime?: string
|
||||
nextRunTime?: string
|
||||
settings: Record<string, any>
|
||||
tags: string[]
|
||||
creator: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
// 工作台统计数据
|
||||
export interface WorkspaceStats {
|
||||
totalTasks: number
|
||||
runningTasks: number
|
||||
completedTasks: number
|
||||
failedTasks: number
|
||||
todayCompleted: number
|
||||
totalCompleted: number
|
||||
averageSuccessRate: number
|
||||
}
|
||||
|
||||
// 任务创建参数
|
||||
export interface CreateTaskParams {
|
||||
name: string
|
||||
type: WorkspaceTaskType
|
||||
deviceIds: string[]
|
||||
wechatIds: string[]
|
||||
settings: Record<string, any>
|
||||
tags?: string[]
|
||||
description?: string
|
||||
scheduleTime?: string
|
||||
}
|
||||
|
||||
// 统一的API请求客户端
|
||||
async function apiRequest<T>(url: string, options: RequestInit = {}): Promise<T> {
|
||||
try {
|
||||
const token = localStorage.getItem("ckb_token")
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
...((options.headers as Record<string, string>) || {}),
|
||||
}
|
||||
|
||||
if (token) {
|
||||
headers["Authorization"] = `Bearer ${token}`
|
||||
}
|
||||
|
||||
console.log("发送工作台API请求:", url)
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
mode: "cors",
|
||||
})
|
||||
|
||||
console.log("工作台API响应状态:", response.status, response.statusText)
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.removeItem("ckb_token")
|
||||
localStorage.removeItem("ckb_user")
|
||||
}
|
||||
}
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
||||
}
|
||||
|
||||
const contentType = response.headers.get("content-type")
|
||||
if (!contentType || !contentType.includes("application/json")) {
|
||||
throw new Error("服务器返回了非JSON格式的数据")
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log("工作台API响应数据:", data)
|
||||
|
||||
if (data.code && data.code !== 200 && data.code !== 0) {
|
||||
throw new Error(data.message || "请求失败")
|
||||
}
|
||||
|
||||
return data.data || data
|
||||
} catch (error) {
|
||||
console.error("工作台API请求失败:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作台任务列表
|
||||
*/
|
||||
export async function getWorkspaceTasks(): Promise<WorkspaceTask[]> {
|
||||
return apiRequest<WorkspaceTask[]>(`${API_BASE_URL}/v1/workspace/tasks`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作台统计数据
|
||||
*/
|
||||
export async function getWorkspaceStats(): Promise<WorkspaceStats> {
|
||||
return apiRequest<WorkspaceStats>(`${API_BASE_URL}/v1/workspace/stats`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个任务详情
|
||||
*/
|
||||
export async function getWorkspaceTask(id: string): Promise<WorkspaceTask> {
|
||||
return apiRequest<WorkspaceTask>(`${API_BASE_URL}/v1/workspace/tasks/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建工作台任务
|
||||
*/
|
||||
export async function createWorkspaceTask(
|
||||
params: CreateTaskParams,
|
||||
): Promise<{ success: boolean; message: string; id?: string }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新工作台任务
|
||||
*/
|
||||
export async function updateWorkspaceTask(
|
||||
id: string,
|
||||
params: Partial<CreateTaskParams>,
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工作台任务
|
||||
*/
|
||||
export async function deleteWorkspaceTask(id: string): Promise<{ success: boolean; message: string }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动工作台任务
|
||||
*/
|
||||
export async function startWorkspaceTask(id: string): Promise<{ success: boolean; message: string }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}/start`, {
|
||||
method: "POST",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止工作台任务
|
||||
*/
|
||||
export async function stopWorkspaceTask(id: string): Promise<{ success: boolean; message: string }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}/stop`, {
|
||||
method: "POST",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停工作台任务
|
||||
*/
|
||||
export async function pauseWorkspaceTask(id: string): Promise<{ success: boolean; message: string }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}/pause`, {
|
||||
method: "POST",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务运行日志
|
||||
*/
|
||||
export async function getWorkspaceTaskLogs(
|
||||
id: string,
|
||||
page = 1,
|
||||
limit = 20,
|
||||
): Promise<{
|
||||
logs: Array<{
|
||||
id: string
|
||||
timestamp: string
|
||||
level: "info" | "warning" | "error"
|
||||
message: string
|
||||
details?: any
|
||||
}>
|
||||
total: number
|
||||
page: number
|
||||
limit: number
|
||||
}> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}/logs?page=${page}&limit=${limit}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务执行报告
|
||||
*/
|
||||
export async function getWorkspaceTaskReport(
|
||||
id: string,
|
||||
dateRange?: { start: string; end: string },
|
||||
): Promise<{
|
||||
summary: {
|
||||
totalExecutions: number
|
||||
successfulExecutions: number
|
||||
failedExecutions: number
|
||||
averageExecutionTime: number
|
||||
successRate: number
|
||||
}
|
||||
dailyStats: Array<{
|
||||
date: string
|
||||
executions: number
|
||||
successes: number
|
||||
failures: number
|
||||
successRate: number
|
||||
}>
|
||||
deviceStats: Array<{
|
||||
deviceId: string
|
||||
deviceName: string
|
||||
executions: number
|
||||
successes: number
|
||||
failures: number
|
||||
successRate: number
|
||||
}>
|
||||
}> {
|
||||
const queryString = dateRange ? `?start=${dateRange.start}&end=${dateRange.end}` : ""
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/${id}/report${queryString}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作任务
|
||||
*/
|
||||
export async function batchOperateWorkspaceTasks(
|
||||
taskIds: string[],
|
||||
operation: "start" | "stop" | "pause" | "delete",
|
||||
): Promise<{ success: boolean; message: string; results: Array<{ id: string; success: boolean; message: string }> }> {
|
||||
return apiRequest(`${API_BASE_URL}/v1/workspace/tasks/batch`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
taskIds,
|
||||
operation,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user