Jelajahi Sumber

fix 多公众号

zoujiajian 3 tahun lalu
induk
melakukan
3f04d9172e

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/gzh/UserGzhRelationMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.gzh;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.UserGzhRelation;
+
+/*
+ *项目名: netflix
+ *文件名: UserGzhRelationMapper
+ *创建者: JavaZou
+ *创建时间:2023/1/16 10:58
+ */
+public interface UserGzhRelationMapper extends BaseMapper<UserGzhRelation> {
+}

+ 157 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/MsgEvent.java

@@ -0,0 +1,157 @@
+package com.cyksj.model.dto;
+
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * @author valor.
+ * @date 2019-08-02 12:53
+ */
+@Getter
+@Setter
+@JacksonXmlRootElement(localName = "xml")
+public class MsgEvent {
+
+    /**
+     * 开发者微信号
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "ToUserName")
+    private String toUserName;
+
+    /**
+     * 发送方帐号 (一个OpenID)
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "FromUserName")
+    private String fromUserName;
+
+    /**
+     * 消息创建时间 (整型)
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "CreateTime")
+    private Long createTime;
+
+    /**
+     * 消息类型
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "MsgType")
+    private MsgType msgType;
+
+    /**
+     * 当消息为event时 触发的事件类型
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "Event")
+    private Event event;
+
+    /**
+     * 事件KEY值
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "EventKey")
+    private String eventKey;
+
+    /**
+     * 二维码扫码时 会出现该参数
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "Ticket")
+    private String ticket;
+
+    /**
+     * 当消息为text时 消息内容
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "Content")
+    private String content;
+
+    /**
+     * 图文消息个数
+     *   当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条图文消息
+     *   其余场景最多可回复8条图文消息
+     */
+    @JacksonXmlCData
+    @JacksonXmlProperty(localName = "ArticleCount")
+    private Integer articleCount;
+
+    /**
+     * 图文消息信息,注意,如果图文数超过限制,则将只发限制内的条数
+     */
+    @JacksonXmlProperty(localName = "item")
+    @JacksonXmlElementWrapper(localName = "Articles")
+    private List<Article> articles;
+
+    public enum MsgType {
+        /**
+         * 消息类型
+         */
+        event("事件消息"),
+        text("文本消息"),
+        news("图文消息"),
+        image("图片消息"),
+        voice("语音消息"),
+        video("视频消息"),
+        shortvideo("小视频消息"),
+        location("地理位置消息"),
+        link("链接消息");
+
+        MsgType(String desc) { }
+    }
+
+    public enum Event {
+        /**
+         * 当消息为event时 可能是下列事件
+         */
+        subscribe("订阅"),
+        unsubscribe("取消订阅"),
+        SCAN("订阅状态中, 扫码二维码"),
+        LOCATION("上报地理位置"),
+        CLICK("点击自定义菜单(拉取子菜单 微信不会调用接口)"),
+        VIEW("点击菜单跳转链接"),
+        TEMPLATESENDJOBFINISH("模板消息"),
+        view_miniprogram("点击菜单跳转小程序");
+
+        Event(String desc) { }
+    }
+
+    @Getter
+    @Setter
+    public static class Article {
+        /**
+         * 扫码 图文消息标题
+         */
+        @JacksonXmlCData
+        @JacksonXmlProperty(localName = "Title")
+        private String title;
+
+        /**
+         * 扫码 图文消息描述
+         */
+        @JacksonXmlCData
+        @JacksonXmlProperty(localName = "Description")
+        private String desc;
+
+        /**
+         * 扫码 图文消息图片链接
+         */
+        @JacksonXmlCData
+        @JacksonXmlProperty(localName = "PicUrl")
+        private String picUrl;
+
+        /**
+         * 扫码 图文消息跳转链接
+         */
+        @JacksonXmlCData
+        @JacksonXmlProperty(localName = "Url")
+        private String link;
+    }
+}

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

@@ -0,0 +1,35 @@
+package com.cyksj.model.entity;
+
+import com.cyksj.model.dto.MsgEvent;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: UserGzhRelation
+ *创建者: JavaZou
+ *创建时间:2023/1/16 10:56
+ */
+@Getter
+@Setter
+public class UserGzhRelation {
+	private Long wxAppId;
+
+	private Long userId;
+
+	private String openId;
+
+	private String unionid;
+
+	private MsgEvent.Event event;
+
+	/**
+	 * 是否关注
+	 */
+	private Boolean isSubscribe;
+
+	/**
+	 * 是否是扫描二维码
+	 */
+	private Boolean isQrScene;
+}

+ 13 - 0
netflix-service/src/main/java/com/cyksj/service/gzh/GzhService.java

@@ -0,0 +1,13 @@
+package com.cyksj.service.gzh;
+
+import com.cyksj.model.dto.MsgEvent;
+
+/*
+ *项目名: netflix
+ *文件名: GzhService
+ *创建者: JavaZou
+ *创建时间:2023/1/13 17:09
+ */
+public interface GzhService {
+	String gzhMsg(MsgEvent msgEvent);
+}

