This commit is contained in:
2023-12-07 11:01:45 +08:00
parent f249184cfe
commit 0e9e112885
13 changed files with 74 additions and 74 deletions

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}