diff --git a/austin-common/src/main/java/com/java3y/austin/common/constant/OfficialAccountParamConstant.java b/austin-common/src/main/java/com/java3y/austin/common/constant/OfficialAccountParamConstant.java index 19143e6..9660f10 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/constant/OfficialAccountParamConstant.java +++ b/austin-common/src/main/java/com/java3y/austin/common/constant/OfficialAccountParamConstant.java @@ -24,4 +24,16 @@ public class OfficialAccountParamConstant { public static final String UNSUBSCRIBE_HANDLER = "unSubscribeHandler"; + /** + * 配置的beanName名 + */ + public static final String WE_CHAT_LOGIN_CONFIG = "weChatLoginConfig"; + + + /** + * 二维码场景值的前缀 + */ + public static final String QR_CODE_SCENE_PREFIX = "qrscene_"; + + } diff --git a/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java b/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java index 26f61f5..556c833 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java +++ b/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java @@ -14,45 +14,45 @@ import lombok.ToString; @ToString @AllArgsConstructor public enum RespStatusEnum { - /** - * OK:操作成功 - */ - SUCCESS("0", "操作成功"), - FAIL("-1", "操作失败"), - - - /** - * 客户端 - */ - CLIENT_BAD_PARAMETERS("A0001", "客户端参数错误"), - TEMPLATE_NOT_FOUND("A0002", "找不到模板或模板已被删除"), - TOO_MANY_RECEIVER("A0003", "传入的接收者大于100个"), - - /** - * 系统 - */ - SERVICE_ERROR("B0001", "服务执行异常"), - RESOURCE_NOT_FOUND("B0404", "资源不存在"), - - - /** - * pipeline - */ - CONTEXT_IS_NULL("P0001","流程上下文为空"), - BUSINESS_CODE_IS_NULL("P0002","业务代码为空"), - PROCESS_TEMPLATE_IS_NULL("P0003","流程模板配置为空"), - PROCESS_LIST_IS_NULL("P0004","业务处理器配置为空" ), - - - - ; - - /** - * 响应状态 - */ - private final String code; - /** - * 响应编码 - */ - private final String msg; + /** + * OK:操作成功 + */ + SUCCESS("0", "操作成功"), + FAIL("-1", "操作失败"), + + + /** + * 客户端 + */ + CLIENT_BAD_PARAMETERS("A0001", "客户端参数错误"), + TEMPLATE_NOT_FOUND("A0002", "找不到模板或模板已被删除"), + TOO_MANY_RECEIVER("A0003", "传入的接收者大于100个"), + NOT_LOGIN("A0004", "非测试环境,无须登录"), + + /** + * 系统 + */ + SERVICE_ERROR("B0001", "服务执行异常"), + RESOURCE_NOT_FOUND("B0404", "资源不存在"), + + + /** + * pipeline + */ + CONTEXT_IS_NULL("P0001", "流程上下文为空"), + BUSINESS_CODE_IS_NULL("P0002", "业务代码为空"), + PROCESS_TEMPLATE_IS_NULL("P0003", "流程模板配置为空"), + PROCESS_LIST_IS_NULL("P0004", "业务处理器配置为空"), + + + ; + + /** + * 响应状态 + */ + private final String code; + /** + * 响应编码 + */ + private final String msg; } diff --git a/austin-web/src/main/java/com/java3y/austin/web/config/WeChatLoginConfig.java b/austin-web/src/main/java/com/java3y/austin/web/config/WeChatLoginConfig.java index 57d8669..20f1775 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/config/WeChatLoginConfig.java +++ b/austin-web/src/main/java/com/java3y/austin/web/config/WeChatLoginConfig.java @@ -26,8 +26,8 @@ import java.util.Map; * @author 3y */ @Profile("test") -@Configuration -@ConditionalOnProperty(name = "austin.login.officialAccount.enable", havingValue = "true") +@Configuration("weChatLoginConfig") +@ConditionalOnProperty(name = "austin.login.official.account.enable", havingValue = "true") @Data public class WeChatLoginConfig { @@ -75,6 +75,7 @@ public class WeChatLoginConfig { * 初始化配置信息 */ private void initConfig() { + config = new WxMpDefaultConfigImpl(); config.setAppId(appId); config.setToken(token); config.setSecret(secret); diff --git a/austin-web/src/main/java/com/java3y/austin/web/controller/OfficialAccountController.java b/austin-web/src/main/java/com/java3y/austin/web/controller/OfficialAccountController.java index 0afcfb4..433ccf3 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/controller/OfficialAccountController.java +++ b/austin-web/src/main/java/com/java3y/austin/web/controller/OfficialAccountController.java @@ -22,6 +22,7 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; import me.chanjar.weixin.mp.bean.template.WxMpTemplate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -47,8 +48,8 @@ public class OfficialAccountController { @Autowired private WxServiceUtils wxServiceUtils; - //@Autowired - private WeChatLoginConfig configService; + @Autowired + private ApplicationContext applicationContext; @Autowired private StringRedisTemplate redisTemplate; @@ -111,6 +112,10 @@ public class OfficialAccountController { @ApiOperation("/接收微信的事件消息") public String receiptMessage(HttpServletRequest request, HttpServletResponse response) { try { + WeChatLoginConfig configService = applicationContext.getBean(OfficialAccountParamConstant.WE_CHAT_LOGIN_CONFIG, WeChatLoginConfig.class); + if (configService == null) { + return RespStatusEnum.NOT_LOGIN.getMsg(); + } WxMpService wxMpService = configService.getOfficialAccountLoginService(); String echoStr = request.getParameter(OfficialAccountParamConstant.ECHO_STR); @@ -128,7 +133,6 @@ public class OfficialAccountController { } String encryptType = StrUtil.isBlank(request.getParameter(OfficialAccountParamConstant.ENCRYPT_TYPE)) ? OfficialAccountParamConstant.RAW : request.getParameter(OfficialAccountParamConstant.ENCRYPT_TYPE); - if (OfficialAccountParamConstant.RAW.equals(encryptType)) { WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); log.info("raw inMessage:{}", JSON.toJSONString(inMessage)); @@ -151,6 +155,7 @@ public class OfficialAccountController { /** * 临时给微信服务号登录使用(生成二维码),正常消息推送平台不会有此接口 + * 返回二维码图片url 和 sceneId * * @return */ @@ -158,15 +163,35 @@ public class OfficialAccountController { @ApiOperation("/生成 服务号 二维码") public BasicResultVO getQrCode() { try { + WeChatLoginConfig configService = applicationContext.getBean(OfficialAccountParamConstant.WE_CHAT_LOGIN_CONFIG, WeChatLoginConfig.class); + if (configService == null) { + return BasicResultVO.fail(RespStatusEnum.NOT_LOGIN); + } String id = IdUtil.getSnowflake().nextIdStr(); WxMpService wxMpService = configService.getOfficialAccountLoginService(); WxMpQrCodeTicket ticket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(id, 2592000); String url = wxMpService.getQrcodeService().qrCodePictureUrl(ticket.getTicket()); - return BasicResultVO.success(Convert4Amis.getWxMpQrCode(url)); + return BasicResultVO.success(Convert4Amis.getWxMpQrCode(url, id)); } catch (Exception e) { log.error("OfficialAccountController#getQrCode fail:{}", Throwables.getStackTraceAsString(e)); return BasicResultVO.fail(RespStatusEnum.SERVICE_ERROR); } } + /** + * 临时给微信服务号登录使用(给前端轮询检查是否已登录),正常消息推送平台不会有此接口 + * + * @return + */ + @RequestMapping("/check/login") + @ApiOperation("/检查是否已经登录") + public String checkLogin(String sceneId) { + try { + String userInfo = redisTemplate.opsForValue().get(sceneId); + return Convert4Amis.getLoginJsonp(userInfo); + } catch (Exception e) { + log.error("OfficialAccountController#checkLogin fail:{}", Throwables.getStackTraceAsString(e)); + return null; + } + } } diff --git a/austin-web/src/main/java/com/java3y/austin/web/handler/ScanHandler.java b/austin-web/src/main/java/com/java3y/austin/web/handler/ScanHandler.java index 33b70ed..69ac2ad 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/handler/ScanHandler.java +++ b/austin-web/src/main/java/com/java3y/austin/web/handler/ScanHandler.java @@ -1,6 +1,9 @@ package com.java3y.austin.web.handler; -import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.OfficialAccountParamConstant; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; @@ -24,13 +27,26 @@ public class ScanHandler implements WxMpMessageHandler { @Autowired private StringRedisTemplate redisTemplate; + /** + * 扫码事件 + * + * @param wxMessage + * @param context + * @param wxMpService + * @param sessionManager + * @return + */ @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) { - String content = "您在" + (new DateTime().toString("yyyy-MM-dd HH:mm:ss")) + "通过微信扫码登录austin,感谢您的使用。"; - String openId = wxMessage.getFromUser(); - // 将场景值和用户信息存入redis - //redisTemplate.opsForValue().set(wxMessage.getEventKey(), user, 2, TimeUnit.MINUTES); + String content = DateUtil.now() + StrUtil.COLON + wxMessage.getFromUser() + StrUtil.COLON + OfficialAccountParamConstant.SCAN_HANDLER; + try { +// WxMpUser user = wxMpService.getUserService().userInfo(wxMessage.getFromUser()); +// redisTemplate.opsForValue().set(wxMessage.getEventKey(), JSON.toJSONString(user), 30, TimeUnit.MINUTES); + } catch (Exception e) { + log.error("ScanHandler#handle fail:{}", Throwables.getStackTraceAsString(e)); + } return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .content(content).build(); } + } diff --git a/austin-web/src/main/java/com/java3y/austin/web/handler/SubscribeHandler.java b/austin-web/src/main/java/com/java3y/austin/web/handler/SubscribeHandler.java index eb59506..c5da5af 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/handler/SubscribeHandler.java +++ b/austin-web/src/main/java/com/java3y/austin/web/handler/SubscribeHandler.java @@ -1,12 +1,18 @@ package com.java3y.austin.web.handler; -import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.CommonConstant; +import com.java3y.austin.common.constant.OfficialAccountParamConstant; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import me.chanjar.weixin.mp.bean.result.WxMpUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; @@ -18,6 +24,7 @@ import java.util.concurrent.TimeUnit; /** * @author 3y * 微信服务号 关注 事件 处理器 + * 将eventKey 存储在 redis */ @Component("subscribeHandler") @Slf4j @@ -26,12 +33,25 @@ public class SubscribeHandler implements WxMpMessageHandler { @Autowired private StringRedisTemplate redisTemplate; + /** + * 拿到场景值和用户信息,写入到redis + * + * @param wxMessage + * @param context + * @param wxMpService + * @param sessionManager + * @return + */ @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) { - String content = "您在" + (new DateTime().toString("yyyy-MM-dd HH:mm:ss")) + "关注登录austin,感谢您的使用。"; - String openId = wxMessage.getFromUser(); - // 将场景值和用户信息存入redis - redisTemplate.opsForValue().set(wxMessage.getEventKey(), openId, 2, TimeUnit.MINUTES); + String content = DateUtil.now() + StrUtil.COLON + wxMessage.getFromUser() + StrUtil.COLON + OfficialAccountParamConstant.SUBSCRIBE_HANDLER; + try { + WxMpUser user = wxMpService.getUserService().userInfo(wxMessage.getFromUser()); + String eventKey = wxMessage.getEventKey().replaceAll(OfficialAccountParamConstant.QR_CODE_SCENE_PREFIX, CommonConstant.EMPTY_STRING); + redisTemplate.opsForValue().set(eventKey, JSON.toJSONString(user), 30, TimeUnit.DAYS); + } catch (Exception e) { + log.error("SubscribeHandler#handle fail:{}", Throwables.getStackTraceAsString(e)); + } return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .content(content).build(); } diff --git a/austin-web/src/main/java/com/java3y/austin/web/handler/UnSubscribeHandler.java b/austin-web/src/main/java/com/java3y/austin/web/handler/UnSubscribeHandler.java index 37c4a80..e063d1f 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/handler/UnSubscribeHandler.java +++ b/austin-web/src/main/java/com/java3y/austin/web/handler/UnSubscribeHandler.java @@ -1,6 +1,9 @@ package com.java3y.austin.web.handler; -import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.OfficialAccountParamConstant; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; @@ -21,10 +24,14 @@ public class UnSubscribeHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) { - String content = "您在" + (new DateTime().toString("yyyy-MM-dd HH:mm:ss")) + "关注登录austin,感谢您的使用。"; - String openId = wxMessage.getFromUser(); - // 将场景值和用户信息存入redis - //redisTemplate.opsForValue().set(wxMessage.getEventKey(), user, 2, TimeUnit.MINUTES); + String content = DateUtil.now() + StrUtil.COLON + wxMessage.getFromUser() + StrUtil.COLON + OfficialAccountParamConstant.UNSUBSCRIBE_HANDLER; + try { +// String eventKey = wxMessage.getEventKey().replaceAll(OfficialAccountParamConstant.QR_CODE_SCENE_PREFIX, CommonConstant.EMPTY_STRING); +// String userInfo = redisTemplate.opsForValue().get(eventKey); +// redisTemplate.opsForValue().set(eventKey, JSON.toJSONString(userInfo), 30, TimeUnit.DAYS); + } catch (Exception e) { + log.error("UnSubscribeHandler#handle fail:{}", Throwables.getStackTraceAsString(e)); + } return WxMpXmlOutMessage.TEXT().fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .content(content).build(); } diff --git a/austin-web/src/main/java/com/java3y/austin/web/utils/Convert4Amis.java b/austin-web/src/main/java/com/java3y/austin/web/utils/Convert4Amis.java index d793499..1290f30 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/utils/Convert4Amis.java +++ b/austin-web/src/main/java/com/java3y/austin/web/utils/Convert4Amis.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.java3y.austin.web.vo.amis.CommonAmisVo; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; import me.chanjar.weixin.mp.bean.template.WxMpTemplate; @@ -23,6 +24,7 @@ import java.util.stream.Collectors; * @author 3y * @date 2022/1/23 */ +@Slf4j public class Convert4Amis { /** @@ -53,7 +55,7 @@ public class Convert4Amis { /** * (前端是一个JSONObject传递进来,返回一个JSONArray回去) */ - private static final List PARSE_JSON_OBJ_TO_ARRAY = Arrays.asList("officialAccountParam","miniProgramParam"); + private static final List PARSE_JSON_OBJ_TO_ARRAY = Arrays.asList("officialAccountParam", "miniProgramParam"); /** * 钉钉工作消息OA实际的映射 @@ -135,8 +137,9 @@ public class Convert4Amis { /** * 【这个方法不用看】,纯粹为了适配amis前端 - * + *

* 得到模板的参数 组装好 返回给前端展示 + * * @param wxTemplateId * @param allPrivateTemplate * @return @@ -272,8 +275,9 @@ public class Convert4Amis { /** * 【这个方法不用看】,纯粹为了适配amis前端 - * + *

* 得到模板的参数 组装好 返回给前端展示 + * * @param wxTemplateId * @param templateList * @return @@ -308,11 +312,35 @@ public class Convert4Amis { /** * 【这个方法不用看】,纯粹为了适配amis前端 + *

+ * 得到微信服务号的【带参数】二维码返回给前端 * + * @return + */ + public static CommonAmisVo getWxMpQrCode(String url, String id) { + CommonAmisVo image = CommonAmisVo.builder().type("static-image").value(url).originalSrc(url).name("image").label("扫描关注").fixedSize(true).fixedSizeClassName(url).fixedSizeClassName("h-32").build(); + CommonAmisVo service = CommonAmisVo.builder().type("service").api("${ls:backend_url}/officialAccount/check/login?sceneId=" + id).interval(2000).build(); + return CommonAmisVo.builder().type("form").title("登录").mode("horizontal").body(Arrays.asList(image,service)).build(); + } + + /** + * 【这个方法不用看】,纯粹为了适配amis前端 + *

* 得到微信服务号的【带参数】二维码返回给前端 + * * @return */ - public static CommonAmisVo getWxMpQrCode(String url) { - return CommonAmisVo.builder().type("image").imageMode("original").width("450px").height("450px").title("扫描关注服务号-登录").src(url).build(); + public static String getLoginJsonp(String userInfo) { + if (StrUtil.isBlank(userInfo)) { + log.error("can't get userInfo!"); + return "(function() {})();"; + } else { + return "(function() {\n" + + "\tlocalStorage.setItem(\"openId\", \"123\");\n" + + "\tlocalStorage.setItem(\"userName\", \"333\");\n" + + "\twindow.location.href='index.html';\n" + + "})();"; + } + } } diff --git a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/CommonAmisVo.java b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/CommonAmisVo.java index 7e3c414..8e523f2 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/CommonAmisVo.java +++ b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/CommonAmisVo.java @@ -21,16 +21,26 @@ import java.util.List; public class CommonAmisVo { private String type; + private String id; private String label; private String value; + private String api; + private String schemaApi; + private String mode; private String name; + private boolean fixedSize; + private String fixedSizeClassName; + private String frameImage; + private String originalSrc; + private Integer interval; private boolean required; private String size; + private String target; private boolean addable; @@ -44,11 +54,14 @@ public class CommonAmisVo { private String src; - private String title; private String imageMode; + private String varParam; + + private List body; + /** * columns */ diff --git a/austin-web/src/main/resources/application-test.properties b/austin-web/src/main/resources/application-test.properties index e29933b..4ecc180 100644 --- a/austin-web/src/main/resources/application-test.properties +++ b/austin-web/src/main/resources/application-test.properties @@ -44,8 +44,8 @@ austin.grayLog.ip=austin.graylog # TODO if windows os and need upload file to send message ,replace path !【optional】 austin.business.upload.crowd.path=/Users/3y/temp -# TODO if [login use officialAccount] switch 【optional】, if austin.login.officialAccount.enable=true 【must】 +# TODO if [login use officialAccount] switch 【optional】, if austin.login.official.account.enable=true 【must】 austin.login.official.account.enable=false -austin.login.official.account.appId=1 -austin.login.official.account.secret=1 -austin.login.official.account.token=1 \ No newline at end of file +austin.login.official.account.appId=wx22222b325 +austin.login.official.account.secret=203233fa99 +austin.login.official.account.token=austin123 \ No newline at end of file