删除target文件夹
This commit is contained in:
parent
72d84e2622
commit
3a4d8643b0
|
|
@ -0,0 +1,2 @@
|
|||
/target/
|
||||
/.idea/
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/wecom_robot?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: jiong1114
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password:
|
||||
database: 0
|
||||
timeout: 10000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-wait: -1
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
wecom:
|
||||
corp-id: ww29e81e73b1f4c6fd
|
||||
agent-id: 1000006
|
||||
secret: vltAfKVAH1bqo6WjB99rJaH6iQSXDyx3uf3hbbA8F-M
|
||||
token: 2wuT6pE
|
||||
encoding-aes-key: l0boKM2eqcGT3xV2O03y6VXx9U5l25u0tWQsgF3aNPT
|
||||
|
||||
ai:
|
||||
enabled: true
|
||||
provider: deepseek
|
||||
deepseek:
|
||||
api-key: sk-6cdd32d6d49d4d399b479d99e02d1672
|
||||
base-url: https://api.deepseek.com/v1
|
||||
model: deepseek-chat
|
||||
openai:
|
||||
api-key: your_openai_api_key
|
||||
base-url: https://api.openai.com/v1
|
||||
model: gpt-3.5-turbo
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.wecom.robot: debug
|
||||
org.springframework.web: info
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://host.docker.internal:3316/wecom_robot?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: jiong1114
|
||||
redis:
|
||||
host: host.docker.internal
|
||||
port: 6379
|
||||
password: jiong1114
|
||||
database: 0
|
||||
timeout: 10000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 16
|
||||
max-wait: -1
|
||||
max-idle: 8
|
||||
min-idle: 2
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||
|
||||
wecom:
|
||||
corp-id: ww29e81e73b1f4c6fd
|
||||
agent-id: 1000006
|
||||
secret: vltAfKVAH1bqo6WjB99rJaH6iQSXDyx3uf3hbbA8F-M
|
||||
token: 2wuT6pE
|
||||
encoding-aes-key: l0boKM2eqcGT3xV2O03y6VXx9U5l25u0tWQsgF3aNPT
|
||||
|
||||
ai:
|
||||
enabled: true
|
||||
provider: deepseek
|
||||
deepseek:
|
||||
api-key: sk-6cdd32d6d49d4d399b479d99e02d1672
|
||||
base-url: https://api.deepseek.com/v1
|
||||
model: deepseek-chat
|
||||
openai:
|
||||
api-key: your_openai_api_key
|
||||
base-url: https://api.openai.com/v1
|
||||
model: gpt-3.5-turbo
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.wecom.robot: info
|
||||
org.springframework.web: warn
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: wecom-robot
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
type-aliases-package: com.wecom.robot.entity
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
wecom:
|
||||
kf:
|
||||
callback-url: /wecom/callback
|
||||
|
||||
transfer:
|
||||
keywords:
|
||||
- 人工
|
||||
- 转人工
|
||||
- 投诉
|
||||
- 客服
|
||||
- 人工客服
|
||||
confidence-threshold: 0.6
|
||||
max-fail-rounds: 3
|
||||
max-session-duration: 1800000
|
||||
max-message-rounds: 50
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,77 +0,0 @@
|
|||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS wecom_robot DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE wecom_robot;
|
||||
|
||||
-- 会话表
|
||||
CREATE TABLE IF NOT EXISTS `session` (
|
||||
`session_id` VARCHAR(128) NOT NULL COMMENT '会话ID',
|
||||
`customer_id` VARCHAR(64) NOT NULL COMMENT '客户ID (external_userid)',
|
||||
`kf_id` VARCHAR(64) NOT NULL COMMENT '客服账号ID (open_kfid)',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'AI' COMMENT '状态: AI/PENDING/MANUAL/CLOSED',
|
||||
`wx_service_state` INT DEFAULT 0 COMMENT '微信会话状态: 0-未处理/1-智能助手/2-待接入池/3-人工接待/4-已结束',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`manual_cs_id` VARCHAR(64) DEFAULT NULL COMMENT '人工客服ID',
|
||||
`metadata` TEXT DEFAULT NULL COMMENT '扩展信息JSON',
|
||||
PRIMARY KEY (`session_id`),
|
||||
INDEX `idx_customer_id` (`customer_id`),
|
||||
INDEX `idx_kf_id` (`kf_id`),
|
||||
INDEX `idx_status` (`status`),
|
||||
INDEX `idx_updated_at` (`updated_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话表';
|
||||
|
||||
-- 消息表
|
||||
CREATE TABLE IF NOT EXISTS `message` (
|
||||
`msg_id` VARCHAR(128) NOT NULL COMMENT '消息ID',
|
||||
`session_id` VARCHAR(128) NOT NULL COMMENT '会话ID',
|
||||
`sender_type` VARCHAR(20) NOT NULL COMMENT '发送者类型: customer/ai/manual',
|
||||
`sender_id` VARCHAR(64) NOT NULL COMMENT '发送者标识',
|
||||
`content` TEXT NOT NULL COMMENT '消息内容',
|
||||
`msg_type` VARCHAR(20) NOT NULL DEFAULT 'text' COMMENT '消息类型: text/image/link等',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`raw_data` TEXT DEFAULT NULL COMMENT '原始消息数据JSON',
|
||||
PRIMARY KEY (`msg_id`),
|
||||
INDEX `idx_session_id` (`session_id`),
|
||||
INDEX `idx_created_at` (`created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='消息表';
|
||||
|
||||
-- 客服账号表
|
||||
CREATE TABLE IF NOT EXISTS `kf_account` (
|
||||
`kf_id` VARCHAR(64) NOT NULL COMMENT '客服账号ID',
|
||||
`name` VARCHAR(100) DEFAULT NULL COMMENT '客服昵称',
|
||||
`avatar` VARCHAR(500) DEFAULT NULL COMMENT '头像URL',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'offline' COMMENT '状态: online/offline',
|
||||
`bind_manual_id` VARCHAR(64) DEFAULT NULL COMMENT '绑定的企业微信员工ID',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`kf_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客服账号表';
|
||||
|
||||
-- 转人工记录表
|
||||
CREATE TABLE IF NOT EXISTS `transfer_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增ID',
|
||||
`session_id` VARCHAR(128) NOT NULL COMMENT '会话ID',
|
||||
`trigger_reason` VARCHAR(200) DEFAULT NULL COMMENT '触发原因',
|
||||
`trigger_time` DATETIME NOT NULL COMMENT '触发时间',
|
||||
`accepted_time` DATETIME DEFAULT NULL COMMENT '客服接入时间',
|
||||
`accepted_cs_id` VARCHAR(64) DEFAULT NULL COMMENT '接入的客服ID',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_session_id` (`session_id`),
|
||||
INDEX `idx_trigger_time` (`trigger_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转人工记录表';
|
||||
|
||||
-- 快捷回复表 (可选)
|
||||
CREATE TABLE IF NOT EXISTS `quick_reply` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '自增ID',
|
||||
`cs_id` VARCHAR(64) DEFAULT NULL COMMENT '客服ID,为空表示公共',
|
||||
`category` VARCHAR(50) DEFAULT NULL COMMENT '分类',
|
||||
`content` VARCHAR(500) NOT NULL COMMENT '回复内容',
|
||||
`sort_order` INT DEFAULT 0 COMMENT '排序',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_cs_id` (`cs_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='快捷回复表';
|
||||
|
||||
-- 如果表已存在,添加新字段
|
||||
ALTER TABLE `session` ADD COLUMN IF NOT EXISTS `wx_service_state` INT DEFAULT 0 COMMENT '微信会话状态';
|
||||
|
|
@ -1,455 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>聊天记录查询</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
background: #1890ff;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.header p {
|
||||
opacity: 0.8;
|
||||
font-size: 14px;
|
||||
}
|
||||
.filters {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.filter-group label {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
select, input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
min-width: 200px;
|
||||
}
|
||||
select:focus, input:focus {
|
||||
outline: none;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
.btn {
|
||||
padding: 8px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.btn-primary {
|
||||
background: #1890ff;
|
||||
color: white;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 350px 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
.session-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.session-list-header {
|
||||
padding: 15px 20px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
font-weight: 600;
|
||||
}
|
||||
.session-item {
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.session-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.session-item.active {
|
||||
background: #e6f7ff;
|
||||
border-left: 3px solid #1890ff;
|
||||
}
|
||||
.session-item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.session-customer-id {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
.session-status {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.status-AI { background: #e6f7ff; color: #1890ff; }
|
||||
.status-PENDING { background: #fff7e6; color: #fa8c16; }
|
||||
.status-MANUAL { background: #f6ffed; color: #52c41a; }
|
||||
.status-CLOSED { background: #f5f5f5; color: #999; }
|
||||
.session-meta {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
.chat-panel {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 600px;
|
||||
}
|
||||
.chat-header {
|
||||
padding: 15px 20px;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.chat-header-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
background: #fafafa;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.message.customer {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.message.ai, .message.manual {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.message-sender {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.message-content {
|
||||
max-width: 70%;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.message.customer .message-content {
|
||||
background: white;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
.message.ai .message-content {
|
||||
background: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
}
|
||||
.message.manual .message-content {
|
||||
background: #f6ffed;
|
||||
border: 1px solid #b7eb8f;
|
||||
}
|
||||
.message-time {
|
||||
font-size: 11px;
|
||||
color: #bbb;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
}
|
||||
.empty-state svg {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #999;
|
||||
}
|
||||
.no-sessions {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>聊天记录查询</h1>
|
||||
<p>查看各客服账号的历史聊天记录</p>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<div class="filter-group">
|
||||
<label>客服账号:</label>
|
||||
<select id="kfAccountSelect">
|
||||
<option value="">请选择客服账号</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>会话状态:</label>
|
||||
<select id="statusSelect">
|
||||
<option value="all">全部</option>
|
||||
<option value="AI">AI接待中</option>
|
||||
<option value="PENDING">待接入</option>
|
||||
<option value="MANUAL">人工接待中</option>
|
||||
<option value="CLOSED">已结束</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="loadSessions()">查询会话</button>
|
||||
<button class="btn btn-secondary" onclick="refreshKfAccounts()">刷新账号</button>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="session-list">
|
||||
<div class="session-list-header">
|
||||
会话列表 (<span id="sessionCount">0</span>)
|
||||
</div>
|
||||
<div id="sessionListContainer">
|
||||
<div class="no-sessions">请选择客服账号并查询</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-panel">
|
||||
<div class="chat-header">
|
||||
<div class="chat-header-info" id="chatHeaderInfo">请选择会话查看聊天记录</div>
|
||||
</div>
|
||||
<div class="chat-messages" id="chatMessagesContainer">
|
||||
<div class="empty-state">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>
|
||||
</svg>
|
||||
<p>选择左侧会话查看聊天记录</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentSessionId = null;
|
||||
|
||||
async function refreshKfAccounts() {
|
||||
try {
|
||||
const response = await fetch('/chat-history/api/kf-accounts');
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
const select = document.getElementById('kfAccountSelect');
|
||||
select.innerHTML = '<option value="">请选择客服账号</option>';
|
||||
|
||||
result.data.forEach(account => {
|
||||
const option = document.createElement('option');
|
||||
option.value = account.openKfId;
|
||||
option.textContent = account.name || account.openKfId;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
if (result.data.length === 0) {
|
||||
alert('未获取到客服账号,请检查配置');
|
||||
}
|
||||
} else {
|
||||
alert('获取客服账号失败: ' + result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取客服账号失败:', error);
|
||||
alert('获取客服账号失败,请检查网络连接');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSessions() {
|
||||
const kfId = document.getElementById('kfAccountSelect').value;
|
||||
const status = document.getElementById('statusSelect').value;
|
||||
|
||||
if (!kfId) {
|
||||
alert('请选择客服账号');
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document.getElementById('sessionListContainer');
|
||||
container.innerHTML = '<div class="loading">加载中...</div>';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/chat-history/api/sessions?openKfId=${encodeURIComponent(kfId)}&status=${status}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
document.getElementById('sessionCount').textContent = result.data.length;
|
||||
renderSessionList(result.data);
|
||||
} else {
|
||||
container.innerHTML = `<div class="no-sessions">查询失败: ${result.message}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载会话列表失败:', error);
|
||||
container.innerHTML = '<div class="no-sessions">加载失败,请重试</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderSessionList(sessions) {
|
||||
const container = document.getElementById('sessionListContainer');
|
||||
|
||||
if (sessions.length === 0) {
|
||||
container.innerHTML = '<div class="no-sessions">暂无会话记录</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = sessions.map(session => `
|
||||
<div class="session-item" data-session-id="${session.sessionId}" onclick="selectSession('${session.sessionId}')">
|
||||
<div class="session-item-header">
|
||||
<span class="session-customer-id">${session.customerId.substring(0, 15)}...</span>
|
||||
<span class="session-status status-${session.status}">${getStatusText(session.status)}</span>
|
||||
</div>
|
||||
<div class="session-meta">
|
||||
消息: ${session.messageCount} 条 | ${formatTime(session.updatedAt)}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function getStatusText(status) {
|
||||
const map = {
|
||||
'AI': 'AI接待',
|
||||
'PENDING': '待接入',
|
||||
'MANUAL': '人工接待',
|
||||
'CLOSED': '已结束'
|
||||
};
|
||||
return map[status] || status;
|
||||
}
|
||||
|
||||
function formatTime(timeStr) {
|
||||
if (!timeStr) return '-';
|
||||
const date = new Date(timeStr);
|
||||
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
async function selectSession(sessionId) {
|
||||
currentSessionId = sessionId;
|
||||
|
||||
document.querySelectorAll('.session-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
document.querySelector(`[data-session-id="${sessionId}"]`).classList.add('active');
|
||||
|
||||
const container = document.getElementById('chatMessagesContainer');
|
||||
container.innerHTML = '<div class="loading">加载中...</div>';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/chat-history/api/messages?sessionId=${encodeURIComponent(sessionId)}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 0) {
|
||||
document.getElementById('chatHeaderInfo').textContent =
|
||||
`会话ID: ${sessionId} | 消息数: ${result.data.length}`;
|
||||
renderMessages(result.data);
|
||||
} else {
|
||||
container.innerHTML = `<div class="no-sessions">加载失败: ${result.message}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载消息失败:', error);
|
||||
container.innerHTML = '<div class="no-sessions">加载失败,请重试</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderMessages(messages) {
|
||||
const container = document.getElementById('chatMessagesContainer');
|
||||
|
||||
if (messages.length === 0) {
|
||||
container.innerHTML = '<div class="empty-state"><p>暂无消息记录</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = messages.map(msg => {
|
||||
const senderName = getSenderName(msg.senderType, msg.senderId);
|
||||
return `
|
||||
<div class="message ${msg.senderType}">
|
||||
<div class="message-sender">${senderName}</div>
|
||||
<div class="message-content">${escapeHtml(msg.content)}</div>
|
||||
<div class="message-time">${formatTime(msg.createdAt)}</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
|
||||
function getSenderName(senderType, senderId) {
|
||||
switch (senderType) {
|
||||
case 'customer': return '客户';
|
||||
case 'ai': return 'AI助手';
|
||||
case 'manual': return `客服(${senderId || '未知'})`;
|
||||
default: return senderType;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
refreshKfAccounts();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,458 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>客户模拟端</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.phone-frame {
|
||||
width: 375px;
|
||||
height: 700px;
|
||||
background: #fff;
|
||||
border-radius: 30px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 8px solid #333;
|
||||
}
|
||||
.phone-header {
|
||||
background: #ededed;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
position: relative;
|
||||
}
|
||||
.phone-header .status-bar {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
color: #333;
|
||||
}
|
||||
.phone-header .title {
|
||||
margin-top: 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.phone-header .subtitle {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.chat-area {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.message.sent {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.message.received {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.message-bubble {
|
||||
max-width: 75%;
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
position: relative;
|
||||
}
|
||||
.message.sent .message-bubble {
|
||||
background: #95ec69;
|
||||
border-radius: 8px 0 8px 8px;
|
||||
}
|
||||
.message.received .message-bubble {
|
||||
background: #fff;
|
||||
border-radius: 0 8px 8px 8px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
}
|
||||
.message-time {
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.sender-name {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.typing-indicator {
|
||||
display: none;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
background: #fff;
|
||||
border-radius: 0 8px 8px 8px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
width: fit-content;
|
||||
}
|
||||
.typing-indicator.show {
|
||||
display: flex;
|
||||
}
|
||||
.typing-indicator span {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #999;
|
||||
border-radius: 50%;
|
||||
margin: 0 2px;
|
||||
animation: typing 1.4s infinite;
|
||||
}
|
||||
.typing-indicator span:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.typing-indicator span:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
@keyframes typing {
|
||||
0%, 60%, 100% { transform: translateY(0); }
|
||||
30% { transform: translateY(-4px); }
|
||||
}
|
||||
.input-area {
|
||||
background: #f7f7f7;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
.input-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
.input-row textarea {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
background: #fff;
|
||||
resize: none;
|
||||
height: 40px;
|
||||
max-height: 100px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
}
|
||||
.input-row button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: #07c160;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.input-row button:hover {
|
||||
background: #06ad56;
|
||||
}
|
||||
.input-row button:disabled {
|
||||
background: #ccc;
|
||||
}
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.quick-action {
|
||||
padding: 6px 12px;
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 15px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.quick-action:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.settings-panel {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
z-index: 100;
|
||||
display: none;
|
||||
}
|
||||
.settings-panel.show {
|
||||
display: block;
|
||||
}
|
||||
.settings-panel h4 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.settings-panel input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.settings-panel button {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
background: #07c160;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.settings-btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.transfer-notice {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffc107;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
font-size: 12px;
|
||||
color: #856404;
|
||||
text-align: center;
|
||||
}
|
||||
.system-message {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-frame">
|
||||
<div class="phone-header">
|
||||
<div class="status-bar">
|
||||
<span id="currentTime">12:00</span>
|
||||
<span>📶 🔋</span>
|
||||
</div>
|
||||
<div class="title">智能客服</div>
|
||||
<div class="subtitle" id="statusText">AI在线</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-area" id="chatArea">
|
||||
<div class="system-message">会话已开始</div>
|
||||
<div class="message received">
|
||||
<div class="sender-name">客服</div>
|
||||
<div class="message-bubble">您好!我是智能客服,有什么可以帮您的吗?</div>
|
||||
<div class="message-time">刚刚</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-area">
|
||||
<div class="input-row">
|
||||
<textarea id="messageInput" placeholder="输入消息..." rows="1"></textarea>
|
||||
<button onclick="sendMessage()" id="sendBtn">➤</button>
|
||||
</div>
|
||||
<div class="quick-actions">
|
||||
<button class="quick-action" onclick="quickSend('你好')">你好</button>
|
||||
<button class="quick-action" onclick="quickSend('转人工')">转人工</button>
|
||||
<button class="quick-action" onclick="quickSend('投诉')">投诉</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="settings-btn" onclick="toggleSettings()">⚙️</button>
|
||||
|
||||
<div class="settings-panel" id="settingsPanel">
|
||||
<h4>测试设置</h4>
|
||||
<input type="text" id="customerId" placeholder="客户ID" value="customer_001">
|
||||
<input type="text" id="kfId" placeholder="客服账号ID" value="kf_001">
|
||||
<button onclick="saveSettings()">保存设置</button>
|
||||
<button onclick="clearChat()" style="margin-top: 8px; background: #ff4d4f;">清空聊天</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const baseUrl = window.location.origin;
|
||||
let customerId = 'customer_001';
|
||||
let kfId = 'kf_001';
|
||||
let sessionStatus = 'AI';
|
||||
|
||||
function updateTime() {
|
||||
const now = new Date();
|
||||
document.getElementById('currentTime').textContent =
|
||||
now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
setInterval(updateTime, 1000);
|
||||
updateTime();
|
||||
|
||||
function toggleSettings() {
|
||||
document.getElementById('settingsPanel').classList.toggle('show');
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
customerId = document.getElementById('customerId').value || 'customer_001';
|
||||
kfId = document.getElementById('kfId').value || 'kf_001';
|
||||
toggleSettings();
|
||||
addSystemMessage('设置已更新');
|
||||
}
|
||||
|
||||
function clearChat() {
|
||||
document.getElementById('chatArea').innerHTML = '<div class="system-message">会话已重置</div>';
|
||||
sessionStatus = 'AI';
|
||||
document.getElementById('statusText').textContent = 'AI在线';
|
||||
toggleSettings();
|
||||
}
|
||||
|
||||
function addSystemMessage(text) {
|
||||
const chatArea = document.getElementById('chatArea');
|
||||
const msg = document.createElement('div');
|
||||
msg.className = 'system-message';
|
||||
msg.textContent = text;
|
||||
chatArea.appendChild(msg);
|
||||
chatArea.scrollTop = chatArea.scrollHeight;
|
||||
}
|
||||
|
||||
function addMessage(content, isSent, senderName = '') {
|
||||
const chatArea = document.getElementById('chatArea');
|
||||
const msg = document.createElement('div');
|
||||
msg.className = 'message ' + (isSent ? 'sent' : 'received');
|
||||
|
||||
const time = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
||||
|
||||
msg.innerHTML = `
|
||||
${senderName ? '<div class="sender-name">' + senderName + '</div>' : ''}
|
||||
<div class="message-bubble">${content}</div>
|
||||
<div class="message-time">${time}</div>
|
||||
`;
|
||||
|
||||
chatArea.appendChild(msg);
|
||||
chatArea.scrollTop = chatArea.scrollHeight;
|
||||
}
|
||||
|
||||
function showTyping() {
|
||||
const chatArea = document.getElementById('chatArea');
|
||||
const typing = document.createElement('div');
|
||||
typing.className = 'message received';
|
||||
typing.id = 'typingIndicator';
|
||||
typing.innerHTML = `
|
||||
<div class="typing-indicator show">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
`;
|
||||
chatArea.appendChild(typing);
|
||||
chatArea.scrollTop = chatArea.scrollHeight;
|
||||
}
|
||||
|
||||
function hideTyping() {
|
||||
const typing = document.getElementById('typingIndicator');
|
||||
if (typing) typing.remove();
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
const input = document.getElementById('messageInput');
|
||||
const content = input.value.trim();
|
||||
if (!content) return;
|
||||
|
||||
addMessage(content, true);
|
||||
input.value = '';
|
||||
|
||||
showTyping();
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/test/send-message?' +
|
||||
'customerId=' + encodeURIComponent(customerId) +
|
||||
'&kfId=' + encodeURIComponent(kfId) +
|
||||
'&content=' + encodeURIComponent(content), {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
setTimeout(() => {
|
||||
hideTyping();
|
||||
|
||||
if (result.code === 200) {
|
||||
if (content.includes('人工') || content.includes('转人工') || content.includes('投诉')) {
|
||||
sessionStatus = 'PENDING';
|
||||
document.getElementById('statusText').textContent = '等待人工接入...';
|
||||
addTransferNotice();
|
||||
}
|
||||
}
|
||||
}, 1000 + Math.random() * 1000);
|
||||
|
||||
} catch (error) {
|
||||
hideTyping();
|
||||
console.error('发送失败:', error);
|
||||
addMessage('消息发送失败,请重试', false, '系统');
|
||||
}
|
||||
}
|
||||
|
||||
function addTransferNotice() {
|
||||
const chatArea = document.getElementById('chatArea');
|
||||
const notice = document.createElement('div');
|
||||
notice.className = 'transfer-notice';
|
||||
notice.textContent = '正在为您转接人工客服,请稍候...';
|
||||
chatArea.appendChild(notice);
|
||||
chatArea.scrollTop = chatArea.scrollHeight;
|
||||
}
|
||||
|
||||
function quickSend(text) {
|
||||
document.getElementById('messageInput').value = text;
|
||||
sendMessage();
|
||||
}
|
||||
|
||||
document.getElementById('messageInput').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('messageInput').addEventListener('input', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = Math.min(this.scrollHeight, 100) + 'px';
|
||||
});
|
||||
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/api/sessions?status=MANUAL');
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
const mySession = result.data.find(s =>
|
||||
s.customerId === customerId && s.status === 'MANUAL'
|
||||
);
|
||||
if (mySession && sessionStatus !== 'MANUAL') {
|
||||
sessionStatus = 'MANUAL';
|
||||
document.getElementById('statusText').textContent = '人工客服接待中';
|
||||
addSystemMessage('人工客服已接入');
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,638 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>人工客服工作台</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f5f5;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
.sidebar {
|
||||
width: 300px;
|
||||
background: #fff;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.sidebar-header {
|
||||
padding: 15px;
|
||||
background: #1890ff;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.session-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
.session-tab {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.session-tab.active {
|
||||
border-bottom-color: #1890ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
.session-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.session-item {
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.session-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.session-item.active {
|
||||
background: #e6f7ff;
|
||||
}
|
||||
.session-item .customer-id {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.session-item .last-msg {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.session-item .time {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.status-pending {
|
||||
background: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
.status-manual {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.chat-header {
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
}
|
||||
.message.customer {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.message.ai, .message.manual {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.message-content {
|
||||
max-width: 60%;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
}
|
||||
.message.customer .message-content {
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
.message.ai .message-content {
|
||||
background: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
}
|
||||
.message.manual .message-content {
|
||||
background: #f6ffed;
|
||||
border: 1px solid #b7eb8f;
|
||||
}
|
||||
.message-sender {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.message-time {
|
||||
font-size: 10px;
|
||||
color: #bbb;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.chat-input {
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.chat-input textarea {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
resize: none;
|
||||
height: 60px;
|
||||
}
|
||||
.chat-input button {
|
||||
padding: 10px 20px;
|
||||
background: #1890ff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.chat-input button:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
.chat-input button:disabled {
|
||||
background: #d9d9d9;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.empty-state {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
}
|
||||
.connection-status {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.connected {
|
||||
background: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
.disconnected {
|
||||
background: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.actions button {
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #d9d9d9;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.actions button:hover {
|
||||
border-color: #1890ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
.test-panel {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
background: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
width: 300px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-panel h4 {
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.test-panel input, .test-panel textarea {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.test-panel button {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
background: #52c41a;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.test-panel button:hover {
|
||||
background: #73d13d;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
客服工作台 <span id="csId">CS_001</span>
|
||||
</div>
|
||||
<div class="session-tabs">
|
||||
<div class="session-tab active" data-status="PENDING" onclick="switchTab('PENDING')">
|
||||
待接入 (<span id="pendingCount">0</span>)
|
||||
</div>
|
||||
<div class="session-tab" data-status="MANUAL" onclick="switchTab('MANUAL')">
|
||||
进行中 (<span id="manualCount">0</span>)
|
||||
</div>
|
||||
</div>
|
||||
<div class="session-list" id="sessionList">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div id="chatArea" style="display: none; height: 100%; flex-direction: column;">
|
||||
<div class="chat-header">
|
||||
<div>
|
||||
<strong id="currentCustomer">-</strong>
|
||||
<span class="status-badge" id="currentStatus">-</span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button onclick="acceptSession()" id="acceptBtn">接入会话</button>
|
||||
<button onclick="closeSession()" id="closeBtn">结束会话</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-messages" id="chatMessages">
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<textarea id="messageInput" placeholder="输入消息..."></textarea>
|
||||
<button onclick="sendMessage()" id="sendBtn" disabled>发送</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="emptyState" class="empty-state">
|
||||
<div style="text-align: center;">
|
||||
<p>请从左侧选择一个会话</p>
|
||||
<p style="margin-top: 10px; font-size: 12px;">WebSocket: <span id="wsStatus" class="connection-status disconnected">未连接</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-panel">
|
||||
<h4>🧪 模拟客户消息</h4>
|
||||
<input type="text" id="testCustomerId" placeholder="客户ID" value="test_customer_001">
|
||||
<input type="text" id="testKfId" placeholder="客服账号ID" value="test_kf_001">
|
||||
<textarea id="testContent" placeholder="消息内容"></textarea>
|
||||
<button onclick="sendTestMessage()">发送测试消息</button>
|
||||
<button onclick="triggerTransfer()" style="margin-top: 5px; background: #fa8c16;">触发转人工</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let ws = null;
|
||||
let currentSessionId = null;
|
||||
let currentStatus = null;
|
||||
let csId = 'CS_001';
|
||||
const baseUrl = window.location.origin;
|
||||
|
||||
function connectWebSocket() {
|
||||
const wsUrl = baseUrl.replace('http', 'ws') + '/ws/cs/' + csId;
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = function() {
|
||||
document.getElementById('wsStatus').className = 'connection-status connected';
|
||||
document.getElementById('wsStatus').textContent = '已连接';
|
||||
console.log('WebSocket已连接');
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
document.getElementById('wsStatus').className = 'connection-status disconnected';
|
||||
document.getElementById('wsStatus').textContent = '已断开';
|
||||
console.log('WebSocket已断开');
|
||||
setTimeout(connectWebSocket, 3000);
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('收到消息:', data);
|
||||
handleWebSocketMessage(data);
|
||||
};
|
||||
|
||||
ws.onerror = function(error) {
|
||||
console.error('WebSocket错误:', error);
|
||||
};
|
||||
}
|
||||
|
||||
function handleWebSocketMessage(data) {
|
||||
switch(data.type) {
|
||||
case 'new_pending_session':
|
||||
alert('有新的待接入会话!');
|
||||
loadSessions();
|
||||
break;
|
||||
case 'new_message':
|
||||
case 'customer_message':
|
||||
if (currentSessionId === data.sessionId) {
|
||||
addMessage('customer', data.content, data.timestamp);
|
||||
}
|
||||
loadSessions();
|
||||
break;
|
||||
case 'session_accepted':
|
||||
if (currentSessionId === data.sessionId) {
|
||||
currentStatus = 'MANUAL';
|
||||
updateChatHeader();
|
||||
document.getElementById('sendBtn').disabled = false;
|
||||
document.getElementById('acceptBtn').disabled = true;
|
||||
}
|
||||
break;
|
||||
case 'session_closed':
|
||||
if (currentSessionId === data.sessionId) {
|
||||
alert('会话已结束');
|
||||
currentSessionId = null;
|
||||
showEmptyState();
|
||||
}
|
||||
loadSessions();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function switchTab(status) {
|
||||
document.querySelectorAll('.session-tab').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
if (tab.dataset.status === status) {
|
||||
tab.classList.add('active');
|
||||
}
|
||||
});
|
||||
loadSessions(status);
|
||||
}
|
||||
|
||||
async function loadSessions(status = 'PENDING') {
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/api/sessions?status=' + status);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200) {
|
||||
renderSessionList(result.data, status);
|
||||
|
||||
if (status === 'PENDING') {
|
||||
document.getElementById('pendingCount').textContent = result.data.length;
|
||||
} else {
|
||||
document.getElementById('manualCount').textContent = result.data.length;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载会话列表失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSessionList(sessions, status) {
|
||||
const list = document.getElementById('sessionList');
|
||||
list.innerHTML = '';
|
||||
|
||||
sessions.forEach(session => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'session-item' + (currentSessionId === session.sessionId ? ' active' : '');
|
||||
item.onclick = () => selectSession(session);
|
||||
|
||||
const time = session.lastMessageTime ? new Date(session.lastMessageTime).toLocaleString() : '-';
|
||||
|
||||
item.innerHTML = `
|
||||
<div class="customer-id">
|
||||
${session.customerId}
|
||||
<span class="status-badge status-${session.status.toLowerCase()}">${session.status}</span>
|
||||
</div>
|
||||
<div class="last-msg">${session.lastMessage || '暂无消息'}</div>
|
||||
<div class="time">${time}</div>
|
||||
`;
|
||||
|
||||
list.appendChild(item);
|
||||
});
|
||||
|
||||
if (sessions.length === 0) {
|
||||
list.innerHTML = '<div style="padding: 20px; text-align: center; color: #999;">暂无会话</div>';
|
||||
}
|
||||
}
|
||||
|
||||
async function selectSession(session) {
|
||||
currentSessionId = session.sessionId;
|
||||
currentStatus = session.status;
|
||||
|
||||
document.querySelectorAll('.session-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
event.currentTarget.classList.add('active');
|
||||
|
||||
document.getElementById('emptyState').style.display = 'none';
|
||||
document.getElementById('chatArea').style.display = 'flex';
|
||||
|
||||
updateChatHeader();
|
||||
await loadHistory();
|
||||
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'bind_session',
|
||||
sessionId: currentSessionId
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function updateChatHeader() {
|
||||
document.getElementById('currentCustomer').textContent = currentSessionId;
|
||||
|
||||
const statusBadge = document.getElementById('currentStatus');
|
||||
statusBadge.textContent = currentStatus;
|
||||
statusBadge.className = 'status-badge status-' + currentStatus.toLowerCase();
|
||||
|
||||
document.getElementById('acceptBtn').disabled = currentStatus !== 'PENDING';
|
||||
document.getElementById('sendBtn').disabled = currentStatus !== 'MANUAL';
|
||||
document.getElementById('closeBtn').disabled = currentStatus !== 'MANUAL';
|
||||
}
|
||||
|
||||
async function loadHistory() {
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/api/sessions/' + currentSessionId + '/history');
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200) {
|
||||
const container = document.getElementById('chatMessages');
|
||||
container.innerHTML = '';
|
||||
|
||||
result.data.forEach(msg => {
|
||||
addMessage(msg.senderType, msg.content, msg.createdAt);
|
||||
});
|
||||
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载历史消息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(senderType, content, timestamp) {
|
||||
const container = document.getElementById('chatMessages');
|
||||
const msg = document.createElement('div');
|
||||
msg.className = 'message ' + senderType;
|
||||
|
||||
const senderName = senderType === 'customer' ? '客户' :
|
||||
senderType === 'ai' ? 'AI客服' : '人工客服';
|
||||
const time = timestamp ? new Date(timestamp).toLocaleString() : new Date().toLocaleString();
|
||||
|
||||
msg.innerHTML = `
|
||||
<div class="message-content">
|
||||
<div class="message-sender">${senderName}</div>
|
||||
<div>${content}</div>
|
||||
<div class="message-time">${time}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.appendChild(msg);
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
|
||||
async function acceptSession() {
|
||||
if (!currentSessionId) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/api/sessions/' + currentSessionId + '/accept', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ csId: csId })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
currentStatus = 'MANUAL';
|
||||
updateChatHeader();
|
||||
loadSessions('PENDING');
|
||||
loadSessions('MANUAL');
|
||||
} else {
|
||||
alert('接入失败: ' + result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('接入会话失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
if (!currentSessionId || currentStatus !== 'MANUAL') return;
|
||||
|
||||
const content = document.getElementById('messageInput').value.trim();
|
||||
if (!content) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/api/sessions/' + currentSessionId + '/message', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content: content, msgType: 'text' })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
addMessage('manual', content);
|
||||
document.getElementById('messageInput').value = '';
|
||||
} else {
|
||||
alert('发送失败: ' + result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送消息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function closeSession() {
|
||||
if (!currentSessionId) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/api/sessions/' + currentSessionId + '/close', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
currentSessionId = null;
|
||||
showEmptyState();
|
||||
loadSessions('PENDING');
|
||||
loadSessions('MANUAL');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('结束会话失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function showEmptyState() {
|
||||
document.getElementById('emptyState').style.display = 'flex';
|
||||
document.getElementById('chatArea').style.display = 'none';
|
||||
}
|
||||
|
||||
async function sendTestMessage() {
|
||||
const customerId = document.getElementById('testCustomerId').value;
|
||||
const kfId = document.getElementById('testKfId').value;
|
||||
const content = document.getElementById('testContent').value;
|
||||
|
||||
if (!content) {
|
||||
alert('请输入消息内容');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/test/send-message?customerId=' + customerId + '&kfId=' + kfId + '&content=' + encodeURIComponent(content), {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
alert('消息已发送!');
|
||||
document.getElementById('testContent').value = '';
|
||||
setTimeout(() => loadSessions('PENDING'), 500);
|
||||
setTimeout(() => loadSessions('MANUAL'), 500);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送测试消息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function triggerTransfer() {
|
||||
const customerId = document.getElementById('testCustomerId').value;
|
||||
const kfId = document.getElementById('testKfId').value;
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl + '/test/trigger-transfer?customerId=' + customerId + '&kfId=' + kfId, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code === 200) {
|
||||
alert('已触发转人工!');
|
||||
setTimeout(() => loadSessions('PENDING'), 500);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('触发转人工失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('messageInput').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
connectWebSocket();
|
||||
loadSessions('PENDING');
|
||||
loadSessions('MANUAL');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
artifactId=wecom-robot
|
||||
groupId=com.wecom
|
||||
version=1.0.0
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
com\wecom\robot\dto\WxAccessToken.class
|
||||
com\wecom\robot\dto\ChatCompletionResponse$Choice.class
|
||||
com\wecom\robot\dto\WxCallbackMessage.class
|
||||
com\wecom\robot\websocket\CsWebSocketHandler.class
|
||||
com\wecom\robot\dto\AcceptSessionRequest.class
|
||||
com\wecom\robot\dto\SyncMsgResponse$MsgItem.class
|
||||
com\wecom\robot\dto\WxSendMessageRequest$TextContent.class
|
||||
com\wecom\robot\controller\WecomCallbackController.class
|
||||
com\wecom\robot\dto\WxSendMessageRequest$ImageContent.class
|
||||
com\wecom\robot\config\AiConfig$DeepSeekConfig.class
|
||||
com\wecom\robot\mapper\TransferLogMapper.class
|
||||
com\wecom\robot\service\WecomApiService.class
|
||||
com\wecom\robot\dto\SyncMsgResponse.class
|
||||
com\wecom\robot\dto\SessionInfo.class
|
||||
com\wecom\robot\config\WecomConfig$KfConfig.class
|
||||
com\wecom\robot\dto\WxSendMessageRequest$LinkContent.class
|
||||
com\wecom\robot\mapper\KfAccountMapper.class
|
||||
com\wecom\robot\service\MessageProcessService.class
|
||||
com\wecom\robot\dto\ApiResponse.class
|
||||
com\wecom\robot\mapper\MessageMapper.class
|
||||
com\wecom\robot\config\AiConfig$OpenAiConfig.class
|
||||
com\wecom\robot\dto\ChatCompletionResponse$Usage.class
|
||||
com\wecom\robot\dto\MessageInfo.class
|
||||
com\wecom\robot\WecomRobotApplication.class
|
||||
com\wecom\robot\dto\ChatCompletionResponse.class
|
||||
com\wecom\robot\config\WecomConfig.class
|
||||
com\wecom\robot\dto\ChatCompletionRequest.class
|
||||
com\wecom\robot\dto\ChatCompletionResponse$Message.class
|
||||
com\wecom\robot\service\AiService.class
|
||||
com\wecom\robot\util\XmlUtil.class
|
||||
com\wecom\robot\service\WebSocketService.class
|
||||
com\wecom\robot\config\TransferConfig.class
|
||||
com\wecom\robot\dto\ChatCompletionRequest$Message.class
|
||||
com\wecom\robot\dto\SendMessageRequest.class
|
||||
com\wecom\robot\service\TransferService.class
|
||||
com\wecom\robot\controller\SessionController.class
|
||||
com\wecom\robot\entity\Session.class
|
||||
com\wecom\robot\controller\TestController.class
|
||||
com\wecom\robot\entity\Message.class
|
||||
com\wecom\robot\entity\TransferLog.class
|
||||
com\wecom\robot\service\SessionManagerService.class
|
||||
com\wecom\robot\dto\WxSendMessageRequest.class
|
||||
com\wecom\robot\entity\KfAccount.class
|
||||
com\wecom\robot\config\AiConfig.class
|
||||
com\wecom\robot\mapper\SessionMapper.class
|
||||
com\wecom\robot\config\WebSocketConfig.class
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\entity\Session.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\service\WebSocketService.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\controller\WecomCallbackController.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\PKCS7Encoder.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\Sample.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\config\AiConfig.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\controller\DebugController.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\ApiResponse.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\XMLParse.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\SessionInfo.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\SyncMsgResponse.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\entity\TransferLog.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\SendMessageRequest.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\websocket\CsWebSocketHandler.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\mapper\MessageMapper.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\mapper\SessionMapper.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\WecomRobotApplication.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\XmlUtil.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\ChatCompletionRequest.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\AcceptSessionRequest.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\controller\SessionController.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\service\SessionManagerService.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\AesException.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\WXBizMsgCrypt.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\config\WebSocketConfig.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\service\TransferService.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\MessageInfo.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\service\MessageProcessService.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\ChatCompletionResponse.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\service\AiService.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\ByteGroup.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\WxCallbackMessage.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\entity\Message.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\service\WecomApiService.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\ServiceStateResponse.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\config\TransferConfig.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\entity\KfAccount.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\mapper\TransferLogMapper.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\mapper\KfAccountMapper.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\controller\TestController.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\controller\ChatHistoryController.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\util\SHA1.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\config\WecomConfig.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\WxAccessToken.java
|
||||
E:\AiProject\wecom-robot\src\main\java\com\wecom\robot\dto\WxSendMessageRequest.java
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue