package com.doumee.service.business.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.doumee.core.constants.Constants; import com.doumee.core.utils.Utils; import com.doumee.dao.business.*; import com.doumee.dao.business.model.*; import com.doumee.dao.dto.DataBoardQueryDTO; import com.doumee.dao.vo.*; import com.doumee.service.business.DataBoardService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; @Slf4j @Service public class DataBoardServiceImpl implements DataBoardService { @Autowired private MemberMapper memberMapper; @Autowired private ShopInfoMapper shopInfoMapper; @Autowired private DriverInfoMapper driverInfoMapper; @Autowired private OrdersMapper ordersMapper; @Autowired private OrdersDetailMapper ordersDetailMapper; @Autowired private OtherOrdersMapper otherOrdersMapper; @Autowired private OrdersRefundMapper ordersRefundMapper; @Override public DataBoardVO overview(DataBoardQueryDTO query) { DataBoardVO vo = new DataBoardVO(); // 会员总数(不受时间/门店影响) vo.setMemberCount(memberMapper.selectCount(new QueryWrapper().lambda() .eq(Member::getDeleted, Constants.ZERO) .eq(Member::getStatus, Constants.ZERO))); // 门店总数(auditStatus=3 正式版本) vo.setShopCount(shopInfoMapper.selectCount(new QueryWrapper().lambda() .eq(ShopInfo::getAuditStatus, Constants.THREE) .eq(ShopInfo::getVersionType, Constants.ZERO) .eq(ShopInfo::getDeleted, Constants.ZERO))); // 司机总数(auditStatus=3 正式版本) vo.setDriverCount(driverInfoMapper.selectCount(new QueryWrapper().lambda() .eq(DriverInfo::getAuditStatus, Constants.THREE) .eq(DriverInfo::getVersionType, Constants.ZERO) .eq(DriverInfo::getDeleted, Constants.ZERO))); // 周期总订单数 vo.setOrderCount(ordersMapper.selectCount(buildOrderQueryWrapper(query))); // 周期营收 List orders = ordersMapper.selectList(buildOrderQueryWrapper(query)); long orderRevenue = 0L; for (Orders o : orders) { long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; long overdue = (o.getOverdueStatus() != null && o.getOverdueStatus() == 2 && o.getOverdueAmount() != null) ? o.getOverdueAmount() : 0L; orderRevenue += pay - refund + overdue; } // 加上逾期费用订单(OtherOrders type=2) QueryWrapper otherQw = new QueryWrapper<>(); otherQw.lambda() .eq(OtherOrders::getType, 2) .eq(OtherOrders::getPayStatus, Constants.ONE) .eq(OtherOrders::getDeleted, Constants.ZERO); if (query.getStartDate() != null) { otherQw.lambda().ge(OtherOrders::getPayTime, query.getStartDate()); } if (query.getEndDate() != null) { otherQw.lambda().le(OtherOrders::getPayTime, Utils.Date.getEnd(query.getEndDate())); } List otherOrders = otherOrdersMapper.selectList(otherQw); long otherRevenue = otherOrders.stream() .mapToLong(o -> o.getPayAccount() != null ? o.getPayAccount() : 0L).sum(); vo.setTotalRevenue(BigDecimal.valueOf(orderRevenue + otherRevenue) .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); // 行李类型占比 List orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); List luggageTypeList = new ArrayList<>(); if (!orderIds.isEmpty()) { List details = ordersDetailMapper.selectList(new QueryWrapper().lambda() .in(OrdersDetail::getOrderId, orderIds) .eq(OrdersDetail::getDeleted, Constants.ZERO)); Map> grouped = details.stream() .filter(d -> StringUtils.isNotBlank(d.getLuggageName())) .collect(Collectors.groupingBy(OrdersDetail::getLuggageName)); for (Map.Entry> entry : grouped.entrySet()) { LuggageTypeItem item = new LuggageTypeItem(); item.setLuggageName(entry.getKey()); item.setOrderCount((long) entry.getValue().stream().map(OrdersDetail::getOrderId).distinct().count()); item.setLuggageCount((long) entry.getValue().stream() .mapToInt(d -> d.getNum() != null ? d.getNum() : 0).sum()); luggageTypeList.add(item); } } vo.setLuggageTypeList(luggageTypeList); return vo; } @Override public List memberTrend() { Date startDate = get30DaysAgoStart(); List members = memberMapper.selectList(new QueryWrapper().lambda() .eq(Member::getDeleted, Constants.ZERO) .eq(Member::getStatus, Constants.ZERO) .ge(Member::getCreateTime, startDate)); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Map map = members.stream() .collect(Collectors.groupingBy(m -> sdf.format(m.getCreateTime()), Collectors.counting())); return buildDailyList(startDate, (date) -> { MemberTrendVO vo = new MemberTrendVO(); vo.setDate(date); vo.setCount(map.getOrDefault(date, 0L)); return vo; }); } @Override public List orderTrend() { Date startDate = get30DaysAgoStart(); List orders = ordersMapper.selectList(new QueryWrapper().lambda() .eq(Orders::getDeleted, Constants.ZERO) .in(Orders::getStatus, Constants.OrderStatus.waitDeposit.getKey(), Constants.OrderStatus.deposited.getKey(), Constants.OrderStatus.accepted.getKey(), Constants.OrderStatus.delivering.getKey(), Constants.OrderStatus.arrived.getKey(), Constants.OrderStatus.overdue.getKey()) .ge(Orders::getCreateTime, startDate)); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Map> grouped = orders.stream() .collect(Collectors.groupingBy(o -> sdf.format(o.getCreateTime()))); return buildDailyList(startDate, (date) -> { List dayOrders = grouped.getOrDefault(date, Collections.emptyList()); OrderTrendVO vo = new OrderTrendVO(); vo.setDate(date); vo.setLocalCount(dayOrders.stream().filter(o -> Constants.equalsInteger(o.getType(), Constants.ZERO)).count()); vo.setRemoteCount(dayOrders.stream().filter(o -> Constants.equalsInteger(o.getType(), Constants.ONE)).count()); return vo; }); } @Override public List revenueTrend() { Date startDate = get30DaysAgoStart(); List orders = ordersMapper.selectList(new QueryWrapper().lambda() .eq(Orders::getDeleted, Constants.ZERO) .in(Orders::getStatus, Constants.OrderStatus.waitDeposit.getKey(), Constants.OrderStatus.deposited.getKey(), Constants.OrderStatus.accepted.getKey(), Constants.OrderStatus.delivering.getKey(), Constants.OrderStatus.arrived.getKey(), Constants.OrderStatus.overdue.getKey()) .ge(Orders::getCreateTime, startDate)); List otherOrders = otherOrdersMapper.selectList(new QueryWrapper().lambda() .eq(OtherOrders::getType, 2) .eq(OtherOrders::getPayStatus, Constants.ONE) .eq(OtherOrders::getDeleted, Constants.ZERO) .ge(OtherOrders::getPayTime, startDate)); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Map localOrderRevenue = new HashMap<>(); Map remoteOrderRevenue = new HashMap<>(); for (Orders o : orders) { String date = sdf.format(o.getCreateTime()); long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; long overdue = (o.getOverdueStatus() != null && o.getOverdueStatus() == 2 && o.getOverdueAmount() != null) ? o.getOverdueAmount() : 0L; long rev = pay - refund + overdue; if (Constants.equalsInteger(o.getType(), Constants.ZERO)) { localOrderRevenue.merge(date, rev, Long::sum); } else { remoteOrderRevenue.merge(date, rev, Long::sum); } } Map otherRevenueMap = new HashMap<>(); for (OtherOrders oo : otherOrders) { String date = sdf.format(oo.getPayTime()); long amt = oo.getPayAccount() != null ? oo.getPayAccount() : 0L; otherRevenueMap.merge(date, amt, Long::sum); } return buildDailyList(startDate, (date) -> { RevenueTrendVO vo = new RevenueTrendVO(); vo.setDate(date); long local = localOrderRevenue.getOrDefault(date, 0L) + otherRevenueMap.getOrDefault(date, 0L); long remote = remoteOrderRevenue.getOrDefault(date, 0L); vo.setLocalRevenue(BigDecimal.valueOf(local).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); vo.setRemoteRevenue(BigDecimal.valueOf(remote).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); return vo; }); } @Override public ShopPerformanceVO shopPerformance(DataBoardQueryDTO query) { ShopPerformanceVO vo = new ShopPerformanceVO(); // 1. 总订单数 + 营收(status 1-6) QueryWrapper qw = buildOrderQueryWrapper(query); List orders = ordersMapper.selectList(qw); vo.setTotalOrderCount((long) orders.size()); long revenue = 0L; for (Orders o : orders) { long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; long overdue = o.getOverdueAmount() != null ? o.getOverdueAmount() : 0L; long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; revenue += pay + overdue - refund; } vo.setTotalRevenue(BigDecimal.valueOf(Math.max(revenue, 0L)) .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); // 2. 已完成订单数(status=7,同等筛选条件) QueryWrapper completedQw = new QueryWrapper<>(); completedQw.lambda() .eq(Orders::getDeleted, Constants.ZERO) .eq(Orders::getStatus, Constants.OrderStatus.finished.getKey()); if (query.getStartDate() != null) { completedQw.lambda().ge(Orders::getCreateTime, query.getStartDate()); } if (query.getEndDate() != null) { completedQw.lambda().le(Orders::getCreateTime, Utils.Date.getEnd(query.getEndDate())); } if (query.getShopId() != null) { completedQw.lambda().and(w -> w.eq(Orders::getDepositShopId, query.getShopId()) .or().eq(Orders::getTakeShopId, query.getShopId())); } vo.setCompletedCount(ordersMapper.selectCount(completedQw)); // 3. 已退款订单数(orders_refund status=1 的去重订单数) List orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); if (!orderIds.isEmpty()) { QueryWrapper refundQw = new QueryWrapper<>(); refundQw.lambda() .in(OrdersRefund::getOrderId, orderIds) .eq(OrdersRefund::getStatus, Constants.ONE) .eq(OrdersRefund::getDeleted, Constants.ZERO); List refundRecords = ordersRefundMapper.selectList(refundQw); long refundedOrderCount = refundRecords.stream() .map(OrdersRefund::getOrderId).distinct().count(); vo.setRefundedCount(refundedOrderCount); } else { vo.setRefundedCount(0L); } // 4. 订单完成率 & 退款率(分母 = status 1-6 + status 7) long totalCount = vo.getTotalOrderCount() + vo.getCompletedCount(); if (totalCount > 0) { BigDecimal hundred = BigDecimal.valueOf(100); vo.setCompletionRate(BigDecimal.valueOf(vo.getCompletedCount()) .multiply(hundred).divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP)); vo.setRefundRate(BigDecimal.valueOf(vo.getRefundedCount()) .multiply(hundred).divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP)); } else { vo.setCompletionRate(BigDecimal.ZERO); vo.setRefundRate(BigDecimal.ZERO); } return vo; } // ========== 私有方法 ========== private QueryWrapper buildOrderQueryWrapper(DataBoardQueryDTO query) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda() .eq(Orders::getDeleted, Constants.ZERO) .in(Orders::getStatus, Constants.OrderStatus.waitDeposit.getKey(), Constants.OrderStatus.deposited.getKey(), Constants.OrderStatus.accepted.getKey(), Constants.OrderStatus.delivering.getKey(), Constants.OrderStatus.arrived.getKey(), Constants.OrderStatus.overdue.getKey()); if (query.getStartDate() != null) { qw.lambda().ge(Orders::getCreateTime, query.getStartDate()); } if (query.getEndDate() != null) { qw.lambda().le(Orders::getCreateTime, Utils.Date.getEnd(query.getEndDate())); } if (query.getShopId() != null) { qw.lambda().and(w -> w.eq(Orders::getDepositShopId, query.getShopId()) .or().eq(Orders::getTakeShopId, query.getShopId())); } return qw; } private Date get30DaysAgoStart() { Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); cal.add(Calendar.DAY_OF_MONTH, -29); return cal.getTime(); } @FunctionalInterface private interface DailyVOBuilder { T build(String date); } private List buildDailyList(Date startDate, DailyVOBuilder builder) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); List result = new ArrayList<>(); Calendar loop = Calendar.getInstance(); loop.setTime(startDate); Calendar end = Calendar.getInstance(); while (!loop.after(end)) { result.add(builder.build(sdf.format(loop.getTime()))); loop.add(Calendar.DAY_OF_MONTH, 1); } return result; } }