|
|
@@ -0,0 +1,184 @@
|
|
|
+package org.springblade.salesPart.coupon.service.impl;
|
|
|
+
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import org.springblade.core.tenant.annotation.TenantIgnore;
|
|
|
+import org.springblade.core.excel.util.ExcelUtil;
|
|
|
+import org.springblade.core.tool.utils.BeanUtil;
|
|
|
+import org.springblade.salesPart.coupon.excel.CouponUsageRankExcel;
|
|
|
+import org.springblade.salesPart.coupon.mapper.CouponScreenMapper;
|
|
|
+import org.springblade.salesPart.coupon.screen.*;
|
|
|
+import org.springblade.salesPart.coupon.service.ICouponScreenService;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.YearMonth;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 优惠券大屏统计实现
|
|
|
+ *
|
|
|
+ * @author Rain
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class CouponScreenServiceImpl implements ICouponScreenService {
|
|
|
+
|
|
|
+ private final CouponScreenMapper couponScreenMapper;
|
|
|
+
|
|
|
+ private static final DateTimeFormatter DAY = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public CouponScreenOverviewVO overview(CouponScreenQuery query) {
|
|
|
+ normalizeQuery(query);
|
|
|
+ CouponScreenOverviewVO vo = new CouponScreenOverviewVO();
|
|
|
+ vo.setStoreName(null);
|
|
|
+ vo.setKpis(buildKpi(query));
|
|
|
+ vo.setTrend(buildTrend(query));
|
|
|
+ vo.setUsageRank(buildUsageRank(query));
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public Map<String, Object> acquireUseTrend(CouponScreenQuery query) {
|
|
|
+ normalizeQuery(query);
|
|
|
+ Map<String, Object> m = new HashMap<>(2);
|
|
|
+ m.put("trend", buildTrend(query));
|
|
|
+ return m;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<CustomerSelectVo> getCustomerList() {
|
|
|
+ return couponScreenMapper.getCustomerList();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @TenantIgnore
|
|
|
+ public void exportUsageRank(CouponScreenQuery query, HttpServletResponse response) {
|
|
|
+ normalizeQuery(query);
|
|
|
+ List<UsageRankItemVO> rankList = buildUsageRank(query);
|
|
|
+ List<CouponUsageRankExcel> excelList = BeanUtil.copy(rankList, CouponUsageRankExcel.class);
|
|
|
+ ExcelUtil.export(response, "优惠券使用排行", "使用排行", excelList, CouponUsageRankExcel.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void normalizeQuery(CouponScreenQuery query) {
|
|
|
+ if (query == null) {
|
|
|
+ throw new IllegalArgumentException("query不能为空");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private CouponScreenKpiVO buildKpi(CouponScreenQuery query) {
|
|
|
+ CouponScreenKpiVO k = new CouponScreenKpiVO();
|
|
|
+ CouponOrderAmountAggVO orderAmt = couponScreenMapper.sumOrderRedPacketByPayStatus(
|
|
|
+ emptyToNull(query.getTenantId()), emptyToNull(query.getYearMonth()));
|
|
|
+ CouponUserCouponCountAggVO uc = couponScreenMapper.sumUserCouponCounts(
|
|
|
+ emptyToNull(query.getTenantId()), emptyToNull(query.getYearMonth()));
|
|
|
+
|
|
|
+ k.setPaidRedPacketAmount(orderAmt == null ? BigDecimal.ZERO : nullSafe(orderAmt.getPaid()));
|
|
|
+ k.setUnpaidRedPacketAmount(orderAmt == null ? BigDecimal.ZERO : nullSafe(orderAmt.getUnpaid()));
|
|
|
+ k.setTotalReceived(uc == null ? 0L : nullSafe(uc.getTotalReceived()));
|
|
|
+ k.setTotalUsed(uc == null ? 0L : nullSafe(uc.getTotalUsed()));
|
|
|
+ return k;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<UsageRankItemVO> buildUsageRank(CouponScreenQuery query) {
|
|
|
+ List<UsageRankItemVO> list = couponScreenMapper.usageRankByCustomer(
|
|
|
+ emptyToNull(query.getTenantId()), emptyToNull(query.getYearMonth()), 50);
|
|
|
+ return list != null ? list : Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<CouponTrendDailyVO> buildTrend(CouponScreenQuery query) {
|
|
|
+ String ym = emptyToNull(query.getYearMonth());
|
|
|
+ LocalDate from;
|
|
|
+ LocalDate to;
|
|
|
+ if (ym != null) {
|
|
|
+ YearMonth m = YearMonth.parse(ym);
|
|
|
+ from = m.atDay(1);
|
|
|
+ to = m.atEndOfMonth();
|
|
|
+ } else {
|
|
|
+ to = LocalDate.now();
|
|
|
+ from = to.minusDays(89);
|
|
|
+ }
|
|
|
+ String trendFrom = from.format(DAY);
|
|
|
+ String trendTo = to.format(DAY);
|
|
|
+
|
|
|
+ List<CouponDailyOrderAmountVO> orderDaily = couponScreenMapper.dailyOrderRedPacket(
|
|
|
+ emptyToNull(query.getTenantId()), ym, trendFrom, trendTo);
|
|
|
+ List<CouponDailyCountVO> recvDaily = couponScreenMapper.dailyReceivedCount(
|
|
|
+ emptyToNull(query.getTenantId()), ym, trendFrom, trendTo);
|
|
|
+ List<CouponDailyCountVO> usedDaily = couponScreenMapper.dailyUsedCount(
|
|
|
+ emptyToNull(query.getTenantId()), ym, trendFrom, trendTo);
|
|
|
+
|
|
|
+ Map<String, BigDecimal> paidByDay = new HashMap<>();
|
|
|
+ Map<String, BigDecimal> unpaidByDay = new HashMap<>();
|
|
|
+ Map<String, Long> recvByDay = new HashMap<>();
|
|
|
+ Map<String, Long> usedByDay = new HashMap<>();
|
|
|
+ if (orderDaily != null) {
|
|
|
+ for (CouponDailyOrderAmountVO row : orderDaily) {
|
|
|
+ String key = normalizeDtKey(row.getDt());
|
|
|
+ paidByDay.put(key, nullSafe(row.getPaid()));
|
|
|
+ unpaidByDay.put(key, nullSafe(row.getUnpaid()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (recvDaily != null) {
|
|
|
+ for (CouponDailyCountVO row : recvDaily) {
|
|
|
+ recvByDay.put(normalizeDtKey(row.getDt()), nullSafe(row.getCnt()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (usedDaily != null) {
|
|
|
+ for (CouponDailyCountVO row : usedDaily) {
|
|
|
+ usedByDay.put(normalizeDtKey(row.getDt()), nullSafe(row.getCnt()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<CouponTrendDailyVO> out = new ArrayList<>();
|
|
|
+ for (LocalDate d = from; !d.isAfter(to); d = d.plusDays(1)) {
|
|
|
+ String key = d.format(DAY);
|
|
|
+ CouponTrendDailyVO row = new CouponTrendDailyVO();
|
|
|
+ row.setDate(key);
|
|
|
+ row.setPaidRedPacketAmount(paidByDay.getOrDefault(key, BigDecimal.ZERO));
|
|
|
+ row.setUnpaidRedPacketAmount(unpaidByDay.getOrDefault(key, BigDecimal.ZERO));
|
|
|
+ row.setReceivedCount(recvByDay.getOrDefault(key, 0L));
|
|
|
+ row.setUsedCount(usedByDay.getOrDefault(key, 0L));
|
|
|
+ out.add(row);
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String emptyToNull(String s) {
|
|
|
+ return StringUtils.hasText(s) ? s : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统一 yyyy-MM-dd,避免 JDBC 返回 Date/Timestamp 导致 key 对不上
|
|
|
+ */
|
|
|
+ private static String normalizeDtKey(Object dt) {
|
|
|
+ if (dt == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ if (dt instanceof java.sql.Date) {
|
|
|
+ return ((java.sql.Date) dt).toLocalDate().format(DAY);
|
|
|
+ }
|
|
|
+ if (dt instanceof Date) {
|
|
|
+ return new java.sql.Timestamp(((Date) dt).getTime()).toLocalDateTime().toLocalDate().format(DAY);
|
|
|
+ }
|
|
|
+ if (dt instanceof LocalDate) {
|
|
|
+ return ((LocalDate) dt).format(DAY);
|
|
|
+ }
|
|
|
+ String s = dt.toString();
|
|
|
+ return s.length() >= 10 ? s.substring(0, 10) : s;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static BigDecimal nullSafe(BigDecimal value) {
|
|
|
+ return value == null ? BigDecimal.ZERO : value;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Long nullSafe(Long value) {
|
|
|
+ return value == null ? 0L : value;
|
|
|
+ }
|
|
|
+}
|