feat: chat integration, tenant admin spec, backend chat service, miniprogram updates, DEMO moved to tmp, XCX-TEST removed, migrations & docs
This commit is contained in:
452
tmp/DEMO-miniprogram/miniprogram/services/api.ts
Normal file
452
tmp/DEMO-miniprogram/miniprogram/services/api.ts
Normal file
@@ -0,0 +1,452 @@
|
||||
/**
|
||||
* 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: '打赏课' },
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user