
一、项目背景与需求分析
1.1 业务场景
在企业日常运营中,客服人员需要同时处理来自多个企业微信群和个人好友的咨询消息。传统的人工处理方式存在以下痛点:
-
消息分散:消息分散在不同群组和好友对话中,难以集中管理
-
响应滞后:人工逐一回复效率低下,客户等待时间长
-
信息孤岛:重要信息无法自动流转到相关部门
-
重复劳动:相似问题需要反复回答
1.2 系统目标
本文设计并实现了一套企业微信消息接收与转发系统,核心功能包括:
-
实时消息监听:通过Python脚本监听企业微信消息,推送到Java后端
-
智能消息聚合:将短时间内的多条消息智能合并,减少大模型调用次数
-
AI智能回复:接入扣子(Coze)大模型,实现自动问答
-
灵活转发配置:支持将指定来源的消息转发到指定目标群组
-
结构化信息提取:从消息中提取车牌号、姓名、手机号等关键信息
二、系统架构设计
2.1 整体架构
text
┌─────────────────────────────────────────────────────────────────┐
│ 前端Vue管理界面 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │好友/群管理│ │关系绑定配置│ │消息记录 │ │ 登录/注册 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ HTTP
▼
┌─────────────────────────────────────────────────────────────────┐
│ Spring Boot 后端服务 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ BigController│ │ 定时任务 │ │ 消息处理引擎 │ │
│ │ (核心控制器) │ │ (定时刷新) │ │ (聚合/过滤/转发) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Coze大模型 │ │ OSS存储 │ │ 邮件服务 │ │
│ │ 智能体调用 │ │ 图片上传 │ │ 验证码 │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 数据存储层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────────────────┐ │
│ │ MySQL │ │ 本地文件 │ │ Redis │ │ 阿里云OSS │ │
│ │ (主数据) │ │(消息缓存)│ │ (可选) │ │ (图片存储) │ │
│ └─────────┘ └─────────┘ └─────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 企业微信客户端 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ 群聊消息 │ │ 私聊消息 │ │ 图片消息 │ │ 位置消息 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 技术选型
| 技术组件 | 选型 | 说明 |
|---|---|---|
| 后端框架 | Spring Boot 2.x | 企业级微服务框架 |
| ORM框架 | MyBatis-Plus | 简化数据库操作 |
| 前端框架 | Vue 2.x + Element UI | 后台管理界面 |
| 消息监听 | Python + 企业微信SDK | 实时监听消息 |
| AI引擎 | 扣子(Coze)大模型 | 智能回复与信息提取 |
| 数据库 | MySQL 8.0 | 主数据存储 |
| 文件存储 | 阿里云OSS | 图片存储 |
| JSON处理 | Fastjson + Gson + Jackson | 多格式解析 |
三、核心功能实现
3.1 消息监听与接收
3.1.1 Python消息监听端
系统通过Python脚本实时监听企业微信消息,主要监听的消息类型包括:
| 类型码 | 消息类型 | 说明 |
|---|---|---|
| 11041 | 文本消息 | 用户发送的文本内容 |
| 11042 | 图片消息 | 用户发送的图片 |
| 11046 | 位置消息 | 用户共享的位置信息 |
| 11078 | 群名称变更 | 群聊名称修改通知 |
Python端将监听到的消息封装后,通过HTTP请求发送到Java后端的/big/receive_python_msg接口。
3.1.2 消息数据模型
java
// 微信消息信息封装
public class WxInfo {
private String id; // 消息唯一ID
private String sender; // 发送者信息 (名称&&&ID)
private String groupName; // 群聊信息 (名称&&&ID)
private String sendContent; // 发送内容
private String imageUrl; // 图片URL
private String mapInfo; // 位置信息
private long sendTime; // 发送时间戳
}
// Python推送消息解析类
static class ChatInfoJson {
ChatData data;
}
static class ChatData {
String sender; // 发送者userId
String conversation_id; // 对话ID
String sender_name; // 发送者名称
String content; // 消息内容
String send_time; // 发送时间
String receiver; // 接收者
String title; // 位置标题
String address; // 位置地址
Cdn cdn; // CDN信息(图片)
double latitude; // 纬度
double longitude; // 经度
}
3.2 消息处理流程
3.2.1 消息处理流程图
text
┌─────────────────────────────────────────────────────────────┐
│ 接收到消息 │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 判断消息类型 (11041/11042/11046) │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 判断是否自己发送 (sender == SELF_ID) │
│ 是 → 忽略 (不记录自己发送的消息) │
│ 否 → 继续处理 │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 构建 WxInfo 对象并存储 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 文本消息 → 直接保存内容 │ │
│ │ 图片消息 → 下载图片到本地 │ │
│ │ 位置消息 → 解析经纬度/标题/地址 │ │
│ └─────────────────────────────────────────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 添加到内存列表 Constants.WX_INFO_LIST │
│ 写入本地文件 wx_info_list.txt │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 判断消息数量 > 10 或 超时 5分钟 │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 消息聚合处理 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 按发送者/群组 聚合多条消息 │ │
│ │ 合并内容、图片、位置等信息 │ │
│ └─────────────────────────────────────────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 判断是否包含手机号或图片 │
│ 是 → 调用大模型提取信息 │
│ 否 → 仅保存,不转发 │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 查询转发配置 (relation_detail) │
│ 根据来源ID查询目标ID列表 │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 执行消息转发 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 发送文本消息 (包含车牌/姓名/手机号) │ │
│ │ 发送位置消息 (如有) │ │
│ │ 发送图片消息 (如有) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
3.2.2 消息聚合策略
为了避免频繁调用大模型API造成资源浪费,系统实现了智能消息聚合机制:
java
// 消息聚合核心逻辑 (Step5_InfoIntegrator.integrateWxInfosWithRelation)
public static List<MergeRelation> integrateWxInfosWithRelation(List<WxInfo> wxInfoList) {
// 1. 按发送者/群组分组
Map<String, List<WxInfo>> groupMap = wxInfoList.stream()
.collect(Collectors.groupingBy(wxInfo -> {
if (wxInfo.getGroupName().contains("R:")) {
return wxInfo.getGroupName(); // 群组维度
} else {
return wxInfo.getSender(); // 个人维度
}
}));
// 2. 对每组消息进行合并
List<MergeRelation> mergeRelations = new ArrayList<>();
for (Map.Entry<String, List<WxInfo>> entry : groupMap.entrySet()) {
MergeRelation relation = new MergeRelation();
relation.setOriginalInfos(entry.getValue());
relation.setMergedInfo(mergeWxInfo(entry.getValue()));
mergeRelations.add(relation);
}
return mergeRelations;
}
3.3 图片处理
3.3.1 图片下载与存储
当接收到图片消息时,系统需要从企业微信CDN下载图片:
java
case 11042:
// 图片消息处理
String savePath = Constants.IMAGE_SAVE_PATH + "\\" + System.currentTimeMillis() + ".jpg";
Map<String, Object> result1 = downloadImage(
1, 1,
chatInfoJson.data.cdn.url,
chatInfoJson.data.cdn.auth_key,
chatInfoJson.data.cdn.aes_key,
chatInfoJson.data.cdn.size,
savePath
);
imageUrl = savePath;
tempSendContent = "发送的图片消息---";
mark = "image";
break;
3.3.2 图片转发
转发图片时,需要将本地路径中的反斜杠转换为双反斜杠:
java
if ((coze9Res.imageUrl != null) && !coze9Res.imageUrl.equals("null")) {
String imagePath = coze9Res.imageUrl.replaceAll("&&", "\\\\\\\\");
Step5_SendImage.doWorkSend(tempRelationDetail.getOneTar(), imagePath);
}
3.4 大模型智能处理
3.4.1 信息提取智能体
系统调用扣子大模型进行结构化信息提取:
java
String uploadInfo = Step9_InfoExtractSmart.SmartWork(
UUID.randomUUID().toString(),
wxInfo.toString()
);
// 解析返回结果
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
Coze9Res coze9Res = mapper.readValue(uploadInfo, Coze9Res.class);
Coze9Res 数据结构:
java
public class Coze9Res {
private String licensePlateNumber; // 车牌号
private String name; // 姓名
private String phone; // 手机号
private String sendContent; // 原始发送内容
private String imageUrl; // 图片URL
private String mapInfo; // 位置信息
}
3.4.2 智能回复
系统还支持大模型自动回复:
java
public void modelGiveAnswer(String user_id, Boolean group, String addId) throws Exception {
QueryWrapper<Msg> msgQueryWrapper = new QueryWrapper<>();
String finalQueryId = group ? user_id + addId : user_id;
msgQueryWrapper.eq("chat_room_or_user_id", finalQueryId);
List<Msg> msgList = msgMapper.selectList(msgQueryWrapper);
String answer = Step2_AnswerSmart.SmartWork(
"7642293093576425524", // botId
finalQueryId, // user_id
msgList // 历史消息列表
);
// 保存回复内容到数据库
}
3.5 转发关系配置
3.5.1 数据模型
java
// 关系主表
public class Relation {
private Integer id;
private String src; // 来源ID列表,用---分隔
private String tar; // 目标ID列表,用---分隔
}
// 关系明细表
public class RelationDetail {
private Integer id;
private Integer relationId; // 关联主表ID
private String oneSrc; // 单个来源ID
private String oneTar; // 单个目标ID
}
3.5.2 配置管理界面
前端Vue界面实现了可视化的关系绑定配置:
vue
<div class="selection-area">
<!-- 左侧:接收消息的来源 -->
<div class="selection-panel left-panel">
<div class="panel-header">
<span><strong>接收消息来源</strong></span>
</div>
<div class="panel-body">
<el-checkbox-group v-model="selectedSources">
<div v-for="item in filteredSourceList" :key="item.id">
<el-checkbox :label="item.id">
{{ item.type === 'group' ? '👥' : '👤' }} {{ item.name }}
</el-checkbox>
</div>
</el-checkbox-group>
</div>
</div>
<!-- 右侧:转发到的目标 -->
<div class="selection-panel right-panel">
<div class="panel-header">
<span><strong>转发到目标</strong></span>
</div>
<div class="panel-body">
<el-checkbox-group v-model="selectedTargets">
<div v-for="item in filteredTargetList" :key="item.id">
<el-checkbox :label="item.id">
{{ item.type === 'group' ? '👥' : '👤' }} {{ item.name }}
</el-checkbox>
</div>
</el-checkbox-group>
</div>
</div>
</div>
3.6 定时任务
系统配置了两个定时任务进行数据同步:
java
// 每10分钟刷新好友和群信息
@Scheduled(fixedRate = 10 * 60 * 1000)
public void scheduledRefresh() {
Step1_Friend.doWork(); // 同步好友列表
Step2_Friend.doWork(); // 同步好友详细信息
}
// 每12小时全量刷新群信息
@Scheduled(fixedRate = 30 * 720 * 60 * 1000)
public void scheduledRefreshGroup() {
Step3_GroupOnce.doWork(); // 全量同步群列表
}
3.7 文件持久化
3.7.1 消息缓存文件
系统使用本地文件 wx_info_list.txt 作为消息缓存,防止内存溢出:
java
public static void appendToFile(WxInfo wxInfo) {
lock.writeLock().lock();
try {
List<WxInfo> existingList = loadFromFile();
existingList.add(wxInfo);
writeFullArray(existingList);
} finally {
lock.writeLock().unlock();
}
}
private static void writeFullArray(List<WxInfo> list) throws IOException {
ObjectMapper prettyMapper = new ObjectMapper();
prettyMapper.enable(SerializationFeature.INDENT_OUTPUT);
prettyMapper.writeValue(new File(FILE_PATH), list);
}
3.7.2 读写锁保护
使用 ReentrantReadWriteLock 保证多线程安全:
java
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 读操作使用读锁
public static List<WxInfo> loadFromFile() {
lock.readLock().lock();
try {
// 读取文件
} finally {
lock.readLock().unlock();
}
}
// 写操作使用写锁
public static void appendToFile(WxInfo wxInfo) {
lock.writeLock().lock();
try {
// 写入文件
} finally {
lock.writeLock().unlock();
}
}
四、前端管理界面
4.1 好友与群管理
vue
<template>
<div>
<!-- 好友列表 -->
<el-table :data="friendList" border>
<el-table-column prop="nickName" label="昵称"></el-table-column>
<el-table-column prop="userId" label="用户ID"></el-table-column>
<el-table-column prop="remark" label="备注"></el-table-column>
<el-table-column prop="conversationId" label="会话ID"></el-table-column>
</el-table>
<!-- 群列表 -->
<el-table :data="groupList" border>
<el-table-column prop="nickName" label="群名称"></el-table-column>
<el-table-column prop="chatRoomId" label="群ID"></el-table-column>
</el-table>
</div>
</template>
4.2 关系绑定配置界面
界面采用左右双栏设计:
-
左侧面板:显示所有可选的来源(好友和群聊),支持搜索过滤
-
右侧面板:显示所有可选的目标(好友和群聊),支持搜索过滤
-
预览区域:实时展示当前选择的来源→目标映射关系
-
关系列表:展示所有已保存的绑定关系,支持编辑和删除
4.3 登录与注册
4.3.1 邮箱验证码
java
@PostMapping("/email_code")
public Res email_code(@RequestBody User user) {
// 5分钟内验证码有效
long diffLongTime = nowTime.getTime() - existEmail.getSendTime().getTime();
if (diffLongTime <= (5 * 60 * 1000)) {
return Res.error(Constants.CODE_600, "验证码5分钟内有效,请5分钟后再获取");
}
// 生成4位随机验证码并发送邮件
String code = RandomUtil.randomNumbers(4);
javaMailSender.send(simpleMailMessage);
}
4.3.2 密码加密存储
java
// 使用SHA256加密
user.setPassword(MyUtils.getSHA256StrJava(user.getPassword()));
// SHA256工具方法
public static String getSHA256StrJava(String str) {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
}
4.3.3 JWT Token认证
java
String token = Token.productToken(existUser);
existUser.setToken(token);
// Token生成
public static String productToken(User user) {
String secret = user.getId() + user.getPassword() + System.currentTimeMillis();
// 使用Base64编码
return Base64.getEncoder().encodeToString(secret.getBytes());
}
五、关键技术难点与解决方案
5.1 消息去重与防重复转发
问题:同一消息可能被多次处理导致重复转发
解决方案:
-
每个消息生成唯一ID(使用时间戳)
-
处理完成后从列表和文件中移除
-
使用Set集合收集待移除的ID
java
Set<String> idsToRemove = new HashSet<>();
for (MergeRelation relation : mergeRelations) {
// 处理逻辑...
for (WxInfo originalInfo : relation.getOriginalInfos()) {
if (originalInfo.getId() != null) {
idsToRemove.add(originalInfo.getId());
}
}
}
// 批量移除
Constants.WX_INFO_LIST.removeIf(wxInfo ->
wxInfo.getId() != null && idsToRemove.contains(wxInfo.getId())
);
5.2 图片路径处理
问题:Windows路径中的反斜杠在JSON传输中需要转义
解决方案:
java
// 存储时使用双反斜杠
String imagePath = coze9Res.imageUrl.replaceAll("&&", "\\\\\\\\");
// 或者使用正斜杠统一路径
String savePath = Constants.IMAGE_SAVE_PATH + "/" + System.currentTimeMillis() + ".jpg";
5.3 大模型返回结果解析
问题:大模型返回的JSON可能包含非标准格式
解决方案:
java
ObjectMapper mapper = new ObjectMapper();
// 启用宽松模式
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
mapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
5.4 消息超时处理
问题:消息超过5分钟未处理可能导致用户等待过久
解决方案:
java
long currentTime = System.currentTimeMillis() / 1000;
Map<Boolean, List<WxInfo>> partitioned = Constants.WX_INFO_LIST.stream()
.collect(Collectors.partitioningBy(
wxInfo -> (currentTime - wxInfo.getSendTime()) <= 300
));
// 有效数据保留,过期数据立即处理
List<WxInfo> validList = partitioned.get(true);
List<WxInfo> expiredList = partitioned.get(false);
六、系统优化建议
6.1 性能优化
-
批量数据库操作:使用批量插入代替逐条插入
-
异步处理:消息转发使用异步线程池
-
缓存优化:热点数据使用Redis缓存
6.2 可扩展性
-
消息类型扩展:支持更多企业微信消息类型
-
智能体扩展:支持对接多个大模型平台
-
转发策略扩展:支持条件转发、优先级转发等
6.3 监控与日志
-
消息处理链路追踪:记录每条消息的完整处理链路
-
异常告警:关键异常发送邮件或短信告警
-
性能指标:统计消息处理延迟、成功率等
七、总结
本文设计并实现了一套完整的企业微信消息接收与转发系统,通过Spring Boot后端、Vue前端、Python消息监听和Coze大模型的组合,实现了:
-
实时消息监听:支持文本、图片、位置等多种消息类型
-
智能消息聚合:减少大模型调用次数,降低API成本
-
AI信息提取:从消息中自动提取车牌号、姓名、手机号等关键信息
-
灵活转发配置:可视化界面支持多对多转发关系配置
-
消息持久化:本地文件缓存保证消息不丢失
该系统已经在实际企业场景中投入使用,显著提升了客服响应效率和消息处理能力,为企业的数字化转型提供了有力支撑。