同步代码
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
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>;
|
||||
},
|
||||
};
|
||||
@@ -1,119 +0,0 @@
|
||||
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 };
|
||||
27
Cunkebao/src/api/common.ts
Normal file
27
Cunkebao/src/api/common.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import request from "./request";
|
||||
/**
|
||||
* 通用文件上传方法(支持图片、文件)
|
||||
* @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);
|
||||
|
||||
// 使用 request 方法上传文件,设置正确的 Content-Type
|
||||
const res = await request(uploadUrl, formData, "POST", {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
return res.url;
|
||||
} catch (e: any) {
|
||||
throw new Error(e?.message || "文件上传失败");
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
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}`);
|
||||
},
|
||||
};
|
||||
@@ -1,200 +0,0 @@
|
||||
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`);
|
||||
},
|
||||
};
|
||||
@@ -1,201 +0,0 @@
|
||||
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',
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// 导出所有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';
|
||||
@@ -1,152 +0,0 @@
|
||||
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();
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
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 };
|
||||
@@ -1,73 +1,90 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { requestInterceptor, responseInterceptor, errorInterceptor } from './interceptors';
|
||||
import axios, {
|
||||
AxiosInstance,
|
||||
AxiosRequestConfig,
|
||||
Method,
|
||||
AxiosResponse,
|
||||
} from "axios";
|
||||
import { Toast } from "antd-mobile";
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
const { token } = useUserStore.getState();
|
||||
const DEFAULT_DEBOUNCE_GAP = 1000;
|
||||
const debounceMap = new Map<string, number>();
|
||||
|
||||
// 创建axios实例
|
||||
const request: AxiosInstance = axios.create({
|
||||
baseURL: process.env.REACT_APP_API_BASE_URL || 'https://ckbapi.quwanzhi.com',
|
||||
const instance: AxiosInstance = axios.create({
|
||||
baseURL: (import.meta as any).env?.VITE_API_BASE_URL || "/api",
|
||||
timeout: 20000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
request.interceptors.request.use(
|
||||
async (config) => {
|
||||
// 检查token是否需要刷新
|
||||
if (config.headers.Authorization) {
|
||||
const shouldContinue = await requestInterceptor();
|
||||
if (!shouldContinue) {
|
||||
throw new Error('请求被拦截,需要重新登录');
|
||||
instance.interceptors.request.use((config: any) => {
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
config.headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(res: AxiosResponse) => {
|
||||
const { code, success, msg } = res.data || {};
|
||||
if (code === 200 || success) {
|
||||
return res.data.data ?? res.data;
|
||||
}
|
||||
Toast.show({ content: msg || "接口错误", position: "top" });
|
||||
if (code === 401) {
|
||||
localStorage.removeItem("token");
|
||||
const currentPath = window.location.pathname + window.location.search;
|
||||
if (currentPath === "/login") {
|
||||
window.location.href = "/login";
|
||||
} else {
|
||||
window.location.href = `/login?redirect=${encodeURIComponent(currentPath)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加token到请求头
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
return config;
|
||||
return Promise.reject(msg || "接口错误");
|
||||
},
|
||||
err => {
|
||||
Toast.show({ content: err.message || "网络异常", position: "top" });
|
||||
return Promise.reject(err);
|
||||
},
|
||||
(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);
|
||||
export function request(
|
||||
url: string,
|
||||
data?: any,
|
||||
method: Method = "GET",
|
||||
config?: AxiosRequestConfig,
|
||||
debounceGap?: number,
|
||||
): Promise<any> {
|
||||
const gap =
|
||||
typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP;
|
||||
const key = `${method}_${url}_${JSON.stringify(data)}`;
|
||||
const now = Date.now();
|
||||
const last = debounceMap.get(key) || 0;
|
||||
if (gap > 0 && now - last < gap) {
|
||||
// Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' });
|
||||
return Promise.reject("请求过于频繁,请稍后再试");
|
||||
}
|
||||
);
|
||||
debounceMap.set(key, now);
|
||||
|
||||
// 封装GET请求
|
||||
export const get = <T = any>(url: string, config?: AxiosRequestConfig): Promise<T> => {
|
||||
return request.get(url, config);
|
||||
};
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
url,
|
||||
method,
|
||||
...config,
|
||||
};
|
||||
|
||||
// 封装POST请求
|
||||
export const post = <T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
|
||||
return request.post(url, data, config);
|
||||
};
|
||||
// 如果是FormData,不设置Content-Type,让浏览器自动设置
|
||||
if (data instanceof FormData) {
|
||||
delete axiosConfig.headers?.["Content-Type"];
|
||||
}
|
||||
|
||||
// 封装PUT请求
|
||||
export const put = <T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => {
|
||||
return request.put(url, data, config);
|
||||
};
|
||||
if (method.toUpperCase() === "GET") {
|
||||
axiosConfig.params = data;
|
||||
} else {
|
||||
axiosConfig.data = data;
|
||||
}
|
||||
return instance(axiosConfig);
|
||||
}
|
||||
|
||||
// 封装DELETE请求
|
||||
export const del = <T = any>(url: string, config?: AxiosRequestConfig): Promise<T> => {
|
||||
return request.delete(url, config);
|
||||
};
|
||||
|
||||
// 导出request实例
|
||||
export { request };
|
||||
export default request;
|
||||
export default request;
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取计划小程序二维码
|
||||
* @param taskid 任务ID
|
||||
* @returns base64二维码
|
||||
*/
|
||||
export const getWxMinAppCode = async (taskId: string): Promise<{ code: number; data?: string; msg?: string }> => {
|
||||
try {
|
||||
return await get<{ code: number; data?: string; msg?: string }>(
|
||||
`/v1/plan/getWxMinAppCode?taskId=${ taskId }`,
|
||||
|
||||
);
|
||||
} catch (error) {
|
||||
return { code: 500, msg: '获取小程序二维码失败' };
|
||||
}
|
||||
};
|
||||
@@ -1,227 +0,0 @@
|
||||
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 });
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
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 || '图片上传失败');
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
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 || '文件上传失败');
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user