改造为支持人天统计
This commit is contained in:
@@ -116,10 +116,11 @@ public class LbOrderRowController {
|
|||||||
|
|
||||||
@PostMapping("/generate-daily-sum")
|
@PostMapping("/generate-daily-sum")
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "按日汇总生成 sum_data",
|
summary = "按日汇总生成 sum_data 与人天统计",
|
||||||
description =
|
description =
|
||||||
"按购买时间区间、租户 id 查询 lb_order_row 明细,每天生成一条 sum_data(today_total_money_sum、"
|
"按购买时间区间、租户 id 查询 lb_order_row 明细:每天生成一条 sum_data(today_total_money_sum、"
|
||||||
+ "today_unresell_count、today_order_count 等)并写入表")
|
+ "today_unresell_count、today_order_count、avg_amt 等);同时按 buyer_phone + 购买日期"
|
||||||
|
+ "生成 day_stat 人天统计(当天购买总单数、当天购买总金额、平均金额)并写入表")
|
||||||
public ResponseEntity<Map<String, Object>> generateDailySum(
|
public ResponseEntity<Map<String, Object>> generateDailySum(
|
||||||
@Parameter(description = "汇总条件", required = true) @RequestBody
|
@Parameter(description = "汇总条件", required = true) @RequestBody
|
||||||
LbOrderRowGenerateSumRequest request) {
|
LbOrderRowGenerateSumRequest request) {
|
||||||
|
|||||||
@@ -146,4 +146,8 @@ public class LbOrderRow implements Serializable {
|
|||||||
@TableField("today_order_count")
|
@TableField("today_order_count")
|
||||||
@Schema(description = "当天交易单数")
|
@Schema(description = "当天交易单数")
|
||||||
private Integer todayOrderCount;
|
private Integer todayOrderCount;
|
||||||
|
|
||||||
|
@TableField("avg_amt")
|
||||||
|
@Schema(description = "平均金额(sum_data 按日汇总时:总金额 / 当天交易单数)")
|
||||||
|
private BigDecimal avgAmt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ public interface ILbOrderRowService extends IService<LbOrderRow> {
|
|||||||
Integer hxrOrderStatus);
|
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);
|
Map<String, Object> generateDailySumData(String buyTimeStart, String buyTimeEnd, String tenantId);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
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 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 int DINGTALK_SELLER_CONFIRM_BATCH_SIZE = 15;
|
||||||
|
|
||||||
private static final String DINGTALK_SELLER_CONFIRM_PREFIX =
|
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);
|
String nowStr = LocalDateTime.now().format(DAY_TIME_FMT);
|
||||||
List<LbOrderRow> sumRows = new ArrayList<>(byDay.size());
|
List<LbOrderRow> sumRows = new ArrayList<>(byDay.size());
|
||||||
|
List<LbOrderRow> buyerDayStatRows = new ArrayList<>();
|
||||||
|
int skippedNoBuyerPhone = 0;
|
||||||
for (Map.Entry<LocalDate, List<LbOrderRow>> entry : byDay.entrySet()) {
|
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;
|
boolean ok = upserted >= 0;
|
||||||
result.put("success", ok);
|
result.put("success", ok);
|
||||||
result.put("message", ok ? "按日汇总完成" : "保存失败");
|
result.put("message", ok ? "按日汇总完成" : "保存失败");
|
||||||
result.put("generated", ok ? upserted : 0);
|
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("sourceCount", details.size());
|
||||||
result.put("skippedNoBuyTime", skippedNoBuyTime);
|
result.put("skippedNoBuyTime", skippedNoBuyTime);
|
||||||
|
result.put("skippedNoBuyerPhone", skippedNoBuyerPhone);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
result.put("data", sumRows);
|
result.put("data", sumRows);
|
||||||
|
result.put("buyerDayStatData", buyerDayStatRows);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} 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);
|
String payTime = day.atStartOfDay().format(DAY_TIME_FMT);
|
||||||
Long id = resolveSumRowId(tenantId, payTime);
|
Long id = resolveSumRowId(tenantId, payTime);
|
||||||
|
|
||||||
@@ -595,7 +632,8 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
|
|||||||
sum.setDataType(SUM_DATA_TYPE);
|
sum.setDataType(SUM_DATA_TYPE);
|
||||||
sum.setTodayTotalMoneySum(moneySum);
|
sum.setTodayTotalMoneySum(moneySum);
|
||||||
sum.setTodayUnresellCount(unresellCount);
|
sum.setTodayUnresellCount(unresellCount);
|
||||||
sum.setTodayOrderCount(dayRows.size());
|
sum.setTodayOrderCount(orderCount);
|
||||||
|
sum.setAvgAmt(avgAmt);
|
||||||
sum.setSellerId(SUM_PLACEHOLDER_USER_ID);
|
sum.setSellerId(SUM_PLACEHOLDER_USER_ID);
|
||||||
sum.setBuyerId(SUM_PLACEHOLDER_USER_ID);
|
sum.setBuyerId(SUM_PLACEHOLDER_USER_ID);
|
||||||
sum.setOrderSn(SUM_ORDER_SN);
|
sum.setOrderSn(SUM_ORDER_SN);
|
||||||
@@ -613,6 +651,70 @@ public class LbOrderRowServiceImpl extends ServiceImpl<LbOrderRowMapper, LbOrder
|
|||||||
return sum;
|
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 */
|
/** 同一天、同一租户已存在 sum_data 则复用其 id,否则生成占位 id */
|
||||||
private Long resolveSumRowId(String tenantId, String payTime) {
|
private Long resolveSumRowId(String tenantId, String payTime) {
|
||||||
LambdaQueryWrapper<LbOrderRow> w = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<LbOrderRow> w = new LambdaQueryWrapper<>();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
pay_time, pay_img, status, is_resell, is_show,
|
pay_time, pay_img, status, is_resell, is_show,
|
||||||
consignee, phone, province, city, area, address,
|
consignee, phone, province, city, area, address,
|
||||||
merchandise_id, confirm_time, buy_time, created_at, updated_at,
|
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
|
) VALUES
|
||||||
<foreach collection="list" item="item" separator=",">
|
<foreach collection="list" item="item" separator=",">
|
||||||
(
|
(
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
#{item.payTime}, #{item.payImg}, #{item.status}, #{item.isResell}, #{item.isShow},
|
#{item.payTime}, #{item.payImg}, #{item.status}, #{item.isResell}, #{item.isShow},
|
||||||
#{item.consignee}, #{item.phone}, #{item.province}, #{item.city}, #{item.area}, #{item.address},
|
#{item.consignee}, #{item.phone}, #{item.province}, #{item.city}, #{item.area}, #{item.address},
|
||||||
#{item.merchandiseId}, #{item.confirmTime}, #{item.buyTime}, #{item.createdAt}, #{item.updatedAt},
|
#{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>
|
</foreach>
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
@@ -56,7 +56,8 @@
|
|||||||
updated_at = VALUES(updated_at),
|
updated_at = VALUES(updated_at),
|
||||||
today_total_money_sum = VALUES(today_total_money_sum),
|
today_total_money_sum = VALUES(today_total_money_sum),
|
||||||
today_unresell_count = VALUES(today_unresell_count),
|
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>
|
</insert>
|
||||||
|
|
||||||
<select id="selectLatestPaidStatsByTenantId" resultType="com.rj.dto.LbBuyerTradeStats">
|
<select id="selectLatestPaidStatsByTenantId" resultType="com.rj.dto.LbBuyerTradeStats">
|
||||||
|
|||||||
Reference in New Issue
Block a user