diff --git a/src/main/java/com/rj/controller/LbDailyUserTradeController.java b/src/main/java/com/rj/controller/LbDailyUserTradeController.java index 798a1bc..8bf9406 100644 --- a/src/main/java/com/rj/controller/LbDailyUserTradeController.java +++ b/src/main/java/com/rj/controller/LbDailyUserTradeController.java @@ -301,6 +301,48 @@ public class LbDailyUserTradeController { } } + @PostMapping("/calculate-report/by-date-and-tenant") + @Operation(summary = "按日期和租户计算报表", description = "根据报表日期和租户ID计算当天交易报表并写入 lb_daily_user_trade_report") + public ResponseEntity> calculateReportByDateAndTenant( + @Parameter(description = "报表日期,格式:yyyy-MM-dd", required = true) + @RequestParam String reportDate, + @Parameter(description = "租户ID", required = true) + @RequestParam String tenantId) { + Map result = new HashMap<>(); + try { + if (tenantId == null || tenantId.trim().isEmpty()) { + result.put("success", false); + result.put("message", "tenantId不能为空"); + return ResponseEntity.badRequest().body(result); + } + if (reportDate == null || reportDate.trim().isEmpty()) { + result.put("success", false); + result.put("message", "reportDate不能为空"); + return ResponseEntity.badRequest().body(result); + } + + LocalDate parsedReportDate; + try { + parsedReportDate = LocalDate.parse(reportDate.trim()); + } catch (Exception e) { + result.put("success", false); + result.put("message", "报表日期格式错误,请使用 yyyy-MM-dd"); + return ResponseEntity.badRequest().body(result); + } + + Map serviceResult = lbDailyUserTradeService.calculateDailyTradeReport(parsedReportDate, tenantId); + Boolean success = (Boolean) serviceResult.get("success"); + if (Boolean.TRUE.equals(success)) { + return ResponseEntity.ok(serviceResult); + } + return ResponseEntity.badRequest().body(serviceResult); + } catch (Exception e) { + result.put("success", false); + result.put("message", "计算异常:" + e.getMessage()); + return ResponseEntity.internalServerError().body(result); + } + } + @PostMapping("/import") @Operation(summary = "导入交易记录", description = "按Excel模板导入当天用户交易记录") public ResponseEntity> importExcel( diff --git a/src/main/java/com/rj/service/ILbDailyUserTradeService.java b/src/main/java/com/rj/service/ILbDailyUserTradeService.java index 17f16ef..63e8249 100644 --- a/src/main/java/com/rj/service/ILbDailyUserTradeService.java +++ b/src/main/java/com/rj/service/ILbDailyUserTradeService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.rj.entity.LbDailyUserTrade; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDate; import java.util.Map; /** @@ -14,4 +15,6 @@ import java.util.Map; public interface ILbDailyUserTradeService extends IService { Map importFromExcel(MultipartFile file, String defaultReportDate, String tenantId); + + Map calculateDailyTradeReport(LocalDate reportDate, String tenantId); } diff --git a/src/main/java/com/rj/service/impl/LbDailyUserTradeServiceImpl.java b/src/main/java/com/rj/service/impl/LbDailyUserTradeServiceImpl.java index afbcf00..270c032 100644 --- a/src/main/java/com/rj/service/impl/LbDailyUserTradeServiceImpl.java +++ b/src/main/java/com/rj/service/impl/LbDailyUserTradeServiceImpl.java @@ -1,10 +1,13 @@ package com.rj.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.rj.entity.LbDailyUserTrade; +import com.rj.entity.LbDailyUserTradeReport; import com.rj.mapper.LbDailyUserTradeMapper; import com.rj.service.ILbDailyUserTradeService; +import com.rj.service.ILbDailyUserTradeReportService; import org.apache.poi.poifs.filesystem.FileMagic; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; @@ -16,6 +19,7 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -46,6 +50,96 @@ import java.util.regex.Pattern; @Service public class LbDailyUserTradeServiceImpl extends ServiceImpl implements ILbDailyUserTradeService { private static final Logger log = LoggerFactory.getLogger(LbDailyUserTradeServiceImpl.class); + private static final BigDecimal SERVICE_RATE = new BigDecimal("0.01"); + + @Autowired + private ILbDailyUserTradeReportService lbDailyUserTradeReportService; + + @Override + public Map calculateDailyTradeReport(LocalDate reportDate, String tenantId) { + Map result = new HashMap<>(); + if (reportDate == null) { + result.put("success", false); + result.put("message", "reportDate不能为空"); + return result; + } + if (StringUtils.isBlank(tenantId)) { + result.put("success", false); + result.put("message", "tenantId不能为空"); + return result; + } + + String normalizedTenantId = tenantId.trim(); + LambdaQueryWrapper currentDayWrapper = new LambdaQueryWrapper<>(); + currentDayWrapper.eq(LbDailyUserTrade::getTenantId, normalizedTenantId) + .eq(LbDailyUserTrade::getReportDate, reportDate) + .and(wrapper -> wrapper + .ne(LbDailyUserTrade::getDailySellAmt, BigDecimal.ZERO) + .or() + .ne(LbDailyUserTrade::getDailyBuyAmt, BigDecimal.ZERO) + .or() + .ne(LbDailyUserTrade::getDiffAmt, BigDecimal.ZERO)); + + List currentDayTrades = this.list(currentDayWrapper); + if (currentDayTrades.isEmpty()) { + result.put("success", true); + result.put("message", "未匹配到可计算数据"); + result.put("insertCount", 0); + return result; + } + + LocalDate yesterday = reportDate.minusDays(1); + LocalDateTime now = LocalDateTime.now(); + List reportList = new ArrayList<>(); + + for (LbDailyUserTrade trade : currentDayTrades) { + BigDecimal dailySellAmt = trade.getDailySellAmt() == null ? BigDecimal.ZERO : trade.getDailySellAmt(); + BigDecimal dailyBuyAmt = trade.getDailyBuyAmt() == null ? BigDecimal.ZERO : trade.getDailyBuyAmt(); + BigDecimal diffAmt = trade.getDiffAmt() == null ? BigDecimal.ZERO : trade.getDiffAmt(); + + LambdaQueryWrapper yesterdayWrapper = new LambdaQueryWrapper<>(); + yesterdayWrapper.eq(LbDailyUserTrade::getTenantId, normalizedTenantId) + .eq(LbDailyUserTrade::getReportDate, yesterday) + .eq(LbDailyUserTrade::getUserId, trade.getUserId()); + if (StringUtils.isNotBlank(trade.getNickname())) { + yesterdayWrapper.eq(LbDailyUserTrade::getNickname, trade.getNickname().trim()); + } + yesterdayWrapper + .orderByDesc(LbDailyUserTrade::getUpdatedAt) + .last("limit 1"); + LbDailyUserTrade lastDateRecord = this.getOne(yesterdayWrapper, false); + BigDecimal yestodaySellAmt = lastDateRecord == null || lastDateRecord.getDailyBuyAmt() == null + ? BigDecimal.ZERO + : lastDateRecord.getDailyBuyAmt(); + + LbDailyUserTradeReport report = new LbDailyUserTradeReport(); + report.setId(UUID.randomUUID().toString()); + report.setTenantId(normalizedTenantId); + report.setUserId(trade.getUserId()); + report.setNickname(trade.getNickname()); + report.setYestodaySellAmt(yestodaySellAmt); + report.setDailySellAmt(dailySellAmt); + report.setDailyBuyAmt(dailyBuyAmt); + report.setServiceAmt(dailyBuyAmt.multiply(SERVICE_RATE)); + report.setDiffAmt(diffAmt); + report.setReportDate(reportDate.atStartOfDay()); + report.setCreatedAt(now); + report.setUpdatedAt(now); + reportList.add(report); + } + + boolean saved = lbDailyUserTradeReportService.saveBatch(reportList); + if (!saved) { + result.put("success", false); + result.put("message", "报表插入失败"); + return result; + } + + result.put("success", true); + result.put("message", "计算并写入报表成功"); + result.put("insertCount", reportList.size()); + return result; + } @Override public Map importFromExcel(MultipartFile file, String defaultReportDate, String tenantId) { @@ -204,16 +298,75 @@ public class LbDailyUserTradeServiceImpl extends ServiceImpl map, Row headerRow, DataFormatter formatter) { + if (map.containsKey("dailySellAmt") && map.containsKey("dailyBuyAmt")) { + return; + } + Integer nicknameIndex = map.get("nickname"); + Integer userIdIndex = map.get("userId"); + Integer diffIndex = map.get("diffAmt"); + + int startIndex = -1; + if (nicknameIndex != null) { + startIndex = nicknameIndex; + } else if (userIdIndex != null) { + startIndex = userIdIndex; + } + + if (startIndex < 0) { + return; + } + + int candidateSellIndex = startIndex + 1; + int candidateBuyIndex = startIndex + 2; + + int lastCellNum = headerRow.getLastCellNum(); + if (lastCellNum < 0) { + return; + } + + if (diffIndex != null) { + if (candidateSellIndex >= diffIndex || candidateBuyIndex >= diffIndex) { + candidateSellIndex = -1; + candidateBuyIndex = -1; + for (int i = startIndex + 1; i < diffIndex; i++) { + String header = normalizeHeader(formatter.formatCellValue(headerRow.getCell(i))); + if (header.isEmpty()) { + continue; + } + if (candidateSellIndex < 0) { + candidateSellIndex = i; + } else { + candidateBuyIndex = i; + break; + } + } + } + } + + if (!map.containsKey("dailySellAmt") + && candidateSellIndex >= 0 + && candidateSellIndex < lastCellNum) { + map.put("dailySellAmt", candidateSellIndex); + } + if (!map.containsKey("dailyBuyAmt") + && candidateBuyIndex >= 0 + && candidateBuyIndex < lastCellNum) { + map.put("dailyBuyAmt", candidateBuyIndex); + } + } + private String resolveFieldByHeader(String header) { Map> aliasMap = new HashMap<>(); aliasMap.put("tenantId", Set.of("tenantid", "tenant_id", "租户id", "租户")); aliasMap.put("userId", Set.of("userid", "user_id", "用户id", "用户")); aliasMap.put("nickname", Set.of("nickname", "昵称", "用户名", "客户昵称")); - aliasMap.put("dailySellAmt", Set.of("dailysellamt", "daily_sell_amt", "单日卖出金额", "卖出金额", "卖出")); - aliasMap.put("dailyBuyAmt", Set.of("dailybuyamt", "daily_buy_amt", "当日买入金额", "买入金额", "买入")); + aliasMap.put("dailySellAmt", Set.of("dailysellamt", "daily_sell_amt", "单日卖出金额", "卖出金额", "卖出", "卖总")); + aliasMap.put("dailyBuyAmt", Set.of("dailybuyamt", "daily_buy_amt", "当日买入金额", "买入金额", "买入", "买总")); aliasMap.put("diffAmt", Set.of("diffamt", "diff_amt", "差额", "净额")); aliasMap.put("promoterId", Set.of("promoterid", "promoter_id", "推广人id")); aliasMap.put("promoterName", Set.of("promotername", "promoter_name", "推广人", "邀请人")); @@ -225,6 +378,12 @@ public class LbDailyUserTradeServiceImpl extends ServiceImpl