67 Commits

Author SHA1 Message Date
huangqizhen 0cc5a7be1e Merge remote-tracking branch 'origin/milestone-20260224-现金钱包' into milestone-20260224-现金钱包 1 month ago
huangqizhen 287cb02681 3.26 钱包余额数据同步 1 month ago
sunjiabei 6635bd0ca2 20260322消耗优化 1 month ago
sunjiabei eb65b621d6 20260322消耗优化 1 month ago
wangguorui bcf3a9e912 20260322 充值优化 1 month ago
sunjiabei 2967aea5d0 20260322消耗优化 1 month ago
wangguorui 973e1784ba Merge remote-tracking branch 'origin/milestone-20260224-现金钱包' into milestone-20260224-现金钱包 1 month ago
wangguorui e07d8cf80f 20260319 充值优化 1 month ago
huangqizhen 8bffa4d67c 3.18 收款可以修改到账钱包 1 month ago
huangqizhen 1da0deea2c 3.18 现金免费金币校验 1 month ago
huangqizhen 9bf99f9ef5 3.18 现金免费金币校验 1 month ago
wangguorui 33c177a1c8 20260318 翻译2 1 month ago
wangguorui c2449f95cd 20260318 翻译 1 month ago
huangqizhen 59629ae285 3.18 现金免费金币校验 1 month ago
sunjiabei 9e4ed96508 20260318全部退款部分退 1 month ago
huangqizhen 17e751608b 3.17 修改退款类型 1 month ago
huangqizhen c3a87aa35e 3.17 修改退款充值 1 month ago
wangguorui 58af4e12a1 20260318 添加到账地区与钱包的校验 1 month ago
huangqizhen cd152a3c16 3.17 修改退款原订单状态 1 month ago
sunjiabei 62ca4b73a9 20260317钱包排序 1 month ago
wangguorui 44650f7b2c 20260317 优化退款 1 month ago
huangqizhen e34d6afb9d 3.17 现金退款校验与修改钱包余额 1 month ago
huangqizhen 5808d624e6 3.17 现金退款校验与修改钱包余额 1 month ago
huangqizhen 90d2bdfbbd 3.16 现金退款提交分配地区 1 month ago
huangqizhen 5b382602ef 3.14 修改部分退款无效问题 1 month ago
huangqizhen b2e3aea910 3.13 退款提交拦截 1 month ago
wangguorui 8b926f7bdc 20260315 优化查询翻译 1 month ago
wangguorui a347f36135 20260315 查询增加钱包id 1 month ago
wangguorui e28d273dd8 20260315 钱包余额查询OK 1 month ago
huangqizhen 596ae1cd56 3.13 退款更新 1 month ago
wangguorui 41690ec3a5 20260315 钱包查询 1 month ago
wangguorui 94b6f1691e 20260315 钱包查询翻译 1 month ago
wangguorui 06eaff7364 20260312 退款优化Pro 2 months ago
huangqizhen 283e3429d0 3.12 数据更新 2 months ago
huangqizhen 66bba6a3e8 Merge branch 'refs/heads/huangqizheng/feature-20260309142559-钱包退款' into milestone-20260224-现金钱包 2 months ago
huangqizhen 34f8577c0f 3.12 数据更新 2 months ago
wangguorui 69d24cc5e5 20260312 退款优化 2 months ago
wangguorui 3805414c59 20260311 钱包余额导出优化max 2 months ago
wangguorui 3fc202e4f1 20260309 钱包明细查询优化pro 2 months ago
huangqizhen 5ced47c379 Merge branch 'refs/heads/huangqizheng/feature-20260309142559-钱包退款' into milestone-20260224-现金钱包 2 months ago
huangqizhen 14946e5777 3.9 钱包退款 2 months ago
wangguorui 9f4367ee39 20260309 钱包明晰导出优化pro 2 months ago
huangqizhen 56d82da607 3.9 钱包退款 2 months ago
wangguorui 7ae4a56dbc 20260309 钱包余额导出优化pro 2 months ago
wangguorui 2322add37d 20260309 钱包余额导出优化 2 months ago
wangguorui 347184b665 20260308 钱包明细导出优化 2 months ago
wangguorui 896e4ed59b 20260308 钱包明细导出 2 months ago
sunjiabei c6c0196a43 20260306用户余额 2 months ago
wangguorui 57bb175303 20260306钱包id优化 2 months ago
wangguorui e061510504 Merge remote-tracking branch 'origin/milestone-20260224-现金钱包' into milestone-20260224-现金钱包 2 months ago
wangguorui 1c9599550b 20260306充值钱包金额查询 2 months ago
sunjiabei e6d3153166 20260306用户余额 2 months ago
wangguorui 0effaf6691 20260306充值钱包明细查询 2 months ago
wangguorui 4709a90bc8 20260306充值钱包修改 2 months ago
wangguorui 3a69d302d6 Merge remote-tracking branch 'origin/milestone-20260224-现金钱包' into milestone-20260224-现金钱包 2 months ago
wangguorui f46c08a643 20260305创建钱包明细记录 2 months ago
sunjiabei ac49b4de37 20260305钱包消费 2 months ago
wangguorui f8b817600b 20260305翻译优化 2 months ago
wangguorui 549dd9478d 20260305翻译优化 2 months ago
wangguorui 1ef1e6c13f 20260305充值钱包id不为空 2 months ago
wangguorui fe0a572729 20260305充值逻辑 2 months ago
wangguorui 3f9a016fe1 20260305新增钱包余额逻辑 2 months ago
wangguorui b3172862b8 20260305创建实体类 2 months ago
sunjiabei c7ca35fc8d 20260305钱包消费 2 months ago
sunjiabei e3a5239bb9 20260305免费金币去除 2 months ago
sunjiabei 82d61197da 20260304钱包逻辑修改 2 months ago
sunjiabei d58e616048 20260303钱包查询,历史钱包初始化 2 months ago
  1. 6
      src/main/java/com/example/demo/Export/ExportService.java
  2. 67
      src/main/java/com/example/demo/Export/ExportServiceImpl.java
  3. 399
      src/main/java/com/example/demo/Mysql/MysqlServiceImpl.java
  4. 68
      src/main/java/com/example/demo/Util/ExcelHeaderTranslator.java
  5. 238
      src/main/java/com/example/demo/controller/cash/CashCollectionController.java
  6. 23
      src/main/java/com/example/demo/controller/cash/CashRefundController.java
  7. 2
      src/main/java/com/example/demo/controller/coin/AuditController.java
  8. 596
      src/main/java/com/example/demo/controller/coin/ExportController.java
  9. 9
      src/main/java/com/example/demo/controller/coin/GoldDetailController.java
  10. 33
      src/main/java/com/example/demo/domain/DTO/RegionWalletDTO.java
  11. 30
      src/main/java/com/example/demo/domain/DTO/UserWalletDTO.java
  12. 30
      src/main/java/com/example/demo/domain/DTO/UserWalletRecordDTO.java
  13. 26
      src/main/java/com/example/demo/domain/DTO/WalletDTO.java
  14. 1
      src/main/java/com/example/demo/domain/entity/CashRecord.java
  15. 4
      src/main/java/com/example/demo/domain/entity/User.java
  16. 28
      src/main/java/com/example/demo/domain/entity/UserRegionWallet.java
  17. 23
      src/main/java/com/example/demo/domain/entity/UserWalletRecord.java
  18. 23
      src/main/java/com/example/demo/domain/entity/Wallet.java
  19. 2
      src/main/java/com/example/demo/domain/vo/cash/CashCollection.java
  20. 3
      src/main/java/com/example/demo/domain/vo/cash/CashRecordRefund.java
  21. 37
      src/main/java/com/example/demo/domain/vo/cash/UserWalletRecordVO.java
  22. 29
      src/main/java/com/example/demo/domain/vo/cash/UserWalletVO.java
  23. 17
      src/main/java/com/example/demo/domain/vo/cash/WalletItem.java
  24. 1
      src/main/java/com/example/demo/domain/vo/coin/AuditRequest.java
  25. 5
      src/main/java/com/example/demo/domain/vo/coin/Page.java
  26. 35
      src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java
  27. 37
      src/main/java/com/example/demo/mapper/cash/UserRegionWalletMapper.java
  28. 11
      src/main/java/com/example/demo/mapper/coin/ConsumeMapper.java
  29. 2
      src/main/java/com/example/demo/mapper/coin/GoldDetailMapper.java
  30. 34
      src/main/java/com/example/demo/mapper/coin/WalletMapper.java
  31. 24
      src/main/java/com/example/demo/service/Wallet/WalletService.java
  32. 8
      src/main/java/com/example/demo/service/cash/CashCollectionService.java
  33. 2
      src/main/java/com/example/demo/service/cash/RefundService.java
  34. 2
      src/main/java/com/example/demo/service/coin/AuditService.java
  35. 2
      src/main/java/com/example/demo/service/coin/ExportExcelService.java
  36. 27
      src/main/java/com/example/demo/service/listen/UserWalletListener.java
  37. 27
      src/main/java/com/example/demo/service/listen/UserWalletRecordListener.java
  38. 82
      src/main/java/com/example/demo/serviceImpl/Wallet/WalletServiceImpl.java
  39. 76
      src/main/java/com/example/demo/serviceImpl/cash/CashAuditServiceImpl.java
  40. 142
      src/main/java/com/example/demo/serviceImpl/cash/CashCollectionServiceImpl.java
  41. 311
      src/main/java/com/example/demo/serviceImpl/cash/CashRefundServiceImpl.java
  42. 91
      src/main/java/com/example/demo/serviceImpl/coin/AuditServiceImpl.java
  43. 113
      src/main/java/com/example/demo/serviceImpl/coin/ConsumeServiceImpl.java
  44. 224
      src/main/java/com/example/demo/serviceImpl/coin/ExportExcelServiceImpl.java
  45. 9
      src/main/java/com/example/demo/serviceImpl/coin/GoldDetailServiceImpl.java
  46. 7
      src/main/java/com/example/demo/serviceImpl/coin/TranslationServiceImpl.java
  47. 130
      src/main/resources/cashMapper/CashCollectionMapper.xml
  48. 29
      src/main/resources/cashMapper/UserRegionWalletMapper.xml
  49. 60
      src/main/resources/mapper/ConsumeMapper.xml
  50. 13
      src/main/resources/mapper/GoldDetailMapper.xml
  51. 92
      src/main/resources/mapper/WalletMapper.xml

6
src/main/java/com/example/demo/Export/ExportService.java

@ -36,4 +36,10 @@ public interface ExportService {
Result addExportPerformance(PerformanceDTO dto);
Result addExportFundDTO(FundDTO dto);
// 用户钱包明细导出
Result addExportUserWalletRecord(UserWalletRecordDTO dto);
// 用户钱包余额导出
Result addExportUserWallet(UserWalletDTO dto);
}

67
src/main/java/com/example/demo/Export/ExportServiceImpl.java

@ -11,9 +11,7 @@ import com.example.demo.Util.RedisUtil;
import com.example.demo.service.coin.AdminService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -144,11 +142,21 @@ public class ExportServiceImpl implements ExportService {
performanceDTO.setUrl("");
performanceDTO.setFileName(generateFileName("业绩归属明细", adminName, lang));
performanceDTO.setDataNum(0);
}else if (dto instanceof FundDTO fundDTO){
} else if (dto instanceof FundDTO fundDTO){
fundDTO.setAccount(Integer.valueOf(account));
fundDTO.setUrl("");
fundDTO.setFileName(generateFileName("资金明细", adminName, lang));
fundDTO.setDataNum(0);
} else if(dto instanceof UserWalletRecordDTO userWalletRecordDTO){
userWalletRecordDTO.setAccount(Integer.valueOf(account));
userWalletRecordDTO.setUrl("");
userWalletRecordDTO.setFileName(generateFileName("用户钱包明细", adminName, lang));
userWalletRecordDTO.setDataNum(0);
} else if (dto instanceof UserWalletDTO userWalletDTO) {
userWalletDTO.setAccount(Integer.valueOf(account));
userWalletDTO.setUrl("");
userWalletDTO.setFileName(generateFileName("用户钱包余额", adminName, lang));
userWalletDTO.setDataNum(0);
}
}
@ -276,6 +284,26 @@ public class ExportServiceImpl implements ExportService {
fundDTO.getFileName(),
fundDTO.getDataNum()
);
}else if (dto instanceof UserWalletRecordDTO userWalletRecordDTO){
goldDetailMapper.insertExportRecord(
idHolder,
account,
userWalletRecordDTO.getType(),
userWalletRecordDTO.getState(),
userWalletRecordDTO.getUrl(),
userWalletRecordDTO.getFileName(),
userWalletRecordDTO.getDataNum()
);
}else if (dto instanceof UserWalletDTO userWalletDTO){
goldDetailMapper.insertExportRecord(
idHolder,
account,
userWalletDTO.getType(),
userWalletDTO.getState(),
userWalletDTO.getUrl(),
userWalletDTO.getFileName(),
userWalletDTO.getDataNum()
);
}
}
@ -313,6 +341,29 @@ public class ExportServiceImpl implements ExportService {
requestData.put(requestDataKey, performanceDTO);
}else if (dto instanceof FundDTO fundDTO){
requestData.put(requestDataKey, fundDTO.getFundsDTO());
}else if (dto instanceof UserWalletRecordDTO UserWalletRecordDTO){
// 特殊处理优先使用 userWalletRecordVO如果为空则尝试从其他字段获取
if (UserWalletRecordDTO.getUserWalletRecordVO() != null) {
requestData.put("userWalletRecordVO", UserWalletRecordDTO.getUserWalletRecordVO());
} else if (UserWalletRecordDTO.getUserWalletRecord() != null) {
// 如果前端传的是 userWalletRecord转换为 VO
com.example.demo.domain.entity.UserWalletRecord record = UserWalletRecordDTO.getUserWalletRecord();
com.example.demo.domain.vo.cash.UserWalletRecordVO vo = new com.example.demo.domain.vo.cash.UserWalletRecordVO();
vo.setJwcode(record.getJwcode());
vo.setWalletId(record.getWalletId());
requestData.put("userWalletRecordVO", vo);
} else {
// 都为空放一个空的 VO 对象
requestData.put("userWalletRecordVO", new com.example.demo.domain.vo.cash.UserWalletRecordVO());
}
}else if (dto instanceof UserWalletDTO userWalletDTO){
// 特殊处理将查询参数放入 JSON
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("jwcode", userWalletDTO.getJwcode());
queryParams.put("market", userWalletDTO.getMarket());
queryParams.put("page", userWalletDTO.getPage());
queryParams.put("pageSize", userWalletDTO.getPageSize());
requestData.put(requestDataKey, queryParams);
}
exportData.put("requestData", requestData);
@ -390,4 +441,14 @@ public class ExportServiceImpl implements ExportService {
public Result addExportFundDTO(FundDTO dto) {
return addExport(dto, "资金流水明细", "fund:queue:export_queue", "fundsDTO", dto.getLang());
}
@Override
public Result addExportUserWalletRecord(UserWalletRecordDTO dto) {
return addExport(dto, "用户钱包明细", "user_wallet_record:queue:export_queue", "userWalletRecordVO", dto.getLang());
}
@Override
public Result addExportUserWallet(UserWalletDTO dto) {
return addExport(dto, "用户钱包余额", "user_wallet:queue:export_queue", "userWalletDTO", dto.getLang());
}
}

399
src/main/java/com/example/demo/Mysql/MysqlServiceImpl.java

