You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1041 lines
50 KiB

6 months ago
2 months ago
5 months ago
6 months ago
2 months ago
6 months ago
6 months ago
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
5 months ago
2 months ago
6 months ago
5 months ago
6 months ago
5 months ago
6 months ago
5 months ago
6 months ago
5 months ago
5 months ago
5 months ago
6 months ago
5 months ago
2 months ago
2 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
1 month ago
6 months ago
  1. package com.example.demo.serviceImpl.cash;
  2. import com.example.demo.Util.LanguageTranslationUtil;
  3. import com.example.demo.Util.SimpleIdGenerator;
  4. import com.example.demo.domain.entity.*;
  5. import com.example.demo.Util.BusinessException;
  6. import com.example.demo.Util.GoldTistV2;
  7. import com.example.demo.config.RabbitMQConfig;
  8. import com.example.demo.domain.vo.bean.Region;
  9. import com.example.demo.domain.vo.cash.*;
  10. import com.example.demo.domain.vo.coin.Messages;
  11. import com.example.demo.domain.vo.coin.Result;
  12. import com.example.demo.exception.SystemException;
  13. import com.example.demo.mapper.cash.CashCollectionMapper;
  14. import com.example.demo.mapper.cash.CashRefundMapper;
  15. import com.example.demo.mapper.coin.*;
  16. import com.example.demo.service.Wallet.WalletService;
  17. import com.example.demo.service.cash.RefundService;
  18. import com.example.demo.service.coin.TranslationService;
  19. import com.github.pagehelper.PageHelper;
  20. import com.github.pagehelper.PageInfo;
  21. import lombok.extern.slf4j.Slf4j;
  22. import org.apache.commons.collections4.CollectionUtils;
  23. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  24. import org.springframework.beans.factory.annotation.Autowired;
  25. import org.springframework.stereotype.Service;
  26. import org.springframework.transaction.annotation.Transactional;
  27. import org.springframework.web.bind.annotation.RequestHeader;
  28. import com.example.demo.domain.DTO.Currency;
  29. import java.math.BigDecimal;
  30. import java.math.RoundingMode;
  31. import java.time.LocalDate;
  32. import java.util.*;
  33. import java.util.function.Function;
  34. import java.util.stream.Collectors;
  35. import static org.apache.commons.lang3.StringUtils.substring;
  36. /**
  37. * @program: GOLD
  38. * @ClassName CashRefundServiceImpl
  39. * @description:
  40. * @author: huangqizhen
  41. * @create: 202509-28 15:02
  42. * @Version 1.0
  43. **/
  44. @Service
  45. @Slf4j
  46. public class CashRefundServiceImpl implements RefundService {
  47. @Autowired
  48. private CashRefundMapper cashRefundMapper;
  49. @Autowired
  50. private RefundMapper refundMapper;
  51. @Autowired
  52. private AuditMapper auditMapper;
  53. @Autowired
  54. private MarketMapper marketMapper;
  55. @Autowired
  56. private RabbitTemplate rabbitTemplate;
  57. @Autowired
  58. private OperationLogMapper operationLogMapper;
  59. @Autowired
  60. private CashCollectionMapper cashCollectionMapper;
  61. @Autowired
  62. private LanguageTranslationUtil languageTranslationUtil;
  63. @Autowired
  64. private WalletService walletService;
  65. @Autowired
  66. private TranslationService translationService;
  67. @Autowired
  68. private WalletMapper walletMapper;
  69. @Autowired
  70. private UserMapper userMapper;
  71. @Override
  72. public PageInfo<CashRecordDTO> select(Integer pageNum, Integer pageSize, CashRecordDTO cashRecordDTO) {
  73. PageHelper.startPage(pageNum, pageSize); //必须要直接跟mapper
  74. List<CashRecordDTO> list = cashRefundMapper.select(cashRecordDTO);
  75. if (list.isEmpty()) {
  76. return new PageInfo<>(list);
  77. }
  78. // 批量收集ID
  79. Set<Integer> relatedIds = new HashSet<>();
  80. Set<Integer> marketIds = new HashSet<>();
  81. Set<Integer> submitterIds = new HashSet<>();
  82. Set<Integer> auditIds = new HashSet<>();
  83. Set<Integer> executorIds = new HashSet<>();
  84. list.forEach(item -> {
  85. if (item.getRelatedId() != null) relatedIds.add(item.getRelatedId());
  86. if (item.getMarket() != null) marketIds.add(item.getMarket());
  87. if (item.getSubmitterId() != null) submitterIds.add(item.getSubmitterId());
  88. if (item.getAuditId() != null) auditIds.add(item.getAuditId());
  89. if (item.getExecutor() != null) executorIds.add(item.getExecutor());
  90. });
  91. // 批量查询
  92. Map<Integer, CashCollection> cashCollectionMap = cashCollectionMapper.selectBatchIds(relatedIds)
  93. .stream().collect(Collectors.toMap(CashCollection::getId, Function.identity()));
  94. Map<Integer, String> marketNameMap = marketMapper.getMarketByIds(marketIds)
  95. .stream().collect(Collectors.toMap(Market::getId, Market::getName));
  96. Map<Integer, String> submitterNameMap = auditMapper.getNamesByIds(submitterIds)
  97. .stream().collect(Collectors.toMap(Admin::getId, Admin::getAdminName));
  98. Map<Integer, LhlAudit> auditMap = cashRefundMapper.getAuditBatch(auditIds)
  99. .stream().collect(Collectors.toMap(LhlAudit::getId, Function.identity()));
  100. Map<String, String> executorNameMap = auditMapper.getNamesByJwcodes(executorIds)
  101. .stream().collect(Collectors.toMap(Admin::getAccount, Admin::getAdminName));
  102. // 处理数据
  103. list.forEach(item -> {
  104. CashCollection cashCollection = cashCollectionMap.get(item.getRelatedId());
  105. if (cashCollection != null) {
  106. processCashCollection(item, cashCollection);
  107. }
  108. String marketName = marketNameMap.get(item.getMarket());
  109. String submitter = submitterNameMap.get(item.getSubmitterId());
  110. LhlAudit lhlAudit = auditMap.get(item.getAuditId());
  111. String executorName = executorNameMap.get(String.valueOf(item.getExecutor()));
  112. item.setMarketName(marketName != null ? marketName : "");
  113. item.setSubmitter(submitter != null ? submitter : "");
  114. item.setExecutorName(executorName != null ? executorName : "");
  115. if (lhlAudit != null) {
  116. item.setAreaServise(lhlAudit.getAreaServise());
  117. item.setAreaFinance(lhlAudit.getAreaFinance());
  118. item.setAreaCharge(lhlAudit.getAreaCharge());
  119. item.setHeadFinance(lhlAudit.getHeadFinance());
  120. }
  121. });
  122. return new PageInfo<>(list);
  123. }
  124. private void processCashCollection(CashRecordDTO item, CashCollection cashCollection) {
  125. // 设置默认值
  126. Integer freeGold = cashCollection.getFreeGold() != null ? cashCollection.getFreeGold() : 0;
  127. Integer permanentGold = cashCollection.getPermanentGold() != null ? cashCollection.getPermanentGold() : 0;
  128. BigDecimal free = new BigDecimal(freeGold).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
  129. BigDecimal permanent = new BigDecimal(permanentGold).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
  130. item.setGold(permanent);
  131. item.setFree(free);
  132. item.setActivity(cashCollection.getActivity());
  133. item.setPaymentCurrency(cashCollection.getPaymentCurrency());
  134. if (cashCollection.getPaymentCurrency() != null) {
  135. item.setPaymentAmount(cashCollection.getPaymentAmount().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
  136. } else item.setPaymentAmount(cashCollection.getPaymentAmount());
  137. item.setReceivedCurrency(cashCollection.getReceivedCurrency());
  138. if (cashCollection.getReceivedCurrency() != null) {
  139. item.setReceivedAmount(cashCollection.getReceivedAmount().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
  140. } else item.setReceivedAmount(cashCollection.getReceivedAmount());
  141. item.setPayType(cashCollection.getPayType());
  142. item.setPayTime(cashCollection.getPayTime());
  143. item.setPayBankCode(cashCollection.getBankCode());
  144. item.setPaySubmitter(cashCollection.getSubmitterName());
  145. item.setAudit(cashCollection.getAuditName());
  146. item.setReceivedTime(cashCollection.getReceivedTime());
  147. item.setPayVoucher(cashCollection.getVoucher());
  148. item.setPayRemark(cashCollection.getRemark());
  149. item.setHandlingCharge(cashCollection.getHandlingCharge().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
  150. // 处理金币金额
  151. if (item.getPermanentGold() != null) {
  152. item.setPermanentGold(item.getPermanentGold().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
  153. }
  154. if (item.getFreeGold() != null) {
  155. item.setFreeGold(item.getFreeGold().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
  156. }
  157. }
  158. @Override
  159. public int add(CashRecordRefund cashRecordRefund, @RequestHeader(defaultValue = "zh_CN") String lang) throws Exception {
  160. try {
  161. if (cashRecordRefund.getJwcode() == null) {
  162. throw new Exception("未输入精网号");
  163. }
  164. if (cashRecordRefund.getRefundModel() == null) {
  165. throw new Exception("请填充退款类型");
  166. }
  167. if (cashRecordRefund.getRefundReason() == null) {
  168. throw new Exception("请填写退款理由");
  169. }
  170. if (cashRecordRefund.getHandlingCharge() == null) {
  171. throw new Exception("请先填写手续费");
  172. }
  173. if (cashRecordRefund.getMarket() == null || cashRecordRefund.getMarket().trim().isEmpty()) {
  174. throw new Exception("请选择所属地区");
  175. }
  176. if(cashRecordRefund.getWalletId() != null) {
  177. Integer wallet = cashRecordRefund.getWalletId();
  178. UserRegionWallet userRegionWallet = walletMapper.selectWallet(cashRecordRefund.getJwcode(), wallet);
  179. User user = userMapper.selectUserByJwcode(cashRecordRefund.getJwcode());
  180. if (user.getCurrentFreeJune().add(user.getCurrentFreeDecember()).compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundFree())) < 0) {
  181. String errorMsg = languageTranslationUtil.translate("用户钱包余额不足", lang);
  182. throw new BusinessException(errorMsg);
  183. }
  184. if (userRegionWallet == null) {
  185. //初始化钱包
  186. walletMapper.insert(new UserRegionWallet(null, cashRecordRefund.getJwcode(), wallet, BigDecimal.ZERO, new Date(), new Date()));
  187. log.warn("用户钱包不存在,已初始化钱包");
  188. }
  189. if (userRegionWallet.getCurrentPermanentGold().compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundGold())) < 0) {
  190. throw new BusinessException("用户钱包金币不足");
  191. }
  192. }
  193. CashRecordDone cashRecordDonetwo = new CashRecordDone();
  194. cashRecordDonetwo.setAreaServise(cashRecordRefund.getAreaServise());
  195. cashRefundMapper.addAudit(cashRecordDonetwo);
  196. cashRecordRefund.setAuditId(cashRecordDonetwo.getId());
  197. cashRecordRefund.setStatus(10);
  198. //生成订单号后半部分
  199. String orderNumber = cashRecordRefund.getOrderCode();
  200. //构建订单信息
  201. cashRecordRefund.setOrderCode("TK" + orderNumber); //订单号
  202. // 查询市场 ID,增加空值检查和多语言支持
  203. String marketName = cashRecordRefund.getMarket();
  204. String marketId = marketMapper.getMarketId(marketName);
  205. // 如果直接查询失败,尝试将英文名称转换为中文后再次查询
  206. if (marketId == null || marketId.trim().isEmpty()) {
  207. String chineseMarketName = translationService.findChineseSimplifiedByTranslation(marketName, "en");
  208. if (chineseMarketName != null && !chineseMarketName.equals(marketName)) {
  209. marketId = marketMapper.getMarketId(chineseMarketName);
  210. }
  211. }
  212. if (marketId == null || marketId.trim().isEmpty()) {
  213. throw new Exception("无效的所属地区:" + cashRecordRefund.getMarket());
  214. }
  215. cashRecordRefund.setMarket(String.valueOf(Integer.valueOf(marketId)));
  216. cashRefundMapper.insert(cashRecordRefund);
  217. CashRecordDone cashRecordDone1 = new CashRecordDone();
  218. cashRecordDone1.setId(cashRecordRefund.getOriginalOrderId());
  219. cashRecordDone1.setStatus(6);
  220. if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null) {
  221. cashRefundMapper.updateStatus(cashRecordDone1);
  222. } else {
  223. return Result.error("提交失败").getCode();
  224. }
  225. // 发送退款创建消息
  226. Messages message = new Messages();
  227. message.setJwcode(cashRecordRefund.getJwcode());
  228. message.setName(cashRecordRefund.getName());
  229. message.setStatus(cashRecordRefund.getStatus());
  230. message.setDesc("的客服退款申请待审核,前往处理");
  231. message.setTitle("现金退款--新增退款");
  232. message.setType(0);
  233. message.setTypeId(cashRecordRefund.getId());
  234. message.setMarket(Integer.valueOf(cashRecordRefund.getMarket()));
  235. String marketName2 = marketMapper.getMarketNameById(String.valueOf(message.getMarket()));
  236. message.setMarketName(marketName2);
  237. message.setQueryId(103);
  238. rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
  239. return Result.success("提交成功").getCode();
  240. } catch (Exception e) {
  241. log.error("add error", e);
  242. throw e;
  243. }
  244. }
  245. @Override
  246. public int update(CashRecordDone cashRecordDone) throws Exception {
  247. if (cashRecordDone.getJwcode() == null) {
  248. throw new RuntimeException("未输入精网号");
  249. }
  250. if (cashRecordDone.getPaymentAmount() == null) {
  251. throw new RuntimeException("未输入付款金额");
  252. }
  253. if (cashRecordDone.getPaymentCurrency() == null) {
  254. throw new RuntimeException("未输入付款币种");
  255. }
  256. if (cashRecordDone.getRefundModel() == null) {
  257. throw new RuntimeException("请填写退款类型");
  258. }
  259. if (cashRecordDone.getRefundReason() == null) {
  260. throw new RuntimeException("请填写退款理由");
  261. }
  262. if (cashRecordDone.getNewRefundGold() == null) {
  263. cashRecordDone.setNewRefundGold(BigDecimal.valueOf(0));
  264. }
  265. if (cashRecordDone.getNewRefundFree() == null) {
  266. cashRecordDone.setNewRefundFree(BigDecimal.valueOf(0));
  267. }
  268. int result = cashRefundMapper.update(cashRecordDone);
  269. CashRecordDTO cashRecordDTO = cashRefundMapper.selectById(cashRecordDone.getId());
  270. if (result > 0) {
  271. // 发送审核消息
  272. Messages message = new Messages();
  273. message.setJwcode(cashRecordDTO.getJwcode());
  274. message.setName(cashRecordDTO.getName());
  275. message.setStatus(cashRecordDTO.getStatus());
  276. message.setDesc("的退款申请待审核,前往处理");
  277. message.setTitle("现金管理--退款审批");
  278. message.setType(1);
  279. message.setTypeId(cashRecordDTO.getId());
  280. message.setMarket(cashRecordDTO.getMarket());
  281. String marketName = marketMapper.getMarketNameById(String.valueOf(message.getMarket()));
  282. message.setMarketName(marketName);
  283. message.setQueryId(103);
  284. rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
  285. }
  286. return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
  287. }
  288. @Override
  289. public int withdraw(CashRecordDone cashRecordDone) {
  290. // 撤回订单
  291. int result = cashRefundMapper.withdraw(cashRecordDone.getId());
  292. if (result > 0) {
  293. // 更新与该订单关联的消息记录的flag字段为1
  294. cashRefundMapper.updateMessageFlagByOrderId(cashRecordDone.getId());
  295. }
  296. return result;
  297. }
  298. @Override
  299. public int review(CashRecordDone cashRecordDone, @RequestHeader(defaultValue = "zh_CN") String lang) throws Exception {
  300. if (cashRecordDone.getStatus() == 12 || cashRecordDone.getStatus() == 22) {
  301. if (cashRecordDone.getOrderCode() == null) {
  302. throw new RuntimeException("未输入订单号");
  303. }
  304. CashRecordDone cashRecordDone1 = new CashRecordDone();
  305. cashRecordDone1.setId(cashRecordDone.getRelatedId());
  306. cashRecordDone1.setOrderCode(cashRecordDone.getOrderCode().substring(2));
  307. cashRecordDone1.setStatus(4);
  308. if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null) {
  309. cashRefundMapper.updateStatus(cashRecordDone1);
  310. }
  311. }
  312. cashRefundMapper.updateAudit(cashRecordDone);
  313. int result = cashRefundMapper.review(cashRecordDone);
  314. CashRecordDTO cashRecordDTO = cashRefundMapper.selectById(cashRecordDone.getId());
  315. if (result > 0) {
  316. // 发送审核消息
  317. Messages message = new Messages();
  318. message.setJwcode(cashRecordDTO.getJwcode());
  319. message.setName(cashRecordDTO.getName());
  320. message.setStatus(cashRecordDTO.getStatus());
  321. if (cashRecordDTO.getStatus()==20) {
  322. message.setDesc("的现金退款申请待审批,前往处理");
  323. message.setTitle("现金管理--退款审批(负责人)");
  324. message.setQueryId(107);
  325. } else if (cashRecordDTO.getStatus()==30) {
  326. message.setDesc("的现金退款申请待审核,前往处理");
  327. message.setTitle("现金管理--退款审批(总部财务)");
  328. message.setQueryId(111);
  329. } else {
  330. message.setDesc("的现金退款申请已被驳回,前往查看详情");
  331. message.setTitle("现金管理--退款提交");
  332. message.setQueryId(99);
  333. }
  334. message.setType(1);
  335. message.setTypeId(cashRecordDTO.getId());
  336. message.setMarket(cashRecordDTO.getMarket());
  337. String marketName = marketMapper.getMarketNameById(String.valueOf(message.getMarket()));
  338. message.setMarketName(marketName);
  339. rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
  340. }
  341. return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
  342. }
  343. @Override
  344. public int executor(CashRecordDone cashRecordDone) throws Exception {
  345. if (cashRecordDone.getRefundVoucher() == null) {
  346. throw new RuntimeException("未输入退款凭证");
  347. }
  348. if (cashRecordDone.getRefundTime() == null) {
  349. throw new RuntimeException("未输入退款时间");
  350. }
  351. if (cashRecordDone.getRefundRemark() == null) {
  352. throw new RuntimeException("未输入退款备注");
  353. }
  354. if (cashRecordDone.getRefundChannels() == null) {
  355. throw new RuntimeException("未输入退款途径");
  356. }
  357. if (cashRecordDone.getRefundCurrency() == null) {
  358. throw new RuntimeException("未输入退款币种");
  359. }
  360. if (cashRecordDone.getRefundAmount() == null) {
  361. throw new RuntimeException("未输入退款金额");
  362. }
  363. int result = cashRefundMapper.executor(cashRecordDone);
  364. return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
  365. }
  366. @Override
  367. public int updateStatus(CashRecordDone cashRecordDone) {
  368. return cashRefundMapper.updateStatus(cashRecordDone);
  369. }
  370. @Transactional(rollbackFor = Exception.class)
  371. @Override
  372. public int finalreview(CashRecordDone cashRecordDone, @RequestHeader(defaultValue = "zh_CN") String lang) {
  373. if (cashRecordDone.getPermanentGold() == null) {
  374. cashRecordDone.setPermanentGold(0);
  375. }
  376. if (cashRecordDone.getFreeGold() == null) {
  377. cashRecordDone.setFreeGold(0);
  378. }
  379. if (cashRecordDone.getStatus() == 32) {
  380. CashRecordDone cashRecordDone1 = new CashRecordDone();
  381. cashRecordDone1.setOrderCode(cashRecordDone.getOrderCode().substring(2));
  382. cashRecordDone1.setStatus(4);
  383. if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null) {
  384. cashRefundMapper.updateStatus(cashRecordDone1);
  385. }
  386. }
  387. if (cashRecordDone.getGoodsName() != null && cashRecordDone.getStatus() == 40 &&
  388. (cashRecordDone.getGoodsName().equals(languageTranslationUtil.translate("金币充值", lang)) ||
  389. cashRecordDone.getGoodsName().contains(languageTranslationUtil.translate("金币充值", lang)))) {
  390. UserGoldRecord userGoldRecord = new UserGoldRecord();
  391. userGoldRecord.setOrderCode(cashRecordDone.getOrderCode());
  392. String orderCode = cashRecordDone.getOrderCode();
  393. if (orderCode != null && orderCode.length() > 4 && orderCode.startsWith("TKXJ")) {
  394. orderCode = "XJCZ" + orderCode.substring(4);
  395. }
  396. userGoldRecord.setType((byte) 2);
  397. userGoldRecord.setIsRefund((byte) 1);
  398. userGoldRecord.setRefundType("金币退款");
  399. if (cashRecordDone.getRefundModel() == 1) {
  400. userGoldRecord.setRefundModel(Byte.valueOf("1"));
  401. } else if (cashRecordDone.getRefundModel() == 0) {
  402. userGoldRecord.setRefundModel(Byte.valueOf("0"));
  403. }
  404. userGoldRecord.setJwcode(cashRecordDone.getJwcode());
  405. userGoldRecord.setSumGold(cashRecordDone.getPermanentGold() + cashRecordDone.getFreeGold());
  406. userGoldRecord.setPermanentGold(cashRecordDone.getPermanentGold());
  407. int currentMonth = LocalDate.now().getMonthValue();
  408. if (currentMonth >= 1 && currentMonth <= 6) {
  409. // 1-6月:设置12月额度,6月保持默认值
  410. userGoldRecord.setFreeJune(0);
  411. userGoldRecord.setFreeDecember(cashRecordDone.getFreeGold());
  412. } else {
  413. // 7-12月:设置6月额度,12月保持默认值
  414. userGoldRecord.setFreeJune(cashRecordDone.getFreeGold());
  415. userGoldRecord.setFreeDecember(0);
  416. }
  417. userGoldRecord.setGoodsName("金币充值");
  418. userGoldRecord.setPayPlatform("金币系统");
  419. userGoldRecord.setRemark(cashRecordDone.getRemark());
  420. userGoldRecord.setAdminId(cashRecordDone.getAdminId());
  421. userGoldRecord.setAuditStatus(1);
  422. userGoldRecord.setTaskGold(0);
  423. userGoldRecord.setCreateTime(new Date());
  424. userGoldRecord.setUpdateTime(new Date());
  425. String auditName = auditMapper.getName(cashRecordDone.getAuditId());
  426. refundMapper.add(userGoldRecord);
  427. cashRefundMapper.updategold(orderCode);
  428. User user = new User();
  429. user.setJwcode(userGoldRecord.getJwcode());
  430. user.setCurrentPermanentGold(BigDecimal.valueOf(-userGoldRecord.getPermanentGold())); //当前永久金币
  431. user.setCurrentFreeJune(BigDecimal.valueOf(-userGoldRecord.getFreeJune())); //当前六月免费金币
  432. user.setCurrentFreeDecember(BigDecimal.valueOf(-userGoldRecord.getFreeDecember())); //当前十二月免费金币
  433. User usergold = userMapper.selectUserByJwcode(userGoldRecord.getJwcode());
  434. if(usergold.getCurrentFreeJune().add(user.getCurrentFreeJune()).compareTo(BigDecimal.ZERO)<0){
  435. throw new RuntimeException("当前六月免费金币不足");
  436. }
  437. if (usergold.getCurrentFreeDecember().add(user.getCurrentFreeDecember()).compareTo(BigDecimal.ZERO)<0){
  438. throw new RuntimeException("当前十二月免费金币不足");
  439. }
  440. auditMapper.updateUserGold(user);
  441. // 钱包更新 - 按原始充值流水 wallet_id 优先级顺序原路退回(1-10,越小优先级越高)
  442. String orderCodeA = "XJ" + orderCode.substring(4);
  443. // 1. 查询原始充值流水(type=0 充值,status=0 正常)
  444. List<UserWalletRecord> originalRecords = walletService.selectUserWalletRecord(userGoldRecord.getJwcode(), orderCodeA);
  445. if (!CollectionUtils.isEmpty(originalRecords)) {
  446. // 过滤充值记录并按 wallet_id 升序排序
  447. originalRecords = originalRecords.stream()
  448. .filter(r -> r.getType() == 0 && r.getStatus() == 0)
  449. .sorted(Comparator.comparing(UserWalletRecord::getWalletId))
  450. .collect(Collectors.toList());
  451. // 2. 本次退款总金额(取绝对值)
  452. BigDecimal totalRefundAmount = BigDecimal.valueOf(userGoldRecord.getPermanentGold()).abs();
  453. if (totalRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
  454. // 🔥【新增】预校验:检查用户余额是否足够退款
  455. BigDecimal userTotalBalance = getUserTotalBalance(userGoldRecord.getJwcode(), originalRecords);
  456. if (userTotalBalance.compareTo(totalRefundAmount) < 0) {
  457. // 🔥 余额不足,直接报错返回(根据项目规范选择抛异常或返回错误码)
  458. String errorMsg = String.format("退款失败:用户余额不足 | jwcode=%s, 待退金额=%s, 当前余额=%s",
  459. userGoldRecord.getJwcode(), totalRefundAmount, userTotalBalance);
  460. log.error(errorMsg);
  461. throw new BusinessException("WALLET_REFUND_INSUFFICIENT"+ errorMsg);
  462. // 或者:return Result.fail("WALLET_REFUND_INSUFFICIENT", "余额不足,无法退款");
  463. }
  464. BigDecimal remainingRefund = totalRefundAmount;
  465. // 3. 按原始流水顺序退回(支持多钱包)
  466. for (UserWalletRecord record : originalRecords) {
  467. if (remainingRefund.compareTo(BigDecimal.ZERO) <= 0) {
  468. break;
  469. }
  470. Integer walletId = record.getWalletId();
  471. BigDecimal originalAmount = BigDecimal.valueOf(record.getAmount()).abs();
  472. if (originalAmount.compareTo(BigDecimal.ZERO) <= 0) {
  473. continue;
  474. }
  475. // 4. 计算本次退回金额
  476. BigDecimal refundAmount = originalAmount.min(remainingRefund);
  477. if (refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
  478. continue;
  479. }
  480. // 5. 插入退款流水(amount 存负数,表示扣减)
  481. UserWalletRecord refundRecord = new UserWalletRecord();
  482. refundRecord.setType(2); // 充值退款类型
  483. refundRecord.setJwcode(userGoldRecord.getJwcode());
  484. refundRecord.setWalletId(walletId);
  485. refundRecord.setAmount(refundAmount.negate().intValue()); // 🔥 负数扣款
  486. refundRecord.setOrderCode("TK" + orderCodeA);
  487. if (cashRecordDone.getRefundModel()==1) {
  488. refundRecord.setDescription("部分退款");
  489. }
  490. else if (cashRecordDone.getRefundModel()==0) {
  491. refundRecord.setDescription("全部退款");
  492. }
  493. refundRecord.setStatus(0);
  494. walletService.addUserWalletRecord(refundRecord);
  495. // 6. 查询并更新钱包余额
  496. UserRegionWallet currentWallet = walletMapper.selectWallet(userGoldRecord.getJwcode(), walletId);
  497. if (currentWallet == null) {
  498. log.error("钱包记录不存在,无法退款 | jwcode={}, walletId={}", userGoldRecord.getJwcode(), walletId);
  499. continue;
  500. }
  501. // 7. 扣减余额
  502. BigDecimal newBalance = currentWallet.getCurrentPermanentGold().subtract(refundAmount);
  503. // 🔥 兜底:理论上预校验已通过,这里不会再出现负数,但保留防御式编程
  504. if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
  505. log.warn("退款后余额为负,强制置零 | jwcode={}, walletId={}", userGoldRecord.getJwcode(), walletId);
  506. newBalance = BigDecimal.ZERO;
  507. }
  508. currentWallet.setCurrentPermanentGold(newBalance);
  509. walletMapper.updateWallet(currentWallet);
  510. // 8. 更新原流水状态
  511. walletService.updateUserWalletRecord(record.getId());
  512. // 9. 扣减剩余待退金额
  513. remainingRefund = remainingRefund.subtract(refundAmount);
  514. }
  515. }
  516. }
  517. GoldTistV2.addCoinNew(userGoldRecord.getJwcode().toString(), 58, //退款免费+永久金币-充值
  518. (double) (userGoldRecord.getFreeDecember() + userGoldRecord.getFreeJune() + userGoldRecord.getPermanentGold()) / 100, SimpleIdGenerator.generateId(),
  519. userGoldRecord.getRemark(), (double) userGoldRecord.getPermanentGold() / 100, auditName, "退款金币充值");
  520. }
  521. cashRefundMapper.updateAudit(cashRecordDone);
  522. int result = cashRefundMapper.review(cashRecordDone);
  523. CashRecordDTO cashRecordDTO = cashRefundMapper.selectById(cashRecordDone.getId());
  524. if (result > 0) {
  525. // 发送审核消息
  526. Messages message = new Messages();
  527. message.setJwcode(cashRecordDTO.getJwcode());
  528. message.setName(cashRecordDTO.getName());
  529. message.setStatus(cashRecordDTO.getStatus());
  530. message.setDesc(cashRecordDTO.getStatus() != 32 ? "的退款记录需填写执行明细,前往查看处理" : "的现金退款申请已被驳回,前往查看详情");
  531. message.setTitle(cashRecordDTO.getStatus() != 32 ? "现金管理--执行明细填写":"现金管理--退款提交");
  532. message.setType(1);
  533. message.setTypeId(cashRecordDTO.getId());
  534. message.setMarket(cashRecordDTO.getMarket());
  535. String marketName = marketMapper.getMarketNameById(String.valueOf(message.getMarket()));
  536. message.setMarketName(marketName);
  537. message.setQueryId(cashRecordDTO.getStatus() != 32 ? 115:99);
  538. if (cashRecordDTO.getStatus() != 32) {
  539. log.info("Setting executor to: {}", cashRecordDTO.getExecutor());
  540. message.setExecutor(cashRecordDTO.getExecutor());
  541. log.info("Executor after setting: {}", message.getExecutor());
  542. }
  543. rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
  544. }
  545. return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
  546. }
  547. @Override
  548. public PageInfo<CashRecordDTO> financeSelect(Integer pageNum, Integer pageSize, CashRecordDTO cashRecordDTO) {
  549. PageHelper.startPage(pageNum, pageSize); //必须要直接跟mapper
  550. // System.out.println(goldDetail.getMarkets());
  551. List<CashRecordDTO> list = cashRefundMapper.financeSelect(cashRecordDTO);
  552. if (list.isEmpty()) {
  553. return new PageInfo<>(list);
  554. }
  555. // 批量收集ID
  556. Set<Integer> relatedIds = new HashSet<>();
  557. Set<Integer> marketIds = new HashSet<>();
  558. Set<Integer> submitterIds = new HashSet<>();
  559. Set<Integer> auditIds = new HashSet<>();
  560. Set<Integer> executorIds = new HashSet<>();
  561. list.forEach(item -> {
  562. if (item.getRelatedId() != null) relatedIds.add(item.getRelatedId());
  563. if (item.getMarket() != null) marketIds.add(item.getMarket());
  564. if (item.getSubmitterId() != null) submitterIds.add(item.getSubmitterId());
  565. if (item.getAuditId() != null) auditIds.add(item.getAuditId());
  566. if (item.getExecutor() != null) executorIds.add(item.getExecutor());
  567. });
  568. // 批量查询
  569. Map<Integer, CashCollection> cashCollectionMap = cashCollectionMapper.selectBatchIds(relatedIds)
  570. .stream().collect(Collectors.toMap(CashCollection::getId, Function.identity()));
  571. Map<Integer, String> marketNameMap = marketMapper.getMarketByIds(marketIds)
  572. .stream().collect(Collectors.toMap(Market::getId, Market::getName));
  573. Map<Integer, String> submitterNameMap = auditMapper.getNamesByIds(submitterIds)
  574. .stream().collect(Collectors.toMap(Admin::getId, Admin::getAdminName));
  575. Map<Integer, LhlAudit> auditMap = cashRefundMapper.getAuditBatch(auditIds)
  576. .stream().collect(Collectors.toMap(LhlAudit::getId, Function.identity()));
  577. Map<String, String> executorNameMap = auditMapper.getNamesByJwcodes(executorIds)
  578. .stream().collect(Collectors.toMap(Admin::getAccount, Admin::getAdminName));
  579. // 处理数据
  580. list.forEach(item -> {
  581. CashCollection cashCollection = cashCollectionMap.get(item.getRelatedId());
  582. if (cashCollection != null) {
  583. processCashCollection(item, cashCollection);
  584. }
  585. String marketName = marketNameMap.get(item.getMarket());
  586. String submitter = submitterNameMap.get(item.getSubmitterId());
  587. LhlAudit lhlAudit = auditMap.get(item.getAuditId());
  588. String executorName = executorNameMap.get(String.valueOf(item.getExecutor()));
  589. item.setMarketName(marketName != null ? marketName : "");
  590. item.setSubmitter(submitter != null ? submitter : "");
  591. item.setExecutorName(executorName != null ? executorName : "");
  592. if (lhlAudit != null) {
  593. item.setAreaServise(lhlAudit.getAreaServise());
  594. item.setAreaFinance(lhlAudit.getAreaFinance());
  595. item.setAreaCharge(lhlAudit.getAreaCharge());
  596. item.setHeadFinance(lhlAudit.getHeadFinance());
  597. }
  598. });
  599. return new PageInfo<>(list);
  600. }
  601. @Override
  602. public PageInfo<CashRecordDTO> financeSelect2(Integer pageNum, Integer pageSize, CashRecordDTO cashRecordDTO) {
  603. PageHelper.startPage(pageNum, pageSize); //必须要直接跟mapper
  604. // System.out.println(goldDetail.getMarkets());
  605. List<CashRecordDTO> list = cashRefundMapper.financeSelect(cashRecordDTO);
  606. if (list.isEmpty()) {
  607. return new PageInfo<>(list);
  608. }
  609. // 批量收集ID
  610. Set<Integer> relatedIds = new HashSet<>();
  611. Set<Integer> marketIds = new HashSet<>();
  612. Set<Integer> submitterIds = new HashSet<>();
  613. Set<Integer> auditIds = new HashSet<>();
  614. Set<Integer> executorIds = new HashSet<>();
  615. list.forEach(item -> {
  616. if (item.getRelatedId() != null) relatedIds.add(item.getRelatedId());
  617. if (item.getMarket() != null) marketIds.add(item.getMarket());
  618. if (item.getSubmitterId() != null) submitterIds.add(item.getSubmitterId());
  619. if (item.getAuditId() != null) auditIds.add(item.getAuditId());
  620. if (item.getExecutor() != null) executorIds.add(item.getExecutor());
  621. });
  622. // 批量查询
  623. Map<Integer, CashCollection> cashCollectionMap = cashCollectionMapper.selectBatchIds(relatedIds)
  624. .stream().collect(Collectors.toMap(CashCollection::getId, Function.identity()));
  625. Map<Integer, String> marketNameMap = marketMapper.getMarketByIds(marketIds)
  626. .stream().collect(Collectors.toMap(Market::getId, Market::getName));
  627. Map<Integer, String> submitterNameMap = auditMapper.getNamesByIds(submitterIds)
  628. .stream().collect(Collectors.toMap(Admin::getId, Admin::getAdminName));
  629. Map<Integer, LhlAudit> auditMap = cashRefundMapper.getAuditBatch(auditIds)
  630. .stream().collect(Collectors.toMap(LhlAudit::getId, Function.identity()));
  631. Map<String, String> executorNameMap = auditMapper.getNamesByJwcodes(executorIds)
  632. .stream().collect(Collectors.toMap(Admin::getAccount, Admin::getAdminName));
  633. // 处理数据
  634. list.forEach(item -> {
  635. CashCollection cashCollection = cashCollectionMap.get(item.getRelatedId());
  636. if (cashCollection != null) {
  637. processCashCollection(item, cashCollection);
  638. }
  639. String marketName = marketNameMap.get(item.getMarket());
  640. String submitter = submitterNameMap.get(item.getSubmitterId());
  641. LhlAudit lhlAudit = auditMap.get(item.getAuditId());
  642. String executorName = executorNameMap.get(String.valueOf(item.getExecutor()));
  643. item.setMarketName(marketName != null ? marketName : "");
  644. item.setSubmitter(submitter != null ? submitter : "");
  645. item.setExecutorName(executorName != null ? executorName : "");
  646. if (lhlAudit != null) {
  647. item.setAreaServise(lhlAudit.getAreaServise());
  648. item.setAreaFinance(lhlAudit.getAreaFinance());
  649. item.setAreaCharge(lhlAudit.getAreaCharge());
  650. item.setHeadFinance(lhlAudit.getHeadFinance());
  651. }
  652. });
  653. return new PageInfo<>(list);
  654. }
  655. @Override
  656. public PageInfo<CashRecordDTO> exSelect(Integer pageNum, Integer pageSize, CashRecordDTO cashRecordDTO) {
  657. PageHelper.startPage(pageNum, pageSize); //必须要直接跟mapper
  658. // System.out.println(goldDetail.getMarkets());
  659. List<CashRecordDTO> list = cashRefundMapper.exSelect(cashRecordDTO);
  660. System.out.println(list);
  661. if (list.isEmpty()) {
  662. return new PageInfo<>(list);
  663. }
  664. // 批量收集ID
  665. Set<Integer> relatedIds = new HashSet<>();
  666. Set<Integer> marketIds = new HashSet<>();
  667. Set<Integer> submitterIds = new HashSet<>();
  668. Set<Integer> auditIds = new HashSet<>();
  669. Set<Integer> executorIds = new HashSet<>();
  670. list.forEach(item -> {
  671. if (item.getRelatedId() != null) relatedIds.add(item.getRelatedId());
  672. if (item.getMarket() != null) marketIds.add(item.getMarket());
  673. if (item.getSubmitterId() != null) submitterIds.add(item.getSubmitterId());
  674. if (item.getAuditId() != null) auditIds.add(item.getAuditId());
  675. if (item.getExecutor() != null) executorIds.add(item.getExecutor());
  676. });
  677. // 批量查询
  678. Map<Integer, CashCollection> cashCollectionMap = cashCollectionMapper.selectBatchIds(relatedIds)
  679. .stream().collect(Collectors.toMap(CashCollection::getId, Function.identity()));
  680. Map<Integer, String> marketNameMap = marketMapper.getMarketByIds(marketIds)
  681. .stream().collect(Collectors.toMap(Market::getId, Market::getName));
  682. Map<Integer, String> submitterNameMap = auditMapper.getNamesByIds(submitterIds)
  683. .stream().collect(Collectors.toMap(Admin::getId, Admin::getAdminName));
  684. Map<Integer, LhlAudit> auditMap = cashRefundMapper.getAuditBatch(auditIds)
  685. .stream().collect(Collectors.toMap(LhlAudit::getId, Function.identity()));
  686. Map<String, String> executorNameMap = auditMapper.getNamesByJwcodes(executorIds)
  687. .stream().collect(Collectors.toMap(Admin::getAccount, Admin::getAdminName));
  688. // 处理数据
  689. list.forEach(item -> {
  690. CashCollection cashCollection = cashCollectionMap.get(item.getRelatedId());
  691. if (cashCollection != null) {
  692. processCashCollection(item, cashCollection);
  693. }
  694. String marketName = marketNameMap.get(item.getMarket());
  695. String submitter = submitterNameMap.get(item.getSubmitterId());
  696. LhlAudit lhlAudit = auditMap.get(item.getAuditId());
  697. String executorName = executorNameMap.get(String.valueOf(item.getExecutor()));
  698. item.setMarketName(marketName != null ? marketName : "");
  699. item.setSubmitter(submitter != null ? submitter : "");
  700. item.setExecutorName(executorName != null ? executorName : "");
  701. if (lhlAudit != null) {
  702. item.setAreaServise(lhlAudit.getAreaServise());
  703. item.setAreaFinance(lhlAudit.getAreaFinance());
  704. item.setAreaCharge(lhlAudit.getAreaCharge());
  705. item.setHeadFinance(lhlAudit.getHeadFinance());
  706. }
  707. });
  708. return new PageInfo<>(list);
  709. }
  710. @Override
  711. @Transactional(rollbackFor = Exception.class)
  712. public void addOnline(CashRecordRefund cashRecordRefund,String lang) {
  713. try {
  714. if (cashRecordRefund.getJwcode() == null) {
  715. throw new BusinessException("未输入精网号");
  716. }
  717. if (cashRecordRefund.getRefundModel() == null) {
  718. throw new BusinessException("请填充退款类型");
  719. }
  720. if (cashRecordRefund.getRefundReason() == null) {
  721. throw new BusinessException("请填写退款理由");
  722. }
  723. if (cashRecordRefund.getMarket() == null || cashRecordRefund.getMarket().trim().isEmpty()) {
  724. throw new BusinessException("请选择所属地区");
  725. }
  726. String payType = cashRecordRefund.getPayType();
  727. Integer wallet = null;
  728. if (payType == null || payType.trim().isEmpty()) {
  729. throw new SystemException("未传输支付方式");
  730. }
  731. if (payType.equals("Stripe")||payType.equals("PaymentAsia"))
  732. {
  733. wallet = 2;
  734. }
  735. if (payType.equals("FirstData")||payType.equals("Grabpay")||payType.equals("Nets")||payType.equals("PayPal")||payType.equals("IOS"))
  736. {
  737. wallet = 5;
  738. }
  739. if (payType.equals("Stripe2"))
  740. {
  741. wallet = 3;
  742. }
  743. if (payType.equals("iPay88"))
  744. {
  745. wallet = 4;
  746. }
  747. if (payType.equals("E-Transfer"))
  748. {
  749. wallet = 6;
  750. }
  751. if (payType.equals("paysolution"))
  752. {
  753. wallet = 8;
  754. }
  755. UserRegionWallet userRegionWallet = walletMapper.selectWallet(cashRecordRefund.getJwcode(), wallet);
  756. User user = userMapper.selectUserByJwcode(cashRecordRefund.getJwcode());
  757. if (user.getCurrentFreeJune().add(user.getCurrentFreeDecember()).compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundFree()))<0){
  758. String errorMsg = languageTranslationUtil.translate("用户钱包余额不足", lang);
  759. throw new BusinessException(errorMsg);
  760. }
  761. if (userRegionWallet == null) {
  762. //初始化钱包
  763. walletMapper.insert(new UserRegionWallet(null, cashRecordRefund.getJwcode(), wallet, BigDecimal.ZERO, new Date(), new Date()));
  764. log.warn("用户钱包不存在,已初始化钱包");
  765. }
  766. if (userRegionWallet.getCurrentPermanentGold().compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundGold())) < 0) {
  767. throw new BusinessException("用户钱包金币不足");
  768. }
  769. CashRecordDone cashRecordDonetwo = new CashRecordDone();
  770. cashRecordDonetwo.setAreaServise(cashRecordRefund.getAreaServise());
  771. cashRefundMapper.addAudit(cashRecordDonetwo);
  772. cashRecordRefund.setAuditId(cashRecordDonetwo.getId());
  773. cashRecordRefund.setStatus(20);
  774. //生成订单号后半部分
  775. String orderNumber = cashRecordRefund.getOrderCode();
  776. //构建订单信息
  777. cashRecordRefund.setOrderCode("TK" + orderNumber); //订单号
  778. // 查询市场 ID,增加空值检查
  779. String marketName = cashRecordRefund.getMarket();
  780. // 如果传入的是英文名称,需要转换为中文名称再查询
  781. String marketId = marketMapper.getMarketId(marketName);
  782. if (marketId == null || marketId.trim().isEmpty()) {
  783. // 尝试将英文名称转换为中文后再次查询
  784. String chineseMarketName = translationService.findChineseSimplifiedByTranslation(marketName, "en");
  785. if (chineseMarketName != null && !chineseMarketName.equals(marketName)) {
  786. marketId = marketMapper.getMarketId(chineseMarketName);
  787. }
  788. }
  789. if (marketId == null || marketId.trim().isEmpty()) {
  790. throw new BusinessException("无效的所属地区:" + cashRecordRefund.getMarket());
  791. }
  792. cashRecordRefund.setMarket(String.valueOf(Integer.valueOf(marketId)));
  793. cashRefundMapper.insert(cashRecordRefund);
  794. CashRecordDone cashRecordDone1 = new CashRecordDone();
  795. cashRecordDone1.setId(cashRecordRefund.getOriginalOrderId());
  796. cashRecordDone1.setStatus(6);
  797. if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null) {
  798. cashRefundMapper.updateStatus(cashRecordDone1);
  799. } else {
  800. throw new SystemException("提交失败");
  801. }
  802. } catch (BusinessException | SystemException e) {
  803. throw e;
  804. } catch (Exception e) {
  805. log.error("addOnline error", e);
  806. throw new SystemException("提交失败:" + e.getMessage());
  807. }
  808. }
  809. @Override
  810. public PageInfo<FundsDTO> funds(Integer pageNum, Integer pageSize, FundsDTO fundsDTO) {
  811. // 1. 分页查询主数据
  812. PageHelper.startPage(pageNum, pageSize);
  813. if (fundsDTO.getStatuses() == null || fundsDTO.getStatuses().isEmpty()) {
  814. fundsDTO.setStatuses(Arrays.asList(4, 6));
  815. }
  816. List<FundsDTO> list = cashRefundMapper.selectfunds(fundsDTO);
  817. // 2. 收集 status == 6 的记录 ID
  818. List<Integer> needQueryIds = new ArrayList<>();
  819. for (FundsDTO dto : list) {
  820. if (dto.getStatus() != null && dto.getStatus() == 6) {
  821. needQueryIds.add(dto.getId());
  822. }
  823. }
  824. // 3. 批量查询 refundDetail 信息(用于负数处理)
  825. if (!needQueryIds.isEmpty()) {
  826. List<FundsDTO> detailList = cashRefundMapper.selectRefundCount(needQueryIds);
  827. Map<Integer, FundsDTO> detailMap = new HashMap<>();
  828. for (FundsDTO detail : detailList) {
  829. // 假设 detail.getRelatedId() 对应主表的 id
  830. detailMap.put(detail.getRelatedId(), detail);
  831. }
  832. // 回填 refundAmount(取负)和 refundCurrency
  833. for (FundsDTO dto : list) {
  834. if (dto.getStatus() != null && dto.getStatus() == 6) {
  835. FundsDTO detail = detailMap.get(dto.getId());
  836. if (detail != null) {
  837. BigDecimal amount = detail.getRefundAmount();
  838. if (amount != null) {
  839. dto.setRefundAmount(amount.negate()); // 转为负数
  840. } else {
  841. dto.setRefundAmount(null); // 或设为 BigDecimal.ZERO
  842. }
  843. dto.setRefundCurrency(detail.getRefundCurrency());
  844. }
  845. }
  846. }
  847. }
  848. // 4. 收集所有需要转换的 regionId 和 currencyId
  849. Set<Integer> regionIds = new HashSet<>();
  850. Set<Integer> currencyIds = new HashSet<>();
  851. Set<Integer> reCurrencyIds = new HashSet<>();
  852. for (FundsDTO dto : list) {
  853. if (dto.getMarket() != null) {
  854. regionIds.add(dto.getMarket());
  855. }
  856. if (dto.getPaymentCurrency() != null) {
  857. currencyIds.add(dto.getPaymentCurrency());
  858. }
  859. if (dto.getReceivedCurrency() != null) {
  860. reCurrencyIds.add(dto.getReceivedCurrency());
  861. }
  862. }
  863. // 5. 批量查询地区字典
  864. Map<Integer, String> regionMap = new HashMap<>();
  865. if (!regionIds.isEmpty()) {
  866. List<Region> regions = refundMapper.selectByIds(new ArrayList<>(regionIds));
  867. for (Region region : regions) {
  868. regionMap.put(region.getId(), region.getName());
  869. }
  870. }
  871. // 6. 批量查询币种字典
  872. Map<Integer, String> currencyMap = new HashMap<>();
  873. if (!currencyIds.isEmpty()) {
  874. List<Currency> currencies = refundMapper.selectByCIds(new ArrayList<>(currencyIds));
  875. for (Currency currency : currencies) {
  876. currencyMap.put(currency.getId(), currency.getName()); // 或 getCode(),按需调整
  877. }
  878. }
  879. Map<Integer, String> reCurrencyMap = new HashMap<>();
  880. if (!reCurrencyIds.isEmpty()) {
  881. List<Currency> reCurrencies = refundMapper.selectByCIds(new ArrayList<>(reCurrencyIds));
  882. for (Currency reCurrency : reCurrencies) {
  883. reCurrencyMap.put(reCurrency.getId(), reCurrency.getName()); // 或 getCode(),按需调整
  884. }
  885. }
  886. // 7. 回填地区名称和币种名称到 DTO
  887. for (FundsDTO dto : list) {
  888. dto.setMarketName(regionMap.get(dto.getMarket()));
  889. dto.setPaymentCurrencyName(currencyMap.get(dto.getPaymentCurrency()));
  890. dto.setReceivedCurrencyName(reCurrencyMap.get(dto.getReceivedCurrency()));
  891. }
  892. // 8. 返回分页结果
  893. return new PageInfo<>(list);
  894. }
  895. /**
  896. * 计算用户指定钱包列表的总可用余额
  897. * @param jwcode 用户标识
  898. * @param records 原始充值流水列表用于提取 wallet_id 列表
  899. * @return 总余额BigDecimal
  900. */
  901. private BigDecimal getUserTotalBalance(Integer jwcode, List<UserWalletRecord> records) {
  902. // 提取涉及到的 wallet_id 列表(去重)
  903. List<Integer> walletIds = records.stream()
  904. .map(UserWalletRecord::getWalletId)
  905. .distinct()
  906. .collect(Collectors.toList());
  907. if (CollectionUtils.isEmpty(walletIds)) {
  908. return BigDecimal.ZERO;
  909. }
  910. // 批量查询钱包当前余额(避免 N+1 查询)
  911. List<UserRegionWallet> wallets = walletMapper.selectWalletsByJwcodeAndIds(jwcode, walletIds);
  912. // 汇总余额
  913. return wallets.stream()
  914. .map(UserRegionWallet::getCurrentPermanentGold)
  915. .filter(Objects::nonNull)
  916. .reduce(BigDecimal.ZERO, BigDecimal::add);
  917. }
  918. }