Commit a455fe6e authored by wux's avatar wux

将jd_dev分支代码合并到release分支

parent bb93b5dc
This diff is collapsed.
alter table ecw_channel add COLUMN `bubble_exempt_weight` decimal(15,2) DEFAULT NULL COMMENT '免泡重量';
INSERT INTO jiedao.system_menu
(id, name, permission, menu_type, sort, parent_id, `path`, icon, component, status, creator, create_time, updater, update_time, deleted, is_show_in_menu_bar, name_en, keepalive, redirect, badge_field)
VALUES(2391, '重泡货操作权限', '', 1, 1, 1559, 'order pao buttons', '#', NULL, 0, '1', '2024-11-20 17:16:53', '1', '2024-11-20 17:16:53', 0, 0, 'Order PAO Buttons', 0, NULL, NULL);
INSERT INTO jiedao.system_menu
(id, name, permission, menu_type, sort, parent_id, `path`, icon, component, status, creator, create_time, updater, update_time, deleted, is_show_in_menu_bar, name_en, keepalive, redirect, badge_field)
VALUES(2392, '设为半泡', 'ecw:weight_deal:half_throw', 3, 1, 2391, '', '', '', 0, '1', '2024-11-20 17:22:48', '1', '2024-11-20 17:34:50', 0, 1, 'semi Bubble', 0, NULL, NULL);
INSERT INTO jiedao.system_menu
(id, name, permission, menu_type, sort, parent_id, `path`, icon, component, status, creator, create_time, updater, update_time, deleted, is_show_in_menu_bar, name_en, keepalive, redirect, badge_field)
VALUES(2393, '设为全泡', 'ecw:weight_deal:process', 3, 1, 2391, '', '', '', 0, '1', '2024-11-20 17:25:46', '1', '2024-11-20 17:35:04', 0, 1, 'full Bubble', 0, NULL, NULL);
INSERT INTO jiedao.system_menu
(id, name, permission, menu_type, sort, parent_id, `path`, icon, component, status, creator, create_time, updater, update_time, deleted, is_show_in_menu_bar, name_en, keepalive, redirect, badge_field)
VALUES(2394, '设为普货', 'ecw:weight_deal:general_cargo', 3, 1, 2391, '', '', '', 0, '1', '2024-11-20 17:28:52', '1', '2024-11-20 17:35:26', 0, 1, 'normal Cargo', 0, NULL, NULL);
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(101, '客户放货推荐', '101', 'customer_source', 0, 'default', '', NULL, '1', '2024-11-19 10:34:42', '1', '2024-11-19 14:48:21', 0, 'Shipper Release', 'Libération Expéditeur');
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(5, '后台放货信息获得', '5', 'customer_from', 0, 'default', '', NULL, '1', '2024-11-25 18:20:55', '1', '2024-11-25 18:21:25', 0, 'Cargo', NULL);
This diff is collapsed.
-- 订单V值获取积分,首单修改成多选,添加全部选项
INSERT INTO jiedao.system_dict_type (name, `type`, status, remark, creator, create_time, updater, update_time, deleted) VALUES('是否选择', 'yes_or_no_or_all', 0, NULL, '1', '2024-12-06 10:39:28', '1', '2024-12-06 10:39:28', 0);
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(0, '是', '0', 'yes_or_no_or_all', 0, 'default', '', NULL, '1', '2024-12-06 10:51:10', '1', '2024-12-06 10:51:10', 0, 'YES', 'YES');
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(1, '否', '1', 'yes_or_no_or_all', 0, 'default', '', NULL, '1', '2024-12-06 10:51:42', '1', '2024-12-06 10:51:42', 0, 'NO', 'NO');
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(2, '不限', '2', 'yes_or_no_or_all', 0, 'default', '', NULL, '1', '2024-12-06 10:53:29', '1', '2024-12-06 10:53:29', 0, 'ALL', 'ALL');
--订单V值获取积分,客户方字典添加
INSERT INTO jiedao.system_dict_type (name, `type`, status, remark, creator, create_time, updater, update_time, deleted) VALUES('客户方', 'customer_side', 0, '客户方:1.发货人 2.收货人', '1', '2024-12-07 13:50:41', '1', '2024-12-07 13:50:41', 0);
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(0, '发货人', '1', 'customer_side', 0, 'default', '', NULL, '1', '2024-12-09 15:53:27', '1', '2024-12-09 15:53:27', 0, 'shipper', 'shipper');
INSERT INTO jiedao.system_dict_data (sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted, label_en, label_fr) VALUES(0, '收货人', '2', 'customer_side', 0, 'default', '', NULL, '1', '2024-12-09 15:53:48', '1', '2024-12-09 15:53:48', 0, 'consignee', 'consignee');
\ No newline at end of file
--app站内信,添加英文、法文字段
ALTER TABLE ecw_web_internal_message
ADD COLUMN title_en VARCHAR(255) COMMENT '标题英文',
ADD COLUMN title_fr VARCHAR(255) COMMENT '标题法文',
ADD COLUMN content_en LONGTEXT COMMENT '内容英文',
ADD COLUMN content_fr LONGTEXT COMMENT '内容法文';
--站内信为英法字段赋值
UPDATE ecw_web_internal_message
SET title_en = title,
title_fr = title,
content_en = content,
content_fr = content;
\ No newline at end of file
This diff is collapsed.
......@@ -21,6 +21,7 @@
<knife4j.version>3.0.2</knife4j.version>
<swagger-annotations.version>1.5.22</swagger-annotations.version>
<servlet.versoin>2.5</servlet.versoin>
<okhttp.version>4.9.3</okhttp.version>
<!-- DB 相关 -->
<mysql.version>5.1.46</mysql.version>
<druid.version>1.2.9</druid.version>
......@@ -51,6 +52,7 @@
<mapstruct.version>1.4.1.Final</mapstruct.version>
<hutool.version>5.6.1</hutool.version>
<pinyin4j.version>2.5.0</pinyin4j.version>
<jackson.version>2.16.1</jackson.version>
<!--修改版本 2.2.7 -->
<easyexcel.verion>2.2.7</easyexcel.verion>
<ooxml.verion>1.0</ooxml.verion>
......@@ -436,6 +438,29 @@
<artifactId>ueditor-spring-boot-starter</artifactId>
<version>${ueditor.version}</version>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- xml -->
</dependencies>
</dependencyManagement>
</project>
......@@ -3,6 +3,9 @@ package cn.iocoder.yudao.framework.common.util.date;
import cn.hutool.core.date.DateUtil;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
......@@ -167,4 +170,24 @@ public class DateUtils {
calendar.add(Calendar.DAY_OF_MONTH, days);
return calendar.getTime();
}
/**
* 解析并转换时间为本地时间
* 本方法将给定的ISO 8601格式的日期时间字符串解析为日期对象,并转换为本地时间
*
* @param deliverTime ISO 8601格式的日期时间字符串
* @return 返回转换后的本地时间日期对象
*/
public static Date parseAndConvertToLocalDate(String deliverTime) {
// 定义 ISO 8601 格式的日期时间解析器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
// 将字符串解析为 ZonedDateTime 对象
ZonedDateTime utcDateTime = ZonedDateTime.parse(deliverTime, formatter);
// 获取本地时区
ZoneId localZoneId = ZoneId.systemDefault();
// 将 UTC 时间转换为本地时间
ZonedDateTime localDateTime = utcDateTime.withZoneSameInstant(localZoneId);
// 将 ZonedDateTime 转换为 Date
return Date.from(localDateTime.toInstant());
}
}
......@@ -19,6 +19,10 @@ public class DictDataRespDTO {
* 字典英文标签
*/
private String labelEn;
/**
* 字典英文标签
*/
private String labelFr;
/**
* 字典值
*/
......
package cn.iocoder.yudao.framework.http.collection;
import java.util.Collection;
/**
* 集合工具类
* @author Wenyi Feng
* @since 2018-10-15
*/
public class CollectionUtils {
/**
* 如果集合为{@code null}或者空,则返回{@code true}。
* 否则,返回{@code false}
*
* @param collection 待检查的集合
* @return 集合是否为空
*/
public static boolean isEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
/**
* 判断数组是空数组
*
* @param array 待判断的数据
* @return true:空 / false:非空
*/
public static boolean isEmpty(String[] array) {
return array == null || array.length == 0;
}
/**
* 如果集合不为{@code null}或者空,则返回{@code true}。
* 否则,返回{@code false}
*
* @param collection 待检查的集合
* @return 集合是否不为空
*/
public static boolean isNotEmpty(Collection<?> collection) {
return !isEmpty(collection);
}
/**
* 判断数组不是空数组
*
* @param array 待判断的数据
* @return true:非空 / false:空
*/
public static boolean isNotEmpty(String[] array) {
return !isEmpty(array);
}
}
package cn.iocoder.yudao.framework.http.collection;
import java.util.Map;
/**
* Map工具类
* <ul>
* <li>判断Map是否为空</li>
* <li>判断Map是否不为空</li>
* </ul>
*
* @author Wenyi Feng
* @since 2018-11-17
*/
public class MapUtils {
/**
* 判断Map是否为空
*
* @param map 待判断的Map
* @return 是否为空,true:空;false:不为空
*/
public static boolean isEmpty(Map<?, ?> map) {
if (map == null){
return true;
}
return map.isEmpty();
}
/**
* 判断Map是否不为空
*
* @param map 待判断的Map
* @return 是否不为空,true:不为空;false:空
*/
public static boolean isNotEmpty(Map<?, ?> map) {
return !isEmpty(map);
}
}
package cn.iocoder.yudao.framework.http.convert;
import cn.iocoder.yudao.framework.http.util.StrUtils;
/**
* 进制转换工具类
* @author Wenyi Feng.
*/
public class HexUtils {
/**
* 二进制转十六进制
*
* @param bytes [ellipsis]
* @return [ellipsis]
*/
public static String bin2Hex(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
String hex = Integer.toHexString(aByte & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 16进制转化为 2进制
*
* @param hexStr 16进制字符串
* @return byte[]
*/
public static byte[] hex2Bin(String hexStr) {
if (StrUtils.isBlank(hexStr)) {
return null;
}
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
\ No newline at end of file
package cn.iocoder.yudao.framework.http.convert;
import cn.iocoder.yudao.framework.http.util.StrUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 参数处理工具类
* @author Wenyi Feng
* @since 2018-10-28
*/
public class ParamUtils {
/**
* 将Map型转为请求参数型
*
* @param data Map类型的参数
* @return url请求的参数
* @throws UnsupportedEncodingException 异常
*/
public static String getUrlParamsByMap(Map<String, String> data) throws UnsupportedEncodingException {
if (data == null || data.isEmpty()) {
return null;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> i : data.entrySet()) {
sb.append(i.getKey())
.append("=")
.append(URLEncoder.encode(i.getValue(), StandardCharsets.UTF_8.toString()))
.append("&");
}
String str = sb.toString();
return str.substring(0, str.length() - 1);
}
/**
* 将url参数转换成map
* @param param [ellipsis]
* @return 参数Map
*/
public static Map<String, String> getUrlParams(String param) {
Map<String, String> map = new HashMap<>();
if (StrUtils.isBlank(param)) {
return map;
}
String[] params = param.split("&");
for (String s : params) {
String[] p = s.split("=");
if (p.length == 2) {
map.put(p[0], p[1]);
}
}
return map;
}
}
package cn.iocoder.yudao.framework.http.core;
import cn.iocoder.yudao.framework.http.collection.MapUtils;
import cn.iocoder.yudao.framework.http.util.StrUtils;
import lombok.Getter;
import lombok.Setter;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.util.*;
/**
* 请求所需要参数
*
* @author Erwin Feng
* @since 2022-11-18
*/
@Getter
@Setter
public class Request {
private static final String PARAM_DEFAULT_KEY = "__default__";
/**
* 请求地址
*/
private String url;
/**
* 请求方式
*/
private Method method;
/**
* 请求参数
*/
@Getter
private Map<String, Object> param;
private ParamFormat paramFormat = ParamFormat.STRING;
private List<FileBo> fileList;
/**
* 请求工具
*/
private Util util = Util.OkHttp;
public enum ParamFormat {
STRING, FORM, JSON
}
/**
* 请求方法枚举
*/
public enum Method {
GET, POST, PUT, PATCH, DELETE
}
/**
* 请求工具枚举
*/
public enum Util {
// JDK,
OkHttp,
// AsyncHttpClient
}
/**
* 日志级别枚举
*/
public enum LogLevel {
DEBUG, INFO, ERROR
}
/**
* 创建 Request
*
* @param url 请求地址
* @param method 请求方法
* @param param 请求参数
* @return Request
*/
public static Request create(String url, Method method, String param) {
Request request = new Request();
request.setUrl(url);
request.setMethod(method);
request.setParam(param);
return request;
}
/**
* 创建 Request
*
* @param url 请求地址
* @param method 请求方法
* @param param 请求参数
* @return Request
*/
public static Request create(String url, Method method, Map<String, Object> param) {
Request request = new Request();
request.setUrl(url);
request.setMethod(method);
request.setParam(param);
return request;
}
/**
* 请求可选参数
*/
@Getter
@Setter
public static class Option {
/**
* 连接超时时间,秒,默认5秒
*/
private Integer connectTimeoutSecond = 5;
/**
* 读取超时时间,秒,默认45秒
*/
private Integer readTimeoutSecond = 45;
/**
* 请求头
*/
private Map<String, String> headers = new HashMap<>();
/**
* ssl
*/
private SSLSocketFactory sslContextFactory;
/**
* HostnameVerifier
*/
private HostnameVerifier hostnameVerifier;
/**
* 日志级别
*/
private LogLevel logLevel;
/**
* 创建 Request.Option
*
* @param connectTimeoutSecond 连接超时时间,秒
* @param readTimeoutSecond 读取超时时间,秒
* @return Request.Option
*/
public static Option create(Integer connectTimeoutSecond, Integer readTimeoutSecond) {
Option option = new Option();
Optional.ofNullable(connectTimeoutSecond).ifPresent(option::setConnectTimeoutSecond);
Optional.ofNullable(readTimeoutSecond).ifPresent(option::setReadTimeoutSecond);
return option;
}
/**
* 创建 Request.Option
*
* @param connectTimeoutSecond 连接超时时间,秒
* @param readTimeoutSecond 读取超时时间,秒
* @param headers http headers
* @return Request.Option
*/
public static Option create(Integer connectTimeoutSecond, Integer readTimeoutSecond, Map<String, String> headers) {
Option option = new Option();
Optional.ofNullable(connectTimeoutSecond).ifPresent(option::setConnectTimeoutSecond);
Optional.ofNullable(readTimeoutSecond).ifPresent(option::setReadTimeoutSecond);
Optional.ofNullable(headers).ifPresent(option::setHeaders);
return option;
}
}
public Request setParam(String param) {
if (StrUtils.isNotBlank(param)) {
Map<String, Object> map = new HashMap<>();
map.put(PARAM_DEFAULT_KEY, param);
this.param = map;
}
return this;
}
public Request setParam(Map<String, Object> map) {
this.param = map;
return this;
}
public Request bak(Map<String, Object> map) {
if (MapUtils.isEmpty(map)) {
return this;
}
StringJoiner stringJoiner = new StringJoiner("&", "", "");
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (StrUtils.isNotBlank(entry.getKey())) {
stringJoiner.add(entry.getKey() + "=" + entry.getValue());
}
}
String param = stringJoiner.toString();
return setParam(param);
}
public static class MediaConstant {
/**
* application/json
*/
public static final String APPLICATION_JSON_VALUE = "application/json";
/**
* application/...form...
*/
public static final String APPLICATION_FORM_VALUE = "application/x-www-form-urlencoded";
}
@Getter
@Setter
public static class FileBo {
private String paramName = "file";
private String fileName;
private File file;
}
}
\ No newline at end of file
package cn.iocoder.yudao.framework.http.core;
import lombok.Getter;
import lombok.Setter;
/**
* @author Erwin Feng
* @since 2022-11-24
*/
@Getter
@Setter
public class Response {
private int code;
private String msg;
private String body;
}
package cn.iocoder.yudao.framework.http.core.client;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response;
import java.io.IOException;
import java.util.Objects;
/**
* HTTP 请求客户端 接口
*
* @author Erwin Feng
* @since 2022-11-24
*/
public interface HttpClient {
String PARAM_DEFAULT_KEY = "__default__";
/**
* 执行 http 请求
*
* @param request 请求
* @param option http 可选配置
* @return http 响应结果
* @throws IOException IO 异常
*/
Response execute(Request request, Request.Option option) throws IOException;
default Integer getTimeoutSecond(Integer timeoutSecond) {
if (Objects.nonNull(timeoutSecond) && timeoutSecond > 0) {
return timeoutSecond;
}
return null;
}
}
package cn.iocoder.yudao.framework.http.core.client;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.client.impl.OkHttpClient;
/**
* HTTP CLIENT 工厂
*
* @author Erwin Feng
* @since 2022-11-24
*/
public class HttpClientFactory {
/**
* 获取 http client
*
* @param httpUtil http 工具
* @return http client 实现
*/
public static HttpClient get(Request.Util httpUtil) {
if (Request.Util.OkHttp == httpUtil) {
return new OkHttpClient();
} else {
throw new RuntimeException("not find http util: " + httpUtil.name());
}
}
}
package cn.iocoder.yudao.framework.http.exception;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 异常工具类
*
* @author Erwin Feng
* @since 2020/8/13
*/
public class ExceptionUtils {
/**
* 获取异常栈信息
*
* @param throwable 异常
* @return 异常信息
*/
public static String getStackTrace(Throwable throwable) {
if (throwable == null) {
return "";
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, Boolean.TRUE);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
}
package cn.iocoder.yudao.framework.http.sms;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* BulksmsHttp 类用于处理与 Bulksms 相关的 HTTP 请求。
* 主要功能包括发送短信和设置短信参数。
*
* @author wuxian
* @since 2024-11-27
**/
@Slf4j
public class BulksmsHttp {
// 请求的URL地址
private static final String REQ_URL = "https://portal.nigeriabulksms.com/api/";
/**
* 发送请求到 Bulksms 服务器。
*
* @param param 请求参数
* @return 响应对象
*/
public Response sendReq(Map<String, Object> param) {
// 设置请求头
Map<String, String> header = new HashMap<>();
header.put("Accept", "text/html,application/xhtml+xml,application/xml");
header.put("Content-Type", "application/json");
// 发送POST请求
return HttpUtils.post(REQ_URL, param, header, Request.ParamFormat.FORM);
}
/**
* 设置发送短信的参数。
*
* @param mobiles 接收短信的手机号
* @param message 短信内容
* @param senderName 发送者名称
* @param username 用户名
* @param password 密码
* @return 参数映射
*/
@NotNull
public Map<String, Object> setParams(String mobiles, String message, String senderName, String username, String password) {
Map<String, Object> param = new HashMap<>();
param.put("username", username);
param.put("password", password);
param.put("mobiles", mobiles);
param.put("message", message);
param.put("sender", senderName);
return param;
}
public static void main(String[] args) {
BulksmsHttp test = new BulksmsHttp();
String mobiles = "2347087010202";
String sender = "ECLogistics";
String message = "Good day This is E&C logistics(ship from China to Nigeria),It has been a while since we last worked together,and we would like to reestablish our partnership. Our main service:GROUPAGE/FULL CONTAINER/AIR CARGO/COMPRESSING/CONSOLITAION .We value your business and would appreciate the opportunity to serve you once again.Have a Nice day! My WA :+861 592 035 652 7";
String username = "ecit@ewchina.net";
String password = "0z@@vtj1h";
Map<String, Object> param = test.setParams(mobiles, sender, message, username, password);
test.sendReq(param);
}
}
package cn.iocoder.yudao.framework.http.sms;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* SendChampHttp类用于处理与SendChamp API相关的HTTP请求,以发送短信和获取短信状态
* 该类包含了构造请求参数和请求头的方法,以及执行HTTP请求的方法
*
* @author wuxian
* @since 2024-10-23
**/
@Slf4j
public class SendChampHttp {
// 定义发送短信的API URL
private static final String SMS_URL = "https://api.sendchamp.com/api/v1/sms/send";
// 定义获取短信状态的API URL前缀
private static final String RECEIVE_URL = "https://api.sendchamp.com/api/v1/sms/status/";
/**
* 发送POST请求以发送短信
*
* @param param 请求参数,包含短信的接收者、消息内容等
* @param header 请求头,包含API密钥等信息
* @return 返回API的响应结果
*/
public Response postReq(Map<String, Object> param, Map<String, String> header) {
return HttpUtils.post(SMS_URL, param, header, Request.ParamFormat.JSON);
}
/**
* 发送GET请求以获取短信发送状态
*
* @param header 请求头,包含API密钥等信息
* @param id 短信发送的唯一标识符
* @return 返回API的响应结果
*/
public Response getReceiveStatus(Map<String, String> header, String id) {
return HttpUtils.get(RECEIVE_URL + id, header, "");
}
/**
* 设置HTTP请求的头信息
*
* @param apiKey SendChamp API密钥
* @return 返回构造好的请求头Map对象
*/
@NotNull
public Map<String, String> setHeader(String apiKey) {
Map<String, String> header = new HashMap<>();
header.put("Accept", "application/json,text/plain,*/*");
header.put("Content-Type", "application/json");
header.put("Authorization", apiKey);
return header;
}
/**
* 设置发送短信的请求参数
*
* @param to 短信接收者的电话号码
* @param message 短信内容
* @param senderName 发送者名称
* @return 返回构造好的请求参数Map对象
*/
@NotNull
public Map<String, Object> setParams(String to, String message, String senderName) {
Map<String, Object> param = new HashMap<>();
param.put("to", to);
param.put("message", message);
param.put("sender_name", senderName);
param.put("route", "dnd");
return param;
}
public static void main(String[] args) {
SendChampHttp test = new SendChampHttp();
String to = "8618926674857";
String senderName = "ECLogistics";
String message = "Good day!This is E&C logistics(ship from China to Nigeria),It has been a while since we last worked together, and we would like to reestablish our partnership. Our main service: GROUPAGE/FULL CONTAINE";
Map<String, Object> param = test.setParams(to, message, senderName);
String apiKey = "Bearer sendchamp_live_$2a$10$vQPdaDjl96Ybc5tzFmZYg.nqGirXuJBGDqJArthZnFR8P9mM5Z/JO";
Map<String, String> header = test.setHeader(apiKey);
test.postReq(param, header);
// test.getReceiveStatus(header, "66e6e9df-b454-4df7-a968-af944a535757");
}
}
package cn.iocoder.yudao.framework.http.sms;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.core.client.HttpClient;
import cn.iocoder.yudao.framework.http.core.client.HttpClientFactory;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author wuxian
* @since 2024-10-23
**/
@Slf4j
public class WhisperClientHttp {
public void testPost(){
// HttpResponse<String> response = Unirest.post("https://whispersms.xyz/api/send_message/")
// .header("Authorization", "Api_key gAAAAABl5ZDpoKnKVaK0scmWo7GQeKDA1sn3OZBeMXUz211vZv5QqSEtr7_LD5ISTqSD0PnWLRWavYmJ3nQJiutT-sgp0dyHR2HIL6FYEhD3t2CNuoHJoSIOia5ffo_rjfxW_pk8co0i7UMTYANNGxsRNJLQTu_Alw==")
// //.header("Authorization","Token 0371a29044432d36cc2508b7ce51e70f466c7d95")
// .header("Content-Type", "application/json")
// .body("{\"contacts\": [\"8618102810628\"],\"sender_id\": \"EC\",\"message\": \"say hello everybody.\",\"priority_route\": False,\"campaign_name\": \"mytest\"}")
// .asString();
// System.out.println("response is ------------------------>"+response.getBody());;
}
public void postReq(List<String> phonenums, String message){
HttpClient client = HttpClientFactory.get(Request.Util.OkHttp);
String sendUrl = "https://whispersms.xyz/whisper/send_message/";
//String sendUrl = "https://whispersms.xyz/api/send_message/";
Map<String, Object> param = new HashMap<>();
param.put("contacts",phonenums);
param.put("message",message);
param.put("sender_id","EC");
param.put("priority_route","False");
param.put("campaign_name","EClogistics");
Map<String, String> header = new HashMap<>();
//header.put("Accept","application/json,text/plain,*/*");
header.put("Content-Type", "application/json");
header.put("Authorization","Token 0371a29044432d36cc2508b7ce51e70f466c7d95");
//header.put("Authorization", "Api_key gAAAAABl5ZDpoKnKVaK0scmWo7GQeKDA1sn3OZBeMXUz211vZv5QqSEtr7_LD5ISTqSD0PnWLRWavYmJ3nQJiutT-sgp0dyHR2HIL6FYEhD3t2CNuoHJoSIOia5ffo_rjfxW_pk8co0i7UMTYANNGxsRNJLQTu_Alw==");
Request req = Request.create(sendUrl,Request.Method.POST,param);
req.setParamFormat(Request.ParamFormat.JSON);
try {
Response res = client.execute(req,Request.Option.create(0,0,header));
log.info("response code ---------------------------->"+res.getCode());
log.info("response content is --------------------》"+res.getBody());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args){
WhisperClientHttp test = new WhisperClientHttp();
//test.testPost();
List<String> phonenums = new ArrayList<>();
phonenums.add("8618102810628");
phonenums.add("2348140352000");
String message = "Good day !This is E&C logistics(ship from China to Nigeria) ,It has been a while since we last worked together, and we would like to reestablish our partnership. " +
"Our main service: GROUPAGE/FULL CONTAINER/AIR CARGO/COMPRESSING/CONSOLITAION ." +
"We value your business and would appreciate the opportunity to serve you once again." +
"Have a Nice day! My wa :+8615920356527";
test.postReq(phonenums,message);
//test.testPost();
}
}
package cn.iocoder.yudao.framework.http.sms;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* YCloudWhatsappHttp类用于处理与 Whatsapp 相关的 HTTP 请求,以发送短信和获取短信状态。
*
* @author wuxian
* @since 2024-10-30
**/
@Slf4j
public class YCloudWhatsappHttp {
//请求地址
private static final String WA_URL = "https://api.ycloud.com/v2/whatsapp/messages/sendDirectly";
private static final String RECEIVE_URL = "https://api.ycloud.com/v2/whatsapp/messages/";
/**
* 发送WhatsApp消息
*
* @param header 请求头,包含认证信息
* @param param 请求参数,包含消息内容
* @return 响应结果
*/
public Response postWhatsapp(Map<String, String> header, Map<String, Object> param) {
return HttpUtils.post(WA_URL, param, header, Request.ParamFormat.JSON);
}
/**
* 获取消息发送状态
*
* @param header 请求头,包含认证信息
* @param id 消息ID
* @return 响应结果
*/
public Response getReceiveStatus(Map<String, String> header, String id) {
return HttpUtils.get(RECEIVE_URL + id, header, "");
}
/**
* 设置请求参数
*
* @param code 验证码
* @param templateName 模板名称
* @param lanCode 语言编号
* @param to 接收手机号
* @return 参数
*/
public Map<String, Object> setParams(String code, String templateName, String lanCode, String to) {
//构建模板参数
Map<String, Object> template = new HashMap<>();
Map<String, String> language = new HashMap<>();
Parameters parameters = new Parameters();
Button button = new Button();
button.setIndex("0");
button.setType("button");
button.setSub_type("url");
//模板包含语言(language)、组件(components),组件包含参数(parameters)
parameters.setType("text");
parameters.setText(code);
List<Object> list1 = new ArrayList<>();
list1.add(parameters);
//构建json中的组件
button.setParameters(list1);
List<Object> list2 = new ArrayList<>();
list2.add(new Components().setType("body").setParameters(list1));
list2.add(button);
language.put("code", lanCode);
language.put("policy", "deterministic");
template.put("language", language);
template.put("name", templateName);
template.put("components", list2);
//构建最终requestBody的json
Map<String, Object> param = new HashMap<>();
param.put("from", "8618022485824");
param.put("to", to);
param.put("type", "template");
param.put("template", template);
return param;
}
/**
* 设置请求头
*
* @param apiKey API密钥
* @return 请求头
*/
public Map<String, String> setHeader(String apiKey) {
//构建http请求的头
Map<String, String> header = new HashMap<>();
header.put("Accept", "application/json");
header.put("Content-Type", "application/json");
header.put("X-API-Key", apiKey);
return header;
}
public static void main(String[] args) {
YCloudWhatsappHttp test = new YCloudWhatsappHttp();
String apiKey = "9dbd912f56c101e53b23cb7b758ffda8";
String code = "8888";
String templateName = "ec_verification";
String lanCode = "en";
test.postWhatsapp(test.setHeader(apiKey), test.setParams(code, templateName, lanCode, "8618926674857"));
}
}
@lombok.Setter
@lombok.Getter
class Parameters {
private String type;
private String text;
}
@lombok.Setter
@lombok.Getter
class Components {
private String type;
private List<Object> parameters = null;
}
@lombok.Setter
@lombok.Getter
class Button {
private String type;
private String sub_type;
private String index;
private List<Object> parameters = null;
}
package cn.iocoder.yudao.framework.http.util;
import cn.iocoder.yudao.framework.http.core.Request;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.core.client.HttpClient;
import cn.iocoder.yudao.framework.http.core.client.HttpClientFactory;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
* Http 工具类
*
* @author Jayden
* @date 2024/11/28
*/
@Slf4j
public class HttpUtils {
// 创建一个HttpClient实例,用于执行HTTP请求
private static final HttpClient HTTP_CLIENT = HttpClientFactory.get(Request.Util.OkHttp);
/**
* 发送POST请求并返回响应结果
*
* @param url 请求的URL地址
* @param params 请求参数,键值对形式
* @param headers 请求头,键值对形式
* @param paramFormat 请求参数的格式
* @return Response对象,包含响应结果
*/
public static Response post(String url, Map<String, Object> params, Map<String, String> headers, Request.ParamFormat paramFormat) {
// 创建请求对象,指定URL、请求方法和参数
Request request = Request.create(url, Request.Method.POST, params);
request.setParamFormat(paramFormat);
try {
// 执行请求并获取响应
Response response = HTTP_CLIENT.execute(request, Request.Option.create(0, 0, headers));
// 记录响应码和响应内容
log.info("response code ---------------------------->" + response.getCode());
log.info("response content is --------------------》" + response.getBody());
return response;
} catch (Exception e) {
// 记录错误信息并抛出运行时异常
log.error("Error executing POST request: " + e.getMessage(), e);
throw new RuntimeException("Error executing POST request: " + e.getMessage(), e);
}
}
/**
* 执行GET请求
*
* @param url 请求的URL
* @param headers 请求头,键值对形式
* @return 返回响应对象,包含响应码、响应内容等信息
* @throws RuntimeException 如果执行请求时发生异常,则抛出运行时异常
*/
public static Response get(String url, Map<String, String> headers, String param) {
// 创建请求对象,指定URL和请求方法
Request request = Request.create(url, Request.Method.GET, param);
try {
// 执行请求并获取响应
Response response = HTTP_CLIENT.execute(request, Request.Option.create(0, 0, headers));
// 记录响应码和响应内容
log.info("response code ---------------------------->" + response.getCode());
log.info("response content is --------------------》" + response.getBody());
return response;
} catch (Exception e) {
// 记录错误信息并抛出运行时异常
log.error("Error executing GET request: " + e.getMessage(), e);
throw new RuntimeException("Error executing GET request: " + e.getMessage(), e);
}
}
}
......@@ -65,7 +65,7 @@ public class I18nMessage {
* @return
*/
public static String getMessage(String message, Integer lang) {
Locale locale = lang == 0 ? Locale.SIMPLIFIED_CHINESE : Locale.ENGLISH;
Locale locale = Languages.getLocaleByCode(lang);
try {
return accessor.getMessage(message, locale);
} catch (Exception e) {
......
......@@ -19,7 +19,11 @@ public enum LangEnum {
ZH("zh", 0),
/*** 英文 */
EN("en", 1);
EN("en", 1),
/*** 法文 */
FR("fr", 2);
private String language;
......
package cn.iocoder.yudao.framework.i18n.core;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Locale;
@Getter
public enum Languages {
SIMPLIFIED_CHINESE(0, Locale.SIMPLIFIED_CHINESE),
ENGLISH(1, Locale.ENGLISH),
// 集成法语
FRENCH(2, Locale.FRENCH);
private int code;
private Locale locale;
Languages(int code, Locale locale) {
this.code = code;
this.locale = locale;
}
public int getCode() {
return code;
}
public Locale getLocale() {
return locale;
}
public static Locale getLocaleByCode(int code) {
for (Languages languages : values()) {
if (languages.getCode() == code) {
return languages.getLocale();
}
}
// 默认返回简体中文
return SIMPLIFIED_CHINESE.getLocale();
}
}
......@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.idempotent.core.aop;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import cn.iocoder.yudao.framework.idempotent.core.redis.IdempotentRedisDAO;
......@@ -49,7 +50,7 @@ public class IdempotentAspect {
// 锁定失败,抛出异常
if (!success) {
log.info("[beforePointCut][方法({}) 参数({}) 存在重复请求]", joinPoint.getSignature().toString(), joinPoint.getArgs());
throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), idempotent.message());
throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), I18nMessage.getMessage(GlobalErrorCodeConstants.REPEATED_REQUESTS.getMsg()));
}
}
......
......@@ -111,6 +111,8 @@ public class YudaoMQAutoConfiguration {
.autoAcknowledge(false) // 不自动 ack
.cancelOnError(throwable -> false); // 默认配置,发生异常就取消消费,显然不符合预期;因此,我们设置为 false
container.register(builder.build(), listener);
log.info("[redisMessageListenerContainer][注册 streamKey({}) 对应的监听器({})]",
listener.getStreamKey(), listener.getClass().getName());
});
return container;
}
......
package cn.iocoder.yudao.framework.sms.core.client;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import java.util.List;
......@@ -25,15 +23,27 @@ public interface SmsClient {
/**
* 发送消息
*
* @param logId 日志编号
* @param mobile 手机号
* @param apiTemplateId 短信 API 的模板编号
* @param logId 日志编号
* @param mobile 手机号
* @param apiTemplateId 短信 API 的模板编号
* @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
* @return 短信发送结果
*/
SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId,
List<KeyValue<String, Object>> templateParams);
/**
* 发送消息
*
* @param logId 日志编号
* @param mobile 手机号
* @param smsTemplateDTO 模板
* @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
* @return 短信发送结果
*/
SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO,
List<KeyValue<String, Object>> templateParams);
/**
* 解析接收短信的接收结果
*
......@@ -51,4 +61,9 @@ public interface SmsClient {
*/
SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId);
/**
* 查询短信发送状态
*
*/
SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO);
}
......@@ -11,9 +11,9 @@ import lombok.ToString;
/**
* 短信的 CommonResult 拓展类
*
* <p>
* 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
*
* <p>
* 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
*
* @author 捷道源码
......@@ -25,7 +25,7 @@ public class SmsCommonResult<T> extends CommonResult<T> {
/**
* API 返回错误码
*
* <p>
* 由于第三方的错误码可能是字符串,所以使用 String 类型
*/
private String apiCode;
......
package cn.iocoder.yudao.framework.sms.core.client.dto;
import lombok.Data;
/**
* SendChamp发送返回结果
*
* 该类用于封装SendChamp发送操作的返回结果,包括发送状态码、数据信息、错误信息等
* 主要用于处理和解析SendChamp API的响应内容
*
* @author Jayden
* @date 2024/11/23
*/
@Data
public class SendChampResult {
// 响应状态码,用于表示发送操作的结果状态
private int code;
// 发送数据信息,包括发送消息的ID、业务ID和总联系人数
private Data data;
// 错误信息,用于描述发送操作中可能出现的错误详情
private String errors;
// 消息信息,通常用于提供更详细的响应描述
private String message;
// 响应状态,以字符串形式表示发送操作的状态
private String status;
/**
* 发送数据信息
* 该内部类用于详细描述发送操作的相关数据,包括发送消息的唯一标识、业务标识和联系人的总数
*/
@lombok.Data
public static class Data {
// 发送消息的唯一标识符
private String id;
// 业务标识符,用于关联发送消息的特定业务
private String businessId;
// 总联系人数,表示发送消息所涉及的联系人数量
private int totalContacts;
}
}
package cn.iocoder.yudao.framework.sms.core.client.dto;
import cn.iocoder.yudao.framework.common.util.spring.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.util.Map;
/**
* 日志dto
*
* @author Jayden
* @date 2024/11/6
*/
@Data
@ToString(callSuper = true)
public class SmsLogDTO {
/**
* 自增编号
*/
private Long id;
// ========= 渠道相关字段 =========
/**
* 短信渠道编号
* <p>
*/
private Long channelId;
/**
* 短信渠道编码
* <p>
*/
private String channelCode;
// ========= 模板相关字段 =========
/**
* 模板编号
* <p>
*/
private Long templateId;
/**
* 模板编码
* <p>
*/
private String templateCode;
/**
* 短信类型
* <p>
*/
private Integer templateType;
/**
*
*/
private String templateContent;
/**
*
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
/**
* 短信 API 的模板编号
* <p>
*/
private String apiTemplateId;
// ========= 手机相关字段 =========
/**
* 手机号
*/
private String mobile;
/**
* 用户编号
*/
private Long userId;
/**
* 用户类型
* <p>
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
// ========= 发送相关字段 =========
/**
* 发送状态
* <p>
*/
private Integer sendStatus;
/**
* 发送时间
*/
private Date sendTime;
/**
* 发送结果的编码
* <p>
* 枚举 {@link SmsFrameworkErrorCodeConstants}
*/
private Integer sendCode;
/**
* 发送结果的提示
* <p>
* 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
* 异常情况下,通过格式化 Exception 的提示存储
*/
private String sendMsg;
/**
* 短信 API 发送结果的编码
* <p>
* 由于第三方的错误码可能是字符串,所以使用 String 类型
*/
private String apiSendCode;
/**
* 短信 API 发送失败的提示
*/
private String apiSendMsg;
/**
* 短信 API 发送返回的唯一请求 ID
* <p>
* 用于和短信 API 进行定位于排错
*/
private String apiRequestId;
/**
* 短信 API 发送返回的序号
* <p>
* 用于和短信 API 平台的发送记录关联
*/
private String apiSerialNo;
// ========= 接收相关字段 =========
/**
* 接收状态
* <p>
*/
private Integer receiveStatus;
/**
* 接收时间
*/
private Date receiveTime;
/**
* 短信 API 接收结果的编码
*/
private String apiReceiveCode;
/**
* 短信 API 接收结果的提示
*/
private String apiReceiveMsg;
/**
* 重发的短信日志id
*/
private Long smsLogId;
/**
* 重发次数
*/
private Integer num;
/**
* 短信节点表id
*/
private Long nodeId;
/**
* 节点模板序列号
*/
private Integer nodeTemplateSn;
/**
* 发送类型
*/
private Integer messageType;
}
......@@ -40,7 +40,7 @@ public class SmsReceiveRespDTO {
private String serialNo;
/**
* 短信日志编号
*
* <p>
* 对应 SysSmsLogDO 的编号
*/
private Long logId;
......
package cn.iocoder.yudao.framework.sms.core.client.dto;
import lombok.Data;
import lombok.ToString;
/**
* 模板dto
*
* @author Jayden
* @date 2024/11/6
*/
@Data
@ToString(callSuper = true)
public class SmsTemplateDTO {
/**
* 自增编号
*/
private Long id;
// ========= 模板相关字段 =========
/**
* 短信类型
*/
private Integer type;
/**
* 启用状态
*/
private Integer status;
/**
* 模板编码,保证唯一
*/
private String code;
/**
* 模板名称
*/
private String name;
/**
* 模板内容
*/
private String content;
/**
* 备注
*/
private String remark;
/**
* 短信 API 的模板编号
*/
private String apiTemplateId;
// ========= 渠道相关字段 =========
/**
* 短信渠道编号
*/
private Long channelId;
/**
* 短信渠道编码
*/
private String channelCode;
/**
* 节点
*/
private String nodeValue;
/**
* 运输方式
*/
private Long transportId;
/**
* 发送类型
*/
private Integer messageType;
/**
* 语言
*/
private String language;
}
......@@ -21,7 +21,7 @@ public class SmsTemplateRespDTO {
private String content;
/**
* 审核状态
*
* <p>
* 枚举 {@link SmsTemplateAuditStatusEnum}
*/
private Integer auditStatus;
......
......@@ -3,7 +3,10 @@ package cn.iocoder.yudao.framework.sms.core.client.impl;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
import cn.iocoder.yudao.framework.sms.core.client.impl.aliyun.AliyunSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.bulk.BulkSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp.SendChampSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.yCloud.YCloudSmsClient;
import cn.iocoder.yudao.framework.sms.core.client.impl.yunpian.YunpianSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
......@@ -18,12 +21,13 @@ import java.util.concurrent.ConcurrentMap;
/**
* 短信客户端工厂接口
*
* @author zzf
* @author Jayden
*/
@Validated
@Slf4j
public class SmsClientFactoryImpl implements SmsClientFactory {
/**
* 短信客户端 Map
* key:渠道编号,使用 {@link SmsChannelProperties#getId()}
......@@ -33,34 +37,51 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
/**
* 短信客户端 Map
* key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()}
*
* <p>
* 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。
* 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients}
*/
private final ConcurrentMap<String, AbstractSmsClient> channelCodeClients = new ConcurrentHashMap<>();
/**
* 构造方法,初始化 channelCodeClients 集合
*/
public SmsClientFactoryImpl() {
// 初始化 channelCodeClients 集合
Arrays.stream(SmsChannelEnum.values()).forEach(channel -> {
// 创建一个空的 SmsChannelProperties 对象
SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode())
.setApiKey("default").setApiSecret("default");
// 创建 Sms 客户端
AbstractSmsClient smsClient = createSmsClient(properties);
channelCodeClients.put(channel.getCode(), smsClient);
});
}
/**
* 根据渠道编号获取短信客户端
*
* @param channelId 渠道编号
* @return 短信客户端
*/
@Override
public SmsClient getSmsClient(Long channelId) {
return channelIdClients.get(channelId);
}
/**
* 根据渠道编码获取短信客户端
*
* @param channelCode 渠道编码
* @return 短信客户端
*/
@Override
public SmsClient getSmsClient(String channelCode) {
return channelCodeClients.get(channelCode);
}
/**
* 创建或更新短信客户端
*
* @param properties 渠道属性
*/
@Override
public void createOrUpdateSmsClient(SmsChannelProperties properties) {
AbstractSmsClient client = channelIdClients.get(properties.getId());
......@@ -73,14 +94,29 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
}
}
/**
* 根据渠道属性创建短信客户端
*
* @param properties 渠道属性
* @return 短信客户端
*/
private AbstractSmsClient createSmsClient(SmsChannelProperties properties) {
SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode());
Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum));
// 创建客户端
switch (channelEnum) {
case ALIYUN: return new AliyunSmsClient(properties);
case YUN_PIAN: return new YunpianSmsClient(properties);
case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties);
case ALIYUN:
return new AliyunSmsClient(properties);
case YUN_PIAN:
return new YunpianSmsClient(properties);
case DEBUG_DING_TALK:
return new DebugDingTalkSmsClient(properties);
case SENDCHAMP:
return new SendChampSmsClient(properties);
case YCLOUD:
return new YCloudSmsClient(properties);
case BULK:
return new BulkSmsClient(properties);
}
// 创建失败,错误日志 + 抛出异常
log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties);
......
......@@ -3,20 +3,21 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.exceptions.ClientException;
......@@ -25,9 +26,11 @@ import com.aliyuncs.profile.IClientProfile;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import io.undertow.util.DateUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
......@@ -56,6 +59,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
*/
private volatile IAcsClient client;
private static final String DELIVERED_STATUS = "DELIVERED";
public AliyunSmsClient(SmsChannelProperties properties) {
super(properties, new AliyunSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
......@@ -74,9 +79,9 @@ public class AliyunSmsClient extends AbstractSmsClient {
// 构建参数
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(mobile);
if(mobile.startsWith("0086")||mobile.startsWith("86")||mobile.startsWith("+86")||mobile.startsWith("+0086")) {
if (mobile.startsWith("0086") || mobile.startsWith("86") || mobile.startsWith("+86") || mobile.startsWith("+0086")) {
request.setSignName(properties.getSignature());
}else{
} else {
request.setSignName(properties.getSignatureEn());
}
request.setTemplateCode(apiTemplateId);
......@@ -87,7 +92,7 @@ public class AliyunSmsClient extends AbstractSmsClient {
}
@Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) {
List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
return statuses.stream().map(status -> {
SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
......@@ -116,10 +121,14 @@ public class AliyunSmsClient extends AbstractSmsClient {
@VisibleForTesting
Integer convertSmsTemplateAuditStatus(Integer templateStatus) {
switch (templateStatus) {
case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus();
default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
case 0:
return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
case 1:
return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
case 2:
return SmsTemplateAuditStatusEnum.FAIL.getStatus();
default:
throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
}
}
......@@ -150,10 +159,97 @@ public class AliyunSmsClient extends AbstractSmsClient {
return ex.getErrMsg() + " => " + ex.getErrorDescription();
}
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long sendLogId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
// 构建参数
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(mobile);
String apiTemplateId = smsTemplateDTO.getApiTemplateId();
request.setSignName(properties.getSignature());
request.setTemplateCode(apiTemplateId);
request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
request.setOutId(String.valueOf(sendLogId));
// 执行请求
return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId()));
}
/**
* 短信接收状态
* 获取短信日志的接收状态
* 通过调用阿里云短信服务的查询接口,更新短信日志的接收状态
*
* 参见 https://help.aliyun.com/document_detail/101867.html 文档
* @param smsLogDO 短信日志实体,包含查询所需的参数,如手机号、发送时间等
* @return 更新了接收状态的短信日志实体
*/
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认设置接收状态为未接收到
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
// 创建查询发送详情的请求
QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest();
// 设置查询参数,包括手机号(去掉国际拨号)、页大小、当前页、发送日期和业务ID
querySendDetailsRequest.setPhoneNumber(removeCountryCode(smsLogDO.getMobile()));
querySendDetailsRequest.setPageSize(1L);
querySendDetailsRequest.setCurrentPage(1L);
querySendDetailsRequest.setSendDate(formatDate(smsLogDO.getSendTime()));
querySendDetailsRequest.setBizId(smsLogDO.getApiSerialNo());
// 初始化查询响应对象
QuerySendDetailsResponse acsResponse;
try {
// 发起查询请求并获取响应
acsResponse = client.getAcsResponse(querySendDetailsRequest);
} catch (ClientException e) {
// 如果查询出错,记录日志并返回初始化的短信日志DTO
log.error("Error querying send details: ", e);
return smsLogDTO;
}
// 如果查询成功且有查询结果,更新短信日志的接收状态
if (acsResponse != null && !acsResponse.getSmsSendDetailDTOs().isEmpty()) {
// 获取第一条查询结果
QuerySendDetailsResponse.SmsSendDetailDTO smsSendDetailDTO = acsResponse.getSmsSendDetailDTOs().get(0);
// 如果短信发送成功,更新接收状态为已接收到,并设置接收时间
if (DELIVERED_STATUS.equals(smsSendDetailDTO.getErrCode())) {
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_10.getValue());
String receiveDate = smsSendDetailDTO.getReceiveDate();
if (receiveDate != null) {
smsLogDTO.setReceiveTime(DateUtils.parseDate(receiveDate));
}
}
}
// 返回更新后的短信日志DTO
return smsLogDTO;
}
/**
* 去掉手机号开头的国家代码 "86"
*
* @param phoneNumber 包含国家代码的手机号
* @return 去掉国家代码后的手机号
*/
public static String removeCountryCode(String phoneNumber) {
// 使用 replaceFirst 方法去掉开头的 "86"
return phoneNumber.replaceFirst("^86", "");
}
/**
* 将 Date 对象格式化为指定格式的字符串
*
* @param date 需要格式化的 Date 对象
* @return 格式化后的字符串,格式为 "yyyyMMdd"
*/
public static String formatDate(Date date) {
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyyMMdd");
return outputFormat.format(date);
}
/**
* 短信接收状态
* <p>
* 参见 <a href="https://help.aliyun.com/document_detail/101867.html">...</a> 文档
*
* @author 捷道源码
*/
......@@ -198,14 +294,14 @@ public class AliyunSmsClient extends AbstractSmsClient {
private String bizId;
/**
* 用户序列号
*
* <p>
* 这里我们传递的是 SysSmsLogDO 的日志编号
*/
@JsonProperty("out_id")
private String outId;
/**
* 短信长度,例如说 1、2、3
*
* <p>
* 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送
*/
@JsonProperty("sms_size")
......
......@@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
/**
* 阿里云的 SmsCodeMapping 实现类
*
* 参见 https://help.aliyun.com/document_detail/101346.htm 文档
* <p>
* 参见 <a href="https://help.aliyun.com/document_detail/101346.htm">...</a> 文档
*
* @author 捷道源码
*/
......
package cn.iocoder.yudao.framework.sms.core.client.impl.bulk;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.sms.BulksmsHttp;
import cn.iocoder.yudao.framework.http.util.StrUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
/**
* Bulk客户端
*
* @author jayden
*/
@Slf4j
public class BulkSmsClient extends AbstractSmsClient {
// 创建HTTP客户端实例,用于发送短信
private final BulksmsHttp bulksmsHttp = new BulksmsHttp();
public BulkSmsClient(SmsChannelProperties properties) {
super(properties, new BulkSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
}
@Override
protected void doInit() {
}
@Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
return null;
}
/**
* 发送短信服务(版本2)
* 该方法用于发送短信,会根据模板内容和参数动态生成短信内容,并处理发送过程
*
* @param logId 日志ID,用于追踪和记录
* @param mobile 手机号码,短信接收方
* @param smsTemplateDTO 短信模板信息,包括模板内容等
* @param templateParams 模板参数,用于替换模板中的占位符
* @return 返回短信发送结果,包括状态码、消息ID等信息
*/
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
SmsCommonResult<SmsSendRespDTO> smsCommonResult;
// 获取并处理短信模板内容
String content = StrUtils.processTemplateContent(smsTemplateDTO, templateParams);
try {
// 设置发送短信所需的参数
Map<String, Object> param = bulksmsHttp.setParams(mobile, content, "ECLogistics", properties.getApiKey(), properties.getApiSecret());
// 发送POST请求
Response response = bulksmsHttp.sendReq(param);
// 解析响应结果
Map<?, ?> result = JSON.parseObject(response.getBody(), Map.class);
smsCommonResult = SmsCommonResult.build((String) result.get("status"), null, null, null, codeMapping);
} catch (Exception e) {
// 记录发送短信时发生的错误
log.error("Error sending SMS: ", e);
// 返回内部服务器错误结果
return SmsCommonResult.build("500", "Internal Server Error", null, null, codeMapping);
}
// 返回请求结果
return smsCommonResult;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认接收成功,因为没有提供对应的api获取状态
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_10.getValue());
return smsLogDTO;
}
@Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) {
return null;
}
@Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
return SmsCommonResult.build("OK", "", null, data, codeMapping);
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.bulk;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import org.apache.commons.lang3.StringUtils;
/**
* Bulk SmsCodeMapping 实现类
*
* @author Jayden
*/
public class BulkSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
if (StringUtils.isNotBlank(apiCode) && "OK".equals(apiCode)) {
return GlobalErrorCodeConstants.SUCCESS;
}
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}
......@@ -9,9 +9,7 @@ import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
......@@ -93,4 +91,13 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
return SmsCommonResult.build("0", "success", null, data, codeMapping);
}
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
return null;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
return null;
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.sms.SendChampHttp;
import cn.iocoder.yudao.framework.http.util.StrUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* SendChamp客户端
*
* @author jayden
*/
@Slf4j
public class SendChampSmsClient extends AbstractSmsClient {
// 创建HTTP客户端实例,用于发送短信
private final SendChampHttp sendchampHttp = new SendChampHttp();
public SendChampSmsClient(SmsChannelProperties properties) {
super(properties, new SendChampSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
}
@Override
protected void doInit() {
}
@Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
return null;
}
/**
* 发送短信服务(版本2)
* 该方法用于发送短信,会根据模板内容和参数动态生成短信内容,并处理发送过程
*
* @param logId 日志ID,用于追踪和记录
* @param mobile 手机号码,短信接收方
* @param smsTemplateDTO 短信模板信息,包括模板内容等
* @param templateParams 模板参数,用于替换模板中的占位符
* @return 返回短信发送结果,包括状态码、消息ID等信息
*/
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
SmsCommonResult<SmsSendRespDTO> smsCommonResult = SmsCommonResult.build("400", "Bad Request", null, null, codeMapping);
// 获取并处理短信模板内容
String content = StrUtils.processTemplateContent(smsTemplateDTO, templateParams);
try {
// 如果信息超过200字符,需要拆分成多条短信发送
List<String> messages = splitContent(content);
for (String message : messages) {
// 设置发送短信所需的参数
Map<String, Object> param = sendchampHttp.setParams(mobile, message, "Sendchamp");
// 设置请求头,包含认证信息
Map<String, String> header = sendchampHttp.setHeader("Bearer " + properties.getApiSecret());
// 发送POST请求
Response response = sendchampHttp.postReq(param, header);
// 解析响应结果
SendChampResult sendChampResult = JSON.parseObject(response.getBody(), SendChampResult.class);
SendChampResult.Data data = sendChampResult.getData();
// 构建发送结果
String id = data.getBusinessId();
if (StringUtils.isBlank(id)) {
id = data.getId();
}
smsCommonResult = SmsCommonResult.build(sendChampResult.getMessage(), sendChampResult.getMessage(), id, null, codeMapping);
smsCommonResult.setData(new SmsSendRespDTO().setSerialNo(id));
}
} catch (Exception e) {
// 记录发送短信时发生的错误
log.error("Error sending SMS: ", e);
// 返回内部服务器错误结果
return SmsCommonResult.build("500", "Internal Server Error", null, null, codeMapping);
}
// 返回请求结果
return smsCommonResult;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认设置接收状态为未接收到
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
return smsLogDTO;
}
/**
* 拆分内容
* 将内容拆分成多个部分,每个部分不超过 maxLength 字符
*
* @param content 待拆分的内容
* @return 拆分后的内容列表
*/
private List<String> splitContent(String content) {
List<String> messages = new ArrayList<>();
int start = 0;
while (start < content.length()) {
int end = Math.min(start + 200, content.length());
messages.add(content.substring(start, end));
start = end;
}
return messages;
}
@Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) {
return null;
}
@Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
return SmsCommonResult.build("accepted", "accepted", null, data, codeMapping);
}
/**
* 短信接收状态
* <p>
*
* @author 捷道源码
*/
@Data
public static class SmsReceiveStatus {
/**
* module
*/
@JsonProperty("module")
private String module;
/**
* reference
*/
@JsonProperty("reference")
private String reference;
/**
* 状态
*/
@JsonProperty("status")
private String status;
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.sendchamp;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
/**
* SendChamp SmsCodeMapping 实现类
* @author Jayden
*/
public class SendChampSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
switch (apiCode) {
case "accepted":
case "processing":
case "send":
case "delivered":
return GlobalErrorCodeConstants.SUCCESS;
}
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.yCloud;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.http.core.Response;
import cn.iocoder.yudao.framework.http.sms.YCloudWhatsappHttp;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.ReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.enums.YCloudReceiveStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* ycloud客户端
*
* @author Jayden
* @date 2024/11/7
*/
@Slf4j
public class YCloudSmsClient extends AbstractSmsClient {
// 创建HTTP客户端实例,用于发送短信
private final YCloudWhatsappHttp yCloudWhatsappHttp = new YCloudWhatsappHttp();
public YCloudSmsClient(SmsChannelProperties properties) {
super(properties, new YCloudSmsCodeMapping());
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
}
@Override
protected void doInit() {
}
@Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
return null;
}
/**
* 发送短信V2版本
* 该方法用于发送短信,根据提供的日志ID、手机号码、短信模板和模板参数进行短信发送
* 主要逻辑包括处理模板参数、调用HTTP工具发送短信以及解析响应结果
*
* @param logId 日志ID,用于追踪和记录发送短信的日志
* @param mobile 手机号码,指定短信接收者的电话号码
* @param smsTemplateDTO 短信模板数据传输对象,包含短信模板的相关信息
* @param templateParams 模板参数列表,包含短信模板中的变量和对应的值
* @return 返回一个通用的结果对象,包含短信发送的相关信息和结果状态
*/
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
// 默认验证码为空字符串
String code = "";
// 如果模板参数不为空,则遍历模板参数以获取验证码
if (templateParams != null) {
for (KeyValue<String, Object> entry : templateParams) {
code = (String) entry.getValue();
}
}
// 发送短信并获取响应
Response response = yCloudWhatsappHttp.postWhatsapp(yCloudWhatsappHttp.setHeader(properties.getApiSecret()),
yCloudWhatsappHttp.setParams(code, smsTemplateDTO.getApiTemplateId(), smsTemplateDTO.getLanguage(), mobile));
// 解析结果
Map<?, ?> responseObj = JSON.parseObject(response.getBody(), Map.class);
// 构建短信发送结果对象
SmsCommonResult<SmsSendRespDTO> smsCommonResult = SmsCommonResult.build(String.valueOf(responseObj.get("status")), String.valueOf(responseObj.get("status")), String.valueOf(responseObj.get("id")), null, codeMapping);
// 设置短信发送结果的详细信息
smsCommonResult.setData(new SmsSendRespDTO().setSerialNo(String.valueOf(responseObj.get("id"))));
// 返回短信发送结果
return smsCommonResult;
}
/**
* 获取短信接收状态
* 本方法通过调用第三方API来获取短信的实际接收状态,并更新短信日志DTO中的相关信息
*
* @param smsLogDO 短信日志DTO,包含需要查询接收状态的短信的相关信息
* @return 返回更新了接收状态和接收时间的短信日志DTO
*/
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
// 初始化短信日志DTO,并默认设置接收状态为未接收到
SmsLogDTO smsLogDTO = new SmsLogDTO();
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
try {
// 发送短信并获取响应
Response response = yCloudWhatsappHttp.getReceiveStatus(yCloudWhatsappHttp.setHeader(properties.getApiSecret()), smsLogDO.getApiSerialNo());
// 解析响应内容为对象,以便后续处理
SmsReceiveStatus smsReceiveStatus = JSON.parseObject(response.getBody(), SmsReceiveStatus.class);
// 获取短信状态
if (smsReceiveStatus == null) {
return smsLogDTO;
}
String status = smsReceiveStatus.getStatus();
// 如果短信状态为已送达或已读取,则进一步处理
if (YCloudReceiveStatusEnum.DELIVERED.getCode().equals(status) || YCloudReceiveStatusEnum.READ.getCode().equals(status)) {
// 获取送达时间
String deliverTime = smsReceiveStatus.getDeliverTime();
// 解析并转换送达时间为本地时间
Date localDate = DateUtils.parseAndConvertToLocalDate(deliverTime);
// 更新短信日志DTO的接收时间和接收状态
smsLogDTO.setReceiveTime(localDate);
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_10.getValue());
}
} catch (Exception e) {
// 记录异常信息
log.error("Error occurred while getting receive status: ", e);
// 保持默认的接收状态为未接收到
smsLogDTO.setReceiveStatus(ReceiveStatusEnum.Receive_STATUS_20.getValue());
}
// 返回更新后的短信日志DTO
return smsLogDTO;
}
@Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) {
throw new UnsupportedOperationException("无回调");
}
@Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
return SmsCommonResult.build("accepted", "accepted", null, data, codeMapping);
}
/**
* 短信接收状态
* <p>
*
* @author 捷道源码
*/
@Data
public static class SmsReceiveStatus {
/**
* status
*/
@JsonProperty("status")
private String status;
/**
* reference
*/
@JsonProperty("deliverTime")
private String deliverTime;
}
}
package cn.iocoder.yudao.framework.sms.core.client.impl.yCloud;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
/**
* ycloud SmsCodeMapping 实现类
*
* @author Jayden
* @date 2024/11/7
*/
public class YCloudSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
switch (apiCode) {
case "accepted":
case "read":
case "send":
case "delivered":
return GlobalErrorCodeConstants.SUCCESS;
}
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}
......@@ -6,9 +6,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.iocoder.yudao.framework.common.util.json.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.*;
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
......@@ -148,6 +146,16 @@ public class YunpianSmsClient extends AbstractSmsClient {
return sendResult.getMsg() + " => " + sendResult.getDetail();
}
@Override
public SmsCommonResult<SmsSendRespDTO> sendSmsV2(Long logId, String mobile, SmsTemplateDTO smsTemplateDTO, List<KeyValue<String, Object>> templateParams) {
return null;
}
@Override
public SmsLogDTO getReceiveStatus(SmsLogDTO smsLogDO) {
return null;
}
/**
* 短信接收状态
*
......
package cn.iocoder.yudao.framework.sms.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 短信接收状态
*
* @author jayden
*/
@Getter
@AllArgsConstructor
public enum ReceiveStatusEnum {
RECEIVE_STATUS_0("等待结果", 0),
Receive_STATUS_10("接收成功", 10),
Receive_STATUS_20("接收失败", 20);
private final String name;
private final Integer value;
}
package cn.iocoder.yudao.framework.sms.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 返回状态
* @author Jayden
* @date 2024/11/23
*/
@Getter
@AllArgsConstructor
public enum SendChampReceiveStatusEnum {
SENT("sent","The message has been sent successfully."),
DELIVERED("delivered","The message has been delivered to the customer."),
PENDING("pending","The message has been sent to a downstream mobile operator and provider and we are waiting for Delivery Report to be returned."),
FAILED("failed","The message couldn't be sent and delivered. More detail can be found in the reason and the error code."),
LOW_FUNDS("low_funds","The message failed due to insufficient funds."),
REJECTED("rejected","The message has been rejected, this is usually due to malicious requests."),
EXPIRED("expired","The message session has expired.");
private final String code;
private final String description;
}
......@@ -17,6 +17,9 @@ public enum SmsChannelEnum {
DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"),
YUN_PIAN("YUN_PIAN", "云片"),
ALIYUN("ALIYUN", "阿里云"),
SENDCHAMP("SENDCHAMP", "Sendchamp"),
YCLOUD("YCLOUD", "Ycloud"),
BULK("BULK", "bulk")
// TENCENT("TENCENT", "腾讯云"),
// HUA_WEI("HUA_WEI", "华为云"),
;
......@@ -32,7 +35,9 @@ public enum SmsChannelEnum {
public static SmsChannelEnum getByCode(String code) {
SmsChannelEnum channelEnum = ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
if (null == channelEnum) { return DEBUG_DING_TALK; }
if (null == channelEnum) {
return DEBUG_DING_TALK;
}
return channelEnum;
}
......
package cn.iocoder.yudao.framework.sms.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 返回状态
*
* @author Jayden
* @date 2024/11/23
*/
@Getter
@AllArgsConstructor
public enum YCloudReceiveStatusEnum {
ACCEPTED("accepted", "The messaging request is accepted by our system."),
FAILED("failed", "A message sent by your business failed to send."),
SENT("sent", "A message sent by your business is in transit within WhatsApp's systems."),
DELIVERED("delivered", "A message sent by your business was delivered to the user's device."),
READ("read", "A message sent by your business was read by the user.");
private final String code;
private final String description;
}
package cn.iocoder.yudao.framework.apollo.core.constants;
/**
* 缓存的key 常量
*
* @author Jayden
* @date 2024/11/6
*/
public class CacheConstants {
public static final String COLON = ":";
/**
* 短信节点表缓存
*/
public static final String SYSTEM_SMS_NODE_KEY = "system_sms_node:";
/**
* 短信节点表缓存
*/
public static final String SYSTEM_SMS_TEMPLATE_KEY = "system_sms_template:";
}
......@@ -21,4 +21,14 @@ public class Constants {
* 不接收货物code编码常量配置
*/
public static final String NOT_ACCEPTED_PROD_CODE = "F";
/**
* 目的仓不选择key
*/
public static final String RECEIVE_ADDR_KEY = "1:1:1";
/**
* 目的仓全选key
*/
public static final String RECEIVE_ADDR_ALL_KEY = "0:0:0";
}
......@@ -101,4 +101,9 @@ public class QueryChannelInfoEvent {
* 泡重比例
*/
private BigDecimal bubbleWeightRatio;
/**
* 免泡重量
*/
private BigDecimal bubbleExemptWeight;
}
......@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.customer.dal.dataobject.customer;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.customer.vo.customerFollowup.CustomerFollowupBackVO;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
......@@ -148,6 +149,7 @@ public class CustomerDO extends BaseDO {
* <p>
* 枚举 {@link TODO common_status 对应的类}
*/
@TableField( updateStrategy = FieldStrategy.IGNORED)
private Long customerService;
......
package cn.iocoder.yudao.module.customer.service.api;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.module.customer.dal.dataobject.customer.CustomerDO;
import cn.iocoder.yudao.module.customer.dal.dataobject.customerContacts.CustomerContactsDO;
import cn.iocoder.yudao.module.customer.service.customer.CustomerService;
......@@ -32,10 +34,10 @@ public class CustomerApiImpl implements CustomerApi {
@Resource
private CountryService countryService;
public void associateCustomerAuto(String areaCode, String phone, Long memberUserId, String newName ,String memberCode) {
public void associateCustomerAuto(String areaCode, String phone, Long memberUserId, String newName, String memberCode) {
//先查询此用户是否有关联老客户
CustomerContactsDO customerContactsDO = customerContactsService.getUniqueOneAndValidate(areaCode, phone, null);
CustomerContactsDO customerContactsDO = customerContactsService.getUniqueOneAndValidate(areaCode, phone, null);
if (customerContactsDO != null) {
//若有关联,直接更新userId
customerContactsDO.setUserid(memberUserId);
......@@ -95,6 +97,68 @@ public class CustomerApiImpl implements CustomerApi {
}
public void associateCargoCustomerAuto(String destTitleEn,Integer transTypeId,String areaCode, String phone, String newName, String email) {
//先查询此用户是否有关联老客户
CustomerContactsDO customerContactsDO = customerContactsService.getUniqueOneAndValidate(areaCode, phone, null);
if (customerContactsDO == null) {
CustomerCreateReqVO customerCreateReqVO = new CustomerCreateReqVO();
DictDataRespDTO transPortTypeDictDataRespDTO = DictFrameworkUtils.getDictDataFromCache("transport_type", String.valueOf(transTypeId));
DictDataRespDTO customertTransPortTypeDictDataRespDTO = DictFrameworkUtils.parseDictDataFromCache("customer_transport_type", transPortTypeDictDataRespDTO.getLabel());
if (customertTransPortTypeDictDataRespDTO!=null){
customerCreateReqVO.setTransportType(customertTransPortTypeDictDataRespDTO.getValue());
}
DictDataRespDTO pickupPointDictDataRespDTO = DictFrameworkUtils.parseDictDataFromCache("pickup_points", destTitleEn);
if (pickupPointDictDataRespDTO!=null){
customerCreateReqVO.setPickupPoints(pickupPointDictDataRespDTO.getValue());
}
//初始化名字为手机号
customerCreateReqVO.setName(newName);
customerCreateReqVO.setNameEn(newName);
//普通
customerCreateReqVO.setLevel(1);
//会员注册
customerCreateReqVO.setSource(101);
//注册会员是公司资源
customerCreateReqVO.setResourceType(2);
//线索客户
customerCreateReqVO.setStatus(CustomerStatusEnum.CLUE.getValue());
//更新 状态更新时间
customerCreateReqVO.setStatusUpdateTime(new Date());
//注册时自动创建
customerCreateReqVO.setIsAuto(1);
//客户类型 默认为收货人
customerCreateReqVO.setType("1");
//国家
CountryDO countryDO = countryService.getCountry(areaCode);
if (countryDO != null) {
customerCreateReqVO.setCountry(countryDO.getId());
}
//添加联系人
CustomerContactsCreateReqVO customerContactsCreateReqVO = new CustomerContactsCreateReqVO();
customerContactsCreateReqVO.setName(newName);
customerContactsCreateReqVO.setNameEn(newName);
//默认联系人
customerContactsCreateReqVO.setIsDefault(1);
customerContactsCreateReqVO.setAreaCode(areaCode);
customerContactsCreateReqVO.setPhoneNew(phone);
customerContactsCreateReqVO.setEmail(email);
customerCreateReqVO.setCustomerContacts(Arrays.asList(customerContactsCreateReqVO));
customerCreateReqVO.setCustomerOperateLogRemark("控货收货人创建. 会员名称:" + newName + "; 电话:" + phone);
// 控货收货人创建
customerCreateReqVO.setCreateFrom(CustomerCreateFromEnum.CARGO_CONSIGNOR.getValue());
customerService.createCustomer(customerCreateReqVO);
}
}
@Override
public void approvalCustomerDelay(String approveId, Integer result) {
customerService.approvalCustomerDelay(approveId, result);
......
......@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.customer.service.customer.complaint;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.i18n.core.LangEnum;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
......@@ -92,8 +93,12 @@ public class CustomerComplaintServiceImpl extends AbstractService<CustomerCompla
InternalMessageCreateDto dto = InternalMessageCreateDto.builder().toIdList(Arrays.asList(updateReqVO.getCustomerId()))
.fromId("")
.sendTime(new Date())
.title(I18nMessage.getMessage("app.message.customer.complain.title"))
.content(I18nMessage.getMessage("app.message.customer.complain.content"))
.title(I18nMessage.getMessage("app.message.customer.complain.title", LangEnum.ZH.getLang()))
.titleEn(I18nMessage.getMessage("app.message.customer.complain.title", LangEnum.EN.getLang()))
.titleFr(I18nMessage.getMessage("app.message.customer.complain.title", LangEnum.FR.getLang()))
.content(I18nMessage.getMessage("app.message.customer.complain.content", LangEnum.ZH.getLang()))
.contentEn(I18nMessage.getMessage("app.message.customer.complain.content", LangEnum.EN.getLang()))
.contentFr(I18nMessage.getMessage("app.message.customer.complain.content", LangEnum.FR.getLang()))
.type(3).build();
clientInternalMessageApi.createInternalMessage(dto);
}
......
......@@ -80,6 +80,8 @@
<result column="vip_level_name_en" jdbcType="VARCHAR" property="vipLevelNameEn"/>
<result column="busi_country_name_zh" jdbcType="VARCHAR" property="busiCountryNameZh"/>
<result column="busi_country_name_en" jdbcType="VARCHAR" property="busiCountryNameEn"/>
<result column="country_name_zh" jdbcType="VARCHAR" property="countryNameZh"/>
<result column="country_name_en" jdbcType="VARCHAR" property="countryNameEn"/>
<result column="competitor_names" jdbcType="VARCHAR" property="competitorNames"/>
<result column="creator_name" jdbcType="VARCHAR" property="creatorName"/>
<result column="updater_name" jdbcType="VARCHAR" property="updaterName"/>
......
......@@ -57,6 +57,10 @@ public class WarehouseDO extends BaseDO implements Serializable {
* 英文名称
*/
private String titleEn;
/**
* 英文名称
*/
private String titleFr;
/**
* 容量
*/
......@@ -69,6 +73,10 @@ public class WarehouseDO extends BaseDO implements Serializable {
* 英文地址
*/
private String addressEn;
/**
* 英文地址
*/
private String addressFr;
/**
* 洲
*/
......
......@@ -100,10 +100,14 @@ public class WarehouseLineDO extends BaseDO {
@TableField(exist = false)
private String startTitleEn;
@TableField(exist = false)
private String startTitleFr;
@TableField(exist = false)
private String destTitleZh;
@TableField(exist = false)
private String destTitleEn;
@TableField(exist = false)
private String destTitleFr;
@TableField(exist = false)
private Long startCityId;
@TableField(exist = false)
private Long destCityId;
......
......@@ -68,14 +68,17 @@ public interface WarehouseLineMapper extends BaseMapperX<WarehouseLineDO> {
"ew_start.guojia as start_country_id,",
"ew_start.title_zh as start_title_zh,",
"ew_start.title_en as start_title_en,",
"ew_start.title_fr as start_title_fr,",
"ew_start.qianzhui as start_qianzhui,",
"ew_start_country.title_zh as start_country_title_zh,",
"ew_start_country.title_en as start_country_title_en,",
"ew_start_country.title_fr as start_country_title_fr,",
"ew_start.volume as start_volume,",
"ew_start.rent_free_days as start_rent_free_days,",
"ew_start.lock_recipient_days as start_lock_recipient_days,",
"ew_start.address_zh as start_address_zh,",
"ew_start.address_en as start_address_en,",
"ew_start.address_fr as start_address_fr,",
"ew_start.head as start_head,",
"ew_start.tell as start_tell,",
"ew_start.kyc_status as kycStatus,",
......@@ -83,16 +86,20 @@ public interface WarehouseLineMapper extends BaseMapperX<WarehouseLineDO> {
"ew_dest.guojia as dest_country_id,",
"ew_dest.title_zh as dest_title_zh,",
"ew_dest.title_en as dest_title_en,",
"ew_dest.title_fr as dest_title_fr,",
"ew_dest.qianzhui as dest_qianzhui,",
"ew_dest_country.title_zh as dest_country_title_zh,",
"ew_dest_country.title_en as dest_country_title_en,",
"ew_dest_country.title_fr as dest_country_title_fr,",
"ew_dest_city.title_zh as dest_city_title_zh,",
"ew_dest_city.title_en as dest_city_title_en,",
"ew_dest_city.title_fr as dest_city_title_fr,",
"ew_dest.volume as dest_volume,",
"ew_dest.rent_free_days as dest_rent_free_days,",
"ew_dest.lock_recipient_days as dest_lock_recipient_days,",
"ew_dest.address_zh as dest_address_zh,",
"ew_dest.address_en as dest_address_en,",
"ew_dest.address_fr as dest_address_fr,",
"ew_dest.head as dest_head,",
"ew_dest.tell as dest_tell",
"FROM",
......
......@@ -33,6 +33,8 @@ public interface WarehouseMapper extends BaseMapperX<WarehouseDO> {
.like(WarehouseDO::getTitleZh, reqVO.getKeywords())
.or()
.like(WarehouseDO::getTitleEn, reqVO.getKeywords())
.or()
.like(WarehouseDO::getTitleFr, reqVO.getKeywords())
)
.orderByDesc(WarehouseDO::getId));
}
......@@ -142,14 +144,17 @@ public interface WarehouseMapper extends BaseMapperX<WarehouseDO> {
"w.id as warehouseId,",
"w.title_zh as warehouseTitleZh,",
"w.title_en as warehouseTitleEn,",
"w.title_fr as warehouseTitleFr,",
"r1.id as guojia,",
"r1.sort as guojiaSort,",
"r1.title_zh as guojiaName,",
"r1.title_en as guojiaNameEn,",
"r1.title_fr as guojiaNameFr,",
"r2.id as shi, ",
"r2.sort as shiSort, ",
"r2.title_zh as shiName, ",
"r2.title_en as shiNameEn ",
"r2.title_en as shiNameEn, ",
"r2.title_fr as shiNameFr ",
"FROM ecw_warehouse w ",
"LEFT JOIN ecw_region r1 ",
"ON w.guojia = r1.id ",
......
......@@ -23,6 +23,9 @@ public class LogisticsInfoDto {
@ApiModelProperty(value = "始发地仓库英文名称")
private String startTitleEn;
@ApiModelProperty(value = "始发地仓库法文名称")
private String startTitleFr;
@ApiModelProperty(value = "始发地仓库前缀简称")
private String startQianzhui;
......@@ -32,6 +35,9 @@ public class LogisticsInfoDto {
@ApiModelProperty(value = "始发地国家英文名称")
private String startCountryTitleEn;
@ApiModelProperty(value = "始发地国家法文名称")
private String startCountryTitleFr;
// @ApiModelProperty(value = "始发地城市中文名称")
// private String startCityTitleZh;
//
......@@ -53,6 +59,9 @@ public class LogisticsInfoDto {
@ApiModelProperty(value = "始发地仓库英文地址")
private String startAddressEn;
@ApiModelProperty(value = "始发地仓库法文地址")
private String startAddressFr;
@ApiModelProperty(value = "始发地仓库负责人")
private String startHead;
......@@ -73,6 +82,10 @@ public class LogisticsInfoDto {
@ApiModelProperty(value = "目的地仓库英文名称")
private String destTitleEn;
@ApiModelProperty(value = "目的地仓库法文名称")
private String destTitleFr;
@ApiModelProperty(value = "目的地仓库前缀简称")
private String destQianzhui;
......@@ -82,12 +95,18 @@ public class LogisticsInfoDto {
@ApiModelProperty(value = "目的地国家英文名称")
private String destCountryTitleEn;
@ApiModelProperty(value = "目的地国家法文名称")
private String destCountryTitleFr;
@ApiModelProperty(value = "目的地城市中文名称")
private String destCityTitleZh;
@ApiModelProperty(value = "目的地城市英文名称")
private String destCityTitleEn;
@ApiModelProperty(value = "目的地城市法文名称")
private String destCityTitleFr;
@ApiModelProperty(value = "目的地仓库容量")
private String destVolume;
......@@ -103,6 +122,9 @@ public class LogisticsInfoDto {
@ApiModelProperty(value = "目的地仓库英文地址")
private String destAddressEn;
@ApiModelProperty(value = "目的地仓库法文地址")
private String destAddressFr;
@ApiModelProperty(value = "目的地仓库负责人")
private String destHead;
......
......@@ -564,7 +564,7 @@ public class WarehouseServiceImpl implements WarehouseService {
}
@Override
@Cacheable(cacheNames = "jd:warehouse:list", key = "#tradeType")
// @Cacheable(cacheNames = "jd:warehouse:list", key = "#tradeType")
public WarehouseListVO getGuojiaAndShiAndWarehouseList(Integer tradeType) {
WarehouseListVO vo = new WarehouseListVO();
List<WarehouseTreeVO> list = warehouseMapper.getWarehouseTreeList(tradeType);
......@@ -580,7 +580,9 @@ public class WarehouseServiceImpl implements WarehouseService {
v.setShiSort(warehouseTreeVO.getShiSort());
v.setShiName(warehouseTreeVO.getShiName());
v.setShiNameEn(warehouseTreeVO.getShiNameEn());
v.setShiNameFr(warehouseTreeVO.getShiNameFr());
v.setGuojiaNameEn(warehouseTreeVO.getGuojiaNameEn());
v.setGuojiaNameFr(warehouseTreeVO.getGuojiaNameFr());
v.setGuojiaName(warehouseTreeVO.getGuojiaName());
v.setGuojia(warehouseTreeVO.getGuojia());
v.setGuojiaSort(warehouseTreeVO.getGuojiaSort());
......@@ -596,6 +598,7 @@ public class WarehouseServiceImpl implements WarehouseService {
List<WarehouseTreeVO> shiWarehouseList = entry.getValue();
WarehouseTreeVO warehouseTreeVO = shiWarehouseList.get(0);
v.setGuojiaNameEn(warehouseTreeVO.getGuojiaNameEn());
v.setGuojiaNameFr(warehouseTreeVO.getGuojiaNameFr());
v.setGuojiaName(warehouseTreeVO.getGuojiaName());
v.setGuojiaSort(warehouseTreeVO.getGuojiaSort());
guojiaList.add(v);
......
......@@ -32,6 +32,9 @@ public class WarehouseBaseVO {
@ApiModelProperty(value = "英文名称")
private String titleEn;
@ApiModelProperty(value = "法文名称")
private String titleFr;
@ApiModelProperty(value = "容量")
private String volume;
......@@ -41,6 +44,9 @@ public class WarehouseBaseVO {
@ApiModelProperty(value = "英文地址")
private String addressEn;
@ApiModelProperty(value = "法文地址")
private String addressFr;
@ApiModelProperty(value = "洲")
private Long zhou;
......
......@@ -18,6 +18,8 @@ public class WarehouseTreeVO {
private String guojiaName ;
@ApiModelProperty("国家英文名称")
private String guojiaNameEn ;
@ApiModelProperty("国家法文名称")
private String guojiaNameFr ;
@ApiModelProperty("城市区域id")
private Long shi;
@ApiModelProperty("城市排序")
......@@ -26,13 +28,16 @@ public class WarehouseTreeVO {
private String shiName;
@ApiModelProperty("城市英文名称")
private String shiNameEn;
@ApiModelProperty("城市法文名称")
private String shiNameFr;
@ApiModelProperty("仓库id")
private Long warehouseId;
@ApiModelProperty("仓库中文名称")
private String warehouseTitleZh;
@ApiModelProperty("仓库英文名称")
private String warehouseTitleEn;
@ApiModelProperty("仓库法文名称")
private String warehouseTitleFr;
@ApiModelProperty("排序")
private Integer sort;
......
......@@ -13,8 +13,10 @@
<result property="destQianzhui" column="dest_qianzhui" />
<result property="startTitleZh" column="start_title_zh" />
<result property="startTitleEn" column="start_title_en" />
<result property="startTitleFr" column="start_title_fr" />
<result property="destTitleZh" column="dest_title_zh" />
<result property="destTitleEn" column="dest_title_en" />
<result property="destTitleFr" column="dest_title_fr" />
</resultMap>
<select id="openedRouterList" resultMap="warehouseLineEntity">
......@@ -22,10 +24,12 @@
ewl.*,
ew_start.title_zh start_title_zh,
ew_start.title_en start_title_en,
ew_start.title_fr start_title_fr,
ew_start.qianzhui start_qianzhui,
ew_dest.qianzhui dest_qianzhui,
ew_dest.title_zh dest_title_zh,
ew_dest.title_en dest_title_en,
ew_dest.title_fr dest_title_fr,
ew_start.shi start_city_id,
ew_dest.shi dest_city_id,
ew_start.guojia start_country_id,
......
......@@ -28,11 +28,19 @@ public class InternalMessageCreateDto {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date sendTime;
@ApiModelProperty(value = "标题")
@ApiModelProperty(value = "标题中文")
private String title;
@ApiModelProperty(value = "标题英文")
private String titleEn;
@ApiModelProperty(value = "标题法文")
private String titleFr;
@ApiModelProperty(value = "内容")
@ApiModelProperty(value = "内容中文")
private String content;
@ApiModelProperty(value = "内容英文")
private String contentEn;
@ApiModelProperty(value = "内容法文")
private String contentFr;
@ApiModelProperty(value = "1订单消息,2出货消息,3提醒消息")
private Integer type;
......
......@@ -17,7 +17,8 @@ public enum CustomerCreateFromEnum {
IMPORT(1, "导入"),
BACKEND(2, "后台用户创建"),
APP_REGISTER(3, "APP注册"),
APP_CONSIGNOR(4, "APP发货人创建");
APP_CONSIGNOR(4, "APP发货人创建"),
CARGO_CONSIGNOR(5, "控货收货人创建");
;
/**
......
......@@ -69,7 +69,7 @@ public enum CustomerOperateTypeEnum {
CUSTOMER_MERGE(33, "客户合并"),
VERIFICATION_CODE(34, "验证码发送"),
DELETE(100, "删除"),
......
......@@ -19,6 +19,10 @@ public class RegionBaseVO {
@NotNull(message = "地区英文不能为空")
private String titleEn;
@ApiModelProperty(value = "地区法文", required = true)
@NotNull(message = "地区法文不能为空")
private String titleFr;
@ApiModelProperty(value = "简称", required = true)
@NotNull(message = "简称不能为空")
private String shortName;
......
......@@ -14,6 +14,9 @@ public class RegionListReqVO {
@ApiModelProperty(value = "地区英文")
private String titleEn;
@ApiModelProperty(value = "地区法文")
private String titleFr;
@ApiModelProperty(value = "简称")
private String shortName;
......
......@@ -16,6 +16,9 @@ public class UnitBaseVO {
@ApiModelProperty(value = "英文名称")
private String titleEn;
@ApiModelProperty(value = "法文名称")
private String titleFr;
@ApiModelProperty(value = "单位符号")
private String fuhao;
......
......@@ -20,6 +20,9 @@ public class UnitPageReqVO extends PageParam {
@ApiModelProperty(value = "英文名称")
private String titleEn;
@ApiModelProperty(value = "法文名称")
private String titleFr;
@ApiModelProperty(value = "单位符号")
private String fuhao;
......
......@@ -31,6 +31,10 @@ public class CountryDO extends BaseDO {
* 国家英文名称
*/
private String nameEn;
/**
* 国家法文名称
*/
private String nameFr;
/**
* 简称
*/
......
......@@ -22,7 +22,7 @@ import java.util.Date;
public class WebInternalMessageDO extends BaseDO {
/**
*
*
*/
@TableId
private Long id;
......@@ -35,13 +35,29 @@ public class WebInternalMessageDO extends BaseDO {
*/
private Date sendTime;
/**
* 标题
* 标题中文
*/
private String title;
/**
* 内容
* 标题英文
*/
private String titleEn;
/**
* 标题法文
*/
private String titleFr;
/**
* 内容中文
*/
private String content;
/**
* 内容英文
*/
private String contentEn;
/**
* 内容法文
*/
private String contentFr;
/**
* 1订单消息,2出货消息,3提醒消息
*/
......
......@@ -31,6 +31,10 @@ public class RegionDO extends BaseDO {
* 地区英文
*/
private String titleEn;
/**
* 地区法文
*/
private String titleFr;
/**
* 简称
*/
......
......@@ -19,7 +19,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
public class UnitDO extends BaseDO {
/**
*
*
*/
@TableId
private Integer id;
......@@ -31,6 +31,10 @@ public class UnitDO extends BaseDO {
* 英文名称
*/
private String titleEn;
/**
* 法文名称
*/
private String titleFr;
/**
* 单位符号
*/
......
......@@ -38,6 +38,7 @@ public interface CountryMapper extends AbstractMapper<CountryDO> {
return selectList(new LambdaQuery<CountryDO>()
.likeIfPresent(CountryDO::getNameZh, vo.getNameZh())
.likeIfPresent(CountryDO::getNameEn, vo.getNameEn())
.likeIfPresent(CountryDO::getNameFr, vo.getNameFr())
.likeIfPresent(CountryDO::getNameShort, vo.getNameShort())
.likeIfPresent(CountryDO::getTel, vo.getTel())
.betweenIfPresent(CountryDO::getCreateTime, vo.getBeginCreateTime(), vo.getEndCreateTime())
......
......@@ -52,6 +52,7 @@ public interface RegionMapper extends BaseMapperX<RegionDO> {
return selectList(new LambdaQueryWrapperX<RegionDO>()
.likeIfPresent(RegionDO::getTitleZh, reqVO.getTitleZh())
.likeIfPresent(RegionDO::getTitleEn, reqVO.getTitleEn())
.likeIfPresent(RegionDO::getTitleFr, reqVO.getTitleFr())
.likeIfPresent(RegionDO::getLanguage, reqVO.getLanguage())
.likeIfPresent(RegionDO::getType, reqVO.getType())
.eqIfPresent(RegionDO::getStatus, reqVO.getStatus()));
......
......@@ -21,6 +21,7 @@ public interface UnitMapper extends BaseMapperX<UnitDO> {
return selectPage(reqVO, new LambdaQueryWrapperX<UnitDO>()
.eqIfPresent(UnitDO::getTitleZh, reqVO.getTitleZh())
.eqIfPresent(UnitDO::getTitleEn, reqVO.getTitleEn())
.eqIfPresent(UnitDO::getTitleFr, reqVO.getTitleFr())
.eqIfPresent(UnitDO::getFuhao, reqVO.getFuhao())
.eqIfPresent(UnitDO::getStatus, reqVO.getStatus())
.eqIfPresent(UnitDO::getAorder, reqVO.getAorder())
......
......@@ -30,6 +30,10 @@ public class CountryBackVO {
@ApiModelProperty(value = "国家英文名称", required = true)
private String nameEn;
@ExcelProperty("国家法文名称")
@ApiModelProperty(value = "国家法文名称", required = true)
private String nameFr;
@ExcelProperty("简称")
@ApiModelProperty(value = "简称")
private String nameShort;
......
......@@ -17,6 +17,9 @@ public class CountryQueryVO {
@ApiModelProperty(value = "国家英文名称")
private String nameEn;
@ApiModelProperty(value = "国家法文名称")
private String nameFr;
@ApiModelProperty(value = "简称")
private String nameShort;
......
......@@ -32,14 +32,30 @@ public class InternalMessageBackVO {
@ApiModelProperty(value = "发送时间")
private Date sendTime;
@ExcelProperty("标题")
@ApiModelProperty(value = "标题")
@ExcelProperty("标题中文")
@ApiModelProperty(value = "标题中文")
private String title;
@ExcelProperty("内容")
@ApiModelProperty(value = "内容")
@ExcelProperty("标题英文")
@ApiModelProperty(value = "标题英文")
private String titleEn;
@ExcelProperty("标题法文")
@ApiModelProperty(value = "标题法文")
private String titleFr;
@ExcelProperty("内容中文")
@ApiModelProperty(value = "内容中文")
private String content;
@ExcelProperty("内容英文")
@ApiModelProperty(value = "内容英文")
private String contentEn;
@ExcelProperty("内容法文")
@ApiModelProperty(value = "内容法文")
private String contentFr;
@ExcelProperty("1订单消息,2出货消息,3提醒消息")
@ApiModelProperty(value = "1订单消息,2出货消息,3提醒消息")
private Integer type;
......
......@@ -24,12 +24,24 @@ public class InternalMessageBaseVO {
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private Date sendTime;
@ApiModelProperty(value = "标题")
@ApiModelProperty(value = "标题中文")
private String title;
@ApiModelProperty(value = "内容")
@ApiModelProperty(value = "标题英文")
private String titleEn;
@ApiModelProperty(value = "标题法文")
private String titleFr;
@ApiModelProperty(value = "内容中文")
private String content;
@ApiModelProperty(value = "内容英文")
private String contentEn;
@ApiModelProperty(value = "内容法文")
private String contentFr;
@ApiModelProperty(value = "1订单消息,2出货消息,3提醒消息")
private Integer type;
......
......@@ -24,12 +24,24 @@ public class InternalMessageQueryVO {
@ApiModelProperty(value = "结束发送时间")
private Date endSendTime;
@ApiModelProperty(value = "标题")
@ApiModelProperty(value = "标题中文")
private String title;
@ApiModelProperty(value = "内容")
@ApiModelProperty(value = "标题英文")
private String titleEn;
@ApiModelProperty(value = "标题法文")
private String titleFr;
@ApiModelProperty(value = "内容中文")
private String content;
@ApiModelProperty(value = "内容英文")
private String contentEn;
@ApiModelProperty(value = "内容法文")
private String contentFr;
@ApiModelProperty(value = "1订单消息,2出货消息,3提醒消息")
private Integer type;
......
......@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.api.user;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.member.api.user.dto.UserSelectReqVO;
import cn.iocoder.yudao.module.member.enums.UserOperationLogTypeEnum;
/**
* 会员用户的 API 接口
......@@ -27,4 +28,6 @@ public interface MemberUserApi {
UserRespDTO getUserByMobile(String mobile);
void createUserOperationLog(String mobile, UserOperationLogTypeEnum typeEnum, String content);
}
package cn.iocoder.yudao.module.member.enums;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author Smile
*/
@AllArgsConstructor
@Getter
public enum CustomerSideEnum {
CONSIGNOR(1, "发货人","shipper"),
CONSIGNEE(2, "收货人", "consignee"),
;
private final Integer value;
private final String name;
private final String nameEn;
public static CustomerSideEnum valueOf(Integer value) {
return ArrayUtil.firstMatch(customerSide-> customerSide.getValue().equals(value) , CustomerSideEnum.values());
}
}
......@@ -70,4 +70,5 @@ public interface ErrorCodeConstants {
ErrorCode USER_ADDRESS_NOT_EXISTS = new ErrorCode(1004008026, "user.address.not.exists");
ErrorCode USER_ADDRESS_FIELD_ERROR = new ErrorCode(1004008027, "user.address.field.error");
ErrorCode SCORE_RULE_CUSTOMERSIDE_ERROR = new ErrorCode(1004008028, "score.rule.customer.side.error");
}
......@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.enums;
import java.util.stream.Stream;
public enum TransportTypeEnum {
OTHER(0, "其他"),
OCEAN_LCL(1, "海运拼柜"),
SPECIAL_LINE_AIR_FREIGHT(3, "专线空运");
......@@ -22,6 +23,7 @@ public enum TransportTypeEnum {
public String getName() {
return name;
}
public static TransportTypeEnum parseByValue(int value) {
return Stream.of(values()).filter(e -> e.getValue() == value).findAny().orElse(null);
}
......
......@@ -13,9 +13,12 @@ public enum UserOperationLogTypeEnum {
LOGIN(1, "登录"),
AUTHENTIFICATION(2, "实名认证"),
AUDIT(3, "认证审核"),
REGIST(4, "注册");
REGIST(4, "注册"),
RELEASE(5, "修改放货密码"),
EXCHANGE(6, "兑换礼品"),
SEND_CODE(7, "发送验证码");
private final Integer value;
private final Integer value;
private final String desc;
}
package cn.iocoder.yudao.module.member.enums;
public enum YesOrNoOrAllTypeEnum {
YES,
NO,
ALL;
}
package cn.iocoder.yudao.module.member.api.user;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.customer.dal.dataobject.customer.CustomerDO;
import cn.iocoder.yudao.module.customer.dal.mysql.customer.CustomerMapper;
import cn.iocoder.yudao.module.customer.service.customerOperateLog.CustomerOperateLogService;
import cn.iocoder.yudao.module.customer.vo.customerOperateLog.CustomerOperateLogCreateReqVO;
import cn.iocoder.yudao.module.ecw.enums.CustomerOperateTypeEnum;
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.member.api.user.dto.UserSelectReqVO;
import cn.iocoder.yudao.module.member.convert.user.UserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.enums.UserOperationLogTypeEnum;
import cn.iocoder.yudao.module.member.service.auth.MemberAuthService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import cn.iocoder.yudao.module.member.service.userOperationLog.UserOperationLogService;
import cn.iocoder.yudao.module.member.vo.userOperationLog.UserOperationLogCreateReqVO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
/**
* 会员用户的 API 实现类
*
......@@ -23,6 +34,18 @@ public class MemberUserApiImpl implements MemberUserApi {
@Resource
private MemberUserService userService;
@Resource
private MemberAuthService memberAuthService;
@Resource
private UserOperationLogService userOperationLogService;
@Resource
private CustomerMapper customerMapper;
@Resource
private CustomerOperateLogService customerOperateLogService;
@Override
public UserRespDTO getUser(Long id) {
MemberUserDO user = userService.info(id);
......@@ -35,6 +58,33 @@ public class MemberUserApiImpl implements MemberUserApi {
return UserConvert.INSTANCE.convert2(user);
}
@Override
public void createUserOperationLog(String mobile, UserOperationLogTypeEnum typeEnum, String content) {
// 检验用户是否存在
MemberUserDO userDO = memberAuthService.checkUserIfExists(mobile);
//记录日志
UserOperationLogCreateReqVO userOperationLogCreateReqVO = new UserOperationLogCreateReqVO();
userOperationLogCreateReqVO
.setUserId(userDO.getId())
.setType(typeEnum.getValue())
.setTitle(typeEnum.getDesc())
.setIp(getClientIP())
.setContent(content);
userOperationLogService.createUserOperationLog(userOperationLogCreateReqVO);
//通过memberUserId获取Customer
CustomerDO customer = customerMapper.getByMemberUserId(userDO.getId());
CustomerOperateLogCreateReqVO customerOperateLogCreateReqVO = new CustomerOperateLogCreateReqVO()
.setOperator(1l)
.setOperatorName("admin")
.setCustomerId(customer.getId())
.setNumber(customer.getNumber())
.setName(customer.getName())
.setOperateType(CustomerOperateTypeEnum.VERIFICATION_CODE.getValue())
.setRemark(content);
//记录日志
customerOperateLogService.createOperateLog(customerOperateLogCreateReqVO);
}
@Override
public PageResult<UserRespDTO> getUserList(UserSelectReqVO userSelectReqVO) {
PageResult<MemberUserDO> memberUserDOS = userService.selectList(userSelectReqVO);
......
......@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.util.ExcelUtils;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.i18n.core.LangEnum;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
......@@ -295,8 +296,12 @@ public class MemberUserController {
InternalMessageCreateDto dto = InternalMessageCreateDto.builder().toIdList(Arrays.asList(userCardAuth.getUserId()))
.fromId("")
.sendTime(new Date())
.title(I18nMessage.getMessage("app.message.kyc.success.title"))
.content(I18nMessage.getMessage("app.message.kyc.success.content"))
.title(I18nMessage.getMessage("app.message.kyc.success.title", LangEnum.ZH.getLang()))
.titleEn(I18nMessage.getMessage("app.message.kyc.success.title", LangEnum.EN.getLang()))
.titleFr(I18nMessage.getMessage("app.message.kyc.success.title", LangEnum.FR.getLang()))
.content(I18nMessage.getMessage("app.message.kyc.success.content", LangEnum.ZH.getLang()))
.contentEn(I18nMessage.getMessage("app.message.kyc.success.content", LangEnum.EN.getLang()))
.contentFr(I18nMessage.getMessage("app.message.kyc.success.content", LangEnum.FR.getLang()))
.type(3).build();
clientInternalMessageApi.createInternalMessage(dto);
}
......@@ -305,8 +310,12 @@ public class MemberUserController {
InternalMessageCreateDto dto = InternalMessageCreateDto.builder().toIdList(Arrays.asList(userCardAuth.getUserId()))
.fromId("")
.sendTime(new Date())
.title(I18nMessage.getMessage("app.message.kyc.failed.title"))
.content(I18nMessage.getMessage("app.message.kyc.failed.content") + reqVO.getAuditRemark())
.title(I18nMessage.getMessage("app.message.kyc.failed.title", LangEnum.ZH.getLang()))
.titleEn(I18nMessage.getMessage("app.message.kyc.failed.title", LangEnum.EN.getLang()))
.titleFr(I18nMessage.getMessage("app.message.kyc.failed.title", LangEnum.FR.getLang()))
.content(I18nMessage.getMessage("app.message.kyc.failed.content", LangEnum.ZH.getLang()) + reqVO.getAuditRemark())
.contentEn(I18nMessage.getMessage("app.message.kyc.failed.content", LangEnum.EN.getLang()) + reqVO.getAuditRemark())
.contentFr(I18nMessage.getMessage("app.message.kyc.failed.content", LangEnum.FR.getLang()) + reqVO.getAuditRemark())
.type(3).build();
clientInternalMessageApi.createInternalMessage(dto);
}
......@@ -342,8 +351,12 @@ public class MemberUserController {
InternalMessageCreateDto dto = InternalMessageCreateDto.builder().toIdList(Arrays.asList(userCardAuthId))
.fromId("")
.sendTime(new Date())
.title(I18nMessage.getMessage("app.message.kyc.success.title"))
.content(I18nMessage.getMessage("app.message.kyc.success.content"))
.title(I18nMessage.getMessage("app.message.kyc.success.title", LangEnum.ZH.getLang()))
.titleEn(I18nMessage.getMessage("app.message.kyc.success.title", LangEnum.EN.getLang()))
.titleFr(I18nMessage.getMessage("app.message.kyc.success.title", LangEnum.FR.getLang()))
.content(I18nMessage.getMessage("app.message.kyc.success.content", LangEnum.ZH.getLang()))
.contentEn(I18nMessage.getMessage("app.message.kyc.success.content", LangEnum.EN.getLang()))
.contentFr(I18nMessage.getMessage("app.message.kyc.success.content", LangEnum.FR.getLang()))
.type(3).build();
clientInternalMessageApi.createInternalMessage(dto);
}
......
package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
......@@ -36,9 +33,8 @@ public class AppAuthCheckCodeReqVO {
@Pattern(regexp = "^[0-9]+$", message = "{app.auth.captcha.pattern}")
private String code;
@ApiModelProperty(value = "发送场景", example = "1", notes = "对应 SmsSceneEnum 枚举")
@ApiModelProperty(value = "节点")
@NotNull(message = "{app.sms.scene.not.blank}")
@InEnum(value = SmsSceneEnum.class, message = "{app.sms.scene.not.range}")
private Integer scene;
private String nodeValue;
}
package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
/**
* @author Administrator
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment