diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/config/SiqConfig.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/config/SiqConfig.java index 2683aa75..8b20f9ef 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/config/SiqConfig.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/config/SiqConfig.java @@ -16,12 +16,27 @@ public class SiqConfig /** 深i企对接URL */ private String siqUrl; + /** 深i企对接URL */ + private String siqBaseUrl; + /** 深i企对接code */ private String code; /** 深i企对接密钥 */ private String secretKey; + /** 深i企应用编号 */ + private String appId; + + /** 深i企消息应用编号 */ + private String msgAppId; + + /** 深i企消息应用密钥 */ + private String msgSecretKey; + + /** 深i企消息模板ID */ + private String msgTemplateId; + /** 模拟数据开关 */ private MockConfig mock = new MockConfig(); @@ -65,6 +80,38 @@ public class SiqConfig this.secretKey = secretKey; } + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getMsgAppId() { + return msgAppId; + } + + public void setMsgAppId(String msgAppId) { + this.msgAppId = msgAppId; + } + + public String getMsgSecretKey() { + return msgSecretKey; + } + + public void setMsgSecretKey(String msgSecretKey) { + this.msgSecretKey = msgSecretKey; + } + + public String getMsgTemplateId() { + return msgTemplateId; + } + + public void setMsgTemplateId(String msgTemplateId) { + this.msgTemplateId = msgTemplateId; + } + public MockConfig getMock() { return mock; } @@ -72,4 +119,12 @@ public class SiqConfig public void setMock(MockConfig mock) { this.mock = mock; } + + public String getSiqBaseUrl() { + return siqBaseUrl; + } + + public void setSiqBaseUrl(String siqBaseUrl) { + this.siqBaseUrl = siqBaseUrl; + } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/siq/SIQHttpUtil.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/siq/SIQHttpUtil.java new file mode 100644 index 00000000..48b0672d --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/siq/SIQHttpUtil.java @@ -0,0 +1,64 @@ +package com.zdxt.enforcementcode.common.util.siq; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSONObject; +import com.esotericsoftware.minlog.Log; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class SIQHttpUtil { + + + /** + * POST带签名通用请求 + * @param url 完整地址 + * @param bodyJson 请求体,无参数传 "" + * @param secretKey 平台分配的密钥 + * @param appId 应用编号 + * @return 响应字符串 + */ + public static String postWithSign(String url, String bodyJson, String secretKey, String appId) throws Exception { + Log.info("请求URL:{}", url); + Log.info("请求参数:{}", bodyJson); + SIQSignUtil.SignInfo signInfo = SIQSignUtil.generateSign(secretKey, appId, bodyJson); + + HttpRequest request = HttpRequest.post(url); + request.header("App-Id", signInfo.getAppId()); + request.header("App-Secret", signInfo.getAppSecret()); + request.header("Timestamp", String.valueOf(signInfo.getTimestamp())); + request.header("Nonce", signInfo.getNonce()); + request.header("Signature", signInfo.getSignature()); + request.header("Content-Type", "application/json;charset=utf-8"); + request.body(bodyJson); + + Map headerMap = new HashMap<>(); + request.headers().forEach((key, values) -> { + if (values != null && !values.isEmpty()) { + headerMap.put(key, String.join(", ", values)); + } + }); + System.out.println("========== 完整请求头 =========="); + System.out.println(JSONObject.toJSONString(headerMap, true)); + System.out.println("========== 请求头结束 =========="); + + Log.info("深I企发送请求body:"); + Log.info( bodyJson); + + Log.info("深I企发送请求:"); + System.out.println(JSONObject.toJSONString(request,true)); + HttpResponse response = request.execute(); + Log.info("深I企返回参数:{}", JSONObject.toJSONString(response)); + if (!response.isOk()) { + response.close(); + throw new RuntimeException("调用深I企消息中心接口失败,http状态码:" + response.getStatus()); + } + String result = response.body(); + response.close(); + return result; + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/siq/SIQSignUtil.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/siq/SIQSignUtil.java new file mode 100644 index 00000000..5750ef57 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/siq/SIQSignUtil.java @@ -0,0 +1,107 @@ +package com.zdxt.enforcementcode.common.util.siq; + +import com.esotericsoftware.minlog.Log; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.SecureRandom; + +/** + * 深i企二期开放接口签名工具 + */ +public class SIQSignUtil { + + /** + * HmacSHA256加密 + * @param key hmac密钥(文档要求用nonce) + * @param msg 待加密原文 + * @return 加密字节数组 + */ + public static byte[] hmac256(byte[] key, String msg) throws Exception { + Mac mac = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm()); + mac.init(secretKeySpec); + return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8)); + } + + /** + * SHA256 转小写16进制 + */ + public static String sha256Hex(String s) throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] d = md.digest(s.getBytes(StandardCharsets.UTF_8)); + return DatatypeConverter.printHexBinary(d).toLowerCase(); + } + + /** + * 生成完整签名对象,包含timestamp、nonce、signature + * @param secretKey 平台分配的密钥(对应配置 siq.msgSecretKey) + * @param appId 应用编号(对应配置 siq.msgAppId) + * @param requestBody 请求体完整JSON字符串 + * @return 签名信息 + */ + public static SignInfo generateSign(String secretKey, String appId, String requestBody) throws Exception { + // 1. 秒级时间戳 + long timestamp = System.currentTimeMillis() / 1000; + // 2. 6位随机数字Nonce + String nonce = get6RandomNum(); + // 3. 请求体sha256 + String bodySha256 = sha256Hex(requestBody); + // 4. 拼接原文:secretKey + timestamp + nonce + bodySha256 + String hmacSource = secretKey + timestamp + nonce + bodySha256; + + // 5. hmac密钥为nonce字节数组 + byte[] hmacResult = hmac256(hmacSource.getBytes(StandardCharsets.UTF_8), nonce); + // 6. 转小写16进制签名 + String signature = DatatypeConverter.printHexBinary(hmacResult).toLowerCase(); + Log.info("Signature: " + signature); + SignInfo info = new SignInfo(); + info.setAppId(appId); + info.setAppSecret(secretKey); + info.setTimestamp(timestamp); + info.setNonce(nonce); + info.setSignature(signature); + return info; + } + + /** + * 生成6位随机数字字符串 + */ + private static String get6RandomNum() { + SecureRandom random = new SecureRandom(); + int num = random.nextInt(900000) + 100000; + return String.valueOf(num); + } + + /** + * 签名结果实体,用于填充HTTP Header + */ + public static class SignInfo { + private String appId; + private long timestamp; + private String nonce; + private String signature; + private String appSecret; + + // getter setter + public String getAppId() { return appId; } + public void setAppId(String appId) { this.appId = appId; } + public long getTimestamp() { return timestamp; } + public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + public String getNonce() { return nonce; } + public void setNonce(String nonce) { this.nonce = nonce; } + public String getSignature() { return signature; } + public void setSignature(String signature) { this.signature = signature; } + + public String getAppSecret() { + return appSecret; + } + + public void setAppSecret(String appSecret) { + this.appSecret = appSecret; + } + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnforcementRegistrationController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnforcementRegistrationController.java index b94d2ccc..eeaa0e2b 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnforcementRegistrationController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnforcementRegistrationController.java @@ -422,7 +422,7 @@ public class AppEnforcementRegistrationController extends BaseController { } } if (!userIds.isEmpty()) { - sysUserVos = sysUserService.selectUserByIds(userIds, null); + sysUserVos = sysUserService.selectUserByIds(userIds); } } String tmpUserShow = ""; @@ -489,7 +489,7 @@ public class AppEnforcementRegistrationController extends BaseController { } } if (!userIds.isEmpty()) { - userBeans1 = sysUserService.selectUserByIds(userIds, null); + userBeans1 = sysUserService.selectUserByIds(userIds); } } String tmpUserShow1 = ""; @@ -513,7 +513,7 @@ public class AppEnforcementRegistrationController extends BaseController { } } if (!userIds.isEmpty()) { - userBeans2 = sysUserService.selectUserByIds(userIds, null); + userBeans2 = sysUserService.selectUserByIds(userIds); } } String tmpUserShow2 = ""; diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/SIQMsgParamBo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/SIQMsgParamBo.java new file mode 100644 index 00000000..b37a3ba7 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/SIQMsgParamBo.java @@ -0,0 +1,25 @@ +package com.zdxt.enforcementcode.domain.bo.thirdparty; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class SIQMsgParamBo { + + /** + * 创建人 + */ + private String createBy; + + /** + * 接收人 + */ + private String receiveAccount; + + /** + * 内容 + */ + private Map additionalData; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/SIQSendMsgBo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/SIQSendMsgBo.java new file mode 100644 index 00000000..20ad6d93 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/SIQSendMsgBo.java @@ -0,0 +1,25 @@ +package com.zdxt.enforcementcode.domain.bo.thirdparty; + +import lombok.Data; + +@Data +public class SIQSendMsgBo { + + /** + * 消息任务类型。delay:定时发送,planSendTime 不能 + * 为空,now:立即发送,planSendTime 为空 + */ + private String sendType; + /** + * 通道:0短信 1邮件 2企微 3语音 4公众号 5小程序 6站内信 + */ + private Integer sendChannel; + /** 模板授权ID */ + private String templateId; + /** 超时秒数 */ + private Integer expire; + /** 发送人 */ + private String createBy; + /** 接收人数组 */ + private ToUserMetaBo[] toUser; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/ToUserMetaBo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/ToUserMetaBo.java new file mode 100644 index 00000000..42c805b4 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/thirdparty/ToUserMetaBo.java @@ -0,0 +1,17 @@ +package com.zdxt.enforcementcode.domain.bo.thirdparty; + +import lombok.Data; + +import java.util.Map; + +@Data +public class ToUserMetaBo { + /** 消息序号,业务自行生成唯一 */ + private String messageId; + /** 接收账号 */ + private String receiveAccount; + /** 模板变量 */ + private Map variables; + /** 附加业务数据 */ + private Map additionalData; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/AnalyzeResultVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/AnalyzeResultVo.java new file mode 100644 index 00000000..5998c230 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/AnalyzeResultVo.java @@ -0,0 +1,14 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import java.util.List; + +public class AnalyzeResultVo { + /** 总数量 */ + private Integer totalCount; + /** 成功数量 */ + private Integer successCount; + /** 失败数量 */ + private Integer failedCount; + /** 失败详情 */ + private List failedResults; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/LimitPeriod.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/LimitPeriod.java new file mode 100644 index 00000000..2d80c16e --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/LimitPeriod.java @@ -0,0 +1,12 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; + +@Data +public class LimitPeriod { + /** 周期类型:日/周/月/年等编码 */ + private Integer periodType; + + /** 周期数值,如type=月、value=1代表每月 */ + private Integer periodValue; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/MessageDetailVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/MessageDetailVo.java new file mode 100644 index 00000000..28f7676b --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/MessageDetailVo.java @@ -0,0 +1,29 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; + +@Data +public class MessageDetailVo { + /** 消息序号 */ + private String messageId; + /** 接收时间 */ + private Long createTime; + /** 超期时间 */ + private Long expireTime; + /** 发送完成时间 */ + private Long sendFinishedTime; + /** 消息发送类型:delay/now */ + private String sendType; + /** 接收人姓名 */ + private String principalName; + /** 接收人账号 */ + private String receiveAccount; + /** 解析结果 */ + private String analyzeResult; + /** 解析说明 */ + private String analyzeRemarks; + /** 发送状态:0待发送 1成功 2正在发送 3异常 4失败 5取消 */ + private Integer sendStatus; + /** 发送消息内容 */ + private String sendContent; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/MessageTaskItemVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/MessageTaskItemVo.java new file mode 100644 index 00000000..9af0e036 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/MessageTaskItemVo.java @@ -0,0 +1,15 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; + +import java.util.List; + +@Data +public class MessageTaskItemVo { + /** 消息任务唯一标示 */ + private String taskId; + /** 发送类型:now立即发送,delay定时发送 */ + private String sendType; + /** 解析结果 */ + private AnalyzeResultVo analyzeResult; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQResult.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQResult.java new file mode 100644 index 00000000..5c1ec84a --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQResult.java @@ -0,0 +1,7 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +public class SIQResult { + String errcode; //响应编码 + String errmsg ;//响应编码 + String data; //返回的数据 +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQTaskQueryBo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQTaskQueryBo.java new file mode 100644 index 00000000..1310a73f --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQTaskQueryBo.java @@ -0,0 +1,15 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; + +@Data +public class SIQTaskQueryBo { + /** 消息任务唯一标示 */ + private String taskId; + /** 消息序号数组,为空则查询该任务下所有消息 */ + private String[] messageIds; + /** 页数 */ + private Integer pageNo; + /** 每页大小 */ + private Integer pageSize; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQTemplateVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQTemplateVo.java new file mode 100644 index 00000000..f7ded30e --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SIQTemplateVo.java @@ -0,0 +1,56 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import java.util.List; + +public class SIQTemplateVo { + /** + * 模板授权ID + * 唯一标识模板与应用授权匹配关系 + */ + private String templateId; + + /** 授权模板名称 */ + private String templateName; + + /** + * 模板归属渠道分类 + * 0-短信,1-邮件,2-企业微信,3-语音,4-微信公众号,5-小程序,6-站内信 + */ + private Integer sendChannel; + + /** + * 模板授权状态 + * 1-正常,2-禁用,3-冻结 + */ + private Integer authorizationStatus; + + /** 授权开始时间戳(Long类型秒级时间) */ + private Long authorizationTimeForm; + + /** 授权截止时间戳(Long类型秒级时间) */ + private Long authorizationTimeTo; + + /** 当前周期内剩余可发送限额次数 */ + private Integer limitNumber; + + /** 限额统计方式编码 */ + private Integer limitType; + + /** 限额周期配置对象 */ + private LimitPeriod limitPeriod; + + /** 模板展示示例文案 */ + private String templateDemo; + + /** 模板实际发送正文内容 */ + private String templateContent; + + /** 模板占位变量参数数组 */ + private List templateVariable; + + /** 授权说明备注,非必填 */ + private String authorizationRemarks; + + /** 异常/冻结原因说明,非必填 */ + private String errorRemarks; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SendMsgDataVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SendMsgDataVo.java new file mode 100644 index 00000000..3f37e546 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SendMsgDataVo.java @@ -0,0 +1,10 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; +import java.util.List; + +@Data +public class SendMsgDataVo { + /** 消息任务列表 */ + private List list; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SendMsgVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SendMsgVo.java new file mode 100644 index 00000000..05774338 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/SendMsgVo.java @@ -0,0 +1,17 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; + +@Data +public class SendMsgVo { + /** 响应编码,0表示成功 */ + private Integer errcode; + /** 响应消息 */ + private String errmsg; + /** 响应数据 */ + private Object data; + + public boolean isSuccess() { + return errcode == 0; + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/TaskQueryResultVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/TaskQueryResultVo.java new file mode 100644 index 00000000..29e8abaf --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/TaskQueryResultVo.java @@ -0,0 +1,32 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +import lombok.Data; +import java.util.List; + +@Data +public class TaskQueryResultVo { + /** 消息任务唯一标示 */ + private String taskId; + /** 发送状态:0待发送 1成功 2正在发送 3异常 4失败 5取消 */ + private Integer sendStatus; + /** 总消息数量 */ + private Integer totalCount; + /** 发送成功数量 */ + private Integer sendSuccessCount; + /** 发送失败数量 */ + private Integer sendFailedCount; + /** 创建人 */ + private String createBy; + /** 创建时间 */ + private Long createTime; + /** 消息发送类型:DELAY/now */ + private String sendType; + /** 预约发送开始时间 */ + private Long planSendTime; + /** 发送完成时间 */ + private Long sendFinishedTime; + /** 超时时间(秒) */ + private Integer expire; + /** 消息详情列表 */ + private MessageDetailVo[] messageDetails; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/TemplateVariable.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/TemplateVariable.java new file mode 100644 index 00000000..3349c6a0 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/siq/TemplateVariable.java @@ -0,0 +1,18 @@ +package com.zdxt.enforcementcode.domain.vo.siq; + +public class TemplateVariable { + /** + * 变量名称,模板内占位标识 + */ + private String varName; + + /** + * 变量业务描述 + */ + private String varDesc; + + /** + * 变量示例填充值 + */ + private String example; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java index a23a9ae7..f3b1fcb4 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java @@ -142,7 +142,7 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService RLock lock = redissonClient.getLock("siq_login_lock:" + lockKey); boolean locked = false; try { - locked = lock.tryLock(0, 1, TimeUnit.SECONDS); + locked = lock.tryLock(5, 60*5, TimeUnit.SECONDS); if (!locked) { log.info("用户正在登录中,用户信息:{}", userInfo.toJSONString()); throw new BaseException("用户正在登录中,请稍后再试"); @@ -221,8 +221,7 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(EnterpriseUser::getLoginName, loginName) - .eq(EnterpriseUser::getUserType, userType); + queryWrapper.eq(EnterpriseUser::getLoginName, loginName); EnterpriseUser existUser = enterpriseUserMapper.selectOne(queryWrapper); Long userId; diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/ISIQService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/ISIQService.java new file mode 100644 index 00000000..fb22731f --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/ISIQService.java @@ -0,0 +1,32 @@ +package com.zdxt.enforcementcode.service.thirdparty; + +import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQMsgParamBo; +import com.zdxt.enforcementcode.domain.vo.siq.SIQTaskQueryBo; +import com.zdxt.enforcementcode.domain.vo.siq.SIQTemplateVo; +import com.zdxt.enforcementcode.domain.vo.siq.TaskQueryResultVo; + +/** + * 投诉事项Service接口 + * + * @author Lion Li + * @date 2026-04-17 + */ +public interface ISIQService { + + /** + * + * @param templateId + * @return + * @throws Exception + */ + SIQTemplateVo getTemplateById(String templateId); + + void sendMsg(SIQMsgParamBo paramBo); + + /** + * 按批次查询消息发送结果 + * @param queryBo 查询参数 + * @return 任务查询结果 + */ + TaskQueryResultVo queryTaskResult(SIQTaskQueryBo queryBo); +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/impl/SIQServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/impl/SIQServiceImpl.java new file mode 100644 index 00000000..ef7d32f1 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/thirdparty/impl/SIQServiceImpl.java @@ -0,0 +1,116 @@ +package com.zdxt.enforcementcode.service.thirdparty.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.IdUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.esotericsoftware.minlog.Log; +import com.zdxt.enforcementcode.common.config.SiqConfig; +import com.zdxt.enforcementcode.common.util.StringUtils; +import com.zdxt.enforcementcode.common.util.siq.SIQHttpUtil; +import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQMsgParamBo; +import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQSendMsgBo; +import com.zdxt.enforcementcode.domain.bo.thirdparty.ToUserMetaBo; +import com.zdxt.enforcementcode.domain.vo.siq.*; +import com.zdxt.enforcementcode.service.thirdparty.ISIQService; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.base.BaseException; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 投诉事项Service业务层处理 + * + * @author Lion Li + * @date 2026-04-17 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SIQServiceImpl implements ISIQService { + + @Resource + private SiqConfig siqConfig; + @Override + public SIQTemplateVo getTemplateById(String templateId) { + try{ + // 拼接查询模板详情接口完整地址 + String url = siqConfig.getSiqBaseUrl() + "/mpc/api/messageTemplate/v1/" + templateId + "/get"; + // 当前接口无业务请求体,固定传入空字符串参与签名计算 + String emptyBody = ""; + // 发起带签名POST请求 + String respJson = SIQHttpUtil.postWithSign(url, emptyBody, siqConfig.getMsgSecretKey(), siqConfig.getMsgAppId()); + // JSON字符串转换为实体对象返回 + return JSON.parseObject(respJson, SIQTemplateVo.class); + }catch (Exception e){ + throw new BaseException("获取模板详情失败"); + } + } + @Override + public void sendMsg(SIQMsgParamBo paramBo) { + try { + if (StringUtils.isBlank(paramBo.getReceiveAccount())) { + throw new BaseException("接收账号不能为空"); + } + SIQSendMsgBo sendMsgBo = new SIQSendMsgBo(); + sendMsgBo.setSendChannel(6); + sendMsgBo.setSendType("now"); + sendMsgBo.setTemplateId(siqConfig.getMsgTemplateId()); + sendMsgBo.setExpire(120); + sendMsgBo.setCreateBy(paramBo.getCreateBy()); + + ToUserMetaBo toUserMetaBo = new ToUserMetaBo(); + toUserMetaBo.setMessageId(IdUtil.simpleUUID()); + toUserMetaBo.setVariables(paramBo.getAdditionalData()); + toUserMetaBo.setReceiveAccount(paramBo.getReceiveAccount()); + sendMsgBo.setToUser(new ToUserMetaBo[]{toUserMetaBo}); + + String url = siqConfig.getSiqBaseUrl() + "/egb/api/mpc/api/messageTask/v1/singleCreate"; + String body = JSONObject.toJSONString(sendMsgBo); + log.info("单条发送站内信请求参数: {}", body); + String respJson = SIQHttpUtil.postWithSign(url, body, siqConfig.getMsgSecretKey(), siqConfig.getMsgAppId()); + log.info("单条发送站内信响应结果: {}", respJson); + SendMsgVo sendMsg = JSON.parseObject(respJson, SendMsgVo.class); + if(sendMsg.isSuccess()){ + SendMsgDataVo sendMsgDataVo = JSON.parseObject(JSON.toJSONString(sendMsg.getData()), SendMsgDataVo.class); + List list = sendMsgDataVo.getList(); + if(CollectionUtil.isNotEmpty(list)){ + MessageTaskItemVo messageTaskItemVo = list.get(0); + log.info("单条发送站内信成功,taskId: {}", messageTaskItemVo.getTaskId()); + } + + } + } catch (Exception e) { + log.error("单条发送站内信失败", e); + throw new BaseException("单条发送站内信失败"); + } + } + + @Override + public TaskQueryResultVo queryTaskResult(SIQTaskQueryBo queryBo) { + try { + if (StringUtils.isBlank(queryBo.getTaskId())) { + throw new BaseException("taskId不能为空"); + } + String url = siqConfig.getSiqBaseUrl() + "/egb/api/mpc/api/messageTask/v1/byTaskId/get"; + String body = JSONObject.toJSONString(queryBo); + log.info("查询消息任务结果请求参数: {}", body); + String respJson = SIQHttpUtil.postWithSign(url, body, siqConfig.getMsgSecretKey(), siqConfig.getMsgAppId()); + log.info("查询消息任务结果响应: {}", respJson); + TaskQueryResultVo sendMsgDataVo = null; + SendMsgVo sendMsg = JSON.parseObject(respJson, SendMsgVo.class); + if (sendMsg.isSuccess()) { + sendMsgDataVo = JSON.parseObject(JSON.toJSONString(sendMsg.getData()), TaskQueryResultVo.class); + Log.info("查询消息任务结果成功,消息: {}", JSONObject.toJSONString(sendMsgDataVo)); + } + return sendMsgDataVo; + } catch (Exception e) { + log.error("查询消息任务结果失败", e); + throw new BaseException("查询消息任务结果失败"); + } + } + +} diff --git a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java index 4fbed74c..1238175b 100644 --- a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java +++ b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java @@ -151,4 +151,5 @@ public interface SysUserMapper extends BaseMapperPlus { List selectUserByIds(String[] ids); + } diff --git a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ISysUserService.java b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ISysUserService.java index a33b484d..ac0ca840 100644 --- a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ISysUserService.java +++ b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ISysUserService.java @@ -263,4 +263,12 @@ public interface ISysUserService { * @return 用户信息 */ SysUserVo getCurrentUser(); + + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @return 用户列表信息 + */ + List selectUserByIds(List userIds); } diff --git a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 277d309c..e34f83c9 100644 --- a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -881,6 +881,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return userBean; } + @Override + public List selectUserByIds(List userIds) { + return this.baseMapper.selectUserByIds(Convert.toStrArray(userIds)); + } + /** * 判断是否为机构 * @param deptBeanDataSource diff --git a/zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysUserMapper.xml b/zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysUserMapper.xml index cb333ba6..8fa648b9 100644 --- a/zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -167,7 +167,7 @@ diff --git a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml index a507b9ae..2338a530 100644 --- a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml +++ b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml @@ -291,8 +291,13 @@ siq: # secretKey: SXRpWEhVN3RHM3ZFbzRQcQ== #测试 siqUrl: https://egbpub.siqhz.com/egb/api/thirdauth + siqBaseUrl: https://egbpub.siqhz.com code: TEST + appId: CE07DEE723E241DFB34D80090A569B12 secretKey: ENC(6+WSjXj7UebpPJGq69e7aVZ1ZIrL9wnPcD+rU5XDFCNYGQM8fVvtcQ==) + msgAppId: CE07DEE723E241DFB34D80090A569B12 + msgSecretKey: 0C4E08DCC7DD490E86EF2B79E81691D1 + msgTemplateId: 2076972618633498625 # 模拟数据开关(true=使用模拟数据,false=调用真实接口) mock: enabled: false diff --git a/zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/siq/SiqControllerTest.java b/zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/siq/SiqControllerTest.java new file mode 100644 index 00000000..bc08bd87 --- /dev/null +++ b/zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/siq/SiqControllerTest.java @@ -0,0 +1,118 @@ +package com.zdxt.rest.test.siq; + +import cn.dev33.satoken.context.mock.SaTokenContextMockUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.dev33.satoken.stp.StpUtil; +import com.zdxt.enforcementcode.common.util.siq.SIQHttpUtil; +import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQMsgParamBo; +import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQSendMsgBo; +import com.zdxt.enforcementcode.domain.bo.thirdparty.ToUserMetaBo; +import com.zdxt.enforcementcode.domain.vo.siq.SIQTaskQueryBo; +import com.zdxt.enforcementcode.domain.vo.siq.SIQTemplateVo; +import com.zdxt.enforcementcode.domain.vo.siq.TaskQueryResultVo; +import com.zdxt.enforcementcode.service.thirdparty.ISIQService; +import org.dromara.ZdxtAdminApplication; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.satoken.utils.LoginHelper; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * ISIQService 深i企消息中心接口测试 + *

+ * 包含三类测试: + * 1. getTemplateById - 查询模板详情 + * 2. sendMsg - 发送单条消息 + * 3. queryTaskResult - 按批次查询发送结果 + *

+ * + * @author zdxt + */ +@SpringBootTest(classes = ZdxtAdminApplication.class) +@AutoConfigureMockMvc +@DisplayName("ISIQService 深i企消息中心接口测试") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SiqControllerTest { + + @Autowired + private ISIQService siqService; + + private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e"; + + @BeforeEach + public void setUp() { + SaTokenContextMockUtil.setMockContext(); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest(); + mockRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest)); + + LoginUser loginUser = new LoginUser(); + loginUser.setUserId(1L); + loginUser.setUserType("sys_user"); + loginUser.setTenantId("000000"); + loginUser.setUsername("test_admin"); + loginUser.setNickname("测试管理员"); + loginUser.setDeptId("103"); + loginUser.setDeptName("测试部门"); + loginUser.setRolePermission(new HashSet<>()); + loginUser.setMenuPermission(new HashSet<>()); + + SaLoginParameter param = new SaLoginParameter(); + param.setExtra(LoginHelper.CLIENT_KEY, TEST_CLIENT_ID); + LoginHelper.login(loginUser, param); + } + + @AfterEach + public void tearDown() { + try { + StpUtil.logout(); + } catch (Exception ignored) { + } + RequestContextHolder.resetRequestAttributes(); + SaTokenContextMockUtil.clearContext(); + } + + + @DisplayName("发送消息 - 固定参数直接请求深I企接口") + @Test + @Order(7) + public void testSendMsg_smsChannel() throws Exception { + SIQMsgParamBo paramBo = new SIQMsgParamBo(); + paramBo.setReceiveAccount("3846cbc20dce4140a42e33254414e02f"); + Map variables = new HashMap<>(); + variables.put("enterName", "深圳市腾讯计算机系统有限公司"); + variables.put("dept", "深圳市司法局"); + variables.put("time", "2026-07-30"); + paramBo.setAdditionalData(variables); + siqService.sendMsg(paramBo); + } +// +// @DisplayName("查询任务结果 - 按taskId查询发送状态") +// @Test +// @Order(8) +// public void testQueryTaskResult() throws Exception { +// SIQTaskQueryBo queryBo = new SIQTaskQueryBo(); +// queryBo.setTaskId("a3514ac05fd74d9a92abea91cd2395b9"); +// queryBo.setPageNo(1); +// queryBo.setPageSize(10); +// +// TaskQueryResultVo result = siqService.queryTaskResult(queryBo); +// assertNotNull(result, "查询结果不应为空"); +// System.out.println("任务ID: " + result.getTaskId()); +// System.out.println("发送状态: " + result.getSendStatus()); +// System.out.println("总数: " + result.getTotalCount()); +// System.out.println("成功数: " + result.getSendSuccessCount()); +// System.out.println("失败数: " + result.getSendFailedCount()); +// } + +}