+ 106 - 0
netflix-service/src/main/java/com/cyksj/service/gzh/factory/EventMsgService.java

@@ -0,0 +1,106 @@
+package com.cyksj.service.gzh.factory;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.mapper.UserMapper;
+import com.cyksj.mapper.WxAppMapper;
+import com.cyksj.mapper.gzh.UserGzhRelationMapper;
+import com.cyksj.model.dto.GzhUnionidUserinfo;
+import com.cyksj.model.dto.MsgEvent;
+import com.cyksj.model.entity.User;
+import com.cyksj.model.entity.UserGzhRelation;
+import com.cyksj.model.entity.WxApp;
+import com.cyksj.service.wechat.WeChatService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/*
+ *项目名: netflix
+ *文件名: EventMsgService
+ *创建者: JavaZou
+ *创建时间:2023/1/13 18:16
+ */
+@Service("event")
+@RequiredArgsConstructor
+@Slf4j
+public class EventMsgService implements GzhMsgService{
+	private final UserMapper userMapper;
+
+	private final UserGzhRelationMapper userGzhRelationMapper;
+
+	private final WeChatService weChatService;
+
+	private final WxAppMapper wxAppMapper;
+
+	@Override
+	public String handleNotify(MsgEvent msgEvent) {
+		try {
+			if (msgEvent.getEvent() == MsgEvent.Event.subscribe) {
+				String openId = msgEvent.getFromUserName();
+				if (StrUtil.isEmpty(openId)) {
+					return "";
+				}
+				//该用户是否存在于用户表里面
+				User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class)
+						.eq(User::getOpenId, openId)
+						.select(User::getId)
+						.last("limit 1"));
+				GzhUnionidUserinfo info = weChatService.getUserInfo2(weChatService.getAccessToken(), openId);
+				if (user == null) {
+					user.setNickname(info.getNickname())
+							.setSex(info.getSex())
+							.setCountry(info.getCountry())
+							.setProvince(info.getProvince())
+							.setCity(info.getCity())
+							.setHeadimgurl(info.getHeadimgurl());
+					user.setOpenId(openId);
+					user.setUnionid(info.getUnionid());
+					userMapper.insert(user);
+
+					saveGzhUserRelation(msgEvent, user.getId(), openId, info.getUnionid());
+				} else {
+					//之前关注过
+					if (user.getUnionid() == null || !user.getUnionid().equals(info.getUnionid())) {
+						user.setUnionid(info.getUnionid());
+						userMapper.updateById(user);
+					}
+					UserGzhRelation userGzhRelation = userGzhRelationMapper.selectOne(Wrappers.lambdaQuery(UserGzhRelation.class)
+							.eq(UserGzhRelation::getOpenId, msgEvent.getFromUserName()).last("limit 1"));
+					if (userGzhRelation == null) {
+						saveGzhUserRelation(msgEvent, user.getId(), openId, info.getUnionid());
+					}
+				}
+			} else if (msgEvent.getEvent() == MsgEvent.Event.unsubscribe) {
+				UserGzhRelation userGzhRelation = userGzhRelationMapper.selectOne(Wrappers.lambdaQuery(UserGzhRelation.class)
+						.eq(UserGzhRelation::getOpenId, msgEvent.getFromUserName()).last("limit 1"));
+				if (userGzhRelation == null) {
+					return "";
+				}
+				userGzhRelation.setIsSubscribe(false);
+				userGzhRelationMapper.updateById(userGzhRelation);
+			}
+			return "";
+		} catch (Exception e) {
+			log.error("接收微信公众:{},报错:{]", msgEvent.getToUserName(), e == null ? "" : e.getMessage());
+			return "";
+		}
+	}
+
+	private void saveGzhUserRelation(MsgEvent msgEvent, Long userId, String openId, String unionid) {
+		UserGzhRelation userGzhRelation = new UserGzhRelation();
+		userGzhRelation.setUserId(userId);
+
+		WxApp wxApp = wxAppMapper.selectOne(Wrappers.lambdaQuery(WxApp.class)
+				.eq(WxApp::getAppId, msgEvent.getToUserName()).last("limit 1"));
+		userGzhRelation.setWxAppId(wxApp.getId());
+		userGzhRelation.setIsSubscribe(true);
+		userGzhRelation.setEvent(msgEvent.getEvent());
+		userGzhRelation.setOpenId(openId);
+		userGzhRelation.setUnionid(unionid);
+		if (StrUtil.isNotEmpty(msgEvent.getEventKey())) {
+			userGzhRelation.setIsQrScene(true);
+		}
+		userGzhRelationMapper.insert(userGzhRelation);
+	}
+}

+ 13 - 0
netflix-service/src/main/java/com/cyksj/service/gzh/factory/GzhMsgService.java

