package com.nova.sankuai.service.impl;
|
|
import cn.hutool.core.util.BooleanUtil;
|
import cn.hutool.crypto.asymmetric.KeyType;
|
import cn.hutool.crypto.asymmetric.RSA;
|
import cn.hutool.http.HttpRequest;
|
import cn.hutool.http.HttpResponse;
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONObject;
|
import com.alipay.api.AlipayApiException;
|
import com.alipay.api.AlipayConstants;
|
import com.alipay.api.DefaultAlipayClient;
|
import com.alipay.api.request.AlipayTradeWapPayRequest;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.nova.sankuai.domain.dto.*;
|
import com.nova.sankuai.domain.entity.*;
|
import com.nova.sankuai.domain.vo.AlipayOrderInfoVO;
|
import com.nova.sankuai.infra.config.AlipayConfigProperties;
|
import com.nova.sankuai.infra.config.CommonException;
|
import com.nova.sankuai.infra.mapper.*;
|
import com.nova.sankuai.infra.utils.MessageSendUtil;
|
import com.nova.sankuai.security.UserDetail;
|
import com.nova.sankuai.service.AlipayOrderInfoConverter;
|
import com.nova.sankuai.service.IAlipayPlatformService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.http.MediaType;
|
import org.springframework.stereotype.Service;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.nio.charset.StandardCharsets;
|
import java.security.MessageDigest;
|
import java.security.NoSuchAlgorithmException;
|
import java.text.DecimalFormat;
|
import java.text.SimpleDateFormat;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.*;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Executors;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.stream.Collectors;
|
|
@Slf4j
|
@Service
|
public class AlipayPlatformServiceImpl implements IAlipayPlatformService {
|
|
/*
|
* 参考文档:https://opendocs.alipay.com/open/203/105285
|
*/
|
|
private final SysAlipayAccountMapper alipayAccountMapper;
|
private final SysAlipayAccountUsageMapper alipayAccountUsageMapper;
|
private final AlipayExcessTextmsgReceiverMapper alipayExcessTextmsgReceiverMapper;
|
private final AlipayExcessTextmsgSendRecordMapper alipayExcessTextmsgSendRecordMapper;
|
private final AlipayChannelRecordMapper alipayChannelRecordMapper;
|
private final AlipayOrderInfoMapper alipayOrderInfoMapper;
|
private final AlipayRefundRecordMapper alipayRefundRecordMapper;
|
private final AlipayOrderInfoConverter converter;
|
private final AlipayConfigProperties alipayConf;
|
private final ObjectMapper objectMapper;
|
private final RSA rsa;
|
|
private final VipCardOrderMapper vipCardOrderMapper;
|
private final CustomerUserMapper userMapper;
|
|
private final Lock decisionLock = new ReentrantLock();
|
private final Lock alipayExcessRemindLock = new ReentrantLock();
|
private final Lock alipayExcessPreventionLock = new ReentrantLock();
|
|
private final ExecutorService exec = Executors.newFixedThreadPool(5);
|
|
private final MessageDigest digest;
|
|
private final MessageSendUtil messageSendUtil;
|
|
public AlipayPlatformServiceImpl(SysAlipayAccountMapper alipayAccountMapper,
|
SysAlipayAccountUsageMapper alipayAccountUsageMapper,
|
AlipayExcessTextmsgReceiverMapper alipayExcessTextmsgReceiverMapper,
|
AlipayExcessTextmsgSendRecordMapper alipayExcessTextmsgSendRecordMapper,
|
AlipayChannelRecordMapper alipayChannelRecordMapper,
|
AlipayOrderInfoMapper alipayOrderInfoMapper,
|
AlipayRefundRecordMapper alipayRefundRecordMapper,
|
AlipayOrderInfoConverter converter,
|
AlipayConfigProperties alipayConf,
|
ObjectMapper objectMapper,
|
MessageSendUtil messageSendUtil,
|
@Value("${loginrsa.key-pair.public}") String publicKey,
|
@Value("${loginrsa.key-pair.private}") String privateKey,
|
VipCardOrderMapper vipCardOrderMapper,
|
CustomerUserMapper userMapper) throws NoSuchAlgorithmException {
|
this.alipayAccountMapper = alipayAccountMapper;
|
this.alipayAccountUsageMapper = alipayAccountUsageMapper;
|
this.alipayExcessTextmsgReceiverMapper = alipayExcessTextmsgReceiverMapper;
|
this.alipayExcessTextmsgSendRecordMapper = alipayExcessTextmsgSendRecordMapper;
|
this.alipayChannelRecordMapper = alipayChannelRecordMapper;
|
this.alipayOrderInfoMapper = alipayOrderInfoMapper;
|
this.alipayRefundRecordMapper = alipayRefundRecordMapper;
|
this.converter = converter;
|
this.alipayConf = alipayConf;
|
this.vipCardOrderMapper = vipCardOrderMapper;
|
this.userMapper = userMapper;
|
this.rsa = new RSA(privateKey, publicKey);
|
this.messageSendUtil = messageSendUtil;
|
|
digest = MessageDigest.getInstance("MD5");
|
this.objectMapper = objectMapper;
|
}
|
|
@Override
|
public String orderCreate(AlipayOrderCreateDTO createDTO) {
|
String orderId = createDTO.getChannelId() + "_" + createDTO.getOrderId();
|
|
// 渠道编码检查
|
AlipayChannelRecord channel = checkChannelID(createDTO.getChannelId());
|
|
// 当日支付宝可交易金额是否超额
|
long usageId = accountDecision(orderId, createDTO.getTotalAmount());
|
SysAlipayAccount account = alipayAccountMapper.selectByUsageId(usageId);
|
|
try {
|
// 支付宝订单生成
|
String form = generateAlipayForm(account,
|
orderId, createDTO.getTotalAmount().toPlainString(),
|
createDTO.getOrderSubject(), createDTO.getReturnUrl(),
|
alipayConf.getPlatformNotifyUrl());
|
|
// 生成成功后记录订单信息
|
AlipayOrderInfo order = new AlipayOrderInfo();
|
order.setId(IdWorker.getId());
|
order.setChannelId(createDTO.getChannelId());
|
order.setOrderId(createDTO.getOrderId());
|
order.setOrderSubject(createDTO.getOrderSubject());
|
order.setOrderBody(createDTO.getOrderBody());
|
order.setGmtCreate(new Date());
|
order.setTotalAmount(createDTO.getTotalAmount());
|
order.setOrderStatus("WAIT_BUYER_PAY");
|
order.setAppId(account.getAppId());
|
order.setNotifyUrl(createDTO.getNotifyUrl() != null ? createDTO.getNotifyUrl() : "");
|
|
// 渠道当日是否限额
|
int insert = 0, syncLimit = 30, times = 0;
|
Date date = new Date();
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
String from = sdf.format(date) + " 00:00:00";
|
String to = sdf.format(date) + " 23:59:59";
|
while (insert == 0) {
|
if (times >= syncLimit) {
|
throw new CommonException("订单生成错误! 并发异常");
|
}
|
times++;
|
sleepWhile();
|
int vers = checkChannelTradingLimit(channel, createDTO.getTotalAmount());
|
if (vers == -1) {
|
// 执行正常插入
|
order.setRecordVers(0);
|
insert = alipayOrderInfoMapper.insertSelective(order);
|
} else {
|
// 执行版本号插入
|
order.setRecordVers(vers);
|
insert = alipayOrderInfoMapper.insertSelectiveWithVers(order, from, to);
|
}
|
}
|
return form;
|
} catch (Exception e) {
|
// 订单生成失败
|
cancelAlipayOrder(orderId);
|
alipayAccountUsageMapper.deleteById(usageId);
|
throw new CommonException("支付宝订单生成失败!reason=" + e.getMessage());
|
}
|
}
|
|
private void cancelAlipayOrder(String outTradeNo) {
|
}
|
|
private void sleepWhile() {
|
try {
|
TimeUnit.MILLISECONDS.sleep(10L);
|
} catch (InterruptedException ignored) {
|
}
|
}
|
|
private int checkChannelTradingLimit(AlipayChannelRecord channel, BigDecimal amount) {
|
BigDecimal dailyLimit = channel.getDailyLimit();
|
Date date = new Date();
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
String from = sdf.format(date) + " 00:00:00";
|
String to = sdf.format(date) + " 23:59:59";
|
int count = alipayOrderInfoMapper.checkTodayHasTradeOrder(channel.getChannelId(), from, to);
|
if (count == 0) {
|
// 当日还没有任何一笔交易
|
if (dailyLimit.subtract(amount).doubleValue() < 0) {
|
throw new CommonException("渠道交易超额");
|
}
|
return -1;
|
}
|
|
String channelId = channel.getChannelId();
|
AlipayChannelDailyLimitDTO limit = alipayOrderInfoMapper.checkChannelIdTradingLimit(channelId, from, to, dailyLimit);
|
if (limit == null) {
|
// 目标渠道当日达到限额
|
throw new CommonException("渠道已限额");
|
}
|
if (dailyLimit.subtract(limit.getTotal()).subtract(amount).doubleValue() < 0) {
|
throw new CommonException("渠道交易超额");
|
}
|
return limit.getVers();
|
}
|
|
@Override
|
public String generateAlipayForm(SysAlipayAccount account,
|
String orderId, String amount, String subject,
|
String returnURL, String notifyURL) throws AlipayApiException {
|
DefaultAlipayClient client = new DefaultAlipayClient(
|
alipayConf.getUrl(),
|
account.getAppId(),
|
account.getPrivateKey(),
|
"json",
|
AlipayConstants.CHARSET_UTF8,
|
account.getPublicKey(),
|
"RSA2");
|
AlipayTradeWapPayRequest alipayReq = new AlipayTradeWapPayRequest();
|
alipayReq.setReturnUrl(returnURL);
|
alipayReq.setNotifyUrl(notifyURL);
|
alipayReq.setBizContent(new JSONObject() {{
|
put("out_trade_no", orderId);
|
put("total_amount", amount);
|
put("subject", subject);
|
put("product_code", "QUICK_WAP_WAY");
|
}}.toJSONString());
|
return client.pageExecute(alipayReq).getBody();
|
}
|
|
private AlipayChannelRecord checkChannelID(String channelId) {
|
AlipayChannelRecord usingChannel = alipayChannelRecordMapper.selectByChannelIdAndStatus(channelId, "using");
|
if (usingChannel == null) {
|
throw new CommonException("渠道ID无效");
|
}
|
return usingChannel;
|
}
|
|
private final Map<String, LocalDate> msgSendFlag = new ConcurrentHashMap<>(4);
|
|
@Override
|
public long accountDecision(String orderId, BigDecimal amount) {
|
try {
|
if (!decisionLock.tryLock(10L, TimeUnit.SECONDS)) {
|
throw new RuntimeException("支付宝账户决定 - 获取锁超时");
|
}
|
LocalDate today = LocalDate.now();
|
|
SysAlipayAccountUsage usage = new SysAlipayAccountUsage();
|
usage.setId(IdWorker.getId());
|
usage.setOrderId(orderId);
|
usage.setCreateDate(today);
|
usage.setSingleAmount(amount);
|
usage.setStatus("init");
|
usage.setCreateDatetime(LocalDateTime.now());
|
int insert = alipayAccountUsageMapper.insertWithSelect(usage);
|
if (insert == 0) {
|
if (!today.equals(msgSendFlag.get("满额提醒"))) {
|
// 没提醒过,或已经是第二天
|
msgSendFlag.put("满额提醒", today);
|
log.info("alipay->进入满额提醒");
|
alipayExcessRemind();
|
}
|
throw new CommonException("所有支付宝账户已限额,请联系管理员");
|
} else {
|
// 如果没有超额,也可能是管理员手工重置了账户可用余额
|
msgSendFlag.remove("满额提醒");
|
alipayExcessPrevention();
|
return usage.getId();
|
}
|
} catch (InterruptedException e) {
|
throw new RuntimeException("支付宝账户决定 - 获取锁中断", e);
|
} finally {
|
decisionLock.unlock();
|
}
|
}
|
|
@Override
|
public void handleNotify(Map<String, String[]> map) {
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
try {
|
String outTradeNo = nil(map.get("out_trade_no"), it -> it[0]);
|
int splitIndex = outTradeNo.indexOf("_");
|
String channelId = outTradeNo.substring(0, splitIndex);
|
String orderId = outTradeNo.substring(splitIndex + 1);
|
|
AlipayOrderInfo order = new AlipayOrderInfo();
|
order.setAlipaySerial(nil(map.get("trade_no"), it -> it[0])); // 支付宝流水号 trade_no
|
order.setChannelId(channelId);
|
order.setOrderId(orderId);
|
order.setGmtPayment(nil(map.get("gmt_create"), it -> sdf.parse(it[0])));
|
order.setGmtRefund(nil(map.get("gmt_refund"), it -> sdf.parse(it[0])));
|
order.setGmtClose(nil(map.get("gmt_close"), it -> sdf.parse(it[0])));
|
order.setBuyerId(nil(map.get("buyer_id"), it -> it[0]));
|
order.setSellerId(nil(map.get("seller_id"), it -> it[0]));
|
order.setReceiptAmount(newBigDecimal(nil(map.get("receipt_amount"), it -> it[0])));
|
order.setPointAmount(newBigDecimal(nil(map.get("invoice_amount"), it -> it[0])));
|
order.setBuyerPayAmount(newBigDecimal(nil(map.get("buyer_pay_amount"), it -> it[0])));
|
order.setInvoiceAmount(newBigDecimal(nil(map.get("point_amount"), it -> it[0])));
|
order.setRefundFee(newBigDecimal(nil(map.get("refund_fee"), it -> it[0])));
|
order.setOrderStatus(nil(map.get("trade_status"), it -> it[0]));
|
|
if ("TRADE_CLOSED".equals(order.getOrderStatus())) {
|
// 关闭订单
|
alipayAccountUsageMapper.delete(Wrappers.<SysAlipayAccountUsage>lambdaUpdate()
|
.eq(SysAlipayAccountUsage::getOrderId, outTradeNo));
|
} else {
|
alipayAccountUsageMapper.updateStatus(outTradeNo);
|
}
|
|
alipayOrderInfoMapper.updateByOrderSelective(order);
|
|
AlipayOrderQueryDTO condition = new AlipayOrderQueryDTO();
|
condition.setChannelId(channelId);
|
condition.setOrderId(orderId);
|
AlipayOrderInfo orderResult = alipayOrderInfoMapper.selectByChannelIdAndOrderId(condition);
|
|
String notifyUrl = orderResult.getNotifyUrl();
|
if (StringUtils.isNotBlank(notifyUrl)) {
|
ArrayList<String> errors = new ArrayList<>();
|
for (String url : notifyUrl.split(",")) {
|
HttpResponse resp = HttpRequest.post(url)
|
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
.body(toJsonString(sign(toJsonString(converter.toView(orderResult)))))
|
.execute();
|
if (!resp.isOk()) {
|
errors.add(String.format("[%s] resp is not ok: %s", url, resp));
|
} else if (!"ok".equals(resp.body())) {
|
errors.add(String.format("[%s] resp is not ok: %s", url, resp.body()));
|
}
|
}
|
if (errors.size() > 0) {
|
throw new CommonException("回调地址中存在返回错误:" + errors);
|
}
|
}
|
} catch (Exception e) {
|
log.error("AlipayPlatform回调异常! error = " + e.getMessage() + " map = " + JSON.toJSONString(map), e);
|
map.forEach((k, v) -> log.error("alipay callback param: k = " + k + ", v = " + Arrays.toString(v)));
|
throw new RuntimeException(e);
|
}
|
}
|
|
@FunctionalInterface
|
interface MyFunc<T, R> {
|
R apply(T in) throws Exception;
|
}
|
|
private <IN, OUT> OUT nil(IN in, MyFunc<IN, OUT> fun) throws Exception {
|
if (in == null) {
|
return null;
|
}
|
return fun.apply(in);
|
}
|
|
private BigDecimal newBigDecimal(String amount) {
|
if (amount == null) {
|
return null;
|
}
|
return new BigDecimal(amount);
|
}
|
|
@Override
|
public <T> T decode(String encodedString, Class<T> type) {
|
try {
|
if (BooleanUtil.isTrue(alipayConf.getPlatformEncryptFlag())) {
|
byte[] content = rsa.decrypt(Base64.getDecoder().decode(encodedString), KeyType.PrivateKey);
|
return objectMapper.readValue(new String(content), type);
|
}
|
return objectMapper.readValue(encodedString, type);
|
} catch (Exception e) {
|
throw new CommonException("解密失败", e);
|
}
|
}
|
|
@Override
|
public String encode(String inst) {
|
try {
|
if (BooleanUtil.isTrue(alipayConf.getPlatformEncryptFlag())) {
|
byte[] bytes = rsa.encrypt(inst.getBytes(StandardCharsets.UTF_8), KeyType.PrivateKey);
|
return Base64.getEncoder().encodeToString(bytes);
|
}
|
return inst;
|
} catch (Exception e) {
|
throw new RuntimeException("加密失败", e);
|
}
|
}
|
|
@Override
|
public AlipayOrderInfoVO orderQuery(AlipayOrderQueryDTO queryDTO) {
|
AlipayOrderInfo orderInst = alipayOrderInfoMapper.selectByChannelIdAndOrderId(queryDTO);
|
if (orderInst == null) {
|
throw new CommonException("订单查询失败! 订单[" + queryDTO.getOrderId() + "]不存在");
|
}
|
return converter.toView(orderInst);
|
}
|
|
@Override
|
public String toJsonString(Object obj) {
|
if (obj instanceof String) {
|
return ((String) obj);
|
}
|
try {
|
return objectMapper.writeValueAsString(obj);
|
} catch (JsonProcessingException e) {
|
String msg = "ObjectMapper 序列化出错: " + e.getMessage();
|
log.error(msg);
|
throw new CommonException(msg);
|
}
|
}
|
|
@Override
|
public Map<String, String> sign(String content) {
|
String sign = Base64.getEncoder().encodeToString(digest.digest(content.getBytes(StandardCharsets.UTF_8)));
|
Map<String, String> map = new HashMap<>();
|
map.put("sign", sign);
|
map.put("value", encode(content));
|
return map;
|
}
|
|
@Override
|
public void transferAccount(AlipayTransferAccountDTO param) {
|
|
}
|
|
@Override
|
public void refund(AlipayRefundDTO refundDTO) {
|
// 保存退款记录
|
AlipayRefundRecord record = converter.toEntity(refundDTO);
|
record.setRefundReqTime(new Date());
|
alipayRefundRecordMapper.insertSelective(record);
|
}
|
|
@Override
|
public IPage<AlipayRefundRecord> refundList(AlipayRefundQueryDTO queryDTO) {
|
Page<?> page = new Page<>(queryDTO.getPageNo(), queryDTO.getPageSize());
|
return alipayRefundRecordMapper.selectPage(page, queryDTO);
|
}
|
|
@Override
|
public void refundFinish(AlipayRefundFinishDTO finishDTO) {
|
AlipayRefundRecord condition = new AlipayRefundRecord();
|
condition.setId(finishDTO.getRefundId());
|
condition.setRefundStatus(finishDTO.getRefundStatus());
|
condition.setDealUserId(finishDTO.getDealUserId());
|
condition.setDealTime(finishDTO.getDealFinishTime());
|
condition.setDealDesc(finishDTO.getDealDesc());
|
alipayRefundRecordMapper.updateByPrimaryKeySelective(condition);
|
}
|
|
@Override
|
public BigDecimal refundAmountCalculate(UserDetail user) {
|
Date now = new Date();
|
List<Integer> prices = new ArrayList<>();
|
|
// 会员服务
|
if (userMapper.selectVipDateInTimeCount(user.getId(), now) > 0) {
|
prices.add(vipCardOrderMapper.findLatestEndOrderPriceByUserIdAndVipTypes(user.getId(), Collections.singletonList(0)));
|
}
|
// 审批加速包
|
if (userMapper.selectQueueVipCount(user.getId()) > 0) {
|
prices.add(vipCardOrderMapper.findLatestEndOrderPriceByUserIdAndVipTypes(user.getId(), Collections.singletonList(1)));
|
}
|
// 贷超会员
|
if (userMapper.selectLoanVipDateInTimeCount(user.getId(), now) > 0) {
|
prices.add(vipCardOrderMapper.findLatestEndOrderPriceByUserIdAndVipTypes(user.getId(), Collections.singletonList(2)));
|
}
|
BigDecimal unitPrice = new BigDecimal(prices.stream().filter(Objects::nonNull).reduce(0, (Integer::sum)));
|
return unitPrice.divide(BigDecimal.valueOf(100L), 2, RoundingMode.DOWN);
|
}
|
|
/**
|
* 满额提醒
|
*/
|
private void alipayExcessRemind() {
|
try {
|
LocalDate today = LocalDate.now();
|
LambdaQueryWrapper<AlipayExcessTextmsgSendRecord> query = Wrappers.<AlipayExcessTextmsgSendRecord>lambdaQuery()
|
.eq(AlipayExcessTextmsgSendRecord::getSendDate, today)
|
.eq(AlipayExcessTextmsgSendRecord::getSendType, 1);
|
Integer count = alipayExcessTextmsgSendRecordMapper.selectCount(query);
|
if (count == 0) {
|
log.info("alipay->今日未发送满额提醒");
|
try {
|
if (!alipayExcessRemindLock.tryLock(10L, TimeUnit.SECONDS)) {
|
throw new CommonException("支付宝限额提醒 - 获取锁超时");
|
}
|
count = alipayExcessTextmsgSendRecordMapper.selectCount(query);
|
if (count == 0) {
|
List<String> phones = alipayExcessTextmsgReceiverMapper.selectList(
|
Wrappers.<AlipayExcessTextmsgReceiver>lambdaQuery().eq(AlipayExcessTextmsgReceiver::getStatus, 1))
|
.stream()
|
.map(AlipayExcessTextmsgReceiver::getPhone)
|
.collect(Collectors.toList());
|
String content = "【三快分期】系统已达到支付限额,无法继续接受订单,请立即切换支付渠道";
|
phones.forEach(phone -> {
|
AlipayExcessTextmsgSendRecord record = new AlipayExcessTextmsgSendRecord();
|
record.setReceiverPhone(phone);
|
record.setTextContent(content);
|
record.setSendDate(today);
|
record.setSendDatetime(LocalDateTime.now());
|
record.setSendType(1);
|
alipayExcessTextmsgSendRecordMapper.insert(record);
|
messageSendUtil.sendMessage(phone, content);
|
log.info("alipay->号码{}已发送短信", phone);
|
});
|
}
|
} catch (InterruptedException e) {
|
throw new CommonException("支付宝限额提醒 - 获取锁中断", e);
|
} finally {
|
alipayExcessRemindLock.unlock();
|
}
|
}
|
} catch (RuntimeException e) {
|
log.info("alipay->满额提醒异常:{}:{}", e.getClass(), e.getMessage());
|
throw e;
|
}
|
}
|
|
/**
|
* 满额预警
|
*/
|
private void alipayExcessPrevention() {
|
String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
|
BigDecimal todayTotal = alipayAccountUsageMapper.selectSumAmountInDay(today);
|
BigDecimal total = alipayAccountMapper.selectTotalAmount();
|
|
BigDecimal divide = todayTotal.divide(total, 3, RoundingMode.HALF_UP);
|
if (divide.doubleValue() < 0.9) {
|
// 未达到预警阈值
|
msgSendFlag.remove("满额预警");
|
return;
|
}
|
|
LocalDate todayDate = LocalDate.now();
|
if (todayDate.equals(msgSendFlag.get("满额预警"))) {
|
return;
|
}
|
msgSendFlag.put("满额预警", todayDate);
|
|
LambdaQueryWrapper<AlipayExcessTextmsgSendRecord> query = Wrappers.<AlipayExcessTextmsgSendRecord>lambdaQuery()
|
.eq(AlipayExcessTextmsgSendRecord::getSendDate, todayDate)
|
.eq(AlipayExcessTextmsgSendRecord::getSendType, 2);
|
Integer count = alipayExcessTextmsgSendRecordMapper.selectCount(query);
|
if (count == 0) {
|
try {
|
if (!alipayExcessPreventionLock.tryLock(10L, TimeUnit.SECONDS)) {
|
throw new CommonException("支付宝限额提醒 - 获取锁超时");
|
}
|
count = alipayExcessTextmsgSendRecordMapper.selectCount(query);
|
if (count == 0) {
|
BigDecimal rest = total.subtract(todayTotal);
|
DecimalFormat decimalFormat = new DecimalFormat();
|
decimalFormat.applyPattern("0.00");
|
String content = String.format("【三快分期】系统支付限额即将达到上限,请注意切换支付渠道,已用%s,剩余%s",
|
decimalFormat.format(todayTotal.doubleValue()),
|
decimalFormat.format(rest.doubleValue()));
|
|
List<String> phones = alipayExcessTextmsgReceiverMapper.selectList(
|
Wrappers.<AlipayExcessTextmsgReceiver>lambdaQuery()
|
.eq(AlipayExcessTextmsgReceiver::getStatus, 1))
|
.stream()
|
.map(AlipayExcessTextmsgReceiver::getPhone)
|
.collect(Collectors.toList());
|
phones.forEach(phone -> {
|
AlipayExcessTextmsgSendRecord record = new AlipayExcessTextmsgSendRecord();
|
record.setReceiverPhone(phone);
|
record.setTextContent(content);
|
record.setSendDate(todayDate);
|
record.setSendDatetime(LocalDateTime.now());
|
record.setSendType(2);
|
alipayExcessTextmsgSendRecordMapper.insert(record);
|
});
|
// 避免网络原因造成占用锁过长时间
|
exec.execute(() -> phones.forEach(e -> messageSendUtil.sendMessage(e, content)));
|
}
|
} catch (InterruptedException e) {
|
throw new CommonException("支付宝限额提醒 - 获取锁中断", e);
|
} finally {
|
alipayExcessPreventionLock.unlock();
|
}
|
}
|
}
|
|
@Override
|
public <T> T decodeByPublicKey(String encodedString, Class<T> type) {
|
try {
|
if (BooleanUtil.isTrue(alipayConf.getPlatformEncryptFlag())) {
|
byte[] content = rsa.decrypt(Base64.getDecoder().decode(encodedString), KeyType.PublicKey);
|
return objectMapper.readValue(new String(content), type);
|
}
|
return objectMapper.readValue(encodedString, type);
|
} catch (Exception e) {
|
throw new CommonException("解密失败", e);
|
}
|
}
|
}
|