feat: 本次提交更新内容如下

定版本转移2025年7月17日
This commit is contained in:
2025-07-17 10:22:38 +08:00
parent 0f860d01e4
commit 92a3d407a7
645 changed files with 30755 additions and 118800 deletions

82
Cunkebao/src/api/auth.ts Normal file
View File

@@ -0,0 +1,82 @@
import { request } from './request';
import type { ApiResponse } from '@/types/common';
// 登录响应数据类型
export interface LoginResponse {
token: string;
token_expired: string;
member: {
id: number;
username: string;
account: string;
avatar?: string;
s2_accountId: string;
};
}
// 验证码响应类型
export interface VerificationCodeResponse {
code: string;
expire_time: string;
}
// 认证相关API
export const authApi = {
// 账号密码登录
login: async (account: string, password: string) => {
const response = await request.post<ApiResponse<LoginResponse>>('/v1/auth/login', {
account,
password,
typeId: 1 // 默认使用用户类型1
});
return response as unknown as ApiResponse<LoginResponse>;
},
// 验证码登录
loginWithCode: async (account: string, code: string) => {
const response = await request.post<ApiResponse<LoginResponse>>('/v1/auth/login/code', {
account,
code,
typeId: 1
});
return response as unknown as ApiResponse<LoginResponse>;
},
// 发送验证码
sendVerificationCode: async (account: string) => {
const response = await request.post<ApiResponse<VerificationCodeResponse>>('/v1/auth/send-code', {
account,
type: 'login' // 登录验证码
});
return response as unknown as ApiResponse<VerificationCodeResponse>;
},
// 获取用户信息
getUserInfo: async () => {
const response = await request.get<ApiResponse<any>>('/v1/auth/info');
return response as unknown as ApiResponse<any>;
},
// 刷新Token
refreshToken: async () => {
const response = await request.post<ApiResponse<{ token: string; token_expired: string }>>('/v1/auth/refresh', {});
return response as unknown as ApiResponse<{ token: string; token_expired: string }>;
},
// 微信登录
wechatLogin: async (code: string) => {
const response = await request.post<ApiResponse<LoginResponse>>('/v1/auth/wechat', {
code
});
return response as unknown as ApiResponse<LoginResponse>;
},
// Apple登录
appleLogin: async (identityToken: string, authorizationCode: string) => {
const response = await request.post<ApiResponse<LoginResponse>>('/v1/auth/apple', {
identity_token: identityToken,
authorization_code: authorizationCode
});
return response as unknown as ApiResponse<LoginResponse>;
},
};

View File

@@ -0,0 +1,119 @@
import { get, post, del } from './request';
import {
LikeTask,
CreateLikeTaskData,
UpdateLikeTaskData,
LikeRecord,
ApiResponse,
PaginatedResponse
} from '@/types/auto-like';
// 获取自动点赞任务列表
export async function fetchAutoLikeTasks(): Promise<LikeTask[]> {
try {
const res = await get<ApiResponse<PaginatedResponse<LikeTask>>>('/v1/workbench/list?type=1&page=1&limit=100');
if (res.code === 200 && res.data) {
return res.data.list || [];
}
return [];
} catch (error) {
console.error('获取自动点赞任务失败:', error);
return [];
}
}
// 获取单个任务详情
export async function fetchAutoLikeTaskDetail(id: string): Promise<LikeTask | null> {
try {
console.log(`Fetching task detail for id: ${id}`);
// 使用any类型来处理可能的不同响应结构
const res = await get<any>(`/v1/workbench/detail?id=${id}`);
console.log('Task detail API response:', res);
if (res.code === 200) {
// 检查响应中的data字段
if (res.data) {
// 如果data是对象直接返回
if (typeof res.data === 'object') {
return res.data;
} else {
console.error('Task detail API response data is not an object:', res.data);
return null;
}
} else {
console.error('Task detail API response missing data field:', res);
return null;
}
}
console.error('Task detail API error:', res.msg || 'Unknown error');
return null;
} catch (error) {
console.error('获取任务详情失败:', error);
return null;
}
}
// 创建自动点赞任务
export async function createAutoLikeTask(data: CreateLikeTaskData): Promise<ApiResponse> {
return post('/v1/workbench/create', {
...data,
type: 1 // 自动点赞类型
});
}
// 更新自动点赞任务
export async function updateAutoLikeTask(data: UpdateLikeTaskData): Promise<ApiResponse> {
return post('/v1/workbench/update', {
...data,
type: 1 // 自动点赞类型
});
}
// 删除自动点赞任务
export async function deleteAutoLikeTask(id: string): Promise<ApiResponse> {
return del('/v1/workbench/delete', { params: { id } });
}
// 切换任务状态
export async function toggleAutoLikeTask(id: string, status: string): Promise<ApiResponse> {
return post('/v1/workbench/update-status', { id, status });
}
// 复制自动点赞任务
export async function copyAutoLikeTask(id: string): Promise<ApiResponse> {
return post('/v1/workbench/copy', { id });
}
// 获取点赞记录
export async function fetchLikeRecords(
workbenchId: string,
page: number = 1,
limit: number = 20,
keyword?: string
): Promise<PaginatedResponse<LikeRecord>> {
try {
const params = new URLSearchParams({
workbenchId,
page: page.toString(),
limit: limit.toString()
});
if (keyword) {
params.append('keyword', keyword);
}
const res = await get<ApiResponse<PaginatedResponse<LikeRecord>>>(`/v1/workbench/like-records?${params.toString()}`);
if (res.code === 200 && res.data) {
return res.data;
}
return { list: [], total: 0, page, limit };
} catch (error) {
console.error('获取点赞记录失败:', error);
return { list: [], total: 0, page, limit };
}
}
export type { LikeTask, LikeRecord, CreateLikeTaskData };

View File