@ -27,6 +27,7 @@ import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Date;
import java.util.stream.Collectors;
@Service
@ -40,8 +41,7 @@ public class MysqlServiceImpl implements MysqlService {
Set<Integer> validTwoTypes = new HashSet<>(Arrays.asList(52,61));
Set<Integer> validThreeTypes = new HashSet<>(Arrays.asList(10, 16, 30, 31, 32, 33, 34, 39, 44));
Set<Integer> validFourTypes = new HashSet<>(Arrays.asList(55, 56, 57, 58, 59, 63, 64, 65));
LocalDateTime now = LocalDateTime.now();
Month currentMonth = now.getMonth();
@Autowired
private AdminService adminService;
@Autowired
@ -128,9 +128,12 @@ public class MysqlServiceImpl implements MysqlService {
Set<String> existingUids = getExistingUids(mysqlConn, batchUids);
logger.info("已存在记录数: {}", existingUids.size());
// 👇 步骤3准备批量插入
// 👇 步骤 3准备批量插入
try (PreparedStatement mysqlStmt = mysqlConn.prepareStatement(insertSql)) {
// 👇 新增收集新记录列表
List<RecordData> newRecords = new ArrayList<>();
for (RecordData data : batchRecords) {
if (validFourTypes.contains(data.gtype)) {
logger.debug("跳过 validFourTypes 类型记录,gtype={}, uid={}", data.gtype, data.uid);
@ -140,24 +143,25 @@ public class MysqlServiceImpl implements MysqlService {
logger.debug("跳过 operation_platform=4 的记录,uid={}", data.uid);
continue;
}
// 👇 跳过已存在的记录避免重复更新用户余额
if (existingUids.contains(data.uid)) {
logger.debug("跳过重复记录,uid={}", data.uid);
continue;
}
// 👇 新增添加到新记录列表
newRecords.add(data);
// 👇 设置插入参数
setStatementParams(mysqlStmt, data);
mysqlStmt.addBatch();
// 👇 只有新记录才更新用户余额关键修复
updateUserBalance(mysqlConn, data);
}
// 👇 执行批量插入
int[] results = mysqlStmt.executeBatch();
logger.info("成功插入新记录 {} 条", results.length);
// 修正只传新记录15
updateUserRegionWallet(mysqlConn, newRecords);
}
offset += pageSize;
@ -236,9 +240,12 @@ public class MysqlServiceImpl implements MysqlService {
Set<String> existingUids = getExistingUids(mysqlConn, batchUids);
logger.info("已存在记录数: {}", existingUids.size());
// 👇 步骤3准备批量插入
// 👇 步骤 3准备批量插入
try (PreparedStatement mysqlStmt = mysqlConn.prepareStatement(insertSql)) {
// 👇 新增收集新记录列表
List<RecordData> newRecords = new ArrayList<>();
for (RecordData data : batchRecords) {
if (validFourTypes.contains(data.gtype)) {
logger.debug("跳过 validFourTypes 类型记录,gtype={}, uid={}", data.gtype, data.uid);
@ -248,24 +255,25 @@ public class MysqlServiceImpl implements MysqlService {
logger.debug("跳过 operation_platform=4 的记录,uid={}", data.uid);
continue;
}
// 👇 跳过已存在的记录避免重复更新用户余额
if (existingUids.contains(data.uid)) {
logger.debug("跳过重复记录,uid={}", data.uid);
continue;
}
// 👇 新增添加到新记录列表
newRecords.add(data);
// 👇 设置插入参数
setStatementParams(mysqlStmt, data);
mysqlStmt.addBatch();
// 👇 只有新记录才更新用户余额关键修复
updateUserBalance(mysqlConn, data);
}
// 👇 执行批量插入
int[] results = mysqlStmt.executeBatch();
logger.info("成功插入新记录 {} 条", results.length);
// 修正只传新记录
updateUserRegionWallet(mysqlConn, newRecords);
}
offset += pageSize;
@ -281,10 +289,12 @@ public class MysqlServiceImpl implements MysqlService {
static class RecordData {
int gtype, jwcode, free, core_jb, buy_jb;
Integer permanent_gold;
Timestamp cz_time;
String cz_user, cz_bz, operation_platform, goods_name, uid;
String orderNumber; // 预生成避免重复计算
String linkId;
}
private RecordData extractRecordData(ResultSet rs) throws SQLException {
@ -294,6 +304,9 @@ public class MysqlServiceImpl implements MysqlService {
data.free = rs.getInt("free");
data.core_jb = rs.getInt("core_jb");
data.buy_jb = rs.getInt("buy_jb");
// 👇👇👇 新增permanent_gold = buy_jb 👇👇👇
data.permanent_gold = data.buy_jb;
// 👆👆👆 新增结束 👆👆👆
data.cz_time = rs.getTimestamp("cz_time");
data.cz_user = rs.getString("cz_user");
data.cz_bz = rs.getString("cz_bz");
@ -330,6 +343,9 @@ public class MysqlServiceImpl implements MysqlService {
return existing;
}
private void setStatementParams(PreparedStatement stmt, RecordData data) throws SQLException {
Month currentMonth = LocalDateTime.now().getMonth();
String name = data.cz_user;
// 设置 admin_id
@ -430,6 +446,8 @@ public class MysqlServiceImpl implements MysqlService {
private void updateUserBalance(Connection conn, RecordData data) throws Exception {
logger.info("处理用户余额更新,jwcode={}", data.jwcode);
Month currentMonth = LocalDateTime.now().getMonth();
User user = userService.selectAllUser(String.valueOf(data.jwcode));
BigDecimal freeBD = BigDecimal.valueOf(data.free);
BigDecimal buyJbBD = BigDecimal.valueOf(data.buy_jb);
@ -552,4 +570,357 @@ public class MysqlServiceImpl implements MysqlService {
return defaultValue;
}
/**
* g_order 获取 pay_style路由更新 user_region_wallet 余额
* 逻辑
* - 充值validZeroTypespermanent_gold > 0linkId 需为 G+数字格式 g_order 路由 wallet_id 累加
* - 消费validOneTypespermanent_gold < 0 不判断 G 格式 取绝对值按 wallet_id 从小到大扣减
* 钱包更新是附加逻辑异常不影响金币主流程
*/
/**
* g_order 获取 pay_style路由更新 user_region_wallet 余额
* 调试版添加详细日志追踪
*/
private void updateUserRegionWallet(Connection mysqlConn, List<RecordData> records) {
// 👇👇👇 强制输出绕过日志配置确保能看到👇👇👇
System.out.println("🚨🚨🚨 [钱包更新] updateUserRegionWallet 被调用!records大小=" + (records == null ? 0 : records.size()));
System.err.println("🚨🚨🚨 [钱包更新] updateUserRegionWallet 被调用!records大小=" + (records == null ? 0 : records.size()));
if (records == null || records.isEmpty()) {
logger.debug("updateUserRegionWallet: records 为空,跳过钱包更新");
return;
}
// 1. 按业务类型分离记录 + 详细日志追踪
Map<Long, RecordData> rechargeOrderIdMap = new HashMap<>();
Map<Integer, Integer> jwcodeConsumeTotalMap = new HashMap<>();
int totalRecords = records.size();
int filteredByType = 0; // gtype 不属于充值/消费
int filteredByGold = 0; // permanent_gold 不符合正负要求
int filteredByLinkId = 0; // 充值记录 linkId 格式不符
int rechargeCandidate = 0; // 充值候选数
int consumeCandidate = 0; // 消费候选数
logger.info("🔍 [钱包更新] 开始分析 {} 条记录", totalRecords);
for (int i = 0; i < records.size(); i++) {
RecordData r = records.get(i);
// 记录每条数据的关键字段
logger.info("🔍 [记录#{}/{}] gtype={}, permanent_gold={}, linkId='{}', jwcode={}",
i+1, totalRecords, r.gtype, r.permanent_gold, r.linkId, r.jwcode);
// 👉 充值类型判断
if (validZeroTypes.contains(r.gtype)) {
logger.debug(" → 属于充值类型 (validZeroTypes)");
if (r.permanent_gold == null) {
logger.info(" ⏭️ [过滤] permanent_gold 为 null");
filteredByGold++;
continue;
}
if (r.permanent_gold <= 0) {
logger.info(" ⏭️ [过滤] 充值记录 permanent_gold={} <= 0", r.permanent_gold);
filteredByGold++;
continue;
}
if (r.linkId == null) {
logger.info(" ⏭️ [过滤] 充值记录 linkId 为 null");
filteredByLinkId++;
continue;
}
if (!r.linkId.matches("^G\\d+$")) {
logger.info(" ⏭️ [过滤] 充值记录 linkId='{}' 不符合 G+数字格式", r.linkId);
filteredByLinkId++;
continue;
}
// 充值候选
try {
long orderId = Long.parseLong(r.linkId.substring(1));
rechargeOrderIdMap.put(orderId, r);
rechargeCandidate++;
logger.info(" ✅ [充值候选] orderId={}, permanent_gold={}, jwcode={}",
orderId, r.permanent_gold, r.jwcode);
} catch (NumberFormatException e) {
logger.warn(" ⚠️ [异常] linkId='{}' 解析 orderId 失败", r.linkId, e);
filteredByLinkId++;
}
}
// 👉 消费类型判断
else if (validOneTypes.contains(r.gtype)) {
logger.debug(" → 属于消费类型 (validOneTypes)");
if (r.permanent_gold == null) {
logger.info(" ⏭️ [过滤] permanent_gold 为 null");
filteredByGold++;
continue;
}
if (r.permanent_gold >= 0) {
logger.info(" ⏭️ [过滤] 消费记录 permanent_gold={} >= 0 (应该是负数)", r.permanent_gold);
filteredByGold++;
continue;
}
// 消费候选 不判断 G 格式
int consumeAmount = Math.abs(r.permanent_gold);
jwcodeConsumeTotalMap.merge(r.jwcode, consumeAmount, Integer::sum);
consumeCandidate++;
logger.info(" ✅ [消费候选] consumeAmount={}, jwcode={}, 累计扣减={}",
consumeAmount, r.jwcode, jwcodeConsumeTotalMap.get(r.jwcode));
}
// 👉 其他类型
else {
logger.debug(" → gtype={} 不属于充值/消费类型", r.gtype);
filteredByType++;
}
}
// 📊 统计汇总
logger.info("📊 [钱包更新统计] 总记录={}, 类型过滤={}, 金额过滤={}, linkId过滤={}, 充值候选={}, 消费候选={}",
totalRecords, filteredByType, filteredByGold, filteredByLinkId, rechargeCandidate, consumeCandidate);
// 2. 处理充值记录
if (!rechargeOrderIdMap.isEmpty()) {
logger.info("💰 开始处理 {} 条充值记录", rechargeOrderIdMap.size());
processRechargeWallet(mysqlConn, rechargeOrderIdMap);
} else {
logger.info("💰 无充值记录需要处理");
}
// 3. 处理消费记录
if (!jwcodeConsumeTotalMap.isEmpty()) {
logger.info("💸 开始处理 {} 个用户的消费扣减", jwcodeConsumeTotalMap.size());
processConsumeWallet(mysqlConn, jwcodeConsumeTotalMap);
} else {
logger.info("💸 无消费记录需要处理");
}
logger.info("✅ [钱包更新] 方法执行完成");
}
/**
* 处理充值 pay_style 路由到 wallet_id累加 permanent_gold
* 调试版添加详细日志
*/
private void processRechargeWallet(Connection mysqlConn, Map<Long, RecordData> orderIdRecordMap) {
logger.info("💰 [充值处理] 开始,订单数={}", orderIdRecordMap.size());
List<Long> orderIds = new ArrayList<>(orderIdRecordMap.keySet());
String inSql = String.join(",", Collections.nCopies(orderIds.size(), "?"));
String querySql = "SELECT id, jwcode, pay_style FROM g_order WHERE id IN (" + inSql + ") AND state = 1";
Map<Integer, Integer> jwcodePayStyleMap = new HashMap<>();
try (PreparedStatement stmt = mysqlConn.prepareStatement(querySql)) {
for (int i = 0; i < orderIds.size(); i++) {
stmt.setLong(i + 1, orderIds.get(i));
}
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
int jwcode = rs.getInt("jwcode");
int payStyle = rs.getInt("pay_style");
jwcodePayStyleMap.put(jwcode, payStyle);
logger.debug("💰 [g_order匹配] id={}, jwcode={}, pay_style={}",
rs.getLong("id"), jwcode, payStyle);
}
}
} catch (SQLException e) {
logger.error("💰 [充值处理] 查询 g_order 失败", e);
return;
}
if (jwcodePayStyleMap.isEmpty()) {
logger.warn("💰 [充值处理] g_order 未匹配到任何支付记录,跳过");
return;
}
logger.info("💰 [充值处理] g_order 匹配成功 {} 条", jwcodePayStyleMap.size());
Map<String, Integer> userWalletGoldMap = new HashMap<>();
for (RecordData data : orderIdRecordMap.values()) {
Integer payStyle = jwcodePayStyleMap.get(data.jwcode);
if (payStyle == null) {
logger.debug("💰 [跳过] jwcode={} 未匹配到 pay_style", data.jwcode);
continue;
}
Integer walletId = mapPayStyleToWalletId(payStyle);
if (walletId == null) {
logger.debug("💰 [跳过] payStyle={} 未映射到 wallet_id", payStyle);
continue;
}
String key = data.jwcode + "_" + walletId;
userWalletGoldMap.merge(key, data.permanent_gold, Integer::sum);
logger.info("💰 [路由成功] jwcode={}, payStyle={}, walletId={}, amount={}",
data.jwcode, payStyle, walletId, data.permanent_gold);
}
if (userWalletGoldMap.isEmpty()) {
logger.info("💰 [充值处理] 无有效记录需要更新钱包");
return;
}
logger.info("💰 [充值处理] 待更新钱包记录 {} 条", userWalletGoldMap.size());
String upsertSql = """
INSERT INTO user_region_wallet
(jwcode, wallet_id, current_permanent_gold, create_time, update_time)
VALUES (?, ?, ?, NOW(), NOW())
ON DUPLICATE KEY UPDATE
current_permanent_gold = current_permanent_gold + VALUES(current_permanent_gold),
update_time = NOW()
""";
try (PreparedStatement stmt = mysqlConn.prepareStatement(upsertSql)) {
for (Map.Entry<String, Integer> entry : userWalletGoldMap.entrySet()) {
String[] parts = entry.getKey().split("_");
int jwcode = Integer.parseInt(parts[0]);
int walletId = Integer.parseInt(parts[1]);
stmt.setInt(1, jwcode);
stmt.setString(2, String.valueOf(walletId));
stmt.setInt(3, entry.getValue());
stmt.addBatch();
}
int[] results = stmt.executeBatch();
logger.info("✅ [充值完成] 地区钱包充值更新成功 {} 条", results.length);
} catch (SQLException e) {
logger.error("❌ [充值失败] 更新 user_region_wallet 异常", e);
}
}
/**
* 处理消费 wallet_id 从小到大顺序扣减 permanent_gold
* 调试版添加详细日志
*/
private void processConsumeWallet(Connection mysqlConn, Map<Integer, Integer> jwcodeConsumeMap) {
logger.info("💸 [消费处理] 开始,用户数={}", jwcodeConsumeMap.size());
for (Map.Entry<Integer, Integer> entry : jwcodeConsumeMap.entrySet()) {
int jwcode = entry.getKey();
int totalConsume = entry.getValue();
logger.info("💸 [消费用户] jwcode={}, 待扣总额={}", jwcode, totalConsume);
try {
// 1. 查询用户有余额的钱包
List<WalletInfo> wallets = queryUserWallets(mysqlConn, jwcode);
if (wallets.isEmpty()) {
logger.warn("💸 [跳过] 用户 {} 无可用地区钱包(余额>0)", jwcode);
continue;
}
logger.info("💸 [钱包列表] 用户 {} 有 {} 个有余额的钱包: {}",
jwcode, wallets.size(),
wallets.stream().map(w -> "walletId=" + w.walletId + ":余额" + w.balance)
.collect(Collectors.joining(", ")));
// 2. 计算扣减方案
int remaining = totalConsume;
List<WalletDeduct> deductList = new ArrayList<>();
for (WalletInfo wallet : wallets) {
if (remaining <= 0) break;
int deductAmount = Math.min(wallet.balance, remaining);
if (deductAmount > 0) {
deductList.add(new WalletDeduct(wallet.walletId, deductAmount));
remaining -= deductAmount;
logger.debug("💸 [扣减方案] walletId={}, 扣减={}, 剩余待扣={}",
wallet.walletId, deductAmount, remaining);
}
}
if (remaining > 0) {
logger.warn("💸 [余额不足] 用户 {} 钱包余额不足,待扣: {}, 已安排: {}",
jwcode, remaining, totalConsume - remaining);
}
// 3. 批量执行扣减
if (!deductList.isEmpty()) {
executeWalletDeducts(mysqlConn, jwcode, deductList);
logger.info("✅ [消费完成] 用户 {} 扣减成功,总额: {}, 钱包数: {}",
jwcode, totalConsume, deductList.size());
} else {
logger.info("💸 [无操作] 用户 {} 无扣减方案", jwcode);
}
} catch (SQLException e) {
logger.error("❌ [消费失败] 用户 {} 扣减异常", jwcode, e);
}
}
logger.info("💸 [消费处理] 执行完成");
}
/**
* 查询用户有余额的地区钱包 wallet_id 升序
*/
private List<WalletInfo> queryUserWallets(Connection conn, int jwcode) throws SQLException {
String sql = "SELECT wallet_id, current_permanent_gold FROM user_region_wallet WHERE jwcode = ? AND current_permanent_gold > 0 ORDER BY wallet_id ASC";
List<WalletInfo> result = new ArrayList<>();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, jwcode);
logger.debug("🔍 [查询钱包] SQL: {}, jwcode={}", sql, jwcode);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
WalletInfo info = new WalletInfo();
info.walletId = rs.getInt("wallet_id");
info.balance = rs.getInt("current_permanent_gold");
result.add(info);
logger.debug(" 📦 [钱包] walletId={}, balance={}", info.walletId, info.balance);
}
}
}
return result;
}
/**
* 批量执行钱包扣减乐观锁防并发
*/
private void executeWalletDeducts(Connection conn, int jwcode, List<WalletDeduct> deducts) throws SQLException {
String sql = "UPDATE user_region_wallet SET current_permanent_gold = current_permanent_gold - ?, update_time = NOW() WHERE jwcode = ? AND wallet_id = ? AND current_permanent_gold >= ?";
logger.debug("🔧 [执行扣减] SQL: {}, 扣减数={}", sql, deducts.size());
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (WalletDeduct deduct : deducts) {
stmt.setInt(1, deduct.amount);
stmt.setInt(2, jwcode);
stmt.setString(3, String.valueOf(deduct.walletId));
stmt.setInt(4, deduct.amount);
stmt.addBatch();
logger.debug(" ➖ [扣减] walletId={}, amount={}", deduct.walletId, deduct.amount);
}
int[] results = stmt.executeBatch();
logger.debug("✅ [扣减执行] 成功 {} 条", results.length);
}
}
// 钱包余额信息内部辅助类
static class WalletInfo {
int walletId;
int balance;
}
// 扣减指令内部辅助类
static class WalletDeduct {
int walletId;
int amount;
WalletDeduct(int walletId, int amount) {
this.walletId = walletId;
this.amount = amount;
}
}
/**
* pay_style wallet_id 映射 请替换成你实际的 wallet_id
*/
private Integer mapPayStyleToWalletId(Integer payStyle) {
if (payStyle == null) return null;
return switch (payStyle) {
case 3,4,9 -> 5; // 微信/支付宝 华东钱包
case 5,6 -> 2; // Stripe/PaymentAsia 华南钱包
case 7 -> 4; // 其他 西南钱包
case 10 -> 8;
case 15 -> 3;
default -> 1;
};
}
}

68
src/main/java/com/example/demo/Util/ExcelHeaderTranslator.java

@ -592,6 +592,74 @@ public class ExcelHeaderTranslator {
}
/**
* 获取用户钱包明细的 Excel 表头映射
* 返回 Map<字段名中文表头>
*/
public Map<String, String> getUserWalletHeaders(String lang) {
Map<String, String> headers = new LinkedHashMap<>();
// 定义所有表头的原始中文名称对应 UserWalletRecordVO 类的字段
headers.put("jwcode", "精网号");
headers.put("userName", "姓名");
headers.put("marketName", "所属地区");
headers.put("walletName", "钱包名称");
headers.put("typeText", "交易类型");
headers.put("amount", "交易金额");
headers.put("orderCode", "订单号");
headers.put("description", "交易说明");
headers.put("statusText", "状态");
headers.put("createTime", "创建时间");
// 如果需要翻译则翻译表头
if (!isChineseLanguage(lang)) {
return translateHeaders(headers, lang);
}
return headers;
}
/**
* 获取用户钱包明细表头顺序
*/
public List<String> getUserWalletColumnOrder() {
return Arrays.asList(
"jwcode", "userName", "marketName", "walletName", "typeText", "amount",
"orderCode", "description", "statusText", "createTime"
);
}
/**
* 获取用户钱包余额的 Excel 表头映射
* 返回 Map<字段名中文表头>
*/
public Map<String, String> getUserWalletBalanceHeaders(String lang) {
Map<String, String> headers = new LinkedHashMap<>();
// 定义所有表头的原始中文名称对应 UserWalletVO 类的字段
headers.put("jwcode", "精网号");
headers.put("userName", "姓名");
headers.put("marketName", "所属地区");
headers.put("walletName", "钱包名称");
headers.put("currentPermanentGold", "当前永久金币");
// 如果需要翻译则翻译表头
if (!isChineseLanguage(lang)) {
return translateHeaders(headers, lang);
}
return headers;
}
/**
* 获取用户钱包余额表头顺序
*/
public List<String> getUserWalletBalanceColumnOrder() {
return Arrays.asList(
"jwcode", "userName", "marketName", "walletName", "currentPermanentGold"
);
}
/**
* 翻译表头
*/
private Map<String, String> translateHeaders(Map<String, String> headers, String lang) {

238
src/main/java/com/example/demo/controller/cash/CashCollectionController.java

@ -2,15 +2,13 @@ package com.example.demo.controller.cash;
import com.example.demo.Util.JWTUtil;
import com.example.demo.Util.LanguageTranslationUtil;
import com.example.demo.config.interfac.Log;
import com.example.demo.domain.DTO.PerformanceDTO;
import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.entity.CashRecord;
import com.example.demo.domain.entity.RechargeActivity;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.PerformanceVO;
import com.example.demo.domain.entity.*;
import com.example.demo.domain.vo.cash.*;
import com.example.demo.domain.vo.coin.Page;
import com.example.demo.domain.vo.coin.Result;
import com.example.demo.service.Wallet.WalletService;
import com.example.demo.service.cash.CashCollectionService;
import com.example.demo.service.coin.TranslationService;
import com.github.pagehelper.PageInfo;
@ -25,6 +23,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @program: gold-java
@ -49,6 +48,9 @@ public class CashCollectionController {
@Autowired
private TranslationService translationService;
@Autowired
private WalletService walletService;
//根据精网号获取姓名和地区
@PostMapping("/getNameAndMarket")
public Result getNameAndMarket(@RequestBody Integer jwcode, @RequestHeader(defaultValue = "zh_CN") String lang) {
@ -230,6 +232,14 @@ public class CashCollectionController {
@PostMapping("/complete")
public Result complete(@RequestBody CashRecord cashRecord, @RequestHeader(defaultValue = "zh_CN") String lang) {
try {
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertTranslatedRecordFieldsToChinese(cashRecord, languageCode);
}
String result = cashCollectionService.complete(cashRecord);
String successMsg = languageTranslationUtil.translate(result, lang);
return Result.success(successMsg);
@ -269,7 +279,7 @@ public class CashCollectionController {
}
}
//根据goldcoin订单号查询收款订单
//根据gold coin订单号查询收款订单
@PostMapping("/selectByGoldCoinOrderCode")
public Result selectByGoldCoinOrderCode(@RequestBody String orderNo, @RequestHeader(defaultValue = "zh_CN") String lang) {
try {
@ -314,6 +324,162 @@ public class CashCollectionController {
}
}
// 根据精网号和钱包 ID 查询用户钱包明细列表
@PostMapping("/selectWalletRecords")
public Result selectWalletRecords(@RequestBody Page page, @RequestHeader(defaultValue = "zh_CN") String lang) {
try {
if (ObjectUtils.isEmpty(page.getPageNum())) {
return Result.error("页码数为空!");
}
if (ObjectUtils.isEmpty(page.getPageSize())) {
return Result.error("页大小为空!");
}
UserWalletRecord queryCondition = page.getUserWalletRecord();
if (queryCondition == null ||
(queryCondition.getJwcode() == null && queryCondition.getWalletId() == null)) {
return Result.error("精网号和钱包 ID 不能同时为空");
}
Result result = Result.success(cashCollectionService.selectWalletRecordsByJwcodeAndWalletId(
page.getPageNum(),
page.getPageSize(),
queryCondition.getJwcode(),
queryCondition.getWalletId()
));
// 对返回结果进行多语言转换
if (result.getCode() == 200 && result.getData() instanceof PageInfo) {
PageInfo<UserWalletRecordVO> pageInfo = (PageInfo<UserWalletRecordVO>) result.getData();
translateWalletRecordVOs(pageInfo, lang);
}
return result;
} catch (Exception e) {
return Result.error("查询失败" + ": " + e.getMessage());
}
}
// 根据精网号和地区查询用户的所有钱包 ID 和金币数量
@PostMapping("/selectUserWallets")
public Result selectUserWallets(@RequestBody Map<String, Object> params, @RequestHeader(defaultValue = "zh_CN") String lang) {
try {
Integer jwcode = null;
String market = null;
Integer pageNum = null;
Integer pageSize = null;
if (params.containsKey("jwcode")) {
Object jwcodeObj = params.get("jwcode");
if (jwcodeObj != null) {
String jwcodeStr = jwcodeObj.toString();
if (!jwcodeStr.isEmpty()) {
try {
jwcode = ((Number) jwcodeObj).intValue();
} catch (Exception e) {
// 如果转换失败保持为 null
}
}
}
}
if (params.containsKey("market")) {
Object marketObj = params.get("market");
if (marketObj != null && !marketObj.toString().isEmpty()) {
market = marketObj.toString();
}
}
if (params.containsKey("pageNum")) {
Object pageNumObj = params.get("pageNum");
if (pageNumObj != null && !pageNumObj.toString().isEmpty()) {
try {
pageNum = ((Number) pageNumObj).intValue();
} catch (Exception e) {
pageNum = 1;
}
}
}
if (params.containsKey("pageSize")) {
Object pageSizeObj = params.get("pageSize");
if (pageSizeObj != null && !pageSizeObj.toString().isEmpty()) {
try {
pageSize = ((Number) pageSizeObj).intValue();
} catch (Exception e) {
pageSize = 10;
}
}
}
if (pageNum == null) {
pageNum = 1;
}
if (pageSize == null) {
pageSize = 10;
}
PageInfo<UserWalletVO> result = cashCollectionService.selectUserWallets(jwcode, market, pageNum, pageSize);
// 对返回结果进行多语言转换
if (result != null && result.getList() != null) {
for (UserWalletVO vo : result.getList()) {
translateUserWalletVO(vo, lang);
}
}
return Result.success(result);
} catch (Exception e) {
return Result.error("查询失败" + ": " + e.getMessage());
}
}
/**
* 查询所有钱包类型
*/
@Log("查询所有钱包类型")
@PostMapping("/selectAllWallets")
public Result selectAllWallets(@RequestHeader(defaultValue = "zh_CN") String lang) {
try {
List<Wallet> wallets = walletService.selectAllWallets();
// 对钱包名称进行多语言转换
if (wallets != null && !"zh".equalsIgnoreCase(parseLanguageCode(lang))
&& !"zh_cn".equalsIgnoreCase(parseLanguageCode(lang))) {
for (Wallet wallet : wallets) {
if (wallet.getWalletName() != null) {
wallet.setWalletName(
languageTranslationUtil.translate(wallet.getWalletName().toString(), lang)
);
}
}
}
return Result.success(wallets);
} catch (Exception e) {
log.error("查询所有钱包类型失败", e);
return Result.error("查询失败");
}
}
/**
* 转换用户钱包 VO 的多语言字段
*/
private void translateUserWalletVO(UserWalletVO vo, String lang) {
if (vo != null) {
// 翻译地区名称
if (vo.getMarketName() != null) {
vo.setMarketName(languageTranslationUtil.translate(vo.getMarketName(), lang));
}
// 翻译钱包名称
if (vo.getWalletList() != null) {
for (WalletItem wallet : vo.getWalletList()) {
if (wallet.getWalletName() != null) {
wallet.setWalletName(languageTranslationUtil.translate(wallet.getWalletName(), lang));
}
}
}
}
}
/**
* 转换现金收款订单的多语言字段
@ -351,6 +517,10 @@ public class CashCollectionController {
if (collection.getPaymentCurrency() != null) {
collection.setPaymentCurrency(languageTranslationUtil.translate(collection.getPaymentCurrency(), lang));
}
// 翻译到账币种
if (collection.getReceivedCurrency() != null) {
collection.setReceivedCurrency(languageTranslationUtil.translate(collection.getReceivedCurrency(), lang));
}
// 翻译数量单位
if (collection.getNumUnit() != null) {
collection.setNumUnit(languageTranslationUtil.translate(collection.getNumUnit(), lang));
@ -475,6 +645,13 @@ public class CashCollectionController {
cashRecord.getReceivedMarket(), languageCode);
cashRecord.setReceivedMarket(chineseMarket);
}
// 转换原始付款币种
if (cashRecord.getPaymentCurrencyOrig() != null && !cashRecord.getPaymentCurrencyOrig().isEmpty()) {
String chinesePaymentCurrencyOrig = translationService.findChineseSimplifiedByTranslation(
cashRecord.getPaymentCurrencyOrig(), languageCode);
cashRecord.setPaymentCurrencyOrig(chinesePaymentCurrencyOrig);
}
}
}
@ -511,6 +688,13 @@ public class CashCollectionController {
cashCollection.setPaymentCurrency(chineseCurrency);
}
// 转换到账币种
if (cashCollection.getReceivedCurrency() != null && !cashCollection.getReceivedCurrency().isEmpty()) {
String chineseCurrency = translationService.findChineseSimplifiedByTranslation(
cashCollection.getReceivedCurrency(), languageCode);
cashCollection.setReceivedCurrency(chineseCurrency);
}
// 转换提交人地区
if (cashCollection.getSubmitterMarket() != null && !cashCollection.getSubmitterMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
@ -562,4 +746,44 @@ public class CashCollectionController {
}
}
/**
* 转换用户钱包明细 VO 列表的多语言字段
*/
private void translateWalletRecordVOs(PageInfo<UserWalletRecordVO> pageInfo, String lang) {
if (pageInfo != null && pageInfo.getList() != null) {
for (UserWalletRecordVO vo : pageInfo.getList()) {
translateWalletRecordVO(vo, lang);
}
}
}
/**
* 转换用户钱包明细 VO 的多语言字段
*/
private void translateWalletRecordVO(UserWalletRecordVO vo, String lang) {
if (vo != null) {
// 翻译地区名称
if (vo.getMarketName() != null) {
vo.setMarketName(languageTranslationUtil.translate(vo.getMarketName(), lang));
}
// 翻译钱包名称
if (vo.getWalletName() != null) {
vo.setWalletName(languageTranslationUtil.translate(vo.getWalletName(), lang));
}
// 翻译交易说明
if (vo.getDescription() != null) {
vo.setDescription(languageTranslationUtil.translate(vo.getDescription(), lang));
}
// 翻译交易类型
if (vo.getType() != null) {
String typeText = vo.getType() == 0 ? "充值" : (vo.getType() == 1 ? "消耗" : "退款");
vo.setTypeText(languageTranslationUtil.translate(typeText, lang));
}
// 翻译状态
if (vo.getStatus() != null) {
String statusText = vo.getStatus() == 0 ? "正常" : "已退款";
vo.setStatusText(languageTranslationUtil.translate(statusText, lang));
}
}
}
}

23
src/main/java/com/example/demo/controller/cash/CashRefundController.java

@ -373,13 +373,12 @@ public class CashRefundController {
}
cashRecordRefund.setStatus(20);
int resultCode = refundService.add(cashRecordRefund,lang);
refundService.addOnline(cashRecordRefund, lang);
String successMsg = languageTranslationUtil.translate("提交成功", lang);
if (resultCode > 0) {
translateCashRecordRefundFields(cashRecordRefund, lang);
}
return Result.success(successMsg, cashRecordRefund);
} catch (Exception e) {
log.error("addOnline error", e);
String errorMsg = languageTranslationUtil.translate(e.getMessage(), lang);
return Result.error(errorMsg);
}
@ -759,50 +758,52 @@ public class CashRefundController {
* 将退款记录查询条件中的翻译字段转换为中文简体
*/
private void convertTranslatedRefundFieldsToChinese(CashRecordRefund cashRecordRefund, String languageCode) {
if (cashRecordRefund != null) {
if (cashRecordRefund == null) {
return;
}
// 转换退款理由
if (cashRecordRefund.getRefundReason() != null && !cashRecordRefund.getRefundReason().isEmpty()) {
if (cashRecordRefund.getRefundReason() != null && !cashRecordRefund.getRefundReason().trim().isEmpty()) {
String chineseReason = translationService.findChineseSimplifiedByTranslation(
cashRecordRefund.getRefundReason(), languageCode);
cashRecordRefund.setRefundReason(chineseReason);
}
// 转换退款备注
if (cashRecordRefund.getRefundRemark() != null && !cashRecordRefund.getRefundRemark().isEmpty()) {
if (cashRecordRefund.getRefundRemark() != null && !cashRecordRefund.getRefundRemark().trim().isEmpty()) {
String chineseRemark = translationService.findChineseSimplifiedByTranslation(
cashRecordRefund.getRefundRemark(), languageCode);
cashRecordRefund.setRefundRemark(chineseRemark);
}
// 转换退款途径
if (cashRecordRefund.getRefundChannels() != null && !cashRecordRefund.getRefundChannels().isEmpty()) {
if (cashRecordRefund.getRefundChannels() != null && !cashRecordRefund.getRefundChannels().trim().isEmpty()) {
String chineseChannels = translationService.findChineseSimplifiedByTranslation(
cashRecordRefund.getRefundChannels(), languageCode);
cashRecordRefund.setRefundChannels(chineseChannels);
}
// 转换退款币种
if (cashRecordRefund.getRefundCurrency() != null && !cashRecordRefund.getRefundCurrency().isEmpty()) {
if (cashRecordRefund.getRefundCurrency() != null && !cashRecordRefund.getRefundCurrency().trim().isEmpty()) {
String chineseCurrency = translationService.findChineseSimplifiedByTranslation(
cashRecordRefund.getRefundCurrency(), languageCode);
cashRecordRefund.setRefundCurrency(chineseCurrency);
}
// 转换所属地区
if (cashRecordRefund.getMarketName() != null && !cashRecordRefund.getMarketName().isEmpty()) {
if (cashRecordRefund.getMarketName() != null && !cashRecordRefund.getMarketName().trim().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
cashRecordRefund.getMarketName(), languageCode);
cashRecordRefund.setMarketName(chineseMarket);
}
// 转换提交人地区
if (cashRecordRefund.getSubmitterMarket() != null && !cashRecordRefund.getSubmitterMarket().isEmpty()) {
if (cashRecordRefund.getSubmitterMarket() != null && !cashRecordRefund.getSubmitterMarket().trim().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
cashRecordRefund.getSubmitterMarket(), languageCode);
cashRecordRefund.setSubmitterMarket(chineseMarket);
}
}
}
/**
* 将资金查询条件中的翻译字段转换为中文简体

2
src/main/java/com/example/demo/controller/coin/AuditController.java

@ -53,7 +53,7 @@ public class AuditController {
@PostMapping("audit")
public ResponseEntity<Map<String, Object>> auditOrder(@RequestBody AuditRequest request) throws Exception {
boolean result = auditService.auditOrder(request.getToken(), request.getOrderCode(), request.getAuditId(),
request.getAction(), request.getRejectReason(), request.getPrice(), request.getLinkId()
request.getAction(), request.getRejectReason(), request.getPrice(), request.getLinkId(), request.getRefundModel()
);
Map<String, Object> resp = new HashMap<>();

596
src/main/java/com/example/demo/controller/coin/ExportController.java

@ -9,10 +9,7 @@ import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.entity.Export;
import com.example.demo.domain.DTO.CashDTO;
import com.example.demo.domain.vo.bean.*;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.CashRecordDTO;
import com.example.demo.domain.vo.cash.FundsDTO;
import com.example.demo.domain.vo.cash.PerformanceVO;
import com.example.demo.domain.vo.cash.*;
import com.example.demo.domain.vo.coin.*;
import com.example.demo.service.coin.ExportExcelService;
import com.example.demo.Export.ExportService;
@ -57,7 +54,7 @@ public class ExportController {
@PostMapping("/export")
public Result export(@Valid @RequestBody Export export, @RequestHeader("token") String token) throws Exception {
try {
UserDetails token1= JWTUtil.getUserDetailsList(String.valueOf(token), Admin.class);
UserDetails token1 = JWTUtil.getUserDetailsList(String.valueOf(token), Admin.class);
export.setAccount(Integer.valueOf(token1.getUsername()));
return Result.success(exportExcelService.getExcel(export));
} catch (Exception e) {
@ -75,20 +72,10 @@ public class ExportController {
String token = request.getHeader("token");
dto.setToken(token);
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertRechargeTranslatedFieldsToChinese(dto.getRechargeUser(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportRecharge(dto);
@ -107,20 +94,10 @@ public class ExportController {
String token = request.getHeader("token");
dto.setToken(token);
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertRefundTranslatedFieldsToChinese(dto.getRefundUser(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportRefund(dto);
@ -139,20 +116,10 @@ public class ExportController {
String token = request.getHeader("token");
dto.setToken(token);
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertConsumeTranslatedFieldsToChinese(dto.getConsumeUser(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportConsume(dto);
@ -168,20 +135,10 @@ public class ExportController {
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertLiveTranslatedFieldsToChinese(dto.getBeanConsumeLive(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportLive(dto);
@ -192,25 +149,15 @@ public class ExportController {
}
@PostMapping("/exportFan")
public Result export(@Valid @RequestBody FanDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang){
public Result export(@Valid @RequestBody FanDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) {
String lockKey = "export:lock:" + dto.getToken();
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertFanTranslatedFieldsToChinese(dto.getBeanConsumeFan(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportFan(dto);
@ -221,25 +168,15 @@ public class ExportController {
}
@PostMapping("/exportArticle")
public Result export(@Valid @RequestBody ArticleDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang){
public Result export(@Valid @RequestBody ArticleDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) {
String lockKey = "export:lock:" + dto.getToken();
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertArticleTranslatedFieldsToChinese(dto.getBeanConsumeArticle(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportArticle(dto);
@ -250,25 +187,15 @@ public class ExportController {
}
@PostMapping("/exportBean")
public Result export(@Valid @RequestBody BeanRechargeDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang){
public Result export(@Valid @RequestBody BeanRechargeDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) {
String lockKey = "export:lock:" + dto.getToken();
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertBeanTranslatedFieldsToChinese(dto.getBeanSystemRechargeInfo(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportBean(dto);
@ -279,25 +206,15 @@ public class ExportController {
}
@PostMapping("/exportol")
public Result exportol(@Valid @RequestBody OnlineDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) throws Exception {
public Result exportol(@Valid @RequestBody OnlineDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) {
String lockKey = "export:lock:" + dto.getToken();
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertOnlineTranslatedFieldsToChinese(dto.getBeanOnlineRechargeInfo(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportOnline(dto);
@ -316,20 +233,10 @@ public class ExportController {
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertFinanceTranslatedFieldsToChinese(dto.getCashRecordDTO(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportFinance(dto);
@ -345,24 +252,14 @@ public class ExportController {
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang); // 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果不是中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertCashTranslatedFieldsToChinese(dto.getCashCollection(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportCash(dto);
}finally {
} finally {
// 释放锁
redisLockUtil.unlock(lockKey, requestId);
}
@ -374,20 +271,10 @@ public class ExportController {
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
page.getPerformanceDTO().setLang(lang);// 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果非中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertPerformanceTranslatedFieldsToChinese(page.getPerformanceDTO().getPerformanceVO(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportPerformance(page.getPerformanceDTO());
@ -396,6 +283,7 @@ public class ExportController {
redisLockUtil.unlock(lockKey, requestId);
}
}
/**
* 资金导出
*/
@ -405,20 +293,10 @@ public class ExportController {
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang);// 设置语言参数
// 解析语言代码
String languageCode = parseLanguageCode(lang);
// 如果非中文环境将查询条件中的翻译文本转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertFundsTranslatedFieldsToChinese(dto.getFundsDTO(), languageCode);
}
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
String errorMsg = languageTranslationUtil.translate("操作太频繁,请稍后重试", lang);
throw new BusinessException(errorMsg);
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportFundDTO(dto);
@ -428,417 +306,47 @@ public class ExportController {
}
}
/**
* 解析语言代码
*/
private String parseLanguageCode(String langHeader) {
if (langHeader == null || langHeader.isEmpty()) {
return "zh";
}
// 处理类似 "en-US" "zh-TW" 的情况
if (langHeader.contains("-")) {
String[] parts = langHeader.split("-");
// 特殊处理中文繁体
if ("zh".equalsIgnoreCase(parts[0]) && "TW".equalsIgnoreCase(parts[1])) {
return "zh_TW";
}
return parts[0].toLowerCase();
}
return langHeader.toLowerCase();
}
/**
* 将充值查询条件中的翻译字段转换为中文简体
*/
private void convertRechargeTranslatedFieldsToChinese(RechargeUser rechargeUser, String languageCode) {
if (rechargeUser != null) {
// 转换市场名称
if (rechargeUser.getMarket() != null && !rechargeUser.getMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
rechargeUser.getMarket(), languageCode);
rechargeUser.setMarket(chineseMarket);
}
// 转换汇率名称
if (rechargeUser.getRateName() != null && !rechargeUser.getRateName().isEmpty()) {
String chineseRateName = translationService.findChineseSimplifiedByTranslation(
rechargeUser.getRateName(), languageCode);
rechargeUser.setRateName(chineseRateName);
}
// 转换支付方式
if (rechargeUser.getPayModel() != null && !rechargeUser.getPayModel().isEmpty()) {
String chinesePayModel = translationService.findChineseSimplifiedByTranslation(
rechargeUser.getPayModel(), languageCode);
rechargeUser.setPayModel(chinesePayModel);
}
// 转换备注
if (rechargeUser.getRemark() != null && !rechargeUser.getRemark().isEmpty()) {
String chineseRemark = translationService.findChineseSimplifiedByTranslation(
rechargeUser.getRemark(), languageCode);
rechargeUser.setRemark(chineseRemark);
}
}
}
/**
* 将退款查询条件中的翻译字段转换为中文简体
*/
private void convertRefundTranslatedFieldsToChinese(RefundUser refundUser, String languageCode) {
if (refundUser != null) {
// 转换市场名称
if (refundUser.getMarket() != null && !refundUser.getMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
refundUser.getMarket(), languageCode);
refundUser.setMarket(chineseMarket);
}
// 转换商品名称
if (refundUser.getGoodsName() != null && !refundUser.getGoodsName().isEmpty()) {
String chineseGoodsName = translationService.findChineseSimplifiedByTranslation(
refundUser.getGoodsName(), languageCode);
refundUser.setGoodsName(chineseGoodsName);
}
// 转换退款类型
if (refundUser.getRefundType() != null && !refundUser.getRefundType().isEmpty()) {
String chineseRefundType = translationService.findChineseSimplifiedByTranslation(
refundUser.getRefundType(), languageCode);
refundUser.setRefundType(chineseRefundType);
}
// 转换备注
if (refundUser.getRemark() != null && !refundUser.getRemark().isEmpty()) {
String chineseRemark = translationService.findChineseSimplifiedByTranslation(
refundUser.getRemark(), languageCode);
refundUser.setRemark(chineseRemark);
}
}
}
/**
* 将消费查询条件中的翻译字段转换为中文简体
* 用户钱包明细导出
*/
private void convertConsumeTranslatedFieldsToChinese(ConsumeUser consumeUser, String languageCode) {
if (consumeUser != null) {
// 转换商品名称
if (consumeUser.getGoodsName() != null && !consumeUser.getGoodsName().isEmpty()) {
String chineseName = translationService.findChineseSimplifiedByTranslation(
consumeUser.getGoodsName(), languageCode);
consumeUser.setGoodsName(chineseName);
}
// 转换市场名称
if (consumeUser.getMarket() != null && !consumeUser.getMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
consumeUser.getMarket(), languageCode);
consumeUser.setMarket(chineseMarket);
}
}
}
/**
* 将直播查询条件中的翻译字段转换为中文简体
*/
private void convertLiveTranslatedFieldsToChinese(BeanConsumeLive beanConsumeLive, String languageCode) {
if (beanConsumeLive != null) {
//转换地区
if (beanConsumeLive.getDept() != null && !beanConsumeLive.getDept().isEmpty()) {
String chineseDept = translationService.findChineseSimplifiedByTranslation(
beanConsumeLive.getDept(), languageCode);
beanConsumeLive.setDept(chineseDept);
}
//转换礼物名称
if (beanConsumeLive.getGift() != null && !beanConsumeLive.getGift().isEmpty()) {
String chineseGift = translationService.findChineseSimplifiedByTranslation(
beanConsumeLive.getGift(), languageCode);
beanConsumeLive.setGift(chineseGift);
}
//转换直播频道
if (beanConsumeLive.getLiveChannel() != null && !beanConsumeLive.getLiveChannel().isEmpty()) {
String chineseChannel = translationService.findChineseSimplifiedByTranslation(
beanConsumeLive.getLiveChannel(), languageCode);
beanConsumeLive.setLiveChannel(chineseChannel);
}
//转换直播名称
if (beanConsumeLive.getLiveName() != null && !beanConsumeLive.getLiveName().isEmpty()) {
String chineseName = translationService.findChineseSimplifiedByTranslation(
beanConsumeLive.getLiveName(), languageCode);
beanConsumeLive.setLiveName(chineseName);
}
}
}
/**
* 将粉丝查询条件中的翻译字段转换为中文简体
*/
private void convertFanTranslatedFieldsToChinese(BeanConsumeFan beanConsumeFan, String languageCode) {
if (beanConsumeFan != null) {
// 转换地区
if (beanConsumeFan.getDept() != null && !beanConsumeFan.getDept().isEmpty()) {
String chineseDept = translationService.findChineseSimplifiedByTranslation(
beanConsumeFan.getDept(), languageCode);
beanConsumeFan.setDept(chineseDept);
}
// 转换频道
if (beanConsumeFan.getChannel() != null && !beanConsumeFan.getChannel().isEmpty()) {
String chineseChannel = translationService.findChineseSimplifiedByTranslation(
beanConsumeFan.getChannel(), languageCode);
beanConsumeFan.setChannel(chineseChannel);
}
// 转换会员类型
if (beanConsumeFan.getTypeDesc() != null && !beanConsumeFan.getTypeDesc().isEmpty()) {
String chineseType = translationService.findChineseSimplifiedByTranslation(
beanConsumeFan.getTypeDesc(), languageCode);
beanConsumeFan.setTypeDesc(chineseType);
}
}
}
/**
* 将文章查询条件中的翻译字段转换为中文简体
*/
private void convertArticleTranslatedFieldsToChinese(BeanConsumeArticle beanConsumeArticle, String languageCode) {
if (beanConsumeArticle != null) {
// 转换文章名称
if (beanConsumeArticle.getArticleName() != null && !beanConsumeArticle.getArticleName().isEmpty()) {
String chineseName = translationService.findChineseSimplifiedByTranslation(
beanConsumeArticle.getArticleName(), languageCode);
beanConsumeArticle.setArticleName(chineseName);
}
// 转换地区
if (beanConsumeArticle.getDept() != null && !beanConsumeArticle.getDept().isEmpty()) {
String chineseDept = translationService.findChineseSimplifiedByTranslation(
beanConsumeArticle.getDept(), languageCode);
beanConsumeArticle.setDept(chineseDept);
}
// 转换类型
if (beanConsumeArticle.getTypeDesc() != null && !beanConsumeArticle.getTypeDesc().isEmpty()) {
String chineseType = translationService.findChineseSimplifiedByTranslation(
beanConsumeArticle.getTypeDesc(), languageCode);
beanConsumeArticle.setTypeDesc(chineseType);
}
}
}
/**
* 将金豆充值查询条件中的翻译字段转换为中文简体
*/
private void convertBeanTranslatedFieldsToChinese(BeanSystemRechargeInfo beanSystemRechargeInfo, String languageCode) {
if (beanSystemRechargeInfo != null) {
// 转换市场
if (beanSystemRechargeInfo.getMarket() != null && !beanSystemRechargeInfo.getMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
beanSystemRechargeInfo.getMarket(), languageCode);
beanSystemRechargeInfo.setMarket(chineseMarket);
}
// 转换备注
if (beanSystemRechargeInfo.getRemark() != null && !beanSystemRechargeInfo.getRemark().isEmpty()) {
String chineseRemark = translationService.findChineseSimplifiedByTranslation(
beanSystemRechargeInfo.getRemark(), languageCode);
beanSystemRechargeInfo.setRemark(chineseRemark);
}
}
}
/**
* 将导出查询条件中的翻译字段转换为中文简体
*/
private void convertOnlineTranslatedFieldsToChinese(BeanOnlineRechargeInfo beanOnlineRechargeInfo, String languageCode) {
if (beanOnlineRechargeInfo != null) {
// 转换市场
if (beanOnlineRechargeInfo.getMarket() != null && !beanOnlineRechargeInfo.getMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
beanOnlineRechargeInfo.getMarket(), languageCode);
beanOnlineRechargeInfo.setMarket(chineseMarket);
}
// 转换充值平台
if (beanOnlineRechargeInfo.getPlatformDesc() != null && !beanOnlineRechargeInfo.getPlatformDesc().isEmpty()) {
String chinesePlatform = translationService.findChineseSimplifiedByTranslation(
beanOnlineRechargeInfo.getPlatformDesc(), languageCode);
beanOnlineRechargeInfo.setPlatformDesc(chinesePlatform);
}
}
}
/**
* 将负责人导出查询条件中的翻译字段转换为中文简体
*/
private void convertFinanceTranslatedFieldsToChinese(CashRecordDTO cashRecordDTO, String languageCode) {
if (cashRecordDTO != null) {
// 转换商品名称
if (cashRecordDTO.getGoodsName() != null && !cashRecordDTO.getGoodsName().isEmpty()) {
String chineseGoodsName = translationService.findChineseSimplifiedByTranslation(
cashRecordDTO.getGoodsName(), languageCode);
cashRecordDTO.setGoodsName(chineseGoodsName);
}
// 转换备注
if (cashRecordDTO.getRemark() != null && !cashRecordDTO.getRemark().isEmpty()) {
String chineseRemark = translationService.findChineseSimplifiedByTranslation(
cashRecordDTO.getRemark(), languageCode);
cashRecordDTO.setRemark(chineseRemark);
}
// 转换退款理由
if (cashRecordDTO.getRefundReason() != null && !cashRecordDTO.getRefundReason().isEmpty()) {
String chineseRefundReason = translationService.findChineseSimplifiedByTranslation(
cashRecordDTO.getRefundReason(), languageCode);
cashRecordDTO.setRefundReason(chineseRefundReason);
}
// 转换驳回理由
if (cashRecordDTO.getRejectReason() != null && !cashRecordDTO.getRejectReason().isEmpty()) {
String chineseRejectReason = translationService.findChineseSimplifiedByTranslation(
cashRecordDTO.getRejectReason(), languageCode);
cashRecordDTO.setRejectReason(chineseRejectReason);
}
}
}
/**
* 将现金导出查询条件中的翻译字段转换为中文简体
*/
private void convertCashTranslatedFieldsToChinese(CashCollection cashCollection, String languageCode) {
if (cashCollection != null) {
// 转换地区
if (cashCollection.getMarket() != null && !cashCollection.getMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
cashCollection.getMarket(), languageCode);
cashCollection.setMarket(chineseMarket);
}
// 转换活动名称
if (cashCollection.getActivity() != null && !cashCollection.getActivity().isEmpty()) {
String chineseActivity = translationService.findChineseSimplifiedByTranslation(
cashCollection.getActivity(), languageCode);
cashCollection.setActivity(chineseActivity);
}
// 转换商品名称
if (cashCollection.getGoodsName() != null && !cashCollection.getGoodsName().isEmpty()) {
String chineseGoodsName = translationService.findChineseSimplifiedByTranslation(
cashCollection.getGoodsName(), languageCode);
cashCollection.setGoodsName(chineseGoodsName);
}
// 转换单位
if (cashCollection.getNumUnit() != null && !cashCollection.getNumUnit().isEmpty()) {
String chineseNumUnit = translationService.findChineseSimplifiedByTranslation(
cashCollection.getNumUnit(), languageCode);
cashCollection.setNumUnit(chineseNumUnit);
}
// 转换支付方式
if (cashCollection.getPayType() != null && !cashCollection.getPayType().isEmpty()) {
String chinesePayType = translationService.findChineseSimplifiedByTranslation(
cashCollection.getPayType(), languageCode);
cashCollection.setPayType(chinesePayType);
}
// 转换到账地区
if (cashCollection.getReceivedMarket() != null && !cashCollection.getReceivedMarket().isEmpty()) {
String chineseReceivedMarket = translationService.findChineseSimplifiedByTranslation(
cashCollection.getReceivedMarket(), languageCode);
cashCollection.setReceivedMarket(chineseReceivedMarket);
}
// 转换币种
if (cashCollection.getPaymentCurrency() != null && !cashCollection.getPaymentCurrency().isEmpty()) {
String chineseCurrency = translationService.findChineseSimplifiedByTranslation(
cashCollection.getPaymentCurrency(), languageCode);
cashCollection.setPaymentCurrency(chineseCurrency);
}
// 转换币种
if (cashCollection.getReceivedCurrency() != null && !cashCollection.getReceivedCurrency().isEmpty()) {
String chineseCurrency = translationService.findChineseSimplifiedByTranslation(
cashCollection.getReceivedCurrency(), languageCode);
cashCollection.setReceivedCurrency(chineseCurrency);
}
// 转换备注
if (cashCollection.getRemark() != null && !cashCollection.getRemark().isEmpty()) {
String chineseRemark = translationService.findChineseSimplifiedByTranslation(
cashCollection.getRemark(), languageCode);
cashCollection.setRemark(chineseRemark);
}
// 转换拒绝理由
if (cashCollection.getRejectReason() != null && !cashCollection.getRejectReason().isEmpty()) {
String chineseRejectReason = translationService.findChineseSimplifiedByTranslation(
cashCollection.getRejectReason(), languageCode);
cashCollection.setRejectReason(chineseRejectReason);
}
// 转换提交人地区
if (cashCollection.getSubmitterMarket() != null && !cashCollection.getSubmitterMarket().isEmpty()) {
String chineseMarket = translationService.findChineseSimplifiedByTranslation(
cashCollection.getSubmitterMarket(), languageCode);
cashCollection.setSubmitterMarket(chineseMarket);
@PostMapping("/exportUserWalletRecord")
public Result exportUserWalletRecord(@Valid @RequestBody UserWalletRecordDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) {
String lockKey = "export:lock:" + dto.getToken();
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang);// 设置语言参数
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportUserWalletRecord(dto);
} finally {
// 释放锁
redisLockUtil.unlock(lockKey, requestId);
}
}
/**
* 将业绩归属条件中的翻译字段转换为中文简体
*/
private void convertPerformanceTranslatedFieldsToChinese(PerformanceVO performanceVO, String languageCode) {
if (performanceVO != null) {
// 翻译地区
if (performanceVO.getMarketName() != null && !performanceVO.getMarketName().isEmpty()) {
String chineseMarketName = translationService.findChineseSimplifiedByTranslation(
performanceVO.getMarketName(), languageCode);
performanceVO.setMarketName(chineseMarketName);
}
// 翻译付款币种
if (performanceVO.getPaymentCurrency() != null && !performanceVO.getPaymentCurrency().isEmpty()) {
String chinesePaymentCurrency = translationService.findChineseSimplifiedByTranslation(
performanceVO.getPaymentCurrency(), languageCode);
performanceVO.setPaymentCurrency(chinesePaymentCurrency);
}
// 翻译到账币种
if (performanceVO.getReceivedCurrency() != null && !performanceVO.getReceivedCurrency().isEmpty()) {
String chineseReceivedCurrency = translationService.findChineseSimplifiedByTranslation(
performanceVO.getReceivedCurrency(), languageCode);
performanceVO.setReceivedCurrency(chineseReceivedCurrency);
}
}
}
/**
* 将资金流水账中的翻译字段转换为中文简体
* 用户钱包余额导出
*/
private void convertFundsTranslatedFieldsToChinese(FundsDTO fundsDTO, String languageCode) {
if (fundsDTO != null) {
// 翻译地区
if (fundsDTO.getMarketName() != null && !fundsDTO.getMarketName().isEmpty()) {
String chineseMarketName = translationService.findChineseSimplifiedByTranslation(
fundsDTO.getMarketName(), languageCode);
fundsDTO.setMarketName(chineseMarketName);
}
// 翻译付款币种
if (fundsDTO.getPaymentCurrencyName() != null && !fundsDTO.getPaymentCurrencyName().isEmpty()) {
String chinesePaymentCurrency = translationService.findChineseSimplifiedByTranslation(
fundsDTO.getPaymentCurrencyName(), languageCode);
fundsDTO.setPaymentCurrencyName(chinesePaymentCurrency);
}
// 翻译到账币种
if (fundsDTO.getReceivedCurrencyName() != null && !fundsDTO.getReceivedCurrencyName().isEmpty()) {
String chineseReceivedCurrency = translationService.findChineseSimplifiedByTranslation(
fundsDTO.getReceivedCurrencyName(), languageCode);
fundsDTO.setReceivedCurrencyName(chineseReceivedCurrency);
@PostMapping("/exportUserWallet")
public Result exportUserWallet(@Valid @RequestBody UserWalletDTO dto, @RequestHeader(defaultValue = "zh_CN") String lang) {
String lockKey = "export:lock:" + dto.getToken();
String requestId = UUID.randomUUID().toString();
long expireTime = 5000;
dto.setLang(lang);// 设置语言参数
try {
// 尝试获取锁
if (!redisLockUtil.tryLock(lockKey, requestId, expireTime)) {
throw new BusinessException("操作太频繁,请稍后重试");
}
// 执行业务逻辑
return exportService.addExportUserWallet(dto);
} finally {
// 释放锁
redisLockUtil.unlock(lockKey, requestId);
}
}
}

9
src/main/java/com/example/demo/controller/coin/GoldDetailController.java

@ -7,6 +7,7 @@ import com.example.demo.Util.RedisLockUtil;
import com.example.demo.config.interfac.Log;
import com.example.demo.domain.DTO.GoldDetailDTO;
import com.example.demo.domain.DTO.GoldUserDTO;
import com.example.demo.domain.DTO.WalletDTO;
import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.vo.coin.GoldDetail;
@ -465,6 +466,14 @@ public class GoldDetailController {
if (user.getMarket() != null) {
user.setMarket(languageTranslationUtil.translate(user.getMarket(), lang));
}
// 翻译钱包
if (user.getWalletList() != null) {
for (WalletDTO wallet : user.getWalletList()) {
if (wallet.getWalletName() != null) {
wallet.setWalletName(languageTranslationUtil.translate(wallet.getWalletName(), lang));
}
}
}
}
}
}

33
src/main/java/com/example/demo/domain/DTO/RegionWalletDTO.java

@ -0,0 +1,33 @@
package com.example.demo.domain.DTO;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
/**
* @program: gold-java
* @ClassName Region_Wallet
* @description:
* @author: Double
* @create: 202603-03 17:24
* @Version 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RegionWalletDTO {
private Integer id; //钱包id
private Integer jwcode; // 精网号
private Integer walletId; // 钱包id
private BigDecimal currentPermanentGold; // 当前永久金币
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime; // 创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date updateTime; // 更新时间
private Integer priority;
}

30
src/main/java/com/example/demo/domain/DTO/UserWalletDTO.java

@ -0,0 +1,30 @@
package com.example.demo.domain.DTO;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
// 用户钱包余额导出
@Data
public class UserWalletDTO {
private String token;
private String url = "";
private String fileName = "";
private Integer sort = 0;
private String field = "";
private Integer account;
private Integer type = 17; //类型
private Integer state = 0; //状态
private String text = ""; //关键词搜索
private Integer dataNum = 0;
private String deptid = "";
private String lang;
// 查询条件字段
private Integer jwcode;
private String market;
@NotNull(message = "page 不能为空")
private Integer page = 1;
@NotNull(message = "pageSize 不能为空")
private Integer pageSize = 20;
}

30
src/main/java/com/example/demo/domain/DTO/UserWalletRecordDTO.java

@ -0,0 +1,30 @@
package com.example.demo.domain.DTO;
import com.example.demo.domain.entity.UserWalletRecord;
import com.example.demo.domain.vo.cash.UserWalletRecordVO;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
// 用户钱包明细导出
@Data
public class UserWalletRecordDTO {
private String token;
private String url = "";
private String fileName = "";
private Integer sort = 0;
private String field = "";
private Integer account;
private Integer type = 16; //类型
private Integer state = 0; //状态
private String text = ""; //关键词搜索
private Integer dataNum = 0;
private String deptid = "";
private String lang;
private UserWalletRecordVO userWalletRecordVO;
private UserWalletRecord userWalletRecord;
@NotNull(message = "page不能为空")
private Integer page = 1;
@NotNull(message = "pageSize不能为空")
private Integer pageSize = 20;
}

26
src/main/java/com/example/demo/domain/DTO/WalletDTO.java

@ -0,0 +1,26 @@
package com.example.demo.domain.DTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @program: gold-java
* @ClassName WalletDTO
* @description:
* @author: Double
* @create: 202603-05 11:47
* @Version 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WalletDTO {
private Integer id;
private String walletName; //钱包名称
private Integer walletId;
private BigDecimal permanentGold;
}

1
src/main/java/com/example/demo/domain/entity/CashRecord.java

@ -39,6 +39,7 @@ public class CashRecord implements Serializable {
private String numUnit; // 数量单位 //
private Integer permanentGold; // 永久金币数量
private Integer freeGold; // 免费金币数量
private Integer walletId; // 钱包 ID
private String payload; //平台
// 金额信息

4
src/main/java/com/example/demo/domain/entity/User.java

@ -2,6 +2,7 @@ package com.example.demo.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.example.demo.domain.DTO.WalletDTO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
@ -69,6 +70,9 @@ public class User implements Serializable {
private Integer UserFlag;//是否员工号
@ExcelIgnore
private Set<String> roles = new HashSet<>(); // 用户角色集合
@ExcelIgnore
private List<WalletDTO> walletList; // 钱包列表
public boolean hasRole(String role) {
return roles != null && roles.contains(role);
}

28
src/main/java/com/example/demo/domain/entity/UserRegionWallet.java

@ -0,0 +1,28 @@
package com.example.demo.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
/**
* @program: gold-java
* @ClassName UserRegionWallet
* @description: 用户多地区钱包余额实体类
* @author: Hungry
* @create: 2026-03-05
* @Version 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserRegionWallet {
private Long id; // 主键 ID
private Integer jwcode; // 精网号
private Integer walletId; // 钱包 ID关联 wallet .id
private BigDecimal currentPermanentGold; // 当前永久金币
private Date createTime; // 创建时间
private Date updateTime; // 更新时间
}

23
src/main/java/com/example/demo/domain/entity/UserWalletRecord.java

@ -0,0 +1,23 @@
package com.example.demo.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 用户钱包明细实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserWalletRecord {
private Integer id; // 主键 ID
private Integer jwcode; // 精网号
private Integer walletId; // 钱包 ID
private Integer type; // 交易类型0=充值1=消耗2=退款
private Integer amount; // 交易金额
private String orderCode; // 交易单号
private String description; // 交易说明
private Integer status; // 状态0=正常1=已退款
private Date createTime; // 创建时间
}

23
src/main/java/com/example/demo/domain/entity/Wallet.java

@ -0,0 +1,23 @@
package com.example.demo.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @program: gold-java
* @ClassName Wallet
* @description:
* @author: Double
* @create: 202603-04 11:57
* @Version 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Wallet {
private Integer id; //钱包id
private String walletName; // 钱包名称
private Integer priority; // 优先级
}

2
src/main/java/com/example/demo/domain/vo/cash/CashCollection.java

@ -47,6 +47,8 @@ public class CashCollection implements Serializable {
private String numUnit; //数量单位 //
private Integer permanentGold; // 永久金币数量
private Integer freeGold; // 免费金币数量
@ExcelIgnore
private Integer walletId; // 钱包 ID
private String paymentCurrency; // 付款币种
private BigDecimal paymentAmount; // 付款金额
private String receivedCurrency; // 到账币种

3
src/main/java/com/example/demo/domain/vo/cash/CashRecordRefund.java

@ -203,5 +203,8 @@ public class CashRecordRefund {
private Integer partRefundGold;
private Integer partRefundFree;
private BigDecimal handlingCharge;
private String payType;
private String receivedMarket;
private Integer walletId;
}

37
src/main/java/com/example/demo/domain/vo/cash/UserWalletRecordVO.java

@ -0,0 +1,37 @@
package com.example.demo.domain.vo.cash;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 用户钱包明细 VO包含用户信息
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserWalletRecordVO {
@ExcelIgnore
private Integer id; // 明细 ID
private Integer jwcode; // 精网号
private String userName; // 用户名
@ExcelIgnore
private String market; // 地区 ID
private String marketName; // 地区名称
@ExcelIgnore
private Integer walletId; // 钱包 ID
private String walletName; // 钱包名称
@ExcelIgnore
private Integer type; // 交易类型0=充值1=消耗2=退款
private String typeText; // 交易类型文本多语言
private Integer amount; // 交易金额
private String orderCode; // 交易单号
private String description; // 交易说明
@ExcelIgnore
private Integer status; // 状态0=正常1=已退款
private String statusText; // 状态文本多语言
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime; // 创建时间
}

29
src/main/java/com/example/demo/domain/vo/cash/UserWalletVO.java

@ -0,0 +1,29 @@
package com.example.demo.domain.vo.cash;
import com.alibaba.excel.annotation.ExcelIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
// 用户钱包 VO包含用户名地区和所有钱包列表
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserWalletVO {
private Integer jwcode; // 精网号
private String userName; // 用户名
@ExcelIgnore
private String market; // 地区代码
private String marketName; // 地区名称
// 用于扁平化导出的字段
@ExcelIgnore
private Integer walletId; // 钱包 ID
private String walletName; // 钱包名称
private BigDecimal currentPermanentGold; // 当前永久金币数量
private List<WalletItem> walletList; // 钱包列表仅用于查询结果
}

17
src/main/java/com/example/demo/domain/vo/cash/WalletItem.java

@ -0,0 +1,17 @@
package com.example.demo.domain.vo.cash;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
// 钱包项单个钱包信息
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WalletItem {
private Integer walletId; // 钱包 ID
private String walletName; // 钱包名称
private BigDecimal currentPermanentGold; // 当前永久金币数量
}

1
src/main/java/com/example/demo/domain/vo/coin/AuditRequest.java

@ -19,5 +19,6 @@ public class AuditRequest {
private String sortOrder; //排序顺序
private BigDecimal price; //原价
private String linkId;//标识
private Integer refundModel;
}

5
src/main/java/com/example/demo/domain/vo/coin/Page.java

@ -2,7 +2,9 @@ package com.example.demo.domain.vo.coin;
import com.example.demo.domain.DTO.BeanConsumeCartDTO;
import com.example.demo.domain.DTO.PerformanceDTO;
import com.example.demo.domain.DTO.UserWalletRecordDTO;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.UserWalletRecord;
import com.example.demo.domain.vo.bean.*;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.CashRecordDTO;
@ -56,5 +58,6 @@ public class Page {
private PerformanceDTO performanceDTO;//业绩归属
private FundsDTO fundsDTO;//现金收款
private PerformanceVO performanceVO;//业绩归属
private UserWalletRecordDTO UserWalletRecordDTO; //用户钱包明细查询条件
private UserWalletRecord userWalletRecord; //用户钱包明细查询条件
}

35
src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java

@ -2,11 +2,11 @@ package com.example.demo.mapper.cash;
//import com.example.demo.domain.DTO.PaymentDTO;
import com.example.demo.domain.DTO.*;
import com.example.demo.domain.entity.CashRecord;
import com.example.demo.domain.entity.GOrder;
import com.example.demo.domain.entity.RechargeActivity;
import com.example.demo.domain.entity.*;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.PerformanceVO;
import com.example.demo.domain.vo.cash.UserWalletRecordVO;
import com.example.demo.domain.vo.cash.UserWalletVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -31,7 +31,7 @@ public interface CashCollectionMapper {
String getMarketByJwcode(@Param("jwcode") Integer jwcode);
//新增收款订单
void add(CashRecord cashRecord);
//根据订单号获取订单id状态
//根据订单号获取订单id状态钱包id永久金币数量精网号和备注
CashRecord selectByOrderCode(@Param("orderCode") String orderCode);
//更新订单状态
int updateStatus(@Param("orderCode") String orderCode,
@ -98,4 +98,31 @@ public interface CashCollectionMapper {
@Param("type") Integer type,
@Param("flag") Integer flag);
// 根据 jwcode walletId 查询用户钱包
UserRegionWallet selectUserWallet(@Param("jwcode") Integer jwcode,
@Param("walletId") Integer walletId);
// 增加用户钱包永久金币
int addUserWalletPermanentGold(@Param("jwcode") Integer jwcode,
@Param("walletId") Integer walletId,
@Param("amount") Integer amount);
// 插入或更新用户钱包记录
void insertUserWallet(UserRegionWallet userRegionWallet);
// 插入用户钱包明细记录
void insertUserWalletRecord(UserWalletRecord userWalletRecord);
// 根据精网号和钱包 ID 查询用户钱包明细列表
List<UserWalletRecordVO> selectWalletRecordsByJwcodeAndWalletId(
@Param("jwcode") Integer jwcode,
@Param("walletId") Integer walletId);
// 查询符合条件的精网号列表用于分页每个精网号算一条记录
List<Integer> selectDistinctJwcodes(@Param("jwcode") Integer jwcode,
@Param("market") String market);
// 根据精网号列表查询用户的所有钱包信息
List<UserWalletVO> selectUserWalletsByJwcodes(@Param("jwcodeList") List<Integer> jwcodeList,
@Param("market") String market);
}

37
src/main/java/com/example/demo/mapper/cash/UserRegionWalletMapper.java

@ -0,0 +1,37 @@
package com.example.demo.mapper.cash;
import com.example.demo.domain.entity.UserRegionWallet;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
/**
* @program: gold-java
* @ClassName UserRegionWalletMapper
* @description: 用户钱包 Mapper 接口
* @author: Ethan
* @create: 2026-03-05
* @Version 1.0
**/
@Mapper
public interface UserRegionWalletMapper {
/**
* 根据 jwcode walletId 查询用户钱包
*/
UserRegionWallet selectByJwcodeAndWalletId(@Param("jwcode") Integer jwcode,
@Param("walletId") Integer walletId);
/**
* 插入或更新用户钱包余额
*/
int insertOrUpdate(UserRegionWallet userRegionWallet);
/**
* 增加用户钱包永久金币
*/
int addPermanentGold(@Param("jwcode") Integer jwcode,
@Param("walletId") Integer walletId,
@Param("amount") BigDecimal amount);
}

11
src/main/java/com/example/demo/mapper/coin/ConsumeMapper.java

@ -1,6 +1,10 @@
package com.example.demo.mapper.coin;
import com.example.demo.domain.DTO.RegionWalletDTO;
import com.example.demo.domain.DTO.WalletDTO;
import com.example.demo.domain.entity.UserGoldRecord;
import com.example.demo.domain.entity.UserWalletRecord;
import com.example.demo.domain.entity.Wallet;
import com.example.demo.domain.vo.coin.ConsumeUser;
import com.example.demo.domain.vo.coin.Gold;
import com.example.demo.domain.vo.coin.RefundUser;
@ -44,4 +48,11 @@ public interface ConsumeMapper {
void updateUserGold(UserGoldRecord userGoldRecord);
Gold sumGold(ConsumeUser consumeUser);
//查询钱包
List<RegionWalletDTO> selectRegionWalletByJwcode(Integer jwcode);
List<Wallet> selectWallet();
void createRegionWallet(Integer jwcode);
void updateRegionWallet(WalletDTO wallet);
void addRegionWalletRecord(UserWalletRecord userWalletRecord);
}

2
src/main/java/com/example/demo/mapper/coin/GoldDetailMapper.java

@ -1,5 +1,6 @@
package com.example.demo.mapper.coin;
import com.example.demo.domain.DTO.WalletDTO;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.vo.coin.Gold;
import com.example.demo.domain.vo.coin.GoldDetail;
@ -46,4 +47,5 @@ public interface GoldDetailMapper {
@Param("fileName") String fileName,
@Param("dataNum") Integer dataNum
);
List<WalletDTO> getWalletDTOList(String jwcode);
}

34
src/main/java/com/example/demo/mapper/coin/WalletMapper.java

@ -0,0 +1,34 @@
package com.example.demo.mapper.coin;
import com.example.demo.domain.entity.GOrder;
import com.example.demo.domain.entity.UserRegionWallet;
import com.example.demo.domain.entity.UserWalletRecord;
import com.example.demo.domain.entity.Wallet;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @program: GOLD
* @ClassName WalletMapper
* @description:
* @author: huangqizhen
* @create: 202603-06 13:56
* @Version 1.0
**/
@Mapper
public interface WalletMapper {
void updateWallet(UserRegionWallet userRegionWallet);
UserRegionWallet selectWallet(Integer jwcode, Integer walletId);
List<UserWalletRecord> selectWalletRecord(Integer jwcode, String orderCode);
void updateWalletRecord(Integer id);
GOrder MysqlConnection(String linkId);
// 查询所有钱包类型
List<Wallet> selectAllWallets();
void addUserWalletRecord(UserWalletRecord userWalletRecord);
void insert(UserRegionWallet userRegionWallet);
List<UserRegionWallet> selectWalletsByJwcodeAndIds(Integer jwcode, List<Integer> walletIds);
}

24
src/main/java/com/example/demo/service/Wallet/WalletService.java

@ -0,0 +1,24 @@
package com.example.demo.service.Wallet;
import com.example.demo.domain.entity.*;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @program: GOLD
* @ClassName WalletService
* @description:
* @author: huangqizhen
* @create: 202603-05 14:00
* @Version 1.0
**/
public interface WalletService {
void updateUserGoldRecord(UserRegionWallet userRegionWallet);
List<UserWalletRecord> selectUserWalletRecord(Integer jwcode, String orderCode);
void updateUserWalletRecord(Integer id);
GOrder MysqlConnection(String linkId);
void addUserWalletRecord(UserWalletRecord userWalletRecord);
// 查询所有钱包类型
List<Wallet> selectAllWallets();
}

8
src/main/java/com/example/demo/service/cash/CashCollectionService.java

@ -6,6 +6,8 @@ import com.example.demo.domain.entity.GOrder;
import com.example.demo.domain.entity.RechargeActivity;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.PerformanceVO;
import com.example.demo.domain.vo.cash.UserWalletRecordVO;
import com.example.demo.domain.vo.cash.UserWalletVO;
import com.example.demo.domain.vo.coin.Result;
import com.github.pagehelper.PageInfo;
@ -48,4 +50,10 @@ public interface CashCollectionService {
PageInfo<PerformanceVO> performanceSelect(Integer pageNum, Integer pageSize, PerformanceDTO performanceDTO);
// 根据精网号和钱包 ID 查询用户钱包明细列表分页
PageInfo<UserWalletRecordVO> selectWalletRecordsByJwcodeAndWalletId(
Integer pageNum, Integer pageSize, Integer jwcode, Integer walletId);
// 根据精网号和地区查询用户的所有钱包 ID 和金币数量包含用户名和地区分页
PageInfo<UserWalletVO> selectUserWallets(Integer jwcode, String market, Integer pageNum, Integer pageSize);
}

2
src/main/java/com/example/demo/service/cash/RefundService.java

@ -36,7 +36,7 @@ public interface RefundService {
PageInfo<CashRecordDTO> exSelect(Integer pageNum, Integer pageSize, CashRecordDTO cashRecordDTO);
//新增线上退款记录
void addOnline(CashRecordRefund cashRecordRefund);
void addOnline(CashRecordRefund cashRecordRefund, String lang);
PageInfo<FundsDTO> funds(Integer pageNum, Integer pageSize, FundsDTO fundsDTO);
}

2
src/main/java/com/example/demo/service/coin/AuditService.java

@ -19,7 +19,7 @@ import java.math.BigDecimal;
public interface AuditService {
//审核订单并修改用户余额等
boolean auditOrder(String token, String orderCode, Integer auditId, Integer action, String rejectReason, BigDecimal price, String linkId) throws Exception;
boolean auditOrder(String token, String orderCode, Integer auditId, Integer action, String rejectReason, BigDecimal price, String linkId, Integer refundModel) throws Exception;
//多条件查询充值审核订单
PageInfo<RechargeAudit> selectRechargeBy(Integer pageNum, Integer pageSize, RechargeAudit rechargeAudit);
//多条件查询退款审核订单

2
src/main/java/com/example/demo/service/coin/ExportExcelService.java

@ -27,6 +27,8 @@ public interface ExportExcelService {
Exception FanExcel(String message) throws Exception;
Exception ArticleExcel(String message) throws Exception;
Exception PerformanceExcel(String message) throws Exception;
Exception UserWalletRecordExcel(String message) throws Exception;
Exception UserWalletExcel(String message) throws Exception;
List<Export> getExcel(Export export);
Exception BeanExcel(String message) throws Exception;

27
src/main/java/com/example/demo/service/listen/UserWalletListener.java

@ -0,0 +1,27 @@
package com.example.demo.service.listen;
import com.example.demo.Util.RedisUtil;
import com.example.demo.service.coin.ExportExcelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserWalletListener extends BaseMessageListener<String>{
@Autowired
private ExportExcelService exportExcelService;
protected UserWalletListener(RedisUtil redisQueueUtil) {
super(redisQueueUtil,"user_wallet:queue:export_queue");
}
@Override
protected void handleMessage(String message) {
validateMessage( message);
try {
Thread.sleep(5000);
exportExcelService.UserWalletExcel(message);
} catch (Exception e) {
handleException(e, message);
}
}
}

27
src/main/java/com/example/demo/service/listen/UserWalletRecordListener.java

@ -0,0 +1,27 @@
package com.example.demo.service.listen;
import com.example.demo.Util.RedisUtil;
import com.example.demo.service.coin.ExportExcelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserWalletRecordListener extends BaseMessageListener<String>{
@Autowired
private ExportExcelService exportExcelService;
protected UserWalletRecordListener(RedisUtil redisQueueUtil) {
super(redisQueueUtil,"user_wallet_record:queue:export_queue");
}
@Override
protected void handleMessage(String message) {
validateMessage( message);
try {
Thread.sleep(5000);
exportExcelService.UserWalletRecordExcel(message);
} catch (Exception e) {
handleException(e, message);
}
}
}

82
src/main/java/com/example/demo/serviceImpl/Wallet/WalletServiceImpl.java

@ -0,0 +1,82 @@
package com.example.demo.serviceImpl.Wallet;
import com.example.demo.Util.BusinessException;
import com.example.demo.domain.entity.*;
import com.example.demo.exception.SystemException;
import com.example.demo.mapper.coin.WalletMapper;
import com.example.demo.service.Wallet.WalletService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
/**
* @program: GOLD
* @ClassName WalletServiceImpl
* @description:
* @author: huangqizhen
* @create: 202603-06 13:53
* @Version 1.0
**/
@Service
@RequiredArgsConstructor
public class WalletServiceImpl implements WalletService {
@Autowired
private WalletMapper walletMapper;
@Override
public void updateUserGoldRecord(UserRegionWallet userRegionWallet) {
BigDecimal currentPermanentGold = userRegionWallet.getCurrentPermanentGold();
if (currentPermanentGold == null)
throw new SystemException("钱包金币传参错误");
if (userRegionWallet.getJwcode()!= null|| userRegionWallet.getWalletId() != null){
UserRegionWallet wallet = walletMapper.selectWallet(userRegionWallet.getJwcode(), userRegionWallet.getWalletId());
if (wallet == null){
throw new BusinessException("该用户钱包不存在");
}
else {
if (currentPermanentGold.compareTo(BigDecimal.ZERO) < 0 && wallet.getCurrentPermanentGold().compareTo(currentPermanentGold) < 0){
throw new BusinessException("用户钱包金币不足");
}
userRegionWallet.setCurrentPermanentGold(wallet.getCurrentPermanentGold().add(currentPermanentGold));
walletMapper.updateWallet(userRegionWallet);
}
}
}
@Override
public List<UserWalletRecord> selectUserWalletRecord(Integer jwcode, String orderCode) {
if (jwcode == null){
throw new SystemException("精网号传参错误");
}
return walletMapper.selectWalletRecord(jwcode, orderCode);
}
@Override
public void updateUserWalletRecord(Integer id) {
walletMapper.updateWalletRecord(id);
}
@Override
public GOrder MysqlConnection(String linkId) {
if (linkId == null)
throw new SystemException("连接ID传参错误");
GOrder gOrder = walletMapper.MysqlConnection(linkId);
if (gOrder == null)
throw new BusinessException("该连接ID不存在");
return gOrder;
}
@Override
public List<Wallet> selectAllWallets() {
return walletMapper.selectAllWallets();
}
@Override
public void addUserWalletRecord(UserWalletRecord userWalletRecord) {
walletMapper.addUserWalletRecord(userWalletRecord);
}
}

76
src/main/java/com/example/demo/serviceImpl/cash/CashAuditServiceImpl.java

@ -3,9 +3,7 @@ package com.example.demo.serviceImpl.cash;
import com.example.demo.Util.GoldTistV2;
import com.example.demo.Util.SimpleIdGenerator;
import com.example.demo.config.RabbitMQConfig;
import com.example.demo.domain.entity.CashRecord;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.UserGoldRecord;
import com.example.demo.domain.entity.*;
import com.example.demo.domain.vo.cash.CashCollectionMessage;
import com.example.demo.domain.vo.coin.Messages;
import com.example.demo.mapper.cash.CashAuditMapper;
@ -14,6 +12,7 @@ import com.example.demo.mapper.coin.AuditMapper;
import com.example.demo.mapper.coin.MarketMapper;
import com.example.demo.mapper.coin.RechargeMapper;
import com.example.demo.service.cash.CashAuditService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -36,6 +35,7 @@ import static net.sf.jsqlparser.parser.feature.Feature.update;
* @Version 1.0
**/
@Service
@Slf4j
public class CashAuditServiceImpl implements CashAuditService {
@Autowired
private AuditMapper auditMapper;
@ -83,11 +83,11 @@ public class CashAuditServiceImpl implements CashAuditService {
int currentMonth = LocalDate.now().getMonthValue();
// 根据当前月份设置对应字段
if (currentMonth >= 1 && currentMonth <= 6) {
// 1-6月设置6月额度12月保持默认值
// 1-6 设置 6 月额度12 月保持默认值
rechargeOrder.setFreeJune(order.getFreeGold());
rechargeOrder.setFreeDecember(0);
} else {
// 7-12月设置12月额度6月保持默认值
// 7-12 设置 12 月额度6 月保持默认值
rechargeOrder.setFreeJune(0);
rechargeOrder.setFreeDecember(order.getFreeGold());
}
@ -98,7 +98,7 @@ public class CashAuditServiceImpl implements CashAuditService {
rechargeOrder.setVoucher(order.getVoucher());
rechargeOrder.setPayPlatform("金币系统");
rechargeOrder.setPayModel(order.getPayType());
//把时间转换成Date
//把时间转换成 Date
Date date = Date.from(order.getPayTime().toInstant(ZoneOffset.ofHours(8)));
rechargeOrder.setPayTime(date);
rechargeOrder.setAdminId(order.getSubmitterId());
@ -110,7 +110,55 @@ public class CashAuditServiceImpl implements CashAuditService {
rechargeOrder.setLinkId(order.getLinkId());
rechargeMapper.add(rechargeOrder);
//往ERP加一条充值数据
// 先从数据库中获取订单的 walletId
CashRecord dbRecord = cashCollectionMapper.selectByOrderCode(orderCode);
Integer walletId = dbRecord != null ? dbRecord.getWalletId() : null;
// 充值到指定钱包
if (walletId != null && order.getPermanentGold() > 0) {
try {
// 尝试更新用户钱包余额
int updateRows = cashCollectionMapper.addUserWalletPermanentGold(
order.getJwcode(),
walletId,
order.getPermanentGold()
);
// 如果没有更新任何行说明记录不存在需要插入新记录
if (updateRows == 0) {
UserRegionWallet wallet = new UserRegionWallet();
wallet.setJwcode(order.getJwcode());
wallet.setWalletId(walletId);
wallet.setCurrentPermanentGold(new BigDecimal(order.getPermanentGold()));
cashCollectionMapper.insertUserWallet(wallet);
log.info("创建新的钱包记录:jwcode={}, walletId={}, permanentGold={}",
order.getJwcode(), walletId, order.getPermanentGold());
} else {
log.info("充值成功:jwcode={}, walletId={}, 增加金币={}",
order.getJwcode(), walletId, order.getPermanentGold());
}
// 创建钱包明细记录
UserWalletRecord walletRecord = new UserWalletRecord();
walletRecord.setJwcode(order.getJwcode());
walletRecord.setWalletId(walletId);
walletRecord.setType(0); // 0=充值
walletRecord.setAmount(order.getPermanentGold());
walletRecord.setOrderCode(orderCode);
walletRecord.setDescription(order.getPayType());
walletRecord.setStatus(0); // 0=正常
cashCollectionMapper.insertUserWalletRecord(walletRecord);
log.info("创建钱包明细记录:jwcode={}, walletId={}, orderCode={}, amount={}",
order.getJwcode(), walletId, orderCode, order.getPermanentGold());
} catch (Exception e) {
log.error("充值钱包失败:jwcode={}, walletId={}, error={}",
order.getJwcode(), walletId, e.getMessage(), e);
// 不抛出异常避免影响主流程但记录错误日志
}
}
// ERP 加一条充值数据
GoldTistV2.addCoinNew(order.getJwcode().toString(), 64, //充值永久金币
(double) (order.getPermanentGold()) / 100,order.getLinkId(),
order.getRemark(), (double) (order.getPermanentGold()) / 100, auditName, "金币充值");
@ -123,12 +171,12 @@ public class CashAuditServiceImpl implements CashAuditService {
user.setSumConsumePermanent(BigDecimal.valueOf(order.getPermanentGold()));
// 根据当前月份设置对应字段
if (currentMonth >= 1 && currentMonth <= 6) {
// 1-6月设置12月额度6月保持默认值
// 1-6 设置 12 月额度6 月保持默认值
user.setSumFreeJune(BigDecimal.valueOf(0));
user.setSumFreeDecember(BigDecimal.valueOf(order.getFreeGold()));
user.setCurrentFreeDecember(BigDecimal.valueOf(order.getFreeGold()));
} else {
// 7-12月设置6月额度12月保持默认值
// 7-12 设置 6 月额度12 月保持默认值
user.setSumFreeJune(BigDecimal.valueOf(order.getFreeGold()));
user.setCurrentFreeJune(BigDecimal.valueOf(order.getFreeGold()));
user.setSumFreeDecember(BigDecimal.valueOf(0));
@ -148,11 +196,11 @@ public class CashAuditServiceImpl implements CashAuditService {
int currentMonth = LocalDate.now().getMonthValue();
// 根据当前月份设置对应字段
if (currentMonth >= 1 && currentMonth <= 6) {
// 1-6月设置6月额度12月保持默认值
// 1-6 设置 6 月额度12 月保持默认值
rechargeOrder.setFreeJune(order.getFreeGold());
rechargeOrder.setFreeDecember(0);
} else {
// 7-12月设置12月额度6月保持默认值
// 7-12 设置 12 月额度6 月保持默认值
rechargeOrder.setFreeJune(0);
rechargeOrder.setFreeDecember(order.getFreeGold());
}
@ -165,7 +213,7 @@ public class CashAuditServiceImpl implements CashAuditService {
rechargeOrder.setAuditStatus(1);
rechargeOrder.setCreateTime(new Date());
rechargeMapper.add(rechargeOrder);
//往ERP加一条充值数据
// ERP 加一条充值数据
GoldTistV2.addCoinNew(order.getJwcode().toString(), 63, //充值免费
(double) (order.getFreeGold()) / 100,
order.getRemark(), SimpleIdGenerator.generateId(),0, auditName, "金币充值");
@ -175,12 +223,12 @@ public class CashAuditServiceImpl implements CashAuditService {
user.setSumConsumePermanent(BigDecimal.valueOf(0));
// 根据当前月份设置对应字段
if (currentMonth >= 1 && currentMonth <= 6) {
// 1-6月设置6月额度12月保持默认值
// 1-6 设置 6 月额度12 月保持默认值
user.setSumFreeJune(BigDecimal.valueOf(order.getFreeGold()));
user.setSumFreeJune(BigDecimal.valueOf(order.getFreeGold()));
user.setCurrentFreeDecember(BigDecimal.valueOf(0));
} else {
// 7-12月设置12月额度6月保持默认值
// 7-12 设置 12 月额度6 月保持默认值
user.setSumFreeJune(BigDecimal.valueOf(0));
user.setSumFreeDecember(BigDecimal.valueOf(order.getFreeGold()));
user.setSumFreeDecember(BigDecimal.valueOf(order.getFreeGold()));

142
src/main/java/com/example/demo/serviceImpl/cash/CashCollectionServiceImpl.java

@ -5,9 +5,7 @@ import com.example.demo.Util.LanguageTranslationUtil;
import com.example.demo.config.RabbitMQConfig;
import com.example.demo.domain.DTO.PerformanceDTO;
import com.example.demo.domain.entity.*;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.CashCollectionMessage;
import com.example.demo.domain.vo.cash.PerformanceVO;
import com.example.demo.domain.vo.cash.*;
import com.example.demo.domain.vo.coin.GoldUser;
import com.example.demo.domain.vo.coin.Messages;
import com.example.demo.domain.vo.coin.Result;
@ -31,10 +29,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -67,7 +62,7 @@ public class CashCollectionServiceImpl implements CashCollectionService {
throw new IllegalArgumentException("精网号不能为空");
}
if (cashCollection.getJwcode() < 10000000 || cashCollection.getJwcode() > 99999999) {
throw new IllegalArgumentException("精网号必须为8位");
throw new IllegalArgumentException("精网号必须为 8 位");
}
if (cashCollection.getName() == null || cashCollection.getName().isEmpty()){
throw new IllegalArgumentException("客户姓名不能为空");
@ -82,6 +77,9 @@ public class CashCollectionServiceImpl implements CashCollectionService {
if (cashCollection.getPermanentGold() == 0 && cashCollection.getFreeGold() == 0) {
throw new IllegalArgumentException("金币数量不能为空");
}
if (cashCollection.getWalletId() == null || cashCollection.getWalletId() < 1 || cashCollection.getWalletId() > 10) {
throw new IllegalArgumentException("钱包 ID 为 1~10");
}
}
if (!cashCollection.getGoodsName().equals("金币充值")) {
if (cashCollection.getGoodNum() == 0) {
@ -106,6 +104,10 @@ public class CashCollectionServiceImpl implements CashCollectionService {
if (cashCollection.getPayTime() == null) {
throw new IllegalArgumentException("付款时间不能为空");
}
// 校验钱包 ID 和到账地区的对应关系
validateWalletAndMarket(cashCollection.getWalletId(), cashCollection.getReceivedMarket());
//生成订单号后半部分
String orderNumber = UUID.randomUUID().toString().replaceAll("-", "");
CashRecord cashRecord = new CashRecord();
@ -119,6 +121,7 @@ public class CashCollectionServiceImpl implements CashCollectionService {
cashRecord.setNumUnit(cashCollection.getNumUnit()); //数量单位
cashRecord.setPermanentGold(cashCollection.getPermanentGold()); //永久金币
cashRecord.setFreeGold(cashCollection.getFreeGold()); //免费金币
cashRecord.setWalletId(cashCollection.getWalletId()); // 钱包 ID
cashRecord.setPaymentCurrency(cashCollection.getPaymentCurrency()); //付款币种
cashRecord.setPaymentAmount(cashCollection.getPaymentAmount()); //付款金额
cashRecord.setReceivedMarket(cashCollection.getReceivedMarket()); //到账地区
@ -127,11 +130,11 @@ public class CashCollectionServiceImpl implements CashCollectionService {
cashRecord.setVoucher(cashCollection.getVoucher()); //转账凭证
cashRecord.setRemark(cashCollection.getRemark()); //备注
cashRecord.setStatus(0); //订单状态付款线下财务待审核
cashRecord.setSubmitterId(cashCollection.getSubmitterId()); //提交人ID
cashRecord.setSubmitterId(cashCollection.getSubmitterId()); //提交人 ID
cashRecord.setSubmitterMarket(cashCollection.getSubmitterMarket());
cashRecord.setOrderType(1); //订单类型1-收款
cashRecord.setMarket(cashCollection.getMarket());
//地区根据jwcode插入
//地区根据 jwcode 插入
//cashRecord.setMarket(cashCollectionMapper.getMarketByJwcode(cashRecord.getJwcode()));
//插入新收款订单
cashCollectionMapper.add(cashRecord);
@ -249,7 +252,7 @@ public class CashCollectionServiceImpl implements CashCollectionService {
throw new IllegalArgumentException("精网号不能为空");
}
if (cashRecord.getJwcode() < 10000000 || cashRecord.getJwcode() > 99999999) {
throw new IllegalArgumentException("精网号必须为8位");
throw new IllegalArgumentException("精网号必须为 8 位");
}
if (cashRecord.getName() == null) {
throw new IllegalArgumentException("客户姓名不能为空");
@ -266,7 +269,9 @@ public class CashCollectionServiceImpl implements CashCollectionService {
}if (cashRecord.getPermanentGold() == 0){
throw new IllegalArgumentException("永久金币数量不能为空");
}
if (cashRecord.getWalletId() == null) {
throw new IllegalArgumentException("钱包 ID 不能为空");
}
}
if (!cashRecord.getGoodsName().equals("金币充值")) {
if (cashRecord.getGoodNum() == 0) {
@ -292,11 +297,14 @@ public class CashCollectionServiceImpl implements CashCollectionService {
throw new IllegalArgumentException("付款时间不能为空");
}
// 校验钱包 ID 和到账地区的对应关系
validateWalletAndMarket(cashRecord.getWalletId(), cashRecord.getReceivedMarket());
CashRecord status = cashCollectionMapper.selectByOrderCode(cashRecord.getOrderCode());
if (!status.getStatus().equals(5)) {
throw new IllegalArgumentException("只允许编辑已撤回订单");
}
//地区根据jwcode插入弃用插入前调用接口获取地区和姓名之后前端传入
//地区根据 jwcode 插入弃用插入前调用接口获取地区和姓名之后前端传入
//cashRecord.setMarket(cashCollectionMapper.getMarketByJwcode(cashRecord.getJwcode()));
int rows = cashCollectionMapper.updateByOrderCode(cashRecord);
if (rows > 0) {
@ -371,6 +379,7 @@ public class CashCollectionServiceImpl implements CashCollectionService {
//补全手续费等内容
@Override
@Transactional(rollbackFor = Exception.class)
public String complete(CashRecord cashRecord) {
@ -381,8 +390,7 @@ public class CashCollectionServiceImpl implements CashCollectionService {
int rows = cashCollectionMapper.complete(cashRecord);
String goodsName = cashCollectionMapper.selectGoodsNameByCode(cashRecord.getOrderCode());
if (goodsName .equals("金币充值")) {
if (goodsName != null && goodsName.equals("金币充值")) {
cashRecord.setOrderCode(cashRecord.getOrderCode().replace("XJ_", "XJCZ_"));
//修改金币订单
cashCollectionMapper.updateGoldOrder(cashRecord);
@ -595,5 +603,109 @@ public class CashCollectionServiceImpl implements CashCollectionService {
return new PageInfo<>(performanceVOs);
}
// 根据精网号和钱包 ID 查询用户钱包明细列表分页
@Override
public PageInfo<UserWalletRecordVO> selectWalletRecordsByJwcodeAndWalletId(
Integer pageNum, Integer pageSize, Integer jwcode, Integer walletId) {
PageHelper.startPage(pageNum, pageSize);
List<UserWalletRecordVO> records = cashCollectionMapper.selectWalletRecordsByJwcodeAndWalletId(jwcode, walletId);
return new PageInfo<>(records);
}
// 根据精网号和地区查询用户的所有钱包 ID 和金币数量包含用户名和地区分页
@Override
public PageInfo<UserWalletVO> selectUserWallets(Integer jwcode, String market, Integer pageNum, Integer pageSize) {
// 第一步先查询符合条件的精网号列表分页
PageHelper.startPage(pageNum, pageSize);
List<Integer> jwcodeList = cashCollectionMapper.selectDistinctJwcodes(jwcode, market);
PageInfo<Integer> jwcodePageInfo = new PageInfo<>(jwcodeList);
// 如果没有符合条件的记录直接返回空结果
if (jwcodeList == null || jwcodeList.isEmpty()) {
PageInfo<UserWalletVO> emptyResult = new PageInfo<>();
emptyResult.setList(new ArrayList<>());
emptyResult.setTotal(0);
emptyResult.setPages(0);
emptyResult.setPageNum(pageNum);
emptyResult.setPageSize(pageSize);
return emptyResult;
}
// 第二步根据精网号列表查询用户的钱包信息不分页返回这些精网号的所有钱包
List<UserWalletVO> allWallets = cashCollectionMapper.selectUserWalletsByJwcodes(jwcodeList, market);
// 第三步将钱包信息按精网号分组组装
Map<Integer, UserWalletVO> userWalletMap = new LinkedHashMap<>();
for (UserWalletVO wallet : allWallets) {
Integer key = wallet.getJwcode();
if (!userWalletMap.containsKey(key)) {
UserWalletVO userWallet = new UserWalletVO();
userWallet.setJwcode(wallet.getJwcode());
userWallet.setUserName(wallet.getUserName());
userWallet.setMarket(wallet.getMarket());
userWallet.setMarketName(wallet.getMarketName());
userWallet.setWalletList(new ArrayList<>());
userWalletMap.put(key, userWallet);
}
// 添加钱包明细
if (wallet.getWalletList() != null) {
userWalletMap.get(key).getWalletList().addAll(wallet.getWalletList());
}
}
// 第四步按照精网号列表的顺序构建最终结果
List<UserWalletVO> result = new ArrayList<>();
for (Integer jwc : jwcodeList) {
UserWalletVO userWallet = userWalletMap.get(jwc);
if (userWallet != null) {
result.add(userWallet);
}
}
// 第五步构建并返回 PageInfo
PageInfo<UserWalletVO> resultPageInfo = new PageInfo<>(result);
resultPageInfo.setTotal(jwcodePageInfo.getTotal());
resultPageInfo.setPages(jwcodePageInfo.getPages());
resultPageInfo.setPageNum(pageNum);
resultPageInfo.setPageSize(pageSize);
return resultPageInfo;
}
/**
* 校验钱包 ID 和到账地区的对应关系
* @param walletId 钱包 ID
* @param receivedMarket 到账地区 ID
*/
private void validateWalletAndMarket(Integer walletId, String receivedMarket) {
if (walletId == null) {
return; // 非金币充值不需要校验
}
Map<Integer, String> walletMarketMap = new HashMap<>();
walletMarketMap.put(2, "13"); // 香港
walletMarketMap.put(3, "4"); // 新加坡 HC
walletMarketMap.put(4, "5"); // 马来西亚
walletMarketMap.put(5, "4"); // 新加坡 CM
walletMarketMap.put(6, "24016"); // 加拿大
walletMarketMap.put(7, "24018"); // 泰国 HS
walletMarketMap.put(8, "24018"); // 泰国 HA
walletMarketMap.put(9, "24022"); // 越南 HCM
walletMarketMap.put(10, "24033");// 北京
// 钱包 ID=1 为历史钱包无限制不需要校验
if (walletId == 1) {
return;
}
String expectedMarket = walletMarketMap.get(walletId);
if (expectedMarket == null) {
throw new IllegalArgumentException("无效的钱包 ID: " + walletId);
}
if (!expectedMarket.equals(receivedMarket)) {
String marketName = marketMapper.getMarketNameById(expectedMarket);
throw new IllegalArgumentException("钱包 ID=" + walletId + " 对应的到账地区应为:" + marketName + "(" + expectedMarket + ")");
}
}
}

311
src/main/java/com/example/demo/serviceImpl/cash/CashRefundServiceImpl.java

@ -2,13 +2,10 @@ package com.example.demo.serviceImpl.cash;
import com.example.demo.Util.LanguageTranslationUtil;
import com.example.demo.Util.SimpleIdGenerator;
import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.entity.*;
import com.example.demo.Util.BusinessException;
import com.example.demo.Util.GoldTistV2;
import com.example.demo.config.RabbitMQConfig;
import com.example.demo.domain.entity.Market;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.UserGoldRecord;
import com.example.demo.domain.vo.bean.Region;
import com.example.demo.domain.vo.cash.*;
import com.example.demo.domain.vo.coin.Messages;
@ -16,17 +13,18 @@ import com.example.demo.domain.vo.coin.Result;
import com.example.demo.exception.SystemException;
import com.example.demo.mapper.cash.CashCollectionMapper;
import com.example.demo.mapper.cash.CashRefundMapper;
import com.example.demo.mapper.coin.AuditMapper;
import com.example.demo.mapper.coin.MarketMapper;
import com.example.demo.mapper.coin.OperationLogMapper;
import com.example.demo.mapper.coin.RefundMapper;
import com.example.demo.mapper.coin.*;
import com.example.demo.service.Wallet.WalletService;
import com.example.demo.service.cash.RefundService;
import com.example.demo.service.coin.TranslationService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestHeader;
import com.example.demo.domain.DTO.Currency;
@ -67,6 +65,14 @@ public class CashRefundServiceImpl implements RefundService {
private CashCollectionMapper cashCollectionMapper;
@Autowired
private LanguageTranslationUtil languageTranslationUtil;
@Autowired
private WalletService walletService;
@Autowired
private TranslationService translationService;
@Autowired
private WalletMapper walletMapper;
@Autowired
private UserMapper userMapper;
@Override
public PageInfo<CashRecordDTO> select(Integer pageNum, Integer pageSize, CashRecordDTO cashRecordDTO) {
@ -176,6 +182,7 @@ public class CashRefundServiceImpl implements RefundService {
@Override
public int add(CashRecordRefund cashRecordRefund, @RequestHeader(defaultValue = "zh_CN") String lang) throws Exception {
try {
if (cashRecordRefund.getJwcode() == null) {
throw new Exception("未输入精网号");
}
@ -188,6 +195,55 @@ public class CashRefundServiceImpl implements RefundService {
if (cashRecordRefund.getHandlingCharge() == null) {
throw new Exception("请先填写手续费");
}
if (cashRecordRefund.getMarket() == null || cashRecordRefund.getMarket().trim().isEmpty()) {
throw new Exception("请选择所属地区");
}
if(cashRecordRefund.getWalletId() != null) {
String payType = cashRecordRefund.getPayType();
Integer wallet = null;
if (payType == null || payType.trim().isEmpty()) {
throw new SystemException("未穿输支付方式");
}
if (payType.equals("Stripe") || payType.equals("PaymentAsia")) {
wallet = 2;
}
if (payType.equals("FirstData") || payType.equals("Grabpay") || payType.equals("Nets") || payType.equals("PayPal") || payType.equals("IOS")) {
wallet = 5;
}
if (payType.equals("Stripe2")) {
wallet = 3;
}
if (payType.equals("Ipay88")) {
wallet = 4;
}
if (payType.equals("E-Transfer")) {
wallet = 6;
}
if (payType.equals("paysolution")) {
wallet = 8;
}
String payType1 = languageTranslationUtil.translate("银行转账", lang);
String payType2 = languageTranslationUtil.translate("现金", lang);
String payType3 = languageTranslationUtil.translate("支票", lang);
String payType4 = languageTranslationUtil.translate("刷卡", lang);
if (payType.equals(payType1) || payType.equals(payType2) || payType.equals(payType3) || payType.equals(payType4)) {
wallet = cashRecordRefund.getWalletId();
}
UserRegionWallet userRegionWallet = walletMapper.selectWallet(cashRecordRefund.getJwcode(), wallet);
User user = userMapper.selectUserByJwcode(cashRecordRefund.getJwcode());
if (user.getCurrentFreeJune().add(user.getCurrentFreeDecember()).compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundFree())) < 0) {
String errorMsg = languageTranslationUtil.translate("用户钱包余额不足", lang);
throw new BusinessException(errorMsg);
}
if (userRegionWallet == null) {
//初始化钱包
walletMapper.insert(new UserRegionWallet(null, cashRecordRefund.getJwcode(), wallet, BigDecimal.ZERO, new Date(), new Date()));
log.warn("用户钱包不存在,已初始化钱包");
}
if (userRegionWallet.getCurrentPermanentGold().compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundGold())) < 0) {
throw new BusinessException("用户钱包金币不足");
}
}
CashRecordDone cashRecordDonetwo = new CashRecordDone();
cashRecordDonetwo.setAreaServise(cashRecordRefund.getAreaServise());
cashRefundMapper.addAudit(cashRecordDonetwo);
@ -198,14 +254,33 @@ public class CashRefundServiceImpl implements RefundService {
String orderNumber = cashRecordRefund.getOrderCode();
//构建订单信息
cashRecordRefund.setOrderCode("TK" + orderNumber); //订单号
cashRecordRefund.setMarket(String.valueOf(Integer.valueOf(marketMapper.getMarketId(cashRecordRefund.getMarket()))));
// 查询市场 ID增加空值检查和多语言支持
String marketName = cashRecordRefund.getMarket();
String marketId = marketMapper.getMarketId(marketName);
// 如果直接查询失败尝试将英文名称转换为中文后再次查询
if (marketId == null || marketId.trim().isEmpty()) {
String chineseMarketName = translationService.findChineseSimplifiedByTranslation(marketName, "en");
if (chineseMarketName != null && !chineseMarketName.equals(marketName)) {
marketId = marketMapper.getMarketId(chineseMarketName);
}
}
if (marketId == null || marketId.trim().isEmpty()) {
throw new Exception("无效的所属地区:" + cashRecordRefund.getMarket());
}
cashRecordRefund.setMarket(String.valueOf(Integer.valueOf(marketId)));
cashRefundMapper.insert(cashRecordRefund);
CashRecordDone cashRecordDone1 = new CashRecordDone();
cashRecordDone1.setId(cashRecordRefund.getOriginalOrderId());
cashRecordDone1.setStatus(6);
if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null)
if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null) {
cashRefundMapper.updateStatus(cashRecordDone1);
else return Result.error("提交失败").getCode();
} else {
return Result.error("提交失败").getCode();
}
// 发送退款创建消息
Messages message = new Messages();
@ -217,13 +292,16 @@ public class CashRefundServiceImpl implements RefundService {
message.setType(0);
message.setTypeId(cashRecordRefund.getId());
message.setMarket(Integer.valueOf(cashRecordRefund.getMarket()));
String marketName = marketMapper.getMarketNameById(String.valueOf(message.getMarket()));
message.setMarketName(marketName);
String marketName2 = marketMapper.getMarketNameById(String.valueOf(message.getMarket()));
message.setMarketName(marketName2);
message.setQueryId(103);
rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
return Result.success("提交成功").getCode();
} catch (Exception e) {
log.error("add error", e);
throw e;
}
}
@Override
@ -249,6 +327,7 @@ public class CashRefundServiceImpl implements RefundService {
if (cashRecordDone.getNewRefundFree() == null) {
cashRecordDone.setNewRefundFree(BigDecimal.valueOf(0));
}
int result = cashRefundMapper.update(cashRecordDone);
CashRecordDTO cashRecordDTO = cashRefundMapper.selectById(cashRecordDone.getId());
if (result > 0) {
@ -361,6 +440,7 @@ public class CashRefundServiceImpl implements RefundService {
return cashRefundMapper.updateStatus(cashRecordDone);
}
@Transactional(rollbackFor = Exception.class)
@Override
public int finalreview(CashRecordDone cashRecordDone, @RequestHeader(defaultValue = "zh_CN") String lang) {
if (cashRecordDone.getPermanentGold() == null) {
@ -426,6 +506,97 @@ public class CashRefundServiceImpl implements RefundService {
user.setCurrentFreeJune(BigDecimal.valueOf(-userGoldRecord.getFreeJune())); //当前六月免费金币
user.setCurrentFreeDecember(BigDecimal.valueOf(-userGoldRecord.getFreeDecember())); //当前十二月免费金币
auditMapper.updateUserGold(user);
// 钱包更新 - 按原始充值流水 wallet_id 优先级顺序原路退回1-10越小优先级越高
String orderCodeA = "XJ" + orderCode.substring(4);
// 1. 查询原始充值流水type=0 充值status=0 正常
List<UserWalletRecord> originalRecords = walletService.selectUserWalletRecord(userGoldRecord.getJwcode(), orderCodeA);
if (!CollectionUtils.isEmpty(originalRecords)) {
// 过滤充值记录并按 wallet_id 升序排序
originalRecords = originalRecords.stream()
.filter(r -> r.getType() == 0 && r.getStatus() == 0)
.sorted(Comparator.comparing(UserWalletRecord::getWalletId))
.collect(Collectors.toList());
// 2. 本次退款总金额取绝对值
BigDecimal totalRefundAmount = BigDecimal.valueOf(userGoldRecord.getPermanentGold()).abs();
if (totalRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
// 🔥新增预校验检查用户余额是否足够退款
BigDecimal userTotalBalance = getUserTotalBalance(userGoldRecord.getJwcode(), originalRecords);
if (userTotalBalance.compareTo(totalRefundAmount) < 0) {
// 🔥 余额不足直接报错返回根据项目规范选择抛异常或返回错误码
String errorMsg = String.format("退款失败:用户余额不足 | jwcode=%s, 待退金额=%s, 当前余额=%s",
userGoldRecord.getJwcode(), totalRefundAmount, userTotalBalance);
log.error(errorMsg);
throw new BusinessException("WALLET_REFUND_INSUFFICIENT"+ errorMsg);
// 或者return Result.fail("WALLET_REFUND_INSUFFICIENT", "余额不足,无法退款");
}
BigDecimal remainingRefund = totalRefundAmount;
// 3. 按原始流水顺序退回支持多钱包
for (UserWalletRecord record : originalRecords) {
if (remainingRefund.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
Integer walletId = record.getWalletId();
BigDecimal originalAmount = BigDecimal.valueOf(record.getAmount()).abs();
if (originalAmount.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
// 4. 计算本次退回金额
BigDecimal refundAmount = originalAmount.min(remainingRefund);
if (refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
// 5. 插入退款流水amount 存负数表示扣减
UserWalletRecord refundRecord = new UserWalletRecord();
refundRecord.setType(2); // 充值退款类型
refundRecord.setJwcode(userGoldRecord.getJwcode());
refundRecord.setWalletId(walletId);
refundRecord.setAmount(refundAmount.negate().intValue()); // 🔥 负数扣款
refundRecord.setOrderCode("TK" + orderCodeA);
if (cashRecordDone.getRefundModel()==1) {
refundRecord.setDescription("部分退款");
}
else if (cashRecordDone.getRefundModel()==0) {
refundRecord.setDescription("全部退款");
}
refundRecord.setStatus(0);
walletService.addUserWalletRecord(refundRecord);
// 6. 查询并更新钱包余额
UserRegionWallet currentWallet = walletMapper.selectWallet(userGoldRecord.getJwcode(), walletId);
if (currentWallet == null) {
log.error("钱包记录不存在,无法退款 | jwcode={}, walletId={}", userGoldRecord.getJwcode(), walletId);
continue;
}
// 7. 扣减余额
BigDecimal newBalance = currentWallet.getCurrentPermanentGold().subtract(refundAmount);
// 🔥 兜底理论上预校验已通过这里不会再出现负数但保留防御式编程
if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
log.warn("退款后余额为负,强制置零 | jwcode={}, walletId={}", userGoldRecord.getJwcode(), walletId);
newBalance = BigDecimal.ZERO;
}
currentWallet.setCurrentPermanentGold(newBalance);
walletMapper.updateWallet(currentWallet);
// 8. 更新原流水状态
walletService.updateUserWalletRecord(record.getId());
// 9. 扣减剩余待退金额
remainingRefund = remainingRefund.subtract(refundAmount);
}
}
}
GoldTistV2.addCoinNew(userGoldRecord.getJwcode().toString(), 58, //退款免费+永久金币-充值
(double) (userGoldRecord.getFreeDecember() + userGoldRecord.getFreeJune() + userGoldRecord.getPermanentGold()) / 100, SimpleIdGenerator.generateId(),
userGoldRecord.getRemark(), (double) userGoldRecord.getPermanentGold() / 100, auditName, "退款金币充值");
@ -661,7 +832,9 @@ public class CashRefundServiceImpl implements RefundService {
}
@Override
public void addOnline(CashRecordRefund cashRecordRefund) {
@Transactional(rollbackFor = Exception.class)
public void addOnline(CashRecordRefund cashRecordRefund,String lang) {
try {
if (cashRecordRefund.getJwcode() == null) {
throw new BusinessException("未输入精网号");
}
@ -671,6 +844,53 @@ public class CashRefundServiceImpl implements RefundService {
if (cashRecordRefund.getRefundReason() == null) {
throw new BusinessException("请填写退款理由");
}
if (cashRecordRefund.getMarket() == null || cashRecordRefund.getMarket().trim().isEmpty()) {
throw new BusinessException("请选择所属地区");
}
String payType = cashRecordRefund.getPayType();
Integer wallet = null;
if (payType == null || payType.trim().isEmpty()) {
throw new SystemException("未传输支付方式");
}
if (payType.equals("Stripe")||payType.equals("PaymentAsia"))
{
wallet = 2;
}
if (payType.equals("FirstData")||payType.equals("Grabpay")||payType.equals("Nets")||payType.equals("PayPal")||payType.equals("IOS"))
{
wallet = 5;
}
if (payType.equals("Stripe2"))
{
wallet = 3;
}
if (payType.equals("iPay88"))
{
wallet = 4;
}
if (payType.equals("E-Transfer"))
{
wallet = 6;
}
if (payType.equals("paysolution"))
{
wallet = 8;
}
UserRegionWallet userRegionWallet = walletMapper.selectWallet(cashRecordRefund.getJwcode(), wallet);
User user = userMapper.selectUserByJwcode(cashRecordRefund.getJwcode());
if (user.getCurrentFreeJune().add(user.getCurrentFreeDecember()).compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundFree()))<0){
String errorMsg = languageTranslationUtil.translate("用户钱包余额不足", lang);
throw new BusinessException(errorMsg);
}
if (userRegionWallet == null) {
//初始化钱包
walletMapper.insert(new UserRegionWallet(null, cashRecordRefund.getJwcode(), wallet, BigDecimal.ZERO, new Date(), new Date()));
log.warn("用户钱包不存在,已初始化钱包");
}
if (userRegionWallet.getCurrentPermanentGold().compareTo(BigDecimal.valueOf(cashRecordRefund.getPartRefundGold())) < 0) {
throw new BusinessException("用户钱包金币不足");
}
CashRecordDone cashRecordDonetwo = new CashRecordDone();
cashRecordDonetwo.setAreaServise(cashRecordRefund.getAreaServise());
cashRefundMapper.addAudit(cashRecordDonetwo);
@ -681,14 +901,41 @@ public class CashRefundServiceImpl implements RefundService {
String orderNumber = cashRecordRefund.getOrderCode();
//构建订单信息
cashRecordRefund.setOrderCode("TK" + orderNumber); //订单号
cashRecordRefund.setMarket(String.valueOf(Integer.valueOf(marketMapper.getMarketId(cashRecordRefund.getMarket()))));
// 查询市场 ID增加空值检查
String marketName = cashRecordRefund.getMarket();
// 如果传入的是英文名称需要转换为中文名称再查询
String marketId = marketMapper.getMarketId(marketName);
if (marketId == null || marketId.trim().isEmpty()) {
// 尝试将英文名称转换为中文后再次查询
String chineseMarketName = translationService.findChineseSimplifiedByTranslation(marketName, "en");
if (chineseMarketName != null && !chineseMarketName.equals(marketName)) {
marketId = marketMapper.getMarketId(chineseMarketName);
}
}
if (marketId == null || marketId.trim().isEmpty()) {
throw new BusinessException("无效的所属地区:" + cashRecordRefund.getMarket());
}
cashRecordRefund.setMarket(String.valueOf(Integer.valueOf(marketId)));
cashRefundMapper.insert(cashRecordRefund);
CashRecordDone cashRecordDone1 = new CashRecordDone();
cashRecordDone1.setId(cashRecordRefund.getId());
cashRecordDone1.setId(cashRecordRefund.getOriginalOrderId());
cashRecordDone1.setStatus(6);
if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null)
if (cashRecordDone1.getId() != null || cashRecordDone1.getOrderCode() != null) {
cashRefundMapper.updateStatus(cashRecordDone1);
else throw new SystemException("提交失败");
} else {
throw new SystemException("提交失败");
}
} catch (BusinessException | SystemException e) {
throw e;
} catch (Exception e) {
log.error("addOnline error", e);
throw new SystemException("提交失败:" + e.getMessage());
}
}
@Override
@ -786,5 +1033,31 @@ public class CashRefundServiceImpl implements RefundService {
// 8. 返回分页结果
return new PageInfo<>(list);
}
/**
* 计算用户指定钱包列表的总可用余额
* @param jwcode 用户标识
* @param records 原始充值流水列表用于提取 wallet_id 列表
* @return 总余额BigDecimal
*/
private BigDecimal getUserTotalBalance(Integer jwcode, List<UserWalletRecord> records) {
// 提取涉及到的 wallet_id 列表去重
List<Integer> walletIds = records.stream()
.map(UserWalletRecord::getWalletId)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(walletIds)) {
return BigDecimal.ZERO;
}
// 批量查询钱包当前余额避免 N+1 查询
List<UserRegionWallet> wallets = walletMapper.selectWalletsByJwcodeAndIds(jwcode, walletIds);
// 汇总余额
return wallets.stream()
.map(UserRegionWallet::getCurrentPermanentGold)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}

91
src/main/java/com/example/demo/serviceImpl/coin/AuditServiceImpl.java

@ -6,19 +6,24 @@ import com.example.demo.config.GlobalExceptionHandler;
import com.example.demo.config.RabbitMQConfig;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.UserGoldRecord;
import com.example.demo.domain.entity.UserRegionWallet;
import com.example.demo.domain.entity.UserWalletRecord;
import com.example.demo.domain.vo.coin.*;
import com.example.demo.exception.RedCheckException;
import com.example.demo.mapper.Temporary.RedMapper;
import com.example.demo.mapper.coin.AuditMapper;
import com.example.demo.mapper.coin.MarketMapper;
import com.example.demo.mapper.coin.UserMapper;
import com.example.demo.mapper.coin.WalletMapper;
import com.example.demo.service.Temporary.RedService;
import com.example.demo.service.Wallet.WalletService;
import com.example.demo.service.coin.AdminService;
import com.example.demo.service.coin.AuditService;
import com.example.demo.service.coin.GeneralService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -26,10 +31,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
* @program: gold-java
@ -58,14 +61,18 @@ public class AuditServiceImpl implements AuditService {
@Autowired
private RedService redService;
@Autowired
private WalletMapper walletMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private WalletService walletService;
/*
审核订单并修改用户余额等
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean auditOrder(String token, String orderCode, Integer auditId, Integer action, String rejectReason, BigDecimal price, String linkId) throws Exception {
public boolean auditOrder(String token, String orderCode, Integer auditId, Integer action, String rejectReason, BigDecimal price, String linkId ,Integer refundModel) throws Exception {
if (linkId == null) {
linkId = orderCode;
@ -202,6 +209,80 @@ public class AuditServiceImpl implements AuditService {
update.setCurrentTaskGold(BigDecimal.valueOf(order.getTaskGold())); //当前任务金币
auditMapper.updateUserGold(update);
//钱包更新
// List<UserWalletRecord> userWalletList = walletService.selectUserWalletRecord(order.getJwcode(), oldOrderCode);
// UserRegionWallet userRegionWallet = new UserRegionWallet();
// 钱包更新 - 按原始扣款流水 wallet_id 优先级顺序原路退回1-10越小优先级越高
// 1. 查询原始扣款流水type=1 消耗status=0 正常
List<UserWalletRecord> originalRecords = walletService.selectUserWalletRecord(order.getJwcode(), oldOrderCode);
if (!CollectionUtils.isEmpty(originalRecords)) {
originalRecords = originalRecords.stream()
.filter(r -> r.getType() == 1 && r.getStatus() == 0)
// 修改点使用 comparingInt reversed
.sorted(Comparator.comparingInt(UserWalletRecord::getWalletId).reversed())
.collect(Collectors.toList());
// 2. 本次退款总金额
BigDecimal totalRefundAmount = BigDecimal.valueOf(order.getPermanentGold());
if (totalRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal remainingRefund = totalRefundAmount;
// 3. 按原始流水顺序退回
for (UserWalletRecord record : originalRecords) {
if (remainingRefund.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
Integer walletId = record.getWalletId();
// 🔥 关键修正取绝对值兼容负数存储
BigDecimal originalAmount = BigDecimal.valueOf(record.getAmount()).abs();
if (originalAmount.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
// 4. 计算本次退回金额 = min(原扣金额剩余待退金额)
BigDecimal refundAmount = originalAmount.min(remainingRefund);
if (refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
// 5. 插入退款流水记录type=2 退款
UserWalletRecord refundRecord = new UserWalletRecord();
refundRecord.setJwcode(order.getJwcode());
refundRecord.setWalletId(walletId);
refundRecord.setType(2); // 退款类型
refundRecord.setAmount(refundAmount.intValue()); // 🔥 退款存正数
refundRecord.setOrderCode(order.getOrderCode());
if(refundModel==0){
refundRecord.setDescription("全部退款");
}else{
refundRecord.setDescription("部分退款");
}
refundRecord.setStatus(0);
walletService.addUserWalletRecord(refundRecord);
// 6. 查询并更新钱包余额覆盖更新先查后改
UserRegionWallet currentWallet = walletMapper.selectWallet(order.getJwcode(), walletId);
if (currentWallet == null) {
log.error("钱包记录不存在,无法退款 | jwcode={}, walletId={}", order.getJwcode(), walletId);
continue;
}
// 7. 计算新余额 + 覆盖更新
BigDecimal newBalance = currentWallet.getCurrentPermanentGold().add(refundAmount);
currentWallet.setCurrentPermanentGold(newBalance);
walletMapper.updateWallet(currentWallet);
walletService.updateUserWalletRecord(record.getId());
// 8. 扣减剩余待退金额
remainingRefund = remainingRefund.subtract(refundAmount);
}
}
}
//商品消费退款
//erp增加退款数据
if (oldOrder.getType() == 1) {

113
src/main/java/com/example/demo/serviceImpl/coin/ConsumeServiceImpl.java

@ -2,8 +2,11 @@ package com.example.demo.serviceImpl.coin;
import com.example.demo.Util.GoldTistV2;
import com.example.demo.Util.SimpleIdGenerator;
import com.example.demo.domain.DTO.RegionWalletDTO;
import com.example.demo.domain.DTO.WalletDTO;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.UserGoldRecord;
import com.example.demo.domain.entity.UserWalletRecord;
import com.example.demo.domain.vo.coin.*;
import com.example.demo.exception.SystemException;
import com.example.demo.mapper.coin.ConsumeMapper;
@ -11,7 +14,6 @@ import com.example.demo.mapper.coin.MarketMapper;
import com.example.demo.mapper.coin.UserMapper;
import com.example.demo.service.Temporary.RedService;
import com.example.demo.service.coin.ConsumeService;
import com.example.demo.service.coin.MarketService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
@ -22,8 +24,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -150,6 +150,10 @@ public class ConsumeServiceImpl implements ConsumeService {
if (goldUser == null) {
return Result.error("用户不存在");
}
List<RegionWalletDTO> regionWallets = consumeMapper.selectRegionWalletByJwcode(consumeUser.getJwcode());
if(regionWallets == null || regionWallets.isEmpty()){
return Result.error("用户钱包不存在");
}
UserGoldRecord userGoldRecord = new UserGoldRecord();
String orderNumber = UUID.randomUUID().toString().replaceAll("-", "");
@ -200,31 +204,62 @@ public class ConsumeServiceImpl implements ConsumeService {
userGoldRecord.setPayTime(new Date());
userGoldRecord.setLinkId(SimpleIdGenerator.generateId());
if (consumeUser.getPermanentGold().compareTo(BigDecimal.ZERO)!=0||consumeUser.getFreeGold().compareTo(BigDecimal.ZERO)!=0||consumeUser.getTaskGold().compareTo(BigDecimal.ZERO)!=0){
// if(consumeUser.getJwcode().equals(94226013)){
String result = GoldTistV2.addCoinNew(userGoldRecord.getJwcode().toString(), 65,
(double) (userGoldRecord.getPermanentGold() + userGoldRecord.getFreeDecember() + userGoldRecord.getFreeJune() + userGoldRecord.getTaskGold()) / 100,
userGoldRecord.getLinkId(), userGoldRecord.getRemark(),0, consumeUser.getAdminName(), userGoldRecord.getGoodsName());
// System.out.println("9"+result+"9");
result = result.replaceAll("[\r\n]", "");
//返回状态1加成功2减成功其他失败 -5 金币不足 -6 类型错误 -7签名错误
if (!result.equals("2")) {
result = GoldTistV2.addCoinNew(userGoldRecord.getJwcode().toString(), 65,
(double) (userGoldRecord.getPermanentGold() + userGoldRecord.getFreeDecember() + userGoldRecord.getFreeJune() + userGoldRecord.getTaskGold()) / 100,
userGoldRecord.getLinkId(), userGoldRecord.getRemark(), 0, consumeUser.getAdminName(), userGoldRecord.getGoodsName());
//返回状态1加成功2减成功其他失败 -5 金币不足 -6 类型错误 -7签名错误
if (!result.equals("2")) {
String errorMsg = "减金币失败,数据未进erp,返回状态:" + result;
log.error(errorMsg); // 保留日志记录便于问题排查
throw new SystemException(errorMsg); // 抛出系统异常中断流程并传递错误信息
if(consumeUser.getPermanentGold().compareTo(BigDecimal.ZERO)!=0) {
// 需要扣除的永久金币总量
BigDecimal remainingPermanentGold = consumeUser.getPermanentGold();
// 遍历钱包列表依次扣款
for(RegionWalletDTO wallet : regionWallets) {
// 如果剩余需要扣除的金额为0结束循环
if(remainingPermanentGold.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
// 获取当前钱包的永久金币余额
BigDecimal walletBalance = wallet.getCurrentPermanentGold() != null ? wallet.getCurrentPermanentGold() : BigDecimal.ZERO;
// 计算当前钱包可以扣除的金额不超过钱包余额和剩余需要扣除的金额
BigDecimal deductAmount = BigDecimal.ZERO;
if(walletBalance.compareTo(remainingPermanentGold) >= 0) {
// 钱包余额足够扣除剩余全部金额
deductAmount = remainingPermanentGold;
remainingPermanentGold = BigDecimal.ZERO;
} else {
// 钱包余额不足扣除全部余额
deductAmount = walletBalance;
remainingPermanentGold = remainingPermanentGold.subtract(deductAmount);
}
// 如果需要扣除金额大于0执行扣款操作
if(deductAmount.compareTo(BigDecimal.ZERO) > 0) {
WalletDTO updateWallet = new WalletDTO();
updateWallet.setId(wallet.getId());
UserWalletRecord userWalletRecord = new UserWalletRecord();
userWalletRecord.setJwcode(consumeUser.getJwcode());
userWalletRecord.setWalletId(wallet.getWalletId());
userWalletRecord.setAmount(-deductAmount.intValue());
userWalletRecord.setOrderCode(userGoldRecord.getOrderCode());
userWalletRecord.setDescription("购买" + consumeUser.getGoodsName());
// 设置需要扣除的永久金币金额
updateWallet.setPermanentGold(deductAmount);
// 调用Mapper方法更新钱包余额
consumeMapper.updateRegionWallet(updateWallet);
consumeMapper.addRegionWalletRecord(userWalletRecord);
}
}
// 检查是否所有需要扣除的金额都已扣除
if(remainingPermanentGold.compareTo(BigDecimal.ZERO) > 0) {
// 如果还有剩余金额未扣除说明所有钱包余额不足
String errorMsg = "所有钱包永久金币余额不足,剩余需要扣除:" + remainingPermanentGold.divide(BigDecimal.valueOf(100));
log.error(errorMsg);
throw new SystemException(errorMsg);
}
//0金币消耗往erp传一条金币为空的记录
else if (consumeUser.getPermanentGold().compareTo(BigDecimal.ZERO)==0&&consumeUser.getFreeGold().compareTo(BigDecimal.ZERO)==0&&consumeUser.getTaskGold().compareTo(BigDecimal.ZERO)==0){
GoldTistV2.addCoinRecordNew(String.valueOf(consumeUser.getJwcode()), consumeUser.getRemark(),consumeUser.getAdminName(),consumeUser.getGoodsName());
}
consumeMapper.add(userGoldRecord);
consumeMapper.updateUserGold(userGoldRecord);
User user = new User();
@ -253,7 +288,39 @@ public class ConsumeServiceImpl implements ConsumeService {
// }
//data返回订单号和创建时间
OrderResultVO resultVO = OrderResultVO.build("XF_" + orderNumber,userGoldRecord.getCreateTime());
if (consumeUser.getPermanentGold().compareTo(BigDecimal.ZERO)!=0||consumeUser.getFreeGold().compareTo(BigDecimal.ZERO)!=0||consumeUser.getTaskGold().compareTo(BigDecimal.ZERO)!=0){
// if(consumeUser.getJwcode().equals(94226013)){
String result = GoldTistV2.addCoinNew(userGoldRecord.getJwcode().toString(), 65,
(double) (userGoldRecord.getPermanentGold() + userGoldRecord.getFreeDecember() + userGoldRecord.getFreeJune() + userGoldRecord.getTaskGold()) / 100,
userGoldRecord.getLinkId(), userGoldRecord.getRemark(),0, consumeUser.getAdminName(), userGoldRecord.getGoodsName());
// System.out.println("9"+result+"9");
result = result.replaceAll("[\r\n]", "");
//返回状态1加成功2减成功其他失败 -5 金币不足 -6 类型错误 -7签名错误
if (!result.equals("2")) {
result = GoldTistV2.addCoinNew(userGoldRecord.getJwcode().toString(), 65,
(double) (userGoldRecord.getPermanentGold() + userGoldRecord.getFreeDecember() + userGoldRecord.getFreeJune() + userGoldRecord.getTaskGold()) / 100,
userGoldRecord.getLinkId(), userGoldRecord.getRemark(), 0, consumeUser.getAdminName(), userGoldRecord.getGoodsName());
//返回状态1加成功2减成功其他失败 -5 金币不足 -6 类型错误 -7签名错误
if (!result.equals("2")) {
String errorMsg = "减金币失败,数据未进erp,返回状态:" + result;
log.error(errorMsg); // 保留日志记录便于问题排查
throw new SystemException(errorMsg); // 抛出系统异常中断流程并传递错误信息
}
}
}
//0金币消耗往erp传一条金币为空的记录
else if (consumeUser.getPermanentGold().compareTo(BigDecimal.ZERO)==0&&consumeUser.getFreeGold().compareTo(BigDecimal.ZERO)==0&&consumeUser.getTaskGold().compareTo(BigDecimal.ZERO)==0){
GoldTistV2.addCoinRecordNew(String.valueOf(consumeUser.getJwcode()), consumeUser.getRemark(),consumeUser.getAdminName(),consumeUser.getGoodsName());}
return Result.success(resultVO);
}
@Override

224
src/main/java/com/example/demo/serviceImpl/coin/ExportExcelServiceImpl.java

@ -17,19 +17,18 @@ import com.example.demo.controller.coin.GoldDetailController;
import com.example.demo.controller.coin.RechargeController;
import com.example.demo.controller.coin.RefundController;
import com.example.demo.domain.DTO.PerformanceDTO;
import com.example.demo.domain.DTO.UserWalletRecordDTO;
import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.entity.Export;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.vo.bean.*;
import com.example.demo.domain.vo.cash.CashCollection;
import com.example.demo.domain.vo.cash.CashRecordDTO;
import com.example.demo.domain.vo.cash.FundsDTO;
import com.example.demo.domain.vo.cash.PerformanceVO;
import com.example.demo.domain.vo.cash.*;
import com.example.demo.domain.vo.coin.*;
import com.example.demo.mapper.coin.ExportMapper;
import com.example.demo.mapper.coin.MarketMapper;
import com.example.demo.service.cash.CashCollectionService;
import com.example.demo.service.coin.ExportExcelService;
import com.example.demo.service.coin.MarketService;
import com.fasterxml.jackson.databind.JsonNode;
@ -93,6 +92,8 @@ public class ExportExcelServiceImpl implements ExportExcelService {
private ExportMapper exportMapper;
@Autowired
private MarketService marketService;
@Autowired
private CashCollectionService cashCollectionService;
@Transactional
@Override
@ -499,6 +500,113 @@ public class ExportExcelServiceImpl implements ExportExcelService {
});
}
@Transactional
@Override
public Exception UserWalletRecordExcel(String message) throws Exception {
return exportExcelGeneric(message, "userWalletRecord", page -> {
try {
JsonNode rootNode = objectMapper.readTree(message);
JsonNode requestDataNode = rootNode.path("requestData");
JsonNode userWalletRecordVONode = requestDataNode.path("userWalletRecordVO");
com.example.demo.domain.vo.cash.UserWalletRecordVO userWalletRecordVO =
objectMapper.treeToValue(userWalletRecordVONode, com.example.demo.domain.vo.cash.UserWalletRecordVO.class);
com.example.demo.domain.entity.UserWalletRecord userWalletRecord = new com.example.demo.domain.entity.UserWalletRecord();
if (userWalletRecordVO != null) {
userWalletRecord.setJwcode(userWalletRecordVO.getJwcode());
userWalletRecord.setWalletId(userWalletRecordVO.getWalletId());
}
page.setUserWalletRecord(userWalletRecord);
// 从请求数据中获取语言设置如果没有则使用默认值
String lang = "zh_CN";
JsonNode langNode = rootNode.path("lang");
if (langNode != null && !langNode.asText().isEmpty()) {
lang = langNode.asText();
}
return cashCollectionController.selectWalletRecords(page, lang);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Transactional
@Override
public Exception UserWalletExcel(String message) throws Exception {
return exportExcelGeneric(message, "userWallet", page -> {
try {
JsonNode rootNode = objectMapper.readTree(message);
JsonNode requestDataNode = rootNode.path("requestData");
JsonNode userWalletDTONode = requestDataNode.path("userWalletDTO");
// 解析查询参数
Integer jwcode = null;
String market = null;
Integer pageNum = userWalletDTONode.path("page").asInt(1);
Integer pageSize = userWalletDTONode.path("pageSize").asInt(20);
JsonNode jwcodeNode = userWalletDTONode.path("jwcode");
if (!jwcodeNode.isMissingNode() && !jwcodeNode.isNull()) {
String jwcodeStr = jwcodeNode.asText();
if (!jwcodeStr.isEmpty()) {
try {
jwcode = Integer.parseInt(jwcodeStr.trim());
} catch (NumberFormatException e) {
// 如果转换失败保持为 null
log.warn("精网号格式错误:{}", jwcodeStr);
}
}
}
JsonNode marketNode = userWalletDTONode.path("market");
if (!marketNode.isMissingNode() && !marketNode.isNull()) {
String marketStr = marketNode.asText();
if (!marketStr.isEmpty()) {
market = marketStr.trim();
}
}
// 从请求数据中获取语言设置
String lang = "zh_CN";
JsonNode langNode = rootNode.path("lang");
if (langNode != null && !langNode.asText().isEmpty()) {
lang = langNode.asText();
}
// 调用查询接口
PageInfo<UserWalletVO> result = cashCollectionService.selectUserWallets(jwcode, market, pageNum, pageSize);
// 翻译处理
List<UserWalletVO> flatList = new ArrayList<>();
if (result != null && result.getList() != null) {
translateUserWalletList(result.getList(), lang);
for (UserWalletVO vo : result.getList()) {
if (vo.getWalletList() != null && !vo.getWalletList().isEmpty()) {
for (WalletItem wallet : vo.getWalletList()) {
if (wallet.getWalletId() != null) {
UserWalletVO flatVO = new UserWalletVO();
flatVO.setJwcode(vo.getJwcode());
flatVO.setUserName(vo.getUserName());
flatVO.setMarketName(vo.getMarketName());
flatVO.setWalletId(wallet.getWalletId());
flatVO.setWalletName(wallet.getWalletName());
flatVO.setCurrentPermanentGold(wallet.getCurrentPermanentGold());
flatList.add(flatVO);
}
}
}
}
result.setList(flatList);
}
return Result.success(result);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
@Override
public List<Export> getExcel(Export export) {
List<Export> list = exportMapper.getExportRecord(export.getAccount(),export.getType());
@ -849,6 +957,46 @@ public class ExportExcelServiceImpl implements ExportExcelService {
.head(head)
.build();
}
// 如果是钱包明细表添加动态表头处理器
else if ("userWalletRecord".equals(exportType)) {
Map<String, String> headers = excelHeaderTranslator.getUserWalletHeaders(lang);
List<String> columnOrder = excelHeaderTranslator.getUserWalletColumnOrder();
// 构建自定义表头
List<List<String>> head = new ArrayList<>();
for (String fieldName : columnOrder) {
String headerText = headers.get(fieldName);
if (headerText != null) {
List<String> headItems = new ArrayList<>();
headItems.add(headerText);
head.add(headItems);
}
}
writeSheet = EasyExcel.writerSheet("Sheet1")
.head(head)
.build();
}
// 如果是用户钱包余额表添加动态表头处理器
else if ("userWallet".equals(exportType)) {
Map<String, String> headers = excelHeaderTranslator.getUserWalletBalanceHeaders(lang);
List<String> columnOrder = excelHeaderTranslator.getUserWalletBalanceColumnOrder();
// 构建自定义表头
List<List<String>> head = new ArrayList<>();
for (String fieldName : columnOrder) {
String headerText = headers.get(fieldName);
if (headerText != null) {
List<String> headItems = new ArrayList<>();
headItems.add(headerText);
head.add(headItems);
}
}
writeSheet = EasyExcel.writerSheet("Sheet1")
.head(head)
.build();
}
else {
writeSheet = EasyExcel.writerSheet("Sheet1").build();
}
@ -964,6 +1112,11 @@ public class ExportExcelServiceImpl implements ExportExcelService {
translateFundsList((List<FundsDTO>) list, lang);
}
// 添加钱包明细翻译支持
if ("userWalletRecord".equals(exportType) && list.get(0) instanceof UserWalletRecordVO) {
translateUserWalletRecordList((List<UserWalletRecordVO>) list, lang);
}
excelWriter.write(list, writeSheet);
page.setPageNum(page.getPageNum() + 1);
totalCount += list.size();
@ -1116,6 +1269,10 @@ public class ExportExcelServiceImpl implements ExportExcelService {
return PerformanceVO.class;
case "fundUser":
return FundsDTO.class;
case "userWalletRecord":
return UserWalletRecordVO.class;
case "userWallet":
return UserWalletVO.class;
default:
throw new IllegalArgumentException("不支持的导出类型: " + exportType);
}
@ -1207,6 +1364,10 @@ public class ExportExcelServiceImpl implements ExportExcelService {
String modelDesc = convertRefundModelToString(cashRecord.getRefundModel());
cashRecord.setRefundModelDesc(modelDesc);
}
if (cashRecord.getStatus() != null) {
String statusDesc = getStatusDescription(cashRecord.getStatus());
cashRecord.setStatusDesc(statusDesc);
}
}
}
}
@ -1855,4 +2016,59 @@ public class ExportExcelServiceImpl implements ExportExcelService {
}
}
}
/**
* 翻译钱包明细列表
*/
private void translateUserWalletRecordList(List<UserWalletRecordVO> list, String lang) {
if (list == null || list.isEmpty() || "zh_CN".equalsIgnoreCase(lang) || "zh".equalsIgnoreCase(lang)) {
return;
}
for (UserWalletRecordVO item : list) {
// 翻译所属地区名称
if (item.getMarketName() != null && !item.getMarketName().isEmpty()) {
item.setMarketName(languageTranslationUtil.translate(item.getMarketName(), lang));
}
// 翻译钱包名称
if (item.getWalletName() != null && !item.getWalletName().isEmpty()) {
item.setWalletName(languageTranslationUtil.translate(item.getWalletName(), lang));
}
// 翻译交易类型
if (item.getTypeText() != null && !item.getTypeText().isEmpty()) {
item.setTypeText(languageTranslationUtil.translate(item.getTypeText(), lang));
}
// 翻译状态
if (item.getStatusText() != null && !item.getStatusText().isEmpty()) {
item.setStatusText(languageTranslationUtil.translate(item.getStatusText(), lang));
}
// 翻译交易说明
if (item.getDescription() != null && !item.getDescription().isEmpty()) {
item.setDescription(languageTranslationUtil.translate(item.getDescription(), lang));
}
}
}
/**
* 翻译用户钱包列表
*/
private void translateUserWalletList(List<UserWalletVO> list, String lang) {
if (list == null || list.isEmpty() || "zh_CN".equalsIgnoreCase(lang) || "zh".equalsIgnoreCase(lang)) {
return;
}
for (UserWalletVO item : list) {
// 翻译地区名称
if (item.getMarketName() != null && !item.getMarketName().isEmpty()) {
item.setMarketName(languageTranslationUtil.translate(item.getMarketName(), lang));
}
// 翻译钱包名称
if (item.getWalletList() != null) {
for (WalletItem wallet : item.getWalletList()) {
if (wallet.getWalletName() != null && !wallet.getWalletName().isEmpty()) {
wallet.setWalletName(languageTranslationUtil.translate(wallet.getWalletName(), lang));
}
}
}
}
}
}

9
src/main/java/com/example/demo/serviceImpl/coin/GoldDetailServiceImpl.java

@ -5,6 +5,7 @@ import com.example.demo.Util.LanguageTranslationUtil;
import com.example.demo.Util.RedisUtil;
import com.example.demo.domain.DTO.GoldDetailDTO;
import com.example.demo.domain.DTO.GoldUserDTO;
import com.example.demo.domain.DTO.WalletDTO;
import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.entity.User;
import com.example.demo.exception.SystemException;
@ -66,8 +67,14 @@ public class GoldDetailServiceImpl implements GoldDetailService {
@Override
public PageInfo<User> getGold(Integer pageNum, Integer pageSize, User user) {
PageHelper.startPage(pageNum, pageSize); //必须要直接跟mapper
List<User> list = goldDetailMapper.getGold(user);
// 为每个用户填充钱包列表
for(User u : list) {
if(u != null && u.getJwcode() != null) {
List<WalletDTO> walletList = goldDetailMapper.getWalletDTOList(String.valueOf(u.getJwcode()));
u.setWalletList(walletList);
}
}
return new PageInfo<>(list);
}

7
src/main/java/com/example/demo/serviceImpl/coin/TranslationServiceImpl.java

@ -98,7 +98,12 @@ public class TranslationServiceImpl implements TranslationService {
@Override
public String findChineseSimplifiedByTranslation(String translatedText, String language) {
// 处理空值情况
if (translatedText == null || translatedText.isEmpty()) {
if (translatedText == null) {
return null;
}
// 处理空字符串
if (translatedText.trim().isEmpty()) {
return translatedText;
}

130
src/main/resources/cashMapper/CashCollectionMapper.xml

@ -10,11 +10,11 @@
order_code,bank_code,goods_name,good_num,num_unit,permanent_gold,free_gold,
payment_currency,payment_amount,received_market,
pay_type,pay_time,status,submitter_id,submitter_market,payload,audit_time,
voucher,remark)
voucher,remark,wallet_id)
values(#{orderType},#{jwcode},#{name},#{market},#{activity},
#{orderCode},#{bankCode},#{goodsName},#{goodNum},#{numUnit},#{permanentGold},#{freeGold},#{paymentCurrency},
#{paymentAmount},#{receivedMarket},#{payType},#{payTime},
#{status},#{submitterId},#{submitterMarket},#{payload},#{auditTime},#{voucher},#{remark})
#{status},#{submitterId},#{submitterMarket},#{payload},#{auditTime},#{voucher},#{remark},#{walletId})
</insert>
<!-- <insert id="syncToCashRecord">
insert into cash_record(order_type,jwcode,name,market,activity,
@ -54,6 +54,7 @@
voucher = #{cashRecordCollection.voucher},
remark = #{cashRecordCollection.remark},
status = 0,
wallet_id = #{cashRecordCollection.walletId},
</set>
WHERE order_code = #{cashRecordCollection.orderCode}
AND status = 5
@ -119,9 +120,9 @@
<select id="getMarketByJwcode" resultType="java.lang.String">
select market from user where jwcode=#{jwcode}
</select>
<!--根据订单号获取订单id与状态-->
<!--根据订单号获取订单id、状态、钱包id、永久金币数量、精网号和备注-->
<select id="selectByOrderCode" resultType="com.example.demo.domain.entity.CashRecord">
select id ,status
select id ,status, wallet_id, permanent_gold, jwcode, remark
from cash_record_collection
where order_code=#{orderCode}
</select>
@ -132,6 +133,7 @@
ra.activity_name as activity,cr.order_code,cr.bank_code,
cr.goods_name,cr.good_num,
cr.num_unit,
cr.wallet_id,
cr.permanent_gold/100 as permanentGold,cr.free_gold/100 as freeGold,cr.payment_amount/100 as paymentAmount,
r1.rate_name as paymentCurrency,
r2.rate_name as receivedCurrency,
@ -389,6 +391,126 @@
<select id="selectGoodsNameByCode" resultType="java.lang.String">
select goods_name from cash_record_collection where order_code=#{orderCode}
</select>
<!-- 根据 jwcode 和 walletId 查询用户钱包 -->
<select id="selectUserWallet" resultType="com.example.demo.domain.entity.UserRegionWallet">
SELECT id, jwcode, wallet_id, current_permanent_gold, create_time, update_time
FROM user_region_wallet
WHERE jwcode = #{jwcode} AND wallet_id = #{walletId}
</select>
<!-- 增加用户钱包永久金币 -->
<update id="addUserWalletPermanentGold">
UPDATE user_region_wallet
SET current_permanent_gold = current_permanent_gold + #{amount},
update_time = NOW()
WHERE jwcode = #{jwcode} AND wallet_id = #{walletId}
</update>
<!-- 如果钱包记录不存在则插入 -->
<insert id="insertUserWallet" parameterType="com.example.demo.domain.entity.UserRegionWallet">
INSERT INTO user_region_wallet (jwcode, wallet_id, current_permanent_gold, create_time, update_time)
VALUES (#{jwcode}, #{walletId}, #{currentPermanentGold}, NOW(), NOW())
ON DUPLICATE KEY UPDATE
current_permanent_gold = current_permanent_gold + VALUES(current_permanent_gold),
update_time = NOW()
</insert>
<!-- 插入用户钱包明细记录 -->
<insert id="insertUserWalletRecord" parameterType="com.example.demo.domain.entity.UserWalletRecord">
INSERT INTO user_wallet_record (jwcode, wallet_id, type, amount, order_code, description, status, create_time)
VALUES (#{jwcode}, #{walletId}, #{type}, #{amount}, #{orderCode}, #{description}, #{status}, NOW())
</insert>
<!-- 根据精网号和钱包 ID 查询用户钱包明细列表 -->
<select id="selectWalletRecordsByJwcodeAndWalletId" resultType="com.example.demo.domain.vo.cash.UserWalletRecordVO">
SELECT
uwr.id,
uwr.jwcode,
u.name as userName,
u.market,
m.name as marketName,
uwr.wallet_id,
w.wallet_name as walletName,
uwr.type,
uwr.amount/100 as amount,
uwr.order_code as orderCode,
uwr.description,
uwr.status,
uwr.create_time as createTime
FROM user_wallet_record uwr
LEFT JOIN user u ON uwr.jwcode = u.jwcode
LEFT JOIN market m ON u.market = m.id
LEFT JOIN wallet w ON uwr.wallet_id = w.id
<where>
<if test="jwcode != null">
AND uwr.jwcode = #{jwcode}
</if>
<if test="walletId != null">
AND uwr.wallet_id = #{walletId}
</if>
</where>
ORDER BY uwr.create_time DESC
</select>
<!-- 根据精网号和地区查询用户的所有钱包 ID 和金币数量(包含用户名和地区) -->
<resultMap id="UserWalletVOResultMap" type="com.example.demo.domain.vo.cash.UserWalletVO">
<id property="jwcode" column="jwcode"/>
<result property="userName" column="userName"/>
<result property="market" column="market"/>
<result property="marketName" column="marketName"/>
<collection property="walletList" ofType="com.example.demo.domain.vo.cash.WalletItem">
<id property="walletId" column="walletId"/>
<result property="walletName" column="walletName"/>
<result property="currentPermanentGold" column="currentPermanentGold"/>
</collection>
</resultMap>
<!-- 查询符合条件的精网号列表(用于分页,每个精网号算一条记录) -->
<select id="selectDistinctJwcodes" resultType="java.lang.Integer">
SELECT DISTINCT u.jwcode
FROM user u
LEFT JOIN market m ON u.market = m.id
LEFT JOIN user_region_wallet wr ON u.jwcode = wr.jwcode
<where>
<if test="jwcode != null">
AND u.jwcode = #{jwcode}
</if>
<if test="market != null and market != ''">
AND u.market = #{market}
</if>
AND wr.wallet_id IS NOT NULL
</where>
ORDER BY u.jwcode
</select>
<!-- 根据精网号列表查询用户的所有钱包信息 -->
<select id="selectUserWalletsByJwcodes" resultMap="UserWalletVOResultMap">
SELECT
u.jwcode,
u.name as userName,
u.market,
m.name as marketName,
wr.wallet_id as walletId,
w.wallet_name as walletName,
COALESCE(wr.current_permanent_gold, 0)/100 as currentPermanentGold
FROM user u
LEFT JOIN market m ON u.market = m.id
LEFT JOIN user_region_wallet wr ON u.jwcode = wr.jwcode
LEFT JOIN wallet w ON wr.wallet_id = w.id
<where>
u.jwcode IN
<foreach collection="jwcodeList" item="jwcode" open="(" separator="," close=")">
#{jwcode}
</foreach>
<if test="market != null and market != ''">
AND u.market = #{market}
</if>
AND wr.wallet_id IS NOT NULL
</where>
ORDER BY u.jwcode, wr.wallet_id
</select>
<!--根据OrderCode订单号更新收款订单-->
<update id="updateByGoldCoinOrderCodeByPayment">
update cash_record_collection

29
src/main/resources/cashMapper/UserRegionWalletMapper.xml

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.cash.UserRegionWalletMapper">
<!-- 根据 jwcode 和 walletId 查询用户钱包 -->
<select id="selectByJwcodeAndWalletId" resultType="com.example.demo.domain.entity.UserRegionWallet">
SELECT id, jwcode, wallet_id, current_permanent_gold, create_time, update_time
FROM user_region_wallet
WHERE jwcode = #{jwcode} AND wallet_id = #{walletId}
</select>
<!-- 插入或更新用户钱包余额 -->
<insert id="insertOrUpdate" parameterType="com.example.demo.domain.entity.UserRegionWallet">
INSERT INTO user_region_wallet (jwcode, wallet_id, current_permanent_gold, create_time, update_time)
VALUES (#{jwcode}, #{walletId}, #{currentPermanentGold}, NOW(), NOW())
ON DUPLICATE KEY UPDATE
current_permanent_gold = VALUES(current_permanent_gold),
update_time = NOW()
</insert>
<!-- 增加用户钱包永久金币 -->
<update id="addPermanentGold">
UPDATE user_region_wallet
SET current_permanent_gold = current_permanent_gold + #{amount},
update_time = NOW()
WHERE jwcode = #{jwcode} AND wallet_id = #{walletId}
</update>
</mapper>

60
src/main/resources/mapper/ConsumeMapper.xml

@ -341,4 +341,64 @@
where jwcode = #{jwcode}
</update>
<select id="selectRegionWalletByJwcode" resultType="com.example.demo.domain.DTO.RegionWalletDTO">
SELECT urw.id,
urw.jwcode,
urw.wallet_id,
urw.current_permanent_gold,
w.priority
FROM user_region_wallet urw
left join wallet w on urw.wallet_id = w.id
WHERE jwcode = #{jwcode}
order by w.priority
</select>
<select id="selectWallet" resultType="com.example.demo.domain.entity.Wallet">
SELECT id,
wallet_name,
priority
FROM wallet
</select>
<insert id="createRegionWallet" parameterType="java.lang.Integer">
INSERT INTO user_region_wallet (jwcode, wallet_id, current_permanent_gold)
SELECT #{jwcode}, 1, u.current_permanent_gold
FROM user u
WHERE u.jwcode = #{jwcode}
</insert>
<!-- 更新钱包金币余额 -->
<update id="updateRegionWallet" parameterType="com.example.demo.domain.DTO.WalletDTO">
UPDATE user_region_wallet
<set>
<if test="permanentGold != null">
current_permanent_gold = current_permanent_gold - #{permanentGold},
</if>
</set>
WHERE id = #{id}
</update>
<!-- 添加钱包记录 -->
<insert id="addRegionWalletRecord" parameterType="com.example.demo.domain.entity.UserWalletRecord">
INSERT INTO user_wallet_record
<trim prefix="(" suffix=")" suffixOverrides=",">
jwcode,
wallet_id,
type,
amount,
order_code,
description,
status,
create_time
</trim>
VALUES
<trim prefix="(" suffix=")" suffixOverrides=",">
#{jwcode},
#{walletId},
1,
#{amount},
#{orderCode},
#{description},
0,
NOW()
</trim>
</insert>
</mapper>

13
src/main/resources/mapper/GoldDetailMapper.xml

@ -268,4 +268,17 @@
</if>
</where>
</select>
<select id="getWalletDTOList" resultType="com.example.demo.domain.DTO.WalletDTO">
SELECT
urw.id,
w.wallet_name,
urw.wallet_id,
ROUND(IFNULL(urw.current_permanent_gold, 0) / 100.0, 2) AS permanentGold
FROM user_region_wallet urw
left join wallet w on w.id=urw.wallet_id
WHERE urw.jwcode = #{jwcode}
ORDER BY w.priority
</select>
</mapper>

92
src/main/resources/mapper/WalletMapper.xml

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.coin.WalletMapper">
<insert id="addUserWalletRecord">
insert into user_wallet_record(jwcode, wallet_id, type, amount, order_code, description, status)
values(#{jwcode}, #{walletId}, #{type}, #{amount}, #{orderCode}, #{description}, 1)
</insert>
<insert id="insert">
insert into user_region_wallet(jwcode, wallet_id, current_permanent_gold)
values(#{jwcode}, #{walletId}, #{currentPermanentGold})
</insert>
<!-- 结果映射 -->
<resultMap id="WalletResult" type="com.example.demo.domain.entity.Wallet">
<id property="id" column="id"/>
<result property="walletName" column="wallet_name"/>
<result property="priority" column="priority"/>
</resultMap>
<resultMap id="UserRegionWalletResult" type="com.example.demo.domain.entity.UserRegionWallet">
<id property="id" column="id"/>
<result property="jwcode" column="jwcode"/>
<result property="walletId" column="wallet_id"/>
<result property="currentPermanentGold" column="current_permanent_gold"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<update id="updateWallet">
update user_region_wallet set current_permanent_gold = #{currentPermanentGold} where jwcode = #{jwcode} and wallet_id = #{walletId}
</update>
<update id="updateWalletRecord">
update user_wallet_record set status = 1 where id = #{id}
</update>
<select id="selectWallet" resultType="com.example.demo.domain.entity.UserRegionWallet">
select jwcode,
wallet_id,
current_permanent_gold
from user_region_wallet where jwcode = #{jwcode} and wallet_id = #{walletId}
</select>
<select id="selectWalletRecord" resultType="com.example.demo.domain.entity.UserWalletRecord">
select jwcode,
wallet_id,
type,
amount,
order_code,
description,
status,
id
from user_wallet_record where jwcode = #{jwcode} and order_code = #{orderCode}
</select>
<select id="MysqlConnection" resultType="com.example.demo.domain.entity.GOrder">
select id,
jwcode,
order_no,
type,
merchant_id,
price,
count,
pay_style
from g_order where id = #{linkId}
</select>
<!-- 查询所有钱包类型 -->
<select id="selectAllWallets" resultMap="WalletResult">
SELECT
id,
wallet_name,
priority
FROM wallet
ORDER BY priority ASC
</select>
<select id="selectWalletsByJwcodeAndIds" resultType="com.example.demo.domain.entity.UserRegionWallet">
SELECT
id,
jwcode,
wallet_id,
current_permanent_gold,
create_time,
update_time
FROM user_region_wallet
WHERE jwcode = #{jwcode}
AND wallet_id IN
<foreach item="item" index="index" collection="walletIds" separator="," close=")" open="(">
#{item}
</foreach>
</select>
</mapper>
Loading…
Cancel
Save