diff --git a/pmhub-base/pmhub-base-notice/pom.xml b/pmhub-base/pmhub-base-notice/pom.xml new file mode 100644 index 00000000..6104dd30 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + com.laigeoffer.pmhub-cloud + pmhub-base + 0.0.1 + + + pmhub-base-notice + + + pmhub-base-notice 消息队列 Rocketmq 及消息推送专属公共组件 + + + + + + org.springframework + spring-context + + + + + cn.hutool + hutool-all + + + + + org.projectlombok + lombok + + + + + org.apache.rocketmq + rocketmq-client-java + + + org.apache.tomcat + annotations-api + + + + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.1 + + + jsqlparser + com.github.jsqlparser + + + mybatis-spring + org.mybatis + + + + + + com.baomidou + mybatis-plus-extension + 3.5.1 + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.laigeoffer.pmhub-cloud + pmhub-base-core + + + mybatis-spring + org.mybatis + + + jsqlparser + com.github.jsqlparser + + + + + + + + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/consumer/OAMessageConsumer.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/consumer/OAMessageConsumer.java new file mode 100644 index 00000000..22564efc --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/consumer/OAMessageConsumer.java @@ -0,0 +1,279 @@ +package com.laigeoffer.pmhub.base.notice.consumer; + +import cn.hutool.json.JSONUtil; +import cn.hutool.log.LogFactory; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.laigeoffer.pmhub.base.notice.domain.dto.*; +import com.laigeoffer.pmhub.base.notice.domain.entity.WxResult; +import com.laigeoffer.pmhub.base.notice.utils.*; +import org.apache.rocketmq.client.apis.ClientConfiguration; +import org.apache.rocketmq.client.apis.ClientException; +import org.apache.rocketmq.client.apis.ClientServiceProvider; +import org.apache.rocketmq.client.apis.consumer.ConsumeResult; +import org.apache.rocketmq.client.apis.consumer.FilterExpression; +import org.apache.rocketmq.client.apis.consumer.FilterExpressionType; +import org.apache.rocketmq.client.apis.consumer.PushConsumer; +import org.springframework.boot.CommandLineRunner; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collections; + +/** + * OA message消费者 + * + * @author canghe + * @date 2023/07/21 + */ +//@Component +public class OAMessageConsumer implements CommandLineRunner { + + + /** + * 微信topic + * */ +// @Value("${pmhub.rocketMQ.topic.wxMessage}") + private String WX_TOPIC; + + + /** + * 服务器地址 + * */ +// @Value("${pmhub.rocketMQ.addr}") + private String addr; + + + + /** + * 消费组 + * */ +// @Value("${pmhub.rocketMQ.topic.consumerGroup}") + private String WX_CONSUMER_GROUP; + + + /** + * 运行注册监听器 + * + * @param args 参数 + * @throws Exception 异常 + */ + @Override + public void run(String... args) throws Exception { + final ClientServiceProvider provider = ClientServiceProvider.loadService(); + ClientConfiguration clientConfiguration = ClientConfiguration.newBuilder() + .setEndpoints(addr) + .build(); + + // 初始化PushConsumer,需要绑定消费者分组ConsumerGroup、通信参数以及订阅关系。 + try { + FilterExpression wxFilterExpression = new FilterExpression(RocketMqUtils.mqTag, FilterExpressionType.TAG); + + PushConsumer pushConsumer = provider.newPushConsumerBuilder() + .setClientConfiguration(clientConfiguration) + // 设置消费者分组。 + .setConsumerGroup(WX_CONSUMER_GROUP) + // 设置预绑定的订阅关系。 + .setSubscriptionExpressions(Collections.singletonMap(WX_TOPIC, wxFilterExpression)) + // 设置消费监听器。 + .setMessageListener(messageView -> { + // 处理消息并返回消费结果。 + LogFactory.get().info("Consume message successfully, messageId={}", messageView.getMessageId()); + + + try { + Charset charset = StandardCharsets.UTF_8; + String json = charset.decode(messageView.getBody()).toString(); + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(json); + String type = jsonNode.get("type").asText(); + LogFactory.get().info(">>>>>>>>>>>>>>>>>>>>消息类型:"+type); + switch (type){ + case "任务审批提醒": + ProcessRemindDTO processRemindDTO = JSONUtil.toBean(json, ProcessRemindDTO.class); + WxResult wxResult = MessageUtils.sendMessage(processRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO = new MessageDataDTO(); + messageDataDTO.setMsgCode(wxResult.getResponse_code()); + messageDataDTO.setMsgTime(System.currentTimeMillis()); + messageDataDTO.setWxUserName(processRemindDTO.getWxUserName()); + RedisUtils.set(processRemindDTO.getTaskId() + "_" + processRemindDTO.getAssignee(), messageDataDTO); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(processRemindDTO.getUserName(), processRemindDTO.getOaTitle() + , processRemindDTO.getOaContext(), processRemindDTO.getLinkUrl(), processRemindDTO.getTaskId() + "_" + processRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增消息instanceId:{}, taskId:{}"+processRemindDTO.getInstId(), processRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult)); + break; + case "采购入库审批提醒": + InboundRemindDTO inboundRemindDTO = JSONUtil.toBean(json, InboundRemindDTO.class); + WxResult wxResult2 = MessageUtils.sendMessage(inboundRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO2 = new MessageDataDTO(); + messageDataDTO2.setMsgCode(wxResult2.getResponse_code()); + messageDataDTO2.setMsgTime(System.currentTimeMillis()); + messageDataDTO2.setWxUserName(inboundRemindDTO.getWxUserName()); + RedisUtils.set(inboundRemindDTO.getTaskId() + "_" + inboundRemindDTO.getAssignee(), messageDataDTO2); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(inboundRemindDTO.getUserName(), inboundRemindDTO.getOaTitle() + , inboundRemindDTO.getOaContext(), inboundRemindDTO.getLinkUrl(), inboundRemindDTO.getTaskId() + "_" + inboundRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增采购入库消息instanceId:{}, taskId:{}" + inboundRemindDTO.getInstId(), inboundRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult2)); + break; + case "采购退货出库审批提醒": + OutboundRemindDTO outboundRemindDTO = JSONUtil.toBean(json, OutboundRemindDTO.class); + WxResult wxResult3 = MessageUtils.sendMessage(outboundRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO3 = new MessageDataDTO(); + messageDataDTO3.setMsgCode(wxResult3.getResponse_code()); + messageDataDTO3.setMsgTime(System.currentTimeMillis()); + messageDataDTO3.setWxUserName(outboundRemindDTO.getWxUserName()); + RedisUtils.set(outboundRemindDTO.getTaskId() + "_" + outboundRemindDTO.getAssignee(), messageDataDTO3); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(outboundRemindDTO.getUserName(), outboundRemindDTO.getOaTitle() + , outboundRemindDTO.getOaContext(), outboundRemindDTO.getLinkUrl(), outboundRemindDTO.getTaskId() + "_" + outboundRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增采购退货出库消息instanceId:{}, taskId:{}" + outboundRemindDTO.getInstId(), outboundRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult3)); + break; + case "其他入库审批提醒": + OtherIntoRemindDTO otherIntoRemindDTO = JSONUtil.toBean(json, OtherIntoRemindDTO.class); + WxResult wxResult4 = MessageUtils.sendMessage(otherIntoRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO4 = new MessageDataDTO(); + messageDataDTO4.setMsgCode(wxResult4.getResponse_code()); + messageDataDTO4.setMsgTime(System.currentTimeMillis()); + messageDataDTO4.setWxUserName(otherIntoRemindDTO.getWxUserName()); + RedisUtils.set(otherIntoRemindDTO.getTaskId() + "_" + otherIntoRemindDTO.getAssignee(), messageDataDTO4); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(otherIntoRemindDTO.getUserName(), otherIntoRemindDTO.getOaTitle() + , otherIntoRemindDTO.getOaContext(), otherIntoRemindDTO.getLinkUrl(), otherIntoRemindDTO.getTaskId() + "_" + otherIntoRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增其他入库消息instanceId:{}, taskId:{}" + otherIntoRemindDTO.getInstId(), otherIntoRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult4)); + break; + case "其他出库审批提醒": + OtherOutRemindDTO otherOutRemindDTO = JSONUtil.toBean(json, OtherOutRemindDTO.class); + WxResult wxResult5 = MessageUtils.sendMessage(otherOutRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO5 = new MessageDataDTO(); + messageDataDTO5.setMsgCode(wxResult5.getResponse_code()); + messageDataDTO5.setMsgTime(System.currentTimeMillis()); + messageDataDTO5.setWxUserName(otherOutRemindDTO.getWxUserName()); + RedisUtils.set(otherOutRemindDTO.getTaskId() + "_" + otherOutRemindDTO.getAssignee(), messageDataDTO5); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(otherOutRemindDTO.getUserName(), otherOutRemindDTO.getOaTitle() + , otherOutRemindDTO.getOaContext(), otherOutRemindDTO.getLinkUrl(), otherOutRemindDTO.getTaskId() + "_" + otherOutRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增其他出库出库消息instanceId:{}, taskId:{}" + otherOutRemindDTO.getInstId(), otherOutRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult5)); + break; + case "归还入库审批提醒": + ReturnIntoRemindDTO returnIntoRemindDTO = JSONUtil.toBean(json, ReturnIntoRemindDTO.class); + WxResult wxResult6 = MessageUtils.sendMessage(returnIntoRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO6 = new MessageDataDTO(); + messageDataDTO6.setMsgCode(wxResult6.getResponse_code()); + messageDataDTO6.setMsgTime(System.currentTimeMillis()); + messageDataDTO6.setWxUserName(returnIntoRemindDTO.getWxUserName()); + RedisUtils.set(returnIntoRemindDTO.getTaskId() + "_" + returnIntoRemindDTO.getAssignee(), messageDataDTO6); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(returnIntoRemindDTO.getUserName(), returnIntoRemindDTO.getOaTitle() + , returnIntoRemindDTO.getOaContext(), returnIntoRemindDTO.getLinkUrl(), returnIntoRemindDTO.getTaskId() + "_" + returnIntoRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增归还入库消息instanceId:{}, taskId:{}" + returnIntoRemindDTO.getInstId(), returnIntoRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult6)); + break; + case "供应商审批提醒": + ProviderRemindDTO providerRemindDTO = JSONUtil.toBean(json, ProviderRemindDTO.class); + WxResult wxResult7 = MessageUtils.sendMessage(providerRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO7 = new MessageDataDTO(); + messageDataDTO7.setMsgCode(wxResult7.getResponse_code()); + messageDataDTO7.setMsgTime(System.currentTimeMillis()); + messageDataDTO7.setWxUserName(providerRemindDTO.getWxUserName()); + RedisUtils.set(providerRemindDTO.getTaskId() + "_" + providerRemindDTO.getAssignee(), messageDataDTO7); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(providerRemindDTO.getUserName(), providerRemindDTO.getOaTitle() + , providerRemindDTO.getOaContext(), providerRemindDTO.getLinkUrl(), providerRemindDTO.getTaskId() + "_" + providerRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增采购管理消息instanceId:{}, taskId:{}" + providerRemindDTO.getInstId(), providerRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult7)); + break; + case "物料报废审批提醒": + ScrappedOutRemindDTO scrappedOutRemindDTO = JSONUtil.toBean(json, ScrappedOutRemindDTO.class); + WxResult wxResult8 = MessageUtils.sendMessage(scrappedOutRemindDTO.toWxMessage()); + + // 信息发送成功,保存message + // cleanMessage(processRemindDTO.getInstId()); + MessageDataDTO messageDataDTO8 = new MessageDataDTO(); + messageDataDTO8.setMsgCode(wxResult8.getResponse_code()); + messageDataDTO8.setMsgTime(System.currentTimeMillis()); + messageDataDTO8.setWxUserName(scrappedOutRemindDTO.getWxUserName()); + RedisUtils.set(scrappedOutRemindDTO.getTaskId() + "_" + scrappedOutRemindDTO.getAssignee(), messageDataDTO8); + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(scrappedOutRemindDTO.getUserName(), scrappedOutRemindDTO.getOaTitle() + , scrappedOutRemindDTO.getOaContext(), scrappedOutRemindDTO.getLinkUrl(), scrappedOutRemindDTO.getTaskId() + "_" + scrappedOutRemindDTO.getAssignee())), OAUtils.SEND_MESSAGE_API); + LogFactory.get().info("新增物料报废消息instanceId:{}, taskId:{}" + scrappedOutRemindDTO.getInstId(), scrappedOutRemindDTO.getTaskId()); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult8)); + break; + case "审批流结束回执": + // 消息回执 + ProcessReturnDTO processReturnDTO = JSONUtil.toBean(json, ProcessReturnDTO.class); + LogFactory.get().info(JSONUtil.toJsonStr(MessageUtils.sendMessage(processReturnDTO.toWxMessage()))); + // 发送消息至泛微 + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(processReturnDTO.getUserName(), processReturnDTO.getOaTitle() + , processReturnDTO.getOaContext(), processReturnDTO.getLinkUrl(), null)), OAUtils.SEND_MESSAGE_API); + break; + case "待办提醒": + // 待办提醒 + TodoRemindDTO todoRemindDTO = JSONUtil.toBean(json, TodoRemindDTO.class); + LogFactory.get().info(JSONUtil.toJsonStr(MessageUtils.sendMessage(todoRemindDTO.toWxMessage()))); + // 发送消息至泛微 + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(todoRemindDTO.getUserName(), todoRemindDTO.getOaTitle() + , todoRemindDTO.getOaContext(), todoRemindDTO.getLinkUrl(), null)), OAUtils.SEND_MESSAGE_API); + + break; + case "任务逾期提醒": + // 任务逾期提醒 + TaskOverdueRemindDTO taskOverdueRemindDTO = JSONUtil.toBean(json, TaskOverdueRemindDTO.class); + LogFactory.get().info(JSONUtil.toJsonStr(MessageUtils.sendMessage(taskOverdueRemindDTO.toWxMessage()))); + // 发送消息至泛微 + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(taskOverdueRemindDTO.getUserName(), taskOverdueRemindDTO.getOaTitle() + , taskOverdueRemindDTO.getOaContext(), taskOverdueRemindDTO.getLinkUrl(), null)), OAUtils.SEND_MESSAGE_API); + break; + case "任务已逾期提醒": + // 任务逾期提醒 + TaskOvertimeRemindDTO taskOvertimeRemindDTO = JSONUtil.toBean(json, TaskOvertimeRemindDTO.class); + LogFactory.get().info(JSONUtil.toJsonStr(MessageUtils.sendMessage(taskOvertimeRemindDTO.toWxMessage()))); + // 发送消息至泛微 + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(taskOvertimeRemindDTO.getUserName(), taskOvertimeRemindDTO.getOaTitle() + , taskOvertimeRemindDTO.getOaContext(), taskOvertimeRemindDTO.getLinkUrl(), null)), OAUtils.SEND_MESSAGE_API); + break; + case "任务指派提醒": + // 任务指派提醒 + TaskAssignRemindDTO taskAssignRemindDTO = JSONUtil.toBean(json, TaskAssignRemindDTO.class); + LogFactory.get().info(JSONUtil.toJsonStr(MessageUtils.sendMessage(taskAssignRemindDTO.toWxMessage()))); + // 发送消息至泛微 + OAUtils.restfulCall2(OAUtils.SEND_MESSAGE_API, OAUtils.mapToStr(OAUtils.sendCustomMessageSingle(taskAssignRemindDTO.getUserName(), taskAssignRemindDTO.getOaTitle() + , taskAssignRemindDTO.getOaContext(), taskAssignRemindDTO.getLinkUrl(), null)), OAUtils.SEND_MESSAGE_API); + break; + default: + break; + } + }catch (Exception ex){ + LogFactory.get().error("未知的微信审批提醒消息:"); + LogFactory.get().error(ex); + return ConsumeResult.SUCCESS; + } + return ConsumeResult.SUCCESS; + }) + .build(); + LogFactory.get().info("企微通知消息RocketMQ通道已建立,TOPIC:"+WX_TOPIC); + } catch (ClientException e) { + LogFactory.get().error(e); + } + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ActionListDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ActionListDTO.java new file mode 100644 index 00000000..324528cc --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ActionListDTO.java @@ -0,0 +1,24 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 操作列表,列表长度取值范围为 [1, 3] + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class ActionListDTO { + + /** + * 操作的描述文案 + * */ + private String text; + + + /** + * 操作key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复 + * */ + private String key; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ActionMenuDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ActionMenuDTO.java new file mode 100644 index 00000000..f614d41d --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ActionMenuDTO.java @@ -0,0 +1,24 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 卡片右上角更多操作按钮 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class ActionMenuDTO { + + /** + * 更多操作界面的描述 + * */ + private String desc; + + /** + * 操作列表,列表长度取值范围为 [1, 3] + * */ + private List action_list; +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ButtonDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ButtonDTO.java new file mode 100644 index 00000000..e683831d --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ButtonDTO.java @@ -0,0 +1,47 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import com.laigeoffer.pmhub.base.notice.enums.ButtonStateEnum; +import lombok.Data; + +/** + * 按钮,列表长度不超过6 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class ButtonDTO { + + /** + * 按钮点击事件类型,0 或不填代表回调点击事件,1 代表跳转url + * */ + private Integer type; + + /** + * 按钮文案,建议不超过10个字 + * */ + private String text; + + + /** + * 按钮样式,目前可填1~4,不填或错填默认1 + * */ + private Integer style; + + /** + * 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复,button_list.type是0时必填 + * */ + private String key; + + /** + * 跳转事件的url,button_list.type是1时必填 + * */ + private String url; + + /*以下部分为更新按钮时需要的*/ + + /** + * 需要更新的按钮的文案 + * */ + private ButtonStateEnum replace_name; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/CardActionDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/CardActionDTO.java new file mode 100644 index 00000000..1ff636c0 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/CardActionDTO.java @@ -0,0 +1,33 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 整体卡片的点击跳转事件,text_notice必填本字段 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class CardActionDTO { + + /** + * 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] + * */ + private Integer type; + + /** + * 跳转链接的url,type是1时必填 + * */ + private String url; + + /** + * 跳转链接的小程序的appid,必须是与当前应用关联的小程序,type是2时必填 + * */ + private String appid; + + /** + * 跳转链接的小程序的pagepath,type是2时选填 + * */ + private String pagepath; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/EmphasisContentDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/EmphasisContentDTO.java new file mode 100644 index 00000000..c5cadba3 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/EmphasisContentDTO.java @@ -0,0 +1,22 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 关键数据样式 + * @author canghe + */ +@Data +public class EmphasisContentDTO { + + /** + * 关键数据样式的数据内容,建议不超过14个字 + * */ + private String title; + + /** + * 关键数据样式的数据描述内容,建议不超过22个字 + * */ + private String desc; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/HorizontalContentListDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/HorizontalContentListDTO.java new file mode 100644 index 00000000..b4b64bfa --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/HorizontalContentListDTO.java @@ -0,0 +1,43 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class HorizontalContentListDTO { + + /** + * 链接类型,0或不填代表不是链接,1 代表跳转url,2 代表下载附件,3 代表点击跳转成员详情 + * */ + private Integer type; + + /** + * 二级标题,建议不超过5个字 + * */ + private String keyname; + + /** + * 二级文本,type是2,该字段代表文件名称(要包含文件类型),建议不超过30个字,(支持id转译) + * */ + private String value; + + /** + * 链接跳转的url,horizontal_content_list.type是1时必填 + * */ + private String url; + + /** + * 附件的media_id,horizontal_content_list.type是2时必填 + * */ + private String media_id; + + /** + * 成员详情的userid,horizontal_content_list.type是3时必填 + * */ + private String userid; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/InboundRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/InboundRemindDTO.java new file mode 100644 index 00000000..7506936a --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/InboundRemindDTO.java @@ -0,0 +1,174 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = InboundRemindDTO.class, name = "采购入库审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class InboundRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/JumpListDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/JumpListDTO.java new file mode 100644 index 00000000..30993946 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/JumpListDTO.java @@ -0,0 +1,38 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class JumpListDTO { + + /** + * 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序 + * */ + private Integer type; + + /** + * 跳转链接样式的文案内容,建议不超过18个字 + * */ + private String title; + + /** + * 跳转链接的url,jump_list.type是1时必填 + * */ + private String url; + + /** + * 跳转链接的小程序的appid,必须是与当前应用关联的小程序,jump_list.type是2时必填 + * */ + private String appid; + + /** + * 跳转链接的小程序的pagepath,jump_list.type是2时选填 + * */ + private String pagepath; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/MainTitleDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/MainTitleDTO.java new file mode 100644 index 00000000..24560aa4 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/MainTitleDTO.java @@ -0,0 +1,22 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 消息标题 + * @author canghe + */ +@Data +public class MainTitleDTO { + + /** + * 一级标题,建议不超过36个字,文本通知型卡片本字段非必填,但不可本字段和sub_title_text都不填,(支持id转译) + * */ + private String title; + + /** + * 标题辅助信息,建议不超过44个字,(支持id转译) + * */ + private String desc; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/NoticeWxMessageDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/NoticeWxMessageDTO.java new file mode 100644 index 00000000..eb5b2d8c --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/NoticeWxMessageDTO.java @@ -0,0 +1,19 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import com.laigeoffer.pmhub.base.notice.domain.entity.WxMessage; +import lombok.Data; + +/** + * 提醒消息 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class NoticeWxMessageDTO extends WxMessage { + + /** + * 卡片模板 + * */ + private TextCardDTO textcard; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OtherIntoRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OtherIntoRemindDTO.java new file mode 100644 index 00000000..19b25230 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OtherIntoRemindDTO.java @@ -0,0 +1,174 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = OtherIntoRemindDTO.class, name = "其他入库审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class OtherIntoRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OtherOutRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OtherOutRemindDTO.java new file mode 100644 index 00000000..89710b40 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OtherOutRemindDTO.java @@ -0,0 +1,174 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = OtherOutRemindDTO.class, name = "其他出库审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class OtherOutRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的其他出库审批提醒"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OutboundRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OutboundRemindDTO.java new file mode 100644 index 00000000..c0310ba5 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/OutboundRemindDTO.java @@ -0,0 +1,174 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = OutboundRemindDTO.class, name = "采购退货出库审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class OutboundRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessRemindDTO.java new file mode 100644 index 00000000..85676f5d --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessRemindDTO.java @@ -0,0 +1,175 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = ProcessRemindDTO.class, name = "任务审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class ProcessRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(1); + quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + urlInfo.setUrl(detailUrl); + horizontalContentListDTOs.add(urlInfo); + + templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessReturnDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessReturnDTO.java new file mode 100644 index 00000000..428bc0a3 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessReturnDTO.java @@ -0,0 +1,204 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批流结束回执 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = ProcessReturnDTO.class, name = "审批流结束回执") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class ProcessReturnDTO extends Message { + + private static final String HEAD_DESC = "审批流程提醒"; + private static final String TYPE_TITLE = "您申请的流程%s"; + + private static final String[] DETAIL_TEXT = {"处理时间","说明"}; + + private static final String BUTTON_TEXT = "查看详情"; + + /** + * 企微消息类型 + * */ + public final String msgType = "text_notice"; + + /** + * 流程名称 + * */ + private String processName; + + /** + * 审批状态 + * */ + private String processState; + + /** + * 审批状态说明 + * */ + private String processStateDesc; + + + /** + * 处理时间 + * */ + private String timeStr; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + + /** + * 审批内容备注 + * */ + private String remarks; + + private String userName; + + private String oaTitle; + private String oaContext; + private String linkUrl; + + + public ProcessReturnDTO(String processName + , String processState + , String processStateDesc + , String timeStr + , String detailUrl + , String panelUrl + , String remarks, String userName, String oaTitle, String oaContext, String linkUrl){ + this.processName = processName; + this.processState = processState; + this.processStateDesc = processStateDesc; + this.timeStr = timeStr; + this.detailUrl = detailUrl; + this.panelUrl = panelUrl; + this.remarks =remarks; + this.userName = userName; + this.oaTitle = oaTitle; + this.oaContext = oaContext; + this.linkUrl = linkUrl; + } + + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.TEXT_NOTICE.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(HEAD_DESC); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + // 主题 + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(String.format(TYPE_TITLE,processState)); + mainTitleDTO.setDesc(String.format("【%s】任务状态变更",processName)); + templateCardDTO.setMain_title(mainTitleDTO); + + // 状态描述 + EmphasisContentDTO emphasisContent = new EmphasisContentDTO(); + emphasisContent.setTitle(processState); + emphasisContent.setDesc(processStateDesc); + templateCardDTO.setEmphasis_content(emphasisContent); + + // 下方排列 + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setKeyname(DETAIL_TEXT[0]); + urlInfo.setValue(timeStr); + horizontalContentListDTOs.add(urlInfo); + HorizontalContentListDTO urlInfo1 = new HorizontalContentListDTO(); + urlInfo1.setKeyname(DETAIL_TEXT[1]); + urlInfo1.setValue(remarks); + horizontalContentListDTOs.add(urlInfo1); + templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List jumpListDTOS = new ArrayList<>(); + JumpListDTO b1 = new JumpListDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setTitle(BUTTON_TEXT); + jumpListDTOS.add(b1); + templateCardDTO.setJump_list(jumpListDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessWxMessageDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessWxMessageDTO.java new file mode 100644 index 00000000..d70b1944 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessWxMessageDTO.java @@ -0,0 +1,19 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import com.laigeoffer.pmhub.base.notice.domain.entity.WxMessage; +import lombok.Data; + +/** + * 微信提醒消息模板 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class ProcessWxMessageDTO extends WxMessage { + + /** + * 卡片模板 + * */ + private TemplateCardDTO template_card; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessWxMessageStateUpdateDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessWxMessageStateUpdateDTO.java new file mode 100644 index 00000000..30614e59 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProcessWxMessageStateUpdateDTO.java @@ -0,0 +1,19 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import com.laigeoffer.pmhub.base.notice.domain.entity.WxUpdateMessage; +import lombok.Data; + +/** + * 更新微信消息按钮状态模板 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class ProcessWxMessageStateUpdateDTO extends WxUpdateMessage { + + /** + * 按钮 + * */ + private ButtonDTO button = new ButtonDTO(); + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProviderRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProviderRemindDTO.java new file mode 100644 index 00000000..faefe865 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ProviderRemindDTO.java @@ -0,0 +1,174 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = ProviderRemindDTO.class, name = "供应商审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class ProviderRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/QuoteAreaDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/QuoteAreaDTO.java new file mode 100644 index 00000000..e6e1e2aa --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/QuoteAreaDTO.java @@ -0,0 +1,44 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 引用文献样式 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class QuoteAreaDTO { + + /** + * 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 + * */ + private Integer type; + + /** + * 点击跳转的url,quote_area.type是1时必填 + * */ + private String url; + + /** + * 点击跳转的小程序的appid,必须是与当前应用关联的小程序,quote_area.type是2时必填 + * */ + private String appid; + + /** + * 点击跳转的小程序的pagepath,quote_area.type是2时选填 + * */ + private String pagepath; + + /** + * 引用文献样式的标题 + * */ + private String title; + + /** + * 引用文献样式的引用文案 + * */ + private String quote_text; + + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ReturnIntoRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ReturnIntoRemindDTO.java new file mode 100644 index 00000000..26cfffd9 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ReturnIntoRemindDTO.java @@ -0,0 +1,174 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 审批提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = ReturnIntoRemindDTO.class, name = "归还入库审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class ReturnIntoRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ScrappedOutRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ScrappedOutRemindDTO.java new file mode 100644 index 00000000..a7835ce1 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/ScrappedOutRemindDTO.java @@ -0,0 +1,175 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.enums.CardTypeEnum; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author canghe + * @date 2023-06-29 09:11 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = ScrappedOutRemindDTO.class, name = "物料报废审批提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class ScrappedOutRemindDTO extends Message { + + private String headDesc = "审批流程提醒"; + private String typeTitle = "您有一个新的审批申请"; + + private String[] detailText = {"申请详情","点击查看"}; + private String userText = "申请人"; + private String buttonText = "开始处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public String msgType = "template_card"; + + /** + * 审批类型 + * */ + private String processType; + + + /** + * 申请人姓名 + * */ + private String createUserName; + + /** + * 申请详情url + * */ + private String detailUrl; + + /** + * 审批界面url + * */ + private String panelUrl; + + /** + * 审批内容标题 + * */ + private String title; + + /** + * 审批内容备注 + * */ + private String remarks; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + /** + * 转微信消息 + * */ + public ProcessWxMessageDTO toWxMessage(){ + ProcessWxMessageDTO processWxMessageDTO = new ProcessWxMessageDTO(); + + // 设置通知对象 + // 如果传入了用户id + if (ObjectUtil.isNotNull(getUserIds())){ + if (getUserIds().size()==0){ + // 如果用户id长度为0,则为全体人员 + processWxMessageDTO.setTouser("@all"); + }else { + // 拼接微信格式的用户id + processWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + } + }else{ + // 没有设置用户id + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了部门 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getDeptList(),"|")); + } + if (ObjectUtil.isNotEmpty(getDeptList())){ + // 设置了标签 + processWxMessageDTO.setToparty(StringCreateUtils.listStringCompose(getTags(),"|")); + } + } + + // 设置应用id + processWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + processWxMessageDTO.setEnable_id_trans(1); + }else { + processWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + processWxMessageDTO.setEnable_duplicate_check(1); + }else { + processWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + processWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TemplateCardDTO templateCardDTO = new TemplateCardDTO(); + templateCardDTO.setCard_type(CardTypeEnum.BUTTON_INTERACTION.toString()); + SourceDTO sourceDTO = new SourceDTO(); + sourceDTO.setDesc(headDesc); + sourceDTO.setDesc_color(1); + templateCardDTO.setSource(sourceDTO); + // 消息id + templateCardDTO.setTask_id(System.currentTimeMillis()+ IdUtil.simpleUUID()); + + MainTitleDTO mainTitleDTO = new MainTitleDTO(); + mainTitleDTO.setTitle(typeTitle); + mainTitleDTO.setDesc(processType); + templateCardDTO.setMain_title(mainTitleDTO); + + QuoteAreaDTO quoteAreaDTO = new QuoteAreaDTO(); + quoteAreaDTO.setType(0); + //quoteAreaDTO.setUrl(detailUrl); + quoteAreaDTO.setTitle(title); + quoteAreaDTO.setQuote_text(remarks); + templateCardDTO.setQuote_area(quoteAreaDTO); + + templateCardDTO.setSub_title_text("申请人:"+createUserName); + + List horizontalContentListDTOs = new ArrayList<>(); + HorizontalContentListDTO urlInfo = new HorizontalContentListDTO(); + urlInfo.setType(1); + urlInfo.setKeyname(detailText[0]); + urlInfo.setValue(detailText[1]); + //urlInfo.setUrl(detailUrl); + //horizontalContentListDTOs.add(urlInfo); + //templateCardDTO.setHorizontal_content_list(horizontalContentListDTOs); + + + CardActionDTO cardActionDTO = new CardActionDTO(); + cardActionDTO.setType(1); + cardActionDTO.setUrl(panelUrl); + templateCardDTO.setCard_action(cardActionDTO); + + List buttonDTOS = new ArrayList<>(); + ButtonDTO b1 = new ButtonDTO(); + b1.setType(1); + b1.setUrl(panelUrl); + b1.setText(buttonText); + buttonDTOS.add(b1); + templateCardDTO.setButton_list(buttonDTOS); + + processWxMessageDTO.setTemplate_card(templateCardDTO); + return processWxMessageDTO; + } + +} + diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/SourceDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/SourceDTO.java new file mode 100644 index 00000000..9b604d98 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/SourceDTO.java @@ -0,0 +1,28 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 卡片来源样式信息,不需要来源样式可不填写 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * @author canghe + */ +@Data +public class SourceDTO { + + /** + * 来源图片的url,来源图片的尺寸建议为72*72 + * */ + private String icon_url; + + /** + * 来源图片的描述,建议不超过20个字,(支持id转译) + * */ + private String desc; + + /** + * 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 + * */ + private Integer desc_color; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskAssignRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskAssignRemindDTO.java new file mode 100644 index 00000000..5f4a72ba --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskAssignRemindDTO.java @@ -0,0 +1,96 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 任务指派提醒 + * + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = TaskAssignRemindDTO.class, name = "任务指派提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class TaskAssignRemindDTO extends Message { + + private static final String DESC = "
%s
【%s】给您指派了任务【%s】,请及时处理!
"; + private static final String MSG_TITLE = "任务指派提醒"; + private static final String BUTTON_TEXT = "查看详情"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + */ + public final String msgType = "textcard"; + + /** + * 任务名 + */ + private String taskName; + + /** + * 详情url + */ + private String detailUrl; + + /** + * 指派人 + */ + private String creator; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + + /** + * 转微信消息 + */ + public NoticeWxMessageDTO toWxMessage() { + NoticeWxMessageDTO noticeWxMessageDTO = new NoticeWxMessageDTO(); + + // 设置通知对象 + // 拼接微信格式的用户id + noticeWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(), "|")); + + // 设置应用id + noticeWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans()) && getEnableIdTrans()) { + noticeWxMessageDTO.setEnable_id_trans(1); + } else { + noticeWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck()) && getEnableDuplicateCheck()) { + noticeWxMessageDTO.setEnable_duplicate_check(1); + } else { + noticeWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + noticeWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TextCardDTO textcard = new TextCardDTO(); + textcard.setBtntxt(BUTTON_TEXT); + textcard.setUrl(detailUrl); + textcard.setDescription(String.format(DESC, DateUtil.now(), creator, taskName)); + textcard.setTitle(MSG_TITLE); + + noticeWxMessageDTO.setMsgtype(msgType); + noticeWxMessageDTO.setTextcard(textcard); + return noticeWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskOverdueRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskOverdueRemindDTO.java new file mode 100644 index 00000000..4c03c7a3 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskOverdueRemindDTO.java @@ -0,0 +1,95 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 任务逾期提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = TaskOverdueRemindDTO.class, name = "任务逾期提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class TaskOverdueRemindDTO extends Message { + + private static final String DESC = "
%s
您的任务【%s】还有【%s】天到期,请及时处理!
"; + private static final String MSG_TITLE = "任务即将逾期提醒"; + private static final String BUTTON_TEXT = "查看详情"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public final String msgType = "textcard"; + + /** + * 任务名 + * */ + private String taskName; + + /** + * 详情url + * */ + private String detailUrl; + + /** + * 剩余天数 + * */ + private Integer num; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + + /** + * 转微信消息 + * */ + public NoticeWxMessageDTO toWxMessage(){ + NoticeWxMessageDTO noticeWxMessageDTO = new NoticeWxMessageDTO(); + + // 设置通知对象 + // 拼接微信格式的用户id + noticeWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + + // 设置应用id + noticeWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + noticeWxMessageDTO.setEnable_id_trans(1); + }else { + noticeWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + noticeWxMessageDTO.setEnable_duplicate_check(1); + }else { + noticeWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + noticeWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TextCardDTO textcard = new TextCardDTO(); + textcard.setBtntxt(BUTTON_TEXT); + textcard.setUrl(detailUrl); + textcard.setDescription(String.format(DESC, DateUtil.now(),taskName,num)); + textcard.setTitle(MSG_TITLE); + + noticeWxMessageDTO.setMsgtype(msgType); + noticeWxMessageDTO.setTextcard(textcard); + return noticeWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskOvertimeRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskOvertimeRemindDTO.java new file mode 100644 index 00000000..527080a6 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TaskOvertimeRemindDTO.java @@ -0,0 +1,91 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 任务已逾期提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = TaskOvertimeRemindDTO.class, name = "任务已逾期提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class TaskOvertimeRemindDTO extends Message { + + private static final String DESC = "
%s
您的任务【%s】已经逾期,请及时处理!
"; + private static final String MSG_TITLE = "任务逾期提醒"; + private static final String BUTTON_TEXT = "查看详情"; + + /** + * 企微消息类型固定为 textCard + * */ + public final String msgType = "textcard"; + + + /** + * 任务名 + * */ + private String taskName; + + + /** + * 详情url + * */ + private String detailUrl; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + + /** + * 转微信消息 + * */ + public NoticeWxMessageDTO toWxMessage(){ + NoticeWxMessageDTO noticeWxMessageDTO = new NoticeWxMessageDTO(); + + // 设置通知对象 + // 拼接微信格式的用户id + noticeWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + + // 设置应用id + noticeWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + noticeWxMessageDTO.setEnable_id_trans(1); + }else { + noticeWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + noticeWxMessageDTO.setEnable_duplicate_check(1); + }else { + noticeWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + noticeWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + // 设置模板消息 + TextCardDTO textcard = new TextCardDTO(); + textcard.setBtntxt(BUTTON_TEXT); + textcard.setUrl(detailUrl); + textcard.setDescription(String.format(DESC, DateUtil.now(),taskName)); + textcard.setTitle(MSG_TITLE); + + noticeWxMessageDTO.setMsgtype(msgType); + noticeWxMessageDTO.setTextcard(textcard); + return noticeWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TemplateCardDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TemplateCardDTO.java new file mode 100644 index 00000000..d36b83b5 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TemplateCardDTO.java @@ -0,0 +1,82 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 模板卡片,默认为文本信息卡片 + * TODO: 2023/2/27 此类的变量名为匹配微信接口使用按下划命名,后期安排优化 + * 格式参考: + * + * + * @author canghe + */ +@Data +public class TemplateCardDTO { + + /** + * 模板卡片类型,文本通知型卡片填写 "text_notice" + * */ + private String card_type = "text_notice"; + + + /** + * 卡片来源样式信息,不需要来源样式可不填写 + * */ + private SourceDTO source; + + /** + * 卡片右上角更多操作按钮 + * */ + private ActionMenuDTO action_menu; + + /** + * 任务id,同一个应用任务id不能重复,只能由数字、字母和“_-@”组成,最长128字节,填了action_menu字段的话本字段必填 + * */ + private String task_id; + + /** + * 消息标题 + * */ + private MainTitleDTO main_title; + + + /** + * 引用文献样式 + * */ + private QuoteAreaDTO quote_area; + + /** + * 关键数据样式 + * */ + private EmphasisContentDTO emphasis_content; + + + /** + * 二级普通文本,建议不超过160个字,(支持id转译) + * */ + private String sub_title_text; + + /** + * 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + * */ + private List horizontal_content_list; + + /** + * 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + * */ + private List jump_list; + + /** + * 整体卡片的点击跳转事件,text_notice必填本字段 + * */ + private CardActionDTO card_action; + + /** + * 按钮 + * */ + private List button_list; + + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TextCardDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TextCardDTO.java new file mode 100644 index 00000000..be624f6a --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TextCardDTO.java @@ -0,0 +1,31 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import lombok.Data; + +/** + * 文本卡片消息 + * @author canghe + */ +@Data +public class TextCardDTO { + + /** + * 标题,不超过128个字节,超过会自动截断(支持id转译) + * */ + String title; + + /** + * 描述,不超过512个字节,超过会自动截断(支持id转译) + * */ + String description; + + /** + * 点击后跳转的链接。最长2048字节,请确保包含了协议头(http/https) + * */ + String url; + + /** + * 按钮文字。 默认为“详情”, 不超过4个文字,超过自动截断。 + * */ + String btntxt; +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TodoRemindDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TodoRemindDTO.java new file mode 100644 index 00000000..852f76e4 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/dto/TodoRemindDTO.java @@ -0,0 +1,91 @@ +package com.laigeoffer.pmhub.base.notice.domain.dto; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.laigeoffer.pmhub.base.notice.domain.entity.Message; +import com.laigeoffer.pmhub.base.notice.utils.StringCreateUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 待办提醒 + * @author canghe + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = TodoRemindDTO.class, name = "待办提醒") +}) +@EqualsAndHashCode(callSuper = true) +@Data +public class TodoRemindDTO extends Message { + + private static final String DESC = "
%s
您目前还有【%s】个审批请求未处理,请及时处理
"; + private static final String MSG_TITLE = "待办任务提醒"; + private static final String BUTTON_TEXT = "前往处理"; + + /** + * 企微消息类型固定为TEMPLATE_CARD + * */ + public final String msgType = "textcard"; + + + /** + * 待办详情url + * */ + private String detailUrl; + + /** + * 待办事项个数 + * */ + private Integer num; + + private String oaTitle; + private String oaContext; + private String userName; + private String linkUrl; + + + /** + * 转微信消息 + * */ + public NoticeWxMessageDTO toWxMessage(){ + NoticeWxMessageDTO noticeWxMessageDTO = new NoticeWxMessageDTO(); + + // 设置通知对象 + // 拼接微信格式的用户id + noticeWxMessageDTO.setTouser(StringCreateUtils.listStringCompose(getUserIds(),"|")); + + // 设置应用id + noticeWxMessageDTO.setAgentid(getAgentId()); + + // 是否开启id转译 + if (ObjectUtil.isNotEmpty(getEnableIdTrans())&&getEnableIdTrans()){ + noticeWxMessageDTO.setEnable_id_trans(1); + }else { + noticeWxMessageDTO.setEnable_id_trans(0); + } + // 是否开启重复消息检查 + if (ObjectUtil.isNotEmpty(getEnableDuplicateCheck())&&getEnableDuplicateCheck()){ + noticeWxMessageDTO.setEnable_duplicate_check(1); + }else { + noticeWxMessageDTO.setEnable_duplicate_check(0); + } + // 重复消息检查的时间间隔 + noticeWxMessageDTO.setDuplicate_check_interval(getDuplicateCheckInterval()); + + + // 设置模板消息 + TextCardDTO textcard = new TextCardDTO(); + textcard.setBtntxt(BUTTON_TEXT); + textcard.setUrl(detailUrl); + textcard.setDescription(String.format(DESC, DateUtil.now(),num)); + textcard.setTitle(MSG_TITLE); + + noticeWxMessageDTO.setMsgtype(msgType); + noticeWxMessageDTO.setTextcard(textcard); + return noticeWxMessageDTO; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/Message.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/Message.java new file mode 100644 index 00000000..589dc020 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/Message.java @@ -0,0 +1,59 @@ +package com.laigeoffer.pmhub.base.notice.domain.entity; + +import lombok.Data; + +import java.util.List; + +/** + * 消息 + * @author canghe + */ +@Data +public class Message { + + /** + * teskid + * */ + String instId; + + /** + * 需要通知的用户id清单,为null时无效,长度为0时则为全公司用户 + * */ + List userIds; + + /** + * 按部门通知id列表,为null时无效,userIds为全公司时无效 + * */ + List deptList; + + /** + * 按标签通知列表,为null时无效,userIds为全公司时无效 + * */ + List tags; + + /** + * 通知应用id + * */ + Integer agentId; + + /** + * 表示是否开启id转译 + * */ + Boolean enableIdTrans; + + /** + * 是否开启重复消息检查 + * */ + Boolean enableDuplicateCheck; + + /** + * 重复消息检查的时间间隔(秒,最大不超过4小时) + * */ + Integer duplicateCheckInterval; + + String taskId; + + String wxUserName; + + String assignee; +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/MessageCode.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/MessageCode.java new file mode 100644 index 00000000..d20d77da --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/MessageCode.java @@ -0,0 +1,23 @@ +package com.laigeoffer.pmhub.base.notice.domain.entity; + +import cn.hutool.core.date.DateTime; +import lombok.Data; + +/** + * 微信消息和taskid对应表 + * @author canghe + */ +@Data +public class MessageCode { + + String id; + + String taskId; + + String messageCode; + + /** + * 消息时间 + * */ + DateTime createTime; +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxMessage.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxMessage.java new file mode 100644 index 00000000..a70cd80b --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxMessage.java @@ -0,0 +1,52 @@ +package com.laigeoffer.pmhub.base.notice.domain.entity; + +import lombok.Data; + +/** + * 企微消息模板 + * @author canghe + */ +@Data +public class WxMessage { + + /** + * 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为@all,则向关注该企业应用的全部成员发送 + * */ + private String touser; + + /** + * 部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数 + * */ + private String toparty; + + /** + * 标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数 + * */ + private String totag; + + /** + * 消息类型,模板卡片固定为:template_card + * */ + private String msgtype = "template_card"; + + /** + * 企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值 + * */ + private Integer agentid; + + /** + * 表示是否开启id转译,0表示否,1表示是,默认0 + * */ + private Integer enable_id_trans; + + /** + * 表示是否开启重复消息检查,0表示否,1表示是,默认0 + * */ + private Integer enable_duplicate_check; + + /** + * 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时 + * */ + private Integer duplicate_check_interval; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxResult.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxResult.java new file mode 100644 index 00000000..f59c301e --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxResult.java @@ -0,0 +1,20 @@ +package com.laigeoffer.pmhub.base.notice.domain.entity; + +import lombok.Data; + +/** + * 企微返回消息 + * @author canghe + */ +@Data +public class WxResult { + + private String errcode; + + private String errmsg; + + private String msgid; + + private String response_code; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxUpdateMessage.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxUpdateMessage.java new file mode 100644 index 00000000..1ab0d685 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/domain/entity/WxUpdateMessage.java @@ -0,0 +1,48 @@ +package com.laigeoffer.pmhub.base.notice.domain.entity; + +import lombok.Data; + +import java.util.List; + +/** + * 企微消息更新模板 + * @author canghe + */ +@Data +public class WxUpdateMessage { + + /** + * 企业的成员ID列表(最多支持1000个) + * */ + private List userids; + + /** + * 企业的部门ID列表(最多支持100个) + * */ + private List partyids; + + /** + * 标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数 + * */ + private List tagids; + + /** + * 更新整个任务接收人员 + * */ + private Integer atall ; + + /** + * 更新卡片所需要消费的code,可通过发消息接口和回调接口返回值获取,一个code只能调用一次该接口,且只能在72小时内调用 + * */ + private String response_code; + + /** + * 企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值 + * */ + private Integer agentid; + + /** + * 表示是否开启id转译,0表示否,1表示是,默认0 + * */ + private Integer enable_id_trans; +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/enums/ButtonStateEnum.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/enums/ButtonStateEnum.java new file mode 100644 index 00000000..e8a1c68b --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/enums/ButtonStateEnum.java @@ -0,0 +1,30 @@ +package com.laigeoffer.pmhub.base.notice.enums; + +/** + * 按钮状态 + * @author canghe + */ +public enum ButtonStateEnum { + + /** + * 按钮交互型 + * */ + FINISH("已处理"), + + /** + * 文本通知型 + * */ + OVERTIME("已失效"); + + + private String desc; + + ButtonStateEnum(String desc) { + this.desc = desc; + } + + @Override + public String toString(){ + return desc; + } +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/enums/CardTypeEnum.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/enums/CardTypeEnum.java new file mode 100644 index 00000000..c060f40d --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/enums/CardTypeEnum.java @@ -0,0 +1,30 @@ +package com.laigeoffer.pmhub.base.notice.enums; + +/** + * 模板卡片类型 + * @author canghe + */ +public enum CardTypeEnum { + + /** + * 按钮交互型 + * */ + BUTTON_INTERACTION("button_interaction"), + + /** + * 文本通知型 + * */ + TEXT_NOTICE("text_notice"); + + + private String desc; + + CardTypeEnum(String desc) { + this.desc = desc; + } + + @Override + public String toString(){ + return desc; + } +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/exception/AesException.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/exception/AesException.java new file mode 100644 index 00000000..90f39115 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/exception/AesException.java @@ -0,0 +1,59 @@ +package com.laigeoffer.pmhub.base.notice.exception; + +@SuppressWarnings("serial") +public class AesException extends Exception { + + public final static int OK = 0; + public final static int ValidateSignatureError = -40001; + public final static int ParseXmlError = -40002; + public final static int ComputeSignatureError = -40003; + public final static int IllegalAesKey = -40004; + public final static int ValidateCorpidError = -40005; + public final static int EncryptAESError = -40006; + public final static int DecryptAESError = -40007; + public final static int IllegalBuffer = -40008; + //public final static int EncodeBase64Error = -40009; + //public final static int DecodeBase64Error = -40010; + //public final static int GenReturnXmlError = -40011; + + private int code; + + private static String getMessage(int code) { + switch (code) { + case ValidateSignatureError: + return "签名验证错误"; + case ParseXmlError: + return "xml解析失败"; + case ComputeSignatureError: + return "sha加密生成签名失败"; + case IllegalAesKey: + return "SymmetricKey非法"; + case ValidateCorpidError: + return "corpid校验失败"; + case EncryptAESError: + return "aes加密失败"; + case DecryptAESError: + return "aes解密失败"; + case IllegalBuffer: + return "解密后得到的buffer非法"; +// case EncodeBase64Error: +// return "base64加密错误"; +// case DecodeBase64Error: +// return "base64解密错误"; +// case GenReturnXmlError: +// return "xml生成失败"; + default: + return null; // cannot be + } + } + + public int getCode() { + return code; + } + + public AesException(int code) { + super(getMessage(code)); + this.code = code; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/service/OaUtilsService.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/service/OaUtilsService.java new file mode 100644 index 00000000..06d6445e --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/service/OaUtilsService.java @@ -0,0 +1,24 @@ +package com.laigeoffer.pmhub.base.notice.service; + + +import com.laigeoffer.pmhub.base.notice.exception.AesException; + +/** + * OA系统交互工具接口 + * @author canghe + */ +public interface OaUtilsService { + + /** + * 验证回调url + * @param msgSignature 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。签名计算方法参考 消息体签名检验 + * @param timestamp 时间戳。与nonce结合使用,用于防止请求重放攻击。 + * @param nonce 随机数。与timestamp结合使用,用于防止请求重放攻击。 + * @param ciphertext 加密的字符串。需要解密得到消息内容明文 + * @param aesKey aeskey + * @throws AesException 解码异常 + * @return 给服务器返回得验证消息 + * */ + String callBackUrlTest(String aesKey, String timestamp, String nonce, String ciphertext, String msgSignature) throws AesException; + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/service/impl/WorkWxUtilsServiceImpl.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/service/impl/WorkWxUtilsServiceImpl.java new file mode 100644 index 00000000..9cdd6ef0 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/service/impl/WorkWxUtilsServiceImpl.java @@ -0,0 +1,38 @@ +package com.laigeoffer.pmhub.base.notice.service.impl; + +import cn.hutool.log.LogFactory; +import com.laigeoffer.pmhub.base.notice.exception.AesException; +import com.laigeoffer.pmhub.base.notice.service.OaUtilsService; +import com.laigeoffer.pmhub.base.notice.utils.AES256Utils; +import org.springframework.stereotype.Service; + + +/** + * 企微系统交互工具接口 + * @author canghe + */ +@Service +public class WorkWxUtilsServiceImpl implements OaUtilsService { + + /** + * 验证回调url + * @param msgSignature 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。签名计算方法参考 消息体签名检验 + * @param timestamp 时间戳。与nonce结合使用,用于防止请求重放攻击。 + * @param nonce 随机数。与timestamp结合使用,用于防止请求重放攻击。 + * @param ciphertext 加密的字符串。需要解密得到消息内容明文 + * @param aesKey aeskey + * @return 给服务器返回得验证消息 + * */ + @Override + public String callBackUrlTest(String aesKey, String timestamp, String nonce, String ciphertext, String msgSignature) throws AesException { + String randMsg = ""; + try { + randMsg = AES256Utils.decrypt(aesKey,ciphertext); + }catch (AesException aesException){ + LogFactory.get().error(aesException); + return aesException.getMessage(); + } + return randMsg; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/AES256Utils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/AES256Utils.java new file mode 100644 index 00000000..f823b05f --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/AES256Utils.java @@ -0,0 +1,95 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.CharsetUtil; +import com.laigeoffer.pmhub.base.notice.exception.AesException; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.util.Arrays; + +public class AES256Utils { + + private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; + //加密 + public static byte[] AES_cbc_encrypt(byte[] srcData,byte[] key,byte[] iv) throws Exception { + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv)); + byte[] encData = cipher.doFinal(srcData); + return encData; + } + + //解密 + public static byte[] AES_cbc_decrypt(byte[] encData,byte[] key,byte[] iv) throws Exception + { + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv)); + byte[] decbbdt = cipher.doFinal(encData); + return decbbdt; + } + + /** + * 对密文进行解密. + * + * @param text 需要解密的密文 + * @return 解密得到的明文 + * @throws AesException aes解密失败 + */ + public static String decrypt(String key,String text) throws AesException { + byte[] original = new byte[0]; + try { + + byte[] aesKey = Base64.decode(key+ "="); + + // 设置解密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + + // 使用BASE64对密文进行解码 + byte[] encrypted = Base64.decode(text); + + // 解密 + original = cipher.doFinal(encrypted); + } catch (Exception e) { + e.printStackTrace(); + } + + String xmlContent; + try { + // 去除补位字符 + byte[] bytes = PKCS7Encoder.decode(original); + + // 分离16位随机字符串,网络字节序和receiveid + byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); + + int xmlLength = recoverNetworkBytesOrder(networkOrder); + + xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CharsetUtil.CHARSET_UTF_8); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.IllegalBuffer); + } + + return xmlContent; + + } + + /** + * 还原4个字节的网络字节序 + * */ + public static int recoverNetworkBytesOrder(byte[] orderBytes) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= orderBytes[i] & 0xff; + } + return sourceNumber; + } + + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/ByteGroup.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/ByteGroup.java new file mode 100644 index 00000000..1c470928 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/ByteGroup.java @@ -0,0 +1,26 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import java.util.ArrayList; + +class ByteGroup { + ArrayList byteContainer = new ArrayList(); + + public byte[] toBytes() { + byte[] bytes = new byte[byteContainer.size()]; + for (int i = 0; i < byteContainer.size(); i++) { + bytes[i] = byteContainer.get(i); + } + return bytes; + } + + public ByteGroup addBytes(byte[] bytes) { + for (byte b : bytes) { + byteContainer.add(b); + } + return this; + } + + public int size() { + return byteContainer.size(); + } +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/MessageDataDTO.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/MessageDataDTO.java new file mode 100644 index 00000000..23e2c69f --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/MessageDataDTO.java @@ -0,0 +1,19 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author canghe + * @date 2023-04-10 10:59 + */ +@Data +public class MessageDataDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String msgCode; + private Long msgTime; + private String wxUserName; +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/MessageUtils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/MessageUtils.java new file mode 100644 index 00000000..de9f43b4 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/MessageUtils.java @@ -0,0 +1,87 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import cn.hutool.http.HttpRequest; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.laigeoffer.pmhub.base.notice.domain.entity.WxMessage; +import com.laigeoffer.pmhub.base.notice.domain.entity.WxResult; +import com.laigeoffer.pmhub.base.notice.domain.entity.WxUpdateMessage; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 企微消息推送相关工具类 + * @author canghe + */ +@Component +public class MessageUtils { + + /** + * 服务器host + * */ + public static String host; + @Value("${pmhub.workWx.host}") + private void setHost(String host) { + MessageUtils.host = host; + } + + /** + * 后端路由 + * */ + public static String path; + @Value("${pmhub.workWx.path}") + private void setPath(String path) { + MessageUtils.path = path; + } + + /** + * appid + * */ + public static String appid; + @Value("${pmhub.workWx.corpid}") + private void setAppid(String appid) { + MessageUtils.appid = appid; + } + + /** + * agentid + * */ + public static String agentid; + @Value("${pmhub.workWx.agentid}") + private void setAgentid(String agentid) { + MessageUtils.agentid = agentid; + } + + + /** + * 企微消息发送api + * */ + public static final String WORK_WX_MESSAGE_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s"; + + /** + * 企微消息更新api + * */ + public static final String WORK_WX_MESSAGE_UPDATE_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/update_template_card?access_token=%s"; + + /** + * 发送应用消息 + * */ + public static WxResult sendMessage(WxMessage wxMessage){ + wxMessage.setAgentid(PublicUtil.getAgentId()); + String url = String.format(WORK_WX_MESSAGE_URL,PublicUtil.getWorkWxToken()); + JSONObject re = JSONUtil.parseObj(HttpRequest.post(url) + .body(JSONUtil.toJsonStr(wxMessage)) + .execute().body()); + return JSONUtil.toBean(re,WxResult.class); + } + + public static WxResult updateMessage(WxUpdateMessage wxMessage){ + wxMessage.setAgentid(PublicUtil.getAgentId()); + String url = String.format(WORK_WX_MESSAGE_UPDATE_URL,PublicUtil.getWorkWxToken()); + JSONObject re = JSONUtil.parseObj(HttpRequest.post(url) + .body(JSONUtil.toJsonStr(wxMessage)) + .execute().body()); + return JSONUtil.toBean(re,WxResult.class); + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/OAUtils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/OAUtils.java new file mode 100644 index 00000000..778678aa --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/OAUtils.java @@ -0,0 +1,501 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.http.HttpRequest; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.laigeoffer.pmhub.base.core.exception.ServiceException; +import com.laigeoffer.pmhub.base.core.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 泛微微信工具类 + * + * @author canghe + */ +@Component +@Slf4j +public class OAUtils { + + /** + * 模拟缓存服务 + */ + private static final Map SYSTEM_CACHE = new HashMap<>(); + + /** + * 授权地址 + */ + private static final String OA_AUTHORIZE_URL = "/sso/oauth2.0/authorize?client_id=%s&response_type=code&redirect_uri=%s"; + /** + * 获取 access_token 接口 + */ + private static final String OA_ACCESS_TOKEN_URL = "/sso/oauth2.0/accessToken?client_id=%s&client_secret=%s&grant_type=%s&code=%s&redirect_uri=%s"; + /** + * 获取用户信息接口 + */ + private static final String OA_PROFILE_URL = "/sso/oauth2.0/profile?access_token=%s"; + + /** + * 认证退出接口 + */ + private static final String OA_LOGOUT_URL = "/sso/logout?service=%s"; + + public static final String USER_API = "/api/hrm/resful/getHrmUserInfoWithPage"; + public static final String REGISTER_API = "/api/ec/dev/auth/regist"; + public static final String TOKEN_API = "/api/ec/dev/auth/applytoken"; + public static final String SEND_MESSAGE_API = "/api/ec/dev/message/sendCustomMessageSingle"; + public static final String ALTER_MESSAGE_API = "/api/ec/dev/message/alterCustomMessageSingle"; + public static final String DELETE_MESSAGE_API = "/api/ec/dev/message/ deleteCustomMessageSingle"; + + + + /** + * 错误代码的key + */ + public static final String ERROR_CODE = "code"; + public static final int NUM_0 = 0; + private static final String NUM_1 = "1"; + + + /** + * userid的key + */ + public static final String USR_ID = "id"; + + + + /** + * 获取到的凭证,最长为512字节 + */ + private static final String ACCESS_TOKEN = "access_token"; + private static final String GRANT_TYPE = "authorization_code"; + /** + * 魔法值 + */ + private static final String APP_ID = "appid"; + private static final String CPK = "cpk"; + private static final String SPK = "spk"; + private static final String SECRIT = "secrit"; + private static final String SECRET = "secret"; + private static final int TIME_2000 = 2000; + private static final String TIME_3600 = "3600"; + private static final String LOCAL_PRIVATE_KEY = "LOCAL_PRIVATE_KEY"; + private static final String LOCAL_PUBLIC_KEY = "LOCAL_PUBLIC_KEY"; + private static final String SERVER_PUBLIC_KEY = "SERVER_PUBLIC_KEY"; + private static final String SERVER_SECRET = "SERVER_SECRET"; + private static final String SERVER_TOKEN = "SERVER_TOKEN"; + private static final String TOKEN = "token"; + private static final String TIME_NAME = "time"; + private static final String SESSION = "skipsession"; + + private static final String UTF_8 = "utf-8"; + + + /** + * 客户端应用注册ID + */ + private static String CLIENT_ID = "CLIENT_ID"; + + /** + * ecology系统发放的授权许可证(appid) + */ + private static String APPID = "APPID"; + private static String CODE = "CODE"; + + /** + * 客户端应用注册密钥 + */ + private static String CLIENT_SECRET = "CLIENT_SECRET"; + + /** + * 认证中心地址 + */ + public static String SERVER_PATH = ""; + + @Value("${pmhub.oa.appId}") + private void setAppId(String appId) { + OAUtils.APPID = appId; + } + @Value("${pmhub.oa.code}") + private void setCode(String code) { + OAUtils.CODE = code; + } + + + @Value("${pmhub.oa.clientId}") + private void setClientId(String clientId) { + OAUtils.CLIENT_ID = clientId; + } + + @Value("${pmhub.oa.clientSecret}") + private void setClientSecret(String clientSecret) { + OAUtils.CLIENT_SECRET = clientSecret; + } + + /** + * 泛微地址 + */ + @Value("${pmhub.oa.path}") + private void setServerPath(String path) { + SERVER_PATH = path; + } + + public static String getServerPath() { + return SERVER_PATH; + } + + /** + * pmhub平台 host + * */ + public static String host; + @Value("${pmhub.oa.host}") + private void setHost(String host) { + OAUtils.host = host; + } + + /** + * pmhub平台 path2 + * */ + public static String path2; + @Value("${pmhub.oa.path2}") + private void setPath2(String path2) { + OAUtils.path2 = path2; + } + + public static final String ssoPath = "/sso/oa?url="; + + public static String ssoCreate(String target) { + + String redirectUri = ""; + + try { + redirectUri = URLEncoder.encode(String.format(SERVER_PATH + OA_AUTHORIZE_URL, CLIENT_ID, URLEncoder.encode(host + path2 + ssoPath, UTF_8) + URLEncoder.encode(URLEncoder.encode(target, UTF_8), UTF_8)), UTF_8); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return redirectUri; + + } + + public static String ssoCreateLogin(String target, String target2) { + + String redirectUri = ""; + + try { + redirectUri = URLEncoder.encode(target, UTF_8) + URLEncoder.encode(URLEncoder.encode(target2, UTF_8), UTF_8); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return redirectUri; + + } + + /** + * 获取access_token接口 + */ + public static String getOaAccessToken(String code, String redirectUri) { + + JSONObject jsonObject = JSONUtil.parseObj(HttpRequest.post(String.format(SERVER_PATH + OA_ACCESS_TOKEN_URL, CLIENT_ID, CLIENT_SECRET, GRANT_TYPE, code, redirectUri)) + .execute().body()); + + if (jsonObject.getInt(ERROR_CODE) != NUM_0) { + throw new ServiceException("调用泛微 OA 获取 access_token 接口失败"); + } + return jsonObject.getStr(ACCESS_TOKEN); + } + + + /** + * 获取用户信息接口 + */ + public static String getOAUserInfo(String accessToken) { + + // 获取用户信息 + JSONObject jsonObject = JSONUtil.parseObj(HttpRequest.get(String.format(SERVER_PATH + OA_PROFILE_URL, accessToken)) + .execute().body()); + if (jsonObject.getInt(ERROR_CODE) != NUM_0) { + throw new ServiceException("调用泛微 OA 获取用户信息接口失败"); + } + return jsonObject.getStr(USR_ID); + } + + + /** + * 认证退出接口 + */ + public static JSONObject logout(String service) { + // 获取用户基础信息 + JSONObject ipJson = JSONUtil.parseObj(HttpRequest.get(String.format(SERVER_PATH + OA_LOGOUT_URL, service)) + .execute().body()); + return ipJson; + } + + /** + * 第一步: + *

+ * 调用ecology注册接口,根据appid进行注册,将返回服务端公钥和Secret信息 + */ + public static void testRegist(String address) { + + //获取当前系统RSA加密的公钥 + RSA rsa = new RSA(); + String publicKey = rsa.getPublicKeyBase64(); + String privateKey = rsa.getPrivateKeyBase64(); + + // 客户端RSA私钥 + SYSTEM_CACHE.put(LOCAL_PRIVATE_KEY, privateKey); + // 客户端RSA公钥 + SYSTEM_CACHE.put(LOCAL_PUBLIC_KEY, publicKey); + + //调用ECOLOGY系统接口进行注册 + String data = HttpRequest.post(address + REGISTER_API) + .header(APP_ID, APPID) + .header(CPK, publicKey) + .timeout(TIME_2000) + .execute().body(); + + Map datas = JSONUtil.parseObj(data); + + //ECOLOGY返回的系统公钥 + SYSTEM_CACHE.put(SERVER_PUBLIC_KEY, StrUtil.nullToEmpty((String) datas.get(SPK))); + //ECOLOGY返回的系统密钥 + SYSTEM_CACHE.put(SERVER_SECRET, StrUtil.nullToEmpty((String) datas.get(SECRIT))); + } + public static Map testRegist2(String address) { + + //获取当前系统RSA加密的公钥 + RSA rsa = new RSA(); + String publicKey = rsa.getPublicKeyBase64(); + + //调用ECOLOGY系统接口进行注册 + String data = HttpRequest.post(address + REGISTER_API) + .header(APP_ID, APPID) + .header(CPK, publicKey) + .timeout(TIME_2000) + .execute().body(); + + return JSONUtil.parseObj(data); + } + + /** + * 第二步: + *

+ * 通过第一步中注册系统返回信息进行获取token信息 + */ + public static Map testGetoken(String address) { + // 从系统缓存或者数据库中获取ECOLOGY系统公钥和Secret信息 + String secret = SYSTEM_CACHE.get(SERVER_SECRET); + String spk = SYSTEM_CACHE.get(SERVER_PUBLIC_KEY); + + // 如果为空,说明还未进行注册,调用注册接口进行注册认证与数据更新 + if (Objects.isNull(secret) || Objects.isNull(spk)) { + testRegist(address); + // 重新获取最新ECOLOGY系统公钥和Secret信息 + secret = SYSTEM_CACHE.get(SERVER_SECRET); + spk = SYSTEM_CACHE.get(SERVER_PUBLIC_KEY); + } + + // 公钥加密,所以RSA对象私钥为null + RSA rsa = new RSA(null, spk); + //对秘钥进行加密传输,防止篡改数据 + String encryptSecret = rsa.encryptBase64(secret, CharsetUtil.CHARSET_UTF_8, KeyType.PublicKey); + + //调用ECOLOGY系统接口进行注册 + String data = HttpRequest.post(address + TOKEN_API) + .header(APP_ID, APPID) + .header(SECRET, encryptSecret) + .header(TIME_NAME, TIME_3600) + .execute().body(); + + Map datas = JSONUtil.parseObj(data); + + //ECOLOGY返回的token + // TODO 为Token缓存设置过期时间 + SYSTEM_CACHE.put(SERVER_TOKEN, StrUtil.nullToEmpty((String) datas.get(TOKEN))); + + return datas; + } + + public static String testGetToken(String address) { + + Map map = testRegist2(address); + + // 公钥加密,所以RSA对象私钥为null + RSA rsa = new RSA(null, StrUtil.nullToEmpty((String) map.get(SPK))); + //对秘钥进行加密传输,防止篡改数据 + String encryptSecret = rsa.encryptBase64(StrUtil.nullToEmpty((String) map.get(SECRIT)), CharsetUtil.CHARSET_UTF_8, KeyType.PublicKey); + + //调用ECOLOGY系统接口进行注册 + String data = HttpRequest.post(address + TOKEN_API) + .header(APP_ID, APPID) + .header(SECRET, encryptSecret) + .header(TIME_NAME, TIME_3600) + .execute().body(); + + Map datas = JSONUtil.parseObj(data); + + //ECOLOGY返回的token + return StrUtil.nullToEmpty((String) datas.get(TOKEN)); + } + /** + * 第三步: + *

+ * 调用ecology系统的rest接口,请求头部带上token和用户标识认证信息 + * + * @param api rest api 接口地址(该测试代码仅支持GET请求) + * @param jsonParams 请求参数json串 + *

+ * 注意:ECOLOGY系统所有POST接口调用请求头请设置 "Content-Type","application/x-www-form-urlencoded; charset=utf-8" + */ + public static JSONObject restfulCall(String api, String jsonParams, String type) { + log.info("调用泛微接口:{},{},{}", api, jsonParams, type); + + // ECOLOGY 返回的 token + String token = SYSTEM_CACHE.get(SERVER_TOKEN); + if (StrUtil.isEmpty(token)) { + token = (String) testGetoken(SERVER_PATH).get(TOKEN); + } + + String spk = SYSTEM_CACHE.get(SERVER_PUBLIC_KEY); + // 封装请求头参数 + RSA rsa = new RSA(null, spk); + + // 调用 ECOLOGY 系统接口 + if (StringUtils.isNotBlank(type)) { + return JSONUtil.parseObj(HttpRequest.post(SERVER_PATH + api + jsonParams) + .header(APP_ID, APPID) + .header(TOKEN, token) + .header(SESSION, NUM_1) + .execute().body()); + } + JSONObject jsonObject = JSONUtil.parseObj(HttpRequest.post(SERVER_PATH + api) + .header(APP_ID, APPID) + .header(TOKEN, token) + .header(SESSION, NUM_1) + .body(jsonParams) + .execute().body()); + log.info("调用泛微查询人员信息返回:{}", jsonObject); + return jsonObject; + } + + public static JSONObject restfulCall2(String api, String jsonParams, String type) { + log.info("调用泛微接口:{},{},{}", api, jsonParams, type); + + // ECOLOGY 返回的 token + String token = testGetToken(SERVER_PATH); + + // 调用 ECOLOGY 系统接口 + if (StringUtils.isNotBlank(type)) { + return JSONUtil.parseObj(HttpRequest.post(SERVER_PATH + api + jsonParams) + .header(APP_ID, APPID) + .header(TOKEN, token) + .header(SESSION, NUM_1) + .execute().body()); + } + JSONObject jsonObject = JSONUtil.parseObj(HttpRequest.post(SERVER_PATH + api) + .header(APP_ID, APPID) + .header(TOKEN, token) + .header(SESSION, NUM_1) + .body(jsonParams) + .execute().body()); + log.info("调用泛微查询返回:{}", jsonObject); + return jsonObject; + } + + /** + * 将 Map 转换成字符串参数,用于 POST GET 请求 + * + * @param map + * @return + */ + public static String mapToStr(Map map) { + StringBuilder stringBuilder = new StringBuilder(); + if (map != null) { + for (Map.Entry entry : map.entrySet()) { + stringBuilder.append(entry.getKey()); + if (entry.getValue() != null) + stringBuilder.append("=").append(entry.getValue()); + stringBuilder.append("&"); + } + } + if (stringBuilder.length() > 0) + return "?" + stringBuilder.substring(0, stringBuilder.length() - 1); + return null; + } + + /** + * 发送消息,参数封装 + * @return + */ + public static Map sendCustomMessageSingle(String loginId, String title, String context, String redirectUri, String businessId) { + + Map map = new HashMap<>(); + + // 消息来源,新建消息来源获取code 请查看文档第四大点补充 + map.put("code", CODE); + //接收人登录名 + map.put("loginIdList", loginId); + //creater的值 创建人OA系统id / 创建人登录名 / 创建人编号 / 创建人姓名(对应接收人所传入的形式) + map.put("creater", "1"); + map.put("title", title); + map.put("context", context); + map.put("linkUrl", redirectUri); + map.put("linkMobileUrl", redirectUri); + //消息来源code +“|”+业务id 消息需要打上已处理标记 + if (StringUtils.isNotBlank(businessId)) { + map.put("targetId", CODE + "|" + businessId); + } + + return map; + } + + /** + * 将消息打上已处理标记,参数封装 + * @return + */ + public static Map alterCustomMessageSingle(String businessId, String state, String loginId) { + + Map map = new HashMap<>(); + //修改消息状态时所依据的条件,在消息发送时也需要插入,字符串拼接方式为业务id前加消息来源的“code|" + map.put("targetId", CODE + "|" + businessId); + //待处理 0 已处理 1 已同意 2 已拒绝 3 已删除 27 已暂停 34 已撤销 35 + map.put("bizState", state); + map.put("loginIdList", loginId); + + return map; + } + + + /** + * 删除消息,参数封装 + * @return + */ + public static Map deleteCustomMessageSingle(Long userId, String state, String loginId) { + + Map map = new HashMap<>(); + //修改消息状态时所依据的条件,在消息发送时也需要插入,字符串拼接方式为业务id前加消息来源的“code|" + map.put("targetId", CODE + "|" + userId); + //待处理 0 已处理 1 已同意 2 已拒绝 3 已删除 27 已暂停 34 已撤销 35 + map.put("bizState", state); + map.put("loginIdList", loginId); + + return map; + } + + +} + + diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/PKCS7Encoder.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/PKCS7Encoder.java new file mode 100644 index 00000000..a5e4b90a --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/PKCS7Encoder.java @@ -0,0 +1,67 @@ +/** + * 对企业微信发送给企业后台的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package com.laigeoffer.pmhub.base.notice.utils; + +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * 提供基于PKCS7算法的加解密接口. + */ +class PKCS7Encoder { + static Charset CHARSET = Charset.forName("utf-8"); + static int BLOCK_SIZE = 32; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + if (amountToPad == 0) { + amountToPad = BLOCK_SIZE; + } + // 获得补位所用的字符 + char padChr = chr(amountToPad); + String tmp = new String(); + for (int index = 0; index < amountToPad; index++) { + tmp += padChr; + } + return tmp.getBytes(CHARSET); + } + + /** + * 删除解密后明文的补位字符 + * + * @param decrypted 解密后的明文 + * @return 删除补位字符后的明文 + */ + static byte[] decode(byte[] decrypted) { + int pad = (int) decrypted[decrypted.length - 1]; + if (pad < 1 || pad > 32) { + pad = 0; + } + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码 + * + * @param a 需要转化的数字 + * @return 转化得到的字符 + */ + static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/PublicUtil.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/PublicUtil.java new file mode 100644 index 00000000..1279aa9d --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/PublicUtil.java @@ -0,0 +1,243 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.util.IdUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.log.LogFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +/** + * 企业微信工具类 + * @author canghe + */ +@Component +public class PublicUtil { + + + /** + * 单点登录用户信息验证缓存,证明是由企业微信过来的用户,限制浏览器直接请求(超时时间为五分钟) + * */ + private static TimedCache wxUserCache = CacheUtil.newTimedCache(5*60*1000); + + /** + * 企微鉴权接口地址 + * */ + private static final String WORK_WX_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s"; + /** + * 企微IP列表接口地址 + * */ + private static final String WORK_WX_IP_URL = " https://qyapi.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token=%s"; + + /** + * 企微用户基础信息查询接口地址 + * */ + private static final String WORK_WX_USER_BASE_INFO = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=%s&code=%s"; + + /** + * 企微用户公开信息查询接口地址 + * */ + private static final String WORK_WX_USER_PUBLIC_INFO = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s"; + + /** + * 企微用户敏感信息查询接口地址 + * */ + private static final String WORK_WX_USER_DETAIL_INFO = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=%s"; + + /** + * 错误代码的key + * */ + public static final String ERR_CODE_KEY_NAME = "errcode"; + + /** + * 错误信息的key + * */ + public static final String ERR_MSG_KEY_NAME = "errmsg"; + + /** + * userid的key + * */ + public static final String USR_ID_KEY = "userid"; + + /** + * 证明是企微单点登录的用户token + * */ + public static final String USR_TOKEN_KEY = "tmp_token"; + + /** + * 敏感信息ticket的key + * */ + public static final String USR_TICKET_KEY = "user_ticket"; + + /** + * 用户姓名的key + * */ + public static final String USR_NICK_KEY = "name"; + + /** + * 用户手机的key + * */ + public static final String USR_MOBILE_KEY = "mobile"; + + /** + * 用户邮箱的key + * */ + public static final String USR_EMAIL_KEY = "biz_mail"; + + /** + * 用户头像的key + * */ + public static final String USR_AVATAR_KEY = "avatar"; + + + /** + * 获取到的凭证,最长为512字节 + */ + private static String ACCESS_TOKEN = "ACCESS_TOKEN"; + + /** + * 企业ID + * */ + private static String CORP_ID = "CORP_ID"; + + /** + * 应用的凭证密钥 + * */ + private static String CORP_SECRET = "CORP_SECRET"; + + /** + * 应用id + * */ + private static Integer AGENT_ID = 0; + + /** + * 后端代理转发路由 + * */ + private static String SERVER_PATH = ""; + + + @Value("${pmhub.workWx.corpid}") + private void setCorpId(String corpId) { + PublicUtil.CORP_ID = corpId; + } + + @Value("${pmhub.workWx.corpsecret}") + private void setCorpSecret(String corpSecret) { + PublicUtil.CORP_SECRET = corpSecret; + } + + @Value("${pmhub.workWx.agentid}") + private void setAgentId(Integer agentid) { + PublicUtil.AGENT_ID = agentid; + } + + public static Integer getAgentId(){ + return AGENT_ID; + } + + @Value("${pmhub.workWx.path}") + private void setServerPath(String path) { + SERVER_PATH = path; + } + + public static String getServerPath() { + return SERVER_PATH; + } + + /** + * 获取企微token + * */ + public static String getWorkWxToken(){ + + // 拉取企微ip地址列表,测试token是否还有效,无效则重新获取企微token + JSONObject ipJson = JSONUtil.parseObj(HttpRequest.get(String.format(WORK_WX_IP_URL,ACCESS_TOKEN)) + .execute().body()); + + if (ipJson.getInt(ERR_CODE_KEY_NAME) != 0){ + String result2 = HttpRequest.get(String.format(WORK_WX_URL,CORP_ID,CORP_SECRET)) + .execute().body(); + JSONObject resultJson = JSONUtil.parseObj(result2); + try { + if (resultJson.getInt(ERR_CODE_KEY_NAME) == 0){ + ACCESS_TOKEN = resultJson.getStr("access_token"); + }else { + throw new RuntimeException(resultJson.getStr(ERR_MSG_KEY_NAME)); + } + }catch (Exception ex){ + LogFactory.get().error(ex); + throw new RuntimeException(ex.getMessage()); + } + } + return ACCESS_TOKEN; + } + + + /** + * 通过用户code获取用户基础信息 + * */ + public static JSONObject getWxUserBaseInfo(String code){ + // 获取用户基础信息 + JSONObject ipJson = JSONUtil.parseObj(HttpRequest.get(String.format(WORK_WX_USER_BASE_INFO,getWorkWxToken(),code)) + .execute().body()); + + if (ipJson.getInt(ERR_CODE_KEY_NAME) == 0){ + String token = IdUtil.randomUUID(); + wxUserCache.put(ipJson.getStr(USR_ID_KEY), token); + ipJson.set(USR_TOKEN_KEY,token); + } + + return ipJson; + } + + /** + * 比对token 判断是否是企微过来的用户 + * @return 是否是企微SSO请求 + * */ + public static Boolean checkToken(String userName,String token){ + if (StringUtils.isEmpty(userName)||StringUtils.isEmpty(token)){ + return false; + }else { + String uToken = wxUserCache.get(userName); + if (!StringUtils.isEmpty(uToken)){ + if (token.equals(uToken)){ + // 验证成功,移除token防止被重复使用 + wxUserCache.remove(userName); + return true; + }else { + return false; + } + }else { + return false; + } + } + } + + /** + * 通过用户名获取用户姓名等公开信息 + * */ + public static JSONObject getWxUserPublicInfo(String userName){ + // 获取用户基础信息 + JSONObject ipJson = JSONUtil.parseObj(HttpRequest.get(String.format(WORK_WX_USER_PUBLIC_INFO,getWorkWxToken(),userName)) + .execute().body()); + return ipJson; + } + + + /** + * 通过用户ticket获取用户敏感信息 + * */ + public static JSONObject getWxUserDetailInfo(String ticket){ + JSONObject jsonObject = new JSONObject(); + jsonObject.set("user_ticket",ticket); + // 获取用户敏感信息 + JSONObject userInfo = JSONUtil.parseObj(HttpRequest.post(String.format(WORK_WX_USER_DETAIL_INFO,getWorkWxToken())) + .body(jsonObject.toString()) + .execute().body()); + return userInfo; + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/RedisUtils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/RedisUtils.java new file mode 100644 index 00000000..586a1812 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/RedisUtils.java @@ -0,0 +1,91 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import com.laigeoffer.pmhub.base.core.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +/** + * @author canghe + * @date 2023-04-10 11:04 + */ +@Slf4j +@Component +public class RedisUtils { + + @Resource + private RedisTemplate redisTemplate; + + /** + * 赋值一个静态的redis + */ + public static RedisTemplate redis; + + /** + * 此注解表示构造时赋值 + */ + @PostConstruct + public void redisTemplate() { + redis = this.redisTemplate; + } + + + /** + * 根据key读取数据 + */ + public static Object get(final String key) { + if (StringUtils.isBlank(key)) { + return null; + } + try { + return redis.opsForValue().get(key); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 写入数据 + */ + public static boolean set(final String key, Object value) { + if (StringUtils.isBlank(key)) { + return false; + } + try { + redis.opsForValue().set(key, value); + log.info("存入redis成功,key:{},value:{}", key, value); + return true; + } catch (Exception e) { + log.error("存入redis失败,key:{},value:{}", key, value); + e.printStackTrace(); + } + return false; + } + + /** + * 删除对应的value + * + * @param key + */ + + public static void remove(final String key) { + if (exists(key)) { + redis.delete(key); + } + } + + /** + * 判断缓存中是否有对应的value + * + * @param key + * @return + */ + + public static boolean exists(final String key) { + return Boolean.TRUE.equals(redis.hasKey(key)); + } +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/RocketMqUtils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/RocketMqUtils.java new file mode 100644 index 00000000..678022fa --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/RocketMqUtils.java @@ -0,0 +1,127 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONUtil; +import cn.hutool.log.LogFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.laigeoffer.pmhub.base.notice.domain.dto.ProcessWxMessageStateUpdateDTO; +import com.laigeoffer.pmhub.base.notice.domain.entity.WxResult; +import com.laigeoffer.pmhub.base.notice.enums.ButtonStateEnum; +import org.apache.rocketmq.client.apis.ClientConfiguration; +import org.apache.rocketmq.client.apis.ClientConfigurationBuilder; +import org.apache.rocketmq.client.apis.ClientException; +import org.apache.rocketmq.client.apis.ClientServiceProvider; +import org.apache.rocketmq.client.apis.message.Message; +import org.apache.rocketmq.client.apis.producer.Producer; +import org.apache.rocketmq.client.apis.producer.SendReceipt; + +import java.io.IOException; + +/** + * RocketMQ连接工具 + * @author canghe + */ +//@Component +public class RocketMqUtils { + + + /** + * 连接地址 + * */ + private static String addr; + + + /** + * 微信topic + * */ + private static String WX_TOPIC; + +// @Value("${pmhub.rocketMQ.addr}") + private void setAddr(String addr) { + RocketMqUtils.addr = addr; + } +// @Value("${pmhub.rocketMQ.topic.wxMessage}") + public void setWxTopic(String wxMessage) { + WX_TOPIC = wxMessage; + } + + /** + * rocketmq 消息Tag + * */ + public final static String mqTag = "WX_MASSAGE"; + + + + + + + + + + /** + * 推送到微信topic + * */ + public static void push2Wx(com.laigeoffer.pmhub.base.notice.domain.entity.Message ob){ + + try { + + String key = IdUtil.simpleUUID(); + ObjectMapper objectMapper = new ObjectMapper(); + // 接入点地址,需要设置成Proxy的地址和端口列表,一般是xxx:8081;xxx:8081。 + String endpoint = addr; + // 消息发送的目标Topic名称,需要提前创建。 + String topic = WX_TOPIC; + ClientServiceProvider provider = ClientServiceProvider.loadService(); + ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(endpoint); + ClientConfiguration configuration = builder.build(); + // 初始化Producer时需要设置通信配置以及预绑定的Topic。 + Producer producer = provider.newProducerBuilder() + .setTopics(topic) + .setClientConfiguration(configuration) + .build(); + // 普通消息发送。 + Message message = provider.newMessageBuilder() + .setTopic(topic) + // 设置消息索引键,可根据关键字精确查找某条消息。 + .setKeys(key) + // 设置消息Tag,用于消费端根据指定Tag过滤消息。 + .setTag(mqTag) + // 消息体。 + .setBody(objectMapper.writeValueAsString(ob).getBytes()) + .build(); + + // 发送消息,需要关注发送结果,并捕获失败等异常。 + SendReceipt sendReceipt = producer.send(message); + LogFactory.get().info("Send message successfully, messageId={}", sendReceipt.getMessageId()); + + producer.close(); + } catch (ClientException | IOException e) { + LogFactory.get().error("推送微信消息时发生错误:", e); + } + + } + + + public static void cleanMessage(String instId){ + LogFactory.get().info("清理消息:" + instId); + try { + // 清理消息 + MessageDataDTO messageDataDTO = (MessageDataDTO) RedisUtils.get(instId); + if (messageDataDTO != null) { + ProcessWxMessageStateUpdateDTO processWxMessageStateUpdateDTO = new ProcessWxMessageStateUpdateDTO(); + processWxMessageStateUpdateDTO.setAtall(1); + processWxMessageStateUpdateDTO.setResponse_code(messageDataDTO.getMsgCode()); + processWxMessageStateUpdateDTO.getButton().setReplace_name(ButtonStateEnum.FINISH); + WxResult wxResult = MessageUtils.updateMessage(processWxMessageStateUpdateDTO); + LogFactory.get().info(JSONUtil.toJsonStr(wxResult)); + RedisUtils.remove(instId); + LogFactory.get().info("消息存在"); + } + } catch (Exception ex){ + LogFactory.get().info("消息不存在"); + } + } + + + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/SHA1.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/SHA1.java new file mode 100644 index 00000000..9013e4c4 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/SHA1.java @@ -0,0 +1,64 @@ +/** + * 对企业微信发送给企业后台的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package com.laigeoffer.pmhub.base.notice.utils; + + +import com.laigeoffer.pmhub.base.notice.exception.AesException; + +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * SHA1 class + * + * 计算消息签名接口. + */ +class SHA1 { + + /** + * 用SHA1算法生成安全签名 + * @param token 票据 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @param encrypt 密文 + * @return 安全签名 + * @throws AesException + */ + public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException + { + try { + String[] array = new String[] { token, timestamp, nonce, encrypt }; + StringBuffer sb = new StringBuffer(); + // 字符串排序 + Arrays.sort(array); + for (int i = 0; i < 4; i++) { + sb.append(array[i]); + } + String str = sb.toString(); + // SHA1签名生成 + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + byte[] digest = md.digest(); + + StringBuffer hexstr = new StringBuffer(); + String shaHex = ""; + for (int i = 0; i < digest.length; i++) { + shaHex = Integer.toHexString(digest[i] & 0xFF); + if (shaHex.length() < 2) { + hexstr.append(0); + } + hexstr.append(shaHex); + } + return hexstr.toString(); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.ComputeSignatureError); + } + } +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/SsoUrlUtils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/SsoUrlUtils.java new file mode 100644 index 00000000..659df229 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/SsoUrlUtils.java @@ -0,0 +1,23 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import cn.hutool.core.util.URLUtil; + +/** + * 企微单点登录地址创建 + * @author canghe + */ +public class SsoUrlUtils { + + public static final String ssoPath = "/sso/wx?url="; + + public static final String wxOauth2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_privateinfo&state=STATE&agentid=%s#wechat_redirect"; + + + /** + * + * */ + public static String ssoCreate(String appid,String agentid,String taget){ + return String.format(wxOauth2Url,appid, URLUtil.encode(taget),agentid); + } + +} diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/StringCreateUtils.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/StringCreateUtils.java new file mode 100644 index 00000000..04140b39 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/StringCreateUtils.java @@ -0,0 +1,29 @@ +package com.laigeoffer.pmhub.base.notice.utils; + +import java.util.List; + +/** + * 字符串拼接工具 + * @author canghe + */ +public class StringCreateUtils { + + /** + * 将文本数组合并为一个用分隔符拼接的字符串 + * @param list 文本数组 + * @param flag 分隔符 + * @return 拼接的字符串 + * */ + public static String listStringCompose(List list,String flag){ + StringBuilder str = new StringBuilder(); + for (int i=0;i + *

  • 第三方回复加密消息给企业微信
  • + *
  • 第三方收到企业微信发送的消息,验证消息的安全性,并对消息进行解密。
  • + * + * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 + *
      + *
    1. 在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: + * ...
    2. + *
    3. 下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
    4. + *
    5. 如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
    6. + *
    7. 如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件
    8. + *
    + * @author canghe + */ +public class WXBizMsgCrypt { + static Charset CHARSET = Charset.forName("utf-8"); + Base64 base64 = new Base64(); + byte[] aesKey; + String token; + String receiveid; + + /** + * 构造函数 + * @param token 企业微信后台,开发者设置的token + * @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey + * @param receiveid, 不同场景含义不同,详见文档 + * + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public WXBizMsgCrypt(String token, String encodingAesKey, String receiveid) throws AesException { + if (encodingAesKey.length() != 43) { + throw new AesException(AesException.IllegalAesKey); + } + + this.token = token; + this.receiveid = receiveid; + aesKey = Base64.decode(encodingAesKey + "="); + } + + // 生成4个字节的网络字节序 + byte[] getNetworkBytesOrder(int sourceNumber) { + byte[] orderBytes = new byte[4]; + orderBytes[3] = (byte) (sourceNumber & 0xFF); + orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); + orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); + orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); + return orderBytes; + } + + // 还原4个字节的网络字节序 + int recoverNetworkBytesOrder(byte[] orderBytes) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= orderBytes[i] & 0xff; + } + return sourceNumber; + } + + // 随机生成16位字符串 + String getRandomStr() { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 16; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + + /** + * 对明文进行加密. + * + * @param text 需要加密的明文 + * @return 加密后base64编码的字符串 + * @throws AesException aes加密失败 + */ + String encrypt(String randomStr, String text) throws AesException { + ByteGroup byteCollector = new ByteGroup(); + byte[] randomStrBytes = randomStr.getBytes(CHARSET); + byte[] textBytes = text.getBytes(CHARSET); + byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); + byte[] receiveidBytes = receiveid.getBytes(CHARSET); + + // randomStr + networkBytesOrder + text + receiveid + byteCollector.addBytes(randomStrBytes); + byteCollector.addBytes(networkBytesOrder); + byteCollector.addBytes(textBytes); + byteCollector.addBytes(receiveidBytes); + + // ... + pad: 使用自定义的填充方式对明文进行补位填充 + byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); + byteCollector.addBytes(padBytes); + + // 获得最终的字节流, 未加密 + byte[] unencrypted = byteCollector.toBytes(); + + try { + // 设置加密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + + // 加密 + byte[] encrypted = cipher.doFinal(unencrypted); + + // 使用BASE64对加密后的字符串进行编码 + String base64Encrypted = Base64.encode(encrypted); + + return base64Encrypted; + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.EncryptAESError); + } + } + + /** + * 对密文进行解密. + * + * @param text 需要解密的密文 + * @return 解密得到的明文 + * @throws AesException aes解密失败 + */ + String decrypt(String text) throws AesException { + byte[] original; + try { + // 设置解密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + + // 使用BASE64对密文进行解码 + byte[] encrypted = Base64.decode(text); + + // 解密 + original = cipher.doFinal(encrypted); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.DecryptAESError); + } + + String xmlContent, from_receiveid; + try { + // 去除补位字符 + byte[] bytes = PKCS7Encoder.decode(original); + + // 分离16位随机字符串,网络字节序和receiveid + byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); + + int xmlLength = recoverNetworkBytesOrder(networkOrder); + + xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); + from_receiveid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), + CHARSET); + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.IllegalBuffer); + } + + // receiveid不相同的情况 + if (!from_receiveid.equals(receiveid)) { + throw new AesException(AesException.ValidateCorpidError); + } + return xmlContent; + + } + + /** + * 将企业微信回复用户的消息加密打包. + *
      + *
    1. 对要发送的消息进行AES-CBC加密
    2. + *
    3. 生成安全签名
    4. + *
    5. 将消息密文和安全签名打包成xml格式
    6. + *
    + * + * @param replyMsg 企业微信待回复用户的消息,xml格式的字符串 + * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp + * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce + * + * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException { + // 加密 + String encrypt = encrypt(getRandomStr(), replyMsg); + + // 生成安全签名 + if (timeStamp == "") { + timeStamp = Long.toString(System.currentTimeMillis()); + } + + String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); + + // System.out.println("发送给平台的签名是: " + signature[1].toString()); + // 生成发送的xml + String result = XMLParse.generate(encrypt, signature, timeStamp, nonce); + return result; + } + + /** + * 检验消息的真实性,并且获取解密后的明文. + *
      + *
    1. 利用收到的密文生成安全签名,进行签名验证
    2. + *
    3. 若验证通过,则提取xml中的加密消息
    4. + *
    5. 对消息进行解密
    6. + *
    + * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param postData 密文,对应POST请求的数据 + * + * @return 解密后的原文 + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData) + throws AesException { + + // 密钥,公众账号的app secret + // 提取密文 + Object[] encrypt = XMLParse.extract(postData); + + // 验证安全签名 + String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString()); + + // 和URL中的签名比较是否相等 + // System.out.println("第三方收到URL中的签名:" + msg_sign); + // System.out.println("第三方校验签名:" + signature); + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + + // 解密 + String result = decrypt(encrypt[1].toString()); + return result; + } + + /** + * 验证URL + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param echoStr 随机串,对应URL参数的echostr + * + * @return 解密之后的echostr + * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 + */ + public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr) + throws AesException { + String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr); + + if (!signature.equals(msgSignature)) { + throw new AesException(AesException.ValidateSignatureError); + } + + String result = decrypt(echoStr); + return result; + } + +} \ No newline at end of file diff --git a/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/XMLParse.java b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/XMLParse.java new file mode 100644 index 00000000..0de79092 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/java/com/laigeoffer/pmhub/base/notice/utils/XMLParse.java @@ -0,0 +1,104 @@ +/** + * 对企业微信发送给企业后台的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package com.laigeoffer.pmhub.base.notice.utils; + +import com.laigeoffer.pmhub.base.notice.exception.AesException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; + +/** + * XMLParse class + * + * 提供提取消息格式中的密文及生成回复消息格式的接口. + */ +class XMLParse { + + /** + * 提取出xml数据包中的加密消息 + * @param xmltext 待提取的xml字符串 + * @return 提取出的加密消息字符串 + * @throws AesException + */ + public static Object[] extract(String xmltext) throws AesException { + Object[] result = new Object[3]; + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + + String FEATURE = null; + // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented + // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl + FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; + dbf.setFeature(FEATURE, true); + + // If you can't completely disable DTDs, then at least do the following: + // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities + // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities + // JDK7+ - http://xml.org/sax/features/external-general-entities + FEATURE = "http://xml.org/sax/features/external-general-entities"; + dbf.setFeature(FEATURE, false); + + // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities + // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities + // JDK7+ - http://xml.org/sax/features/external-parameter-entities + FEATURE = "http://xml.org/sax/features/external-parameter-entities"; + dbf.setFeature(FEATURE, false); + + // Disable external DTDs as well + FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + dbf.setFeature(FEATURE, false); + + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + + // And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are a requirement, then + // ensure the entity settings are disabled (as shown above) and beware that SSRF attacks + // (http://cwe.mitre.org/data/definitions/918.html) and denial + // of service attacks (such as billion laughs or decompression bombs via "jar:") are a risk." + + // remaining parser logic + DocumentBuilder db = dbf.newDocumentBuilder(); + StringReader sr = new StringReader(xmltext); + InputSource is = new InputSource(sr); + Document document = db.parse(is); + + Element root = document.getDocumentElement(); + NodeList nodelist1 = root.getElementsByTagName("Encrypt"); + result[0] = 0; + result[1] = nodelist1.item(0).getTextContent(); + return result; + } catch (Exception e) { + e.printStackTrace(); + throw new AesException(AesException.ParseXmlError); + } + } + + /** + * 生成xml消息 + * @param encrypt 加密后的消息密文 + * @param signature 安全签名 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @return 生成的xml字符串 + */ + public static String generate(String encrypt, String signature, String timestamp, String nonce) { + + String format = "\n" + "\n" + + "\n" + + "%3$s\n" + "\n" + ""; + return String.format(format, encrypt, signature, timestamp, nonce); + + } +} diff --git a/pmhub-base/pmhub-base-notice/src/main/resources/META-INF/spring.factories b/pmhub-base/pmhub-base-notice/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..bda1a280 --- /dev/null +++ b/pmhub-base/pmhub-base-notice/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.laigeoffer.pmhub.base.datasource.properties.DruidProperties,\ +com.laigeoffer.pmhub.base.datasource.config.DruidConfig diff --git a/pmhub-base/pom.xml b/pmhub-base/pom.xml index d2763317..bdad7135 100644 --- a/pmhub-base/pom.xml +++ b/pmhub-base/pom.xml @@ -24,6 +24,7 @@ pmhub-base-sensitive pmhub-base-swagger pmhub-base-datasource + pmhub-base-notice diff --git a/pom.xml b/pom.xml index b6033843..d5480acc 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 0.0.1 pmhub - 🔥热门推荐🔥大学春招、秋招、应届项目,SpringCloud+JDK8+RocketMQ等技术架构,完成项目管理+BPM流程管理+AI智能项目,帮助学生主打就业的项目。 + 🔥热门推荐🔥大学春招、秋招、应届项目,SpringCloud + SpringCloud Alibaba + RocketMQ等技术架构,完成项目管理+BPM流程管理+AI智能项目,帮助学生主打就业的项目。 pmhub-gateway @@ -348,8 +348,6 @@ - -