@@ -0,0 +1,69 @@
import { get, post, put, del } from './request';
import type { ApiResponse, PaginatedResponse } from '@/types/common';
// 内容库类型定义
export interface ContentLibrary {
id: string;
name: string;
sourceType: number;
creatorName: string;
updateTime: string;
status: number;
}
// 内容库列表响应
export interface ContentLibraryListResponse {
code: number;
msg: string;
data: {
list: ContentLibrary[];
total: number;
page: number;
limit: number;
};
}
// 获取内容库列表
export const fetchContentLibraryList = async (
page: number = 1,
limit: number = 100,
keyword?: string
): Promise<ContentLibraryListResponse> => {
const params = new URLSearchParams();
params.append('page', page.toString());
params.append('limit', limit.toString());
if (keyword) {
params.append('keyword', keyword);
}
return get<ContentLibraryListResponse>(`/v1/content/library/list?${params.toString()}`);
};
// 内容库API对象
export const contentLibraryApi = {
// 获取内容库列表
async getList(page: number = 1, limit: number = 100, keyword?: string): Promise<ContentLibraryListResponse> {
return fetchContentLibraryList(page, limit, keyword);
},
// 创建内容库
async create(params: { name: string; sourceType: number }): Promise<ApiResponse<ContentLibrary>> {
return post<ApiResponse<ContentLibrary>>('/v1/content/library', params);
},
// 更新内容库
async update(id: string, params: Partial<ContentLibrary>): Promise<ApiResponse<ContentLibrary>> {
return put<ApiResponse<ContentLibrary>>(`/v1/content/library/${id}`, params);
},
// 删除内容库
async delete(id: string): Promise<ApiResponse<void>> {
return del<ApiResponse<void>>(`/v1/content/library/${id}`);
},
// 获取内容库详情
async getById(id: string): Promise<ApiResponse<ContentLibrary>> {
return get<ApiResponse<ContentLibrary>>(`/v1/content/library/${id}`);
},
};

200
Cunkebao/src/api/devices.ts Normal file
View File

