This commit is contained in:
van
2026-04-01 15:52:55 +08:00
parent 6a88a68320
commit 921c8a2374
9 changed files with 399 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.jarvis;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.jarvis.domain.dto.WeComInboundRequest;
import com.ruoyi.jarvis.service.IWeComInboundService;
import com.ruoyi.jarvis.service.IWeComInboundTraceService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
@@ -25,6 +26,8 @@ public class WeComInboundController {
@Resource
private IWeComInboundService weComInboundService;
@Resource
private IWeComInboundTraceService weComInboundTraceService;
@PostMapping("/inbound")
public AjaxResult inbound(
@@ -33,7 +36,9 @@ public class WeComInboundController {
if (!StringUtils.hasText(inboundSecret) || !inboundSecret.equals(secret)) {
return AjaxResult.error("拒绝访问");
}
String reply = weComInboundService.handleInbound(body != null ? body : new WeComInboundRequest());
WeComInboundRequest req = body != null ? body : new WeComInboundRequest();
String reply = weComInboundService.handleInbound(req);
weComInboundTraceService.recordInbound(req, reply);
Map<String, Object> data = new HashMap<>(2);
data.put("reply", reply != null ? reply : "");
return AjaxResult.success(data);

View File

@@ -0,0 +1,46 @@
package com.ruoyi.web.controller.jarvis;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.jarvis.domain.WeComInboundTrace;
import com.ruoyi.jarvis.service.IWeComInboundTraceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 企微 inbound 消息追踪查询
*/
@RestController
@RequestMapping("/jarvis/wecom/inboundTrace")
public class WeComInboundTraceController extends BaseController {
@Autowired
private IWeComInboundTraceService weComInboundTraceService;
@PreAuthorize("@ss.hasPermi('jarvis:wecom:inboundTrace:list')")
@GetMapping("/list")
public TableDataInfo list(WeComInboundTrace query) {
startPage();
List<WeComInboundTrace> list = weComInboundTraceService.selectWeComInboundTraceList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('jarvis:wecom:inboundTrace:list')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return success(weComInboundTraceService.selectWeComInboundTraceById(id));
}
@PreAuthorize("@ss.hasPermi('jarvis:wecom:inboundTrace:remove')")
@Log(title = "企微消息跟踪", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(weComInboundTraceService.deleteWeComInboundTraceByIds(ids));
}
}

View File

@@ -0,0 +1,134 @@
package com.ruoyi.jarvis.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import java.util.Date;
/**
* 企微 inbound 消息追踪 wecom_inbound_trace
*/
public class WeComInboundTrace extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long id;
@Excel(name = "消息ID")
private String msgId;
@Excel(name = "AgentID")
private String agentId;
@Excel(name = "CorpId")
private String corpId;
@Excel(name = "发送人UserID")
private String fromUserName;
private String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "微信发送时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date wxMsgTime;
private String replyContent;
@Excel(name = "会话进行中", readConverterExp = "0=否,1=是")
private Integer sessionActive;
@Excel(name = "会话场景")
private String sessionScene;
@Excel(name = "会话步骤")
private String sessionStep;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMsgId() {
return msgId;
}
public void setMsgId(String msgId) {
this.msgId = msgId;
}
public String getAgentId() {
return agentId;
}
public void setAgentId(String agentId) {
this.agentId = agentId;
}
public String getCorpId() {
return corpId;
}
public void setCorpId(String corpId) {
this.corpId = corpId;
}
public String getFromUserName() {
return fromUserName;
}
public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getWxMsgTime() {
return wxMsgTime;
}
public void setWxMsgTime(Date wxMsgTime) {
this.wxMsgTime = wxMsgTime;
}
public String getReplyContent() {
return replyContent;
}
public void setReplyContent(String replyContent) {
this.replyContent = replyContent;
}
public Integer getSessionActive() {
return sessionActive;
}
public void setSessionActive(Integer sessionActive) {
this.sessionActive = sessionActive;
}
public String getSessionScene() {
return sessionScene;
}
public void setSessionScene(String sessionScene) {
this.sessionScene = sessionScene;
}
public String getSessionStep() {
return sessionStep;
}
public void setSessionStep(String sessionStep) {
this.sessionStep = sessionStep;
}
}

View File

@@ -15,6 +15,8 @@ public class WeComInboundRequest {
private String toUserName;
private String agentId;
private String msgId;
/** 企微 XML CreateTime秒级 Unix 时间戳wxSend 传入) */
private Long wxCreateTime;
public String getFromUserName() {
return fromUserName;
@@ -55,4 +57,12 @@ public class WeComInboundRequest {
public void setMsgId(String msgId) {
this.msgId = msgId;
}
public Long getWxCreateTime() {
return wxCreateTime;
}
public void setWxCreateTime(Long wxCreateTime) {
this.wxCreateTime = wxCreateTime;
}
}

View File

@@ -0,0 +1,16 @@
package com.ruoyi.jarvis.mapper;
import com.ruoyi.jarvis.domain.WeComInboundTrace;
import java.util.List;
public interface WeComInboundTraceMapper {
int insertWeComInboundTrace(WeComInboundTrace trace);
WeComInboundTrace selectWeComInboundTraceById(Long id);
List<WeComInboundTrace> selectWeComInboundTraceList(WeComInboundTrace query);
int deleteWeComInboundTraceByIds(Long[] ids);
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.jarvis.service;
import com.ruoyi.jarvis.domain.WeComInboundTrace;
import com.ruoyi.jarvis.domain.dto.WeComInboundRequest;
import java.util.List;
public interface IWeComInboundTraceService {
void recordInbound(WeComInboundRequest request, String reply);
WeComInboundTrace selectWeComInboundTraceById(Long id);
List<WeComInboundTrace> selectWeComInboundTraceList(WeComInboundTrace query);
int deleteWeComInboundTraceByIds(Long[] ids);
}

View File

@@ -0,0 +1,73 @@
package com.ruoyi.jarvis.service.impl;
import com.ruoyi.jarvis.domain.WeComInboundTrace;
import com.ruoyi.jarvis.domain.dto.WeComChatSession;
import com.ruoyi.jarvis.domain.dto.WeComInboundRequest;
import com.ruoyi.jarvis.mapper.WeComInboundTraceMapper;
import com.ruoyi.jarvis.service.IWeComChatSessionService;
import com.ruoyi.jarvis.service.IWeComInboundTraceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
@Service
public class WeComInboundTraceServiceImpl implements IWeComInboundTraceService {
private static final Logger log = LoggerFactory.getLogger(WeComInboundTraceServiceImpl.class);
@Resource
private WeComInboundTraceMapper weComInboundTraceMapper;
@Resource
private IWeComChatSessionService weComChatSessionService;
@Override
public void recordInbound(WeComInboundRequest request, String reply) {
if (request == null || !StringUtils.hasText(request.getFromUserName())) {
return;
}
try {
String from = request.getFromUserName().trim();
WeComInboundTrace trace = new WeComInboundTrace();
trace.setMsgId(request.getMsgId());
trace.setAgentId(request.getAgentId());
trace.setCorpId(request.getToUserName());
trace.setFromUserName(from);
trace.setContent(request.getContent());
Long wxSec = request.getWxCreateTime();
if (wxSec != null && wxSec > 0) {
trace.setWxMsgTime(new Date(wxSec * 1000L));
}
trace.setReplyContent(reply != null ? reply : "");
WeComChatSession session = weComChatSessionService.get(from);
trace.setSessionActive(session != null ? 1 : 0);
if (session != null) {
trace.setSessionScene(session.getScene());
trace.setSessionStep(session.getStep());
}
weComInboundTraceMapper.insertWeComInboundTrace(trace);
} catch (Exception e) {
log.warn("企微消息追踪落库失败: {}", e.toString());
}
}
@Override
public WeComInboundTrace selectWeComInboundTraceById(Long id) {
return weComInboundTraceMapper.selectWeComInboundTraceById(id);
}
@Override
public List<WeComInboundTrace> selectWeComInboundTraceList(WeComInboundTrace query) {
return weComInboundTraceMapper.selectWeComInboundTraceList(query);
}
@Override
public int deleteWeComInboundTraceByIds(Long[] ids) {
return weComInboundTraceMapper.deleteWeComInboundTraceByIds(ids);
}
}

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.jarvis.mapper.WeComInboundTraceMapper">
<resultMap id="WeComInboundTraceResult" type="WeComInboundTrace">
<id property="id" column="id"/>
<result property="msgId" column="msg_id"/>
<result property="agentId" column="agent_id"/>
<result property="corpId" column="corp_id"/>
<result property="fromUserName" column="from_user_name"/>
<result property="content" column="content"/>
<result property="wxMsgTime" column="wx_msg_time"/>
<result property="replyContent" column="reply_content"/>
<result property="sessionActive" column="session_active"/>
<result property="sessionScene" column="session_scene"/>
<result property="sessionStep" column="session_step"/>
<result property="createTime" column="create_time"/>
</resultMap>
<sql id="selectVo">
select id, msg_id, agent_id, corp_id, from_user_name, content, wx_msg_time,
reply_content, session_active, session_scene, session_step, create_time
from wecom_inbound_trace
</sql>
<insert id="insertWeComInboundTrace" parameterType="WeComInboundTrace" useGeneratedKeys="true" keyProperty="id">
insert into wecom_inbound_trace (
msg_id, agent_id, corp_id, from_user_name, content, wx_msg_time,
reply_content, session_active, session_scene, session_step
) values (
#{msgId}, #{agentId}, #{corpId}, #{fromUserName}, #{content}, #{wxMsgTime},
#{replyContent}, #{sessionActive}, #{sessionScene}, #{sessionStep}
)
</insert>
<select id="selectWeComInboundTraceById" resultMap="WeComInboundTraceResult">
<include refid="selectVo"/>
where id = #{id}
</select>
<select id="selectWeComInboundTraceList" parameterType="WeComInboundTrace" resultMap="WeComInboundTraceResult">
<include refid="selectVo"/>
<where>
<if test="msgId != null and msgId != ''">and msg_id = #{msgId}</if>
<if test="fromUserName != null and fromUserName != ''">and from_user_name like concat('%', #{fromUserName}, '%')</if>
<if test="agentId != null and agentId != ''">and agent_id = #{agentId}</if>
<if test="sessionActive != null">and session_active = #{sessionActive}</if>
<if test="params.beginTime != null and params.beginTime != ''">
and create_time &gt;= #{params.beginTime}
</if>
<if test="params.endTime != null and params.endTime != ''">
and create_time &lt;= concat(#{params.endTime}, ' 23:59:59')
</if>
</where>
order by id desc
</select>
<delete id="deleteWeComInboundTraceByIds" parameterType="Long">
delete from wecom_inbound_trace where id in
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@@ -0,0 +1,33 @@
-- 企业微信桥接消息追踪Jarvis 落库,供后台查看)
DROP TABLE IF EXISTS `wecom_inbound_trace`;
CREATE TABLE `wecom_inbound_trace` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`msg_id` varchar(64) DEFAULT NULL COMMENT '企微消息 MsgId',
`agent_id` varchar(32) DEFAULT NULL COMMENT '应用 AgentID',
`corp_id` varchar(64) DEFAULT NULL COMMENT '企业 CorpIdXML ToUserName',
`from_user_name` varchar(128) NOT NULL COMMENT '发送人成员 UserIDFromUserName',
`content` mediumtext COMMENT '用户发送内容',
`wx_msg_time` datetime DEFAULT NULL COMMENT '微信侧 CreateTime秒级时间戳转换',
`reply_content` mediumtext COMMENT 'Jarvis 返回给 wxSend 的回复文本',
`session_active` tinyint(4) NOT NULL DEFAULT 0 COMMENT '处理完成后是否存在多轮会话0否 1是',
`session_scene` varchar(64) DEFAULT NULL COMMENT '会话场景(如 JD_LOGISTICS_SHARE',
`session_step` varchar(64) DEFAULT NULL COMMENT '会话步骤(如 WAIT_REMARK',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '服务端接收处理时间',
PRIMARY KEY (`id`),
KEY `idx_from_user` (`from_user_name`),
KEY `idx_msg_id` (`msg_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='企微 inbound 消息追踪';
-- 菜单:挂在「系统监控」下,与日志文件类似;执行后请给需要角色分配该菜单权限
INSERT INTO sys_menu VALUES (
2090, '企微消息跟踪', 2, 8, 'wecomInboundTrace', 'jarvis/wecomInboundTrace/index', '', '', 1, 0, 'C', '0', '0',
'jarvis:wecom:inboundTrace:list', 'wechat', 'admin', sysdate(), '', NULL, '企微回调经 Jarvis 处理记录'
);
INSERT INTO sys_menu VALUES (
2092, '删除', 2090, 1, '', '', '', '', 1, 0, 'F', '0', '0',
'jarvis:wecom:inboundTrace:remove', '#', 'admin', sysdate(), '', NULL, ''
);
-- 若需超级管理员角色默认拥有role_id 按实际调整,常见管理员为 1
-- INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 2090), (1, 2092);