统计交易金额
This commit is contained in:
@@ -411,6 +411,64 @@ public class LbDailyUserTradeReportController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats/user-trade-with-team/by-date-range-and-tenant")
|
||||||
|
@Operation(summary = "按日期范围统计本人及递归下级交易金额", description = "根据开始日期、结束日期、租户ID,基于 lb_department_user 上下级(parent_id 为父用户 user_id)与 lb_daily_user_trade_report,对 yestoday_buy_amt、daily_sell_amt、daily_buy_amt、diff_amt 四类分别汇总 own*/team*/total*(team 为全部下级递归,不含本人);不含 data_type=report_sum")
|
||||||
|
public ResponseEntity<Map<String, Object>> statsUserTradeWithTeamByDateRangeAndTenant(
|
||||||
|
@Parameter(description = "开始日期,格式:yyyy-MM-dd", required = true)
|
||||||
|
@RequestParam String startDate,
|
||||||
|
@Parameter(description = "结束日期,格式:yyyy-MM-dd", required = true)
|
||||||
|
@RequestParam String endDate,
|
||||||
|
@Parameter(description = "租户ID", required = true)
|
||||||
|
@RequestParam String tenantId) {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
try {
|
||||||
|
if (tenantId == null || tenantId.trim().isEmpty()) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "tenantId不能为空");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
if (startDate == null || startDate.trim().isEmpty()) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "startDate不能为空");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
if (endDate == null || endDate.trim().isEmpty()) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "endDate不能为空");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate parsedStartDate;
|
||||||
|
LocalDate parsedEndDate;
|
||||||
|
try {
|
||||||
|
parsedStartDate = LocalDate.parse(startDate.trim());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "startDate格式错误,请使用 yyyy-MM-dd");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
parsedEndDate = LocalDate.parse(endDate.trim());
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "endDate格式错误,请使用 yyyy-MM-dd");
|
||||||
|
return ResponseEntity.badRequest().body(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> serviceResult = lbDailyUserTradeReportService
|
||||||
|
.listUserTradeAmountWithTeamByDateRangeAndTenant(parsedStartDate, parsedEndDate, tenantId.trim());
|
||||||
|
Boolean success = (Boolean) serviceResult.get("success");
|
||||||
|
if (Boolean.FALSE.equals(success)) {
|
||||||
|
return ResponseEntity.badRequest().body(serviceResult);
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(serviceResult);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "统计异常:" + e.getMessage());
|
||||||
|
return ResponseEntity.internalServerError().body(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/calculate-report-sum/by-date-and-tenant")
|
@PostMapping("/calculate-report-sum/by-date-and-tenant")
|
||||||
@Operation(summary = "按日期和租户汇总报表", description = "根据报表日期和租户ID汇总报表字段并新增一条 report_sum 记录")
|
@Operation(summary = "按日期和租户汇总报表", description = "根据报表日期和租户ID汇总报表字段并新增一条 report_sum 记录")
|
||||||
public ResponseEntity<Map<String, Object>> calculateReportSumByDateAndTenant(
|
public ResponseEntity<Map<String, Object>> calculateReportSumByDateAndTenant(
|
||||||
|
|||||||
@@ -13,4 +13,10 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public interface ILbDailyUserTradeReportService extends IService<LbDailyUserTradeReport> {
|
public interface ILbDailyUserTradeReportService extends IService<LbDailyUserTradeReport> {
|
||||||
Map<String, Object> calculateReportSumByDateAndTenant(LocalDate reportDate, String tenantId);
|
Map<String, Object> calculateReportSumByDateAndTenant(LocalDate reportDate, String tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期范围与租户统计每人四类金额(昨日买入、当日卖出、当日买入、差额):本人汇总与递归下级汇总。
|
||||||
|
*/
|
||||||
|
Map<String, Object> listUserTradeAmountWithTeamByDateRangeAndTenant(
|
||||||
|
LocalDate startDate, LocalDate endDate, String tenantId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,24 @@ package com.rj.service.impl;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.rj.entity.LbDailyUserTradeReport;
|
import com.rj.entity.LbDailyUserTradeReport;
|
||||||
|
import com.rj.entity.LbDepartmentUser;
|
||||||
import com.rj.mapper.LbDailyUserTradeReportMapper;
|
import com.rj.mapper.LbDailyUserTradeReportMapper;
|
||||||
import com.rj.service.ILbDailyUserTradeReportService;
|
import com.rj.service.ILbDailyUserTradeReportService;
|
||||||
|
import com.rj.service.ILbDepartmentUserService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,6 +34,9 @@ public class LbDailyUserTradeReportServiceImpl extends ServiceImpl<LbDailyUserTr
|
|||||||
|
|
||||||
private static final String DATA_TYPE_REPORT_SUM = "report_sum";
|
private static final String DATA_TYPE_REPORT_SUM = "report_sum";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ILbDepartmentUserService lbDepartmentUserService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> calculateReportSumByDateAndTenant(LocalDate reportDate, String tenantId) {
|
public Map<String, Object> calculateReportSumByDateAndTenant(LocalDate reportDate, String tenantId) {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
@@ -81,7 +93,206 @@ public class LbDailyUserTradeReportServiceImpl extends ServiceImpl<LbDailyUserTr
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal defaultZero(BigDecimal value) {
|
private static BigDecimal defaultZero(BigDecimal value) {
|
||||||
return value == null ? BigDecimal.ZERO : 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user