zoujiajian 1 éve
szülő
commit
87776d1265

+ 35 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpKfAccountSessionChat.java

@@ -3,6 +3,8 @@ package com.cyksj.model.entity;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.util.Date;
+
 /**
  * 项目名: yhlxj2
  * 文件名: CorpKfAccountSessionChat
@@ -28,5 +30,38 @@ public class CorpKfAccountSessionChat extends BaseEntity{
 
 	private String msgContent;
 
+	/**
+	 * 	链接标题
+	 */
+	private String linkTitle;
+
+	/**
+	 * 链接描述
+	 */
+	private String linkDesc;
+
+	/**
+	 * 链接 点击后跳转的链接
+	 */
+	private String linkUrl;
+
+	/**
+	 * 链接 缩略图链接
+	 */
+	private String linkPicUrl;
+
+	/**
+	 * 秒长
+	 */
 	private Long sendTime;
+
+	/**
+	 * 发送日期
+	 */
+	private Date sendDate;
+
+	/**
+	 * 图片、视频、文件展示链接
+	 */
+	private String url;
 }

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/CorpKfServicerSessionRecord.java

@@ -21,7 +21,7 @@ public class CorpKfServicerSessionRecord extends BaseEntity{
 	 */
 	private String fromUserid;
 
-	private String servicerUserId;
+	private String servicerUserid;
 
 	private String externalUserid;
 

+ 16 - 0
netflix-dao/src/main/java/com/cyksj/model/kf/WxCorpFileMsg.java

@@ -0,0 +1,16 @@
+package com.cyksj.model.kf;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: WxCorpFileMsg
+ * 创建者: JavaZou
+ * 创建时间:2025/1/6 17:40
+ */
+@Getter
+@Setter
+public class WxCorpFileMsg {
+	private String media_id;
+}

+ 16 - 0
netflix-dao/src/main/java/com/cyksj/model/kf/WxCorpImageMsg.java

@@ -0,0 +1,16 @@
+package com.cyksj.model.kf;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: WxCorpImageMsg
+ * 创建者: JavaZou
+ * 创建时间:2025/1/6 17:29
+ */
+@Getter
+@Setter
+public class WxCorpImageMsg {
+	private String media_id;
+}

+ 26 - 0
netflix-dao/src/main/java/com/cyksj/model/kf/WxCorpLinkMsg.java

@@ -0,0 +1,26 @@
+package com.cyksj.model.kf;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: WxCorpLinkMsg
+ * 创建者: JavaZou
+ * 创建时间:2025/1/6 17:39
+ */
+@Getter
+@Setter
+public class WxCorpLinkMsg {
+	//链接消息
+	private String title;
+
+	//描述
+	private String desc;
+
+	//点击后跳转的链接
+	private String url;
+
+	//缩略图链接
+	private String pic_url;
+}

+ 69 - 0
netflix-dao/src/main/java/com/cyksj/model/kf/WxCorpMenuMsg.java

@@ -0,0 +1,69 @@
+package com.cyksj.model.kf;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: WxCorpMenuMsg
+ * 创建者: JavaZou
+ * 创建时间:2025/1/6 17:41
+ */
+@Getter
+@Setter
+public class WxCorpMenuMsg {
+	//	起始文本
+	private String head_content;
+
+	/**
+	 * 菜单项配置
+	 */
+	private List<ListData> list;
+
+	//结束文本
+	private String tail_content;
+
+	@Getter
+	@Setter
+	public static class ListData{
+		//类型
+		private String type;
+
+		//点击
+		private Click click;
+
+		//跳转页面
+		private View view;
+
+		//小程序
+		private Miniprogram miniprogram;
+
+		@Getter
+		@Setter
+		public static class Click {
+			private String id;
+
+			private String content;
+		}
+
+		@Getter
+		@Setter
+		public static class View {
+			private String url;
+
+			private String content;
+		}
+
+		@Getter
+		@Setter
+		public static class Miniprogram {
+			private String appid;
+
+			private String pagepath;
+
+			private String content;
+		}
+	}
+}

+ 19 - 0
netflix-dao/src/main/java/com/cyksj/model/kf/WxCorpVideoMsg.java

@@ -0,0 +1,19 @@
+package com.cyksj.model.kf;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: WxCorpVideoMsg
+ * 创建者: JavaZou
+ * 创建时间:2025/1/6 17:28
+ */
+@Getter
+@Setter
+public class WxCorpVideoMsg {
+	/**
+	 * 图片文件id
+	 */
+	private String media_id;
+}

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/model/kf/WxCpKfMsgListResp.java

@@ -58,5 +58,15 @@ public class WxCpKfMsgListResp {
     private WxCorpChatMsg mergedMsg;
     //文本消息
     private WxCorpTextMsg text;
+    //图片消息
+    private WxCorpImageMsg image;
+    //视频消息
+    private WxCorpVideoMsg video;
+    //链接消息
+    private WxCorpLinkMsg link;
+    //文件消息
+    private WxCorpFileMsg file;
+    //菜单消息
+    private WxCorpMenuMsg msgmenu;
   }
 }