@@ -0,0 +1,200 @@
import { get, post, put, del } from './request';
import type { ApiResponse, PaginatedResponse } from '@/types/common';
import type {
Device,
DeviceStats,
DeviceTaskRecord,
QueryDeviceParams,
CreateDeviceParams,
UpdateDeviceParams,
DeviceStatus,
ServerDevicesResponse
} from '@/types/device';
const API_BASE = "/devices";
// 获取设备列表 - 连接到服务器/v1/devices接口
export const fetchDeviceList = async (page: number = 1, limit: number = 20, keyword?: string): Promise<ServerDevicesResponse> => {
const params = new URLSearchParams();
params.append('page', page.toString());
params.append('limit', limit.toString());
if (keyword) {
params.append('keyword', keyword);
}
return get<ServerDevicesResponse>(`/v1/devices?${params.toString()}`);
};
// 获取设备详情 - 连接到服务器/v1/devices/:id接口
export const fetchDeviceDetail = async (id: string | number): Promise<ApiResponse<any>> => {
return get<ApiResponse<any>>(`/v1/devices/${id}`);
};
// 获取设备关联的微信账号
export const fetchDeviceRelatedAccounts = async (id: string | number): Promise<ApiResponse<any>> => {
return get<ApiResponse<any>>(`/v1/wechats/related-device/${id}`);
};
// 获取设备操作记录
export const fetchDeviceHandleLogs = async (id: string | number, page: number = 1, limit: number = 10): Promise<ApiResponse<any>> => {
return get<ApiResponse<any>>(`/v1/devices/${id}/handle-logs?page=${page}&limit=${limit}`);
};
// 更新设备任务配置
export const updateDeviceTaskConfig = async (
config: {
deviceId: string | number;
autoAddFriend?: boolean;
autoReply?: boolean;
momentsSync?: boolean;
aiChat?: boolean;
}
): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>(`/v1/devices/task-config`, config);
};
// 删除设备
export const deleteDevice = async (id: number): Promise<ApiResponse<any>> => {
return del<ApiResponse<any>>(`/v1/devices/${id}`);
};
// 设备管理API
export const devicesApi = {
// 获取设备列表
async getList(page: number = 1, limit: number = 20, keyword?: string): Promise<ServerDevicesResponse> {
const params = new URLSearchParams();
params.append('page', page.toString());
params.append('limit', limit.toString());
if (keyword) {
params.append('keyword', keyword);
}
return get<ServerDevicesResponse>(`/v1/devices?${params.toString()}`);
},
// 获取设备二维码
async getQRCode(accountId: string): Promise<ApiResponse<{ qrCode: string }>> {
return post<ApiResponse<{ qrCode: string }>>('/v1/api/device/add', { accountId });
},
// 通过IMEI添加设备
async addByImei(imei: string, name: string): Promise<ApiResponse<any>> {
return post<ApiResponse<any>>('/v1/api/device/add-by-imei', { imei, name });
},
// 创建设备
async create(params: CreateDeviceParams): Promise<ApiResponse<Device>> {
return post<ApiResponse<Device>>(`${API_BASE}`, params);
},
// 更新设备
async update(params: UpdateDeviceParams): Promise<ApiResponse<Device>> {
return put<ApiResponse<Device>>(`${API_BASE}/${params.id}`, params);
},
// 获取设备详情
async getById(id: string): Promise<ApiResponse<Device>> {
return get<ApiResponse<Device>>(`${API_BASE}/${id}`);
},
// 查询设备列表
async query(params: QueryDeviceParams): Promise<ApiResponse<PaginatedResponse<Device>>> {
// 创建一个新对象用于构建URLSearchParams
const queryParams: Record<string, string> = {};
// 按需将params中的属性添加到queryParams
if (params.keyword) queryParams.keyword = params.keyword;
if (params.status) queryParams.status = params.status;
if (params.type) queryParams.type = params.type;
if (params.page) queryParams.page = params.page.toString();
if (params.pageSize) queryParams.pageSize = params.pageSize.toString();
// 特殊处理需要JSON序列化的属性
if (params.tags) queryParams.tags = JSON.stringify(params.tags);
if (params.dateRange) queryParams.dateRange = JSON.stringify(params.dateRange);
// 构建查询字符串
const queryString = new URLSearchParams(queryParams).toString();
return get<ApiResponse<PaginatedResponse<Device>>>(`${API_BASE}?${queryString}`);
},
// 删除设备(旧版本)
async deleteById(id: string): Promise<ApiResponse<void>> {
return del<ApiResponse<void>>(`${API_BASE}/${id}`);
},
// 删除设备(新版本)
async delete(id: number): Promise<ApiResponse<any>> {
return del<ApiResponse<any>>(`/v1/devices/${id}`);
},
// 重启设备
async restart(id: string): Promise<ApiResponse<void>> {
return post<ApiResponse<void>>(`${API_BASE}/${id}/restart`);
},
// 解绑设备
async unbind(id: string): Promise<ApiResponse<void>> {
return post<ApiResponse<void>>(`${API_BASE}/${id}/unbind`);
},
// 获取设备统计数据
async getStats(id: string): Promise<ApiResponse<DeviceStats>> {
return get<ApiResponse<DeviceStats>>(`${API_BASE}/${id}/stats`);
},
// 获取设备任务记录
async getTaskRecords(id: string, page = 1, pageSize = 20): Promise<ApiResponse<PaginatedResponse<DeviceTaskRecord>>> {
return get<ApiResponse<PaginatedResponse<DeviceTaskRecord>>>(`${API_BASE}/${id}/tasks?page=${page}&pageSize=${pageSize}`);
},
// 批量更新设备标签
async updateTags(ids: string[], tags: string[]): Promise<ApiResponse<void>> {
return post<ApiResponse<void>>(`${API_BASE}/tags`, { deviceIds: ids, tags });
},
// 批量导出设备数据
async exportDevices(ids: string[]): Promise<Blob> {
const response = await fetch(`${process.env.REACT_APP_API_BASE || 'http://localhost:3000/api'}${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>>> {
return post<ApiResponse<Record<string, DeviceStatus>>>(`${API_BASE}/status`, { deviceIds: ids });
},
// 获取设备关联的微信账号
async getRelatedAccounts(id: string | number): Promise<ApiResponse<any>> {
return get<ApiResponse<any>>(`/v1/wechats/related-device/${id}`);
},
// 获取设备操作记录
async getHandleLogs(id: string | number, page: number = 1, limit: number = 10): Promise<ApiResponse<any>> {
return get<ApiResponse<any>>(`/v1/devices/${id}/handle-logs?page=${page}&limit=${limit}`);
},
// 更新设备任务配置
async updateTaskConfig(config: {
deviceId: string | number;
autoAddFriend?: boolean;
autoReply?: boolean;
momentsSync?: boolean;
aiChat?: boolean;
}): Promise<ApiResponse<any>> {
return post<ApiResponse<any>>(`/v1/devices/task-config`, config);
},
// 获取设备任务配置
async getTaskConfig(id: string | number): Promise<ApiResponse<any>> {
return get<ApiResponse<any>>(`/v1/devices/${id}/task-config`);
},
};

View File

@@ -0,0 +1,201 @@
import { get, post, put, del } from './request';
// 群发推送任务类型定义
export interface GroupPushTask {
id: string;
name: string;
status: number; // 1: 运行中, 2: 已暂停
deviceCount: number;
targetGroups: string[];
pushCount: number;
successCount: number;
lastPushTime: string;
createTime: string;
creator: string;
pushInterval: number;
maxPushPerDay: number;
timeRange: { start: string; end: string };
messageType: 'text' | 'image' | 'video' | 'link';
messageContent: string;
targetTags: string[];
pushMode: 'immediate' | 'scheduled';
scheduledTime?: string;
}
// API响应类型
interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
/**
* 获取群发推送任务列表
*/
export async function fetchGroupPushTasks(): Promise<GroupPushTask[]> {
try {
const response = await get<ApiResponse<GroupPushTask[]>>('/v1/workspace/group-push/tasks');
if (response.code === 200 && Array.isArray(response.data)) {
return response.data;
}
// 如果API不可用返回模拟数据
return getMockGroupPushTasks();
} catch (error) {
console.error('获取群发推送任务失败:', error);
// 返回模拟数据作为降级方案
return getMockGroupPushTasks();
}
}
/**
* 删除群发推送任务
*/
export async function deleteGroupPushTask(id: string): Promise<ApiResponse> {
try {
const response = await del<ApiResponse>(`/v1/workspace/group-push/tasks/${id}`);
return response;
} catch (error) {
console.error('删除群发推送任务失败:', error);
throw error;
}
}
/**
* 切换群发推送任务状态
*/
export async function toggleGroupPushTask(id: string, status: string): Promise<ApiResponse> {
try {
const response = await post<ApiResponse>(`/v1/workspace/group-push/tasks/${id}/toggle`, {
status
});
return response;
} catch (error) {
console.error('切换群发推送任务状态失败:', error);
throw error;
}
}
/**
* 复制群发推送任务
*/
export async function copyGroupPushTask(id: string): Promise<ApiResponse> {
try {
const response = await post<ApiResponse>(`/v1/workspace/group-push/tasks/${id}/copy`);
return response;
} catch (error) {
console.error('复制群发推送任务失败:', error);
throw error;
}
}
/**
* 创建群发推送任务
*/
export async function createGroupPushTask(taskData: Partial<GroupPushTask>): Promise<ApiResponse> {
try {
const response = await post<ApiResponse>('/v1/workspace/group-push/tasks', taskData);
return response;
} catch (error) {
console.error('创建群发推送任务失败:', error);
throw error;
}
}
/**
* 更新群发推送任务
*/
export async function updateGroupPushTask(id: string, taskData: Partial<GroupPushTask>): Promise<ApiResponse> {
try {
const response = await put<ApiResponse>(`/v1/workspace/group-push/tasks/${id}`, taskData);
return response;
} catch (error) {
console.error('更新群发推送任务失败:', error);
throw error;
}
}
/**
* 获取群发推送任务详情
*/
export async function getGroupPushTaskDetail(id: string): Promise<GroupPushTask> {
try {
const response = await get<ApiResponse<GroupPushTask>>(`/v1/workspace/group-push/tasks/${id}`);
if (response.code === 200 && response.data) {
return response.data;
}
throw new Error(response.message || '获取任务详情失败');
} catch (error) {
console.error('获取群发推送任务详情失败:', error);
throw error;
}
}
/**
* 模拟数据 - 当API不可用时使用
*/
function getMockGroupPushTasks(): GroupPushTask[] {
return [
{
id: '1',
name: '产品推广群发',
deviceCount: 2,
targetGroups: ['VIP客户群', '潜在客户群'],
pushCount: 156,
successCount: 142,
lastPushTime: '2025-02-06 13:12:35',
createTime: '2024-11-20 19:04:14',
creator: 'admin',
status: 1, // 运行中
pushInterval: 60,
maxPushPerDay: 200,
timeRange: { start: '09:00', end: '21:00' },
messageType: 'text',
messageContent: '新品上市,限时优惠!点击查看详情...',
targetTags: ['VIP客户', '高意向'],
pushMode: 'immediate',
},
{
id: '2',
name: '活动通知推送',
deviceCount: 1,
targetGroups: ['活动群', '推广群'],
pushCount: 89,
successCount: 78,
lastPushTime: '2024-03-04 14:09:35',
createTime: '2024-03-04 14:29:04',
creator: 'manager',
status: 2, // 已暂停
pushInterval: 120,
maxPushPerDay: 100,
timeRange: { start: '10:00', end: '20:00' },
messageType: 'image',
messageContent: '活动海报.jpg',
targetTags: ['活跃用户', '中意向'],
pushMode: 'scheduled',
scheduledTime: '2024-03-05 10:00:00',
},
{
id: '3',
name: '新客户欢迎消息',
deviceCount: 3,
targetGroups: ['新客户群', '体验群'],
pushCount: 234,
successCount: 218,
lastPushTime: '2025-02-06 15:30:22',
createTime: '2024-12-01 09:15:30',
creator: 'admin',
status: 1, // 运行中
pushInterval: 30,
maxPushPerDay: 300,
timeRange: { start: '08:00', end: '22:00' },
messageType: 'text',
messageContent: '欢迎加入我们的大家庭!这里有最新的产品信息和优惠活动...',
targetTags: ['新客户', '欢迎'],
pushMode: 'immediate',
},
];
}

14
Cunkebao/src/api/index.ts Normal file
View File

@@ -0,0 +1,14 @@
// 导出所有API相关的内容
export * from './auth';
export * from './utils';
export * from './interceptors';
export * from './request';
// 导出现有的API模块
export * from './devices';
export * from './scenarios';
export * from './wechat-accounts';
export * from './trafficDistribution';
// 默认导出request实例
export { default as request } from './request';

View File

@@ -0,0 +1,152 @@
import { refreshAuthToken, isTokenExpiringSoon, clearToken } from './utils';
// Token过期处理
export const handleTokenExpired = () => {
if (typeof window !== 'undefined') {
// 清除本地存储
clearToken();
// 跳转到登录页面
setTimeout(() => {
window.location.href = '/login';
}, 0);
}
};
// 显示API错误但不会重定向
export const showApiError = (error: any, defaultMessage: string = '请求失败') => {
if (typeof window === 'undefined') return; // 服务端不处理
let errorMessage = defaultMessage;
// 尝试从各种可能的错误格式中获取消息
if (error) {
if (typeof error === 'string') {
errorMessage = error;
} else if (error instanceof Error) {
errorMessage = error.message || defaultMessage;
} else if (typeof error === 'object') {
// 尝试从API响应中获取错误消息
errorMessage = error.msg || error.message || error.error || defaultMessage;
}
}
// 显示错误消息
console.error('API错误:', errorMessage);
// 这里可以集成toast系统
// 由于toast context在组件层级这里暂时用console
// 实际项目中可以通过事件系统或其他方式集成
};
// 请求拦截器 - 检查token是否需要刷新
export const requestInterceptor = async (): Promise<boolean> => {
if (typeof window === 'undefined') {
return true;
}
// 检查token是否即将过期
if (isTokenExpiringSoon()) {
try {
console.log('Token即将过期尝试刷新...');
const success = await refreshAuthToken();
if (!success) {
console.log('Token刷新失败需要重新登录');
handleTokenExpired();
return false;
}
console.log('Token刷新成功');
} catch (error) {
console.error('Token刷新过程中出错:', error);
handleTokenExpired();
return false;
}
}
return true;
};
// 响应拦截器 - 处理常见错误
export const responseInterceptor = (response: any, result: any) => {
// 处理401未授权
if (response?.status === 401 || (result && result.code === 401)) {
handleTokenExpired();
throw new Error('登录已过期,请重新登录');
}
// 处理403禁止访问
if (response?.status === 403 || (result && result.code === 403)) {
throw new Error('没有权限访问此资源');
}
// 处理404未找到
if (response?.status === 404 || (result && result.code === 404)) {
throw new Error('请求的资源不存在');
}
// 处理500服务器错误
if (response?.status >= 500 || (result && result.code >= 500)) {
throw new Error('服务器内部错误,请稍后重试');
}
return result;
};
// 错误拦截器 - 统一错误处理
export const errorInterceptor = (error: any) => {
console.error('API请求错误:', error);
let errorMessage = '网络请求失败,请稍后重试';
if (error) {
if (typeof error === 'string') {
errorMessage = error;
} else if (error instanceof Error) {
errorMessage = error.message;
} else if (error.name === 'TypeError' && error.message.includes('fetch')) {
errorMessage = '网络连接失败,请检查网络设置';
} else if (error.name === 'AbortError') {
errorMessage = '请求已取消';
}
}
showApiError(error, errorMessage);
throw new Error(errorMessage);
};
// 网络状态监听
export const setupNetworkListener = () => {
if (typeof window === 'undefined') return;
const handleOnline = () => {
console.log('网络已连接');
// 可以在这里添加网络恢复后的处理逻辑
};
const handleOffline = () => {
console.log('网络已断开');
showApiError(null, '网络连接已断开,请检查网络设置');
};
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
// 返回清理函数
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
};
// 初始化拦截器
export const initInterceptors = () => {
// 设置网络监听
const cleanupNetwork = setupNetworkListener();
// 返回清理函数
return () => {
if (cleanupNetwork) {
cleanupNetwork();
}
};
};

View File

@@ -0,0 +1,111 @@
import { get, post, del } from './request';
import {
MomentsSyncTask,
CreateMomentsSyncData,
UpdateMomentsSyncData,
SyncRecord,
ApiResponse,
PaginatedResponse
} from '@/types/moments-sync';
// 获取朋友圈同步任务列表
export async function fetchMomentsSyncTasks(): Promise<MomentsSyncTask[]> {
try {
const res = await get<ApiResponse<PaginatedResponse<MomentsSyncTask>>>('/v1/workbench/list?type=2&page=1&limit=100');
if (res.code === 200 && res.data) {
return res.data.list || [];
}
return [];
} catch (error) {
console.error('获取朋友圈同步任务失败:', error);
return [];
}
}
// 获取单个任务详情
export async function fetchMomentsSyncTaskDetail(id: string): Promise<MomentsSyncTask | null> {
try {
const res = await get<ApiResponse<MomentsSyncTask>>(`/v1/workbench/detail?id=${id}`);
if (res.code === 200 && res.data) {
return res.data;
}
return null;
} catch (error) {
console.error('获取任务详情失败:', error);
return null;
}
}
// 创建朋友圈同步任务
export async function createMomentsSyncTask(data: CreateMomentsSyncData): Promise<ApiResponse> {
return post('/v1/workbench/create', {
...data,
type: 2 // 朋友圈同步类型
});
}
// 更新朋友圈同步任务
export async function updateMomentsSyncTask(data: UpdateMomentsSyncData): Promise<ApiResponse> {
return post('/v1/workbench/update', {
...data,
type: 2 // 朋友圈同步类型
});
}
// 删除朋友圈同步任务
export async function deleteMomentsSyncTask(id: string): Promise<ApiResponse> {
return del('/v1/workbench/delete', { params: { id } });
}
// 切换任务状态
export async function toggleMomentsSyncTask(id: string, status: string): Promise<ApiResponse> {
return post('/v1/workbench/update-status', { id, status });
}
// 复制朋友圈同步任务
export async function copyMomentsSyncTask(id: string): Promise<ApiResponse> {
return post('/v1/workbench/copy', { id });
}
// 获取同步记录
export async function fetchSyncRecords(
workbenchId: string,
page: number = 1,
limit: number = 20,
keyword?: string
): Promise<PaginatedResponse<SyncRecord>> {
try {
const params = new URLSearchParams({
workbenchId,
page: page.toString(),
limit: limit.toString()
});
if (keyword) {
params.append('keyword', keyword);
}
const res = await get<ApiResponse<PaginatedResponse<SyncRecord>>>(`/v1/workbench/sync-records?${params.toString()}`);
if (res.code === 200 && res.data) {
return res.data;
}
return { list: [], total: 0, page, limit };
} catch (error) {
console.error('获取同步记录失败:', error);
return { list: [], total: 0, page, limit };
}
}
// 手动同步
export async function syncMoments(id: string): Promise<ApiResponse> {
return post('/v1/workbench/sync', { id });
}
// 同步所有任务
export async function syncAllMoments(): Promise<ApiResponse> {
return post('/v1/workbench/sync-all', { type: 2 });
}
export type { MomentsSyncTask, SyncRecord, CreateMomentsSyncData };

View File

@@ -0,0 +1,73 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { requestInterceptor, responseInterceptor, errorInterceptor } from './interceptors';
// 创建axios实例
const request: AxiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_BASE_URL || 'https://ckbapi.quwanzhi.com',
timeout: 20000,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器
request.interceptors.request.use(
async (config) => {
// 检查token是否需要刷新
if (config.headers.Authorization) {
const shouldContinue = await requestInterceptor();
if (!shouldContinue) {
throw new Error('请求被拦截,需要重新登录');
}
}
// 添加token到请求头
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
request.interceptors.response.use(
(response: AxiosResponse) => {
// 处理响应数据
const result = response.data;
const processedResult = responseInterceptor(response, result);
return processedResult;
},
(error) => {
// 统一错误处理
return errorInterceptor(error);
}
);
// 封装GET请求
export const get = <T = any>(url: string, config?: AxiosRequestConfig): Promise<T> => {
return request.get(url, config);
};
// 封装POST请求
export const post = <T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
return request.post(url, data, config);
};
// 封装PUT请求
export const put = <T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
return request.put(url, data, config);
};
// 封装DELETE请求
export const del = <T = any>(url: string, config?: AxiosRequestConfig): Promise<T> => {
return request.delete(url, config);
};
// 导出request实例
export { request };
export default request;

View File

@@ -0,0 +1,311 @@
import { get, del, post,put } from './request';
import type { ApiResponse } from '@/types/common';
// 服务器返回的场景数据类型
export interface SceneItem {
id: number;
name: string;
image: string;
status: number;
createTime: number;
updateTime: number | null;
deleteTime: number | null;
}
// 前端使用的场景数据类型
export interface Channel {
id: string;
name: string;
icon: string;
stats: {
daily: number;
growth: number;
};
link?: string;
plans?: Plan[];
}
// 计划类型
export interface Plan {
id: string;
name: string;
isNew?: boolean;
status: "active" | "paused" | "completed";
acquisitionCount: number;
}
// 任务类型
export interface Task {
id: string;
name: string;
status: number;
stats: {
devices: number;
acquired: number;
added: number;
};
lastUpdated: string;
executionTime: string;
nextExecutionTime: string;
trend: { date: string; customers: number }[];
}
// 消息内容类型
export interface MessageContent {
id: string;
type: string; // "text" | "image" | "video" | "file" | "miniprogram" | "link" | "group" 等
content?: string;
intervalUnit?: "seconds" | "minutes";
sendInterval?: number;
// 其他可选字段
[key: string]: any;
}
// 每天的消息计划
export interface MessagePlan {
day: number;
messages: MessageContent[];
}
// 海报类型
export interface Poster {
id: string;
name: string;
type: string;
preview: string;
}
// 标签类型
export interface Tag {
id: string;
name: string;
[key: string]: any;
}
// textUrl类型
export interface TextUrl {
apiKey: string;
originalString?: string;
sign?: string;
fullUrl: string;
}
// 计划详情类型
export interface PlanDetail {
id: number;
name: string;
scenario: number;
scenarioTags: Tag[];
customTags: Tag[];
posters: Poster[];
device: string[];
enabled: boolean;
addInterval: number;
remarkFormat: string;
endTime: string;
greeting: string;
startTime: string;
remarkType: string;
addFriendInterval: number;
messagePlans: MessagePlan[];
sceneId: number | string;
userId: number;
companyId: number;
status: number;
apiKey: string;
wxMinAppSrc?: any;
textUrl: TextUrl;
[key: string]: any;
}
/**
* 获取获客场景列表
*
* @param params 查询参数
* @returns 获客场景列表
*/
export const fetchScenes = async (params: {
page?: number;
limit?: number;
keyword?: string;
} = {}): Promise<ApiResponse<SceneItem[]>> => {
const { page = 1, limit = 10, keyword = "" } = params;
const queryParams = new URLSearchParams();
queryParams.append("page", String(page));
queryParams.append("limit", String(limit));
if (keyword) {
queryParams.append("keyword", keyword);
}
try {
return await get<ApiResponse<SceneItem[]>>(`/v1/plan/scenes?${queryParams.toString()}`);
} catch (error) {
console.error("Error fetching scenes:", error);
// 返回一个错误响应
return {
code: 500,
msg: "获取场景列表失败",
data: []
};
}
};
/**
* 获取场景详情
*
* @param id 场景ID
* @returns 场景详情
*/
export const fetchSceneDetail = async (id: string | number): Promise<ApiResponse<SceneItem>> => {
try {
return await get<ApiResponse<SceneItem>>(`/v1/plan/scenes/${id}`);
} catch (error) {
console.error("Error fetching scene detail:", error);
return {
code: 500,
msg: "获取场景详情失败",
data: null
};
}
};
/**
* 获取场景名称
*
* @param channel 场景标识
* @returns 场景名称
*/
export const fetchSceneName = async (channel: string): Promise<ApiResponse<{ name: string }>> => {
try {
return await get<ApiResponse<{ name: string }>>(`/v1/plan/scenes-detail?id=${channel}`);
} catch (error) {
console.error("Error fetching scene name:", error);
return {
code: 500,
msg: "获取场景名称失败",
data: { name: channel }
};
}
};
/**
* 获取计划列表
*
* @param channel 场景标识
* @param page 页码
* @param pageSize 每页数量
* @returns 计划列表
*/
export const fetchPlanList = async (
channel: string,
page: number = 1,
pageSize: number = 10
): Promise<ApiResponse<{ list: Task[]; total: number }>> => {
try {
return await get<ApiResponse<{ list: Task[]; total: number }>>(
`/v1/plan/list?sceneId=${channel}&page=${page}&pageSize=${pageSize}`
);
} catch (error) {
console.error("Error fetching plan list:", error);
return {
code: 500,
msg: "获取计划列表失败",
data: { list: [], total: 0 }
};
}
};
/**
* 复制计划
*
* @param planId 计划ID
* @returns 复制结果
*/
export const copyPlan = async (planId: string): Promise<ApiResponse<any>> => {
try {
return await get<ApiResponse<any>>(`/v1/plan/copy?planId=${planId}`);
} catch (error) {
console.error("Error copying plan:", error);
return {
code: 500,
msg: "复制计划失败",
data: null
};
}
};
/**
* 删除计划
*
* @param planId 计划ID
* @returns 删除结果
*/
export const deletePlan = async (planId: string): Promise<ApiResponse<any>> => {
try {
return await del<ApiResponse<any>>(`/v1/plan/delete?planId=${planId}`);
} catch (error) {
console.error("Error deleting plan:", error);
return {
code: 500,
msg: "删除计划失败",
data: null
};
}
};
/**
* 获取计划详情
*
* @param planId 计划ID
* @returns 计划详情
*/
export const fetchPlanDetail = async (planId: string): Promise<ApiResponse<PlanDetail>> => {
try {
return await get<ApiResponse<PlanDetail>>(`/v1/plan/detail?planId=${planId}`);
} catch (error) {
console.error("Error fetching plan detail:", error);
return {
code: 500,
msg: "获取计划详情失败",
data: null
};
}
};
/**
* 将服务器返回的场景数据转换为前端展示需要的格式
*
* @param item 服务器返回的场景数据
* @returns 前端展示的场景数据
*/
export const transformSceneItem = (item: SceneItem): Channel => {
// 为每个场景生成随机的"今日"数据和"增长百分比"
const dailyCount = Math.floor(Math.random() * 100);
const growthPercent = Math.floor(Math.random() * 40) - 10; // -10% 到 30% 的随机值
// 默认图标(如果服务器没有返回)
const defaultIcon = "/assets/icons/poster-icon.svg";
return {
id: String(item.id),
name: item.name,
icon: item.image || defaultIcon,
stats: {
daily: dailyCount,
growth: growthPercent
}
};
};
export const getPlanScenes = () => get<any>('/v1/plan/scenes');
export async function createScenarioPlan(data: any) {
return post('/v1/plan/create', data);
}
// 编辑计划
export async function updateScenarioPlan(planId: number | string, data: any) {
return await put(`/v1/plan/update?planId=${planId}`, data);
}

View File

@@ -0,0 +1,227 @@
import { get, post, put, del } from './request';
import type { ApiResponse } from '@/types/common';
// 工作台任务类型
export enum WorkbenchTaskType {
MOMENTS_SYNC = 1, // 朋友圈同步
GROUP_PUSH = 2, // 社群推送
AUTO_LIKE = 3, // 自动点赞
AUTO_GROUP = 4, // 自动建群
TRAFFIC_DISTRIBUTION = 5, // 流量分发
}
// 工作台任务状态
export enum WorkbenchTaskStatus {
PENDING = 0, // 待处理
RUNNING = 1, // 运行中
PAUSED = 2, // 已暂停
COMPLETED = 3, // 已完成
FAILED = 4, // 失败
}
// 账号类型
export interface Account {
id: string;
userName: string;
realName: string;
nickname: string;
memo: string;
}
// 账号列表响应类型
export interface AccountListResponse {
list: Account[];
total: number;
page: number;
limit: number;
}
// 流量池类型
export interface TrafficPool {
id: string;
name: string;
count: number;
description?: string;
deviceIds: string[];
createTime?: string;
updateTime?: string;
}
// 流量池列表响应类型
export interface TrafficPoolListResponse {
list: TrafficPool[];
total: number;
page: number;
pageSize: number;
}
// 流量分发规则类型
export interface DistributionRule {
id: number;
name: string;
type: number;
status: number;
autoStart: number;
createTime: string;
updateTime: string;
companyId: number;
config?: {
id: number;
workbenchId: number;
distributeType: number; // 1-均分配, 2-优先级分配, 3-比例分配
maxPerDay: number; // 每日最大分配量
timeType: number; // 1-全天, 2-自定义时间段
startTime: string; // 开始时间
endTime: string; // 结束时间
account: string[]; // 账号列表
devices: string[]; // 设备列表
pools: string[]; // 流量池列表
createTime: string;
updateTime: string;
lastUpdated: string;
total: {
dailyAverage: number; // 日均分发量
totalAccounts: number; // 分发账户总数
deviceCount: number; // 分发设备数量
poolCount: number; // 流量池数量
totalUsers: number; // 总用户数
};
};
auto_like?: any;
moments_sync?: any;
group_push?: any;
}
// 流量分发列表响应类型
export interface TrafficDistributionListResponse {
list: DistributionRule[];
total: number;
page: number;
limit: number;
}
/**
* 获取账号列表
* @param params 查询参数
* @returns 账号列表
*/
export const fetchAccountList = async (params: {
page?: number; // 页码
limit?: number; // 每页数量
keyword?: string; // 搜索关键词
} = {}): Promise<ApiResponse<AccountListResponse>> => {
const { page = 1, limit = 10, keyword = "" } = params;
const queryParams = new URLSearchParams();
queryParams.append('page', page.toString());
queryParams.append('limit', limit.toString());
if (keyword) {
queryParams.append('keyword', keyword);
}
return get<ApiResponse<AccountListResponse>>(`/v1/workbench/account-list?${queryParams.toString()}`);
};
/**
* 获取设备标签(流量池)列表
* @param params 查询参数
* @returns 流量池列表
*/
export const fetchDeviceLabels = async (params: {
deviceIds: string[]; // 设备ID列表
page?: number; // 页码
pageSize?: number; // 每页数量
keyword?: string; // 搜索关键词
}): Promise<ApiResponse<TrafficPoolListResponse>> => {
const { deviceIds, page = 1, pageSize = 10, keyword = "" } = params;
const queryParams = new URLSearchParams();
queryParams.append('deviceIds', deviceIds.join(','));
queryParams.append('page', page.toString());
queryParams.append('pageSize', pageSize.toString());
if (keyword) {
queryParams.append('keyword', keyword);
}
return get<ApiResponse<TrafficPoolListResponse>>(`/v1/workbench/device-labels?${queryParams.toString()}`);
};
/**
* 获取流量分发规则列表
* @param params 查询参数
* @returns 流量分发规则列表
*/
export const fetchDistributionRules = async (params: {
page?: number;
limit?: number;
keyword?: string;
} = {}): Promise<ApiResponse<TrafficDistributionListResponse>> => {
const { page = 1, limit = 10, keyword = "" } = params;
const queryParams = new URLSearchParams();
queryParams.append('type', WorkbenchTaskType.TRAFFIC_DISTRIBUTION.toString());
queryParams.append('page', page.toString());
queryParams.append('limit', limit.toString());
if (keyword) {
queryParams.append('keyword', keyword);
}
return get<ApiResponse<TrafficDistributionListResponse>>(`/v1/workbench/list?${queryParams.toString()}`);
};
/**
* 获取流量分发规则详情
* @param id 规则ID
* @returns 流量分发规则详情
*/
export const fetchDistributionRuleDetail = async (id: string): Promise<ApiResponse<DistributionRule>> => {
return get<ApiResponse<DistributionRule>>(`/v1/workbench/detail?id=${id}`);
};
/**
* 创建流量分发规则
* @param params 创建参数
* @returns 创建结果
*/
export const createDistributionRule = async (params: any): Promise<ApiResponse<{ id: string }>> => {
return post<ApiResponse<{ id: string }>>('/v1/workbench/create', {
...params,
type: WorkbenchTaskType.TRAFFIC_DISTRIBUTION
});
};
/**
* 更新流量分发规则
* @param id 规则ID
* @param params 更新参数
* @returns 更新结果
*/
export const updateDistributionRule = async (id : string, params: any): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>(`/v1/workbench/update`, {
id: id,
...params,
type: WorkbenchTaskType.TRAFFIC_DISTRIBUTION
});
};
/**
* 删除流量分发规则
* @param id 规则ID
* @returns 删除结果
*/
export const deleteDistributionRule = async (id: string): Promise<ApiResponse<any>> => {
return del<ApiResponse<any>>(`/v1/workbench/delete?id=${id}`);
};
/**
* 启动/暂停流量分发规则
* @param id 规则ID
* @param status 状态1-启动0-暂停
* @returns 操作结果
*/
export const toggleDistributionRuleStatus = async (id: string, status: 0 | 1): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>('/v1/workbench/update-status', { id, status });
};

View File

@@ -0,0 +1,18 @@
import { request } from './request';
import type { AxiosResponse } from 'axios';
// 上传图片,返回图片地址
export async function uploadImage(file: File): Promise<string> {
const formData = new FormData();
formData.append('file', file);
const response: AxiosResponse<any> = await request.post('/v1/attachment/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
const res = response.data || response;
if (res?.url) {
return res.url;
}
throw new Error(res?.msg || '图片上传失败');
}

195
Cunkebao/src/api/utils.ts Normal file
View File

@@ -0,0 +1,195 @@
import { authApi } from './auth';
import { get, post, put, del } from './request';
import type { ApiResponse, PaginatedResponse } from '@/types/common';
// 设置token到localStorage
export const setToken = (token: string) => {
if (typeof window !== 'undefined') {
localStorage.setItem('token', token);
}
};
// 获取token
export const getToken = (): string | null => {
if (typeof window !== 'undefined') {
return localStorage.getItem('token');
}
return null;
};
// 清除token
export const clearToken = () => {
if (typeof window !== 'undefined') {
localStorage.removeItem('token');
localStorage.removeItem('userInfo');
localStorage.removeItem('token_expired');
localStorage.removeItem('s2_accountId');
}
};
// 验证token是否有效
export const validateToken = async (): Promise<boolean> => {
try {
const response = await authApi.getUserInfo();
return response.code === 200;
} catch (error) {
console.error('Token验证失败:', error);
return false;
}
};
// 刷新令牌
export const refreshAuthToken = async (): Promise<boolean> => {
if (typeof window === 'undefined') {
return false;
}
try {
const response = await authApi.refreshToken();
if (response.code === 200 && response.data?.token) {
setToken(response.data.token);
// 更新过期时间
if (response.data.token_expired) {
localStorage.setItem('token_expired', response.data.token_expired);
}
return true;
}
return false;
} catch (error) {
console.error('刷新Token失败:', error);
return false;
}
};
// 检查token是否即将过期
export const isTokenExpiringSoon = (): boolean => {
if (typeof window === 'undefined') {
return false;
}
const tokenExpired = localStorage.getItem('token_expired');
if (!tokenExpired) return true;
try {
const expiredTime = new Date(tokenExpired).getTime();
const currentTime = new Date().getTime();
// 提前10分钟认为即将过期
return currentTime >= (expiredTime - 10 * 60 * 1000);
} catch (error) {
console.error('解析token过期时间失败:', error);
return true;
}
};
// 检查token是否已过期
export const isTokenExpired = (): boolean => {
if (typeof window === 'undefined') {
return false;
}
const tokenExpired = localStorage.getItem('token_expired');
if (!tokenExpired) return true;
try {
const expiredTime = new Date(tokenExpired).getTime();
const currentTime = new Date().getTime();
// 提前5分钟认为过期给刷新留出时间
return currentTime >= (expiredTime - 5 * 60 * 1000);
} catch (error) {
console.error('解析token过期时间失败:', error);
return true;
}
};
// 请求去重器
class RequestDeduplicator {
private pendingRequests = new Map<string, Promise<any>>();
async deduplicate<T>(key: string, requestFn: () => Promise<T>): Promise<T> {
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key)!;
}
const promise = requestFn();
this.pendingRequests.set(key, promise);
try {
const result = await promise;
return result;
} finally {
this.pendingRequests.delete(key);
}
}
getPendingCount(): number {
return this.pendingRequests.size;
}
clear(): void {
this.pendingRequests.clear();
}
}
// 请求取消管理器
class RequestCancelManager {
private abortControllers = new Map<string, AbortController>();
createController(key: string): AbortController {
// 取消之前的请求
this.cancelRequest(key);
const controller = new AbortController();
this.abortControllers.set(key, controller);
return controller;
}
cancelRequest(key: string): void {
const controller = this.abortControllers.get(key);
if (controller) {
controller.abort();
this.abortControllers.delete(key);
}
}
cancelAllRequests(): void {
this.abortControllers.forEach(controller => controller.abort());
this.abortControllers.clear();
}
getController(key: string): AbortController | undefined {
return this.abortControllers.get(key);
}
}
// 导出单例实例
export const requestDeduplicator = new RequestDeduplicator();
export const requestCancelManager = new RequestCancelManager();
/**
* 通用文件上传方法(支持图片、文件)
* @param {File} file - 要上传的文件对象
* @param {string} [uploadUrl='/v1/attachment/upload'] - 上传接口地址
* @returns {Promise<string>} - 上传成功后返回文件url
*/
export async function uploadFile(file: File, uploadUrl: string = '/v1/attachment/upload'): Promise<string> {
try {
// 创建 FormData 对象用于文件上传
const formData = new FormData();
formData.append('file', file);
// 使用 post 方法上传文件,设置正确的 Content-Type
const res = await post(uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
// 检查响应结果
if (res?.code === 200 && res?.data?.url) {
return res.data.url;
} else {
throw new Error(res?.msg || '文件上传失败');
}
} catch (e: any) {
throw new Error(e?.message || '文件上传失败');
}
}

View File

@@ -0,0 +1,207 @@
import { get, post, put } from './request';
import type { ApiResponse } from '@/types/common';
// 添加接口返回数据类型定义
interface WechatAccountSummary {
accountAge: string;
activityLevel: {
allTimes: number;
dayTimes: number;
};
accountWeight: {
scope: number;
ageWeight: number;
activityWeigth: number;
restrictWeight: number;
realNameWeight: number;
};
statistics: {
todayAdded: number;
addLimit: number;
};
restrictions: {
id: number;
level: string;
reason: string;
date: string;
}[];
}
interface QueryWechatAccountParams {
page?: number;
limit?: number;
keyword?: string;
sort?: string;
order?: string;
}
/**
* 获取微信账号列表
* @param params 查询参数
* @returns 微信账号列表响应
*/
export const fetchWechatAccountList = async (params: QueryWechatAccountParams = {}): Promise<ApiResponse<{
list: any[];
total: number;
page: number;
limit: number;
}>> => {
const queryParams = new URLSearchParams();
// 添加查询参数
if (params.page) queryParams.append('page', params.page.toString());
if (params.limit) queryParams.append('limit', params.limit.toString());
if (params.keyword) queryParams.append('nickname', params.keyword); // 使用nickname作为关键词搜索参数
if (params.sort) queryParams.append('sort', params.sort);
if (params.order) queryParams.append('order', params.order);
// 发起API请求
return get<ApiResponse<{
list: any[];
total: number;
page: number;
limit: number;
}>>(`/v1/wechats?${queryParams.toString()}`);
};
/**
* 刷新微信账号状态
* @returns 刷新结果
*/
export const refreshWechatAccounts = async (): Promise<ApiResponse<any>> => {
return put<ApiResponse<any>>('/v1/wechats/refresh', {});
};
/**
* 执行微信好友转移
* @param sourceId 源微信账号ID
* @param targetId 目标微信账号ID
* @returns 转移结果
*/
export const transferWechatFriends = async (sourceId: string | number, targetId: string | number): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>('/v1/wechats/transfer-friends', {
source_id: sourceId,
target_id: targetId
});
};
/**
* 将服务器返回的微信账号数据转换为前端使用的格式
* @param serverAccount 服务器返回的微信账号数据
* @returns 前端使用的微信账号数据
*/
export const transformWechatAccount = (serverAccount: any): any => {
// 从deviceInfo中提取设备信息
let deviceName = '';
if (serverAccount.deviceInfo) {
// 尝试解析设备信息字符串
const deviceInfo = serverAccount.deviceInfo.split(' ');
if (deviceInfo.length > 0) {
// 提取设备名称
if (deviceInfo.length > 1) {
deviceName = deviceInfo[1] ? deviceInfo[1].replace(/[()]/g, '').trim() : '';
}
}
}
// 如果没有设备名称,使用备用名称
if (!deviceName) {
deviceName = serverAccount.deviceMemo || '未命名设备';
}
// 假设每天最多可添加20个好友
const maxDailyAdds = 20;
const todayAdded = serverAccount.todayNewFriendCount || 0;
return {
id: serverAccount.id.toString(),
avatar: serverAccount.avatar || '',
nickname: serverAccount.nickname || serverAccount.accountNickname || '未命名',
wechatId: serverAccount.wechatId || '',
deviceId: serverAccount.deviceId || '',
deviceName,
friendCount: serverAccount.totalFriend || 0,
todayAdded,
remainingAdds: serverAccount.canAddFriendCount || (maxDailyAdds - todayAdded),
maxDailyAdds,
status: serverAccount.wechatStatus === 1 ? "normal" : "abnormal" as "normal" | "abnormal",
lastActive: new Date().toLocaleString() // 服务端未提供,使用当前时间
};
};
/**
* 获取微信好友列表
* @param wechatId 微信账号ID
* @param page 页码
* @param pageSize 每页数量
* @param searchQuery 搜索关键词
* @returns 好友列表数据
*/
export const fetchWechatFriends = async (wechatId: string, page: number = 1, pageSize: number = 20, searchQuery: string = ''): Promise<ApiResponse<{
list: any[];
total: number;
page: number;
limit: number;
}>> => {
try {
const queryParams = new URLSearchParams();
queryParams.append('page', page.toString());
queryParams.append('limit', pageSize.toString());
if (searchQuery) {
queryParams.append('search', searchQuery);
}
return get<ApiResponse<{
list: any[];
total: number;
page: number;
limit: number;
}>>(`/v1/wechats/${wechatId}/friends?${queryParams.toString()}`);
} catch (error) {
console.error("获取好友列表失败:", error);
throw error;
}
};
/**
* 获取微信账号概览信息
* @param id 微信账号ID
* @returns 微信账号概览信息
*/
export const fetchWechatAccountSummary = async (wechatId: string): Promise<ApiResponse<WechatAccountSummary>> => {
try {
return get<ApiResponse<WechatAccountSummary>>(`/v1/wechats/${wechatId}/summary`);
} catch (error) {
console.error("获取账号概览失败:", error);
throw error;
}
};
/**
* 获取好友详情信息
* @param wechatId 微信账号ID
* @param friendId 好友ID
* @returns 好友详情信息
*/
export interface WechatFriendDetail {
id: number;
avatar: string;
nickname: string;
region: string;
wechatId: string;
addDate: string;
tags: string[];
memo: string;
source: string;
}
export const fetchWechatFriendDetail = async (wechatId: string): Promise<ApiResponse<WechatFriendDetail>> => {
try {
return get<ApiResponse<WechatFriendDetail>>(`/v1/wechats/${wechatId}/friend-detail`);
} catch (error) {
console.error("获取好友详情失败:", error);
throw error;
}
};