实现 P1 客户/知识库/对话记录与渠道设置

- 客户管理接通创建、编辑、删除与详情历史会话
- 知识库接通分类/条目 CRUD,按角色控制写权限
- 对话记录增强筛选、客户名、消息与操作时间线
- 新增租户渠道 API,系统设置渠道管理可启用与复制嵌入代码
This commit is contained in:
yml2213
2026-07-15 11:18:01 +08:00
parent 9485c28abd
commit 65014d80de
8 changed files with 1232 additions and 247 deletions
+40 -12
View File
@@ -1,13 +1,14 @@
import { get, post, put, getList } from './request'
import { get, post, put, del, getList } from './request'
export interface LoginParams { username: string; password: string }
export interface LoginResult { token: string; user_id: number; tenant_id: number; nickname: string; role: string }
export interface Session {
id: number; tenant_id: number; channel_id: number; customer_id: number; agent_id: number | null
status: string; priority: string; unread_count: number; satisfaction_score: number | null
last_message?: string; last_message_at?: string | null
created_at: string; ended_at: string | null
id: number; tenant_id: number; channel_id: number; customer_id: number; agent_id: number | null
status: string; priority: string; unread_count: number; satisfaction_score: number | null
last_message?: string; last_message_at?: string | null
satisfaction_text?: string
created_at: string; ended_at: string | null
}
export interface Message {
@@ -26,11 +27,20 @@ export interface Customer {
source: string; status: string; conversation_count: number; last_contact_at: string
}
export interface KnowledgeCategory {
id: number; tenant_id: number; parent_id: number | null; name: string
}
export interface KnowledgeEntry {
id: number; title: string; content: string; status: string; usage_count: number
category_id: number; updated_at: string
}
export interface Channel {
id: number; tenant_id: number; type: string; name: string; status: string
config: string; script_code: string; channel_key: string
}
export interface Tenant {
id: number; name: string; plan_id: number; seat_count: number; expire_at: string; status: string
contact_name: string; contact_phone: string; contact_email: string
@@ -52,8 +62,8 @@ export const getSessions = (params?: { status?: string; priority?: string; page?
const search = new URLSearchParams()
if (params?.status) search.set('status', params.status)
if (params?.priority) search.set('priority', params.priority)
if (params?.page) search.set('page', String(params.page))
if (params?.pageSize) search.set('pageSize', String(params.pageSize))
if (params?.page) search.set('page', String(params.page))
if (params?.pageSize) search.set('pageSize', String(params.pageSize))
return getList<Session>(`/sessions?${search}`)
}
export const getSession = (id: number) => get<{ session: Session; messages: Message[]; events: SessionEvent[]; pending_count: number }>(`/sessions/${id}`)
@@ -68,24 +78,42 @@ export const sendSessionMessage = (id: number, content: string, type: 'text' | '
export const getAvailableAgents = () => get<AvailableAgent[]>('/agents/available')
// Customers
export const getCustomers = (params?: { search?: string; status?: string; page?: number; pageSize?: number }) => {
export const getCustomers = (params?: { search?: string; status?: string; source?: string; page?: number; pageSize?: number }) => {
const search = new URLSearchParams()
if (params?.search) search.set('search', params.search)
if (params?.status) search.set('status', params.status)
if (params?.page) search.set('page', String(params.page || 1))
if (params?.pageSize) search.set('pageSize', String(params.pageSize))
if (params?.source) search.set('source', params.source)
if (params?.page) search.set('page', String(params.page || 1))
if (params?.pageSize) search.set('pageSize', String(params.pageSize))
return getList<Customer>(`/customers?${search}`)
}
export const getCustomer = (id: number) => get<{ customer: Customer; sessions: Session[] }>(`/customers/${id}`)
export const createCustomer = (data: Partial<Customer>) => post<Customer>('/customers', data)
export const updateCustomer = (id: number, data: Partial<Customer>) => put<Customer>(`/customers/${id}`, data)
export const deleteCustomer = (id: number) => del(`/customers/${id}`)
// Knowledge
export const getKnowledgeCategories = () => get<unknown[]>('/knowledge/categories')
export const getKnowledgeEntries = (params?: { category_id?: string; search?: string; page?: number }) => {
export const getKnowledgeCategories = () => get<KnowledgeCategory[]>('/knowledge/categories')
export const createKnowledgeCategory = (data: { name: string; parent_id?: number | null }) => post<KnowledgeCategory>('/knowledge/categories', data)
export const getKnowledgeEntries = (params?: { category_id?: string; search?: string; status?: string; page?: number; pageSize?: number }) => {
const search = new URLSearchParams()
if (params?.category_id) search.set('category_id', params.category_id)
if (params?.search) search.set('search', params.search)
if (params?.status) search.set('status', params.status)
if (params?.page) search.set('page', String(params.page || 1))
if (params?.pageSize) search.set('pageSize', String(params.pageSize || 10))
return getList<KnowledgeEntry>(`/knowledge/entries?${search}`)
}
export const createKnowledgeEntry = (data: { title: string; content: string; category_id: number; status?: string }) =>
post<KnowledgeEntry>('/knowledge/entries', data)
export const updateKnowledgeEntry = (id: number, data: Partial<KnowledgeEntry>) =>
put<KnowledgeEntry>(`/knowledge/entries/${id}`, data)
export const deleteKnowledgeEntry = (id: number) => del(`/knowledge/entries/${id}`)
// Channels
export const getChannels = () => get<Channel[]>('/channels')
export const createChannel = (data: { type: string; name?: string }) => post<Channel>('/channels', data)
export const updateChannel = (id: number, data: { name?: string; status?: string }) => put<Channel>(`/channels/${id}`, data)
// Statistics
export const getKPIs = () => get<StatisticsKpis>('/statistics/kpi')