增加好友、群检索功能,方便针对性的回访客户,搜索+快速选择群发沟通~

一、项目背景与需求分析

在日常的微信运营工作中,经常需要对多个好友或群聊进行批量消息发送,例如活动通知、节日祝福、重要公告等场景。传统的逐一手动发送方式效率低下,且容易遗漏。本文介绍如何构建一套完整的微信管理工具,实现好友/群组数据的可视化管理以及批量消息的快速发送。

1.1 核心功能需求

  • 好友列表管理:展示所有好友信息,支持多选、全选、清空

  • 群聊列表管理:展示所有群组信息,支持多选、全选、清空

  • 批量消息发送:向选中的多个会话(好友或群聊)同时发送消息

  • 数据同步:支持好友/群组信息的批量新增与更新

1.2 技术选型

层级 技术栈
后端框架 Spring Boot + MyBatis-Plus
前端框架 原生HTML/CSS/JavaScript
数据库 MySQL
微信SDK 基于微信个人号/企业微信SDK
JSON处理 Gson + Jackson

二、后端架构设计

2.1 项目结构

com.black
├── controller
│   └── BigController.java      # 核心控制器
├── mapper
│   ├── FriendMapper.java       # 好友数据访问
│   └── Group1Mapper.java       # 群聊数据访问
├── pojo
│   ├── Friend.java             # 好友实体
│   ├── Group1.java             # 群聊实体
│   └── Msg.java                # 消息实体
├── util
│   └── Res.java                # 统一响应结果
└── step
    └── Step3_SendText.java     # 微信消息发送封装

2.2 数据库设计

好友表(friend)

CREATE TABLE friend (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id VARCHAR(64) NOT NULL COMMENT '微信用户ID',
    nick_name VARCHAR(100) COMMENT '昵称',
    remark VARCHAR(100) COMMENT '备注名',
    create_time DATETIME,
    update_time DATETIME
);

群聊表(group1)

CREATE TABLE group1 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    room_id VARCHAR(64) NOT NULL COMMENT '群聊房间ID',
    nick_name VARCHAR(100) COMMENT '群名称',
    remark VARCHAR(100) COMMENT '群备注',
    create_time DATETIME,
    update_time DATETIME
);

2.3 核心控制器实现

2.3.1 好友数据批量同步
@PostMapping("/insert_friend")
public void insert_friend(@RequestBody List<Friend> insertFriendList) {
    // 查询现有数据
    List<Friend> existFriendList = friendMapper.selectList(null);
    Map<String, Friend> existFriendMap = existFriendList.stream()
        .collect(Collectors.toMap(Friend::getUserId, Function.identity()));
    
    // 分离新增和更新
    List<Friend> newFriendList = new ArrayList<>();
    List<Friend> updateFriendList = new ArrayList<>();
    
    for (Friend friend : insertFriendList) {
        if (existFriendMap.containsKey(friend.getUserId())) {
            Friend existFriend = existFriendMap.get(friend.getUserId());
            existFriend.setRemark(friend.getRemark());
            updateFriendList.add(existFriend);
        } else {
            newFriendList.add(friend);
        }
    }
    
    // 批量操作
    if (!newFriendList.isEmpty()) {
        friendMapper.insertBatchFriend(newFriendList);
    }
    if (!updateFriendList.isEmpty()) {
        friendMapper.updateBatchById(updateFriendList);
    }
}

设计亮点

  1. 使用Collectors.toMap构建索引Map,时间复杂度O(n)

  2. 分离新增/更新逻辑,避免重复操作

  3. 批量操作提升数据库性能

2.3.2 群聊数据同步

群聊的同步逻辑与好友完全一致,体现了代码的复用性设计:

