部门交易金额

This commit is contained in:
2026-06-19 21:13:56 +08:00
parent ce72d87d12
commit df823119ed
5 changed files with 394 additions and 0 deletions

View File

@@ -761,6 +761,170 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
}
}
@Override
public Map<String, Object> getDepartmentUserTreeCopy(String tenantId, Integer isActive, LocalDate joinDate, LocalDate lastTradeDate,
String buyStartTime, String buyEndTime) {
Map<String, Object> result = new HashMap<>();
try {
if (tenantId == null || tenantId.trim().isEmpty()) {
result.put("success", false);
result.put("message", "租户id不能为空");
return result;
}
String tenantIdTrim = tenantId.trim();
List<LbUser> lbUsers = lbUserMapper.selectList(
new LambdaQueryWrapper<LbUser>().eq(LbUser::getTenantId, tenantIdTrim));
if (lbUsers == null || lbUsers.isEmpty()) {
result.put("success", true);
result.put("message", "查询成功");
result.put("data", new ArrayList<>());
result.put("total", 0);
result.put("tenantId", tenantIdTrim);
return result;
}
Map<String, LbUser> userById = new LinkedHashMap<>();
for (LbUser lbUser : lbUsers) {
if (lbUser.getId() != null) {
userById.put(String.valueOf(lbUser.getId()), lbUser);
}
}
Map<String, LbBuyerTradeStats> tradeStatsByUserId = loadBuyerTradeStatsFromOrderRow(tenantIdTrim);
boolean hasFilter = isActive != null || joinDate != null || lastTradeDate != null;
Set<String> matchedUserIds = new HashSet<>();
Map<String, LocalDate> joinDateByUserId = new HashMap<>();
Map<String, LocalDate> lastTradeDateByUserId = new HashMap<>();
Map<String, BigDecimal> lastBuyAmtByUserId = new HashMap<>();
Map<String, Integer> isActiveByUserId = new HashMap<>();
for (LbUser lbUser : lbUsers) {
if (lbUser.getId() == null) {
continue;
}
String userId = String.valueOf(lbUser.getId());
LocalDate userJoinDate = lbUser.getJoinTime() != null
? lbUser.getJoinTime().toLocalDate() : null;
LbBuyerTradeStats tradeStats = tradeStatsByUserId.get(userId);
LocalDate userLastTradeDate = tradeStats == null
? null : parseOrderTimeToDate(tradeStats.getPayTime());
BigDecimal userLastBuyAmt = tradeStats == null ? null : tradeStats.getTotalMoney();
int userIsActive = tradeStats == null ? 0 : 1;
joinDateByUserId.put(userId, userJoinDate);
lastTradeDateByUserId.put(userId, userLastTradeDate);
lastBuyAmtByUserId.put(userId, userLastBuyAmt);
isActiveByUserId.put(userId, userIsActive);
if (!hasFilter || matchesTreeFilter(
userIsActive, userJoinDate, userLastTradeDate, isActive, joinDate, lastTradeDate)) {
matchedUserIds.add(userId);
}
}
Set<String> visibleUserIds = hasFilter
? includeAncestorUserIds(matchedUserIds, userById)
: new HashSet<>(userById.keySet());
Map<String, Map<String, Object>> nodeByUserId = new LinkedHashMap<>();
Map<String, String> parentByUserId = new HashMap<>();
Map<String, List<String>> childrenByUserId = new HashMap<>();
for (String userId : visibleUserIds) {
LbUser lbUser = userById.get(userId);
if (lbUser == null) {
continue;
}
nodeByUserId.put(userId, toLbUserTreeNode(
lbUser,
joinDateByUserId.get(userId),
lastTradeDateByUserId.get(userId),
lastBuyAmtByUserId.get(userId),
isActiveByUserId.getOrDefault(userId, 0)));
String parentUserId = resolveLbUserParentId(lbUser, userById);
if (parentUserId != null && visibleUserIds.contains(parentUserId)) {
parentByUserId.put(userId, parentUserId);
childrenByUserId.computeIfAbsent(parentUserId, k -> new ArrayList<>()).add(userId);
}
}
boolean hasTradeTimeFilter = (buyStartTime != null && !buyStartTime.trim().isEmpty())
|| (buyEndTime != null && !buyEndTime.trim().isEmpty());
Map<String, BigDecimal> tradeMoneySumByUserId = new HashMap<>();
Map<String, BigDecimal> userTradeMoney = new HashMap<>();
if (hasTradeTimeFilter) {
if (!visibleUserIds.isEmpty()) {
List<Map<String, Object>> tradeResults = lbOrderRowMapper.selectSumTotalMoneyGroupByBuyerIds(
tenantIdTrim, new ArrayList<>(visibleUserIds), buyStartTime, buyEndTime);
for (Map<String, Object> row : tradeResults) {
String buyerId = String.valueOf(row.get("buyerId"));
BigDecimal totalMoney = (BigDecimal) row.get("totalMoney");
userTradeMoney.put(buyerId, totalMoney != null ? totalMoney : BigDecimal.ZERO);
}
for (String userId : visibleUserIds) {
BigDecimal sumAmount = calculateRecursiveTradeSumOptimized(userId, childrenByUserId, userTradeMoney);
tradeMoneySumByUserId.put(userId, sumAmount);
}
}
}
List<Map<String, Object>> treeRoots = new ArrayList<>();
for (Map.Entry<String, Map<String, Object>> entry : nodeByUserId.entrySet()) {
String userId = entry.getKey();
Map<String, Object> node = entry.getValue();
if (hasTradeTimeFilter) {
node.put("selfTradeMoney", userTradeMoney.getOrDefault(userId, BigDecimal.ZERO));
node.put("tradeMoneySum", tradeMoneySumByUserId.getOrDefault(userId, BigDecimal.ZERO));
}
String parentUserId = parentByUserId.get(userId);
Map<String, Object> parentNode = parentUserId == null ? null : nodeByUserId.get(parentUserId);
if (parentNode == null) {
treeRoots.add(node);
continue;
}
@SuppressWarnings("unchecked")
List<Map<String, Object>> children = (List<Map<String, Object>>) parentNode.get("children");
children.add(node);
}
sortDepartmentUserTreeByName(treeRoots);
List<Map<String, Object>> treeData = skipFirstTreeLevel(treeRoots);
result.put("success", true);
result.put("message", "查询成功");
result.put("data", treeData);
result.put("total", countTreeNodes(treeData));
result.put("tenantId", tenantIdTrim);
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "查询树形结构异常:" + e.getMessage());
return result;
}
}
/**
* 优化后的递归计算:从内存 Map 中获取数据,避免重复数据库查询
*/
private BigDecimal calculateRecursiveTradeSumOptimized(String userId, Map<String, List<String>> childrenByUserId,
Map<String, BigDecimal> userTradeMoney) {
BigDecimal sum = userTradeMoney.getOrDefault(userId, BigDecimal.ZERO);
List<String> children = childrenByUserId.get(userId);
if (children != null && !children.isEmpty()) {
for (String childId : children) {
sum = sum.add(calculateRecursiveTradeSumOptimized(childId, childrenByUserId, userTradeMoney));
}
}
return sum;
}
/**
* 去掉第 1 级根节点,将其 children 提升为返回给前端的顶层。
*/
@@ -1135,6 +1299,153 @@ public class LbDepartmentUserServiceImpl extends ServiceImpl<LbDepartmentUserMap
}
}
@Override
public Map<String, Object> getDepartmentUserTreeSumTotalMoney(String tenantId, Integer isActive, LocalDate joinDate, LocalDate lastTradeDate, String buyStartTime, String buyEndTime) {
Map<String, Object> result = new HashMap<>();
try {
if (tenantId == null || tenantId.trim().isEmpty()) {
result.put("success", false);
result.put("message", "租户id不能为空");
return result;
}
String tenantIdTrim = tenantId.trim();
List<LbUser> lbUsers = lbUserMapper.selectList(
new LambdaQueryWrapper<LbUser>().eq(LbUser::getTenantId, tenantIdTrim));
if (lbUsers == null || lbUsers.isEmpty()) {
result.put("success", true);
result.put("message", "查询成功");
result.put("data", new ArrayList<>());
result.put("total", 0);
result.put("tenantId", tenantIdTrim);
return result;
}
Map<String, LbUser> userById = new LinkedHashMap<>();
for (LbUser lbUser : lbUsers) {
if (lbUser.getId() != null) {
userById.put(String.valueOf(lbUser.getId()), lbUser);
}
}
Map<String, LbBuyerTradeStats> tradeStatsByUserId = loadBuyerTradeStatsFromOrderRow(tenantIdTrim);
boolean hasFilter = isActive != null || joinDate != null || lastTradeDate != null;
Set<String> matchedUserIds = new HashSet<>();
Map<String, LocalDate> joinDateByUserId = new HashMap<>();
Map<String, LocalDate> lastTradeDateByUserId = new HashMap<>();
Map<String, BigDecimal> lastBuyAmtByUserId = new HashMap<>();
Map<String, Integer> isActiveByUserId = new HashMap<>();
for (LbUser lbUser : lbUsers) {
if (lbUser.getId() == null) {
continue;
}
String userId = String.valueOf(lbUser.getId());
LocalDate userJoinDate = lbUser.getJoinTime() != null
? lbUser.getJoinTime().toLocalDate() : null;
LbBuyerTradeStats tradeStats = tradeStatsByUserId.get(userId);
LocalDate userLastTradeDate = tradeStats == null
? null : parseOrderTimeToDate(tradeStats.getPayTime());
BigDecimal userLastBuyAmt = tradeStats == null ? null : tradeStats.getTotalMoney();
int userIsActive = tradeStats == null ? 0 : 1;
joinDateByUserId.put(userId, userJoinDate);
lastTradeDateByUserId.put(userId, userLastTradeDate);
lastBuyAmtByUserId.put(userId, userLastBuyAmt);
isActiveByUserId.put(userId, userIsActive);
if (!hasFilter || matchesTreeFilter(
userIsActive, userJoinDate, userLastTradeDate, isActive, joinDate, lastTradeDate)) {
matchedUserIds.add(userId);
}
}
Set<String> visibleUserIds = hasFilter
? includeAncestorUserIds(matchedUserIds, userById)
: new HashSet<>(userById.keySet());
Map<String, Map<String, Object>> nodeByUserId = new LinkedHashMap<>();
Map<String, String> parentByUserId = new HashMap<>();
Map<String, List<String>> childrenByUserId = new HashMap<>();
for (String userId : visibleUserIds) {
LbUser lbUser = userById.get(userId);
if (lbUser == null) {
continue;
}
nodeByUserId.put(userId, toLbUserTreeNode(
lbUser,
joinDateByUserId.get(userId),
lastTradeDateByUserId.get(userId),
lastBuyAmtByUserId.get(userId),
isActiveByUserId.getOrDefault(userId, 0)));
String parentUserId = resolveLbUserParentId(lbUser, userById);
if (parentUserId != null && visibleUserIds.contains(parentUserId)) {
parentByUserId.put(userId, parentUserId);
childrenByUserId.computeIfAbsent(parentUserId, k -> new ArrayList<>()).add(userId);
}
}
boolean hasTradeTimeFilter = (buyStartTime != null && !buyStartTime.trim().isEmpty())
|| (buyEndTime != null && !buyEndTime.trim().isEmpty());
Map<String, BigDecimal> tradeMoneySumByUserId = new HashMap<>();
Map<String, BigDecimal> userTradeMoney = new HashMap<>();
if (hasTradeTimeFilter) {
if (!visibleUserIds.isEmpty()) {
List<Map<String, Object>> tradeResults = lbOrderRowMapper.selectSumTotalMoneyGroupByBuyerIds(
tenantIdTrim, new ArrayList<>(visibleUserIds), buyStartTime, buyEndTime);
for (Map<String, Object> row : tradeResults) {
String buyerId = String.valueOf(row.get("buyerId"));
BigDecimal totalMoney = (BigDecimal) row.get("totalMoney");
userTradeMoney.put(buyerId, totalMoney != null ? totalMoney : BigDecimal.ZERO);
}
for (String userId : visibleUserIds) {
BigDecimal sumAmount = calculateRecursiveTradeSumOptimized(userId, childrenByUserId, userTradeMoney);
tradeMoneySumByUserId.put(userId, sumAmount);
}
}
}
List<Map<String, Object>> treeRoots = new ArrayList<>();
for (Map.Entry<String, Map<String, Object>> entry : nodeByUserId.entrySet()) {
String userId = entry.getKey();
Map<String, Object> node = entry.getValue();
if (hasTradeTimeFilter) {
node.put("selfTradeMoney", userTradeMoney.getOrDefault(userId, BigDecimal.ZERO));
node.put("tradeMoneySum", tradeMoneySumByUserId.getOrDefault(userId, BigDecimal.ZERO));
}
String parentUserId = parentByUserId.get(userId);
Map<String, Object> parentNode = parentUserId == null ? null : nodeByUserId.get(parentUserId);
if (parentNode == null) {
treeRoots.add(node);
continue;
}
@SuppressWarnings("unchecked")
List<Map<String, Object>> children = (List<Map<String, Object>>) parentNode.get("children");
children.add(node);
}
sortDepartmentUserTreeByName(treeRoots);
List<Map<String, Object>> treeData = skipFirstTreeLevel(treeRoots);
result.put("success", true);
result.put("message", "查询成功");
result.put("data", treeData);
result.put("total", countTreeNodes(treeData));
result.put("tenantId", tenantIdTrim);
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "查询树形结构异常:" + e.getMessage());
return result;
}
}
private static final class OrderBuyStats {
private String earliestBuyTime;
private LocalDateTime earliestBuyDateTime;