From a428f4a69d63483a5b81875389a33f4f184fa8c9 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Wed, 21 May 2025 15:58:06 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=80=9D=E8=BF=85=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../small/client/service/imp/SxDataServiceImp.java | 5 ++--- .../service/ShopBaseProductCategoryService.java | 2 ++ .../impl/ShopBaseProductCategoryServiceImpl.java | 8 ++++++++ .../service/impl/ShopNumberSeqServiceImpl.java | 3 +++ .../sync/controller/SyncThirdDataController.java | 10 +++++----- .../sync/service/impl/SyncBaseThirdSxAbstract.java | 13 +++++++------ .../sync/service/impl/SyncThirdDataServiceImpl.java | 11 +++++++++-- 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java index 1aea50fd..7d665a97 100644 --- a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java +++ b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java @@ -284,11 +284,11 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService private void syncAllGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel){ String where="where 1=1"; Integer total =0; + String parentId=getAndCacheTree(dataBaseInfo,dataBaseInfo.getCategoryName());//加载缓存用的 if(DicEnum.SYNCTYPE_02.getCode().equals(dataBaseInfo.getSyncType())){ if (StringUtils.isNotEmpty(dataBaseInfo.getCategoryName())) { - String parentId=getAndCacheTree(dataBaseInfo,dataBaseInfo.getCategoryName()); - String childrens= commonCache.get(CommonCache.CACHE_CATEGROY+parentId); //syncGoodsSearchModel.setItemClsno(childrens); + String childrens= commonCache.get(CommonCache.CACHE_CATEGROY+parentId); where += " and b.item_clsno in ('" + childrens + "')"; } if(StringUtils.isNotEmpty(commentModel.getSyncTime())){ @@ -336,7 +336,6 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService folders.add(String.valueOf(i)); syncCount+=sxSyncGoods.size(); } - // folders.add(String.valueOf(2)); //folders.add(String.valueOf(4)); //folders.add(String.valueOf(5)); log.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java index b50f1792..ffd983e1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java @@ -149,4 +149,6 @@ public interface ShopBaseProductCategoryService extends IBaseService baseWrapper=new QueryWrapper<>(); baseWrapper.select("MAX(product_id) as product_id"); ShopProductBase shopProductBase= shopProductBaseServiceImpl.getOne(baseWrapper); - Long productId= shopProductBase.getProduct_id()+1; + Long productId= shopProductBase.getProduct_id(); //QueryWrapper baseSeWrapper=new QueryWrapper(); //baseSeWrapper.eq("prefix", "product_id"); ShopNumberSeq shopNumberSeqBase= new ShopNumberSeq(); @@ -679,7 +680,7 @@ public abstract class SyncBaseThirdSxAbstract{ QueryWrapper itemQuery=new QueryWrapper<>(); itemQuery.select("MAX(item_id) as item_id"); ShopProductItem shopProductItem= shopProductItemServiceImpl.getOne(itemQuery); - Long itemtId= shopProductItem.getItem_id()+1; + Long itemtId= shopProductItem.getItem_id(); // QueryWrapper itemWrapper=new QueryWrapper(); //itemWrapper.eq("prefix", "item_id"); ShopNumberSeq shopNumberSeqItem= new ShopNumberSeq(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java index 08fb43bc..987cea9f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java @@ -43,6 +43,7 @@ import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.core.web.service.RedisService; +import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService; import com.suisung.mall.shop.number.service.ShopNumberSeqService; import com.suisung.mall.shop.page.service.OssService; import com.suisung.mall.shop.sixun.dao.SxDataDao; @@ -72,6 +73,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.File; @@ -122,7 +124,8 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements private final static String CLIENTFILEPATH="sxclient/"; - + @Autowired + private ShopBaseProductCategoryService shopBaseProductCategoryService; /** * 批量保存商品的分类 @@ -452,6 +455,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements * @param folders */ @Override + @Async public void SyncReadSxFileData(String appKey, String sign, String syncType, List folders) { SyncApp syncApp = syncAppService.getOne(new LambdaQueryWrapper() .select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) @@ -473,6 +477,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements }); syncPrimaryKey(); shopNumberSeqService.clearKey(); + shopBaseProductCategoryService.clearCategoryCache(storeId); ExecutorService executor = Executors.newFixedThreadPool(6); List> futures = new ArrayList<>(); // 提交任务 @@ -488,7 +493,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements while (true){ count++; String taskName=newFolders.get(taskId); - String fileName="good_"+taskId+".txt"; + String fileName="good_"+(taskId+1)+".txt"; JSONArray jsonArray=new ThreadFileUtils().processFolder(taskName,newFolders.get(taskId)); try { baseSaveOrUpdateGoodsBatch(jsonArray,storeId); @@ -497,6 +502,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements return "成功" + taskId; }catch (Exception e){ if(count<2){ + //Thread.sleep(100); continue; } fails.getAndIncrement(); @@ -519,6 +525,7 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements //记录到数据库 syncPrimaryKey(); shopNumberSeqService.clearKey(); + shopBaseProductCategoryService.clearCategoryCache(storeId); List syncFileLogs=new ArrayList<>(); for (int i = 0; i < failFolders.size(); i++) { String path=failFolders.get(i); From d3e2596585c8a5f1d795c66566eb4429eefcd5f8 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Wed, 21 May 2025 16:45:18 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E9=93=B6=E8=A1=8C=E5=8D=A1Bin=E6=8E=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8D=95=E7=8B=AC=E7=BB=99=E5=95=86=E5=AE=B6?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=BA=97=E9=93=BA=E6=8E=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E5=88=86=E8=B4=A6=E4=B8=9A=E5=8A=A1=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/modules/store/ShopMchEntry.java | 3 + .../impl/EsignContractServiceImpl.java | 2 +- .../controller/mobile/LakalaController.java | 2 +- .../shop/lakala/service/LakalaApiService.java | 2 +- .../service/LklLedgerReceiverService.java | 10 + .../service/impl/LakalaApiServiceImpl.java | 343 ++++++++++++------ .../impl/LklLedgerReceiverServiceImpl.java | 32 +- .../lakala/service/impl/LklTkServiceImpl.java | 73 ++-- .../store/service/ShopMchEntryService.java | 3 +- .../service/impl/ShopMchEntryServiceImpl.java | 5 +- .../impl/ShopStoreBaseServiceImpl.java | 23 +- 11 files changed, 344 insertions(+), 154 deletions(-) diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java index 0131dff4..b7e38ce4 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java @@ -246,6 +246,9 @@ public class ShopMchEntry implements Serializable { @ApiModelProperty(value = "是否签署电子合同:1-是;2-否;") private Integer has_ec_signed; + @ApiModelProperty(value = "是否进件成功:1-是;2-否;") + private Integer has_apply_mer; + @ApiModelProperty(value = "是否申请分账业务:1-是;2-否;") private Integer has_apply_split; diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java index a9981128..dd191c06 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java @@ -316,7 +316,7 @@ public class EsignContractServiceImpl extends BaseServiceImpl(new JSONObject().put("code", 200).put("msg", "success").toString(), HttpStatus.OK); } 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 bb67ced0..fc8e70a9 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 @@ -48,7 +48,7 @@ public class LakalaController extends BaseControllerImpl { @ApiOperation(value = "获取银行卡的 BIN 信息", notes = "获取银行卡的 BIN 信息") @RequestMapping(value = "/bankCardBin", method = RequestMethod.POST) - public JSONObject bankCardBin(@RequestBody JSONObject paramsJSON) { + public CommonResult bankCardBin(@RequestBody JSONObject paramsJSON) { return lakalaPayService.getBankCardBin(paramsJSON.getStr("bankCardNo")); } 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 602c77a3..ee485fc4 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 @@ -180,7 +180,7 @@ public interface LakalaApiService { * @param bankCardNo * @return */ - JSONObject getBankCardBin(String bankCardNo); + CommonResult getBankCardBin(String bankCardNo); /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java index 26886a3a..149eab1b 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java @@ -82,4 +82,14 @@ public interface LklLedgerReceiverService extends IBaseService getByCondition(String LicenseNo, String ContactMobile); + + /** + * 根据条件查询记录数量 + * + * @param LicenseNo + * @param ContactMobile + * @param platformId + * @return + */ + Long countByCondition(String LicenseNo, String ContactMobile, Long platformId); } 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 c3d0abd8..f469924d 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 @@ -11,6 +11,7 @@ package com.suisung.mall.shop.lakala.service.impl; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.ijpay.core.kit.IpKit; @@ -612,8 +613,12 @@ public class LakalaApiServiceImpl implements LakalaApiService { JSONObject header = new JSONObject(); header.put("Authorization", authorization); + log.debug("申请入网电子合同请求参数:{}", JsonUtil.toJSONString(reqBody)); + String errMsg = ""; ResponseEntity response = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class); + log.debug("申请入网电子合同响应参数:{}", JsonUtil.toJSONString(response)); + if (ObjectUtil.isEmpty(response) || response.getStatusCode() != HttpStatus.OK) { errMsg = "申请入网电子合同失败,无响应数据!"; shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), "", CommonConstant.MCH_APPR_STA_LKL_NOPASS, errMsg); @@ -628,7 +633,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { } JSONObject respData = respBody.getJSONObject("resp_data"); - if (respBody.getJSONObject("resp_data") == null) { + if (respData == null) { errMsg = "申请入网电子合同失败,返回数据有误"; shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), "", CommonConstant.MCH_APPR_STA_LKL_NOPASS, errMsg); return Pair.of(false, errMsg); @@ -981,98 +986,42 @@ public class LakalaApiServiceImpl implements LakalaApiService { } /** - * 商户分账业务开通申请 + * 申请商户分账业务开通 + * 流程:提交分账业务申请(同时申请分账接收方)-等待拉卡拉审核通过-绑定商户和接收方关系-等待拉卡拉审核通过-完成整个流程 * - * @param paramsJSON + * @param paramsJSON {merCupNo 拉卡拉外部商户号} * @return */ @Override public CommonResult applyLedgerMer(JSONObject paramsJSON) { - Pair resultPair = innerApplyLedgerMer(paramsJSON.getStr("merCupNo")); + // 检查参数 + if (ObjectUtil.isEmpty(paramsJSON) || StrUtil.isBlank(paramsJSON.getStr("merCupNo"))) { + return CommonResult.failed("请填写商户号!"); + } + + String merCupNo = paramsJSON.getStr("merCupNo"); + + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerCupNo(merCupNo); + if (shopMchEntry == null) { + return CommonResult.failed("无法获取入驻记录"); + } + + Long receiverCnt = lklLedgerReceiverService.countByCondition("", "", shopMchEntry.getDistributor_id()); + if (receiverCnt <= 0) { + // 1:新增一个接收方记录,起码要一个平台方,代理商根据入驻信息新增 + Boolean success = lklLedgerReceiverService.innerApplyLedgerReceiver(merCupNo, shopMchEntry.getDistributor_id()); + if (!success) { + return CommonResult.failed("申请分账接收方失败"); + } + } + + Pair resultPair = innerApplyLedgerMer(merCupNo); if (!resultPair.getFirst()) { return CommonResult.failed(resultPair.getSecond()); } return CommonResult.success(); -// log.debug("商户分账业务开通申请1开始"); -// -// // TODO 判断是否已经申请过? -// // 1. 配置初始化 -// initLKLSDK(); -// -// //2. 装配数据 -// V2MmsOpenApiLedgerApplyLedgerMerRequest req = new V2MmsOpenApiLedgerApplyLedgerMerRequest(); -// req.setVersion("2.0"); -// req.setOrderNo(StringUtils.genLklOrderNo(8));// 14位年月日时(24小时制)分秒+8位的随机数 -// req.setOrgCode(orgCode); -// req.setMerInnerNo(paramsJSON.getStr("merInnerNo"));// 从进件申请返回的商户号 -// req.setMerCupNo(paramsJSON.getStr("merCupNo")); // 从进件申请返回的商户号 -// req.setContactMobile(paramsJSON.getStr("contactMobile")); // 商户入驻注册的手机号 -// // 分账比例为了考虑低价订单的运费占比高,分账比例暂时定70%分账给商户,30%分账给平台 -// // new BigDecimal(paramsJSON.getStr("splitLowestRatio")) -// req.setSplitLowestRatio(new BigDecimal(splitLowestRatio)); -// String fileName = paramsJSON.getStr("splitEntrustFileName"); -// req.setSplitEntrustFileName(fileName); -// -// // 分账结算委托书文件上传到拉卡拉服务器 -// JSONObject fileUploadResp = uploadFile(req.getOrderNo(), "SPLIT_ENTRUST_FILE", StringUtils.getFileExt(fileName), UploadUtil.fileUrlToBase64(paramsJSON.getStr("splitEntrustFile"))); -// if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) { -// throw new ApiException(I18nUtil._("分账结算委托书上传失败!")); -// } -// -// String splitEntrustFilePath = fileUploadResp.getStr("attFileId"); -// req.setSplitEntrustFilePath(splitEntrustFilePath); //比如:G1/M00/06/64/CrFdEmBQc-aAGc_XAAAiIbS3WIE960.pdf; -// -// // 正式上线的时候,调整 api 地址 -// String domain = projectDomain; -// if (isProdProject()) { -// domain += "/api"; -// } -// // 给拉卡拉通知的回调地址 -// String retUrl = domain + "/mobile/shop/lakala/ledger/applyLedgerMerNotify"; -// req.setRetUrl(retUrl); -// -// paramsJSON.set("orderNo", req.getOrderNo()); -// paramsJSON.set("version", "2.0"); -// paramsJSON.set("ret_url", retUrl); -// paramsJSON.set("org_code", orgCode); -// paramsJSON.set("split_entrust_file_path", splitEntrustFilePath); -// -// String errMsg = "商户分账业务开通申请:"; -// -// try { -// //3. 发送请求 -// String responseStr = LKLSDK.httpPost(req); -// -// // 成功返回示例:{'retCode':'000000','retMsg':'申请已受理,请等待审核结果','respData':{'version':'1.0','orderNo':'KFPT20230223181025407788734','orgCode':'1','applyId':681201215598657536}} -// JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); -// if (StrUtil.isBlank(responseStr) || lakalaRespJSON == null) { -// return CommonResult.failed(I18nUtil._("申请开通分账失败!")); -// } -// -// if (!lakalaRespJSON.getStr("retCode").equals(lklSuccessCode)) { -// return CommonResult.failed(lakalaRespJSON.getStr("retMsg")); -// } -// -// paramsJSON.set("apply_id", lakalaRespJSON.getByPath("respData.applyId")); -// paramsJSON.set("remark", lakalaRespJSON.getStr("retMsg")); -// paramsJSON.set("audit_status_text", paramsJSON.get("remark")); -// -// paramsJSON.set("mch_id", paramsJSON.get("mchId")); -// -// // 新增数据 -// // 将 JSON 对象的键名转换为下划线命名 -// LklLedgerMember lklLedgerMember = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerMember.class); -// lklLedgerMemberService.saveOrUpdateByMerCupNo(lklLedgerMember); -// -// return CommonResult.success(null, "提交成功,待审核中!"); -// } catch (SDKException e) { -// errMsg += e.getMessage(); -// log.error(errMsg); -// shopMchEntryService.updateMerchEntryApprovalByMchId(paramsJSON.getLong("mchId"), paramsJSON.getStr("mchMobile"), CommonConstant.MCH_APPR_STA_NOPASS, errMsg); -// throw new ApiException(I18nUtil._("商家申请开通分账出错!"), e); -// } } /** @@ -1160,7 +1109,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { } // 更新商家的hasApplySplit状态=1; - shopMchEntryService.updateMulStatus("", merCupNo, 0, 1, 0, 0); + shopMchEntryService.updateMulStatus("", merCupNo, 0, 1, 0, 0, 0); respData.put("code", "SUCCESS"); respData.put("message", "操作成功!"); @@ -1262,7 +1211,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { } // 更新商家分账申请状态为已申请(hasApplySplit=1) - shopMchEntryService.updateMulStatus("", merCupNo, 0, 1, 0, 0); + shopMchEntryService.updateMulStatus("", merCupNo, 0, 1, 0, 0, 0); log.debug("商户分账申请业务回调:处理成功,applyId={}", applyId); return JSONUtil.createObj().put("code", "SUCCESS").put("message", "操作成功!"); @@ -1275,8 +1224,152 @@ public class LakalaApiServiceImpl implements LakalaApiService { * @param paramsJSON * @return */ + @Transactional @Override public CommonResult applyLedgerReceiver(JSONObject paramsJSON) { + // 前置检查:避免重复创建 + LklLedgerReceiver existingReceiver = lklLedgerReceiverService.getByCondition( + paramsJSON.getStr("licenseNo"), + paramsJSON.getStr("contactMobile"), + paramsJSON.getLong("platformId") + ); + if (existingReceiver != null) { + return CommonResult.success(existingReceiver, "该接收方已创建过!"); + } + + // 1. 配置初始化 + initLKLSDK(); + + // 2. 装配数据 + V2MmsOpenApiLedgerApplyLedgerReceiverRequest req = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest(); + String orderNo = StringUtils.genLklOrderNo(8); // 8位随机数 + String mchMobile = paramsJSON.getStr("contactMobile"); + + // 基础参数设置 + req.setOrderNo(orderNo); + req.setOrgCode(orgCode); + req.setVersion("2.0"); + req.setReceiverName(paramsJSON.getStr("receiverName")); + req.setContactMobile(mchMobile); + req.setLicenseNo(paramsJSON.getStr("licenseNo")); + req.setLicenseName(paramsJSON.getStr("licenseName")); + req.setLegalPersonName(paramsJSON.getStr("legalPersonName")); + req.setLegalPersonCertificateType(paramsJSON.getStr("legalPersonCertificateType")); + req.setLegalPersonCertificateNo(paramsJSON.getStr("legalPersonCertificateNo")); + req.setAcctNo(paramsJSON.getStr("acctNo")); + req.setAcctName(paramsJSON.getStr("acctName")); + req.setAcctTypeCode(paramsJSON.getStr("acctTypeCode")); + req.setAcctCertificateType(paramsJSON.getStr("acctCertificateType")); + req.setAcctCertificateNo(paramsJSON.getStr("acctCertificateNo")); + req.setAcctOpenBankCode(paramsJSON.getStr("acctOpenBankCode")); + req.setAcctOpenBankName(paramsJSON.getStr("acctOpenBankName")); + req.setAcctClearBankCode(paramsJSON.getStr("acctClearBankCode")); + + // 附件处理(优化循环逻辑) + JSONArray attachList = paramsJSON.getJSONArray("attachList"); + if (attachList != null && !attachList.isEmpty()) { + List processedAttachments = new ArrayList<>(); + + for (Object obj : attachList) { + if (!(obj instanceof JSONObject)) continue; + + JSONObject attachJSON = (JSONObject) obj; + String fileName = attachJSON.getStr("attachName"); + String attachType = attachJSON.getStr("attachType"); + String fileUrl = attachJSON.getStr("attachStoreFile"); + + if (StrUtil.isBlank(fileName) || StrUtil.isBlank(attachType) || StrUtil.isBlank(fileUrl)) { + log.warn("附件参数不完整,跳过: {}", attachJSON); + continue; + } + + try { + String fileBase64 = UploadUtil.fileUrlToBase64(fileUrl); + JSONObject uploadResponse = uploadFile( + StringUtils.genLklOrderNo(8), + attachType, + StringUtils.getFileExt(fileName), + fileBase64 + ); + + if (uploadResponse == null || StrUtil.isBlank(uploadResponse.getStr("attFileId"))) { + log.error("附件上传失败: {} ({})", fileName, attachType); + continue; + } + + V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo info = + new V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo(); + info.setAttachName(fileName); + info.setAttachType(attachType); + info.setAttachStorePath(uploadResponse.getStr("attFileId")); + processedAttachments.add(info); + } catch (Exception e) { + log.error("处理附件时出错: {} ({})", fileName, attachType, e); + } + } + + if (!processedAttachments.isEmpty()) { + req.setAttachList(processedAttachments); + paramsJSON.set("attach_list", JSONUtil.toJsonStr(processedAttachments)); + } + } + + // 保存请求参数 + paramsJSON.set("orderNo", orderNo); + paramsJSON.set("version", "2.0"); + paramsJSON.set("org_code", orgCode); + + // 3. 发送请求并处理响应 + try { + String responseStr = LKLSDK.httpPost(req); + + if (StrUtil.isBlank(responseStr)) { + return CommonResult.failed(I18nUtil._("创建分账接收方响应数据无效!")); + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (lakalaRespJSON == null || !lklSuccessCode.equals(lakalaRespJSON.getStr("retCode"))) { + String errorMsg = lakalaRespJSON != null + ? lakalaRespJSON.getStr("retMsg", "未知错误") + : "响应解析失败"; + return CommonResult.failed(I18nUtil._(errorMsg)); + } + + // 提取响应数据 + paramsJSON.set("receiver_no", lakalaRespJSON.getByPath("respData.receiverNo")); + paramsJSON.set("org_id", lakalaRespJSON.getByPath("respData.orgId")); + paramsJSON.set("org_name", lakalaRespJSON.getByPath("respData.orgName")); + + // 转换并保存数据 + LklLedgerReceiver receiver = JSONUtil.toBean( + StringUtils.convertCamelToSnake(paramsJSON.toString()), + LklLedgerReceiver.class + ); + + if (!lklLedgerReceiverService.saveOrUpdateByReceiverNo(receiver)) { + log.error("接收方创建成功,但更新本地数据失败!"); + return CommonResult.failed(I18nUtil._("接收方创建成功,但更新本地数据失败!")); + } + + // 更新商户分账状态 + shopMchEntryService.updateMulStatus(mchMobile, "", 0, 0, 1, 0, 0); + return CommonResult.success(receiver, "创建接收方成功!"); + + } catch (Exception e) { + log.error("接收方创建失败:{}", e.getMessage(), e); + throw new ApiException(I18nUtil._("创建接收方失败: {}" + e.getMessage()), e); + } + } + + /** + * 分账接收方创建申请 + * 参考:https://o.lakala.com/#/home/document/detail?id=380 + * + * @param paramsJSON + * @return + */ + // @Override + public CommonResult applyLedgerReceiverTemp(JSONObject paramsJSON) { // 判断分账接收方记录是否存在,存在就不再新建了 LklLedgerReceiver lklLedgerReceiverOld = lklLedgerReceiverService.getByCondition(paramsJSON.getStr("licenseNo"), paramsJSON.getStr("contactMobile"), paramsJSON.getLong("platformId")); if (lklLedgerReceiverOld != null) { @@ -1313,6 +1406,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { req.setAcctOpenBankName(paramsJSON.getStr("acctOpenBankName")); req.setAcctClearBankCode(paramsJSON.getStr("acctClearBankCode")); + if (paramsJSON.getJSONArray("attachList") != null && paramsJSON.getJSONArray("attachList").size() > 0) { List attachList = new ArrayList<>(); V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo attachInfo = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo(); @@ -1373,7 +1467,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { } // 更新商户分账多个状态 has_apply_receiver=1 - shopMchEntryService.updateMulStatus(mchMobile, "", 0, 0, 1, 0); + shopMchEntryService.updateMulStatus(mchMobile, "", 0, 0, 1, 0, 0); return CommonResult.success(lklLedgerReceiver, "创建接收方成功!"); } catch (Exception e) { @@ -1716,48 +1810,77 @@ public class LakalaApiServiceImpl implements LakalaApiService { } // 6. 更新商家绑定状态 - shopMchEntryService.updateMulStatus("", merCupNo, 0, 0, 0, 1); + shopMchEntryService.updateMulStatus("", merCupNo, 0, 0, 0, 1, 0); log.debug("分账商家绑定接收方回调处理完成,merCupNo:{}", merCupNo); return JSONUtil.createObj().put("code", "SUCCESS").put("message", "操作成功"); } /** - * 银行卡信息查询 + * 查询银行卡Bin信息 * 参考:https://o.lakala.com/#/home/document/detail?id=179 * * @param bankCardNo 银行卡号 * @return bankCode, bankName, clearingBankCode */ @Override - public JSONObject getBankCardBin(String bankCardNo) { + public CommonResult getBankCardBin(String bankCardNo) { + // 参数校验 if (StrUtil.isBlank(bankCardNo)) { - return null; + return CommonResult.failed("银行卡号不能为空"); } - // 1. 配置初始化 - initLKLSDK(); + // 构建请求参数 + JSONObject reqData = new JSONObject() + .put("version", "1.0") + .put("orderNo", StringUtils.genLklOrderNo(8)) + .put("orgCode", orgCode) + .put("cardNo", bankCardNo); -// //2. 装配数据 -// V2MmsOpenApiBankCardBinRequest req = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest(); -// -// JSONObject formData = new JSONObject(); -// formData.putByPath("reqData.version", "2.0"); -// formData.putByPath("reqData.orderNo", StringUtils.genLklOrderNo(8)); -// formData.putByPath("reqData.orgCode", orgCode); -// formData.putByPath("reqData.cardNo", bankCardNo); -// -// String urlPath = "/api/v2/mms/openApi/cardBin"; -// ResponseEntity response = RestTemplateHttpUtil.sendPostFormDataBackEntity(buildLklServiceUrl(urlPath), header, formData, JSONObject.class); -// if (ObjectUtil.isEmpty(response) || response.getStatusCode() != HttpStatus.OK) { -// return null; -// } -// JSONObject result = response.getBody(); -// if (result == null) { -// return null; -// } + JSONObject reqBody = new JSONObject() + .put("timestamp", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMddHHmmss")) + .put("version", "1.0.0") + .put("reqData", reqData); - return null; + // 构建请求URL(优化逻辑) + String reqUrl = serverUrl + "/api/v2/mms/openApi/cardBin"; + + // 生成授权信息 + String privateKey = LakalaUtil.getResourceFile(priKeyPath, false, true); + String authorization = LakalaUtil.genAuthorization(privateKey, appId, serialNo, reqBody.toString()); + + // 设置请求头 + JSONObject header = new JSONObject().put("Authorization", authorization); + + log.debug("查询银行卡Bin信息请求参数:{}", JsonUtil.toJSONString(reqBody)); + + // 发送请求并获取响应 + ResponseEntity response = RestTemplateHttpUtil.sendPostBodyBackEntity( + reqUrl, header, reqBody, JSONObject.class); + + + // 响应校验(简化逻辑) + if (ObjectUtil.isEmpty(response) || !HttpStatus.OK.equals(response.getStatusCode())) { + return CommonResult.failed("查询银行卡Bin信息失败,无响应数据!"); + } + + JSONObject respBody = response.getBody(); + log.debug("查询银行卡Bin信息响应参数:{}", respBody); + if (respBody == null || !lklSuccessCode.equals(respBody.getStr("retCode"))) { + String errorMsg = respBody != null && StrUtil.isNotBlank(respBody.getStr("retMsg")) + ? respBody.getStr("msg") + : "返回状态有误"; + return CommonResult.failed("查询银行卡Bin信息失败," + errorMsg); + } + + + // 获取并返回结果数据 + JSONObject respData = respBody.getJSONObject("respData"); + if (respData == null) { + return CommonResult.failed("查询银行卡Bin信息失败,返回数据有误"); + } + + return CommonResult.success(respData); } /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerReceiverServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerReceiverServiceImpl.java index 573a4682..c12d6641 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerReceiverServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerReceiverServiceImpl.java @@ -210,6 +210,7 @@ public class LklLedgerReceiverServiceImpl extends BaseServiceImpl 0; if (success) { // 更新多个状态 - shopMchEntryService.updateMulStatus("", merCupNo, 0, 0, 1, 0); + shopMchEntryService.updateMulStatus("", merCupNo, 0, 0, 1, 0, 0); } return success; @@ -311,4 +312,31 @@ public class LklLedgerReceiverServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotBlank(LicenseNo)) { + queryWrapper.eq("license_no", LicenseNo); + } + if (StrUtil.isNotBlank(ContactMobile)) { + queryWrapper.eq("contact_mobile", ContactMobile); + } + + if (ObjectUtil.isNotEmpty(platformId) && platformId > 0) { + queryWrapper.eq("platform_id", platformId); + } + + queryWrapper.eq("status", CommonConstant.Enable); + + return count(queryWrapper); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java index 266eed9b..3465c69c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java @@ -510,8 +510,9 @@ public class LklTkServiceImpl { } JSONObject respBody = response.getBody(); + logger.debug("进件返回结果:{}", respBody); if (response.getStatusCode() != HttpStatus.OK && ObjectUtil.isNotEmpty(respBody)) { - String errMsg = respBody.getStr("message") == null ? "返回状态有误" : respBody.getStr("message"); + String errMsg = respBody.getStr("message") == null ? "未知错误" : respBody.getStr("message"); return Pair.of(false, "进件失败:" + errMsg); } @@ -544,6 +545,7 @@ public class LklTkServiceImpl { // 解密请求参数 String requestBody = LakalaUtil.getBody(request); logger.debug("拉卡拉进件异步通知返回参数:{}", requestBody); + if (StrUtil.isBlank(requestBody)) { return new JSONObject().set("code", "400").set("message", "返回参数为空"); } @@ -562,39 +564,44 @@ public class LklTkServiceImpl { String notifyPubKey = LakalaUtil.getResourceFile(notifyPubKeyPath, false, false); String data = LakalaUtil.decryptNotifyData(notifyPubKey, srcData); if (StrUtil.isBlank(data)) { - return new JSONObject().set("code", "400").set("message", "密文解密出错!"); + return new JSONObject().set("code", "400").set("message", "数据解密出错!"); } logger.debug("拉卡拉进件异步通知data解密成功,开始处理逻辑"); // 逻辑处理 JSONObject dataJSON = JSONUtil.parseObj(data); - if (dataJSON.isEmpty() || StrUtil.isBlank(dataJSON.getStr("externalCustomerNo")) || StrUtil.isBlank(dataJSON.getStr("status"))) { + String auditStatus = dataJSON.getStr("status"); + String merCupNo = dataJSON.getStr("externalCustomerNo"); //拉卡拉外部商户号 + String merInnerNo = dataJSON.getStr("customerNo"); //拉卡拉内部商户号 + String termNos = dataJSON.getStr("termNos"); //拉卡拉分配的业务终端号 + + // 合并参数校验,减少嵌套 + if (dataJSON.isEmpty() || + StrUtil.isBlank(auditStatus) || + StrUtil.isBlank(merCupNo) || + StrUtil.isBlank(merInnerNo)) { return new JSONObject().set("code", "500").set("message", "参数解析出错"); } - String auditStatus = dataJSON.getStr("status"); + // 校验审核状态 if (!"SUCCESS".equals(auditStatus) && !"REVIEW_PASS".equals(auditStatus)) { + logger.debug("返回的审核状态:{}", auditStatus); return new JSONObject().set("code", "FAIL").set("message", "返回审核状态有误"); } // RMK 拉卡拉进价提交成功,边处理周边的数据,边等待审核异步通知 // 给商家入驻表增加拉卡拉的商户号和拉卡拉返回的数据 - String merCupNo = dataJSON.getStr("externalCustomerNo"); //拉卡拉外部商户号 - String merInnerNo = dataJSON.getStr("customerNo"); //拉卡拉内部商户号 - String termNos = dataJSON.getStr("termNos"); //拉卡拉分配的业务终端号 - if (StrUtil.isBlank(merInnerNo) || StrUtil.isBlank(merCupNo)) { - return new JSONObject().set("code", "500").set("message", "返回数据有误"); - } - ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerInnerNo(merInnerNo); if (ObjectUtil.isEmpty(shopMchEntry)) { - logger.error("拉卡拉进件异步通知:返回的内部商户号:{} 入驻信息不存在!", merInnerNo); - return new JSONObject().put("code", "500").put("message", "内部商户号:" + merInnerNo + " 入驻信息不存在"); + logger.error("拉卡拉进件异步通知:{}内部商户号入驻信息不存在!", merInnerNo); + return new JSONObject().put("code", "500").put("message", merInnerNo + "内部商户号入驻信息不存在"); } - Boolean success = shopMchEntryService.updateMerchEntryLklAuditStatusByLklMerCupNo(merInnerNo, merCupNo, termNos, CommonConstant.Enable, null, data); + Boolean success = shopMchEntryService.updateMerchEntryLklAuditStatusByLklMerCupNo( + merInnerNo, merCupNo, termNos, CommonConstant.Enable, null, data); + if (!success) { return new JSONObject().set("code", "500").set("message", "更新商户号失败"); // throw new ApiException("更新商户号失败"); @@ -618,23 +625,26 @@ public class LklTkServiceImpl { if (success && StrUtil.isNotBlank(shopMchEntry.getContract_download_url())) { // TODO 新建一个正式的已审核通过的店铺, 新建之前判断是否已经新建过了? - // 新建一个正式的已审核通过的店铺 -// ShopMchEntry shopEntry = shopMchEntryService.getShopMerchEntryByMerCupNo(merCupNo); -// if (shopEntry != null && !CommonConstant.Enable.equals(shopEntry.getStore_status())) { -// String mchMobile = shopEntry.getLogin_mobile(); -// Pair retPair = shopStoreBaseService.merchEntryInfo2StoreInfo(mchMobile); -// if (retPair.getFirst() > 0) { -// // 2025-05-17暂停e签宝电子合同生成流程 -// // 更改合同记录表的店铺id -// // esignContractService.updateContractStoreId(mchMobile, retPair.getFirst()); -// // 填充合同模版表的店铺Id -// // esignContractFillingFileService.updateContractFillingStoreId(mchMobile, retPair.getFirst()); -// // 店铺创建状态已完成 -// shopMchEntryService.updateMerchEntryStoreStatus(mchMobile, CommonConstant.Enable); -// } else { -// throw new ApiException("商家进件:初始化店铺失败!"); -// } -// } + // 新建一个正式的已审核通过的店铺,不要抛异常,使用补偿机制,可以独立初始化店铺 + ShopMchEntry shopEntry = shopMchEntryService.getShopMerchEntryByMerCupNo(merCupNo); + if (shopEntry != null && !CommonConstant.Enable.equals(shopEntry.getStore_status())) { + String mchMobile = shopEntry.getLogin_mobile(); + Pair retPair = shopStoreBaseService.merchEntryInfo2StoreInfo(mchMobile); + + if (retPair.getFirst() > 0) { + // 2025-05-17暂停e签宝电子合同生成流程 + // 更改合同记录表的店铺id + // esignContractService.updateContractStoreId(mchMobile, retPair.getFirst()); + // 填充合同模版表的店铺Id + // esignContractFillingFileService.updateContractFillingStoreId(mchMobile, retPair.getFirst()); + // 店铺创建状态已完成 + shopMchEntryService.updateMerchEntryStoreStatus(mchMobile, CommonConstant.Enable); + logger.info("商家进件:初始化店铺成功!"); + } else { + logger.error("商家进件:初始化店铺失败!"); + // throw new ApiException("商家进件:初始化店铺失败!"); + } + } // 1、(电子合同)给商家申请分账功能使用;务必检查是否申请过?申请过忽略 // 下一步等待拉卡拉审核通过,再绑定接收方和商家的关系 @@ -643,6 +653,7 @@ public class LklTkServiceImpl { // 2:新增一个接收方记录,起码要一个平台方,代理商根据入驻信息新增 Boolean genSuccess = lklLedgerReceiverService.innerApplyLedgerReceiver(merCupNo, shopMchEntry.getDistributor_id()); + // 统一处理分账申请结果 if (retPair.getFirst() && genSuccess) { return new JSONObject().put("code", "200").put("message", "处理成功"); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java index fb83e784..29127239 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java @@ -213,8 +213,9 @@ public interface ShopMchEntryService { * @param hasApplySplit * @param hasApplyReceiver * @param hasBindReceiver + * @param hasApplyMer * @return */ - Boolean updateMulStatus(String mchMobile, String merCupNo, Integer hasEcSigned, Integer hasApplySplit, Integer hasApplyReceiver, Integer hasBindReceiver); + Boolean updateMulStatus(String mchMobile, String merCupNo, Integer hasEcSigned, Integer hasApplySplit, Integer hasApplyReceiver, Integer hasBindReceiver, Integer hasApplyMer); } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java index 3639e6bf..fd23bdd3 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java @@ -847,6 +847,7 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("lkl_mer_inner_no", lklInnerMerNo); + updateWrapper.set("has_apply_mer", CommonConstant.Enable); // 是否进件成功:1-是;2-否; if (StrUtil.isNotBlank(lklMerCupNo)) { updateWrapper.set("lkl_mer_cup_no", lklMerCupNo); } @@ -974,11 +975,12 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl merchEntryInfo2StoreInfo(String mchMobile) { if (StrUtil.isBlank(mchMobile)) { logger.error("生成店铺:商家手机号不能为空"); - return Pair.of(0, "商家手机号不能为空"); + return Pair.of(0, "商家手机不能为空"); } // 从绑定关系中,获取商家注册账号信息 Integer userId = accountService.getUserBindConnectUserIdByCondition(mchMobile, BindCode.MOBILE, CommonConstant.USER_TYPE_MCH); if (userId == null) { logger.error("生成店铺:绑定关系中获取不到该手机{}商家的账号", mchMobile); - return Pair.of(0, "商家账号异常!"); + return Pair.of(0, "该商家手机未注册账号!"); } ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByCondition(mchMobile, ""); @@ -3019,6 +3019,17 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl Date: Wed, 21 May 2025 22:40:44 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A5=E5=BF=97bash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.MD | 44 ++++++++++++++++--- .../service/impl/LakalaApiServiceImpl.java | 3 +- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/README.MD b/README.MD index da73b328..ec991b94 100644 --- a/README.MD +++ b/README.MD @@ -27,7 +27,6 @@ mall-im } 清除 docker 日志 -docker ps -aq | xargs docker inspect --format='{{.LogPath}}' | xargs truncate -s 0 查看哪个文件夹占空间 du -sh * | sort -h @@ -36,10 +35,41 @@ cd /data/docker/overlay2 查看哪个文件夹占空间 du -sh * | sort -h -进入到 大文件的目录下,执行以下脚本 -for file in *log*; do -cat /dev/null > "$file" -done +### 删除 docker 产生的日志文件:update:2025-05-21 -或者递归删除log 文件 -find overlay2 -type f -name "*.log.*" -print0 | xargs -0 -I {} cat /dev/null > {} \ No newline at end of file +* 避免误删: 先用 -print 参数预览匹配的文件,确认无误后再执行 -delete: +* 删除 7 天前 + +``` bash +-- 打印所有匹配文件路径 +find /data/docker/overlay2 -type f \( \ + -name "*.log" \ + -o -name "*.log.[0-9]*" \ + -o -name "*.log.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" \ +\) -not \( \ + -name "mysql*.log*" \ + -o -name "*.idx" \ + -o -name "*.lck" \ + -o -name "*.js" \ + -o -name "*.yml" \ + -o -name "*.toml" \ + -o -name "*.gz" \ + -o -name "*mysql*" \ +\) -print + +-- 删除 7 天前的日志文件 +find /data/docker/overlay2 -type f \( \ + -name "*.log" \ + -o -name "*.log.[0-9]*" \ + -o -name "*.log.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" \ +\) -not \( \ + -name "mysql*.log*" \ + -o -name "*.idx" \ + -o -name "*.lck" \ + -o -name "*.js" \ + -o -name "*.yml" \ + -o -name "*.toml" \ + -o -name "*.gz" \ + -o -name "*mysql*" \ +\) -mtime +7 -delete +``` \ No newline at end of file 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 f469924d..0514233e 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 @@ -1872,8 +1872,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { : "返回状态有误"; return CommonResult.failed("查询银行卡Bin信息失败," + errorMsg); } - - + // 获取并返回结果数据 JSONObject respData = respBody.getJSONObject("respData"); if (respData == null) { From 00a4536c579b31902bf504508a75acfac4861284 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Thu, 22 May 2025 09:07:46 +0800 Subject: [PATCH 04/11] =?UTF-8?q?im=E6=96=B0=E5=A2=9E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websocket/service/onchat/MallsuiteImSocketHandler.java | 5 +---- .../src/main/java/com/suisung/mall/im/pojo/vo/MineDTO.java | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java b/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java index 3e552707..9a110c5c 100644 --- a/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java +++ b/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java @@ -194,7 +194,7 @@ public class MallsuiteImSocketHandler implements WebSocketHandler { sendVO.setMsg_type(mine.getType()); sendVO.setItem_id(mine.getItem_id()); sendVO.setMsg(new HashMap()); - + sendVO.setMessage_length(mine.getLength()); String sender = mine.getId();//信息发送者登录名(loginName)或user_id String receiver = to.getId();//信息接收者,如果是私聊就是用户loginName,如果是群聊就是群组id @@ -205,9 +205,6 @@ public class MallsuiteImSocketHandler implements WebSocketHandler { if (!mine.getId().equals(to.getId())) { sendVO.setMine(false); - if ("voice".equals(receiveDTO.getMine().getType()) || "video".equals(receiveDTO.getMine().getType())) { - sendVO.setMessage_length(receiveDTO.getMine().getContent()); - } //保存聊天记录 if ("friend".equals(type) || "user".equals(type)) { //如果是私聊 diff --git a/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/MineDTO.java b/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/MineDTO.java index 8a3a6037..1a55ea5b 100644 --- a/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/MineDTO.java +++ b/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/MineDTO.java @@ -42,6 +42,9 @@ public class MineDTO implements Serializable { @ApiModelProperty(value = "商品编号") private Long item_id = 0l; + @ApiModelProperty(value = "长度") + private String length = "0"; + @Override public String toString() { From 8e6da147a1b689b393b0bf342c2b794771804752 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 22 May 2025 09:15:10 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A5=E5=BF=97bash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shop/lakala/service/impl/LakalaApiServiceImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 0514233e..89675780 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 @@ -1872,7 +1872,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { : "返回状态有误"; return CommonResult.failed("查询银行卡Bin信息失败," + errorMsg); } - + // 获取并返回结果数据 JSONObject respData = respBody.getJSONObject("respData"); if (respData == null) { @@ -1899,7 +1899,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { */ @Override public JSONObject queryMchCanSplitAmt(String merchantNo, String logNo, String logDate) { - if (StrUtil.isBlank(merchantNo) || (StrUtil.isBlank(logNo) && StrUtil.isBlank(logDate))) { + if (StringUtils.isAnyBlank(merchantNo, logNo, logDate)) { return null; } @@ -1934,7 +1934,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { } /** - * 拉卡拉订单分账,用户下单成功之后,进行分账 + * 拉卡拉订单分账,用户下单成功之后(大约15秒后),进行分账 * 说明:分账指令是异步处理模式,响应报文成功时,指令状态是”status”: “PROCESSING”,需要等待分账结果通知,或者主动发起查询,建议主动发起查询与分账指令动作之间间隔15秒以上。 * 参考:https://o.lakala.com/#/home/document/detail?id=389 * @@ -1967,7 +1967,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { return Pair.of(true, "分账成功执行,处理中"); } catch (SDKException e) { - log.error("账户余额查询失败:", e); + log.error("分账发生错误:", e); return Pair.of(false, "分账发生错误"); } } From dd80552720eb72625e76b00fd818d688863d4a04 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Fri, 23 May 2025 00:35:50 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E5=88=86=E8=B4=A6=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lakala/LklLedgerMerReceiverBind.java | 1 + .../modules/lakala/LklOrderSeparate.java | 3 + .../common/modules/order/ShopOrderLkl.java | 70 ++++++++ .../shop/lakala/service/LakalaApiService.java | 10 +- .../lakala/service/LklLedgerEcService.java | 2 +- .../service/LklLedgerMemberService.java | 2 +- .../LklLedgerMerReceiverBindService.java | 16 +- .../service/LklLedgerReceiverService.java | 2 +- .../service/impl/LakalaApiServiceImpl.java | 157 ++++++++++++++---- .../service/impl/LklLedgerEcServiceImpl.java | 2 +- .../impl/LklLedgerMemberServiceImpl.java | 4 +- .../LklLedgerMerReceiverBindServiceImpl.java | 40 ++++- .../impl/LklLedgerReceiverServiceImpl.java | 2 +- .../lakala/service/impl/LklTkServiceImpl.java | 37 +++-- .../shop/order/mapper/ShopOrderLklMapper.java | 15 ++ .../order/service/ShopOrderLklService.java | 34 ++++ .../service/impl/ShopOrderLklServiceImpl.java | 67 ++++++++ .../admin/ShopStoreBaseController.java | 2 +- .../store/service/ShopStoreBaseService.java | 3 +- .../impl/ShopStoreBaseServiceImpl.java | 28 +++- .../mapper/order/ShopOrderLklMapper.xml | 8 + 21 files changed, 435 insertions(+), 70 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderLklMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java create mode 100644 mall-shop/src/main/resources/mapper/order/ShopOrderLklMapper.xml diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMerReceiverBind.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMerReceiverBind.java index 5b714f5e..2495d37a 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMerReceiverBind.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMerReceiverBind.java @@ -40,6 +40,7 @@ public class LklLedgerMerReceiverBind implements Serializable { private String entrust_file_path; private String ret_url; private String apply_id; + private Long platform_id; private String audit_status; private String audit_status_text; private String remark; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java index d0562013..bd3ef01c 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java @@ -46,6 +46,9 @@ public class LklOrderSeparate { @ApiModelProperty(value = "分账指令流水号,分账系统生成唯一流水") private String separate_no; + @ApiModelProperty(value = "平台订单Id") + private String order_id; + @ApiModelProperty(value = "分账总金额,单位:分") private String total_amt; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java new file mode 100644 index 00000000..b147537a --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java @@ -0,0 +1,70 @@ +package com.suisung.mall.common.modules.order; + +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.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + *

+ * 关联拉卡拉的店铺订单信息 + *

+ * + * @author Xinze + * @since 2021-04-30 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("shop_order_lkl") +@ApiModel(value = "ShopOrderLkl对象", description = "关联拉卡拉的店铺订单信息") +public class ShopOrderLkl implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "自增Id") + @TableId(value = "id", type = IdType.INPUT) + private Long id; + + private String store_id; + + private String order_id; + + private String log_no; + + private String log_date; + + private String merchant_no; + + private Integer total_amt; + + private Integer shopping_fee; + + private BigDecimal split_ratio; + + private String payment_time; + + private String account_type; + + private String trans_type; + + private String lkl_trade_no; + + private String lkl_term_no; + + private String lkl_resp; + + private Integer status; + + private Date created_at; + + private Date updated_at; +} 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 ee485fc4..3b015460 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 @@ -9,7 +9,6 @@ package com.suisung.mall.shop.lakala.service; import cn.hutool.json.JSONObject; -import com.lkl.laop.sdk.request.V3SacsSeparateRequest; import com.suisung.mall.common.api.CommonResult; import org.springframework.data.util.Pair; @@ -190,7 +189,8 @@ public interface LakalaApiService { * @param merchantNo 拉卡拉外部商户号 * @param logNo 拉卡拉对账单流水号 * @param logDate 拉卡拉对账单交易日期 yyyyMMdd - * @return 响应结果 { + * @return 响应结果 分账总金额,可分账金额 键值对 + * { * "merchant_no": "82229005943096D", * "total_separate_amt": "9900", * "can_separate_amt": "0", @@ -198,15 +198,15 @@ public interface LakalaApiService { * "log_no": "66210306990190" * } */ - JSONObject queryMchCanSplitAmt(String merchantNo, String logNo, String logDate); + Pair queryMchCanSplitAmt(String merchantNo, String logNo, String logDate); /** * 拉卡拉订单分账,用户下单成功之后,进行分账 * 说明:分账指令是异步处理模式,响应报文成功时,指令状态是”status”: “PROCESSING”,需要等待分账结果通知,或者主动发起查询,建议主动发起查询与分账指令动作之间间隔15秒以上。 * 参考:https://o.lakala.com/#/home/document/detail?id=389 * - * @param v3SacsSeparateRequest + * @param orderId 平台订单号 * @return */ - Pair innerDoOrderSeparate(V3SacsSeparateRequest v3SacsSeparateRequest); + Pair innerDoOrderSeparate(String orderId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerEcService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerEcService.java index 63afe7aa..343e8bd3 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerEcService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerEcService.java @@ -19,7 +19,7 @@ public interface LklLedgerEcService extends IBaseService { * @param record * @return */ - Boolean saveOrUpdateByMchId(LklLedgerEc record); + Boolean addOrUpdateByMchId(LklLedgerEc record); /** * 根据applyId更新记录 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMemberService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMemberService.java index 29bfbda5..d0a6aa46 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMemberService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMemberService.java @@ -36,7 +36,7 @@ public interface LklLedgerMemberService extends IBaseService { * @param record * @return */ - Boolean saveOrUpdateByMerCupNo(LklLedgerMember record); + Boolean addOrUpdateByMerCupNo(LklLedgerMember record); /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java index 238f54f1..59f54b1e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java @@ -11,6 +11,8 @@ package com.suisung.mall.shop.lakala.service; import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind; import com.suisung.mall.core.web.service.IBaseService; +import java.util.List; + public interface LklLedgerMerReceiverBindService extends IBaseService { /** @@ -19,10 +21,10 @@ public interface LklLedgerMerReceiverBindService extends IBaseService selectByMerCupNo(String merCupNo, Boolean isPlatform); + /** * 更新审核结果 * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java index 149eab1b..6efa80e8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java @@ -22,7 +22,7 @@ public interface LklLedgerReceiverService extends IBaseService queryMchCanSplitAmt(String merchantNo, String logNo, String logDate) { if (StringUtils.isAnyBlank(merchantNo, logNo, logDate)) { return null; } @@ -1926,7 +1935,14 @@ public class LakalaApiServiceImpl implements LakalaApiService { return null; } - return (JSONObject) lklRespJSON.get("resp_data"); + JSONObject respData = (JSONObject) lklRespJSON.get("resp_data"); + if (respData == null + || StringUtils.isAnyBlank(respData.getStr("total_separate_amt"), respData.getStr("can_separate_amt"))) { + log.error(I18nUtil._("返回值有误!")); + return null; + } + + return Pair.of(respData.getStr("total_separate_amt"), respData.getStr("can_separate_amt")); } catch (SDKException e) { log.error("账户余额查询失败:", e); return null; @@ -1938,38 +1954,123 @@ public class LakalaApiServiceImpl implements LakalaApiService { * 说明:分账指令是异步处理模式,响应报文成功时,指令状态是”status”: “PROCESSING”,需要等待分账结果通知,或者主动发起查询,建议主动发起查询与分账指令动作之间间隔15秒以上。 * 参考:https://o.lakala.com/#/home/document/detail?id=389 * - * @param v3SacsSeparateRequest + * @param orderId 平台订单Id * @return */ @Override - public Pair innerDoOrderSeparate(V3SacsSeparateRequest v3SacsSeparateRequest) { - if (v3SacsSeparateRequest == null) { - return Pair.of(false, "分账参数不能为空"); + public Pair innerDoOrderSeparate(String orderId) { + if (StrUtil.isBlank(orderId)) { + return Pair.of(false, "订单号不能为空"); } - // 1. 配置初始化 - initLKLSDK(); - try { - log.debug("分账执行请求参数:{}", JSONUtil.toJsonStr(v3SacsSeparateRequest)); - //3. 发送请求 - String responseStr = LKLSDK.httpPost(v3SacsSeparateRequest); - if (StrUtil.isBlank(responseStr)) { - return Pair.of(false, "服务器无返回值!"); + // 1. 配置初始化 + initLKLSDK(); + + // 获取订单信息、店铺信息,商户信息、分账信息 + List orderList = shopOrderLklService.selectByOrderId(orderId, ""); + if (CollectionUtil.isEmpty(orderList)) { + return Pair.of(false, "订单不存在"); } - JSONObject lklRespJSON = JSONUtil.parseObj(responseStr); - if (lklRespJSON == null || !lklSacsSuccessCode.equals(lklRespJSON.getStr("code")) || lklRespJSON.get("resp_data") == null) { - log.error("返回值有误!"); - return Pair.of(false, "返回值有误!"); - } - return Pair.of(true, "分账成功执行,处理中"); + for (ShopOrderLkl order : orderList) { + String merchantNo = order.getMerchant_no(); + + // 分账总金额 + Integer paymentAmount = order.getTotal_amt(); + Integer shoppingFee = order.getShopping_fee(); + BigDecimal splitRatio = order.getSplit_ratio(); + if (paymentAmount <= 0 || (shoppingFee != null && paymentAmount - shoppingFee <= 0)) { + return Pair.of(false, "订单金额有误"); + } + + V3SacsSeparateRequest req = new V3SacsSeparateRequest(); + req.setMerchantNo(merchantNo); + req.setLogNo(orderId); + req.setLogDate(order.getLog_date()); + req.setOutSeparateNo(StringUtils.genLklOrderNo(18)); // 14+18=32位商户分账指令流水号,每个商户号下唯一,否则会校验失败 + req.setTotalAmt(order.getTotal_amt().toString()); + req.setLklOrgNo(orgCode); + req.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0 + req.setNotifyUrl(projectDomain + "/api/v1/lkl/order/separate/notify"); + + + List platformList = lklLedgerMerReceiverBindService.selectByMerCupNo(merchantNo, true); + List distributorList = lklLedgerMerReceiverBindService.selectByMerCupNo(merchantNo, false); + if (CollectionUtil.isEmpty(platformList)) { + return Pair.of(false, "商户未绑定分账平台方"); + } + + + List recvDatas = new ArrayList<>(); + + + // 是否有运费 + if (shoppingFee != null && shoppingFee > 0) { + V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); + recvData.setRecvNo(platformList.get(0).getReceiver_no()); + recvData.setSeparateValue(shoppingFee.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData); + } + + Integer splitAmount = paymentAmount - shoppingFee; + if (!CollectionUtils.isEmpty(distributorList) + && (splitRatio != null && splitRatio.compareTo(BigDecimal.ONE) > 0)) { + // 平台方分账 + BigDecimal separateValue = new BigDecimal(splitAmount).multiply(new BigDecimal("0.01")); // 平台收取1%手续费 + if (separateValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); + recvData.setRecvNo(platformList.get(0).getReceiver_no()); + recvData.setSeparateValue(separateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData); + } + + // 代理方分账, 100-1-splitRatio 1%是平台方的,其余都是代理商的 + BigDecimal separateValue2 = new BigDecimal(splitAmount).multiply(new BigDecimal(99).subtract(splitRatio).divide(new BigDecimal(100))); + if (separateValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas recvData2 = new V3SacsSeparateRecvDatas(); + recvData2.setRecvNo(distributorList.get(0).getReceiver_no()); + recvData2.setSeparateValue(separateValue2.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData2); + } + } else { + // 仅平台方一方分账 + BigDecimal separateValue = new BigDecimal(splitAmount).multiply(new BigDecimal(100).subtract(splitRatio).divide(new BigDecimal(100))); + if (separateValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); + recvData.setRecvNo(platformList.get(0).getReceiver_no()); + recvData.setSeparateValue(separateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData); + } + } + + req.setRecvDatas(recvDatas); + + + log.debug("分账执行请求参数:{}", JSONUtil.toJsonStr(req)); + + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + if (StrUtil.isBlank(responseStr)) { + return Pair.of(false, "服务器无返回值!"); + } + + JSONObject lklRespJSON = JSONUtil.parseObj(responseStr); + if (lklRespJSON == null || !lklSacsSuccessCode.equals(lklRespJSON.getStr("code")) || lklRespJSON.get("resp_data") == null) { + log.error("分账返回值有误!"); + return Pair.of(false, "返回值有误!"); + } + + return Pair.of(true, "分账成功执行,处理中"); + } } catch (SDKException e) { log.error("分账发生错误:", e); return Pair.of(false, "分账发生错误"); } + + return null; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerEcServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerEcServiceImpl.java index 2cf0f6ca..eeb2003d 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerEcServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerEcServiceImpl.java @@ -47,7 +47,7 @@ public class LklLedgerEcServiceImpl extends BaseServiceImpl selectByMerCupNo(String merCupNo, Boolean isPlatform) { + if (StrUtil.isBlank(merCupNo)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_cup_no", merCupNo) + .eq("audit_status", CommonConstant.Enable); + if (isPlatform != null && isPlatform) { + queryWrapper.eq("platform_id", 0); + } else if (isPlatform != null && !isPlatform) { + queryWrapper.gt("platform_id", 0); + } + + queryWrapper.orderByAsc("id"); + + return list(queryWrapper); + } + /** * 更新审核结果 * @@ -111,7 +145,7 @@ public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl retPair = shopStoreBaseService.merchEntryInfo2StoreInfo(mchMobile); - - if (retPair.getFirst() > 0) { - // 2025-05-17暂停e签宝电子合同生成流程 - // 更改合同记录表的店铺id - // esignContractService.updateContractStoreId(mchMobile, retPair.getFirst()); - // 填充合同模版表的店铺Id - // esignContractFillingFileService.updateContractFillingStoreId(mchMobile, retPair.getFirst()); - // 店铺创建状态已完成 - shopMchEntryService.updateMerchEntryStoreStatus(mchMobile, CommonConstant.Enable); - logger.info("商家进件:初始化店铺成功!"); - } else { - logger.error("商家进件:初始化店铺失败!"); - // throw new ApiException("商家进件:初始化店铺失败!"); - } - } +// if (shopEntry != null && !CommonConstant.Enable.equals(shopEntry.getStore_status())) { +// String mchMobile = shopEntry.getLogin_mobile(); +// // 禁止往外抛异常,如果失败使用补偿机制,创建新店 +// Pair retPair = shopStoreBaseService.merchEntryInfo2StoreInfo(mchMobile, false); +// +// if (retPair.getFirst() > 0) { +// // 2025-05-17暂停e签宝电子合同生成流程 +// // 更改合同记录表的店铺id +// // esignContractService.updateContractStoreId(mchMobile, retPair.getFirst()); +// // 填充合同模版表的店铺Id +// // esignContractFillingFileService.updateContractFillingStoreId(mchMobile, retPair.getFirst()); +// // 店铺创建状态已完成 +// shopMchEntryService.updateMerchEntryStoreStatus(mchMobile, CommonConstant.Enable); +// logger.info("商家进件:初始化店铺成功!"); +// } else { +// logger.error("商家进件:初始化店铺失败!"); +// // throw new ApiException("商家进件:初始化店铺失败!"); +// } +// } // 1、(电子合同)给商家申请分账功能使用;务必检查是否申请过?申请过忽略 // 下一步等待拉卡拉审核通过,再绑定接收方和商家的关系 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderLklMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderLklMapper.java new file mode 100644 index 00000000..6d954634 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderLklMapper.java @@ -0,0 +1,15 @@ +/* + * 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.order.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.order.ShopOrderLkl; + +public interface ShopOrderLklMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java new file mode 100644 index 00000000..1e4c12ef --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderLklService.java @@ -0,0 +1,34 @@ +/* + * 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.order.service; + +import com.suisung.mall.common.modules.order.ShopOrderLkl; +import com.suisung.mall.core.web.service.IBaseService; + +import java.util.List; + +public interface ShopOrderLklService extends IBaseService { + + /** + * 添加或更新记录 + * + * @param record + * @return + */ + Boolean addOrUpdateByStoreOrder(ShopOrderLkl record); + + /** + * 根据订单编号查询多条记录(因为一个订单里可能有多个店铺的商品) + * + * @param orderId + * @param storeId + * @return + */ + List selectByOrderId(String orderId, String storeId); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java new file mode 100644 index 00000000..18a2eb37 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLklServiceImpl.java @@ -0,0 +1,67 @@ +/* + * 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.order.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.modules.order.ShopOrderLkl; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.order.mapper.ShopOrderLklMapper; +import com.suisung.mall.shop.order.service.ShopOrderLklService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class ShopOrderLklServiceImpl extends BaseServiceImpl implements ShopOrderLklService { + + @Override + public Boolean addOrUpdateByStoreOrder(ShopOrderLkl record) { + if (record == null + || StringUtils.isAnyBlank(record.getOrder_id(), record.getStore_id(), record.getLog_no())) { + return false; + } + + List existsRecordList = selectByOrderId(record.getOrder_id(), record.getStore_id()); + if (CollectionUtil.isNotEmpty(existsRecordList) + && existsRecordList.get(0) != null + && existsRecordList.get(0).getId() > 0) { + // 更新记录 + record.setId(existsRecordList.get(0).getId()); + return updateById(record); + } + + return add(record); + } + + /** + * 根据订单编号查询多条记录(因为一个订单里可能有多个店铺的商品) + * + * @param orderId + * @return + */ + @Override + public List selectByOrderId(String orderId, String storeId) { + if (StringUtils.isBlank(orderId)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("order_id", orderId); + if (StrUtil.isNotBlank(storeId)) { + queryWrapper.eq("store_id", storeId); + } + + return list(queryWrapper); + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreBaseController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreBaseController.java index 44393d0d..7119d95a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreBaseController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreBaseController.java @@ -254,7 +254,7 @@ public class ShopStoreBaseController extends BaseControllerImpl { @ApiOperation(value = "商家入驻资料转成店铺", notes = "商家入驻资料转成店铺") @RequestMapping(value = "/mchinfo/to/storeinfo", method = RequestMethod.POST) public CommonResult merchEntryInfo2StoreInfo(@RequestBody JSONObject jsonParam) { - Pair result = shopStoreBaseService.merchEntryInfo2StoreInfo(jsonParam.getStr("mchMobile")); + Pair result = shopStoreBaseService.merchEntryInfo2StoreInfo(jsonParam.getStr("mchMobile"), true); if (result.getFirst().equals(0)) { return CommonResult.failed(result.getSecond()); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreBaseService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreBaseService.java index 47ac019b..a62579ff 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreBaseService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreBaseService.java @@ -141,9 +141,10 @@ public interface ShopStoreBaseService extends IBaseService { * (重要)入驻审批通过并且合同盖章完结之后,把商家入驻信息转换成店铺信息,正式生成店铺所需的数据 * * @param mchMobile + * @param allowThrown 是否允许抛出异常 * @return 店铺Id */ - Pair merchEntryInfo2StoreInfo(String mchMobile); + Pair merchEntryInfo2StoreInfo(String mchMobile, Boolean allowThrown); /** * 根据店铺名称判断店铺是否存在 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java index a49ec76a..7767ed28 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreBaseServiceImpl.java @@ -2991,11 +2991,12 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl merchEntryInfo2StoreInfo(String mchMobile) { + public Pair merchEntryInfo2StoreInfo(String mchMobile, Boolean allowThrown) { if (StrUtil.isBlank(mchMobile)) { logger.error("生成店铺:商家手机号不能为空"); return Pair.of(0, "商家手机不能为空"); @@ -3071,7 +3072,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl + + + + + * + + From 8158131291480aac46e43b3d213bf03b9cb88f1f Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Fri, 23 May 2025 11:25:05 +0800 Subject: [PATCH 07/11] =?UTF-8?q?im=E7=A6=BB=E7=BA=BF=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E7=B1=BB=E5=9E=8B=E5=92=8C=E9=95=BF=E5=BA=A6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websocket/service/onchat/MallsuiteImSocketHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java b/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java index 9a110c5c..82ed6eb2 100644 --- a/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java +++ b/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java @@ -221,9 +221,9 @@ public class MallsuiteImSocketHandler implements WebSocketHandler { } else { sendVO.setToid(mine.getId()); sendVO.setContent(to.getName() + " 对方现在离线,他将在上线后收到你的消息!"); - + sendVO.setMsg_type("text"); + sendVO.setMessage_length("0"); sendVO.setId(to.getId()); - this.sendMessageToUser(sender, sendVO);//同时向本人发送对方不在线消息 chat.setStatus("0");//设置为未读 } From f0504bac5e1164ce68817a2208b3fca520d663f4 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Fri, 23 May 2025 14:21:06 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/resources/application.yml b/client/src/main/resources/application.yml index 6b48c137..eb19c279 100644 --- a/client/src/main/resources/application.yml +++ b/client/src/main/resources/application.yml @@ -12,4 +12,4 @@ logging: charset: console: UTF-8 -remoteIp: https://mall.gpxscs.cn \ No newline at end of file +remoteIp: https://mall.gpxscs.cn/api \ No newline at end of file From f73a82cdd6d4efb77db74783d8a23058f5f4b16f Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Sat, 24 May 2025 00:26:05 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E5=88=86=E8=B4=A6=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../suisung/mall/common/utils/CommonUtil.java | 16 ++ .../controller/mobile/LakalaController.java | 26 +- .../shop/lakala/service/LakalaApiService.java | 9 + .../LklLedgerMerReceiverBindService.java | 19 +- .../service/LklOrderSeparateService.java | 23 ++ .../service/impl/LakalaApiServiceImpl.java | 237 +++++++++++++----- .../LklLedgerMerReceiverBindServiceImpl.java | 36 ++- .../impl/LklOrderSeparateServiceImpl.java | 50 ++++ .../service/impl/ShopMchEntryServiceImpl.java | 2 +- 9 files changed, 352 insertions(+), 66 deletions(-) create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderSeparateService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/CommonUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/CommonUtil.java index 30311956..2fb4341e 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/CommonUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/CommonUtil.java @@ -140,6 +140,8 @@ public class CommonUtil { /** * Double 四舍五入,不保留小数点 + * 2.5=3 + * 2.3=2 * * @param d * @return @@ -150,6 +152,8 @@ public class CommonUtil { /** * Decimal 四舍五入,不保留小数点 + * 2.5=3 + * 2.3=2 * * @param d * @return @@ -158,6 +162,18 @@ public class CommonUtil { return d.setScale(0, RoundingMode.HALF_UP); } + /** + * Decimal 不四舍五入,不保留小数点 + * 2.5 = 2 + * 2.2 = 2 + * + * @param d + * @return + */ + public static BigDecimal DecimalRoundHalfDown(BigDecimal d) { + return d.setScale(0, RoundingMode.HALF_DOWN); + } + /** * 根据上一个订单状态和当前状态,获取订单状态变化备注 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 fc8e70a9..19b4d6dd 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 @@ -12,6 +12,7 @@ import cn.hutool.json.JSONObject; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.service.impl.BaseControllerImpl; import com.suisung.mall.shop.lakala.service.LakalaApiService; +import com.suisung.mall.shop.message.service.ShopMessageTemplateService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; @@ -23,6 +24,8 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; @Api(tags = "拉卡拉相关接口 - 前端控制器") @RestController @@ -32,11 +35,20 @@ public class LakalaController extends BaseControllerImpl { @Resource private LakalaApiService lakalaPayService; + @Resource + private ShopMessageTemplateService shopMessageTemplateService; + @ApiOperation(value = "测试案例", notes = "测试案例") @RequestMapping(value = "/testcase", method = RequestMethod.POST) public Object testcase(@RequestBody JSONObject paramsJSON) { - return lakalaPayService.applyLedgerMerEc(paramsJSON.getStr("mchMobile")); +// return lakalaPayService.applyLedgerMerEc(paramsJSON.getStr("mchMobile")); // return lakalaPayService.LedgerMerEcDownload(975790666910121984L); + + Map tmplArgs = new HashMap<>(1); + tmplArgs.put("name", "桂平市厚德贸易有限公司"); // 商家公司名称 + // 所有店铺管理员的发送邮件, 提醒商家:您有一笔新的订单 ${order_id},请及时处理。 + shopMessageTemplateService.aliyunSmsSend("17777525395", "SMS_486545331", tmplArgs);//SMS_479760276 + return CommonResult.success(); } @ApiOperation(value = "本地文件转base64", notes = "本地文件转base64") @@ -103,4 +115,16 @@ public class LakalaController extends BaseControllerImpl { return new ResponseEntity<>(resp, HttpStatus.INTERNAL_SERVER_ERROR); } + + // https://mall.gpxscs.cn/api/mobile/shop/lakala/sacs/separateNotify + @ApiOperation(value = "订单分账异步回调通知", notes = "订单分账异步回调通知") + @RequestMapping(value = "/sacs/separateNotify", method = RequestMethod.POST) + public ResponseEntity sacsSeparateNotify(HttpServletRequest request) { + JSONObject resp = lakalaPayService.sacsSeparateNotify(request); + if (resp != null && "SUCCESS".equals(resp.get("code"))) { + return new ResponseEntity<>(resp, HttpStatus.OK); + } + + return new ResponseEntity<>(resp, HttpStatus.INTERNAL_SERVER_ERROR); + } } 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 3b015460..9384d1ae 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 @@ -209,4 +209,13 @@ public interface LakalaApiService { * @return */ Pair innerDoOrderSeparate(String orderId); + + /** + * 分账结果通知 + * 参考:https://o.lakala.com/#/home/document/detail?id=393 + * + * @param request + * @return + */ + JSONObject sacsSeparateNotify(HttpServletRequest request); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java index 59f54b1e..f1a5a965 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java @@ -40,7 +40,24 @@ public interface LklLedgerMerReceiverBindService extends IBaseService selectByMerCupNo(String merCupNo, Boolean isPlatform); + List selectListByMerCupNo(String merCupNo, Boolean isPlatform); + + + /** + * 根据商户编号查询一条平台绑定记录 + * + * @param merCupNo + * @return + */ + LklLedgerMerReceiverBind getPlatformByMerCupNo(String merCupNo); + + /** + * 根据商户编号查询一条代理商绑定记录 + * + * @param merCupNo + * @return + */ + List selectDistributorByMerCupNo(String merCupNo); /** * 更新审核结果 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderSeparateService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderSeparateService.java new file mode 100644 index 00000000..07ea7bac --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderSeparateService.java @@ -0,0 +1,23 @@ +/* + * 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.LklOrderSeparate; +import com.suisung.mall.core.web.service.IBaseService; + +public interface LklOrderSeparateService extends IBaseService { + + /** + * 新增或更新记录 + * + * @param record + * @return + */ + Boolean addOrUpdateByReceiverNo(LklOrderSeparate record); +} 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 d0c4a9cb..318dd1e5 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 @@ -26,10 +26,7 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.feignService.ShopService; -import com.suisung.mall.common.modules.lakala.LklLedgerEc; -import com.suisung.mall.common.modules.lakala.LklLedgerMember; -import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind; -import com.suisung.mall.common.modules.lakala.LklLedgerReceiver; +import com.suisung.mall.common.modules.lakala.*; import com.suisung.mall.common.modules.order.ShopOrderLkl; import com.suisung.mall.common.modules.store.ShopMchEntry; import com.suisung.mall.common.modules.store.ShopStoreBase; @@ -144,6 +141,10 @@ public class LakalaApiServiceImpl implements LakalaApiService { @Resource private LklTkServiceImpl lklTkService; + @Lazy + @Resource + private LklOrderSeparateService lklOrderSeparateService; + @Resource private OssService ossService; @@ -1969,80 +1970,99 @@ public class LakalaApiServiceImpl implements LakalaApiService { initLKLSDK(); // 获取订单信息、店铺信息,商户信息、分账信息 - List orderList = shopOrderLklService.selectByOrderId(orderId, ""); - if (CollectionUtil.isEmpty(orderList)) { + List shopOrderLklList = shopOrderLklService.selectByOrderId(orderId, ""); + if (CollectionUtil.isEmpty(shopOrderLklList)) { return Pair.of(false, "订单不存在"); } - for (ShopOrderLkl order : orderList) { - String merchantNo = order.getMerchant_no(); + int successCnt = 0; + // 一个订单里包含了多个商家的时候,需要处理 + for (ShopOrderLkl shopOrderLkl : shopOrderLklList) { + String merchantNo = shopOrderLkl.getMerchant_no(); // 分账总金额 - Integer paymentAmount = order.getTotal_amt(); - Integer shoppingFee = order.getShopping_fee(); - BigDecimal splitRatio = order.getSplit_ratio(); - if (paymentAmount <= 0 || (shoppingFee != null && paymentAmount - shoppingFee <= 0)) { - return Pair.of(false, "订单金额有误"); + Integer paymentAmount = shopOrderLkl.getTotal_amt(); + Integer shoppingFee = shopOrderLkl.getShopping_fee(); + BigDecimal splitRatioMch = shopOrderLkl.getSplit_ratio(); + // 应付款金额大于零,并且大于运费。 + if (paymentAmount <= 0 + || (shoppingFee != null && paymentAmount - shoppingFee <= 0)) { + log.error("店铺{}订单{}金额有误或运费高于分账金额,不会进行分账", shopOrderLkl.getStore_id(), orderId); + continue; + } + + // 平台和代理商的分成比例 + BigDecimal splitRatioNoMch = new BigDecimal("100").subtract(shopOrderLkl.getSplit_ratio()); + LklLedgerMerReceiverBind platform = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo); + List distributorList = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo); + if (platform == null) { + log.error("店铺{}订单{}未绑定平台方,不会进行分账", shopOrderLkl.getStore_id(), orderId); + continue; + } + + boolean canSplit = true; // 能分账并扣运费 + if (splitRatioMch == null || splitRatioMch.compareTo(new BigDecimal("100")) >= 0) { + log.warn("店铺{}分账比例为0,只扣运费,不会进行分账", shopOrderLkl.getStore_id()); + canSplit = false; //不分账,只扣运费 } V3SacsSeparateRequest req = new V3SacsSeparateRequest(); - req.setMerchantNo(merchantNo); - req.setLogNo(orderId); - req.setLogDate(order.getLog_date()); + req.setMerchantNo(merchantNo); // 拉卡拉商户号 + req.setLogNo(shopOrderLkl.getLog_no()); + req.setLogDate(shopOrderLkl.getLog_date()); req.setOutSeparateNo(StringUtils.genLklOrderNo(18)); // 14+18=32位商户分账指令流水号,每个商户号下唯一,否则会校验失败 - req.setTotalAmt(order.getTotal_amt().toString()); + req.setTotalAmt(shopOrderLkl.getTotal_amt().toString()); req.setLklOrgNo(orgCode); req.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0 - req.setNotifyUrl(projectDomain + "/api/v1/lkl/order/separate/notify"); - - - List platformList = lklLedgerMerReceiverBindService.selectByMerCupNo(merchantNo, true); - List distributorList = lklLedgerMerReceiverBindService.selectByMerCupNo(merchantNo, false); - if (CollectionUtil.isEmpty(platformList)) { - return Pair.of(false, "商户未绑定分账平台方"); - } - + req.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify"); List recvDatas = new ArrayList<>(); - - // 是否有运费 + // 平台先代扣掉运费 if (shoppingFee != null && shoppingFee > 0) { V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); - recvData.setRecvNo(platformList.get(0).getReceiver_no()); + recvData.setRecvNo(platform.getReceiver_no()); recvData.setSeparateValue(shoppingFee.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 recvDatas.add(recvData); } - Integer splitAmount = paymentAmount - shoppingFee; - if (!CollectionUtils.isEmpty(distributorList) - && (splitRatio != null && splitRatio.compareTo(BigDecimal.ONE) > 0)) { - // 平台方分账 - BigDecimal separateValue = new BigDecimal(splitAmount).multiply(new BigDecimal("0.01")); // 平台收取1%手续费 - if (separateValue.compareTo(BigDecimal.ZERO) > 0) { - V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); - recvData.setRecvNo(platformList.get(0).getReceiver_no()); - recvData.setSeparateValue(separateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData); - } + if (canSplit) { //扣完运费后还能能分账 - // 代理方分账, 100-1-splitRatio 1%是平台方的,其余都是代理商的 - BigDecimal separateValue2 = new BigDecimal(splitAmount).multiply(new BigDecimal(99).subtract(splitRatio).divide(new BigDecimal(100))); - if (separateValue.compareTo(BigDecimal.ZERO) > 0) { - V3SacsSeparateRecvDatas recvData2 = new V3SacsSeparateRecvDatas(); - recvData2.setRecvNo(distributorList.get(0).getReceiver_no()); - recvData2.setSeparateValue(separateValue2.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData2); - } - } else { - // 仅平台方一方分账 - BigDecimal separateValue = new BigDecimal(splitAmount).multiply(new BigDecimal(100).subtract(splitRatio).divide(new BigDecimal(100))); - if (separateValue.compareTo(BigDecimal.ZERO) > 0) { - V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); - recvData.setRecvNo(platformList.get(0).getReceiver_no()); - recvData.setSeparateValue(separateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 - recvDatas.add(recvData); + // 应付款扣除运费后,得出实际分账的金额,单位:分 + Integer splitAmount = paymentAmount - shoppingFee; + if (!CollectionUtils.isEmpty(distributorList) + && (splitRatioMch != null && splitRatioNoMch.compareTo(BigDecimal.ONE) > 0)) { // 有平台和代理商,并且分账比例大于1的情况 + + // 平台方分账 + BigDecimal platformSeparateValue = CommonUtil.DecimalRoundHalfDown(new BigDecimal(splitAmount).multiply(new BigDecimal("0.01"))); // 平台收取1%手续费 + if (platformSeparateValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); + recvData.setRecvNo(platform.getReceiver_no()); + recvData.setSeparateValue(platformSeparateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData); + } + + // TODO: 一级以上的代理商以后做处理 + // 一级代理方分账, 分账比例扣掉1%的平台方费,其余都是代理商的 + BigDecimal splitRatioDistributor = splitRatioNoMch.subtract(new BigDecimal("1")).divide(new BigDecimal(100)); + BigDecimal distributorSeparateValue = CommonUtil.DecimalRoundHalfDown(new BigDecimal(splitAmount).multiply(splitRatioDistributor)); + if (distributorSeparateValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas recvData2 = new V3SacsSeparateRecvDatas(); + recvData2.setRecvNo(distributorList.get(0).getReceiver_no()); + recvData2.setSeparateValue(distributorSeparateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData2); + } + } else { + // 仅平台方一方分账 + BigDecimal splitRatioPlatform = CommonUtil.DecimalRoundHalfDown(splitRatioNoMch.divide(new BigDecimal(100))); + BigDecimal separateValue = new BigDecimal(splitAmount).multiply(splitRatioPlatform); + if (separateValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas(); + recvData.setRecvNo(platform.getReceiver_no()); + recvData.setSeparateValue(separateValue.toString()); // 分账金额,单位分,分账金额不能大于订单金额,分账金额不能小于0 + recvDatas.add(recvData); + } } } @@ -2054,23 +2074,118 @@ public class LakalaApiServiceImpl implements LakalaApiService { //3. 发送请求 String responseStr = LKLSDK.httpPost(req); if (StrUtil.isBlank(responseStr)) { - return Pair.of(false, "服务器无返回值!"); + log.error("服务器无返回值!"); + continue; } + log.debug("分账执行响应参数:{}", responseStr); JSONObject lklRespJSON = JSONUtil.parseObj(responseStr); - if (lklRespJSON == null || !lklSacsSuccessCode.equals(lklRespJSON.getStr("code")) || lklRespJSON.get("resp_data") == null) { + if (lklRespJSON == null + || !lklSacsSuccessCode.equals(lklRespJSON.getStr("code")) + || lklRespJSON.getJSONObject("resp_data") == null) { log.error("分账返回值有误!"); - return Pair.of(false, "返回值有误!"); + continue; } - return Pair.of(true, "分账成功执行,处理中"); + JSONObject respData = lklRespJSON.getJSONObject("resp_data"); + + // 4. 处理返回结果,等待异步通知 + LklOrderSeparate record = new LklOrderSeparate(); + record.setMerchant_no(merchantNo); + record.setLog_no(shopOrderLkl.getLog_no()); + record.setLog_date(shopOrderLkl.getLog_date()); + record.setOrder_id(shopOrderLkl.getOrder_id()); + record.setTotal_amt(shopOrderLkl.getTotal_amt().toString()); + record.setOut_separate_no(req.getOutSeparateNo());// 必填参数 + record.setNotify_url(req.getNotifyUrl()); + record.setLkl_org_no(req.getLklOrgNo()); + record.setRecv_datas(JSONUtil.toJsonStr(req.getRecvDatas())); + record.setSeparate_no(respData.getStr("separate_no")); // 必填参数 + record.setStatus(respData.getStr("status")); + + lklOrderSeparateService.addOrUpdateByReceiverNo(record); + + successCnt++; } + + if (successCnt <= 0) { + return Pair.of(false, "分账失败"); + } else if (successCnt != shopOrderLklList.size()) { + return Pair.of(true, "部分订单分账执行成功,处理中"); + } + + return Pair.of(true, "全部订单分账执行成功,处理中"); } catch (SDKException e) { log.error("分账发生错误:", e); return Pair.of(false, "分账发生错误"); } + } - return null; + /** + * 分账结果通知 + * 参考:https://o.lakala.com/#/home/document/detail?id=393 + * + * @param request + * @return + */ + @Override + public JSONObject sacsSeparateNotify(HttpServletRequest request) { + log.debug("分账结果通知异步回调通知开始"); + + // 验签 + String authorization = request.getHeader("Authorization"); + String requestBody = LakalaUtil.getBody(request); + log.debug("分账结果通知异步回调返回requestbody 参数:{}\n authorization参数:{}\n", requestBody, authorization); + + + if (!LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath)) { + log.error("分账结果通知异步回调:验签失败"); + return JSONUtil.createObj().put("code", "FAIL").put("message", "验签失败!"); + } + + JSONObject paramsJSON = JSONUtil.parseObj(requestBody); + if (paramsJSON == null) { + log.error("分账结果通知异步回调:请求参数为空"); + return JSONUtil.createObj().put("code", "FAIL").put("message", "请求参数为空"); + } + + String logNo = paramsJSON.getStr("log_no"); + String separateNo = paramsJSON.getStr("separate_no"); + String outSeparateNo = paramsJSON.getStr("out_separate_no"); + String status = paramsJSON.getStr("status"); + String finalStatus = paramsJSON.getStr("final_status"); + + if (StrUtil.isBlank(outSeparateNo) || StrUtil.isBlank(separateNo) || StrUtil.isBlank(status)) { + String errMsg = "分账结果通知异步回调:缺少必要参数(outSeparateNo/separateNo/status)"; + log.error(errMsg + ":logNo={}, separateNo={}, status={}", outSeparateNo, separateNo, status); + return JSONUtil.createObj().put("code", "FAIL").put("message", "返回缺少必要参数"); + } + + try { + + // 4. 处理返回结果,等待异步通知 + LklOrderSeparate record = new LklOrderSeparate(); + record.setLog_no(logNo); + record.setSeparate_no(separateNo); + record.setOut_separate_no(paramsJSON.getStr("out_separate_no")); + record.setStatus(status); + record.setFinal_status(finalStatus); + record.setCal_type(paramsJSON.getStr("cal_type")); + record.setSeparate_type(paramsJSON.getStr("separate_type")); + record.setSeparate_date(paramsJSON.getStr("separate_date")); + record.setDetail_datas(paramsJSON.getJSONArray("detail_datas").toString()); + + Boolean success = lklOrderSeparateService.addOrUpdateByReceiverNo(record); + if (!success) { + return JSONUtil.createObj().put("code", "FAIL").put("message", "处理数据失败!"); + } + + log.debug("分账结果通知异步回调:处理成功,separateNo={}", separateNo); + return JSONUtil.createObj().put("code", "SUCCESS").put("message", "操作成功!"); + } catch (Exception e) { + log.error("分账结果通知异步回调:处理数据异常", e); + return JSONUtil.createObj().put("code", "FAIL").put("message", "处理数据异常"); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMerReceiverBindServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMerReceiverBindServiceImpl.java index ae08d1a5..8c8a31dd 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMerReceiverBindServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMerReceiverBindServiceImpl.java @@ -73,14 +73,14 @@ public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl selectByMerCupNo(String merCupNo, Boolean isPlatform) { + public List selectListByMerCupNo(String merCupNo, Boolean isPlatform) { if (StrUtil.isBlank(merCupNo)) { return null; } @@ -99,6 +99,38 @@ public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl list = selectListByMerCupNo(merCupNo, true); + if (CollectionUtil.isEmpty(list)) { + return null; + } + + return list.get(0); + } + + /** + * 根据商户编号查询一条代理商绑定记录 + * + * @param merCupNo + * @return + */ + @Override + public List selectDistributorByMerCupNo(String merCupNo) { + List list = selectListByMerCupNo(merCupNo, false); + if (CollectionUtil.isEmpty(list)) { + return null; + } + + return list; + } + /** * 更新审核结果 * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java new file mode 100644 index 00000000..6389e544 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java @@ -0,0 +1,50 @@ +/* + * 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.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.modules.lakala.LklOrderSeparate; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.lakala.mapper.LklOrderSeparateMapper; +import com.suisung.mall.shop.lakala.service.LklOrderSeparateService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class LklOrderSeparateServiceImpl extends BaseServiceImpl implements LklOrderSeparateService { + + /** + * 新增或更新记录 + * + * @param record + * @return + */ + @Override + public Boolean addOrUpdateByReceiverNo(LklOrderSeparate record) { + if (record == null || (StrUtil.isBlank(record.getOut_separate_no()) && StrUtil.isBlank(record.getSeparate_no()))) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("separate_no", record.getSeparate_no()).eq("out_separate_no", record.getOut_separate_no()); + if (StrUtil.isNotBlank(record.getSeparate_no())) { + queryWrapper.eq("log_no", record.getLog_no()); + } + LklOrderSeparate existsRecord = getOne(queryWrapper); + if (existsRecord != null && existsRecord.getId() > 0) { + // 更新记录 + record.setId(existsRecord.getId()); + return updateById(record); + } + + return add(record); + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java index fd23bdd3..472384b9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java @@ -247,7 +247,7 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(1); tmplArgs.put("name", record.getBiz_license_company()); // 商家公司名称 // 所有店铺管理员的发送邮件, 提醒商家:您有一笔新的订单 ${order_id},请及时处理。 - shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_479760276", tmplArgs);//SMS_475836097 + shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs);//SMS_479760276 } return CommonResult.success(); From c511e991d1f17125ecf3ba2940f27fd85c0f5076 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Sat, 24 May 2025 15:44:35 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E4=B8=AA=E6=8E=A8=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/controller/WebController.java | 10 +- .../mall/account/RedisKeyManage/RedisKey.java | 6 ++ .../account/controller/LoginController.java | 1 - .../mobile/SinglePushController.java | 35 +++++++ .../account/mapper/UserDeviceBindMapper.java | 9 ++ .../service/AccountSinglePushService.java | 16 ++++ .../service/UserDeviceBindService.java | 14 +++ .../impl/AccountSinglePushServiceImpl.java | 62 +++++++++++++ .../impl/AccountUserBaseServiceImpl.java | 22 ++++- .../impl/UserDeviceBindServiceImpl.java | 60 ++++++++++++ .../mall/auth/controller/AuthController.java | 4 +- .../mall/auth/domain/Oauth2TokenDto.java | 4 + mall-common/pom.xml | 9 ++ .../mall/common/RedisKeyManage/RedisKey.java | 6 ++ .../common/feignService/AccountService.java | 3 + .../modules/singlepush/UserDeviceBind.java | 59 ++++++++++++ .../common/service/SinglePushBaseService.java | 12 +++ .../impl/SinglePushBaseServiceImpl.java | 93 +++++++++++++++++++ .../mall/common/utils/ContextUtil.java | 15 +-- .../src/main/resources/application-local.yml | 7 +- .../src/main/resources/application-prod.yml | 5 + .../src/main/resources/application-test.yml | 7 +- .../src/main/resources/application-uat.yml | 5 + mall-shop/pom.xml | 1 - .../controller/SyncThirdDataController.java | 14 +-- sql/shop/dev/20250522_ddl.sql | 13 +++ 26 files changed, 466 insertions(+), 26 deletions(-) create mode 100644 mall-account/src/main/java/com/suisung/mall/account/RedisKeyManage/RedisKey.java create mode 100644 mall-account/src/main/java/com/suisung/mall/account/controller/mobile/SinglePushController.java create mode 100644 mall-account/src/main/java/com/suisung/mall/account/mapper/UserDeviceBindMapper.java create mode 100644 mall-account/src/main/java/com/suisung/mall/account/service/AccountSinglePushService.java create mode 100644 mall-account/src/main/java/com/suisung/mall/account/service/UserDeviceBindService.java create mode 100644 mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountSinglePushServiceImpl.java create mode 100644 mall-account/src/main/java/com/suisung/mall/account/service/impl/UserDeviceBindServiceImpl.java create mode 100644 mall-common/src/main/java/com/suisung/mall/common/RedisKeyManage/RedisKey.java create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/singlepush/UserDeviceBind.java create mode 100644 mall-common/src/main/java/com/suisung/mall/common/service/SinglePushBaseService.java create mode 100644 mall-common/src/main/java/com/suisung/mall/common/service/impl/SinglePushBaseServiceImpl.java create mode 100644 sql/shop/dev/20250522_ddl.sql diff --git a/client/src/main/java/com/small/client/controller/WebController.java b/client/src/main/java/com/small/client/controller/WebController.java index 737b0be9..65aa2b66 100644 --- a/client/src/main/java/com/small/client/controller/WebController.java +++ b/client/src/main/java/com/small/client/controller/WebController.java @@ -54,11 +54,11 @@ public class WebController { sxDataService.SyncVipList(new DataBaseInfo(),sxDataService.getCommentModel()); } - @RequestMapping("/getAppSign") - public void getAppSign(){ - log.info("getAppSign"); - sxDataService.getAppSign(); - } +// @RequestMapping("/getAppSign") +// public void getAppSign(){ +// log.info("getAppSign"); +// sxDataService.getAppSign(); +// } @RequestMapping("/downLoadClient") public void downLoadClient(){ diff --git a/mall-account/src/main/java/com/suisung/mall/account/RedisKeyManage/RedisKey.java b/mall-account/src/main/java/com/suisung/mall/account/RedisKeyManage/RedisKey.java new file mode 100644 index 00000000..6edf2b41 --- /dev/null +++ b/mall-account/src/main/java/com/suisung/mall/account/RedisKeyManage/RedisKey.java @@ -0,0 +1,6 @@ +package com.suisung.mall.account.RedisKeyManage; + +public class RedisKey { + + public static final String SINGLEPUSHTOKE="singlePush:toke"; +} diff --git a/mall-account/src/main/java/com/suisung/mall/account/controller/LoginController.java b/mall-account/src/main/java/com/suisung/mall/account/controller/LoginController.java index 1ead17bf..217ca92c 100644 --- a/mall-account/src/main/java/com/suisung/mall/account/controller/LoginController.java +++ b/mall-account/src/main/java/com/suisung/mall/account/controller/LoginController.java @@ -80,7 +80,6 @@ public class LoginController extends BaseControllerImpl { params.put("grant_type", "password"); params.put("username", user_account); params.put("password", user_password); - CommonResult result = accountUserBaseService.login(params); // user_account 判断是否绑定了手机号 diff --git a/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/SinglePushController.java b/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/SinglePushController.java new file mode 100644 index 00000000..bed0f6e8 --- /dev/null +++ b/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/SinglePushController.java @@ -0,0 +1,35 @@ +package com.suisung.mall.account.controller.mobile; + +import com.suisung.mall.account.service.AccountSinglePushService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/admin/account/singlePus") +@Slf4j +public class SinglePushController { + @Autowired + private AccountSinglePushService accountSinglePushService; + + @RequestMapping("/getSingleToken") + public String getToken() { + return accountSinglePushService.getTokenString(); + } + + @RequestMapping("/delSingleToken") + public String delSingleToken() { + accountSinglePushService.delToken(); + return "success"; + } + + @PostMapping("/pushTocid") + public String pushTocid(@RequestParam String message,@RequestParam String userId) { + log.info("pushTocid"); + accountSinglePushService.pushTocid(message,userId); + return "success"; + } +} diff --git a/mall-account/src/main/java/com/suisung/mall/account/mapper/UserDeviceBindMapper.java b/mall-account/src/main/java/com/suisung/mall/account/mapper/UserDeviceBindMapper.java new file mode 100644 index 00000000..1331574b --- /dev/null +++ b/mall-account/src/main/java/com/suisung/mall/account/mapper/UserDeviceBindMapper.java @@ -0,0 +1,9 @@ +package com.suisung.mall.account.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.singlepush.UserDeviceBind; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserDeviceBindMapper extends BaseMapper { +} diff --git a/mall-account/src/main/java/com/suisung/mall/account/service/AccountSinglePushService.java b/mall-account/src/main/java/com/suisung/mall/account/service/AccountSinglePushService.java new file mode 100644 index 00000000..b06a43f3 --- /dev/null +++ b/mall-account/src/main/java/com/suisung/mall/account/service/AccountSinglePushService.java @@ -0,0 +1,16 @@ +package com.suisung.mall.account.service; + +import com.getui.push.v2.sdk.dto.res.TokenDTO; +import com.suisung.mall.common.service.SinglePushBaseService; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Service; + + +public interface AccountSinglePushService extends SinglePushBaseService { + + String getTokenString(); + + void delToken(); + + void pushTocid(String message,String userId); +} diff --git a/mall-account/src/main/java/com/suisung/mall/account/service/UserDeviceBindService.java b/mall-account/src/main/java/com/suisung/mall/account/service/UserDeviceBindService.java new file mode 100644 index 00000000..80c7fff2 --- /dev/null +++ b/mall-account/src/main/java/com/suisung/mall/account/service/UserDeviceBindService.java @@ -0,0 +1,14 @@ +package com.suisung.mall.account.service; + +import com.suisung.mall.common.modules.singlepush.UserDeviceBind; +import com.suisung.mall.core.web.service.IBaseService; +import org.springframework.stereotype.Service; + +import java.util.List; + +public interface UserDeviceBindService extends IBaseService { + + void saveUserDeviceBind(UserDeviceBind userDeviceBind); + + List getActive(UserDeviceBind userDeviceBind); +} diff --git a/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountSinglePushServiceImpl.java b/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountSinglePushServiceImpl.java new file mode 100644 index 00000000..9013a090 --- /dev/null +++ b/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountSinglePushServiceImpl.java @@ -0,0 +1,62 @@ +package com.suisung.mall.account.service.impl; +import cn.hutool.core.collection.CollectionUtil; +import com.getui.push.v2.sdk.api.PushApi; +import com.getui.push.v2.sdk.common.ApiResult; +import com.getui.push.v2.sdk.dto.req.Audience; +import com.getui.push.v2.sdk.dto.req.message.PushDTO; +import com.getui.push.v2.sdk.dto.req.message.PushMessage; +import com.suisung.mall.account.service.AccountSinglePushService; +import com.suisung.mall.account.service.UserDeviceBindService; +import com.suisung.mall.common.modules.singlepush.UserDeviceBind; +import com.suisung.mall.common.service.impl.SinglePushBaseServiceImpl; +import com.suisung.mall.common.utils.ContextUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@lombok.extern.slf4j.Slf4j +public class AccountSinglePushServiceImpl extends SinglePushBaseServiceImpl implements AccountSinglePushService { + + @Autowired + private UserDeviceBindService userDeviceBindService; + + @Override + public String getTokenString() { + return super.getTokenDTO().getToken(); + } + + public void delToken(){ + super.delToken(); + } + + @Override + public void pushTocid(String message,String userId) { + UserDeviceBind userDeviceBind= new UserDeviceBind(); + userDeviceBind.setUserId(userId); + List list= userDeviceBindService.getActive(userDeviceBind); + if(CollectionUtil.isEmpty(list)){ + log.error("登录时绑定设备不存在"); + return ; + } + String cid=list.get(0).getCid(); + PushApi pushApi= this.getApiHelper().creatApi(PushApi.class); + PushDTO pushDTO=new PushDTO<>(); + Audience audience=new Audience(); + audience.addCid(cid); + pushDTO.setAudience(audience); + pushDTO.setRequestId(UUID.randomUUID().toString()); + PushMessage pushMessage=new PushMessage(); + pushMessage.setDuration(this.getDuration()); + pushMessage.setTransmission(message); + pushDTO.setPushMessage(pushMessage); + ApiResult>> mapApiResult= pushApi.pushToSingleByCid(pushDTO,super.getTokenDTO()); + if(mapApiResult.getCode()==0){ + log.info("用户为{},cid为{}发送成功消息成功",list.get(0).getUserId(),list.get(0).getCid()); + } + log.info("mapApiResult的数据{}",mapApiResult); + } + + +} diff --git a/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountUserBaseServiceImpl.java b/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountUserBaseServiceImpl.java index 7ec21c10..1e1a9a14 100644 --- a/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountUserBaseServiceImpl.java +++ b/mall-account/src/main/java/com/suisung/mall/account/service/impl/AccountUserBaseServiceImpl.java @@ -50,6 +50,7 @@ import com.suisung.mall.common.modules.distribution.ShopDistributionUser; import com.suisung.mall.common.modules.distribution.ShopDistributionUserCommission; import com.suisung.mall.common.modules.pay.PayUserResource; import com.suisung.mall.common.modules.plantform.ShopPlantformSubsiteUser; +import com.suisung.mall.common.modules.singlepush.UserDeviceBind; import com.suisung.mall.common.pojo.dto.SmsDto; import com.suisung.mall.common.pojo.req.WxUserInfoReq; import com.suisung.mall.common.service.MessageService; @@ -65,9 +66,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -136,6 +139,8 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl params) { @@ -216,10 +221,21 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl getUserInfo(Integer user_id) { QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -3034,6 +3050,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl implements UserDeviceBindService { + @Override + public void saveUserDeviceBind(UserDeviceBind userDeviceBind) { + if(StringUtils.isEmpty(userDeviceBind.getCid())){ + throw new RuntimeException("绑定cid不能为空"); + } + if(StringUtils.isEmpty(userDeviceBind.getOsType())){ + throw new RuntimeException("操作系统类型不能为空"); + } + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("user_id",userDeviceBind.getCid()); + UserDeviceBind bind=getOne(wrapper); + if(bind==null){ + UserDto userDto= ContextUtil.getCurrentUser(); + assert userDto != null; + userDeviceBind.setUserId(userDto.getUser_account()); + userDeviceBind.setUserType(userDto.getRole_id()); + userDeviceBind.setLastActive(new Date()); + userDeviceBind.setBindTime(new Date()); + this.saveOrUpdate(userDeviceBind); + log.info("保存用户设备绑定关系实体成功"); + }else { + if(!bind.getCid().equals(userDeviceBind.getCid())){ + bind.setCid(userDeviceBind.getCid()); + } + if(!bind.getOsType().equals(userDeviceBind.getOsType())){ + bind.setCid(userDeviceBind.getOsType()); + } + bind.setLastActive(new Date()); + bind.setBindTime(new Date()); + this.saveOrUpdate(bind); + } + } + + @Override + public List getActive(UserDeviceBind userDeviceBind) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("user_id",userDeviceBind.getUserId()).orderByDesc("bind_time"); + return list(wrapper); + } +} diff --git a/mall-auth/src/main/java/com/suisung/mall/auth/controller/AuthController.java b/mall-auth/src/main/java/com/suisung/mall/auth/controller/AuthController.java index 4bcabf99..85f99c34 100644 --- a/mall-auth/src/main/java/com/suisung/mall/auth/controller/AuthController.java +++ b/mall-auth/src/main/java/com/suisung/mall/auth/controller/AuthController.java @@ -54,7 +54,9 @@ public class AuthController { .token(oAuth2AccessToken.getValue()) .refreshToken(oAuth2AccessToken.getRefreshToken().getValue()) .expiresIn(oAuth2AccessToken.getExpiresIn()) - .tokenHead(AuthConstant.JWT_TOKEN_PREFIX).build(); + .tokenHead(AuthConstant.JWT_TOKEN_PREFIX) + .cid(parameters.get("cid")).build(); + } catch (Exception e) { logger.error(I18nUtil._("Oauth2获取token失败!") + e.getMessage(), e); return CommonResult.failed(e.getMessage()); diff --git a/mall-auth/src/main/java/com/suisung/mall/auth/domain/Oauth2TokenDto.java b/mall-auth/src/main/java/com/suisung/mall/auth/domain/Oauth2TokenDto.java index 74a68e39..19116adb 100644 --- a/mall-auth/src/main/java/com/suisung/mall/auth/domain/Oauth2TokenDto.java +++ b/mall-auth/src/main/java/com/suisung/mall/auth/domain/Oauth2TokenDto.java @@ -20,4 +20,8 @@ public class Oauth2TokenDto { private String tokenHead; @ApiModelProperty("有效时间(秒)") private int expiresIn; + + //个推使用 + @ApiModelProperty("cid") + private String cid; } diff --git a/mall-common/pom.xml b/mall-common/pom.xml index 3af6b2f1..c73d8eec 100644 --- a/mall-common/pom.xml +++ b/mall-common/pom.xml @@ -279,6 +279,15 @@ jieba-analysis 1.0.2 + + + + com.getui.push + restful-sdk + 1.0.6.1 + ${project.basedir}/src/main/resources/lib/restful-sdk-1.0.6.1.jar + system + diff --git a/mall-common/src/main/java/com/suisung/mall/common/RedisKeyManage/RedisKey.java b/mall-common/src/main/java/com/suisung/mall/common/RedisKeyManage/RedisKey.java new file mode 100644 index 00000000..5dc5902d --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/RedisKeyManage/RedisKey.java @@ -0,0 +1,6 @@ +package com.suisung.mall.common.RedisKeyManage; + +public class RedisKey { + + public static final String SINGLEPUSHTOKE="singlePush:toke"; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/feignService/AccountService.java b/mall-common/src/main/java/com/suisung/mall/common/feignService/AccountService.java index 379cfc0b..3811ba5f 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/feignService/AccountService.java +++ b/mall-common/src/main/java/com/suisung/mall/common/feignService/AccountService.java @@ -215,4 +215,7 @@ public interface AccountService { Integer getUserBindConnectUserIdByCondition(@RequestParam(name = "bind_id") String bind_id, @RequestParam(name = "bind_type") Integer bind_type, @RequestParam(name = "user_type", required = false) Integer user_type); + + @PostMapping("/admin/account/pushTocid") + String pushTocid(@RequestParam(name = "message") String message,@RequestParam(name = "userId") String userId); } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/singlepush/UserDeviceBind.java b/mall-common/src/main/java/com/suisung/mall/common/modules/singlepush/UserDeviceBind.java new file mode 100644 index 00000000..cc5ed36d --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/singlepush/UserDeviceBind.java @@ -0,0 +1,59 @@ +package com.suisung.mall.common.modules.singlepush; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +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.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("user_device_bind") // MyBatis-Plus 表名注解 +@ApiModel("用户设备绑定实体") // Swagger 模型描述 +public class UserDeviceBind implements Serializable { + + @TableId(value = "id", type = IdType.AUTO) // 主键自增 + @ApiModelProperty(value = "主键ID", hidden = true) // 隐藏此字段不展示在Swagger文档 + private Long id; + + @TableField("user_id") // 明确字段映射(可省略,默认自动转下划线) + @ApiModelProperty(value = "用户唯一标识", required = true, example = "U20231001") + private String userId; + + @TableField("cid") + @ApiModelProperty(value = "设备CID", required = true, example = "CID_ABCD1234") + private String cid; + + @TableField("user_type") + @ApiModelProperty( + value = "用户类型 (1=普通用户, 2=管理员, 3=商家)", + required = true, + example = "1", + notes = "1-普通用户, 2-管理员, 3-商家" + ) + private Integer userType; + + @TableField("os_type") + @ApiModelProperty( + value = "手机系统类型", + example = "Android", + notes = "Android, iOS" + ) + private String osType; + + @TableField("bind_time") + @ApiModelProperty(value = "绑定时间", example = "2023-10-01 12:00:00") + private Date bindTime; + + @TableField("last_active") + @ApiModelProperty(value = "最后活跃时间", example = "2023-10-05 14:30:00") + private Date lastActive; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/service/SinglePushBaseService.java b/mall-common/src/main/java/com/suisung/mall/common/service/SinglePushBaseService.java new file mode 100644 index 00000000..63e6e798 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/service/SinglePushBaseService.java @@ -0,0 +1,12 @@ +package com.suisung.mall.common.service; + +import com.getui.push.v2.sdk.dto.res.TokenDTO; +import com.suisung.mall.common.pojo.dto.SmsDto; + +/** + * 第三方服务 + */ +public interface SinglePushBaseService { + TokenDTO getTokenDTO(); + void delToken(); +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/service/impl/SinglePushBaseServiceImpl.java b/mall-common/src/main/java/com/suisung/mall/common/service/impl/SinglePushBaseServiceImpl.java new file mode 100644 index 00000000..3bf539e4 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/service/impl/SinglePushBaseServiceImpl.java @@ -0,0 +1,93 @@ +package com.suisung.mall.common.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.map.MapUtil; +import com.getui.push.v2.sdk.ApiHelper; +import com.getui.push.v2.sdk.GtApiConfiguration; +import com.getui.push.v2.sdk.api.AuthApi; +import com.getui.push.v2.sdk.common.ApiResult; +import com.getui.push.v2.sdk.dto.req.AuthDTO; +import com.getui.push.v2.sdk.dto.res.TokenDTO; +import com.suisung.mall.common.RedisKeyManage.RedisKey; +import com.suisung.mall.common.service.SinglePushBaseService; +import com.suisung.mall.core.web.service.RedisService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +@Service +@lombok.extern.slf4j.Slf4j +public class SinglePushBaseServiceImpl implements SinglePushBaseService { + @Value("${single.push.mastersecret}") + private String mastersecret; + + @Value("${single.push.appkey}") + private String appkey; + @Value("${single.push.appId}") + private String appId; + @Autowired + private RedisService redisService; + + @Override + public TokenDTO getTokenDTO() { + TokenDTO token=new TokenDTO(); + if(redisService.get(RedisKey.SINGLEPUSHTOKE)==null){ + return getTokenFromSingelPush(); + }else { + Map map= (Map) redisService.get(RedisKey.SINGLEPUSHTOKE); + token.setToken(map.get("token").toString()); + token.setExpireTime((Long) map.get("expireTime")); + } + return token; + } + + @Override + public void delToken(){ + if(redisService.get(RedisKey.SINGLEPUSHTOKE)!=null){ + Map map= (Map) redisService.get(RedisKey.SINGLEPUSHTOKE); + AuthApi authApi = getApiHelper().creatApi(AuthApi.class); + String token = (String) map.get("token"); + TokenDTO tokenDTO = new TokenDTO(); + tokenDTO.setToken(token); + tokenDTO.setExpireTime((Long) map.get("expireTime")); + log.info("token--{}",tokenDTO); + authApi.close(token,tokenDTO); + redisService.del(RedisKey.SINGLEPUSHTOKE); + } + } + + public ApiHelper getApiHelper(){ + GtApiConfiguration config = new GtApiConfiguration(); + config.setAppKey(appkey); + config.setMasterSecret(mastersecret); + config.setAppId(appId); + return ApiHelper.build(config); + } + + public String getDuration(){ + long timestamp = System.currentTimeMillis(); // 当前时间的时间戳 + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(timestamp); + calendar.add(Calendar.MINUTE, 10); // 增加10分钟 + long newTimestamp = calendar.getTimeInMillis(); // 获取新的时间戳 + return timestamp+"-"+newTimestamp; + } + + private TokenDTO getTokenFromSingelPush(){ + AuthApi authApi= getApiHelper().creatApi(AuthApi.class); + AuthDTO authDTO= AuthDTO.build(appkey, mastersecret); + ApiResult tokenDTOApiResult= authApi.auth(authDTO); + TokenDTO token=null; + if (tokenDTOApiResult!= null) { + token = tokenDTOApiResult.getData(); + Map map= BeanUtil.beanToMap(token); + log.info("tokenDTO:{}",token); + redisService.set(RedisKey.SINGLEPUSHTOKE, map,(token.getExpireTime()/1000)-60); + } + return token; + } +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java index b5214cff..cda710ba 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java @@ -39,13 +39,14 @@ public class ContextUtil { */ public static UserDto getCurrentUser() { try { - UserDto loginUser = staticUserInfoService.getUser(); - log.info("##### 当前登录用户:{}###", JsonUtil.object2json(loginUser)); - return loginUser;//todo 测试去除 -// UserDto user= new UserDto(); -// user.setStore_id("1"); -// user.setRole_id(9); -// return user; +// UserDto loginUser = staticUserInfoService.getUser(); +// log.info("##### 当前登录用户:{}###", JsonUtil.object2json(loginUser)); +// return loginUser;//todo 测试去除 + UserDto user= new UserDto(); + user.setStore_id("1"); + user.setRole_id(9); + user.setUser_account("18260885688"); + return user; } catch (Exception e) { System.out.println(e.getMessage()); } diff --git a/mall-common/src/main/resources/application-local.yml b/mall-common/src/main/resources/application-local.yml index 133826be..53d510c5 100644 --- a/mall-common/src/main/resources/application-local.yml +++ b/mall-common/src/main/resources/application-local.yml @@ -32,4 +32,9 @@ baidu: map: app_id: 116444176 ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v - url: https://api.map.baidu.com/geoconv/v2/? \ No newline at end of file + url: https://api.map.baidu.com/geoconv/v2/? +single: # 个推配置 + push: + mastersecret: jYnpS1xYhh6GfyZQMlciJ + appkey: neXXX9r1Tc7gMxN2PIcHA1 + appId: KXgzOaKSzd5HG3p9IPaVa8 \ No newline at end of file diff --git a/mall-common/src/main/resources/application-prod.yml b/mall-common/src/main/resources/application-prod.yml index 7104124c..1e5279a1 100644 --- a/mall-common/src/main/resources/application-prod.yml +++ b/mall-common/src/main/resources/application-prod.yml @@ -33,3 +33,8 @@ baidu: app_id: 116444176 ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v url: https://api.map.baidu.com/geoconv/v2/? +single: # 个推配置 + push: + mastersecret: jYnpS1xYhh6GfyZQMlciJ + appkey: neXXX9r1Tc7gMxN2PIcHA1 + appId: KXgzOaKSzd5HG3p9IPaVa8 \ No newline at end of file diff --git a/mall-common/src/main/resources/application-test.yml b/mall-common/src/main/resources/application-test.yml index fd6dca23..4e42a7dc 100644 --- a/mall-common/src/main/resources/application-test.yml +++ b/mall-common/src/main/resources/application-test.yml @@ -32,4 +32,9 @@ baidu: map: app_id: 116444176 ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v - url: https://api.map.baidu.com/geoconv/v2/? \ No newline at end of file + url: https://api.map.baidu.com/geoconv/v2/? +single: # 个推配置 + push: + mastersecret: jYnpS1xYhh6GfyZQMlciJ + appkey: neXXX9r1Tc7gMxN2PIcHA1 + appId: KXgzOaKSzd5HG3p9IPaVa8 \ No newline at end of file diff --git a/mall-common/src/main/resources/application-uat.yml b/mall-common/src/main/resources/application-uat.yml index 770dd4db..4e42a7dc 100644 --- a/mall-common/src/main/resources/application-uat.yml +++ b/mall-common/src/main/resources/application-uat.yml @@ -33,3 +33,8 @@ baidu: app_id: 116444176 ak: qWKt2xbrqXsp2yK35YYXVBNZgrbiCG5v url: https://api.map.baidu.com/geoconv/v2/? +single: # 个推配置 + push: + mastersecret: jYnpS1xYhh6GfyZQMlciJ + appkey: neXXX9r1Tc7gMxN2PIcHA1 + appId: KXgzOaKSzd5HG3p9IPaVa8 \ No newline at end of file diff --git a/mall-shop/pom.xml b/mall-shop/pom.xml index 49369c3e..7edcf4b9 100644 --- a/mall-shop/pom.xml +++ b/mall-shop/pom.xml @@ -301,7 +301,6 @@ 9.2.1.jre8 - diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java index f690ad67..cefeb867 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java @@ -31,8 +31,8 @@ import java.util.Map; @RestController @RequestMapping("/shop/sync/third") public class SyncThirdDataController { - @Resource - private SyncAppService syncAppService; +// @Resource +// private SyncAppService syncAppService; @Resource private SyncThirdDataService syncThirdDataService; @@ -125,11 +125,11 @@ public class SyncThirdDataController { return readSxData(folders,appKey,sign,syncType); } - @ApiOperation(value = "获取加密密钥", notes = "获取加密密钥") - @RequestMapping(value = "/getAppSign", method = RequestMethod.POST) - public Map getAppSign(@RequestParam String primaryKey) { - return syncAppService.getAppSign(primaryKey); - } +// @ApiOperation(value = "获取加密密钥", notes = "获取加密密钥") +// @RequestMapping(value = "/getAppSign", method = RequestMethod.POST) +// public Map getAppSign(@RequestParam String primaryKey) { +// return syncAppService.getAppSign(primaryKey); +// } /** * 客户端查询配置 diff --git a/sql/shop/dev/20250522_ddl.sql b/sql/shop/dev/20250522_ddl.sql new file mode 100644 index 00000000..2c157fb4 --- /dev/null +++ b/sql/shop/dev/20250522_ddl.sql @@ -0,0 +1,13 @@ +CREATE TABLE `user_device_bind` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` varchar(32) NOT NULL COMMENT '用户唯一标识', + `cid` varchar(64) NOT NULL COMMENT '设备CID', + `user_type` tinyint(4) NOT NULL COMMENT '用户类型 (1=普通用户, 2=管理员, 3=商家)', + `os_type` varchar(16) NOT NULL COMMENT '手机系统类型 (Android/iOS)', + `bind_time` datetime NOT NULL COMMENT '绑定时间', + `last_active` datetime NOT NULL COMMENT '最后活跃时间', + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_cid` (`cid`), + KEY `idx_last_active` (`last_active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户设备绑定关系表'; \ No newline at end of file From b753dedf71719db7d6462d1d4c72e006786235c1 Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Mon, 26 May 2025 01:09:59 +0800 Subject: [PATCH 11/11] =?UTF-8?q?=E5=88=86=E8=B4=A6=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/PayUserPayServiceImpl.java | 77 ++-- .../service/impl/LakalaApiServiceImpl.java | 329 ++++++++++++++---- .../impl/LklOrderSeparateServiceImpl.java | 2 +- .../mall/shop/lakala/utils/LakalaUtil.java | 23 ++ .../impl/ShopOrderBaseServiceImpl.java | 4 +- .../service/impl/ShopOrderLklServiceImpl.java | 2 +- 6 files changed, 316 insertions(+), 121 deletions(-) diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java index e1836f0d..64bdd1f7 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayUserPayServiceImpl.java @@ -1268,73 +1268,70 @@ public class PayUserPayServiceImpl extends BaseServiceImpl authMap = LakalaUtil.getLakalaAuthorizationMap(authorization); - if (authMap != null && authMap.get("signature") != null) { + if (authMap != null && authMap.containsKey("signature")) { params.put("sign", authMap.get("signature")); + } else { + logger.error("缺少签名信息"); + return lklNotifyMsg(false, "缺少签名信息"); } - // 基于安全考虑,检测支付模式及数据 - // 判断是门店 店铺 平台 - Integer payment_store_id = 0; - Integer payment_chain_id = 0; - String orderSubject = ""; - Integer userId = 0; - + // 查询交易信息 QueryWrapper tradeQueryWrapper = new QueryWrapper<>(); tradeQueryWrapper.eq("order_id", order_id); PayConsumeTrade trade_row_tmp = payConsumeTradeService.findOne(tradeQueryWrapper); - if (trade_row_tmp != null) { - payment_store_id = trade_row_tmp.getStore_id(); - orderSubject = trade_row_tmp.getTrade_title(); - userId = trade_row_tmp.getBuyer_id(); - } + Integer payment_store_id = trade_row_tmp != null ? trade_row_tmp.getStore_id() : 0; + String orderSubject = trade_row_tmp != null ? trade_row_tmp.getTrade_title() : ""; + Integer userId = trade_row_tmp != null ? trade_row_tmp.getBuyer_id() : 0; + + // 查询支付渠道 QueryWrapper channelQueryWrapper = new QueryWrapper<>(); channelQueryWrapper.eq("payment_channel_code", "lakala"); PayPaymentChannel payPaymentChannel = payPaymentChannelService.findOne(channelQueryWrapper); + + if (payPaymentChannel == null) { + logger.error("支付渠道不存在"); + return lklNotifyMsg(false, "支付渠道不存在"); + } Integer payment_channel_id = payPaymentChannel.getPayment_channel_id(); // 插入充值记录 - PayConsumeDeposit notify_row = createNotify(params, payPaymentChannel); - notify_row.setOrder_id(order_id); - notify_row.setStore_id(payment_store_id); // 所属店铺 - notify_row.setChain_id(payment_chain_id); // 所属门店 - notify_row.setPayment_channel_id(payment_channel_id); - notify_row.setDeposit_subject(orderSubject); - notify_row.setDeposit_body(orderSubject); - notify_row.setUser_id(userId); - // notify_row.setDeposit_state(1);// 支付状态:0-默认; 1-接收正确数据处理完逻辑; 9-异常订单 - // notify_row.setDeposit_async(0); // 是否同步:0-同步; 1-异步回调使用 - - - BigDecimal zero = BigDecimal.ZERO; + PayConsumeDeposit payConsumeDeposit = createNotify(params, payPaymentChannel); + payConsumeDeposit.setOrder_id(order_id); + payConsumeDeposit.setStore_id(payment_store_id); // 所属店铺 + payConsumeDeposit.setChain_id(0); // 所属门店默认为0 + payConsumeDeposit.setPayment_channel_id(payment_channel_id); + payConsumeDeposit.setDeposit_subject(orderSubject); + payConsumeDeposit.setDeposit_body(orderSubject); + payConsumeDeposit.setUser_id(userId); // 判断是否联合支付 PayConsumeTradeCombine tradeCombine = payConsumeTradeCombineService.get(order_id); TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); try { if (tradeCombine != null && StrUtil.isNotBlank(tradeCombine.getOrder_ids())) { - notify_row.setOrder_id(tradeCombine.getOrder_ids()); + payConsumeDeposit.setOrder_id(tradeCombine.getOrder_ids()); } - if (!payConsumeDepositService.processDeposit(notify_row, zero, zero, zero, zero, zero)) { + if (!payConsumeDepositService.processDeposit(payConsumeDeposit, BigDecimal.ZERO, BigDecimal.ZERO, + BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO)) { log.error("支付失败!"); return lklNotifyMsg(false, "支付失败!"); } @@ -1342,13 +1339,13 @@ public class PayUserPayServiceImpl extends BaseServiceImpl checkResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath); + if (!checkResult.getFirst()) { + return JSONUtil.createObj().set("code", "FAIL").set("retMsg", checkResult.getSecond()); + } + JSONObject paramsJSON = JSONUtil.parseObj(checkResult.getSecond()); String errMsg = "入网电子合同申请回调:"; - boolean checkSuccess = LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath); - if (!checkSuccess) { - log.error(errMsg + "验签失败"); - return JSONUtil.createObj().set("code", "FAIL").set("retMsg", "验签失败!"); - } - - JSONObject paramsJSON = JSONUtil.parseObj(requestBody); JSONObject respData = new JSONObject(); respData.put("code", "FAIL"); @@ -1143,17 +1137,12 @@ public class LakalaApiServiceImpl implements LakalaApiService { log.debug("商户分账申请业务异步回调通知开始"); // 验签 - String authorization = request.getHeader("Authorization"); - String requestBody = LakalaUtil.getBody(request); - log.debug("商户分账申请业务异步回调返回requestbody 参数:{}\n authorization参数:{}\n", requestBody, authorization); - - - if (!LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath)) { - log.error("商户分账申请业务回调:验签失败"); - return JSONUtil.createObj().put("code", "FAIL").put("message", "验签失败!"); + Pair checkResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath); + if (!checkResult.getFirst()) { + return JSONUtil.createObj().set("code", "FAIL").set("retMsg", checkResult.getSecond()); } + JSONObject paramsJSON = JSONUtil.parseObj(checkResult.getSecond()); - JSONObject paramsJSON = JSONUtil.parseObj(requestBody); if (paramsJSON == null) { log.error("商户分账申请业务回调:请求参数为空"); return JSONUtil.createObj().put("code", "FAIL").put("message", "请求参数为空"); @@ -1767,17 +1756,13 @@ public class LakalaApiServiceImpl implements LakalaApiService { log.debug("分账商家绑定接收方异步回调通知开始"); // 1. 验签处理(提前失败返回) - String authorization = request.getHeader("Authorization"); - String requestBody = LakalaUtil.getBody(request); - log.debug("回调参数:requestBody={}\n authorization={}", requestBody, authorization); - - if (!LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath)) { - log.error("验签失败,拒绝处理回调"); - return JSONUtil.createObj().put("code", "FAIL").put("message", "验签失败"); + // 验签 + Pair checkResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath); + if (!checkResult.getFirst()) { + return JSONUtil.createObj().set("code", "FAIL").set("retMsg", checkResult.getSecond()); } + JSONObject paramsJSON = JSONUtil.parseObj(checkResult.getSecond()); - // 2. 参数解析与校验 - JSONObject paramsJSON = JSONUtil.parseObj(requestBody); if (paramsJSON == null || !paramsJSON.containsKey("respData")) { log.error("回调参数缺失respData字段"); return JSONUtil.createObj().put("code", "FAIL").put("message", "参数格式错误"); @@ -1960,6 +1945,179 @@ public class LakalaApiServiceImpl implements LakalaApiService { */ @Override public Pair innerDoOrderSeparate(String orderId) { + // 输入参数校验 + if (StrUtil.isBlank(orderId)) { + return Pair.of(false, "订单号不能为空"); + } + + try { + // 初始化拉卡拉SDK + initLKLSDK(); + + // 查询订单信息 + List shopOrders = shopOrderLklService.selectByOrderId(orderId, ""); + if (CollectionUtil.isEmpty(shopOrders)) { + return Pair.of(false, "订单不存在"); + } + + int totalCount = shopOrders.size(); + int successCount = 0; + StringBuilder errorMessages = new StringBuilder(); + + // 遍历处理每个店铺订单的分账 + for (ShopOrderLkl order : shopOrders) { + String merchantNo = order.getMerchant_no(); + Integer paymentAmount = order.getTotal_amt(); + Integer shoppingFee = order.getShopping_fee(); + BigDecimal splitRatioMch = order.getSplit_ratio(); + + // 金额合法性校验 + if (paymentAmount <= 0 || (shoppingFee != null && paymentAmount <= shoppingFee)) { + String errorMsg = String.format("店铺[%s]订单金额异常或运费过高,跳过分账", order.getStore_id()); + log.error(errorMsg); + errorMessages.append(errorMsg).append("; "); + continue; + } + + // 获取分账接收方信息 + LklLedgerMerReceiverBind platform = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo); + List distributors = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo); + + if (platform == null) { + String errorMsg = String.format("店铺[%s]未绑定平台方接收账户,跳过分账", order.getStore_id()); + log.error(errorMsg); + errorMessages.append(errorMsg).append("; "); + continue; + } + + // 判断是否可以分账(商家比例非100%) + boolean canSplit = splitRatioMch != null && splitRatioMch.compareTo(new BigDecimal("100")) < 0; + if (!canSplit) { + log.warn("店铺[{}]分账比例为0,仅扣除运费", order.getStore_id()); + } + + // 构建分账请求对象 + V3SacsSeparateRequest request = new V3SacsSeparateRequest(); + request.setMerchantNo(merchantNo); + request.setLogNo(order.getLog_no()); + request.setLogDate(order.getLog_date()); + request.setOutSeparateNo(orderId); + request.setTotalAmt(paymentAmount.toString()); + request.setLklOrgNo(orgCode); + request.setCalType("0"); + request.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify"); + + // 构建分账接收方列表 + List recvDatas = new ArrayList<>(); + + // 1. 先处理运费分账(如果有运费) + if (shoppingFee != null && shoppingFee > 0) { + V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); + receiver.setRecvNo(platform.getReceiver_no()); + receiver.setSeparateValue(shoppingFee.toString()); + recvDatas.add(receiver); + } + + // 2. 再处理剩余金额的分账(如果可以分账) + if (canSplit) { + // 计算实际可分账金额(扣除运费后) + Integer splitAmount = paymentAmount - shoppingFee; + // 计算平台+代理商的总比例 + BigDecimal splitRatioNoMch = new BigDecimal("100").subtract(splitRatioMch); + + if (!CollectionUtils.isEmpty(distributors) && splitRatioNoMch.compareTo(BigDecimal.ONE) > 0) { + // 平台+代理商分账模式 + // 平台收取1%手续费 + BigDecimal platformValue = CommonUtil.DecimalRoundHalfDown(new BigDecimal(splitAmount).multiply(new BigDecimal("0.01"))); + if (platformValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); + receiver.setRecvNo(platform.getReceiver_no()); + receiver.setSeparateValue(platformValue.toString()); + recvDatas.add(receiver); + } + + // 代理商分账(扣除平台1%后的剩余比例) + BigDecimal distributorRatio = splitRatioNoMch.subtract(new BigDecimal("1")).divide(new BigDecimal(100)); + BigDecimal distributorValue = CommonUtil.DecimalRoundHalfDown(new BigDecimal(splitAmount).multiply(distributorRatio)); + if (distributorValue.compareTo(BigDecimal.ZERO) > 0 && !distributors.isEmpty()) { + V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); + receiver.setRecvNo(distributors.get(0).getReceiver_no()); + receiver.setSeparateValue(distributorValue.toString()); + recvDatas.add(receiver); + } + } else { + // 仅平台分账模式 + BigDecimal platformRatio = splitRatioNoMch.divide(new BigDecimal(100)); + BigDecimal platformValue = new BigDecimal(splitAmount).multiply(platformRatio); + if (platformValue.compareTo(BigDecimal.ZERO) > 0) { + V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); + receiver.setRecvNo(platform.getReceiver_no()); + receiver.setSeparateValue(platformValue.toString()); + recvDatas.add(receiver); + } + } + } + + // 设置分账接收方列表 + request.setRecvDatas(recvDatas); + log.debug("分账请求参数: {}", JSONUtil.toJsonStr(request)); + + // 发送分账请求 + String response = LKLSDK.httpPost(request); + if (StrUtil.isBlank(response)) { + errorMessages.append("拉卡拉无响应; "); + continue; + } + + log.debug("分账响应结果: {}", response); + + // 解析响应结果 + JSONObject respJson = JSONUtil.parseObj(response); + if (respJson == null || !lklSacsSuccessCode.equals(respJson.getStr("code")) || respJson.getJSONObject("resp_data") == null) { + errorMessages.append("拉卡拉返回格式异常; "); + log.error("拉卡拉分账失败:{}", response); + continue; + } + + // 保存分账记录 + JSONObject respData = respJson.getJSONObject("resp_data"); + LklOrderSeparate record = new LklOrderSeparate(); + record.setSeparate_no(respData.getStr("separate_no")); + record.setOut_separate_no(request.getOutSeparateNo()); + record.setMerchant_no(merchantNo); + record.setLog_no(request.getLogNo()); + record.setLog_date(request.getLogDate()); + record.setOrder_id(order.getOrder_id()); + record.setTotal_amt(request.getTotalAmt()); + record.setNotify_url(request.getNotifyUrl()); + record.setLkl_org_no(request.getLklOrgNo()); + record.setRecv_datas(JSONUtil.toJsonStr(request.getRecvDatas())); + record.setStatus(respData.getStr("status")); + + try { + lklOrderSeparateService.addOrUpdateByReceiverNo(record); + successCount++; + } catch (Exception e) { + log.error("保存分账记录失败: {}", e.getMessage(), e); + errorMessages.append("保存分账记录失败; "); + } + } + + // 返回最终处理结果 + if (successCount == 0) { + return Pair.of(false, "分账全部失败: " + errorMessages); + } else if (successCount < totalCount) { + return Pair.of(true, "部分分账成功,处理中: " + errorMessages); + } else { + return Pair.of(true, "全部订单分账已提交处理"); + } + } catch (Exception e) { + log.error("分账系统异常: {}", e.getMessage(), e); + return Pair.of(false, "系统异常,请稍后重试"); + } + } + + public Pair innerDoOrderSeparateTemp(String orderId) { if (StrUtil.isBlank(orderId)) { return Pair.of(false, "订单号不能为空"); } @@ -1975,7 +2133,6 @@ public class LakalaApiServiceImpl implements LakalaApiService { return Pair.of(false, "订单不存在"); } - int successCnt = 0; // 一个订单里包含了多个商家的时候,需要处理 for (ShopOrderLkl shopOrderLkl : shopOrderLklList) { @@ -2011,7 +2168,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { req.setMerchantNo(merchantNo); // 拉卡拉商户号 req.setLogNo(shopOrderLkl.getLog_no()); req.setLogDate(shopOrderLkl.getLog_date()); - req.setOutSeparateNo(StringUtils.genLklOrderNo(18)); // 14+18=32位商户分账指令流水号,每个商户号下唯一,否则会校验失败 + req.setOutSeparateNo(orderId); // 暂时传入平台订单Id req.setTotalAmt(shopOrderLkl.getTotal_amt().toString()); req.setLklOrgNo(orgCode); req.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0 @@ -2091,18 +2248,17 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 4. 处理返回结果,等待异步通知 LklOrderSeparate record = new LklOrderSeparate(); + record.setSeparate_no(respData.getStr("separate_no")); // 必填参数 + record.setOut_separate_no(req.getOutSeparateNo());// 必填参数 record.setMerchant_no(merchantNo); record.setLog_no(shopOrderLkl.getLog_no()); record.setLog_date(shopOrderLkl.getLog_date()); record.setOrder_id(shopOrderLkl.getOrder_id()); record.setTotal_amt(shopOrderLkl.getTotal_amt().toString()); - record.setOut_separate_no(req.getOutSeparateNo());// 必填参数 record.setNotify_url(req.getNotifyUrl()); record.setLkl_org_no(req.getLklOrgNo()); record.setRecv_datas(JSONUtil.toJsonStr(req.getRecvDatas())); - record.setSeparate_no(respData.getStr("separate_no")); // 必填参数 record.setStatus(respData.getStr("status")); - lklOrderSeparateService.addOrUpdateByReceiverNo(record); successCnt++; @@ -2130,61 +2286,80 @@ public class LakalaApiServiceImpl implements LakalaApiService { */ @Override public JSONObject sacsSeparateNotify(HttpServletRequest request) { - log.debug("分账结果通知异步回调通知开始"); + log.debug("分账结果通知异步回调开始"); - // 验签 - String authorization = request.getHeader("Authorization"); - String requestBody = LakalaUtil.getBody(request); - log.debug("分账结果通知异步回调返回requestbody 参数:{}\n authorization参数:{}\n", requestBody, authorization); - - - if (!LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath)) { - log.error("分账结果通知异步回调:验签失败"); - return JSONUtil.createObj().put("code", "FAIL").put("message", "验签失败!"); + // 1. 验签处理 + Pair signCheckResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath); + if (!signCheckResult.getFirst()) { + log.warn("分账通知验签失败: {}", signCheckResult.getSecond()); + return JSONUtil.createObj() + .set("code", "FAIL") + .set("retMsg", signCheckResult.getSecond()); } - JSONObject paramsJSON = JSONUtil.parseObj(requestBody); - if (paramsJSON == null) { - log.error("分账结果通知异步回调:请求参数为空"); - return JSONUtil.createObj().put("code", "FAIL").put("message", "请求参数为空"); + // 2. 解析请求参数 + JSONObject paramsJson = JSONUtil.parseObj(signCheckResult.getSecond()); + if (paramsJson == null) { + log.error("分账通知参数解析失败: 请求参数为空"); + return JSONUtil.createObj() + .put("code", "FAIL") + .put("message", "请求参数为空"); } - String logNo = paramsJSON.getStr("log_no"); - String separateNo = paramsJSON.getStr("separate_no"); - String outSeparateNo = paramsJSON.getStr("out_separate_no"); - String status = paramsJSON.getStr("status"); - String finalStatus = paramsJSON.getStr("final_status"); + // 3. 提取关键参数并校验 + String logNo = paramsJson.getStr("log_no"); + String separateNo = paramsJson.getStr("separate_no"); + String outSeparateNo = paramsJson.getStr("out_separate_no"); + String status = paramsJson.getStr("status"); + String finalStatus = paramsJson.getStr("final_status"); - if (StrUtil.isBlank(outSeparateNo) || StrUtil.isBlank(separateNo) || StrUtil.isBlank(status)) { - String errMsg = "分账结果通知异步回调:缺少必要参数(outSeparateNo/separateNo/status)"; - log.error(errMsg + ":logNo={}, separateNo={}, status={}", outSeparateNo, separateNo, status); - return JSONUtil.createObj().put("code", "FAIL").put("message", "返回缺少必要参数"); + List missingParams = new ArrayList<>(); + if (StrUtil.isBlank(outSeparateNo)) missingParams.add("outSeparateNo"); + if (StrUtil.isBlank(separateNo)) missingParams.add("separateNo"); + if (StrUtil.isBlank(status)) missingParams.add("status"); + + if (!missingParams.isEmpty()) { + String errorMsg = "分账通知缺少必要参数: " + String.join(", ", missingParams); + log.error(errorMsg + ", 参数详情: {}", paramsJson); + return JSONUtil.createObj() + .put("code", "FAIL") + .put("message", errorMsg); } + // 4. 构建分账记录对象 + LklOrderSeparate record = new LklOrderSeparate(); + record.setLog_no(logNo); + record.setSeparate_no(separateNo); + record.setOut_separate_no(outSeparateNo); + record.setStatus(status); + record.setFinal_status(finalStatus); + record.setCal_type(paramsJson.getStr("cal_type")); + record.setSeparate_type(paramsJson.getStr("separate_type")); + record.setSeparate_date(paramsJson.getStr("separate_date")); + + // 处理detail_datas(避免空指针) + JSONArray detailDatas = paramsJson.getJSONArray("detail_datas"); + record.setDetail_datas(detailDatas != null ? detailDatas.toString() : "[]"); + + // 5. 持久化处理 try { - - // 4. 处理返回结果,等待异步通知 - LklOrderSeparate record = new LklOrderSeparate(); - record.setLog_no(logNo); - record.setSeparate_no(separateNo); - record.setOut_separate_no(paramsJSON.getStr("out_separate_no")); - record.setStatus(status); - record.setFinal_status(finalStatus); - record.setCal_type(paramsJSON.getStr("cal_type")); - record.setSeparate_type(paramsJSON.getStr("separate_type")); - record.setSeparate_date(paramsJSON.getStr("separate_date")); - record.setDetail_datas(paramsJSON.getJSONArray("detail_datas").toString()); - - Boolean success = lklOrderSeparateService.addOrUpdateByReceiverNo(record); - if (!success) { - return JSONUtil.createObj().put("code", "FAIL").put("message", "处理数据失败!"); + boolean updateSuccess = lklOrderSeparateService.addOrUpdateByReceiverNo(record); + if (!updateSuccess) { + log.error("分账记录更新失败, separateNo={}", separateNo); + return JSONUtil.createObj() + .put("code", "FAIL") + .put("message", "数据更新失败"); } - log.debug("分账结果通知异步回调:处理成功,separateNo={}", separateNo); - return JSONUtil.createObj().put("code", "SUCCESS").put("message", "操作成功!"); + log.debug("分账通知处理成功, separateNo={}, status={}", separateNo, status); + return JSONUtil.createObj() + .put("code", "SUCCESS") + .put("message", "操作成功"); } catch (Exception e) { - log.error("分账结果通知异步回调:处理数据异常", e); - return JSONUtil.createObj().put("code", "FAIL").put("message", "处理数据异常"); + log.error("分账通知数据处理异常, separateNo={}: {}", separateNo, e.getMessage(), e); + return JSONUtil.createObj() + .put("code", "FAIL") + .put("message", "系统处理异常"); } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java index 6389e544..d285873e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderSeparateServiceImpl.java @@ -22,7 +22,7 @@ import org.springframework.stereotype.Service; public class LklOrderSeparateServiceImpl extends BaseServiceImpl implements LklOrderSeparateService { /** - * 新增或更新记录 + * 新增或更新记录(根据out_separate_no 和 separate_no 更新记录) * * @param record * @return diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/utils/LakalaUtil.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/utils/LakalaUtil.java index 1f9e943d..7956777f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/utils/LakalaUtil.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/utils/LakalaUtil.java @@ -18,6 +18,7 @@ import com.suisung.mall.common.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.springframework.core.io.ClassPathResource; +import org.springframework.data.util.Pair; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -403,4 +404,26 @@ public class LakalaUtil { return null; } + + /** + * 拉卡拉异步回调通知,签名验证公共方法 + * + * @param request + * @param lklNotifyCerPath + * @return 验签成功,返回解密后的json字符串,验签失败,返回错误信息 + */ + public static Pair chkLklApiNotifySign(HttpServletRequest request, String lklNotifyCerPath) { + // 验签 + String authorization = request.getHeader("Authorization"); + String requestBody = getBody(request); + log.debug("{} 回调通知 requestbody 参数:{}\n authorization参数:{}\n", request.getRequestURL(), requestBody, authorization); + + boolean checkSuccess = LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath); + if (!checkSuccess) { + log.error("{} 回调通知验签失败", request.getRequestURL()); + return Pair.of(false, "验签失败!"); + } + + return Pair.of(true, requestBody); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index cb35487f..ca31d97f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -2743,7 +2743,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl order_rows = null; + List order_rows; List review_id_row = new ArrayList<>(); // 检测数据是否合法,过滤允许修改的数据 if (CollUtil.isNotEmpty(order_ids)) { @@ -2821,7 +2821,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("order_id", orderId); + queryWrapper.eq("order_id", orderId).orderByAsc("id"); if (StrUtil.isNotBlank(storeId)) { queryWrapper.eq("store_id", storeId); }