mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-25 21:45:08 +08:00
改名
This commit is contained in:
@@ -0,0 +1,582 @@
|
||||
package com.jsowell.pile.service.programlogic;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.jsowell.adapay.service.AdapayService;
|
||||
import com.jsowell.adapay.vo.PaymentInfo;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
|
||||
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import com.jsowell.common.enums.ykc.*;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.pile.domain.*;
|
||||
import com.jsowell.pile.dto.*;
|
||||
import com.jsowell.pile.service.*;
|
||||
import com.jsowell.pile.transaction.service.TransactionService;
|
||||
import com.jsowell.pile.vo.uniapp.PileConnectorDetailVO;
|
||||
import com.jsowell.pile.vo.web.BalanceDeductionAmountVO;
|
||||
import com.jsowell.pile.vo.web.BillingTemplateVO;
|
||||
import com.jsowell.pile.vo.web.PileStationVO;
|
||||
import com.jsowell.wxpay.service.WxAppletRemoteService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 描述订单逻辑
|
||||
*/
|
||||
public abstract class AbstractProgramLogic implements InitializingBean {
|
||||
protected Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Value("${adapay.callback}")
|
||||
protected String ADAPAY_CALLBACK_URL;
|
||||
|
||||
@Resource
|
||||
protected IOrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
protected IPileBasicInfoService pileBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
protected IPileStationInfoService pileStationInfoService;
|
||||
|
||||
@Autowired
|
||||
protected IPileBillingTemplateService pileBillingTemplateService;
|
||||
|
||||
@Autowired
|
||||
protected IMemberBasicInfoService memberBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
protected MemberAdapayRecordService memberAdapayRecordService;
|
||||
|
||||
@Autowired
|
||||
protected OrderPayRecordService orderPayRecordService;
|
||||
|
||||
@Autowired
|
||||
protected IMemberTransactionRecordService memberTransactionRecordService;
|
||||
|
||||
@Autowired
|
||||
protected AdapayService adapayService;
|
||||
|
||||
@Autowired
|
||||
protected AdapayMemberAccountService adapayMemberAccountService;
|
||||
|
||||
@Autowired
|
||||
protected IPileMerchantInfoService pileMerchantInfoService;
|
||||
|
||||
@Autowired
|
||||
protected TransactionService transactionService;
|
||||
|
||||
@Autowired
|
||||
protected AdapayCallbackRecordService adapayCallbackRecordService;
|
||||
|
||||
@Autowired
|
||||
protected PileAuthCardService pileAuthCardService;
|
||||
|
||||
@Autowired
|
||||
protected IMemberPlateNumberRelationService memberPlateNumberRelationService;
|
||||
|
||||
@Autowired
|
||||
protected WxAppletRemoteService wxAppletRemoteService;
|
||||
|
||||
@Autowired
|
||||
protected OrderMonitorDataService orderMonitorDataService;
|
||||
|
||||
@Autowired
|
||||
protected MemberWalletInfoService memberWalletInfoService;
|
||||
|
||||
@Autowired
|
||||
protected RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 生成订单
|
||||
*/
|
||||
public abstract OrderBasicInfo generateOrder(GenerateOrderDTO dto) throws ParseException;
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
public abstract Map<String, Object> payOrder(PayOrderDTO dto);
|
||||
|
||||
/**
|
||||
* 订单结算
|
||||
*/
|
||||
public abstract void settleOrder(TransactionRecordsData data, OrderBasicInfo orderBasicInfo);
|
||||
|
||||
/**
|
||||
* 订单退款
|
||||
*/
|
||||
public abstract void refundOrder(OrderBasicInfo orderBasicInfo);
|
||||
|
||||
/**
|
||||
* 余额充值
|
||||
*/
|
||||
public abstract Map<String, Object> rechargeBalance(WeixinPayDTO dto);
|
||||
|
||||
/**
|
||||
* 余额退款
|
||||
* @param dto
|
||||
*/
|
||||
public abstract void refundBalance(ApplyRefundDTO dto);
|
||||
|
||||
/**
|
||||
* 订单支付成功 支付回调
|
||||
* 支付成功后掉用这个方法
|
||||
* 1. 修改订单支付状态
|
||||
* 2. 发送启动充电指令
|
||||
*/
|
||||
protected void payOrderSuccessCallback(PayOrderSuccessCallbackDTO dto) {
|
||||
orderBasicInfoService.payOrderSuccessCallback(dto);
|
||||
}
|
||||
|
||||
protected List<BalanceDeductionAmountVO> calculateTheBalanceDeductionAmount(String memberId, BigDecimal amount) {
|
||||
return orderBasicInfoService.calculateTheBalanceDeductionAmount(memberId, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理前端传的参数
|
||||
* pileConnectorCode = pileSn + connectorCode
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
protected void analysisPileParameter(BasicPileDTO dto) {
|
||||
if (StringUtils.isBlank(dto.getPileSn()) || StringUtils.isBlank(dto.getConnectorCode())) {
|
||||
// 从pileConnectorCode解析
|
||||
String pileConnectorCode = dto.getPileConnectorCode();
|
||||
if (StringUtils.isNotEmpty(pileConnectorCode) && pileConnectorCode.length() == Constants.PILE_CONNECTOR_CODE_LENGTH) {
|
||||
dto.setPileSn(StringUtils.substring(pileConnectorCode, 0, pileConnectorCode.length() - 2));
|
||||
dto.setConnectorCode(StringUtils.substring(pileConnectorCode, pileConnectorCode.length() - 2, pileConnectorCode.length()));
|
||||
} else {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_DATA_LENGTH_ERROR);
|
||||
}
|
||||
} else {
|
||||
// 说明pileSn 和 connectorCode前端传了,那就校验一下长度
|
||||
if (dto.getPileSn().length() != Constants.PILE_SN_LENGTH || dto.getConnectorCode().length() != Constants.CONNECTOR_CODE_LENGTH) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_DATA_LENGTH_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验充电桩相关的信息
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
protected void checkPileInfo(GenerateOrderDTO dto) {
|
||||
// 查询充电桩状态 是否空闲 枪口是否占用
|
||||
PileConnectorDetailVO pileConnector = pileBasicInfoService.queryPileConnectorDetail(dto.getPileSn() + dto.getConnectorCode());
|
||||
if (pileConnector == null) {
|
||||
logger.error("checkPileInfo充电枪口为空 pileSn:{}, connectorCode:{}", dto.getPileSn(), dto.getConnectorCode());
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_CONNECTOR_INFO_NULL_ERROR);
|
||||
}
|
||||
|
||||
// 查询站点状态
|
||||
PileStationVO stationInfo = pileStationInfoService.getStationInfo(pileConnector.getStationId());
|
||||
if (stationInfo == null || StringUtils.equals(stationInfo.getOpenFlag(), Constants.ZERO)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_STATION_IS_NOT_OPEN);
|
||||
}
|
||||
|
||||
// 校验启动桩使用的小程序,和充电桩所属一级运营商是否一致
|
||||
String merchantIdByAppId = pileMerchantInfoService.getFirstLevelMerchantIdByAppId(dto.getAppId());
|
||||
String merchantIdByMerchantId = pileMerchantInfoService.getFirstLevelMerchantIdByMerchantId(stationInfo.getMerchantId());
|
||||
if (!StringUtils.equals(merchantIdByAppId, merchantIdByMerchantId)) {
|
||||
throw new BusinessException("", "当前桩运营商与小程序所属运营商不一致");
|
||||
}
|
||||
|
||||
// 如果是鉴权卡或者vin启动,不判断枪口状态
|
||||
if (!(StringUtils.equals(dto.getStartMode(), StartModeEnum.AUTH_CARD.getValue())
|
||||
|| StringUtils.equals(dto.getStartMode(), StartModeEnum.VIN_CODE.getValue()))) {
|
||||
// 判断枪口状态
|
||||
if (!(StringUtils.equals(pileConnector.getConnectorStatus(), PileConnectorDataBaseStatusEnum.FREE.getValue())
|
||||
|| StringUtils.equals(pileConnector.getConnectorStatus(), PileConnectorDataBaseStatusEnum.OCCUPIED_NOT_CHARGED.getValue()))) {
|
||||
logger.error("checkPileInfo充电枪口状态不正确,当前状态为:{}", pileConnector.getConnectorStatus());
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_PILE_CONNECTOR_STATUS_ERROR);
|
||||
}
|
||||
}
|
||||
// 查询充电桩的计费模板
|
||||
BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(dto.getPileSn());
|
||||
if (billingTemplateVO == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_BILLING_TEMPLATE_NULL_ERROR);
|
||||
}
|
||||
dto.setPileConnector(pileConnector);
|
||||
dto.setBillingTemplate(billingTemplateVO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 返回更新后的OrderBasicInfo对象
|
||||
* 专用方法,其他地方如果要用请仔细检查
|
||||
* 【公共方法】
|
||||
*/
|
||||
protected void returnUpdateOrderBasicInfo(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
// 消费金额就是订单总金额/交易记录传过来的消费金额, 四舍五入保留两位小数
|
||||
BigDecimal orderAmount = new BigDecimal(data.getConsumptionAmount()).setScale(2, RoundingMode.HALF_UP);
|
||||
// 付款金额 - 实际消费金额,如果有剩余,需要走退款操作 当使用余额支付时payAmount = principalPay + giftPay
|
||||
BigDecimal payAmount = orderBasicInfo.getPayAmount();
|
||||
|
||||
// 有时候充电桩到达金额停止充电会多出一点金额,比如实际需要充50元的电,充电桩传来的消费金额为50.01元,在后台记录的时候需要舍去
|
||||
if (orderAmount.compareTo(payAmount) > 0) {
|
||||
logger.info("结算订单:【{}】充电桩传来的消费金额:【{}】大于付款金额:【{}】, 消费金额设置为付款金额相等数据", orderCode, orderAmount, payAmount);
|
||||
orderAmount = payAmount;
|
||||
}
|
||||
orderBasicInfo.setOrderAmount(orderAmount); // 订单总金额
|
||||
|
||||
// 虚拟金额 指订单消费中不参与结算的部分
|
||||
BigDecimal virtualAmount = BigDecimal.ZERO;
|
||||
if (OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue().equals(orderBasicInfo.getPayMode())) {
|
||||
// 白名单支付所消费的金额,都属于虚拟金额,不参与结算对账
|
||||
virtualAmount = new BigDecimal(orderAmount.toString());
|
||||
}
|
||||
|
||||
// 剩余需要退回的金额 residue
|
||||
BigDecimal residue = payAmount.subtract(orderAmount);
|
||||
|
||||
// 把交易记录中的用电量,金额等信息 更新到orderBasicInfo和orderDetail
|
||||
orderBasicInfo.setVirtualAmount(virtualAmount); // 虚拟金额
|
||||
orderBasicInfo.setSettleAmount(orderAmount.subtract(virtualAmount)); // 结算金额
|
||||
orderBasicInfo.setOrderStatus(OrderStatusEnum.ORDER_COMPLETE.getValue());
|
||||
orderBasicInfo.setReason(data.getStopReasonMsg()); // 充电停止原因
|
||||
orderBasicInfo.setSettlementTime(DateUtils.getNowDate()); // 结算时间
|
||||
orderBasicInfo.setRefundAmount(residue); // 结算退款金额
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新数据后的orderDetail对象
|
||||
* 专用方法,其他地方如果要用请仔细检查
|
||||
* 【公共方法】
|
||||
* @return 查询并更新过数据的orderDetail
|
||||
*/
|
||||
protected OrderDetail returnUpdateOrderDetail(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
|
||||
// 更新订单详情 查询订单详情 修改订单数据
|
||||
OrderDetail orderDetail = orderBasicInfoService.getOrderDetailByOrderCode(orderCode);
|
||||
try {
|
||||
// 总电费金额
|
||||
BigDecimal totalElectricityAmount = BigDecimal.ZERO;
|
||||
|
||||
// 尖时段用电量
|
||||
String sharpUsedElectricity = data.getSharpUsedElectricity();
|
||||
if (sharpUsedElectricity != null) {
|
||||
orderDetail.setSharpUsedElectricity(new BigDecimal(sharpUsedElectricity));
|
||||
if (data.getSharpPrice() != null) {
|
||||
orderDetail.setSharpPrice(new BigDecimal(data.getSharpPrice()));
|
||||
}
|
||||
if (data.getSharpAmount() != null) {
|
||||
orderDetail.setSharpAmount(new BigDecimal(data.getSharpAmount()));
|
||||
}
|
||||
// 计算该时段电费
|
||||
BigDecimal multiply = orderDetail.getSharpElectricityPrice()
|
||||
.multiply(new BigDecimal(sharpUsedElectricity))
|
||||
.setScale(2, RoundingMode.DOWN);
|
||||
totalElectricityAmount = totalElectricityAmount.add(multiply);
|
||||
}
|
||||
|
||||
// 峰时段用电量
|
||||
String peakUsedElectricity = data.getPeakUsedElectricity();
|
||||
if (peakUsedElectricity != null) {
|
||||
orderDetail.setPeakUsedElectricity(new BigDecimal(peakUsedElectricity));
|
||||
if (data.getPeakPrice() != null) {
|
||||
orderDetail.setPeakPrice(new BigDecimal(data.getPeakPrice()));
|
||||
}
|
||||
if (data.getPeakAmount() != null) {
|
||||
orderDetail.setPeakAmount(new BigDecimal(data.getPeakAmount()));
|
||||
}
|
||||
// 计算该时段电费
|
||||
BigDecimal multiply = orderDetail.getPeakElectricityPrice()
|
||||
.multiply(new BigDecimal(peakUsedElectricity))
|
||||
.setScale(2, RoundingMode.DOWN);
|
||||
totalElectricityAmount = totalElectricityAmount.add(multiply);
|
||||
}
|
||||
|
||||
// 平时段用电量
|
||||
String flatUsedElectricity = data.getFlatUsedElectricity();
|
||||
if (flatUsedElectricity != null) {
|
||||
orderDetail.setFlatUsedElectricity(new BigDecimal(flatUsedElectricity));
|
||||
if (data.getFlatPrice() != null) {
|
||||
orderDetail.setFlatPrice(new BigDecimal(data.getFlatPrice()));
|
||||
}
|
||||
if (data.getFlatAmount() != null) {
|
||||
orderDetail.setFlatAmount(new BigDecimal(data.getFlatAmount()));
|
||||
}
|
||||
// 计算该时段电费
|
||||
BigDecimal multiply = orderDetail.getFlatElectricityPrice()
|
||||
.multiply(new BigDecimal(flatUsedElectricity))
|
||||
.setScale(2, RoundingMode.DOWN);
|
||||
totalElectricityAmount = totalElectricityAmount.add(multiply);
|
||||
}
|
||||
|
||||
// 谷时段用电量
|
||||
String valleyUsedElectricity = data.getValleyUsedElectricity();
|
||||
if (valleyUsedElectricity != null) {
|
||||
orderDetail.setValleyUsedElectricity(new BigDecimal(valleyUsedElectricity));
|
||||
if (data.getValleyPrice() != null) {
|
||||
orderDetail.setValleyPrice(new BigDecimal(data.getValleyPrice()));
|
||||
}
|
||||
if (data.getValleyAmount() != null) {
|
||||
orderDetail.setValleyAmount(new BigDecimal(data.getValleyAmount()));
|
||||
}
|
||||
// 计算该时段电费
|
||||
BigDecimal multiply = orderDetail.getValleyElectricityPrice()
|
||||
.multiply(new BigDecimal(valleyUsedElectricity))
|
||||
.setScale(2, RoundingMode.DOWN);
|
||||
totalElectricityAmount = totalElectricityAmount.add(multiply);
|
||||
}
|
||||
|
||||
// 如果算出来的电费金额大于总消费金额,则电费金额等于总消费金额
|
||||
if (totalElectricityAmount.compareTo(orderAmount) > 0) {
|
||||
totalElectricityAmount = orderAmount;
|
||||
}
|
||||
orderDetail.setTotalElectricityAmount(totalElectricityAmount);
|
||||
orderDetail.setTotalServiceAmount(orderAmount.subtract(totalElectricityAmount));
|
||||
orderDetail.setTotalUsedElectricity(new BigDecimal(data.getTotalElectricity())); // 总用电量
|
||||
orderDetail.setTotalOrderAmount(orderAmount); // 订单总金额
|
||||
} catch (Exception e) {
|
||||
logger.error("设置订单详情参数发生异常", e);
|
||||
}
|
||||
return orderDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额支付 计算需要退回的金额
|
||||
* 【公共方法】
|
||||
* @param principalPay 本金支付金额
|
||||
* @param giftPay 赠送金额支付的金额
|
||||
* @param orderAmount 订单消费金额
|
||||
* @return
|
||||
*/
|
||||
protected Map<String, BigDecimal> calculateReturnAmount(BigDecimal principalPay, BigDecimal giftPay, BigDecimal orderAmount) {
|
||||
Map<String, BigDecimal> resultMap = Maps.newHashMap();
|
||||
|
||||
// 消费金额优先使用本金
|
||||
BigDecimal returnPrincipal = null; // 退回本金金额
|
||||
BigDecimal returnGift = null; // 退回赠送金额
|
||||
|
||||
// 余额支付 有3种情况
|
||||
if (principalPay != null && giftPay == null) {
|
||||
// 只有本金支付
|
||||
BigDecimal subtract = principalPay.subtract(orderAmount);
|
||||
if (subtract.compareTo(BigDecimal.ZERO) > 0) {
|
||||
returnPrincipal = subtract;
|
||||
}
|
||||
}
|
||||
if (principalPay == null && giftPay != null) {
|
||||
// 只有赠送金额支付
|
||||
BigDecimal subtract = giftPay.subtract(orderAmount);
|
||||
if (subtract.compareTo(BigDecimal.ZERO) > 0) {
|
||||
returnGift = subtract;
|
||||
}
|
||||
}
|
||||
if (principalPay != null && giftPay != null) {
|
||||
// 本金+赠送支付
|
||||
BigDecimal subtract = principalPay.subtract(orderAmount);
|
||||
if (subtract.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 本金减掉订单金额后还有剩余,那就把剩余的退回,赠送原封不动退回
|
||||
returnPrincipal = subtract;
|
||||
returnGift = giftPay;
|
||||
} else if (subtract.compareTo(BigDecimal.ZERO) == 0) {
|
||||
// 本金刚好够,那赠送金额支付的原封不动退回
|
||||
returnGift = giftPay;
|
||||
} else {
|
||||
returnGift = giftPay.subtract(subtract.negate());
|
||||
}
|
||||
}
|
||||
|
||||
resultMap.put("returnPrincipal", returnPrincipal);
|
||||
resultMap.put("returnGift", returnGift);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// BigDecimal orderAmount= new BigDecimal("0.06");
|
||||
// List<OrderPayRecord> payRecordList = Lists.newArrayList();
|
||||
String deductionRecord = "[{\"paymentId\":\"002212023091416010010548305359259512832\",\"amount\":0.97},{\"paymentId\":\"002212023091416110010548307874223849472\",\"amount\":1.00}]";
|
||||
// payRecordList.add(OrderPayRecord.builder().deductionRecord(deductionRecord).build());
|
||||
|
||||
Object object = JSON.parse(deductionRecord);
|
||||
|
||||
// 都放入list里
|
||||
List<PaymentInfo> resultList = Lists.newArrayList();
|
||||
String s = JSON.toJSONString(object);
|
||||
if (object instanceof JSONArray) {
|
||||
List<PaymentInfo> paymentInfos = JSON.parseArray(s, PaymentInfo.class);
|
||||
resultList.addAll(paymentInfos);
|
||||
} else {
|
||||
PaymentInfo paymentInfo = JSON.parseObject(s, PaymentInfo.class);
|
||||
resultList.add(paymentInfo);
|
||||
}
|
||||
System.out.println(JSON.toJSONString(resultList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算解冻金额
|
||||
* @param orderAmount 订单消费金额
|
||||
* @param payRecordList 订单支付记录
|
||||
*/
|
||||
public List<Map<String, Object>> calculateUnfreezeAmount(BigDecimal orderAmount, List<OrderPayRecord> payRecordList) {
|
||||
List<Map<String, Object>> resultList = Lists.newArrayList();
|
||||
|
||||
BigDecimal tempAmount = new BigDecimal(orderAmount.toString()); // 临时金额
|
||||
for (OrderPayRecord record : payRecordList) {
|
||||
List<PaymentInfo> paymentInfos = parseDeductionRecord(record.getDeductionRecord());
|
||||
for (PaymentInfo object : paymentInfos) {
|
||||
String paymentId = object.getPaymentId();
|
||||
BigDecimal payAmount = new BigDecimal(object.getAmount()); // 此交易单支付的金额
|
||||
|
||||
// 该笔支付解冻金额
|
||||
BigDecimal unfreezeAmount;
|
||||
|
||||
if (tempAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
unfreezeAmount = payAmount;
|
||||
Map<String, Object> map = Maps.newHashMap();
|
||||
map.put("paymentId", paymentId);
|
||||
map.put("unfreezeAmount", unfreezeAmount);
|
||||
resultList.add(map);
|
||||
} else {
|
||||
BigDecimal min = tempAmount.min(payAmount);
|
||||
unfreezeAmount = payAmount.subtract(min);
|
||||
tempAmount = tempAmount.subtract(min);
|
||||
Map<String, Object> map = Maps.newHashMap();
|
||||
map.put("paymentId", paymentId);
|
||||
map.put("unfreezeAmount", unfreezeAmount);
|
||||
resultList.add(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析deductionRecord
|
||||
* 【公共方法】
|
||||
*/
|
||||
public List<PaymentInfo> parseDeductionRecord(String deductionRecord) {
|
||||
List<PaymentInfo> resultList = Lists.newArrayList();
|
||||
if (StringUtils.isBlank(deductionRecord)) {
|
||||
return resultList;
|
||||
}
|
||||
Object object = JSON.parse(deductionRecord);
|
||||
|
||||
// 都放入list里
|
||||
String s = JSON.toJSONString(object);
|
||||
if (object instanceof JSONArray) {
|
||||
List<PaymentInfo> paymentInfos = JSON.parseArray(s, PaymentInfo.class);
|
||||
resultList.addAll(paymentInfos);
|
||||
} else {
|
||||
PaymentInfo paymentInfo = JSON.parseObject(s, PaymentInfo.class);
|
||||
resultList.add(paymentInfo);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从redis中取出实时记录保存到表中j
|
||||
* 当订单完成的时候调用
|
||||
* 【公共方法】
|
||||
*/
|
||||
protected void realTimeMonitorDataRedis2DB(String transactionCode, String orderCode) {
|
||||
try {
|
||||
if (StringUtils.isBlank(transactionCode) || StringUtils.isBlank(orderCode)) {
|
||||
return;
|
||||
}
|
||||
// 校验有没有保存过
|
||||
OrderMonitorData orderMonitorData = orderMonitorDataService.selectByOrderCode(orderCode);
|
||||
if (orderMonitorData != null) {
|
||||
return;
|
||||
}
|
||||
List<RealTimeMonitorData> chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(transactionCode);
|
||||
if (CollectionUtils.isEmpty(chargingRealTimeData)) {
|
||||
return;
|
||||
}
|
||||
List<RealTimeMonitorData> list = Lists.newArrayList();
|
||||
for (RealTimeMonitorData data : chargingRealTimeData) {
|
||||
RealTimeMonitorData build = RealTimeMonitorData.builder()
|
||||
.outputCurrent(data.getOutputCurrent())
|
||||
.outputCurrent(data.getOutputCurrent()) // 电流
|
||||
.outputVoltage(data.getOutputVoltage()) // 电压
|
||||
.outputPower(data.getOutputPower()) // 功率
|
||||
.SOC(data.getSOC()) // soc
|
||||
.dateTime(data.getDateTime()) // 时间
|
||||
.batteryMaxTemperature(data.getBatteryMaxTemperature())
|
||||
.chargingAmount(data.getChargingAmount())
|
||||
.chargingDegree(data.getChargingDegree())
|
||||
.sumChargingTime(data.getSumChargingTime())
|
||||
.timeRemaining(data.getTimeRemaining())
|
||||
.gunLineTemperature(data.getGunLineTemperature())
|
||||
.build();
|
||||
list.add(build);
|
||||
}
|
||||
|
||||
OrderMonitorData record = new OrderMonitorData();
|
||||
record.setOrderCode(orderCode);
|
||||
record.setTransactionCode(transactionCode);
|
||||
record.setMonitorData(JSONObject.toJSONString(list));
|
||||
int insert = orderMonitorDataService.insertSelective(record);
|
||||
if (insert > 0) {
|
||||
// 删除redis中缓存
|
||||
String pileConnectorCode = transactionCode.substring(0, 16);
|
||||
String redisKey = CacheConstants.PILE_REAL_TIME_MONITOR_DATA + pileConnectorCode + "_" + transactionCode;
|
||||
redisCache.deleteObject(redisKey);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("redis中取出实时记录保存到表发生异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡状态解锁
|
||||
* 【公共方法】
|
||||
*/
|
||||
protected void cardStatusUnlocked(String logicCard) {
|
||||
try {
|
||||
// 根据物理卡号查出当前为锁定状态的卡
|
||||
PileAuthCard cardInfo = pileAuthCardService.selectSomeStatusCardInfo(CardStatusEnum.START_LOCK.getCode(), logicCard);
|
||||
if (cardInfo != null) {
|
||||
// 将此卡状态改为正常
|
||||
cardInfo.setStatus(CardStatusEnum.NORMAL.getCode());
|
||||
pileAuthCardService.updatePileAuthCard(cardInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("解锁卡状态 error,", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁vin状态
|
||||
* 【公共方法】
|
||||
* @param vinCode
|
||||
*/
|
||||
protected void vinStatusUnlocked(String vinCode) {
|
||||
try {
|
||||
MemberPlateNumberRelation plateInfo = memberPlateNumberRelationService.getMemberPlateInfoByVinCode(vinCode);
|
||||
if (plateInfo != null && (StringUtils.equals(plateInfo.getVinStatus(), "2"))) {
|
||||
plateInfo.setVinStatus("1");
|
||||
memberPlateNumberRelationService.updateMemberPlateNumberRelation(plateInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("解锁vin状态 error,", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,736 @@
|
||||
package com.jsowell.pile.service.programlogic;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.huifu.adapay.core.exception.BaseAdaPayException;
|
||||
import com.huifu.adapay.model.Payment;
|
||||
import com.jsowell.adapay.common.CreateAdaPaymentParam;
|
||||
import com.jsowell.adapay.config.AbstractAdapayConfig;
|
||||
import com.jsowell.adapay.factory.AdapayConfigFactory;
|
||||
import com.jsowell.adapay.response.PaymentReverseResponse;
|
||||
import com.jsowell.adapay.vo.PaymentInfo;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
|
||||
import com.jsowell.common.enums.AcquirerEnum;
|
||||
import com.jsowell.common.enums.DelFlagEnum;
|
||||
import com.jsowell.common.enums.MemberWalletEnum;
|
||||
import com.jsowell.common.enums.adapay.MerchantDelayModeEnum;
|
||||
import com.jsowell.common.enums.ykc.*;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.util.AdapayUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.id.IdUtils;
|
||||
import com.jsowell.common.util.id.SnowflakeIdWorker;
|
||||
import com.jsowell.pile.domain.*;
|
||||
import com.jsowell.pile.dto.*;
|
||||
import com.jsowell.pile.transaction.dto.OrderTransactionDTO;
|
||||
import com.jsowell.pile.vo.uniapp.MemberVO;
|
||||
import com.jsowell.pile.vo.web.BalanceDeductionAmountVO;
|
||||
import com.jsowell.pile.vo.web.BillingTemplateVO;
|
||||
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
|
||||
import com.jsowell.wxpay.dto.WechatSendMsgDTO;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 设置延时分账的运营商订单逻辑
|
||||
*/
|
||||
@Service
|
||||
public class DelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 注册为 延时分账
|
||||
ProgramLogicFactory.register(MerchantDelayModeEnum.DELAY.getValue(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Override
|
||||
public OrderBasicInfo generateOrder(GenerateOrderDTO dto) throws ParseException {
|
||||
logger.info("【{}】-生成订单start, param:{}", this.getClass().getSimpleName(), JSONObject.toJSONString(dto));
|
||||
// 处理前端传的参数
|
||||
analysisPileParameter(dto);
|
||||
|
||||
// 校验充电桩相关的信息
|
||||
checkPileInfo(dto);
|
||||
|
||||
// 保存订单到数据库 saveOrder2Database
|
||||
return saveOrder2Database(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单编号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String generateNewOrderCode() {
|
||||
while (true) {
|
||||
String orderCode = "C" + IdUtils.getOrderCode();
|
||||
// 通过orderCode查询是否已经存在
|
||||
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
|
||||
if (orderBasicInfo == null) {
|
||||
return orderCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存订单信息到数据库
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
protected OrderBasicInfo saveOrder2Database(GenerateOrderDTO dto) throws ParseException {
|
||||
String orderCode = generateNewOrderCode();
|
||||
String transactionCode = IdUtils.generateTransactionCode(dto.getPileSn(), dto.getConnectorCode());
|
||||
|
||||
if (StringUtils.isBlank(dto.getStartType())) {
|
||||
dto.setStartType(StartTypeEnum.NOW.getValue());
|
||||
}
|
||||
|
||||
String stationId = dto.getPileConnector().getStationId();
|
||||
// 查询站点信息
|
||||
PileStationInfo pileStationInfo = pileStationInfoService.selectPileStationInfoById(Long.valueOf(stationId));
|
||||
String merchantId = pileStationInfo != null ? String.valueOf(pileStationInfo.getMerchantId()) : "";
|
||||
String plateNumber = dto.getPlateNumber() != null ? dto.getPlateNumber() : "";
|
||||
// 订单基本信息
|
||||
OrderBasicInfo orderBasicInfo = OrderBasicInfo.builder()
|
||||
.orderCode(orderCode)
|
||||
.transactionCode(transactionCode)
|
||||
.orderStatus(OrderStatusEnum.NOT_START.getValue())
|
||||
.memberId(dto.getMemberId())
|
||||
.stationId(stationId)
|
||||
.merchantId(merchantId)
|
||||
.pileSn(dto.getPileSn())
|
||||
.connectorCode(dto.getConnectorCode())
|
||||
.pileConnectorCode(dto.getPileSn() + dto.getConnectorCode())
|
||||
.startMode(dto.getStartMode())
|
||||
.payStatus(Constants.ZERO)
|
||||
.payAmount(dto.getChargeAmount())
|
||||
.payMode(dto.getPayMode())
|
||||
.plateNumber(plateNumber)
|
||||
.orderAmount(BigDecimal.ZERO)
|
||||
.virtualAmount(BigDecimal.ZERO)
|
||||
.settleAmount(BigDecimal.ZERO)
|
||||
.startType(dto.getStartType())
|
||||
.build();
|
||||
if (StringUtils.equals(dto.getStartMode(), StartModeEnum.AUTH_CARD.getValue())) {
|
||||
// 鉴权卡启动
|
||||
orderBasicInfo.setLogicCard(dto.getPileAuthCardInfo().getLogicCard());
|
||||
}
|
||||
if (StringUtils.equals(dto.getStartMode(), StartModeEnum.VIN_CODE.getValue())) {
|
||||
// vin启动
|
||||
orderBasicInfo.setVinCode(dto.getMemberPlateNumberRelation().getVinCode());
|
||||
}
|
||||
|
||||
if (StringUtils.equals(dto.getStartType(), StartTypeEnum.APPOINTMENT.getValue())) {
|
||||
orderBasicInfo.setAppointmentTime(DateUtils.parseDate(dto.getAppointmentTime(), DateUtils.YYYY_MM_DD_HH_MM_SS));
|
||||
}
|
||||
|
||||
// 订单详情
|
||||
BillingTemplateVO billingTemplate = dto.getBillingTemplate();
|
||||
logger.info("订单使用的计费模板-orderCode:{}, billingTemplate:{}", orderCode, JSONObject.toJSONString(billingTemplate));
|
||||
BigDecimal sharpElectricityPrice = billingTemplate.getSharpElectricityPrice() != null ? billingTemplate.getSharpElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal sharpServicePrice = billingTemplate.getSharpServicePrice() != null ? billingTemplate.getSharpServicePrice() : BigDecimal.ZERO;
|
||||
BigDecimal peakElectricityPrice = billingTemplate.getPeakElectricityPrice() != null ? billingTemplate.getPeakElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal peakServicePrice = billingTemplate.getPeakServicePrice() != null ? billingTemplate.getPeakServicePrice() : BigDecimal.ZERO;
|
||||
BigDecimal flatElectricityPrice = billingTemplate.getFlatElectricityPrice() != null ? billingTemplate.getFlatElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal flatServicePrice = billingTemplate.getFlatServicePrice() != null ? billingTemplate.getFlatServicePrice() : BigDecimal.ZERO;
|
||||
BigDecimal valleyElectricityPrice = billingTemplate.getValleyElectricityPrice() != null ? billingTemplate.getValleyElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal valleyServicePrice = billingTemplate.getValleyServicePrice() != null ? billingTemplate.getValleyServicePrice() : BigDecimal.ZERO;
|
||||
|
||||
OrderDetail orderDetail = OrderDetail.builder()
|
||||
.orderCode(orderCode)
|
||||
.sharpPrice(sharpElectricityPrice.add(sharpServicePrice))
|
||||
.sharpElectricityPrice(sharpElectricityPrice)
|
||||
.sharpServicePrice(sharpServicePrice)
|
||||
.peakPrice(peakElectricityPrice.add(peakServicePrice))
|
||||
.peakElectricityPrice(peakElectricityPrice)
|
||||
.peakServicePrice(peakServicePrice)
|
||||
.flatPrice(flatElectricityPrice.add(flatServicePrice))
|
||||
.flatElectricityPrice(flatElectricityPrice)
|
||||
.flatServicePrice(flatServicePrice)
|
||||
.valleyPrice(valleyElectricityPrice.add(valleyServicePrice))
|
||||
.valleyElectricityPrice(valleyElectricityPrice)
|
||||
.valleyServicePrice(valleyServicePrice)
|
||||
.build();
|
||||
|
||||
OrderTransactionDTO createOrderTransactionDTO = OrderTransactionDTO.builder()
|
||||
.orderBasicInfo(orderBasicInfo)
|
||||
.orderDetail(orderDetail)
|
||||
.build();
|
||||
transactionService.doCreateOrder(createOrderTransactionDTO);
|
||||
return orderBasicInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> payOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
|
||||
if (orderInfo == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_INFO_IS_NULL);
|
||||
}
|
||||
if (!StringUtils.equals(orderInfo.getPayStatus(), OrderPayStatusEnum.unpaid.getValue())) {
|
||||
// 订单已支付
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_ORDER_IS_NOT_TO_BE_PAID_ERROR);
|
||||
}
|
||||
Map<String, Object> resultMap = Maps.newHashMap();
|
||||
if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
|
||||
// 余额支付
|
||||
balancePayOrderV2(dto);
|
||||
} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
|
||||
// 2023-07-11 全部改为汇付支付
|
||||
dto.setGoodsTitle("充电费用");
|
||||
dto.setGoodsDesc("充电桩预付款金额");
|
||||
dto.setType(ScenarioEnum.ORDER.getValue());
|
||||
Map<String, Object> weixinMap = onlinePaymentOrder(dto);
|
||||
|
||||
// 返回微信支付参数
|
||||
resultMap.put("weixinMap", weixinMap);
|
||||
} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())) { // 白名单支付
|
||||
// 白名单支付可以直接调支付回调方法
|
||||
dto.setPayAmount(new BigDecimal("500"));
|
||||
whitelistPaymentOrder(dto);
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额支付订单
|
||||
* 使用的是通过汇付充值的余额
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void balancePayOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-余额支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
// 订单编号
|
||||
String orderCode = dto.getOrderCode();
|
||||
// 支付金额
|
||||
BigDecimal chargeAmount = dto.getPayAmount();
|
||||
// 查询该会员的余额
|
||||
MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId());
|
||||
BigDecimal totalAccountAmount = memberVO.getPrincipalBalance();
|
||||
|
||||
if (totalAccountAmount.compareTo(chargeAmount) < 0) {
|
||||
// 总余额小于充电金额
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT);
|
||||
}
|
||||
BigDecimal principalPay = chargeAmount;
|
||||
|
||||
// 更新会员钱包
|
||||
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
|
||||
.memberId(dto.getMemberId())
|
||||
.type(MemberWalletEnum.TYPE_OUT.getValue())
|
||||
.subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
|
||||
.updatePrincipalBalance(principalPay) // 使用本金支付的金额
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
// 查询余额充值有剩余的记录
|
||||
List<BalanceDeductionAmountVO> list = calculateTheBalanceDeductionAmount(dto.getMemberId(), chargeAmount);
|
||||
|
||||
// 记录订单支付流水
|
||||
BigDecimal payAmt = BigDecimal.ZERO;
|
||||
List<PaymentInfo> paymentInfos = Lists.newArrayList();
|
||||
for (BalanceDeductionAmountVO balanceDeductionAmountVO : list) {
|
||||
String paymentId = balanceDeductionAmountVO.getPaymentId();
|
||||
// 此交易单扣除金额
|
||||
BigDecimal deductionAmount = balanceDeductionAmountVO.getDeductionAmount();
|
||||
payAmt = payAmt.add(deductionAmount);
|
||||
|
||||
PaymentInfo paymentInfo = new PaymentInfo();
|
||||
paymentInfo.setPaymentId(paymentId);
|
||||
paymentInfo.setAmount(deductionAmount.toString());
|
||||
paymentInfos.add(paymentInfo);
|
||||
}
|
||||
OrderPayRecord build = OrderPayRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.payMode(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
|
||||
.payAmount(payAmt)
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.deductionRecord(JSON.toJSONString(paymentInfos))
|
||||
.createBy(dto.getMemberId())
|
||||
.delFlag(DelFlagEnum.NORMAL.getValue())
|
||||
.build();
|
||||
|
||||
// 记录支订单付流水
|
||||
List<OrderPayRecord> payRecordList = Lists.newArrayList(build);
|
||||
orderPayRecordService.batchInsert(payRecordList);
|
||||
|
||||
// 把消费金额冻结
|
||||
for (OrderPayRecord record : payRecordList) {
|
||||
List<PaymentInfo> paymentInfoList = parseDeductionRecord(record.getDeductionRecord());
|
||||
// 循环冻结金额
|
||||
for (PaymentInfo paymentInfo : paymentInfoList) {
|
||||
String paymentId = paymentInfo.getPaymentId();
|
||||
BigDecimal amount = new BigDecimal(paymentInfo.getAmount());
|
||||
// 余额支付 临时冻结金额
|
||||
memberAdapayRecordService.updateFreezeAmount(paymentId, amount);
|
||||
}
|
||||
}
|
||||
|
||||
// 余额支付可以直接调支付回调方法
|
||||
PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
|
||||
.orderCode(orderCode)
|
||||
.payAmount(chargeAmount)
|
||||
.payMode(dto.getPayMode())
|
||||
.startMode(dto.getStartMode())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.build();
|
||||
payOrderSuccessCallback(callbackDTO);
|
||||
|
||||
// 余额支付订单 记录会员交易流水
|
||||
MemberTransactionRecord record = MemberTransactionRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.scenarioType(ScenarioEnum.ORDER.getValue())
|
||||
.memberId(memberVO.getMemberId())
|
||||
.actionType(ActionTypeEnum.FORWARD.getValue())
|
||||
.payMode(PayModeEnum.PAYMENT_OF_BALANCE.getValue())
|
||||
.paymentInstitutions(PaymentInstitutionsEnum.LOCAL_ACCOUNTS.getValue())
|
||||
.amount(dto.getPayAmount()) // 单位元
|
||||
.build();
|
||||
memberTransactionRecordService.insertSelective(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 万车充小程序-余额支付订单逻辑
|
||||
* 使用的是通过后管充值的余额
|
||||
* 包括赠送金
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void balancePayOrderV2(PayOrderDTO dto) {
|
||||
logger.info("【{}】-余额支付订单V2start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
// 订单编号
|
||||
String orderCode = dto.getOrderCode();
|
||||
// 查订单详情
|
||||
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
|
||||
// 订单所属运营商id
|
||||
String merchantId = orderBasicInfo.getMerchantId();
|
||||
// 支付金额
|
||||
BigDecimal chargeAmount = dto.getPayAmount();
|
||||
// 会员id
|
||||
String memberId = dto.getMemberId();
|
||||
// 查询该会员的余额 在所属运营商的余额
|
||||
MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId(), merchantId);
|
||||
// 总余额
|
||||
BigDecimal totalAccountAmount = memberVO.getTotalAccountAmount();
|
||||
if (totalAccountAmount.compareTo(chargeAmount) < 0) {
|
||||
// 总余额小于充电金额
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT);
|
||||
}
|
||||
BigDecimal principalPay = chargeAmount;
|
||||
|
||||
// 更新会员钱包 全部金额都用于支付订单
|
||||
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
|
||||
.memberId(memberId)
|
||||
.targetMerchantId(merchantId)
|
||||
.type(MemberWalletEnum.TYPE_OUT.getValue())
|
||||
.subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
|
||||
.updatePrincipalBalance(memberVO.getPrincipalBalance()) // 使用本金支付的金额
|
||||
.updateGiftBalance(memberVO.getGiftBalance()) // 赠送金额支付
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
// 本金支付
|
||||
OrderPayRecord principalPayRecord = OrderPayRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.payMode(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
|
||||
.payAmount(memberVO.getPrincipalBalance())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.createBy(memberId)
|
||||
.createTime(DateUtils.getNowDate())
|
||||
.delFlag(DelFlagEnum.NORMAL.getValue())
|
||||
.build();
|
||||
|
||||
// 赠送金支付
|
||||
OrderPayRecord giftPayRecord = OrderPayRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.payMode(OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue())
|
||||
.payAmount(memberVO.getGiftBalance())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.createBy(memberId)
|
||||
.createTime(DateUtils.getNowDate())
|
||||
.delFlag(DelFlagEnum.NORMAL.getValue())
|
||||
.build();
|
||||
|
||||
// 记录支订单付流水 记录两条支付
|
||||
List<OrderPayRecord> payRecordList = Lists.newArrayList(principalPayRecord, giftPayRecord);
|
||||
orderPayRecordService.batchInsert(payRecordList);
|
||||
|
||||
// 余额支付可以直接调支付回调方法
|
||||
PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
|
||||
.orderCode(orderCode)
|
||||
.payAmount(chargeAmount)
|
||||
.payMode(dto.getPayMode())
|
||||
.startMode(dto.getStartMode())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.build();
|
||||
payOrderSuccessCallback(callbackDTO);
|
||||
|
||||
// 余额支付订单 记录会员交易流水
|
||||
MemberTransactionRecord record = MemberTransactionRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.scenarioType(ScenarioEnum.ORDER.getValue())
|
||||
.memberId(memberId)
|
||||
.actionType(ActionTypeEnum.FORWARD.getValue())
|
||||
.payMode(PayModeEnum.PAYMENT_OF_BALANCE.getValue())
|
||||
.paymentInstitutions(PaymentInstitutionsEnum.LOCAL_ACCOUNTS.getValue())
|
||||
.amount(dto.getPayAmount()) // 单位元
|
||||
.build();
|
||||
memberTransactionRecordService.insertSelective(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线支付订单
|
||||
*/
|
||||
public Map<String, Object> onlinePaymentOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-在线支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
Map<String, Object> weixinMap = adapayService.createPayment(dto);
|
||||
return weixinMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 白名单支付订单
|
||||
*/
|
||||
public void whitelistPaymentOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-白名单支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
String orderCode = dto.getOrderCode();
|
||||
BigDecimal payAmount = dto.getPayAmount();
|
||||
String payMode = dto.getPayMode();
|
||||
|
||||
// 白名单直接算支付成功
|
||||
PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
|
||||
.orderCode(orderCode)
|
||||
.payAmount(payAmount)
|
||||
.payMode(payMode)
|
||||
.startMode(dto.getStartMode())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.build();
|
||||
payOrderSuccessCallback(callbackDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单结算
|
||||
*/
|
||||
@Override
|
||||
public void settleOrder(TransactionRecordsData data, OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-结算订单start data:{}, orderBasicInfo:{}", this.getClass().getSimpleName(), data.toString(), JSON.toJSONString(orderBasicInfo));
|
||||
// 判断订单状态
|
||||
if (StringUtils.equals(orderBasicInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())) {
|
||||
logger.info("结算订单:{}, 是订单完成状态", orderBasicInfo.getOrderCode());
|
||||
return;
|
||||
}
|
||||
// 获取更新数据后的orderBasicInfo对象
|
||||
returnUpdateOrderBasicInfo(orderBasicInfo, data);
|
||||
// 获取更新数据后的orderDetail对象/更新订单详情 查询订单详情 修改订单数据
|
||||
OrderDetail orderDetail = returnUpdateOrderDetail(orderBasicInfo, data);
|
||||
// 更新数据库
|
||||
OrderTransactionDTO dto = new OrderTransactionDTO();
|
||||
dto.setOrderBasicInfo(orderBasicInfo);
|
||||
dto.setOrderDetail(orderDetail);
|
||||
transactionService.doUpdateOrder(dto);
|
||||
|
||||
// 订单退款
|
||||
refundOrder(orderBasicInfo);
|
||||
|
||||
// 将卡/vin状态解锁
|
||||
if (!StringUtils.equals("0000000000000000", data.getLogicCard())) {
|
||||
cardStatusUnlocked(orderBasicInfo.getLogicCard());
|
||||
}
|
||||
|
||||
// 如果是vin启动,将启动锁定状态改为正常
|
||||
if (StringUtils.equals(data.getTransactionIdentifier(), "05")) {
|
||||
vinStatusUnlocked(data.getVinCode());
|
||||
}
|
||||
|
||||
// 发送停止充电订阅消息
|
||||
sendMsg(orderBasicInfo);
|
||||
|
||||
// 从redis中取出实时记录保存到表中
|
||||
realTimeMonitorDataRedis2DB(orderBasicInfo.getTransactionCode(), orderBasicInfo.getOrderCode());
|
||||
|
||||
logger.info("结算订单end:{} OrderTransactionDTO:{}", orderBasicInfo.getOrderCode(), JSONObject.toJSONString(dto));
|
||||
}
|
||||
|
||||
// uniApp 发送停止充电订阅消息
|
||||
private void sendMsg(OrderBasicInfo orderBasicInfo) {
|
||||
try {
|
||||
WechatSendMsgDTO wechatSendMsgDTO = new WechatSendMsgDTO();
|
||||
wechatSendMsgDTO.setOrderCode(orderBasicInfo.getOrderCode());
|
||||
Map<String, String> resultMap = wxAppletRemoteService.stopChargingSendMsg(wechatSendMsgDTO);
|
||||
logger.info("小程序发送充电停止推送消息 result:{}", JSON.toJSONString(resultMap));
|
||||
} catch (Exception e) {
|
||||
logger.error("小程序发送充电停止推送消息 error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refundOrder(OrderBasicInfo orderBasicInfo) {
|
||||
try {
|
||||
String payMode = orderBasicInfo.getPayMode();
|
||||
if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
|
||||
// 余额支付
|
||||
balancePaymentOrderRefund(orderBasicInfo);
|
||||
} else if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
|
||||
// 微信支付
|
||||
onlinePaymentOrderRefund(orderBasicInfo);
|
||||
} else {
|
||||
// 白名单支付
|
||||
logger.info("订单:{}使用白名单支付,不进行退款处理", orderBasicInfo.getOrderCode());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("【{}】-订单退款逻辑异常orderCode:{}", this.getClass().getSimpleName(), orderBasicInfo.getOrderCode(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额充值
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> rechargeBalance(WeixinPayDTO dto) {
|
||||
logger.info("【{}】-余额充值start dto:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
// 获取支付配置
|
||||
AbstractAdapayConfig config = AdapayConfigFactory.getConfig(dto.getWechatAppId());
|
||||
if (config == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR);
|
||||
}
|
||||
logger.info("使用汇付支付充值余额 支付配置参数:{}", JSON.toJSONString(config));
|
||||
|
||||
String goodsTitle = "充值余额";
|
||||
String goodsDesc = "会员充值余额";
|
||||
|
||||
// 查询延时支付模式,由一级运营商配置决定
|
||||
String delayMode = pileMerchantInfoService.getDelayModeByWechatAppId(dto.getWechatAppId());
|
||||
String payMode = MerchantDelayModeEnum.getAdapayPayMode(delayMode);
|
||||
// 封装对象
|
||||
// String payMode = Constants.ADAPAY_PAY_MODE_DELAY; // 汇付延时分账
|
||||
CreateAdaPaymentParam createAdaPaymentParam = new CreateAdaPaymentParam();
|
||||
createAdaPaymentParam.setOrder_no(SnowflakeIdWorker.getSnowflakeId());
|
||||
createAdaPaymentParam.setPay_amt(AdapayUtil.formatAmount(dto.getAmount()));
|
||||
// createAdaPaymentParam.setApp_id(ADAPAY_APP_ID); // 移动到配置文件中
|
||||
createAdaPaymentParam.setApp_id(config.getAdapayAppId()); // 移动到配置文件中
|
||||
createAdaPaymentParam.setPay_channel("wx_lite"); // todo 如果以后有支付宝等别的渠道,这里需要做修改,判断是什么渠道的请求
|
||||
createAdaPaymentParam.setGoods_title(goodsTitle);
|
||||
createAdaPaymentParam.setGoods_desc(goodsDesc);
|
||||
createAdaPaymentParam.setExpend(JSONObject.toJSONString(ImmutableMap.of("open_id", dto.getOpenId())));
|
||||
// 异步通知地址,url为http/https路径,服务器POST回调,URL 上请勿附带参数
|
||||
createAdaPaymentParam.setNotify_url(ADAPAY_CALLBACK_URL);
|
||||
Map<String, String> map = Maps.newHashMap();
|
||||
map.put("type", ScenarioEnum.BALANCE.getValue());
|
||||
map.put("payMode", payMode);
|
||||
map.put("memberId", dto.getMemberId());
|
||||
createAdaPaymentParam.setDescription(JSON.toJSONString(map));
|
||||
// 延时分账
|
||||
if (StringUtils.isNotBlank(payMode)) {
|
||||
createAdaPaymentParam.setPay_mode(payMode);
|
||||
}
|
||||
try {
|
||||
logger.info("创建汇付支付参数:{}", JSONObject.toJSONString(createAdaPaymentParam));
|
||||
Map<String, Object> response = Payment.create(BeanMap.create(createAdaPaymentParam), config.getWechatAppId());
|
||||
logger.info("创建汇付支付参数反参:{}", JSONObject.toJSONString(response));
|
||||
if (response != null && !response.isEmpty()) {
|
||||
JSONObject jsonObject = JSONObject.parseObject(response.get("expend").toString());
|
||||
JSONObject pay_info = jsonObject.getJSONObject("pay_info");
|
||||
return JSONObject.parseObject(pay_info.toJSONString(), new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
}
|
||||
} catch (BaseAdaPayException e) {
|
||||
logger.error("汇付-获取支付对象发生异常", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额退款
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Override
|
||||
public void refundBalance(ApplyRefundDTO dto) {
|
||||
logger.info("【{}】-余额退款start dto:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
// 做个拦截,如果前一笔退款没有完成,就抛异常
|
||||
String redisKey = CacheConstants.MEMBER_BALANCE_REFUNDS_ARE_IN_PROGRESS + dto.getMemberId();
|
||||
PaymentReverseResponse redisResult = redisCache.getCacheObject(redisKey);
|
||||
if (Objects.nonNull(redisResult)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_REFUNDS_ARE_IN_PROGRESS_ERROR);
|
||||
}
|
||||
|
||||
// 查会员余额
|
||||
// MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId());
|
||||
// if (memberVO == null) {
|
||||
// throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_NOT_FOUND_ERROR);
|
||||
// }
|
||||
MemberWalletInfo memberWalletInfo = memberWalletInfoService.selectByWalletCode(dto.getWalletCode());
|
||||
if (memberWalletInfo == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_WALLET_NOT_FOUND_ERROR);
|
||||
}
|
||||
|
||||
// 校验退款金额
|
||||
BigDecimal principalBalance = memberWalletInfo.getPrincipalBalance();
|
||||
BigDecimal refundAmount = dto.getRefundAmount();
|
||||
if (refundAmount.compareTo(principalBalance) > 0) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_REFUND_MEMBER_BALANCE_ERROR);
|
||||
}
|
||||
|
||||
// 查询用户充值余额订单 过滤掉已经退款的充值订单
|
||||
List<BalanceDeductionAmountVO> list = calculateTheBalanceDeductionAmount(dto.getMemberId(), refundAmount);
|
||||
for (BalanceDeductionAmountVO vo : list) {
|
||||
String paymentId = vo.getPaymentId();
|
||||
BigDecimal deductionAmount = vo.getDeductionAmount();
|
||||
|
||||
// 调汇付的交易撤销接口
|
||||
PaymentReverseResponse request = adapayService.createPaymentReverseRequest(paymentId, deductionAmount,
|
||||
dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.BALANCE.getValue(), null);
|
||||
|
||||
if (request != null && request.isNotFailed()) {
|
||||
memberAdapayRecordService.updateRefundAmount(paymentId, deductionAmount);
|
||||
|
||||
// 放缓存
|
||||
redisCache.setCacheObject(redisKey, request, CacheConstants.cache_expire_time_30m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额支付订单退款
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void balancePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-余额支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
// 运营商id
|
||||
String merchantId = orderBasicInfo.getMerchantId();
|
||||
// 订单消费金额
|
||||
BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
|
||||
// 查支付记录
|
||||
List<OrderPayRecord> payRecordList = orderPayRecordService.getOrderPayRecordList(orderCode);
|
||||
// 根据payMode分组
|
||||
Map<String, List<OrderPayRecord>> payRecordMap = payRecordList.stream().collect(Collectors.groupingBy(OrderPayRecord::getPayMode));
|
||||
// 获取本金支付的记录
|
||||
List<OrderPayRecord> principalPayRecordList = payRecordMap.get(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue());
|
||||
BigDecimal principalPay = principalPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
// 获取赠送金支付的记录
|
||||
List<OrderPayRecord> giftPayRecordList = payRecordMap.get(OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue());
|
||||
BigDecimal giftPay = giftPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
// 计算需要退回的金额
|
||||
Map<String, BigDecimal> returnAmountMap = calculateReturnAmount(principalPay, giftPay, orderAmount);
|
||||
logger.info("结算订单:{}, 剩余金额退回余额, 订单消费金额:{}, 本金支付金额:{}, 赠送支付金额:{}, 退回金额map:{}",
|
||||
orderCode, orderAmount, principalPay, null, JSONObject.toJSONString(returnAmountMap));
|
||||
|
||||
// 需要退回本金的金额
|
||||
BigDecimal returnPrincipal = returnAmountMap.get("returnPrincipal");
|
||||
// 需要退回赠送金的金额
|
||||
BigDecimal returnGift = returnAmountMap.get("returnGift");
|
||||
|
||||
// 更新会员钱包/余额退回到钱包
|
||||
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
|
||||
.memberId(orderBasicInfo.getMemberId())
|
||||
.targetMerchantId(merchantId)
|
||||
.type(MemberWalletEnum.TYPE_IN.getValue()) // 进账
|
||||
.subType(MemberWalletEnum.SUBTYPE_ORDER_SETTLEMENT_REFUND.getValue()) // 订单结算退款
|
||||
.updatePrincipalBalance(returnPrincipal)
|
||||
.updateGiftBalance(returnGift)
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
// 更新order_pay_record, 解冻部分
|
||||
// List<Map<String, Object>> list = calculateUnfreezeAmount(orderAmount, payRecordList);
|
||||
// for (Map<String, Object> map : list) {
|
||||
// String paymentId = (String) map.get("paymentId");
|
||||
// BigDecimal unfreezeAmount = (BigDecimal) map.get("unfreezeAmount");
|
||||
// memberAdapayRecordService.unfreezeAmount(paymentId, unfreezeAmount);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线支付订单退款
|
||||
*/
|
||||
private void onlinePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-在线支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
|
||||
// 需要退款的金额
|
||||
BigDecimal refundAmount = orderBasicInfo.getRefundAmount();
|
||||
|
||||
// 微信退款逻辑
|
||||
ApplyRefundDTO applyRefundDTO = new ApplyRefundDTO();
|
||||
applyRefundDTO.setOrderCode(orderCode);
|
||||
applyRefundDTO.setRefundType(Constants.ONE);
|
||||
applyRefundDTO.setRefundAmount(refundAmount);
|
||||
|
||||
// 汇付退款需要一级运营商的小程序appId, 否则会退款失败
|
||||
String wechatAppId = pileMerchantInfoService.queryAppIdByMerchantId(orderBasicInfo.getMerchantId());
|
||||
if (StringUtils.isNotBlank(wechatAppId)) {
|
||||
applyRefundDTO.setWechatAppId(wechatAppId);
|
||||
}
|
||||
this.refundOrderWithAdapay(applyRefundDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇付支付-订单退款处理逻辑
|
||||
* 汇付支付订单退款
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
private void refundOrderWithAdapay(ApplyRefundDTO dto) {
|
||||
logger.info("【{}】-汇付支付订单:{}, 执行退款逻辑 param:{}", this.getClass().getSimpleName(), dto.getOrderCode(), JSON.toJSONString(dto));
|
||||
// 查出来原来的支付信息
|
||||
AdapayCallbackRecord callbackRecord = adapayCallbackRecordService.selectByOrderCode(dto.getOrderCode());
|
||||
if (Objects.isNull(callbackRecord)) {
|
||||
logger.error("汇付支付orderCode:{}, 订单退款处理逻辑, 查询订单微信支付记录为空!", dto.getOrderCode());
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_REFUND_ORDER_CALLBACK_RECORD_ERROR);
|
||||
}
|
||||
|
||||
String paymentId = callbackRecord.getPaymentId();
|
||||
// 判断支付金额和退款金额
|
||||
BigDecimal refundAmount = dto.getRefundAmount();
|
||||
BigDecimal payAmt = callbackRecord.getPayAmt();
|
||||
if (refundAmount.compareTo(payAmt) > 0) {
|
||||
logger.error("汇付支付订单号:{}, 退款金额:{}(元),大于付款金额{}(元), 抛出异常", dto.getOrderCode(), refundAmount, payAmt);
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_REFUND_ORDER_AMOUNT_ERROR);
|
||||
}
|
||||
|
||||
// BigDecimal refundAmt = null; // 交易退款金额
|
||||
// 延迟分账未确认调撤销调撤销接口退款
|
||||
// PaymentReverseOperation operation = new PaymentReverseOperation();
|
||||
// operation.setPaymentId(paymentId);
|
||||
// operation.setReverseAmt(refundAmount);
|
||||
// operation.setMerchantKey(dto.getWechatAppId());
|
||||
// operation.setMemberId(dto.getMemberId());
|
||||
// operation.setScenarioType(ScenarioEnum.ORDER.getValue());
|
||||
// operation.setOrderCode(dto.getOrderCode());
|
||||
// PaymentReverseResponse response = adapayService.createPaymentReverseRequest(operation);
|
||||
PaymentReverseResponse response = adapayService.createPaymentReverseRequest(paymentId, refundAmount,
|
||||
dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.ORDER.getValue(), dto.getOrderCode());
|
||||
if (response != null && response.isNotFailed()) {
|
||||
// 交易退款金额
|
||||
BigDecimal refundAmt = new BigDecimal(response.getReverse_amt());
|
||||
memberAdapayRecordService.updateRefundAmountFromFreezeAmount(paymentId, refundAmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
package com.jsowell.pile.service.programlogic;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.huifu.adapay.core.exception.BaseAdaPayException;
|
||||
import com.huifu.adapay.model.Payment;
|
||||
import com.jsowell.adapay.common.CreateAdaPaymentParam;
|
||||
import com.jsowell.adapay.config.AbstractAdapayConfig;
|
||||
import com.jsowell.adapay.factory.AdapayConfigFactory;
|
||||
import com.jsowell.adapay.response.RefundResponse;
|
||||
import com.jsowell.adapay.vo.PaymentInfo;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
|
||||
import com.jsowell.common.enums.AcquirerEnum;
|
||||
import com.jsowell.common.enums.DelFlagEnum;
|
||||
import com.jsowell.common.enums.MemberWalletEnum;
|
||||
import com.jsowell.common.enums.adapay.MerchantDelayModeEnum;
|
||||
import com.jsowell.common.enums.ykc.*;
|
||||
import com.jsowell.common.exception.BusinessException;
|
||||
import com.jsowell.common.util.AdapayUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.id.IdUtils;
|
||||
import com.jsowell.common.util.id.SnowflakeIdWorker;
|
||||
import com.jsowell.pile.domain.*;
|
||||
import com.jsowell.pile.dto.*;
|
||||
import com.jsowell.pile.transaction.dto.OrderTransactionDTO;
|
||||
import com.jsowell.pile.vo.uniapp.MemberVO;
|
||||
import com.jsowell.pile.vo.web.BalanceDeductionAmountVO;
|
||||
import com.jsowell.pile.vo.web.BillingTemplateVO;
|
||||
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
|
||||
import com.jsowell.wxpay.dto.WechatSendMsgDTO;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 没有设置延时分账的运营商订单逻辑
|
||||
*/
|
||||
@Service
|
||||
public class NotDelayMerchantProgramLogic extends AbstractProgramLogic {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 注册为 非延时分账
|
||||
ProgramLogicFactory.register(MerchantDelayModeEnum.NOT_DELAY.getValue(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Override
|
||||
public OrderBasicInfo generateOrder(GenerateOrderDTO dto) throws ParseException {
|
||||
logger.info("【{}】-生成订单start, param:{}", this.getClass().getSimpleName(), JSONObject.toJSONString(dto));
|
||||
// 处理前端传的参数
|
||||
analysisPileParameter(dto);
|
||||
|
||||
// 校验充电桩相关的信息
|
||||
checkPileInfo(dto);
|
||||
|
||||
// 保存订单到数据库 saveOrder2Database
|
||||
return saveOrder2Database(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单编号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String generateNewOrderCode() {
|
||||
while (true) {
|
||||
String orderCode = "C" + IdUtils.getOrderCode();
|
||||
// 通过orderCode查询是否已经存在
|
||||
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
|
||||
if (orderBasicInfo == null) {
|
||||
return orderCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存订单信息到数据库
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
protected OrderBasicInfo saveOrder2Database(GenerateOrderDTO dto) throws ParseException {
|
||||
String orderCode = generateNewOrderCode();
|
||||
String transactionCode = IdUtils.generateTransactionCode(dto.getPileSn(), dto.getConnectorCode());
|
||||
|
||||
if (StringUtils.isBlank(dto.getStartType())) {
|
||||
dto.setStartType(StartTypeEnum.NOW.getValue());
|
||||
}
|
||||
|
||||
String stationId = dto.getPileConnector().getStationId();
|
||||
// 查询站点信息
|
||||
PileStationInfo pileStationInfo = pileStationInfoService.selectPileStationInfoById(Long.valueOf(stationId));
|
||||
String merchantId = pileStationInfo != null ? String.valueOf(pileStationInfo.getMerchantId()) : "";
|
||||
String plateNumber = dto.getPlateNumber() != null ? dto.getPlateNumber() : "";
|
||||
// 订单基本信息
|
||||
OrderBasicInfo orderBasicInfo = OrderBasicInfo.builder()
|
||||
.orderCode(orderCode)
|
||||
.transactionCode(transactionCode)
|
||||
.orderStatus(OrderStatusEnum.NOT_START.getValue())
|
||||
.memberId(dto.getMemberId())
|
||||
.stationId(stationId)
|
||||
.merchantId(merchantId)
|
||||
.pileSn(dto.getPileSn())
|
||||
.connectorCode(dto.getConnectorCode())
|
||||
.pileConnectorCode(dto.getPileSn() + dto.getConnectorCode())
|
||||
.startMode(dto.getStartMode())
|
||||
.payStatus(Constants.ZERO)
|
||||
.payAmount(dto.getChargeAmount())
|
||||
.payMode(dto.getPayMode())
|
||||
.plateNumber(plateNumber)
|
||||
.orderAmount(BigDecimal.ZERO)
|
||||
.virtualAmount(BigDecimal.ZERO)
|
||||
.settleAmount(BigDecimal.ZERO)
|
||||
.startType(dto.getStartType())
|
||||
.build();
|
||||
if (StringUtils.equals(dto.getStartMode(), StartModeEnum.AUTH_CARD.getValue())) {
|
||||
// 鉴权卡启动
|
||||
orderBasicInfo.setLogicCard(dto.getPileAuthCardInfo().getLogicCard());
|
||||
}
|
||||
if (StringUtils.equals(dto.getStartMode(), StartModeEnum.VIN_CODE.getValue())) {
|
||||
// vin启动
|
||||
orderBasicInfo.setVinCode(dto.getMemberPlateNumberRelation().getVinCode());
|
||||
}
|
||||
|
||||
if (StringUtils.equals(dto.getStartType(), StartTypeEnum.APPOINTMENT.getValue())) {
|
||||
orderBasicInfo.setAppointmentTime(DateUtils.parseDate(dto.getAppointmentTime(), DateUtils.YYYY_MM_DD_HH_MM_SS));
|
||||
}
|
||||
|
||||
// 订单详情
|
||||
BillingTemplateVO billingTemplate = dto.getBillingTemplate();
|
||||
logger.info("订单使用的计费模板-orderCode:{}, billingTemplate:{}", orderCode, JSONObject.toJSONString(billingTemplate));
|
||||
BigDecimal sharpElectricityPrice = billingTemplate.getSharpElectricityPrice() != null ? billingTemplate.getSharpElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal sharpServicePrice = billingTemplate.getSharpServicePrice() != null ? billingTemplate.getSharpServicePrice() : BigDecimal.ZERO;
|
||||
BigDecimal peakElectricityPrice = billingTemplate.getPeakElectricityPrice() != null ? billingTemplate.getPeakElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal peakServicePrice = billingTemplate.getPeakServicePrice() != null ? billingTemplate.getPeakServicePrice() : BigDecimal.ZERO;
|
||||
BigDecimal flatElectricityPrice = billingTemplate.getFlatElectricityPrice() != null ? billingTemplate.getFlatElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal flatServicePrice = billingTemplate.getFlatServicePrice() != null ? billingTemplate.getFlatServicePrice() : BigDecimal.ZERO;
|
||||
BigDecimal valleyElectricityPrice = billingTemplate.getValleyElectricityPrice() != null ? billingTemplate.getValleyElectricityPrice() : BigDecimal.ZERO;
|
||||
BigDecimal valleyServicePrice = billingTemplate.getValleyServicePrice() != null ? billingTemplate.getValleyServicePrice() : BigDecimal.ZERO;
|
||||
|
||||
OrderDetail orderDetail = OrderDetail.builder()
|
||||
.orderCode(orderCode)
|
||||
.sharpPrice(sharpElectricityPrice.add(sharpServicePrice))
|
||||
.sharpElectricityPrice(sharpElectricityPrice)
|
||||
.sharpServicePrice(sharpServicePrice)
|
||||
.peakPrice(peakElectricityPrice.add(peakServicePrice))
|
||||
.peakElectricityPrice(peakElectricityPrice)
|
||||
.peakServicePrice(peakServicePrice)
|
||||
.flatPrice(flatElectricityPrice.add(flatServicePrice))
|
||||
.flatElectricityPrice(flatElectricityPrice)
|
||||
.flatServicePrice(flatServicePrice)
|
||||
.valleyPrice(valleyElectricityPrice.add(valleyServicePrice))
|
||||
.valleyElectricityPrice(valleyElectricityPrice)
|
||||
.valleyServicePrice(valleyServicePrice)
|
||||
.build();
|
||||
|
||||
OrderTransactionDTO createOrderTransactionDTO = OrderTransactionDTO.builder()
|
||||
.orderBasicInfo(orderBasicInfo)
|
||||
.orderDetail(orderDetail)
|
||||
.build();
|
||||
transactionService.doCreateOrder(createOrderTransactionDTO);
|
||||
return orderBasicInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> payOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
|
||||
if (orderInfo == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_INFO_IS_NULL);
|
||||
}
|
||||
if (!StringUtils.equals(orderInfo.getPayStatus(), OrderPayStatusEnum.unpaid.getValue())) {
|
||||
// 订单已支付
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_ORDER_IS_NOT_TO_BE_PAID_ERROR);
|
||||
}
|
||||
Map<String, Object> resultMap = Maps.newHashMap();
|
||||
if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
|
||||
// 余额支付
|
||||
balancePayOrder(dto);
|
||||
} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
|
||||
// 2023-07-11 全部改为汇付支付
|
||||
dto.setGoodsTitle("充电费用");
|
||||
dto.setGoodsDesc("充电桩预付款金额");
|
||||
dto.setType(ScenarioEnum.ORDER.getValue());
|
||||
Map<String, Object> weixinMap = onlinePaymentOrder(dto);
|
||||
|
||||
// 返回微信支付参数
|
||||
resultMap.put("weixinMap", weixinMap);
|
||||
} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())) { // 白名单支付
|
||||
// 白名单支付可以直接调支付回调方法
|
||||
dto.setPayAmount(new BigDecimal("500"));
|
||||
whitelistPaymentOrder(dto);
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 余额支付订单
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void balancePayOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-余额支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
String orderCode = dto.getOrderCode(); // 订单编号
|
||||
BigDecimal chargeAmount = dto.getPayAmount(); // 支付金额
|
||||
// 查询该会员的余额
|
||||
MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId());
|
||||
BigDecimal totalAccountAmount = memberVO.getPrincipalBalance();
|
||||
|
||||
if (totalAccountAmount.compareTo(chargeAmount) < 0) {
|
||||
// 总余额小于充电金额
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT);
|
||||
}
|
||||
BigDecimal principalPay = chargeAmount;
|
||||
|
||||
// 更新会员钱包
|
||||
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
|
||||
.memberId(dto.getMemberId())
|
||||
.type(MemberWalletEnum.TYPE_OUT.getValue())
|
||||
.subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
|
||||
.updatePrincipalBalance(principalPay) // 使用本金支付的金额
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
// 查询余额充值有剩余的记录
|
||||
List<BalanceDeductionAmountVO> list = calculateTheBalanceDeductionAmount(dto.getMemberId(), chargeAmount);
|
||||
// 记录支订单付流水
|
||||
List<PaymentInfo> paymentInfos = Lists.newArrayList();
|
||||
BigDecimal deductionAmount = BigDecimal.ZERO;
|
||||
for (BalanceDeductionAmountVO balanceDeductionAmountVO : list) {
|
||||
String paymentId = balanceDeductionAmountVO.getPaymentId();
|
||||
deductionAmount = deductionAmount.add(balanceDeductionAmountVO.getDeductionAmount());
|
||||
|
||||
PaymentInfo paymentInfo = new PaymentInfo();
|
||||
paymentInfo.setPaymentId(paymentId);
|
||||
paymentInfo.setAmount(deductionAmount.toString());
|
||||
paymentInfos.add(paymentInfo);
|
||||
}
|
||||
// 记录流水
|
||||
OrderPayRecord orderPayRecord = OrderPayRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.payMode(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
|
||||
.payAmount(deductionAmount)
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.deductionRecord(JSON.toJSONString(paymentInfos))
|
||||
.createBy(dto.getMemberId())
|
||||
.delFlag(DelFlagEnum.NORMAL.getValue())
|
||||
.build();
|
||||
|
||||
// 订单支付流水入库
|
||||
List<OrderPayRecord> payRecordList = Lists.newArrayList(orderPayRecord);
|
||||
orderPayRecordService.batchInsert(payRecordList);
|
||||
|
||||
// 把消费金额冻结
|
||||
for (OrderPayRecord record : payRecordList) {
|
||||
List<PaymentInfo> paymentInfoList = parseDeductionRecord(record.getDeductionRecord());
|
||||
// 循环冻结金额
|
||||
for (PaymentInfo paymentInfo : paymentInfoList) {
|
||||
String paymentId = paymentInfo.getPaymentId();
|
||||
BigDecimal amount = new BigDecimal(paymentInfo.getAmount());
|
||||
// 余额支付 临时冻结金额
|
||||
memberAdapayRecordService.updateFreezeAmount(paymentId, amount);
|
||||
}
|
||||
}
|
||||
|
||||
// 余额支付可以直接调支付回调方法
|
||||
PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
|
||||
.orderCode(orderCode)
|
||||
.payAmount(chargeAmount)
|
||||
.payMode(dto.getPayMode())
|
||||
.startMode(dto.getStartMode())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.build();
|
||||
payOrderSuccessCallback(callbackDTO);
|
||||
|
||||
// 余额支付订单 记录会员交易流水
|
||||
MemberTransactionRecord record = MemberTransactionRecord.builder()
|
||||
.orderCode(orderCode)
|
||||
.scenarioType(ScenarioEnum.ORDER.getValue())
|
||||
.memberId(memberVO.getMemberId())
|
||||
.actionType(ActionTypeEnum.FORWARD.getValue())
|
||||
.payMode(PayModeEnum.PAYMENT_OF_BALANCE.getValue())
|
||||
.paymentInstitutions(PaymentInstitutionsEnum.LOCAL_ACCOUNTS.getValue())
|
||||
.amount(dto.getPayAmount()) // 单位元
|
||||
.build();
|
||||
memberTransactionRecordService.insertSelective(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线支付订单
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
public Map<String, Object> onlinePaymentOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-在线支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
Map<String, Object> weixinMap = adapayService.createPayment(dto);
|
||||
return weixinMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 白名单支付订单
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
public void whitelistPaymentOrder(PayOrderDTO dto) {
|
||||
logger.info("【{}】-白名单支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
String orderCode = dto.getOrderCode();
|
||||
BigDecimal payAmount = dto.getPayAmount();
|
||||
String payMode = dto.getPayMode();
|
||||
|
||||
// 白名单直接算支付成功
|
||||
PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
|
||||
.orderCode(orderCode)
|
||||
.payAmount(payAmount)
|
||||
.payMode(payMode)
|
||||
.startMode(dto.getStartMode())
|
||||
.acquirer(AcquirerEnum.LOCAL.getValue())
|
||||
.build();
|
||||
payOrderSuccessCallback(callbackDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单结算
|
||||
*
|
||||
* @param data
|
||||
* @param orderBasicInfo
|
||||
*/
|
||||
@Override
|
||||
public void settleOrder(TransactionRecordsData data, OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-结算订单start data:{}, orderBasicInfo:{}", this.getClass().getSimpleName(), data.toString(), JSON.toJSONString(orderBasicInfo));
|
||||
// 判断订单状态
|
||||
if (StringUtils.equals(orderBasicInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())) {
|
||||
logger.info("结算订单:{}, 是订单完成状态", orderBasicInfo.getOrderCode());
|
||||
return;
|
||||
}
|
||||
// 获取更新数据后的orderBasicInfo对象
|
||||
returnUpdateOrderBasicInfo(orderBasicInfo, data);
|
||||
// 获取更新数据后的orderDetail对象/更新订单详情 查询订单详情 修改订单数据
|
||||
OrderDetail orderDetail = returnUpdateOrderDetail(orderBasicInfo, data);
|
||||
// 更新数据库
|
||||
OrderTransactionDTO dto = new OrderTransactionDTO();
|
||||
dto.setOrderBasicInfo(orderBasicInfo);
|
||||
dto.setOrderDetail(orderDetail);
|
||||
transactionService.doUpdateOrder(dto);
|
||||
|
||||
// 订单退款
|
||||
refundOrder(orderBasicInfo);
|
||||
|
||||
// 将卡/vin状态解锁
|
||||
if (!StringUtils.equals("0000000000000000", data.getLogicCard())) {
|
||||
cardStatusUnlocked(orderBasicInfo.getLogicCard());
|
||||
}
|
||||
|
||||
// 如果是vin启动,将启动锁定状态改为正常
|
||||
if (StringUtils.equals(data.getTransactionIdentifier(), "05")) {
|
||||
vinStatusUnlocked(data.getVinCode());
|
||||
}
|
||||
|
||||
// 发送停止充电订阅消息
|
||||
sendMsg(orderBasicInfo);
|
||||
|
||||
// 从redis中取出实时记录保存到表中
|
||||
realTimeMonitorDataRedis2DB(orderBasicInfo.getTransactionCode(), orderBasicInfo.getOrderCode());
|
||||
|
||||
// TODO 如果该站点的停车场优惠券信息配置不为空,则需绑定一张优惠券
|
||||
|
||||
logger.info("结算订单end:{} OrderTransactionDTO:{}", orderBasicInfo.getOrderCode(), JSONObject.toJSONString(dto));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refundOrder(OrderBasicInfo orderBasicInfo) {
|
||||
try {
|
||||
String payMode = orderBasicInfo.getPayMode();
|
||||
if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
|
||||
// 余额支付
|
||||
balancePaymentOrderRefund(orderBasicInfo);
|
||||
} else if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
|
||||
// 微信支付
|
||||
onlinePaymentOrderRefund(orderBasicInfo);
|
||||
} else {
|
||||
// 白名单支付
|
||||
logger.info("订单:{}使用白名单支付,不进行退款处理", orderBasicInfo.getOrderCode());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("{}-订单退款逻辑异常orderCode:{}", this.getClass().getSimpleName(), orderBasicInfo.getOrderCode(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额充值
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> rechargeBalance(WeixinPayDTO dto) {
|
||||
logger.info("【{}】-余额充值start dto:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
// 获取支付配置
|
||||
AbstractAdapayConfig config = AdapayConfigFactory.getConfig(dto.getWechatAppId());
|
||||
if (config == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR);
|
||||
}
|
||||
logger.info("使用汇付支付充值余额 支付配置参数:{}", JSON.toJSONString(config));
|
||||
|
||||
String goodsTitle = "充值余额";
|
||||
String goodsDesc = "会员充值余额";
|
||||
|
||||
// 查询延时支付模式,由一级运营商配置决定
|
||||
String delayMode = pileMerchantInfoService.getDelayModeByWechatAppId(dto.getWechatAppId());
|
||||
String payMode = MerchantDelayModeEnum.getAdapayPayMode(delayMode);
|
||||
// 封装对象
|
||||
// String payMode = Constants.ADAPAY_PAY_MODE_DELAY; // 汇付延时分账
|
||||
CreateAdaPaymentParam createAdaPaymentParam = new CreateAdaPaymentParam();
|
||||
createAdaPaymentParam.setOrder_no(SnowflakeIdWorker.getSnowflakeId());
|
||||
createAdaPaymentParam.setPay_amt(AdapayUtil.formatAmount(dto.getAmount()));
|
||||
// createAdaPaymentParam.setApp_id(ADAPAY_APP_ID); // 移动到配置文件中
|
||||
createAdaPaymentParam.setApp_id(config.getAdapayAppId()); // 移动到配置文件中
|
||||
createAdaPaymentParam.setPay_channel("wx_lite"); // todo 如果以后有支付宝等别的渠道,这里需要做修改,判断是什么渠道的请求
|
||||
createAdaPaymentParam.setGoods_title(goodsTitle);
|
||||
createAdaPaymentParam.setGoods_desc(goodsDesc);
|
||||
createAdaPaymentParam.setExpend(JSONObject.toJSONString(ImmutableMap.of("open_id", dto.getOpenId())));
|
||||
// 异步通知地址,url为http/https路径,服务器POST回调,URL 上请勿附带参数
|
||||
createAdaPaymentParam.setNotify_url(ADAPAY_CALLBACK_URL);
|
||||
Map<String, String> map = Maps.newHashMap();
|
||||
map.put("type", ScenarioEnum.BALANCE.getValue());
|
||||
map.put("payMode", payMode);
|
||||
map.put("memberId", dto.getMemberId());
|
||||
createAdaPaymentParam.setDescription(JSON.toJSONString(map));
|
||||
try {
|
||||
logger.info("创建汇付支付参数:{}", JSONObject.toJSONString(createAdaPaymentParam));
|
||||
Map<String, Object> response = Payment.create(BeanMap.create(createAdaPaymentParam), config.getWechatAppId());
|
||||
logger.info("创建汇付支付参数反参:{}", JSONObject.toJSONString(response));
|
||||
if (response != null && !response.isEmpty()) {
|
||||
JSONObject jsonObject = JSONObject.parseObject(response.get("expend").toString());
|
||||
JSONObject pay_info = jsonObject.getJSONObject("pay_info");
|
||||
return JSONObject.parseObject(pay_info.toJSONString(), new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
}
|
||||
} catch (BaseAdaPayException e) {
|
||||
logger.error("汇付-获取支付对象发生异常", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额退款
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
@Override
|
||||
public void refundBalance(ApplyRefundDTO dto) {
|
||||
logger.info("【{}】-余额退款start dto:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
|
||||
// 做个拦截,如果前一笔退款没有完成,就抛异常
|
||||
String redisKey = CacheConstants.MEMBER_BALANCE_REFUNDS_ARE_IN_PROGRESS + dto.getMemberId();
|
||||
RefundResponse redisResult = redisCache.getCacheObject(redisKey);
|
||||
if (Objects.nonNull(redisResult)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_REFUNDS_ARE_IN_PROGRESS_ERROR);
|
||||
}
|
||||
|
||||
// 查会员余额
|
||||
// MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId());
|
||||
// if (memberVO == null) {
|
||||
// throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_NOT_FOUND_ERROR);
|
||||
// }
|
||||
|
||||
MemberWalletInfo memberWalletInfo = memberWalletInfoService.selectByWalletCode(dto.getWalletCode());
|
||||
if (memberWalletInfo == null) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_WALLET_NOT_FOUND_ERROR);
|
||||
}
|
||||
|
||||
// 校验退款金额
|
||||
BigDecimal principalBalance = memberWalletInfo.getPrincipalBalance();
|
||||
BigDecimal refundAmount = dto.getRefundAmount();
|
||||
if (refundAmount.compareTo(principalBalance) > 0) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_REFUND_MEMBER_BALANCE_ERROR);
|
||||
}
|
||||
|
||||
// 查询用户充值余额订单 过滤掉已经退款的充值订单
|
||||
List<BalanceDeductionAmountVO> list = calculateTheBalanceDeductionAmount(dto.getMemberId(), refundAmount);
|
||||
logger.info("【{}】-余额退款, 需要退款list:{}", this.getClass().getSimpleName(), JSON.toJSONString(list));
|
||||
for (BalanceDeductionAmountVO vo : list) {
|
||||
String paymentId = vo.getPaymentId();
|
||||
BigDecimal deductionAmount = vo.getDeductionAmount();
|
||||
|
||||
// 调汇付的交易退款接口
|
||||
RefundResponse response = adapayService.createRefundRequest(paymentId, deductionAmount,
|
||||
dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.BALANCE.getValue(), dto.getOrderCode());
|
||||
|
||||
if (response != null && response.isNotFailed()) {
|
||||
memberAdapayRecordService.updateRefundAmount(paymentId, deductionAmount);
|
||||
|
||||
// 放缓存
|
||||
redisCache.setCacheObject(redisKey, response, CacheConstants.cache_expire_time_30m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// uniApp 发送停止充电订阅消息
|
||||
private void sendMsg(OrderBasicInfo orderBasicInfo) {
|
||||
try {
|
||||
WechatSendMsgDTO wechatSendMsgDTO = new WechatSendMsgDTO();
|
||||
wechatSendMsgDTO.setOrderCode(orderBasicInfo.getOrderCode());
|
||||
Map<String, String> resultMap = wxAppletRemoteService.stopChargingSendMsg(wechatSendMsgDTO);
|
||||
logger.info("小程序发送充电停止推送消息 result:{}", JSON.toJSONString(resultMap));
|
||||
} catch (Exception e) {
|
||||
logger.error("小程序发送充电停止推送消息 error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额支付订单退款
|
||||
*
|
||||
* @param orderBasicInfo
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void balancePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-余额支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
// 订单消费金额
|
||||
BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
|
||||
// 查支付记录
|
||||
List<OrderPayRecord> payRecordList = orderPayRecordService.getOrderPayRecordList(orderCode);
|
||||
|
||||
Map<String, OrderPayRecord> payRecordMap = payRecordList.stream()
|
||||
.collect(Collectors.toMap(OrderPayRecord::getPayMode, Function.identity(), (k1, k2) -> k1));
|
||||
// 取出本金支付金额
|
||||
BigDecimal principalPay = null;
|
||||
|
||||
// 获取本金支付的记录
|
||||
OrderPayRecord principalPayRecord = payRecordMap.get(Constants.ONE);
|
||||
if (principalPayRecord != null) {
|
||||
principalPay = principalPayRecord.getPayAmount();
|
||||
}
|
||||
|
||||
// 计算需要退回的金额
|
||||
Map<String, BigDecimal> returnAmountMap = calculateReturnAmount(principalPay, null, orderAmount);
|
||||
logger.info("结算订单:{}, 剩余金额退回余额, 订单消费金额:{}, 本金支付金额:{}, 赠送支付金额:{}, 退回金额map:{}",
|
||||
orderCode, orderAmount, principalPay, null, JSONObject.toJSONString(returnAmountMap));
|
||||
|
||||
// 需要退回本金的金额
|
||||
BigDecimal returnPrincipal = returnAmountMap.get("returnPrincipal");
|
||||
|
||||
// 更新会员钱包/余额退回到钱包
|
||||
UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
|
||||
.memberId(orderBasicInfo.getMemberId())
|
||||
.type(MemberWalletEnum.TYPE_IN.getValue()) // 进账
|
||||
.subType(MemberWalletEnum.SUBTYPE_ORDER_SETTLEMENT_REFUND.getValue()) // 订单结算退款
|
||||
.updatePrincipalBalance(returnPrincipal)
|
||||
.relatedOrderCode(orderCode)
|
||||
.build();
|
||||
memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);
|
||||
|
||||
// 更新order_pay_record, 解冻部分
|
||||
List<Map<String, Object>> list = calculateUnfreezeAmount(orderAmount, payRecordList);
|
||||
for (Map<String, Object> map : list) {
|
||||
String paymentId = (String) map.get("paymentId");
|
||||
BigDecimal unfreezeAmount = (BigDecimal) map.get("unfreezeAmount");
|
||||
memberAdapayRecordService.unfreezeAmount(paymentId, unfreezeAmount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线支付订单退款
|
||||
*
|
||||
* @param orderBasicInfo
|
||||
*/
|
||||
private void onlinePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
|
||||
logger.info("【{}】-在线支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
|
||||
// 订单编号
|
||||
String orderCode = orderBasicInfo.getOrderCode();
|
||||
|
||||
// 需要退款的金额
|
||||
BigDecimal refundAmount = orderBasicInfo.getRefundAmount();
|
||||
|
||||
// 微信退款逻辑
|
||||
ApplyRefundDTO applyRefundDTO = new ApplyRefundDTO();
|
||||
applyRefundDTO.setOrderCode(orderCode);
|
||||
applyRefundDTO.setRefundType(Constants.ONE);
|
||||
applyRefundDTO.setRefundAmount(refundAmount);
|
||||
|
||||
// 汇付退款需要一级运营商的小程序appId, 否则会退款失败
|
||||
String wechatAppId = pileMerchantInfoService.queryAppIdByMerchantId(orderBasicInfo.getMerchantId());
|
||||
if (StringUtils.isNotBlank(wechatAppId)) {
|
||||
applyRefundDTO.setWechatAppId(wechatAppId);
|
||||
}
|
||||
refundOrderWithAdapay(applyRefundDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇付支付-订单退款处理逻辑
|
||||
* 汇付支付订单退款
|
||||
*
|
||||
* @param dto
|
||||
*/
|
||||
private void refundOrderWithAdapay(ApplyRefundDTO dto) {
|
||||
logger.info("【{}】-汇付支付订单:{}, 执行退款逻辑 param:{}", this.getClass().getSimpleName(), dto.getOrderCode(), JSON.toJSONString(dto));
|
||||
// 查出来原来的支付信息
|
||||
AdapayCallbackRecord callbackRecord = adapayCallbackRecordService.selectByOrderCode(dto.getOrderCode());
|
||||
if (Objects.isNull(callbackRecord)) {
|
||||
logger.error("汇付支付orderCode:{}, 订单退款处理逻辑, 查询订单微信支付记录为空!", dto.getOrderCode());
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_REFUND_ORDER_CALLBACK_RECORD_ERROR);
|
||||
}
|
||||
|
||||
String paymentId = callbackRecord.getPaymentId();
|
||||
// 判断支付金额和退款金额
|
||||
BigDecimal refundAmount = dto.getRefundAmount();
|
||||
BigDecimal payAmt = callbackRecord.getPayAmt();
|
||||
if (refundAmount.compareTo(payAmt) > 0) {
|
||||
logger.error("汇付支付订单号:{}, 退款金额:{}(元),大于付款金额{}(元), 抛出异常", dto.getOrderCode(), refundAmount, payAmt);
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_REFUND_ORDER_AMOUNT_ERROR);
|
||||
}
|
||||
|
||||
// 实时分账的调退款接口
|
||||
RefundResponse refundRequest = adapayService.createRefundRequest(paymentId, refundAmount,
|
||||
dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.ORDER.getValue(), dto.getOrderCode());
|
||||
if (refundRequest != null && refundRequest.isNotFailed()) {
|
||||
// 交易退款金额
|
||||
BigDecimal refundAmt = new BigDecimal(refundRequest.getRefund_amt());
|
||||
// 解冻金额并更新消费金额
|
||||
memberAdapayRecordService.unfreezeAmountAndUpdateSpendAmount(paymentId, payAmt, refundAmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.jsowell.pile.service.programlogic;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 工厂设计模式
|
||||
* 云快充操作
|
||||
*/
|
||||
public class ProgramLogicFactory {
|
||||
|
||||
private static Map<String, AbstractProgramLogic> programLogicMap = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* 注册
|
||||
* @param str
|
||||
* @param handler
|
||||
*/
|
||||
public static void register(String str, AbstractProgramLogic handler) {
|
||||
if (StringUtils.isBlank(str) || Objects.isNull(handler)) {
|
||||
return;
|
||||
}
|
||||
programLogicMap.put(str, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static AbstractProgramLogic getProgramLogic(String name) {
|
||||
return programLogicMap.get(name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user