chore: initial baseline with P0-safety .gitignore
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 坐席 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与坐席相关的所有 HTTP 请求
|
||||
// 对应后端 API:/api/agents
|
||||
// 包括:登录、获取当前坐席、更新状态、获取坐席列表
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Schema 保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 坐席对象(对应后端 AgentResponse) */
|
||||
export interface Agent {
|
||||
/** 坐席ID */
|
||||
id: string
|
||||
/** 企微用户ID */
|
||||
user_id: string
|
||||
/** 坐席姓名 */
|
||||
name: string
|
||||
/** 坐席状态: online/offline/busy */
|
||||
status: string
|
||||
/** 当前服务会话数 */
|
||||
current_load: number
|
||||
/** 最大同时服务数 */
|
||||
max_load: number
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 更新时间 */
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/** 登录响应数据(包含坐席信息和 token) */
|
||||
export interface LoginData extends Agent {
|
||||
/** 认证 token(存入 localStorage,后续请求自动携带) */
|
||||
token: string
|
||||
}
|
||||
|
||||
/** 坐席列表响应 */
|
||||
export interface AgentListData {
|
||||
/** 坐席列表 */
|
||||
items: Agent[]
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 坐席登录
|
||||
* 第一步使用简单的用户名登录(无密码验证)
|
||||
* admin 角色需要 OTP 二次验证
|
||||
* 登录成功后 token 存入 localStorage,后续请求自动携带
|
||||
*
|
||||
* @param userId - 企微用户ID
|
||||
* @param name - 坐席姓名
|
||||
* @param otpCode - OTP 动态码(admin 角色必填)
|
||||
* @returns 坐席信息和 token
|
||||
*/
|
||||
export async function login(userId: string, name: string, otpCode?: string): Promise<LoginData> {
|
||||
const response: AxiosResponse = await apiClient.post('/agents/login', {
|
||||
user_id: userId,
|
||||
name: name,
|
||||
otp_code: otpCode || undefined,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前坐席信息
|
||||
* 需要在请求头中携带有效的 token
|
||||
*
|
||||
* @returns 当前坐席信息
|
||||
*/
|
||||
export async function getCurrentAgent(): Promise<Agent> {
|
||||
const response: AxiosResponse = await apiClient.get('/agents/me')
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新坐席状态
|
||||
* 坐席可以切换为 online/busy/offline
|
||||
*
|
||||
* @param status - 新的坐席状态: online/busy/offline
|
||||
* @returns 更新后的坐席信息
|
||||
*/
|
||||
export async function updateAgentStatus(status: string): Promise<Agent> {
|
||||
const response: AxiosResponse = await apiClient.put('/agents/me/status', {
|
||||
status,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取坐席列表
|
||||
* 用于转接选择时展示可用的坐席列表
|
||||
*
|
||||
* @param status - 按状态过滤(可选): online/busy/offline
|
||||
* @returns 坐席列表
|
||||
*/
|
||||
export async function getAgents(status?: string): Promise<AgentListData> {
|
||||
const params: Record<string, string> = {}
|
||||
if (status) {
|
||||
params.status = status
|
||||
}
|
||||
const response: AxiosResponse = await apiClient.get('/agents', { params })
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// OTP 双因素认证
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** OTP 绑定响应 */
|
||||
export interface OtpBindData {
|
||||
/** 二维码图片(base64) */
|
||||
qr_code: string
|
||||
/** 密钥(手动输入用) */
|
||||
secret: string
|
||||
}
|
||||
|
||||
/** OTP 验证响应 */
|
||||
export interface OtpVerifyData {
|
||||
/** 是否已启用 */
|
||||
otp_enabled: boolean
|
||||
/** 消息 */
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定 OTP
|
||||
* 为当前坐席生成 OTP 密钥和二维码
|
||||
* 返回二维码(base64)和密钥供手动输入
|
||||
*
|
||||
* @returns OTP 绑定信息(二维码和密钥)
|
||||
*/
|
||||
export async function bindOtp(): Promise<OtpBindData> {
|
||||
const response: AxiosResponse = await apiClient.post('/agents/otp-bind')
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并启用 OTP
|
||||
* 用户输入 OTP 码验证成功后,启用 OTP
|
||||
*
|
||||
* @param userId - 坐席ID
|
||||
* @param otpCode - OTP 动态码
|
||||
* @returns 验证结果
|
||||
*/
|
||||
export async function verifyOtp(userId: string, otpCode: string): Promise<OtpVerifyData> {
|
||||
const response: AxiosResponse = await apiClient.post('/agents/otp-verify', {
|
||||
user_id: userId,
|
||||
otp_code: otpCode,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑 OTP
|
||||
* 解绑后 otp_secret 和 otp_enabled 都清空
|
||||
*
|
||||
* @returns 解绑结果
|
||||
*/
|
||||
export async function unbindOtp(): Promise<{ message: string }> {
|
||||
const response: AxiosResponse = await apiClient.post('/agents/otp-unbind')
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 会话 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与会话相关的所有 HTTP 请求
|
||||
// 对应后端 API:/api/conversations
|
||||
// 包括:获取会话列表、会话详情、接单、结单、置顶、代办、转接
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Schema 保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 会话标签集合(对应后端 ConversationTags) */
|
||||
export interface ConversationTags {
|
||||
/** 招手标记(员工说"转人工"或点击敲桌子按钮) */
|
||||
hand_raise: boolean
|
||||
/** 需介入标记(追问超过N轮) */
|
||||
need_intervene: boolean
|
||||
/** 情绪标记: neutral/worried/angry/urgent */
|
||||
emotion: string
|
||||
/** 触发情绪标记的关键词列表 */
|
||||
emotion_keywords: string[]
|
||||
/** 追问轮次计数 */
|
||||
repeat_count: number
|
||||
}
|
||||
|
||||
/** 会话对象(对应后端 ConversationResponse) */
|
||||
export interface Conversation {
|
||||
/** 会话ID */
|
||||
id: string
|
||||
/** 企微员工UserID */
|
||||
employee_id: string
|
||||
/** 员工姓名 */
|
||||
employee_name: string
|
||||
/** 部门 */
|
||||
department: string
|
||||
/** 岗位 */
|
||||
position: string
|
||||
/** 等级 */
|
||||
level: string
|
||||
/** 会话状态: ai_handling/queued/serving/resolved */
|
||||
status: string
|
||||
/** VIP标记 */
|
||||
is_vip: boolean
|
||||
/** 置顶标记 */
|
||||
is_pinned: boolean
|
||||
/** 代办标记 */
|
||||
is_todo: boolean
|
||||
/** 紧急度评分(1-5) */
|
||||
urgency_score: number
|
||||
/** 标签集合 */
|
||||
tags: ConversationTags
|
||||
/** 分配的坐席ID */
|
||||
assigned_agent_id: string | null
|
||||
/** 最后消息时间 */
|
||||
last_message_at: string | null
|
||||
/** 最后消息摘要 */
|
||||
last_message_summary: string
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 更新时间 */
|
||||
updated_at: string
|
||||
/** 是否为当前坐席的会话 */
|
||||
is_mine: boolean
|
||||
/** 分配的坐席姓名(其他坐席会话显示用) */
|
||||
assigned_agent_name: string | null
|
||||
/** 是否可以接手(其他坐席已接单的会话为 True) */
|
||||
can_grab: boolean
|
||||
/** 协作坐席ID列表 */
|
||||
collaborating_agent_ids: string[]
|
||||
/** 协作坐席姓名映射(agent_id → name) */
|
||||
collaborating_agent_names: Record<string, string>
|
||||
/** 是否为协作坐席(非主责) */
|
||||
is_collaborator: boolean
|
||||
/** 影响范围(受影响人数,0=未评估) */
|
||||
impact_scope: number
|
||||
/** 阻断性标记(问题是否阻断员工正常工作流程) */
|
||||
is_blocking: boolean
|
||||
/** 情绪状态(normal/worried/angry/urgent) */
|
||||
emotion_state: string
|
||||
/** 被邀请参与会话的人员列表(邀请功能 P0-09~P0-11) */
|
||||
participants: ParticipantInfo[]
|
||||
}
|
||||
|
||||
/** 参与者信息(邀请功能) */
|
||||
export interface ParticipantInfo {
|
||||
/** 企微员工UserID 或部门ID */
|
||||
id: string
|
||||
/** 姓名 或 部门名称 */
|
||||
name: string
|
||||
/** 部门(仅员工类型有) */
|
||||
department: string
|
||||
/** 类型: employee(个人)或 department(部门) */
|
||||
type: 'employee' | 'department'
|
||||
/** 头像URL(从企微通讯录或employees表获取,无头像时为空字符串) */
|
||||
avatar?: string
|
||||
/** 是否已加入(通过链接加入后为 true) */
|
||||
joined?: boolean
|
||||
/** 加入时间(ISO 格式字符串) */
|
||||
joined_at?: string
|
||||
}
|
||||
|
||||
/** 会话列表响应(对应后端 ConversationListResponse) */
|
||||
export interface ConversationListData {
|
||||
/** 会话列表 */
|
||||
items: Conversation[]
|
||||
/** 总数 */
|
||||
total: number
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取坐席会话列表
|
||||
* 支持按状态和坐席ID过滤,按紧急度排序
|
||||
*
|
||||
* @param params - 查询参数
|
||||
* @param params.status - 按状态过滤(可选)
|
||||
* @param params.agent_id - 按坐席ID过滤(可选)
|
||||
* @param params.page - 页码(从1开始)
|
||||
* @param params.page_size - 每页数量
|
||||
* @returns 会话列表数据
|
||||
*/
|
||||
export async function getConversations(params?: {
|
||||
status?: string
|
||||
agent_id?: string
|
||||
page?: number
|
||||
page_size?: number
|
||||
}): Promise<ConversationListData> {
|
||||
const response: AxiosResponse = await apiClient.get('/conversations', { params })
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话详情
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 会话详情
|
||||
*/
|
||||
export async function getConversation(conversationId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.get(`/conversations/${conversationId}`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 坐席接单(接入会话)
|
||||
* 将会话状态从 queued 改为 serving
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param agentId - 接单的坐席ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function assignConversation(conversationId: string, agentId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/assign`, {
|
||||
agent_id: agentId,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 结单
|
||||
* 将会话状态改为 resolved
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function resolveConversation(conversationId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/resolve`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换置顶状态
|
||||
* 每次调用切换:置顶→取消置顶,取消置顶→置顶
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function togglePin(conversationId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/pin`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换代办状态
|
||||
* 每次调用切换:代办→取消代办,取消代办→代办
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function toggleTodo(conversationId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/todo`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 转接会话到另一个坐席
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param targetAgentId - 目标坐席ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function transferConversation(conversationId: string, targetAgentId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/transfer`, {
|
||||
agent_id: targetAgentId,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 接手其他坐席的会话(抢单)
|
||||
* 接手后原坐席自动释放,会话变为当前坐席的
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 接手后的会话信息
|
||||
*/
|
||||
export async function grabConversation(conversationId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/grab`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 摇人 — 邀请坐席加入协作
|
||||
* 邀请后坐席B将出现在协作列表中,可查看和回复但不能结单
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param agentId - 被邀请的坐席ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function inviteCollaborator(
|
||||
conversationId: string,
|
||||
agentId: string
|
||||
): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/invite`,
|
||||
{ agent_id: agentId }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出协作
|
||||
* 坐席从协作列表中移除
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function leaveCollaboration(conversationId: string): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(`/conversations/${conversationId}/leave`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 邀请功能 API(P0-09~P0-11)
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 邀请参与者请求参数 */
|
||||
export interface InviteParticipantParams {
|
||||
/** 被邀请人列表 */
|
||||
participants: Array<{
|
||||
id: string
|
||||
name: string
|
||||
department?: string
|
||||
type: 'employee' | 'department'
|
||||
}>
|
||||
/** 历史消息共享模式: recent10/all/none */
|
||||
history_mode?: 'recent10' | 'all' | 'none'
|
||||
}
|
||||
|
||||
/**
|
||||
* 邀请员工/部门加入会话(P0-09)
|
||||
* 向被邀请人发送企微卡片通知,含「加入会话」按钮
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param params - 邀请参数(含被邀请人列表和历史共享模式)
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function inviteParticipant(
|
||||
conversationId: string,
|
||||
params: InviteParticipantParams
|
||||
): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/invite-participant`,
|
||||
params
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 被邀请人加入会话(P0-10)
|
||||
* 点击企微卡片链接后调用,更新 joined 状态
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param employeeId - 加入的员工企微UserID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function joinConversation(
|
||||
conversationId: string,
|
||||
employeeId: string
|
||||
): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/join`,
|
||||
{ employee_id: employeeId }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除参与者(P0-11)
|
||||
* 只有主责坐席可以移除
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param userId - 被移除的员工UserID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function removeParticipant(
|
||||
conversationId: string,
|
||||
userId: string
|
||||
): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.delete(
|
||||
`/conversations/${conversationId}/participants/${userId}`
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 参与者主动退出会话
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param employeeId - 退出的员工企微UserID
|
||||
* @returns 更新后的会话信息
|
||||
*/
|
||||
export async function leaveAsParticipant(
|
||||
conversationId: string,
|
||||
employeeId: string
|
||||
): Promise<Conversation> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/leave-participant`,
|
||||
{ employee_id: employeeId }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 坐席工作台 Axios 实例与拦截器
|
||||
// =============================================================================
|
||||
// 说明:创建 Axios 实例,配置:
|
||||
// 1. 请求基础 URL
|
||||
// 2. 请求拦截器(添加认证头等)
|
||||
// 3. 响应拦截器(统一错误处理)
|
||||
// =============================================================================
|
||||
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
|
||||
// ElementPlus 消息提示
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 创建 Axios 实例
|
||||
// --------------------------------------------------------------------------
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
// 基础 URL:所有请求会自动加上这个前缀
|
||||
// 开发环境通过 Vite proxy 转发到后端
|
||||
baseURL: '/api',
|
||||
// 请求超时时间(20秒,原10秒)
|
||||
// 原因:图片/文件上传、AI消息处理等场景后端处理需要更多时间
|
||||
// 修复截图发送超时Bug
|
||||
timeout: 20000,
|
||||
// 默认请求头
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 请求拦截器
|
||||
// --------------------------------------------------------------------------
|
||||
// 在每个请求发送前执行,用于添加认证信息等
|
||||
apiClient.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
// 从 localStorage 获取坐席 token,添加到请求头
|
||||
const token = localStorage.getItem('agent_token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
// 请求配置错误时直接返回
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 响应拦截器
|
||||
// --------------------------------------------------------------------------
|
||||
// 在每个响应返回后执行,用于统一处理错误
|
||||
apiClient.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
// 从响应中提取业务数据
|
||||
const res = response.data
|
||||
|
||||
// 统一响应格式:{code: 0, data: {}, message: "success"}
|
||||
// code === 0 表示业务成功
|
||||
if (res.code !== 0) {
|
||||
// 业务错误:显示错误消息
|
||||
ElMessage.error(res.message || '请求失败')
|
||||
|
||||
// 特殊错误码处理
|
||||
if (res.code === 1002) {
|
||||
// 未授权:跳转到登录页
|
||||
// 动态导入避免循环依赖
|
||||
import('@/router').then(router => {
|
||||
router.default.push('/login')
|
||||
})
|
||||
}
|
||||
|
||||
// 返回 rejected Promise,让调用方的 catch 能捕获
|
||||
return Promise.reject(new Error(res.message || '请求失败'))
|
||||
}
|
||||
|
||||
// 业务成功:返回完整响应(调用方从 response.data.data 获取业务数据)
|
||||
return response
|
||||
},
|
||||
(error) => {
|
||||
// 网络错误或服务器错误(HTTP 状态码非 2xx)
|
||||
let message = '网络异常,请稍后重试'
|
||||
|
||||
if (error.response) {
|
||||
// 服务器返回了错误状态码
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
message = '未授权,请重新登录'
|
||||
break
|
||||
case 403:
|
||||
message = '拒绝访问'
|
||||
break
|
||||
case 404:
|
||||
message = '请求的资源不存在'
|
||||
break
|
||||
case 500:
|
||||
message = '服务器内部错误'
|
||||
break
|
||||
default:
|
||||
message = `请求失败 (${error.response.status})`
|
||||
}
|
||||
} else if (error.code === 'ECONNABORTED') {
|
||||
// 请求超时
|
||||
message = '请求超时,请稍后重试'
|
||||
}
|
||||
|
||||
// 显示错误提示
|
||||
ElMessage.error(message)
|
||||
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 导出 Axios 实例,供 API 模块使用
|
||||
export default apiClient
|
||||
@@ -0,0 +1,232 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 消息 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与消息相关的所有 HTTP 请求
|
||||
// 对应后端 API:/api/conversations/{id}/messages
|
||||
// 包括:获取消息列表、发送消息、轮询新消息
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Schema 保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 消息对象(对应后端 MessageResponse) */
|
||||
export interface Message {
|
||||
/** 消息ID */
|
||||
id: string
|
||||
/** 所属会话ID */
|
||||
conversation_id: string
|
||||
/** 发送者类型: employee/agent/ai/system */
|
||||
sender_type: string
|
||||
/** 发送者ID */
|
||||
sender_id: string
|
||||
/** 发送者姓名 */
|
||||
sender_name: string
|
||||
/** 消息内容 */
|
||||
content: string
|
||||
/** 消息类型: text/image/voice/video/file/location */
|
||||
msg_type: string
|
||||
/** 是否为AI建议 */
|
||||
ai_suggestion: boolean
|
||||
/** 是否已读 */
|
||||
is_read: boolean
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 企微媒体文件ID(非文本消息时使用) */
|
||||
media_id?: string
|
||||
/** 媒体文件访问URL(M1 新增:本地存储的文件URL) */
|
||||
media_url?: string
|
||||
/** 文件名(文件消息时使用) */
|
||||
file_name?: string
|
||||
/** 文件大小(字节) */
|
||||
file_size?: number
|
||||
/** 扩展元数据(pic_url/format/location 等) */
|
||||
extra_data?: Record<string, any>
|
||||
/** 引用回复:被回复的消息ID(M1 新增) */
|
||||
reply_to_id?: string
|
||||
/** 消息状态:sending/sent/delivered/read(M2 新增) */
|
||||
status?: string
|
||||
/** 可撤回截止时间(M2 新增) */
|
||||
recallable_until?: string
|
||||
}
|
||||
|
||||
/** 消息列表响应(对应后端 MessageListResponse) */
|
||||
export interface MessageListData {
|
||||
/** 消息列表 */
|
||||
items: Message[]
|
||||
/** 是否还有更多历史消息 */
|
||||
has_more: boolean
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取会话消息列表(分页)
|
||||
* 默认返回最新的 limit 条消息
|
||||
* 支持向上加载历史消息(通过 before 参数指定消息ID)
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param params - 查询参数
|
||||
* @param params.limit - 每页消息数量(默认50)
|
||||
* @param params.before - 加载此消息ID之前的消息(向上翻页)
|
||||
* @returns 消息列表数据
|
||||
*/
|
||||
export async function getMessages(
|
||||
conversationId: string,
|
||||
params?: {
|
||||
limit?: number
|
||||
before?: string
|
||||
}
|
||||
): Promise<MessageListData> {
|
||||
const response: AxiosResponse = await apiClient.get(
|
||||
`/conversations/${conversationId}/messages`,
|
||||
{ params }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 坐席发送消息
|
||||
* 消息同时存入数据库和调用企微API发送给员工
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param content - 消息内容
|
||||
* @param msgType - 消息类型(默认 text)
|
||||
* @param options - 可选的文件/图片消息参数
|
||||
* @returns 发送的消息对象
|
||||
*/
|
||||
export async function sendMessage(
|
||||
conversationId: string,
|
||||
content: string,
|
||||
msgType: string = 'text',
|
||||
options?: {
|
||||
media_url?: string
|
||||
file_name?: string
|
||||
file_size?: number
|
||||
reply_to_id?: string
|
||||
}
|
||||
): Promise<Message> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/messages`,
|
||||
{
|
||||
content,
|
||||
msg_type: msgType,
|
||||
...options,
|
||||
},
|
||||
{
|
||||
// 图片/文件消息后端处理可能较慢(存储 + 企微API),增加超时到30秒
|
||||
// 修复截图发送超时Bug:apiClient默认10s不够
|
||||
timeout: 30000,
|
||||
}
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 坐席轮询新消息
|
||||
* 前端每 3-5 秒调用一次,获取上次轮询后的新消息
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param afterMessageId - 上次轮询的最后一消息ID(返回此之后的消息)
|
||||
* @returns 新消息列表数据
|
||||
*/
|
||||
export async function pollMessages(
|
||||
conversationId: string,
|
||||
afterMessageId?: string
|
||||
): Promise<MessageListData> {
|
||||
const params: Record<string, string> = {}
|
||||
if (afterMessageId) {
|
||||
params.after_message_id = afterMessageId
|
||||
}
|
||||
const response: AxiosResponse = await apiClient.get(
|
||||
`/conversations/${conversationId}/messages/poll`,
|
||||
{ params }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回消息(2分钟内)
|
||||
*
|
||||
* @param messageId - 消息ID
|
||||
* @returns 撤回结果
|
||||
*/
|
||||
export async function recallMessage(messageId: string): Promise<any> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/messages/${messageId}/recall`
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
*
|
||||
* @param messageId - 消息ID
|
||||
* @returns 删除结果
|
||||
*/
|
||||
export async function deleteMessage(messageId: string): Promise<any> {
|
||||
const response: AxiosResponse = await apiClient.delete(
|
||||
`/messages/${messageId}`
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记会话已读
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 标记结果
|
||||
*/
|
||||
export async function markConversationRead(conversationId: string): Promise<any> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/mark-read`
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*
|
||||
* @param file - 图片文件
|
||||
* @returns 上传结果
|
||||
*/
|
||||
export async function uploadImage(file: File): Promise<{
|
||||
url: string
|
||||
filename: string
|
||||
file_size: number
|
||||
}> {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
'/messages/image',
|
||||
formData,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' } }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file - 文件
|
||||
* @returns 上传结果
|
||||
*/
|
||||
export async function uploadMessageFile(file: File): Promise<{
|
||||
url: string
|
||||
filename: string
|
||||
file_size: number
|
||||
}> {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
'/messages/file',
|
||||
formData,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' } }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 快速回复模板 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与快速回复模板相关的所有 HTTP 请求
|
||||
// 对应后端 API:/api/quick-replies
|
||||
// 包括:获取模板列表、创建模板、更新模板、删除模板
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Schema 保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 快速回复模板对象(对应后端 QuickReplyResponse) */
|
||||
export interface QuickReply {
|
||||
/** 模板ID */
|
||||
id: string
|
||||
/** 分类:账号/网络/软件/硬件/通用 */
|
||||
category: string
|
||||
/** 模板标题 */
|
||||
title: string
|
||||
/** 模板内容(支持 {employee_name} 等变量) */
|
||||
content: string
|
||||
/** 可用变量列表 */
|
||||
variables: string[]
|
||||
/** 排序权重 */
|
||||
sort_order: number
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 更新时间 */
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/** 快速回复列表响应 */
|
||||
export interface QuickReplyListData {
|
||||
/** 模板列表 */
|
||||
items: QuickReply[]
|
||||
}
|
||||
|
||||
/** 创建模板参数 */
|
||||
export interface QuickReplyCreateParams {
|
||||
/** 分类(默认"通用") */
|
||||
category?: string
|
||||
/** 模板标题 */
|
||||
title: string
|
||||
/** 模板内容 */
|
||||
content: string
|
||||
/** 可用变量列表 */
|
||||
variables?: string[]
|
||||
/** 排序权重 */
|
||||
sort_order?: number
|
||||
}
|
||||
|
||||
/** 更新模板参数(所有字段可选) */
|
||||
export interface QuickReplyUpdateParams {
|
||||
/** 分类 */
|
||||
category?: string
|
||||
/** 模板标题 */
|
||||
title?: string
|
||||
/** 模板内容 */
|
||||
content?: string
|
||||
/** 可用变量列表 */
|
||||
variables?: string[]
|
||||
/** 排序权重 */
|
||||
sort_order?: number
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取快速回复模板列表
|
||||
* 支持按分类过滤,按 sort_order 排序
|
||||
*
|
||||
* @param category - 按分类过滤(可选)
|
||||
* @returns 模板列表
|
||||
*/
|
||||
export async function getQuickReplies(category?: string): Promise<QuickReplyListData> {
|
||||
const params: Record<string, string> = {}
|
||||
if (category) {
|
||||
params.category = category
|
||||
}
|
||||
const response: AxiosResponse = await apiClient.get('/quick-replies', { params })
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建快速回复模板
|
||||
*
|
||||
* @param data - 创建参数
|
||||
* @returns 创建的模板
|
||||
*/
|
||||
export async function createQuickReply(data: QuickReplyCreateParams): Promise<QuickReply> {
|
||||
const response: AxiosResponse = await apiClient.post('/quick-replies', data)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新快速回复模板
|
||||
* 只更新传入的字段(部分更新)
|
||||
*
|
||||
* @param templateId - 模板ID
|
||||
* @param data - 更新参数
|
||||
* @returns 更新后的模板
|
||||
*/
|
||||
export async function updateQuickReply(templateId: string, data: QuickReplyUpdateParams): Promise<QuickReply> {
|
||||
const response: AxiosResponse = await apiClient.put(`/quick-replies/${templateId}`, data)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除快速回复模板
|
||||
*
|
||||
* @param templateId - 模板ID
|
||||
* @returns 无数据
|
||||
*/
|
||||
export async function deleteQuickReply(templateId: string): Promise<void> {
|
||||
await apiClient.delete(`/quick-replies/${templateId}`)
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 坐席备注 API(与快速回复在同一模块方便管理)
|
||||
// 对应后端 API:/api/agent-notes
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 备注对象 */
|
||||
export interface AgentNote {
|
||||
/** 备注ID */
|
||||
id: string
|
||||
/** 所属会话ID */
|
||||
conversation_id: string
|
||||
/** 坐席ID */
|
||||
agent_id: string
|
||||
/** 备注内容 */
|
||||
content: string
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 更新时间 */
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/** 备注列表响应 */
|
||||
export interface AgentNoteListData {
|
||||
/** 备注列表 */
|
||||
items: AgentNote[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取员工的所有备注
|
||||
* 通过员工ID查找其所有会话的备注
|
||||
*
|
||||
* @param employeeId - 员工企微 UserID
|
||||
* @returns 备注列表
|
||||
*/
|
||||
export async function getAgentNotes(employeeId: string): Promise<AgentNoteListData> {
|
||||
const response: AxiosResponse = await apiClient.get(`/agent-notes/${employeeId}`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加坐席备注
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @param agentId - 坐席ID
|
||||
* @param content - 备注内容
|
||||
* @returns 创建的备注
|
||||
*/
|
||||
export async function createAgentNote(
|
||||
conversationId: string,
|
||||
agentId: string,
|
||||
content: string
|
||||
): Promise<AgentNote> {
|
||||
const response: AxiosResponse = await apiClient.post('/agent-notes', {
|
||||
conversation_id: conversationId,
|
||||
agent_id: agentId,
|
||||
content,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新坐席备注
|
||||
*
|
||||
* @param noteId - 备注ID
|
||||
* @param content - 新的备注内容
|
||||
* @returns 更新后的备注
|
||||
*/
|
||||
export async function updateAgentNote(noteId: string, content: string): Promise<AgentNote> {
|
||||
const response: AxiosResponse = await apiClient.put(`/agent-notes/${noteId}`, {
|
||||
content,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除坐席备注
|
||||
*
|
||||
* @param noteId - 备注ID
|
||||
*/
|
||||
export async function deleteAgentNote(noteId: string): Promise<void> {
|
||||
await apiClient.delete(`/agent-notes/${noteId}`)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 系统管理 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装系统级配置管理的 HTTP 请求
|
||||
// 对应后端 API:/api/system/emergency-mode
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 应急模式状态响应 */
|
||||
export interface EmergencyModeData {
|
||||
/** 是否启用应急模式 */
|
||||
emergency_mode: boolean
|
||||
/** 启用时的引导文案(仅开启时返回) */
|
||||
employee_service_guide?: string
|
||||
}
|
||||
|
||||
/** 切换应急模式请求 */
|
||||
export interface EmergencyModeToggle {
|
||||
/** 是否启用应急模式 */
|
||||
emergency_mode: boolean
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 查询应急模式状态
|
||||
*/
|
||||
export async function getEmergencyMode(): Promise<EmergencyModeData> {
|
||||
const response: AxiosResponse = await apiClient.get('/system/emergency-mode')
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换应急模式开关
|
||||
*
|
||||
* @param enabled - true 开启应急模式,false 关闭
|
||||
*/
|
||||
export async function toggleEmergencyMode(enabled: boolean): Promise<EmergencyModeData> {
|
||||
const response: AxiosResponse = await apiClient.put('/system/emergency-mode', {
|
||||
emergency_mode: enabled,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 待办事项 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与待办事项相关的所有 HTTP 请求
|
||||
// 对应后端 API:/api/todo-items
|
||||
// 包括:获取待办列表、获取待办详情、更新待办状态
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Schema 保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 待办事项对象(对应后端 TodoItemResponse) */
|
||||
export interface TodoItemData {
|
||||
/** 待办唯一标识 */
|
||||
id: string
|
||||
/** 待办类型: ticket/approval/device */
|
||||
type: string
|
||||
/** 待办标题 */
|
||||
title: string
|
||||
/** 优先级: urgent/high/normal */
|
||||
priority: string
|
||||
/** 详细描述(JSON) */
|
||||
description: Record<string, any>
|
||||
/** 状态: pending/processing/resolved */
|
||||
status: string
|
||||
/** 分配的坐席ID */
|
||||
assigned_agent_id: string | null
|
||||
/** 企业微信企业ID */
|
||||
corp_id: string
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 更新时间 */
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/** 待办事项列表响应 */
|
||||
export interface TodoItemListData {
|
||||
/** 待办事项列表 */
|
||||
items: TodoItemData[]
|
||||
/** 总数 */
|
||||
total: number
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取当前坐席待办列表
|
||||
*
|
||||
* @param params - 查询参数
|
||||
* @param params.status - 按状态过滤(可选)
|
||||
* @param params.priority - 按优先级过滤(可选)
|
||||
* @returns 待办事项列表数据
|
||||
*/
|
||||
export async function getTodoItems(params?: {
|
||||
status?: string
|
||||
priority?: string
|
||||
}): Promise<TodoItemListData> {
|
||||
const response: AxiosResponse = await apiClient.get('/todo-items', { params })
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待办事项详情
|
||||
*
|
||||
* @param id - 待办事项ID
|
||||
* @returns 待办事项详情
|
||||
*/
|
||||
export async function getTodoItem(id: string): Promise<TodoItemData> {
|
||||
const response: AxiosResponse = await apiClient.get(`/todo-items/${id}`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新待办事项状态
|
||||
*
|
||||
* @param id - 待办事项ID
|
||||
* @param status - 新状态(pending/processing/resolved)
|
||||
* @returns 更新后的待办事项
|
||||
*/
|
||||
export async function updateTodoStatus(id: string, status: string): Promise<TodoItemData> {
|
||||
const response: AxiosResponse = await apiClient.put(`/todo-items/${id}/status`, {
|
||||
status,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 排查模板 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与排查模板相关的所有 HTTP 请求
|
||||
// 对应后端 API:/api/troubleshooting-templates
|
||||
// 包括:获取模板列表、获取模板详情
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Schema 保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 排查步骤路径节点 */
|
||||
export interface PathStep {
|
||||
/** 步骤标题 */
|
||||
label: string
|
||||
/** 步骤状态: done / current / pending */
|
||||
status: 'done' | 'current' | 'pending'
|
||||
}
|
||||
|
||||
/** 决策树递归节点 */
|
||||
export interface FlowchartNode {
|
||||
/** 节点唯一标识 */
|
||||
id: string
|
||||
/** 节点类型: step / decision */
|
||||
type: 'step' | 'decision'
|
||||
/** 节点标签文字 */
|
||||
label: string
|
||||
/** 节点状态: done / current / pending */
|
||||
status?: 'done' | 'current' | 'pending'
|
||||
/** 子节点列表(step 类型) */
|
||||
children?: FlowchartNode[]
|
||||
/** "是" 分支(decision 类型) */
|
||||
yes_branch?: FlowchartNode
|
||||
/** "否" 分支(decision 类型) */
|
||||
no_branch?: FlowchartNode
|
||||
}
|
||||
|
||||
/** 排查模板对象(对应后端 TroubleshootingTemplateResponse) */
|
||||
export interface TroubleshootingTemplate {
|
||||
/** 模板唯一标识 */
|
||||
id: string
|
||||
/** 模板名称 */
|
||||
name: string
|
||||
/** 分类: vpn/email/system/account */
|
||||
category: string
|
||||
/** 排障步骤路径 */
|
||||
path_steps: PathStep[]
|
||||
/** 流程图定义 */
|
||||
flowchart: FlowchartNode
|
||||
/** 是否启用 */
|
||||
is_active: boolean
|
||||
/** 创建时间 */
|
||||
created_at: string
|
||||
/** 更新时间 */
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/** 排查模板列表响应 */
|
||||
export interface TroubleshootingTemplateListData {
|
||||
/** 模板列表 */
|
||||
items: TroubleshootingTemplate[]
|
||||
/** 总数 */
|
||||
total: number
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取排查模板列表
|
||||
* 支持按分类过滤
|
||||
*
|
||||
* @param params - 查询参数
|
||||
* @param params.category - 按分类过滤(可选)
|
||||
* @returns 排查模板列表数据
|
||||
*/
|
||||
export async function getTroubleshootingTemplates(params?: {
|
||||
category?: string
|
||||
}): Promise<TroubleshootingTemplateListData> {
|
||||
const response: AxiosResponse = await apiClient.get('/troubleshooting-templates', { params })
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取排查模板详情
|
||||
*
|
||||
* @param id - 模板ID
|
||||
* @returns 排查模板详情
|
||||
*/
|
||||
export async function getTroubleshootingTemplate(id: string): Promise<TroubleshootingTemplate> {
|
||||
const response: AxiosResponse = await apiClient.get(`/troubleshooting-templates/${id}`)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新员工 IT 技能等级
|
||||
*
|
||||
* @param employeeId - 员工记录ID
|
||||
* @param itLevel - 新的IT技能等级
|
||||
* @returns 更新后的员工信息
|
||||
*/
|
||||
export async function updateEmployeeItLevel(
|
||||
employeeId: string,
|
||||
itLevel: string
|
||||
): Promise<Record<string, any>> {
|
||||
const response: AxiosResponse = await apiClient.put(
|
||||
`/employees/${employeeId}/it-level`,
|
||||
{ it_level: itLevel, source: 'manual' }
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — 文件上传 API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装文件上传相关的 HTTP 请求
|
||||
// 对应后端 API:/api/upload
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 上传响应数据 */
|
||||
export interface UploadResponse {
|
||||
/** 文件访问URL(前端用于展示/下载) */
|
||||
url: string
|
||||
/** 原始文件名(显示用) */
|
||||
filename: string
|
||||
/** 文件大小(字节) */
|
||||
file_size: number
|
||||
/** 消息类型(image 或 file) */
|
||||
msg_type: 'image' | 'file'
|
||||
/** 文件扩展名 */
|
||||
extension: string
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 上传文件到服务器
|
||||
*
|
||||
* 处理流程:
|
||||
* 1. 前端选择文件(点击上传 / 粘贴图片 / 拖拽文件)
|
||||
* 2. 调用此函数将文件上传到后端
|
||||
* 3. 后端返回文件URL和元数据
|
||||
* 4. 前端调用 sendMessage 将文件URL作为消息发送
|
||||
*
|
||||
* @param file - 要上传的文件对象(File / Blob)
|
||||
* @returns 上传响应数据(包含文件URL、文件名等)
|
||||
*/
|
||||
export async function uploadFile(file: File | Blob): Promise<UploadResponse> {
|
||||
// 构建 FormData(multipart/form-data 格式,后端用 UploadFile 接收)
|
||||
const formData = new FormData()
|
||||
|
||||
if (file instanceof File) {
|
||||
// File 对象自带 name 属性,直接 append
|
||||
formData.append('file', file)
|
||||
} else {
|
||||
// Blob 对象没有 name 属性,必须手动指定文件名
|
||||
// 不指定文件名时,浏览器默认用 "blob" 作为文件名,后端无法识别扩展名
|
||||
const fileName = `paste-image-${Date.now()}.png`
|
||||
formData.append('file', file, fileName)
|
||||
}
|
||||
|
||||
const response: AxiosResponse = await apiClient.post('/upload', formData, {
|
||||
// 必须显式删除 Content-Type,让浏览器自动生成带 boundary 的 multipart/form-data
|
||||
// 原因:apiClient 实例默认设置了 'Content-Type': 'application/json'
|
||||
// 如果不覆盖,Axios 会保留 application/json,后端无法解析 FormData 中的 file 字段
|
||||
headers: {
|
||||
'Content-Type': undefined,
|
||||
},
|
||||
timeout: 60000,
|
||||
})
|
||||
return response.data.data
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// =============================================================================
|
||||
// 企微IT智能服务台 — AI Wingman API 调用模块
|
||||
// =============================================================================
|
||||
// 说明:封装与 AI Wingman(坐席智能副驾驶)相关的 HTTP 请求
|
||||
// 对应后端 API:/api/conversations/{id}/wingman
|
||||
// 包括:生成草稿回复、生成会话摘要、生成标签建议
|
||||
// =============================================================================
|
||||
|
||||
import apiClient from './index'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TypeScript 类型定义 — 与后端 Wingman API 响应格式保持一致
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/** 草稿回复结果 */
|
||||
export interface DraftResult {
|
||||
/** 草稿内容 */
|
||||
content: string
|
||||
/** 置信度(0-1) */
|
||||
confidence: number
|
||||
/** 生成推理说明 */
|
||||
reasoning: string
|
||||
}
|
||||
|
||||
/** 会话摘要结果 */
|
||||
export interface SummaryResult {
|
||||
/** 问题描述 */
|
||||
problem: string
|
||||
/** 原因分析 */
|
||||
cause: string
|
||||
/** 解决方案 */
|
||||
solution: string
|
||||
}
|
||||
|
||||
/** 标签建议结果 */
|
||||
export interface TagsResult {
|
||||
/** 建议标签列表 */
|
||||
suggested_tags: string[]
|
||||
/** 分类 */
|
||||
category: string
|
||||
/** 优先级: low/medium/high */
|
||||
priority: string
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// API 函数
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 生成 AI 草稿回复
|
||||
* 基于当前会话的消息历史,生成坐席可以采纳的草稿回复
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 草稿回复结果
|
||||
*/
|
||||
export async function generateDraft(conversationId: string): Promise<DraftResult> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/wingman/draft`
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成会话自动摘要
|
||||
* 基于完整对话生成结构化摘要,包含问题、原因、解决方案
|
||||
* 通常在结单时调用
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 会话摘要结果
|
||||
*/
|
||||
export async function generateSummary(conversationId: string): Promise<SummaryResult> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/wingman/summary`
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成自动标签建议
|
||||
* 基于对话内容建议标签分类
|
||||
*
|
||||
* @param conversationId - 会话ID
|
||||
* @returns 标签建议结果
|
||||
*/
|
||||
export async function suggestTags(conversationId: string): Promise<TagsResult> {
|
||||
const response: AxiosResponse = await apiClient.post(
|
||||
`/conversations/${conversationId}/wingman/tags`
|
||||
)
|
||||
return response.data.data
|
||||
}
|
||||
Reference in New Issue
Block a user