Merge branch 'test' of http://172.16.46.63:30001/gitlab/base-version/ztcloud into test
* 'test' of http://172.16.46.63:30001/gitlab/base-version/ztcloud: [+]增加国密SM4加解密工具包 [+]增加国密SM4接口加解密 [#]修改部门推送消息逻辑
This commit is contained in:
@@ -2,15 +2,17 @@ package com.zt.plat.module.system.api.esp;
|
||||
|
||||
import com.zt.plat.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.object.ObjectUtils;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.dept.dto.DeptMsgRespDTO;
|
||||
import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO;
|
||||
import com.zt.plat.module.system.api.esp.dto.EspDto;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||
import com.zt.plat.module.system.service.dept.IEspService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@RestController
|
||||
@Validated
|
||||
@@ -18,16 +20,26 @@ public class EspApiImpl implements EspApi {
|
||||
|
||||
|
||||
@Resource
|
||||
private IEspService deptService;
|
||||
private IEspService espService;
|
||||
|
||||
|
||||
@Override
|
||||
public CommonResult<List<EspDto>> pushMsg(DeptSaveReqDTO syncReqDTO)
|
||||
public CommonResult<Long> createDept(DeptSaveReqDTO createReqVO) {
|
||||
DeptSaveReqVO reqVO = BeanUtils.toBean(createReqVO, DeptSaveReqVO.class);
|
||||
Long deptId = espService.createDept(reqVO);
|
||||
return success(deptId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CommonResult<List<DeptMsgRespDTO>> selectDepMsg(DeptSaveReqDTO syncReqDTO)
|
||||
{
|
||||
if(Objects.isNull(syncReqDTO) || null == syncReqDTO.getId())
|
||||
{
|
||||
return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
|
||||
"ID不能为空");
|
||||
}
|
||||
return CommonResult.success(deptService.pushMsg(syncReqDTO));
|
||||
return espService.selectDepMsg(syncReqDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.zt.plat.module.system.api.msg;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.sms.dto.log.SmsLogRespDTO;
|
||||
import com.zt.plat.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
|
||||
import com.zt.plat.module.system.service.sms.SmsLogService;
|
||||
import com.zt.plat.module.system.service.sms.SmsSendService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@RestController
|
||||
@Validated
|
||||
public class MsgSendApiImpl implements MsgSendApi {
|
||||
|
||||
@Resource
|
||||
private SmsSendService smsSendService;
|
||||
@Resource
|
||||
private SmsLogService smsLogService;
|
||||
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendTextMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendImageMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<SmsLogRespDTO> getSmsLog(Long id) {
|
||||
return success(BeanUtils.toBean(smsLogService.getSmsLog(id), SmsLogRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendVideoMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendFileMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<SmsLogRespDTO> sendTextCardMsg(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendTextCardMsgPich01(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendNewsMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<SmsLogRespDTO> sendMpNewsMsg(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendMarkdownMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> sendMiniProgramNoticeMsg(SmsSendSingleToUserReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<SmsLogRespDTO> sendInteractiveTaskCardMsg(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms;
|
||||
|
||||
import com.zt.plat.module.system.service.sms.SmsSendService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "管理后台 - 消息回调")
|
||||
@RestController
|
||||
@RequestMapping("/system/sms/callback")
|
||||
public class MsgCallBackController {
|
||||
|
||||
|
||||
@Resource
|
||||
private SmsSendService smsSendService;
|
||||
|
||||
}
|
||||
@@ -8,11 +8,9 @@ import com.zt.plat.module.system.service.sms.SmsSendService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 短信回调")
|
||||
@@ -71,4 +69,13 @@ public class SmsCallbackController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/zle")
|
||||
@PermitAll
|
||||
@TenantIgnore
|
||||
@Operation(summary = "中铝e办短信的回调")
|
||||
public CommonResult<Boolean> receiveZleSmsStatus(@RequestBody String requestBody) throws Throwable {
|
||||
smsSendService.receiveSmsStatus(SmsChannelEnum.ZLE.getCode(), requestBody);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,11 +6,10 @@ import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.framework.excel.core.util.ExcelUtils;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.SmsTemplateRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.SmsTemplateSendReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.TextMessage;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.*;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.zt.plat.module.system.service.msg.ISendMsgService;
|
||||
import com.zt.plat.module.system.service.sms.SmsSendService;
|
||||
import com.zt.plat.module.system.service.sms.SmsTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -19,13 +18,13 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.zt.plat.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 短信模板")
|
||||
@@ -37,6 +36,8 @@ public class SmsTemplateController {
|
||||
private SmsTemplateService smsTemplateService;
|
||||
@Resource
|
||||
private SmsSendService smsSendService;
|
||||
@Resource
|
||||
private ISendMsgService sendMsgService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建短信模板")
|
||||
@@ -100,4 +101,18 @@ public class SmsTemplateController {
|
||||
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
|
||||
}
|
||||
|
||||
@PostMapping("/send-msg")
|
||||
@Operation(summary = "发送消息")
|
||||
@PreAuthorize("@ss.hasPermission('system:sms-template:send-msg')")
|
||||
public CommonResult<Object> sendMsg(@Valid @RequestBody MsgTemplateSendReqVO sendReqVO,TextMessage textMessage) throws Exception{
|
||||
|
||||
String msgtype = textMessage.getMsgtype();
|
||||
if (StringUtils.isBlank(msgtype)){
|
||||
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数缺失:%s",msgtype));
|
||||
}
|
||||
//发送消息到MQ
|
||||
CommonResult<Object> objectCommonResult = sendMsgService.sendTextMsg(sendReqVO,textMessage);
|
||||
return success(objectCommonResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业微信消息发送对象父类
|
||||
*
|
||||
* @author dongqiang.hao
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class BaseMessage implements Serializable {
|
||||
|
||||
/**
|
||||
* 指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为”@all”,则向该企业应用的全部成员发送
|
||||
*/
|
||||
private String touser;
|
||||
/**
|
||||
* 指定接收消息的部门,部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为”@all”时忽略本参数
|
||||
*/
|
||||
private String toparty;
|
||||
/**
|
||||
* 指定接收消息的标签,标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为”@all”时忽略本参数
|
||||
*/
|
||||
private String totag;
|
||||
/**
|
||||
* 企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值
|
||||
*/
|
||||
private String agentid;
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private String msgtype;
|
||||
/**
|
||||
* 发送消息的自建应用类型
|
||||
*/
|
||||
private String appType;
|
||||
/**
|
||||
* 表示是否开启重复消息检查,0表示否,1表示是,默认0
|
||||
*/
|
||||
private Integer enable_duplicate_check = 0;
|
||||
/**
|
||||
* 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时
|
||||
*/
|
||||
private Integer duplicate_check_interval;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 任务卡片消息按钮对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class Btn implements Serializable {
|
||||
|
||||
/**
|
||||
* 按钮key值,用户点击后,会产生任务卡片回调事件,回调事件会带上该key值,只能由数字、字母和“_-@”组成,最长支持128字节
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 按钮名称,最长支持18个字节,超过则截断
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 按钮字体颜色,可选“red”或者“blue”,默认为“blue”
|
||||
*/
|
||||
private String color;
|
||||
|
||||
/**
|
||||
* 按钮字体是否加粗,默认false
|
||||
*/
|
||||
private Boolean is_bold;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 小程序通知消息内容元素对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class ContentItem implements Serializable {
|
||||
/**
|
||||
* 元素对象键值,长度10个汉字以内
|
||||
*/
|
||||
private String key;
|
||||
/**
|
||||
* 元素对象值,长度30个汉字以内(支持id转译)
|
||||
*/
|
||||
private String value;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信文件消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class FileMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:file
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.FILE.getCode();
|
||||
|
||||
/**
|
||||
* 企业微信文件消息体对象
|
||||
*/
|
||||
private Media file = new Media();
|
||||
|
||||
/**
|
||||
* 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
|
||||
*/
|
||||
private Integer safe = 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 企业微信图片消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class ImageMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:image
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.IMAGE.getCode();
|
||||
|
||||
/**
|
||||
* 企业微信图片消息体对象
|
||||
*/
|
||||
private Media image = new Media();
|
||||
|
||||
/**
|
||||
* 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
|
||||
*/
|
||||
private Integer safe = 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 企业微信任务卡片消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class InteractiveTaskCard implements Serializable {
|
||||
|
||||
/**
|
||||
* 标题,不超过128个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 描述,不超过512个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 点击后跳转的链接。最长2048字节,请确保包含了协议头(http/https)
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 任务id,同一个应用发送的任务卡片消息的任务id不能重复,只能由数字、字母和“_-@”组成,最长支持128字节
|
||||
*/
|
||||
private String task_id;
|
||||
|
||||
/**
|
||||
* 按钮列表,按钮个数为1~2个。
|
||||
*/
|
||||
private List<Btn> btn = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信任务卡片消息发送对象
|
||||
* 仅企业微信3.1.6及以上版本支持
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class InteractiveTaskCardMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:interactive_taskcard
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.INTERACTIVE_TASKCARD.getCode();
|
||||
/**
|
||||
* 任务卡片消息体对象,
|
||||
*/
|
||||
private InteractiveTaskCard interactive_taskcard = new InteractiveTaskCard();
|
||||
/**
|
||||
* 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
*/
|
||||
private Integer enable_id_trans = 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* 企业微信markdown内容消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class Markdown implements Serializable {
|
||||
|
||||
/**
|
||||
* markdown内容,最长不超过2048个字节,必须是utf8编码
|
||||
*/
|
||||
private String content;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信markdown消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class MarkdownMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:markdown
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.MARKDOWN.getCode();
|
||||
/**
|
||||
* markdown消息内容对象,
|
||||
*/
|
||||
private Markdown markdown = new Markdown();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* 企业微信媒体消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class Media implements Serializable {
|
||||
/**
|
||||
* 文件上传时,企业微信服务器读取文件流的name
|
||||
*/
|
||||
public static final String MEDIA_NAME = "media";
|
||||
/**
|
||||
* 图片、语音、视频媒体文件id,可以调用上传临时素材接口获取
|
||||
*/
|
||||
private String media_id;
|
||||
/**
|
||||
* 视频消息的标题,不超过128个字节,超过会自动截断(视频消息)
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 视频消息的描述,不超过512个字节,超过会自动截断(视频消息)
|
||||
*/
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 企业微信图文消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class MiniProgramNotice implements Serializable {
|
||||
/**
|
||||
* 小程序appid,必须是与当前应用关联的小程序
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 点击消息卡片后的小程序页面,仅限本小程序内的页面。该字段不填则消息点击后不跳转。
|
||||
*/
|
||||
private String page;
|
||||
|
||||
/**
|
||||
* 消息标题,长度限制4-12个汉字(支持id转译)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息描述,长度限制4-12个汉字(支持id转译)
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 是否放大第一个content_item
|
||||
*/
|
||||
private String emphasis_first_item;
|
||||
|
||||
/**
|
||||
* 消息内容键值对,最多允许10个item
|
||||
*/
|
||||
private List<ContentItem> content_item = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信小程序通知消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class MiniProgramNoticeMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:miniprogram_notice
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.MINIPROGRAM_NOTICE.getCode();
|
||||
/**
|
||||
* 小程序通知消息内容对象,
|
||||
*/
|
||||
private MiniProgramNotice miniprogram_notice = new MiniProgramNotice();
|
||||
/**
|
||||
* 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
*/
|
||||
private Integer enable_id_trans = 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.msg.MpNews;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信图文消息发送对象(文件存储在企业微信)
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class MpNewsMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:mpnews
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.MPNEWS.getCode();
|
||||
|
||||
/**
|
||||
* 企业微信图文消息体对象
|
||||
*/
|
||||
private MpNews mpnews = new MpNews();
|
||||
|
||||
/**
|
||||
* 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
|
||||
*/
|
||||
private Integer safe = 0;
|
||||
|
||||
/**
|
||||
* 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
*/
|
||||
private Integer enable_id_trans = 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.msg.News;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信图文消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class NewsMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:news
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.NEWS.getCode();
|
||||
|
||||
/**
|
||||
* 企业微信图文消息体对象
|
||||
*/
|
||||
private News news = new News();
|
||||
|
||||
/**
|
||||
* 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
*/
|
||||
private Integer enable_id_trans = 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.util.StringSolveUtils;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* 企业微信文本消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class Text implements Serializable {
|
||||
|
||||
/**
|
||||
* 点击事件消息模板
|
||||
*/
|
||||
public static final String URL_CONTENT_TEMPLATE = "<a href=\"${url}\">${content}</a>";
|
||||
|
||||
/**
|
||||
* 消息内容,最长不超过2048个字节,超过将截断(支持id转译)
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息点击url地址(自定义属性)
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 构建点击事件消息模板
|
||||
*/
|
||||
public void buildUrlContent() {
|
||||
if (StringUtils.isNotBlank(this.getUrl())) {
|
||||
Map<String, Object> substituteMap = new HashMap<>(2);
|
||||
substituteMap.put("url", this.getUrl());
|
||||
substituteMap.put("content", this.getContent());
|
||||
this.setContent(StringSolveUtils.placeholderReplace(URL_CONTENT_TEMPLATE, substituteMap));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.msg.TextCard;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信文本卡片消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class TextCardMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:textcard
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.TEXTCARD.getCode();
|
||||
/**
|
||||
* 消息内容对象
|
||||
*/
|
||||
private TextCard textcard = new TextCard();
|
||||
/**
|
||||
* 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
*/
|
||||
private Integer enable_id_trans = 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信文本消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class TextMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:text
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.TEXT.getCode();
|
||||
/**
|
||||
* 消息内容对象,其中text参数的content字段可以支持换行、以及A标签,即可打开自定义的网页(可参考以上示例代码)(注意:换行符请用转义过的\n)
|
||||
*/
|
||||
private Text text = new Text();
|
||||
/**
|
||||
* 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
|
||||
*/
|
||||
private Integer safe = 0;
|
||||
/**
|
||||
* 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
*/
|
||||
private Integer enable_id_trans = 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信视频消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class VideoMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:video
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.VIDEO.getCode();
|
||||
|
||||
/**
|
||||
* 企业微信视频消息体对象
|
||||
*/
|
||||
private Media video = new Media();
|
||||
|
||||
/**
|
||||
* 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
|
||||
*/
|
||||
private Integer safe = 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.msg;
|
||||
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 企业微信语音消息发送对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class VoiceMessage extends BaseMessage {
|
||||
/**
|
||||
* 消息类型,此时固定为:voice
|
||||
*/
|
||||
private final String msgtype = WxMsgTypeConstant.VOICE.getCode();
|
||||
|
||||
/**
|
||||
* 企业微信语音消息体对象
|
||||
*/
|
||||
private Media voice = new Media();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.zt.plat.module.system.controller.admin.sms.vo.template;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 短信模板的发送 Request VO")
|
||||
@Data
|
||||
public class MsgTemplateSendReqVO {
|
||||
|
||||
/* @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
|
||||
@NotNull(message = "手机号不能为空")
|
||||
private String mobile;*/
|
||||
|
||||
@Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
|
||||
@NotNull(message = "模板编码不能为空")
|
||||
private String templateCode;
|
||||
|
||||
@Schema(description = "模板参数")
|
||||
private Map<String, Object> templateParams;
|
||||
|
||||
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户属性", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01")
|
||||
@NotNull(message = "用户属性不能为空")
|
||||
private Integer userType;
|
||||
|
||||
}
|
||||
@@ -74,14 +74,15 @@ public class DeptDO extends TenantBaseDO {
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 是否公司
|
||||
*/
|
||||
private Boolean isCompany;
|
||||
|
||||
/**
|
||||
* 是否集团
|
||||
*/
|
||||
private Boolean isGroup;
|
||||
/**
|
||||
* 是否公司
|
||||
*/
|
||||
private Boolean isCompany;
|
||||
|
||||
/**
|
||||
* 部门来源类型
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.SmsChannelEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 消息渠道 DO
|
||||
*
|
||||
* @author zzf
|
||||
* @since 2021-01-25
|
||||
*/
|
||||
@TableName(value = "system_sms_channel", autoResultMap = true)
|
||||
@KeySequence("system_sms_channel_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@TenantIgnore
|
||||
public class MsgChannelDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 渠道编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
/**
|
||||
* 短信签名
|
||||
*/
|
||||
private String signature;
|
||||
/**
|
||||
* 企业编号(epid)
|
||||
*/
|
||||
private String epid;
|
||||
/**
|
||||
* 渠道编码
|
||||
* 枚举 {@link SmsChannelEnum}
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 启用状态
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
/**
|
||||
* 短信 API 的账号
|
||||
*/
|
||||
private String apiKey;
|
||||
/**
|
||||
* 短信 API 的密钥
|
||||
*/
|
||||
private String apiSecret;
|
||||
/**
|
||||
* 短信发送回调 URL
|
||||
*/
|
||||
private String callbackUrl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.zt.plat.module.system.enums.sms.SmsTemplateTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息模板 DO
|
||||
* @author zzf
|
||||
* @since 2021-01-25
|
||||
*/
|
||||
@TableName(value = "system_sms_template", autoResultMap = true)
|
||||
@KeySequence("system_sms_template_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@TenantIgnore
|
||||
public class MsgTemplateDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 自增编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
// ========= 模板相关字段 =========
|
||||
|
||||
/**
|
||||
* 短信类型
|
||||
* 枚举 {@link SmsTemplateTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 启用状态
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 模板编码,保证唯一
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 模板名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 模板内容
|
||||
* 内容的参数,使用 {} 包括,例如说 {name}
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 参数数组(自动根据内容生成)
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> params;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
/**
|
||||
* 短信 API 的模板编号
|
||||
*/
|
||||
private String apiTemplateId;
|
||||
|
||||
// ========= 渠道相关字段 =========
|
||||
|
||||
/**
|
||||
* 短信渠道编号
|
||||
* 关联 {@link SmsChannelDO#getId()}
|
||||
*/
|
||||
private Long channelId;
|
||||
/**
|
||||
* 短信渠道编码
|
||||
* 冗余 {@link SmsChannelDO#getCode()}
|
||||
*/
|
||||
private String channelCode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* Activemq消息队列
|
||||
*
|
||||
* @author Dy
|
||||
* @since 2021-07-22
|
||||
*/
|
||||
@TableName(value = "sys_active_mq", autoResultMap = true)
|
||||
@KeySequence("sys_active_mq_seq")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@TenantIgnore
|
||||
public class SysActiveMq implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "ID", type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
@TableField("TITLE")
|
||||
private String title;
|
||||
|
||||
@TableField("JMS_TYPE")
|
||||
private String jmsType;
|
||||
|
||||
@TableField("CONTENT_TYPE")
|
||||
private String contentType;
|
||||
|
||||
@TableField("DATETIME")
|
||||
private String datetime;
|
||||
|
||||
@TableField("CONSUME_DATETIME")
|
||||
private String consumeDatetime;
|
||||
|
||||
@TableField("PUBLISHER")
|
||||
private String publisher;
|
||||
|
||||
@TableField("LISTENER_CLASS_NAME")
|
||||
private String listenerClassName;
|
||||
|
||||
@TableField("CONSUME_FLAG")
|
||||
private String consumeFlag;
|
||||
|
||||
@TableField("CONSUME_MESSAGE")
|
||||
private String consumeMessage;
|
||||
|
||||
@TableField("TEXT_CONTENT")
|
||||
private String textContent;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* Activemq消息队列
|
||||
*
|
||||
* @author Dy
|
||||
* @since 2021-07-22
|
||||
*/
|
||||
@TableName(value = "sys_active_mq_log", autoResultMap = true)
|
||||
@KeySequence("sys_active_mq_log_seq")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@TenantIgnore
|
||||
public class SysActiveMqLog implements Serializable {
|
||||
|
||||
|
||||
@TableId(value = "ID", type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
@TableField("TITLE")
|
||||
private String title;
|
||||
|
||||
@TableField("JMS_TYPE")
|
||||
private String jmsType;
|
||||
|
||||
@TableField("CONTENT_TYPE")
|
||||
private String contentType;
|
||||
|
||||
@TableField("DATETIME")
|
||||
private String datetime;
|
||||
|
||||
@TableField("CONSUME_DATETIME")
|
||||
private String consumeDatetime;
|
||||
|
||||
@TableField("PUBLISHER")
|
||||
private String publisher;
|
||||
|
||||
@TableField("LISTENER_CLASS_NAME")
|
||||
private String listenerClassName;
|
||||
|
||||
@TableField("CONSUME_FLAG")
|
||||
private String consumeFlag;
|
||||
|
||||
@TableField("CONSUME_MESSAGE")
|
||||
private String consumeMessage;
|
||||
|
||||
@TableField("TEXT_CONTENT")
|
||||
private String textContent;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* 企业微信审核配置表
|
||||
* @author Dy
|
||||
* @since 2021-08-26
|
||||
*/
|
||||
@TableName(value = "sys_wx_audit_config", autoResultMap = true)
|
||||
@KeySequence("sys_wx_audit_config_seq")
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@TenantIgnore
|
||||
public class SysWxAuditConfig implements Serializable {
|
||||
|
||||
/**
|
||||
* 状态-生效
|
||||
*/
|
||||
public static final String STATE_ACTIVE = "active";
|
||||
/**
|
||||
* 状态-未生效
|
||||
*/
|
||||
public static final String STATE_INACTIVE = "inactive";
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@TableField("APP_TYPE")
|
||||
private String appType;
|
||||
|
||||
@TableField("CORP_ID")
|
||||
private String corpId;
|
||||
|
||||
@TableField("CORP_SECRET")
|
||||
private String corpSecret;
|
||||
|
||||
@TableField("AGENT_ID")
|
||||
private String agentId;
|
||||
|
||||
@TableField("STATE")
|
||||
private String state;
|
||||
|
||||
@TableField("SORT_INDEX")
|
||||
private Integer sortIndex;
|
||||
|
||||
@TableField("EXT1")
|
||||
private String ext1;
|
||||
|
||||
@TableField("EXT2")
|
||||
private String ext2;
|
||||
|
||||
@TableField("EXT3")
|
||||
private String ext3;
|
||||
|
||||
@TableField("CREATE_USER")
|
||||
private String createUser;
|
||||
|
||||
@TableField("CREATE_USER_NAME")
|
||||
private String createUserName;
|
||||
|
||||
@TableField("ID")
|
||||
private Integer id;
|
||||
|
||||
@TableField("WX_CONTEXT_PATH")
|
||||
private String wxContextPath;
|
||||
|
||||
@TableField("HTTP_CONTEXT_PATH")
|
||||
private String httpContextPath;
|
||||
|
||||
@TableField("UPDATE_TIME")
|
||||
private String updateTime;
|
||||
|
||||
@TableField("CREATE_TIME")
|
||||
private String createTime;
|
||||
|
||||
@TableField("UPDATE_USER")
|
||||
private String updateUser;
|
||||
|
||||
@TableField("UPDATE_USER_NAME")
|
||||
private String updateUserName;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.zt.plat.module.system.dal.mysql.msg;
|
||||
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysActiveMqLog;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsLogDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SysActiveMqDao extends BaseMapperX<SysActiveMqLog> {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.client.dto.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* 企业微信图文消息体对象
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
public class Article implements Serializable {
|
||||
|
||||
/**
|
||||
* 标题,不超过128个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 描述,不超过512个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图 1068*455,小图150*150。
|
||||
*/
|
||||
private String url;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.client.dto.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业微信图文消息体对象(文件存储在企业微信)
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class MpArticle implements Serializable {
|
||||
|
||||
/**
|
||||
* 标题,不超过128个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 图文消息缩略图的media_id, 可以通过素材管理接口获得。此处thumb_media_id即上传接口返回的media_id
|
||||
*/
|
||||
private String thumb_media_id;
|
||||
|
||||
/**
|
||||
* 图文消息的作者,不超过64个字节
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 图文消息点击“阅读原文”之后的页面链接
|
||||
*/
|
||||
private String content_source_url;
|
||||
|
||||
/**
|
||||
* 图文消息的内容,支持html标签,不超过666 K个字节(支持id转译)
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 图文消息的描述,不超过512个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String digest;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.client.dto.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业微信图文消息体对象(文件存储在企业微信)
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class MpNews implements Serializable {
|
||||
/**
|
||||
* 图文消息,一个图文消息支持1到8条图文
|
||||
*/
|
||||
private List<MpArticle> articles = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.client.dto.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业微信图文消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
public class News implements Serializable {
|
||||
/**
|
||||
* 图文消息,一个图文消息支持1到8条图文
|
||||
*/
|
||||
private List<Article> articles = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.client.dto.msg;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业微信文本卡片消息体对象
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class TextCard implements Serializable {
|
||||
|
||||
/**
|
||||
* 标题,不超过128个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 描述,不超过512个字节,超过会自动截断(支持id转译)
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 消息点击点击后跳转的链接。最长2048字节,请确保包含了协议头(http/https)
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 按钮文字。 默认为“详情”, 不超过4个文字,超过自动截断。
|
||||
*/
|
||||
private String btntxt;
|
||||
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import com.zt.plat.module.system.framework.sms.core.property.SmsChannelPropertie
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -82,6 +81,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
|
||||
case HUAWEI: return new HuaweiSmsClient(properties);
|
||||
case QINIU: return new QiniuSmsClient(properties);
|
||||
case HL95: return new Hl95SmsClient(properties);
|
||||
case ZLE: return new ZleSmsClient(properties);
|
||||
}
|
||||
// 创建失败,错误日志 + 抛出异常
|
||||
log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties);
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.client.impl;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.common.core.KeyValue;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.impl.extra.SmsBalanceClient;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
|
||||
import com.zt.plat.module.system.framework.sms.core.property.SmsChannelProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class ZleSmsClient extends AbstractSmsClient implements SmsBalanceClient {
|
||||
|
||||
|
||||
private static final String SEND_URL = "https://api.sms.95ytx.com:9091/mxt/send";
|
||||
private static final String BALANCE_URL = "https://api.sms.95ytx.com:9091/mxt/getfee";
|
||||
private static final DateTimeFormatter STATUS_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
|
||||
public ZleSmsClient(SmsChannelProperties properties) {
|
||||
super(properties);
|
||||
Assert.notEmpty(properties.getApiKey(), "用户名(apiKey) 不能为空");
|
||||
Assert.notEmpty(properties.getApiSecret(), "密码(apiSecret) 不能为空");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsSendRespDTO sendSms(Long logId, String mobile, String content, String apiTemplateId,
|
||||
List<KeyValue<String, Object>> templateParams) {
|
||||
Assert.notEmpty(properties.getEpid(), "中铝e办需要配置 epid");
|
||||
Assert.notEmpty(properties.getSignature(), "短信签名不能为空");
|
||||
String finalContent = appendSignatureIfMissing(content, properties.getSignature());
|
||||
String linkId = buildLinkId(logId);
|
||||
|
||||
Map<String, Object> form = new HashMap<>();
|
||||
form.put("username", properties.getApiKey());
|
||||
form.put("password", properties.getApiSecret());
|
||||
form.put("epid", properties.getEpid());
|
||||
form.put("phone", mobile);
|
||||
form.put("message", finalContent);
|
||||
form.put("linkid", linkId);
|
||||
// subcode 可为空
|
||||
|
||||
String resp;
|
||||
try (HttpResponse response = HttpRequest.post(SEND_URL)
|
||||
.form(form)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.execute()) {
|
||||
resp = StrUtil.trim(response.body());
|
||||
}
|
||||
|
||||
boolean success = StrUtil.equals(resp, "00");
|
||||
return new SmsSendRespDTO()
|
||||
.setSuccess(success)
|
||||
.setApiCode(resp)
|
||||
.setApiMsg(resp)
|
||||
.setApiRequestId(linkId)
|
||||
.setSerialNo(linkId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析短信状态
|
||||
* @param text 结果
|
||||
* @return List<SmsReceiveRespDTO>
|
||||
*/
|
||||
@Override
|
||||
public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
|
||||
JSONObject obj = JSONUtil.parseObj(text, false);
|
||||
String reportCode = obj.getStr("FReportCode");
|
||||
String linkId = obj.getStr("FLinkID");
|
||||
LocalDateTime deliverTime = parseDeliverTime(obj.getStr("FDeliverTime"));
|
||||
String mobile = obj.getStr("FDestAddr");
|
||||
boolean success = StrUtil.equalsIgnoreCase(reportCode, "DELIVRD") || StrUtil.equals(reportCode, "0");
|
||||
Long logId = parseLongSafely(linkId);
|
||||
|
||||
SmsReceiveRespDTO dto = new SmsReceiveRespDTO()
|
||||
.setSuccess(success)
|
||||
.setErrorCode(reportCode)
|
||||
.setErrorMsg(reportCode)
|
||||
.setMobile(mobile)
|
||||
.setReceiveTime(deliverTime)
|
||||
.setSerialNo(linkId)
|
||||
.setLogId(logId);
|
||||
return Collections.singletonList(dto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) {
|
||||
// 中铝e办无模板审核接口,直接返回可用
|
||||
return new SmsTemplateRespDTO()
|
||||
.setId(apiTemplateId)
|
||||
.setContent(apiTemplateId)
|
||||
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询余额
|
||||
* @return Integer
|
||||
*/
|
||||
@Override
|
||||
public Integer queryBalance() {
|
||||
Assert.notEmpty(properties.getEpid(), "中铝e办需要配置 epid");
|
||||
Map<String, Object> form = MapUtil.<String, Object>builder()
|
||||
.put("username", properties.getApiKey())
|
||||
.put("password", properties.getApiSecret())
|
||||
.put("epid", properties.getEpid())
|
||||
.build();
|
||||
String resp;
|
||||
try (HttpResponse response = HttpRequest.get(BALANCE_URL)
|
||||
.form(form)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.execute()) {
|
||||
if (response.getStatus() != HttpURLConnection.HTTP_OK) {
|
||||
throw new IllegalStateException("余额查询失败,HTTP 状态码:" + response.getStatus());
|
||||
}
|
||||
resp = StrUtil.trim(response.body());
|
||||
}
|
||||
if (!StrUtil.isNumeric(resp)) {
|
||||
throw new IllegalStateException("余额查询失败,返回值:" + resp);
|
||||
}
|
||||
return Integer.valueOf(resp);
|
||||
}
|
||||
|
||||
private static String appendSignatureIfMissing(String content, String signature) {
|
||||
if (StrUtil.isBlank(signature)) {
|
||||
return content;
|
||||
}
|
||||
String wrapped = StrUtil.startWithAny(signature, "【", "[") ? signature : "【" + signature + "】";
|
||||
return StrUtil.startWith(content, wrapped) ? content : wrapped + content;
|
||||
}
|
||||
|
||||
private static String buildLinkId(Long logId) {
|
||||
String raw = String.valueOf(logId);
|
||||
return raw.length() > 20 ? raw.substring(raw.length() - 20) : raw;
|
||||
}
|
||||
|
||||
private static LocalDateTime parseDeliverTime(String timeText) {
|
||||
if (StrUtil.isBlank(timeText)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return LocalDateTime.parse(timeText, STATUS_TIME_FORMATTER);
|
||||
} catch (Exception ex) {
|
||||
log.warn("[parseDeliverTime][无法解析时间:{}]", timeText, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Long parseLongSafely(String text) {
|
||||
try {
|
||||
return Long.parseLong(text);
|
||||
} catch (Exception ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.enums;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* 审核类型静态枚举类
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Component
|
||||
public class AuditConstants {
|
||||
|
||||
/**
|
||||
* 中铝E办驰宏数字化应用(企业微信)
|
||||
*/
|
||||
public static final String CHINALCO_ZLEB_CHSZH = "chinalco_zleb_chszh";
|
||||
|
||||
/**
|
||||
* 中铜阳光采购应用(企业微信)
|
||||
*/
|
||||
public static final String CHINALCO_SUNEPS = "chinalco_suneps";
|
||||
|
||||
|
||||
/**
|
||||
* token身份认证令牌
|
||||
*/
|
||||
public static final String TOKEN = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
/**
|
||||
* 企业微信审核页面加载配置代码集ID
|
||||
*/
|
||||
public static final String AUDIT_CODE_SET = "qywx.audit_dispatcher";
|
||||
|
||||
/**
|
||||
* 阳光采购企业微信审核回调地址集合映射
|
||||
*/
|
||||
public static final Map<String, String> REDIRECT_URL_MAPPING = new HashMap<>(1);
|
||||
/**
|
||||
* 企业微信审核回调地址-需要微信认证,使用时需要在前面添加微信请求uri
|
||||
*/
|
||||
public static final String REDIRECT_URL_AUTH = "/qywx/${appType}/index?appType=${appType}&approveType=${approveType}&processInstanceId=${processInstanceId}&taskId=${taskId}&companyCode=${companyCode}&businessId=${businessId}";
|
||||
/**
|
||||
* 企业微信审核回调地址-不需要微信认证,使用时需要在前面添加微信请求uri
|
||||
*/
|
||||
public static final String REDIRECT_URL = "/${appType}/index?appType=${appType}&approveType=${approveType}&processInstanceId=${processInstanceId}&taskId=${taskId}&companyCode=${companyCode}&businessId=${businessId}";
|
||||
|
||||
static {
|
||||
REDIRECT_URL_MAPPING.put("noAuth", REDIRECT_URL);
|
||||
REDIRECT_URL_MAPPING.put("auth", REDIRECT_URL_AUTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批类型
|
||||
*/
|
||||
public enum SunepsAuditType {
|
||||
|
||||
/**
|
||||
* 询价单审批
|
||||
*/
|
||||
REQUEST_AUDIT("requestAudit", "询价单审批"),
|
||||
/**
|
||||
* 会审会签审批
|
||||
*/
|
||||
RESULT_AUDIT("resultAudit", "会审会签审批"),
|
||||
/**
|
||||
* 流标审批
|
||||
*/
|
||||
FLOW_AUDIT("flowAudit", "流标审批"),
|
||||
/**
|
||||
* 谈判方案审批
|
||||
*/
|
||||
NEGO_PLAN("negoPlan", "谈判方案审批"),
|
||||
/**
|
||||
* 终止审批
|
||||
*/
|
||||
TERM_AUDIT("termAudit", "终止审批"),
|
||||
/**
|
||||
* 评标报告审批
|
||||
*/
|
||||
REQUEST_EVA_REPORT("requestEvaReport", "评标报告审批"),
|
||||
/**
|
||||
* 供应商注册
|
||||
*/
|
||||
SUPPLIER_REGISTER("supplierRegister", "供应商注册"),
|
||||
/**
|
||||
* 供应商自荐
|
||||
*/
|
||||
SUPPLIER_SELF("supplierSelf", "供应商自荐"),
|
||||
/**
|
||||
* 供应商基本信息变更
|
||||
*/
|
||||
SUPPLIER_BASE_INFO("supplierBaseInfo", "供应商基本信息变更"),
|
||||
/**
|
||||
* 供应商启用
|
||||
*/
|
||||
SUPPLIER_ENABLE("supplierEnable", "供应商启用"),
|
||||
/**
|
||||
* 供应商可供大类变更
|
||||
*/
|
||||
SUPPLIER_MATE_TYPE("supplierMateType", "供应商可供大类变更"),
|
||||
/**
|
||||
* 合格供应商变更
|
||||
*/
|
||||
SUPPLIER_CHANGE("supplierChange", "合格供应商变更"),
|
||||
/**
|
||||
* 特种供应商认证审批
|
||||
*/
|
||||
T_SUPP_AUDIT("TSuppAudit", "特种供应商认证审批"),
|
||||
/**
|
||||
* 审核消息卡片描述模型
|
||||
*/
|
||||
AUDIT_DESCRIPTION_FORMAT("<div class=\"gray\">${subTitle}</div> <div class=\"normal\">${description}</div> <div class=\"normal\">当前节点:${taskName}</div> <div class=\"highlight\">点击查看详细情况</div>", "审核消息卡片描述模型"),
|
||||
|
||||
/**
|
||||
* 审核回调url,使用时需要在前面添加微信请求uri
|
||||
*/
|
||||
REDIRECT_URL("/qywx/${appType}/index?appType=${appType}&approveType=${approveType}&processInstanceId=${processInstanceId}&taskId=${taskId}&companyCode=${companyCode}&businessId=${businessId}", "审核回调url");
|
||||
|
||||
/**
|
||||
* 消息全局键key-key
|
||||
*/
|
||||
public static final String CODE_KEY = "code";
|
||||
|
||||
/**
|
||||
* 消息全局值value-key
|
||||
*/
|
||||
public static final String MSG_KEY = "msg";
|
||||
|
||||
/**
|
||||
* 审核类型代码
|
||||
*/
|
||||
private final String code;
|
||||
/**
|
||||
* 审核类型说明
|
||||
*/
|
||||
private final String msg;
|
||||
|
||||
SunepsAuditType(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.enums;
|
||||
|
||||
/**
|
||||
* 消息ActiveMQ静态变量枚举
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
public enum JmsConstant {
|
||||
/**
|
||||
* 测试点对点消息队列,使用时需要添加环境前缀JmsProperties jmsProperties===>jmsProperties.getQueuePrefix()
|
||||
*/
|
||||
QUEUE_TEST("queue.test", "测试点对点消息队列"),
|
||||
/**
|
||||
* 测试主题消息队列,使用时需要添加环境前缀JmsProperties jmsProperties===>jmsProperties.getTopicPrefix()
|
||||
*/
|
||||
TOPIC_TEST("topic.test", "测试主题消息队列"),
|
||||
/**
|
||||
* 消息类型-队列
|
||||
*/
|
||||
JMS_TYPE_QUEUE("queue", "消息类型-队列"),
|
||||
/**
|
||||
* 消息类型-主题
|
||||
*/
|
||||
JMS_TYPE_TOPIC("topic", "消息类型-主题"),
|
||||
/**
|
||||
* 消息消费结果-成功
|
||||
*/
|
||||
CONSUME_SUCCESS("success", "消息消费结果-成功"),
|
||||
/**
|
||||
* 消息消费结果-失败
|
||||
*/
|
||||
CONSUME_FAILURE("failure", "消息消费结果-失败");
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private final String code;
|
||||
/**
|
||||
* 说明
|
||||
*/
|
||||
private final String msg;
|
||||
|
||||
JmsConstant(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public enum SmsChannelEnum {
|
||||
HUAWEI("HUAWEI", "华为云"),
|
||||
QINIU("QINIU", "七牛云"),
|
||||
HL95("HL95", "鸿联九五"),
|
||||
ZLE("ZLE", "中铝e办"),
|
||||
;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.zt.plat.module.system.framework.sms.core.enums;
|
||||
|
||||
/**
|
||||
* 企业微信消息类型枚举类
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
public enum WxMsgTypeConstant {
|
||||
/**
|
||||
* 文本消息:其中text参数的content字段可以支持换行、以及A标签,即可打开自定义的网页(可参考以上示例代码)(注意:换行符请用转义过的\n)
|
||||
*/
|
||||
TEXT("text", "文本消息"),
|
||||
/**
|
||||
* 图片消息
|
||||
*/
|
||||
IMAGE("image", "图片消息"),
|
||||
/**
|
||||
* 语音消息
|
||||
*/
|
||||
VOICE("voice", "语音消息"),
|
||||
/**
|
||||
* 视频消息
|
||||
*/
|
||||
VIDEO("video", "视频消息"),
|
||||
/**
|
||||
* 文件消息
|
||||
*/
|
||||
FILE("file", "文件消息"),
|
||||
/**
|
||||
* 卡片消息的展现形式非常灵活,支持使用br标签或者空格来进行换行处理,也支持使用div标签来使用不同的字体颜色,目前内置了3种文字颜色:灰色(gray)、高亮(highlight)、默认黑色(normal),将其作为div标签的class属性即可,具体用法请参考上面的示例。
|
||||
*/
|
||||
TEXTCARD("textcard", "文本卡片消息"),
|
||||
/**
|
||||
* 图文消息
|
||||
*/
|
||||
NEWS("news", "图文消息"),
|
||||
/**
|
||||
* mpnews类型的图文消息,跟普通的图文消息一致,唯一的差异是图文内容存储在企业微信。多次发送mpnews,会被认为是不同的图文,阅读、点赞的统计会被分开计算。
|
||||
*/
|
||||
MPNEWS("mpnews", "图文消息(mpnews)"),
|
||||
/**
|
||||
* 目前仅支持markdown语法的子集,微工作台(原企业号)不支持展示markdown消息
|
||||
*/
|
||||
MARKDOWN("markdown", "markdown消息"),
|
||||
/**
|
||||
* 小程序通知消息
|
||||
* 小程序通知消息只允许绑定了小程序的应用发送,之前,消息会通过统一的会话【小程序通知】发送给用户。
|
||||
* 从2019年6月28日起,用户收到的小程序通知会出现在各个独立的应用中。
|
||||
* 不支持@all全员发送
|
||||
*/
|
||||
MINIPROGRAM_NOTICE("miniprogram_notice", "小程序通知消息"),
|
||||
/**
|
||||
* 任务卡片消息
|
||||
* 仅企业微信3.1.6及以上版本支持
|
||||
* 任务卡片消息的展现支持简单的markdown语法,详情请见附录支持的markdown语法 。
|
||||
* 要发送该类型的消息,应用必须配置好回调URL,详见配置应用回调,用户点击任务卡片的按钮后,企业微信会回调任务卡片事件到该URL,配置的URL按任务卡片更新消息协议返回数据即可。
|
||||
* 开发者可以通过更新任务卡片消息状态接口更新卡片状态。
|
||||
*/
|
||||
INTERACTIVE_TASKCARD("interactive_taskcard", "任务卡片消息");
|
||||
|
||||
/**
|
||||
* 类型key
|
||||
*/
|
||||
private final String code;
|
||||
/**
|
||||
* 类型说明
|
||||
*/
|
||||
private final String msg;
|
||||
|
||||
/**
|
||||
* 构造器
|
||||
*
|
||||
* @param code 类型
|
||||
* @param msg 说明
|
||||
*/
|
||||
WxMsgTypeConstant(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取说明
|
||||
*/
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.zt.plat.module.system.mq.consumer.msg;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.TextCardMessage;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.msg.TextCard;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.AuditConstants;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.JmsConstant;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import com.zt.plat.module.system.mq.message.sms.SmsSendMessage;
|
||||
import com.zt.plat.module.system.service.msg.ISendWxMsgService;
|
||||
import com.zt.plat.module.system.service.msg.ISysRocketMqService;
|
||||
import com.zt.plat.module.system.util.StringSolveUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||
import org.apache.rocketmq.spring.core.RocketMQListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 中铝 e 办的消费者
|
||||
*
|
||||
* @author zzf
|
||||
*/
|
||||
@Component
|
||||
@RocketMQMessageListener(
|
||||
topic = SmsSendMessage.TOPIC,
|
||||
consumerGroup = SmsSendMessage.TOPIC + "_CONSUMER"
|
||||
)
|
||||
@Slf4j
|
||||
public class MsgSendConsumer implements RocketMQListener<MessageExt> {
|
||||
|
||||
|
||||
@Resource
|
||||
private ISysRocketMqService sysActiveMqService;
|
||||
|
||||
@Resource
|
||||
private ISendWxMsgService sendWxMsgService;
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(MessageExt msg) {
|
||||
|
||||
log.info("中铝e办[onMessage][消息内容({})]", msg.toString());
|
||||
String msgId = msg.getMsgId();
|
||||
int reconsumeTimes = msg.getReconsumeTimes();
|
||||
String consumeMsg;
|
||||
try {
|
||||
|
||||
if (msg.getBody() == null || msg.getBody().length == 0){
|
||||
log.error("[onMessage][消息体为空 msgId={}]", msgId);
|
||||
return;
|
||||
}
|
||||
|
||||
String json = new String(msg.getBody(), StandardCharsets.UTF_8);
|
||||
JSONObject jsonObject = JSON.parseObject(json);
|
||||
String toUser = jsonObject.getString("touser"); // 企业微信接收者
|
||||
String userName = jsonObject.getString("userName"); // 用户名
|
||||
String userEname = jsonObject.getString("userEname"); // 英文名
|
||||
String userCname = jsonObject.getString("userCname"); // 中文名
|
||||
String desc = jsonObject.getString("description");
|
||||
// 校验接受用户
|
||||
if (StringUtils.isBlank(toUser)) {
|
||||
consumeMsg = "企业微信消息发送失败,用户【" + userEname + "-" + userCname + "】未绑定企业微信账号!";
|
||||
//TODO 添加文本消息
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), msg, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_FAILURE.getCode(), consumeMsg);
|
||||
} else {
|
||||
String msgtype = jsonObject.getString("msgtype");
|
||||
// 文本卡片消息
|
||||
if (Objects.equals(msgtype, WxMsgTypeConstant.TEXTCARD.getCode())) {
|
||||
/* 构建审批回调url */
|
||||
String appType = jsonObject.getString("appType");
|
||||
String approveType = jsonObject.getString("approveType");
|
||||
String companyCode = jsonObject.getString("userCompanyCode");
|
||||
String taskId = jsonObject.getString("taskId");
|
||||
String processInstanceId = jsonObject.getString("processInstanceId");
|
||||
String businessId = jsonObject.getString("businessId");
|
||||
Map<String, Object> substituteMap = new HashMap<>(5);
|
||||
substituteMap.put("appType", appType.toLowerCase(Locale.ROOT));
|
||||
substituteMap.put("approveType", approveType);
|
||||
substituteMap.put("companyCode", companyCode);
|
||||
substituteMap.put("taskId", taskId);
|
||||
substituteMap.put("processInstanceId", processInstanceId);
|
||||
substituteMap.put("businessId", businessId);
|
||||
String redirectUrl = StringSolveUtils.placeholderReplace(AuditConstants.SunepsAuditType.REDIRECT_URL.getCode(), substituteMap);
|
||||
|
||||
/* 构建文本卡片消息 */
|
||||
String title = jsonObject.getString("title");
|
||||
String taskName = jsonObject.getString("taskName");
|
||||
String datetime = jsonObject.getString("datetime");
|
||||
String btnTxt = jsonObject.getString("btntxt");
|
||||
substituteMap.put("subTitle", datetime + "——" + userName);
|
||||
substituteMap.put("description", desc);
|
||||
substituteMap.put("taskName", taskName);
|
||||
String description = StringSolveUtils.placeholderReplace(AuditConstants.SunepsAuditType.AUDIT_DESCRIPTION_FORMAT.getCode(), substituteMap);
|
||||
TextCardMessage textCardMessage = new TextCardMessage();
|
||||
TextCard textCard = new TextCard();
|
||||
textCard.setTitle(title)
|
||||
.setDescription(description)
|
||||
.setUrl(redirectUrl);
|
||||
if (StringUtils.isNotBlank(btnTxt)) {
|
||||
textCard.setBtntxt(btnTxt);
|
||||
}
|
||||
textCardMessage.setTextcard(textCard).setAppType(appType).setTouser(toUser);
|
||||
//TODO 发送企业微信文本卡片消息
|
||||
sendWxMsgService.sendTextCardMsg(textCardMessage);
|
||||
consumeMsg = "企业微信消息发送成功,接收用户【" + userEname + "-" + msgtype + "】!";
|
||||
//TODO 保存文本卡片消息队列消费的消息信息
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), msg, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_SUCCESS.getCode(), consumeMsg);
|
||||
}
|
||||
|
||||
// 文本消息
|
||||
if (Objects.equals(msgtype, WxMsgTypeConstant.FILE.getCode())) {
|
||||
/* 构建审批回调url */
|
||||
String appType = jsonObject.getString("appType");
|
||||
String approveType = jsonObject.getString("approveType");
|
||||
String companyCode = jsonObject.getString("userCompanyCode");
|
||||
String taskId = jsonObject.getString("taskId");
|
||||
String processInstanceId = jsonObject.getString("processInstanceId");
|
||||
String businessId = jsonObject.getString("businessId");
|
||||
Map<String, Object> substituteMap = new HashMap<>(5);
|
||||
substituteMap.put("appType", appType.toLowerCase(Locale.ROOT));
|
||||
substituteMap.put("approveType", approveType);
|
||||
substituteMap.put("companyCode", companyCode);
|
||||
substituteMap.put("taskId", taskId);
|
||||
substituteMap.put("processInstanceId", processInstanceId);
|
||||
substituteMap.put("businessId", businessId);
|
||||
String redirectUrl = StringSolveUtils.placeholderReplace(AuditConstants.SunepsAuditType.REDIRECT_URL.getCode(), substituteMap);
|
||||
|
||||
/* 构建文本消息 */
|
||||
String title = jsonObject.getString("title");
|
||||
String taskName = jsonObject.getString("taskName");
|
||||
String datetime = jsonObject.getString("datetime");
|
||||
String btnTxt = jsonObject.getString("btntxt");
|
||||
substituteMap.put("subTitle", datetime + "——" + userName);
|
||||
substituteMap.put("description", desc);
|
||||
substituteMap.put("taskName", taskName);
|
||||
String description = StringSolveUtils.placeholderReplace(AuditConstants.SunepsAuditType.AUDIT_DESCRIPTION_FORMAT.getCode(), substituteMap);
|
||||
TextCardMessage textCardMessage = new TextCardMessage();
|
||||
TextCard textCard = new TextCard();
|
||||
textCard.setTitle(title)
|
||||
.setDescription(description)
|
||||
.setUrl(redirectUrl);
|
||||
if (StringUtils.isNotBlank(btnTxt)) {
|
||||
textCard.setBtntxt(btnTxt);
|
||||
}
|
||||
textCardMessage.setTextcard(textCard).setAppType(appType).setTouser(toUser);
|
||||
//TODO 送企业微信文本卡片消息
|
||||
sendWxMsgService.sendTextCardMsg(textCardMessage);
|
||||
consumeMsg = "企业微信消息发送成功,接收用户【" + userEname + "-" + msgtype + "】!";
|
||||
//TODO 存文本卡片消息队列消费的消息信息
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), msg, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_SUCCESS.getCode(), consumeMsg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("消息发送失败!{}", e.getMessage(), e);
|
||||
try {
|
||||
// 重试时不重复保存队列消息
|
||||
if (reconsumeTimes>0) {
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), msg, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_FAILURE.getCode(), e.getMessage());
|
||||
}
|
||||
} catch (Exception jmsException) {
|
||||
log.error("消息保存失败!{}", jmsException.getMessage(), jmsException);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.zt.plat.module.system.mq.message.msg;
|
||||
|
||||
import com.zt.plat.framework.common.core.KeyValue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息发送消息
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Data
|
||||
public class MsgSendMessage {
|
||||
|
||||
public static final String TOPIC = "SMS_SEND_TOPIC";
|
||||
/**
|
||||
* 消息日志编号
|
||||
*/
|
||||
@NotNull(message = "短信日志编号不能为空")
|
||||
private Long logId;
|
||||
|
||||
/**
|
||||
* 消息内容(已按模板格式化后的文本)
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 消息渠道编号
|
||||
*/
|
||||
@NotNull(message = "短信渠道编号不能为空")
|
||||
private Long channelId;
|
||||
/**
|
||||
* 消息 API 的模板编号
|
||||
*/
|
||||
@NotNull(message = "消息 API 的模板编号不能为空")
|
||||
private String apiTemplateId;
|
||||
/**
|
||||
* 短信模板参数
|
||||
*/
|
||||
private List<KeyValue<String, Object>> templateParams;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.zt.plat.module.system.mq.producer.msg;
|
||||
|
||||
import com.zt.plat.framework.common.core.KeyValue;
|
||||
import com.zt.plat.module.system.mq.message.sms.SmsSendMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sms 短信相关消息的 Producer
|
||||
*
|
||||
* @author zzf
|
||||
* @since 2021/3/9 16:35
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MsgProducer {
|
||||
|
||||
@Resource
|
||||
private RocketMQTemplate rocketMQTemplate;
|
||||
|
||||
/**
|
||||
* 发送 {@link SmsSendMessage} 消息
|
||||
* @param logId 短信日志编号
|
||||
* @param channelId 渠道编号
|
||||
* @param apiTemplateId 短信模板编号
|
||||
* @param templateParams 短信模板参数
|
||||
*/
|
||||
public void sendMsg(Long logId, String content,
|
||||
Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
|
||||
SmsSendMessage message = new SmsSendMessage().setLogId(logId).setContent(content);
|
||||
message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams);
|
||||
rocketMQTemplate.syncSend(SmsSendMessage.TOPIC, message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +1,37 @@
|
||||
package com.zt.plat.module.system.service.dept;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.dept.dto.DeptMsgRespDTO;
|
||||
import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO;
|
||||
import com.zt.plat.module.system.api.esp.dto.EspDto;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.DeptMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.EspMapper;
|
||||
import com.zt.plat.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.zt.plat.module.system.enums.dept.DeptSourceEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.seata.common.result.Result;
|
||||
import org.apache.seata.spring.annotation.GlobalTransactional;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -33,13 +44,31 @@ import static com.zt.plat.module.system.enums.ErrorCodeConstants.*;
|
||||
@Validated
|
||||
public class EspServiceImpl implements IEspService {
|
||||
|
||||
|
||||
@Resource
|
||||
private DeptExternalCodeService deptExternalCodeService;
|
||||
|
||||
@Resource
|
||||
private EspMapper espMapper;
|
||||
|
||||
@Resource
|
||||
private DeptMapper deptMapper;
|
||||
|
||||
@Resource
|
||||
private CacheManager cacheManager;
|
||||
|
||||
@Resource
|
||||
private com.zt.plat.module.system.mq.producer.databus.DatabusChangeProducer databusChangeProducer;
|
||||
|
||||
private static final String ROOT_CODE_PREFIX = "ZT";
|
||||
private static final String EXTERNAL_CODE_PREFIX = "CU";
|
||||
private static final int CODE_SEGMENT_LENGTH = 3;
|
||||
private static final int MAX_SEQUENCE = 999;
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
private static final Comparator<DeptDO> DEPT_COMPARATOR = Comparator
|
||||
.comparing(DeptDO::getSort, Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
.thenComparing(DeptDO::getId, Comparator.nullsLast(Comparator.naturalOrder()));
|
||||
|
||||
@Override
|
||||
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#createReqVO.deptId", beforeInvocation = false)
|
||||
public Long createDeptPushMsg(EspSaveRespVo createReqVO) {
|
||||
@@ -103,12 +132,243 @@ public class EspServiceImpl implements IEspService {
|
||||
return espMapper.selectListByDeptId(deptId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EspDto> pushMsg(DeptSaveReqDTO syncReqDTO) {
|
||||
|
||||
return BeanUtils.toBean(espMapper.selectpushMsg(syncReqDTO), EspDto.class);
|
||||
@Override
|
||||
@GlobalTransactional(rollbackFor = Exception.class)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
|
||||
allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
|
||||
public Long createDept(DeptSaveReqVO createReqVO) {
|
||||
// 允许上级组织为空,视为顶级组织
|
||||
createReqVO.setParentId(normalizeParentId(createReqVO.getParentId()));
|
||||
// 创建时默认有效
|
||||
createReqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
// 默认部门来源:未指定时视为外部部门
|
||||
if (createReqVO.getDeptSource() == null) {
|
||||
createReqVO.setDeptSource(DeptSourceEnum.EXTERNAL.getSource());
|
||||
}
|
||||
// 校验父部门的有效性
|
||||
validateParentDept(null, createReqVO.getParentId());
|
||||
// 校验部门名的唯一性
|
||||
validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
|
||||
// 生成并校验部门编码(所有来源统一走生成逻辑,iWork 不再豁免)
|
||||
if (Boolean.TRUE.equals(createReqVO.getDelayCodeGeneration())) {
|
||||
createReqVO.setCode(null);
|
||||
} else {
|
||||
String resolvedCode = generateDeptCode(createReqVO.getParentId(), createReqVO.getDeptSource());
|
||||
validateDeptCodeUnique(null, resolvedCode);
|
||||
createReqVO.setCode(resolvedCode);
|
||||
}
|
||||
|
||||
// 插入部门
|
||||
DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class);
|
||||
// 设置部门来源(前置已默认化,此处兜底)
|
||||
if (dept.getDeptSource() == null) {
|
||||
dept.setDeptSource(DeptSourceEnum.EXTERNAL.getSource());
|
||||
}
|
||||
int insert = deptMapper.insert(dept);
|
||||
if (insert == 0) {
|
||||
throw exception(USER_DEPT_SAVE_EXISTS);
|
||||
}
|
||||
// 外部编码映射
|
||||
upsertExternalMappingIfPresent(dept.getId(), createReqVO);
|
||||
// 发布部门创建事件
|
||||
databusChangeProducer.sendDeptCreatedMessage(dept);
|
||||
//推送消息
|
||||
DeptSaveReqDTO deptSaveReqDTO = new DeptSaveReqDTO();
|
||||
BeanUtils.copyProperties(dept,deptSaveReqDTO);
|
||||
return dept.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<DeptMsgRespDTO>> selectDepMsg(DeptSaveReqDTO syncReqDTO) {
|
||||
if (syncReqDTO == null) {
|
||||
return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST);
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<DeptPushMsgDO> wrapper = new LambdaQueryWrapper<>();
|
||||
if(syncReqDTO.getId() != null){
|
||||
wrapper.eq(DeptPushMsgDO::getId, syncReqDTO.getId());
|
||||
}
|
||||
|
||||
List<DeptPushMsgDO> list = espMapper.selectList(wrapper);
|
||||
if (CollectionUtils.isEmpty(list)){
|
||||
return CommonResult.error(GlobalErrorCodeConstants.NOT_FOUND);
|
||||
}
|
||||
|
||||
ArrayList<DeptMsgRespDTO> lists = new ArrayList<>(list.size());
|
||||
for (DeptPushMsgDO deptPushMsgDO : list){
|
||||
DeptMsgRespDTO deptMsgRespDTO = new DeptMsgRespDTO();
|
||||
BeanUtils.copyProperties(deptPushMsgDO, deptMsgRespDTO);
|
||||
lists.add(deptMsgRespDTO);
|
||||
}
|
||||
return CommonResult.success(lists);
|
||||
}
|
||||
|
||||
private Long normalizeParentId(Long parentId) {
|
||||
return parentId == null ? DeptDO.PARENT_ID_ROOT : parentId;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void validateParentDept(Long id, Long parentId) {
|
||||
if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) {
|
||||
return;
|
||||
}
|
||||
// 1. 不能设置自己为父部门
|
||||
if (Objects.equals(id, parentId)) {
|
||||
throw exception(DEPT_PARENT_ERROR);
|
||||
}
|
||||
// 2. 父部门不存在
|
||||
DeptDO parentDept = deptMapper.selectById(parentId);
|
||||
if (parentDept == null) {
|
||||
return;
|
||||
}
|
||||
// 3. 递归校验父部门,如果父部门是自己的子部门,则报错,避免形成环路
|
||||
if (id == null) { // id 为空,说明新增,不需要考虑环路
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < Short.MAX_VALUE; i++) {
|
||||
// 3.1 校验环路
|
||||
parentId = parentDept.getParentId();
|
||||
if (Objects.equals(id, parentId)) {
|
||||
throw exception(DEPT_PARENT_IS_CHILD);
|
||||
}
|
||||
// 3.2 继续递归下一级父部门
|
||||
if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) {
|
||||
break;
|
||||
}
|
||||
parentDept = deptMapper.selectById(parentId);
|
||||
if (parentDept == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String generateDeptCode(Long parentId, Integer deptSource) {
|
||||
Long effectiveParentId = normalizeParentId(parentId);
|
||||
String prefix = resolveCodePrefix(effectiveParentId, deptSource);
|
||||
int nextSequence = determineNextSequence(effectiveParentId, prefix);
|
||||
assertSequenceRange(nextSequence);
|
||||
return prefix + formatSequence(nextSequence);
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
void validateDeptNameUnique(Long id, Long parentId, String name) {
|
||||
Long effectiveParentId = normalizeParentId(parentId);
|
||||
if (Objects.equals(effectiveParentId, DeptDO.PARENT_ID_ROOT)) {
|
||||
return;
|
||||
}
|
||||
DeptDO dept = deptMapper.selectByParentIdAndName(effectiveParentId, name);
|
||||
if (dept == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 id 的部门
|
||||
if (id == null) {
|
||||
throw exception(DEPT_NAME_DUPLICATE);
|
||||
}
|
||||
if (ObjectUtil.notEqual(dept.getId(), id)) {
|
||||
throw exception(DEPT_NAME_DUPLICATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
void validateDeptCodeUnique(Long id, String code) {
|
||||
if (StrUtil.isBlank(code)) {
|
||||
return;
|
||||
}
|
||||
DeptDO dept = deptMapper.selectByCode(code);
|
||||
if (dept == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 id 的部门
|
||||
if (id == null) {
|
||||
throw exception(DEPT_CODE_DUPLICATE);
|
||||
}
|
||||
if (ObjectUtil.notEqual(dept.getId(), id)) {
|
||||
throw exception(DEPT_CODE_DUPLICATE);
|
||||
}
|
||||
}
|
||||
|
||||
private void upsertExternalMappingIfPresent(Long deptId, DeptSaveReqVO reqVO) {
|
||||
String systemCode = StrUtil.trimToNull(reqVO.getExternalSystemCode());
|
||||
String externalCode = StrUtil.trimToNull(reqVO.getExternalDeptCode());
|
||||
if (StrUtil.hasEmpty(systemCode, externalCode) || deptId == null) {
|
||||
return;
|
||||
}
|
||||
String externalName = StrUtil.trimToNull(reqVO.getExternalDeptName());
|
||||
deptExternalCodeService.saveOrUpdateDeptExternalCode(deptId, systemCode, externalCode, externalName, reqVO.getStatus());
|
||||
}
|
||||
|
||||
|
||||
private String resolveCodePrefix(Long parentId, Integer deptSource) {
|
||||
boolean isExternal = Objects.equals(deptSource, DeptSourceEnum.EXTERNAL.getSource());
|
||||
if (DeptDO.PARENT_ID_ROOT.equals(parentId)) {
|
||||
return isExternal ? EXTERNAL_CODE_PREFIX : ROOT_CODE_PREFIX;
|
||||
}
|
||||
|
||||
DeptDO parentDept = deptMapper.selectById(parentId);
|
||||
if (parentDept == null || StrUtil.isBlank(parentDept.getCode())) {
|
||||
return isExternal ? EXTERNAL_CODE_PREFIX : ROOT_CODE_PREFIX;
|
||||
}
|
||||
|
||||
String parentCode = parentDept.getCode();
|
||||
if (isExternal) {
|
||||
if (parentCode.startsWith(EXTERNAL_CODE_PREFIX)) {
|
||||
return parentCode;
|
||||
}
|
||||
if (parentCode.startsWith(ROOT_CODE_PREFIX)) {
|
||||
return EXTERNAL_CODE_PREFIX + parentCode.substring(ROOT_CODE_PREFIX.length());
|
||||
}
|
||||
return EXTERNAL_CODE_PREFIX;
|
||||
}
|
||||
return parentCode;
|
||||
}
|
||||
|
||||
|
||||
private int determineNextSequence(Long parentId, String prefix) {
|
||||
DeptDO lastChild = deptMapper.selectLastChildByCode(parentId, prefix);
|
||||
Integer sequence = parseSequence(lastChild != null ? lastChild.getCode() : null, prefix);
|
||||
if (sequence != null) {
|
||||
return sequence + 1;
|
||||
}
|
||||
return deptMapper.selectListByParentId(parentId, null).stream()
|
||||
.map(DeptDO::getCode)
|
||||
.map(code -> parseSequence(code, prefix))
|
||||
.filter(Objects::nonNull)
|
||||
.max(Integer::compareTo)
|
||||
.map(val -> val + 1)
|
||||
.orElse(1);
|
||||
}
|
||||
|
||||
|
||||
private void assertSequenceRange(int sequence) {
|
||||
if (sequence > MAX_SEQUENCE) {
|
||||
throw exception(DEPT_CODE_OUT_OF_RANGE);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatSequence(int sequence) {
|
||||
return StrUtil.padPre(String.valueOf(sequence), CODE_SEGMENT_LENGTH, '0');
|
||||
}
|
||||
|
||||
private Integer parseSequence(String code, String prefix) {
|
||||
if (StrUtil.isBlank(code) || StrUtil.isBlank(prefix) || !code.startsWith(prefix)) {
|
||||
return null;
|
||||
}
|
||||
String suffix = code.substring(prefix.length());
|
||||
if (suffix.length() != CODE_SEGMENT_LENGTH || !StrUtil.isNumeric(suffix)) {
|
||||
return null;
|
||||
}
|
||||
return Integer.parseInt(suffix);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private DeptPushMsgDO validateExists(Long id) {
|
||||
if (id == null) {
|
||||
throw exception(DEPT_EXTERNAL_RELATION_NOT_EXISTS);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.zt.plat.module.system.service.dept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.api.dept.dto.DeptMsgRespDTO;
|
||||
import com.zt.plat.module.system.api.dept.dto.DeptSaveReqDTO;
|
||||
import com.zt.plat.module.system.api.esp.dto.EspDto;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门推送消息 Service 接口
|
||||
*/
|
||||
@@ -47,11 +48,18 @@ public interface IEspService {
|
||||
*/
|
||||
List<DeptPushMsgDO> getPushMsgByDeptId(Long deptId);
|
||||
|
||||
/**
|
||||
* 创建部门
|
||||
* @param reqVO 部门信息
|
||||
* @return 部门编号
|
||||
*/
|
||||
Long createDept(DeptSaveReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 推送部门数据到外部系统
|
||||
* @param syncReqDTO 同步请求
|
||||
*/
|
||||
List<EspDto> pushMsg(DeptSaveReqDTO syncReqDTO);
|
||||
CommonResult<List<DeptMsgRespDTO>> selectDepMsg(DeptSaveReqDTO syncReqDTO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import com.zt.plat.module.system.mq.message.sms.SmsSendMessage;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* 监听消息 Service 接口
|
||||
* @author ZT
|
||||
*/
|
||||
public interface IMonitorMsgService {
|
||||
|
||||
/**
|
||||
* 发送单条短信给管理后台的用户
|
||||
* 在 mobile 为空时,使用 userId 加载对应管理员的手机号
|
||||
* @param mobile 手机号
|
||||
* @param userId 用户编号
|
||||
* @param templateCode 短信模板编号
|
||||
* @param templateParams 短信模板参数
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long sendSingleSmsToAdmin(String mobile, Long userId,
|
||||
String templateCode, Map<String, Object> templateParams);
|
||||
|
||||
/**
|
||||
* 发送单条短信给用户 APP 的用户
|
||||
* 在 mobile 为空时,使用 userId 加载对应会员的手机号
|
||||
* @param mobile 手机号
|
||||
* @param userId 用户编号
|
||||
* @param templateCode 短信模板编号
|
||||
* @param templateParams 短信模板参数
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long sendSingleSmsToMember(String mobile, Long userId,
|
||||
String templateCode, Map<String, Object> templateParams);
|
||||
|
||||
/**
|
||||
* 发送单条短信给用户
|
||||
* @param mobile 手机号
|
||||
* @param userId 用户编号
|
||||
* @param userType 用户类型
|
||||
* @param templateCode 短信模板编号
|
||||
* @param templateParams 短信模板参数
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long sendSingleSms(String mobile, Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams);
|
||||
|
||||
default void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams) {
|
||||
throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行真正的短信发送
|
||||
* 注意,该方法仅仅提供给 MQ Consumer 使用
|
||||
* @param message 短信
|
||||
*/
|
||||
void doSendSms(SmsSendMessage message);
|
||||
|
||||
/**
|
||||
* 接收短信的接收结果
|
||||
* @param channelCode 渠道编码
|
||||
* @param text 结果内容
|
||||
* @throws Throwable 处理失败时,抛出异常
|
||||
*/
|
||||
void receiveSmsStatus(String channelCode, String text) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.TextMessage;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsLogDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* 消息日志 Service 接口
|
||||
*
|
||||
* @author zzf
|
||||
*/
|
||||
public interface IMsgLogService {
|
||||
|
||||
/**
|
||||
* 创建短信日志
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userType 用户类型
|
||||
* @param isSend 是否发送
|
||||
* @param template 短信模板
|
||||
* @param templateContent 短信内容
|
||||
* @param templateParams 短信参数
|
||||
* @return 发送日志编号
|
||||
*/
|
||||
Long createSmsLog(Long userId, Integer userType, Boolean isSend,
|
||||
SmsTemplateDO template, String templateContent, Map<String, Object> templateParams);
|
||||
|
||||
/**
|
||||
* 更新日志的发送结果
|
||||
*
|
||||
* @param id 日志编号
|
||||
* @param success 发送是否成功
|
||||
* @param apiSendCode 短信 API 发送结果的编码
|
||||
* @param apiSendMsg 短信 API 发送失败的提示
|
||||
* @param apiRequestId 短信 API 发送返回的唯一请求 ID
|
||||
* @param apiSerialNo 短信 API 发送返回的序号
|
||||
*/
|
||||
void updateSmsSendResult(Long id, Boolean success,
|
||||
String apiSendCode, String apiSendMsg,
|
||||
String apiRequestId, String apiSerialNo);
|
||||
|
||||
/**
|
||||
* 更新日志的接收结果
|
||||
*
|
||||
* @param id 日志编号
|
||||
* @param success 是否接收成功
|
||||
* @param receiveTime 用户接收时间
|
||||
* @param apiReceiveCode API 接收结果的编码
|
||||
* @param apiReceiveMsg API 接收结果的说明
|
||||
*/
|
||||
void updateSmsReceiveResult(Long id, Boolean success,
|
||||
LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg);
|
||||
|
||||
/**
|
||||
* 获得短信日志分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 短信日志分页
|
||||
*/
|
||||
PageResult<SmsLogDO> getSmsLogPage(SmsLogPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 根据日志编号查询短信日志
|
||||
*
|
||||
* @param id 日志编号
|
||||
* @return 短信日志
|
||||
*/
|
||||
SmsLogDO getSmsLog(Long id);
|
||||
|
||||
/**
|
||||
* 创建消息日志
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userType 用户类型
|
||||
* @param isSend 是否发送
|
||||
* @param template 短信模板
|
||||
* @param textMessage 短信内容
|
||||
* @param templateParams 短信参数
|
||||
* @return 创建消息日志编号
|
||||
*/
|
||||
Long createMsgLog(Long userId, Integer userType, Boolean isSend, SmsTemplateDO template, TextMessage textMessage, Map<String, Object> templateParams);
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.*;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.MsgTemplateSendReqVO;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 生产者发送消息接口类
|
||||
*
|
||||
* @author dongqiang.hao
|
||||
*/
|
||||
public interface ISendMsgService {
|
||||
|
||||
/**
|
||||
* 发送文本消息
|
||||
*
|
||||
* @param textMessage 文本消息包装对象
|
||||
* @return r 响应对象
|
||||
*/
|
||||
CommonResult<Object> sendTextMsg(MsgTemplateSendReqVO sendReqVO, TextMessage textMessage) throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信图片消息
|
||||
*
|
||||
* @param imageMessage 图片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendImageMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,ImageMessage imageMessage)throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* 发送企业微信语音消息
|
||||
*
|
||||
* @param voiceMessage 语音消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendVoiceMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,VoiceMessage voiceMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信视频消息
|
||||
*
|
||||
* @param videoMessage 视频消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendVideoMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,VideoMessage videoMessage)throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* 发送企业微信文件消息
|
||||
*
|
||||
* @param fileMessage 文件消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendFileMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,FileMessage fileMessage)throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* 发送企业微信文本卡片消息
|
||||
*
|
||||
* @param textCardMessage 文本卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendTextCardMsgPich01(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,TextCardMessage textCardMessage) throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信文本卡片消息
|
||||
*
|
||||
* @param textCardMessage 文本卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendTextCardMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,TextCardMessage textCardMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信图文消息
|
||||
*
|
||||
* @param newsMessage 图文消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendNewsMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,NewsMessage newsMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信图文消息(mpnews)
|
||||
*
|
||||
* @param mpNewsMessage 图文消息(mpnews)对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendMpNewsMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,MpNewsMessage mpNewsMessage)throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* 发送企业微信markdown消息
|
||||
*
|
||||
* @param markdownMessage markdown消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendMarkdownMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,MarkdownMessage markdownMessage) throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信小程序通知消息
|
||||
*
|
||||
* @param miniProgramNoticeMessage 小程序通知消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendMiniProgramNoticeMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,MiniProgramNoticeMessage miniProgramNoticeMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信任务卡片消息
|
||||
*
|
||||
* @param interactiveTaskCardMessage 任务卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendInteractiveTaskCardMsg(Long userId, Integer userType,
|
||||
String templateCode, Map<String, Object> templateParams,InteractiveTaskCardMessage interactiveTaskCardMessage)throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.*;
|
||||
|
||||
/**
|
||||
* TODO ============================== 企微消息发送接口类 =========================================
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
public interface ISendWxMsgService {
|
||||
|
||||
/**
|
||||
* 发送企业微信文本消息
|
||||
*
|
||||
* @param textMessage 文本消息包装对象
|
||||
* @return r 响应对象
|
||||
*/
|
||||
CommonResult<Object> sendTextMsg(TextMessage textMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信图片消息
|
||||
*
|
||||
* @param imageMessage 图片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendImageMsg(ImageMessage imageMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信语音消息
|
||||
*
|
||||
* @param voiceMessage 语音消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendVoiceMsg(VoiceMessage voiceMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信视频消息
|
||||
*
|
||||
* @param videoMessage 视频消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendVideoMsg(VideoMessage videoMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信文件消息
|
||||
*
|
||||
* @param fileMessage 文件消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendFileMsg(FileMessage fileMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信文本卡片消息
|
||||
*
|
||||
* @param textCardMessage 文本卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendTextCardMsg(TextCardMessage textCardMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信文本卡片消息 -物资存货智能管理 预警信息
|
||||
*
|
||||
* @param textCardMessage 文本卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendTextCardMsgPich01(TextCardMessage textCardMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信图文消息
|
||||
*
|
||||
* @param newsMessage 图文消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendNewsMsg(NewsMessage newsMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信图文消息(mpnews)
|
||||
*
|
||||
* @param mpNewsMessage 图文消息(mpnews)对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendMpNewsMsg(MpNewsMessage mpNewsMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信markdown消息
|
||||
*
|
||||
* @param markdownMessage markdown消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendMarkdownMsg(MarkdownMessage markdownMessage)throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信小程序通知消息
|
||||
*
|
||||
* @param miniProgramNoticeMessage 小程序通知消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendMiniProgramNoticeMsg(MiniProgramNoticeMessage miniProgramNoticeMessage) throws Exception;
|
||||
|
||||
/**
|
||||
* 发送企业微信任务卡片消息
|
||||
*
|
||||
* @param interactiveTaskCardMessage 任务卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
CommonResult<Object> sendInteractiveTaskCardMsg(InteractiveTaskCardMessage interactiveTaskCardMessage) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysActiveMqLog;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
/**
|
||||
* RocketMq消息队列 服务类
|
||||
* @author dongqiang.hao
|
||||
* @since 2025-01-07
|
||||
*/
|
||||
public interface ISysRocketMqService extends IService<SysActiveMqLog> {
|
||||
|
||||
/**
|
||||
* 保存消息队列消费的消息信息
|
||||
*
|
||||
* @param listenerClassName 队列监听类名
|
||||
* @param tm 消息对象
|
||||
* @param jmsType 消息类型(QUEUE-队列/TOPIC-主题)
|
||||
* @throws Exception 消息消费异常
|
||||
*/
|
||||
void saveByTextMessage(String listenerClassName, MessageExt tm, String jmsType) throws Exception;
|
||||
|
||||
/**
|
||||
* 保存文本卡片消息队列消费的消息信息
|
||||
*
|
||||
* @param listenerClassName 队列监听类名
|
||||
* @param tm 消息对象
|
||||
* @param jmsType 消息类型(QUEUE-队列/TOPIC-主题)
|
||||
* @param consumeFlag 消费结果(success-成功/failure-失败)
|
||||
* @param consumeMessage 消费失败消息
|
||||
* @throws Exception 消息消费异常
|
||||
*/
|
||||
void saveByTextMessage(String listenerClassName, MessageExt tm, String jmsType, String consumeFlag, String consumeMessage) throws Exception;
|
||||
|
||||
/**
|
||||
* 保存阳光采购消息队列消费的消息信息
|
||||
*
|
||||
* @param listenerClassName 队列监听类名
|
||||
* @param tm 消息对象
|
||||
* @param jmsType 消息类型(QUEUE-队列/TOPIC-主题)
|
||||
* @param consumeFlag 消费结果(success-成功/failure-失败)
|
||||
* @param consumeMessage 消费失败消息
|
||||
* @throws Exception 消息消费异常
|
||||
*/
|
||||
void saveSunepsByTextMessage(String listenerClassName, MessageExt tm, String jmsType, String consumeFlag, String consumeMessage) throws Exception;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysWxAuditConfig;
|
||||
|
||||
/**
|
||||
* 企业微信审核配置表 服务类
|
||||
*
|
||||
* @author Dy
|
||||
* @since 2021-08-26
|
||||
*/
|
||||
public interface SysWxAuditConfigService extends IService<SysWxAuditConfig> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package com.zt.plat.module.system.service.msg.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 系统参数配置组件
|
||||
* TODO
|
||||
* @author luzemin
|
||||
*/
|
||||
@Component
|
||||
@PropertySource("classpath:config.properties")
|
||||
public class HttpUrlConfig {
|
||||
|
||||
/**
|
||||
* 企业微信审核涉及的app
|
||||
*/
|
||||
public static final String[] APP_TYPES = new String[]{"suneps", "pscs", "market", "bssim", "dzxs", "boli", "lmps"};
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——提交审核
|
||||
*/
|
||||
public static final String COMMIT_URL = "commitUrl";
|
||||
/**
|
||||
* 企业微信审核http请求url——获取审核历史信息
|
||||
*/
|
||||
public static final String COMMENT_URL = "commentUrl";
|
||||
/**
|
||||
* 企业微信审核http请求url——获取用户待办
|
||||
*/
|
||||
public static final String TODO_URL = "todoUrl";
|
||||
/**
|
||||
* 企业微信审核http请求url——获取用户已办
|
||||
*/
|
||||
public static final String DONE_URL = "doneUrl";
|
||||
/**
|
||||
* 企业微信审核http请求url——获取流程是否结束信息
|
||||
*/
|
||||
public static final String FINISH_URL = "finishUrl";
|
||||
/**
|
||||
* 企业微信审核http请求url——文件存储
|
||||
*/
|
||||
public static final String STORAGE_URL = "storageUrl";
|
||||
/**
|
||||
* 获取合同文本内容请求url
|
||||
*/
|
||||
public static final String RENDERBPOCONTENT_URL = "renderBpoContentUrl";
|
||||
/**
|
||||
* 获字化采购报表地址
|
||||
*/
|
||||
private static final String FINE_REPORT_URL = "fineReportUrl";
|
||||
/**
|
||||
* 获数字化附件管理地址
|
||||
*/
|
||||
private static final String FILE_STORAGE_URL = "fileStorageUrl";
|
||||
|
||||
/**
|
||||
* 获数字化附件预览
|
||||
*/
|
||||
private static final String FILE_VIEW_URL = "fileViewUrl";
|
||||
|
||||
/**
|
||||
* 应用http调用地址
|
||||
*/
|
||||
private static final String HTTP_CONTEXT_PATH = "httpContextPath";
|
||||
|
||||
/**
|
||||
* 微信审核应用地址
|
||||
*/
|
||||
private static final String WX_CONTEXT_PATH = "wxContextPath";
|
||||
|
||||
/**
|
||||
* 微信审核应用地址
|
||||
*/
|
||||
private static final String WX_SUBMIT_DATA = "wxSubmitData";
|
||||
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——提交审核应用和url映射集合
|
||||
*/
|
||||
private static final Map<String, String> COMMIT_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 企业微信审核http请求url——获取审核历史信息应用和url映射集合
|
||||
*/
|
||||
private static final Map<String, String> COMMENT_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 企业微信审核http请求url——获取用户待办应用和url映射集合
|
||||
*/
|
||||
private static final Map<String, String> TODO_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 企业微信审核http请求url——获取用户已办应用和url映射集合
|
||||
*/
|
||||
private static final Map<String, String> DONE_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 企业微信审核http请求url——文件存储应用和url映射集合
|
||||
*/
|
||||
private static final Map<String, String> STORAGE_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 企业微信审核http请求url——文件存储应用和url映射集合
|
||||
*/
|
||||
private static final Map<String, String> FINISH_URL_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 获取合同文本内容请求url
|
||||
*/
|
||||
private static final Map<String, String> RENDERBPOCONTENT_URL_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 获取合同文本内容请求url
|
||||
*/
|
||||
private static final Map<String, String> FINE_REPORT_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 获取合同文本内容请求url
|
||||
*/
|
||||
private static final Map<String, String> FILE_VIEW_URL_MAPPING = new HashMap<>();
|
||||
/**
|
||||
* 获取合同文本内容请求url
|
||||
*/
|
||||
private static final Map<String, String> FILE_STORAGE_URL_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 获取应用http调用地址
|
||||
*/
|
||||
private static final Map<String, String> HTTP_CONTEXT_PATH_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 微信审核应用地址
|
||||
*/
|
||||
private static final Map<String, String> WX_CONTEXT_PATH_MAPPING = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 微信审核应用地址
|
||||
*/
|
||||
private static final Map<String, String> WX_SUBMIT_DATA_MAPPING = new HashMap<>();
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void initConfig() throws IOException {
|
||||
Properties properties = new Properties();
|
||||
InputStream configInputStream = this.getClass().getClassLoader().getResourceAsStream("config.properties");
|
||||
properties.load(configInputStream);
|
||||
Arrays.stream(APP_TYPES).forEach(appType -> {
|
||||
COMMIT_URL_MAPPING.put(appType.concat(".").concat(COMMIT_URL), properties.getProperty(appType.concat(".").concat(COMMIT_URL)));
|
||||
COMMENT_URL_MAPPING.put(appType.concat(".").concat(COMMENT_URL), properties.getProperty(appType.concat(".").concat(COMMENT_URL)));
|
||||
TODO_URL_MAPPING.put(appType.concat(".").concat(TODO_URL), properties.getProperty(appType.concat(".").concat(TODO_URL)));
|
||||
DONE_URL_MAPPING.put(appType.concat(".").concat(DONE_URL), properties.getProperty(appType.concat(".").concat(DONE_URL)));
|
||||
STORAGE_URL_MAPPING.put(appType.concat(".").concat(STORAGE_URL), properties.getProperty(appType.concat(".").concat(STORAGE_URL)));
|
||||
FINISH_URL_MAPPING.put(appType.concat(".").concat(FINISH_URL), properties.getProperty(appType.concat(".").concat(FINISH_URL)));
|
||||
RENDERBPOCONTENT_URL_MAPPING.put(appType.concat(".").concat(RENDERBPOCONTENT_URL), properties.getProperty(appType.concat(".").concat(RENDERBPOCONTENT_URL)));
|
||||
FINE_REPORT_URL_MAPPING.put(appType.concat(".").concat(FINE_REPORT_URL), properties.getProperty(appType.concat(".").concat(FINE_REPORT_URL)));
|
||||
FILE_STORAGE_URL_MAPPING.put(appType.concat(".").concat(FILE_STORAGE_URL), properties.getProperty(appType.concat(".").concat(FILE_STORAGE_URL)));
|
||||
FILE_VIEW_URL_MAPPING.put(appType.concat(".").concat(FILE_VIEW_URL), properties.getProperty(appType.concat(".").concat(FILE_VIEW_URL)));
|
||||
HTTP_CONTEXT_PATH_MAPPING.put(appType.concat(".").concat(HTTP_CONTEXT_PATH), properties.getProperty(appType.concat(".").concat(HTTP_CONTEXT_PATH)));
|
||||
WX_CONTEXT_PATH_MAPPING.put(appType.concat(".").concat(WX_CONTEXT_PATH), properties.getProperty(appType.concat(".").concat(WX_CONTEXT_PATH)));
|
||||
WX_SUBMIT_DATA_MAPPING.put(appType.concat(".").concat(WX_SUBMIT_DATA), properties.getProperty(appType.concat(".").concat(WX_SUBMIT_DATA)));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——提交审核
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 对应应用提交审核请求url
|
||||
*/
|
||||
public String getCommitUrl(String appType) {
|
||||
return COMMIT_URL_MAPPING.get(appType.concat(".").concat(COMMIT_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——获取审核历史信息
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 对应应用获取审核历史信息请求url
|
||||
*/
|
||||
public String getCommentUrl(String appType) {
|
||||
return COMMENT_URL_MAPPING.get(appType.concat(".").concat(COMMENT_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——获取用户待办
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 对应应用获取用户待办请求url
|
||||
*/
|
||||
public String getTodoUrl(String appType) {
|
||||
return TODO_URL_MAPPING.get(appType.concat(".").concat(TODO_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——获取用户已办
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 对应应用获取用户已办请求url
|
||||
*/
|
||||
public String getDoneUrl(String appType) {
|
||||
return DONE_URL_MAPPING.get(appType.concat(".").concat(DONE_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——文件存储
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 对应应用文件存储请求url
|
||||
*/
|
||||
public String getStorageUrl(String appType) {
|
||||
return STORAGE_URL_MAPPING.get(appType.concat(".").concat(STORAGE_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信审核http请求url——获取流程是否结束信息
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 对应应用获取审核历史信息请求url
|
||||
*/
|
||||
public String getFinishUrl(String appType) {
|
||||
return FINISH_URL_MAPPING.get(appType.concat(".").concat(FINISH_URL));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取合同文本内容
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取合同文本内容请求url
|
||||
*/
|
||||
public String getRenderBpoContentUrl(String appType) {
|
||||
return RENDERBPOCONTENT_URL_MAPPING.get(appType.concat(".").concat(RENDERBPOCONTENT_URL));
|
||||
}
|
||||
/**
|
||||
* 数字化采购报表地址
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取请求url
|
||||
*/
|
||||
public String getFineReportUrl(String appType) {
|
||||
return FINE_REPORT_URL_MAPPING.get(appType.concat(".").concat(FINE_REPORT_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字化附件管理地址
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取合同文本内容请求url
|
||||
*/
|
||||
public String getFileStorageUrl(String appType) {
|
||||
return FILE_STORAGE_URL_MAPPING.get(appType.concat(".").concat(FILE_STORAGE_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字化附件预览
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取合同文本内容请求url
|
||||
*/
|
||||
public String getFileViewUrl(String appType) {
|
||||
return FILE_VIEW_URL_MAPPING.get(appType.concat(".").concat(FILE_VIEW_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用http调用地址
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取合同文本内容请求url
|
||||
*/
|
||||
public String getHttpContextPath(String appType) {
|
||||
return HTTP_CONTEXT_PATH_MAPPING.get(appType.concat(".").concat(HTTP_CONTEXT_PATH));
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用http调用地址
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取合同文本内容请求url
|
||||
*/
|
||||
public String getWxContextPath(String appType) {
|
||||
return WX_CONTEXT_PATH_MAPPING.get(appType.concat(".").concat(WX_CONTEXT_PATH));
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用http调用地址
|
||||
*
|
||||
* @param appType 应用类型
|
||||
* @return 获取合同文本内容请求url
|
||||
*/
|
||||
public String getWxSubmitData(String appType) {
|
||||
return WX_SUBMIT_DATA_MAPPING.get(appType.concat(".").concat(WX_SUBMIT_DATA));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
package com.zt.plat.module.system.service.msg.config;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.zt.plat.framework.common.util.http.HttpClientUtils;
|
||||
import com.zt.plat.module.system.api.sms.dto.code.User;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysWxAuditConfig;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.AuditConstants;
|
||||
import com.zt.plat.module.system.service.msg.SysWxAuditConfigService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
@PropertySource("classpath:qywx.properties")
|
||||
public class WeiXinProperties {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private SysWxAuditConfigService sysWxAuditConfigService;
|
||||
|
||||
/**
|
||||
* 企业微信——企业access_token获取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyAccessTokenUrl}")
|
||||
private String qyAccessTokenUrl;
|
||||
|
||||
/**
|
||||
* 企业微信——消息发送url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qySendMsgUrl}")
|
||||
private String qySendMsgUrl;
|
||||
|
||||
/**
|
||||
* 企业微信——用户Oauth2认证授权url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyAuthorizeUrl}")
|
||||
private String qyAuthorizeUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-企业自建应用用户信息获取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyUserInfoUrl}")
|
||||
private String qyUserInfoUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-企业自建应用用户信息获取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyUserUrl}")
|
||||
private String qyUserUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-上传临时素材url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyUploadTempFileUrl}")
|
||||
private String qyUploadTempFileUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-部门创建url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyDeptCreateUrl}")
|
||||
private String qyDeptCreateUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-部门更新url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyDeptUpdateUrl}")
|
||||
private String qyDeptUpdateUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-部门删除url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyDeptDeleteUrl}")
|
||||
private String qyDeptDeleteUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-部门列表拉取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyDeptQueryListUrl}")
|
||||
private String qyDeptQueryListUrl;
|
||||
/**
|
||||
* 企业微信-部门列表拉取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyMenuCreateUrl}")
|
||||
private String qyMenuCreateUrl;
|
||||
/**
|
||||
* 企业微信-部门列表拉取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyMenuDeleteUrl}")
|
||||
private String qyMenuDeleteUrl;
|
||||
/**
|
||||
* 企业微信-部门列表拉取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${qyMenuQueryUrl}")
|
||||
private String qyMenuQueryUrl;
|
||||
|
||||
/**
|
||||
* 企业微信-ticket获取url
|
||||
*/
|
||||
@Getter
|
||||
@Value("${ticketUrl}")
|
||||
private String ticketUrl;
|
||||
|
||||
/**
|
||||
* 获取企业微信-企业access_token,先从redis持久化缓存中获取,若不存在,则从腾讯获取并加入缓存,key为企业微信corpId+corpSecret
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @return String 企业微信-企业access_token
|
||||
*/
|
||||
public String getQyAccessToken(String corpId, String corpSecret) {
|
||||
Object accessToken = redisTemplate.opsForValue().get(corpId.concat(corpSecret));
|
||||
if (Objects.nonNull(accessToken)) {
|
||||
return accessToken.toString();
|
||||
} else {
|
||||
JSONObject jsonObject = HttpClientUtils.httpGet(this.buildQyAccessTokenUrl(corpId, corpSecret));
|
||||
String qyAccessToken = jsonObject.getString("access_token");
|
||||
String expiresIn = jsonObject.getString("expires_in");
|
||||
redisTemplate.opsForValue().set(corpId.concat(corpSecret), qyAccessToken, Long.parseLong(expiresIn), TimeUnit.SECONDS);
|
||||
return qyAccessToken;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JsapiTicket,先从redis持久化缓存中获取,若不存在,则从腾讯获取并加入缓存,key为ticket
|
||||
*/
|
||||
public String getJsapiTicket(String accessToken) {
|
||||
Object ticket = redisTemplate.opsForValue().get("ticket");
|
||||
if (Objects.nonNull(ticket)) {
|
||||
return ticket.toString();
|
||||
} else {
|
||||
JSONObject jsonObject = HttpClientUtils.httpGet(this.buildTicketUrl(accessToken));
|
||||
String qyTicket = jsonObject.getString("ticket");
|
||||
String expiresIn = jsonObject.getString("expires_in");
|
||||
redisTemplate.opsForValue().set("ticket", qyTicket, Long.parseLong(expiresIn), TimeUnit.SECONDS);
|
||||
return qyTicket;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业微信内建应用简单用户信息对象
|
||||
*
|
||||
* @param code 企业微信Oauth2授权成功后获取的授权code,每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @return QywxUser企业颞部开发获取的用户信息对象
|
||||
*/
|
||||
public User getUser(String code, String corpId, String corpSecret) {
|
||||
JSONObject jsonObject = HttpClientUtils.httpGet(this.buildQyUserInfoUrl(code, corpId, corpSecret));
|
||||
return jsonObject.toJavaObject(User.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信access_token获取url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
*/
|
||||
public String buildQyAccessTokenUrl(String corpId, String corpSecret) {
|
||||
return this.getQyAccessTokenUrl().replace("CORP_ID", corpId).replace("CORP_SECRET", corpSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建获取企业微信内建应用简单用户信息对象获取url
|
||||
*
|
||||
* @param code 企业微信Oauth2授权成功后获取的授权code
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
*/
|
||||
public String buildQyUserInfoUrl(String code, String corpId, String corpSecret) {
|
||||
return this.getQyUserInfoUrl().replaceAll("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replaceAll("CODE", code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建读取成员url
|
||||
*
|
||||
* @param userId 企业微信用户id
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
*/
|
||||
public String buildQyUserUrl(String userId, String corpId, String corpSecret) {
|
||||
return this.getQyUserUrl().replaceAll("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replaceAll("USER_ID", userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建消息发送url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
*/
|
||||
public String buildQySendMsgUrl(String corpId, String corpSecret) {
|
||||
return this.getQySendMsgUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信授权url
|
||||
*
|
||||
* @param redirectUrl 授权成功重定向url
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param state 授权成功重定向url携带的自定义参数
|
||||
* @return 企业微信授权url
|
||||
*/
|
||||
public String buildQyAuthorizeUrl(String corpId, String agentId, String redirectUrl, String state) {
|
||||
return this.getQyAuthorizeUrl()
|
||||
.replaceAll("CORPID", corpId)
|
||||
.replaceAll("REDIRECT_URI", redirectUrl)
|
||||
.replaceAll("AGENTID", agentId)
|
||||
.replaceAll("STATE", state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传临时素材url
|
||||
*
|
||||
* @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video),普通文件(file)
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @return String 上传临时素材url
|
||||
*/
|
||||
public String buildQyUploadTempFileUrl(String type, String corpId, String corpSecret) {
|
||||
return this.getQyUploadTempFileUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replaceAll("TYPE", type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信-部门创建url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @return String 部门创建url
|
||||
*/
|
||||
public String buildQyDeptCreateUrl(String corpId, String corpSecret) {
|
||||
return this.getQyDeptCreateUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信-部门更新url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @return String 部门更新url
|
||||
*/
|
||||
public String buildQyDeptUpdateUrl(String corpId, String corpSecret) {
|
||||
return this.getQyDeptUpdateUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信-部门删除url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @param id 部门id
|
||||
* @return String 部门删除url
|
||||
*/
|
||||
public String buildQyDeptDeleteUrl(String id, String corpId, String corpSecret) {
|
||||
return this.getQyDeptDeleteUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replaceAll("ID", id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信-部门列表拉取url
|
||||
*
|
||||
* @param id 部门id
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @return String 部门列表拉取url
|
||||
*/
|
||||
public String buildQyDeptQueryListUrl(String id, String corpId, String corpSecret) {
|
||||
return this.getQyDeptQueryListUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replaceAll("DEPARTMENT_ID", id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信应用菜单创建url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @param agentId 企业微信企业应用ID
|
||||
* @return String 应用菜单创建url
|
||||
*/
|
||||
public String buildQyMenuCreateUrl(String corpId, String corpSecret, String agentId) {
|
||||
return this.getQyMenuCreateUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replace("AGENT_ID", agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信应用菜单删除url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @param agentId 企业微信企业应用ID
|
||||
* @return string 应用菜单删除url
|
||||
*/
|
||||
public String buildQyMenuDeleteUrl(String corpId, String corpSecret, String agentId) {
|
||||
return this.getQyMenuDeleteUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replace("AGENT_ID", agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信应用菜单拉取url
|
||||
*
|
||||
* @param corpId 企业微信企业ID
|
||||
* @param corpSecret 企业微信企业应用密钥
|
||||
* @param agentId 企业微信企业应用ID
|
||||
* @return string 应用菜单拉取url
|
||||
*/
|
||||
public String buildQyMenuQueryUrl(String corpId, String corpSecret, String agentId) {
|
||||
return this.getQyMenuQueryUrl().replace("ACCESS_TOKEN", this.getQyAccessToken(corpId, corpSecret)).replace("AGENT_ID", agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建企业微信应用ticket获取url
|
||||
*/
|
||||
public String buildTicketUrl(String accessToken) {
|
||||
return this.getTicketUrl().replace("ACCESS_TOKEN", accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业微信消息发送应用配置
|
||||
*
|
||||
* @param appType 服务应用类型
|
||||
* @return SysWxAuditConfig 企业微信消息发送应用配置
|
||||
*/
|
||||
/**
|
||||
* 获取企业微信消息发送应用配置
|
||||
*
|
||||
* @param appType 服务应用类型
|
||||
* @return SysWxAuditConfig 企业微信消息发送应用配置
|
||||
*/
|
||||
public SysWxAuditConfig buildSysWxAuditConfig(String appType) throws Exception{
|
||||
if (StringUtils.isBlank(appType)) {
|
||||
throw new Exception("请求缺少服务应用类型!");
|
||||
|
||||
}
|
||||
List<SysWxAuditConfig> sysWxAuditConfigs = sysWxAuditConfigService.list(new LambdaQueryWrapper<SysWxAuditConfig>()
|
||||
.eq(SysWxAuditConfig::getAppType, appType)
|
||||
.eq(SysWxAuditConfig::getState, SysWxAuditConfig.STATE_ACTIVE)
|
||||
.eq(SysWxAuditConfig::getExt1, AuditConstants.CHINALCO_ZLEB_CHSZH));
|
||||
SysWxAuditConfig sysWxAuditConfig = sysWxAuditConfigs.get(0);
|
||||
if (StringUtils.isBlank(sysWxAuditConfig.getWxContextPath())) {
|
||||
throw new Exception("微信审核应用上下文根未配置,请配置后重试!");
|
||||
}
|
||||
if (StringUtils.isBlank(sysWxAuditConfig.getHttpContextPath())) {
|
||||
throw new Exception("微信审核http远程请求上下文根未配置,请配置后重试!");
|
||||
}
|
||||
if (StringUtils.isBlank(sysWxAuditConfig.getCorpId())) {
|
||||
throw new Exception("微信审核企业ID未配置,请配置后重试!");
|
||||
}
|
||||
if (StringUtils.isBlank(sysWxAuditConfig.getCorpSecret())) {
|
||||
throw new Exception("微信审核企业应用密钥未配置,请配置后重试!");
|
||||
}
|
||||
if (StringUtils.isBlank(sysWxAuditConfig.getAgentId())) {
|
||||
throw new Exception("微信审核企业应用ID未配置,请配置后重试!");
|
||||
}
|
||||
return sysWxAuditConfig;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.zt.plat.module.system.service.msg.impl;
|
||||
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.TextMessage;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsLogDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.zt.plat.module.system.dal.mysql.sms.SmsLogMapper;
|
||||
import com.zt.plat.module.system.enums.sms.SmsReceiveStatusEnum;
|
||||
import com.zt.plat.module.system.enums.sms.SmsSendStatusEnum;
|
||||
import com.zt.plat.module.system.service.msg.IMsgLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class MsgLogServiceImpl implements IMsgLogService {
|
||||
|
||||
@Resource
|
||||
private SmsLogMapper smsLogMapper;
|
||||
|
||||
|
||||
@Override
|
||||
public Long createSmsLog(Long userId, Integer userType, Boolean isSend, SmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSmsSendResult(Long id, Boolean success, String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SmsLogDO> getSmsLogPage(SmsLogPageReqVO pageReqVO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsLogDO getSmsLog(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建消息日志
|
||||
*/
|
||||
@Override
|
||||
public Long createMsgLog(Long userId, Integer userType, Boolean isSend, SmsTemplateDO template,
|
||||
TextMessage textMessage, Map<String, Object> templateParams) {
|
||||
SmsLogDO.SmsLogDOBuilder logBuilder = SmsLogDO.builder();
|
||||
// 根据是否要发送,设置状态
|
||||
logBuilder.sendStatus(Objects.equals(isSend, true) ? SmsSendStatusEnum.INIT.getStatus()
|
||||
: SmsSendStatusEnum.IGNORE.getStatus());
|
||||
// 设置手机相关字段
|
||||
//logBuilder.mobile(mobile).userId(userId).userType(userType);
|
||||
// 设置模板相关字段
|
||||
logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType());
|
||||
logBuilder.templateContent(String.valueOf(textMessage)).templateParams(templateParams)
|
||||
.apiTemplateId(template.getApiTemplateId());
|
||||
// 设置渠道相关字段
|
||||
logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode());
|
||||
// 设置接收相关字段
|
||||
logBuilder.receiveStatus(SmsReceiveStatusEnum.INIT.getStatus());
|
||||
|
||||
// 插入数据库
|
||||
SmsLogDO logDO = logBuilder.build();
|
||||
smsLogMapper.insert(logDO);
|
||||
return logDO.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.zt.plat.module.system.service.msg.impl;
|
||||
|
||||
/**
|
||||
* 流程审核企业微信待办消息队列监听
|
||||
*/
|
||||
/*
|
||||
@Slf4j
|
||||
@Component
|
||||
@RocketMQMessageListener(topic = "springboot-mq",consumerGroup = "consumer-service")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class RocketmqMsgQueueReceiver implements RocketMQListener<String> {
|
||||
|
||||
@Resource
|
||||
private ISysRocketMqService sysActiveMqService;
|
||||
|
||||
@Resource
|
||||
private ISendMsgService sendMsgService;
|
||||
|
||||
*/
|
||||
/**
|
||||
* 接受消息
|
||||
*//*
|
||||
|
||||
@Override
|
||||
public void onMessage(String jsonStr) {
|
||||
TextMessage tm = JSON.parseObject(jsonStr, TextMessage.class);
|
||||
log.info("接收到消息: {}", tm);
|
||||
String consumeMsg;
|
||||
try {
|
||||
Text text = tm.getText();
|
||||
String toUser = tm.getTouser();// 企业微信接收者
|
||||
String userName = tm.getToparty(); // 部门
|
||||
String userEname = tm.getText().getContent();//标签
|
||||
String agentid = tm.getAgentid();//企微ID
|
||||
String msgtype = tm.getMsgtype();//消息类型
|
||||
String appType = tm.getAppType();// 消息自建类型
|
||||
Integer enableDuplicateCheck = tm.getEnable_duplicate_check();//是否重启重复消息检查
|
||||
Integer duplicateCheckInterval = tm.getDuplicate_check_interval();// 是否重复消息检查的时间间隔
|
||||
String userCname = tm.getText().getContent();
|
||||
// 校验接受用户
|
||||
if (StringUtils.isBlank(toUser)) {
|
||||
consumeMsg = "企业微信消息发送失败,用户【" + userEname + "-" + userCname + "】未绑定企业微信账号!";
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), tm, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_FAILURE.getCode(), consumeMsg);
|
||||
} else {
|
||||
String msgType = tm.getMsgtype();
|
||||
if (Objects.equals(msgType, WxMsgTypeConstant.TEXTCARD.getCode())) {
|
||||
*/
|
||||
/* 构建审批回调url *//*
|
||||
|
||||
String appType = tm.getStringProperty("appType");
|
||||
String approveType = tm.getStringProperty("approveType");
|
||||
String companyCode = tm.getStringProperty("userCompanyCode");
|
||||
String taskId = tm.getStringProperty("taskId");
|
||||
String processInstanceId = tm.getStringProperty("processInstanceId");
|
||||
String businessId = tm.getStringProperty("businessId");
|
||||
Map<String, Object> substituteMap = new HashMap<>(5);
|
||||
substituteMap.put("appType", appType.toLowerCase(Locale.ROOT));
|
||||
substituteMap.put("approveType", approveType);
|
||||
substituteMap.put("companyCode", companyCode);
|
||||
substituteMap.put("taskId", taskId);
|
||||
substituteMap.put("processInstanceId", processInstanceId);
|
||||
substituteMap.put("businessId", businessId);
|
||||
String redirectUrl = StringSolveUtils.placeholderReplace(AuditConstants.SunepsAuditType.REDIRECT_URL.getCode(), substituteMap);
|
||||
|
||||
*/
|
||||
/* 构建文本卡片消息 *//*
|
||||
|
||||
String title = tm.getStringProperty("title");
|
||||
String taskName = tm.getStringProperty("taskName");
|
||||
String datetime = tm.getStringProperty("datetime");
|
||||
String btnTxt = tm.getStringProperty("btntxt");
|
||||
substituteMap.put("subTitle", datetime + "——" + userName);
|
||||
substituteMap.put("description", descriptionText);
|
||||
substituteMap.put("taskName", taskName);
|
||||
String description = StringSolveUtils.placeholderReplace(AuditConstants.SunepsAuditType.AUDIT_DESCRIPTION_FORMAT.getCode(), substituteMap);
|
||||
TextCardMessage textCardMessage = new TextCardMessage();
|
||||
TextCard textCard = new TextCard();
|
||||
textCard.setTitle(title)
|
||||
.setDescription(description)
|
||||
.setUrl(redirectUrl);
|
||||
if (StringUtils.isNotBlank(btnTxt)) {
|
||||
textCard.setBtntxt(btnTxt);
|
||||
}
|
||||
textCardMessage.setTextcard(textCard).setAppType(appType).setTouser(toUser);
|
||||
sendMsgService.sendTextCardMsg(textCardMessage);
|
||||
consumeMsg = "企业微信消息发送成功,接收用户【" + userEname + "-" + userCname + "】!";
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), tm, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_SUCCESS.getCode(), consumeMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("中铝e办发送消息异常:{}",e.getMessage(),e);
|
||||
try {
|
||||
// 重试时不重复保存队列消息
|
||||
if (!message.getJMSRedelivered()) {
|
||||
sysActiveMqService.saveByTextMessage(this.getClass().getName(), tm, JmsConstant.JMS_TYPE_QUEUE.getCode(), JmsConstant.CONSUME_FAILURE.getCode(), e.getMessage());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
log.error("中铝e办发送消息异常:{}", exception.getMessage(),e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,732 @@
|
||||
package com.zt.plat.module.system.service.msg.impl;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.zt.plat.framework.common.core.KeyValue;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.validation.MatcherSolveUtils;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.*;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.template.MsgTemplateSendReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import com.zt.plat.module.system.mq.producer.msg.MsgProducer;
|
||||
import com.zt.plat.module.system.service.msg.IMsgLogService;
|
||||
import com.zt.plat.module.system.service.msg.ISendMsgService;
|
||||
import com.zt.plat.module.system.service.sms.SmsChannelService;
|
||||
import com.zt.plat.module.system.service.sms.SmsTemplateService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.module.system.enums.ErrorCodeConstants.*;
|
||||
/**
|
||||
* 消息发送接口实现
|
||||
* TODO =========================================== 参考这个发送消息类文件===========================================================
|
||||
* @author luzemin
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class SendMsgServiceImpl implements ISendMsgService {
|
||||
|
||||
/**
|
||||
* 文本消息内容限制
|
||||
*/
|
||||
public static final int INT_2048 = 2048;
|
||||
|
||||
@Resource
|
||||
private SmsChannelService smsChannelService;
|
||||
|
||||
@Resource
|
||||
private SmsTemplateService smsTemplateService;
|
||||
|
||||
@Resource
|
||||
private IMsgLogService msgLogService;
|
||||
|
||||
@Resource
|
||||
private MsgProducer msgProducer;
|
||||
/**
|
||||
* 发送MQ文本消息
|
||||
* 用户ID,模版内容,消息类型、模版编码
|
||||
* @param textMessage 文本消息包装对象
|
||||
* @return r 响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendTextMsg(MsgTemplateSendReqVO sendReqVO, TextMessage textMessage) throws Exception {
|
||||
|
||||
Long userId = sendReqVO.getUserId();
|
||||
Integer userType = sendReqVO.getUserType();
|
||||
String templateCode = sendReqVO.getTemplateCode();
|
||||
Map<String, Object> templateParams = sendReqVO.getTemplateParams();
|
||||
// 校验模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(textMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(textMessage.getMsgtype(), WxMsgTypeConstant.TEXT.getCode())) {
|
||||
throw new Exception("文本消息类型必须为" + WxMsgTypeConstant.TEXT.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(textMessage.getText())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (StringUtils.isBlank(textMessage.getText().getContent())) {
|
||||
throw new Exception("消息发送消息体不能为空!");
|
||||
}
|
||||
/* 消息内容长度检验 */
|
||||
if (textMessage.getText().getContent().length() > INT_2048) {
|
||||
throw new Exception("消息发送消息内容,最长不超过2048个字节,超过将截断!");
|
||||
}
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
//格式化短信内容
|
||||
//String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
//创建发送日志
|
||||
Long sendLogId = msgLogService.createMsgLog(userId, userType, isSend, template, textMessage, templateParams);
|
||||
|
||||
//发送 MQ 消息,异步执行发送消息(发送日志、内容、渠道ID、模版ID)
|
||||
if (isSend) {
|
||||
msgProducer.sendMsg(sendLogId, textMessage.getText().getContent(), template.getChannelId(), template.getApiTemplateId(), newTemplateParams);
|
||||
}
|
||||
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendImageMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, ImageMessage imageMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(imageMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(imageMessage.getMsgtype(), WxMsgTypeConstant.IMAGE.getCode())) {
|
||||
throw new Exception("图片消息类型必须为" + WxMsgTypeConstant.IMAGE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(imageMessage.getImage())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 图片媒体ID校验 */
|
||||
if (StringUtils.isBlank(imageMessage.getImage().getMedia_id())) {
|
||||
throw new Exception("图片媒体ID不能为空!");
|
||||
}
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendVoiceMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, VoiceMessage voiceMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(voiceMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(voiceMessage.getMsgtype(), WxMsgTypeConstant.VOICE.getCode())) {
|
||||
throw new Exception("语音消息类型必须为" + WxMsgTypeConstant.VOICE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(voiceMessage.getVoice())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 语音媒体ID校验 */
|
||||
if (StringUtils.isBlank(voiceMessage.getVoice().getMedia_id())) {
|
||||
throw new Exception("语音媒体ID不能为空!");
|
||||
}
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendVideoMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, VideoMessage videoMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(videoMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(videoMessage.getMsgtype(), WxMsgTypeConstant.VIDEO.getCode())) {
|
||||
throw new Exception("视频消息类型必须为" + WxMsgTypeConstant.VIDEO.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(videoMessage.getVideo())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 视频媒体ID校验 */
|
||||
if (StringUtils.isBlank(videoMessage.getVideo().getMedia_id())) {
|
||||
throw new Exception("视频媒体ID不能为空!");
|
||||
}
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendFileMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, FileMessage fileMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(fileMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(fileMessage.getMsgtype(), WxMsgTypeConstant.FILE.getCode())) {
|
||||
throw new Exception("文件消息类型必须为" + WxMsgTypeConstant.FILE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(fileMessage.getFile())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 文件媒体ID校验 */
|
||||
if (StringUtils.isBlank(fileMessage.getFile().getMedia_id())) {
|
||||
throw new Exception("文件媒体ID不能为空!");
|
||||
}
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendTextCardMsgPich01(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, TextCardMessage textCardMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(textCardMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(textCardMessage.getMsgtype(), WxMsgTypeConstant.TEXTCARD.getCode())) {
|
||||
throw new Exception("文本卡片消息类型必须为" + WxMsgTypeConstant.TEXTCARD.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(textCardMessage.getTextcard())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 文本卡片消息标题校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getTitle())) {
|
||||
throw new Exception("文本卡片消息比标题不能为空!");
|
||||
}
|
||||
/* 文本卡片消息描述校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getDescription())) {
|
||||
throw new Exception("文本卡片消息描述校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("文本卡片消息点击url校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url正确性校验 */
|
||||
textCardMessage.getTextcard().setUrl(textCardMessage.getTextcard().getUrl());
|
||||
if (!MatcherSolveUtils.checkInternetURL(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendTextCardMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, TextCardMessage textCardMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(textCardMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(textCardMessage.getMsgtype(), WxMsgTypeConstant.TEXTCARD.getCode())) {
|
||||
throw new Exception("文本卡片消息类型必须为" + WxMsgTypeConstant.TEXTCARD.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(textCardMessage.getTextcard())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 文本卡片消息标题校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getTitle())) {
|
||||
throw new Exception("文本卡片消息比标题不能为空!");
|
||||
}
|
||||
/* 文本卡片消息描述校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getDescription())) {
|
||||
throw new Exception("文本卡片消息描述校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("文本卡片消息点击url校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url正确性校验 */
|
||||
if (!MatcherSolveUtils.checkInternetURL(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendNewsMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, NewsMessage newsMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(newsMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(newsMessage.getMsgtype(), WxMsgTypeConstant.NEWS.getCode())) {
|
||||
throw new Exception("图文消息类型必须为" + WxMsgTypeConstant.NEWS.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(newsMessage.getNews())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 图文消息校验 */
|
||||
if (newsMessage.getNews().getArticles().isEmpty()) {
|
||||
throw new Exception("图文消息内容不能为空!");
|
||||
} else {
|
||||
//newsMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
newsMessage.getNews().getArticles().forEach(article -> {
|
||||
/* 消息标题校验 */
|
||||
if (StringUtils.isBlank(article.getTitle())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容标题不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息url校验 */
|
||||
if (StringUtils.isBlank(article.getUrl())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容点击url不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息url正确性校验 */
|
||||
// article.setUrl(sysWxAuditConfig.getWxContextPath() + article.getUrl());
|
||||
if (!MatcherSolveUtils.checkInternetURL(article.getUrl())) {
|
||||
try {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendMpNewsMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, MpNewsMessage mpNewsMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(mpNewsMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(mpNewsMessage.getMsgtype(), WxMsgTypeConstant.MPNEWS.getCode())) {
|
||||
throw new Exception("图文(mpnews)消息类型必须为" + WxMsgTypeConstant.MPNEWS.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(mpNewsMessage.getMpnews())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 图文消息校验 */
|
||||
if (mpNewsMessage.getMpnews().getArticles().isEmpty()) {
|
||||
throw new Exception("图文消息内容不能为空!");
|
||||
} else {
|
||||
mpNewsMessage.getMpnews().getArticles().forEach(article -> {
|
||||
/* 消息标题校验 */
|
||||
if (StringUtils.isBlank(article.getTitle())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容标题不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息图片媒体ID校验 */
|
||||
if (StringUtils.isBlank(article.getThumb_media_id())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容图片媒体ID不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (StringUtils.isBlank(article.getContent())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendMarkdownMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, MarkdownMessage markdownMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(markdownMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(markdownMessage.getMsgtype(), WxMsgTypeConstant.MARKDOWN.getCode())) {
|
||||
throw new Exception("markdown消息消息类型必须为" + WxMsgTypeConstant.MARKDOWN.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(markdownMessage.getMarkdown())) {
|
||||
throw new Exception("markdown消息内容能为空!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (StringUtils.isBlank(markdownMessage.getMarkdown().getContent())) {
|
||||
throw new Exception("markdown消息内容能为空!");
|
||||
}
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendMiniProgramNoticeMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, MiniProgramNoticeMessage miniProgramNoticeMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
|
||||
/* 基础信息校验 */
|
||||
if (StringUtils.isAllBlank(miniProgramNoticeMessage.getTouser(), miniProgramNoticeMessage.getToparty(), miniProgramNoticeMessage.getTotag())) {
|
||||
throw new Exception("消息发送对象不能为空!");
|
||||
}
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(miniProgramNoticeMessage.getMsgtype(), WxMsgTypeConstant.MINIPROGRAM_NOTICE.getCode())) {
|
||||
throw new Exception("小程序通知消息消息类型必须为" + WxMsgTypeConstant.MINIPROGRAM_NOTICE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(miniProgramNoticeMessage.getMiniprogram_notice())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 小程序appid校验 */
|
||||
if (StringUtils.isBlank(miniProgramNoticeMessage.getMiniprogram_notice().getAppid())) {
|
||||
throw new Exception("小程序appid,必须是与当前应用关联的小程序!");
|
||||
}
|
||||
/* 小程序标题校验 */
|
||||
if (StringUtils.isBlank(miniProgramNoticeMessage.getMiniprogram_notice().getTitle())) {
|
||||
throw new Exception("消息标题不能为空,长度限制4-12个汉字!");
|
||||
}
|
||||
/* 小程序消息内容校验 */
|
||||
if (Objects.isNull(miniProgramNoticeMessage.getMiniprogram_notice().getContent_item())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 小程序消息内容校验 */
|
||||
if (miniProgramNoticeMessage.getMiniprogram_notice().getContent_item().isEmpty()) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 小程序消息内容校验key-value校验 */
|
||||
miniProgramNoticeMessage.getMiniprogram_notice().getContent_item().forEach(contentItem -> {
|
||||
/* 小程序消息内容key校验 */
|
||||
if (StringUtils.isBlank(contentItem.getKey())) {
|
||||
try {
|
||||
throw new Exception("小程序消息内容key不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 小程序消息内容value校验 */
|
||||
if (StringUtils.isBlank(contentItem.getValue())) {
|
||||
try {
|
||||
throw new Exception("小程序消息内容value不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Object> sendInteractiveTaskCardMsg(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams, InteractiveTaskCardMessage interactiveTaskCardMessage) throws Exception {
|
||||
|
||||
// 校验短信模板是否合法
|
||||
SmsTemplateDO template = validateSmsTemplate(templateCode);
|
||||
// 校验短信渠道是否合法
|
||||
SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId());
|
||||
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
|
||||
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
|
||||
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(interactiveTaskCardMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(interactiveTaskCardMessage.getMsgtype(), WxMsgTypeConstant.INTERACTIVE_TASKCARD.getCode())) {
|
||||
throw new Exception("任务卡片消息消息类型必须为" + WxMsgTypeConstant.INTERACTIVE_TASKCARD.getCode() + "!");
|
||||
}
|
||||
/* 任务卡片消息内容校验 */
|
||||
if (Objects.isNull(interactiveTaskCardMessage.getInteractive_taskcard())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 任务卡片消息内容标题校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTitle())) {
|
||||
throw new Exception("任务卡片消息内容标题不能为空!");
|
||||
}
|
||||
/* 任务卡片消息url校验 */
|
||||
if (!MatcherSolveUtils.checkInternetURL(interactiveTaskCardMessage.getInteractive_taskcard().getUrl())) {
|
||||
throw new Exception("任务卡片消息内容标题不能为空,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
/* 任务卡片消息内容任务id校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTask_id())) {
|
||||
throw new Exception("任务卡片消息内容任务id不能为空,任务id,同一个应用发送的任务卡片消息的任务id不能重复,只能由数字、字母和“_-@”组成,最长支持128字节!");
|
||||
}
|
||||
/* 任务卡片消息内容按钮组校验 */
|
||||
if (Objects.isNull(interactiveTaskCardMessage.getInteractive_taskcard().getBtn())) {
|
||||
throw new Exception("任务卡片消息内容按钮组不能为空!");
|
||||
}
|
||||
/* 任务卡片消息内容按钮组校验 */
|
||||
interactiveTaskCardMessage.getInteractive_taskcard().getBtn().forEach(btn -> {
|
||||
/* 任务卡片消息内容按钮组按钮key校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTitle())) {
|
||||
try {
|
||||
throw new Exception("任务卡片消息内容按钮组按钮key不能为空,按钮key值,用户点击后,会产生任务卡片回调事件,回调事件会带上该key值,只能由数字、字母和“_-@”组成,最长支持128字节!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 任务卡片消息内容按钮组阿牛name校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTitle())) {
|
||||
try {
|
||||
throw new Exception("任务卡片消息内容按钮组阿牛name不能为空,按钮名称,最长支持18个字节,超过则截断!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = msgLogService.createSmsLog(userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
/* if (isSend) {
|
||||
msgProducer.sendSendMsg(sendLogId, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
}*/
|
||||
return CommonResult.success(sendLogId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 企业微信消息发送基础信息校验
|
||||
*
|
||||
* @param baseMessage 企业微信消息体
|
||||
*/
|
||||
private void msgSendBaseCheck(BaseMessage baseMessage) throws Exception{
|
||||
if (StringUtils.isAllBlank(baseMessage.getTouser(), baseMessage.getToparty(), baseMessage.getTotag())) {
|
||||
throw new Exception("消息发送对象不能为空!");
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SmsTemplateDO validateSmsTemplate(String templateCode) {
|
||||
// 获得短信模板。考虑到效率,从缓存中获取
|
||||
SmsTemplateDO template = smsTemplateService.getSmsTemplateByCodeFromCache(templateCode);
|
||||
// 短信模板不存在
|
||||
if (template == null) {
|
||||
throw exception(MSG_CALLBACK_SIGN_INVALID);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
SmsChannelDO validateSmsChannel(Long channelId) {
|
||||
// 获得短信模板。考虑到效率,从缓存中获取
|
||||
SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId);
|
||||
// 短信模板不存在
|
||||
if (channelDO == null) {
|
||||
throw exception(MSG_CHANNEL_NOT_EXISTS);
|
||||
}
|
||||
return channelDO;
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
List<KeyValue<String, Object>> buildTemplateParams(SmsTemplateDO template, Map<String, Object> templateParams) {
|
||||
return template.getParams().stream().map(key -> {
|
||||
Object value = templateParams.get(key);
|
||||
if (value == null) {
|
||||
throw exception(MSG_CALLBACK_SIGN_INVALID, key);
|
||||
}
|
||||
return new KeyValue<>(key, value);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
package com.zt.plat.module.system.service.msg.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.http.HttpClientUtils;
|
||||
import com.zt.plat.framework.common.util.validation.MatcherSolveUtils;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.WxMsgTypeConstant;
|
||||
import com.zt.plat.module.system.service.msg.config.WeiXinProperties;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysWxAuditConfig;
|
||||
import com.zt.plat.module.system.service.msg.ISendWxMsgService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.zt.plat.module.system.controller.admin.sms.vo.msg.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.Objects;
|
||||
/**
|
||||
* TODO ================================ 中铝e办MQ生产者 ======================================
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class SendWxMsgServiceImpl implements ISendWxMsgService {
|
||||
|
||||
|
||||
/**
|
||||
* 文本消息内容限制
|
||||
*/
|
||||
public static final int INT_2048 = 2048;
|
||||
/**
|
||||
* 企业微信消息发送响应结果代码key
|
||||
*/
|
||||
public static final String WX_MSG_RESPONSE_CODE = "errcode";
|
||||
/**
|
||||
* 企业微信消息发送成功响应代码值
|
||||
*/
|
||||
public static final String WX_MSG_RESPONSE_SUCCESS_CODE = "0";
|
||||
|
||||
@Resource
|
||||
private WeiXinProperties weiXinProperties;
|
||||
|
||||
/**
|
||||
* 发送企业微信文本消息
|
||||
*
|
||||
* @param textMessage 文本消息对象
|
||||
* @return r 响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendTextMsg(TextMessage textMessage) throws Exception{
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(textMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(textMessage.getMsgtype(), WxMsgTypeConstant.TEXT.getCode())) {
|
||||
throw new Exception("文本消息类型必须为" + WxMsgTypeConstant.TEXT.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(textMessage.getText())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (StringUtils.isBlank(textMessage.getText().getContent())) {
|
||||
throw new Exception("消息发送消息体不能为空!");
|
||||
}
|
||||
/* 消息内容长度检验 */
|
||||
if (textMessage.getText().getContent().length() > INT_2048) {
|
||||
throw new Exception("消息发送消息内容,最长不超过2048个字节,超过将截断!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(textMessage);
|
||||
textMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
/* 如果消息包含点击链接,重新包装处理消息内容 */
|
||||
if (StringUtils.isNotBlank(textMessage.getText().getUrl())) {
|
||||
textMessage.getText().setUrl(sysWxAuditConfig.getWxContextPath() + textMessage.getText().getUrl());
|
||||
/* url校验 */
|
||||
if (!MatcherSolveUtils.checkInternetURL(textMessage.getText().getUrl())) {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
textMessage.getText().buildUrlContent();
|
||||
}
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(textMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信图片消息
|
||||
*
|
||||
* @param imageMessage 图片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendImageMsg(ImageMessage imageMessage) throws Exception{
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(imageMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(imageMessage.getMsgtype(), WxMsgTypeConstant.IMAGE.getCode())) {
|
||||
throw new Exception("图片消息类型必须为" + WxMsgTypeConstant.IMAGE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(imageMessage.getImage())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 图片媒体ID校验 */
|
||||
if (StringUtils.isBlank(imageMessage.getImage().getMedia_id())) {
|
||||
throw new Exception("图片媒体ID不能为空!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(imageMessage);
|
||||
imageMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(imageMessage));
|
||||
System.out.println("图片消息发送返回:" + jsonObject.toJSONString());
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信语音消息
|
||||
*
|
||||
* @param voiceMessage 语音消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendVoiceMsg(VoiceMessage voiceMessage) throws Exception{
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(voiceMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(voiceMessage.getMsgtype(), WxMsgTypeConstant.VOICE.getCode())) {
|
||||
throw new Exception("语音消息类型必须为" + WxMsgTypeConstant.VOICE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(voiceMessage.getVoice())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 语音媒体ID校验 */
|
||||
if (StringUtils.isBlank(voiceMessage.getVoice().getMedia_id())) {
|
||||
throw new Exception("语音媒体ID不能为空!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(voiceMessage);
|
||||
voiceMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(voiceMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信视频消息
|
||||
*
|
||||
* @param videoMessage 视频消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendVideoMsg(VideoMessage videoMessage) throws Exception{
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(videoMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(videoMessage.getMsgtype(), WxMsgTypeConstant.VIDEO.getCode())) {
|
||||
throw new Exception("视频消息类型必须为" + WxMsgTypeConstant.VIDEO.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(videoMessage.getVideo())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 视频媒体ID校验 */
|
||||
if (StringUtils.isBlank(videoMessage.getVideo().getMedia_id())) {
|
||||
throw new Exception("视频媒体ID不能为空!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(videoMessage);
|
||||
videoMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(videoMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信文件消息
|
||||
*
|
||||
* @param fileMessage 文件消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendFileMsg(FileMessage fileMessage) throws Exception{
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(fileMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(fileMessage.getMsgtype(), WxMsgTypeConstant.FILE.getCode())) {
|
||||
throw new Exception("文件消息类型必须为" + WxMsgTypeConstant.FILE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(fileMessage.getFile())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 文件媒体ID校验 */
|
||||
if (StringUtils.isBlank(fileMessage.getFile().getMedia_id())) {
|
||||
throw new Exception("文件媒体ID不能为空!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(fileMessage);
|
||||
fileMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(fileMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信文本卡片消息
|
||||
*
|
||||
* @param textCardMessage 文本卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendTextCardMsg(TextCardMessage textCardMessage) throws Exception{
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(textCardMessage);
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(textCardMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(textCardMessage.getMsgtype(), WxMsgTypeConstant.TEXTCARD.getCode())) {
|
||||
throw new Exception("文本卡片消息类型必须为" + WxMsgTypeConstant.TEXTCARD.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(textCardMessage.getTextcard())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 文本卡片消息标题校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getTitle())) {
|
||||
throw new Exception("文本卡片消息比标题不能为空!");
|
||||
}
|
||||
/* 文本卡片消息描述校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getDescription())) {
|
||||
throw new Exception("文本卡片消息描述校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("文本卡片消息点击url校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url正确性校验 */
|
||||
textCardMessage.getTextcard().setUrl(sysWxAuditConfig.getWxContextPath() + textCardMessage.getTextcard().getUrl());
|
||||
if (!MatcherSolveUtils.checkInternetURL(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
textCardMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
//TODO 此出发中铝 e 办
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(textCardMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信文本卡片消息
|
||||
*
|
||||
* @param textCardMessage 文本卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendTextCardMsgPich01(TextCardMessage textCardMessage) throws Exception{
|
||||
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(textCardMessage);
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(textCardMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(textCardMessage.getMsgtype(), WxMsgTypeConstant.TEXTCARD.getCode())) {
|
||||
throw new Exception("文本卡片消息类型必须为" + WxMsgTypeConstant.TEXTCARD.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(textCardMessage.getTextcard())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 文本卡片消息标题校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getTitle())) {
|
||||
throw new Exception("文本卡片消息比标题不能为空!");
|
||||
}
|
||||
/* 文本卡片消息描述校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getDescription())) {
|
||||
throw new Exception("文本卡片消息描述校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url校验 */
|
||||
if (StringUtils.isBlank(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("文本卡片消息点击url校验不能为空!");
|
||||
}
|
||||
/* 文本卡片消息点击url正确性校验 */
|
||||
textCardMessage.getTextcard().setUrl(textCardMessage.getTextcard().getUrl());
|
||||
if (!MatcherSolveUtils.checkInternetURL(textCardMessage.getTextcard().getUrl())) {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
textCardMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(textCardMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信图文消息
|
||||
*
|
||||
* @param newsMessage 图文消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendNewsMsg(NewsMessage newsMessage) throws Exception{
|
||||
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(newsMessage);
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(newsMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(newsMessage.getMsgtype(), WxMsgTypeConstant.NEWS.getCode())) {
|
||||
throw new Exception("图文消息类型必须为" + WxMsgTypeConstant.NEWS.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(newsMessage.getNews())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 图文消息校验 */
|
||||
if (newsMessage.getNews().getArticles().isEmpty()) {
|
||||
throw new Exception("图文消息内容不能为空!");
|
||||
} else {
|
||||
newsMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
newsMessage.getNews().getArticles().forEach(article -> {
|
||||
/* 消息标题校验 */
|
||||
if (StringUtils.isBlank(article.getTitle())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容标题不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息url校验 */
|
||||
if (StringUtils.isBlank(article.getUrl())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容点击url不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息url正确性校验 */
|
||||
article.setUrl(sysWxAuditConfig.getWxContextPath() + article.getUrl());
|
||||
if (!MatcherSolveUtils.checkInternetURL(article.getUrl())) {
|
||||
try {
|
||||
throw new Exception("消息发送消息内容,点击跳转url不是正确的网络url链接!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/* 消息发送 */
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(newsMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信图文消息(mpnews)
|
||||
*
|
||||
* @param mpNewsMessage 图文消息(mpnews)对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendMpNewsMsg(MpNewsMessage mpNewsMessage)throws Exception {
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(mpNewsMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(mpNewsMessage.getMsgtype(), WxMsgTypeConstant.MPNEWS.getCode())) {
|
||||
throw new Exception("图文(mpnews)消息类型必须为" + WxMsgTypeConstant.MPNEWS.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(mpNewsMessage.getMpnews())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 图文消息校验 */
|
||||
if (mpNewsMessage.getMpnews().getArticles().isEmpty()) {
|
||||
throw new Exception("图文消息内容不能为空!");
|
||||
} else {
|
||||
mpNewsMessage.getMpnews().getArticles().forEach(article -> {
|
||||
/* 消息标题校验 */
|
||||
if (StringUtils.isBlank(article.getTitle())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容标题不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息图片媒体ID校验 */
|
||||
if (StringUtils.isBlank(article.getThumb_media_id())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容图片媒体ID不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (StringUtils.isBlank(article.getContent())) {
|
||||
try {
|
||||
throw new Exception("图文消息内容能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(mpNewsMessage);
|
||||
mpNewsMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(mpNewsMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信markdown消息
|
||||
*
|
||||
* @param markdownMessage markdown消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendMarkdownMsg(MarkdownMessage markdownMessage) throws Exception{
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(markdownMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(markdownMessage.getMsgtype(), WxMsgTypeConstant.MARKDOWN.getCode())) {
|
||||
throw new Exception("markdown消息消息类型必须为" + WxMsgTypeConstant.MARKDOWN.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(markdownMessage.getMarkdown())) {
|
||||
throw new Exception("markdown消息内容能为空!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (StringUtils.isBlank(markdownMessage.getMarkdown().getContent())) {
|
||||
throw new Exception("markdown消息内容能为空!");
|
||||
}
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(markdownMessage);
|
||||
markdownMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(markdownMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信小程序通知消息
|
||||
*
|
||||
* @param miniProgramNoticeMessage 小程序通知消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendMiniProgramNoticeMsg(MiniProgramNoticeMessage miniProgramNoticeMessage) throws Exception {
|
||||
/* 基础信息校验 */
|
||||
if (StringUtils.isAllBlank(miniProgramNoticeMessage.getTouser(), miniProgramNoticeMessage.getToparty(), miniProgramNoticeMessage.getTotag())) {
|
||||
throw new Exception("消息发送对象不能为空!");
|
||||
}
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(miniProgramNoticeMessage.getMsgtype(), WxMsgTypeConstant.MINIPROGRAM_NOTICE.getCode())) {
|
||||
throw new Exception("小程序通知消息消息类型必须为" + WxMsgTypeConstant.MINIPROGRAM_NOTICE.getCode() + "!");
|
||||
}
|
||||
/* 消息内容校验 */
|
||||
if (Objects.isNull(miniProgramNoticeMessage.getMiniprogram_notice())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 小程序appid校验 */
|
||||
if (StringUtils.isBlank(miniProgramNoticeMessage.getMiniprogram_notice().getAppid())) {
|
||||
throw new Exception("小程序appid,必须是与当前应用关联的小程序!");
|
||||
}
|
||||
/* 小程序标题校验 */
|
||||
if (StringUtils.isBlank(miniProgramNoticeMessage.getMiniprogram_notice().getTitle())) {
|
||||
throw new Exception("消息标题不能为空,长度限制4-12个汉字!");
|
||||
}
|
||||
/* 小程序消息内容校验 */
|
||||
if (Objects.isNull(miniProgramNoticeMessage.getMiniprogram_notice().getContent_item())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 小程序消息内容校验 */
|
||||
if (miniProgramNoticeMessage.getMiniprogram_notice().getContent_item().isEmpty()) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 小程序消息内容校验key-value校验 */
|
||||
miniProgramNoticeMessage.getMiniprogram_notice().getContent_item().forEach(contentItem -> {
|
||||
/* 小程序消息内容key校验 */
|
||||
if (StringUtils.isBlank(contentItem.getKey())) {
|
||||
try {
|
||||
throw new Exception("小程序消息内容key不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 小程序消息内容value校验 */
|
||||
if (StringUtils.isBlank(contentItem.getValue())) {
|
||||
try {
|
||||
throw new Exception("小程序消息内容value不能为空!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
/* 消息发送 */
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(miniProgramNoticeMessage);
|
||||
miniProgramNoticeMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(miniProgramNoticeMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送企业微信任务卡片消息
|
||||
*
|
||||
* @param interactiveTaskCardMessage 任务卡片消息对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
@Override
|
||||
public CommonResult<Object> sendInteractiveTaskCardMsg(InteractiveTaskCardMessage interactiveTaskCardMessage) throws Exception {
|
||||
SysWxAuditConfig sysWxAuditConfig = this.buildSysWxAuditConfig(interactiveTaskCardMessage);
|
||||
/* 基础信息校验 */
|
||||
msgSendBaseCheck(interactiveTaskCardMessage);
|
||||
/* 消息类型校验 */
|
||||
if (!Objects.equals(interactiveTaskCardMessage.getMsgtype(), WxMsgTypeConstant.INTERACTIVE_TASKCARD.getCode())) {
|
||||
throw new Exception("任务卡片消息消息类型必须为" + WxMsgTypeConstant.INTERACTIVE_TASKCARD.getCode() + "!");
|
||||
}
|
||||
/* 任务卡片消息内容校验 */
|
||||
if (Objects.isNull(interactiveTaskCardMessage.getInteractive_taskcard())) {
|
||||
throw new Exception("消息内容不能为空!");
|
||||
}
|
||||
/* 任务卡片消息内容标题校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTitle())) {
|
||||
throw new Exception("任务卡片消息内容标题不能为空!");
|
||||
}
|
||||
/* 任务卡片消息url校验 */
|
||||
interactiveTaskCardMessage.getInteractive_taskcard().setUrl(sysWxAuditConfig.getWxContextPath() + interactiveTaskCardMessage.getInteractive_taskcard().getUrl());
|
||||
if (!MatcherSolveUtils.checkInternetURL(interactiveTaskCardMessage.getInteractive_taskcard().getUrl())) {
|
||||
throw new Exception("任务卡片消息内容标题不能为空,点击跳转url不是正确的网络url链接!");
|
||||
}
|
||||
/* 任务卡片消息内容任务id校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTask_id())) {
|
||||
throw new Exception("任务卡片消息内容任务id不能为空,任务id,同一个应用发送的任务卡片消息的任务id不能重复,只能由数字、字母和“_-@”组成,最长支持128字节!");
|
||||
}
|
||||
/* 任务卡片消息内容按钮组校验 */
|
||||
if (Objects.isNull(interactiveTaskCardMessage.getInteractive_taskcard().getBtn())) {
|
||||
throw new Exception("任务卡片消息内容按钮组不能为空!");
|
||||
}
|
||||
/* 任务卡片消息内容按钮组校验 */
|
||||
interactiveTaskCardMessage.getInteractive_taskcard().getBtn().forEach(btn -> {
|
||||
/* 任务卡片消息内容按钮组按钮key校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTitle())) {
|
||||
try {
|
||||
throw new Exception("任务卡片消息内容按钮组按钮key不能为空,按钮key值,用户点击后,会产生任务卡片回调事件,回调事件会带上该key值,只能由数字、字母和“_-@”组成,最长支持128字节!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
/* 任务卡片消息内容按钮组阿牛name校验 */
|
||||
if (StringUtils.isBlank(interactiveTaskCardMessage.getInteractive_taskcard().getTitle())) {
|
||||
try {
|
||||
throw new Exception("任务卡片消息内容按钮组阿牛name不能为空,按钮名称,最长支持18个字节,超过则截断!");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
/* 消息发送 */
|
||||
interactiveTaskCardMessage.setAgentid(sysWxAuditConfig.getAgentId());
|
||||
JSONObject jsonObject = HttpClientUtils.httpPost(weiXinProperties.buildQySendMsgUrl(sysWxAuditConfig.getCorpId(), sysWxAuditConfig.getCorpSecret()), (JSONObject) JSONObject.toJSON(interactiveTaskCardMessage));
|
||||
return buildQywxMsgSendResult(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 企业微信消息发送基础信息校验
|
||||
*
|
||||
* @param baseMessage 企业微信消息体
|
||||
*/
|
||||
private void msgSendBaseCheck(BaseMessage baseMessage) throws Exception{
|
||||
if (StringUtils.isAllBlank(baseMessage.getTouser(), baseMessage.getToparty(), baseMessage.getTotag())) {
|
||||
throw new Exception("消息发送对象不能为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业微信消息发送应用配置
|
||||
*
|
||||
* @param baseMessage 企业微信消息体
|
||||
* @return SysWxAuditConfig 企业微信消息发送应用配置
|
||||
*/
|
||||
private SysWxAuditConfig buildSysWxAuditConfig(BaseMessage baseMessage) throws Exception{
|
||||
return weiXinProperties.buildSysWxAuditConfig(baseMessage.getAppType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理企业微信消息发送结果响应数据
|
||||
* @param jsonObject 企业微信消息发送结果对象
|
||||
* @return R 消息发送响应对象
|
||||
*/
|
||||
private CommonResult<Object> buildQywxMsgSendResult(JSONObject jsonObject) {
|
||||
if (Objects.equals(jsonObject.getString(WX_MSG_RESPONSE_CODE), WX_MSG_RESPONSE_SUCCESS_CODE)) {
|
||||
return CommonResult.success(jsonObject);
|
||||
} else {
|
||||
return CommonResult.error(100029,"发送中铝e办失败{}").setData(jsonObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.zt.plat.module.system.service.msg.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.zt.plat.framework.common.util.http.HttpClientUtils;
|
||||
import com.zt.plat.framework.common.util.security.DESUtil;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysActiveMq;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysActiveMqLog;
|
||||
import com.zt.plat.module.system.dal.mysql.msg.SysActiveMqDao;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.AuditConstants;
|
||||
import com.zt.plat.module.system.framework.sms.core.enums.JmsConstant;
|
||||
import com.zt.plat.module.system.service.msg.ISysRocketMqService;
|
||||
import com.zt.plat.module.system.service.msg.config.HttpUrlConfig;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Activemq 消息队列 服务实现类
|
||||
*
|
||||
* @author Dy
|
||||
* @since 2021-07-22
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = {Exception.class, Error.class}, propagation = Propagation.REQUIRES_NEW)
|
||||
public class SysActiveMqServiceImpl extends ServiceImpl<SysActiveMqDao, SysActiveMqLog> implements ISysRocketMqService {
|
||||
|
||||
@Resource
|
||||
private HttpUrlConfig httpUrlConfig;
|
||||
|
||||
|
||||
/**
|
||||
* 保存消息队列消费的消息信息
|
||||
*
|
||||
* @param listenerClassName 队列监听类名
|
||||
* @param tm 消息对象
|
||||
* @param jmsType 消息类型(QUEUE-队列/TOPIC-主题)
|
||||
*/
|
||||
@Override
|
||||
public void saveByTextMessage(String listenerClassName, MessageExt tm, String jmsType) throws Exception {
|
||||
this.saveByTextMessage(listenerClassName, tm, jmsType, JmsConstant.CONSUME_SUCCESS.getCode(), "消费成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存消息队列消费的消息信息
|
||||
*
|
||||
* @param listenerClassName 队列监听类名
|
||||
* @param tm 消息对象
|
||||
* @param jmsType 消息类型(QUEUE-队列/TOPIC-主题)
|
||||
* @param consumeFlag 消费结果(success-成功/failure-失败)
|
||||
* @param consumeMessage 消费失败消息
|
||||
*/
|
||||
@Override
|
||||
public void saveByTextMessage(String listenerClassName, MessageExt tm, String jmsType, String consumeFlag, String consumeMessage) throws Exception {
|
||||
SysActiveMqLog sysActiveMqLog = new SysActiveMqLog();
|
||||
String json = new String(tm.getBody(), StandardCharsets.UTF_8);
|
||||
com.alibaba.fastjson2.JSONObject jsonObject = JSON.parseObject(json);
|
||||
sysActiveMqLog.setTitle(jsonObject.getString("title"))
|
||||
.setDatetime(jsonObject.getString("datetime"))
|
||||
.setPublisher(jsonObject.getString("publisher"))
|
||||
.setContentType(jsonObject.getString("type"))
|
||||
.setConsumeDatetime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
|
||||
.setTextContent(jsonObject.getString("content"))
|
||||
.setListenerClassName(listenerClassName)
|
||||
.setJmsType(jmsType)
|
||||
.setConsumeFlag(consumeFlag)
|
||||
.setConsumeMessage(consumeMessage);
|
||||
this.baseMapper.insert(sysActiveMqLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSunepsByTextMessage(String listenerClassName, MessageExt tm, String jmsType, String consumeFlag, String consumeMessage) throws Exception {
|
||||
|
||||
SysActiveMq sysActiveMq = new SysActiveMq();
|
||||
String json = new String(tm.getBody(), StandardCharsets.UTF_8);
|
||||
com.alibaba.fastjson2.JSONObject jsonObject = JSON.parseObject(json);
|
||||
|
||||
sysActiveMq.setTitle(jsonObject.getString("title"))
|
||||
.setDatetime(jsonObject.getString("datetime"))
|
||||
.setPublisher(jsonObject.getString("publisher"))
|
||||
.setContentType(jsonObject.getString("type"))
|
||||
.setConsumeDatetime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
|
||||
.setTextContent(jsonObject.getString("content"))
|
||||
.setListenerClassName(listenerClassName)
|
||||
.setJmsType(jmsType)
|
||||
.setConsumeFlag(consumeFlag)
|
||||
.setConsumeMessage(consumeMessage);
|
||||
String token = Arrays.toString(new DESUtil().encrypt(AuditConstants.TOKEN.getBytes()));
|
||||
HttpClientUtils.httpPostByToken(httpUrlConfig.getHttpContextPath("suneps") + "/ieps/mobile/saveMqLog", JSONObject.toJSONString(sysActiveMq), token);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.zt.plat.module.system.service.msg.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.zt.plat.module.system.dal.dataobject.msg.SysWxAuditConfig;
|
||||
import com.zt.plat.module.system.service.msg.SysWxAuditConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Service
|
||||
public class SysWxAuditConfigImpl implements SysWxAuditConfigService {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean saveBatch(Collection<SysWxAuditConfig> entityList, int batchSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveOrUpdateBatch(Collection<SysWxAuditConfig> entityList, int batchSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateBatchById(Collection<SysWxAuditConfig> entityList, int batchSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveOrUpdate(SysWxAuditConfig entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysWxAuditConfig getOne(Wrapper<SysWxAuditConfig> queryWrapper, boolean throwEx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SysWxAuditConfig> getOneOpt(Wrapper<SysWxAuditConfig> queryWrapper, boolean throwEx) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getMap(Wrapper<SysWxAuditConfig> queryWrapper) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V getObj(Wrapper<SysWxAuditConfig> queryWrapper, Function<? super Object, V> mapper) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseMapper<SysWxAuditConfig> getBaseMapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<SysWxAuditConfig> getEntityClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||
Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
|
||||
|
||||
// 发送 MQ 消息,异步执行发送短信
|
||||
//TODO 发送 MQ 消息,异步执行发送短信
|
||||
if (isSend) {
|
||||
smsProducer.sendSmsSendMessage(sendLogId, mobile, content, template.getChannelId(),
|
||||
template.getApiTemplateId(), newTemplateParams);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.module.system.util;
|
||||
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 字符串处理工具类
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
public class StringSolveUtils {
|
||||
|
||||
/**
|
||||
* 使用map集合替换字符串中${key}
|
||||
*
|
||||
* @param str 待替换字符串
|
||||
* @param substituteMap 替代品属性集合
|
||||
* @return 替换后字符串
|
||||
*/
|
||||
public static String placeholderReplace(String str, Map<String, Object> substituteMap) {
|
||||
StringSubstitutor stringSubstitutor = new StringSubstitutor(substituteMap);
|
||||
return stringSubstitutor.replace(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
# ================= ????? (PSCS) =================
|
||||
# ?????????http url
|
||||
pscs.commitUrl=/pscs/mobile/doSubmit
|
||||
# ?????????http url
|
||||
pscs.wxSubmitData=/pscs/mobile/doSubmitData
|
||||
# ?????????????http url
|
||||
pscs.commentUrl=/pscs/mobile/getApproveCommentById/
|
||||
# ?????????????http url
|
||||
pscs.todoUrl=/pscs/mobile/getApproveToDoByUserName/
|
||||
# ?????????????http url
|
||||
pscs.doneUrl=/pscs/mobile/getApproveDoneByUserName/
|
||||
# ?????????????http url
|
||||
pscs.finishUrl=/pscs/mobile/getApproveVariablesById/
|
||||
# ???????????http url
|
||||
pscs.storageUrl=http://cg.chncopper.com/storage/
|
||||
# ???????????????
|
||||
pscs.renderBpoContentUrl=/pscs/mobile/renderBpoContent
|
||||
# ?????????
|
||||
pscs.fineReportUrl=https://xs.chxz.com/FineReport/ReportServer
|
||||
# ?????????
|
||||
pscs.fileStorageUrl=https://xs.chxz.com/storage/
|
||||
# ???????
|
||||
pscs.fileViewUrl=https://xs.chxz.com/fv/onlinePreview
|
||||
# ?????pscs????
|
||||
pscs.httpContextPath=https://szh.chxz.com/omc
|
||||
# ?????pscs????????
|
||||
pscs.wxContextPath=http://xs.chxz.com/qywx
|
||||
|
||||
# ================= ????? (BSSIM) =================
|
||||
# ?????????http url
|
||||
bssim.commitUrl=/bssim/mobile/doSubmit
|
||||
# ?????????????http url
|
||||
bssim.commentUrl=/bssim/mobile/getApproveCommentById/
|
||||
# ?????????????http url
|
||||
bssim.todoUrl=/bssim/mobile/getApproveToDoByUserName/
|
||||
# ?????????????http url
|
||||
bssim.doneUrl=/bssim/mobile/getApproveDoneByUserName/
|
||||
# ?????????????http url
|
||||
bssim.finishUrl=/bssim/mobile/getApproveVariablesById/
|
||||
# ???????????http url
|
||||
bssim.storageUrl=http://cg.chncopper.com/storage/
|
||||
# ???????????????
|
||||
bssim.renderBpoContentUrl=/bssim/mobile/renderBpoContent
|
||||
# ?????????
|
||||
bssim.fineReportUrl=https://xs.chxz.com/FineReport/ReportServer
|
||||
# ?????????
|
||||
bssim.fileStorageUrl=https://xs.chxz.com/storage/
|
||||
# ???????
|
||||
bssim.fileViewUrl=https://xs.chxz.com/fv/onlinePreview
|
||||
# ?????bssim????
|
||||
bssim.httpContextPath=https://szh.chxz.com/bssim
|
||||
# ?????bssim????????
|
||||
bssim.wxContextPath=http://xs.chxz.com/qywx
|
||||
|
||||
# ================= ???? (SUNEPS) =================
|
||||
# ????????http url
|
||||
suneps.commitUrl=/ieps/mobile/doSubmit
|
||||
# ????????????http url
|
||||
suneps.commentUrl=/ieps/mobile/getApproveCommentById/
|
||||
# ????????????http url
|
||||
suneps.todoUrl=/ieps/mobile/getApproveToDoByUserName/
|
||||
# ????????????http url
|
||||
suneps.doneUrl=/ieps/mobile/getApproveDoneByUserName/
|
||||
# ??????????http url
|
||||
suneps.storageUrl=https://cg.chncopper.com/storage/
|
||||
# ????suneps????
|
||||
suneps.httpContextPath=http://172.16.34.30/ieps
|
||||
# ????suneps????????
|
||||
suneps.wxContextPath=http://xs.chxz.com/qywx
|
||||
|
||||
# ================= ???? (MARKET) =================
|
||||
# ??????http url
|
||||
market.commitUrl=/ieps/mobile/doSubmit
|
||||
# ??????????http url
|
||||
market.commentUrl=/ieps/mobile/getApproveCommentById/
|
||||
# ?????????http url
|
||||
market.todoUrl=/ieps/mobile/getApproveToDoByUserName/
|
||||
# ??????????http url
|
||||
market.doneUrl=/ieps/mobile/getApproveDoneByUserName/
|
||||
# ????market????
|
||||
market.httpContextPath=http://172.16.34.30/market
|
||||
# ????market????????
|
||||
market.wxContextPath=http://xs.chxz.com/qywx
|
||||
|
||||
# ================= ??????? (DZXS) =================
|
||||
# ???????????http url
|
||||
dzxs.commitUrl=/dzxs/mobile/doSubmit
|
||||
# ???????????????http url
|
||||
dzxs.commentUrl=/dzxs/mobile/getApproveCommentById/
|
||||
# ???????????????http url
|
||||
dzxs.todoUrl=/dzxs/mobile/getApproveToDoByUserName/
|
||||
# ???????????????http url
|
||||
dzxs.doneUrl=/dzxs/mobile/getApproveDoneByUserName/
|
||||
# ???????????????http url
|
||||
dzxs.finishUrl=/dzxs/mobile/getApproveVariablesById/
|
||||
# ?????????????http url
|
||||
dzxs.storageUrl=http://cg.chncopper.com/storage/
|
||||
# ???????????
|
||||
dzxs.fineReportUrl=https://xs.chxz.com/FineReport/ReportServer
|
||||
# ?????????
|
||||
dzxs.fileStorageUrl=https://xs.chxz.com/storage/
|
||||
# ???????
|
||||
dzxs.fileViewUrl=https://xs.chxz.com/fv/onlinePreview
|
||||
# ???????dzxs????
|
||||
dzxs.httpContextPath=http://10.198.0.197
|
||||
# ???????dzxs????????
|
||||
dzxs.wxContextPath=http://xs.chxz.com/qywx
|
||||
|
||||
# ================= ??????? (BOLI) =================
|
||||
# ???????boli????http url
|
||||
boli.commitUrl=/pscs/mobile/doSubmit
|
||||
# ???????boli????????http url
|
||||
boli.commentUrl=/pscs/mobile/getApproveCommentById/
|
||||
# ???????boli????????http url
|
||||
boli.todoUrl=/pscs/mobile/getApproveToDoByUserName/
|
||||
# ???????boli????????http url
|
||||
boli.doneUrl=/pscs/mobile/getApproveDoneByUserName/
|
||||
# ???????boli????????http url
|
||||
boli.finishUrl=/pscs/mobile/getApproveVariablesById/
|
||||
# ???????boli??????http url
|
||||
boli.storageUrl=http://cg.chncopper.com/storage/
|
||||
# ???????boli????
|
||||
boli.fineReportUrl=https://xs.chxz.com/FineReport/ReportServer
|
||||
# ?????????
|
||||
boli.fileStorageUrl=https://xs.chxz.com/storage/
|
||||
# ???????
|
||||
boli.fileViewUrl=https://xs.chxz.com/fv/onlinePreview
|
||||
# ???????boli????
|
||||
boli.httpContextPath=https://szh.chxz.com/boli
|
||||
# ???????boli????????
|
||||
boli.wxContextPath=http://xs.chxz.com/qywx
|
||||
|
||||
# ================= ??????? (LMPS) =================
|
||||
# ???????????http url
|
||||
lmps.commitUrl=/lmps/mobile/doSubmit
|
||||
# ???????????????http url
|
||||
lmps.commentUrl=/lmps/mobile/getApproveCommentById/
|
||||
# ???????????????http url
|
||||
lmps.todoUrl=/lmps/mobile/getApproveToDoByUserName/
|
||||
# ???????????????http url
|
||||
lmps.doneUrl=/lmps/mobile/getApproveDoneByUserName/
|
||||
# ???????????????http url
|
||||
lmps.finishUrl=/lmps/mobile/getApproveVariablesById/
|
||||
# ?????????????http url
|
||||
lmps.storageUrl=http://cg.chncopper.com/storage/
|
||||
# ???????????
|
||||
lmps.fineReportUrl=http://xs.chxz.com/FineReport/ReportServer
|
||||
# ?????????
|
||||
lmps.fileStorageUrl=https://xs.chxz.com/storage/
|
||||
# ???????
|
||||
lmps.fileViewUrl=https://xs.chxz.com/fv/onlinePreview
|
||||
# ???????lmps????
|
||||
lmps.httpContextPath=https://szh.chxz.com/lmps
|
||||
# ???????lmps????????
|
||||
lmps.wxContextPath=http://xs.chxz.com/qywx
|
||||
@@ -0,0 +1,43 @@
|
||||
# ================= ?????? =================
|
||||
|
||||
# ????-??access_token??url
|
||||
qyAccessTokenUrl=https://qw.chinalco.com.cn:8443/cgi-bin/gettoken?corpid=CORP_ID&corpsecret=CORP_SECRET
|
||||
|
||||
# ????-??????url
|
||||
qySendMsgUrl=https://qw.chinalco.com.cn:8443/cgi-bin/message/send?access_token=ACCESS_TOKEN
|
||||
|
||||
# ????-????????Oauth2????url
|
||||
qyAuthorizeUrl=https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&agentid=AGENTID&state=STATE#wechat_redirect
|
||||
|
||||
# ????-????????????url
|
||||
qyUserInfoUrl=https://qw.chinalco.com.cn:8443/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
|
||||
|
||||
# ????url
|
||||
qyUserUrl=https://qw.chinalco.com.cn:8443/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USER_ID
|
||||
|
||||
# ????-??????url
|
||||
qyUploadTempFileUrl=https://qw.chinalco.com.cn:8443/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
|
||||
|
||||
# ????-????url
|
||||
qyDeptCreateUrl=
|
||||
|
||||
# ????-????url
|
||||
qyDeptUpdateUrl=
|
||||
|
||||
# ????-????url
|
||||
qyDeptDeleteUrl=
|
||||
|
||||
# ????-??????url
|
||||
qyDeptQueryListUrl=https://qw.chinalco.com.cn:8443/cgi-bin/department/list?access_token=ACCESS_TOKEN&id=DEPARTMENT_ID
|
||||
|
||||
# ????-??????url
|
||||
qyMenuCreateUrl=
|
||||
|
||||
# ????-??????url
|
||||
qyMenuDeleteUrl=
|
||||
|
||||
# ????-??????url
|
||||
qyMenuQueryUrl=
|
||||
|
||||
# ????-ticket??url
|
||||
ticketUrl=https://qw.chinalco.com.cn:8443/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN
|
||||
@@ -0,0 +1,305 @@
|
||||
package com.zt.plat.module.system.service.msg;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.zt.plat.framework.common.core.KeyValue;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.common.enums.UserTypeEnum;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.SmsClient;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
import com.zt.plat.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.zt.plat.module.system.mq.message.sms.SmsSendMessage;
|
||||
import com.zt.plat.module.system.mq.producer.sms.SmsProducer;
|
||||
import com.zt.plat.module.system.service.member.MemberService;
|
||||
import com.zt.plat.module.system.service.sms.SmsChannelService;
|
||||
import com.zt.plat.module.system.service.sms.SmsLogService;
|
||||
import com.zt.plat.module.system.service.sms.SmsSendServiceImpl;
|
||||
import com.zt.plat.module.system.service.sms.SmsTemplateService;
|
||||
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||
import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static com.zt.plat.framework.test.core.util.RandomUtils.*;
|
||||
import static com.zt.plat.module.system.enums.ErrorCodeConstants.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private SmsSendServiceImpl smsSendService;
|
||||
|
||||
@Mock
|
||||
private AdminUserService adminUserService;
|
||||
@Mock
|
||||
private MemberService memberService;
|
||||
@Mock
|
||||
private SmsChannelService smsChannelService;
|
||||
@Mock
|
||||
private SmsTemplateService smsTemplateService;
|
||||
@Mock
|
||||
private SmsLogService smsLogService;
|
||||
@Mock
|
||||
private SmsProducer smsProducer;
|
||||
|
||||
@Test
|
||||
public void testSendSingleSmsToAdmin() {
|
||||
// 准备参数
|
||||
Long userId = randomLongId();
|
||||
String templateCode = randomString();
|
||||
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
|
||||
.put("op", "login").build();
|
||||
// mock adminUserService 的方法
|
||||
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300"));
|
||||
when(adminUserService.getUser(eq(userId))).thenReturn(user);
|
||||
|
||||
// mock SmsTemplateService 的方法
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setContent("验证码为{code}, 操作为{op}");
|
||||
o.setParams(Lists.newArrayList("code", "op"));
|
||||
});
|
||||
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(user.getMobile()), eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(Boolean.TRUE), eq(template),
|
||||
eq(content), eq(templateParams))).thenReturn(smsLogId);
|
||||
|
||||
// 调用
|
||||
Long resultSmsLogId = smsSendService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams);
|
||||
// 断言
|
||||
assertEquals(smsLogId, resultSmsLogId);
|
||||
// 断言调用
|
||||
verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(user.getMobile()), eq(content),
|
||||
eq(template.getChannelId()), eq(template.getApiTemplateId()),
|
||||
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendSingleSmsToUser() {
|
||||
// 准备参数
|
||||
Long userId = randomLongId();
|
||||
String templateCode = randomString();
|
||||
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
|
||||
.put("op", "login").build();
|
||||
// mock memberService 的方法
|
||||
String mobile = "15601691300";
|
||||
when(memberService.getMemberUserMobile(eq(userId))).thenReturn(mobile);
|
||||
|
||||
// mock SmsTemplateService 的方法
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setContent("验证码为{code}, 操作为{op}");
|
||||
o.setParams(Lists.newArrayList("code", "op"));
|
||||
});
|
||||
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(Boolean.TRUE), eq(template),
|
||||
eq(content), eq(templateParams))).thenReturn(smsLogId);
|
||||
|
||||
// 调用
|
||||
Long resultSmsLogId = smsSendService.sendSingleSmsToMember(null, userId, templateCode, templateParams);
|
||||
// 断言
|
||||
assertEquals(smsLogId, resultSmsLogId);
|
||||
// 断言调用
|
||||
verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), eq(content),
|
||||
eq(template.getChannelId()), eq(template.getApiTemplateId()),
|
||||
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功,当短信模板开启时
|
||||
*/
|
||||
@Test
|
||||
public void testSendSingleSms_successWhenSmsTemplateEnable() {
|
||||
// 准备参数
|
||||
String mobile = randomString();
|
||||
Long userId = randomLongId();
|
||||
Integer userType = randomEle(UserTypeEnum.values()).getValue();
|
||||
String templateCode = randomString();
|
||||
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
|
||||
.put("op", "login").build();
|
||||
// mock SmsTemplateService 的方法
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setContent("验证码为{code}, 操作为{op}");
|
||||
o.setParams(Lists.newArrayList("code", "op"));
|
||||
});
|
||||
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template),
|
||||
eq(content), eq(templateParams))).thenReturn(smsLogId);
|
||||
|
||||
// 调用
|
||||
Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
|
||||
// 断言
|
||||
assertEquals(smsLogId, resultSmsLogId);
|
||||
// 断言调用
|
||||
verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), eq(content),
|
||||
eq(template.getChannelId()), eq(template.getApiTemplateId()),
|
||||
eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功,当短信模板关闭时
|
||||
*/
|
||||
@Test
|
||||
public void testSendSingleSms_successWhenSmsTemplateDisable() {
|
||||
// 准备参数
|
||||
String mobile = randomString();
|
||||
Long userId = randomLongId();
|
||||
Integer userType = randomEle(UserTypeEnum.values()).getValue();
|
||||
String templateCode = randomString();
|
||||
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
|
||||
.put("op", "login").build();
|
||||
// mock SmsTemplateService 的方法
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
o.setContent("验证码为{code}, 操作为{op}");
|
||||
o.setParams(Lists.newArrayList("code", "op"));
|
||||
});
|
||||
when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
|
||||
String content = randomString();
|
||||
when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams)))
|
||||
.thenReturn(content);
|
||||
// mock SmsChannelService 的方法
|
||||
SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel);
|
||||
// mock SmsLogService 的方法
|
||||
Long smsLogId = randomLongId();
|
||||
when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template),
|
||||
eq(content), eq(templateParams))).thenReturn(smsLogId);
|
||||
|
||||
// 调用
|
||||
Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
|
||||
// 断言
|
||||
assertEquals(smsLogId, resultSmsLogId);
|
||||
// 断言调用
|
||||
verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(), anyString(),
|
||||
anyLong(), any(), anyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckSmsTemplateValid_notExists() {
|
||||
// 准备参数
|
||||
String templateCode = randomString();
|
||||
// mock 方法
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsSendService.validateSmsTemplate(templateCode),
|
||||
SMS_SEND_TEMPLATE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildTemplateParams_paramMiss() {
|
||||
// 准备参数
|
||||
SmsTemplateDO template = randomPojo(SmsTemplateDO.class,
|
||||
o -> o.setParams(Lists.newArrayList("code")));
|
||||
Map<String, Object> templateParams = new HashMap<>();
|
||||
// mock 方法
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsSendService.buildTemplateParams(template, templateParams),
|
||||
SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckMobile_notExists() {
|
||||
// 准备参数
|
||||
// mock 方法
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> smsSendService.validateMobile(null),
|
||||
SMS_SEND_MOBILE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendBatchNotify() {
|
||||
// 准备参数
|
||||
// mock 方法
|
||||
|
||||
// 调用
|
||||
UnsupportedOperationException exception = Assertions.assertThrows(
|
||||
UnsupportedOperationException.class,
|
||||
() -> smsSendService.sendBatchSms(null, null, null, null, null)
|
||||
);
|
||||
// 断言
|
||||
assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDoSendSms() throws Throwable {
|
||||
// 准备参数
|
||||
SmsSendMessage message = randomPojo(SmsSendMessage.class, o -> o.setContent(randomString()));
|
||||
// mock SmsClientFactory 的方法
|
||||
SmsClient smsClient = spy(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
|
||||
// mock SmsClient 的方法
|
||||
SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class);
|
||||
when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getContent()), eq(message.getApiTemplateId()),
|
||||
eq(message.getTemplateParams()))).thenReturn(sendResult);
|
||||
|
||||
// 调用
|
||||
smsSendService.doSendSms(message);
|
||||
// 断言
|
||||
verify(smsLogService).updateSmsSendResult(eq(message.getLogId()),
|
||||
eq(sendResult.getSuccess()), eq(sendResult.getApiCode()),
|
||||
eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveSmsStatus() throws Throwable {
|
||||
// 准备参数
|
||||
String channelCode = randomString();
|
||||
String text = randomString();
|
||||
// mock SmsClientFactory 的方法
|
||||
SmsClient smsClient = spy(SmsClient.class);
|
||||
when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient);
|
||||
// mock SmsClient 的方法
|
||||
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
|
||||
when(smsClient.parseSmsReceiveStatus(eq(text))).thenReturn(receiveResults);
|
||||
|
||||
// 调用
|
||||
smsSendService.receiveSmsStatus(channelCode, text);
|
||||
// 断言
|
||||
for (SmsReceiveRespDTO result : receiveResults) {
|
||||
verify(smsLogService).updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()),
|
||||
eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorMsg()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user