ruoyi-admin/src/main/java/com/ruoyi/im/ImApiController.java
@@ -300,13 +300,13 @@ .eq(MedicalInsuranceAccount::getAccountStatus, MedicalInsuranceAccount.AccountStatus.ACTIVE) ); user.setKycStatus(userAccount.getKycStatus()); user.setGroupPermissions(userAccount.getGroupPermissions()); user.setAddFriend(userAccount.getAddFriend()); if(ObjectUtil.isEmpty(insuranceAccount)){ return Result.success(user); } user.setAmountClaimed(insuranceAccount.getAmountClaimed()); user.setAlreadyReceived(insuranceAccount.getAlreadyReceived()); user.setGroupPermissions(userAccount.getGroupPermissions()); user.setAddFriend(userAccount.getAddFriend()); return Result.success(user); }catch (Exception e){ log.error("获取失败:",e); ruoyi-admin/src/main/java/com/ruoyi/im/out/UserAccountOut.java
@@ -63,4 +63,16 @@ //下级用户 private List<UserAccountOut> subordinateList; //手持照片 private String name; //身份证 private String idCard; //创建群开关 0 开启 1关闭 private Integer groupPermissions; //添加好友 0 开启 1 关闭 private Integer addFriend; } ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/ImApiServcieImpl.java
@@ -167,11 +167,17 @@ // 注册云信账号(远程调用) Map<String, String> paramMap = new HashMap<>(); paramMap.put("accid", dto.getAccount()); if(StringUtils.isNotEmpty(dto.getNikeName())){ paramMap.put("name", dto.getNikeName()); } paramMap.put("token", dto.getPassword()); if (StringUtils.isNotEmpty(dto.getNikeName())) { Map<String, String> userInfoMap = new HashMap<>(); userInfoMap.put("name", dto.getNikeName()); // 使用 JSON 工具将 Map 转为 JSON 字符串 ObjectMapper objectMapper = new ObjectMapper(); String userInfoJson = objectMapper.writeValueAsString(userInfoMap); paramMap.put("user_information", userInfoJson); } YunxinApiResponse response = yunxinClient.executeV1Api(YUNXIN_CREATE_PATH, paramMap); // 处理云信响应 ruoyi-admin/src/main/java/com/ruoyi/im/service/impl/UserPolicyServiceImpl.java
@@ -83,54 +83,73 @@ } if(userAccount.getBalance().compareTo(insuranceProduct.getPremium()) < 0){ String orderNo = generateOrderNo(); String payOrder = payService.createOrder(insuranceProduct.getPremium().multiply(new BigDecimal("100")), orderNo,dto.getPayProductId()); String payOrder = payService.createOrder( insuranceProduct.getPremium(), orderNo, dto.getPayProductId() ); Map<String, Object> parse = (Map<String, Object>) JSONUtils.parse(payOrder); if("0014".equals(parse.get("errCode"))){ return Result.error("当前支付不可用,请更换其他支付方式!"); Integer code = (Integer) parse.get("code"); if (code == null) { return Result.error("支付系统返回异常"); } if("FAIL".equals(parse.get("retCode"))){ return Result.error("获取支付通道失败!"); } String payUrl; switch (code) { case 200: // 成功,获取支付链接 payUrl = (String) parse.get("result"); userAccountService.updateById(userAccount); userAccountService.updateById(userAccount); UserPolicy userPolicy = new UserPolicy(); userPolicy.setAccount(userAccount.getAccount()); userPolicy.setProductName(insuranceProduct.getProductName()); userPolicy.setUserId(userAccount.getId()); userPolicy.setProductId(insuranceProduct.getId()); userPolicy.setCoverageAmount(insuranceProduct.getCoverageAmount()); userPolicy.setPremium(insuranceProduct.getPremium()); userPolicy.setTerm(insuranceProduct.getTerm()); userPolicy.setName(dto.getName()); userPolicy.setGender(dto.getGender()); userPolicy.setPayStatus(0); userPolicy.setOrderNo(orderNo); userPolicy.setBirthDate(LocalDate.parse(dto.getBirthDate())); UserPolicy userPolicy = new UserPolicy(); userPolicy.setAccount(userAccount.getAccount()); userPolicy.setProductName(insuranceProduct.getProductName()); userPolicy.setUserId(userAccount.getId()); userPolicy.setProductId(insuranceProduct.getId()); userPolicy.setCoverageAmount(insuranceProduct.getCoverageAmount()); userPolicy.setPremium(insuranceProduct.getPremium()); userPolicy.setTerm(insuranceProduct.getTerm()); userPolicy.setName(dto.getName()); userPolicy.setGender(dto.getGender()); userPolicy.setPayStatus(0); userPolicy.setOrderNo(orderNo); userPolicy.setBirthDate(LocalDate.parse(dto.getBirthDate())); userPolicy.setOccupation(dto.getOccupation()); userPolicy.setIdCard(dto.getIdCard()); userPolicy.setPhone(dto.getPhone()); userPolicy.setPolicyNumber(generatePolicyNumber()); userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.PENDING); userPolicy.setCreatedAt(new Date()); userPolicy.setUpdatedAt(new Date()); userPolicy.setApprovalStatus(0); userPolicy.setIsLifelong(insuranceProduct.getTerm() == 0 ? 0 : 1); userPolicy.setOccupation(dto.getOccupation()); userPolicy.setIdCard(dto.getIdCard()); userPolicy.setPhone(dto.getPhone()); userPolicy.setPolicyNumber(generatePolicyNumber()); userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.PENDING); userPolicy.setCreatedAt(new Date()); userPolicy.setUpdatedAt(new Date()); userPolicy.setApprovalStatus(0); userPolicy.setIsLifelong(insuranceProduct.getTerm() == 0 ? 0 : 1); save(userPolicy); // 获取 payUrl Object payParamsObj = parse.get("payParams"); if (payParamsObj instanceof Map) { Map<?, ?> payParamsMap = (Map<?, ?>) payParamsObj; String payUrl = (String) payParamsMap.get("payUrl"); if (payUrl != null && !payUrl.trim().isEmpty()) { save(userPolicy); return Result.success(payUrl); } } return Result.error("支付链接获取失败,请联系客服!"); case 401: return Result.error("未授权访问支付系统"); case 403: return Result.error("禁止访问支付系统"); case 404: return Result.error("支付接口不存在"); case 0014: // 注意:0014可能是字符串,需要根据实际情况处理 return Result.error("当前支付不可用,请更换其他支付方式!"); default: // 其他错误码 String message = (String) parse.get("message"); if (message != null && !message.isEmpty()) { return Result.error("支付失败: " + message); } else { return Result.error("获取支付通道失败!"); } } }else{ userAccount.setBalance(userAccount.getBalance().subtract(insuranceProduct.getPremium())); ruoyi-admin/src/main/java/com/ruoyi/im/util/PayService.java
@@ -3,11 +3,16 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; @Component @@ -19,6 +24,9 @@ @Value("${pay.key}") private String key; @Value("${pay.channelCode}") private String channelCode; @Value("${pay.base-url}") private String baseUrl; @@ -26,50 +34,171 @@ private String callBackUrl; /** * 创建支付订单 * 创建支付订单 - 适配新支付系统(修正版) */ public String createOrder(BigDecimal amount,String orderNo,String payProductId ) { public String createOrder(BigDecimal amount, String orderNo,String payProductId) { try { Map<String, Object> params = new HashMap<>(); params.put("mchId", mchId); params.put("productId", 8089); params.put("mchOrderNo",orderNo ); params.put("amount", 3000); params.put("notifyUrl", callBackUrl); String sign = generateSign(params); // 必需参数 params.put("userCode", mchId); // 商户号 params.put("channelCode", payProductId); // 通道码 params.put("orderId", orderNo); // 订单号 params.put("orderMoney", amount); // 金额(格式化金额) params.put("callbackUrl", callBackUrl); // 回调地址 // 可选参数(根据业务需要添加) // params.put("returnUrl", returnUrl); // params.put("clientIp", clientIp); // params.put("currency", currency); // params.put("gameId", gameId); // params.put("gameIp", gameIp); // 生成签名 String sign = generateNewSign(params); params.put("sign", sign); String url = baseUrl + "/api/pay/create_order"; return sendPost(url, params); // 构建GET请求URL String url = buildGetUrl(baseUrl, params); // 发送GET请求,设置正确的Content-Type和可能的token return sendGet(url); } catch (Exception e) { return "{\"retCode\":\"FAIL\",\"retMsg\":\"" + e.getMessage() + "\"}"; } } /** * 查询订单 * 发送GET请求 - 修正版 */ public String queryOrder(String orderNo) { private String sendGet(String url) { try { Map<String, Object> params = new HashMap<>(); params.put("mchId", mchId); params.put("mchOrderNo", orderNo); HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(10000); // 10秒连接超时 connection.setReadTimeout(10000); // 10秒读取超时 String sign = generateSign(params); params.put("sign", sign); // 设置请求头 connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Accept", "*/*"); String url = baseUrl + "/api/pay/query_order"; return sendPost(url, params); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); return response.toString(); } else { return "{\"retCode\":\"FAIL\",\"retMsg\":\"HTTP error code: " + responseCode + "\"}"; } } catch (Exception e) { return "{\"retCode\":\"FAIL\",\"retMsg\":\"" + e.getMessage() + "\"}"; } } /** * 新支付系统签名生成 - 最终修正版 */ private String generateNewSign(Map<String, Object> params) { try { // 1. 参数名ASCII码从小到大排序(字典序) List<String> keys = new ArrayList<>(params.keySet()); Collections.sort(keys); // 2. 如果参数的值为空不参与签名 StringBuilder stringA = new StringBuilder(); for (String key : keys) { Object value = params.get(key); // 值为空不参与签名,且sign参数不参与签名 if (value != null && !"".equals(value.toString().trim()) && !"sign".equals(key)) { if (stringA.length() > 0) { stringA.append("&"); } stringA.append(key).append("=").append(value.toString()); } } // 3. 在stringA最后拼接上key(根据示例,应该是直接拼接key值,没有key=前缀) // 示例:callbackUrl=http://baidu.com&channelCode=0000&orderId=P12312321123&orderMoney=2.01&userCode=10008&key=8c1511c3413106ac529f6ec9ad095c08 // 注意:示例中实际上是有"&key="的,但文档描述是"在stringA最后拼接上key" // 按照示例,应该是:stringA + "&key=" + key String stringSignTemp = stringA.toString() + "&key=" + key; // 4. 对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为小写 String md5Result = md5(stringSignTemp); return md5Result.toLowerCase(); } catch (Exception e) { throw new RuntimeException("生成签名失败: " + e.getMessage()); } } /** * 金额格式化(确保金额格式正确) */ private String formatAmount(BigDecimal amount) { // 根据接口描述:金额,单位元,小数点的0可随意设置,比如100.10或100.00或100都可以 // 这里我们统一格式化为2位小数 return String.format("%.2f", amount); } /** * 构建GET请求URL */ private String buildGetUrl(String baseUrl, Map<String, Object> params) { try { StringBuilder urlBuilder = new StringBuilder(baseUrl); boolean firstParam = true; for (Map.Entry<String, Object> entry : params.entrySet()) { if (entry.getValue() != null && !"".equals(entry.getValue().toString().trim())) { if (firstParam) { urlBuilder.append("?"); firstParam = false; } else { urlBuilder.append("&"); } urlBuilder.append(URLEncoder.encode(entry.getKey(), "UTF-8")) .append("=") .append(URLEncoder.encode(entry.getValue().toString(), "UTF-8")); } } return urlBuilder.toString(); } catch (UnsupportedEncodingException e) { throw new RuntimeException("构建URL失败: " + e.getMessage()); } } /** * MD5加密工具方法 */ private String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder hexString = new StringBuilder(); for (byte b : messageDigest) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5加密失败", e); } } /** * 验证回调签名 */ public boolean verifySign(Map<String, String> params) { ruoyi-admin/src/main/java/com/ruoyi/web/controller/product/UserPolicyController.java
@@ -2,6 +2,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @@ -16,10 +17,13 @@ import com.ruoyi.im.util.RedisDistributedLock; import com.ruoyi.im.util.UserPolicyUtils; import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.dto.PayCallbackDTO; import com.ruoyi.system.domain.dto.UserPolicyDto; import com.ruoyi.im.service.UserPolicyService; import com.ruoyi.system.service.UserAccountService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.util.CollectionUtils; @@ -28,6 +32,9 @@ import javax.validation.Valid; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; @@ -35,6 +42,7 @@ @RestController @RequestMapping("/userPolicy") @Slf4j public class UserPolicyController extends BaseController { @Autowired @@ -56,6 +64,9 @@ InsuranceProductService insuranceProductService; @Autowired private PayService payService; @Value("${pay.key}") private String key; /** * 保险购买申请 @@ -135,9 +146,7 @@ wrapper.ne(UserPolicy::getApprovalStatus, 0); }else{ wrapper.eq(UserPolicy::getApprovalStatus, 0); wrapper.eq(UserPolicy::getPayStatus,3) .or() .eq(UserPolicy::getPayStatus,2); wrapper.eq(UserPolicy::getPayStatus,2); } // 按创建时间倒序排列 @@ -315,50 +324,151 @@ /** * 支付回调接口 - 支付平台会调用这个接口 */ @PostMapping("/notify") public String payNotify(@RequestParam Map<String, String> params) { System.out.println("收到支付回调: " + params); public String payNotify(@RequestBody PayCallbackDTO callbackDTO) { try { log.info("收到支付回调通知: {}", JSON.toJSONString(callbackDTO)); // 验证签名 if (!payService.verifySign(params)) { System.out.println("签名验证失败"); return "fail"; // 1. 验证签名 if (!verifySign(callbackDTO)) { log.error("签名验证失败: {}", callbackDTO.getOrderId()); return "签名验证失败"; } // 2. 根据订单号查询保单 UserPolicy userPolicy = userPolicyService.getOne(new LambdaQueryWrapper<UserPolicy>() .eq(UserPolicy::getOrderNo, callbackDTO.getOrderId())); if (userPolicy == null) { log.error("订单不存在: {}", callbackDTO.getOrderId()); return "订单不存在"; } if(userPolicy.getPayStatus() == 2){ log.error("订单已支付完成: {}", callbackDTO.getOrderId()); return "订单已支付完成"; } // 3. 根据订单状态处理业务逻辑 boolean processResult = processPayResult(callbackDTO, userPolicy); return processResult ? "success" : "fail"; } catch (Exception e) { log.error("支付回调处理异常: {}", e.getMessage(), e); return "FAIL"; } // 获取关键参数 String orderNo = params.get("mchOrderNo"); String status = params.get("status"); String amount = params.get("amount"); System.out.println("订单号: " + orderNo + ", 状态: " + status + ", 金额: " + amount); // 这里调用您的业务处理逻辑 handlePayResult(orderNo, Integer.valueOf(status), params); return "success"; } private boolean verifySign(PayCallbackDTO callbackDTO) { try { Map<String, Object> params = new HashMap<>(); params.put("channelCode", callbackDTO.getChannelCode()); params.put("orderId", callbackDTO.getOrderId()); params.put("orderMoney", callbackDTO.getOrderMoney()); params.put("orderStatus", callbackDTO.getOrderStatus()); params.put("userCode", callbackDTO.getUserCode()); /** * 处理支付结果 - 这里您自己实现业务逻辑 */ private void handlePayResult(String orderNo, Integer status, Map<String, String> params) { UserPolicy userPolicy = userPolicyService.getOne(new LambdaQueryWrapper<UserPolicy>() .eq(UserPolicy::getOrderNo,orderNo)); if(ObjectUtil.isNotEmpty(userPolicy)){ if (status == 2) { userPolicy.setPayStatus(status); } else if (status == 3) { userPolicy.setPayStatus(status); } else if (0010 == status) { userPolicy.setPayMsg("系统超时或异常"); } else if (0014 == status) { userPolicy.setPayMsg("mchId是系统分配的商户号,不能自己生成"); String generatedSign = generateCallbackSign(params); return generatedSign.equalsIgnoreCase(callbackDTO.getSign()); } catch (Exception e) { log.error("签名验证异常: {}", e.getMessage(), e); return false; } } private String generateCallbackSign(Map<String, Object> params) { try { List<String> keys = new ArrayList<>(params.keySet()); Collections.sort(keys); StringBuilder stringA = new StringBuilder(); for (String key : keys) { Object value = params.get(key); if (value != null && !"".equals(value.toString().trim()) && !"sign".equals(key)) { if (stringA.length() > 0) { stringA.append("&"); } stringA.append(key).append("=").append(value.toString()); } } userPolicyService.updateById(userPolicy); String stringSignTemp = stringA.toString() + "&key=" + key; String md5Result = md5(stringSignTemp); return md5Result.toLowerCase(); } catch (Exception e) { throw new RuntimeException("生成回调签名失败: " + e.getMessage()); } } private boolean processPayResult(PayCallbackDTO callbackDTO, UserPolicy userPolicy) { try { switch (callbackDTO.getOrderStatus()) { case 2: // 已支付 return handlePaySuccess(callbackDTO, userPolicy); case 3: // 超时/过期 return handlePayTimeout(callbackDTO, userPolicy); default: log.info("订单状态未完成: {}, 状态: {}", callbackDTO.getOrderId(), callbackDTO.getOrderStatus()); return true; } } catch (Exception e) { log.error("处理支付结果异常: {}", e.getMessage(), e); return false; } } private boolean handlePaySuccess(PayCallbackDTO callbackDTO, UserPolicy userPolicy) { if (userPolicy.getPayStatus() == 2) { log.info("订单已支付,无需重复处理: {}", callbackDTO.getOrderId()); return true; } userPolicy.setPayStatus(2); // 2-支付成功 userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.ACTIVE); userPolicy.setUpdatedAt(new Date()); boolean updateResult = userPolicyService.updateById(userPolicy); if (updateResult) { log.info("支付成功处理完成: {}", callbackDTO.getOrderId()); return true; } else { log.error("更新保单状态失败: {}", callbackDTO.getOrderId()); return false; } } private boolean handlePayTimeout(PayCallbackDTO callbackDTO, UserPolicy userPolicy) { userPolicy.setPayStatus(3); // 3-支付超时/过期 userPolicy.setPolicyStatus(UserPolicy.PolicyStatus.EXPIRED); userPolicy.setUpdatedAt(new Date()); boolean updateResult = userPolicyService.updateById(userPolicy); if (updateResult) { log.info("支付超时处理完成: {}", callbackDTO.getOrderId()); return true; } else { log.error("更新保单超时状态失败: {}", callbackDTO.getOrderId()); return false; } } private String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder hexString = new StringBuilder(); for (byte b : messageDigest) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5加密失败", e); } } } ruoyi-admin/src/main/java/com/ruoyi/web/controller/user/UserController.java
@@ -13,16 +13,19 @@ import com.ruoyi.im.dto.RegisterDto; import com.ruoyi.im.out.UserAccountOut; import com.ruoyi.im.service.ImApiServcie; import com.ruoyi.im.service.UserKycService; import com.ruoyi.im.util.ConverterUtil; import com.ruoyi.im.util.PhoneNumberValidatorUtil; import com.ruoyi.im.util.SymmetricCryptoUtil; import com.ruoyi.system.domain.UserAccount; import com.ruoyi.system.domain.UserKyc; import com.ruoyi.system.domain.vo.UserAccountUpdateVo; import com.ruoyi.system.domain.vo.UserAccountVo; import com.ruoyi.system.service.UserAccountService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -40,6 +43,9 @@ @Autowired private ImApiServcie imApiServcie; @Autowired private UserKycService userKycService; /** * 获取会员列表 */ @@ -49,17 +55,20 @@ // 创建查询条件包装器 LambdaQueryWrapper<UserAccount> queryWrapper = new LambdaQueryWrapper<>(); // 只有当 keyword 不为空时才添加 OR 条件 if (ObjectUtil.isNotEmpty(vo.getKeywords())) { queryWrapper.and(wrapper -> wrapper .eq(UserAccount::getId, vo.getKeywords()) .or() .like(UserAccount::getPhoneNumber, vo.getKeywords()) .or() .like(UserAccount::getAccount, vo.getKeywords()) .or() .like(UserAccount::getNickname, vo.getKeywords()) ); String keywords = vo.getKeywords().trim(); if (StringUtils.isNotEmpty(keywords)) { queryWrapper.and(wrapper -> wrapper .eq(UserAccount::getInvitationCode, keywords) .or() // 使用右模糊匹配,可以利用索引 .likeRight(UserAccount::getPhoneNumber, keywords) .or() .likeRight(UserAccount::getAccount, keywords) .or() .likeRight(UserAccount::getNickname, keywords) ); } } // 添加其他条件 @@ -74,6 +83,25 @@ queryWrapper.orderByDesc(UserAccount::getCreateTime); startPage(); List<UserAccount> list = userAccountService.list(queryWrapper); List<Integer> idList = list.stream() .map(UserAccount::getId) .collect(Collectors.toList()); if(!CollectionUtils.isEmpty(idList)){ List<UserKyc> kycList = userKycService.list(new LambdaQueryWrapper<UserKyc>() .in(UserKyc::getUserId, idList) ); Map<Integer, UserKyc> kycMap = kycList.stream() .collect(Collectors.toMap(UserKyc::getUserId, kyc -> kyc)); list.forEach(f->{ if(ObjectUtil.isNotEmpty(kycMap.get(f.getId()))){ f.setName(kycMap.get(f.getId()).getName()); f.setIdCard(kycMap.get(f.getId()).getIdCard()); } }); } PageInfo<UserAccount> pageInfo = new PageInfo<>(list); @@ -129,10 +157,6 @@ } } /** * 加载当前层级的直接下级用户 * @param userAccountOuts 当前层级的用户列表 */ private void loadCurrentLevelSubordinates(List<UserAccountOut> userAccountOuts) { if (ObjectUtil.isEmpty(userAccountOuts)) { return; @@ -146,7 +170,6 @@ .collect(Collectors.toList()); if (accounts.isEmpty()) { // 如果没有账号,为所有用户设置空列表 userAccountOuts.forEach(user -> user.setSubordinateList(new ArrayList<>())); return; } @@ -158,6 +181,14 @@ // 转换为输出对象 List<UserAccountOut> allSubordinateOuts = ConverterUtil.convertToList(allSubordinates, UserAccountOut.class); // 收集所有需要设置 KYC 的用户(当前层级 + 下级) List<UserAccountOut> allUsers = new ArrayList<>(); allUsers.addAll(userAccountOuts); allUsers.addAll(allSubordinateOuts); // 一次性设置所有用户的 KYC 信息 setKycInfoForUsers(allUsers); // 按邀请人账号分组 Map<String, List<UserAccountOut>> subordinateMap = allSubordinateOuts.stream() @@ -175,7 +206,42 @@ } } /** * 为用户列表设置 KYC 信息 */ private void setKycInfoForUsers(List<UserAccountOut> users) { if (ObjectUtil.isEmpty(users)) { return; } // 收集用户ID List<Integer> idList = users.stream() .map(UserAccountOut::getId) .filter(ObjectUtil::isNotEmpty) .collect(Collectors.toList()); if (idList.isEmpty()) { return; } // 批量查询 KYC 信息 List<UserKyc> kycList = userKycService.list(new LambdaQueryWrapper<UserKyc>() .in(UserKyc::getUserId, idList) ); // 转换为 Map Map<Integer, UserKyc> kycMap = kycList.stream() .collect(Collectors.toMap(UserKyc::getUserId, kyc -> kyc, (existing, replacement) -> existing)); // 设置 KYC 信息 users.forEach(user -> { if (ObjectUtil.isNotEmpty(user.getId()) && ObjectUtil.isNotEmpty(kycMap.get(user.getId()))) { UserKyc kyc = kycMap.get(user.getId()); user.setName(kyc.getName()); user.setIdCard(kyc.getIdCard()); } }); } /** * 获取用户的下级树形结构 ruoyi-admin/src/main/resources/application.yml
@@ -156,6 +156,7 @@ pay: mch-id: "100453" key: "UHI4O7SDWRP8CTRDSGHN9KW3MIAT7GWWJ8QGL6GGZIKVLHZT3XIYEVFXDLBBWYPNXGHFN9MNN1JCCQKZFQQOVMZEH8PVTUVW2ECYDGAFOQU6GREKZOF4AOHSIRF2SY8E" base-url: "http://pay.hwpal.xyz" key: "2917adc37fd61cbfebc06e22bc91c824" channelCode: "0000" base-url: "https://xm53mksf233fd.top/agency/apis/pay/get" call-back-url: "https://api.imgbt.net/userPolicy/notify" ruoyi-system/src/main/java/com/ruoyi/system/domain/UserAccount.java
@@ -101,4 +101,12 @@ //添加好友 0 开启 1 关闭 private Integer addFriend = 1; //手持照片 @TableField(exist = false) private String name; //身份证 @TableField(exist = false) private String idCard; } ruoyi-system/src/main/java/com/ruoyi/system/domain/UserKyc.java
@@ -48,7 +48,7 @@ //手持照片 private String name; //手持照片 //身份证 private String idCard; //昵称 ruoyi-system/src/main/java/com/ruoyi/system/domain/UserPolicy.java
@@ -86,7 +86,7 @@ //驳回信息 private String message; //支付状态,0-订单生成,1-支付中,2-支付成功,3-业务处理完成 //支付状态,0=拉取失败,1=待支付,2已支付,3超时/过期 private Integer payStatus; //支付订单号 ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/PayCallbackDTO.java
New file @@ -0,0 +1,15 @@ package com.ruoyi.system.domain.dto; import lombok.Data; import java.math.BigDecimal; @Data public class PayCallbackDTO { private String channelCode; private String orderId; private BigDecimal orderMoney; private Integer orderStatus; private String sign; private String userCode; } ruoyi-system/src/main/java/com/ruoyi/system/domain/out/UserOut.java
@@ -35,8 +35,8 @@ private String aboutUs; //创建群开关 0 开启 1关闭 private Integer groupPermissions = 1; private Integer groupPermissions; //添加好友 0 开启 1 关闭 private Integer addFriend = 1; private Integer addFriend; }