Files
Neo-ZQYY/tmp/DEMO-miniprogram/miniprogram/services/api.ts

453 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Service 层 — 统一数据请求入口
*
* 页面只调用 service 函数,不直接引用 mock 数据或 wx.request。
* 联调时只需修改此文件内部实现mock → request页面代码不动。
*
* 当前阶段:全部走 mock
* 联调阶段:逐个替换为 request() 调用
*/
import { request } from '../utils/request'
// ============================================
// Mock 数据导入(联调时逐步删除)
// ============================================
import {
mockTasks,
mockTaskDetails,
mockNotes,
mockPerformance,
mockPerformanceRecords,
mockBoardFinance,
mockCustomers,
mockCoaches,
mockCustomerDetail,
mockChatMessages,
mockChatHistory,
} from '../utils/mock-data'
import type {
Task,
TaskDetail,
Note,
PerformanceData,
PerformanceRecord,
BoardFinanceData,
CustomerCard,
CoachCard,
CustomerDetail as MockCustomerDetail,
ChatMessage,
ChatHistoryItem,
} from '../utils/mock-data'
// ============================================
// 内部工具
// ============================================
/** 模拟网络延迟(联调时删除) */
function delay(ms = 400): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* 是否使用真实 API联调开关
* 联调时改为 true或按模块逐个开启
*/
const USE_REAL_API = false
// ============================================
// 认证模块(已对接真实 API
// ============================================
/** 查询当前用户信息 */
export async function fetchMe(): Promise<ApiUserInfo> {
return request({ url: '/api/xcx/me', method: 'GET', needAuth: true })
}
// ============================================
// 任务模块
// ============================================
export interface FetchTasksParams {
status?: 'pending' | 'completed' | 'abandoned'
page?: number
pageSize?: number
}
/** 获取任务列表 + 绩效概览 */
export async function fetchTasks(params: FetchTasksParams = {}): Promise<{
tasks: Task[]
performance: PerformanceData
total: number
hasMore: boolean
}> {
if (USE_REAL_API) {
const data = await request({
url: '/api/xcx/tasks',
method: 'GET',
data: params,
needAuth: true,
})
// TODO: 联调时映射 snake_case → camelCase
return data
}
await delay()
const filtered = params.status
? mockTasks.filter(t => t.status === params.status)
: mockTasks
return {
tasks: filtered,
performance: mockPerformance,
total: filtered.length,
hasMore: false,
}
}
/** 获取任务详情 */
export async function fetchTaskDetail(taskId: string): Promise<TaskDetail | null> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/tasks/${taskId}`,
method: 'GET',
needAuth: true,
})
}
await delay()
return mockTaskDetails.find(t => t.id === taskId) || mockTaskDetails[0] || null
}
/** 放弃任务 */
export async function abandonTask(taskId: string, reason: string): Promise<void> {
if (USE_REAL_API) {
await request({
url: `/api/xcx/tasks/${taskId}/abandon`,
method: 'POST',
data: { reason },
needAuth: true,
})
return
}
await delay(300)
}
/** 取消放弃(恢复任务) */
export async function restoreTask(taskId: string): Promise<void> {
if (USE_REAL_API) {
await request({
url: `/api/xcx/tasks/${taskId}/restore`,
method: 'POST',
needAuth: true,
})
return
}
await delay(300)
}
/** 置顶任务 */
export async function pinTask(taskId: string): Promise<{ isPinned: boolean }> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/tasks/${taskId}/pin`,
method: 'POST',
needAuth: true,
})
}
await delay(300)
return { isPinned: true }
}
/** 取消置顶任务 */
export async function unpinTask(taskId: string): Promise<{ isPinned: boolean }> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/tasks/${taskId}/unpin`,
method: 'POST',
needAuth: true,
})
}
await delay(300)
return { isPinned: false }
}
// ============================================
// 备注模块
// ============================================
/** 获取备注列表 */
export async function fetchNotes(params: { page?: number; pageSize?: number } = {}): Promise<{
notes: Note[]
total: number
hasMore: boolean
}> {
if (USE_REAL_API) {
return request({ url: '/api/xcx/notes', method: 'GET', data: params, needAuth: true })
}
await delay()
return { notes: mockNotes, total: mockNotes.length, hasMore: false }
}
/** 新增备注 */
export async function createNote(data: {
content: string
taskId?: string
customerId?: string
ratingServiceWillingness?: number
ratingRevisitLikelihood?: number
}): Promise<{ id: string; createdAt: string }> {
if (USE_REAL_API) {
return request({ url: '/api/xcx/notes', method: 'POST', data, needAuth: true })
}
await delay(300)
return { id: `note-${Date.now()}`, createdAt: new Date().toISOString() }
}
/** 删除备注 */
export async function deleteNote(noteId: string): Promise<void> {
if (USE_REAL_API) {
await request({ url: `/api/xcx/notes/${noteId}`, method: 'DELETE', needAuth: true })
return
}
await delay(200)
}
// ============================================
// 绩效模块
// ============================================
/** 绩效概览 */
export async function fetchPerformanceOverview(params: {
year: number
month: number
}): Promise<PerformanceData> {
if (USE_REAL_API) {
return request({ url: '/api/xcx/performance', method: 'GET', data: params, needAuth: true })
}
await delay()
return mockPerformance
}
/** 绩效明细(按月) */
export async function fetchPerformanceRecords(params: {
year: number
month: number
page?: number
pageSize?: number
}): Promise<{ records: PerformanceRecord[]; hasMore: boolean }> {
if (USE_REAL_API) {
return request({
url: '/api/xcx/performance/records',
method: 'GET',
data: params,
needAuth: true,
})
}
await delay()
return { records: mockPerformanceRecords, hasMore: false }
}
// ============================================
// 客户模块
// ============================================
/** 客户详情 */
export async function fetchCustomerDetail(customerId: string): Promise<MockCustomerDetail | null> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/customers/${customerId}`,
method: 'GET',
needAuth: true,
})
}
await delay()
return mockCustomerDetail || null
}
/** 客户服务记录 */
export async function fetchCustomerRecords(params: {
customerId: string
year?: number
month?: number
table?: string
}): Promise<{ records: any[]; hasMore: boolean }> {
if (USE_REAL_API) {
const { customerId, ...rest } = params
return request({
url: `/api/xcx/customers/${customerId}/records`,
method: 'GET',
data: rest,
needAuth: true,
})
}
await delay()
// 联调前返回空,页面内联 mock 数据仍生效
return { records: [], hasMore: false }
}
// ============================================
// 看板模块
// ============================================
/** 助教看板 */
export async function fetchBoardCoaches(params: {
skill?: string
sort?: string
time?: string
} = {}): Promise<CoachCard[]> {
if (USE_REAL_API) {
const data = await request({
url: '/api/xcx/board/coaches',
method: 'GET',
data: params,
needAuth: true,
})
return data.items
}
await delay()
return mockCoaches
}
// CHANGE 2026-03-19 | RNS1.3 T13: 增加 page/pageSize 参数支持分页
/** 客户看板 */
export async function fetchBoardCustomers(params: {
dimension?: string
project?: string
page?: number
pageSize?: number
} = {}): Promise<CustomerCard[]> {
if (USE_REAL_API) {
const data = await request({
url: '/api/xcx/board/customers',
method: 'GET',
data: params,
needAuth: true,
})
return data.items
}
await delay()
return mockCustomers
}
// CHANGE 2026-03-19 | RNS1.3 T14: 扩展参数为 time/area/compare
/** 财务看板 */
export async function fetchBoardFinance(params: {
time?: string
area?: string
compare?: number
} = {}): Promise<BoardFinanceData> {
if (USE_REAL_API) {
return request({
url: '/api/xcx/board/finance',
method: 'GET',
data: params,
needAuth: true,
})
}
await delay()
return mockBoardFinance
}
// ============================================
// 助教模块
// ============================================
/** 助教详情 */
export async function fetchCoachDetail(coachId: string): Promise<CoachCard | null> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/coaches/${coachId}`,
method: 'GET',
needAuth: true,
})
}
await delay()
return mockCoaches.find(c => c.id === coachId) || mockCoaches[0] || null
}
// ============================================
// 对话模块
// ============================================
/** 对话历史列表 */
export async function fetchChatHistory(params: {
page?: number
pageSize?: number
} = {}): Promise<{ items: ChatHistoryItem[]; total: number; hasMore: boolean }> {
if (USE_REAL_API) {
return request({
url: '/api/xcx/chat/history',
method: 'GET',
data: params,
needAuth: true,
})
}
await delay()
return { items: mockChatHistory, total: mockChatHistory.length, hasMore: false }
}
/** 对话消息列表 */
export async function fetchChatMessages(chatId: string, params: {
page?: number
pageSize?: number
} = {}): Promise<{ messages: ChatMessage[]; total: number; hasMore: boolean }> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/chat/${chatId}/messages`,
method: 'GET',
data: params,
needAuth: true,
})
}
await delay()
return { messages: mockChatMessages, total: mockChatMessages.length, hasMore: false }
}
/** 发送消息 */
export async function sendChatMessage(chatId: string, content: string): Promise<{
userMessage: { id: string; content: string; createdAt: string }
aiReply: { id: string; content: string; createdAt: string }
}> {
if (USE_REAL_API) {
return request({
url: `/api/xcx/chat/${chatId}/messages`,
method: 'POST',
data: { content },
needAuth: true,
})
}
await delay(800)
return {
userMessage: { id: `msg-${Date.now()}`, content, createdAt: new Date().toISOString() },
aiReply: {
id: `msg-${Date.now() + 1}`,
content: '这是 AI 助手的模拟回复,联调后将替换为真实响应。',
createdAt: new Date().toISOString(),
},
}
}
// ============================================
// 配置模块
// ============================================
/** 技能类型列表REQ-1 */
export async function fetchSkillTypes(): Promise<Array<{ value: string; text: string; icon?: string }>> {
if (USE_REAL_API) {
const data = await request({
url: '/api/xcx/config/skill-types',
method: 'GET',
needAuth: true,
})
return data.skills
}
// 联调前返回硬编码值,与 board-coach 页面 SKILL_OPTIONS 一致
return [
{ value: '', text: '全部' },
{ value: 'chinese', text: '中🎱' },
{ value: 'snooker', text: '🎯斯诺克' },
{ value: 'group', text: '小组课' },
{ value: 'tip', text: '打赏课' },
]
}