diff --git a/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/WeiXinController.java b/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/WeiXinController.java index adaf468a..21715055 100644 --- a/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/WeiXinController.java +++ b/mall-account/src/main/java/com/suisung/mall/account/controller/mobile/WeiXinController.java @@ -84,8 +84,14 @@ public class WeiXinController extends BaseControllerImpl { return CommonResult.success(weiXinService.jsCode2Session(code, encryptedData, iv, activity_id, user_info)); } + @ApiOperation(value = "获取微信的 OpenId") + @RequestMapping(value = "/getWxOpenId", method = {RequestMethod.GET, RequestMethod.POST}) + public CommonResult jsCode2Session(@RequestParam(name = "code") String code) { + return CommonResult.success(weiXinService.getJsCode2Session(code)); + } + @ApiOperation(value = "获取微信小程序用户授权手机号,并绑定登录用户") - @RequestMapping(value = "/getUserPhoneNumber", method = RequestMethod.GET) + @RequestMapping(value = "/getUserPhoneNumber", method = {RequestMethod.GET, RequestMethod.POST}) public CommonResult getUserPhoneNumber(@RequestParam(name = "code") String code) { return CommonResult.success(weiXinService.getUserPhoneNumberAndBindUser(code, getCurrentUser())); } diff --git a/mall-account/src/main/java/com/suisung/mall/account/service/WeiXinService.java b/mall-account/src/main/java/com/suisung/mall/account/service/WeiXinService.java index 581f4faf..c81aadaa 100644 --- a/mall-account/src/main/java/com/suisung/mall/account/service/WeiXinService.java +++ b/mall-account/src/main/java/com/suisung/mall/account/service/WeiXinService.java @@ -35,6 +35,14 @@ public interface WeiXinService { Map checkAppLogin(String code); + /** + * 纯粹获取微信用户的 OpenId + * + * @param code + * @return + */ + Map getJsCode2Session(String code); + Map jsCode2Session(String code, String encryptedData, String iv, String activity_id, String user_info); /** diff --git a/mall-account/src/main/java/com/suisung/mall/account/service/impl/WeiXinServiceImpl.java b/mall-account/src/main/java/com/suisung/mall/account/service/impl/WeiXinServiceImpl.java index 6d679c9d..4cf67f78 100644 --- a/mall-account/src/main/java/com/suisung/mall/account/service/impl/WeiXinServiceImpl.java +++ b/mall-account/src/main/java/com/suisung/mall/account/service/impl/WeiXinServiceImpl.java @@ -359,7 +359,14 @@ public class WeiXinServiceImpl implements WeiXinService { return userInfo; } - private Map getJsCode2Session(String code) { + /** + * 纯粹获取微信用户的 OpenId + * + * @param code + * @return + */ + @Override + public Map getJsCode2Session(String code) { //从微信服务器获得session_key,openid,unionid String url = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code"; diff --git a/mall-admin/src/main/java/com/suisung/mall/admin/controller/admin/AdminAppMarketUpdateController.java b/mall-admin/src/main/java/com/suisung/mall/admin/controller/admin/AdminAppMarketUpdateController.java new file mode 100644 index 00000000..2dd15246 --- /dev/null +++ b/mall-admin/src/main/java/com/suisung/mall/admin/controller/admin/AdminAppMarketUpdateController.java @@ -0,0 +1,56 @@ +/* + * 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.admin.controller.admin; + +import cn.hutool.json.JSONObject; +import com.suisung.mall.admin.service.AdminAppMarketUpdateService; +import com.suisung.mall.common.api.CommonResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + + +@Api(tags = "安卓应用市场 APP 版本检查") +@RestController +@RequestMapping("/admin/admin/app-market-update") +public class AdminAppMarketUpdateController { + + @Resource + private AdminAppMarketUpdateService adminAppMarketUpdateService; + + @ApiOperation(value = "安卓市场热更新记录搜索列表", notes = "安卓市场热更新记录搜索列表") + @RequestMapping(value = "/search/list", method = RequestMethod.POST) + public CommonResult searchList(@RequestBody JSONObject paramsJSON) { + return adminAppMarketUpdateService.searchListInAdmin(paramsJSON.getInt("marketId"), paramsJSON.getStr("keyword"), paramsJSON.getInt("pageNum"), paramsJSON.getInt("pageSize")); + } + + @ApiOperation(value = "新增安卓市场热更新记录", notes = "新增安卓市场热更新记录") + @RequestMapping(value = "/add/new", method = RequestMethod.POST) + public CommonResult addNew(@RequestBody JSONObject paramsJSON) { + return adminAppMarketUpdateService.addNewInAdmin(paramsJSON); + } + + @ApiOperation(value = "修改安卓市场热更新记录", notes = "修改安卓市场热更新记录") + @RequestMapping(value = "/modify", method = RequestMethod.POST) + public CommonResult modify(@RequestBody JSONObject paramsJSON) { + return adminAppMarketUpdateService.modifyInAdmin(paramsJSON); + } + + @ApiOperation(value = "关闭安卓市场热更新记录", notes = "关闭安卓市场热更新记录") + @RequestMapping(value = "/enable-or-disable", method = RequestMethod.POST) + public CommonResult enableOrDisable(@RequestBody JSONObject paramsJSON) { + return adminAppMarketUpdateService.enableOrDisableInAdmin(paramsJSON.getLong("id"), paramsJSON.getInt("status")); + } + +} \ No newline at end of file diff --git a/mall-admin/src/main/java/com/suisung/mall/admin/controller/mobile/AdminAppMarketUpdateController.java b/mall-admin/src/main/java/com/suisung/mall/admin/controller/mobile/AppMarketUpdateController.java similarity index 72% rename from mall-admin/src/main/java/com/suisung/mall/admin/controller/mobile/AdminAppMarketUpdateController.java rename to mall-admin/src/main/java/com/suisung/mall/admin/controller/mobile/AppMarketUpdateController.java index 1ab77400..0e34da81 100644 --- a/mall-admin/src/main/java/com/suisung/mall/admin/controller/mobile/AdminAppMarketUpdateController.java +++ b/mall-admin/src/main/java/com/suisung/mall/admin/controller/mobile/AppMarketUpdateController.java @@ -9,6 +9,7 @@ package com.suisung.mall.admin.controller.mobile; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONObject; import com.suisung.mall.admin.service.AdminAppMarketUpdateService; import com.suisung.mall.common.api.CommonResult; @@ -24,7 +25,7 @@ import javax.annotation.Resource; @Api(tags = "安卓应用市场 APP 版本检查") @RestController @RequestMapping("/mobile/admin/app-market-update") -public class AdminAppMarketUpdateController { +public class AppMarketUpdateController { @Resource private AdminAppMarketUpdateService adminAppMarketUpdateService; @@ -35,4 +36,14 @@ public class AdminAppMarketUpdateController { return adminAppMarketUpdateService.checkLatestVersion(paramsJSON.getInt("marketId"), paramsJSON.getStr("packageName"), paramsJSON.getInt("currVersionKey")); } + @ApiOperation(value = "最新通用商家版App下载地址", notes = "最新通用商家版App下载地址") + @RequestMapping(value = "/last/apk", method = RequestMethod.POST) + public CommonResult lastCommonApp(@RequestBody(required = false) JSONObject paramsJSON) { + Integer marketId = 100; + if (paramsJSON != null && ObjectUtil.isNotEmpty(paramsJSON.getInt("marketId"))) { + marketId = paramsJSON.getInt("marketId"); + } + return adminAppMarketUpdateService.lastAdminAppMarketUpdate(marketId); + } + } diff --git a/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java b/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java index a7fa5262..96a35ed2 100644 --- a/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java +++ b/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java @@ -41,6 +41,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.*; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; @@ -92,6 +93,18 @@ public class OssServiceImpl implements OssService { @Value("${upload.filepath}") private String FILEPATH; + /** + * 判断路径是否存在,不存在创建 + * + * @param dirPath + */ + private static void isChartPathExist(String dirPath) { + File file = new File(dirPath); + if (!file.exists()) { + file.mkdirs(); + } + } + /** * 移动端上传文件 * @@ -163,7 +176,7 @@ public class OssServiceImpl implements OssService { if (uploadType.equals(1)) { url = ConfigConstant.URL_BASE + "/admin/oss/upload/" + dir + "/" + uploadName; // 文件本地路径 - } else if (uploadType.equals(2)){ + } else if (uploadType.equals(2)) { // oss 服务 try { url = uploadObject2OSS(new File(uploadPath), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(uploadName)); @@ -186,13 +199,11 @@ public class OssServiceImpl implements OssService { //截图 //todo 放入upload中 String thumb_file_url = ""; - if (VideoUtil.videoAllowFiles.contains(VideoUtil.getVideoFormat(uploadPath))) - { + if (VideoUtil.videoAllowFiles.contains(VideoUtil.getVideoFormat(uploadPath))) { String cover_path = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg"); String cover_upname = uploadName.replace("." + VideoUtil.getVideoFormat(uploadName), ".jpg"); - if (VideoUtil.getVideoCover(uploadPath, cover_path, 1, "375*667")) - { + if (VideoUtil.getVideoCover(uploadPath, cover_path, 1, "375*667")) { try { thumb_file_url = uploadObject2OSS(new File(cover_path), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat("/").concat(cover_upname)); } catch (Exception e) { @@ -200,9 +211,7 @@ public class OssServiceImpl implements OssService { thumb_file_url = ""; } - } - else - { + } else { thumb_file_url = ""; } } @@ -247,7 +256,7 @@ public class OssServiceImpl implements OssService { // 创建唯一文件名称 String suffix = fileName.substring(fileName.lastIndexOf(".")); String uploadName = IdUtil.simpleUUID() + suffix; - String uploadPath = FILEPATH + "/" + dir + "/" + uploadName; + String uploadPath = FILEPATH + "/" + dir + "/" + uploadName; String imageAllowExt = configService.getConfig("image_allow_ext", ""); String[] imageAllowExtList = imageAllowExt.split(","); @@ -297,9 +306,9 @@ public class OssServiceImpl implements OssService { } } - /** * 腾讯云上传 + * * @param file * @param concat * @return @@ -378,10 +387,10 @@ public class OssServiceImpl implements OssService { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String dir = ALIYUN_OSS_DIR_PREFIX + sdf.format(new Date()); // 签名有效期 - long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000; + long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000L; Date expiration = new Date(expireEndTime); // 文件大小 - long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024; + long maxSize = (long) ALIYUN_OSS_MAX_SIZE * 1024 * 1024; // 回调 OssCallbackParamDTO callback = new OssCallbackParamDTO(); callback.setCallbackUrl(ALIYUN_OSS_CALLBACK); @@ -394,10 +403,10 @@ public class OssServiceImpl implements OssService { policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); - byte[] binaryData = postPolicy.getBytes("utf-8"); + byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8); String policy = BinaryUtil.toBase64String(binaryData); String signature = ossClient.calculatePostSignature(postPolicy); - String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8")); + String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes(StandardCharsets.UTF_8)); // 返回结果 result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId()); result.setPolicy(policy); @@ -486,7 +495,7 @@ public class OssServiceImpl implements OssService { * @return */ private String getUserDirName(UserDto user) { - if (user.getId() == null) { + if (user == null || user.getId() == null) { throw new ApiException(I18nUtil._("获取用户信息失败!")); } if (user.isPlatform()) { @@ -499,16 +508,4 @@ public class OssServiceImpl implements OssService { } } } - - /** - * 判断路径是否存在,不存在创建 - * - * @param dirPath - */ - private static void isChartPathExist(String dirPath) { - File file = new File(dirPath); - if (!file.exists()) { - file.mkdirs(); - } - } } \ No newline at end of file diff --git a/mall-admin/src/main/java/com/suisung/mall/admin/service/AdminAppMarketUpdateService.java b/mall-admin/src/main/java/com/suisung/mall/admin/service/AdminAppMarketUpdateService.java index 67394650..010c31e9 100644 --- a/mall-admin/src/main/java/com/suisung/mall/admin/service/AdminAppMarketUpdateService.java +++ b/mall-admin/src/main/java/com/suisung/mall/admin/service/AdminAppMarketUpdateService.java @@ -8,7 +8,9 @@ package com.suisung.mall.admin.service; +import cn.hutool.json.JSONObject; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.admin.AdminAppMarketUpdate; public interface AdminAppMarketUpdateService { @@ -22,5 +24,57 @@ public interface AdminAppMarketUpdateService { */ CommonResult checkLatestVersion(Integer marketId, String packageName, Integer currVersionKey); + /** + * 获取最新的(默认通用)商家版 App 下载地址 + * + * @param marketId + * @return + */ + CommonResult lastAdminAppMarketUpdate(Integer marketId); + + /** + * 后台管理员搜索列表 + * + * @param marketId + * @param keyword + * @param pageNum + * @param pageSize + * @return + */ + CommonResult searchListInAdmin(Integer marketId, String keyword, Integer pageNum, Integer pageSize); + + /** + * 后台管理员新增 + * + * @param paramsJSON + * @return + */ + CommonResult addNewInAdmin(JSONObject paramsJSON); + + /** + * 后台管理员修改 + * + * @param paramsJSON + * @return + */ + CommonResult modifyInAdmin(JSONObject paramsJSON); + + /** + * 后台管理员禁用/启用 + * + * @param id + * @param status + * @return + */ + CommonResult enableOrDisableInAdmin(Long id, Integer status); + + /** + * 获取最后一条记录 + * + * @param marketId + * @param packageName + * @return + */ + AdminAppMarketUpdate getLastAppMarketUpdate(Integer marketId, String packageName); } diff --git a/mall-admin/src/main/java/com/suisung/mall/admin/service/impl/AdminAppMarketUpdateServiceImpl.java b/mall-admin/src/main/java/com/suisung/mall/admin/service/impl/AdminAppMarketUpdateServiceImpl.java index 63772bd2..5d189e1f 100644 --- a/mall-admin/src/main/java/com/suisung/mall/admin/service/impl/AdminAppMarketUpdateServiceImpl.java +++ b/mall-admin/src/main/java/com/suisung/mall/admin/service/impl/AdminAppMarketUpdateServiceImpl.java @@ -9,16 +9,30 @@ package com.suisung.mall.admin.service.impl; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.admin.mapper.AdminAppMarketUpdateMapper; import com.suisung.mall.admin.service.AdminAppMarketUpdateService; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.modules.admin.AdminAppMarketUpdate; +import com.suisung.mall.common.pojo.dto.FileInfoDTO; +import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import static com.suisung.mall.common.utils.ContextUtil.getCurrentUser; + @Service public class AdminAppMarketUpdateServiceImpl extends BaseServiceImpl implements AdminAppMarketUpdateService { + @Value("${spring.profiles.active}") + private String profile; + /** * 获取某个市场的最新 APP 版本 * @@ -33,20 +47,244 @@ public class AdminAppMarketUpdateServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("market_id", marketId).eq("package_name", packageName) - .orderByDesc("version_key").last("limit 1"); - - AdminAppMarketUpdate adminAppMarketUpdate = getOne(queryWrapper); + AdminAppMarketUpdate adminAppMarketUpdate = getLastAppMarketUpdate(marketId, packageName); if (adminAppMarketUpdate == null) { - return CommonResult.failed("当前应用已是最新版本"); + return CommonResult.success(null, "当前已是最新版本"); } + if (ObjectUtil.isNotEmpty(currVersionKey) && adminAppMarketUpdate.getVersion_key() <= currVersionKey) { + return CommonResult.success(null, "当前已是最新版本"); + } - if (!ObjectUtil.isEmpty(currVersionKey) && adminAppMarketUpdate.getVersion_key() <= currVersionKey) { - return CommonResult.failed("当前应用已是最新版本"); + // 更新包后缀处理 + if (StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_apk_url()) && StrUtil.isBlank(adminAppMarketUpdate.getFile_ext())) { + adminAppMarketUpdate.setFile_ext(StringUtils.getFileExt(adminAppMarketUpdate.getDownload_apk_url())); + } + + // 解决跨域问题,更换包的域名:https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/ 成 https://static.gpxscs.cn/ + if ("prod".equals(profile) && StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_apk_url())) { + String downloadUrl = adminAppMarketUpdate.getDownload_apk_url(); + downloadUrl = downloadUrl.replace("https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/", "https://static.gpxscs.cn/"); + adminAppMarketUpdate.setDownload_apk_url(downloadUrl); + } + + // 解决跨域问题,更换包的域名:https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/ 成 https://static.gpxscs.cn/ + if ("prod".equals(profile) && StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_wgt_url())) { + String downloadWgtUrl = adminAppMarketUpdate.getDownload_wgt_url(); + downloadWgtUrl = downloadWgtUrl.replace("https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/", "https://static.gpxscs.cn/"); + adminAppMarketUpdate.setDownload_wgt_url(downloadWgtUrl); } return CommonResult.success(adminAppMarketUpdate); } + + /** + * 获取最新的(默认通用)商家版 App 下载地址 + * + * @param marketId + * @return + */ + @Override + public CommonResult lastAdminAppMarketUpdate(Integer marketId) { + Integer defMarketId = 100; + if (ObjectUtil.isEmpty(marketId)) { + marketId = defMarketId; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("market_id", marketId).eq("status", CommonConstant.Enable).orderByDesc("version_key"); + + AdminAppMarketUpdate adminAppMarketUpdate = findOne(queryWrapper); + if (adminAppMarketUpdate == null) { + return CommonResult.success(null, "没有找到最新版本"); + } + + return CommonResult.success(adminAppMarketUpdate); + } + + /** + * 后台管理员搜索列表 + * + * @param marketId + * @param keyword + * @return + */ + @Override + public CommonResult searchListInAdmin(Integer marketId, String keyword, Integer pageNum, Integer pageSize) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (ObjectUtil.isNotEmpty(marketId)) { + queryWrapper.eq("market_id", marketId); + } + if (StrUtil.isNotBlank(keyword)) { + queryWrapper.and(wrapper -> wrapper.like("package_name", keyword) + .or().like("version_name", keyword) + .or().like("description", keyword)); + } + + queryWrapper.orderByDesc("id"); + + Page list = lists(queryWrapper, pageNum, pageSize); + + return CommonResult.success(list); + } + + /** + * 后台管理员新增 + * + * @param paramsJSON + * @return + */ + @Override + public CommonResult addNewInAdmin(JSONObject paramsJSON) { + String userId = "0"; + UserDto user = getCurrentUser(); + if (user != null) { + userId = String.valueOf(user.getId()); + } + + if (ObjectUtil.isEmpty(paramsJSON)) { + return CommonResult.failed("缺少必要参数"); + } + + + AdminAppMarketUpdate adminAppMarketUpdate = JSONUtil.toBean(paramsJSON, AdminAppMarketUpdate.class); + if (ObjectUtil.isEmpty(adminAppMarketUpdate)) { + return CommonResult.failed("参数格式有误"); + } + + + if (ObjectUtil.isEmpty(adminAppMarketUpdate.getMarket_id()) || ObjectUtil.isEmpty(adminAppMarketUpdate.getPackage_name()) + || ObjectUtil.isEmpty(adminAppMarketUpdate.getVersion_key()) || ObjectUtil.isEmpty(adminAppMarketUpdate.getVersion_name()) + || ObjectUtil.isEmpty(adminAppMarketUpdate.getDownload_apk_url())) { + return CommonResult.failed("缺少关键参数"); + } + + AdminAppMarketUpdate record = getLastAppMarketUpdate(adminAppMarketUpdate.getMarket_id(), adminAppMarketUpdate.getPackage_name()); + if (record != null && ObjectUtil.isNotEmpty(adminAppMarketUpdate.getVersion_key()) && record.getVersion_key().equals(adminAppMarketUpdate.getVersion_key())) { + return CommonResult.failed("当前版本升级包已存在"); + } + + if ("prod".equals(profile) && StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_apk_url())) { + String downloadUrl = adminAppMarketUpdate.getDownload_apk_url(); + downloadUrl = downloadUrl.replace("https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/", "https://static.gpxscs.cn/"); + adminAppMarketUpdate.setDownload_apk_url(downloadUrl); + } + + // 解决跨域问题,更换包的域名:https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/ 成 https://static.gpxscs.cn/ + if ("prod".equals(profile) && StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_wgt_url())) { + String downloadWgtUrl = adminAppMarketUpdate.getDownload_wgt_url(); + downloadWgtUrl = downloadWgtUrl.replace("https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/", "https://static.gpxscs.cn/"); + adminAppMarketUpdate.setDownload_wgt_url(downloadWgtUrl); + } + + FileInfoDTO fileInfoDTO = StringUtils.getFileInfoFromUrl(adminAppMarketUpdate.getDownload_apk_url()); + if (fileInfoDTO != null) { + adminAppMarketUpdate.setPackage_size(fileInfoDTO.getFileSize()); + adminAppMarketUpdate.setFile_ext(fileInfoDTO.getFileExtension()); + adminAppMarketUpdate.setMd5_checksum(fileInfoDTO.getMd5()); + } + + adminAppMarketUpdate.setCreated_by(userId); + adminAppMarketUpdate.setUpdated_by(userId); + + return add(adminAppMarketUpdate) ? CommonResult.success() : CommonResult.failed("操作失败"); + } + + /** + * 后台管理员修改 + * + * @param paramsJSON + * @return + */ + @Override + public CommonResult modifyInAdmin(JSONObject paramsJSON) { + String userId = "0"; + UserDto user = getCurrentUser(); + if (user != null) { + userId = String.valueOf(user.getId()); + } + + if (ObjectUtil.isEmpty(paramsJSON)) { + return CommonResult.failed("缺少必要参数"); + } + + if (ObjectUtil.isEmpty(paramsJSON.getLong("id"))) { + return CommonResult.failed("缺少id参数"); + } + + AdminAppMarketUpdate adminAppMarketUpdate = JSONUtil.toBean(paramsJSON, AdminAppMarketUpdate.class); + if (ObjectUtil.isEmpty(adminAppMarketUpdate)) { + return CommonResult.failed("参数格式有误"); + } + + if ("prod".equals(profile) && StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_apk_url())) { + String downloadUrl = adminAppMarketUpdate.getDownload_apk_url(); + downloadUrl = downloadUrl.replace("https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/", "https://static.gpxscs.cn/"); + adminAppMarketUpdate.setDownload_apk_url(downloadUrl); + } + + // 解决跨域问题,更换包的域名:https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/ 成 https://static.gpxscs.cn/ + if ("prod".equals(profile) && StrUtil.isNotBlank(adminAppMarketUpdate.getDownload_wgt_url())) { + String downloadWgtUrl = adminAppMarketUpdate.getDownload_wgt_url(); + downloadWgtUrl = downloadWgtUrl.replace("https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/", "https://static.gpxscs.cn/"); + adminAppMarketUpdate.setDownload_wgt_url(downloadWgtUrl); + } + + FileInfoDTO fileInfoDTO = StringUtils.getFileInfoFromUrl(adminAppMarketUpdate.getDownload_apk_url()); + if (fileInfoDTO != null) { + adminAppMarketUpdate.setPackage_size(fileInfoDTO.getFileSize()); + adminAppMarketUpdate.setFile_ext(fileInfoDTO.getFileExtension()); + adminAppMarketUpdate.setMd5_checksum(fileInfoDTO.getMd5()); + } + + adminAppMarketUpdate.setUpdated_by(userId); + + return saveOrUpdate(adminAppMarketUpdate) ? CommonResult.success() : CommonResult.failed("操作失败"); + } + + /** + * 后台管理员禁用/启用 + * + * @param id + * @param status + * @return + */ + @Override + public CommonResult enableOrDisableInAdmin(Long id, Integer status) { + Integer userId = 0; + UserDto user = getCurrentUser(); + if (user != null) { + userId = user.getId(); + } + + if (ObjectUtil.isEmpty(id) || ObjectUtil.isEmpty(status)) { + return CommonResult.failed("缺少必要参数"); + } + + AdminAppMarketUpdate adminAppMarketUpdate = new AdminAppMarketUpdate(); + adminAppMarketUpdate.setId(id); + adminAppMarketUpdate.setStatus(status); + adminAppMarketUpdate.setUpdated_by(String.valueOf(userId)); + return saveOrUpdate(adminAppMarketUpdate) ? CommonResult.success() : CommonResult.failed("操作失败"); + } + + /** + * 获取一条记录 + * + * @param marketId + * @param packageName + * @return + */ + @Override + public AdminAppMarketUpdate getLastAppMarketUpdate(Integer marketId, String packageName) { + if (ObjectUtil.isEmpty(marketId) || StrUtil.isBlank(packageName)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("market_id", marketId).eq("status", CommonConstant.Enable) + .eq("package_name", packageName).orderByDesc("version_key"); + + return findOne(queryWrapper); + } } diff --git a/mall-common/pom.xml b/mall-common/pom.xml index e679edd6..3af6b2f1 100644 --- a/mall-common/pom.xml +++ b/mall-common/pom.xml @@ -102,19 +102,19 @@ - - - - - - + + + + + + - - - - - - + + + + + + @@ -274,6 +274,11 @@ spring-boot-starter-freemarker + + com.huaban + jieba-analysis + 1.0.2 + diff --git a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java index fec451e9..657a063a 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java +++ b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java @@ -45,11 +45,13 @@ public class CommonConstant { public static final Integer PRODUCT_DATA_SOURCE_USER = 1; public static final Integer PRODUCT_DATA_SOURCE_SX = 2; - // 入驻商家的审批状态:1-已通过;2-未通过;3-待审核;4-未申请; + // 入驻审批状态:1-已通过;2-未通过;3-待审核;4-未申请过;5-已提交拉卡拉审核;21-拉卡拉审核未通过; public static final Integer MCH_APPR_STA_PASS = 1; public static final Integer MCH_APPR_STA_NOPASS = 2; public static final Integer MCH_APPR_STA_PADDING = 3; public static final Integer MCH_APPR_STA_NONE = 4; + public static final Integer MCH_APPR_STA_LKL_PADDING = 5; + public static final Integer MCH_APPR_STA_LKL_NOPASS = 21; // 入驻商家主体类型,企业或个人:1-企业;2-个人; public static final Integer MCH_ENTITY_TYPE_QY = 1; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/admin/AdminAppMarketUpdate.java b/mall-common/src/main/java/com/suisung/mall/common/modules/admin/AdminAppMarketUpdate.java index 91e9b252..6608505e 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/admin/AdminAppMarketUpdate.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/admin/AdminAppMarketUpdate.java @@ -43,8 +43,14 @@ public class AdminAppMarketUpdate implements Serializable { @ApiModelProperty(value = "热更新包显示的版本号,用于标识不同的热更新版本,遵循一定命名规则,如 1.23.21") private String version_name; - @ApiModelProperty(value = "热更新包的下载链接,用户可通过此链接下载更新包完成应用更新") - private String download_url; + @ApiModelProperty(value = "apk包的下载链接,用户可通过此链接下载更新包完成应用更新") + private String download_apk_url; + + @ApiModelProperty(value = "增量包的下载链接,用户可通过此链接下载更新包完成应用更新") + private String download_wgt_url; + + @ApiModelProperty(value = "更新包后缀:apk, wgt") + private String file_ext; @ApiModelProperty(value = "热更新的发布时间,记录更新包正式发布的时刻") private Date release_time; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/esign/EsignPlatformInfo.java b/mall-common/src/main/java/com/suisung/mall/common/modules/esign/EsignPlatformInfo.java index 3527672b..ea3e155d 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/esign/EsignPlatformInfo.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/esign/EsignPlatformInfo.java @@ -47,6 +47,15 @@ public class EsignPlatformInfo implements Serializable { @ApiModelProperty(value = "平台方营业执照公司名称") private String license_company; + @ApiModelProperty(value = "平台方营业执照上的经营内容") + private String license_content; + + @ApiModelProperty(value = "代理商的省/市/区,如:广东省/深圳市/福田区") + private String license_area; + + @ApiModelProperty(value = "代理商的省id/市id/区id,如:11000/11100/11101") + private String license_district_id; + @ApiModelProperty(value = "平台方营业执照公司详细地址") private String license_address; @@ -74,7 +83,7 @@ public class EsignPlatformInfo implements Serializable { @ApiModelProperty(value = "收款账户名称") private String rec_acc_name; - @ApiModelProperty(value = "收款账户账户类型:1-个人;2-企业;") + @ApiModelProperty(value = "收款账户账户类型:1-个人(对私);2-企业(对公);") private Integer rec_acc_type; @ApiModelProperty(value = "收款账户证件号") @@ -95,17 +104,11 @@ public class EsignPlatformInfo implements Serializable { @ApiModelProperty(value = "平台方公司维度") private String latitude; - @ApiModelProperty(value = "平台方公司店铺所在的省") - private String province_id; + @ApiModelProperty(value = "代理商等级:0-平台方(只能一条记录);1-一级代理;2-二级代理;3-三级代理;4-四级代理;") + private Integer level; - @ApiModelProperty(value = "平台方公司所在的市") - private String city_id; - - @ApiModelProperty(value = "平台方公司所在的县区") - private String county_id; - - @ApiModelProperty(value = "类型:0-平台方(只能一条记录);1-一级代理;2-二级代理;3-三级代理;4-四级代理;") - private Integer type; + @ApiModelProperty(value = "邀请码,后期跟收益有关") + private String invite_code; @ApiModelProperty(value = "记录状态:1-有效;2-无效;") private Integer status; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklBanks.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklBanks.java new file mode 100644 index 00000000..d09e4bae --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklBanks.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.modules.lakala; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("lkl_banks") +@ApiModel(value = "拉卡拉银行信息(包含账户行号、结清行号)", description = "拉卡拉银行信息(包含账户行号、结清行号)") +public class LklBanks implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增ID") + private Long id; + + @ApiModelProperty(value = "地区号") + private String area_code; + + @ApiModelProperty(value = "总行号") + private String bank_no; + + @ApiModelProperty(value = "银行名称") + private String branch_bank_name; + + @ApiModelProperty(value = "支行行号") + private String branch_bank_no; + + @ApiModelProperty(value = "结清行号") + private String clear_no; + + @ApiModelProperty(value = "记录状态:1-有效;2-无效;") + private Integer status; + + @ApiModelProperty(value = "记录创建时间") + private Date created_at; + + @ApiModelProperty(value = "记录更新时间") + private Date updated_at; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerEc.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerEc.java new file mode 100644 index 00000000..e03f8862 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerEc.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.common.modules.lakala; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("lkl_ledger_ec") +@ApiModel(value = "拉卡拉商家入网电子合同申请表", description = "拉卡拉商家入网电子合同申请表") +public class LklLedgerEc implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增ID") + private Long id; + private Long mch_id; + private String mch_mobile; + private String req_params; + private String resp_body; + private String resp_notify_body; + private String ec_no; + private String ec_name; + private String ec_file; + private String lkl_file_path; + private String ec_status; + private Long ec_apply_id; + private String result_url; + private String notify_url; + private Integer status; + private Date created_at; + private Date updated_at; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMember.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMember.java index 9be106b4..adbaf357 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMember.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerMember.java @@ -8,8 +8,11 @@ package com.suisung.mall.common.modules.lakala; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -25,6 +28,8 @@ import java.util.Date; public class LklLedgerMember implements Serializable { private static final long serialVersionUID = 1L; + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增ID") private Long id; private String order_no; private String org_code; @@ -42,8 +47,9 @@ public class LklLedgerMember implements Serializable { private String split_rule_source; private String ret_url; private String apply_id; - private String audit_status; + private Integer audit_status; private String audit_status_text; + private String audit_resp; private String remark; private Long mch_id; private String version; 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 f3525525..5b714f5e 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 @@ -8,8 +8,11 @@ package com.suisung.mall.common.modules.lakala; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -25,6 +28,8 @@ import java.util.Date; public class LklLedgerMerReceiverBind implements Serializable { private static final long serialVersionUID = 1L; + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增ID") private Long id; private String order_no; private String org_code; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerReceiver.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerReceiver.java index 43ffbb9e..1c13fce2 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerReceiver.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklLedgerReceiver.java @@ -8,8 +8,11 @@ package com.suisung.mall.common.modules.lakala; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -25,6 +28,8 @@ import java.util.Date; public class LklLedgerReceiver implements Serializable { private static final long serialVersionUID = 1L; + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增ID") private Long id; private String order_no; private String org_id; 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 new file mode 100644 index 00000000..d0562013 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.modules.lakala; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("lkl_order_separate") +@ApiModel(value = "拉卡拉分账接收方", description = "拉卡拉分账接收方表") +public class LklOrderSeparate { + + @TableId(value = "id", type = IdType.INPUT) + @ApiModelProperty(value = "自增Id", example = "1") + private Long id; + + @ApiModelProperty(value = "拉卡拉外部商户号") + private String merchant_no; + + @ApiModelProperty(value = "拉卡拉对账单流水号 posp流水号,查清结算用") + private String log_no; + + @ApiModelProperty(value = "交易日期,yyyyMMdd,查清结算用") + private String log_date; + + @ApiModelProperty(value = "商户分账指令流水号,每个商户号下唯一,否则会校验失败") + private String out_separate_no; + + @ApiModelProperty(value = "分账指令流水号,分账系统生成唯一流水") + private String separate_no; + + @ApiModelProperty(value = "分账总金额,单位:分") + private String total_amt; + + @ApiModelProperty(value = "分账计算类型:0- 按照指定金额,1- 按照指定比例。默认 0") + private String cal_type; + + @ApiModelProperty(value = "指令类型 SEPARATE-分账, CANCEL-分账撤销,FALLBACK-分账回退") + private String cmd_type; + + @ApiModelProperty(value = "分账接收类型:0-全部分账到商户本身。1-分账到多方,默认 1") + private String separate_type; + + @ApiModelProperty(value = "分账日期 yyyyMMdd") + private String separate_date; + + @ApiModelProperty(value = "完成日期 yyyyMMdd") + private String finish_date; + + @ApiModelProperty(value = "拉卡拉异步通知回调地址") + private String notify_url; + + @ApiModelProperty(value = "分账接收数据对象,JSON 格式") + private String recv_datas; + + @ApiModelProperty(value = "最终分账明细,JSON 格式") + private String detail_datas; + + @ApiModelProperty(value = "拉卡拉机构编号") + private String lkl_org_no; + + @ApiModelProperty(value = "分账状态:PROCESSING-处理中, ACCEPTED-已受理, SUCCESS-成功, FAIL-失败") + private String status; + + @ApiModelProperty(value = "处理状态:ACCEPTED-已受理, PROCESSING-处理中, FAIL-失败, SUCCESS-成功") + private String final_status; + + @ApiModelProperty(value = "新建时间") + private Date created_at; + + @ApiModelProperty(value = "更新时间") + private Date updated_at; +} \ No newline at end of file diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/merch/ShopMerchEntry.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java similarity index 70% rename from mall-common/src/main/java/com/suisung/mall/common/modules/merch/ShopMerchEntry.java rename to mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java index 15376d3d..0131dff4 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/merch/ShopMerchEntry.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopMchEntry.java @@ -6,7 +6,7 @@ * Vestibulum commodo. Ut rhoncus gravida arcu. */ -package com.suisung.mall.common.modules.merch; +package com.suisung.mall.common.modules.store; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; @@ -25,8 +25,8 @@ import java.util.Date; @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("shop_mch_entry") -@ApiModel(value = "ShopMerchEntry 实体", description = "商家入驻信息表") -public class ShopMerchEntry implements Serializable { +@ApiModel(value = "ShopMchEntry 实体", description = "商家入驻信息表") +public class ShopMchEntry implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "自增ID") @@ -81,16 +81,10 @@ public class ShopMerchEntry implements Serializable { @ApiModelProperty(value = "邮箱") private String email; - @ApiModelProperty(value = "入驻商家店铺所在的省") - private String province_id; + @ApiModelProperty(value = "店铺地区编号,省份code/城市code/区code") + private String store_district; - @ApiModelProperty(value = "入驻商家店铺所在的市") - private String city_id; - - @ApiModelProperty(value = "入驻商家店铺所在的县区") - private String county_id; - - @ApiModelProperty(value = "店铺地区 省份/城市/乡县") + @ApiModelProperty(value = "店铺地区名称,省份/城市/区") private String store_area; @ApiModelProperty(value = "入驻商家店铺的详细地址") @@ -114,14 +108,11 @@ public class ShopMerchEntry implements Serializable { @ApiModelProperty(value = "入驻商家营业执照图片的存储路径") private String biz_license_image; - @ApiModelProperty(value = "入驻商家的许可证类型:1-许可证;2-特许证件,3-其他证件") - private Integer license_type; + @ApiModelProperty(value = "入驻商家营业执照的经营范围") + private String biz_license_content; - @ApiModelProperty(value = "入驻商家的许可证编号") - private String license_number; - - @ApiModelProperty(value = "入驻商家许可证图片的存储路径") - private String license_image; + @ApiModelProperty(value = "营业执照以外的许可证信息列表JSON格式 [{'type':1,'number':'91450881MA5P8MWX69','img':'https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/1.png'}],type:1-许可证;2-特许证件,3-其他证件") + private String other_license_list; @ApiModelProperty(value = "商家营业执照有效开始时间") private String biz_license_period_begin; @@ -174,31 +165,34 @@ public class ShopMerchEntry implements Serializable { @ApiModelProperty(value = "个人身份证有效期结束时间") private String individual_id_period_end; - @ApiModelProperty(value = "入驻商家的开户⾏号") - private String bank_code; - - @ApiModelProperty(value = "入驻商家的开户银行") + @ApiModelProperty(value = "入驻商家的开户银行名称") private String bank_name; - @ApiModelProperty(value = "入驻商家开户银行的支行名称") - private String bank_branch_name; - - @ApiModelProperty(value = "结算账户清算⾏号") - private String clearing_bank_code; - @ApiModelProperty(value = "结算账户银行卡图片") private String bank_image; - @ApiModelProperty(value = "结算银行地区,格式: 省份/城市/乡县") + @ApiModelProperty(value = "结算账号省市区编号 省code/市code/区code,必填项") + private String bank_district; + + @ApiModelProperty(value = "结算银行地区名称,格式: 省份/城市/区") private String bank_area; + @ApiModelProperty(value = "结算账号类型:57-对公 58-对私") + private String account_type; + + @ApiModelProperty(value = "结算账户⾏号") + private String openning_bank_code; + + @ApiModelProperty(value = "结算账户清算⾏号") + private String clearing_bank_code; + @ApiModelProperty(value = "入驻商家的收款账户号码") private String account_number; @ApiModelProperty(value = "入驻商家的收款账户姓名") private String account_holder_name; - @ApiModelProperty(value = "入驻商家的审批状态:1-已通过;2-未通过;3-待审核;") + @ApiModelProperty(value = "入驻商家的审批状态:1-已通过;2-未通过;3-待审核;4-未申请过;5-已提交审核;") private Integer approval_status; @ApiModelProperty(value = "入驻商家审批时的备注信息") @@ -210,12 +204,57 @@ public class ShopMerchEntry implements Serializable { @ApiModelProperty(value = "商家的代理商ID") private Long distributor_id; + @ApiModelProperty(value = "拉卡拉内部商户号") + private String lkl_mer_inner_no; + + @ApiModelProperty(value = "拉卡拉银联商户号(唯一)") + private String lkl_mer_cup_no; + + @ApiModelProperty(value = "拉卡拉分配的业务终端号") + private String lkl_term_no; + + @ApiModelProperty(value = "拉卡拉审核状态:1-已通过;2-未通过") + private Integer lkl_tk_audit_status; + + @ApiModelProperty(value = "拉卡拉进件请求参数") + private String lkl_tk_reg_params; + + @ApiModelProperty(value = "异步通知的请求参数(加密过)") + private String lkl_tk_reg_notify_req; + + @ApiModelProperty(value = "拉卡拉进件成功返回的JSON数据") + private String lkl_tk_reg_resp; + + @ApiModelProperty(value = "拉卡拉入网电子合同编号(进件必须使用)") + private String lkl_ec_no; + + @ApiModelProperty(value = "拉卡拉入网电子合同名称") + private String lkl_ec_name; + + @ApiModelProperty(value = "入网电子合同拉卡拉相对路径") + private String lkl_ec_file_path; + + @ApiModelProperty(value = "拉卡拉入网电子合同签署H5地址(进件必须使用)") + private String lkl_ec_result_url; + @ApiModelProperty(value = "合同签署状态:0-无任何签署;1-一方签署;2-双方已签署;") private Integer signed_status; @ApiModelProperty(value = "店铺创建状态:1-已启用(入驻已审批,合同已生成);2-未启用") private Integer store_status; + @ApiModelProperty(value = "是否签署电子合同:1-是;2-否;") + private Integer has_ec_signed; + + @ApiModelProperty(value = "是否申请分账业务:1-是;2-否;") + private Integer has_apply_split; + + @ApiModelProperty(value = "是否新增分账接收方:1-是;2-否;") + private Integer has_apply_receiver; + + @ApiModelProperty(value = "是否绑定分账接收方:1-是;2-否;") + private Integer has_bind_receiver; + @ApiModelProperty(value = "该商家入驻记录是否有效,0:无效,1:有效") private Integer status; diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/FileInfoDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/FileInfoDTO.java new file mode 100644 index 00000000..f718f776 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/FileInfoDTO.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.pojo.dto; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +@ApiModel(value = "文件信息", description = "文件信息") +public class FileInfoDTO { + + String md5; + String fileExtension; + long fileSize; + + public FileInfoDTO(String md5, String fileExtension, long fileSize) { + this.md5 = md5; + this.fileExtension = fileExtension; + this.fileSize = fileSize; + } + + @Override + public String toString() { + return "MD5: " + md5 + ", 文件后缀: " + fileExtension + ", 文件大小: " + fileSize + " 字节"; + } + +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java new file mode 100644 index 00000000..6308bca5 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.utils; + +import com.huaban.analysis.jieba.JiebaSegmenter; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 结巴分词工具类 + */ +@Component +public class JiebaUtils { + + private final JiebaSegmenter segmenter = new JiebaSegmenter(); + + public static void main(String[] args) { + JiebaUtils jiebaUtils = new JiebaUtils(); + String text = "农行桂平"; + List words = jiebaUtils.segment(text); + System.out.println(words); + } + + /** + * 精确模式分词 + */ + public List segment(String text) { + return segmenter.process(text, JiebaSegmenter.SegMode.INDEX) + .stream() + .map(token -> token.word) + .collect(Collectors.toList()); + } + + /** + * 搜索模式分词(更细粒度) + */ + public List segmentForSearch(String text) { + return segmenter.process(text, JiebaSegmenter.SegMode.SEARCH) + .stream() + .map(token -> token.word) + .collect(Collectors.toList()); + } +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/RSAUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/RSAUtil.java new file mode 100644 index 00000000..01e9a237 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/RSAUtil.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class RSAUtil { + + private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class); + + /** + * 使用私钥对数据进行 SHA256withRSA 签名 + * + * @param data 待签名的数据 + * @param privateKey 私钥(Base64编码) + * @return 签名结果(Base64编码) + */ + public static String signSHA256withRSA(String data, String privateKey) { + try { + // 解码私钥, 并去掉头尾的标识 + byte[] keyBytes = Base64.getDecoder().decode(privateKey); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + + // 生成私钥对象 + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey priKey = keyFactory.generatePrivate(keySpec); + + // 初始化签名 + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(priKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + + // 生成签名并编码为Base64 + byte[] signBytes = signature.sign(); + return Base64.getEncoder().encodeToString(signBytes); + } catch (NoSuchAlgorithmException e) { + logger.error("不支持的加密算法: {}", e.getMessage(), e); + } catch (InvalidKeySpecException e) { + logger.error("私钥格式错误: {}", e.getMessage(), e); + } catch (InvalidKeyException e) { + logger.error("无效的私钥: {}", e.getMessage(), e); + } catch (SignatureException e) { + logger.error("签名处理异常: {}", e.getMessage(), e); + } catch (Exception e) { + logger.error("签名过程发生未知异常: {}", e.getMessage(), e); + } + return null; + } + + /** + * 使用公钥验证 SHA256withRSA 签名 + * + * @param data 原始数据 + * @param sign 签名结果(Base64编码) + * @param publicKey 公钥(Base64编码) + * @return 验证结果 + */ + public static boolean verifySHA256withRSA(String data, String sign, String publicKey) { + try { + // 解码公钥 + byte[] keyBytes = Base64.getDecoder().decode(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + + // 生成公钥对象 + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey pubKey = keyFactory.generatePublic(keySpec); + + // 初始化签名验证 + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(pubKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + + // 验证签名 + byte[] signBytes = Base64.getDecoder().decode(sign); + return signature.verify(signBytes); + } catch (NoSuchAlgorithmException e) { + logger.error("不支持的加密算法: {}", e.getMessage(), e); + } catch (InvalidKeySpecException e) { + logger.error("公钥格式错误: {}", e.getMessage(), e); + } catch (InvalidKeyException e) { + logger.error("无效的公钥: {}", e.getMessage(), e); + } catch (SignatureException e) { + logger.error("签名验证异常: {}", e.getMessage(), e); + } catch (Exception e) { + logger.error("验证过程发生未知异常: {}", e.getMessage(), e); + } + return false; + } + + /** + * 生成 RSA 密钥对 + * + * @param keySize 密钥长度,推荐 2048 + * @return 密钥对 + */ + public static KeyPair generateKeyPair(int keySize) { + KeyPairGenerator keyPairGen = null; + try { + keyPairGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + keyPairGen.initialize(keySize); + return keyPairGen.generateKeyPair(); + } + + /** + * 获取公钥的 Base64 编码字符串 + */ + public static String getPublicKeyString(KeyPair keyPair) { + PublicKey publicKey = keyPair.getPublic(); + return Base64.getEncoder().encodeToString(publicKey.getEncoded()); + } + + /** + * 获取私钥的 Base64 编码字符串 + */ + public static String getPrivateKeyString(KeyPair keyPair) { + PrivateKey privateKey = keyPair.getPrivate(); + return Base64.getEncoder().encodeToString(privateKey.getEncoded()); + } + + // 示例用法 + public static void main(String[] args) { + try { + // 生成密钥对 + KeyPair keyPair = generateKeyPair(2048); + String publicKey = getPublicKeyString(keyPair); + String privateKey = getPrivateKeyString(keyPair); + + // 待签名数据 + String data = "Hello, RSA Signature!"; + + // 签名 + String signature = signSHA256withRSA(data, privateKey); + System.out.println("签名结果: " + signature); + + // 验证 + boolean isValid = verifySHA256withRSA(data, signature, publicKey); + System.out.println("验证结果: " + isValid); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/RestTemplateHttpUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/RestTemplateHttpUtil.java index aa50bcfa..7a4215f8 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/RestTemplateHttpUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/RestTemplateHttpUtil.java @@ -91,6 +91,22 @@ public class RestTemplateHttpUtil { return response.getBody(); } + public static ResponseEntity sendPostBodyBackEntity(String url, JSONObject headers, JSONObject requestBody, Class responseType) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + httpHeaders.add(entry.getKey(), (String) entry.getValue()); + } + } + + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(requestBody, httpHeaders); + + ResponseEntity response = restTemplate.postForEntity(url, entity, responseType); + return response; + } + /** * 发送带 header 的 POST form-data 请求 * diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java index eba58e3e..31fa3e85 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java @@ -1,7 +1,9 @@ package com.suisung.mall.common.utils; +import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.*; +import com.suisung.mall.common.pojo.dto.FileInfoDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -10,8 +12,12 @@ import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; import java.net.URLEncoder; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.text.MessageFormat; import java.text.SimpleDateFormat; @@ -709,6 +715,64 @@ public final class StringUtils extends org.apache.commons.lang3.StringUtils { return false; } + /** + * 根据url文件,生成文件MD5文摘签名 + * + * @param fileUrl + * @return + */ + public static FileInfoDTO getFileInfoFromUrl(String fileUrl) { + if (StrUtil.isBlank(fileUrl)) { + return null; + } + + try { + URL url = new URL(fileUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + + // 获取文件大小 + long fileSize = connection.getContentLengthLong(); + + // 获取文件后缀 + String path = url.getPath(); + String fileExtension = ""; + int dotIndex = path.lastIndexOf('.'); + if (dotIndex != -1) { + fileExtension = path.substring(dotIndex + 1); + } + + try (InputStream inputStream = connection.getInputStream()) { + // 获取 MD5 消息摘要实例 + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] buffer = new byte[8192]; + int bytesRead; + // 从输入流读取数据并更新摘要 + while ((bytesRead = inputStream.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead); + } + // 完成摘要计算 + byte[] md5Bytes = digest.digest(); + // 将字节数组转换为十六进制字符串 + StringBuilder md5String = new StringBuilder(); + for (byte b : md5Bytes) { + md5String.append(String.format("%02x", b)); + } + String md5 = md5String.toString(); + return new FileInfoDTO(StrUtil.isBlank(md5) ? "" : md5, fileExtension, fileSize); + } finally { + connection.disconnect(); + } + } catch (MalformedURLException e) { + logger.error("错误:提供的 URL 格式不正确,请检查 URL 字符串", e); + } catch (NoSuchAlgorithmException e) { + logger.error("错误:系统不支持 MD5 算法,这可能是 Java 环境的问题", e); + } catch (IOException e) { + logger.error("错误:在连接 URL 或读取文件内容时发生 I/O 异常,可能是网络问题或文件不存在", e); + } + return null; + } + /** * 生成的随机数类型 diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java index 5fe43aad..5673d391 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java @@ -3,14 +3,13 @@ package com.suisung.mall.common.utils; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.apache.tika.Tika; +import org.springframework.util.Base64Utils; +import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.net.URLEncoder; +import java.net.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -25,14 +24,17 @@ public class UploadUtil { * token通过header传递的名称 */ public static final String TOKEN_HEADER_NAME = "Authorization"; - - // 除 text/* 外也需要设置输出编码的 content-type private final static List SET_CHARSET_CONTENT_TYPES = Arrays.asList( "application/json", "application/javascript" ); + public static void main(String[] args) { + String base64 = fileUrlToBase64("http://gips3.baidu.com/it/u=1022347589,1106887837&fm=3028&app=3028&f=JPEG&fmt=auto?w=960&h=1280"); + System.out.println(base64); + } + /** * 查看文件, 支持断点续传 * @@ -269,11 +271,76 @@ public class UploadUtil { // inputStream 用完之后,记得关闭 return inputStream; } catch (IOException e) { - e.printStackTrace(); + log.error("发生 I/O 错误: ", e.getMessage()); return null; } } + /** + * 将URL地址转换为Base64字符串 + * + * @param fileUrl + * @return + */ + public static String fileUrlToBase64(String fileUrl) { + try { + URL url = new URL(fileUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + InputStream inputStream = connection.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + byte[] imageBytes = outputStream.toByteArray(); + inputStream.close(); + outputStream.close(); + + return Base64Utils.encodeToString(imageBytes); + } + } catch (IOException e) { + // 可以在这里添加日志记录,方便调试,这里简单打印异常信息 + log.error("处理图片 URL 时出现 IOException: ", e.getMessage()); + } + return null; + } + + public static String fileToBase64(File file) { + if (file == null) { + return null; + } + + try { + byte[] bytes = Files.readAllBytes(file.toPath()); + return Base64Utils.encodeToString(bytes); + } catch (IOException e) { + // 可以在这里添加日志记录,方便调试,这里简单打印异常信息 + log.error("处理file时出现 IOException: ", e.getMessage()); + } + return null; + } + + /** + * 将MultipartFile 图片转换为Base64字符串 + * + * @param multipartFile + * @return + */ + public static String multipartFileToBase64(MultipartFile multipartFile) { + try { + return Base64Utils.encodeToString(multipartFile.getBytes()); + } catch (IOException e) { + // 可以在这里添加日志记录,方便调试,这里简单打印异常信息 + log.error("处理图片 URL 时出现 IOException: ", e.getMessage()); + } + return null; + } + /** * 下载图片到本地 * @@ -317,5 +384,57 @@ public class UploadUtil { return path.substring(lastIndex + 1); } + /** + * 将 base64UrlSafeString Base64字符串转换为文件 + * + * @param base64UrlSafeString + * @return + */ + public static File convertBase64ToFile(String base64UrlSafeString) { + // 输入校验 + if (base64UrlSafeString == null || base64UrlSafeString.isEmpty()) { + log.error("Base64字符串不能为空"); + return null; + // throw new IllegalArgumentException("Base64字符串不能为空"); + } + + try { + // 1. 替换URL安全字符为标准Base64字符 + String standardBase64 = base64UrlSafeString.replace('-', '+').replace('_', '/'); + + // 2. 补齐缺失的填充字符 '=' + while (standardBase64.length() % 4 != 0) { + standardBase64 += '='; + } + + // 3. 解码为字节数组 + byte[] decodedBytes = Base64Utils.decodeFromString(standardBase64); + + // 4. 创建临时文件(自动生成唯一文件名) + File tempFile = File.createTempFile("base64-", ".tmp"); + + // 5. 设置JVM退出时自动删除(可选) + tempFile.deleteOnExit(); + + // 6. 写入文件内容 + try (FileOutputStream fos = new FileOutputStream(tempFile)) { + fos.write(decodedBytes); + } + + return tempFile; + + } catch (IllegalArgumentException e) { + // 处理Base64解码失败 +// throw new IllegalArgumentException("无效的Base64字符串: " + e.getMessage(), e); + log.error("无效的Base64字符串: " + e.getMessage()); + return null; + } catch (IOException e) { + // 处理文件操作异常 +// throw new RuntimeException("临时文件创建失败: " + e.getMessage(), e); + log.error("临时文件创建失败: " + e.getMessage()); + return null; + } + } + } diff --git a/mall-gateway/src/main/resources/application.yml b/mall-gateway/src/main/resources/application.yml index 237ccd9b..f9e85cd1 100644 --- a/mall-gateway/src/main/resources/application.yml +++ b/mall-gateway/src/main/resources/application.yml @@ -69,29 +69,29 @@ secure: - "/admin/account/account-user-base/register" - "/admin/account/account-user-base/login" - "/static/image/**" - - "/mobile/pay/index/notify_url" - #- "/mobile/pay/index/lkl_wxPay_notify_url" #拉卡拉微信支付回调 - - "/mobile/pay/index/return_url" - "/shop/static/**" - - "/mobile/shop/qrcode/getQrcode" - - "/admin/shop/shop-base-config/image" + - "/admin/account/open/**" - "/admin/account/account-user-base/doLogin" + #- "/mobile/pay/index/lkl_wxPay_notify_url" #拉卡拉微信支付回调 + - "/mobile/pay/index/notify_url" + - "/mobile/pay/index/return_url" - "/admin/pay/pay-user-resource/userInfoImport" - "/admin/pay/pay-card-history/payCardHistoryImport" + - "/admin/pay/payController/wxRefundNotify" + - "/mobile/shop/qrcode/getQrcode" - "/admin/shop/shop-purchase-invoice/impPurchaseInvoiceTemp" - "/admin/shop/shop-product-base/impProductTemp" - - "/admin/pay/payController/wxRefundNotify" + - "/admin/shop/shop-base-config/image" + - "/mobile/shop/sf-express/order/status/listening/**" + - "/admin/shop/open/**" + - "/admin/shop/esign/async/notify" #E签宝电子签章异步回调 - "/shop/sf-express/cancel-order/notify" - "/shop/sf-express/rider-order-status/notify" - "/shop/sf-express/order-complete/notify" - "/shop/sync/third/**" - - "/mobile/shop/sf-express/order/status/listening/**" - - "/admin/shop/open/**" - - "/admin/account/open/**" - "/esProduct/**" - "/admin/oss/upload/**" - "/mobile/**/**/test/case" - - "/admin/shop/esign/async/notify" #E签宝电子签章异步回调 universal: urls: - "/admin/account/account-user-base/info" diff --git a/mall-pay/pom.xml b/mall-pay/pom.xml index c491acd1..44ace36e 100644 --- a/mall-pay/pom.xml +++ b/mall-pay/pom.xml @@ -1,5 +1,5 @@ - 4.0.0 @@ -161,6 +161,7 @@ org.springframework.boot spring-boot-maven-plugin + true diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java index 7c7c0e2b..b553420a 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java @@ -103,6 +103,10 @@ public class LakalaPayServiceImpl implements LakalaPayService { return true; } + protected boolean isProdProject() { + return "prod".equalsIgnoreCase(profile); + } + @Override public cn.hutool.json.JSONObject transPreOrder(HttpServletRequest request, HttpServletResponse response, String orderId) { // 1. 配置初始化 @@ -419,19 +423,19 @@ public class LakalaPayServiceImpl implements LakalaPayService { String fileName = paramsJSON.getStr("splitEntrustFileName"); req.setSplitEntrustFileName(fileName); - // 文件上传到拉卡拉服务器 + // 分账结算委托书文件上传到拉卡拉服务器 JSONObject fileUploadResp = uploadFile(orderNo, "SPLIT_ENTRUST_FILE", StringUtils.getFileExt(fileName), paramsJSON.getStr("splitEntrustFile")); if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) { throw new ApiException(I18nUtil._("分账结算委托书上传失败!")); } String splitEntrustFilePath = fileUploadResp.getStr("attFileId"); - req.setSplitEntrustFilePath(splitEntrustFilePath); -// req.setSplitEntrustFilePath("G1/M00/06/64/CrFdEmBQc-aAGc_XAAAiIbS3WIE960.pdf"); + req.setSplitEntrustFilePath(splitEntrustFilePath); //比如:G1/M00/06/64/CrFdEmBQc-aAGc_XAAAiIbS3WIE960.pdf; - if (profile.equals("prod")) { + if (isProdProject()) { projectDomain = projectDomain + "/api"; } + // 给拉卡拉通知的回调地址 String retUrl = projectDomain + "/mobile/pay/lakala/ledger/applyLedgerMerNotify"; req.setRetUrl(retUrl); @@ -445,11 +449,10 @@ public class LakalaPayServiceImpl implements LakalaPayService { //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 (lakalaRespJSON == null || !lakalaRespJSON.getStr("retCode").equals("000000")) { throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg"))); -// responseStr="{'retCode':'000000','retMsg':'申请已受理,请等待审核结果','respData':{'version':'1.0','orderNo':'KFPT20230223181025407788734','orgCode':'1','applyId':681201215598657536}}"; -// lakalaRespJSON = JSONUtil.parseObj(responseStr); } paramsJSON.set("apply_id", lakalaRespJSON.getByPath("respData.applyId")); @@ -464,7 +467,7 @@ public class LakalaPayServiceImpl implements LakalaPayService { return CommonResult.success(null, "提交成功,待审核中!"); } catch (SDKException e) { log.error("分账申请失败:", e); - throw new ApiException(I18nUtil._("申请失败!"), e); + throw new ApiException(I18nUtil._("分账申请失败!"), e); } } @@ -503,6 +506,7 @@ public class LakalaPayServiceImpl implements LakalaPayService { reqData.getStr("auditStatus"), reqData.getStr("auditStatusText"), reqData.getStr("remark")); + if (success) { respData.put("retCode", "000000"); respData.put("retMsg", "操作成功!"); diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java b/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java index 0347ebb3..00ffa9d7 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/utils/LakalaUtil.java @@ -11,7 +11,6 @@ package com.suisung.mall.pay.utils; import cn.hutool.core.util.StrUtil; import com.lkl.laop.sdk.Config2; import com.lkl.laop.sdk.LKLSDK; -import com.lkl.laop.sdk.auth.PrivateKeySigner; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.utils.I18nUtil; import lombok.extern.slf4j.Slf4j; @@ -76,6 +75,7 @@ public class LakalaUtil { } } + /** * 获取 body 请求数据 * @@ -130,7 +130,6 @@ public class LakalaUtil { } } - /** * 签名验证 * @@ -155,9 +154,9 @@ public class LakalaUtil { } } - /** * 签名校验 + * * @param authorization * @param reqBody * @param lklNotifyCerPath @@ -176,11 +175,9 @@ public class LakalaUtil { return false; } - StringBuilder preSignData = new StringBuilder(); - preSignData.append(timestamp).append("\n") - .append(nonceStr).append("\n") - .append(reqBody).append("\n"); - String preSignDataStr = preSignData.toString(); + String preSignDataStr = timestamp + "\n" + + nonceStr + "\n" + + reqBody + "\n"; log.debug("拉卡拉签名明文内容:{}", preSignDataStr); if (verify(lklNotifyCer, preSignDataStr.getBytes(StandardCharsets.UTF_8), signature)) { log.debug("验签成功"); @@ -237,4 +234,5 @@ public class LakalaUtil { return map; } + } diff --git a/mall-pay/src/main/resources/bootstrap-prod.yml b/mall-pay/src/main/resources/bootstrap-prod.yml index ec36094c..e0f6e1fb 100644 --- a/mall-pay/src/main/resources/bootstrap-prod.yml +++ b/mall-pay/src/main/resources/bootstrap-prod.yml @@ -115,7 +115,7 @@ project: domain: @project.domain@ #拉卡拉支付和分账 lakala: - # 服务地址 + #服务地址 server_url: https://s2.lakala.com #应用Id app_id: OP10000439 @@ -127,7 +127,7 @@ lakala: api_pri_key_path: payKey/lakala/prod/api_private_key.pem #拉卡拉平台证书 lkl_platform_cer_path: payKey/lakala/prod/lkl_platform.cer - # 机构代码 + #机构代码 org_code: 980688 #商户号 merchant_no: 8226330599900LN diff --git a/mall-shop/pom.xml b/mall-shop/pom.xml index c6b9b127..49369c3e 100644 --- a/mall-shop/pom.xml +++ b/mall-shop/pom.xml @@ -278,12 +278,30 @@ 9.4.1.jre8 + + + com.lkl.laop.sdk + lkl-laop-java-sdk + 1.0.7 + ${project.basedir}/src/main/resources/lib/lkl-java-sdk-1.0.7.jar + system + + + + + com.github.javen205 + IJPay-Core + 2.8.0 + compile + + com.microsoft.sqlserver mssql-jdbc 9.2.1.jre8 + @@ -345,6 +363,11 @@ bootstrap.yml bootstrap-${profiles.active}.yml **/*.xml + **/*.crt + **/*.pem + **/*.p12 + **/*.cer + **/*.txt @@ -353,6 +376,10 @@ org.springframework.boot spring-boot-maven-plugin + + + true + com.spotify @@ -365,6 +392,11 @@ ttf ttc + p12 + cer + pem + pfx + txt diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/IpUtil.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/IpUtil.java index 576411e9..21c13e73 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/components/IpUtil.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/IpUtil.java @@ -29,11 +29,16 @@ public class IpUtil implements ApplicationRunner { log.error("IP2RegionUtils 没有成功加载数据文件"); return null; } + + if (CheckUtil.isEmpty(ip)) { + return null; + } + try { return searcher.search(ip); } catch (Exception e) { - log.error("IP 格式错误:{}", e); - return null; + log.error("IP:{} 格式错误:{}", ip, e.getMessage()); + return ip; } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java index 222412c1..e71b6a37 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java @@ -13,6 +13,7 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.service.impl.BaseControllerImpl; import com.suisung.mall.shop.esign.service.EsignContractFillingFileService; import com.suisung.mall.shop.esign.service.EsignContractService; +import com.suisung.mall.shop.esign.service.EsignPlatformInfoService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.http.ResponseEntity; @@ -36,11 +37,14 @@ public class EsignController extends BaseControllerImpl { @Resource private EsignContractService esignContractService; + @Resource + private EsignPlatformInfoService esignPlatformInfoService; + @ApiOperation(value = "测试填充模版控件", notes = "测试填充模版控件") @RequestMapping(value = "/testcase", method = RequestMethod.POST) public Object testCase() { //return esignContractFillingFileService.fillDocTemplate("13128997057", "91450881MADEQ92533"); - return esignContractService.getSignedContractFileUrl("27e8dad5491d4d9ab4bc6f8154ae8ff5"); + return esignPlatformInfoService.getDistributorAndPlatformByIds(1L); } @ApiOperation(value = "管理员发起签署电子合同流程", notes = "基于文件发起签署电子合同") diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java index 488f40f7..41b7afe1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java @@ -26,6 +26,14 @@ public interface EsignContractService { */ CommonResult signFlowCreateByFile(String mchMobile); + /** + * 内部调用:发起合同签署流程 + * + * @param mchMobile + * @return + */ + Pair innerSignFlowCreateByFile(String mchMobile); + /** * 签署流程结束异步通知(由e签宝通知) * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignPlatformInfoService.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignPlatformInfoService.java index 538e2ae8..80582157 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignPlatformInfoService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignPlatformInfoService.java @@ -11,6 +11,8 @@ package com.suisung.mall.shop.esign.service; import com.suisung.mall.common.modules.esign.EsignPlatformInfo; import org.springframework.data.util.Pair; +import java.util.List; + /** * 平台方详细信息接口 */ @@ -24,14 +26,29 @@ public interface EsignPlatformInfoService { */ EsignPlatformInfo getDistributorInfoById(Long id); + /** + * 根据ID获取代理商信息 + * + * @param ids + * @return + */ + List getDistributorAndPlatformByIds(Long... ids); + /** * 根据分类和营业执照号获取平台方信息 * - * @param type 分类(必选):类型:0-平台方(只能一条记录);1-一级代理;2-二级代理;3-三级代理;4-四级代理; + * @param level 分类(必选):类型:0-平台方(只能一条记录);1-一级代理;2-二级代理;3-三级代理;4-四级代理; * @param licenseNumber 营业执照号码(可选) * @return */ - EsignPlatformInfo getEsignPlatformInfo(Integer type, String licenseNumber); + EsignPlatformInfo getEsignPlatformInfo(Integer level, String licenseNumber); + + /** + * 是否存在平台方的相关记录信息? + * + * @return + */ + EsignPlatformInfo hasPlatformMch(); /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java index 85865d9a..88ba9d90 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java @@ -20,7 +20,7 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.modules.esign.EsignContractFillingFile; import com.suisung.mall.common.modules.esign.EsignPlatformInfo; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; +import com.suisung.mall.common.modules.store.ShopMchEntry; import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.esign.mapper.EsignContractFillingFileMapper; @@ -32,7 +32,7 @@ import com.suisung.mall.shop.esign.utils.comm.EsignHttpResponse; import com.suisung.mall.shop.esign.utils.enums.EsignRequestType; import com.suisung.mall.shop.esign.utils.exception.EsignDemoException; import com.suisung.mall.shop.page.service.OssService; -import com.suisung.mall.shop.store.service.ShopMerchEntryService; +import com.suisung.mall.shop.store.service.ShopMchEntryService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; @@ -70,7 +70,7 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl() {{ put("componentKey", "mch_company17"); - put("componentValue", shopMerchEntry.getContact_name()); + put("componentValue", shopMchEntry.getContact_name()); }}); } } @@ -234,7 +234,7 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl() {{ put("componentKey", "mch_store_name1"); - put("componentValue", shopMerchEntry.getStore_name()); + put("componentValue", shopMchEntry.getStore_name()); }}); list.add(new HashMap() {{ @@ -244,27 +244,27 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl() {{ put("componentKey", "mch_ratio"); - put("componentValue", shopMerchEntry.getSplit_ratio()); + put("componentValue", shopMchEntry.getSplit_ratio()); }}); list.add(new HashMap() {{ put("componentKey", "settlement_method"); - put("componentValue", shopMerchEntry.getSettlement_method()); + put("componentValue", shopMchEntry.getSettlement_method()); }}); list.add(new HashMap() {{ put("componentKey", "mch_address1"); - put("componentValue", shopMerchEntry.getStore_address()); + put("componentValue", shopMchEntry.getStore_address()); }}); list.add(new HashMap() {{ put("componentKey", "mch_bank1"); - put("componentValue", shopMerchEntry.getBank_name()); + put("componentValue", shopMchEntry.getBank_name()); }}); list.add(new HashMap() {{ put("componentKey", "mch_account_number1"); - put("componentValue", shopMerchEntry.getAccount_number()); + put("componentValue", shopMchEntry.getAccount_number()); }}); // 乙方公司名称 @@ -407,9 +407,9 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl ret = innerSignFlowCreateByFile(mchMobile); + if (!ret.getFirst()) { + return CommonResult.failed(ret.getSecond()); + } -// UserDto user = getCurrentUser(); -// if (!user.isAdmin()) { -// return CommonResult.failed("权限不足!"); -// } -// userId = user.getId().toString(); + return CommonResult.success(null, ret.getSecond()); + } + + @Override + public Pair innerSignFlowCreateByFile(String mchMobile) { + //String userId = "0"; if (StrUtil.isBlank(mchMobile)) { - return CommonResult.failed("缺少必要参数!"); + return Pair.of(false, "缺少必要参数!"); } EsignContract esignContract = getEsignContractByMchMobile(mchMobile); if (esignContract == null) { - return CommonResult.failed("未找到商家合同信息"); + return Pair.of(false, "未找到商家合同信息"); } // 检查商户入驻信息是否被审核通过 // 检查店铺是否已经申请过入驻 - Integer apprStatus = shopMerchEntryService.getApprovalStatus(mchMobile); + Integer apprStatus = shopMchEntryService.getApprovalStatus(mchMobile); if (!CommonConstant.MCH_APPR_STA_PASS.equals(apprStatus)) { - return CommonResult.failed("请先审核商家入驻信息"); + return Pair.of(false, "请先审核商家入驻信息"); } - //"{\"docs\":[{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"fileName\":\"平台商户入驻合同协议.pdf\"}],\"signFlowConfig\":{\"signFlowTitle\":\"平台商户入驻合同协议\",\"signFlowExpireTime\":1746844718000,\"autoFinish\":true,\"notifyUrl\":\"https://mall.gpxscs.cn/asyn/notify\",\"redirectConfig\":{\"redirectUrl\":\"https://mall.gpxscs.cn/\"}},\"signers\":[{\"signConfig\":{\"signOrder\":1},\"noticeConfig\":{\"noticeTypes\":\"1\"},\"signerType\":0,\"psnSignerInfo\":{\"psnAccount\":\"13128997057\",\"psnInfo\":{\"psnName\":\"潘军杰\"}},\"signFields\":[{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":40,\"positionX\":472.3607,\"positionY\":277.19104}}},{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":5,\"positionX\":470.58798,\"positionY\":589.14496}}}]},{\"signConfig\":{\"signOrder\":2},\"noticeConfig\":{\"noticeTypes\":\"1\"},\"signerType\":1,\"orgSignerInfo\":{\"orgName\":\"桂平发发网络有限公司\",\"orgInfo\":{\"orgIDCardNum\":\"91450881MADEQ92533\",\"orgIDCardType\":\"CRED_ORG_USCC\"},\"transactorInfo\":{\"psnAccount\":\"17777525395\",\"psnInfo\":{\"psnName\":\"谢能坤\"}}},\"signFields\":[{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":2,\"positionX\":479.04996,\"positionY\":357.2327}}},{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":5,\"positionX\":255.96832,\"positionY\":588.4553}}}]}]}"; + //"{\"docs\":[{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"fileName\":\"小发同城平台商户合作协议.pdf\"}],\"signFlowConfig\":{\"signFlowTitle\":\"小发同城平台商户合作协议\",\"signFlowExpireTime\":1746844718000,\"autoFinish\":true,\"notifyUrl\":\"https://mall.gpxscs.cn/asyn/notify\",\"redirectConfig\":{\"redirectUrl\":\"https://mall.gpxscs.cn/\"}},\"signers\":[{\"signConfig\":{\"signOrder\":1},\"noticeConfig\":{\"noticeTypes\":\"1\"},\"signerType\":0,\"psnSignerInfo\":{\"psnAccount\":\"13128997057\",\"psnInfo\":{\"psnName\":\"潘军杰\"}},\"signFields\":[{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":40,\"positionX\":472.3607,\"positionY\":277.19104}}},{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":5,\"positionX\":470.58798,\"positionY\":589.14496}}}]},{\"signConfig\":{\"signOrder\":2},\"noticeConfig\":{\"noticeTypes\":\"1\"},\"signerType\":1,\"orgSignerInfo\":{\"orgName\":\"桂平发发网络有限公司\",\"orgInfo\":{\"orgIDCardNum\":\"91450881MADEQ92533\",\"orgIDCardType\":\"CRED_ORG_USCC\"},\"transactorInfo\":{\"psnAccount\":\"17777525395\",\"psnInfo\":{\"psnName\":\"谢能坤\"}}},\"signFields\":[{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":2,\"positionX\":479.04996,\"positionY\":357.2327}}},{\"fileId\":\"ab30d2c5600441f4a7daf512e4d69157\",\"normalSignFieldConfig\":{\"signFieldStyle\":1,\"signFieldPosition\":{\"positionPage\":5,\"positionX\":255.96832,\"positionY\":588.4553}}}]}]}"; // esignContractService. String jsonParams = esignContract.getReq_params(); @@ -196,30 +211,36 @@ public class EsignContractServiceImpl extends BaseServiceImpl { + // 1、(电子合同)给商家申请分账功能使用;务必检查是否申请过?申请过忽略 + Pair retPair = lakalaApiService.innerApplyLedgerMer(esignContract.getMch_mobile()); + if (!retPair.getFirst()) { + log.error("商家申请分账功能失败:{}", retPair.getSecond()); + } - Pair retPair = shopStoreBaseService.merchEntryInfo2StoreInfo(esignContract.getMch_mobile()); - if (retPair.getFirst() > 0) { - // 更改合同记录表的店铺id - updateContractStoreId(esignContract.getMch_mobile(), retPair.getFirst()); - // 填充合同模版表的店铺Id - esignContractFillingFileService.updateContractFillingStoreId(esignContract.getMch_mobile(), retPair.getFirst()); - // 店铺创建状态已完成 - shopMerchEntryService.updateMerchEntryStoreStatus(esignContract.getMch_mobile(), CommonConstant.Enable); - } - - }); + // 更新商家的hasEsigned状态=1 + shopMchEntryService.updateMulStatus(esignContract.getMch_mobile(), "", 1, 0, 0, 0); return new ResponseEntity<>(new JSONObject().put("code", 200).put("msg", "success").toString(), HttpStatus.OK); } @@ -499,7 +514,7 @@ public class EsignContractServiceImpl extends BaseServiceImpl { log.debug("###更改同步合同审核状态和下载地址###"); // 更改同步合同审核状态和下载地址 - if (!shopMerchEntryService.updateMerchEntrySignedStatusAndContractDownloadUrl(esignContract.getMch_mobile(), signFlowStatus, localFileUrl)) { + if (!shopMchEntryService.updateMerchEntrySignedStatusAndContractDownloadUrl(esignContract.getMch_mobile(), signFlowStatus, localFileUrl)) { log.error("###更改同步合同审核状态和下载地址失败###"); } }); @@ -527,8 +542,8 @@ public class EsignContractServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("id", id).gt("type", 0).eq("status", CommonConstant.Enable); + queryWrapper.eq("id", id) + .gt("level", 0) + .eq("status", CommonConstant.Enable).orderByAsc("level"); + List esignPlatformInfos = list(queryWrapper); if (CollectionUtil.isEmpty(esignPlatformInfos)) { return null; @@ -47,21 +50,50 @@ public class EsignPlatformInfoServiceImpl extends BaseServiceImpl getDistributorAndPlatformByIds(Long... ids) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("status", CommonConstant.Enable).orderByAsc("level"); + if (ids != null && ids.length > 0) { + queryWrapper.and(wrapper -> { + wrapper.in("id", ids).gt("level", 0); + wrapper.or(wrapperOr -> { + wrapperOr.eq("level", 0); + }); + }); + } else { + queryWrapper.eq("level", 0); + } + + List esignPlatformInfos = list(queryWrapper); + if (CollectionUtil.isEmpty(esignPlatformInfos)) { + return null; + } + + return esignPlatformInfos; + } + /** * 根据分类和营业执照号获取平台方信息 * - * @param type 分类(可选):类型:0-平台方(只能一条记录);1-一级代理;2-二级代理;3-三级代理;4-四级代理; + * @param level 分类(可选):类型:0-平台方(只能一条记录);1-一级代理;2-二级代理;3-三级代理;4-四级代理; * @param licenseNumber 营业执照号码(可选) * @return */ @Override - public EsignPlatformInfo getEsignPlatformInfo(Integer type, String licenseNumber) { + public EsignPlatformInfo getEsignPlatformInfo(Integer level, String licenseNumber) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("status", CommonConstant.Enable); - if (!ObjectUtils.isEmpty(type)) { - queryWrapper.eq("type", type); + if (!ObjectUtils.isEmpty(level)) { + queryWrapper.eq("level", level); } else { - queryWrapper.eq("type", 0); + queryWrapper.eq("level", 0); } if (StrUtil.isNotBlank(licenseNumber)) { @@ -76,6 +108,24 @@ public class EsignPlatformInfoServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("level", 0).eq("status", CommonConstant.Enable); + + EsignPlatformInfo esignPlatformInfo = getOne(queryWrapper); + if (ObjectUtils.isEmpty(esignPlatformInfo)) { + return null; + } + + return esignPlatformInfo; + } + /** * 获取平台方手机号和营业执照号 * @@ -84,7 +134,7 @@ public class EsignPlatformInfoServiceImpl extends BaseServiceImpl getEsignPlatformMobileAndLicenseNumber() { QueryWrapper queryWrapper = new QueryWrapper(); - queryWrapper.eq("type", 0).select("telephone", "license_number", "legal_person_mobile"); + queryWrapper.eq("level", 0).select("telephone", "license_number", "legal_person_mobile"); List esignPlatformInfos = list(queryWrapper); if (CollectionUtil.isEmpty(esignPlatformInfos)) { return null; diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/LklTkController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LklTkAdminController.java similarity index 80% rename from mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/LklTkController.java rename to mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LklTkAdminController.java index a9f7d7fa..44bfb7cb 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/LklTkController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/admin/LklTkAdminController.java @@ -6,10 +6,10 @@ * Vestibulum commodo. Ut rhoncus gravida arcu. */ -package com.suisung.mall.shop.lakala.controller; +package com.suisung.mall.shop.lakala.controller.admin; import com.suisung.mall.common.service.impl.BaseControllerImpl; -import com.suisung.mall.shop.lakala.service.impl.CommonService; +import com.suisung.mall.shop.lakala.service.impl.LklTkServiceImpl; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.RequestMapping; @@ -21,21 +21,21 @@ import javax.annotation.Resource; @Api(tags = "拉卡拉商户进件控制器") @RestController @RequestMapping("/admin/shop/lakala/tk") -public class LklTkController extends BaseControllerImpl { +public class LklTkAdminController extends BaseControllerImpl { @Resource - private CommonService commonService; + private LklTkServiceImpl lklTkService; @ApiOperation(value = "请求获取token(商户进件)", notes = "请求获取token(商户进件)") @RequestMapping(value = "/token", method = RequestMethod.POST) public String getLklTkAuthorization() { - return commonService.getLklTkAuthorization(); + return lklTkService.getLklTkAuthorization(); } @ApiOperation(value = "请求获取token(商户进件)", notes = "请求获取token(商户进件)") @RequestMapping(value = "/token1", method = RequestMethod.POST) public String getLklTkAuthorization1() { - return commonService.getLklTkAuthorization(); + return lklTkService.getLklTkAuthorization(); } 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 new file mode 100644 index 00000000..bb67ced0 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java @@ -0,0 +1,106 @@ +/* + * 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.controller.mobile; + +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 io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Base64Utils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +@Api(tags = "拉卡拉相关接口 - 前端控制器") +@RestController +@RequestMapping("/mobile/shop/lakala") +public class LakalaController extends BaseControllerImpl { + + @Resource + private LakalaApiService lakalaPayService; + + @ApiOperation(value = "测试案例", notes = "测试案例") + @RequestMapping(value = "/testcase", method = RequestMethod.POST) + public Object testcase(@RequestBody JSONObject paramsJSON) { + return lakalaPayService.applyLedgerMerEc(paramsJSON.getStr("mchMobile")); +// return lakalaPayService.LedgerMerEcDownload(975790666910121984L); + } + + @ApiOperation(value = "本地文件转base64", notes = "本地文件转base64") + @RequestMapping(value = "/file2base64", method = RequestMethod.POST) + public String file2Base64(@RequestParam("file") MultipartFile file) throws IOException { + String str = Base64Utils.encodeToString(file.getBytes()); + return str; + } + + @ApiOperation(value = "获取银行卡的 BIN 信息", notes = "获取银行卡的 BIN 信息") + @RequestMapping(value = "/bankCardBin", method = RequestMethod.POST) + public JSONObject bankCardBin(@RequestBody JSONObject paramsJSON) { + return lakalaPayService.getBankCardBin(paramsJSON.getStr("bankCardNo")); + } + + @ApiOperation(value = "商户入网电子合同申请回调通知", notes = "商户入网电子合同申请回调通知") + @RequestMapping(value = "/ec/applyNotify", method = RequestMethod.POST) + public ResponseEntity ecApplyNotify(HttpServletRequest request) { + JSONObject resp = lakalaPayService.applyLedgerMerEcNotify(request); + if (resp != null && "SUCCESS".equals(resp.get("code"))) { + return new ResponseEntity<>(resp, HttpStatus.OK); + } + + return new ResponseEntity<>(resp, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ApiOperation(value = "商户分账业务开通申请", notes = "商户分账业务开通申请") + @RequestMapping(value = "/ledger/applyLedgerMer", method = RequestMethod.POST) + public CommonResult ledgerApplyLedgerMer(@RequestBody JSONObject paramsJSON) { + return lakalaPayService.applyLedgerMer(paramsJSON); + } + + @ApiOperation(value = "商户分账业务开通申请异步回调通知", notes = "商户分账业务开通申请异步回调通知") + @RequestMapping(value = "/ledger/applyLedgerMerNotify", method = RequestMethod.POST) + public ResponseEntity ledgerApplyLedgerMerNotify(HttpServletRequest request) { + JSONObject resp = lakalaPayService.applyLedgerMerNotify(request); + if (resp != null && "SUCCESS".equals(resp.get("code"))) { + return new ResponseEntity<>(resp, HttpStatus.OK); + } + + return new ResponseEntity<>(resp, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ApiOperation(value = "分账接收方创建申请", notes = "分账接收方创建申请") + @RequestMapping(value = "/ledger/applyLedgerReceiver", method = RequestMethod.POST) + public CommonResult applyLedgerReceiver(@RequestBody JSONObject paramsJSON) { + return lakalaPayService.applyLedgerReceiver(paramsJSON); + } + + @ApiOperation(value = "分账关系绑定申请", notes = "分账关系绑定申请") + @RequestMapping(value = "/ledger/applyBind", method = RequestMethod.POST) + public CommonResult applyBind(@RequestBody JSONObject paramsJSON) { + return lakalaPayService.applyLedgerMerReceiverBind(paramsJSON); + } + + // https://mall.gpxscs.cn/api/mobile/shop/lakala/ledger/applyLedgerMerReceiverBindNotify + @ApiOperation(value = "分账关系绑定申请异步回调通知", notes = "分账关系绑定申请异步回调通知") + @RequestMapping(value = "/ledger/applyLedgerMerReceiverBindNotify", method = RequestMethod.POST) + public ResponseEntity applyBindNotify(HttpServletRequest request) { + JSONObject resp = lakalaPayService.applyLedgerMerReceiverBindNotify(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/controller/mobile/LklTkController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LklTkController.java new file mode 100644 index 00000000..27341391 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LklTkController.java @@ -0,0 +1,97 @@ +/* + * 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.controller.mobile; + +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.service.impl.BaseControllerImpl; +import com.suisung.mall.shop.lakala.service.LklBanksService; +import com.suisung.mall.shop.lakala.service.impl.LklTkServiceImpl; +import com.suisung.mall.shop.lakala.utils.LakalaUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.data.util.Pair; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +@Api(tags = "拉卡拉商户进件控制器") +@RestController +@RequestMapping("/mobile/shop/lakala/tk") +public class LklTkController extends BaseControllerImpl { + + @Resource + private LklTkServiceImpl lklTkService; + + @Resource + private LklBanksService lklBanksService; + + @ApiOperation(value = "解密拉卡拉进件返回的异步通知", notes = "解密拉卡拉进件返回的异步通知") + @RequestMapping(value = "/decode", method = RequestMethod.POST) + public String decryptLklTkData(@RequestParam(name = "data") String data, + @RequestParam(name = "key") String key) { + + return LakalaUtil.decryptNotifyData(key, data); + + } + + @ApiOperation(value = "搜索国内银行(支行)分页列表", notes = "搜索国内银行(支行)分页列表,数据包含有效的收款结清行号") + @RequestMapping(value = "/bank/search", method = RequestMethod.POST) + public CommonResult searchLklBanksPageList(@RequestBody JSONObject paramsJSON) { +// Page list = lklBanksService.searchBranchBanksPageList(paramsJSON.getStr("keyword"), paramsJSON.getInt("pageNum"), paramsJSON.getInt("pageSize")); + IPage list = lklBanksService.pageBranchBanksList("", "", 2, paramsJSON.getStr("keyword"), paramsJSON.getInt("pageNum"), paramsJSON.getInt("pageSize")); + return CommonResult.success(list); + } + + @ApiOperation(value = "拉卡拉进件申请", notes = "拉卡拉进件申请") + @RequestMapping(value = "/registrationMerchant", method = {RequestMethod.POST, RequestMethod.GET}) + public CommonResult registrationMerchant(@RequestBody JSONObject paramsJSON) { + Pair resp = lklTkService.registrationMerchant(paramsJSON.getStr("mchMobile"), paramsJSON.getStr("bizLicenseNumber")); + if (resp.getFirst()) { + return CommonResult.success(null, resp.getSecond()); + } + + return CommonResult.failed(resp.getSecond()); + } + + // https://mall.gpxscs.cn/api/mobile/shop/lakala/ledger/applyLedgerMerReceiverBindNotify + @ApiOperation(value = "拉卡拉进件申请异步回调通知", notes = "拉卡拉进件申请异步回调通知") + @RequestMapping(value = "/registrationMerchantNotify", method = {RequestMethod.POST}) + public ResponseEntity registrationMerchantNotify(HttpServletRequest request) { + JSONObject resp = lklTkService.registrationMerchantNotify(request); + if (resp != null && resp.get("code").equals("200")) { + return new ResponseEntity<>(resp, HttpStatus.OK); + } + + return new ResponseEntity<>(resp, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ApiOperation(value = "上传文件", notes = "上传文件") + @RequestMapping(value = "/uploadOcrImg", method = RequestMethod.POST) + public CommonResult uploadOcrImg(@RequestParam(name = "upfile") MultipartFile file, + @RequestParam(name = "imgType") String imgType) { + + return lklTkService.uploadOcrImg(file, imgType); + } + + @ApiOperation(value = "获取(身份证、营业执照、身份证)图片的信息", notes = "获取(身份证、营业执照、身份证)图片的信息") + @RequestMapping(value = "/imgOcrResult", method = RequestMethod.POST) + public CommonResult imgOcrResult(@RequestParam(name = "batchNo") String batchNo, + @RequestParam(name = "imgType") String imgType) { + return lklTkService.imgOcrResult(batchNo, imgType); + } + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklBanksMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklBanksMapper.java new file mode 100644 index 00000000..c017aa46 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklBanksMapper.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.suisung.mall.common.modules.lakala.LklBanks; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + + +@Repository +public interface LklBanksMapper extends BaseMapper { + + IPage pageBranchBanksList(Page page, @Param("bankNo") String bankNo, @Param("branchBankNo") String branchBankNo, @Param("type") Integer type, @Param("keywords") List keywords); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerEcMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerEcMapper.java new file mode 100644 index 00000000..58542aba --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerEcMapper.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.lakala.LklLedgerEc; +import org.springframework.stereotype.Repository; + + +@Repository +public interface LklLedgerEcMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerMemberMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerMemberMapper.java new file mode 100644 index 00000000..0771b25b --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerMemberMapper.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.lakala.LklLedgerMember; +import org.springframework.stereotype.Repository; + + +@Repository +public interface LklLedgerMemberMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerMerReceiverBindMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerMerReceiverBindMapper.java new file mode 100644 index 00000000..0129a889 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerMerReceiverBindMapper.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind; +import org.springframework.stereotype.Repository; + + +@Repository +public interface LklLedgerMerReceiverBindMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerReceiverMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerReceiverMapper.java new file mode 100644 index 00000000..90703b25 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklLedgerReceiverMapper.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.lakala.LklLedgerReceiver; +import org.springframework.stereotype.Repository; + + +@Repository +public interface LklLedgerReceiverMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderSeparateMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderSeparateMapper.java new file mode 100644 index 00000000..0cc63266 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/mapper/LklOrderSeparateMapper.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.lakala.LklOrderSeparate; +import org.springframework.stereotype.Repository; + + +@Repository +public interface LklOrderSeparateMapper extends BaseMapper { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java new file mode 100644 index 00000000..602c77a3 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java @@ -0,0 +1,212 @@ +/* + * 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 cn.hutool.json.JSONObject; +import com.lkl.laop.sdk.request.V3SacsSeparateRequest; +import com.suisung.mall.common.api.CommonResult; +import org.springframework.data.util.Pair; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 拉卡拉业务接口 + */ +public interface LakalaApiService { + + Boolean initLKLSDK(); + + JSONObject transPreOrder(HttpServletRequest request, HttpServletResponse response, String orderId); + + /** + * 拉卡拉预下单 + * 参考:https://o.lakala.com/#/home/document/detail?id=110 + * + * @param merchantNo 商户号 + * @param termNo 终端号 + * @param xcxAppId 小程序appid + * @param openId openid + * @param orderId 订单号 + * @param subject 订单标题 + * @param totalAmount 订单金额 + * @param notifyURL 回调地址 + * @param requestIP 请求ip + * @param remark 备注 + * @return + */ + JSONObject transPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark); + + /** + * 聚合扫码-交易查询 + * + * @param storeId + * @param orderId + * @return + */ + JSONObject tradeQuery(Integer storeId, String orderId); + + /** + * 聚合扫码-退款交易 + * 主被扫交易发生后,因商家或消费者原因需要退款时,商家调用此接口退还消费者支付款项。 + * 注意: 1、调用退款接口时请保证商户/账户余额大于等于本次退款金额 + * + * @param storeId + * @param out_trade_no + * @param origin_trade_no // 原拉卡拉交易流水号 + * @param refund_amount 单位分,整数数字型字符 + * @param refund_reason + * @param requestIP + * @return + */ + JSONObject refund(Integer storeId, String out_trade_no, String origin_trade_no, String refund_amount, String refund_reason, String requestIP); + + /** + * 账户余额查询 + * 参考:https://o.lakala.com/#/home/document/detail?id=364 + * + * @param orgNo bmcp机构号 + * @param merchantNo 商户号 或 receiveNo 或 商户用户编号 + * @param payNo 账号(若该参数上送,则payType将无效) + * @param payType 账号类型(01:收款账户,02:付款账户,03:分账商户账户,04:分账接收方账户,05:充值代付账户,06:结算代付账户)- 未上送则默认为01 + * @return + */ + JSONObject ewalletBalanceQuery(String orgNo, String merchantNo, String payNo, String payType); + + /** + * 文件上传 + * 参考:https://o.lakala.com/#/home/document/detail?id=90 + * + * @param orderNo + * @param attType + * @param attExtName + * @param attContext + * @return + */ + JSONObject uploadFile(String orderNo, String attType, String attExtName, String attContext); + + /** + * 商家申请入网电子合同 + * + * @param mchMobile + * @return + */ + Pair applyLedgerMerEc(String mchMobile); + + + /** + * 商户分账业务开通申请 + * 参考:https://o.lakala.com/#/home/document/detail?id=379 + * + * @param paramsJSON + * @return + */ + CommonResult applyLedgerMer(JSONObject paramsJSON); + + /** + * 内部调用:商户分账业务开通申请 + * + * @param merCupNo + * @return + */ + Pair innerApplyLedgerMer(String merCupNo); + + /** + * 商户入网电子合同申请回调通知 + * 参考:https://o.lakala.com/#/home/document/detail?id=289 + * + * @param request + * @return + */ + JSONObject applyLedgerMerEcNotify(HttpServletRequest request); + + /** + * 商户入网电子合同下载 + * 参考:https://o.lakala.com/#/home/document/detail?id=294 + * + * @param ecApplyId + * @return + */ + Pair ledgerMerEcDownload(Long ecApplyId); + + /** + * 商户分账业务开通申请回调 + * 参考:https://o.lakala.com/#/home/document/detail?id=379 + * + * @param request + * @return + */ + JSONObject applyLedgerMerNotify(HttpServletRequest request); + + /** + * 分账接收方创建申请 + * 参考:https://o.lakala.com/#/home/document/detail?id=380 + * + * @param paramsJSON + * @return + */ + CommonResult applyLedgerReceiver(JSONObject paramsJSON); + + + /** + * 分账关系绑定申请 + * 参考:https://o.lakala.com/#/home/document/detail?id=386 + * + * @param paramsJSON + * @return + */ + CommonResult applyLedgerMerReceiverBind(JSONObject paramsJSON); + + + /** + * 分账关系绑定申请回调 + * 参考:https://o.lakala.com/#/home/document/detail?id=379 + * + * @param request + * @return + */ + JSONObject applyLedgerMerReceiverBindNotify(HttpServletRequest request); + + /** + * 获取银行卡信息 + * 参考:https://o.lakala.com/#/home/document/detail?id=387 + * + * @param bankCardNo + * @return + */ + JSONObject getBankCardBin(String bankCardNo); + + + /** + * 查询拉卡拉商户可分账的金额 + * 参考:https://o.lakala.com/#/home/document/detail?id=394 + * + * @param merchantNo 拉卡拉外部商户号 + * @param logNo 拉卡拉对账单流水号 + * @param logDate 拉卡拉对账单交易日期 yyyyMMdd + * @return 响应结果 { + * "merchant_no": "82229005943096D", + * "total_separate_amt": "9900", + * "can_separate_amt": "0", + * "log_date": "20221220", + * "log_no": "66210306990190" + * } + */ + JSONObject queryMchCanSplitAmt(String merchantNo, String logNo, String logDate); + + /** + * 拉卡拉订单分账,用户下单成功之后,进行分账 + * 说明:分账指令是异步处理模式,响应报文成功时,指令状态是”status”: “PROCESSING”,需要等待分账结果通知,或者主动发起查询,建议主动发起查询与分账指令动作之间间隔15秒以上。 + * 参考:https://o.lakala.com/#/home/document/detail?id=389 + * + * @param v3SacsSeparateRequest + * @return + */ + Pair innerDoOrderSeparate(V3SacsSeparateRequest v3SacsSeparateRequest); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklBanksService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklBanksService.java new file mode 100644 index 00000000..49adae14 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklBanksService.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; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.suisung.mall.common.modules.lakala.LklBanks; + +import java.util.Map; + +public interface LklBanksService { + + /** + * 根据关键字查询有效记录分页列表(不带地区) + * + * @param keyword + * @param pageNum + * @param pageSize + * @return + */ + Page searchBranchBanksPageList(String keyword, Integer pageNum, Integer pageSize); + + + /** + * 根据关键字分词进行分页查询支行列表(带省市地区) + * + * @param bankNo 总行号 + * @param branchBankNo 支行号 + * @param type 1-店铺地区;2-银行地区 + * @param keyword 查询关键字,做分词 + * @param pageNum + * @param pageSize + * @return + */ + IPage pageBranchBanksList(String bankNo, String branchBankNo, Integer type, String keyword, Integer pageNum, Integer pageSize); + + /** + * 根据支行号查询支行信息(带省市地区) + * + * @param branchBankNo 支行号 + * @return + */ + Map GetLklBankByBranchBankNo(String branchBankNo); +} 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 new file mode 100644 index 00000000..63afe7aa --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerEcService.java @@ -0,0 +1,53 @@ +/* + * 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.LklLedgerEc; +import com.suisung.mall.core.web.service.IBaseService; + +public interface LklLedgerEcService extends IBaseService { + + /** + * 根据商家Id新增或更新记录 + * + * @param record + * @return + */ + Boolean saveOrUpdateByMchId(LklLedgerEc record); + + /** + * 根据applyId更新记录 + * + * @param record + * @return + */ + Boolean updateByApplyId(LklLedgerEc record); + + /** + * 根据接applyId查询记录 + * + * @param applyId + * @param ecStatus + * @param status + * @return + */ + LklLedgerEc getByApplyId(Long applyId, String ecStatus, Integer status); + + /** + * 根据商户手机号查询记录 + * + * @param mchMobile + * @param ecStatus + * @param status + * @return + */ + LklLedgerEc getByMchMobile(String mchMobile, String ecStatus, Integer status); + + +} 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 new file mode 100644 index 00000000..29bfbda5 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMemberService.java @@ -0,0 +1,57 @@ +/* + * 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.LklLedgerMember; +import com.suisung.mall.core.web.service.IBaseService; + +public interface LklLedgerMemberService extends IBaseService { + + /** + * 根据银联商户号查询记录 + * + * @param merCupNo + * @return + */ + LklLedgerMember getByMerCupNo(String merCupNo); + + /** + * 根据申请单号和审核状态查询记录 + * + * @param applyId + * @param auditStatus + * @return + */ + LklLedgerMember getByApplyId(String applyId, Integer auditStatus); + + /** + * 根据银联商户号新增或修改记录 + * + * @param record + * @return + */ + Boolean saveOrUpdateByMerCupNo(LklLedgerMember record); + + + /** + * 更新审核结果 + * + * @param applyId + * @param merInnerNo + * @param merCupNo + * @param entrustFileName + * @param entrustFilePath + * @param auditStatus + * @param auditStatusText + * @param remark + * @return + */ + Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String entrustFileName, String entrustFilePath, String auditStatus, String auditStatusText, String remark); + +} 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 new file mode 100644 index 00000000..238f54f1 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerMerReceiverBindService.java @@ -0,0 +1,48 @@ +/* + * 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.LklLedgerMerReceiverBind; +import com.suisung.mall.core.web.service.IBaseService; + +public interface LklLedgerMerReceiverBindService extends IBaseService { + + /** + * 根据接收方编号新增或更新记录 + * + * @param record + * @return + */ + Boolean saveOrUpdateByMerCupNoReceiverNo(LklLedgerMerReceiverBind record); + + /** + * 根据接收方编号查询记录 + * + * @param merCupNo + * @param receiverNo + * @return + */ + LklLedgerMerReceiverBind getByCondition(String merCupNo, String receiverNo); + + /** + * 更新审核结果 + * + * @param applyId + * @param merInnerNo + * @param merCupNo + * @param receiverNo + * @param entrustFileName + * @param entrustFilePath + * @param auditStatus + * @param auditStatusText + * @param remark + * @return + */ + Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String receiverNo, String entrustFileName, String entrustFilePath, String auditStatus, String auditStatusText, String remark); +} 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 new file mode 100644 index 00000000..26886a3a --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklLedgerReceiverService.java @@ -0,0 +1,85 @@ +/* + * 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 cn.hutool.json.JSONArray; +import com.suisung.mall.common.modules.lakala.LklLedgerReceiver; +import com.suisung.mall.core.web.service.IBaseService; + +import java.util.List; + +public interface LklLedgerReceiverService extends IBaseService { + + /** + * 根据接收方编号新增或更新记录 + * + * @param record + * @return + */ + Boolean saveOrUpdateByReceiverNo(LklLedgerReceiver record); + + /** + * 根据接收方编号查询记录 + * + * @param receiverNo + * @return + */ + LklLedgerReceiver getByReceiverNo(String receiverNo); + + /** + * 通过平台方(代理商) ID 查询记录 + * + * @param platformId + * @return + */ + LklLedgerReceiver getByPlatformId(Long platformId); + + /** + * 通过平台方(代理商)记录信息构建申请分账接收方的请求参数 + * + * @param platformId + * @return + */ + JSONArray buildApplyLedgerReceiverReqParams(Long platformId); + + /** + * 内部调用:申请分账接收方 + * + * @param merCupNo + * @param platformId + * @return + */ + Boolean innerApplyLedgerReceiver(String merCupNo, Long platformId); + + /** + * 是否存在平台方的相关记录信息? + * + * @return + */ + LklLedgerReceiver hasPlatformMch(); + + /** + * 根据条件查询记录 + * + * @param LicenseNo + * @param ContactMobile + * @param platformId + * @return + */ + LklLedgerReceiver getByCondition(String LicenseNo, String ContactMobile, Long platformId); + + /** + * 根据条件查询记录 + * + * @param LicenseNo + * @param ContactMobile + * @return + */ + List getByCondition(String LicenseNo, String ContactMobile); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/CommonService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/CommonService.java deleted file mode 100644 index 61c3fb8f..00000000 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/CommonService.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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.collection.CollUtil; -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.suisung.mall.common.constant.CommonConstant; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; -import com.suisung.mall.common.utils.RestTemplateHttpUtil; -import com.suisung.mall.common.utils.StringUtils; -import com.suisung.mall.common.utils.UploadUtil; -import com.suisung.mall.core.web.service.RedisService; -import com.suisung.mall.shop.store.service.ShopMerchEntryService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.util.Pair; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -@Service -public class CommonService { - - private static final Logger log = LoggerFactory.getLogger(CommonService.class); - @Value("${lakala.tk.server_url}") - private String serverUrl; - - @Value("${lakala.tk.client_id}") - private String clientId; - - @Value("${lakala.tk.client_secret}") - private String clientSecret; - - @Value("${spring.profiles.active}") - private String profile; - - @Resource - private ShopMerchEntryService shopMerchEntryService; - - @Autowired - private RedisService redisService; - - protected String buildLklTkUrl(String urlPath) { - return serverUrl + urlPath; - } - - protected boolean isProd() { - return "prod".equalsIgnoreCase(profile); - } - - /** - * 请求获取token(商户进件) - * - * @return AuthorizationKey - */ - public String getLklTkAuthorization() { - String authorizationKey = "lkl_tk_authorization"; - Object lklTkToken = redisService.get(authorizationKey); - if (ObjectUtil.isNotEmpty(lklTkToken)) { - return lklTkToken.toString(); - } - - Map formData = new HashMap<>(); - formData.put("grant_type", "client_credentials"); - formData.put("client_id", clientId); - formData.put("client_secret", clientSecret); - - String response = RestTemplateHttpUtil.sendPostFormData(buildLklTkUrl("/oauth/token"), null, formData, String.class); - if (ObjectUtil.isEmpty(response)) { - return ""; - } - - JSONObject jsonObj = JSONUtil.parseObj(response); - if (jsonObj == null || StrUtil.isBlank(jsonObj.getStr("access_token"))) { - return ""; - } - - String token = jsonObj.getStr("token_type") + " " + jsonObj.getStr("access_token"); - redisService.set(authorizationKey, token, jsonObj.getLong("expires_in")); - - return token; - } - - /** - * (重要) 请求拉卡拉进件 - * - * @param mchMobile 商家手机号 - * @return - */ - public Pair registrationMerchant(String mchMobile) { - String authorization = getLklTkAuthorization(); - if (StrUtil.isBlank(authorization)) { - return Pair.of(false, "获取拉卡拉token失败"); - } - - JSONObject header = new JSONObject(); - header.put("Authorization", getLklTkAuthorization()); - - // 获取商家入驻信息,组成请求参数 - ShopMerchEntry shopMerchEntry = shopMerchEntryService.getShopMerchEntryByCondition(mchMobile, null, CommonConstant.MCH_APPR_STA_PASS); - if (ObjectUtil.isEmpty(shopMerchEntry)) { - return Pair.of(false, "商家入驻信息不存在"); - } - - JSONObject formData = new JSONObject(); - formData.put("userNo", "29153396"); - formData.put("email", mchMobile + "@163.com"); - formData.put("busiCode", "B2B_SYT"); - formData.put("merRegName", shopMerchEntry.getStore_name()); - - Boolean isQy = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMerchEntry.getEntity_type()); - String merType = isQy ? "TP_MERCHANT" : "TP_PERSONAL"; - formData.put("merType", merType); - - formData.put("merName", shopMerchEntry.getStore_name()); - formData.put("merAddr", StringUtils.removeProvinceCityDistrict(shopMerchEntry.getStore_address())); - - Map areaCode = getAreaCode(shopMerchEntry.getStore_area(), false); - if (ObjectUtil.isNotEmpty(areaCode)) { - formData.put("provinceCode", areaCode.get("provinceCode")); - formData.put("cityCode", areaCode.get("cityCode")); - formData.put("countyCode", areaCode.get("countyCode")); - } - - formData.put("longtude", shopMerchEntry.getStore_longitude()); //longitude 经度 - formData.put("latitude", shopMerchEntry.getStore_latitude()); - formData.put("source", "H5"); - - - formData.put("larIdType", "01"); - String larName = isQy ? shopMerchEntry.getLegal_person_name() : shopMerchEntry.getContact_name(); - String larIdCard = isQy ? shopMerchEntry.getLegal_person_id_number() : shopMerchEntry.getIndividual_id_number(); - String larIdCardStart = isQy ? shopMerchEntry.getLegal_person_id_period_begin() : shopMerchEntry.getIndividual_id_period_begin(); - String larIdCardEnd = isQy ? shopMerchEntry.getLegal_person_id_period_end() : shopMerchEntry.getIndividual_id_period_end(); - formData.put("larName", larName); - formData.put("larIdCard", larIdCard); - formData.put("larIdCardStart", larIdCardStart); // 身份证有效期开始时间 - formData.put("larIdCardEnd", larIdCardEnd); // 身份证有效期结束时间 - - formData.put("businessContent", shopMerchEntry.getSales_info()); - - // 营业执照信息 - if (isQy) { - formData.put("licenseName", shopMerchEntry.getBiz_license_company()); - formData.put("licenseNo", shopMerchEntry.getBiz_license_number()); - formData.put("licenseDtStart", shopMerchEntry.getBiz_license_period_begin()); - formData.put("licenseDtEnd", shopMerchEntry.getBiz_license_period_end()); - } - - formData.put("contactMobile", mchMobile); - formData.put("contactName", shopMerchEntry.getContact_name()); - - formData.put("openningBankCode", shopMerchEntry.getBank_code());//结算账户开户⾏号 - formData.put("openningBankName", shopMerchEntry.getBank_name());//结算账户开户⾏名称 - formData.put("clearingBankCode", shopMerchEntry.getClearing_bank_code());//结算账户清算⾏号 - - formData.put("accountNo", shopMerchEntry.getAccount_number()); //结算人银行卡号 - formData.put("accountName", shopMerchEntry.getAccount_holder_name()); //结算人账户名称 - formData.put("accountIdCard", shopMerchEntry.getLegal_person_id_number());//结算⼈证件号码(身份证) - - formData.put("settleType", "D1"); //结算类型,D0秒到,D1次日结算 - formData.put("settlementType", "AUTOMATIC"); // 结算方式:MANUAL:手动结算(结算至拉卡拉APP钱包),AUTOMATIC:自动结算到银行卡,REGULAR:定时结算(仅企业商户支持) - formData.put("accountType", isQy ? "57" : "58"); //结算账户类型: 57 对公 58 对私 - - //结算信息省份代码 - Map bankAreaCode = getAreaCode(shopMerchEntry.getBank_area(), true); - if (ObjectUtil.isNotEmpty(bankAreaCode)) { - formData.put("settleProvinceCode", bankAreaCode.get("provinceCode")); - formData.put("settleCityCode", bankAreaCode.get("cityCode")); - String[] bankAreaName = shopMerchEntry.getBank_area().split(","); - if (bankAreaName.length >= 2) { - formData.put("settleProvinceName", bankAreaName[0]); - formData.put("settleCityName", bankAreaName[1]); - } - } - - - JSONObject bizContent = new JSONObject(); - bizContent.put("activityId", 687); - bizContent.put("termNum", "1"); - bizContent.put("mcc", "1"); - bizContent.put("fees", new JSONArray() {{ - put(new JSONObject() {{ - put("feeCode", "WECHAT"); - put("feeValue", 0.38); - }}); - }}); - - formData.put("bizContent", bizContent); - - JSONArray attachments = new JSONArray(); - JSONObject ID_CARD_FRONT = updatePhoto(shopMerchEntry.getIndividual_id_images(), "ID_CARD_FRONT", true); - if (ID_CARD_FRONT != null) { - attachments.put(ID_CARD_FRONT); // 身份证正面 - } - - JSONObject ID_CARD_BEHIND = updatePhoto(shopMerchEntry.getIndividual_id_images2(), "ID_CARD_BEHIND", true); - if (ID_CARD_BEHIND != null) { - attachments.put(ID_CARD_BEHIND); // 身份证国徽面 - } - - JSONObject SETTLE_ID_CARD_FRONT = updatePhoto(shopMerchEntry.getLegal_person_id_images(), "SETTLE_ID_CARD_FRONT", true); - if (SETTLE_ID_CARD_FRONT != null) { - attachments.put(SETTLE_ID_CARD_FRONT); // 结算人身份证正面 - } - - JSONObject SETTLE_ID_CARD_BEHIND = updatePhoto(shopMerchEntry.getLegal_person_id_images2(), "SETTLE_ID_CARD_BEHIND", true); - if (SETTLE_ID_CARD_BEHIND != null) { - attachments.put(SETTLE_ID_CARD_BEHIND); // 结算人身份证国徽面 - } - - JSONObject BUSINESS_LICENCE = updatePhoto(shopMerchEntry.getBiz_license_image(), "BUSINESS_LICENCE", true); - if (BUSINESS_LICENCE != null) { - attachments.put(BUSINESS_LICENCE); // 营业执照 - } - - JSONObject SHOP_OUTSIDE_IMG = updatePhoto(shopMerchEntry.getFront_facade_image(), "SHOP_OUTSIDE_IMG", false); - if (SHOP_OUTSIDE_IMG != null) { - attachments.put(SHOP_OUTSIDE_IMG); // 门店门面图片 - } - - JSONObject SHOP_INSIDE_IMG = updatePhoto(shopMerchEntry.getEnvironment_image(), "SHOP_INSIDE_IMG", false); - if (SHOP_INSIDE_IMG != null) { - attachments.put(SHOP_INSIDE_IMG); // 门店内部图片 - } - - JSONObject BANK_CARD = updatePhoto(shopMerchEntry.getBank_image(), "BANK_CARD", true); - if (BANK_CARD != null) { - attachments.put(BANK_CARD); // 银行卡图片 - } - - formData.put("attchments", attachments); - - - String urlPath = "/sit/htkregistration/merchant"; - if (isProd()) { - urlPath = "/registration/merchant"; - } - - ResponseEntity response = RestTemplateHttpUtil.sendPostFormDataBackEntity(buildLklTkUrl(urlPath), header, formData, JSONObject.class); - if (ObjectUtil.isEmpty(response) || response.getStatusCode() != HttpStatus.OK) { - String errMsg = "请求分账系统出错!"; - if (ObjectUtil.isNotEmpty(response.getBody()) && ObjectUtil.isNotEmpty(response.getBody().getStr("message"))) { - errMsg = response.getBody().getStr("message"); - } - return Pair.of(false, "进件失败:" + errMsg); - } - - // 根据入驻商家的代理商或平台方,顺便新增一个接收方记录 - - - // TODO 新增 lkl_ledger_member 数据, 等待异步审核通知,更改状态 - String merchantNo = response.getBody().getStr("merchantNo"); //拉卡拉内部商户号 - return Pair.of(true, "进件成功!"); - } - - /** - * 获取拉卡拉省市区编码 - * - * @param areaName 省份/城市/乡县 - * @return - */ - public Map getAreaCode(String areaName, Boolean isBankArea) { - if (StrUtil.isBlank(areaName)) { - return new HashMap() {{ - put("provinceCode", ""); - put("cityCode", ""); - put("countyCode", ""); - }}; - } - - String[] areaNames = areaName.split("/"); - if (areaNames.length < 2) { - return new HashMap() {{ - put("provinceCode", ""); - put("cityCode", ""); - put("countyCode", ""); - }}; - } - - String provinceName = areaNames[0]; - String cityName = areaNames[1]; - String countryName = ""; - if (areaNames.length >= 3) { - countryName = areaNames[2]; - } - - String urlPath = "/organization"; - if (isBankArea) { - urlPath = "/organization/bank"; - } - String authorization = getLklTkAuthorization(); - if (StrUtil.isBlank(authorization)) { - log.error("获取拉卡拉token失败"); - return new HashMap() {{ - put("provinceCode", ""); - put("cityCode", ""); - put("countyCode", ""); - }}; - } - - JSONObject header = new JSONObject(); - header.put("Authorization", getLklTkAuthorization()); - - Map areaCodeMap = new HashMap<>(); - - // 省份列表 - ResponseEntity response = RestTemplateHttpUtil.sendGetWithHeader(buildLklTkUrl(urlPath + "/1"), header, JSONArray.class); - if (ObjectUtil.isNotEmpty(response) && response.getStatusCode() == HttpStatus.OK) { - String provinceCode = ""; - String cityCode = ""; - String countyCode = ""; - - JSONArray jsonArray = response.getBody(); - if (CollUtil.isNotEmpty(jsonArray)) { - for (JSONObject jsonObject : jsonArray.jsonIter()) { - if (StrUtil.contains(jsonObject.getStr("name"), provinceName)) { - provinceCode = jsonObject.getStr("code"); - areaCodeMap.put("provinceCode", provinceCode); - break; - } - } - - // 城市列表 - if (StrUtil.isNotBlank(provinceCode)) { - response = RestTemplateHttpUtil.sendGetWithHeader(buildLklTkUrl(urlPath + "/" + provinceCode), header, JSONArray.class); - jsonArray = response.getBody(); - if (CollUtil.isNotEmpty(jsonArray)) { - for (JSONObject jsonObject : jsonArray.jsonIter()) { - if (StrUtil.contains(jsonObject.getStr("name"), cityName)) { - cityCode = jsonObject.getStr("code"); - areaCodeMap.put("cityCode", cityCode); - break; - } - } - } - } - - - // 乡列表 - if (StrUtil.isNotBlank(cityCode)) { - response = RestTemplateHttpUtil.sendGetWithHeader(buildLklTkUrl(urlPath + "/" + cityCode), header, JSONArray.class); - jsonArray = response.getBody(); - if (CollUtil.isNotEmpty(jsonArray)) { - for (JSONObject jsonObject : jsonArray.jsonIter()) { - if (StrUtil.contains(jsonObject.getStr("name"), countryName)) { - countyCode = jsonObject.getStr("code"); - areaCodeMap.put("countyCode", countyCode); - break; - } - } - } - } - } - } - - - return areaCodeMap; - } - - public JSONObject updatePhoto(String fileUrl, String imgType, Boolean isOcr) { - if (StrUtil.isBlank(fileUrl)) { - return null; - } - - JSONObject header = new JSONObject(); - header.put("Authorization", getLklTkAuthorization()); - - JSONObject formData = new JSONObject(); - File file = UploadUtil.downloadImageFromUrl(fileUrl); - formData.put("file", file); - formData.put("imgType", imgType); - formData.put("sourcechnl", "0"); - formData.put("isOcr", isOcr); - - String urlPath = "/file/upload"; - ResponseEntity response = RestTemplateHttpUtil.sendPostFormDataBackEntity(buildLklTkUrl(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 jsonObject = new JSONObject(); - jsonObject.put("id", result.get("url")); - jsonObject.put("type", imgType); - return jsonObject; - } - -} 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 new file mode 100644 index 00000000..c3d0abd8 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -0,0 +1,1854 @@ +/* + * 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.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.ijpay.core.kit.IpKit; +import com.lkl.laop.sdk.LKLSDK; +import com.lkl.laop.sdk.exception.SDKException; +import com.lkl.laop.sdk.request.*; +import com.lkl.laop.sdk.request.model.V3LabsTradeLocationInfo; +import com.lkl.laop.sdk.request.model.V3LabsTradePreorderWechatBus; +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.store.ShopMchEntry; +import com.suisung.mall.common.modules.store.ShopStoreBase; +import com.suisung.mall.common.utils.*; +import com.suisung.mall.shop.lakala.service.*; +import com.suisung.mall.shop.lakala.utils.LakalaUtil; +import com.suisung.mall.shop.page.service.OssService; +import com.suisung.mall.shop.store.service.ShopMchEntryService; +import com.suisung.mall.shop.store.service.ShopStoreBaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.util.Pair; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + + +@Slf4j +@Service +public class LakalaApiServiceImpl implements LakalaApiService { + private static final boolean init = false; + private static final String lklSuccessCode = "000000"; + private static final String lklSacsSuccessCode = "SACS0000"; + // 可选的两个参数,不同的店铺商家,可以数据库里配置不同的商户号和终端号 + @Value("${lakala.merchant_no}") + public String merchantNo; // 拉卡拉分配的商户号 + @Value("${lakala.term_no}") + public String termNo; // 拉卡拉分配的终端号码 + + @Value("${lakala.server_url}") + private String serverUrl; //服务地址 + @Value("${lakala.app_id}") + private String appId; // 拉卡拉appId + @Value("${lakala.serial_no}") + private String serialNo; // 你的证书序列号 + @Value("${lakala.api_pri_key_path}") + private String priKeyPath; //商户私钥信息地址 + @Value("${lakala.lkl_platform_cer_path}") + private String lklCerPath; //拉卡拉支付平台证书地址 + @Value("${lakala.api_cert_path}") + private String apiCertPath; + @Value("${lakala.lkl_platform_cer_path}") + private String lklNotifyCerPath; //拉卡拉支付平台证书地址2(用于拉卡拉通知验签) + @Value("${lakala.org_code}") + private String orgCode; + @Value("${lakala.tk.split_lowest_ratio}") + private String splitLowestRatio; + @Value("${project.domain}") + private String projectDomain; + @Value("${spring.profiles.active}") + private String profile; + @Value("${lakala.is_prod}") + private Boolean isLklProd; + @Value("#{accountBaseConfigService.getConfig('tengxun_default_dir')}") + private String TENGXUN_DEFAULT_DIR; + + @Lazy + @Autowired + private ShopService shopService; + + @Autowired + private LklLedgerMemberService lklLedgerMemberService; + + @Autowired + private LklLedgerReceiverService lklLedgerReceiverService; + + @Autowired + private LklLedgerMerReceiverBindService lklLedgerMerReceiverBindService; + + @Lazy + @Resource + private ShopMchEntryService shopMchEntryService; + +// @Lazy +// @Resource +// private +// EsignContractService esignContractService; +// +// @Lazy +// @Resource +// private EsignContractFillingFileService esignContractFillingFileService; + + + @Lazy + @Resource + private ShopStoreBaseService shopStoreBaseService; + + @Lazy + @Autowired + private LklLedgerEcService lklLedgerEcService; + + @Lazy + @Resource + private LklTkServiceImpl lklTkService; + + @Resource + private OssService ossService; + + + /** + * 初始化 拉卡拉SDK + * + * @return + */ + @Override + public Boolean initLKLSDK() { + if (!init) { + return LakalaUtil.initLKLSDK(appId, serialNo, priKeyPath, lklCerPath, lklNotifyCerPath, serverUrl); + } + return true; + } + + protected boolean isProdProject() { +// return false; + // 正式发布时再启用 + return "prod".equalsIgnoreCase(profile); + } + + @Override + public JSONObject transPreOrder(HttpServletRequest request, HttpServletResponse response, String orderId) { + // 1. 配置初始化 + initLKLSDK(); + + //2. 装配数据 + /*** 微信主扫场景示例 */ + V3LabsTransPreorderRequest v3LabsTransPreorderWechatReq = new V3LabsTransPreorderRequest(); + v3LabsTransPreorderWechatReq.setMerchantNo(merchantNo); + v3LabsTransPreorderWechatReq.setTermNo(termNo); + v3LabsTransPreorderWechatReq.setOutTradeNo(orderId); + v3LabsTransPreorderWechatReq.setSubject("油麦菜5斤装特惠"); + v3LabsTransPreorderWechatReq.setAccountType("WECHAT"); + v3LabsTransPreorderWechatReq.setTransType("51"); + v3LabsTransPreorderWechatReq.setTotalAmount("1"); + v3LabsTransPreorderWechatReq.setNotifyUrl("http://mall.gpxscs.cn/notify"); + v3LabsTransPreorderWechatReq.setRemark("测试预下单备注"); + + //地址位置信息 + V3LabsTradeLocationInfo v3LabsTradePreorderLocationInfo1 = new V3LabsTradeLocationInfo(IpKit.getRealIp(request)); + v3LabsTransPreorderWechatReq.setLocationInfo(v3LabsTradePreorderLocationInfo1); + + //微信主扫场景下acc_busi_fields域内容 + V3LabsTradePreorderWechatBus wechatBus = new V3LabsTradePreorderWechatBus(); + wechatBus.setSubAppid("wx5a73f844dac0da5c"); // 小程序appId + wechatBus.setUserId("oDVKR7T0qxg6O8tqIL9SgY6LXqqQ"); // 微信 openId + wechatBus.setDeviceInfo("WEB"); // 终端设备号(门店号或收银设备ID),注意:PC网页或JSAPI支付请传”WEB” + v3LabsTransPreorderWechatReq.setAccBusiFields(wechatBus); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(v3LabsTransPreorderWechatReq); + if (StrUtil.isBlank(responseStr)) { + throw new ApiException(I18nUtil._("下单无响应!")); + } + + //4. 响应 + return JSONUtil.parseObj(responseStr); + } catch (SDKException e) { + LakalaApiServiceImpl.log.error("transPreOrder error", e); + throw new ApiException(I18nUtil._("获取公众号绑定信息失败!"), e); + } + + } + + /** + * 拉卡拉预下单 + * 参考:https://o.lakala.com/#/home/document/detail?id=110 + * + * @param merchantNo 商户号 + * @param termNo 终端号 + * @param xcxAppId 小程序appid + * @param openId openid + * @param orderId 订单号 + * @param subject 订单标题 + * @param totalAmount 订单金额 + * @param notifyURL 回调地址 + * @param requestIP 请求ip + * @param remark 备注 + * @return + */ + @Override + public JSONObject transPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark) { + // 1. 配置初始化 + initLKLSDK(); + + if (StrUtil.isBlank(merchantNo)) { + merchantNo = this.merchantNo; + } + if (StrUtil.isBlank(termNo)) { + termNo = this.termNo; + } + + //2. 装配数据 + /*** 微信主扫场景示例 */ + V3LabsTransPreorderRequest v3LabsTransPreorderWechatReq = new V3LabsTransPreorderRequest(); + v3LabsTransPreorderWechatReq.setMerchantNo(merchantNo); + v3LabsTransPreorderWechatReq.setTermNo(termNo); + v3LabsTransPreorderWechatReq.setOutTradeNo(orderId); + v3LabsTransPreorderWechatReq.setSubject(subject); + //微信:WECHAT 支付宝:ALIPAY 银联:UQRCODEPAY 翼支付: BESTPAY 苏宁易付宝: SUNING 拉卡拉支付账户:LKLACC 网联小钱包:NUCSPAY 京东钱包:JD + v3LabsTransPreorderWechatReq.setAccountType("WECHAT"); + // 41:NATIVE((ALIPAY,云闪付支持,京东白条分期)51:JSAPI(微信公众号支付,支付宝服务窗支付,银联JS支付,翼支付JS支付、拉卡拉钱包支付)71:微信小程序支付 61:APP支付(微信APP支付) + v3LabsTransPreorderWechatReq.setTransType("51"); + v3LabsTransPreorderWechatReq.setTotalAmount(totalAmount); + v3LabsTransPreorderWechatReq.setSettleType("1"); //“0”或者空,常规结算方式,如需接拉卡拉分账通需传“1”,商户未开通分账之前切记不用上送此参数。; + v3LabsTransPreorderWechatReq.setNotifyUrl(notifyURL); + v3LabsTransPreorderWechatReq.setRemark(remark); + + //地址位置信息 + V3LabsTradeLocationInfo v3LabsTradePreorderLocationInfo = new V3LabsTradeLocationInfo(requestIP); + v3LabsTransPreorderWechatReq.setLocationInfo(v3LabsTradePreorderLocationInfo); + + //微信主扫场景下 acc_busi_fields 域内容 + V3LabsTradePreorderWechatBus wechatBus = new V3LabsTradePreorderWechatBus(); + wechatBus.setSubAppid(xcxAppId); // 小程序appId + wechatBus.setUserId(openId); // 微信 openId + wechatBus.setDeviceInfo("WEB"); // 终端设备号(门店号或收银设备ID),注意:PC网页或JSAPI支付请传”WEB” + v3LabsTransPreorderWechatReq.setAccBusiFields(wechatBus); + + try { + log.info("拉卡拉预下单请求参数:{}", JSONUtil.toJsonStr(v3LabsTransPreorderWechatReq)); + + //3. 发送请求 + String responseStr = LKLSDK.httpPost(v3LabsTransPreorderWechatReq); + log.info("拉卡拉预下单响应数据:{}", responseStr); + if (StrUtil.isBlank(responseStr)) { + throw new ApiException(I18nUtil._("下单无响应!")); + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + + // || !lakalaRespJSON.getStr("code").equals("BBS00000") + + //4. 响应 + return lakalaRespJSON; + } catch (SDKException e) { + log.error("拉卡拉支付出错:", e); + throw new ApiException(I18nUtil._("支付失败!"), e); + } + } + + /** + * 聚合扫码-交易查询 + * + * @param storeId + * @param orderId + * @return + */ + @Override + public JSONObject tradeQuery(Integer storeId, String orderId) { + if (ObjectUtil.isEmpty(storeId) || StrUtil.isBlank(orderId)) { + return null; + } + + // 1. 配置初始化 + initLKLSDK(); + + // 这里获取店铺的拉卡拉的商户号和终端号码 + ShopStoreBase shopStoreBase = shopService.getLklMerchantNoAndTermNo(storeId); + if (shopStoreBase == null || StrUtil.isBlank(shopStoreBase.getLkl_merchant_no()) || StrUtil.isBlank(shopStoreBase.getLkl_term_no())) { + log.error("缺少参数,拉卡拉交易查询失败!"); + return null; + } + + //2. 装配数据 + V3LabsQueryTradequeryRequest v3LabsQueryTradequeryRequest = new V3LabsQueryTradequeryRequest(); + v3LabsQueryTradequeryRequest.setMerchantNo(shopStoreBase.getLkl_merchant_no()); + v3LabsQueryTradequeryRequest.setTermNo(shopStoreBase.getLkl_term_no()); + v3LabsQueryTradequeryRequest.setOutTradeNo(orderId); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(v3LabsQueryTradequeryRequest); + if (StrUtil.isBlank(responseStr)) { + throw new ApiException(I18nUtil._("交易查询无响应!")); + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + // || !lakalaRespJSON.getStr("code").equals("BBS00000") + + return lakalaRespJSON; + } catch (SDKException e) { + log.error("交易查询失败:", e); + throw new ApiException(I18nUtil._("交易查询失败!"), e); + } + } + + /** + * 聚合扫码-退款交易 + * 主被扫交易发生后,因商家或消费者原因需要退款时,商家调用此接口退还消费者支付款项。 + * 注意: 1、调用退款接口时请保证商户/账户余额大于等于本次退款金额 + * + * @param storeId + * @param out_trade_no + * @param origin_trade_no // 原拉卡拉交易流水号 + * @param refund_amount 单位分,整数数字型字符 + * @param refund_reason + * @param requestIP + * @return + */ + @Override + public JSONObject refund(Integer storeId, String out_trade_no, String origin_trade_no, String refund_amount, String refund_reason, String requestIP) { + if (ObjectUtil.isEmpty(storeId) || StrUtil.isBlank(out_trade_no) || StrUtil.isBlank(refund_amount) || StrUtil.isBlank(origin_trade_no) || StrUtil.isBlank(requestIP)) { + return null; + } + + // 1. 配置初始化 + initLKLSDK(); + + + // 这里获取店铺的拉卡拉的商户号和终端号码 + ShopStoreBase shopStoreBase = shopService.getLklMerchantNoAndTermNo(storeId); + if (shopStoreBase == null || StrUtil.isBlank(shopStoreBase.getLkl_merchant_no()) || StrUtil.isBlank(shopStoreBase.getLkl_term_no())) { + log.error("缺少参数,退款交易失败!"); + throw new ApiException(I18nUtil._("缺少必要参数!")); + } + + //2. 装配数据 + V3LabsRelationIdmrefundRequest req = new V3LabsRelationIdmrefundRequest(); + req.setOutRefundOrderNo(out_trade_no); + req.setMerchantNo(shopStoreBase.getLkl_merchant_no()); + req.setTermNo(shopStoreBase.getLkl_term_no()); + req.setRefundAmount(refund_amount); + req.setRefundReason(refund_reason); + req.setOriginTradeNo(origin_trade_no); + + V3LabsTradeLocationInfo v3LabsTradeLocationInfo = new V3LabsTradeLocationInfo(requestIP, null, ""); + req.setLocationInfo(v3LabsTradeLocationInfo); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + if (StrUtil.isBlank(responseStr)) { + throw new ApiException(I18nUtil._("退款交易失败!")); + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + + return lakalaRespJSON; + } catch (SDKException e) { + log.error("退款交易失败:", e); + throw new ApiException(I18nUtil._("退款交易!"), e); + } + } + + /** + * 账户余额查询 + * 参考:https://o.lakala.com/#/home/document/detail?id=364 + * + * @param orgNo bmcp机构号 测试 “1” + * @param merchantNo 商户号 或 receiveNo 或 商户用户编号 + * @param payNo 账号(若该参数上送,则payType将无效) + * @param payType 账号类型(01:收款账户,02:付款账户,03:分账商户账户,04:分账接收方账户,05:充值代付账户,06:结算代付账户)- 未上送则默认为01 + * @return + */ + @Override + public JSONObject ewalletBalanceQuery(String orgNo, String merchantNo, String payNo, String payType) { + if (StrUtil.isBlank(orgNo) || StrUtil.isBlank(merchantNo) || (StrUtil.isBlank(payNo) && StrUtil.isBlank(payType))) { + return null; + } + + // 1. 配置初始化 + initLKLSDK(); + + + //2. 装配数据 + V2LaepIndustryEwalletBalanceQueryRequest req = new V2LaepIndustryEwalletBalanceQueryRequest(); + req.setOrgNo(orgNo); + req.setMerchantNo(merchantNo); + req.setPayNo(payNo); + req.setPayType(payType); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + if (StrUtil.isBlank(responseStr)) { + throw new ApiException(I18nUtil._("账户余额查询无响应!")); + } + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + + return lakalaRespJSON; + } catch (SDKException e) { + log.error("账户余额查询失败:", e); + throw new ApiException(I18nUtil._("账户余额查询!"), e); + } + + } + + /** + * 文件上传 + * 参考:https://o.lakala.com/#/home/document/detail?id=90 + * + * @param orderNo + * @param attType + * @param attExtName + * @param attContext + * @return + */ + @Override + public JSONObject uploadFile(String orderNo, String attType, String attExtName, String attContext) { + // 1. 配置初始化 + initLKLSDK(); + + //2. 装配数据 + V2MmsOpenApiUploadFileRequest req = new V2MmsOpenApiUploadFileRequest(); + req.setVersion("2.0"); + req.setOrgCode(orgCode); + req.setOrderNo(orderNo); + req.setAttType(attType); + req.setAttExtName(attExtName); + req.setAttContext(attContext); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (StrUtil.isBlank(responseStr) + || lakalaRespJSON == null) { + throw new ApiException(I18nUtil._("文件上传无响应!")); + } + + if (!lakalaRespJSON.getStr("retCode").equals(lklSuccessCode)) { + throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg"))); + } + + return (JSONObject) lakalaRespJSON.get("respData"); + } catch (SDKException e) { + log.error("文件上传出错:", e); + throw new ApiException(I18nUtil._("文件上传出错!"), e); + } + } + + /** + * 商家申请入网电子合同(给到商家签署合同) + * + * @param mchMobile + * @return + */ + @Override + public Pair applyLedgerMerEc(String mchMobile) { + log.debug("商家开始申请入网电子合同"); + if (StrUtil.isBlank(mchMobile)) { + return Pair.of(false, I18nUtil._("缺少商家必要参数!")); + } + + // 获取商家信息 + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByCondition(mchMobile, ""); + if (shopMchEntry == null) { + return Pair.of(false, I18nUtil._("缺少商家相关信息!")); + } + + LklLedgerEc lklLedgerEc = lklLedgerEcService.getByMchMobile(mchMobile, "", CommonConstant.Enable); + if (lklLedgerEc != null && "COMPLETED".equals(lklLedgerEc.getEc_status())) { + return Pair.of(true, I18nUtil._("商家已经申请过入网电子合同!")); + } + + // 是企业类型商家 + Boolean isQy = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()); + + JSONObject reqData = new JSONObject(); + reqData.put("order_no", StringUtils.genLklOrderNo(8)); + reqData.put("org_id", orgCode); + reqData.put("ec_type_code", "EC007"); + reqData.put("cert_type", "RESIDENT_ID"); + + String larName = isQy ? shopMchEntry.getLegal_person_name() : shopMchEntry.getContact_name(); + String larIdCard = isQy ? shopMchEntry.getLegal_person_id_number() : shopMchEntry.getIndividual_id_number(); + reqData.put("cert_name", larName); + reqData.put("cert_no", larIdCard); + reqData.put("mobile", shopMchEntry.getLogin_mobile()); + + if (isQy) { + reqData.put("business_license_no", shopMchEntry.getBiz_license_number()); + reqData.put("business_license_name", shopMchEntry.getBiz_license_company()); + } + reqData.put("openning_bank_code", shopMchEntry.getOpenning_bank_code()); + reqData.put("openning_bank_name", shopMchEntry.getBank_name()); + reqData.put("acct_type_code", shopMchEntry.getAccount_type());//57 对公、 58 对私 + reqData.put("acct_no", shopMchEntry.getAccount_number()); + reqData.put("acct_name", shopMchEntry.getAccount_holder_name()); + reqData.put("remark", "申请入网电子合同"); + + // 正式上线的时候,调整 api 地址 + String domain = projectDomain; + if (isProdProject()) { + domain += "/api"; + } + + // 给拉卡拉通知的回调地址 TODO 生产环境一定去掉 api + String retUrl = domain + "/mobile/shop/lakala/ec/applyNotify"; + reqData.put("ret_url", retUrl); + + LocalDate today = LocalDate.now(); // 获取当前日期 + String signDate = today.getYear() + "-" + today.getMonthValue() + "-" + today.getDayOfMonth(); + + String platformName = "桂平发发网络有限公司"; + String ratioNum = "0.6"; + if (isLklProd) { + ratioNum = "0.25"; + } + JSONObject ecParams = new JSONObject(); + ecParams.put("A1", isQy ? shopMchEntry.getBiz_license_company() : shopMchEntry.getAccount_holder_name()); + ecParams.put("A30", ratioNum); // 测试环境微信费率0.6 + ecParams.put("A96", "1"); + ecParams.put("A97", "是"); + ecParams.put("A100", "是"); + ecParams.put("A101", "中国境内"); + ecParams.put("A102", platformName); + ecParams.put("A103", "小发同城"); + ecParams.put("A104", today.getYear()); + ecParams.put("A105", today.getMonthValue()); + ecParams.put("A106", today.getDayOfMonth()); + ecParams.put("A107", today.getYear()); + ecParams.put("A108", today.getMonthValue()); + ecParams.put("A109", today.getDayOfMonth()); + ecParams.put("B1", today.getYear()); + ecParams.put("B3", "是"); + ecParams.put("B4", "是"); + ecParams.put("B2", today.getMonthValue()); + ecParams.put("B8", isQy ? shopMchEntry.getBiz_license_company() : shopMchEntry.getAccount_holder_name()); + ecParams.put("B9", StrUtil.sub(shopMchEntry.getSales_info(), 0, 22)); + ecParams.put("B10", isQy ? shopMchEntry.getBiz_license_company() : shopMchEntry.getAccount_holder_name()); + ecParams.put("B14", isQy ? shopMchEntry.getBiz_license_number() : ""); + ecParams.put("B16", isQy ? "是" : "否"); + ecParams.put("B19", shopMchEntry.getOpenning_bank_code()); + ecParams.put("B20", shopMchEntry.getAccount_number()); + + ecParams.put("B24", isQy ? shopMchEntry.getLegal_person_name() : shopMchEntry.getIndividual_id_name()); + ecParams.put("B25", isQy ? shopMchEntry.getLegal_person_id_number() : shopMchEntry.getIndividual_id_number()); + ecParams.put("B26", isQy ? shopMchEntry.getLegal_person_mobile() : shopMchEntry.getLogin_mobile()); + ecParams.put("B27", shopMchEntry.getContact_name()); + ecParams.put("B28", shopMchEntry.getEmail()); + + ecParams.put("B31", shopMchEntry.getStore_name()); + ecParams.put("B32", shopMchEntry.getContact_name()); + ecParams.put("B33", shopMchEntry.getStore_address()); + ecParams.put("B34", shopMchEntry.getLogin_mobile()); + ecParams.put("D1", shopMchEntry.getBank_name()); + ecParams.put("D2", signDate); + ecParams.put("D4", platformName); + ecParams.put("D5", shopMchEntry.getLogin_mobile()); + ecParams.put("D7", signDate); + ecParams.put("D9", signDate); + ecParams.put("D11", signDate); + ecParams.put("D12", signDate); + ecParams.put("E1", platformName); + ecParams.put("E2", "小发同城平台商户合作协议"); + ecParams.put("E3", "2"); + ecParams.put("E5", platformName); + ecParams.put("E6", splitLowestRatio); + ecParams.put("E7", signDate); + ecParams.put("E8", shopMchEntry.getAccount_holder_name()); + + // 注:该字段是json字符串,不是json对象 + reqData.put("ec_content_parameters", ecParams.toString()); + + JSONObject reqBody = new JSONObject(); + reqBody.put("req_time", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMddHHmmss")); + reqBody.put("version", "3.0"); + reqBody.put("req_data", reqData); + + String reqUrl = serverUrl + "/api/v3/mms/open_api/ec/apply"; + if (isLklProd) { + reqUrl = serverUrl + "/api/v3/mms/open_api/ec/apply"; + } + + String privateKey = LakalaUtil.getResourceFile(priKeyPath, false, true); + String authorization = LakalaUtil.genAuthorization(privateKey, appId, serialNo, reqBody.toString()); + + JSONObject header = new JSONObject(); + header.put("Authorization", authorization); + + String errMsg = ""; + ResponseEntity response = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class); + if (ObjectUtil.isEmpty(response) || response.getStatusCode() != HttpStatus.OK) { + errMsg = "申请入网电子合同失败,无响应数据!"; + shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), "", CommonConstant.MCH_APPR_STA_LKL_NOPASS, errMsg); + return Pair.of(false, errMsg); + } + + JSONObject respBody = response.getBody(); + if (ObjectUtil.isNotEmpty(respBody) && !lklSuccessCode.equals(respBody.getStr("code"))) { + errMsg = "申请入网电子合同失败," + (StrUtil.isBlank(respBody.getStr("msg")) ? "返回状态有误" : respBody.getStr("msg")); + shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), "", CommonConstant.MCH_APPR_STA_LKL_NOPASS, errMsg); + return Pair.of(false, errMsg); + } + + JSONObject respData = respBody.getJSONObject("resp_data"); + if (respBody.getJSONObject("resp_data") == null) { + errMsg = "申请入网电子合同失败,返回数据有误"; + shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), "", CommonConstant.MCH_APPR_STA_LKL_NOPASS, errMsg); + return Pair.of(false, errMsg); + } + + // 商家入网申请电子合同处理数据 + // 先写入本地数据库表中 + LklLedgerEc record = new LklLedgerEc(); + record.setMch_id(shopMchEntry.getId()); + record.setMch_mobile(shopMchEntry.getLogin_mobile()); + record.setReq_params(reqBody.toString()); + record.setNotify_url(retUrl); + record.setEc_apply_id(respData.getLong("ec_apply_id")); + record.setResult_url(respData.getStr("result_url")); + record.setResp_body(respBody.toString()); + Boolean success = lklLedgerEcService.saveOrUpdateByMchId(record); + if (!success) { + errMsg = "申请入网电子合同失败,数据保存失败"; + shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), "", CommonConstant.MCH_APPR_STA_NOPASS, errMsg); + return Pair.of(false, errMsg); + } + + return Pair.of(true, "商家入网申请电子合同成功"); + } + + + /** + * 商户分账业务开通申请 + * + * @param merCupNo + * @return + */ + @Override + public Pair innerApplyLedgerMer(String merCupNo) { + log.debug("商户分账业务申请开始"); + + if (StringUtils.isBlank(merCupNo)) { + return Pair.of(false, I18nUtil._("商户号不能为空!")); + } + + // 获取商户入驻记录 + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerCupNo(merCupNo); + if (shopMchEntry == null) { + return Pair.of(false, I18nUtil._("商家入驻信息不存在!")); + } + + // 判断是否已经申请过? + LklLedgerMember lklLedgerMember = lklLedgerMemberService.getByMerCupNo(merCupNo); + if (lklLedgerMember != null && lklLedgerMember.getAudit_status() == 1) { + return Pair.of(true, I18nUtil._("商家已经申请过了!")); + } + + // 1. 配置初始化 + initLKLSDK(); + + //2. 装配数据 + V2MmsOpenApiLedgerApplyLedgerMerRequest req = new V2MmsOpenApiLedgerApplyLedgerMerRequest(); + req.setVersion("2.0"); + req.setOrderNo(StringUtils.genLklOrderNo(8));// 14位年月日时(24小时制)分秒+8位的随机数 + req.setOrgCode(orgCode); +// req.setMerInnerNo(shopMchEntry.getLkl_mer_inner_no());// 从进件申请返回的内部商户号 + req.setMerCupNo(shopMchEntry.getLkl_mer_cup_no()); // 从进件申请返回的商户号(不要传入内部商户号,传银联商户号才有效) + req.setContactMobile(shopMchEntry.getLogin_mobile()); // 商户入驻注册的手机号 + // 分账比例为了考虑低价订单的运费占比高,分账比例暂时定70%分账给商户,30%分账给平台 + req.setSplitLowestRatio(new BigDecimal(splitLowestRatio)); + req.setEleContractNo(shopMchEntry.getLkl_ec_no()); + String fileName = "商家分账授权委托书.pdf";//paramsJSON.getStr("splitEntrustFileName"); + req.setSplitEntrustFileName(fileName); + + // TODO 分账结算委托书文件上传到拉卡拉服务器 +// JSONObject fileUploadResp = uploadFile(req.getOrderNo(), "SPLIT_ENTRUST_FILE", StringUtils.getFileExt(fileName), UploadUtil.fileUrlToBase64(shopMchEntry.getContract_download_url())); +// if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) { +// log.error("商家分账授权委托书{}上传失败!", shopMchEntry.getContract_download_url()); +// // return Pair.of(false, I18nUtil._("商家分账授权委托书上传失败!")); +//// throw new ApiException(I18nUtil._("商家分账授权委托书上传失败!")); +// } + + String splitEntrustFilePath = shopMchEntry.getLkl_ec_file_path(); //fileUploadResp.getStr("attFileId"); + req.setSplitEntrustFilePath(splitEntrustFilePath); //比如:MMS/20250519/165150-39c8bbca513b4cccab1e942999021fd6.pdf; + + String domain = projectDomain; + if (isProdProject()) { + domain += "/api"; + } + // 给拉卡拉通知的回调地址 + String retUrl = domain + "/mobile/shop/lakala/ledger/applyLedgerMerNotify"; + req.setRetUrl(retUrl); + + log.debug("商户分账业务申请请求参数:{}", JSONUtil.toJsonStr(req)); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + if (StrUtil.isBlank(responseStr)) { + return Pair.of(false, I18nUtil._("无返回值,申请开通分账失败!")); + } + + log.debug("商户分账业务申请响应数据:{}", responseStr); + + // 成功返回示例:{'retCode':'000000','retMsg':'申请已受理,请等待审核结果','respData':{'version':'1.0','orderNo':'KFPT20230223181025407788734','orgCode':'1','applyId':681201215598657536}} + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (lakalaRespJSON == null) { + return Pair.of(false, I18nUtil._("无返回值,申请开通分账失败!")); + } + + Object applyId = lakalaRespJSON.getByPath("respData.applyId"); + if (!lakalaRespJSON.getStr("retCode").equals(lklSuccessCode) || applyId == null) { + return Pair.of(false, lakalaRespJSON.getStr("retMsg")); + } + + JSONObject paramsJSON = new JSONObject(); + paramsJSON.put("orderNo", req.getOrderNo()); + paramsJSON.put("org_code", orgCode); + paramsJSON.put("version", "2.0"); + paramsJSON.put("ret_url", retUrl); + paramsJSON.put("mer_inner_no", shopMchEntry.getLkl_mer_inner_no()); + paramsJSON.put("mer_cup_no", shopMchEntry.getLkl_mer_cup_no()); + paramsJSON.put("contact_mobile", shopMchEntry.getLogin_mobile()); + paramsJSON.put("split_lowest_ratio", req.getSplitLowestRatio()); + paramsJSON.put("split_range", "MARK"); + paramsJSON.put("split_launch_mode", "MANUAL"); + paramsJSON.put("settle_type", "01"); + paramsJSON.put("split_rule_source", "TR"); + paramsJSON.put("ele_contract_no", req.getEleContractNo()); + paramsJSON.put("split_entrust_file_name", req.getSplitEntrustFileName()); + paramsJSON.put("split_entrust_file_path", splitEntrustFilePath); + + paramsJSON.set("apply_id", applyId); + paramsJSON.set("remark", lakalaRespJSON.getStr("retMsg")); + paramsJSON.set("audit_status_text", paramsJSON.get("remark")); + paramsJSON.set("mch_id", shopMchEntry.getId()); + + // 新增数据 + // 将 JSON 对象的键名转换为下划线命名 + LklLedgerMember lklLedgerMemberNew = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerMember.class); + if (!lklLedgerMemberService.saveOrUpdateByMerCupNo(lklLedgerMemberNew)) { + return Pair.of(false, I18nUtil._("商户分账业务材料新增失败,待审核中!")); + } + + return Pair.of(true, I18nUtil._("商户分账业务申请提交成功,待审核中!")); + } catch (SDKException e) { + log.error("申请开通分账出错:", e); + return Pair.of(false, I18nUtil._("商家申请开通分账出错!")); +// throw new ApiException(I18nUtil._("商家申请开通分账出错!"), e); + } + } + + /** + * 商户入网电子合同申请回调通知 + * 参考:https://o.lakala.com/#/home/document/detail?id=289 + * + * @param request + * @return + */ + @Transactional + @Override + public JSONObject applyLedgerMerEcNotify(HttpServletRequest request) { + log.debug("商户入网电子合同申请回调通知开始"); + + // 验签 + String authorization = request.getHeader("Authorization"); + String requestBody = LakalaUtil.getBody(request); + log.debug("商户入网电子合同申请回调返回requestbody 参数:{}\n authorization参数:{}\n", requestBody, authorization); + + + 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"); + respData.put("message", "处理失败!"); + + if (paramsJSON != null) { + Long ecApplyId = paramsJSON.getLong("ecApplyId"); + String ecNo = paramsJSON.getStr("ecNo"); + String ecStatus = paramsJSON.getStr("ecStatus"); // COMPLETED + if (ecStatus == null || !ecStatus.equals("COMPLETED")) { + log.debug("入网电子合同申请未签署完成!"); + respData.put("message", "商户入网电子合同尚未签署,请稍候!"); + return respData; + } + + if (ecApplyId == null || StrUtil.isBlank(ecNo)) { + log.error("入网电子合同申请回调:ecApplyId 为空"); + respData.put("message", "ecApplyId 返回空值!"); + return respData; + } + + LklLedgerEc lklLedgerEc = lklLedgerEcService.getByApplyId(ecApplyId, "", CommonConstant.Enable); + if (lklLedgerEc == null) { + log.error("入网电子合同申请回调:找不到对应入网lklLedgerEc电子合同记录"); + respData.put("message", "找不到对应入网电子合同记录!"); +// throw new ApiException("找不到对应入网电子合同记录!"); + return respData; + } + + if ("COMPLETED".equals(lklLedgerEc.getEc_status())) { + respData.put("code", "SUCCESS"); + respData.put("message", "操作成功!"); + log.info("商户入网电子合同申请回调:已处理成功,不需再重新处理"); + return respData; + } + + // 把 base64 合同文件,上传到 cos 服务器,返回 url 地址 + Pair ecFilePair = ledgerMerEcDownload(ecApplyId); + String ecCosFileUrl = ""; + String eclklFilePath = ""; + if (ecFilePair != null) { + ecCosFileUrl = ecFilePair.getFirst(); + eclklFilePath = ecFilePair.getSecond(); + } + + // 更改本地记录状态数据 + LklLedgerEc updRecord = new LklLedgerEc(); + updRecord.setEc_apply_id(ecApplyId); + updRecord.setEc_no(ecNo); + updRecord.setEc_name(paramsJSON.getStr("ecName")); + updRecord.setEc_file(ecCosFileUrl); // 合同本地文件COS URL链接 + updRecord.setLkl_file_path(eclklFilePath); + updRecord.setEc_status(paramsJSON.getStr("ecStatus")); + // 更新本地数据状态和合同编号、合同名字 + Boolean success = lklLedgerEcService.updateByApplyId(updRecord); + if (success) { + // 更新商家入驻表的合同编号,和签署地址,更改状态 + shopMchEntryService.updateMerchEntryLklEcNo(lklLedgerEc.getMch_id(), ecNo, paramsJSON.getStr("ecName"), lklLedgerEc.getResult_url(), ecCosFileUrl, eclklFilePath); + + // TODO 商家电子合同签署完毕后,收到异步通知,触发拉卡拉商家进件(重要环节) + // 下一步,等待拉卡拉系统审核,和人工审核,收到异步通知之后,触发1、e签宝的电子合同签署,2、新增分账接收方 + Pair resultPair = lklTkService.registrationMerchant(lklLedgerEc.getMch_mobile(), ""); + if (!resultPair.getFirst()) { + errMsg += resultPair.getSecond(); + log.error(errMsg); + shopMchEntryService.updateMerchEntryApprovalByMchId(lklLedgerEc.getMch_id(), "", CommonConstant.MCH_APPR_STA_LKL_NOPASS, errMsg); + throw new ApiException(errMsg); + } + + shopMchEntryService.updateMerchEntryApprovalByMchId(lklLedgerEc.getMch_id(), "", CommonConstant.MCH_APPR_STA_LKL_PADDING, "已提交进件申请,请等待机构审核!"); + + respData.put("code", "SUCCESS"); + respData.put("message", "操作成功!"); + log.info("商户入网电子合同申请回调:处理成功"); + return respData; + } + } + + throw new ApiException("商户入网电子合同申请回调:处理失败!"); + } + + /** + * 商户入网盖章电子合同下载, 并上传到 cos 服务器 + * + * @param ecApplyId + * @return cosUrl, lklFilePath + */ + @Override + public Pair ledgerMerEcDownload(Long ecApplyId) { + log.debug("商家开始申请入网电子合同"); + if (ObjectUtil.isEmpty(ecApplyId)) { + return null; + } + + JSONObject reqData = new JSONObject(); + reqData.put("order_no", StringUtils.genLklOrderNo(8)); + reqData.put("org_code", orgCode); + reqData.put("ec_apply_id", ecApplyId); + + JSONObject reqBody = new JSONObject(); + reqBody.put("req_time", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMddHHmmss")); + reqBody.put("version", "1.0"); + reqBody.put("req_data", reqData); + + String reqUrl = serverUrl + "/api/v3/mms/open_api/ec/download"; + if (isLklProd) { + reqUrl = serverUrl + "/api/v3/mms/open_api/ec/download"; + } + + String privateKey = LakalaUtil.getResourceFile(priKeyPath, false, true); + String authorization = LakalaUtil.genAuthorization(privateKey, appId, serialNo, reqBody.toString()); + + JSONObject header = new JSONObject(); + header.put("Authorization", authorization); + + ResponseEntity response = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class); + if (ObjectUtil.isEmpty(response) || response.getStatusCode() != HttpStatus.OK) { + log.error("下载电子合同失败:返回状态有误!"); + return null; + } + + JSONObject respBody = response.getBody(); + if (ObjectUtil.isNotEmpty(respBody) && !lklSuccessCode.equals(respBody.getStr("code"))) { + String errMsg = StrUtil.isBlank(respBody.getStr("msg")) ? "返回状态有误" : respBody.getStr("msg"); + log.error("下载电子合同失败:{}!", errMsg); + return null; + } + + JSONObject respData = respBody.getJSONObject("resp_data"); + if (respBody.getJSONObject("resp_data") == null) { + log.error("下载电子合同失败:返回数据有误!"); + return null; + } + + String ecFile = respData.getStr("ec_file"); + if (StrUtil.isBlank(ecFile)) { + log.error("下载电子合同失败:返回数据有误!"); + return null; + } + + LklLedgerEc lklLedgerEc = lklLedgerEcService.getByApplyId(ecApplyId, "", CommonConstant.Enable); + if (lklLedgerEc == null) { + log.error("下载电子合同失败:未找到对应的入网电子合同记录"); + return null; + } + + String fileBase64 = respData.getStr("ec_file"); + File file = UploadUtil.convertBase64ToFile(fileBase64); + + // REMARK 把合同文件 url 上传到cos服务器 + String cosFileName = TENGXUN_DEFAULT_DIR.concat("/").concat("contract") + .concat("/") + .concat(lklLedgerEc.getMch_mobile()).concat("/") + .concat("signed").concat("/") + .concat(ecApplyId.toString()).concat(".pdf"); + log.debug("拉卡拉电子合同保存地址:{}", cosFileName); + + // 上传到cos服务器 + String cosFileUrl = ossService.uploadObject4OSS(file, cosFileName); + + + // 文件上传到拉卡拉服务器 + JSONObject fileUploadResp = uploadFile(StringUtils.genLklOrderNo(8), + "SPLIT_COOPERATION_FILE", + "pdf", + UploadUtil.fileToBase64(file)); + if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) { + log.error("下载电子合同失败:未找到对应的入网电子合同记录"); + } + + String lklFilePath = fileUploadResp.getStr("attFileId"); + + // 删除临时文件 + file.delete(); + + return Pair.of(cosFileUrl, lklFilePath); + } + + /** + * 商户分账业务开通申请 + * + * @param paramsJSON + * @return + */ + @Override + public CommonResult applyLedgerMer(JSONObject paramsJSON) { + Pair resultPair = innerApplyLedgerMer(paramsJSON.getStr("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); +// } + } + + /** + * 商户分账业务开通申请回调 + * 参考:https://o.lakala.com/#/home/document/detail?id=379 + * + * @param request + * @return + */ + @Transactional +// @Override + public JSONObject applyLedgerMerNotifyTemp(HttpServletRequest request) { + log.debug("商户分账申请业务异步回调通知开始"); + + // 验签 + String authorization = request.getHeader("Authorization"); + String requestBody = LakalaUtil.getBody(request); + log.debug("商户分账申请业务异步回调返回request body参数:{}", requestBody); + log.debug("商户分账申请业务异步回调返回authorization参数:{}", authorization); + + boolean checkSuccess = LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath); + if (!checkSuccess) { + log.error("商户分账申请业务回调:验签失败"); + return JSONUtil.createObj().put("code", "FAIL").put("message", "验签失败!"); + } + + JSONObject paramsJSON = JSONUtil.parseObj(requestBody); + + String errMsg = "商户分账申请业务回调:"; + JSONObject respData = new JSONObject(); + respData.put("code", "FAIL"); + respData.put("message", "处理失败!"); + + if (paramsJSON != null) { + String applyId = paramsJSON.getStr("applyId"); + String auditStatus = paramsJSON.getStr("auditStatus"); + if (StrUtil.isBlank(applyId) || StrUtil.isBlank(auditStatus)) { + errMsg += "缺少返回必要参数"; + log.error(errMsg + ":applyId={}, auditStatus={}", applyId, auditStatus); + + respData.put("message", errMsg); + return respData; + } + + //auditStatus:1:通过,2拒绝 + if (!auditStatus.equals("1")) { + respData.put("message", "商户分账申请被驳回!"); + return respData; + } + + LklLedgerMember lklLedgerMember = lklLedgerMemberService.getByApplyId(applyId, CommonConstant.Enable); + if (lklLedgerMember != null) { + respData.put("code", "SUCCESS"); + respData.put("message", "商户分账申请已处理成功!"); + log.debug("商户分账申请业务回调:已处理成功,不需再重新处理"); + return respData; + } + + // 更改本地分账记录状态数据 + String merCupNo = paramsJSON.getStr("merCupNo"); + Boolean success = lklLedgerMemberService.updateAuditResult(applyId, + paramsJSON.getStr("merInnerNo"), + paramsJSON.getStr("merCupNo"), + paramsJSON.getStr("entrustFileName"), + paramsJSON.getStr("entrustFilePath"), + paramsJSON.getStr("auditStatus"), + paramsJSON.getStr("auditStatusText"), + paramsJSON.getStr("remark") + ); + + if (success) { + // 重要注:商户分账申请业务成功后,同时也会新增接收方,这时系统绑定接收方(平台方和代理商),绑定之前,要判断是否已经绑定过了? + JSONObject bindParamsJSON = new JSONObject(); + bindParamsJSON.put("merInnerNo", paramsJSON.getStr("merInnerNo")); + bindParamsJSON.put("merCupNo", merCupNo); + Pair bindResult = innerApplyLedgerMerReceiverBind(bindParamsJSON); + if (!bindResult.getFirst()) { + errMsg += bindResult.getSecond(); + log.error("商户{}预绑定接收方出错:{}", merCupNo, bindResult.getSecond()); + + shopMchEntryService.updateMerchEntryApprovalByMchId(lklLedgerMember.getMch_id(), "", CommonConstant.MCH_APPR_STA_NOPASS, errMsg); +// respData.put("message", errMsg); + throw new ApiException(errMsg); +// return respData; + } + + // 更新商家的hasApplySplit状态=1; + shopMchEntryService.updateMulStatus("", merCupNo, 0, 1, 0, 0); + + respData.put("code", "SUCCESS"); + respData.put("message", "操作成功!"); + log.debug("商户分账申请业务回调:处理成功"); + return respData; + } + } + + throw new ApiException("商户分账申请业务回调:操作失败!"); + } + + /** + * 商户分账业务开通申请回调 + * 参考:https://o.lakala.com/#/home/document/detail?id=379 + * + * @param request HTTP请求 + * @return 处理结果JSON + */ + @Transactional + @Override + public JSONObject applyLedgerMerNotify(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 applyId = paramsJSON.getStr("applyId"); + String auditStatus = paramsJSON.getStr("auditStatus"); + String merCupNo = paramsJSON.getStr("merCupNo"); + + if (StrUtil.isBlank(applyId) || StrUtil.isBlank(auditStatus) || StrUtil.isBlank(merCupNo)) { + String errMsg = "商户分账申请业务回调:缺少必要参数(applyId/auditStatus/merCupNo)"; + log.error(errMsg + ":applyId={}, auditStatus={}, merCupNo={}", applyId, auditStatus, merCupNo); + return JSONUtil.createObj().put("code", "FAIL").put("message", errMsg); + } + + if (!auditStatus.equals("1")) { + log.warn("商户分账申请业务回调:审核未通过,状态={}", auditStatus); + return JSONUtil.createObj().put("code", "FAIL").put("message", "商户分账申请被驳回!"); + } + + LklLedgerMember lklLedgerMember = lklLedgerMemberService.getByApplyId(applyId, CommonConstant.Enable); + if (lklLedgerMember != null) { + log.debug("商户分账申请业务回调:已处理成功,applyId={}", applyId); + return JSONUtil.createObj().put("code", "SUCCESS").put("message", "商户分账申请已处理成功!"); + } + + String merInnerNo = paramsJSON.getStr("merInnerNo"); + String entrustFileName = paramsJSON.getStr("entrustFileName"); + String entrustFilePath = paramsJSON.getStr("entrustFilePath"); + String auditStatusText = paramsJSON.getStr("auditStatusText"); + String remark = paramsJSON.getStr("remark"); + + Boolean updateSuccess = lklLedgerMemberService.updateAuditResult( + applyId, merInnerNo, merCupNo, entrustFileName, entrustFilePath, + auditStatus, auditStatusText, remark + ); + + if (!updateSuccess) { + log.error("商户分账申请业务回调:更新审核结果失败,applyId={}", applyId); + return JSONUtil.createObj().put("code", "FAIL").put("message", "更新审核结果失败"); + } + + try { + JSONObject bindParamsJSON = new JSONObject() + .put("merInnerNo", merInnerNo) + .put("merCupNo", merCupNo); + + Pair bindResult = innerApplyLedgerMerReceiverBind(bindParamsJSON); + if (!bindResult.getFirst()) { + String errMsg = "商户分账申请业务回调:预绑定接收方失败 - " + bindResult.getSecond(); + log.error("商户{}预绑定接收方出错:{}", merCupNo, errMsg); + + // 更新商户审批状态 + if (lklLedgerMember != null) { // 防御性判断(理论上此时应为null,但保留安全检查) + shopMchEntryService.updateMerchEntryApprovalByMchId( + lklLedgerMember.getMch_id(), "", CommonConstant.MCH_APPR_STA_NOPASS, errMsg + ); + } + return JSONUtil.createObj().put("code", "FAIL").put("message", errMsg); + } + } catch (Exception e) { + log.error("商户分账申请业务回调:绑定接收方异常", e); + return JSONUtil.createObj().put("code", "FAIL").put("message", "绑定接收方失败"); + } + + // 更新商家分账申请状态为已申请(hasApplySplit=1) + shopMchEntryService.updateMulStatus("", merCupNo, 0, 1, 0, 0); + + log.debug("商户分账申请业务回调:处理成功,applyId={}", applyId); + return JSONUtil.createObj().put("code", "SUCCESS").put("message", "操作成功!"); + } + + /** + * 分账接收方创建申请 + * 参考:https://o.lakala.com/#/home/document/detail?id=380 + * + * @param paramsJSON + * @return + */ + @Override + public CommonResult applyLedgerReceiver(JSONObject paramsJSON) { + // 判断分账接收方记录是否存在,存在就不再新建了 + LklLedgerReceiver lklLedgerReceiverOld = lklLedgerReceiverService.getByCondition(paramsJSON.getStr("licenseNo"), paramsJSON.getStr("contactMobile"), paramsJSON.getLong("platformId")); + if (lklLedgerReceiverOld != null) { + return CommonResult.success(lklLedgerReceiverOld, "该接收方已创建过!"); + } + + // 1. 配置初始化 + initLKLSDK(); + + //2. 装配数据 + V2MmsOpenApiLedgerApplyLedgerReceiverRequest req = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest(); + + String orderNo = StringUtils.genLklOrderNo(8); // 8位随机数 + req.setOrderNo(orderNo); + req.setOrgCode(orgCode); + req.setVersion("2.0"); + + String mchMobile = paramsJSON.getStr("contactMobile"); + + 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")); + + if (paramsJSON.getJSONArray("attachList") != null && paramsJSON.getJSONArray("attachList").size() > 0) { + List attachList = new ArrayList<>(); + V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo attachInfo = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo(); + for (JSONObject attachJSON : paramsJSON.getJSONArray("attachList").jsonIter()) { + String fileName = attachJSON.getStr("attachName"); + String attachType = attachJSON.getStr("attachType"); + String fileBase64 = UploadUtil.fileUrlToBase64(attachJSON.getStr("attachStoreFile")); // 这个是 url 地址,不是 base64 字节码 + attachInfo.setAttachName(fileName); + attachInfo.setAttachType(attachType); + + JSONObject fileUploadResp = uploadFile(StringUtils.genLklOrderNo(8), attachType, + StringUtils.getFileExt(fileName), fileBase64); + if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) { + log.error("附件:{},{} 上传失败!", fileName, attachType); + continue; + } + + attachInfo.setAttachStorePath(fileUploadResp.getStr("attFileId")); + attachList.add(attachInfo); + } + + req.setAttachList(attachList); + paramsJSON.set("attach_list", JSONUtil.toJsonStr(attachList)); + } + + paramsJSON.set("orderNo", orderNo); + paramsJSON.set("version", "2.0"); + paramsJSON.set("org_code", orgCode); + + try { + //3. 发送请求,申请创建分账接收方 + String responseStr = LKLSDK.httpPost(req); + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (StrUtil.isBlank(responseStr) || lakalaRespJSON == null) { + return CommonResult.failed(I18nUtil._("创建分账接收方响应数据无效!")); +// throw new ApiException(I18nUtil._("创建分账接收方无响应!")); + } + + + if (!lakalaRespJSON.getStr("retCode").equals(lklSuccessCode)) { + return CommonResult.failed(I18nUtil._(lakalaRespJSON.getStr("retMsg"))); + // throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg"))); + } + + paramsJSON.set("receiver_no", lakalaRespJSON.getByPath("respData.receiverNo")); + paramsJSON.set("org_id", lakalaRespJSON.getByPath("respData.orgId")); + paramsJSON.set("org_name", lakalaRespJSON.getByPath("respData.orgName")); + + // 新增数据 + // 将 JSON 对象的键名转换为下划线命名 + LklLedgerReceiver lklLedgerReceiver = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerReceiver.class); + + // 新增或修改本地数据 + Boolean success = lklLedgerReceiverService.saveOrUpdateByReceiverNo(lklLedgerReceiver); + if (!success) { + log.error("接收方创建成功,但更新本地数据失败!"); + return CommonResult.failed(I18nUtil._("接收方创建成功,但更新本地数据失败!")); + } + + // 更新商户分账多个状态 has_apply_receiver=1 + shopMchEntryService.updateMulStatus(mchMobile, "", 0, 0, 1, 0); + + return CommonResult.success(lklLedgerReceiver, "创建接收方成功!"); + } catch (Exception e) { + log.error("接收方创建失败:{}", e); + throw new ApiException(I18nUtil._("创建接收方失败:{}"), e); + } + } + + /** + * 商家与平台、代理商分账关系绑定申请 + * 参考:https://o.lakala.com/#/home/document/detail?id=386 + * + * @param paramsJSON {merCupNo} + * @return + */ + @Override + public CommonResult applyLedgerMerReceiverBind(JSONObject paramsJSON) { + Pair retPair = innerApplyLedgerMerReceiverBind(paramsJSON); + if (!retPair.getFirst()) { + return CommonResult.failed(retPair.getSecond()); + } + + return CommonResult.success(null, "接收方绑定成功!"); + } + + /** + * 内部调用分账关系绑定申请(废弃) + * 参考:https://o.lakala.com/#/home/document/detail?id=386 + * + * @param paramsJSON + * @return + */ + public Pair innerApplyLedgerMerReceiverBindTemp(JSONObject paramsJSON) { + if (paramsJSON == null) { + return Pair.of(false, I18nUtil._("绑定参数有误!")); + } + + String merCupNo = paramsJSON.getStr("merCupNo"); + if (StrUtil.isBlank(merCupNo)) { + return Pair.of(false, I18nUtil._("商户参数不能为空!")); + } + + // 获取商家入驻记录 + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerCupNo(merCupNo); + if (shopMchEntry == null) { + return Pair.of(false, I18nUtil._("商户入驻记录不存在!")); + } + + // 检查分账方和接收方记录是否存在 + LklLedgerMember lklLedgerMember = lklLedgerMemberService.getByMerCupNo(merCupNo); + if (lklLedgerMember == null) { + return Pair.of(false, I18nUtil._("商家尚未做分账业务申请!")); + } + + // 代理商Id,平台默认为0 +// Long platformId = shopMchEntry.getDistributor_id(); + String entrustFileName = "小发同城合作协议书.pdf"; +// String entrustFileUrl = shopMchEntry.getContract_download_url(); + + int successCnt = 0; + + // 获取平台接收方记录(可能多个记录,平台+代理商) + List lklLedgerReceiverList = lklLedgerReceiverService.getByCondition("", shopMchEntry.getLogin_mobile()); + if (CollectionUtils.isEmpty(lklLedgerReceiverList)) { + return Pair.of(false, I18nUtil._("先新增接收方信息!")); + } + + for (LklLedgerReceiver lklLedgerReceiver : lklLedgerReceiverList) { + LklLedgerMerReceiverBind lklLedgerMerReceiverBindOld = lklLedgerMerReceiverBindService.getByCondition(merCupNo, lklLedgerReceiver.getReceiver_no()); + if (lklLedgerMerReceiverBindOld != null) { + // return Pair.of(false, "分账绑定关系已存在!"); + continue; + } + + // 1. 配置初始化 + initLKLSDK(); + + //2. 装配数据 + V2MmsOpenApiLedgerApplyBindRequest req = new V2MmsOpenApiLedgerApplyBindRequest(); + + String orderNo = StringUtils.genLklOrderNo(8); // 8位随机数 + req.setOrderNo(orderNo); + req.setOrgCode(orgCode); + req.setVersion("2.0"); + + req.setMerInnerNo(lklLedgerMember.getMer_inner_no()); + req.setMerCupNo(merCupNo); + req.setReceiverNo(lklLedgerReceiver.getReceiver_no()); + +// String fileName = paramsJSON.getStr("entrustFileName"); + //String splitEntrustFileBase64 = UploadUtil.fileUrlToBase64(entrustFileUrl); // 这个是 url 地址,不是 base64 字节码 + req.setEntrustFileName(entrustFileName); + + String domain = projectDomain; + if (isProdProject()) { + domain += "/api"; + } + // 给拉卡拉通知的回调地址 + String retUrl = domain + "/mobile/shop/lakala/ledger/applyLedgerMerReceiverBindNotify"; + req.setRetUrl(retUrl); + + // 文件上传到拉卡拉服务器 +// JSONObject fileUploadResp = uploadFile(orderNo, +// "SPLIT_COOPERATION_FILE", +// StringUtils.getFileExt(entrustFileName), +// splitEntrustFileBase64); +// if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) { +// // throw new ApiException(I18nUtil._("合作协议上传失败!")); +// log.error(I18nUtil._("合作协议上传失败!")); +// continue; +// } + + String entrustFilePath = shopMchEntry.getLkl_ec_file_path();//fileUploadResp.getStr("attFileId"); + req.setEntrustFilePath(entrustFilePath); + + paramsJSON.set("orderNo", orderNo); + paramsJSON.set("version", "2.0"); + paramsJSON.set("ret_url", retUrl); + paramsJSON.set("org_code", orgCode); + paramsJSON.set("merInnerNo", lklLedgerMember.getMer_inner_no()); + paramsJSON.set("entrust_file_name", entrustFileName); + paramsJSON.set("entrust_file_path", entrustFilePath); + + try { + log.debug("绑定接收方参数:{}", JSONUtil.toJsonStr(req)); + + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + + JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); + if (lakalaRespJSON == null || !lakalaRespJSON.getStr("retCode").equals(lklSuccessCode)) { +// throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg"))); + log.error(I18nUtil._(lakalaRespJSON.getStr("retMsg"))); + continue; + } + + paramsJSON.set("apply_id", lakalaRespJSON.getByPath("respData.applyId")); + paramsJSON.set("remark", lakalaRespJSON.getStr("retMsg")); + + // 新增数据 + // 将 JSON 对象的键名转换为下划线命名 + LklLedgerMerReceiverBind lklLedgerMerReceiverBind = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerMerReceiverBind.class); + if (lklLedgerMerReceiverBindService.saveOrUpdateByMerCupNoReceiverNo(lklLedgerMerReceiverBind)) { + successCnt += 1; + } +// return Pair.of(true, "提交成功,待审核中!"); + } catch (SDKException e) { + log.error("分账绑定关系申请失败:", e); +// throw new ApiException(I18nUtil._("分账绑定关系申请失败!"), e); +// return Pair.of(false, "分账绑定关系申请失败!"); + } + } + + if (successCnt != lklLedgerReceiverList.size()) { + log.error("部分分账绑定关系提交成功"); + return Pair.of(true, "部分提交成功,待审核中!"); + } + + return Pair.of(true, "提交成功,待审核中!"); + } + + /** + * 内部调用分账关系绑定申请(优化版) + * 参考:https://o.lakala.com/#/home/document/detail?id=386 + * + * @param paramsJSON 包含绑定参数的JSON对象 {merCupNo} + * @return 操作结果及提示信息 + */ + public Pair innerApplyLedgerMerReceiverBind(JSONObject paramsJSON) { + // 1. 参数校验(提前失败) + if (paramsJSON == null) { + return Pair.of(false, I18nUtil._("绑定参数为空")); + } + + String merCupNo = paramsJSON.getStr("merCupNo"); + if (StrUtil.isBlank(merCupNo)) { + return Pair.of(false, I18nUtil._("商户号(merCupNo)为空")); + } + + // 2. 基础数据查询(提前失败) + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerCupNo(merCupNo); + if (shopMchEntry == null) { + return Pair.of(false, I18nUtil._("商户入驻记录不存在")); + } + + LklLedgerMember lklLedgerMember = lklLedgerMemberService.getByMerCupNo(merCupNo); + if (lklLedgerMember == null) { + return Pair.of(false, I18nUtil._("商家尚未做分账业务申请")); + } + + List receiverList = lklLedgerReceiverService.getByCondition("", shopMchEntry.getLogin_mobile()); + if (CollectionUtils.isEmpty(receiverList)) { + return Pair.of(false, I18nUtil._("接收方信息为空")); + } + + // 3. 公共参数准备(避免循环内重复计算) + String domain = projectDomain; + if (isProdProject()) { + domain += "/api"; + } + String retUrl = domain + "/mobile/shop/lakala/ledger/applyLedgerMerReceiverBindNotify"; + String entrustFileName = "小发同城合作协议书.pdf"; + String entrustFilePath = shopMchEntry.getLkl_ec_file_path(); + + int successCount = 0; + int totalCount = receiverList.size(); + + // 5. 初始化SDK(建议移至类初始化或统一配置) + initLKLSDK(); + + // 4. 循环处理接收方绑定 + for (LklLedgerReceiver receiver : receiverList) { + try { + // 跳过已存在的绑定关系 + if (lklLedgerMerReceiverBindService.getByCondition(merCupNo, receiver.getReceiver_no()) != null) { + log.warn("分账绑定关系已存在:merCupNo={}, receiverNo={}", merCupNo, receiver.getReceiver_no()); + continue; + } + + // 6. 构建请求参数 + String orderNo = StringUtils.genLklOrderNo(8); + V2MmsOpenApiLedgerApplyBindRequest request = new V2MmsOpenApiLedgerApplyBindRequest(); + request.setOrderNo(orderNo); + request.setOrgCode(orgCode); + request.setVersion("2.0"); + request.setMerInnerNo(lklLedgerMember.getMer_inner_no()); + request.setMerCupNo(merCupNo); + request.setReceiverNo(receiver.getReceiver_no()); + request.setEntrustFileName(entrustFileName); + request.setEntrustFilePath(entrustFilePath); + request.setRetUrl(retUrl); + + // 7. 记录请求参数 + log.debug("绑定接收方参数:{}", JSONUtil.toJsonStr(request)); + + // 8. 发送请求并处理响应 + String responseStr = LKLSDK.httpPost(request); + JSONObject respJson = JSONUtil.parseObj(responseStr); + + if (respJson == null || !lklSuccessCode.equals(respJson.getStr("retCode"))) { + log.error("拉卡拉响应失败:{}", respJson != null ? respJson.getStr("retMsg") : "无响应数据"); + continue; // 单个失败不影响其他接收方处理 + } + + // 9. 更新参数并保存记录 + paramsJSON.set("orderNo", orderNo); + paramsJSON.set("apply_id", respJson.getByPath("respData.applyId")); + paramsJSON.set("receiver_no", receiver.getReceiver_no()); + paramsJSON.set("remark", respJson.getStr("retMsg")); + + // 转换JSON键名格式并保存 + String snakeJson = StringUtils.convertCamelToSnake(paramsJSON.toString()); + LklLedgerMerReceiverBind bindRecord = JSONUtil.toBean(snakeJson, LklLedgerMerReceiverBind.class); + + if (lklLedgerMerReceiverBindService.saveOrUpdateByMerCupNoReceiverNo(bindRecord)) { + successCount++; + } else { + log.warn("绑定记录保存失败:merCupNo={}, receiverNo={}", merCupNo, receiver.getReceiver_no()); + } + + } catch (Exception e) { + log.error("处理分账绑定失败:merCupNo={}, receiverNo={}", merCupNo, receiver.getReceiver_no(), e); + // 单个接收方处理失败,继续处理其他接收方 + } + } + + // 10. 返回结果 + if (successCount == 0) { + return Pair.of(false, "所有分账绑定均失败"); + } else if (successCount < totalCount) { + return Pair.of(true, "部分提交成功(" + successCount + "/" + totalCount + "),待审核中"); + } else { + return Pair.of(true, "全部提交成功,待审核中"); + } + } + + /** + * 分账关系绑定申请回调 + * 参考:https://o.lakala.com/#/home/document/detail?id=379 + * + * @param request HTTP请求对象 + * @return 处理结果JSON + */ + @Transactional + @Override + public JSONObject applyLedgerMerReceiverBindNotify(HttpServletRequest request) { + log.debug("分账商家绑定接收方异步回调通知开始"); + + // 1. 验签处理(提前失败返回) + String authorization = request.getHeader("Authorization"); + String requestBody = LakalaUtil.getBody(request); + log.debug("回调参数:requestBody={}\nauthorization={}", requestBody, authorization); + + if (!LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath)) { + log.error("验签失败,拒绝处理回调"); + return JSONUtil.createObj().put("code", "FAIL").put("message", "验签失败"); + } + + // 2. 参数解析与校验 + JSONObject paramsJSON = JSONUtil.parseObj(requestBody); + if (paramsJSON == null || !paramsJSON.containsKey("respData")) { + log.error("回调参数缺失respData字段"); + return JSONUtil.createObj().put("code", "FAIL").put("message", "参数格式错误"); + } + + JSONObject respData = paramsJSON.getJSONObject("respData"); + String merCupNo = respData.getStr("merCupNo"); + String applyId = respData.getStr("applyId"); + String auditStatus = respData.getStr("auditStatus"); + + // 3. 核心参数校验 + if (org.apache.commons.lang3.StringUtils.isAnyBlank(merCupNo, applyId, auditStatus)) { + String errorMsg = "关键参数缺失:merCupNo={}, applyId={}, auditStatus={}"; + log.error(errorMsg, merCupNo, applyId, auditStatus); + return JSONUtil.createObj().put("code", "FAIL").put("message", "接收方绑定参数有误"); + } + + // 4. 审核状态判断 + if (!"1".equals(auditStatus)) { + log.warn("绑定审核未通过,状态:{}", auditStatus); + return JSONUtil.createObj().put("code", "FAIL").put("message", "绑定接收方被驳回"); + } + + // 5. 更新绑定记录状态 + Boolean updateSuccess = lklLedgerMerReceiverBindService.updateAuditResult( + applyId, + respData.getStr("merInnerNo"), + merCupNo, + respData.getStr("receiverNo"), + respData.getStr("entrustFileName"), + respData.getStr("entrustFilePath"), + auditStatus, + respData.getStr("auditStatusText"), + respData.getStr("remark") + ); + + if (!updateSuccess) { + log.error("更新绑定记录失败,applyId:{}", applyId); + return JSONUtil.createObj().put("code", "FAIL").put("message", "更新绑定状态失败"); + } + + // 6. 更新商家绑定状态 + shopMchEntryService.updateMulStatus("", merCupNo, 0, 0, 0, 1); + + log.debug("分账商家绑定接收方回调处理完成,merCupNo:{}", merCupNo); + return JSONUtil.createObj().put("code", "SUCCESS").put("message", "操作成功"); + } + + /** + * 银行卡信息查询 + * 参考:https://o.lakala.com/#/home/document/detail?id=179 + * + * @param bankCardNo 银行卡号 + * @return bankCode, bankName, clearingBankCode + */ + @Override + public JSONObject getBankCardBin(String bankCardNo) { + if (StrUtil.isBlank(bankCardNo)) { + return null; + } + + // 1. 配置初始化 + initLKLSDK(); + +// //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; +// } + + return null; + } + + /** + * 查询拉卡拉商户可分账的金额 + * 参考:https://o.lakala.com/#/home/document/detail?id=394 + * + * @param merchantNo 拉卡拉外部商户号 + * @param logNo 拉卡拉对账单流水号 + * @param logDate 拉卡拉对账单交易日期 yyyyMMdd + * @return 响应结果 { + * "merchant_no": "82229005943096D", + * "total_separate_amt": "9900", + * "can_separate_amt": "0", + * "log_date": "20221220", + * "log_no": "66210306990190" + * } + */ + @Override + public JSONObject queryMchCanSplitAmt(String merchantNo, String logNo, String logDate) { + if (StrUtil.isBlank(merchantNo) || (StrUtil.isBlank(logNo) && StrUtil.isBlank(logDate))) { + return null; + } + + // 1. 配置初始化 + initLKLSDK(); + + //2. 装配数据 + V3SacsQueryAmtRequest req = new V3SacsQueryAmtRequest(); + req.setMerchantNo(merchantNo); + req.setLogNo(logNo); + req.setLogDate(logDate); + + try { + //3. 发送请求 + String responseStr = LKLSDK.httpPost(req); + if (StrUtil.isBlank(responseStr)) { + log.error(I18nUtil._("服务器无返回值!")); + return null; + } + + JSONObject lklRespJSON = JSONUtil.parseObj(responseStr); + if (lklRespJSON == null || !lklSacsSuccessCode.equals(lklRespJSON.getStr("code")) || lklRespJSON.get("resp_data") == null) { + log.error(I18nUtil._("返回值有误!")); + return null; + } + + return (JSONObject) lklRespJSON.get("resp_data"); + } catch (SDKException e) { + log.error("账户余额查询失败:", e); + return null; + } + } + + /** + * 拉卡拉订单分账,用户下单成功之后,进行分账 + * 说明:分账指令是异步处理模式,响应报文成功时,指令状态是”status”: “PROCESSING”,需要等待分账结果通知,或者主动发起查询,建议主动发起查询与分账指令动作之间间隔15秒以上。 + * 参考:https://o.lakala.com/#/home/document/detail?id=389 + * + * @param v3SacsSeparateRequest + * @return + */ + @Override + public Pair innerDoOrderSeparate(V3SacsSeparateRequest v3SacsSeparateRequest) { + if (v3SacsSeparateRequest == null) { + 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, "服务器无返回值!"); + } + + 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, "分账发生错误"); + } + } + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklBanksServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklBanksServiceImpl.java new file mode 100644 index 00000000..ff3b762a --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklBanksServiceImpl.java @@ -0,0 +1,117 @@ +/* + * 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.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.modules.lakala.LklBanks; +import com.suisung.mall.common.utils.JiebaUtils; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.lakala.mapper.LklBanksMapper; +import com.suisung.mall.shop.lakala.service.LklBanksService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class LklBanksServiceImpl extends BaseServiceImpl implements LklBanksService { + + @Resource + private JiebaUtils jiebaUtils; + + @Resource + private LklBanksMapper lklBanksMapper; + + /** + * 根据关键字查询有效记录分页列表 + * + * @param keyword + * @param pageNum + * @param pageSize + * @return + */ + @Override + public Page searchBranchBanksPageList(String keyword, Integer pageNum, Integer pageSize) { + if (pageNum == null || pageNum < 1) { + pageNum = 1; + } + if (pageSize == null || pageSize < 1) { + pageSize = 10; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + + queryWrapper.eq("status", CommonConstant.Enable).orderByAsc("branch_bank_no"); + if (StrUtil.isNotBlank(keyword)) { + List keywordList = jiebaUtils.segmentForSearch(keyword); + log.info("分词后的关键词:{}", keywordList); + queryWrapper.and(wrapper -> { +// wrapper.like("branch_bank_name", keyword) +// .or() +// .like("branch_bank_no", keyword) +// .or() +// .like("clear_no", keyword) +// .or() +// .like("bank_no", keyword); + + // 再添加分词后的关键词条件 + + for (String kw : keywordList) { + wrapper.like("branch_bank_name", kw); + } + + }); + } + + return lists(queryWrapper, pageNum, pageSize); + } + + /** + * @param bankNo + * @param branchBankNo + * @param type + * @param keyword + * @param pageNum + * @param pageSize + * @return + */ + @Override + public IPage pageBranchBanksList(String bankNo, String branchBankNo, Integer type, String keyword, Integer pageNum, Integer pageSize) { + Page page = new Page<>(pageNum, pageSize); + return lklBanksMapper.pageBranchBanksList(page, bankNo, branchBankNo, type, jiebaUtils.segmentForSearch(keyword)); + } + + /** + * 根据支行号查询支行信息(带省市地区) + * + * @param branchBankNo 支行号 + * @return + */ + @Override + public Map GetLklBankByBranchBankNo(String branchBankNo) { + if (StrUtil.isBlank(branchBankNo)) { + return null; + } + + IPage list = lklBanksMapper.pageBranchBanksList(new Page<>(1, 1), null, branchBankNo, 2, null); + if (list == null || CollectionUtil.isEmpty(list.getRecords())) { + return null; + } + + return list.getRecords().get(0); + } +} 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 new file mode 100644 index 00000000..2cf0f6ca --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerEcServiceImpl.java @@ -0,0 +1,161 @@ +/* + * 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.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.modules.lakala.LklLedgerEc; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.esign.service.EsignPlatformInfoService; +import com.suisung.mall.shop.lakala.mapper.LklLedgerEcMapper; +import com.suisung.mall.shop.lakala.service.LakalaApiService; +import com.suisung.mall.shop.lakala.service.LklLedgerEcService; +import com.suisung.mall.shop.store.service.ShopMchEntryService; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +@Service +public class LklLedgerEcServiceImpl extends BaseServiceImpl implements LklLedgerEcService { + + + @Resource + private EsignPlatformInfoService esignPlatformInfoService; + + @Lazy + @Resource + private LakalaApiService lakalaApiService; + + @Lazy + @Resource + private ShopMchEntryService shopMchEntryService; + + /** + * 根据商家Id新增或更新记录 + * + * @param record + * @return + */ + @Override + public Boolean saveOrUpdateByMchId(LklLedgerEc record) { + if (record == null || (ObjectUtil.isEmpty(record.getMch_id()) && StrUtil.isBlank(record.getMch_mobile()))) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (ObjectUtil.isEmpty(record.getMch_id())) { + queryWrapper.eq("mch_id", record.getMch_id()); + } + if (ObjectUtil.isEmpty(record.getMch_mobile())) { + queryWrapper.eq("mch_mobile", record.getMch_mobile()); + } + + List existsRecordList = list(queryWrapper); + 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); + } + + + /** + * 根据applyId更新记录 + * + * @param record + * @return + */ + @Override + public Boolean updateByApplyId(LklLedgerEc record) { + if (record == null || ObjectUtil.isEmpty(record.getEc_apply_id())) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("ec_apply_id", record.getEc_apply_id()); + + LklLedgerEc existsRecord = getOne(queryWrapper); + if (existsRecord == null) { + return false; + } + + // 更新记录 + record.setId(existsRecord.getId()); + return updateById(record); + } + + /** + * 根据接applyId查询记录 + * + * @param applyId + * @param ecStatus + * @param status + * @return + */ + @Override + public LklLedgerEc getByApplyId(Long applyId, String ecStatus, Integer status) { + if (ObjectUtil.isEmpty(applyId)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("ec_apply_id", applyId); + if (ObjectUtil.isNotEmpty(status)) { + queryWrapper.eq("status", status); + } + + if (StrUtil.isNotEmpty(ecStatus)) { + queryWrapper.eq("ec_status", ecStatus); + } + + + return getOne(queryWrapper); + } + + /** + * 根据商户手机号查询记录 + * + * @param mchMobile + * @param ecStatus + * @param status + * @return + */ + @Override + public LklLedgerEc getByMchMobile(String mchMobile, String ecStatus, Integer status) { + if (ObjectUtil.isEmpty(mchMobile)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mch_mobile", mchMobile).orderByDesc("id"); + if (ObjectUtil.isNotEmpty(status)) { + queryWrapper.eq("status", status); + } + + if (StrUtil.isNotEmpty(ecStatus)) { + queryWrapper.eq("ec_status", ecStatus); + } + + List lklLedgerEcList = list(queryWrapper); + if (CollectionUtil.isEmpty(lklLedgerEcList)) { + return null; + } + + return lklLedgerEcList.get(0); + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMemberServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMemberServiceImpl.java new file mode 100644 index 00000000..bb354f4c --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMemberServiceImpl.java @@ -0,0 +1,127 @@ +/* + * 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.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.modules.lakala.LklLedgerMember; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.lakala.mapper.LklLedgerMemberMapper; +import com.suisung.mall.shop.lakala.service.LklLedgerMemberService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class LklLedgerMemberServiceImpl extends BaseServiceImpl implements LklLedgerMemberService { + + + /** + * 根据银联商户号查询记录 + * + * @param merCupNo + * @return + */ + @Override + public LklLedgerMember getByMerCupNo(String merCupNo) { + if (StrUtil.isBlank(merCupNo)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_cup_no", merCupNo); + queryWrapper.orderByAsc("id"); + return getOne(queryWrapper); + } + + /** + * 根据申请单号和审核状态查询记录 + * + * @param applyId + * @param auditStatus + * @return + */ + @Override + public LklLedgerMember getByApplyId(String applyId, Integer auditStatus) { + if (StrUtil.isBlank(applyId) || auditStatus == null) { + return null; + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("apply_id", applyId); + queryWrapper.eq("audit_status", auditStatus); + return getOne(queryWrapper); + } + + /** + * 根据银联商户号新增或修改记录 + * + * @param record + * @return + */ + @Override + public Boolean saveOrUpdateByMerCupNo(LklLedgerMember record) { + if (record == null || StrUtil.isBlank(record.getMer_cup_no())) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_cup_no", record.getMer_cup_no()).eq("mch_id", record.getMch_id()); + List existsRecordList = list(queryWrapper); + if (!CollectionUtil.isEmpty(existsRecordList) + && existsRecordList.get(0) != null + && existsRecordList.get(0).getId() > 0) { + // update + record.setId(existsRecordList.get(0).getId()); + return updateById(record); + } + + return add(record); + } + + /** + * 更新审核结果 + * + * @param applyId + * @param merInnerNo + * @param merCupNo + * @param entrustFileName + * @param entrustFilePath + * @param auditStatus + * @param auditStatusText + * @param remark + * @return + */ + @Override + public Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String entrustFileName, String entrustFilePath, String auditStatus, String auditStatusText, String remark) { + if (StrUtil.isBlank(applyId) || StrUtil.isBlank(merCupNo) || StrUtil.isBlank(auditStatus)) { + log.error("缺少参数:applyId={}, merCupNo={}, auditStatus={}", applyId, merCupNo, auditStatus); + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("apply_id", applyId); + updateWrapper.set("mer_inner_no", merInnerNo); + updateWrapper.set("mer_cup_no", merCupNo); + updateWrapper.set("split_entrust_file_name", entrustFileName); + updateWrapper.set("split_entrust_file_path", entrustFilePath); + updateWrapper.set("audit_status", auditStatus); + updateWrapper.set("audit_status_text", auditStatusText); + updateWrapper.set("has_esigned", CommonConstant.Enable); // 电子合同签署完成之后才能做分账申请 + updateWrapper.set("has_apply_split", CommonConstant.Enable); + updateWrapper.set("remark", remark); + + return update(updateWrapper); + } + +} 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 new file mode 100644 index 00000000..004d2064 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerMerReceiverBindServiceImpl.java @@ -0,0 +1,117 @@ +/* + * 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.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.lakala.mapper.LklLedgerMerReceiverBindMapper; +import com.suisung.mall.shop.lakala.service.LklLedgerMerReceiverBindService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl implements LklLedgerMerReceiverBindService { + /** + * 根据接收方编号新增或更新记录 + * + * @param record + * @return + */ + @Override + public Boolean saveOrUpdateByMerCupNoReceiverNo(LklLedgerMerReceiverBind record) { + if (record == null || StrUtil.isBlank(record.getMer_cup_no()) || StrUtil.isBlank(record.getReceiver_no())) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_cup_no", record.getMer_cup_no()) + .eq("receiver_no", record.getReceiver_no()) + .eq("audit_status", CommonConstant.Enable); + List existsRecordList = list(queryWrapper); + if (!CollectionUtil.isEmpty(existsRecordList) + && existsRecordList.get(0) != null + && existsRecordList.get(0).getId() > 0) { + // update + record.setId(existsRecordList.get(0).getId()); + return updateById(record); + } + + return add(record); + } + + public LklLedgerMerReceiverBind getByCondition(String merCupNo, String receiverNo) { + if (StrUtil.isBlank(merCupNo) || StrUtil.isBlank(receiverNo)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mer_cup_no", merCupNo) + .eq("receiver_no", receiverNo) + .eq("audit_status", CommonConstant.Enable); + + + return getOne(queryWrapper); + } + + /** + * 更新审核结果 + * + * @param applyId + * @param merInnerNo + * @param merCupNo + * @param receiverNo + * @param entrustFileName + * @param entrustFilePath + * @param auditStatus + * @param auditStatusText + * @param remark + * @return + */ + @Override + public Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String receiverNo, String entrustFileName, String entrustFilePath, String auditStatus, String auditStatusText, String remark) { + if (StrUtil.isBlank(applyId) || StrUtil.isBlank(merCupNo) || StrUtil.isBlank(receiverNo) || StrUtil.isBlank(auditStatus)) { + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("apply_id", applyId); + if (StrUtil.isNotBlank(merInnerNo)) { + updateWrapper.set("mer_inner_no", merInnerNo); + } + if (StrUtil.isNotBlank(merCupNo)) { + updateWrapper.set("mer_cup_no", merCupNo); + } + if (StrUtil.isNotBlank(receiverNo)) { + updateWrapper.set("receiver_no", receiverNo); + } + if (StrUtil.isNotBlank(entrustFileName)) { + updateWrapper.set("entrust_file_name", entrustFileName); + } + if (StrUtil.isNotBlank(entrustFilePath)) { + updateWrapper.set("entrust_file_path", entrustFilePath); + } + if (StrUtil.isNotBlank(auditStatus)) { + updateWrapper.set("audit_status", auditStatus); + } + if (StrUtil.isNotBlank(auditStatusText)) { + updateWrapper.set("audit_status_text", auditStatusText); + } + if (StrUtil.isNotBlank(remark)) { + updateWrapper.set("remark", remark); + } + + return update(updateWrapper); + } +} 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 new file mode 100644 index 00000000..573a4682 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklLedgerReceiverServiceImpl.java @@ -0,0 +1,314 @@ +/* + * 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.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.api.ResultCode; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.modules.esign.EsignPlatformInfo; +import com.suisung.mall.common.modules.lakala.LklLedgerReceiver; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.esign.service.EsignPlatformInfoService; +import com.suisung.mall.shop.lakala.mapper.LklLedgerReceiverMapper; +import com.suisung.mall.shop.lakala.service.LakalaApiService; +import com.suisung.mall.shop.lakala.service.LklLedgerMemberService; +import com.suisung.mall.shop.lakala.service.LklLedgerReceiverService; +import com.suisung.mall.shop.store.service.ShopMchEntryService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +public class LklLedgerReceiverServiceImpl extends BaseServiceImpl implements LklLedgerReceiverService { + + @Resource + private EsignPlatformInfoService esignPlatformInfoService; + + @Lazy + @Resource + private LakalaApiService lakalaApiService; + + @Lazy + @Resource + private LklLedgerMemberService lklLedgerMemberService; + + @Autowired + private ShopMchEntryService shopMchEntryService; + + /** + * 根据接收方编号新增或更新记录 + * + * @param record + * @return + */ + @Override + public Boolean saveOrUpdateByReceiverNo(LklLedgerReceiver record) { + if (record == null || (StrUtil.isBlank(record.getLicense_no()) && StrUtil.isBlank(record.getContact_mobile()))) { + return false; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotBlank(record.getLicense_no())) { + queryWrapper.eq("license_no", record.getLicense_no()); + } + if (StrUtil.isNotBlank(record.getContact_mobile())) { + queryWrapper.eq("contact_mobile", record.getContact_mobile()); + } + Long platformId = record.getPlatform_id(); + if (ObjectUtil.isEmpty(platformId)) { + platformId = 0L; + } + queryWrapper.eq("platform_id", platformId).eq("status", CommonConstant.Enable); + + List existsRecordList = list(queryWrapper); + 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 receiverNo + * @return + */ + @Override + public LklLedgerReceiver getByReceiverNo(String receiverNo) { + if (StrUtil.isBlank(receiverNo)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("receiver_no", receiverNo).eq("status", CommonConstant.Enable); + queryWrapper.orderByAsc("id"); + + return getOne(queryWrapper); + } + + /** + * 通过平台方(代理商) ID 查询记录 + * + * @param platformId + * @return + */ + @Override + public LklLedgerReceiver getByPlatformId(Long platformId) { + if (ObjectUtil.isEmpty(platformId)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("platform_id", platformId).eq("status", CommonConstant.Enable).orderByAsc("id"); + + List lklLedgerReceivers = list(queryWrapper); + if (CollectionUtil.isEmpty(lklLedgerReceivers)) { + return null; + } + + return lklLedgerReceivers.get(0); + } + + /** + * 通过平台方(代理商) ID 记录信息转成 Lakala 接收方记录 + * 平台方一定要获取,如果有代理商也要获取 + * + * @param platformId + * @return + */ + @Override + public JSONArray buildApplyLedgerReceiverReqParams(Long platformId) { + List ids = new ArrayList<>(); + if (platformId != null && platformId > 0) { + ids.add(platformId); + } + List esignPlatformInfoList = esignPlatformInfoService.getDistributorAndPlatformByIds(ids.toArray(new Long[0])); + if (CollectionUtil.isEmpty(esignPlatformInfoList)) { + return null; + } + + JSONArray reqParams = new JSONArray(); + for (EsignPlatformInfo esignPlatformInfo : esignPlatformInfoList) { + JSONObject reqParam = new JSONObject(); + + reqParam.put("receiverName", esignPlatformInfo.getLicense_company()); + reqParam.put("contactMobile", esignPlatformInfo.getLegal_person_mobile()); + reqParam.put("platformId", esignPlatformInfo.getId()); + reqParam.put("licenseNo", esignPlatformInfo.getLicense_number()); + reqParam.put("licenseName", esignPlatformInfo.getLicense_company()); + reqParam.put("legalPersonName", esignPlatformInfo.getLegal_person_name()); + reqParam.put("legalPersonCertificateType", "17"); + reqParam.put("legalPersonCertificateNo", esignPlatformInfo.getLegal_person_id_card()); + + reqParam.put("acctNo", esignPlatformInfo.getRec_acc_card_no()); + reqParam.put("acctName", esignPlatformInfo.getRec_acc_bank_name()); + reqParam.put("acctTypeCode", "57"); + reqParam.put("acctCertificateType", "17"); + + reqParam.put("acctCertificateNo", esignPlatformInfo.getLegal_person_id_card()); + reqParam.put("acctOpenBankCode", esignPlatformInfo.getRec_acc_bank_no()); + reqParam.put("acctOpenBankName", esignPlatformInfo.getRec_acc_bank_name()); + reqParam.put("acctClearBankCode", esignPlatformInfo.getRec_acc_clear_bank_no()); + + //文件类型参考:https://o.lakala.com/#/home/document/detail?id=90 + JSONArray attachList = new JSONArray(); + if (StrUtil.isNotBlank(esignPlatformInfo.getLicense_image())) { + JSONObject BUSINESS_LICENCE = new JSONObject(); + BUSINESS_LICENCE.put("attachType", "BUSINESS_LICENCE").put("attachName", "营业执照") + .put("attachStoreFile", esignPlatformInfo.getLicense_image()); + attachList.add(BUSINESS_LICENCE); + } + + if (StrUtil.isNotBlank(esignPlatformInfo.getLegal_person_id_card_image1())) { + JSONObject FR_ID_CARD_FRONT = new JSONObject(); + FR_ID_CARD_FRONT.put("attachType", "FR_ID_CARD_FRONT").put("attachName", "法人身份证正面") + .put("attachStoreFile", esignPlatformInfo.getLegal_person_id_card_image1()); + attachList.add(FR_ID_CARD_FRONT); + } + + if (StrUtil.isNotBlank(esignPlatformInfo.getLegal_person_id_card_image2())) { + JSONObject FR_ID_CARD_BEHIND = new JSONObject(); + FR_ID_CARD_BEHIND.put("attachType", "FR_ID_CARD_BEHIND").put("attachName", "法人身份证反面") + .put("attachStoreFile", esignPlatformInfo.getLegal_person_id_card_image2()); + attachList.add(FR_ID_CARD_BEHIND); + } + + if (CollectionUtil.isNotEmpty(attachList)) { + reqParam.put("attachList", attachList); + } + + reqParams.add(reqParam); + } + + return reqParams; + } + + /** + * 内部调用:申请一个或多个分账接收方(平台方和代理商) + * + * @param platformId + * @return + */ + @Override + public Boolean innerApplyLedgerReceiver(String merCupNo, Long platformId) { + // 接收方至少有一个平台方 + JSONArray buildApplyLedgerReceiverReqParams = buildApplyLedgerReceiverReqParams(platformId); + if (buildApplyLedgerReceiverReqParams == null || buildApplyLedgerReceiverReqParams.isEmpty()) { + log.error("获取不到平台或代理商信息"); + return false; + } + + int successCnt = 0; + for (JSONObject reqParam : buildApplyLedgerReceiverReqParams.jsonIter()) { + log.debug("申请分账接收方参数:{}", reqParam.toString()); + CommonResult result = lakalaApiService.applyLedgerReceiver(reqParam); + if (result == null || result.getStatus() != ResultCode.SUCCESS.getStatus()) { + log.error("申请分账接收方失败:{}", result.getMsg()); + continue; + } + + successCnt += 1; + } + + boolean success = successCnt > 0; + if (success) { + // 更新多个状态 + shopMchEntryService.updateMulStatus("", merCupNo, 0, 0, 1, 0); + } + + return success; + } + + /** + * 是否存在平台方的相关记录信息? + * + * @return + */ + @Override + public LklLedgerReceiver hasPlatformMch() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("platform_id", 0).eq("status", CommonConstant.Enable).orderByAsc("id"); + + List lklLedgerReceivers = list(queryWrapper); + if (CollectionUtil.isEmpty(lklLedgerReceivers)) { + return null; + } + + return lklLedgerReceivers.get(0); + } + + /** + * 根据条件查询记录 + * + * @param LicenseNo + * @param ContactMobile + * @param platformId + * @return + */ + @Override + public LklLedgerReceiver getByCondition(String LicenseNo, String ContactMobile, Long platformId) { + if (ObjectUtil.isEmpty(platformId)) { + platformId = 0L; + } + if (StrUtil.isBlank(LicenseNo) && StrUtil.isBlank(ContactMobile)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotBlank(LicenseNo)) { + queryWrapper.eq("license_no", LicenseNo); + } + if (StrUtil.isNotBlank(ContactMobile)) { + queryWrapper.eq("contact_mobile", ContactMobile); + } + + queryWrapper.eq("platform_id", platformId) + .eq("status", CommonConstant.Enable); + + return getOne(queryWrapper); + } + + @Override + public List getByCondition(String LicenseNo, String ContactMobile) { + + if (StrUtil.isBlank(LicenseNo) && StrUtil.isBlank(ContactMobile)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotBlank(LicenseNo)) { + queryWrapper.eq("license_no", LicenseNo); + } + if (StrUtil.isNotBlank(ContactMobile)) { + queryWrapper.eq("contact_mobile", ContactMobile); + } + + queryWrapper.eq("status", CommonConstant.Enable); + + return list(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 new file mode 100644 index 00000000..266eed9b --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java @@ -0,0 +1,838 @@ +/* + * 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.collection.CollUtil; +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.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.api.ResultCode; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.domain.UserDto; +import com.suisung.mall.common.modules.store.ShopMchEntry; +import com.suisung.mall.common.utils.RestTemplateHttpUtil; +import com.suisung.mall.common.utils.StringUtils; +import com.suisung.mall.common.utils.UploadUtil; +import com.suisung.mall.core.web.service.RedisService; +import com.suisung.mall.shop.lakala.service.LklBanksService; +import com.suisung.mall.shop.lakala.service.LklLedgerReceiverService; +import com.suisung.mall.shop.lakala.utils.LakalaUtil; +import com.suisung.mall.shop.page.service.impl.OssServiceImpl; +import com.suisung.mall.shop.store.service.ShopMchEntryService; +import com.suisung.mall.shop.store.service.ShopStoreBaseService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.util.Pair; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + +import static com.suisung.mall.common.utils.ContextUtil.getCurrentUser; + +@Service +public class LklTkServiceImpl { + + private static final Logger logger = LoggerFactory.getLogger(LklTkServiceImpl.class); + + @Value("${lakala.tk.server_url}") + private String tkServerUrl; + + @Value("${lakala.tk.client_id}") + private String clientId; + + @Value("${lakala.tk.client_secret}") + private String clientSecret; + + @Value("${lakala.tk.user_no}") + private String userNo; + + @Value("${lakala.tk.notify_pub_key_path}") + private String notifyPubKeyPath; + + @Value("${spring.profiles.active}") + private String profile; + + @Value("${lakala.is_prod}") + private Boolean isLklProd; + + @Lazy + @Resource + private ShopMchEntryService shopMchEntryService; + + @Lazy + @Resource + private LklLedgerReceiverService lklLedgerReceiverService; + +// @Lazy +// @Resource +// private EsignContractService esignContractService; +// +// @Lazy +// @Resource +// private EsignContractFillingFileService esignContractFillingFileService; + + @Lazy + @Resource + private ShopStoreBaseService shopStoreBaseService; + + @Lazy + @Resource + private LakalaApiServiceImpl lakalaApiService; + + @Lazy + @Resource + private LklBanksService lklBanksService; + + @Lazy + @Resource + private OssServiceImpl ossService; + + @Autowired + private RedisService redisService; + + + protected String buildLklTkUrl(String urlPath) { + return tkServerUrl + urlPath; + } + + protected boolean isProdProject() { + // 生产环境的时候启动该配置 + return "prod".equalsIgnoreCase(profile); + } + + /** + * 上传文件(图片)到拉卡拉,带 OCR识别卡信息,身份证信息,营业执照信息 + * + * @param file 图片URL地址 + * @param imgType 法人身份证正面图:FR_ID_CARD_FRONT; + * 法人身份证反面:FR_ID_CARD_BEHIND; + * 身份证正面:ID_CARD_FRONT; + * 身份证反面:ID_CARD_BEHIND; + * 营业执照:BUSINESS_LICENCE; + * 银行卡:BANK_CARD + * @return + */ + public CommonResult uploadOcrImg(MultipartFile file, String imgType) { + UserDto currentUser = getCurrentUser(); + if (currentUser == null) { + currentUser = new UserDto(); + } + + if (file == null || StrUtil.isBlank(imgType)) { + return CommonResult.failed("上传文件或图片类型不能为空"); + } + + CommonResult ossImgInfo = ossService.uploadFile(file, currentUser); + if (ossImgInfo == null) { + return CommonResult.failed("上传文件失败"); + } + + if (ossImgInfo.getStatus() != ResultCode.SUCCESS.getStatus() || ossImgInfo.getData() == null) { + return CommonResult.failed(ossImgInfo.getMsg()); + } + + String imgURL = JSONUtil.parseObj(ossImgInfo.getData()).getStr("url"); + + String authorization = getLklTkAuthorization(); + if (StrUtil.isBlank(authorization)) { + return CommonResult.failed("获取拉卡拉token失败"); + } + + JSONObject header = new JSONObject(); + header.put("Authorization", authorization); + + + String fileBase64 = UploadUtil.multipartFileToBase64(file); + if (StrUtil.isBlank(fileBase64)) { + return CommonResult.failed("解析文件转换失败"); + } + // Base64Utils.encodeToString(file.getBytes()); + + JSONObject requestBody = new JSONObject(); + requestBody.put("fileBase64", fileBase64); + requestBody.put("imgType", imgType); + requestBody.put("sourcechnl", "0"); // 来源: 0:PC,1:安卓,2:IOS + requestBody.put("isOcr", "true"); + + String urlPath = "/sit/htkregistration/file/base/upload"; + if (isLklProd) { + // 生产环境启用 + urlPath = "/registration/file/base/upload"; + } + + try { + ResponseEntity updResponse = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, requestBody, JSONObject.class); + if (ObjectUtil.isEmpty(updResponse) + || updResponse.getStatusCode() != HttpStatus.OK + || ObjectUtil.isEmpty(updResponse.getBody())) { + return CommonResult.failed("上传文件返回值有误"); + } + + // {batchNo,status,url,showUrl,result{} } + JSONObject updObj = updResponse.getBody(); + String batchNo = updObj.getStr("batchNo"); + if (StrUtil.isBlank(batchNo)) { + return CommonResult.failed("上传文件返回值有误"); + } + + updObj.put("cosURL", imgURL); + + return CommonResult.success(updObj); + } catch (Exception e) { + logger.error("上传文件失败: ", e.getMessage()); + return CommonResult.failed("上传文件失败:" + e.getMessage()); + } + } + + /** + * 根据上传的图片的批次号,获取 OCR 识别结果 + * + * @param batchNo + * @param imgType * ID_CARD_FRONT 身份证正⾯ + * * ID_CARD_BEHIND 身份证反⾯ + * * BUSINESS_LICENCE 营业执照照⽚ + * * BANK_CARD 银行卡(企业对公不需要传) + * * AGREE_MENT 协议 + * * OPENING_PERMIT 开户许可证(企业对公需要传) + * * CHECKSTAND_IMG 收银台照片(必传) + * * SHOP_OUTSIDE_IMG 上传门头照片(必传) + * * SHOP_INSIDE_IMG 商铺内部照片(必传) + * * OTHERS 其他 无对应类型图片,请传其他类型 + * * SETTLE_ID_CARD_FRONT 结算人身份证人像面 + * * SETTLE_ID_CARD_BEHIND 结算人身份证国徽面 + * * LETTER_OF_AUTHORIZATION 法人授权涵 + * @return + */ + public CommonResult imgOcrResult(String batchNo, String imgType) { + if (StrUtil.isBlank(batchNo) || StrUtil.isBlank(imgType)) { + return CommonResult.failed("批次号或图片类型不能为空"); + } + + // 调用 OCR 识别接口 + String authorization = getLklTkAuthorization(); + if (StrUtil.isBlank(authorization)) { + return CommonResult.failed("获取拉卡拉token失败"); + } + + JSONObject header = new JSONObject(); + header.put("Authorization", authorization); + + + JSONObject ocrRequestBody = new JSONObject(); + ocrRequestBody.put("batchNo", batchNo); + ocrRequestBody.put("imgType", imgType); + logger.info("ocr请求参数:{}", ocrRequestBody); + + String urlPath = "/sit/htkregistration/ocr/result"; + if (isLklProd) { + // 生产环境启用 + urlPath = "/registration/ocr/result"; + } + + try { + ResponseEntity ocrResponse = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, ocrRequestBody, JSONObject.class); + if (ObjectUtil.isEmpty(ocrResponse) + || ocrResponse.getStatusCode() != HttpStatus.OK + || ObjectUtil.isEmpty(ocrResponse.getBody())) { + return CommonResult.failed("OCR响应数据有误"); + } + + JSONObject ocrObj = ocrResponse.getBody().get("result", JSONObject.class); + logger.info("ocr返回结果:{}", ocrResponse); + if (ObjectUtil.isEmpty(ocrObj)) { + return CommonResult.failed("OCR返回结果有误"); + } + + return CommonResult.success(ocrObj); + } catch (Exception e) { + logger.error("OCR识别失败: ", e.getMessage()); + return CommonResult.failed("OCR识别失败:" + e.getMessage()); + } + } + + /** + * (商户进件前)请求获取token + * + * @return AuthorizationKey + */ + public String getLklTkAuthorization() { + String authorizationKey = "lkl_tk_authorization"; + Object lklTkToken = redisService.get(authorizationKey); + if (ObjectUtil.isNotEmpty(lklTkToken)) { + return lklTkToken.toString(); + } + + Map formData = new HashMap<>(); + formData.put("grant_type", "client_credentials"); + formData.put("client_id", clientId); + formData.put("client_secret", clientSecret); + + String urlPath = "/sit/htkauth/oauth/token"; + if (isLklProd) { + // 生产环境启用 + urlPath = "/auth/oauth/token"; + } + + + String response = RestTemplateHttpUtil.sendPostFormData(buildLklTkUrl(urlPath), null, formData, String.class); + if (ObjectUtil.isEmpty(response)) { + return ""; + } + + JSONObject jsonObj = JSONUtil.parseObj(response); + if (jsonObj == null || StrUtil.isBlank(jsonObj.getStr("access_token"))) { + return ""; + } + + String token = jsonObj.getStr("token_type") + " " + jsonObj.getStr("access_token"); + redisService.set(authorizationKey, token, jsonObj.getLong("expires_in")); + + return token; + } + + /** + * (重要) 请求拉卡拉进件 + *

+ * 参考拉卡拉给的独立文档:2、拓客SAAS商户管理接口(新).docx + * + * @param mchMobile 商家手机号 + * @return + */ + public Pair registrationMerchant(String mchMobile, String bizLicenseNumber) { + String authorization = getLklTkAuthorization(); + if (StrUtil.isBlank(authorization)) { + return Pair.of(false, "获取拉卡拉token失败"); + } + + JSONObject header = new JSONObject(); + header.put("Authorization", getLklTkAuthorization()); + + // 获取商家入驻信息,组成请求参数 + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByCondition(mchMobile, bizLicenseNumber); + if (ObjectUtil.isEmpty(shopMchEntry)) { + return Pair.of(false, "商家入驻信息不存在"); + } + + JSONObject formData = new JSONObject(); + formData.put("userNo", userNo); + formData.put("busiCode", "WECHAT_PAY");// WECHAT_PAY:专业化扫码;B2B_SYT:B2B收银台; + formData.put("email", shopMchEntry.getEmail()); + formData.put("merRegName", shopMchEntry.getStore_name()); + formData.put("merName", shopMchEntry.getStore_name()); + formData.put("merAddr", StringUtils.removeProvinceCityDistrict(shopMchEntry.getStore_address())); + + // 是企业类型商家 + Boolean isQy = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()); + formData.put("merType", isQy ? "TP_MERCHANT" : "TP_PERSONAL"); + + + formData.put("longtude", shopMchEntry.getStore_longitude()); //longitude 经度 + formData.put("latitude", shopMchEntry.getStore_latitude()); + formData.put("source", "H5"); + + + formData.put("larIdType", "01"); // 01 身份证 ,02 护照,03 港澳通行证,04 台胞证,10 外国人永久居留身份证,11 港妨澳居民居住证,12 台湾居民居住证,13 执行事务合伙人,99 其它证件 + String larName = isQy ? shopMchEntry.getLegal_person_name() : shopMchEntry.getContact_name(); + String larIdCard = isQy ? shopMchEntry.getLegal_person_id_number() : shopMchEntry.getIndividual_id_number(); + String larIdCardStart = isQy ? shopMchEntry.getLegal_person_id_period_begin() : shopMchEntry.getIndividual_id_period_begin(); + String larIdCardEnd = isQy ? shopMchEntry.getLegal_person_id_period_end() : shopMchEntry.getIndividual_id_period_end(); + + // 法人相关信息 + formData.put("larName", larName); + formData.put("larIdCard", larIdCard); + formData.put("larIdCardStart", larIdCardStart); // 身份证有效期开始时间 + formData.put("larIdCardEnd", larIdCardEnd); // 身份证有效期结束时间,长期:9999-12-31 + + // 营业执照上的经营内容 + formData.put("businessContent", shopMchEntry.getBiz_license_content()); + + // 营业执照信息 + if (isQy) { + formData.put("licenseName", shopMchEntry.getBiz_license_company()); + formData.put("licenseNo", shopMchEntry.getBiz_license_number()); + formData.put("licenseDtStart", shopMchEntry.getBiz_license_period_begin()); + formData.put("licenseDtEnd", shopMchEntry.getBiz_license_period_end()); // 长期:9999-12-31 + } + + formData.put("contactMobile", mchMobile); + formData.put("contactName", shopMchEntry.getContact_name());// 联系人姓名 + formData.put("contractNo", shopMchEntry.getLkl_ec_no()); // 拉卡拉入网合同编号 + + // 银行账号关键字段 + formData.put("openningBankCode", shopMchEntry.getOpenning_bank_code());//结算账户开户⾏号 + formData.put("openningBankName", shopMchEntry.getBank_name());//结算账户开户⾏名称 + formData.put("clearingBankCode", shopMchEntry.getClearing_bank_code());//结算账户清算⾏号 + + formData.put("accountType", isQy ? "57" : "58"); //结算账户类型: 57 对公 58 对私 + formData.put("accountNo", shopMchEntry.getAccount_number()); //结算人银行卡号 + formData.put("accountName", shopMchEntry.getAccount_holder_name()); //结算人账户名称 + formData.put("accountIdCard", shopMchEntry.getLegal_person_id_number());//结算⼈证件号码(身份证) + + formData.put("settleType", "D1"); //结算类型,D0秒到,D1次日结算 + // formData.put("settlementType", "AUTOMATIC"); // 结算方式:MANUAL:手动结算(结算至拉卡拉APP钱包),AUTOMATIC:自动结算到银行卡,REGULAR:定时结算(仅企业商户支持) + + // 店铺省市区信息 + Map areaCode = getAreaCode(shopMchEntry.getStore_area(), false); + if (ObjectUtil.isNotEmpty(areaCode)) { + formData.put("provinceCode", areaCode.get("provinceCode")); + formData.put("cityCode", areaCode.get("cityCode")); + formData.put("countyCode", areaCode.get("countyCode")); + } + + //结算信息省市信息 + if (StrUtil.isBlank(shopMchEntry.getBank_district()) || StrUtil.isBlank(shopMchEntry.getBank_area())) { + Map bankInfo = lklBanksService.GetLklBankByBranchBankNo(shopMchEntry.getOpenning_bank_code()); + if (ObjectUtil.isNotEmpty(bankInfo)) { + shopMchEntry.setBank_district(bankInfo.get("district").toString()); + shopMchEntry.setBank_area(bankInfo.get("area").toString()); + } + } + + if (StrUtil.isNotBlank(shopMchEntry.getBank_district()) && StrUtil.isNotBlank(shopMchEntry.getBank_area())) { + // 直接分解取值 + String[] bankAreaCodes = shopMchEntry.getBank_district().split("/"); + String[] bankAreaNames = shopMchEntry.getBank_area().split("/"); + if (bankAreaCodes.length >= 2 && bankAreaNames.length >= 2) { + formData.put("settleProvinceCode", bankAreaCodes[0]); + formData.put("settleProvinceName", bankAreaCodes[1]); + formData.put("settleCityCode", bankAreaNames[0]); + formData.put("settleCityName", bankAreaNames[1]); + } + } else { + Map bankAreaCode = getAreaCode(shopMchEntry.getBank_area(), true); + if (ObjectUtil.isNotEmpty(bankAreaCode)) { + formData.put("settleProvinceCode", bankAreaCode.get("provinceCode")); + formData.put("settleCityCode", bankAreaCode.get("cityCode")); + String[] bankAreaName = shopMchEntry.getBank_area().split("/"); + if (bankAreaName.length >= 2) { + formData.put("settleProvinceName", bankAreaName[0]); + formData.put("settleCityName", bankAreaName[1]); + } + } + } + + // 费率和设备、活动 + JSONObject bizContent = new JSONObject(); + bizContent.put("activityId", 687); + bizContent.put("termNum", "1"); + bizContent.put("mcc", "12015"); // 超市的 code + bizContent.put("fees", new JSONArray() {{ + put(new JSONObject() { + { + put("feeCode", "WECHAT"); + put("feeValue", 0.6); + } + }); + }}); + formData.put("bizContent", bizContent); + + // 附件文件相关开始 + JSONArray attachments = new JSONArray(); + if (isQy) { + JSONObject SETTLE_ID_CARD_FRONT = updatePhoto(shopMchEntry.getLegal_person_id_images(), "FR_ID_CARD_FRONT", false); + if (SETTLE_ID_CARD_FRONT != null) { + attachments.put(SETTLE_ID_CARD_FRONT); // 法人身份证正面 + } + + JSONObject SETTLE_ID_CARD_BEHIND = updatePhoto(shopMchEntry.getLegal_person_id_images2(), "FR_ID_CARD_BEHIND", false); + if (SETTLE_ID_CARD_BEHIND != null) { + attachments.put(SETTLE_ID_CARD_BEHIND); // 法人身份证国徽面 + } + + JSONObject BUSINESS_LICENCE = updatePhoto(shopMchEntry.getBiz_license_image(), "BUSINESS_LICENCE", false); + if (BUSINESS_LICENCE != null) { + attachments.put(BUSINESS_LICENCE); // 营业执照 + } + + } else { + JSONObject ID_CARD_FRONT = updatePhoto(shopMchEntry.getIndividual_id_images(), "ID_CARD_FRONT", false); + if (ID_CARD_FRONT != null) { + attachments.put(ID_CARD_FRONT); // 身份证正面 + } + + JSONObject ID_CARD_BEHIND = updatePhoto(shopMchEntry.getIndividual_id_images2(), "ID_CARD_BEHIND", false); + if (ID_CARD_BEHIND != null) { + attachments.put(ID_CARD_BEHIND); // 身份证国徽面 + } + } + + + JSONObject SHOP_OUTSIDE_IMG = updatePhoto(shopMchEntry.getFront_facade_image(), "SHOP_OUTSIDE_IMG", false); + if (SHOP_OUTSIDE_IMG != null) { + attachments.put(SHOP_OUTSIDE_IMG); // 门店门面图片 + } + + JSONObject SHOP_INSIDE_IMG = updatePhoto(shopMchEntry.getEnvironment_image(), "SHOP_INSIDE_IMG", false); + if (SHOP_INSIDE_IMG != null) { + attachments.put(SHOP_INSIDE_IMG); // 门店内部图片 + } + + JSONObject BANK_CARD = updatePhoto(shopMchEntry.getBank_image(), "BANK_CARD", false); + if (BANK_CARD != null) { + attachments.put(BANK_CARD); // 银行卡图片 + } + formData.put("attchments", attachments); + // 附件文件相关结束 + + String urlPath = "/sit/htkregistration/merchant"; + if (isLklProd) { + // 生产环境启用 + urlPath = "/registration/merchant"; + } + + +// String urlPath = isProd() ? "/registration/merchant" : "/sit/htkregistration/merchant"; + try { + logger.info("进件请求参数:{}", JSONUtil.toJsonStr(formData)); + + ResponseEntity response = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, formData, JSONObject.class); + if (ObjectUtil.isEmpty(response)) { + return Pair.of(false, "请求进件服务无返回值"); + } + + JSONObject respBody = response.getBody(); + if (response.getStatusCode() != HttpStatus.OK && ObjectUtil.isNotEmpty(respBody)) { + String errMsg = respBody.getStr("message") == null ? "返回状态有误" : respBody.getStr("message"); + return Pair.of(false, "进件失败:" + errMsg); + } + + // 更改入驻记录的拉卡拉内部商户号和进件请求参数 + String lklMerInnerNo = respBody.getStr("merchantNo"); //拉卡拉内部商户号 + // 表中的内部外部商户号暂时都传同一个内部商户号,以便异步通知更改记录 + Boolean success = shopMchEntryService.updateMerchEntryLklMerCupNo(mchMobile, CommonConstant.Disable2, lklMerInnerNo, lklMerInnerNo, formData.toString(), respBody.toString()); + if (!success) { + return Pair.of(false, "请求进件成功,但更新商户号失败!"); + } + + } catch (Exception e) { + logger.error("拉卡拉进件异常:{}", e.getMessage()); + return Pair.of(false, "进件失败:" + e.getMessage()); + } + + return Pair.of(true, "提交进件成功,请等待审核!"); + } + + /** + * (重要)拉卡拉进件异步通知 + * + * @param request + * @return + */ + @Transactional + public JSONObject registrationMerchantNotify(HttpServletRequest request) { + logger.debug("拉卡拉进件异步通知开始"); + + // 解密请求参数 + String requestBody = LakalaUtil.getBody(request); + logger.debug("拉卡拉进件异步通知返回参数:{}", requestBody); + if (StrUtil.isBlank(requestBody)) { + return new JSONObject().set("code", "400").set("message", "返回参数为空"); + } + + JSONObject reqBodyJSON = JSONUtil.parseObj(requestBody); + if (reqBodyJSON.isEmpty() || reqBodyJSON.get("data") == null) { + return new JSONObject().set("code", "400").set("message", "参数格式有误,无法解析"); + } + + String srcData = reqBodyJSON.getStr("data"); + if (StrUtil.isBlank(srcData)) { + return new JSONObject().set("code", "400").set("message", "关键参数为空值"); + } + + // 公钥解密出来的数据 + 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", "密文解密出错!"); + } + + logger.debug("拉卡拉进件异步通知data解密成功,开始处理逻辑"); + + // 逻辑处理 + JSONObject dataJSON = JSONUtil.parseObj(data); + if (dataJSON.isEmpty() || StrUtil.isBlank(dataJSON.getStr("externalCustomerNo")) || StrUtil.isBlank(dataJSON.getStr("status"))) { + return new JSONObject().set("code", "500").set("message", "参数解析出错"); + } + + String auditStatus = dataJSON.getStr("status"); + if (!"SUCCESS".equals(auditStatus) && !"REVIEW_PASS".equals(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 + " 入驻信息不存在"); + } + + Boolean success = shopMchEntryService.updateMerchEntryLklAuditStatusByLklMerCupNo(merInnerNo, merCupNo, termNos, CommonConstant.Enable, null, data); + if (!success) { + return new JSONObject().set("code", "500").set("message", "更新商户号失败"); +// throw new ApiException("更新商户号失败"); + } + + // 备注:RMK 采用拉卡拉的入网电子合同签署流程,暂停e签宝的电子合同生成流程 +// logger.debug("###开始异步执行生成电子合同模版和填充模版数据,并生该商家和平台方签署的未盖章合同文件###"); +// // 生成电子合同模版和填充模版数据,并生该商家和平台方签署的未盖章合同文件 +// Boolean genSuccess = esignContractFillingFileService.fillDocTemplate(shopMchEntry.getLogin_mobile(), ""); +// if (!genSuccess) { +// logger.error("###商家入驻电子合同生成失败###"); +// } +// +// // 1、发起E签宝合同签署;签署完成之后,生成分账盖章协议书,下一步才能申请分账功能权限; +// Pair resPair = esignContractService.innerSignFlowCreateByFile(shopMchEntry.getLogin_mobile()); +// if (!resPair.getFirst()) { +// logger.error("###商家发起电子签名失败:{}###", resPair.getSecond()); +// } + + // 给商家申请分账功能使用;务必检查是否申请过?申请过忽略 + 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("商家进件:初始化店铺失败!"); +// } +// } + + // 1、(电子合同)给商家申请分账功能使用;务必检查是否申请过?申请过忽略 + // 下一步等待拉卡拉审核通过,再绑定接收方和商家的关系 + Pair retPair = lakalaApiService.innerApplyLedgerMer(merCupNo); + + // 2:新增一个接收方记录,起码要一个平台方,代理商根据入驻信息新增 + Boolean genSuccess = lklLedgerReceiverService.innerApplyLedgerReceiver(merCupNo, shopMchEntry.getDistributor_id()); + + if (retPair.getFirst() && genSuccess) { + return new JSONObject().put("code", "200").put("message", "处理成功"); + } + + if (!retPair.getFirst()) { + String message = "商家申请分账功能失败:" + retPair.getSecond(); + logger.error(message); + return new JSONObject().set("code", "500").set("message", message); + } + + if (!genSuccess) { + logger.error("申请分账接收方失败"); + return new JSONObject().set("code", "500").set("message", "申请分账接收方失败"); + } + } + + return new JSONObject().set("code", "500").set("message", "进件回调处理失败"); + } + + /** + * 获取拉卡拉店铺的(或银行的)省市区编码 + * + * @param areaName 省份/城市/乡县 + * @return + */ + public Map getAreaCode(String areaName, Boolean isBankArea) { + Map retMap = new HashMap() {{ + put("provinceCode", ""); + put("cityCode", ""); + put("countyCode", ""); + }}; + + + String[] areaNames = areaName.split("/"); + if (StrUtil.isBlank(areaName) || areaNames.length < 2) { + return retMap; + } + + String provinceName = areaNames[0]; + // 因为5个自治区需要简称,所以去掉 壮族,回族,维吾尔族 + provinceName = StrUtil.replace(provinceName, "壮族", "") + .replace("回族", "") + .replace("维吾尔族", "").replace("自治区", ""); + String cityName = areaNames[1]; + String countryName = ""; + if (areaNames.length >= 3) { + countryName = areaNames[2]; + } + + String authorization = getLklTkAuthorization(); + if (StrUtil.isBlank(authorization)) { + logger.error("获取拉卡拉token失败"); + return retMap; + } + + JSONObject header = new JSONObject(); + header.put("Authorization", getLklTkAuthorization()); + + String urlPath = "/sit/htkregistration/organization"; + if (isLklProd) { + // 生产环境启用 + urlPath = "/registration/organization"; + } + urlPath = isBankArea ? urlPath + "/bank" : urlPath; // 银行地区 + + + // 省份列表 + ResponseEntity response = RestTemplateHttpUtil.sendGetWithHeader(buildLklTkUrl(urlPath + "/1"), header, JSONArray.class); + if (ObjectUtil.isNotEmpty(response) && response.getStatusCode() == HttpStatus.OK) { + String provinceCode = ""; + String cityCode = ""; + String countyCode = ""; + + JSONArray jsonArray = response.getBody(); + if (CollUtil.isNotEmpty(jsonArray)) { +// logger.info("省份列表:{}", jsonArray.toString()); + // 省份列表 + for (JSONObject jsonObject : jsonArray.jsonIter()) { + if (StrUtil.contains(jsonObject.getStr("name"), provinceName)) { + provinceCode = jsonObject.getStr("code"); + retMap.put("provinceCode", provinceCode); + break; + } + } + + // 城市列表 + if (StrUtil.isNotBlank(provinceCode)) { + response = RestTemplateHttpUtil.sendGetWithHeader(buildLklTkUrl(urlPath + "/" + provinceCode), header, JSONArray.class); + jsonArray = response.getBody(); + if (CollUtil.isNotEmpty(jsonArray)) { +// logger.info("城市列表:{}", jsonArray.toString()); + for (JSONObject jsonObject : jsonArray.jsonIter()) { + if (StrUtil.contains(jsonObject.getStr("name"), cityName)) { + cityCode = jsonObject.getStr("code"); + retMap.put("cityCode", cityCode); + break; + } + } + } + } + + // 乡县列表 + if (StrUtil.isNotBlank(cityCode)) { + response = RestTemplateHttpUtil.sendGetWithHeader(buildLklTkUrl(urlPath + "/" + cityCode), header, JSONArray.class); + jsonArray = response.getBody(); + if (CollUtil.isNotEmpty(jsonArray)) { +// logger.info("区列表:{}", jsonArray.toString()); + for (JSONObject jsonObject : jsonArray.jsonIter()) { + if (StrUtil.contains(jsonObject.getStr("name"), countryName)) { + countyCode = jsonObject.getStr("code"); + retMap.put("countyCode", countyCode); + break; + } + } + } + } + } + } + + return retMap; + } + + /** + * 上传进件相关图片和类型,返回图片ID和类型 + * + * @param fileUrl + * @param imgType ID_CARD_FRONT 身份证正⾯ + * ID_CARD_BEHIND 身份证反⾯ + * FR_ID_CARD_FRONT 身份证正⾯ + * FR_ID_CARD_BEHIND 身份证反⾯ + * BUSINESS_LICENCE 营业执照照⽚ + * BANK_CARD 银行卡(企业对公不需要传) + * AGREE_MENT 协议 + * OPENING_PERMIT 开户许可证(企业对公需要传) + * CHECKSTAND_IMG 收银台照片(必传) + * SHOP_OUTSIDE_IMG 上传门头照片(必传) + * SHOP_INSIDE_IMG 商铺内部照片(必传) + * OTHERS 其他 无对应类型图片,请传其他类型 + * SETTLE_ID_CARD_FRONT 结算人身份证人像面 + * SETTLE_ID_CARD_BEHIND 结算人身份证国徽面 + * LETTER_OF_AUTHORIZATION 法人授权涵 + * @param isOcr + * @return + */ + public JSONObject updatePhoto(String fileUrl, String imgType, Boolean isOcr) { + if (StrUtil.isBlank(fileUrl) || StrUtil.isBlank(imgType)) { + return null; + } + + JSONObject header = new JSONObject(); + header.put("Authorization", getLklTkAuthorization()); + + String fileBase64 = UploadUtil.fileUrlToBase64(fileUrl); + if (StrUtil.isBlank(fileBase64)) { + logger.error("文件转换失败"); + return null; + } + // Base64Utils.encodeToString(file.getBytes()); + + JSONObject requestBody = new JSONObject(); + requestBody.put("fileBase64", fileBase64); + requestBody.put("imgType", imgType); + requestBody.put("sourcechnl", "0"); // 来源: 0:PC,1:安卓,2:IOS + requestBody.put("isOcr", "true"); + + String urlPath = "/sit/htkregistration/file/base/upload"; + if (isLklProd) { + // 生产环境启用 + urlPath = "/registration/file/base/upload"; + } + + try { + ResponseEntity updResponse = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, requestBody, JSONObject.class); + if (ObjectUtil.isEmpty(updResponse) + || updResponse.getStatusCode() != HttpStatus.OK) { + logger.error("上传文件返回值有误"); + return null; + } + + JSONObject result = updResponse.getBody(); + if (ObjectUtil.isEmpty(result)) { + return null; + } + + return new JSONObject().put("id", result.get("url")).put("type", imgType); + } catch (Exception e) { + logger.error("上传文件异常:{}", e.getMessage()); + return null; + } + } + + +} 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 new file mode 100644 index 00000000..1f9e943d --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/utils/LakalaUtil.java @@ -0,0 +1,406 @@ +/* + * 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.utils; + +import cn.hutool.core.util.StrUtil; +import com.lkl.laop.sdk.Config2; +import com.lkl.laop.sdk.LKLSDK; +import com.suisung.mall.common.exception.ApiException; +import com.suisung.mall.common.utils.I18nUtil; +import com.suisung.mall.common.utils.RSAUtil; +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 javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.cert.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +public class LakalaUtil { + + /** + * 初始化 拉卡拉 SDK(全局只需设置一次) + * + * @return + */ + public static boolean initLKLSDK(String appId, String serialNo, String priKeyPath, String lklCerPath, String lklNotifyCerPath, String serverUrl) { + Config2 config = new Config2(); + config.setAppId(appId); + config.setSerialNo(serialNo); + config.setPriKey(getResourceFile(priKeyPath)); + config.setLklCer(getResourceFile(lklCerPath)); + config.setLklNotifyCer(getResourceFile(lklNotifyCerPath)); + config.setServerUrl(serverUrl); + return LKLSDK.init(config); + } + + /** + * 读取证书文件内容 + * 末尾会有换行符 + * + * @param fileName recource 文件夹下的路径,如:palyKey/wx/lakala_public_key.cer + * @return + */ + public static String getResourceFile(String fileName) { + return getResourceFile(fileName, true, false); + } + + /** + * 获取配置文件内容 + * + * @param fileName recource 文件夹下的路径,如:palyKey/wx/lakala_public_key.cer + * @param keepLineBreaks 保留换行符 + * @return + */ + public static String getResourceFile(String fileName, boolean keepLineBreaks, boolean stripPemHeaders) { + StringBuilder stringBuilder = new StringBuilder(); + try (InputStream inputStream = new ClassPathResource(fileName).getInputStream(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + String line; + String endSeq = keepLineBreaks ? "\n" : ""; + while ((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line).append(endSeq); + } + if (stripPemHeaders) { + return stripPemHeaders(stringBuilder.toString()); + } + return stringBuilder.toString(); + } catch (IOException e) { + // 记录异常信息 + log.error(e.getMessage()); + return ""; + } + } + + /** + * 拉卡拉使用私钥加密数据 + */ + public static String encryptNotifyData(String priKey, String data) { + ByteArrayOutputStream out = null; + try { + log.debug("拉卡拉tk加密私钥:{}", priKey); + log.debug("拉卡拉tk待加密data数据:{}", data); + + // 解码私钥 + byte[] keyBytes = java.util.Base64.getDecoder().decode(priKey); + + // 生成私钥对象 + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); + + // 初始化加密器 + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + + // 分段加密 + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + out = new ByteArrayOutputStream(); + for (int offset = 0; offset < dataBytes.length; offset += 117) { + int blockSize = Math.min(117, dataBytes.length - offset); + byte[] encryptedBlock = cipher.doFinal(dataBytes, offset, blockSize); + out.write(encryptedBlock); + } + + // 编码为Base64字符串 + String encryptDataStr = java.util.Base64.getEncoder().encodeToString(out.toByteArray()); + log.debug("拉卡拉tk数据加密结果:{}", encryptDataStr); + return encryptDataStr; + } catch (Exception e) { + throw new RuntimeException("私钥加密失败", e); + } finally { + closeQuietly(out); + } + } + + /** + * 安全关闭流 + */ + private static void closeQuietly(ByteArrayOutputStream out) { + if (out != null) { + try { + out.close(); + } catch (Exception e) { + // 忽略异常 + } + } + } + + public static String stripPemHeaders(String key) { + if (key == null || key.isEmpty()) { + return key; + } + + // 定义正则表达式:匹配标签行、任意数量连字符和空白字符 + // 1. 包含BEGIN或END的标签行(无论连字符数量) + // 2. 单独的连字符(不在标签行中的) + // 3. 空白字符(空格、制表符、换行等) + Pattern pattern = Pattern.compile( + "-*BEGIN[^-]*-*|-*END[^-]*-*|-|\\s"); + + // 执行替换 + Matcher matcher = pattern.matcher(key); + return matcher.replaceAll(""); + } + + /** + * 获取 body 请求数据 + * + * @param request + * @return + */ + public static String getBody(HttpServletRequest request) { + InputStreamReader in = null; + try { + in = new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8); + StringBuffer bf = new StringBuffer(); + int len; + char[] chs = new char[1024]; + while ((len = in.read(chs)) != -1) { + bf.append(new String(chs, 0, len)); + } + return bf.toString(); + } catch (Exception e) { + log.error("请求头部取数据异常:{}", e); + throw new ApiException(I18nUtil._("获取请求数据失败!"), e); + } finally { + if (null != in) { + try { + in.close(); + } catch (Exception e) { + log.error("流关闭异常:{}", e); + } + } + } + } + + /** + * 获取证书信息 + * + * @param inputStream + * @return + */ + public static X509Certificate loadCertificate(InputStream inputStream) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); +// String publicKeyBase64 = Base64.encodeBase64String(cert.getPublicKey().getEncoded()); +// System.out.println(publicKeyBase64); + cert.checkValidity(); + return cert; + } catch (CertificateExpiredException e) { + throw new RuntimeException("证书已过期", e); + } catch (CertificateNotYetValidException e) { + throw new RuntimeException("证书尚未生效", e); + } catch (CertificateException e) { + throw new RuntimeException("无效的证书", e); + } + } + + /** + * 签名验证 + * + * @param certificate + * @param message + * @param signature + * @return + */ + public static boolean verify(X509Certificate certificate, byte[] message, String signature) { + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initVerify(certificate); + sign.update(message); + byte[] signatureB = Base64.decodeBase64(signature); + return sign.verify(signatureB); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); + } catch (SignatureException e) { + throw new RuntimeException("签名验证过程发生了错误", e); + } catch (InvalidKeyException e) { + throw new RuntimeException("无效的证书", e); + } + } + + /** + * 签名校验 + * + * @param authorization + * @param reqBody + * @param lklNotifyCerPath + * @return + */ + public static boolean verify(String authorization, String reqBody, String lklNotifyCerPath) { + try { + log.debug("拉卡拉 Authorization 内容:{}", authorization); + Map headerMap = getLakalaAuthorizationMap(authorization); + String timestamp = headerMap.get("timestamp"); + String nonceStr = headerMap.get("nonce_str"); + String signature = headerMap.get("signature"); + + X509Certificate lklNotifyCer = loadCertificate(new ClassPathResource(lklNotifyCerPath).getInputStream()); + if (StrUtil.isBlank(timestamp) || StrUtil.isBlank(nonceStr) || StrUtil.isBlank(signature) || lklNotifyCer == null) { + return false; + } + + String preSignDataStr = timestamp + "\n" + + nonceStr + "\n" + + reqBody + "\n"; + log.debug("拉卡拉签名明文内容:{}", preSignDataStr); + if (verify(lklNotifyCer, preSignDataStr.getBytes(StandardCharsets.UTF_8), signature)) { + log.debug("验签成功"); + return true; + } + + log.debug("验签失败"); + return false; + } catch (Exception e) { + log.error("验签发生异常:{}", e); + return false; + } + } + + /** + * 解析请求头中的认证签名等信息 + * 参考:https://o.lakala.com/#/home/document/detail?id=36 + * + * @param authorization 格式如下 + * LKLAPI-SHA256withRSA timestamp="1643271327", + * nonce_str="rQCbASKattHx", + * signature=" + * iPSycbakMt7AgjmwbtaweDVI/RLsQnGvOGiVM93haFkPpT/BxUprYx/GKFLQZebSQMvBfbeWinmnOJlqd3bXgye41BUAVmbItSTOzaQhNyS2kbDwXXGJWmT84aeJWHUB05BWB8ng + * /+X7jrPtsenC6aO7Xgh8jNylJlkU59TKCi7BPGbyHo6pAWJl/Bus0IQps1ay+Eo6Ks3Ins3COV7 + * /lmu5p5FD7TAZsfP+ZvMFObLJOrDQeBTMFKFFWj4ZkjNzNlQqZWlfLv4yLns + * /dKTDLDy5tRO5zwunW+li5YLcwOVf3tbevNFtg53WoBhQnwf838WNvY9zfRhOpCc4fBlWAA==" + * @return + */ + public static Map getLakalaAuthorizationMap(String authorization) { + Map map = new HashMap(); + authorization = authorization.trim(); + int bpos = authorization.indexOf(" "); + String authType = authorization.substring(0, bpos); + String[] typeArr = authType.split("-"); + if (typeArr.length > 1) { + map.put("signSystemCode", typeArr[0]); + map.put("signAlgorithm", typeArr[1]); + } + + String signInfo = authorization.substring(bpos + 1); + String[] infoArr = signInfo.split(","); + String[] var7 = infoArr; + int var8 = infoArr.length; + + for (int var9 = 0; var9 < var8; ++var9) { + String info = var7[var9]; + if (info.contains("=")) { + int fpos = info.indexOf("="); + String value = info.substring(fpos + 1).trim(); + value = value.substring(1, value.length() - 1); + map.put(info.substring(0, fpos).trim(), value); + } + } + + return map; + } + + /** + * 拼接拉卡拉header中的Authorization字段 + * 参考文档:https://o.lakala.com/#/home/document/detail?id=33 + * + * @param privateKey + * @param appId + * @param serialNo + * @param reqBody + * @return + */ + public static String genAuthorization(String privateKey, String appId, String serialNo, String reqBody) { + if (StrUtil.isBlank(privateKey) || StrUtil.isBlank(appId) || StrUtil.isBlank(serialNo) || StrUtil.isBlank(reqBody)) { + log.error("生产拉卡拉签名时缺少参数"); + return ""; + } + + //待签字符串格式:${appid}\n+${serialNo}\n+${timeStamp}\n+${nonceStr}\n+${body}\n + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = StringUtils.genRandomNumber(12); + String builder = appId + "\n" + + serialNo + "\n" + + timestamp + "\n" + + nonceStr + "\n" + + reqBody + "\n"; + + String signature = RSAUtil.signSHA256withRSA(builder, privateKey); + + return String.format("LKLAPI-SHA256withRSA appid=\"%s\",serial_no=\"%s\",timestamp=\"%s\",nonce_str=\"%s\",signature=\"%s\"", + appId, serialNo, timestamp, nonceStr, signature); + } + + /** + * 拉卡拉异步通知数据公钥解密 + * + * @param pubKey Base64公钥 + * @param data Base64数据 + * @return 解密字符串 + */ + public static String decryptNotifyData(String pubKey, String data) { + ByteArrayOutputStream out = null; + try { + log.debug("拉卡拉tk解密公钥:{}", pubKey); + log.debug("拉卡拉tk待解密data数据:{}", data); + java.util.Base64.Decoder decoder = java.util.Base64.getDecoder(); + byte[] keyBytes = decoder.decode(pubKey.getBytes(StandardCharsets.UTF_8)); + byte[] dataBytes = decoder.decode(data.getBytes(StandardCharsets.UTF_8)); + Cipher cipher = Cipher.getInstance("RSA"); + PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes)); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + out = new ByteArrayOutputStream(); + byte[] cache; + for (int i = 0, offset = 0, length = dataBytes.length; length - offset > 0; i++, offset = i * 128) { + if (length - offset > 128) { + cache = cipher.doFinal(dataBytes, offset, 128); + } else { + cache = cipher.doFinal(dataBytes, offset, length - offset); + } + out.write(cache, 0, cache.length); + } + + String decodedDataStr = out.toString("UTF-8"); + log.debug("拉卡拉tk数据解密结果:{}", decodedDataStr); + + return decodedDataStr; + } catch (IllegalArgumentException e) { + log.error("Base64解码失败: {}", e.getMessage(), e); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + log.error("RSA算法初始化失败: {}", e.getMessage(), e); + } catch (InvalidKeySpecException | InvalidKeyException e) { + log.error("私钥格式或类型错误: {}", e.getMessage(), e); + } catch (BadPaddingException | IllegalBlockSizeException e) { + log.error("解密数据块大小或填充错误: {}", e.getMessage(), e); + } catch (Exception e) { + log.error("解密过程中出现未知异常: {}", e.getMessage(), e); + } finally { + closeQuietly(out); + } + + return null; + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java index 051c5e30..514ec790 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/ShopMessageTemplateServiceImpl.java @@ -307,7 +307,7 @@ public class ShopMessageTemplateServiceImpl extends BaseServiceImpl res = new HashMap<>(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java index 9979651b..342b5142 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java @@ -553,8 +553,9 @@ public class OssServiceImpl implements OssService { * @return */ private String getUserDirName(UserDto user) { - if (user.getId() == null) { + if (user == null || user.getId() == null) { //throw new ApiException(I18nUtil._("获取用户信息失败!")); + logger.error("cos 文件上传,获取用户信息失败!"); return "media/plantform/default/"; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopMerchEntryAdminController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopMchEntryAdminController.java similarity index 75% rename from mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopMerchEntryAdminController.java rename to mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopMchEntryAdminController.java index bdf640be..564cc227 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopMerchEntryAdminController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopMchEntryAdminController.java @@ -11,7 +11,7 @@ package com.suisung.mall.shop.store.controller.admin; 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.store.service.ShopMerchEntryService; +import com.suisung.mall.shop.store.service.ShopMchEntryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.RequestBody; @@ -24,10 +24,10 @@ import javax.annotation.Resource; @Api(tags = "店铺基础信息表") @RestController @RequestMapping("/admin/shop/merch") -public class ShopMerchEntryAdminController extends BaseControllerImpl { +public class ShopMchEntryAdminController extends BaseControllerImpl { @Resource - private ShopMerchEntryService shopMerchEntryService; + private ShopMchEntryService shopMchEntryService; /** * 商家申请入驻商城平台 @@ -38,20 +38,20 @@ public class ShopMerchEntryAdminController extends BaseControllerImpl { @ApiOperation(value = "后台-商家申请入驻分页列表", notes = "商家申请入驻分页列表") @RequestMapping(value = "/list", method = RequestMethod.POST) public CommonResult shopMerchEntryList(@RequestBody JSONObject shopMerchEntryJSON) { - return shopMerchEntryService.shopMerchEntryList(shopMerchEntryJSON.getStr("keyword"), shopMerchEntryJSON.getInt("page"), shopMerchEntryJSON.getInt("pageSize")); + return shopMchEntryService.shopMerchEntryList(shopMerchEntryJSON.getStr("keyword"), shopMerchEntryJSON.getInt("page"), shopMerchEntryJSON.getInt("pageSize")); } @ApiOperation(value = "后台-获取商家入驻资料详情", notes = "后台-获取商家入驻资料详情") @RequestMapping(value = "/detail", method = RequestMethod.POST) public CommonResult shopMerchEntryDetail(@RequestBody JSONObject jsonParam) { - return shopMerchEntryService.shopMerchEntryDetail(jsonParam.getLong("id"), "", null); + return shopMchEntryService.shopMerchEntryDetail(jsonParam.getLong("id"), "", null); } @ApiOperation(value = "商家入驻审批", notes = "商家入驻审批") @RequestMapping(value = "/approval", method = RequestMethod.POST) public CommonResult shopMerchEntryApproval(@RequestBody JSONObject jsonParam) { // approvalStatus 入驻商家的审批状态:1-已通过;2-未通过;3-待审核; - return shopMerchEntryService.shopMerchEntryApproval(jsonParam.getLong("id"), jsonParam.getInt("approvalStatus"), jsonParam.getStr("approvalRemark"), jsonParam.getStr("approvalInvalidCol")); + return shopMchEntryService.shopMerchEntryApproval(jsonParam.getLong("id"), jsonParam.getInt("approvalStatus"), jsonParam.getStr("approvalRemark"), jsonParam.getStr("approvalInvalidCol")); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMerchEntryController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java similarity index 88% rename from mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMerchEntryController.java rename to mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java index 7f6d8e32..976a4cab 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMerchEntryController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java @@ -10,11 +10,11 @@ package com.suisung.mall.shop.store.controller.mobile; import cn.hutool.json.JSONObject; import com.suisung.mall.common.api.CommonResult; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; +import com.suisung.mall.common.modules.store.ShopMchEntry; import com.suisung.mall.common.service.impl.BaiduMapServiceImpl; import com.suisung.mall.common.service.impl.BaseControllerImpl; import com.suisung.mall.shop.esign.service.EsignContractFillingFileService; -import com.suisung.mall.shop.store.service.ShopMerchEntryService; +import com.suisung.mall.shop.store.service.ShopMchEntryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; @@ -26,10 +26,10 @@ import java.util.Map; @Api(tags = "店铺基础信息表") @RestController @RequestMapping("/mobile/shop/merch") -public class ShopMerchEntryController extends BaseControllerImpl { +public class ShopMchEntryController extends BaseControllerImpl { @Resource - private ShopMerchEntryService shopMerchEntryService; + private ShopMchEntryService shopMchEntryService; @Resource private EsignContractFillingFileService esignContractFillingFileService; @@ -47,26 +47,26 @@ public class ShopMerchEntryController extends BaseControllerImpl { @ApiOperation(value = "店铺主营分类(类目)", notes = "店铺主营分类(类目)") @RequestMapping(value = "/business/category", method = RequestMethod.POST) public CommonResult shopStoreBusinessCategoryList() { - return shopMerchEntryService.storeBusinessCategoryList(); + return shopMchEntryService.storeBusinessCategoryList(); } @ApiOperation(value = "商家申请入驻商城平台", notes = "商家申请入驻商城平台") @RequestMapping(value = "/apply", method = RequestMethod.POST) public CommonResult shopMerchEntryApply(@RequestBody JSONObject shopMerchEntryJSON) { - return shopMerchEntryService.shopMerchEntryApply(shopMerchEntryJSON); + return shopMchEntryService.shopMerchEntryApply(shopMerchEntryJSON); } @ApiOperation(value = "获取商家入驻资料详情", notes = "获取商家入驻资料详情") @RequestMapping(value = "/detail", method = RequestMethod.POST) public CommonResult shopMerchEntryDetail(@RequestBody JSONObject jsonParam) { - return shopMerchEntryService.shopMerchEntryDetail(null, jsonParam.getStr("mobile"), null); + return shopMchEntryService.shopMerchEntryDetail(null, jsonParam.getStr("mobile"), null); } @ApiOperation(value = "通过手机号mobile获取商家入驻审核状态", notes = "通过手机号获取商家入驻审核状态,远程调用用途") @RequestMapping(value = "/approval/status", method = RequestMethod.POST) public Integer shopMerchEntryApprovalStatus(@RequestBody JSONObject jsonParam) { // approvalStatus 入驻商家的审批状态:1-已通过;2-未通过;3-待审核;4-未申请; - ShopMerchEntry record = shopMerchEntryService.shopMerchEntryApprovalInfo(jsonParam.getStr("mobile")); + ShopMchEntry record = shopMchEntryService.shopMerchEntryApprovalInfo(jsonParam.getStr("mobile")); if (record == null || record.getApproval_status() == null) { return 4; } @@ -78,7 +78,7 @@ public class ShopMerchEntryController extends BaseControllerImpl { @RequestMapping(value = "/fresh/approval/status", method = RequestMethod.POST) public CommonResult shopMerchEntryApprovalStatus2(@RequestBody JSONObject jsonParam) { // approvalStatus 入驻商家的审批状态:1-已通过;2-未通过;3-待审核;4-未申请; - ShopMerchEntry record = shopMerchEntryService.shopMerchEntryApprovalInfo(jsonParam.getStr("mobile")); + ShopMchEntry record = shopMchEntryService.shopMerchEntryApprovalInfo(jsonParam.getStr("mobile")); Map result = new HashMap<>(); if (record == null || record.getApproval_status() == null) { // 配合前端的要求,没有申请过入驻商城平台的时候,返回未申请状态 @@ -103,7 +103,7 @@ public class ShopMerchEntryController extends BaseControllerImpl { @ApiOperation(value = "商家重新申请入驻商城平台", notes = "商家申请入驻材料被驳回的时候,修正材料,重新申请入驻") @RequestMapping(value = "/re-apply", method = RequestMethod.POST) public CommonResult shopMerchEntryReApply(@RequestBody JSONObject shopMerchEntryJSON) { - return shopMerchEntryService.shopMerchEntryReApply(shopMerchEntryJSON); + return shopMchEntryService.shopMerchEntryReApply(shopMerchEntryJSON); } /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/mapper/ShopMerchEntryMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/mapper/ShopMchEntryMapper.java similarity index 80% rename from mall-shop/src/main/java/com/suisung/mall/shop/store/mapper/ShopMerchEntryMapper.java rename to mall-shop/src/main/java/com/suisung/mall/shop/store/mapper/ShopMchEntryMapper.java index 68c7a32f..a40c2a4e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/mapper/ShopMerchEntryMapper.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/mapper/ShopMchEntryMapper.java @@ -9,9 +9,9 @@ package com.suisung.mall.shop.store.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; +import com.suisung.mall.common.modules.store.ShopMchEntry; import org.springframework.stereotype.Component; @Component -public interface ShopMerchEntryMapper extends BaseMapper { +public interface ShopMchEntryMapper extends BaseMapper { } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMerchEntryService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java similarity index 51% rename from mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMerchEntryService.java rename to mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java index 8b90279a..fb83e784 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMerchEntryService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java @@ -10,12 +10,12 @@ package com.suisung.mall.shop.store.service; import cn.hutool.json.JSONObject; import com.suisung.mall.common.api.CommonResult; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; +import com.suisung.mall.common.modules.store.ShopMchEntry; import org.springframework.data.util.Pair; import java.util.List; -public interface ShopMerchEntryService { +public interface ShopMchEntryService { /** * 获取店铺的经营类目列表 @@ -96,17 +96,34 @@ public interface ShopMerchEntryService { * @param loginMobile * @return */ - ShopMerchEntry shopMerchEntryApprovalInfo(String loginMobile); + ShopMchEntry shopMerchEntryApprovalInfo(String loginMobile); /** * 根据商家手机号、营业执照、审批状态获取有效的商家入驻申请记录 * * @param loginMobile * @param bizLicenseNumber - * @param approvalStatus + * @param approvalStatusList * @return */ - ShopMerchEntry getShopMerchEntryByCondition(String loginMobile, String bizLicenseNumber, Integer approvalStatus); + ShopMchEntry getShopMerchEntryByCondition(String loginMobile, String bizLicenseNumber, Integer... approvalStatusList); + + + /** + * 根据拉卡拉外部商户号获取有效的商家入驻申请记录 + * + * @param merCupNo + * @return + */ + ShopMchEntry getShopMerchEntryByMerCupNo(String merCupNo); + + /** + * 根据拉卡拉内部商户号获取有效的商家入驻申请记录 + * + * @param merInnerNo + * @return + */ + ShopMchEntry getShopMerchEntryByMerInnerNo(String merInnerNo); /** * 根据商家注册的手机号,更新合同签署状态和合同下载地址 @@ -127,6 +144,33 @@ public interface ShopMerchEntryService { */ Boolean updateMerchEntryStoreStatus(String loginMobile, Integer storeStatus); + /** + * 更新商家入驻申请的拉卡拉商户号 + * + * @param loginMobile 商家注册的手机号 + * @param lklAuditStatus 拉卡拉审核状态 + * @param lklMerCupNo 拉卡拉银联商户号 + * @param lklMerInnerNo 拉卡拉内部商户号 + * @param lklTkRegParams 进件请求参数 + * @param lklTkRegResp 进件返回的数据 + * @return + */ + Boolean updateMerchEntryLklMerCupNo(String loginMobile, Integer lklAuditStatus, String lklMerCupNo, String lklMerInnerNo, String lklTkRegParams, String lklTkRegResp); + + /** + * 根据拉卡拉内部商户号更新商家入驻申请的拉卡拉审核状态和响应数据 + * + * @param lklInnerMerNo 拉卡拉内部商户号 + * @param lklMerCupNo 拉卡拉外部商户号 + * @param termNos 拉卡拉分配的业务终端号 + * @param lklAuditStatus 拉卡拉审核状态 + * @param approvalStatus 入驻材料审核状态 + * @param lklTkRegNotifyReq lklTkRegResp 异步请求参数 + * @return + */ + Boolean updateMerchEntryLklAuditStatusByLklMerCupNo(String lklInnerMerNo, String lklMerCupNo, String termNos, Integer lklAuditStatus, Integer approvalStatus, String lklTkRegNotifyReq); + + /** * 更新商家入驻申请的店铺 ID * @@ -136,5 +180,41 @@ public interface ShopMerchEntryService { */ Boolean updateMerchEntryStoreId(Long id, Integer storeId); + /** + * 更新拉卡拉入网电子合同、合同名称、签署COS地址, LKL文件相对路径 + * + * @param id + * @param lklEcNo + * @param lklEcName + * @param lklEcResultUrl + * @param ecDownloadUrl + * @param ecLklFilePath + * @return + */ + Boolean updateMerchEntryLklEcNo(Long id, String lklEcNo, String lklEcName, String lklEcResultUrl, String ecDownloadUrl, String ecLklFilePath); + + /** + * 更新商家入驻申请的审批状态和审批备注 + * + * @param mchId 商家入驻申请 ID,mchId和mchMobile至少必填一个 + * @param mchMobile 商家注册的手机号 mchId和mchMobile至少必填一个 + * @param approvalStatus 入驻审批状态:1-已通过;2-未通过;3-待审核;4-未申请过;5-已提交拉卡拉审核;21-拉卡拉审核未通过; + * @param approvalRemark 审批备注 + * @return + */ + Boolean updateMerchEntryApprovalByMchId(Long mchId, String mchMobile, Integer approvalStatus, String approvalRemark); + + /** + * 根据商户号或商家手机号修改商户分账多个状态 + * + * @param mchMobile + * @param merCupNo + * @param hasEcSigned + * @param hasApplySplit + * @param hasApplyReceiver + * @param hasBindReceiver + * @return + */ + Boolean updateMulStatus(String mchMobile, String merCupNo, Integer hasEcSigned, Integer hasApplySplit, Integer hasApplyReceiver, Integer hasBindReceiver); } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMerchEntryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java similarity index 55% rename from mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMerchEntryServiceImpl.java rename to mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java index 880bc269..3639e6bf 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMerchEntryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java @@ -19,7 +19,7 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.CommonConstant; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; +import com.suisung.mall.common.modules.store.ShopMchEntry; import com.suisung.mall.common.utils.BankUtil; import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.common.utils.phone.PhoneNumberUtils; @@ -30,9 +30,11 @@ import com.suisung.mall.shop.components.TaskService; import com.suisung.mall.shop.esign.service.EsignContractFillingFileService; import com.suisung.mall.shop.esign.service.EsignContractService; import com.suisung.mall.shop.esign.service.EsignPlatformInfoService; +import com.suisung.mall.shop.lakala.service.impl.LakalaApiServiceImpl; +import com.suisung.mall.shop.lakala.service.impl.LklTkServiceImpl; import com.suisung.mall.shop.message.service.ShopMessageTemplateService; -import com.suisung.mall.shop.store.mapper.ShopMerchEntryMapper; -import com.suisung.mall.shop.store.service.ShopMerchEntryService; +import com.suisung.mall.shop.store.mapper.ShopMchEntryMapper; +import com.suisung.mall.shop.store.service.ShopMchEntryService; import com.suisung.mall.shop.store.service.ShopStoreBaseService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -41,17 +43,14 @@ import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 商家入驻申请表 */ @Slf4j @Service -public class ShopMerchEntryServiceImpl extends BaseServiceImpl implements ShopMerchEntryService { +public class ShopMchEntryServiceImpl extends BaseServiceImpl implements ShopMchEntryService { @Resource private AccountBaseConfigService accountBaseConfigService; @@ -76,9 +75,17 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); if (StrUtil.isNotBlank(keyword)) { queryWrapper.like("store_name", keyword); } @@ -353,7 +404,7 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl listPage = lists(queryWrapper, pageIndex, pageSize); + Page listPage = lists(queryWrapper, pageIndex, pageSize); return CommonResult.success(listPage); } @@ -375,7 +426,7 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); if (ObjectUtil.isNotEmpty(recordId)) { queryWrapper.eq("id", recordId); } @@ -389,12 +440,16 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl recordList = list(queryWrapper); + List recordList = list(queryWrapper); if (CollectionUtil.isEmpty(recordList)) { return CommonResult.success(null, "暂无申请记录!"); } - ShopMerchEntry record = recordList.get(0); + ShopMchEntry record = recordList.get(0); + // 审核意见空值,返回"[]" + if (StrUtil.isBlank(record.getApproval_invalid_col())) { + record.setApproval_invalid_col("[]"); + } // 试试更新入驻表的合同下载地址和合同签署状态 updateMerchEntrySignedStatusAndContractDownloadUrl(record); @@ -407,7 +462,7 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); - if (approvalStatus.equals(CommonConstant.Enable) && StrUtil.isBlank(approvalRemark)) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + if (approvalStatus.equals(CommonConstant.MCH_APPR_STA_LKL_PADDING) && StrUtil.isBlank(approvalRemark)) { // 审核通过 - approvalRemark = "审核通过,后续将向您发起签署电子合同流程。"; + approvalRemark = "初步审核通过,等待进一步审核。";//"审核通过,后续将向您发起签署电子合同流程。"; updateWrapper.set("signed_status", CommonConstant.CONTRACT_SIGN_STA_ING); - } else if (approvalStatus.equals(CommonConstant.Disable2) && StrUtil.isBlank(approvalRemark)) { + updateWrapper.set("approval_status", CommonConstant.MCH_APPR_STA_LKL_PADDING); // 进入拉卡拉审核中 + + log.info("准备提交给拉卡拉进件审核。"); + } else if (approvalStatus.equals(CommonConstant.MCH_APPR_STA_NOPASS) && StrUtil.isBlank(approvalRemark)) { approvalRemark = "审核未通过,请继续完善入驻资料信息。"; } - // 自动计算商家分成比例 + // 重要备注:自动计算商家分成比例 record.setSplit_ratio(shopBaseStoreCategoryService.getStoreCategoryRatio(record.getBiz_category())); updateWrapper.eq("id", id) @@ -491,21 +549,33 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl { - log.debug("###开始异步执行生成电子合同模版和填充模版数据,并生该商家和平台方签署的未盖章合同文件###"); - // 生成电子合同模版和填充模版数据,并生该商家和平台方签署的未盖章合同文件 - Boolean genSuccess = esignContractFillingFileService.fillDocTemplate(record.getLogin_mobile(), ""); - if (!genSuccess) { - log.error("###商家入驻电子合同生成失败###"); - } - - // 发短信通知商家,入驻申请已通过审核 - }); + if (approvalStatus.equals(CommonConstant.MCH_APPR_STA_NOPASS)) { + return CommonResult.success(null, "驳回成功!"); } + // 审批通过的时候,进行下一步工作:触发去拉卡拉商家入网电子合同签署 + + // 平台人工审核通过后,触发去拉卡拉商家入网电子合同签署 + // 下一步:商家入网电子合同签署完毕后,收到异步通知,触发拉卡拉商家进件(重要环节) + Pair resultPair = lakalaApiService.applyLedgerMerEc(record.getLogin_mobile()); + if (!resultPair.getFirst()) { + return CommonResult.failed(resultPair.getSecond()); + } + +// if (approvalStatus.equals(CommonConstant.Enable)) { +// // 多线程执行电子合同生成和填充 +// taskService.executeTask(() -> { +// log.debug("###开始异步执行生成电子合同模版和填充模版数据,并生该商家和平台方签署的未盖章合同文件###"); +// // 生成电子合同模版和填充模版数据,并生该商家和平台方签署的未盖章合同文件 +// Boolean genSuccess = esignContractFillingFileService.fillDocTemplate(record.getLogin_mobile(), ""); +// if (!genSuccess) { +// log.error("###商家入驻电子合同生成失败###"); +// } +// +// // 发短信通知商家,入驻申请已通过审核 +// }); +// } + return CommonResult.success(); } @@ -524,7 +594,7 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); if (StrUtil.isNotBlank(mobile) && StrUtil.isBlank(bizLicenseNumber)) { queryWrapper.eq("login_mobile", mobile); boolean isApplied = count(queryWrapper) > 0; @@ -561,9 +631,9 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("login_mobile", mobile).select("approval_status").orderByAsc("id"); - List recordList = list(queryWrapper); + List recordList = list(queryWrapper); if (CollectionUtil.isEmpty(recordList)) { return 0; } @@ -579,22 +649,22 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("login_mobile", loginMobile) .select("id", "approval_status", "approval_remark", "login_mobile", "approval_invalid_col", "signed_status", "contract_download_url", "store_status") .orderByAsc("id"); - List recordList = list(queryWrapper); + List recordList = list(queryWrapper); if (CollectionUtil.isEmpty(recordList)) { return null; } - ShopMerchEntry record = recordList.get(0); + ShopMchEntry record = recordList.get(0); // 试试更新入驻表的合同下载地址和合同签署状态 updateMerchEntrySignedStatusAndContractDownloadUrl(record); @@ -607,16 +677,16 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("status", CommonConstant.Enable).orderByAsc("id"); if (StrUtil.isNotBlank(loginMobile)) { queryWrapper.eq("login_mobile", loginMobile); @@ -626,13 +696,50 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl recordList = list(queryWrapper); + if (CollectionUtil.isEmpty(recordList)) { + return null; + } - List recordList = list(queryWrapper); + return recordList.get(0); + } + + /** + * 根据拉卡拉外部商户号获取有效的商家入驻申请记录 + * + * @param merCupNo + * @return + */ + @Override + public ShopMchEntry getShopMerchEntryByMerCupNo(String merCupNo) { + if (StrUtil.isBlank(merCupNo)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("lkl_mer_cup_no", merCupNo).eq("status", CommonConstant.Enable).orderByAsc("id"); + List recordList = list(queryWrapper); + if (CollectionUtil.isEmpty(recordList)) { + return null; + } + + return recordList.get(0); + } + + @Override + public ShopMchEntry getShopMerchEntryByMerInnerNo(String merInnerNo) { + if (StrUtil.isBlank(merInnerNo)) { + return null; + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("lkl_mer_inner_no", merInnerNo).eq("status", CommonConstant.Enable).orderByAsc("id"); + List recordList = list(queryWrapper); if (CollectionUtil.isEmpty(recordList)) { return null; } @@ -654,7 +761,7 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); + UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("login_mobile", loginMobile); if (ObjectUtil.isNotEmpty(signedStatus)) { @@ -678,14 +785,91 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); + UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("login_mobile", loginMobile).set("store_status", storeStatus); return update(updateWrapper); } + @Override + public Boolean updateMerchEntryLklMerCupNo(String loginMobile, Integer lklAuditStatus, String lklMerCupNo, String lklMerInnerNo, String lklTkRegParams, String lklTkRegResp) { + if (StrUtil.isBlank(loginMobile) && StrUtil.isBlank(lklMerCupNo)) { + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("login_mobile", loginMobile); + if (ObjectUtil.isNotEmpty(lklAuditStatus)) { + updateWrapper.set("lkl_tk_audit_status", lklAuditStatus); + } + + if (StrUtil.isNotBlank(lklMerCupNo)) { + updateWrapper.set("lkl_mer_cup_no", lklMerCupNo); // 银联商户号 + } + + if (StrUtil.isNotBlank(lklMerInnerNo)) { + updateWrapper.set("lkl_mer_inner_no", lklMerInnerNo); //内部商户号 + } + + if (StrUtil.isNotBlank(lklTkRegParams)) { + updateWrapper.set("lkl_tk_reg_params", lklTkRegParams); + } + + if (StrUtil.isNotBlank(lklTkRegResp)) { + updateWrapper.set("lkl_tk_reg_resp", lklTkRegResp); + } + + updateWrapper.set("approval_remark", "拉卡拉进件准备进入审核"); + updateWrapper.set("approval_invalid_col", "[]"); + + return update(updateWrapper); + } + + /** + * 根据拉卡拉内部商户号更新商家入驻申请的拉卡拉审核状态和响应数据 + * + * @param lklInnerMerNo 拉卡拉内部商户号 + * @param lklMerCupNo 拉卡拉外部商户号 + * @param termNos 拉卡拉分配的业务终端号 + * @param lklAuditStatus 拉卡拉审核状态 + * @param approvalStatus 入驻材料审核状态 + * @param lklTkRegNotifyReq 异步通知返回的请求数据 + * @return + */ + @Override + public Boolean updateMerchEntryLklAuditStatusByLklMerCupNo(String lklInnerMerNo, String lklMerCupNo, String termNos, Integer lklAuditStatus, Integer approvalStatus, String lklTkRegNotifyReq) { + if (StrUtil.isBlank(lklInnerMerNo)) { + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("lkl_mer_inner_no", lklInnerMerNo); + + if (StrUtil.isNotBlank(lklMerCupNo)) { + updateWrapper.set("lkl_mer_cup_no", lklMerCupNo); + } + + if (StrUtil.isNotBlank(termNos)) { + updateWrapper.set("lkl_term_no", termNos); + } + + if (ObjectUtil.isNotEmpty(lklAuditStatus)) { + updateWrapper.set("lkl_tk_audit_status", lklAuditStatus); + } + + if (ObjectUtil.isNotEmpty(approvalStatus)) { + updateWrapper.set("approval_status", approvalStatus); + } + + if (StrUtil.isNotBlank(lklTkRegNotifyReq)) { + updateWrapper.set("lkl_tk_reg_notify_req", lklTkRegNotifyReq); + } + + return update(updateWrapper); + } + /** * 更新商家入驻申请的店铺 ID * @@ -699,8 +883,135 @@ public class ShopMerchEntryServiceImpl extends BaseServiceImpl() + return update(new UpdateWrapper() .eq("id", id) .set("store_id", storeId)); } + + /** + * 更新拉卡拉入网电子合同和签署地址 + * + * @param id + * @param lklEcNo + * @param lklEcResultUrl + * @param ecDownloadUrl + * @param ecLklFilePath + * @return + */ + @Override + public Boolean updateMerchEntryLklEcNo(Long id, String lklEcNo, String lklEcName, String lklEcResultUrl, String ecDownloadUrl, String ecLklFilePath) { + if (ObjectUtil.isEmpty(id) && StrUtil.isBlank(lklEcNo)) { + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", id); + updateWrapper.set("lkl_ec_no", lklEcNo); + + if (StrUtil.isNotBlank(lklEcName)) { + updateWrapper.set("lkl_ec_name", lklEcName); + } + + + if (StrUtil.isNotBlank(lklEcResultUrl)) { + updateWrapper.set("lkl_ec_result_url", lklEcResultUrl); + } + + if (StrUtil.isNotBlank(ecDownloadUrl)) { + updateWrapper.set("contract_download_url", ecDownloadUrl); +// updateWrapper.set("signed_status", CommonConstant.CONTRACT_SIGN_STA_FINISH); + updateWrapper.set("has_ec_signed", CommonConstant.Enable); + } + + if (StrUtil.isNotBlank(ecLklFilePath)) { + updateWrapper.set("lkl_ec_file_path", ecLklFilePath); + } + + return update(updateWrapper); + } + + /** + * 更新商家入驻申请的审批状态和审批备注 + * + * @param mchId 商家入驻申请 ID,mchId和mchMobile至少必填一个 + * @param mchMobile 商家注册的手机号 mchId和mchMobile至少必填一个 + * @param approvalStatus 入驻商家的审批状态:1-已通过;2-未通过;3-待审核;4-未申请过;5-已提交拉卡拉审核; + * @param approvalRemark 审批备注 + * @return + */ + @Override + public Boolean updateMerchEntryApprovalByMchId(Long mchId, String mchMobile, Integer approvalStatus, String approvalRemark) { + if (ObjectUtil.isEmpty(mchId) && StrUtil.isBlank(mchMobile)) { + log.error("更新入驻审批状态和备注失败,缺少必要参数"); + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + if (ObjectUtil.isNotEmpty(mchId)) { + updateWrapper.eq("id", mchId); + } + if (StrUtil.isNotBlank(mchMobile)) { + updateWrapper.eq("login_mobile", mchMobile); + } + + if (ObjectUtil.isNotEmpty(approvalStatus)) { + updateWrapper.set("approval_status", approvalStatus); + } + + if (StrUtil.isNotBlank(approvalRemark)) { + updateWrapper.set("approval_remark", approvalRemark); + } + + return update(updateWrapper); + } + + /** + * 根据商户号或商家手机号修改商户分账多个状态 + * + * @param mchMobile + * @param merCupNo + * @param hasEcSigned + * @param hasApplySplit + * @param hasApplyReceiver + * @param hasBindReceiver + * @return + */ + @Override + public Boolean updateMulStatus(String mchMobile, String merCupNo, Integer hasEcSigned, + Integer hasApplySplit, Integer hasApplyReceiver, Integer hasBindReceiver) { + // 参数校验 + if (StrUtil.isAllBlank(merCupNo, mchMobile)) { + log.error("缺少merCupNo或mchMobile参数"); + return false; + } + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + // 设置查询条件 + Optional.ofNullable(mchMobile) + .filter(StrUtil::isNotBlank) + .ifPresent(mobile -> updateWrapper.eq("login_mobile", mobile)); + Optional.ofNullable(merCupNo) + .filter(StrUtil::isNotBlank) + .ifPresent(cupNo -> updateWrapper.eq("lkl_mer_cup_no", cupNo)); + + // 流式构建更新字段 + Map fieldMap = new LinkedHashMap<>(); + fieldMap.put("has_ec_signed", hasEcSigned); + fieldMap.put("has_apply_split", hasApplySplit); + fieldMap.put("has_apply_receiver", hasApplyReceiver); + fieldMap.put("has_bind_receiver", hasBindReceiver); + + // 过滤有效字段并设置 + fieldMap.entrySet().stream() + .filter(entry -> entry.getValue() != null && entry.getValue() > 0) + .forEach(entry -> updateWrapper.set(entry.getKey(), entry.getValue())); + + + try { + return update(updateWrapper); + } catch (Exception e) { + log.error("更新商家分账业务多个状态失败", e); + return false; + } + } } 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 bc9312b4..73f8ba9e 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 @@ -31,11 +31,13 @@ import com.suisung.mall.common.feignService.PayService; import com.suisung.mall.common.modules.account.AccountUserBase; import com.suisung.mall.common.modules.account.AccountUserInfo; import com.suisung.mall.common.modules.account.AccountUserSns; -import com.suisung.mall.common.modules.base.*; +import com.suisung.mall.common.modules.base.ShopBaseProductTag; +import com.suisung.mall.common.modules.base.ShopBaseStoreCategory; +import com.suisung.mall.common.modules.base.ShopBaseStoreGrade; +import com.suisung.mall.common.modules.base.ShopPageModule; import com.suisung.mall.common.modules.distribution.ShopDistributionPlantformUser; import com.suisung.mall.common.modules.invoicing.InvoicingCustomerLevel; import com.suisung.mall.common.modules.invoicing.InvoicingWarehouseBase; -import com.suisung.mall.common.modules.merch.ShopMerchEntry; import com.suisung.mall.common.modules.page.ShopPageBase; import com.suisung.mall.common.modules.pay.PayUserResource; import com.suisung.mall.common.modules.plantform.ShopPlantformSubsite; @@ -171,7 +173,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl districtList = shopBaseDistrictService.getFullDistrictByDistrictCode(shopMerchEntry.getCounty_id()); - shopStoreBase.setStore_district_id(shopBaseDistrictService.joinDistrict(districtList, 1, true, "/")); - shopStoreBase.setStore_area(shopBaseDistrictService.joinDistrict(districtList, 2, true, "/")); - shopStoreBase.setStore_address(shopMerchEntry.getStore_address()); - shopStoreBase.setStore_longitude(shopMerchEntry.getStore_longitude()); - shopStoreBase.setStore_latitude(shopMerchEntry.getStore_latitude()); + // List districtList = shopBaseDistrictService.getFullDistrictByDistrictCode(shopMchEntry.getCounty_id()); + //shopStoreBase.setStore_district_id(shopBaseDistrictService.joinDistrict(districtList, 1, true, "/")); + // shopStoreBase.setStore_area(shopBaseDistrictService.joinDistrict(districtList, 2, true, "/")); + + shopStoreBase.setStore_district_id(shopMchEntry.getStore_district()); + shopStoreBase.setStore_area(shopMchEntry.getStore_area()); + shopStoreBase.setStore_address(shopMchEntry.getStore_address()); + shopStoreBase.setStore_longitude(shopMchEntry.getStore_longitude()); + shopStoreBase.setStore_latitude(shopMchEntry.getStore_latitude()); shopStoreBase.setStore_grade_id(1001); // 店铺等级,默认为普通店铺 shopStoreBase.setStore_type(1);//店铺类型(ENUM): 1-卖家店铺; 2-供应商店铺 shopStoreBase.setStore_is_open(1); shopStoreBase.setStore_is_selfsupport(0); shopStoreBase.setStore_o2o_flag(0); shopStoreBase.setSubsite_id(0); + // 拉卡拉外部商户号 + shopStoreBase.setLkl_merchant_no(shopMchEntry.getLkl_mer_cup_no()); + // 拉卡拉分配的终端号 + shopStoreBase.setLkl_term_no(shopMchEntry.getLkl_term_no()); shopStoreBase.setStore_state_id(StateCode.STORE_STATE_YES);//店铺资料信息状态(ENUM):3210-待完善资料; 3220-等待审核 ; 3230-资料审核没有通过;3240-资料审核通过,待付款 shopStoreBase.setStore_time(DateUtil.date()); shopStoreBase.setStore_end_time(System.currentTimeMillis() + 1000L * 60 * 60 * 24 * 365 * 5); // 5年 @@ -3062,7 +3074,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl + + + + + * + + + + diff --git a/mall-shop/src/main/resources/mapper/store/ShopMerchEntryMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklLedgerEcMapper.xml similarity index 76% rename from mall-shop/src/main/resources/mapper/store/ShopMerchEntryMapper.xml rename to mall-shop/src/main/resources/mapper/lakala/LklLedgerEcMapper.xml index 1004d888..0e99c16e 100644 --- a/mall-shop/src/main/resources/mapper/store/ShopMerchEntryMapper.xml +++ b/mall-shop/src/main/resources/mapper/lakala/LklLedgerEcMapper.xml @@ -1,6 +1,6 @@ - + * diff --git a/mall-shop/src/main/resources/mapper/lakala/LklLedgerMemberMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklLedgerMemberMapper.xml new file mode 100644 index 00000000..71d4113d --- /dev/null +++ b/mall-shop/src/main/resources/mapper/lakala/LklLedgerMemberMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/mapper/lakala/LklLedgerMerReceiverBindMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklLedgerMerReceiverBindMapper.xml new file mode 100644 index 00000000..779aa2a5 --- /dev/null +++ b/mall-shop/src/main/resources/mapper/lakala/LklLedgerMerReceiverBindMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/mapper/lakala/LklLedgerReceiverMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklLedgerReceiverMapper.xml new file mode 100644 index 00000000..2d80129e --- /dev/null +++ b/mall-shop/src/main/resources/mapper/lakala/LklLedgerReceiverMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/mapper/lakala/LklOrderSeparateMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklOrderSeparateMapper.xml new file mode 100644 index 00000000..581afe20 --- /dev/null +++ b/mall-shop/src/main/resources/mapper/lakala/LklOrderSeparateMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml index 70aef115..1b90db8f 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml @@ -625,7 +625,9 @@ SELECT ob.order_id, ob.order_time, - oi.order_time + #{expireSeconds}*1000 as arrival_time, + + oi.order_time + #{expireSeconds}*1000 as arrival_time, + ob.order_product_amount, ob.order_payment_amount, ob.currency_id, @@ -714,12 +716,12 @@ - and arrival_time =]]> UNIX_TIMESTAMP() * 1000 + and (oi.order_time + #{expireSeconds}*1000) =]]> UNIX_TIMESTAMP() * 1000 - and arrival_time UNIX_TIMESTAMP() * 1000 + and (oi.order_time + #{expireSeconds}*1000) UNIX_TIMESTAMP() * 1000 diff --git a/mall-shop/src/main/resources/mapper/store/ShopMchEntryMapper.xml b/mall-shop/src/main/resources/mapper/store/ShopMchEntryMapper.xml new file mode 100644 index 00000000..8595af11 --- /dev/null +++ b/mall-shop/src/main/resources/mapper/store/ShopMchEntryMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/OP00000003_cert.cer b/mall-shop/src/main/resources/payKey/lakala/dev/OP00000003_cert.cer new file mode 100644 index 00000000..f85afd02 --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/OP00000003_cert.cer @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIJAN+6gZTEG4TPMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV +BAYTAlVTMREwDwYDVQQIEwhzaGFuZ2hhaTERMA8GA1UEBxMIc2hhbmdoYWkxFDAS +BgNVBAMUC2xha2FsYV8yMDIxMB4XDTIxMDYxODA3MjEzNFoXDTMxMDYxOTA3MjEz +NFowSTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCHNoYW5naGFpMREwDwYDVQQHEwhz +aGFuZ2hhaTEUMBIGA1UEAxQLbGFrYWxhXzIwMjEwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDvDBZyHUDndAGxrIcsCV2njhNO3vCEZotTaWYSYwtDvkcA +b1EjsBFabXZaKigpqFXk5XXNI3NIHP9M8XKzIgGvc65NpLAfRjVql8JiTvLyYd1g +IUcOXMInabu+oX7dQSI1mS8XzqaoVRhDZQWhXcJW9bxMulgnzvk0Ggw07AjGF7si ++hP/Va8SJmN7EJwfQq6TpSxR+WdIHpbWdhZ+NHwitnQwAJTLBFvfk28INM39G7XO +sXdVLfsooFdglVTOHpNuRiQAj9gShCCNrpGsNQxDiJIxE43qRsNsRwigyo6DPJk/ +klgDJa417E2wgP8VrwiXparO4FMzOGK15quuoD7DAgMBAAGjTDBKMAkGA1UdEwQC +MAAwEQYJYIZIAYb4QgEBBAQDAgTwMAsGA1UdDwQEAwIFoDAdBgNVHSUEFjAUBggr +BgEFBQcDAgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAI21YYAlH+Pc1ISv +nbQrGqL8suGL0Hh/8hGaFfrJEJEKr9OeC8jElUhck2MTmfu/Y1lB7r8RBrhGPXi4 +kTXmB6ADs/9+ezNW3WXyFj7fhs3JcZ3mo33T9wyQySDKd//JrEtrTsc/s2PZ602y +qNmPomXSzjrlugaMyC7LI9sR44mc7sQnchjHoxrQFD5/usTFW72UQfYCORsQWYMt +0KKEyAcpRL51RE3xbX1WDtduFYGP62PbwLAn2nCL/j1wlF5hltWj7sditWqKgso5 +F8BTffn2Bb0RdsNxqwMy1cTPrWLeXVOqMDu3ge7hvoav8lZKTjk5Kmqhs7wNAQXK +mg9qSwo= +-----END CERTIFICATE----- diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/OP00000003_private_key.pem b/mall-shop/src/main/resources/payKey/lakala/dev/OP00000003_private_key.pem new file mode 100644 index 00000000..7d95886c --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/OP00000003_private_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDvDBZyHUDndAGx +rIcsCV2njhNO3vCEZotTaWYSYwtDvkcAb1EjsBFabXZaKigpqFXk5XXNI3NIHP9M +8XKzIgGvc65NpLAfRjVql8JiTvLyYd1gIUcOXMInabu+oX7dQSI1mS8XzqaoVRhD +ZQWhXcJW9bxMulgnzvk0Ggw07AjGF7si+hP/Va8SJmN7EJwfQq6TpSxR+WdIHpbW +dhZ+NHwitnQwAJTLBFvfk28INM39G7XOsXdVLfsooFdglVTOHpNuRiQAj9gShCCN +rpGsNQxDiJIxE43qRsNsRwigyo6DPJk/klgDJa417E2wgP8VrwiXparO4FMzOGK1 +5quuoD7DAgMBAAECggEBANhmWOt1EAx3OBFf3f4/fEjylQgRSiqRqg8Ymw6KGuh4 +mE4Md6eW/B6geUOmZjVP7nIIR1wte28M0REWgn8nid8LGf+v1sB5DmIwgAf+8G/7 +qCwd8/VMg3aqgQtRp0ckb5OV2Mv0h2pbnltkWHR8LDIMwymyh5uCApbn/aTrCAZK +NXcPOyAn9tM8Bu3FHk3Pf24Er3SN+bnGxgpzDrFjsDSHjDFT9UMIc2WdA3tuMv9X +3DDn0bRCsHnsIw3WrwY6HQ8mumdbURk+2Ey3eRFfMYxyS96kOgBC2hqZOlDwVPAK +TPtS4hoq+cQ0sRaJQ4T0UALJrBVHa+EESgRaTvrXqAECgYEA+WKmy9hcvp6IWZlk +9Q1JZ+dgIVxrO65zylK2FnD1/vcTx2JMn73WKtQb6vdvTuk+Ruv9hY9PEsf7S8gH +STTmzHOUgo5x0F8yCxXFnfji2juoUnDdpkjtQK5KySDcpQb5kcCJWEVi9v+zObM0 +Zr1Nu5/NreE8EqUl3+7MtHOu1TMCgYEA9WM9P6m4frHPW7h4gs/GISA9LuOdtjLv +AtgCK4cW2mhtGNAMttD8zOBQrRuafcbFAyU9de6nhGwetOhkW9YSV+xRNa7HWTeI +RgXJuJBrluq5e1QGTIwZU/GujpNaR4Qiu0B8TodM/FME7htsyxjmCwEfT6SDYlke +MzTbMa9Q0DECgYBqsR/2+dvD2YMwAgZFKKgNAdoIq8dcwyfamUQ5mZ5EtGQL2yw4 +8zibHh/LiIxgUD1Kjk/qQgNsX45NP4iOc0mCkrgomtRqdy+rumbPTNmQ0BEVJCBP +scd+8pIgNiTvnWpMRvj7gMP0NDTzLI3wnnCRIq8WAtR2jZ0Ejt+ZHBziLQKBgQDi +bEe/zqNmhDuJrpXEXmO7fTv3YB/OVwEj5p1Z/LSho2nHU3Hn3r7lbLYEhUvwctCn +Ll2fzC7Wic1rsGOqOcWDS5NDrZpUQGGF+yE/JEOiZcPwgH+vcjaMtp0TAfRzuQEz +NzV8YGwxB4mtC7E/ViIuVULHAk4ZGZI8PbFkDxjKgQKBgG8jEuLTI1tsP3kyaF3j +Aylnw7SkBc4gfe9knsYlw44YlrDSKr8AOp/zSgwvMYvqT+fygaJ3yf9uIBdrIilq +CHKXccZ9uA/bT5JfIi6jbg3EoE9YhB0+1aGAS1O2dBvUiD8tJ+BjAT4OB0UDpmM6 +QsFLQgFyXgvDnzr/o+hQJelW +-----END PRIVATE KEY----- diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/lkl_notify_cert_v2.cer b/mall-shop/src/main/resources/payKey/lakala/dev/lkl_notify_cert_v2.cer new file mode 100644 index 00000000..12723ebe --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/lkl_notify_cert_v2.cer @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIGAXRTgcMnMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT +AkNOMRAwDgYDVQQIDAdCZWlKaW5nMRAwDgYDVQQHDAdCZWlKaW5nMRcwFQYDVQQK +DA5MYWthbGEgQ28uLEx0ZDEqMCgGA1UEAwwhTGFrYWxhIE9yZ2FuaXphdGlvbiBW +YWxpZGF0aW9uIENBMB4XDTIwMTAxMDA1MjQxNFoXDTMwMTAwODA1MjQxNFowZTEL +MAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaUppbmcxEDAOBgNVBAcMB0JlaUppbmcx +FzAVBgNVBAoMDkxha2FsYSBDby4sTHRkMRkwFwYDVQQDDBBBUElHVy5MQUtBTEEu +Q09NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt1zHL54HiI8d2sLJ +lwoQji3/ln0nsvfZ/XVpOjuB+1YR6/0LdxEDMC/hxI6iH2Rm5MjwWz3dmN/6BZeI +gwGeTOWJUZFARo8UduKrlhC6gWMRpAiiGC8wA8stikc5gYB+UeFVZi/aJ0WN0cpP +JYCvPBhxhMvhVDnd4hNohnR1L7k0ypuWg0YwGjC25FaNAEFBYP9EYUyCJjE//9Z7 +sMzHR9SJYCqqo6r9bOH9G6sWKuEp+osuAh+kJIxJMHfipw7w3tEcWG0hce9u/el4 +cYJtg8/PPMVoccKmeCzMvarr7jdKP4lenJbtwlgyfs+JgNu60KMUJH8RS72wC9NY +uFz09wIDAQABo4HVMIHSMIGSBgNVHSMEgYowgYeAFCnH4DkZPR6CZxRn/kIqVsMo +dJHpoWekZTBjMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UE +BwwHQmVpSmluZzEXMBUGA1UECgwOTGFrYWxhIENvLixMdGQxFzAVBgNVBAMMDkxh +a2FsYSBSb290IENBggYBaiUALIowHQYDVR0OBBYEFJ2Kx9YZfmWpkKFnC33C0r5D +K3rFMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUA +A4IBAQBZoeU0XyH9O0LGF9R+JyGwfU/O5amoB97VeM+5n9v2z8OCiIJ8eXVGKN9L +tl9QkpTEanYwK30KkpHcJP1xfVkhPi/cCMgfTWQ5eKYC7Zm16zk7n4CP6IIgZIqm +TVGsIGKk8RzWseyWPB3lfqMDR52V1tdA1S8lJ7a2Xnpt5M2jkDXoArl3SVSwCb4D +AmThYhak48M++fUJNYII9JBGRdRGbfJ2GSFdPXgesUL2CwlReQwbW4GZkYGOg9LK +CNPK6XShlNdvgPv0CCR08KCYRwC3HZ0y1F0NjaKzYdGNPrvOq9lA495ONZCvzYDo +gmsu/kd6eqxTs/JwdaIYr4sCMg8Z +-----END CERTIFICATE----- diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/tk_api_private_key.txt b/mall-shop/src/main/resources/payKey/lakala/dev/tk_api_private_key.txt new file mode 100644 index 00000000..a17740cc --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/tk_api_private_key.txt @@ -0,0 +1 @@ +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKnlpXdmJcbZHxh3w3Ghy/5wXtUBQcFzRMXgw2d7uYjzAzbIQ/ZCAj6srWoN/obtkU+G62kgcpByGmiVtK4LS+BoelKM705SzV+mcr8mNmr2jwNq1SwDSfgVxx5z00FtXl3LnKvCujvGYOmx6+nX8h6IiyyLX6IP6cFaqpUXHsZVAgMBAAECgYA4NpeM7etJ48T6H4Y3LsWEJkH6UDQlgbIblsaQkstMmLtTgOebrzN28UNfd8njcu9FVOrHGclOKbK7L+1cOLiduWsZKc/c/gAy9wAR4EhoLvlerH9EEPiPWFxdEDbMxPqlkpqLOo+PxHrhTn4vU4CaPdJtL2ujKn7nmsUdUDWo8QJBANS1TlM6nhPt2XlzN5kGfsJ4kBYNjuLXNA2YdNuC2ttYvEXHJ9T70FN/GnRBBIZu47uHH3Ie5nfep+qMk6a8RP8CQQDMecIyI0z1kVt+tOfWKw2ZFLsi74708qTaeR4W1ABtkngj1+bxoWWXr3KqhjqJkWxnhioSfXqu7CScNzjdM1CrAkAQd+ESjI1EmbumrYb2cAxMXi05p98SLPs4uj8B58WuCda5yEuLL9vXOxX/PjFtfxRepn2GxmGtki2J+UxNMnJdAkAFoORjlO0tZU7rcfdfwdeh+xwbnhSFUZiQGv1lC3jnizybX/oPdK3jOwUhBIjf+IzPXLYTxDh4UC/BzRNXo235AkEAhgYBk6H7RU2iIuvwz1c6CtE1gJ8DvEp1F0KOMWMFB0KCpDXUToix0dlMz962FozYENi4X4zYQo6nFwlXeS3Pfw== \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/tk_api_public_key.txt b/mall-shop/src/main/resources/payKey/lakala/dev/tk_api_public_key.txt new file mode 100644 index 00000000..7f699e3a --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/tk_api_public_key.txt @@ -0,0 +1 @@ +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp5aV3ZiXG2R8Yd8Nxocv+cF7VAUHBc0TF4MNne7mI8wM2yEP2QgI+rK1qDf6G7ZFPhutpIHKQchpolbSuC0vgaHpSjO9OUs1fpnK/JjZq9o8DatUsA0n4Fccec9NBbV5dy5yrwro7xmDpsevp1/IeiIssi1+iD+nBWqqVFx7GVQIDAQAB \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/tk_notify_private_key.txt b/mall-shop/src/main/resources/payKey/lakala/dev/tk_notify_private_key.txt new file mode 100644 index 00000000..f8561c8f --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/tk_notify_private_key.txt @@ -0,0 +1 @@ +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALKb2r7+J9aS9/aiDblUUlkMfU6XdIasmEm/GWVOAoIS+CGV+qItHrZ2uIxbdHGlQY0HBGGnPYNUDGipzQtgrEpzWFXvBkmO1tVLdUE5ZuQ6/6pzFgGJTOi9+ujSKE4hbPbTJ4y+/1qqVLypSV51j7m2QZREns8biKzuxKNRzDZFAgMBAAECgYAXLWtq+n8/9I1x92CRirQu8xR1tOi9qzsN4tsQTtm7eGuzrAs8rV89bVWQfTO0pa3Gd8ElTPcKCkeb82D014Qz4z7sLz3reNeYXay8PbCCegQxUn7xzykwckziydKw4cCBZQkmyCYtUZcyf+C2Z+0dX6dTO3Z7+/se0ndJleAbFwJBAO9omFP3+lxxXQgyv99+IJTRFp+xWZnfjWREU8aZQXy+QU1LYCxMafpaIJMOu4qwo1XwfLKelAuLc2kLQMhrEkcCQQC+/Jie67sp744bw2dBWo8LrghvIaPxkH9Sgkce3qHUPx4cK+tF/GtfujB4kKDhpQb4nEJtLhPEBjslL0bWkc0TAkAoRHBylx/+EncyXM7W+XDjdvOWMo8+iVJPzgpFOhvAroNvS8FGUif1GtmlwnGa1zDx0Hw40uFVu8PSpDjmPNQbAkEAsHBpiv/aVB6kmY+HF8BSwIaR8iEQ9Tz58z6595HTzWJWLc6BN5G/nJtE5k0u4+byrsClOXpE9maPQ4YOmnHuEwJAIEYvcN2Ju3on5wglbH2b9889zzg1P9z/1GbFPpi2kZeOZre2HQfojoU686oGBdvDe0BK6+jgir1hzwfJBB7fvQ== \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/dev/tk_notify_public_key.txt b/mall-shop/src/main/resources/payKey/lakala/dev/tk_notify_public_key.txt new file mode 100644 index 00000000..e035f0f4 --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/dev/tk_notify_public_key.txt @@ -0,0 +1 @@ +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCym9q+/ifWkvf2og25VFJZDH1Ol3SGrJhJvxllTgKCEvghlfqiLR62driMW3RxpUGNBwRhpz2DVAxoqc0LYKxKc1hV7wZJjtbVS3VBOWbkOv+qcxYBiUzovfro0ihOIWz20yeMvv9aqlS8qUledY+5tkGURJ7PG4is7sSjUcw2RQIDAQAB \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/api_cert.cer b/mall-shop/src/main/resources/payKey/lakala/prod/api_cert.cer new file mode 100644 index 00000000..0ddadbf3 --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/api_cert.cer @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIGAZSCuCxUMA0GCSqGSIb3DQEBBQUAMGAxFDASBgNVBAMM +C0xBS0FMQS1MQU9QMQswCQYDVQQGEwJDTjEXMBUGA1UECgwOTGFrYWxhIENvLixM +dGQxDzANBgNVBAsMBkxLTC1ZRjERMA8GA1UEBwwIc2hhbmdoYWkwHhcNMjUwMTIw +MDc1ODE1WhcNMzUwMTIwMDc1ODE1WjBgMRQwEgYDVQQDDAtMQUtBTEEtTEFPUDEL +MAkGA1UEBhMCQ04xFzAVBgNVBAoMDkxha2FsYSBDby4sTHRkMQ8wDQYDVQQLDAZM +S0wtWUYxETAPBgNVBAcMCHNoYW5naGFpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAksIeSx7qKgmI7yFSglMBsty3ZoyC8qsCbLSWANwUPYikd5TPojB8 +43wJGJxOBOtDV8FL55q0zX3eQOE4BOVGcDMMUsZsAhy4ST46ne4e8CFVFHmM6TON +tS5Kla0hLjETpUjYStR8En534uoLYvnNPAI1CvmvWiUo+QGd3yc2SmyE+XB/8bKk +wiLjAauGyfb8m2BgCaoBIai+0IImInX2Xb85L0TAu0eYC6fX1T8BCupA4EVOpvLL +cM5P8nye4mibgS46lzlve1soC73W1vGAHnMw4epzk89NHKIMYEmZPoCU6jKuDbR0 +K73QwU0QZt9MSJnTdnWt/PeUJ8PzkhV0RQIDAQABo2AwXjAPBgNVHRMECDAGAQH/ +AgEAMB8GA1UdIwQYMBaAFG1rabju/OE7B3cPuQFZhs77ku5qMB0GA1UdDgQWBBRt +a2m47vzhOwd3D7kBWYbO+5LuajALBgNVHQ8EBAMCBsAwDQYJKoZIhvcNAQEFBQAD +ggEBACzdFsulR2X6HPKX+D2VU7msJyXRVWrAi06SV/zds8lPYUwSTEksbpWejD5L +ABftyv5DL7bU66vL80she2r5d+DbY+gB2hAgRHW+ymzfAqQJOcpciAXZHxc4orf+ +V4YtmLIRT0lg9rfU93D4S9fPe+HHMAmKFQXjnZNiv7DOar62MP0RrI74hWevaZao +gK8cMspxUW7H9VFvJvgGZRTRXVFHIvaPOpvlsdCYfUDomH2sWVLIiaSQoAVXxoET +c0HJB0er7Df2PSBSF/8bl9cVmp5HDB2YgtlfNKaFneGNN2WQtO3gl56heud8hGAe +F4YZkE8ML2mh83QqVSY4AkfWo+0= +-----END CERTIFICATE----- diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/api_private_key.pem b/mall-shop/src/main/resources/payKey/lakala/prod/api_private_key.pem new file mode 100644 index 00000000..68896c6c --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/api_private_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCSwh5LHuoqCYjv +IVKCUwGy3LdmjILyqwJstJYA3BQ9iKR3lM+iMHzjfAkYnE4E60NXwUvnmrTNfd5A +4TgE5UZwMwxSxmwCHLhJPjqd7h7wIVUUeYzpM421LkqVrSEuMROlSNhK1HwSfnfi +6gti+c08AjUK+a9aJSj5AZ3fJzZKbIT5cH/xsqTCIuMBq4bJ9vybYGAJqgEhqL7Q +giYidfZdvzkvRMC7R5gLp9fVPwEK6kDgRU6m8stwzk/yfJ7iaJuBLjqXOW97WygL +vdbW8YAeczDh6nOTz00cogxgSZk+gJTqMq4NtHQrvdDBTRBm30xImdN2da3895Qn +w/OSFXRFAgMBAAECggEACN60qAOpUXscDJ/t9bSAoMfYSdlqPLJ7RgiwHEMw8fsl +PZj/56SYl8MyZYtk58U0X8RuCwR9swHNggxar2kQFc3wC7y0YHhN5xdcwZjXw9mv +47no+1tDUFUI6vz4yWrr7gx2DifDi/aa1lsg7w18CwlzHUO0BnXCgTJI0Wj0ThEj +k3AxCv41a4dwQW2EejaikMdzkWv7p9cG1cjJocBvaURg8RZrw2v1lqXx9iZb5Ywv +XkQP907AAnUn40bLgQ7hyUruxjKPzIM5ZjIER6PTtvGk1Uzt7Kj2YZZ4egYp0PXv +5fEJIcppeIyGfatEZ4U/ZmfA0L7+xtSY8kICndPSTQKBgQDHy5n6N6JdYyyrVFH/ +x4xTHwyiReOzw5Pz2K9uSxZM6pmDWfZexAxkpMycQotZYOk6GYDqml5XAiXAYVKn +i9FCEC6PnFRZ/v7TN3ag3k8eXwLrQu589+X0ppOizIp01IQ8AWAUr1tA/UcqLt8T +5XSHnoEDo9SY9Aw1kskY5TWoowKBgQC8CwRxfFNWk3Na+U4HgvVv8CSrB7rbkLUI +L11jNKFx4xms8uIjBrIencHsRAazoG73wMsY7MzbPtJKIFcXmSKjLFClJ6luN9np +1tYkWXF/nyo6L2IhCG7N28sxYa56XTR0lDnfkW+LiI7iaC1Z+XUtxoY3ZIL+0Dbv +wCfYS2I19wKBgQCtoRjtRJALzHYzZpHkUQx36BHYrBgYTy0yyuqaVRLKQU6rwfDD +pmiGxlkyqvni8L9+yE8qkoDN6IXaTDnCdVWjreBo2ZjMpTfbYuXrI4dqg3NL9NzB +KcZj+Kdw4YSpx04rv546G7KYJgREentPFSdR/MNt4GCPk/dQT4IH6pnZNQKBgQC2 +esKPFqURCPoSuIf+RhnDzPjoeBHe7KihDsXDddmN2WKbMQA+GUtU5qa/jqwqOHA3 +QQJWZ8XMpUQQ5x1dKyAv1NtVyg1jhhOUFwWsNJgtztl9qWnCwJo2byrZ+v8Eg9pZ +n1YiHNJwR87Q62PHQotyJossyf9NI+WL958zCMR/tQKBgCTDIKnfjq4riVLEOLxl +oGzGMowFTp9v8dyvrYj71C0ZDzLg/EH1tTQ4K46GIXJfyZvIATAIisC6R37KNmUA +w8f6YAOSS+iZBSZo36DoGIqJTYGyYgQDDftMsj/gxthgwRO5Zhwbn63ayJkWYyPy +C1RJ27LO8qX+3/s5pV7G9GlO +-----END PRIVATE KEY----- diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/lkl_platform.cer b/mall-shop/src/main/resources/payKey/lakala/prod/lkl_platform.cer new file mode 100644 index 00000000..c24d19c2 --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/lkl_platform.cer @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIGAXUrc4b4MA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT +AkNOMRAwDgYDVQQIDAdCZWlKaW5nMRAwDgYDVQQHDAdCZWlKaW5nMRcwFQYDVQQK +DA5MYWthbGEgQ28uLEx0ZDEqMCgGA1UEAwwhTGFrYWxhIE9yZ2FuaXphdGlvbiBW +YWxpZGF0aW9uIENBMB4XDTIwMTAxNTA4NDk1MloXDTMwMTAxMzA4NDk1MlowZTEL +MAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaUppbmcxEDAOBgNVBAcMB0JlaUppbmcx +FzAVBgNVBAoMDkxha2FsYSBDby4sTHRkMRkwFwYDVQQDDBBBUElHVy5MQUtBTEEu +Q09NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwAXZw9lupWcFXouC +Nhm0DQT47Zf4KOIRF8rqT8Ps3pYzT8odROJ8rq4P+lciGrg29czpqrRM22yQktFr +itvcM7JlE6jFbGH3rycnvGvhRYU/j1N9k0ozm8oVwmKX357/OtGzNivBECGSnU9L +Bkp4Nm9M1K4cOwEuZ0xsQEthZjQYF0mDpnlWmVJL5i1Lq834atN2qrb/mzMHBNtD +JnqRV7rPL39lKpe7LJiitsC2JuW1UbWZZU1NNwA/rz2d83C+KD1DLJ0+sMYY2Q3T +OQ4BPAowDEwOH7XAXrHM/0kRm+ZeIFlwevEGIQWmMt1Ogz+AW4Iq0slINc4wOINK +vH9tHwIDAQABo4HVMIHSMIGSBgNVHSMEgYowgYeAFCnH4DkZPR6CZxRn/kIqVsMo +dJHpoWekZTBjMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UE +BwwHQmVpSmluZzEXMBUGA1UECgwOTGFrYWxhIENvLixMdGQxFzAVBgNVBAMMDkxh +a2FsYSBSb290IENBggYBaiUALIowHQYDVR0OBBYEFIya0Yc4OSBer55JLyA0AYe9 +m8mTMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUA +A4IBAQCBEwOlk3mXigNv94Drn3dcaY2ml/y+8yNpAIuUhuBE00WFoqEX5lOatFy5 +fzdXuC12lBVQ8SjSm3aH7k2X0eXqDzkOHiur2ZBRKmJ++J4TeenuSUOjSIbQK/DT +vxaqFUjYwFSVCyizpy7wfU4wKt+jOuFb9LyULJ9lkM1dV9Kh7Lmd9+nlJYYuPEPU +LJkkVZqSALSiiJudXnTwlISjZTXEAkJpdIlMw+hvPTAkoG95B95M+OV/uLbItGK+ +qT4+RHWo8EbBDPQYo6J4QYHOxRlfMoGBMyrz6XDt7ELLmT7ld4aE02w6KQPfK3gq +kLDT+/STozvaNmXzBJh7J6KqxJBH +-----END CERTIFICATE----- \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/tk_api_private_key.txt b/mall-shop/src/main/resources/payKey/lakala/prod/tk_api_private_key.txt new file mode 100644 index 00000000..a17740cc --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/tk_api_private_key.txt @@ -0,0 +1 @@ +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKnlpXdmJcbZHxh3w3Ghy/5wXtUBQcFzRMXgw2d7uYjzAzbIQ/ZCAj6srWoN/obtkU+G62kgcpByGmiVtK4LS+BoelKM705SzV+mcr8mNmr2jwNq1SwDSfgVxx5z00FtXl3LnKvCujvGYOmx6+nX8h6IiyyLX6IP6cFaqpUXHsZVAgMBAAECgYA4NpeM7etJ48T6H4Y3LsWEJkH6UDQlgbIblsaQkstMmLtTgOebrzN28UNfd8njcu9FVOrHGclOKbK7L+1cOLiduWsZKc/c/gAy9wAR4EhoLvlerH9EEPiPWFxdEDbMxPqlkpqLOo+PxHrhTn4vU4CaPdJtL2ujKn7nmsUdUDWo8QJBANS1TlM6nhPt2XlzN5kGfsJ4kBYNjuLXNA2YdNuC2ttYvEXHJ9T70FN/GnRBBIZu47uHH3Ie5nfep+qMk6a8RP8CQQDMecIyI0z1kVt+tOfWKw2ZFLsi74708qTaeR4W1ABtkngj1+bxoWWXr3KqhjqJkWxnhioSfXqu7CScNzjdM1CrAkAQd+ESjI1EmbumrYb2cAxMXi05p98SLPs4uj8B58WuCda5yEuLL9vXOxX/PjFtfxRepn2GxmGtki2J+UxNMnJdAkAFoORjlO0tZU7rcfdfwdeh+xwbnhSFUZiQGv1lC3jnizybX/oPdK3jOwUhBIjf+IzPXLYTxDh4UC/BzRNXo235AkEAhgYBk6H7RU2iIuvwz1c6CtE1gJ8DvEp1F0KOMWMFB0KCpDXUToix0dlMz962FozYENi4X4zYQo6nFwlXeS3Pfw== \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/tk_api_public_key.txt b/mall-shop/src/main/resources/payKey/lakala/prod/tk_api_public_key.txt new file mode 100644 index 00000000..7f699e3a --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/tk_api_public_key.txt @@ -0,0 +1 @@ +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp5aV3ZiXG2R8Yd8Nxocv+cF7VAUHBc0TF4MNne7mI8wM2yEP2QgI+rK1qDf6G7ZFPhutpIHKQchpolbSuC0vgaHpSjO9OUs1fpnK/JjZq9o8DatUsA0n4Fccec9NBbV5dy5yrwro7xmDpsevp1/IeiIssi1+iD+nBWqqVFx7GVQIDAQAB \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/tk_notify_private_key.txt b/mall-shop/src/main/resources/payKey/lakala/prod/tk_notify_private_key.txt new file mode 100644 index 00000000..558c30e0 --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/tk_notify_private_key.txt @@ -0,0 +1 @@ +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJDy8KWN4t65ifbOrCx+oChGorxzTVzh5RCeOCSS/0LJ57Qoc8BZ+xLyphtITtHtcIRY39cYFmSfjTLnNMV/Y+FC47EeBbDKiiIKg06o3/X+/sW+J9FNQs+6H7L15o9j1P0P0h8bo/cs3T88m9r57JMr3Vb3P/iTE4+6XT4HHRXvAgMBAAECgYAeZCGaxKYVxtcrfWfSKHaSG0VRt+c3m1F464Of/4Yvzb+cIYMriHJBJck4HTARifQ2e5/rLGy9ikqDqsCAzFX3e0/OG9wvvyodCoWYwVy37Zus5HqmYyndhdQGpTvw8lNVA90iOU5q5z8DxKc9iuiPLW0MCpgvQ6Xjsv/vBN3KqQJBAMTODXlM8kfsRaSs5YzSyamrcxruD3x0+vrHtkyR9e5Q+KWu1WcsFeRcRRXwtJYg8+9KiRB/fFOKRCowhzKYciUCQQC8i/18QEAPL3jczJsYe/Zt9lRK9qJjwXdgsPAsm5iwsv8oPUtWEykI+ct89twzbdXJYK4M1AM6d0qwit8rK+mDAkEAhlNJWQCO4U4/no0vxDVe1UKjNr9DrKO/ZgmHwK1jGT0E6TjyiQ/LBWmA8d+vskRdmnbaJHuPJQnbSpyCuqLYTQJBAIv3dC6n16YuPI/UyBIN/Cs3YO3hVz2soj1CPJkwzNHnnyk1D1cPlDfYj+ntrBv1nm4G+k4FmXvukKCsBu2ahJcCQBMBld8cpapoiou+oCeMay+W1W8kxsvwI4pW602FCecE2XJwWjvc/eht0D3j5F8wIvVxlzvX9lkZ5GYpEQBsIuw= \ No newline at end of file diff --git a/mall-shop/src/main/resources/payKey/lakala/prod/tk_notify_public_key.txt b/mall-shop/src/main/resources/payKey/lakala/prod/tk_notify_public_key.txt new file mode 100644 index 00000000..7018fac3 --- /dev/null +++ b/mall-shop/src/main/resources/payKey/lakala/prod/tk_notify_public_key.txt @@ -0,0 +1 @@ +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQ8vCljeLeuYn2zqwsfqAoRqK8c01c4eUQnjgkkv9Cyee0KHPAWfsS8qYbSE7R7XCEWN/XGBZkn40y5zTFf2PhQuOxHgWwyooiCoNOqN/1/v7FvifRTULPuh+y9eaPY9T9D9IfG6P3LN0/PJva+eyTK91W9z/4kxOPul0+Bx0V7wIDAQAB \ No newline at end of file