改造为支持人天统计

This commit is contained in:
2026-06-07 21:05:53 +08:00
parent 6e249db5a1
commit 21ec3321d0
5 changed files with 119 additions and 10 deletions

View File

@@ -116,10 +116,11 @@ public class LbOrderRowController {
@PostMapping("/generate-daily-sum")
@Operation(
summary = "按日汇总生成 sum_data",
summary = "按日汇总生成 sum_data 与人天统计",
description =
"按购买时间区间、租户 id 查询 lb_order_row 明细每天生成一条 sum_datatoday_total_money_sum、"
+ "today_unresell_count、today_order_count 等)并写入表")
"按购买时间区间、租户 id 查询 lb_order_row 明细每天生成一条 sum_datatoday_total_money_sum、"
+ "today_unresell_count、today_order_count、avg_amt 等);同时按 buyer_phone + 购买日期"
+ "生成 day_stat 人天统计(当天购买总单数、当天购买总金额、平均金额)并写入表")
public ResponseEntity<Map<String, Object>> generateDailySum(
@Parameter(description = "汇总条件", required = true) @RequestBody
LbOrderRowGenerateSumRequest request) {

View File

@@ -146,4 +146,8 @@ public class LbOrderRow implements Serializable {
@TableField("today_order_count")
@Schema(description = "当天交易单数")
private Integer todayOrderCount;
@TableField("avg_amt")
@Schema(description = "平均金额sum_data 按日汇总时:总金额 / 当天交易单数)")
private BigDecimal avgAmt;
}

View File

@@ -49,7 +49,8 @@ public interface ILbOrderRowService extends IService<LbOrderRow> {
Integer hxrOrderStatus);
/**
* 按购买时间区间与租户查询明细,按天汇总为 {@code sum_data} 写入 {@code lb_order_row}。
* 按购买时间区间与租户查询明细,按天汇总为 {@code sum_data},并按 {@code buyer_phone} + 购买日期
* 生成 {@code day_stat} 人天统计,写入 {@code lb_order_row}。
*/
Map<String, Object> generateDailySumData(String buyTimeStart, String buyTimeEnd, String tenantId);

View File

@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
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;
@@ -41,6 +42,11 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
private static final String SUM_DATA_TYPE = "sum_data";
/** 按买家手机号 + 购买日期汇总的人天统计 */
private static final String BUYER_DAY_STAT_DATA_TYPE = "day_stat";
private static final long BUYER_DAY_STAT_ID_BASE = 20_000_000_000_000_000L;
private static final int DINGTALK_SELLER_CONFIRM_BATCH_SIZE = 15;
private static final String DINGTALK_SELLER_CONFIRM_PREFIX =
@@ -462,19 +468,45 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
String nowStr = LocalDateTime.now().format(DAY_TIME_FMT);
List<LbOrderRow> sumRows = new ArrayList<>(byDay.size());
List<LbOrderRow> buyerDayStatRows = new ArrayList<>();
int skippedNoBuyerPhone = 0;
for (Map.Entry<LocalDate, List<LbOrderRow>> entry : byDay.entrySet()) {
sumRows.add(buildDailySumRow(tid, entry.getKey(), entry.getValue(), nowStr));
LocalDate day = entry.getKey();
List<LbOrderRow> dayRows = entry.getValue();
sumRows.add(buildDailySumRow(tid, day, dayRows, nowStr));
Map<String, List<LbOrderRow>> byBuyerPhone = new LinkedHashMap<>();
for (LbOrderRow row : dayRows) {
String buyerPhone = trimToNull(row.getBuyerPhone());
if (buyerPhone == null) {
skippedNoBuyerPhone++;
continue;
}
byBuyerPhone.computeIfAbsent(buyerPhone, k -> new ArrayList<>()).add(row);
}
for (Map.Entry<String, List<LbOrderRow>> buyerEntry : byBuyerPhone.entrySet()) {
buyerDayStatRows.add(
buildBuyerDayStatRow(
tid, day, buyerEntry.getKey(), buyerEntry.getValue(), nowStr));
}
}
int upserted = upsertBatch(sumRows);
List<LbOrderRow> allStatRows = new ArrayList<>(sumRows.size() + buyerDayStatRows.size());
allStatRows.addAll(sumRows);
allStatRows.addAll(buyerDayStatRows);
int upserted = upsertBatch(allStatRows);
boolean ok = upserted >= 0;
result.put("success", ok);
result.put("message", ok ? "按日汇总完成" : "保存失败");
result.put("generated", ok ? upserted : 0);
result.put("sumDataGenerated", ok ? sumRows.size() : 0);
result.put("buyerDayStatGenerated", ok ? buyerDayStatRows.size() : 0);
result.put("sourceCount", details.size());
result.put("skippedNoBuyTime", skippedNoBuyTime);
result.put("skippedNoBuyerPhone", skippedNoBuyerPhone);
if (ok) {
result.put("data", sumRows);
result.put("buyerDayStatData", buyerDayStatRows);
}
return result;
} catch (Exception e) {
@@ -586,6 +618,11 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
}
}
int orderCount = dayRows.size();
BigDecimal avgAmt = orderCount > 0
? moneySum.divide(BigDecimal.valueOf(orderCount), 2, RoundingMode.HALF_UP)
: BigDecimal.ZERO;
String payTime = day.atStartOfDay().format(DAY_TIME_FMT);
Long id = resolveSumRowId(tenantId, payTime);
@@ -595,7 +632,8 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
sum.setDataType(SUM_DATA_TYPE);
sum.setTodayTotalMoneySum(moneySum);
sum.setTodayUnresellCount(unresellCount);
sum.setTodayOrderCount(dayRows.size());
sum.setTodayOrderCount(orderCount);
sum.setAvgAmt(avgAmt);
sum.setSellerId(SUM_PLACEHOLDER_USER_ID);
sum.setBuyerId(SUM_PLACEHOLDER_USER_ID);
sum.setOrderSn(SUM_ORDER_SN);
@@ -613,6 +651,70 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
return sum;
}
/** 按 buyer_phone + 购买日期生成人天统计day_stat */
private LbOrderRow buildBuyerDayStatRow(
String tenantId,
LocalDate day,
String buyerPhone,
List<LbOrderRow> buyerDayRows,
String nowStr) {
BigDecimal moneySum = BigDecimal.ZERO;
for (LbOrderRow row : buyerDayRows) {
if (row.getTotalMoney() != null) {
moneySum = moneySum.add(row.getTotalMoney());
}
}
int orderCount = buyerDayRows.size();
BigDecimal avgAmt = orderCount > 0
? moneySum.divide(BigDecimal.valueOf(orderCount), 2, RoundingMode.HALF_UP)
: BigDecimal.ZERO;
String payTime = day.atStartOfDay().format(DAY_TIME_FMT);
Long id = resolveBuyerDayStatRowId(tenantId, buyerPhone, payTime);
LbOrderRow sample = buyerDayRows.get(0);
LbOrderRow stat = new LbOrderRow();
stat.setId(id);
stat.setTenantId(tenantId);
stat.setDataType(BUYER_DAY_STAT_DATA_TYPE);
stat.setBuyerPhone(buyerPhone);
stat.setBuyerId(sample.getBuyerId());
stat.setBuyerName(sample.getBuyerName());
stat.setPhone(buyerPhone);
stat.setTodayTotalMoneySum(moneySum);
stat.setTodayOrderCount(orderCount);
stat.setAvgAmt(avgAmt);
stat.setTotalMoney(moneySum);
stat.setOrderSn("day_stat_" + buyerPhone + "_" + day.format(DAY_FMT));
stat.setPayTime(payTime);
stat.setBuyTime(payTime);
stat.setStatus(1);
stat.setIsShow(1);
stat.setCreatedAt(nowStr);
stat.setUpdatedAt(nowStr);
return stat;
}
/** 同一天、同一租户、同一 buyer_phone 已存在 day_stat 则复用其 id否则生成占位 id */
private Long resolveBuyerDayStatRowId(String tenantId, String buyerPhone, String payTime) {
LambdaQueryWrapper<LbOrderRow> w = new LambdaQueryWrapper<>();
w.eq(LbOrderRow::getTenantId, tenantId)
.eq(LbOrderRow::getDataType, BUYER_DAY_STAT_DATA_TYPE)
.eq(LbOrderRow::getBuyerPhone, buyerPhone)
.eq(LbOrderRow::getPayTime, payTime)
.last("LIMIT 1");
LbOrderRow existing = this.getOne(w, false);
if (existing != null && existing.getId() != null) {
return existing.getId();
}
LocalDate day = LocalDate.parse(payTime.substring(0, 10), DAY_FMT);
long dayKey = day.getYear() * 10000L + day.getMonthValue() * 100L + day.getDayOfMonth();
int phoneSlot = Math.floorMod(buyerPhone.hashCode(), 10000);
int tenantSlot = Math.floorMod(tenantId.hashCode(), 100);
return BUYER_DAY_STAT_ID_BASE + dayKey * 1_000_000L + phoneSlot * 100L + tenantSlot;
}
/** 同一天、同一租户已存在 sum_data 则复用其 id否则生成占位 id */
private Long resolveSumRowId(String tenantId, String payTime) {
LambdaQueryWrapper<LbOrderRow> w = new LambdaQueryWrapper<>();

View File

@@ -12,7 +12,7 @@
pay_time, pay_img, status, is_resell, is_show,
consignee, phone, province, city, area, address,
merchandise_id, confirm_time, buy_time, created_at, updated_at,
today_total_money_sum, today_unresell_count, today_order_count
today_total_money_sum, today_unresell_count, today_order_count, avg_amt
) VALUES
<foreach collection="list" item="item" separator=",">
(
@@ -23,7 +23,7 @@
#{item.payTime}, #{item.payImg}, #{item.status}, #{item.isResell}, #{item.isShow},
#{item.consignee}, #{item.phone}, #{item.province}, #{item.city}, #{item.area}, #{item.address},
#{item.merchandiseId}, #{item.confirmTime}, #{item.buyTime}, #{item.createdAt}, #{item.updatedAt},
#{item.todayTotalMoneySum}, #{item.todayUnresellCount}, #{item.todayOrderCount}
#{item.todayTotalMoneySum}, #{item.todayUnresellCount}, #{item.todayOrderCount}, #{item.avgAmt}
)
</foreach>
ON DUPLICATE KEY UPDATE
@@ -56,7 +56,8 @@
updated_at = VALUES(updated_at),
today_total_money_sum = VALUES(today_total_money_sum),
today_unresell_count = VALUES(today_unresell_count),
today_order_count = VALUES(today_order_count)
today_order_count = VALUES(today_order_count),
avg_amt = VALUES(avg_amt)
</insert>
<select id="selectLatestPaidStatsByTenantId" resultType="com.rj.dto.LbBuyerTradeStats">