+ 1 - 1
netflix-dao/src/main/resources/mapper/CorpKfServicerSessionRecordMapper.xml

@@ -6,7 +6,7 @@
 
     <select id="getAvailableServicer" resultType="com.cyksj.model.dto.CorpKfServicerDto">
         select servicer_userid,count(1) as num
-        from from corp_kf_servicer_session_reocrd
+        from corp_kf_servicer_session_record
         where open_kf_id = #{openKfId}
           and service_state = 0
         group by servicer_userid

+ 5 - 0
netflix-service/src/main/java/com/cyksj/service/corp/WxCorpOps.java

@@ -260,4 +260,9 @@ public interface WxCorpOps {
 	void sendTextMsg(String corpId, WxCorpKfMsgSendRequest wxCpKfMsgSendRequest) throws Exception;
 
 	Integer getCorpKfSessionStatus(String corpId, String openKfId, String externalUserid) throws Exception;
+
+	/**
+	 * 获取素材
+	 */
+	String getMedia(String corpId, String mediaId) throws Exception;
 }

+ 75 - 21
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java

@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.constant.Constant;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.StringUtil;
 import com.cyksj.common.util.Xmls;
 import com.cyksj.common.util.corp.WxCryptUtil;
@@ -573,7 +574,6 @@ public class CorpServiceImpl implements CorpService {
 		List<WxCpKfMsgListResp.WxCpKfMsgItem> enterSessionList = null;
 		List<WxCpKfMsgListResp.WxCpKfMsgItem> sessionStatusChanges = null;
 		List<WxCorpChatMsg> mergedMsgs = null;
-		List<WxCpKfMsgListResp.WxCpKfMsgItem> textList = null;
 		for (WxCpKfMsgListResp.WxCpKfMsgItem item : msgList) {
 			if (item.getEvent() != null && item.getEvent().getEventType().equals("enter_session")) {
 				//会话事件
@@ -591,23 +591,8 @@ public class CorpServiceImpl implements CorpService {
 					sessionStatusChanges.add(item);
 				}
 			}
-			if (item.getMergedMsg() != null) {
-				if (mergedMsgs == null) {
-					mergedMsgs = new ArrayList<>();
-				}
-				WxCorpChatMsg mergedMsg = item.getMergedMsg();
-				mergedMsg.setServicer_userid(item.getServicerUserId());
-				mergedMsg.setExternal_userid(item.getExternalUserId());
-				mergedMsg.setMsgid(item.getMsgId());
-				mergedMsg.setOrigin(item.getOrigin());
-				mergedMsgs.add(mergedMsg);
-			}
-			if (StrUtil.equals("text", item.getMsgType())) {
-				if (textList == null) {
-					textList = new ArrayList<>();
-				}
-				textList.add(item);
-			}
+			//同步客户聊天记录
+			syncKfChatMsg(item);
 		}
 		cursor = wxCpKfMsgListResp.getNextCursor();
 		//进入会话事件
@@ -617,7 +602,7 @@ public class CorpServiceImpl implements CorpService {
 		//同步客服聊天记录
 		syncChatMsg(openKfId, mergedMsgs);
 		//同步满意度
-		syncSessionSatisfaction(openKfId, textList, corpId, corpKfAccountSessionConfig);
+		syncSessionText(openKfId, textList, corpId, corpKfAccountSessionConfig);
 		corpKfAccountSyncMsgProgress.setNextCursor(cursor);
 		//同步信息
 		while (wxCpKfMsgListResp.getHasMore() == 1) {
@@ -625,6 +610,59 @@ public class CorpServiceImpl implements CorpService {
 		}
 	}
 
+	private void syncKfChatMsg(WxCpKfMsgListResp.WxCpKfMsgItem item) throws Exception {
+		String openKfid = item.getOpenKfid();
+		//客户id
+		String externalUserId = item.getExternalUserId();
+		//接待人员id
+		String servicerUserId = item.getServicerUserId();
+		//消息来源。3-微信客户发送的消息 4-系统推送的事件消息 5-接待人员在企业微信客户端发送的消息
+		Integer origin = item.getOrigin();
+		//发送时间
+		Long sendTime = item.getSendTime();
+		//消息id
+		String msgId = item.getMsgId();
+		//消息类型
+		String msgType = item.getMsgType();
+		CorpKfAccountSessionChat corpKfAccountSessionChat = new CorpKfAccountSessionChat();
+		corpKfAccountSessionChat.setOpenKfId(openKfid);
+		corpKfAccountSessionChat.setMsgid(msgId);
+		corpKfAccountSessionChat.setServicerUserid(servicerUserId);
+		corpKfAccountSessionChat.setExternalUserid(externalUserId);
+		corpKfAccountSessionChat.setOrign(origin);
+		corpKfAccountSessionChat.setMsgType(msgType);
+		corpKfAccountSessionChat.setSendTime(sendTime);
+		corpKfAccountSessionChat.setSendDate(DateTime.of(sendTime * 1000));
+		//文本
+		if (item.getText() != null) {
+			corpKfAccountSessionChat.setMsgContent(item.getText().getContent());
+		} else if (item.getMergedMsg() != null) {
+			//聊天记录 转发
+			corpKfAccountSessionChat.setMsgContent(Jsons.toJson(item.getMergedMsg().getItem()));
+		} else if (item.getVideo() != null) {
+			//视频记录
+			setMediaUrl(corpKfAccountSessionChat, item.getVideo().getMedia_id());
+		} else if (item.getFile() != null) {
+			//文件
+			setMediaUrl(corpKfAccountSessionChat, item.getFile().getMedia_id());
+		} else if (item.getImage() != null) {
+			//图片
+			setMediaUrl(corpKfAccountSessionChat, item.getImage().getMedia_id());
+		} else if (item.getLink() != null) {
+			//链接
+			WxCorpLinkMsg link = item.getLink();
+			corpKfAccountSessionChat.setLinkTitle(link.getTitle());
+			corpKfAccountSessionChat.setLinkDesc(link.getDesc());
+			corpKfAccountSessionChat.setLinkUrl(link.getUrl());
+			corpKfAccountSessionChat.setLinkPicUrl(link.getPic_url());
+		} else if (item.getMsgmenu() != null) {
+			//菜单 结束语统计
+
+		}
+		//新增
+		saveChatMsg(corpKfAccountSessionChat);
+	}
+
 	private void syncSessionSatisfaction(String openKfId, List<WxCpKfMsgListResp.WxCpKfMsgItem> textList, String corpId, CorpKfAccountSessionConfig corpKfAccountSessionConfig) throws Exception {
 		if (textList == null) {
 			return;
@@ -738,7 +776,7 @@ public class CorpServiceImpl implements CorpService {
 		if (selectCount == 0) {
 			if (corpKfAccountSessionConfig != null) {
 				Integer maxNum = corpKfServicerSessionRecordMapper.selectCount(Wrappers.lambdaQuery(CorpKfServicerSessionRecord.class)
-						.eq(CorpKfServicerSessionRecord::getServicerUserId, servicerUserid)
+						.eq(CorpKfServicerSessionRecord::getServicerUserid, servicerUserid)
 						.eq(CorpKfServicerSessionRecord::getServiceState, 0));
 				if (maxNum < corpKfAccountSessionConfig.getMaxNum()) {
 					//记录新的客服会话
@@ -756,6 +794,7 @@ public class CorpServiceImpl implements CorpService {
 		String openKfId = corpKfAccount.getOpenKfId();
 		if (enterSessionList != null && enterSessionList.size() > 0) {
 			WxCpKfMsgListResp.WxCpKfMsgItem wxCpKfMsgItem = enterSessionList.get(enterSessionList.size() - 1);
+			DateTime sendTime = DateTime.of(wxCpKfMsgItem.getSendTime() * 1000);
 			WxCpKfEventMsg event = wxCpKfMsgItem.getEvent();
 			String welcomeCode = event.getWelcomeCode();
 			//如果满足发送欢迎语条件(条件为:用户在过去48小时里未收过欢迎语,且未向客服发过消息),会返回该字段。
@@ -870,7 +909,7 @@ public class CorpServiceImpl implements CorpService {
 			CorpKfServicerSessionRecord corpKfServicerSessionRecord = new CorpKfServicerSessionRecord();
 			corpKfServicerSessionRecord.setOpenKfId(openKfId);
 			corpKfServicerSessionRecord.setServiceState(0);
-			corpKfServicerSessionRecord.setServicerUserId(userid);
+			corpKfServicerSessionRecord.setServicerUserid(userid);
 			corpKfServicerSessionRecord.setExternalUserid(externalUserId);
 			corpKfServicerSessionRecord.setChangeType(changeType);
 			corpKfServicerSessionRecordMapper.insert(corpKfServicerSessionRecord);
@@ -917,4 +956,19 @@ public class CorpServiceImpl implements CorpService {
 
 		}
 	}
+
+	/**
+	 * 设置临时素材url
+	 */
+	private void setMediaUrl(CorpKfAccountSessionChat corpKfAccountSessionChat, String mediaId) throws Exception {
+		String url = wxCorpOps.getMedia(null, mediaId);
+		corpKfAccountSessionChat.setUrl(url);
+	}
+
+	/**
+	 * 新增记录
+	 */
+	public void saveChatMsg(CorpKfAccountSessionChat chat) {
+		corpKfAccountSessionChatMapper.insert(chat);
+	}
 }

+ 36 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/WxCorpOpsImpl.java

@@ -36,6 +36,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.imageio.stream.FileImageOutputStream;
 import java.io.File;
 import java.net.URLEncoder;
 import java.net.http.HttpRequest;
@@ -1143,6 +1144,41 @@ public class WxCorpOpsImpl implements WxCorpOps {
 		return re.getInt("service_state");
 	}
 
+	@Override
+	public String getMedia(String corpId, String mediaId) throws Exception {
+		if (StringUtils.isBlank(mediaId)){
+			return null;
+		}
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=" + getAccessToken(corpId) + "&media_id=" + mediaId;
+		HttpResponse<byte []> res =
+				J11HttpC.custom()
+						.ofGet()
+						.url(url)
+						.send(HttpResponse.BodyHandlers.ofByteArray());
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("企业微信获取素材错误:" + res.body());
+		}
+		// 提取文件名
+		String contentDisposition = res.headers().firstValue("Content-Disposition").orElse("");
+		String fileName = null;
+		if (contentDisposition.contains("filename=")) {
+			fileName = contentDisposition.substring(contentDisposition.indexOf("filename=") + 9).replace("\"", "");
+		}
+		byte[] body = res.body();
+		File file = new File(System.currentTimeMillis() + "." + StrUtil.subAfter(fileName, StrUtil.DOT, true));
+		FileImageOutputStream imageOutput = new FileImageOutputStream(file);
+
+		imageOutput.write(body, 0, body.length);
+		//关闭流
+		imageOutput.flush();
+		Map<String, Object> paramMap = new HashMap<>();
+		//文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别
+		paramMap.put("file", FileUtil.file(file.getAbsolutePath()));
+		String result= HttpUtil.post(" https://files.yicanggongyi.com/pic/ups?ossAccount=Sx", paramMap);
+		FileUtil.del(file);
+		return JSONUtil.parseObj(result).getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
+	}
+
 	public static void main(String[] args) throws Exception {
 		String accessToken = "cLKd5cAJOW_0OW2Dv0Lk91_cbYBhUrKDfk-DOwdf2395J6yxoshl9BEpVIq0s4vXunYLDA8IU6-fz8ZN4yZRAARFdXEhcS8Hi9LdXhjYLqD8fGtCr2AV7ZkXOGAMzbyWDbPUERRcERE9Zp3nWnIl4xS7IBJx3AHcujPVDyqxocKRrvtBP3Rv-7QH6yU81p-VF-AdteZMrCkLC_kjFiloQg";
 		String url = "https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/get?access_token=" + accessToken;

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/wxkf/CorpKfController.java

@@ -203,7 +203,7 @@ public class CorpKfController {
 	/**
 	 * 客服上下班
 	 */
-	@PutMapping("/servicer/commute/{openKfId}")
+	@PutMapping("/servicer/commute")
 	@Log(module = "微信客服/客服上下班", businessType = BusinessType.PUT, isSaveRequestData = true)
 	public Result<String> servicerCommute(@RequestBody CorpKfDutySetReq corpKfDutySetReq) throws Exception {
 		long cmsUserId = StpUtil.getLoginIdAsLong();

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/wxkf/CorpKfMsgController.java

@@ -78,7 +78,7 @@ public class CorpKfMsgController {
 	@GetMapping("/get/user/session")
 	public Result<CorpKfServicerSessionRecord> getUserSession(String userid, String externalUserid) {
 		CorpKfServicerSessionRecord corpKfServicerSessionRecord = corpKfServicerSessionRecordMapper.selectOne(Wrappers.lambdaQuery(CorpKfServicerSessionRecord.class)
-				.eq(CorpKfServicerSessionRecord::getServicerUserId, userid)
+				.eq(CorpKfServicerSessionRecord::getServicerUserid, userid)
 				.eq(CorpKfServicerSessionRecord::getExternalUserid, externalUserid)
 				.eq(CorpKfServicerSessionRecord::getServiceState, 0)
 				.last("limit 1"));