mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-25 00:15:06 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.*;
|
||||
@ -50,6 +51,13 @@ public class CollectionUtils {
|
||||
return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {
|
||||
if (ArrayUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return convertList(Arrays.asList(from), func);
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
@ -64,6 +72,13 @@ public class CollectionUtils {
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
|
||||
return map.values()
|
||||
.stream()
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
@ -78,6 +93,13 @@ public class CollectionUtils {
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
@ -155,8 +177,8 @@ public class CollectionUtils {
|
||||
/**
|
||||
* 对比老、新两个列表,找出新增、修改、删除的数据
|
||||
*
|
||||
* @param oldList 老列表
|
||||
* @param newList 新列表
|
||||
* @param oldList 老列表
|
||||
* @param newList 新列表
|
||||
* @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
|
||||
* 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
|
||||
* @return [新增列表、修改列表、删除列表]
|
||||
@ -225,7 +247,8 @@ public class CollectionUtils {
|
||||
return valueFunc.apply(t);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc, BinaryOperator<V> accumulator) {
|
||||
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc,
|
||||
BinaryOperator<V> accumulator) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
|
||||
/**
|
||||
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
||||
@ -23,6 +24,10 @@ public class LocalDateTimeUtils {
|
||||
return LocalDateTime.now().plus(duration);
|
||||
}
|
||||
|
||||
public static LocalDateTime minusTime(Duration duration) {
|
||||
return LocalDateTime.now().minus(duration);
|
||||
}
|
||||
|
||||
public static boolean beforeNow(LocalDateTime date) {
|
||||
return date.isBefore(LocalDateTime.now());
|
||||
}
|
||||
@ -62,6 +67,23 @@ public class LocalDateTimeUtils {
|
||||
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否在该时间范围内
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isBetween(String startTime, String endTime) {
|
||||
if (startTime == null || endTime == null) {
|
||||
return false;
|
||||
}
|
||||
LocalDate nowDate = LocalDate.now();
|
||||
return LocalDateTimeUtil.isIn(LocalDateTime.now(),
|
||||
LocalDateTime.of(nowDate, LocalTime.parse(startTime)),
|
||||
LocalDateTime.of(nowDate, LocalTime.parse(endTime)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断时间段是否重叠
|
||||
*
|
||||
@ -77,4 +99,26 @@ public class LocalDateTimeUtils {
|
||||
LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期所在的月份的开始时间
|
||||
* 例如:2023-09-30 00:00:00,000
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 月份的开始时间
|
||||
*/
|
||||
public static LocalDateTime beginOfMonth(LocalDateTime date) {
|
||||
return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期所在的月份的最后时间
|
||||
* 例如:2023-09-30 23:59:59,999
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 月份的结束时间
|
||||
*/
|
||||
public static LocalDateTime endOfMonth(LocalDateTime date) {
|
||||
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.common.util.number;
|
||||
|
||||
import cn.hutool.core.math.Money;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -16,7 +17,7 @@ public class MoneyUtils {
|
||||
* 计算百分比金额,四舍五入
|
||||
*
|
||||
* @param price 金额
|
||||
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||
* @return 百分比金额
|
||||
*/
|
||||
public static Integer calculateRatePrice(Integer price, Double rate) {
|
||||
@ -27,24 +28,46 @@ public class MoneyUtils {
|
||||
* 计算百分比金额,向下传入
|
||||
*
|
||||
* @param price 金额
|
||||
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||
* @return 百分比金额
|
||||
*/
|
||||
public static Integer calculateRatePriceFloor(Integer price, Double rate) {
|
||||
return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算百分比金额
|
||||
*
|
||||
* @param price 金额
|
||||
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||
* @param scale 保留小数位数
|
||||
* @param roundingMode 舍入模式
|
||||
*/
|
||||
public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) {
|
||||
return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以
|
||||
.divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100
|
||||
}
|
||||
/**
|
||||
* 计算百分比金额
|
||||
*
|
||||
* @param price 金额
|
||||
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||
* @param scale 保留小数位数
|
||||
* @param roundingMode 舍入模式
|
||||
*/
|
||||
public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) {
|
||||
return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以
|
||||
.divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100
|
||||
}
|
||||
|
||||
/**
|
||||
* 分转元
|
||||
*
|
||||
* @param fen 分
|
||||
* @return 元
|
||||
*/
|
||||
public static BigDecimal fenToYuan(int fen) {
|
||||
return new Money(0, fen).getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分转元(字符串)
|
||||
*
|
||||
* 例如说 fen 为 1 时,则结果为 0.01
|
||||
*
|
||||
* @param fen 分
|
||||
* @return 元
|
||||
*/
|
||||
public static String fenToYuanStr(int fen) {
|
||||
return new Money(0, fen).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,4 +13,28 @@ public class NumberUtils {
|
||||
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过经纬度获取地球上两点之间的距离
|
||||
*
|
||||
* 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现,目前它已经被 hutool 删除
|
||||
*
|
||||
* @param lat1 经度1
|
||||
* @param lng1 纬度1
|
||||
* @param lat2 经度2
|
||||
* @param lng2 纬度2
|
||||
* @return 距离,单位:千米
|
||||
*/
|
||||
public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
|
||||
double radLat1 = lat1 * Math.PI / 180.0;
|
||||
double radLat2 = lat2 * Math.PI / 180.0;
|
||||
double a = radLat1 - radLat2;
|
||||
double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
|
||||
double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
|
||||
+ Math.cos(radLat1) * Math.cos(radLat2)
|
||||
* Math.pow(Math.sin(b / 2), 2)));
|
||||
distance = distance * 6378.137;
|
||||
distance = Math.round(distance * 10000d) / 10000d;
|
||||
return distance;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.common.util.spring;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
@ -87,47 +86,4 @@ public class SpringExpressionUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* JoinPoint 切面 批量解析 EL 表达式,转换 jspl参数
|
||||
*
|
||||
* @param joinPoint 切面点
|
||||
* @param info 返回值
|
||||
* @param expressionStrings EL 表达式数组
|
||||
* @return Map<String, Object> 结果
|
||||
* @author 陈賝
|
||||
* @since 2023/6/18 11:20
|
||||
*/
|
||||
// TODO @chenchen: 这个方法,和 parseExpressions 比较接近,是不是可以合并下;
|
||||
public static Map<String, Object> parseExpression(JoinPoint joinPoint, Object info, List<String> expressionStrings) {
|
||||
// 如果为空,则不进行解析
|
||||
if (CollUtil.isEmpty(expressionStrings)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
|
||||
// 第一步,构建解析的上下文 EvaluationContext
|
||||
// 通过 joinPoint 获取被注解方法
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
// 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组
|
||||
String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
|
||||
// Spring 的表达式上下文对象
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
if (ArrayUtil.isNotEmpty(parameterNames)) {
|
||||
//获取方法参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
// 替换 SP EL 里的变量值为实际值, 比如 #user --> user对象
|
||||
context.setVariable(parameterNames[i], args[i]);
|
||||
}
|
||||
context.setVariable("info", info);
|
||||
}
|
||||
// 第二步,逐个参数解析
|
||||
Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);
|
||||
expressionStrings.forEach(key -> {
|
||||
Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);
|
||||
result.put(key, value);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
错误码 ErrorCode 的自动配置功能,提供如下功能:
|
||||
1. 远程读取:项目启动时,从 system-server 服务,读取数据库中的 ErrorCode 错误码,实现错误码的提水可配置;
|
||||
1. 远程读取:项目启动时,从 system-server 服务,读取数据库中的 ErrorCode 错误码,实现错误码的提示可配置;
|
||||
2. 自动更新:管理员在管理后台修数据库中的 ErrorCode 错误码时,项目自动从 system-server 服务加载最新的 ErrorCode 错误码;
|
||||
3. 自动写入:项目启动时,将项目本地的错误码写到 system-server 服务中,方便管理员在管理后台编辑;
|
||||
</description>
|
||||
|
@ -7,12 +7,16 @@ import cn.hutool.core.text.csv.CsvUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* 区域工具类
|
||||
@ -108,7 +112,7 @@ public class AreaUtils {
|
||||
// “递归”父节点
|
||||
area = area.getParent();
|
||||
if (area == null
|
||||
|| ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
|
||||
|| ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
|
||||
break;
|
||||
}
|
||||
sb.insert(0, separator);
|
||||
@ -116,4 +120,37 @@ public class AreaUtils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的区域列表
|
||||
*
|
||||
* @param type 区域类型
|
||||
* @param func 转换函数
|
||||
* @param <T> 结果类型
|
||||
* @return 区域列表
|
||||
*/
|
||||
public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
|
||||
return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
|
||||
}
|
||||
|
||||
// TODO @疯狂:注释写下;
|
||||
public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
|
||||
// TODO @疯狂:这种不要用 while true;因为万一脏数据,可能会死循环;可以转换成 for (int i = 0; i < Byte.MAX; i++) 一般是优先层级;
|
||||
do {
|
||||
Area area = AreaUtils.getArea(id);
|
||||
if (area == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.getType().equals(area.getType())) {
|
||||
return area.getId();
|
||||
}
|
||||
|
||||
if (area.getParent() == null || area.getParent().getId() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
id = area.getParent().getId();
|
||||
} while (true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ -76,4 +78,12 @@ public interface PayClient {
|
||||
*/
|
||||
PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo);
|
||||
|
||||
/**
|
||||
* 调用渠道,进行转账
|
||||
*
|
||||
* @param reqDTO 统一转账请求信息
|
||||
* @return 转账信息
|
||||
*/
|
||||
PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 统一转账 Response DTO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
public class PayTransferRespDTO {
|
||||
|
||||
/**
|
||||
* 转账状态
|
||||
*
|
||||
* 关联 {@link PayTransferStatusRespEnum#getStatus()}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 外部转账单号
|
||||
*
|
||||
*/
|
||||
private String outTransferNo;
|
||||
|
||||
/**
|
||||
* 支付渠道编号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
|
||||
/**
|
||||
* 支付成功时间
|
||||
*/
|
||||
private LocalDateTime successTime;
|
||||
|
||||
/**
|
||||
* 原始的返回结果
|
||||
*/
|
||||
private Object rawData;
|
||||
|
||||
/**
|
||||
* 调用渠道的错误码
|
||||
*/
|
||||
private String channelErrorCode;
|
||||
/**
|
||||
* 调用渠道报错时,错误信息
|
||||
*/
|
||||
private String channelErrorMsg;
|
||||
|
||||
/**
|
||||
* 创建【WAITING】状态的转账返回
|
||||
*/
|
||||
public static PayTransferRespDTO waitingOf(String channelOrderNo,
|
||||
String outTransferNo, Object rawData) {
|
||||
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
||||
respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus();
|
||||
respDTO.channelOrderNo = channelOrderNo;
|
||||
respDTO.outTransferNo = outTransferNo;
|
||||
respDTO.rawData = rawData;
|
||||
return respDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建【CLOSED】状态的转账返回
|
||||
*/
|
||||
public static PayTransferRespDTO closedOf(String channelErrorCode, String channelErrorMsg,
|
||||
String outTransferNo, Object rawData) {
|
||||
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
||||
respDTO.status = PayTransferStatusRespEnum.CLOSED.getStatus();
|
||||
respDTO.channelErrorCode = channelErrorCode;
|
||||
respDTO.channelErrorMsg = channelErrorMsg;
|
||||
// 相对通用的字段
|
||||
respDTO.outTransferNo = outTransferNo;
|
||||
respDTO.rawData = rawData;
|
||||
return respDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建【SUCCESS】状态的转账返回
|
||||
*/
|
||||
public static PayTransferRespDTO successOf(String channelTransferNo, LocalDateTime successTime,
|
||||
String outTransferNo, Object rawData) {
|
||||
PayTransferRespDTO respDTO = new PayTransferRespDTO();
|
||||
respDTO.status = PayTransferStatusRespEnum.SUCCESS.getStatus();
|
||||
respDTO.channelOrderNo = channelTransferNo;
|
||||
respDTO.successTime = successTime;
|
||||
// 相对通用的字段
|
||||
respDTO.outTransferNo = outTransferNo;
|
||||
respDTO.rawData = rawData;
|
||||
return respDTO;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统一转账 Request DTO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
public class PayTransferUnifiedReqDTO {
|
||||
|
||||
/**
|
||||
* 转账类型
|
||||
*
|
||||
* 关联 {@link PayTransferTypeEnum#getType()}
|
||||
*/
|
||||
@NotNull(message = "转账类型不能为空")
|
||||
@InEnum(PayTransferTypeEnum.class)
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
@NotEmpty(message = "用户 IP 不能为空")
|
||||
private String userIp;
|
||||
|
||||
@NotEmpty(message = "外部转账单编号不能为空")
|
||||
private String outTransferNo;
|
||||
|
||||
/**
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
@NotNull(message = "转账金额不能为空")
|
||||
@Min(value = 1, message = "转账金额必须大于零")
|
||||
private Integer price;
|
||||
|
||||
/**
|
||||
* 转账标题
|
||||
*/
|
||||
@NotEmpty(message = "转账标题不能为空")
|
||||
@Length(max = 128, message = "转账标题不能超过 128")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 收款方信息,转账类型不同,收款方信息不同
|
||||
*/
|
||||
@NotEmpty(message = "收款方信息 不能为空")
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
/**
|
||||
* 支付渠道的额外参数
|
||||
*/
|
||||
private Map<String, String> channelExtras;
|
||||
|
||||
}
|
@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -181,6 +183,26 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
||||
protected abstract PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo)
|
||||
throws Throwable;
|
||||
|
||||
@Override
|
||||
public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
ValidationUtils.validate(reqDTO);
|
||||
PayTransferRespDTO resp;
|
||||
try{
|
||||
resp = doUnifiedTransfer(reqDTO);
|
||||
}catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
// 系统异常,则包装成 PayException 异常抛出
|
||||
log.error("[unifiedTransfer][客户端({}) request({}) 发起转账异常]",
|
||||
getId(), toJsonString(reqDTO), ex);
|
||||
throw buildPayException(ex);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO)
|
||||
throws Throwable;
|
||||
|
||||
// ========== 各种工具方法 ==========
|
||||
|
||||
private PayException buildPayException(Throwable ex) {
|
||||
|
@ -50,6 +50,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
|
||||
clientClass.put(ALIPAY_APP, AlipayAppPayClient.class);
|
||||
clientClass.put(ALIPAY_PC, AlipayPcPayClient.class);
|
||||
clientClass.put(ALIPAY_BAR, AlipayBarPayClient.class);
|
||||
// 支付包转账客户端
|
||||
clientClass.put(ALIPAY_TRANSFER, AlipayTransferClient.class);
|
||||
// Mock 支付客户端
|
||||
clientClass.put(MOCK, MockPayClient.class);
|
||||
}
|
||||
|
@ -150,6 +150,10 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
// 2.1 执行请求
|
||||
AlipayTradeRefundResponse response = client.execute(request);
|
||||
if (!response.isSuccess()) {
|
||||
// 当出现 ACQ.SYSTEM_ERROR, 退款可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
|
||||
if (ObjectUtils.equalsAny(response.getSubCode(), "ACQ.SYSTEM_ERROR", "SYSTEM_ERROR")) {
|
||||
return PayRefundRespDTO.waitingOf(null, reqDTO.getOutRefundNo(), response);
|
||||
}
|
||||
return PayRefundRespDTO.failureOf(response.getSubCode(), response.getSubMsg(), reqDTO.getOutRefundNo(), response);
|
||||
}
|
||||
// 2.2 创建返回结果
|
||||
@ -198,6 +202,8 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
||||
return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========== 各种工具方法 ==========
|
||||
|
||||
protected String formatAmount(Integer amount) {
|
||||
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
@ -57,4 +59,8 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
|
||||
reqDTO.getOutTradeNo(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝【App 支付】不支持转账操作");
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
@ -75,4 +77,8 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
|
||||
reqDTO.getOutTradeNo(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝【条码支付】不支持转账操作");
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
@ -67,4 +69,8 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
|
||||
reqDTO.getOutTradeNo(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝【PC 网站】不支持转账操作");
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
@ -54,4 +56,8 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
|
||||
reqDTO.getOutTradeNo(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝【扫码支付】不支持转账操作");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,103 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
|
||||
import com.alipay.api.domain.Participant;
|
||||
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
|
||||
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
|
||||
/**
|
||||
* 支付宝转账的 PayClient 实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Slf4j
|
||||
public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
public AlipayTransferClient(Long channelId, AlipayPayClientConfig config) {
|
||||
super(channelId, PayChannelEnum.ALIPAY_TRANSFER.getCode(), config);
|
||||
}
|
||||
@Override
|
||||
protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝转账不支持统一下单请求");
|
||||
}
|
||||
@Override
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝转账不支持统一退款请求");
|
||||
}
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException {
|
||||
// 1.1 构建 AlipayFundTransUniTransferModel
|
||||
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
|
||||
// ① 通用的参数
|
||||
model.setTransAmount(formatAmount(reqDTO.getPrice())); // 转账金额
|
||||
model.setOrderTitle(reqDTO.getTitle()); // 转账业务的标题,用于在支付宝用户的账单里显示。
|
||||
model.setOutBizNo(reqDTO.getOutTransferNo());
|
||||
model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
|
||||
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER。
|
||||
model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
|
||||
PayTransferTypeEnum transferType = PayTransferTypeEnum.ofType(reqDTO.getType());
|
||||
switch(transferType){
|
||||
case WX_BALANCE :
|
||||
case WALLET_BALANCE : {
|
||||
log.error("[doUnifiedTransfer],支付宝转账不支持的转账类型{}", transferType);
|
||||
throw new UnsupportedOperationException(String.format("支付宝转账不支持转账类型: %s",transferType.getName()));
|
||||
}
|
||||
case ALIPAY_BALANCE : {
|
||||
// ② 个性化的参数
|
||||
Participant payeeInfo = new Participant();
|
||||
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
|
||||
String logonId = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_LOGON_ID");
|
||||
if (StrUtil.isEmpty(logonId)) {
|
||||
throw exception0(BAD_REQUEST.getCode(), "支付包登录 ID 不能为空");
|
||||
}
|
||||
String accountName = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_ACCOUNT_NAME");
|
||||
if (StrUtil.isEmpty(accountName)) {
|
||||
throw exception0(BAD_REQUEST.getCode(), "支付包账户名称不能为空");
|
||||
}
|
||||
payeeInfo.setIdentity(logonId); // 支付宝登录号
|
||||
payeeInfo.setName(accountName); // 支付宝账号姓名
|
||||
model.setPayeeInfo(payeeInfo);
|
||||
// 1.2 构建 AlipayFundTransUniTransferRequest
|
||||
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
|
||||
request.setBizModel(model);
|
||||
// 执行请求
|
||||
AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
|
||||
// 处理结果
|
||||
if (!response.isSuccess()) {
|
||||
// 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
|
||||
if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
|
||||
return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response);
|
||||
}
|
||||
return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
|
||||
reqDTO.getOutTransferNo(), response);
|
||||
}
|
||||
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
|
||||
response.getOutBizNo(), response);
|
||||
}
|
||||
case BANK_CARD : {
|
||||
Participant payeeInfo = new Participant();
|
||||
payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
default: {
|
||||
throw new IllegalStateException("不正确的转账类型: " + transferType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
import cn.hutool.http.Method;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
@ -56,4 +58,8 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
|
||||
reqDTO.getOutTradeNo(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("支付宝【Wap 网站】不支持转账操作");
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
@ -64,4 +66,8 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
|
||||
throw new UnsupportedOperationException("模拟支付无支付回调");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
||||
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
||||
@ -425,6 +427,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
// ========== 各种工具方法 ==========
|
||||
|
||||
static String formatDateV2(LocalDateTime time) {
|
||||
|
@ -28,6 +28,7 @@ public enum PayChannelEnum {
|
||||
ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_TRANSFER("alipay_transfer", "支付宝转账", AlipayPayClientConfig.class),
|
||||
|
||||
MOCK("mock", "模拟支付", NonePayClientConfig.class),
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums.transfer;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 渠道的转账状态枚举
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayTransferStatusRespEnum {
|
||||
|
||||
WAITING(0, "转账中"),
|
||||
|
||||
/**
|
||||
* TODO 转账到银行卡. 会有T+0 T+1 到账的请情况。 还未实现
|
||||
*/
|
||||
IN_PROGRESS(10, "转账进行中"),
|
||||
|
||||
|
||||
SUCCESS(20, "转账成功"),
|
||||
/**
|
||||
* 转账关闭 (失败,或者其它情况)
|
||||
*/
|
||||
CLOSED(30, "转账关闭");
|
||||
|
||||
private final Integer status;
|
||||
private final String name;
|
||||
|
||||
public static boolean isSuccess(Integer status) {
|
||||
return Objects.equals(status, SUCCESS.getStatus());
|
||||
}
|
||||
|
||||
public static boolean isClosed(Integer status) {
|
||||
return Objects.equals(status, CLOSED.getStatus());
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums.transfer;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 转账类型枚举
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PayTransferTypeEnum implements IntArrayValuable {
|
||||
ALIPAY_BALANCE(1, "支付宝余额"),
|
||||
WX_BALANCE(2, "微信余额"),
|
||||
BANK_CARD(3, "银行卡"),
|
||||
WALLET_BALANCE(4, "钱包余额");
|
||||
|
||||
public static final String ALIPAY_LOGON_ID = "ALIPAY_LOGON_ID";
|
||||
public static final String ALIPAY_ACCOUNT_NAME = "ALIPAY_ACCOUNT_NAME";
|
||||
|
||||
private final Integer type;
|
||||
private final String name;
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PayTransferTypeEnum::getType).toArray();
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static PayTransferTypeEnum ofType(Integer type) {
|
||||
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
|
||||
}
|
||||
}
|
@ -11,40 +11,40 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
*/
|
||||
public interface SmsFrameworkErrorCodeConstants {
|
||||
|
||||
ErrorCode SMS_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析");
|
||||
ErrorCode SMS_UNKNOWN = new ErrorCode(2_001_000_000, "未知错误,需要解析");
|
||||
|
||||
// ========== 权限 / 限流等相关 2001000100 ==========
|
||||
// ========== 权限 / 限流等相关 2-001-000-100 ==========
|
||||
|
||||
ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");
|
||||
ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");
|
||||
ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2_001_000_100, "没有发送短信的权限");
|
||||
ErrorCode SMS_IP_DENY = new ErrorCode(2_001_000_100, "IP 不允许发送短信");
|
||||
|
||||
// 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
|
||||
ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流");
|
||||
ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2_001_000_102, "指定手机的发送限流");
|
||||
// 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
|
||||
ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流");
|
||||
ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2_001_000_103, "每天的发送限流");
|
||||
|
||||
ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词");
|
||||
ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2_001_000_104, "短信内容有敏感词");
|
||||
|
||||
// 腾讯云:为避免骚扰用户,营销短信只允许在8点到22点发送。
|
||||
ErrorCode SMS_SEND_MARKET_LIMIT_CONTROL = new ErrorCode(2001000105, "营销短信发送时间限制");
|
||||
ErrorCode SMS_SEND_MARKET_LIMIT_CONTROL = new ErrorCode(2_001_000_105, "营销短信发送时间限制");
|
||||
|
||||
// ========== 模板相关 2001000200 ==========
|
||||
ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在
|
||||
ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确");
|
||||
// ========== 模板相关 2-001-000-200 ==========
|
||||
ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2_001_000_200, "短信模板不合法"); // 包括短信模板不存在
|
||||
ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2_001_000_201, "模板参数不正确");
|
||||
|
||||
// ========== 签名相关 2001000300 ==========
|
||||
ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用");
|
||||
// ========== 签名相关 2-001-000-300 ==========
|
||||
ErrorCode SMS_SIGN_INVALID = new ErrorCode(2_001_000_300, "短信签名不可用");
|
||||
|
||||
// ========== 账户相关 2001000400 ==========
|
||||
ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足");
|
||||
ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在");
|
||||
// ========== 账户相关 2-001-000-400 ==========
|
||||
ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2_001_000_400, "账户余额不足");
|
||||
ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2_001_000_401, "apiKey 不存在");
|
||||
|
||||
// ========== 其它相关 2001000900 开头 ==========
|
||||
ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失");
|
||||
ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确");
|
||||
ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中");
|
||||
ErrorCode SMS_APP_ID_INVALID = new ErrorCode(2001000903, "SdkAppId不合法");
|
||||
// ========== 其它相关 2-001-000-900 开头 ==========
|
||||
ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2_001_000_900, "请求参数缺失");
|
||||
ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2_001_000_901, "手机格式不正确");
|
||||
ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2_001_000_902, "手机号在黑名单中");
|
||||
ErrorCode SMS_APP_ID_INVALID = new ErrorCode(2_001_000_903, "SdkAppId不合法");
|
||||
|
||||
ErrorCode EXCEPTION = new ErrorCode(2001000999, "调用异常");
|
||||
ErrorCode EXCEPTION = new ErrorCode(2_001_000_999, "调用异常");
|
||||
|
||||
}
|
||||
|
@ -40,5 +40,4 @@ public interface JobLogFrameworkService {
|
||||
@NotNull(message = "结束时间不能为空") LocalDateTime endTime,
|
||||
@NotNull(message = "运行时长不能为空") Integer duration,
|
||||
boolean success, String result);
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
@ -55,7 +56,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
}
|
||||
|
||||
default Long selectCount() {
|
||||
return selectCount(new QueryWrapper<T>());
|
||||
return selectCount(new QueryWrapper<>());
|
||||
}
|
||||
|
||||
default Long selectCount(String field, Object value) {
|
||||
|
@ -5,6 +5,7 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// TODO @小吉祥:搞个 job,清理 14 天外的访问日志;
|
||||
/**
|
||||
* API 访问日志
|
||||
*
|
||||
|
@ -13,5 +13,4 @@ public interface ApiAccessLogFrameworkService {
|
||||
* @param apiAccessLog API 访问日志
|
||||
*/
|
||||
void createApiAccessLog(ApiAccessLog apiAccessLog);
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// TODO @小吉祥:搞个 job,清理 14 天外的异常日志;
|
||||
/**
|
||||
* API 错误日志
|
||||
*
|
||||
|
@ -13,5 +13,4 @@ public interface ApiErrorLogFrameworkService {
|
||||
* @param apiErrorLog API 错误日志
|
||||
*/
|
||||
void createApiErrorLog(ApiErrorLog apiErrorLog);
|
||||
|
||||
}
|
||||
|
@ -7,11 +7,17 @@ import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeSerializer;
|
||||
import cn.iocoder.yudao.framework.jackson.core.databind.NumberSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
@AutoConfiguration
|
||||
@ -27,6 +33,10 @@ public class YudaoJacksonAutoConfiguration {
|
||||
// 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型
|
||||
.addSerializer(Long.class, NumberSerializer.INSTANCE)
|
||||
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
|
||||
.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
|
||||
.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
|
||||
.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
|
||||
.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
|
||||
// 新增 LocalDateTime 序列化、反序列化规则
|
||||
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
|
||||
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
|
||||
|
Reference in New Issue
Block a user