@PostMapping("/insert_group1")
public void insert_group1(@RequestBody List<Group1> insertGroup1List) {
    // 相同的模式:查询现有 -> 比对 -> 分离 -> 批量操作
    List<Group1> existGroup1List = group1Mapper.selectList(null);
    Map<String, Group1> existGroup1Map = existGroup1List.stream()
        .collect(Collectors.toMap(Group1::getRoomId, Function.identity()));
    // ... 后续逻辑与好友相同
}
2.3.3 消息发送接口
@PostMapping("/send_text")
public void send_text(@RequestBody Msg msg) throws Exception {
    for (String tempUserOrRoomId : msg.getUserOrRoomIdList()) {
        Map<String, Object> result = sendTextMsg(tempUserOrRoomId, msg.getContent());
        log.info("发送结果: userOrRoomId={}, result={}", tempUserOrRoomId, result);
    }
}
2.3.4 微信消息接收处理
@PostMapping("/receive_python_msg")
public void receive_python_msg(@RequestBody Object pythonMsg) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    String jsonStr = mapper.writeValueAsString(pythonMsg);
    TextMsgJsonParse textMsgJsonParse = gson.fromJson(jsonStr, TextMsgJsonParse.class);
    int eventType = textMsgJsonParse.event_type;
    
    // 处理私聊(2004)和群聊(2000)消息
    if (eventType == 2004 || eventType == 2000) {
        long createTime = textMsgJsonParse.createTime;
        String tempTime = TimestampConverter.doWork(createTime);
        // 根据类型查询用户/群信息
        // 可接入智能回复逻辑
    }
}

2.4 统一响应封装

public class Res {
    private String code;
    private String message;
    private Object object;
    
    public static Res success(Object data) {
        Res res = new Res();
        res.setCode("200");
        res.setMessage("success");
        res.setObject(data);
        return res;
    }
    
    public static Res error(String message) {
        Res res = new Res();
        res.setCode("500");
        res.setMessage(message);
        return res;
    }
}

三、前端界面实现

3.1 布局设计

采用左右双栏布局,左侧展示好友列表,右侧展示群聊列表,底部为消息发送区域:

.dashboard {
    width: 1400px;
    height: 90vh;
    background: white;
    border-radius: 24px;
    display: flex;
    flex-direction: column;
}

.main-content {
    display: flex;
    flex: 1;
    gap: 1px;
    background: #eef2f6;
}

.friends-panel, .groups-panel {
    flex: 1;
    background: white;
    display: flex;
    flex-direction: column;
}

3.2 数据模型

let friends = [];          // 好友列表
let groups = [];           // 群聊列表
let selectedFriends = new Set();   // 选中好友ID集合
let selectedGroups = new Set();    // 选中群聊ID集合

使用Set数据结构存储选中项,具有以下优势:

  • 自动去重

  • O(1)时间复杂度的增删查

  • 与ES6语法完美兼容

3.3 API接口调用

获取好友列表
async function fetchFriends() {
    const response = await fetch(`${API_BASE}/select_friend`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' }
    });
    const resData = await response.json();
    
    // 兼容多种返回格式
    let friendList = resData.object || resData.data || resData;
    friends = friendList.map(f => ({
        userId: f.userId || f.user_id,
        nickName: f.nickName || f.nickname,
        remark: f.remark || ''
    })).filter(f => f.userId);
    
    renderFriends();
}
批量发送消息
async function batchSendMessage() {
    const content = document.getElementById('messageContent').value.trim();
    const userOrRoomIdList = [...selectedFriends, ...selectedGroups];
    
    const payload = {
        userOrRoomIdList: userOrRoomIdList,
        content: content
    };
    
    const response = await fetch(`${API_BASE}/send_text`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
    });
}

3.4 列表渲染与交互

function renderFriends() {
    let html = '';
    friends.forEach(friend => {
        const isSelected = selectedFriends.has(friend.userId);
        html += `
            <div class="contact-item ${isSelected ? 'selected' : ''}" 
                 data-type="friend" data-id="${escapeHtml(friend.userId)}">
                <div class="avatar">${escapeHtml(avatarLetter)}</div>
                <div class="info">
                    <div class="name">${escapeHtml(displayName)}</div>
                    <div class="remark">${escapeHtml(remarkText)}</div>
                </div>
                <div class="checkbox-indicator"></div>
            </div>
        `;
    });
    container.innerHTML = html;
    
    // 事件委托或逐个绑定
    container.querySelectorAll('.contact-item').forEach(el => {
        el.addEventListener('click', () => toggleSelection(el));
    });
}

3.5 选中状态管理

function toggleFriendSelection(userId) {
    if (selectedFriends.has(userId)) {
        selectedFriends.delete(userId);
    } else {
        selectedFriends.add(userId);
    }
    renderFriends();           // 重新渲染
    updateSelectionPreview();  // 更新预览区
}

function updateSelectionPreview() {
    const totalSelected = selectedFriends.size + selectedGroups.size;
    document.getElementById('selectedCount').innerText = totalSelected;
    
    // 构建详情显示
    const selectedFriendNames = [...selectedFriends].map(uid => {
        const f = friends.find(fr => fr.userId === uid);
        return f?.remark || f?.nickName || uid;
    });
    // 更新DOM...
}

四、关键技术点解析

4.1 跨域问题解决

前后端分离架构需要处理CORS:

@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                    .allowedOrigins("http://localhost:8080")
                    .allowedMethods("GET", "POST", "PUT", "DELETE")
                    .allowCredentials(true);
            }
        };
    }
}

4.2 MyBatis-Plus批量操作

自定义批量插入方法:

@Mapper
public interface FriendMapper extends BaseMapper<Friend> {
    void insertBatchFriend(@Param("list") List<Friend> list);
}

对应的XML:

<insert id="insertBatchFriend">
    INSERT INTO friend (user_id, nick_name, remark) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.userId}, #{item.nickName}, #{item.remark})
    </foreach>
</insert>

4.3 微信消息协议解析

微信回调消息格式:

{
    "event_type": 2004,
    "event_desc": "私聊消息",
    "content": {
        "String": "你好,这是一条测试消息"
    },
    "fromUserName": {
        "String": "wxid_xxxx"
    },
    "createTime": 1678901234567
}

使用嵌套静态类解析:

static class TextMsgJsonParse {
    int event_type;
    TextMsgContent content;
    TextMsgFromUserName fromUserName;
    long createTime;
}

static class TextMsgContent {
    String String;  // 注意字段名
}

4.4 状态提示与用户体验

function showToast(msg, isError = false) {
    const toastEl = document.getElementById('toast');
    toastEl.textContent = msg;
    toastEl.style.backgroundColor = isError ? '#b91c1c' : '#1e293b';
    toastEl.style.opacity = '1';
    setTimeout(() => {
        toastEl.style.opacity = '0';
    }, 2500);
}

五、部署与运行

5.1 后端配置

# application.yml
server:
  port: 5000

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/wechat_db
    username: root
    password: 123456

logging:
  level:
    com.black: DEBUG

5.2 前端部署

直接将HTML文件放入Spring Boot的static目录,或单独使用Nginx部署。

5.3 启动命令

# 后端
mvn spring-boot:run

# 前端(如使用live-server)
npx live-server --port=8080

六、优化建议与扩展

6.1 性能优化

  1. 虚拟滚动:当好友/群聊数量超过1000时,使用虚拟列表减少DOM节点

  2. 防抖节流:搜索输入时使用debounce减少请求

  3. 请求合并:批量操作时合并多个请求

6.2 功能扩展

  1. 消息模板:预定义常用消息模板,支持变量替换

  2. 定时发送:集成Quartz实现定时批量发送

  3. 发送记录:记录每条消息的发送状态和回执

  4. 消息去重:防止短时间内向同一用户重复发送相同内容

  5. 智能回复:接入AI大模型实现自动回复

6.3 安全增强

  1. 添加JWT认证,防止接口被恶意调用

  2. 对敏感操作添加验证码

  3. 记录操作日志用于审计

  4. 限制单次发送的数量上限

七、踩坑记录

7.1 Jackson解析问题

当使用@RequestBody Object接收时,Jackson会将其解析为LinkedHashMap,内部嵌套结构也是Map,需要注意类型转换。

7.2 微信ID特殊字符

微信ID可能包含特殊字符,在HTML渲染时需要使用escapeHtml进行转义,防止XSS攻击。

7.3 批量操作事务

当批量插入和更新混合时,建议使用@Transactional确保数据一致性:

@Transactional(rollbackFor = Exception.class)
public void insert_friend(@RequestBody List<Friend> insertFriendList) {
    // 操作逻辑
}

八、总结

本文完整实现了一套微信管理工具,涵盖后端API设计、前端界面开发、数据同步机制、批量消息发送等核心功能。系统采用Spring Boot + MyBatis-Plus提供稳定可靠的后端服务,使用原生JavaScript构建简洁高效的前端界面。

通过这套工具,运营人员可以快速完成批量消息发送任务,大幅提升工作效率。代码结构清晰,易于扩展,可作为企业微信管理系统的参考实现。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