From 8f39816f367332becb2bc6cf1eb859d7cb5d2f24 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 27 Sep 2025 11:28:02 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BE=E5=BA=A6=E5=9C=B0=E5=9B=BE=20ak=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=9B=B4=E6=94=B9=EF=BC=8C=E6=8B=89=E5=8D=A1?= =?UTF-8?q?=E6=8B=89=E6=8F=90=E7=8E=B0=E4=BB=A3=E7=A0=81=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/modules/lakala/LklOrderDraw.java | 185 +++++++++++++++ .../src/main/resources/application-dev.yml | 4 +- .../src/main/resources/application-local.yml | 4 +- .../src/main/resources/application-prod.yml | 4 +- .../src/main/resources/application-test.yml | 4 +- .../src/main/resources/application-uat.yml | 4 +- .../admin/LakalaAdminController.java | 23 +- .../controller/mobile/LakalaController.java | 19 ++ .../lakala/mapper/LklOrderDrawMapper.java | 19 ++ .../shop/lakala/service/LakalaApiService.java | 21 ++ .../lakala/service/LklOrderDrawService.java | 32 +++ .../service/impl/LakalaApiServiceImpl.java | 213 ++++++++++++++++++ .../service/impl/LklOrderDrawServiceImpl.java | 105 +++++++++ mall-shop/src/main/resources/application.yml | 34 +-- .../src/main/resources/bootstrap-dev.yml | 38 +++- .../src/main/resources/bootstrap-local.yml | 38 +++- .../src/main/resources/bootstrap-prod.yml | 38 +++- .../src/main/resources/bootstrap-test.yml | 38 +++- .../src/main/resources/bootstrap-uat.yml | 38 +++- .../mapper/lakala/LklOrderDrawMapper.xml | 8 + .../src/main/resources/static/diy/js/diy.js | 2 +- 21 files changed, 791 insertions(+), 80 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderDraw.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderDrawMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java create mode 100644 mall-shop/src/main/resources/mapper/lakala/LklOrderDrawMapper.xml diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderDraw.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderDraw.java new file mode 100644 index 00000000..d41b1abf --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderDraw.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.modules.lakala; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("lkl_order_draw") +@ApiModel(value = "拉卡拉订单分账金额提现表实体", description = "拉卡拉订单分账金额提现表实体") +public class LklOrderDraw { + + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增Id", example = "1") + private Long id; + + /** + * 钱包ID + */ + @ApiModelProperty(value = "钱包ID", example = "W123456789") + private String ewallet_id; + + /** + * 请求日期 + */ + @ApiModelProperty(value = "请求日期", example = "20231001") + private String req_date; + + /** + * 提款流水号 + */ + @ApiModelProperty(value = "提款流水号", example = "DRAW2023100100001") + private String draw_jnl; + + /** + * 提款金额(单位:元)含手续费 + */ + @ApiModelProperty(value = "提款金额(单位:元)含手续费", example = "1000.00") + private String draw_amt; + + /** + * 手续费 + */ + @ApiModelProperty(value = "手续费", example = "10.00") + private String draw_fee; + + /** + * 提款模式 + */ + @ApiModelProperty(value = "提款模式", example = "ONLINE") + private String draw_mode; + + /** + * 结算模式(01主动提款 02余额自动结算 03 交易自动结算) + */ + @ApiModelProperty(value = "结算模式(01主动提款 02余额自动结算 03 交易自动结算)", example = "01") + private String batch_auto_settle; + + /** + * 自动结算批次号 + */ + @ApiModelProperty(value = "自动结算批次号", example = "BATCH20231001001") + private String batch_no; + + /** + * 结算账户号 + */ + @ApiModelProperty(value = "结算账户号", example = "6222021234567890123") + private String acc_no; + + /** + * 结算账户名 + */ + @ApiModelProperty(value = "结算账户名", example = "张三") + private String acct_name; + + /** + * 提款状态:DRAW.ACCEPTED 提款已受理;DRAW.FREEZE 提款冻结; + * DRAW.PROCESSING 提款处理中;DRAW.SUCCESS 提款成功;DRAW.FAILED 提款失败 + */ + @ApiModelProperty(value = "提款状态:DRAW.ACCEPTED(提款已受理)、DRAW.FREEZE(提款冻结)、DRAW.PROCESSING(提款处理中)、DRAW.SUCCESS(提款成功)、DRAW.FAILED(提款失败)", + example = "DRAW.SUCCESS") + private String draw_state; + + /** + * 结果信息 + */ + @ApiModelProperty(value = "结果信息", example = "提款成功") + private String meno; + + /** + * 商户订单号 + */ + @ApiModelProperty(value = "商户订单号", example = "MER2023100100001") + private String mer_order_no; + + /** + * 结算流水号 + */ + @ApiModelProperty(value = "结算流水号", example = "SETTLE2023100100001") + private String settle_no; + + /** + * 银行行号 + */ + @ApiModelProperty(value = "银行行号", example = "102100099999") + private String bank_no; + + /** + * 银行名称 + */ + @ApiModelProperty(value = "银行名称", example = "中国工商银行") + private String nbk_name; + + /** + * 商户号 + */ + @ApiModelProperty(value = "商户号", example = "M1234567890") + private String merc_id; + + /** + * 完成时间 + */ + @ApiModelProperty(value = "完成时间", example = "2023-10-01 12:00:00") + private String complete_time; + + /** + * 创建时间 + */ + @ApiModelProperty(value = "创建时间", example = "2023-10-01 11:30:00") + private String created_time; + + /** + * 异步通知地址 + */ + @ApiModelProperty(value = "异步通知地址", example = "https://api.example.com/notify") + private String notify_url; + + /** + * 异步通知返回的JSON数据 + */ + @ApiModelProperty(value = "异步通知返回的JSON数据", example = "{\"code\":\"0000\",\"msg\":\"success\"}") + private String notify_resp; + + @ApiModelProperty(value = "备注信息") + private String remark; + + @ApiModelProperty(value = "摘要") + private String summary; + + /** + * 记录状态:1-有效;2-无效; + */ + @ApiModelProperty(value = "记录状态:1-有效;2-无效", example = "1") + private Integer status; + + /** + * 新建时间 + */ + @ApiModelProperty(value = "新建时间", example = "2023-10-01 11:30:00") + private Date created_at; + + /** + * 更新时间 + */ + @ApiModelProperty(value = "更新时间", example = "2023-10-01 12:00:00") + private Date updated_at; +} + diff --git a/mall-common/src/main/resources/application-dev.yml b/mall-common/src/main/resources/application-dev.yml index e9b9540a..e23eb9cf 100644 --- a/mall-common/src/main/resources/application-dev.yml +++ b/mall-common/src/main/resources/application-dev.yml @@ -30,8 +30,8 @@ redis: baidu: map: - app_id: 116444176 - ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v + app_id: 120196890 + ak: YzRPLAOTYyCFVjvlh2vxnaUnH4jPjufM url: https://api.map.baidu.com/geoconv/v2/? getui: # 个推配置 diff --git a/mall-common/src/main/resources/application-local.yml b/mall-common/src/main/resources/application-local.yml index 753e7f0d..6c7ca24d 100644 --- a/mall-common/src/main/resources/application-local.yml +++ b/mall-common/src/main/resources/application-local.yml @@ -30,8 +30,8 @@ redis: baidu: map: - app_id: 116444176 - ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v + app_id: 120196890 + ak: YzRPLAOTYyCFVjvlh2vxnaUnH4jPjufM url: https://api.map.baidu.com/geoconv/v2/? getui: # 个推配置 push: diff --git a/mall-common/src/main/resources/application-prod.yml b/mall-common/src/main/resources/application-prod.yml index 736778a7..f3d810c5 100644 --- a/mall-common/src/main/resources/application-prod.yml +++ b/mall-common/src/main/resources/application-prod.yml @@ -30,8 +30,8 @@ redis: baidu: map: - app_id: 116444176 - ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v + app_id: 120196890 + ak: YzRPLAOTYyCFVjvlh2vxnaUnH4jPjufM url: https://api.map.baidu.com/geoconv/v2/? getui: # 个推配置 push: diff --git a/mall-common/src/main/resources/application-test.yml b/mall-common/src/main/resources/application-test.yml index 82bb8d56..322ead6c 100644 --- a/mall-common/src/main/resources/application-test.yml +++ b/mall-common/src/main/resources/application-test.yml @@ -30,8 +30,8 @@ redis: baidu: map: - app_id: 116444176 - ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v + app_id: 120196890 + ak: YzRPLAOTYyCFVjvlh2vxnaUnH4jPjufM url: https://api.map.baidu.com/geoconv/v2/? getui: # 个推配置 push: diff --git a/mall-common/src/main/resources/application-uat.yml b/mall-common/src/main/resources/application-uat.yml index 38e9680d..df4afea2 100644 --- a/mall-common/src/main/resources/application-uat.yml +++ b/mall-common/src/main/resources/application-uat.yml @@ -30,8 +30,8 @@ redis: baidu: map: - app_id: 116444176 - ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v + app_id: 120196890 + ak: YzRPLAOTYyCFVjvlh2vxnaUnH4jPjufM url: https://api.map.baidu.com/geoconv/v2/? getui: # 个推配置 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java index d57863b4..1eb0afe7 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LakalaAdminController.java @@ -39,6 +39,27 @@ public class LakalaAdminController extends BaseControllerImpl { return CommonResult.failed(); } + @ApiOperation(value = "拉卡拉账户D1提现", notes = "拉卡拉账户D1提现") + @RequestMapping(value = "/ewallet/drawD1", method = RequestMethod.POST) + public CommonResult ewalletWithDrawD1(@RequestBody JSONObject paramsJSON) { + try { + // 参数校验 + if (paramsJSON == null) { + return CommonResult.failed("请求参数不能为空"); + } + + // 执行业务逻辑 + Boolean success = lakalaPayService.ewalletWithDrawD1(paramsJSON.getStr("mercId"), paramsJSON.getStr("merOrderNo"), paramsJSON.getStr("drawAmt"), paramsJSON.getStr("remark"), paramsJSON.getStr("summary")); + if (success) { + return CommonResult.success("账户D1提现提交成功"); + } + + return CommonResult.failed("账户D1提现提交失败"); + } catch (Exception e) { + return CommonResult.failed("系统异常:" + e.getMessage()); + } + } + @ApiOperation(value = "提款模式设置", notes = "提款模式设置") @RequestMapping(value = "/ewallet/settleProfile", method = RequestMethod.POST) public CommonResult ewalletSettleProfile(@RequestBody JSONObject paramsJSON) { @@ -47,7 +68,7 @@ public class LakalaAdminController extends BaseControllerImpl { if (paramsJSON == null) { return CommonResult.failed("请求参数不能为空"); } - + // 执行业务逻辑 Boolean success = lakalaPayService.ewalletSettleProfile(paramsJSON.getStr("mercId"), paramsJSON.getStr("settleType"), paramsJSON.getStr("settleTime")); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java index fbe88173..5a100b20 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java @@ -248,6 +248,25 @@ public class LakalaController extends BaseControllerImpl { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resp); } + + /** + * 分账结果通知 + * 参考:https://o.lakala.com/#/home/document/detail?id=367 + * + * @param request + * @return + */ + @ApiOperation(value = "拉卡拉提现结果通知", notes = "拉卡拉提现结果通知") + @RequestMapping(value = "/ewallet/drawNotify", method = RequestMethod.POST) + public ResponseEntity ewalletWithDrawNotify(HttpServletRequest request) { + JSONObject resp = lakalaPayService.ewalletWithDrawNotify(request); + if (resp != null && "SUCCESS".equals(resp.get("code"))) { + return ResponseEntity.ok(resp); + } + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resp); + } + /** * 分账结果通知 * 参考:https://o.lakala.com/#/home/document/detail?id=393 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderDrawMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderDrawMapper.java new file mode 100644 index 00000000..c83d91d9 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderDrawMapper.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.lakala.LklOrderDraw; +import org.springframework.stereotype.Repository; + + +@Repository +public interface LklOrderDrawMapper extends BaseMapper { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java index 8d1ff881..49366084 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java @@ -330,4 +330,25 @@ public interface LakalaApiService { */ JSONObject ewalletSettleProfileNotify(HttpServletRequest request); + /** + * 拉卡拉账户D1提现 + * 参考:https://o.lakala.com/#/home/document/detail?id=430 + * + * @param mercId 822商户号或receiveNo + * @param merOrderNo 商户订单号 + * @param drawAmt 提现金额(分) + * @param remark + * @param summary + */ + Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String remark, String summary); + + /** + * 拉卡拉账户D1提现结果通知 + * 参考:https://o.lakala.com/#/home/document/detail?id=367 + * + * @param request + * @return + */ + JSONObject ewalletWithDrawNotify(HttpServletRequest request); + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java new file mode 100644 index 00000000..8d074ae6 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.service; + +import com.suisung.mall.common.modules.lakala.LklOrderDraw; +import com.suisung.mall.core.web.service.IBaseService; + +public interface LklOrderDrawService extends IBaseService { + + /** + * 新增或更新记录 + * + * @param record + * @return + */ + Boolean addOrUpdateByMercIdAndMerOrderNo(LklOrderDraw record); + + /** + * 根据拉卡拉对账单流水号和平台订单号查询记录 + * + * @param mercId 商户号 + * @param merOrderNo 商户订单号 + * @return + */ + LklOrderDraw getByByMercIdAndMerOrderNo(String mercId, String merOrderNo); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 6f004db7..70dea245 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -57,6 +57,7 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; @@ -145,6 +146,9 @@ public class LakalaApiServiceImpl implements LakalaApiService { @Resource private LklOrderSeparateService lklOrderSeparateService; + @Resource + private LklOrderDrawService lklOrderDrawService; + @Lazy @Resource private ShopOrderBaseService shopOrderBaseService; @@ -3373,5 +3377,214 @@ public class LakalaApiServiceImpl implements LakalaApiService { .set("message", "处理成功"); } + /** + * 拉卡拉账户D1提现 + * 参考:https://o.lakala.com/#/home/document/detail?id=430 + * + * @param mercId 822商户号或receiveNo + * @param merOrderNo 商户订单号 + * @param drawAmt 提现金额(分) + * @param remark 备注信息 + * @param summary 摘要信息 + * @return 操作结果,成功返回true,失败返回false + */ + @Override + public Boolean ewalletWithDrawD1(String mercId, String merOrderNo, String drawAmt, String remark, String summary) { + // 1. 参数校验 + if (StrUtil.hasBlank(mercId, merOrderNo, drawAmt)) { + log.warn("[D1提现申请] D1提现参数校验失败,关键参数为空: mercId={}, merOrderNo={}, drawAmt={}", + mercId, merOrderNo, drawAmt); + return false; + } + + // 账号类型(01:收款账户,04:分账接收方账户) + String payType = "04"; + if (StrUtil.startWith(mercId, "822")) { + payType = "01"; + } + + try { + // 验证提现金额是否为有效数字 + BigDecimal drawAmtDecimal = Convert.toBigDecimal(drawAmt); + if (drawAmtDecimal == null || drawAmtDecimal.compareTo(BigDecimal.ZERO) <= 0) { + log.warn("[D1提现申请] D1提现金额无效,商户号={},订单号={},提现金额={}", mercId, merOrderNo, drawAmt); + return false; + } + + // 设置默认值 + if (StrUtil.isBlank(remark)) { + remark = String.format("商户订单:%s 账号类型:%s 分账后立即提现", merOrderNo, payType); + } + + log.info("[D1提现申请] 开始处理D1提现,商户号={},订单号={},提现金额={}分", mercId, merOrderNo, drawAmt); + + // 2. 配置初始化 + initLKLSDK(); + + // 3. 装配数据 + V2LaepIndustryEwalletWithdrawD1Request request = new V2LaepIndustryEwalletWithdrawD1Request(); + request.setOrgNo(orgCode); + request.setMerchantNo(mercId); + request.setMerOrderNo(merOrderNo); + request.setPayType(payType); + + // 分转元,保留两位小数点,不要四舍五入 + String drawAmtYuan = drawAmtDecimal.divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN).toString(); + request.setDrawAmt(drawAmtYuan); // 单位:元 + request.setRemark(remark); + if (StrUtil.isNotBlank(summary)) { + request.setSummary(summary); + } + request.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/ewallet/ewallet/drawNotify"); + + // 4. 发送请求 + String responseStr = LKLSDK.httpPost(request); + if (StrUtil.isBlank(responseStr)) { + log.error("[D1提现申请] D1提现请求无响应,商户号={},订单号={}", mercId, merOrderNo); + return false; + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (lakalaRespJSON == null) { + log.error("[D1提现申请] D1提现响应数据解析失败,商户号={},订单号={}", mercId, merOrderNo); + return false; + } + + String retCode = lakalaRespJSON.getStr("retCode"); + Object drawJnl = lakalaRespJSON.getByPath("respData.drawJnl"); + boolean success = "000000".equals(retCode); + + if (success && drawJnl != null) { + log.info("[D1提现申请] D1提现请求成功,商户号={},订单号={},流水号={}", mercId, merOrderNo, drawJnl); + + // 5. 保存提现记录 + LklOrderDraw lklOrderDraw = new LklOrderDraw(); + lklOrderDraw.setMerc_id(mercId); + lklOrderDraw.setMer_order_no(merOrderNo); + lklOrderDraw.setDraw_jnl(Convert.toStr(drawJnl)); + lklOrderDraw.setDraw_amt(drawAmtYuan); + lklOrderDraw.setBatch_auto_settle(payType); + lklOrderDraw.setNotify_url(request.getNotifyUrl()); + lklOrderDraw.setNotify_resp(responseStr); + lklOrderDraw.setRemark(remark); + if (StrUtil.isNotBlank(summary)) { + lklOrderDraw.setSummary(summary); + } + + boolean saveResult = lklOrderDrawService.addOrUpdateByMercIdAndMerOrderNo(lklOrderDraw); + if (saveResult) { + log.info("[D1提现申请] D1提现记录保存成功,商户号={},订单号={},流水号={}", mercId, merOrderNo, drawJnl); + } else { + log.error("[D1提现申请] D1提现记录保存失败,商户号={},订单号={},流水号={}", mercId, merOrderNo, drawJnl); + } + + return saveResult; + } else { + String retMsg = lakalaRespJSON.getStr("retMsg"); + log.error("[D1提现申请] D1提现失败,商户号={},订单号={},错误码={},错误信息={}", + mercId, merOrderNo, retCode, retMsg); + return false; + } + + } catch (SDKException e) { + log.error("[D1提现申请] D1提现SDK调用异常,商户号={},订单号={}", mercId, merOrderNo, e); + return false; + } catch (Exception e) { + log.error("[D1提现申请] D1提现处理异常,商户号={},订单号={}", mercId, merOrderNo, e); + return false; + } + } + + + /** + * 拉卡拉账户D1提现结果通知处理 + * 参考:https://o.lakala.com/#/home/document/detail?id=367 + * + * @param request HTTP请求对象,包含拉卡拉提现结果通知的参数 + * @return JSONObject 响应结果对象 + */ + @Override + public JSONObject ewalletWithDrawNotify(HttpServletRequest request) { + log.debug("[拉卡拉提现结果通知] 开始处理拉卡拉提现结果通知"); + + try { + // 1. 验签处理 - 验证通知来源的合法性 + Pair signCheckResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath, false); + if (!signCheckResult.getFirst()) { + log.warn("[LklOrderDraw] 验签失败: {}", signCheckResult.getSecond()); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", signCheckResult.getSecond()); + } + + // 2. 解析回调参数 + JSONObject paramsJSON = JSONUtil.parseObj(signCheckResult.getSecond()); + if (paramsJSON == null) { + log.warn("[拉卡拉提现结果通知] 回调参数解析失败"); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", "回调参数解析失败"); + } + + String drawState = paramsJSON.getStr("drawState"); + String mercId = paramsJSON.getStr("mercId"); + String merOrderNo = paramsJSON.getStr("merOrderNo"); + + log.info("[拉卡拉提现结果通知] 提现通知参数: drawState={}, mercId={}, merOrderNo={}", drawState, mercId, merOrderNo); + + if (StrUtil.isBlank(mercId) || StrUtil.isBlank(merOrderNo) || StrUtil.isBlank(drawState)) { + log.warn("[拉卡拉提现结果通知] 回调参数缺失: drawState={}, mercId={}, merOrderNo={}", drawState, mercId, merOrderNo); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", "回调参数错误"); + } + + // 只处理成功的提现状态 + if (!"DRAW.SUCCESS".equals(drawState)) { + log.debug("[拉卡拉提现结果通知] 提现状态未成功,忽略处理: drawState={}", drawState); + return JSONUtil.createObj() + .set("code", "SUCCESS") // 返回成功,避免重复通知 + .set("message", "状态未成功,忽略处理"); + } + + // 3. 转换参数并更新数据 + String snakeCaseJson = StringUtils.convertCamelToSnake(signCheckResult.getSecond()); + if (StringUtils.isBlank(snakeCaseJson)) { + log.error("[拉卡拉提现结果通知] 回调参数转换失败,mercId={} merOrderNo={}", mercId, merOrderNo); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", "回调参数转换失败"); + } + + LklOrderDraw lklOrderDraw = JSONUtil.toBean(snakeCaseJson, LklOrderDraw.class); + if (lklOrderDraw == null) { + log.error("[拉卡拉提现结果通知] 回调参数转换为对象失败,mercId={} merOrderNo={}", mercId, merOrderNo); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", "回调参数转换失败"); + } + + boolean isSuccess = lklOrderDrawService.addOrUpdateByMercIdAndMerOrderNo(lklOrderDraw); + if (!isSuccess) { + log.error("[拉卡拉提现结果通知] 数据更新失败,mercId={} merOrderNo={}", mercId, merOrderNo); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", "数据更新失败"); + } + + log.info("[拉卡拉提现结果通知] 拉卡拉提现结果通知处理成功,mercId={} merOrderNo={}", mercId, merOrderNo); + + // 4. 返回成功响应 + return JSONUtil.createObj() + .set("code", "SUCCESS") + .set("message", "处理成功"); + + } catch (Exception e) { + log.error("[拉卡拉提现结果通知] 处理拉卡拉提现结果通知异常", e); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("message", "系统处理异常"); + } + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java new file mode 100644 index 00000000..22e7043c --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java @@ -0,0 +1,105 @@ +package com.suisung.mall.shop.lakala.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.modules.lakala.LklOrderDraw; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.lakala.mapper.LklOrderDrawMapper; +import com.suisung.mall.shop.lakala.service.LklOrderDrawService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class LklOrderDrawServiceImpl extends BaseServiceImpl implements LklOrderDrawService { + /** + * 新增或更新拉卡拉订单提现记录 + * + * @param record 拉卡拉订单提现记录 + * @return 操作结果,成功返回true,失败返回false + */ + @Override + public Boolean addOrUpdateByMercIdAndMerOrderNo(LklOrderDraw record) { + // 参数校验 + if (record == null || (StrUtil.isBlank(record.getMer_order_no()) && StrUtil.isBlank(record.getMerc_id()))) { + log.warn("[LklOrderDraw] 新增或更新记录参数校验失败,record为空或关键参数缺失"); + return false; + } + + try { + log.debug("[LklOrderDraw] 开始处理订单提现记录,商户订单号={},商户号={}", + record.getMer_order_no(), record.getMerc_id()); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_order_no", record.getMer_order_no()) + .eq("merc_id", record.getMerc_id()); + + if (StrUtil.isNotBlank(record.getDraw_jnl())) { + queryWrapper.eq("draw_jnl", record.getDraw_jnl()); + } + + LklOrderDraw existsRecord = getOne(queryWrapper); + if (existsRecord != null && existsRecord.getId() != null && existsRecord.getId() > 0) { + // 更新记录 + log.info("[LklOrderDraw] 记录已存在,执行更新操作,ID={}", existsRecord.getId()); + record.setId(existsRecord.getId()); + boolean updateResult = updateById(record); + if (updateResult) { + log.debug("[LklOrderDraw] 记录更新成功,ID={}", record.getId()); + } else { + log.error("[LklOrderDraw] 记录更新失败,ID={}", record.getId()); + } + return updateResult; + } + + log.info("[LklOrderDraw] 记录不存在,执行新增操作"); + boolean addResult = add(record); + if (addResult) { + log.debug("[LklOrderDraw] 记录新增成功,ID={}", record.getId()); + } else { + log.error("[LklOrderDraw] 记录新增失败"); + } + return addResult; + + } catch (Exception e) { + log.error("[LklOrderDraw] 处理订单提现记录异常,商户订单号={},商户号={}", + record.getMer_order_no(), record.getMerc_id(), e); + return false; + } + } + + /** + * 根据商户号和商户订单号查询拉卡拉订单提现记录 + * + * @param mercId 商户号 + * @param merOrderNo 商户订单号 + * @return 拉卡拉订单分账记录,未找到返回null + */ + @Override + public LklOrderDraw getByByMercIdAndMerOrderNo(String mercId, String merOrderNo) { + // 参数校验 + if (StrUtil.isBlank(mercId) && StrUtil.isBlank(merOrderNo)) { + log.warn("[LklOrderDraw] 查询记录参数校验失败,商户号和商户订单号均为空"); + return null; + } + + try { + log.debug("[LklOrderDraw] 开始查询订单提现记录,商户订单号={},商户号={}", merOrderNo, mercId); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_order_no", merOrderNo).eq("merc_id", mercId); + + LklOrderDraw result = getOne(queryWrapper); + if (result != null) { + log.debug("[LklOrderDraw] 查询到订单提现记录,ID={}", result.getId()); + } else { + log.debug("[LklOrderDraw] 未查询到订单提现记录"); + } + + return result; + } catch (Exception e) { + log.error("[LklOrderDraw] 查询订单提现记录异常,商户订单号={},商户号={}", merOrderNo, mercId, e); + return null; + } + } +} diff --git a/mall-shop/src/main/resources/application.yml b/mall-shop/src/main/resources/application.yml index dca587a2..3f0e2695 100644 --- a/mall-shop/src/main/resources/application.yml +++ b/mall-shop/src/main/resources/application.yml @@ -28,38 +28,6 @@ spring: content-type: text/html; charset=utf-8 suffix: .html template-loader-path: classpath:/templates/ - quartz: - job-store-type: jdbc - jdbc: - initialize-schema: always # 每次启动项目都重新清空定时任务 - properties: - org: - quartz: - scheduler: - instanceName: DefaultQuartzScheduler - instanceId: AUTO - rmi: - export: false - proxy: false - jobStore: - class: org.quartz.impl.jdbcjobstore.JobStoreTX - driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate - tablePrefix: QRTZ_ - isClustered: false - useProperties: false - misfireThreshold: 60000 - threadPool: - class: org.quartz.simpl.SimpleThreadPool - threadCount: 10 - threadPriority: 5 - threadsInheritContextClassLoaderOfInitializingThread: true - dataSource: - myDS: - URL: jdbc:mysql:///test?characterEncoding=utf8&serverTimezone=UTC - user: store - password: brCnv0qLt8s0VqhI - driver: com.mysql.jdbc.Driver - maxConnections: 5 mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml global-config: @@ -100,7 +68,7 @@ ribbon: ConnectTimeout: 60000 #服务请求连接超时时间(毫秒) ReadTimeout: 60000 #服务请求处理超时时间(毫秒) baidu: - ak: "uwBrIUOZuTDMHsuRGm0hdmeG9sosN8sQ" # 百度地图ak + ak: "YzRPLAOTYyCFVjvlh2vxnaUnH4jPjufM" # 百度地图ak # 定时任务列表 job: diff --git a/mall-shop/src/main/resources/bootstrap-dev.yml b/mall-shop/src/main/resources/bootstrap-dev.yml index ff304d3a..6245658f 100644 --- a/mall-shop/src/main/resources/bootstrap-dev.yml +++ b/mall-shop/src/main/resources/bootstrap-dev.yml @@ -66,13 +66,37 @@ spring: dashboard: @sentinel.transport.dashboard@ eager: true quartz: - # 调度器实例名称 - scheduler-name: mallScheduler - # 线程池配置 - thread-pool: - thread-count: 15 # 增加线程数以处理更多并发任务 - # 错过触发器策略 - job-store-type: memory + job-store-type: jdbc + jdbc: + initialize-schema: always + properties: + org: + quartz: + scheduler: + instanceName: DefaultQuartzScheduler + instanceId: AUTO + rmi: + export: false + proxy: false + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: false + useProperties: false + misfireThreshold: 120000 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 15 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + dataSource: + myDS: + URL: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 + user: @mysql.user@ + password: @mysql.pwd@ + driver: @mysql.driver@ + maxConnections: 5 upload: # 图片上传配置 filepath: @upload.filepath@ # 本地环境静态文件路径 seata: diff --git a/mall-shop/src/main/resources/bootstrap-local.yml b/mall-shop/src/main/resources/bootstrap-local.yml index 055e954e..4820f882 100644 --- a/mall-shop/src/main/resources/bootstrap-local.yml +++ b/mall-shop/src/main/resources/bootstrap-local.yml @@ -66,13 +66,37 @@ spring: dashboard: @sentinel.transport.dashboard@ eager: true quartz: - # 调度器实例名称 - scheduler-name: mallScheduler - # 线程池配置 - thread-pool: - thread-count: 15 # 增加线程数以处理更多并发任务 - # 错过触发器策略 - job-store-type: memory + job-store-type: jdbc + jdbc: + initialize-schema: always + properties: + org: + quartz: + scheduler: + instanceName: DefaultQuartzScheduler + instanceId: AUTO + rmi: + export: false + proxy: false + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: false + useProperties: false + misfireThreshold: 120000 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 15 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + dataSource: + myDS: + URL: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 + user: @mysql.user@ + password: @mysql.pwd@ + driver: @mysql.driver@ + maxConnections: 5 upload: # 图片上传配置 filepath: @upload.filepath@ # 本地环境静态文件路径 seata: diff --git a/mall-shop/src/main/resources/bootstrap-prod.yml b/mall-shop/src/main/resources/bootstrap-prod.yml index bdfb2296..7470b438 100644 --- a/mall-shop/src/main/resources/bootstrap-prod.yml +++ b/mall-shop/src/main/resources/bootstrap-prod.yml @@ -73,13 +73,37 @@ spring: dashboard: @sentinel.transport.dashboard@ eager: true quartz: - # 调度器实例名称 - scheduler-name: mallScheduler - # 线程池配置 - thread-pool: - thread-count: 15 # 增加线程数以处理更多并发任务 - # 错过触发器策略 - job-store-type: memory + job-store-type: jdbc + jdbc: + initialize-schema: always + properties: + org: + quartz: + scheduler: + instanceName: DefaultQuartzScheduler + instanceId: AUTO + rmi: + export: false + proxy: false + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: false + useProperties: false + misfireThreshold: 120000 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 15 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + dataSource: + myDS: + URL: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 + user: @mysql.user@ + password: @mysql.pwd@ + driver: @mysql.driver@ + maxConnections: 5 upload: # 图片上传配置 filepath: @upload.filepath@ # 本地环境静态文件路径 seata: diff --git a/mall-shop/src/main/resources/bootstrap-test.yml b/mall-shop/src/main/resources/bootstrap-test.yml index 41089593..1500963d 100644 --- a/mall-shop/src/main/resources/bootstrap-test.yml +++ b/mall-shop/src/main/resources/bootstrap-test.yml @@ -70,13 +70,37 @@ spring: dashboard: @sentinel.transport.dashboard@ eager: true quartz: - # 调度器实例名称 - scheduler-name: mallScheduler - # 线程池配置 - thread-pool: - thread-count: 15 # 增加线程数以处理更多并发任务 - # 错过触发器策略 - job-store-type: memory + job-store-type: jdbc + jdbc: + initialize-schema: always + properties: + org: + quartz: + scheduler: + instanceName: DefaultQuartzScheduler + instanceId: AUTO + rmi: + export: false + proxy: false + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: false + useProperties: false + misfireThreshold: 120000 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 15 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + dataSource: + myDS: + URL: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 + user: @mysql.user@ + password: @mysql.pwd@ + driver: @mysql.driver@ + maxConnections: 5 upload: # 图片上传配置 filepath: @upload.filepath@ # 本地环境静态文件路径 seata: diff --git a/mall-shop/src/main/resources/bootstrap-uat.yml b/mall-shop/src/main/resources/bootstrap-uat.yml index 41089593..1500963d 100644 --- a/mall-shop/src/main/resources/bootstrap-uat.yml +++ b/mall-shop/src/main/resources/bootstrap-uat.yml @@ -70,13 +70,37 @@ spring: dashboard: @sentinel.transport.dashboard@ eager: true quartz: - # 调度器实例名称 - scheduler-name: mallScheduler - # 线程池配置 - thread-pool: - thread-count: 15 # 增加线程数以处理更多并发任务 - # 错过触发器策略 - job-store-type: memory + job-store-type: jdbc + jdbc: + initialize-schema: always + properties: + org: + quartz: + scheduler: + instanceName: DefaultQuartzScheduler + instanceId: AUTO + rmi: + export: false + proxy: false + jobStore: + class: org.quartz.impl.jdbcjobstore.JobStoreTX + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: false + useProperties: false + misfireThreshold: 120000 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 15 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + dataSource: + myDS: + URL: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 + user: @mysql.user@ + password: @mysql.pwd@ + driver: @mysql.driver@ + maxConnections: 5 upload: # 图片上传配置 filepath: @upload.filepath@ # 本地环境静态文件路径 seata: diff --git a/mall-shop/src/main/resources/mapper/lakala/LklOrderDrawMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklOrderDrawMapper.xml new file mode 100644 index 00000000..d82d81e8 --- /dev/null +++ b/mall-shop/src/main/resources/mapper/lakala/LklOrderDrawMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/static/diy/js/diy.js b/mall-shop/src/main/resources/static/diy/js/diy.js index 626ebc1e..5eeb33f6 100644 --- a/mall-shop/src/main/resources/static/diy/js/diy.js +++ b/mall-shop/src/main/resources/static/diy/js/diy.js @@ -20803,7 +20803,7 @@ i[_x41903[4478]][_x41903[473]][_x41903[2345]] = e[_x41903[473]][_x41903[688]], c(); }, s, o, r)) : 16 == i[_x41903[4420]] ? $[_x41903[39]](i[_x41903[4615]][_x41903[473]], function(e, t) { - t[_x41903[124]] == a && (s = r = o = 100, publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) { + t[_x41903[124]] == a && (s = r = o = 10240, publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) { 250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4616])), t[_x41903[2345]] = e[_x41903[473]][_x41903[688]], c();