Merge remote-tracking branch 'base-version/test' into dev
# Conflicts: # zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/security/CryptoSignatureUtils.java # zt-module-system/zt-module-system-api/src/main/java/com/zt/plat/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java # zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/api/databus/DatabusDeptProviderApiImpl.java # zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/sms/SmsCallbackController.java # zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/sms/core/enums/SmsChannelEnum.java
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -282,13 +282,13 @@
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>chenbowen</id>
|
||||
<id>haodongqiang</id>
|
||||
<properties>
|
||||
<!-- <env.name>local</env.name>-->
|
||||
<env.name>dev</env.name>
|
||||
<!-- <config.server-addr>localhost:8848</config.server-addr>-->
|
||||
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
||||
<config.namespace>chenbowen</config.namespace>
|
||||
<config.namespace>haodongqiang</config.namespace>
|
||||
<config.group>DEFAULT_GROUP</config.group>
|
||||
<config.username>nacos</config.username>
|
||||
<config.password>P@ssword25</config.password>
|
||||
|
||||
9
sql/dm/短信渠道中铝e办支持_DM8_20260109.sql
Normal file
9
sql/dm/短信渠道中铝e办支持_DM8_20260109.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- 短信渠道新增鸿联九五支持(达梦8)
|
||||
-- 1) system_sms_channel 表新增 epid 字段
|
||||
-- 2) system_sms_channel_code 字典新增 HL95 选项
|
||||
|
||||
ALTER TABLE system_sms_channel ADD COLUMN epid VARCHAR(64);
|
||||
COMMENT ON COLUMN system_sms_channel.epid IS '企业编号(epid)';
|
||||
|
||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted)
|
||||
VALUES (1593, 5, '中铝e办', 'ZLE', 'system_sms_channel_code', 0, '', '', '', '1', '2026-01-09 00:00:00', '1', '2026-01-09 00:00:00', '0');
|
||||
@@ -163,6 +163,33 @@
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "分页结果")
|
||||
@Data
|
||||
@Data //TODO 分页结果参考这个
|
||||
public final class PageResult<T> implements Serializable {
|
||||
|
||||
@Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
package com.zt.plat.framework.common.util.http;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* HttpClient工具类
|
||||
*
|
||||
* @author luzemin
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpClientUtils {
|
||||
/**
|
||||
* 请求配置对象
|
||||
*/
|
||||
private static final RequestConfig REQUEST_CONFIG;
|
||||
|
||||
static {
|
||||
/* 设置请求和传输超时时间 */
|
||||
REQUEST_CONFIG = RequestConfig.custom().setSocketTimeout(60000).setConnectTimeout(60000).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* post请求传输json参数
|
||||
*
|
||||
* @param url url地址
|
||||
* @param jsonParam 参数
|
||||
* @return JSONObject 请求结果对象
|
||||
*/
|
||||
public static JSONObject httpPost(String url, JSONObject jsonParam) {
|
||||
/* 请求返回结果 */
|
||||
JSONObject jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 构建请求体 */
|
||||
if (StringUtils.isNotBlank(jsonParam.toJSONString())) {
|
||||
StringEntity entity = new StringEntity(jsonParam.toJSONString(), StandardCharsets.UTF_8);
|
||||
entity.setContentEncoding(StandardCharsets.UTF_8.name());
|
||||
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
jsonResult = JSONObject.parseObject(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* post请求传输String参数 例如:name=Jack&sex=1&type=2
|
||||
* Content-type:application/x-www-form-urlencoded
|
||||
*
|
||||
* @param url url地址
|
||||
* @param strParam 参数
|
||||
* @return JSONObject 请求结果对象
|
||||
*/
|
||||
public static JSONObject httpPost(String url, String strParam) {
|
||||
/* 请求返回结果 */
|
||||
JSONObject jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 构建请求体 */
|
||||
if (StringUtils.isNotBlank(strParam)) {
|
||||
StringEntity entity = new StringEntity(strParam, StandardCharsets.UTF_8);
|
||||
entity.setContentEncoding(StandardCharsets.UTF_8.name());
|
||||
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
jsonResult = JSONObject.parseObject(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* post请求传输String参数 例如:name=Jack&sex=1&type=2
|
||||
* Content-type:application/x-www-form-urlencoded
|
||||
*
|
||||
* @param url url地址
|
||||
* @param strParam 参数
|
||||
* @param token 身份认证令牌
|
||||
* @return JSONObject 请求结果对象
|
||||
*/
|
||||
public static JSONObject httpPostByToken(String url, String strParam, String token) {
|
||||
/* 请求返回结果 */
|
||||
JSONObject jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 构建请求体 */
|
||||
if (StringUtils.isNotBlank(strParam)) {
|
||||
StringEntity entity = new StringEntity(strParam, StandardCharsets.UTF_8);
|
||||
entity.setContentEncoding(StandardCharsets.UTF_8.name());
|
||||
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
if (StringUtils.isNotBlank(token)) {
|
||||
httpPost.setHeader("token", token);
|
||||
}
|
||||
|
||||
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
jsonResult = JSONObject.parseObject(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* post请求传输String参数 例如:name=Jack&sex=1&type=2
|
||||
* Content-type:application/x-www-form-urlencoded
|
||||
*
|
||||
* @param url url地址
|
||||
* @param strParam 参数
|
||||
* @return String 请求结果对象
|
||||
*/
|
||||
public static String httpPostStr(String url, String strParam) {
|
||||
/* 请求返回结果 */
|
||||
String jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 构建请求体 */
|
||||
if (StringUtils.isNotBlank(strParam)) {
|
||||
StringEntity entity = new StringEntity(strParam, StandardCharsets.UTF_8);
|
||||
entity.setContentEncoding(StandardCharsets.UTF_8.name());
|
||||
entity.setContentType(ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
jsonResult = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* post请求传输String参数 例如:name=Jack&sex=1&type=2
|
||||
* Content-type:application/x-www-form-urlencoded
|
||||
*
|
||||
* @param url url地址
|
||||
* @param strParam 参数
|
||||
* @param token 身份认证字符串
|
||||
* @return String 请求结果对象
|
||||
*/
|
||||
public static String httpPostStrByToken(String url, String strParam, String token) {
|
||||
/* 请求返回结果 */
|
||||
String jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 构建请求体 */
|
||||
if (StringUtils.isNotBlank(strParam)) {
|
||||
StringEntity entity = new StringEntity(strParam, StandardCharsets.UTF_8);
|
||||
entity.setContentEncoding(StandardCharsets.UTF_8.name());
|
||||
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(token)) {
|
||||
httpPost.setHeader("token", token);
|
||||
}
|
||||
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
jsonResult = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的url地址上传文件
|
||||
*
|
||||
* @param mediaName 服务器定义的读取文件流的name
|
||||
* @param url 文件上传url
|
||||
* @param file 文件对象
|
||||
* @return JSONObject 文件上传结果
|
||||
*/
|
||||
public static JSONObject httpFileUpload(String mediaName, String url, MultipartFile file) {
|
||||
/* 请求返回结果 */
|
||||
JSONObject jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(REQUEST_CONFIG);
|
||||
|
||||
/* 构建请求头 */
|
||||
String boundaryStr = UUID.randomUUID().toString();
|
||||
httpPost.setHeader(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name());
|
||||
httpPost.setHeader(HttpHeaders.CONNECTION, "Keep-Alive");
|
||||
httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.MULTIPART_FORM_DATA.getMimeType() + ";boundary=" + boundaryStr);
|
||||
|
||||
try {
|
||||
/* 构建文件对象 */
|
||||
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
|
||||
multipartEntityBuilder.setBoundary(boundaryStr)
|
||||
.setContentType(ContentType.APPLICATION_OCTET_STREAM)
|
||||
.setCharset(StandardCharsets.UTF_8)
|
||||
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||
multipartEntityBuilder.addBinaryBody(mediaName, file.getInputStream(), ContentType.APPLICATION_OCTET_STREAM, file.getOriginalFilename());
|
||||
HttpEntity entity = multipartEntityBuilder.build();
|
||||
httpPost.setEntity(entity);
|
||||
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
jsonResult = JSONObject.parseObject(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送get请求
|
||||
*
|
||||
* @param url 路径
|
||||
* @return JSONObject 请求结果对象
|
||||
*/
|
||||
public static JSONObject httpGet(String url) {
|
||||
/* 请求返回结果 */
|
||||
JSONObject jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
httpGet.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpGet);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
jsonResult = JSONObject.parseObject(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpGet.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送get请求
|
||||
*
|
||||
* @param url 路径
|
||||
* @return JSONObject 请求结果对象
|
||||
*/
|
||||
public static String httpGetStr(String url) {
|
||||
/* 请求返回结果 */
|
||||
String jsonResult = null;
|
||||
|
||||
/* 构建连接对象 */
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
httpGet.setConfig(REQUEST_CONFIG);
|
||||
|
||||
try {
|
||||
/* 提交请求,构建响应对象 */
|
||||
CloseableHttpResponse response = httpClient.execute(httpGet);
|
||||
|
||||
/* 处理请求结果 */
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
jsonResult = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
httpGet.releaseConnection();
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.zt.plat.framework.common.util.security;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.SM4;
|
||||
import com.zt.plat.framework.common.util.json.JsonUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -11,6 +12,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -27,6 +29,15 @@ public final class CryptoSignatureUtils {
|
||||
private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
|
||||
public static final String SIGNATURE_FIELD = "signature";
|
||||
|
||||
private static final String CHARSET = "UTF-8";
|
||||
|
||||
//@Value("${sa.encrypt.sm4.key}")
|
||||
private static String SM4_KEY = "1234567890123456";
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
private CryptoSignatureUtils() {
|
||||
}
|
||||
|
||||
@@ -220,4 +231,91 @@ public final class CryptoSignatureUtils {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------- 国密方式2 -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*/
|
||||
public static String enCode(String data) {
|
||||
try {
|
||||
// 第一步: SM4 加密
|
||||
SM4 sm4 = new SM4(hexToBytes(stringToHex(SM4_KEY)));
|
||||
String encryptHex = sm4.encryptHex(data);
|
||||
|
||||
// 第二步: Base64 编码
|
||||
return new String(Base64.getEncoder().encode(encryptHex.getBytes(CHARSET)), CHARSET);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("国密加密失败{}",e.getMessage(),e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*/
|
||||
public static String deCode(String data) {
|
||||
try {
|
||||
|
||||
// 第一步: Base64 解码
|
||||
byte[] base64Decode = Base64.getDecoder().decode(data);
|
||||
|
||||
// 第二步: SM4 解密
|
||||
SM4 sm4 = new SM4(hexToBytes(stringToHex(SM4_KEY)));
|
||||
return sm4.decryptStr(new String(base64Decode));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("国密解密失败{}",e.getMessage(),e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String stringToHex(String input) {
|
||||
char[] chars = input.toCharArray();
|
||||
StringBuilder hex = new StringBuilder();
|
||||
for (char c : chars) {
|
||||
hex.append(Integer.toHexString((int) c));
|
||||
}
|
||||
return hex.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 16 进制串转字节数组
|
||||
*
|
||||
* @param hex 16进制字符串
|
||||
* @return byte数组
|
||||
*/
|
||||
public static byte[] hexToBytes(String hex) {
|
||||
int length = hex.length();
|
||||
byte[] result;
|
||||
if (length % 2 == 1) {
|
||||
length++;
|
||||
result = new byte[(length / 2)];
|
||||
hex = "0" + hex;
|
||||
} else {
|
||||
result = new byte[(length / 2)];
|
||||
}
|
||||
int j = 0;
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
result[j] = hexToByte(hex.substring(i, i + 2));
|
||||
j++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 16 进制字符转字节
|
||||
*
|
||||
* @param hex 16进制字符 0x00到0xFF
|
||||
* @return byte
|
||||
*/
|
||||
private static byte hexToByte(String hex) {
|
||||
return (byte) Integer.parseInt(hex, 16);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.zt.plat.framework.common.util.security;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* 3DES加密解密工具类
|
||||
* 注意:如果websphere下报错:Could not find class 'com.sun.crypto.provider.SunJCE'
|
||||
* 解决方法如下: 下载sunjce_provider.jar放到jdk目录\jre\lib\ext里即可解决
|
||||
*
|
||||
* @author apple
|
||||
*/
|
||||
|
||||
public class DESUtil {
|
||||
|
||||
/**
|
||||
* 默认的密钥
|
||||
*/
|
||||
private static final String strDefaultKey = "cymco-20160329000000000000000ABCGHDYFYSSPPOEWWWDDDSSXX-cymco";
|
||||
|
||||
private Cipher encryptCipher = null;
|
||||
|
||||
private Cipher decryptCipher = null;
|
||||
|
||||
/**
|
||||
* 将byte数组转换为表示16进制值的字符串, 如:byte[]{8,18}转换为:0813, 和public static byte[]
|
||||
* hexStr2ByteArr(String strIn) 互为可逆的转换过程
|
||||
* @param arrB 需要转换的byte数组
|
||||
* @return 转换后的字符串
|
||||
* @throws Exception 本方法不处理任何异常,所有异常全部抛出
|
||||
*/
|
||||
public static String byteArr2HexStr(byte[] arrB) throws Exception {
|
||||
int iLen = arrB.length;
|
||||
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
|
||||
StringBuffer sb = new StringBuffer(iLen * 2);
|
||||
for (int i = 0; i < iLen; i++) {
|
||||
int intTmp = arrB[i];
|
||||
// 把负数转换为正数
|
||||
while (intTmp < 0) {
|
||||
intTmp = intTmp + 256;
|
||||
}
|
||||
// 小于0F的数需要在前面补0
|
||||
if (intTmp < 16) {
|
||||
sb.append("0");
|
||||
}
|
||||
sb.append(Integer.toString(intTmp, 16));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将表示16进制值的字符串转换为byte数组, 和public static String byteArr2HexStr(byte[] arrB)
|
||||
* 互为可逆的转换过程
|
||||
* @param strIn 需要转换的字符串
|
||||
* @return 转换后的byte数组
|
||||
* @throws Exception 本方法不处理任何异常,所有异常全部抛出
|
||||
*/
|
||||
public static byte[] hexStr2ByteArr(String strIn) throws Exception {
|
||||
byte[] arrB = strIn.getBytes(StandardCharsets.UTF_8);
|
||||
int iLen = arrB.length;
|
||||
|
||||
// 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
|
||||
byte[] arrOut = new byte[iLen / 2];
|
||||
for (int i = 0; i < iLen; i = i + 2) {
|
||||
String strTmp = new String(arrB, i, 2);
|
||||
arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
|
||||
}
|
||||
return arrOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认构造方法,使用默认密钥
|
||||
* @throws Exception
|
||||
*/
|
||||
public DESUtil() throws Exception {
|
||||
this(strDefaultKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定密钥构造方法
|
||||
* @param strKey 指定的密钥
|
||||
* @throws Exception
|
||||
*/
|
||||
public DESUtil(String strKey) throws Exception {
|
||||
//Security.addProvider(new com.sun.crypto.provider.SunJCE());
|
||||
Key key = getKey(strKey.getBytes());
|
||||
|
||||
encryptCipher = Cipher.getInstance("DES");
|
||||
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
|
||||
decryptCipher = Cipher.getInstance("DES");
|
||||
decryptCipher.init(Cipher.DECRYPT_MODE, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密字节数组
|
||||
* @param arrB 需加密的字节数组
|
||||
* @return 加密后的字节数组
|
||||
* @throws Exception
|
||||
*/
|
||||
public byte[] encrypt(byte[] arrB) throws Exception {
|
||||
return encryptCipher.doFinal(arrB);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密字符串
|
||||
* @param strIn 需加密的字符串
|
||||
* @return 加密后的字符串
|
||||
* @throws Exception
|
||||
*/
|
||||
public String encrypt(String strIn) throws Exception {
|
||||
return byteArr2HexStr(encrypt(strIn.getBytes()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密字节数组
|
||||
* @param arrB 需解密的字节数组
|
||||
* @return 解密后的字节数组
|
||||
* @throws Exception
|
||||
*/
|
||||
public byte[] decrypt(byte[] arrB) throws Exception {
|
||||
return decryptCipher.doFinal(arrB);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密字符串
|
||||
* @param strIn 需解密的字符串
|
||||
* @return 解密后的字符串
|
||||
* @throws Exception
|
||||
*/
|
||||
public String decrypt(String strIn) throws Exception {
|
||||
return new String(decrypt(hexStr2ByteArr(strIn)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定字符串生成密钥,密钥所需的字节数组长度为8位 不足8位时后面补0,超出8位只取前8位
|
||||
* @param arrBTmp 构成该字符串的字节数组
|
||||
* @return 生成的密钥
|
||||
* @throws Exception
|
||||
*/
|
||||
private Key getKey(byte[] arrBTmp) throws Exception {
|
||||
// 创建一个空的8位字节数组(默认值为0)
|
||||
byte[] arrB = new byte[8];
|
||||
|
||||
// 将原始字节数组转换为8位
|
||||
for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
|
||||
arrB[i] = arrBTmp[i];
|
||||
}
|
||||
|
||||
// 生成密钥
|
||||
Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public static String SHA1(String decript) throws NoSuchAlgorithmException {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
digest.update(decript.getBytes());
|
||||
byte messageDigest[] = digest.digest();
|
||||
// Create Hex String
|
||||
StringBuffer hexString = new StringBuffer();
|
||||
// 字节数组转换为 十六进制 数
|
||||
for (int i = 0; i < messageDigest.length; i++) {
|
||||
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
|
||||
if (shaHex.length() < 2) {
|
||||
hexString.append(0);
|
||||
}
|
||||
hexString.append(shaHex);
|
||||
}
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void main(String [] args){
|
||||
DESUtil des;
|
||||
try {
|
||||
des = new DESUtil();
|
||||
String en= des.encrypt("fls123,12121");
|
||||
System.out.println(en);
|
||||
String de = des.decrypt(en);
|
||||
System.out.println(de);
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.zt.plat.framework.common.util.validation;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 正则校验工具
|
||||
*
|
||||
* @author luzemin
|
||||
**/
|
||||
public class MatcherSolveUtils {
|
||||
|
||||
/**
|
||||
* 正则校验正数、负数、和小数
|
||||
*/
|
||||
private static final Pattern IS_NUM = Pattern.compile("^(\\-|\\+)?\\d+(\\.\\d+)?$");
|
||||
/**
|
||||
* 正则校验大于0的数字
|
||||
*/
|
||||
private static final Pattern IS_GT_0_NUM = Pattern.compile("(^[0-9]+\\.\\d+$)|(^[0-9]+$)");
|
||||
/**
|
||||
* 正则校验大于等于0的数字
|
||||
*/
|
||||
private static final Pattern IS_GE_EQ_0_NUM = Pattern.compile("(^[0-9]+\\.\\d+$)|(^[1-9]+\\d*$)");
|
||||
/**
|
||||
* 正则校验字母或者数字组成的字符串
|
||||
*/
|
||||
private static final Pattern EN_NUM = Pattern.compile("^[A-Za-z0-9]+$");
|
||||
/**
|
||||
* 中文、英文、数字但不包括下划线等符号
|
||||
*/
|
||||
private static final Pattern EN_CN_NUM = Pattern.compile("^[\\u4E00-\\u9FA5A-Za-z0-9]+$");
|
||||
/**
|
||||
* Email地址
|
||||
*/
|
||||
private static final Pattern EMAIL = Pattern.compile("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
|
||||
/**
|
||||
* 域名
|
||||
*/
|
||||
private static final Pattern WWW = Pattern.compile("[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\\.?");
|
||||
/**
|
||||
* InternetURL
|
||||
*/
|
||||
private static final Pattern URL = Pattern.compile("(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
private static final Pattern MOBILE_NUM = Pattern.compile("^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$");
|
||||
/**
|
||||
* 车牌号
|
||||
*/
|
||||
private static final Pattern TRUCK_NO = Pattern.compile("^([京津晋冀蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新][ABCDEFGHJKLMNPQRSTUVWXY][1-9DF][1-9ABCDEFGHJKLMNPQRSTUVWXYZ]\\d{3}[1-9DF]|[京津晋冀蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新][ABCDEFGHJKLMNPQRSTUVWXY][\\dABCDEFGHJKLNMxPQRSTUVWXYZ]{5})$");
|
||||
|
||||
/**
|
||||
* 电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号)
|
||||
*/
|
||||
private static final Pattern PHONE_NUM = Pattern.compile("((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$)");
|
||||
/**
|
||||
* 身份证
|
||||
*/
|
||||
private static final Pattern ID_CARD = Pattern.compile("(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)");
|
||||
/**
|
||||
* 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
|
||||
*/
|
||||
private static final Pattern ACCOUNT = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]{4,15}$");
|
||||
/**
|
||||
* 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)
|
||||
*/
|
||||
private static final Pattern PASSWORD = Pattern.compile("^[a-zA-Z]\\w{5,17}$");
|
||||
/**
|
||||
* 强密码(必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-18之间)
|
||||
*/
|
||||
private static final Pattern PASSWORD_STRONG = Pattern.compile("^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,18}$");
|
||||
|
||||
/**
|
||||
* 正则校验正数、负数、和小数
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkIsNum(String str) {
|
||||
return IS_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则校验大于0的数字
|
||||
*
|
||||
* @param str 待校验字符串t
|
||||
*/
|
||||
public static boolean checkIsGt0Num(String str) {
|
||||
return IS_GT_0_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则校验大于等于0的数字
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkIsGtEq0Num(String str) {
|
||||
return IS_GE_EQ_0_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则校验字母或者数字组成的字符串
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkEnNum(String str) {
|
||||
return EN_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 中文、英文、数字但不包括下划线等符号
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkEnCnNum(String str) {
|
||||
return EN_CN_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Email地址
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkEmail(String str) {
|
||||
return EMAIL.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 域名
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkWww(String str) {
|
||||
return WWW.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* InternetURL
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkInternetURL(String str) {
|
||||
return URL.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkMobileNum(String str) {
|
||||
return MOBILE_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 车牌号
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
* @return 校验通过-true,反之-false
|
||||
*/
|
||||
public static boolean checkTruckNo(String str) {
|
||||
return TRUCK_NO.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号)
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkPhoneNum(String str) {
|
||||
return PHONE_NUM.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份证
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkIdCard(String str) {
|
||||
return ID_CARD.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkAccount(String str) {
|
||||
return ACCOUNT.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkPassword(String str) {
|
||||
return PASSWORD.matcher(str).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 强密码(必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-18之间)
|
||||
*
|
||||
* @param str 待校验字符串
|
||||
*/
|
||||
public static boolean checkPasswordStrong(String str) {
|
||||
return PASSWORD_STRONG.matcher(str).matches();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.HEADER_TENAN
|
||||
*
|
||||
* Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中
|
||||
*
|
||||
* @author ZT
|
||||
* @author ZT TODO
|
||||
*/
|
||||
public class TenantRocketMQSendMessageHook implements SendMessageHook {
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
PageSumSupport.tryAttachSummary(this, queryWrapper, pageResult);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
//TODO 分页结果参考这个 ===============================
|
||||
// MyBatis Plus 查询
|
||||
IPage<T> mpPage = MyBatisUtils.buildPage(pageParam, sortingFields);
|
||||
selectPage(mpPage, queryWrapper);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<modules>
|
||||
<module>zt-module-databus-api</module>
|
||||
<module>zt-module-databus-server</module>
|
||||
<module>zt-module-databus-server-app</module>
|
||||
</modules>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
48
zt-module-databus/zt-module-databus-server-app/pom.xml
Normal file
48
zt-module-databus/zt-module-databus-server-app/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>zt-module-databus</artifactId>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>zt-module-databus-server-app</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
Databus 模块启动器。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-databus-server</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -192,24 +192,4 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<modules>
|
||||
<module>zt-module-infra-api</module>
|
||||
<module>zt-module-infra-server</module>
|
||||
<module>zt-module-infra-server-app</module>
|
||||
</modules>
|
||||
<artifactId>zt-module-infra</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /zt-module-infra-server
|
||||
WORKDIR /zt-module-infra-server
|
||||
RUN mkdir -p /zt-module-infra-server-app
|
||||
WORKDIR /zt-module-infra-server-app
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/zt-module-infra-server.jar app.jar
|
||||
COPY ../zt-module-infra-server/target/zt-module-infra-server-app.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
48
zt-module-infra/zt-module-infra-server-app/pom.xml
Normal file
48
zt-module-infra/zt-module-infra-server-app/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>zt-module-infra</artifactId>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>zt-module-infra-server-app</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
infra 模块启动器。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-infra-server</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -149,24 +149,5 @@
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<modules>
|
||||
<module>zt-module-report-api</module>
|
||||
<module>zt-module-report-server</module>
|
||||
<module>zt-module-report-server-app</module>
|
||||
</modules>
|
||||
<artifactId>zt-module-report</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /zt-module-report-server
|
||||
WORKDIR /zt-module-report-server
|
||||
RUN mkdir -p /zt-module-report-server-app
|
||||
WORKDIR /zt-module-report-server-app
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/zt-module-report-server.jar app.jar
|
||||
COPY ../zt-module-report-server/target/zt-module-report-server-app.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
48
zt-module-report/zt-module-report-server-app/pom.xml
Normal file
48
zt-module-report/zt-module-report-server-app/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>zt-module-report</artifactId>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>zt-module-report-server-app</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
report 模块启动器。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-report-server</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -111,23 +111,4 @@
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<modules>
|
||||
<module>zt-module-system-api</module>
|
||||
<module>zt-module-system-server</module>
|
||||
<module>zt-module-system-server-app</module>
|
||||
</modules>
|
||||
<artifactId>zt-module-system</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.zt.plat.module.system.api.dept.dto;
|
||||
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 公司部门推送消息 Response DTO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "RPC 服务 - 部门推送消息 Response DTO")
|
||||
@Data
|
||||
public class DeptMsgRespDTO {
|
||||
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 本系统部门 ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 外部系统标识
|
||||
*/
|
||||
private String systemCode;
|
||||
|
||||
/**
|
||||
* 外部系统组织编码
|
||||
*/
|
||||
private String externalDeptCode;
|
||||
|
||||
/**
|
||||
* 外部系统组织名称
|
||||
*/
|
||||
private String externalDeptName;
|
||||
|
||||
/**
|
||||
* 映射状态
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 是否发送消息
|
||||
*/
|
||||
private Integer isSendMsg;
|
||||
|
||||
}
|
||||
@@ -2,23 +2,27 @@ package com.zt.plat.module.system.api.esp;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.api.dept.dto.*;
|
||||
import com.zt.plat.module.system.api.esp.dto.EspDto;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
@Tag(name = "RPC 服务 - 部门")
|
||||
@Tag(name = "RPC 服务 - 部门推送消息")
|
||||
public interface EspApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/dept";
|
||||
String PREFIX = ApiConstants.PREFIX + "/dept-esp";
|
||||
|
||||
|
||||
@PostMapping(PREFIX + "/create")
|
||||
@Operation(summary = "新增部门")
|
||||
CommonResult<Long> createDept(@RequestBody DeptSaveReqDTO createReqVO);
|
||||
|
||||
|
||||
@PostMapping(PREFIX + "/pushMsg")
|
||||
@Operation(summary = "推送消息")
|
||||
CommonResult<List<EspDto>> pushMsg(@RequestBody DeptSaveReqDTO syncReqDTO);
|
||||
@Operation(summary = "查询部门消息")
|
||||
CommonResult<List<DeptMsgRespDTO>> selectDepMsg(@RequestBody DeptSaveReqDTO syncReqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.zt.plat.module.system.api.msg;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
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.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
@Tag(name = "RPC 服务 - 消息发送")
|
||||
public interface MsgSendApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/msg/send";
|
||||
|
||||
@PostMapping(PREFIX + "/sendTextMsg")
|
||||
@Operation(summary = "发送企业微信文本消息", description = "在 mobile 为空时,使用 userId 加载对应 Admin 的手机号")
|
||||
CommonResult<Long> sendTextMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/sendImageMsg")
|
||||
@Operation(summary = "发送企业微信图片消息", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号")
|
||||
CommonResult<Long> sendImageMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@GetMapping(PREFIX + "/sendVoiceMsg")
|
||||
@Operation(summary = "发送企业微信语音消息")
|
||||
CommonResult<SmsLogRespDTO> getSmsLog(@RequestParam("id") Long id);
|
||||
|
||||
@PostMapping(PREFIX + "/sendVideoMsg")
|
||||
@Operation(summary = "发送企业微信视频消息", description = "在 mobile 为空时,使用 userId 加载对应 Admin 的手机号")
|
||||
CommonResult<Long> sendVideoMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/sendFileMsg")
|
||||
@Operation(summary = "发送企业微信文件消息", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号")
|
||||
CommonResult<Long> sendFileMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@GetMapping(PREFIX + "/sendTextCardMsg")
|
||||
@Operation(summary = "发送企业微信文本卡片消息")
|
||||
CommonResult<SmsLogRespDTO> sendTextCardMsg(@RequestParam("id") Long id);
|
||||
|
||||
@PostMapping(PREFIX + "/sendTextCardMsgPich01")
|
||||
@Operation(summary = "发送企业微信文本卡片消息 -物资存货智能管理 预警信息", description = "在 mobile 为空时,使用 userId 加载对应 Admin 的手机号")
|
||||
CommonResult<Long> sendTextCardMsgPich01(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/sendNewsMsg")
|
||||
@Operation(summary = "发送企业微信图文消息", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号")
|
||||
CommonResult<Long> sendNewsMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@GetMapping(PREFIX + "/sendMpNewsMsg")
|
||||
@Operation(summary = "发送企业微信图文消息(mpnews)")
|
||||
CommonResult<SmsLogRespDTO> sendMpNewsMsg(@RequestParam("id") Long id);
|
||||
|
||||
@PostMapping(PREFIX + "/sendMarkdownMsg")
|
||||
@Operation(summary = "发送企业微信小程序通知消息", description = "在 mobile 为空时,使用 userId 加载对应 Admin 的手机号")
|
||||
CommonResult<Long> sendMarkdownMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@PostMapping(PREFIX + "/sendMiniProgramNoticeMsg")
|
||||
@Operation(summary = "发送企业微信图片消息", description = "在 mobile 为空时,使用 userId 加载对应 Member 的手机号")
|
||||
CommonResult<Long> sendMiniProgramNoticeMsg(@Valid @RequestBody SmsSendSingleToUserReqDTO reqDTO);
|
||||
|
||||
@GetMapping(PREFIX + "/sendInteractiveTaskCardMsg")
|
||||
@Operation(summary = "发送企业微信任务卡片消息")
|
||||
CommonResult<SmsLogRespDTO> sendInteractiveTaskCardMsg(@RequestParam("id") Long id);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zt.plat.module.system.api.sms.dto.code;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class User implements Serializable {
|
||||
/**
|
||||
* 返回码
|
||||
*/
|
||||
private Integer errcode;
|
||||
/**
|
||||
* 对返回码的文本描述内容
|
||||
*/
|
||||
private String errmsg;
|
||||
|
||||
/**
|
||||
* 成员UserID
|
||||
*/
|
||||
private String UserId;
|
||||
|
||||
/**
|
||||
* 手机设备号(由中铝集团在安装时随机生成,删除重装会改变,升级不受影响)
|
||||
*/
|
||||
private String DeviceId;
|
||||
|
||||
/**
|
||||
* 成员身份信息,2:超级管理员, 4:分级管理员,5:普通成员
|
||||
*/
|
||||
private Integer usertype;
|
||||
|
||||
|
||||
/**
|
||||
* 判断受否授权成功
|
||||
*
|
||||
* @return true-授权成功、false-授权失败
|
||||
*/
|
||||
public boolean isAuthorized() {
|
||||
return this.getErrcode() == 0;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.zt.plat.module.system.api.sms.dto.send;
|
||||
|
||||
import com.zt.plat.framework.common.validation.Mobile;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ public class ApiConstants {
|
||||
|
||||
/**
|
||||
* 服务名
|
||||
*
|
||||
* 注意,需要保证和 spring.application.name 保持一致
|
||||
*/
|
||||
public static final String NAME = "system-server";
|
||||
|
||||
@@ -108,6 +108,9 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择");
|
||||
ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板");
|
||||
ErrorCode SMS_CHANNEL_BALANCE_UNSUPPORTED = new ErrorCode(1_002_011_003, "该短信渠道不支持余额查询");
|
||||
ErrorCode MSG_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_004, "消息渠道不存在");
|
||||
|
||||
|
||||
|
||||
// ========== 短信模板 1-002-012-000 ==========
|
||||
ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在");
|
||||
@@ -122,6 +125,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失");
|
||||
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在");
|
||||
ErrorCode SMS_CALLBACK_SIGN_INVALID = new ErrorCode(1_002_013_100, "短信回调签名校验失败");
|
||||
ErrorCode MSG_CALLBACK_SIGN_INVALID = new ErrorCode(1_002_013_101, "消息回调签名校验失败");
|
||||
|
||||
// ========== 短信验证码 1-002-014-000 ==========
|
||||
ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在");
|
||||
@@ -206,6 +210,8 @@ public interface ErrorCodeConstants {
|
||||
// ========== 用户与部门关系 1-002-029-000 ==========
|
||||
ErrorCode USER_DEPT_NOT_EXISTS = new ErrorCode(1_002_029_000, "用户与部门关系不存在");
|
||||
|
||||
ErrorCode USER_DEPT_SAVE_EXISTS = new ErrorCode(1_002_029_001, "插入用户部门失败");
|
||||
|
||||
// ========== 系统序列号分段明细 1-002-030-000 ==========
|
||||
ErrorCode SEQUENCE_DETAIL_NOT_EXISTS = new ErrorCode(1_002_030_000, "系统序列号分段明细不存在");
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /zt-module-system-server
|
||||
WORKDIR /zt-module-system-server
|
||||
RUN mkdir -p /zt-module-system-server-app
|
||||
WORKDIR /zt-module-system-server-app
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/zt-module-system-server.jar app.jar
|
||||
COPY ../zt-module-system-server/target/zt-module-system-server-app.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
48
zt-module-system/zt-module-system-server-app/pom.xml
Normal file
48
zt-module-system/zt-module-system-server-app/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>zt-module-system</artifactId>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>zt-module-system-server-app</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
system 模块启动器。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-system-server</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -200,26 +200,14 @@
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.13.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,7 +123,7 @@ public class DeptController {
|
||||
|
||||
@GetMapping("/top-level-list")
|
||||
@Operation(summary = "获取当前用户可访问的顶级部门列表", description = "用于懒加载,返回当前用户所属部门的最顶层祖先部门,如果用户没有关联任何部门则返回空列表")
|
||||
@PreAuthorize("@ss.hasPermission('system:dept:query')")
|
||||
// @PreAuthorize("@ss.hasPermission('system:dept:query')")
|
||||
public CommonResult<List<DeptRespVO>> getTopLevelDeptList() {
|
||||
List<DeptDO> list = deptService.getTopLevelDeptList();
|
||||
return success(BeanUtils.toBean(list, DeptRespVO.class));
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -7,14 +7,10 @@ import com.zt.plat.module.system.framework.sms.core.enums.SmsChannelEnum;
|
||||
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 org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 短信回调")
|
||||
@@ -73,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;
|
||||
@@ -83,6 +82,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
|
||||
case QINIU: return new QiniuSmsClient(properties);
|
||||
// case CMCC_MAS: return new CmccMasSmsClient(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办"),
|
||||
// CMCC_MAS("CMCC_MAS", "中国移动云MAS"),
|
||||
;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user