统计交易金额

This commit is contained in:
2026-04-30 07:26:17 +08:00
parent 97e90e2742
commit 1ee114756c
3 changed files with 276 additions and 1 deletions

View File

@@ -13,4 +13,10 @@ import java.util.Map;
*/
public interface ILbDailyUserTradeReportService extends IService<LbDailyUserTradeReport> {
Map<String, Object> calculateReportSumByDateAndTenant(LocalDate reportDate, String tenantId);
/**
* 按日期范围与租户统计每人四类金额(昨日买入、当日卖出、当日买入、差额):本人汇总与递归下级汇总。
*/
Map<String, Object> listUserTradeAmountWithTeamByDateRangeAndTenant(
LocalDate startDate, LocalDate endDate, String tenantId);
}

View File

@@ -3,15 +3,24 @@ package com.rj.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rj.entity.LbDailyUserTradeReport;
import com.rj.entity.LbDepartmentUser;
import com.rj.mapper.LbDailyUserTradeReportMapper;
import com.rj.service.ILbDailyUserTradeReportService;
import com.rj.service.ILbDepartmentUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
@@ -25,6 +34,9 @@ public class LbDailyUserTradeReportServiceImpl extends ServiceImpl<LbDailyUserTr
private static final String DATA_TYPE_REPORT_SUM = "report_sum";
@Autowired
private ILbDepartmentUserService lbDepartmentUserService;
@Override
public Map<String, Object> calculateReportSumByDateAndTenant(LocalDate reportDate, String tenantId) {
Map<String, Object> result = new HashMap<>();
@@ -81,7 +93,206 @@ public class LbDailyUserTradeReportServiceImpl extends ServiceImpl<LbDailyUserTr
return result;
}
private BigDecimal defaultZero(BigDecimal value) {
private static BigDecimal defaultZero(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
@Override
public Map<String, Object> listUserTradeAmountWithTeamByDateRangeAndTenant(
LocalDate startDate, LocalDate endDate, String tenantId) {
Map<String, Object> result = new HashMap<>();
if (tenantId == null || tenantId.trim().isEmpty()) {
result.put("success", false);
result.put("message", "tenantId不能为空");
return result;
}
if (startDate == null || endDate == null) {
result.put("success", false);
result.put("message", "开始日期和结束日期不能为空");
return result;
}
if (endDate.isBefore(startDate)) {
result.put("success", false);
result.put("message", "结束日期不能早于开始日期");
return result;
}
String tenantIdTrim = tenantId.trim();
LocalDateTime rangeStart = startDate.atStartOfDay();
LocalDateTime rangeEnd = endDate.atTime(23, 59, 59);
List<LbDepartmentUser> deptUsers = lbDepartmentUserService.list(
new LambdaQueryWrapper<LbDepartmentUser>()
.eq(LbDepartmentUser::getTenantId, tenantIdTrim));
if (deptUsers == null || deptUsers.isEmpty()) {
result.put("success", true);
result.put("message", "查询成功");
result.put("data", List.of());
result.put("total", 0);
result.put("startDate", startDate);
result.put("endDate", endDate);
result.put("tenantId", tenantIdTrim);
return result;
}
Map<String, String> userIdToName = new HashMap<>();
Set<String> tenantUserIds = new HashSet<>();
Map<String, List<String>> childrenByParentUserId = new HashMap<>();
for (LbDepartmentUser row : deptUsers) {
if (row.getUserId() == null || row.getUserId().trim().isEmpty()) {
continue;
}
String uid = row.getUserId().trim();
tenantUserIds.add(uid);
if (row.getName() != null && !row.getName().trim().isEmpty()) {
userIdToName.putIfAbsent(uid, row.getName().trim());
}
String parentUserId = row.getParentId() == null ? null : row.getParentId().trim();
if (parentUserId != null && !parentUserId.isEmpty()) {
List<String> ch = childrenByParentUserId.computeIfAbsent(parentUserId, k -> new ArrayList<>());
if (!ch.contains(uid)) {
ch.add(uid);
}
}
}
for (List<String> list : childrenByParentUserId.values()) {
list.sort(String::compareTo);
}
LambdaQueryWrapper<LbDailyUserTradeReport> tradeQw = new LambdaQueryWrapper<>();
tradeQw.eq(LbDailyUserTradeReport::getTenantId, tenantIdTrim)
.ge(LbDailyUserTradeReport::getReportDate, rangeStart)
.le(LbDailyUserTradeReport::getReportDate, rangeEnd)
.ne(LbDailyUserTradeReport::getDataType, DATA_TYPE_REPORT_SUM);
Map<String, TradeAmtBundle> ownAmtByUserId = new HashMap<>();
for (LbDailyUserTradeReport r : list(tradeQw)) {
if (r.getUserId() == null || r.getUserId().trim().isEmpty()) {
continue;
}
String uid = r.getUserId().trim();
ownAmtByUserId
.computeIfAbsent(uid, k -> TradeAmtBundle.zeros())
.addFromReportRow(r);
if (r.getNickname() != null && !r.getNickname().trim().isEmpty()) {
userIdToName.putIfAbsent(uid, r.getNickname().trim());
}
}
Map<String, TradeAmtBundle> teamAmtMemo = new HashMap<>();
for (String userId : tenantUserIds) {
teamAmtRecursive(userId, childrenByParentUserId, ownAmtByUserId, teamAmtMemo, new HashSet<>());
}
List<Map<String, Object>> rows = new ArrayList<>();
for (String userId : tenantUserIds) {
TradeAmtBundle own = ownAmt(userId, ownAmtByUserId);
TradeAmtBundle team = teamAmt(userId, teamAmtMemo);
TradeAmtBundle total = own.addCopy(team);
Map<String, Object> row = new LinkedHashMap<>();
row.put("userId", userId);
row.put("name", userIdToName.getOrDefault(userId, ""));
row.put("ownYestodayBuyAmt", own.yestodayBuyAmt);
row.put("teamYestodayBuyAmt", team.yestodayBuyAmt);
row.put("totalYestodayBuyAmt", total.yestodayBuyAmt);
row.put("ownDailySellAmt", own.dailySellAmt);
row.put("teamDailySellAmt", team.dailySellAmt);
row.put("totalDailySellAmt", total.dailySellAmt);
row.put("ownDailyBuyAmt", own.dailyBuyAmt);
row.put("teamDailyBuyAmt", team.dailyBuyAmt);
row.put("totalDailyBuyAmt", total.dailyBuyAmt);
row.put("ownDiffAmt", own.diffAmt);
row.put("teamDiffAmt", team.diffAmt);
row.put("totalDiffAmt", total.diffAmt);
rows.add(row);
}
rows.sort(Comparator.comparing((Map<String, Object> m) -> (BigDecimal) m.get("totalDiffAmt")).reversed());
result.put("success", true);
result.put("message", "查询成功");
result.put("data", rows);
result.put("total", rows.size());
result.put("startDate", startDate);
result.put("endDate", endDate);
result.put("tenantId", tenantIdTrim);
return result;
}
private static TradeAmtBundle ownAmt(String userId, Map<String, TradeAmtBundle> ownAmtByUserId) {
TradeAmtBundle b = ownAmtByUserId.get(userId);
return b != null ? b : TradeAmtBundle.zeros();
}
private static TradeAmtBundle teamAmt(String userId, Map<String, TradeAmtBundle> teamAmtMemo) {
TradeAmtBundle b = teamAmtMemo.get(userId);
return b != null ? b : TradeAmtBundle.zeros();
}
/**
* 递归下级不含本人在日期范围内四类金额汇总parent_id 为父用户 user_id。
*/
private TradeAmtBundle teamAmtRecursive(
String parentUserId,
Map<String, List<String>> childrenByParentUserId,
Map<String, TradeAmtBundle> ownAmtByUserId,
Map<String, TradeAmtBundle> memo,
Set<String> stack) {
TradeAmtBundle cached = memo.get(parentUserId);
if (cached != null) {
return cached;
}
if (!stack.add(parentUserId)) {
return TradeAmtBundle.zeros();
}
try {
TradeAmtBundle sum = TradeAmtBundle.zeros();
List<String> children = childrenByParentUserId.get(parentUserId);
if (children != null) {
for (String childUserId : children) {
sum.addInPlace(ownAmt(childUserId, ownAmtByUserId));
sum.addInPlace(teamAmtRecursive(childUserId, childrenByParentUserId, ownAmtByUserId, memo, stack));
}
}
memo.put(parentUserId, sum);
return sum;
} finally {
stack.remove(parentUserId);
}
}
/** 日期范围内按用户汇总的四类金额(与报表字段对应)。 */
private static final class TradeAmtBundle {
BigDecimal yestodayBuyAmt = BigDecimal.ZERO;
BigDecimal dailySellAmt = BigDecimal.ZERO;
BigDecimal dailyBuyAmt = BigDecimal.ZERO;
BigDecimal diffAmt = BigDecimal.ZERO;
static TradeAmtBundle zeros() {
return new TradeAmtBundle();
}
void addFromReportRow(LbDailyUserTradeReport r) {
yestodayBuyAmt = yestodayBuyAmt.add(defaultZero(r.getYestodayBuyAmt()));
dailySellAmt = dailySellAmt.add(defaultZero(r.getDailySellAmt()));
dailyBuyAmt = dailyBuyAmt.add(defaultZero(r.getDailyBuyAmt()));
diffAmt = diffAmt.add(defaultZero(r.getDiffAmt()));
}
void addInPlace(TradeAmtBundle o) {
if (o == null) {
return;
}
yestodayBuyAmt = yestodayBuyAmt.add(defaultZero(o.yestodayBuyAmt));
dailySellAmt = dailySellAmt.add(defaultZero(o.dailySellAmt));
dailyBuyAmt = dailyBuyAmt.add(defaultZero(o.dailyBuyAmt));
diffAmt = diffAmt.add(defaultZero(o.diffAmt));
}
TradeAmtBundle addCopy(TradeAmtBundle o) {
TradeAmtBundle n = zeros();
n.addInPlace(this);
n.addInPlace(o != null ? o : zeros());
return n;
}
}
}