@@ -0,0 +1,13 @@
+package com.cyksj.service.gzh.factory;
+
+import com.cyksj.model.dto.MsgEvent;
+
+/*
+ *项目名: netflix
+ *文件名: GzhMsgService
+ *创建者: JavaZou
+ *创建时间:2023/1/13 18:22
+ */
+public interface GzhMsgService{
+	String handleNotify(MsgEvent msgEvent);
+}

+ 28 - 0
netflix-service/src/main/java/com/cyksj/service/gzh/factory/GzhMsgServiceFactory.java

@@ -0,0 +1,28 @@
+package com.cyksj.service.gzh.factory;
+
+import com.cyksj.model.dto.MsgEvent;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/*
+ *项目名: netflix
+ *文件名: GzhMsgServiceFactory
+ *创建者: JavaZou
+ *创建时间:2023/1/13 18:16
+ */
+@Component
+public class GzhMsgServiceFactory {
+
+	@Resource
+	Map<String, GzhMsgService> ghzMsgServiceMap = new ConcurrentHashMap<>();
+
+
+	public String handleMsg(MsgEvent msgEvent) {
+		GzhMsgService gzhMsgService = ghzMsgServiceMap.get(msgEvent.getMsgType());
+
+		return gzhMsgService.handleNotify(msgEvent);
+	}
+}

+ 20 - 0
netflix-service/src/main/java/com/cyksj/service/gzh/factory/TextMsgService.java

@@ -0,0 +1,20 @@
+package com.cyksj.service.gzh.factory;
+
+import com.cyksj.model.dto.MsgEvent;
+import org.springframework.stereotype.Service;
+
+/*
+ *项目名: netflix
+ *文件名: TextMsgService
+ *创建者: JavaZou
+ *创建时间:2023/1/13 18:16
+ */
+@Service("text")
+public class TextMsgService implements GzhMsgService{
+
+	@Override
+	public String handleNotify(MsgEvent msgEvent) {
+
+		return "";
+	}
+}

+ 26 - 0
netflix-service/src/main/java/com/cyksj/service/gzh/impl/GzhServiceImpl.java

@@ -0,0 +1,26 @@
+package com.cyksj.service.gzh.impl;
+
+import com.cyksj.model.dto.MsgEvent;
+import com.cyksj.service.gzh.GzhService;
+import com.cyksj.service.gzh.factory.GzhMsgServiceFactory;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/*
+ *项目名: netflix
+ *文件名: GzhServiceImpl
+ *创建者: JavaZou
+ *创建时间:2023/1/13 17:09
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class GzhServiceImpl implements GzhService {
+	private final GzhMsgServiceFactory gzhMsgServiceFactory;
+
+	@Override
+	public String gzhMsg(MsgEvent msgEvent){
+		return gzhMsgServiceFactory.handleMsg(msgEvent);
+	}
+}

+ 68 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/gzh/GzhController.java

@@ -0,0 +1,68 @@
+package com.cyksj.web.controller.manage.gzh;
+
+import cn.hutool.core.util.StrUtil;
+import com.cyksj.common.util.IoKit;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.common.util.Xmls;
+import com.cyksj.model.dto.MsgEvent;
+import com.cyksj.service.gzh.GzhService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/*
+ *项目名: netflix
+ *文件名: GzhController
+ *创建者: JavaZou
+ *创建时间:2023/1/13 16:50
+ */
+@RestController
+@RequestMapping("/manage/gzh")
+@RequiredArgsConstructor
+@Slf4j
+public class GzhController {
+	private final HttpServletRequest request;
+
+	private final GzhService gzhService;
+
+	@RequestMapping(value = "/msg/notify", method = {RequestMethod.GET, RequestMethod.POST})
+	public String msgNotify(@RequestParam(value = "msg_signature") String msgSignature, String timestamp, String nonce, @RequestParam(value = "echostr", required = false) String echoStr) throws IOException {
+		if (StrUtil.hasEmpty(msgSignature, timestamp, nonce)) {
+			return StringUtil.EMPTY;
+		}
+		if (StrUtil.endWithIgnoreCase(request.getMethod(), RequestMethod.GET.name())) {
+			if (StrUtil.isBlank(echoStr)) {
+				return StrUtil.EMPTY;
+			}
+			//验证消息推送URL是否正确
+			return echoStr;
+		}
+
+		String content = IoKit.toString(request.getInputStream(), IoKit.Charsets.UTF_8);
+		log.info("公众号消息推送通知: \n{}", content);
+
+		if (StringUtils.isBlank(content)) {
+			return StringUtil.EMPTY;
+		}
+		MsgEvent msgEvent;
+		try {
+			msgEvent = Xmls.parseObject(content, MsgEvent.class);
+		} catch (Exception e) {
+			log.warn(" ! 解析公众号推送XML 失败.");
+			return "";
+		}
+		if (null == msgEvent) {
+			return "";
+		}
+
+		if (msgEvent.getMsgType() == null) {
+			return "";
+		}
+
+		return gzhService.gzhMsg(msgEvent);
+	}
+}