From f12f982e2f05918f0359ccc85e02016411614079 Mon Sep 17 00:00:00 2001 From: HH Date: Tue, 21 Mar 2023 12:46:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9A=E6=97=B6=E5=99=A8=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=8F=90=E7=8E=B0=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ghy/web/timer/AdapaySyncTimer.java | 96 +++++++++++++++++++ .../com/ghy/order/domain/OrderDetail.java | 14 ++- .../ghy/order/mapper/OrderDetailMapper.java | 11 +++ .../ghy/order/service/OrderDetailService.java | 19 +++- .../service/impl/OrderDetailServiceImpl.java | 74 +++++++++----- .../mapper/order/OrderDetailMapper.xml | 22 ++++- .../ghy/payment/service/AdapayService.java | 28 ++++++ .../ghy/payment/service/AdapaySyncTimer.java | 76 --------------- .../quartz/service/impl/OrderServiceImpl.java | 38 ++++---- 9 files changed, 247 insertions(+), 131 deletions(-) create mode 100644 ghy-admin/src/main/java/com/ghy/web/timer/AdapaySyncTimer.java delete mode 100644 ghy-payment/src/main/java/com/ghy/payment/service/AdapaySyncTimer.java diff --git a/ghy-admin/src/main/java/com/ghy/web/timer/AdapaySyncTimer.java b/ghy-admin/src/main/java/com/ghy/web/timer/AdapaySyncTimer.java new file mode 100644 index 00000000..1011deb9 --- /dev/null +++ b/ghy-admin/src/main/java/com/ghy/web/timer/AdapaySyncTimer.java @@ -0,0 +1,96 @@ +package com.ghy.web.timer; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ghy.common.adapay.model.AdapayStatusEnum; +import com.ghy.order.service.OrderDetailService; +import com.ghy.payment.domain.DrawCashRecord; +import com.ghy.payment.mapper.DrawCashRecordMapper; +import com.ghy.payment.service.AdapayService; +import com.huifu.adapay.core.exception.BaseAdaPayException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +/** + * 定时器 + * 主动与Adapay同步支付、撤销支付、提现订单的状态 + */ +@Slf4j +@Component +@EnableScheduling +public class AdapaySyncTimer { + + @Resource + private AdapayService adapayService; + @Resource + private OrderDetailService orderDetailService; + @Resource + private DrawCashRecordMapper drawCashRecordMapper; + + @Scheduled(fixedRate = 5 * 60 * 1000L) + public void syncDrawCash() { + List records = drawCashRecordMapper.selectByStatus("pending"); + if (CollectionUtils.isEmpty(records)) { + log.debug("No pending drawCashRecord."); + return; + } + for (DrawCashRecord record : records) { + Long deptId = record.getDept_id(); + String orderNo = record.getOrder_no(); + if (deptId == null || StringUtils.isBlank(orderNo)) { + continue; + } + try { + syncDrawCashRecord(record, deptId, orderNo); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + } + } + + @Transactional(rollbackFor = Exception.class) + public void syncDrawCashRecord(DrawCashRecord record, Long deptId, String orderNo) throws BaseAdaPayException { + JSONObject response = adapayService.queryDrawCash(deptId, orderNo); + // 这个status代表API调用状态 不代表提现状态 + if (AdapayStatusEnum.succeeded.code.equals(response.getString("status"))) { + JSONArray cashList = response.getJSONArray("cash_list"); + if (!CollectionUtils.isEmpty(cashList)) { + JSONObject cash = cashList.getJSONObject(0); + // 这个才是提现状态 + String transStat = cash.getString("trans_stat"); + switch (transStat) { + // 提现成功 + case "S": + log.info("DrawCashRecord[{}]提现成功", record.getId()); + // 更新提现记录表状态 + drawCashRecordMapper.updateStatus(record.getId(), "succeeded"); + // 更新子订单表状态 + orderDetailService.updateDrawCashStatus(record.getId(), 2, new Date()); + break; + // 提现失败 + case "F": + log.error("DrawCashRecord[{}]提现失败: {}", record.getId(), response.toJSONString()); + // 更新提现记录表状态 + drawCashRecordMapper.updateStatus(record.getId(), "failed"); + // 更新子订单表状态 + orderDetailService.updateDrawCashStatus(record.getId(), -1, null); + break; + default: + break; + } + } + } else { + log.error("DrawCash.query请求失败: {}", response.toJSONString()); + } + } +} diff --git a/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java b/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java index 556c0d79..c8b5733a 100644 --- a/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java +++ b/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java @@ -93,18 +93,28 @@ public class OrderDetail extends BaseEntity { private Integer ledgerAccountStatus; + /** + * 发起提现后Adapay返回的对象ID draw_cash_id + */ + private String drawCashId; + /** * draw_cash_status * 0 未分账 * 1 已分账 * 2 已到账 + * -1 提现失败 */ private Integer drawCashStatus; /** - * 发起提现时间 draw_cash_date + * 发起提现时间 draw_cash_time */ - private Date drawCashDate; + private Date drawCashTime; + /** + * 提现到账时间 arrival_time + */ + private Date arrivalTime; @Excel(name = "付款类型", cellType = Excel.ColumnType.NUMERIC) private Integer payType; diff --git a/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java b/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java index dc445947..cc352836 100644 --- a/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java +++ b/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java @@ -4,6 +4,7 @@ import com.ghy.order.domain.OrderDetail; import com.ghy.order.domain.OrderStatusCount; import org.apache.ibatis.annotations.Param; +import java.util.Date; import java.util.List; /** @@ -89,4 +90,14 @@ public interface OrderDetailMapper { List selectByStatus(List status); List statusCount(); + + /** + * 用提现ID更新订单的提现状态 + * + * @param drawCashId 发起提现后Adapay返回的对象ID + * @param drawCashStatus 提现状态见{@link OrderDetail#getDrawCashStatus()} + * @param arrivalTime 提现到账时间 + * @return 1 + */ + int updateDrawCashStatus(@Param("drawCashId") String drawCashId, @Param("drawCashStatus") int drawCashStatus, @Param("arrivalTime") Date arrivalTime); } diff --git a/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java b/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java index 4d22a13a..ee39008d 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java +++ b/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java @@ -6,6 +6,7 @@ import com.ghy.payment.domain.FinancialChangeRecord; import com.huifu.adapay.core.exception.BaseAdaPayException; import java.math.BigDecimal; +import java.util.Date; import java.util.List; import java.util.Map; @@ -113,6 +114,7 @@ public interface OrderDetailService { /** * 统计符合条件的子单数量 + * * @param orderDetail * @return 数量 */ @@ -120,10 +122,11 @@ public interface OrderDetailService { /** * 子单改价接口 + * * @param orderDetailId 子单id - * @param changeMoney 改价金额 - * @param type 报价类型 - * @param remark 备注 + * @param changeMoney 改价金额 + * @param type 报价类型 + * @param remark 备注 * @return 成功/失败 */ int changePrice(Long orderDetailId, BigDecimal changeMoney, Integer type, String remark) throws Exception; @@ -133,4 +136,14 @@ public interface OrderDetailService { FinancialChangeRecord getChangedPriceRecord(Long orderDetailId); List statusCount(); + + /** + * 用提现ID更新订单的提现状态 + * + * @param drawCashId 发起提现后Adapay返回的对象ID + * @param drawCashStatus 提现状态见{@link OrderDetail#getDrawCashStatus()} + * @param arrivalTime 提现到账时间 + * @return 1 + */ + int updateDrawCashStatus(String drawCashId, int drawCashStatus, Date arrivalTime); } diff --git a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java index f9deeb60..8aca3d4f 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java +++ b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java @@ -34,6 +34,7 @@ import com.huifu.adapay.core.exception.BaseAdaPayException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -63,6 +64,8 @@ public class OrderDetailServiceImpl implements OrderDetailService { .appendValue(DAY_OF_MONTH, 2).appendValue(HOUR_OF_DAY, 2) .appendValue(MINUTE_OF_HOUR, 2).appendValue(SECOND_OF_MINUTE, 2).toFormatter(); + @Resource + private ThreadPoolTaskExecutor executor; @Resource private OrderDetailMapper orderDetailMapper; @Resource @@ -319,7 +322,7 @@ public class OrderDetailServiceImpl implements OrderDetailService { } /** - * 子订单确认完成 + * 子订单确认完成 暂时先用 synchronized 来避免重复提交 * 1.修改子订单状态为 OrderStatus.FINISH.code * 2.将子订单对应的子财务单确认分账 * 3.提现至银行卡 @@ -328,7 +331,6 @@ public class OrderDetailServiceImpl implements OrderDetailService { */ @Override @Transactional(rollbackFor = Exception.class) - // 暂时先用 synchronized 来避免重复提交 public synchronized void finish(Long orderDetailId) throws BaseAdaPayException { // 校验订单 OrderDetail orderDetail = selectById(orderDetailId); @@ -363,14 +365,6 @@ public class OrderDetailServiceImpl implements OrderDetailService { fdUpdate.setPayMoney(platformFeeFD.getPayMoney()); logger.debug("子订单[code={}]的完单流程开始", odCode); - // 修改订单状态 - OrderDetail odUpdate = new OrderDetail(); - odUpdate.setId(orderDetailId); - odUpdate.setOrderStatus(OrderStatus.FINISH.code()); - odUpdate.setDrawCashDate(new Date()); - odUpdate.setDrawCashStatus(1); - orderDetailMapper.updateOrderDetail(odUpdate); - // 加价单手续费补偿成功与否 boolean compensate = false; // 加价单手续费 @@ -488,25 +482,48 @@ public class OrderDetailServiceImpl implements OrderDetailService { } // 待提现金额 String cashAmt = AdapayUtils.bigDecimalToString(dtx); - String orderNo = AdapayUtils.createOrderNo(AdapayOrderType.DRAW_CASH); - try { - JSONObject drawCashResponse = adapayService.drawCash(financialDetail.getDeptId(), orderNo, "T1", - cashAmt, memberId, "订单结算", null); - boolean drawCashStatus = AdapayStatusEnum.pending.code.equals(drawCashResponse.getString("status")) || - AdapayStatusEnum.succeeded.code.equals(drawCashResponse.getString("status")); - if (drawCashStatus) { - // 提现成功 - logger.info("子订单[code={}]自动提现成功", odCode); - } else { - // 提现失败 把信息记录到error日志里 - logger.error("自动发起提现失败: 子订单code={}, deptId={} 失败信息: {}", odCode, cashAmt, drawCashResponse.toJSONString()); - } - } catch (Exception e) { - logger.error("自动发起提现失败: 子订单code={}, deptId={}, memberId={}, amount={}", odCode, financialDetail.getDeptId(), memberId, cashAmt, e); - } + executor.execute(() -> drawCash(orderDetailId, financialDetail.getDeptId(), memberId, cashAmt)); // --------------------- 自动提现流程 end --------------------- } + /** + * 子订单分账后提现 + * + * @param orderDetailId 子订单ID + * @param deptId 企业ID + * @param memberId Adapay用户ID + * @param cashAmt 提现金额 + */ + private void drawCash(Long orderDetailId, Long deptId, String memberId, String cashAmt) { + String orderNo = AdapayUtils.createOrderNo(AdapayOrderType.DRAW_CASH); + JSONObject drawCashResponse; + try { + drawCashResponse = adapayService.drawCash(deptId, orderNo, "T1", + cashAmt, memberId, "订单结算", null); + } catch (BaseAdaPayException e) { + logger.error("自动发起提现失败: 子订单id={}, deptId={}", orderDetailId, cashAmt); + return; + } + boolean drawCashStatus = AdapayStatusEnum.pending.code.equals(drawCashResponse.getString("status")) || + AdapayStatusEnum.succeeded.code.equals(drawCashResponse.getString("status")); + if (drawCashStatus) { + // 提现成功 + logger.info("子订单[id={}]自动提现成功", orderDetailId); + // 修改订单状态 + OrderDetail odUpdate = new OrderDetail(); + odUpdate.setId(orderDetailId); + odUpdate.setOrderStatus(OrderStatus.FINISH.code()); + odUpdate.setDrawCashTime(new Date()); + odUpdate.setDrawCashStatus(1); + // 保存提现ID 用于同步Adapay提现状态 + odUpdate.setDrawCashId(drawCashResponse.getString("id")); + orderDetailMapper.updateOrderDetail(odUpdate); + } else { + // 提现失败 把信息记录到error日志里 + logger.error("自动发起提现失败: 子订单id={}, deptId={} 失败信息: {}", orderDetailId, cashAmt, drawCashResponse.toJSONString()); + } + } + @Override public Long countOrderDetailList(OrderDetail orderDetail) { return orderDetailMapper.countOrderDetailList(orderDetail); @@ -576,4 +593,9 @@ public class OrderDetailServiceImpl implements OrderDetailService { public List statusCount() { return orderDetailMapper.statusCount(); } + + @Override + public int updateDrawCashStatus(String drawCashId, int drawCashStatus, Date arrivalTime) { + return orderDetailMapper.updateDrawCashStatus(drawCashId, drawCashStatus, arrivalTime); + } } diff --git a/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml b/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml index 94db67cd..b4759f9f 100644 --- a/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml +++ b/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml @@ -18,7 +18,9 @@ - + + + @@ -44,7 +46,9 @@ expect_time_end, work_begin_time, work_finish_time, - draw_cash_date, + draw_cash_id, + draw_cash_time, + arrival_time, draw_cash_status, clock_in_location, ledger_account_status, @@ -70,7 +74,9 @@ od.expect_time_end, od.work_begin_time, od.work_finish_time, - od.draw_cash_date, + od.draw_cash_id, + od.draw_cash_time, + od.arrival_time, od.draw_cash_status, od.clock_in_location, od.ledger_account_status, @@ -232,7 +238,9 @@ expect_time_end = null, work_begin_time = #{workBeginTime}, work_finish_time = #{workFinishTime}, - draw_cash_date = #{drawCashDate}, + draw_cash_id = #{drawCashId}, + draw_cash_time = #{drawCashTime}, + arrival_time = #{arrivalTime}, draw_cash_status = #{drawCashStatus}, clock_in_location = #{clockInLocation}, remark = #{remark}, @@ -328,4 +336,10 @@ + + UPDATE order_detail + SET draw_cash_status = #{drawCashStatus}, + arrival_time = #{arrivalTime} + WHERE draw_cash_id = #{drawCashId} + diff --git a/ghy-payment/src/main/java/com/ghy/payment/service/AdapayService.java b/ghy-payment/src/main/java/com/ghy/payment/service/AdapayService.java index 2af82381..51c73ff2 100644 --- a/ghy-payment/src/main/java/com/ghy/payment/service/AdapayService.java +++ b/ghy-payment/src/main/java/com/ghy/payment/service/AdapayService.java @@ -203,6 +203,34 @@ public class AdapayService { return (JSONObject) SettleAccount.create(settleCountParams, deptId.toString()); } + /** + * 查询商户或商户下某个用户结算账户的余额 + * + * @param deptId [必填]商户ID + * @param memberId [必填]商户用户对象 id,只能为英文、数字或者下划线的一种或多种组合,若查询商户本身时,传入值0 + * @param settleAccountId 由Adapay生成的结算账户对象id,若查询商户本身时可为空;而当查询商户下的用户时必填 + * @param acctType 账户类型,01或者为空是基本户,02是手续费账户,03是过渡户 + * @return https://docs.adapay.tech/api/wallet.html#id13 + * 成功示例: {"acct_balance": "0.00", + * "app_id": "app_XXXXXXXX", + * "avl_balance": "0.00", + * "frz_balance": "0.00", + * "last_avl_balance": "0.00", + * "object": "account_balance", + * "status": "succeeded", + * "prod_mode": "true"} + */ + public JSONObject queryAccountBalance(@NotNull Long deptId, @NotNull String memberId, String settleAccountId, String acctType) throws BaseAdaPayException { + // 获取商户的appId + String appId = AdapayConfig.getAppId(deptId); + JSONObject queryParams = new JSONObject(5); + queryParams.put("settle_account_id", settleAccountId); + queryParams.put("member_id", memberId); + queryParams.put("app_id", appId); + queryParams.put("acct_type", acctType); + return (JSONObject) SettleAccount.balance(queryParams, deptId.toString()); + } + /** * 删除结算账户对象 https://docs.adapay.tech/api/trade.html#id50 * 删除结算账户对象是对已创建完成的结算账户对象进行删除操作。 diff --git a/ghy-payment/src/main/java/com/ghy/payment/service/AdapaySyncTimer.java b/ghy-payment/src/main/java/com/ghy/payment/service/AdapaySyncTimer.java deleted file mode 100644 index 4624816d..00000000 --- a/ghy-payment/src/main/java/com/ghy/payment/service/AdapaySyncTimer.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.ghy.payment.service; - -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -import com.ghy.common.adapay.model.AdapayStatusEnum; -import com.ghy.payment.domain.DrawCashRecord; -import com.ghy.payment.mapper.DrawCashRecordMapper; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 定时器 - * 主动与Adapay同步支付、撤销支付、提现订单的状态 - */ -@Slf4j -@Component -@EnableScheduling -public class AdapaySyncTimer { - - @Resource - private AdapayService adapayService; - @Resource - private DrawCashRecordMapper drawCashRecordMapper; - - @Scheduled(fixedRate = 5 * 60 * 1000L) - public void syncDrawCash() { - List records = drawCashRecordMapper.selectByStatus("pending"); - if (CollectionUtils.isEmpty(records)) { - log.debug("No pending drawCashRecord."); - return; - } - for (DrawCashRecord record : records) { - Long deptId = record.getDept_id(); - String orderNo = record.getOrder_no(); - if (deptId == null || StringUtils.isBlank(orderNo)) { - continue; - } - try { - JSONObject response = adapayService.queryDrawCash(deptId, orderNo); - // 这个status代表API调用状态 不代表提现状态 - if (AdapayStatusEnum.succeeded.code.equals(response.getString("status"))) { - JSONArray cashList = response.getJSONArray("cash_list"); - if (!CollectionUtils.isEmpty(cashList)) { - JSONObject cash = cashList.getJSONObject(0); - // 这个才是提现状态 - String transStat = cash.getString("trans_stat"); - switch (transStat) { - // 提现成功 - case "S": - drawCashRecordMapper.updateStatus(record.getId(), "succeeded"); - break; - // 提现失败 - case "F": - drawCashRecordMapper.updateStatus(record.getId(), "failed"); - break; - default: - break; - } - } - } else { - log.error("DrawCash.query请求失败: {}", response.toJSONString()); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - - } - } -} diff --git a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java index 1f4cfc48..4e48982e 100644 --- a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java +++ b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java @@ -21,8 +21,6 @@ import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.math.BigDecimal; -import java.time.DayOfWeek; -import java.time.LocalDate; import java.util.Collections; import java.util.Date; import java.util.List; @@ -182,24 +180,24 @@ public class OrderServiceImpl implements OrderService { @Override public void autoChangeDrawCashStatus() { - LocalDate now = LocalDate.now(); - DayOfWeek dayOfWeek = now.getDayOfWeek(); - if (dayOfWeek.getValue() > 5) { - return; - } - OrderDetail orderDetail = new OrderDetail(); - orderDetail.setDrawCashStatus(1); - List orderDetails = orderDetailService.selectOrderDetailList(orderDetail); - long before24h = System.currentTimeMillis() - (24 * 60 * 60 * 1000); - for (OrderDetail od : orderDetails) { - if (od.getDrawCashDate().getTime() < before24h) { - OrderDetail update = new OrderDetail(); - update.setId(od.getId()); - update.setOrderStatus(2); - orderDetailService.updateOrderDetail(update); - logger.info("订单[{}]自动提现成功", od.getCode()); - } - } +// LocalDate now = LocalDate.now(); +// DayOfWeek dayOfWeek = now.getDayOfWeek(); +// if (dayOfWeek.getValue() > 5) { +// return; +// } +// OrderDetail orderDetail = new OrderDetail(); +// orderDetail.setDrawCashStatus(1); +// List orderDetails = orderDetailService.selectOrderDetailList(orderDetail); +// long before24h = System.currentTimeMillis() - (24 * 60 * 60 * 1000); +// for (OrderDetail od : orderDetails) { +// if (od.getDrawCashDate().getTime() < before24h) { +// OrderDetail update = new OrderDetail(); +// update.setId(od.getId()); +// update.setOrderStatus(2); +// orderDetailService.updateOrderDetail(update); +// logger.info("订单[{}]自动提现成功", od.getCode()); +// } +// } } }