chore: initial baseline with P0-safety .gitignore

This commit is contained in:
Simon
2026-06-14 16:49:18 +08:00
commit 63262292d7
510 changed files with 146008 additions and 0 deletions
+170
View File
@@ -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
}
+347
View File
@@ -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
}
// --------------------------------------------------------------------------
// 邀请功能 APIP0-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
}
+117
View File
@@ -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
+232
View File
@@ -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/readM2 新增) */
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秒
// 修复截图发送超时BugapiClient默认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
}
+205
View File
@@ -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}`)
}
+51
View File
@@ -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
}
+91
View File
@@ -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
}
+116
View File
@@ -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
}
+69
View File
@@ -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> {
// 构建 FormDatamultipart/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
}
+91
View File
@@ -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
}