| | |
| | | import com.doumee.dao.business.*; |
| | | import com.doumee.dao.business.model.*; |
| | | import com.doumee.dao.dto.DataBoardQueryDTO; |
| | | import com.doumee.dao.dto.FinanceQueryDTO; |
| | | import com.doumee.dao.dto.TrendQueryDTO; |
| | | import com.doumee.dao.vo.*; |
| | | import com.doumee.service.business.DataBoardService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | private OtherOrdersMapper otherOrdersMapper; |
| | | @Autowired |
| | | private OrdersRefundMapper ordersRefundMapper; |
| | | @Autowired |
| | | private RevenueMapper revenueMapper; |
| | | |
| | | @Override |
| | | public DataBoardVO overview(DataBoardQueryDTO query) { |
| | | query.resolveDateRange(); |
| | | DataBoardVO vo = new DataBoardVO(); |
| | | // 会员总数(不受时间/门店影响) |
| | | // 会员总数(不受时间/门店影响,userType=0 会员身份) |
| | | vo.setMemberCount(memberMapper.selectCount(new QueryWrapper<Member>().lambda() |
| | | .eq(Member::getDeleted, Constants.ZERO) |
| | | .eq(Member::getStatus, Constants.ZERO))); |
| | | .eq(Member::getUserType, Constants.ZERO))); |
| | | // 门店总数(auditStatus=3 正式版本) |
| | | vo.setShopCount(shopInfoMapper.selectCount(new QueryWrapper<ShopInfo>().lambda() |
| | | .eq(ShopInfo::getAuditStatus, Constants.THREE) |
| | |
| | | |
| | | // 行李类型占比 |
| | | List<Integer> orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); |
| | | List<LuggageTypeItem> luggageTypeList = new ArrayList<>(); |
| | | if (!orderIds.isEmpty()) { |
| | | List<OrdersDetail> details = ordersDetailMapper.selectList(new QueryWrapper<OrdersDetail>().lambda() |
| | | .in(OrdersDetail::getOrderId, orderIds) |
| | | .eq(OrdersDetail::getDeleted, Constants.ZERO)); |
| | | Map<String, List<OrdersDetail>> grouped = details.stream() |
| | | .filter(d -> StringUtils.isNotBlank(d.getLuggageName())) |
| | | .collect(Collectors.groupingBy(OrdersDetail::getLuggageName)); |
| | | for (Map.Entry<String, List<OrdersDetail>> 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(buildLuggageTypeList(orderIds)); |
| | | |
| | | // 门店入驻率:auditStatus=3 的门店数 / 全部门店数 |
| | | long totalShopCount = shopInfoMapper.selectCount(new QueryWrapper<ShopInfo>().lambda() |
| | | .eq(ShopInfo::getVersionType, Constants.ZERO) |
| | | .eq(ShopInfo::getDeleted, Constants.ZERO)); |
| | | if (totalShopCount > 0) { |
| | | vo.setShopSettlementRate(BigDecimal.valueOf(vo.getShopCount()) |
| | | .multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(totalShopCount), 2, RoundingMode.HALF_UP)); |
| | | } else { |
| | | vo.setShopSettlementRate(BigDecimal.ZERO); |
| | | } |
| | | vo.setLuggageTypeList(luggageTypeList); |
| | | |
| | | // 司机通过率:auditStatus=3 的司机数 / 全部司机数 |
| | | long totalDriverCount = driverInfoMapper.selectCount(new QueryWrapper<DriverInfo>().lambda() |
| | | .eq(DriverInfo::getVersionType, Constants.ZERO) |
| | | .eq(DriverInfo::getDeleted, Constants.ZERO)); |
| | | if (totalDriverCount > 0) { |
| | | vo.setDriverPassRate(BigDecimal.valueOf(vo.getDriverCount()) |
| | | .multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(totalDriverCount), 2, RoundingMode.HALF_UP)); |
| | | } else { |
| | | vo.setDriverPassRate(BigDecimal.ZERO); |
| | | } |
| | | |
| | | // 周期退款单数 |
| | | if (!orderIds.isEmpty()) { |
| | | QueryWrapper<OrdersRefund> refundQw = new QueryWrapper<>(); |
| | | refundQw.lambda() |
| | | .in(OrdersRefund::getOrderId, orderIds) |
| | | .eq(OrdersRefund::getStatus, Constants.ONE) |
| | | .eq(OrdersRefund::getDeleted, Constants.ZERO); |
| | | List<OrdersRefund> refundRecords = ordersRefundMapper.selectList(refundQw); |
| | | vo.setRefundOrderCount(refundRecords.stream() |
| | | .map(OrdersRefund::getOrderId).distinct().count()); |
| | | } else { |
| | | vo.setRefundOrderCount(0L); |
| | | } |
| | | |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public List<MemberTrendVO> memberTrend() { |
| | | Date startDate = get30DaysAgoStart(); |
| | | public List<MemberTrendVO> memberTrend(TrendQueryDTO query) { |
| | | TrendDateRange range = parseTrendDateRange(query); |
| | | List<Member> members = memberMapper.selectList(new QueryWrapper<Member>().lambda() |
| | | .eq(Member::getDeleted, Constants.ZERO) |
| | | .eq(Member::getStatus, Constants.ZERO) |
| | | .ge(Member::getCreateTime, startDate)); |
| | | .eq(Member::getUserType, Constants.ZERO) |
| | | .ge(Member::getCreateTime, range.startDate) |
| | | .le(Member::getCreateTime, range.endDate)); |
| | | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | SimpleDateFormat sdf = new SimpleDateFormat(range.pattern); |
| | | Map<String, Long> map = members.stream() |
| | | .collect(Collectors.groupingBy(m -> sdf.format(m.getCreateTime()), Collectors.counting())); |
| | | |
| | | return buildDailyList(startDate, (date) -> { |
| | | return buildTrendList(range, (key, label) -> { |
| | | MemberTrendVO vo = new MemberTrendVO(); |
| | | vo.setDate(date); |
| | | vo.setCount(map.getOrDefault(date, 0L)); |
| | | vo.setDate(label); |
| | | vo.setCount(map.getOrDefault(key, 0L)); |
| | | return vo; |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public List<OrderTrendVO> orderTrend() { |
| | | Date startDate = get30DaysAgoStart(); |
| | | public List<OrderTrendVO> orderTrend(TrendQueryDTO query) { |
| | | TrendDateRange range = parseTrendDateRange(query); |
| | | List<Orders> orders = ordersMapper.selectList(new QueryWrapper<Orders>().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)); |
| | | .ge(Orders::getCreateTime, range.startDate) |
| | | .le(Orders::getCreateTime, range.endDate)); |
| | | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | SimpleDateFormat sdf = new SimpleDateFormat(range.pattern); |
| | | Map<String, List<Orders>> grouped = orders.stream() |
| | | .collect(Collectors.groupingBy(o -> sdf.format(o.getCreateTime()))); |
| | | |
| | | return buildDailyList(startDate, (date) -> { |
| | | List<Orders> dayOrders = grouped.getOrDefault(date, Collections.emptyList()); |
| | | return buildTrendList(range, (key, label) -> { |
| | | List<Orders> dayOrders = grouped.getOrDefault(key, Collections.emptyList()); |
| | | OrderTrendVO vo = new OrderTrendVO(); |
| | | vo.setDate(date); |
| | | vo.setDate(label); |
| | | 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<RevenueTrendVO> revenueTrend() { |
| | | Date startDate = get30DaysAgoStart(); |
| | | public List<RevenueTrendVO> revenueTrend(TrendQueryDTO query) { |
| | | TrendDateRange range = parseTrendDateRange(query); |
| | | SimpleDateFormat sdf = new SimpleDateFormat(range.pattern); |
| | | |
| | | List<Orders> orders = ordersMapper.selectList(new QueryWrapper<Orders>().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)); |
| | | Constants.OrderStatus.waitDeposit.getKey(), |
| | | Constants.OrderStatus.deposited.getKey(), |
| | | Constants.OrderStatus.accepted.getKey(), |
| | | Constants.OrderStatus.delivering.getKey(), |
| | | Constants.OrderStatus.arrived.getKey(), |
| | | Constants.OrderStatus.overdue.getKey(), |
| | | Constants.OrderStatus.finished.getKey()) |
| | | .ge(Orders::getCreateTime, range.startDate) |
| | | .le(Orders::getCreateTime, range.endDate)); |
| | | |
| | | List<OtherOrders> otherOrders = otherOrdersMapper.selectList(new QueryWrapper<OtherOrders>().lambda() |
| | | .eq(OtherOrders::getType, 2) |
| | | .eq(OtherOrders::getPayStatus, Constants.ONE) |
| | | .eq(OtherOrders::getDeleted, Constants.ZERO) |
| | | .ge(OtherOrders::getPayTime, startDate)); |
| | | // 根据筛选出的订单主键查询已支付的逾期费用 |
| | | List<Integer> orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); |
| | | Map<Integer, Long> overduePaidMap = new HashMap<>(); |
| | | if (!orderIds.isEmpty()) { |
| | | List<OtherOrders> overdueOrders = otherOrdersMapper.selectList(new QueryWrapper<OtherOrders>().lambda() |
| | | .eq(OtherOrders::getType, 2) |
| | | .eq(OtherOrders::getPayStatus, Constants.ONE) |
| | | .eq(OtherOrders::getDeleted, Constants.ZERO) |
| | | .in(OtherOrders::getOrderId, orderIds)); |
| | | for (OtherOrders oo : overdueOrders) { |
| | | long amt = oo.getPayAccount() != null ? oo.getPayAccount() : 0L; |
| | | overduePaidMap.merge(oo.getOrderId(), amt, Long::sum); |
| | | } |
| | | } |
| | | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | Map<String, Long> localOrderRevenue = new HashMap<>(); |
| | | Map<String, Long> 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 overdue = overduePaidMap.getOrDefault(o.getId(), 0L); |
| | | long rev = pay - refund + overdue; |
| | | if (Constants.equalsInteger(o.getType(), Constants.ZERO)) { |
| | | localOrderRevenue.merge(date, rev, Long::sum); |
| | |
| | | } |
| | | } |
| | | |
| | | Map<String, Long> 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) -> { |
| | | return buildTrendList(range, (key, label) -> { |
| | | RevenueTrendVO vo = new RevenueTrendVO(); |
| | | vo.setDate(date); |
| | | long local = localOrderRevenue.getOrDefault(date, 0L) + otherRevenueMap.getOrDefault(date, 0L); |
| | | long remote = remoteOrderRevenue.getOrDefault(date, 0L); |
| | | vo.setDate(label); |
| | | long local = localOrderRevenue.getOrDefault(key, 0L); |
| | | long remote = remoteOrderRevenue.getOrDefault(key, 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) { |
| | | query.resolveDateRange(); |
| | | ShopPerformanceVO vo = new ShopPerformanceVO(); |
| | | |
| | | // 1. 总订单数 + 营收(status 1-6) |
| | |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public List<LuggageTypeItem> luggageTypeList(DataBoardQueryDTO query) { |
| | | query.resolveDateRange(); |
| | | List<Orders> orders = ordersMapper.selectList(buildOrderQueryWrapper(query)); |
| | | List<Integer> orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); |
| | | return buildLuggageTypeList(orderIds); |
| | | } |
| | | |
| | | @Override |
| | | public List<LuggageTypeItem> buildLuggageTypeList(List<Integer> orderIds) { |
| | | List<LuggageTypeItem> luggageTypeList = new ArrayList<>(); |
| | | if (orderIds != null && !orderIds.isEmpty()) { |
| | | List<OrdersDetail> details = ordersDetailMapper.selectList(new QueryWrapper<OrdersDetail>().lambda() |
| | | .in(OrdersDetail::getOrderId, orderIds) |
| | | .eq(OrdersDetail::getDeleted, Constants.ZERO)); |
| | | Map<String, List<OrdersDetail>> grouped = details.stream() |
| | | .filter(d -> StringUtils.isNotBlank(d.getLuggageName())) |
| | | .collect(Collectors.groupingBy(OrdersDetail::getLuggageName)); |
| | | for (Map.Entry<String, List<OrdersDetail>> 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); |
| | | } |
| | | } |
| | | return luggageTypeList; |
| | | } |
| | | |
| | | @Override |
| | | public List<FinanceOverviewVO> financeOverview(FinanceQueryDTO query) { |
| | | // 补齐日期:startDate 取月初,endDate 取月末 |
| | | Calendar startCal = Calendar.getInstance(); |
| | | startCal.setTime(query.getStartDate()); |
| | | startCal.set(Calendar.DAY_OF_MONTH, 1); |
| | | startCal.set(Calendar.HOUR_OF_DAY, 0); |
| | | startCal.set(Calendar.MINUTE, 0); |
| | | startCal.set(Calendar.SECOND, 0); |
| | | startCal.set(Calendar.MILLISECOND, 0); |
| | | query.setStartDate(startCal.getTime()); |
| | | |
| | | Calendar endCal = Calendar.getInstance(); |
| | | endCal.setTime(query.getEndDate()); |
| | | endCal.set(Calendar.DAY_OF_MONTH, endCal.getActualMaximum(Calendar.DAY_OF_MONTH)); |
| | | endCal.set(Calendar.HOUR_OF_DAY, 23); |
| | | endCal.set(Calendar.MINUTE, 59); |
| | | endCal.set(Calendar.SECOND, 59); |
| | | endCal.set(Calendar.MILLISECOND, 999); |
| | | query.setEndDate(endCal.getTime()); |
| | | |
| | | List<Orders> orders = ordersMapper.selectList(buildFinanceOrderQueryWrapper(query)); |
| | | |
| | | // 根据筛选出的订单主键查询已支付的逾期费用 |
| | | List<Integer> orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); |
| | | Map<Integer, Long> overduePaidMap = new HashMap<>(); |
| | | if (!orderIds.isEmpty()) { |
| | | List<OtherOrders> overdueOrders = otherOrdersMapper.selectList(new QueryWrapper<OtherOrders>().lambda() |
| | | .eq(OtherOrders::getType, 2) |
| | | .eq(OtherOrders::getPayStatus, Constants.ONE) |
| | | .eq(OtherOrders::getDeleted, Constants.ZERO) |
| | | .in(OtherOrders::getOrderId, orderIds)); |
| | | for (OtherOrders oo : overdueOrders) { |
| | | long amt = oo.getPayAccount() != null ? oo.getPayAccount() : 0L; |
| | | overduePaidMap.merge(oo.getOrderId(), amt, Long::sum); |
| | | } |
| | | } |
| | | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM"); |
| | | Map<String, List<Orders>> grouped = orders.stream() |
| | | .collect(Collectors.groupingBy(o -> sdf.format(o.getCreateTime()))); |
| | | |
| | | // 生成完整年月列表 |
| | | List<String> months = new ArrayList<>(); |
| | | Calendar loop = Calendar.getInstance(); |
| | | loop.setTime(query.getStartDate()); |
| | | loop.set(Calendar.DAY_OF_MONTH, 1); |
| | | Calendar monthEnd = Calendar.getInstance(); |
| | | monthEnd.setTime(query.getEndDate()); |
| | | while (!loop.after(monthEnd)) { |
| | | months.add(sdf.format(loop.getTime())); |
| | | loop.add(Calendar.MONTH, 1); |
| | | } |
| | | |
| | | BigDecimal hundred = BigDecimal.valueOf(100); |
| | | List<FinanceOverviewVO> result = new ArrayList<>(); |
| | | for (String month : months) { |
| | | List<Orders> monthOrders = grouped.getOrDefault(month, Collections.emptyList()); |
| | | long storageRev = 0L, deliveryRev = 0L, shopFee = 0L, driverFeeTotal = 0L, refundTotal = 0L, overdueTotal = 0L; |
| | | for (Orders o : monthOrders) { |
| | | long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; |
| | | long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; |
| | | long overdue = overduePaidMap.getOrDefault(o.getId(), 0L); |
| | | long actual = pay - refund + overdue; |
| | | long depFee = o.getDepositShopFee() != null ? o.getDepositShopFee() : 0L; |
| | | long takeFee = o.getTakeShopFee() != null ? o.getTakeShopFee() : 0L; |
| | | long dFee = Constants.equalsInteger(o.getType(),Constants.ONE)&&o.getDriverFee() != null ? o.getDriverFee() : 0L; |
| | | if (Constants.equalsInteger(o.getType(), Constants.ZERO)) { |
| | | storageRev += actual; |
| | | shopFee += depFee; |
| | | } else { |
| | | deliveryRev += actual; |
| | | shopFee += depFee + takeFee; |
| | | } |
| | | driverFeeTotal += dFee; |
| | | refundTotal += refund; |
| | | overdueTotal += overdue; |
| | | } |
| | | long totalRev = storageRev + deliveryRev; |
| | | long netRev = totalRev - shopFee - driverFeeTotal; |
| | | |
| | | FinanceOverviewVO vo = new FinanceOverviewVO(); |
| | | vo.setDate(month); |
| | | vo.setStorageRevenue(BigDecimal.valueOf(storageRev).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setDeliveryRevenue(BigDecimal.valueOf(deliveryRev).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setTotalRevenue(BigDecimal.valueOf(totalRev).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setShopFeeTotal(BigDecimal.valueOf(shopFee).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setDriverFeeTotal(BigDecimal.valueOf(driverFeeTotal).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setRefundAmount(BigDecimal.valueOf(refundTotal).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setOverdueAmount(BigDecimal.valueOf(overdueTotal).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setNetRevenue(BigDecimal.valueOf(netRev).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | result.add(vo); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public List<ShopTopVO> shopTop(TrendQueryDTO query) { |
| | | TrendDateRange range = parseTrendDateRange(query); |
| | | List<Orders> orders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda() |
| | | .eq(Orders::getDeleted, Constants.ZERO) |
| | | .eq(Orders::getStatus, Constants.OrderStatus.finished.getKey()) |
| | | .ge(Orders::getCreateTime, range.startDate) |
| | | .le(Orders::getCreateTime, range.endDate)); |
| | | |
| | | // 根据筛选出的订单主键查询已支付的逾期费用 |
| | | List<Integer> orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); |
| | | Map<Integer, Long> overduePaidMap = new HashMap<>(); |
| | | if (!orderIds.isEmpty()) { |
| | | List<OtherOrders> overdueOrders = otherOrdersMapper.selectList(new QueryWrapper<OtherOrders>().lambda() |
| | | .eq(OtherOrders::getType, 2) |
| | | .eq(OtherOrders::getPayStatus, Constants.ONE) |
| | | .eq(OtherOrders::getDeleted, Constants.ZERO) |
| | | .in(OtherOrders::getOrderId, orderIds)); |
| | | for (OtherOrders oo : overdueOrders) { |
| | | long amt = oo.getPayAccount() != null ? oo.getPayAccount() : 0L; |
| | | overduePaidMap.merge(oo.getOrderId(), amt, Long::sum); |
| | | } |
| | | } |
| | | |
| | | // 按门店ID归集: key = shopId |
| | | Map<Integer, long[]> map = new LinkedHashMap<>(); |
| | | for (Orders o : orders) { |
| | | long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; |
| | | long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; |
| | | long overdue = overduePaidMap.getOrDefault(o.getId(), 0L); |
| | | boolean hasRefund = refund > 0; |
| | | long rev = pay - refund + overdue; |
| | | |
| | | // 存件门店 |
| | | if (o.getDepositShopId() != null) { |
| | | long depFee = o.getDepositShopFee() != null ? o.getDepositShopFee() : 0L; |
| | | long[] arr = map.computeIfAbsent(o.getDepositShopId(), k -> new long[4]); // [count, revenue, fee, refundCount] |
| | | arr[0]++; |
| | | arr[1] += rev; |
| | | arr[2] += depFee; |
| | | if (hasRefund) arr[3]++; |
| | | } |
| | | // 异地且有取件门店 |
| | | if (Constants.equalsInteger(o.getType(), Constants.ONE) && o.getTakeShopId() != null) { |
| | | long takeFee = o.getTakeShopFee() != null ? o.getTakeShopFee() : 0L; |
| | | long[] arr = map.computeIfAbsent(o.getTakeShopId(), k -> new long[4]); |
| | | arr[0]++; |
| | | arr[1] += rev; |
| | | arr[2] += takeFee; |
| | | if (hasRefund) arr[3]++; |
| | | } |
| | | } |
| | | |
| | | // 批量查门店名称 |
| | | Set<Integer> shopIds = map.keySet(); |
| | | Map<Integer, String> shopNameMap = new HashMap<>(); |
| | | if (!shopIds.isEmpty()) { |
| | | List<ShopInfo> shops = shopInfoMapper.selectList(new QueryWrapper<ShopInfo>().lambda() |
| | | .in(ShopInfo::getId, shopIds) |
| | | .select(ShopInfo::getId, ShopInfo::getName)); |
| | | for (ShopInfo s : shops) { |
| | | shopNameMap.put(s.getId(), s.getName()); |
| | | } |
| | | } |
| | | |
| | | BigDecimal hundred = BigDecimal.valueOf(100); |
| | | List<ShopTopVO> result = new ArrayList<>(); |
| | | for (Map.Entry<Integer, long[]> entry : map.entrySet()) { |
| | | long[] arr = entry.getValue(); |
| | | ShopTopVO vo = new ShopTopVO(); |
| | | vo.setShopId(entry.getKey()); |
| | | vo.setShopName(shopNameMap.getOrDefault(entry.getKey(), "")); |
| | | vo.setCompletedCount(arr[0]); |
| | | vo.setTotalRevenue(BigDecimal.valueOf(arr[1]).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setShopFeeTotal(BigDecimal.valueOf(arr[2]).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setRefundCount(arr[3]); |
| | | vo.setPenaltyAmount(BigDecimal.ZERO); |
| | | result.add(vo); |
| | | } |
| | | result.sort((a, b) -> Long.compare(b.getCompletedCount(), a.getCompletedCount())); |
| | | return result.size() > 10 ? result.subList(0, 10) : result; |
| | | } |
| | | |
| | | @Override |
| | | public List<DriverTopVO> driverTop(TrendQueryDTO query) { |
| | | TrendDateRange range = parseTrendDateRange(query); |
| | | List<Orders> orders = ordersMapper.selectList(new QueryWrapper<Orders>().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.finished.getKey()) |
| | | .eq(Orders::getType, Constants.ONE) |
| | | .ge(Orders::getCreateTime, range.startDate) |
| | | .le(Orders::getCreateTime, range.endDate)); |
| | | |
| | | System.out.println( |
| | | orders.stream().map(i->i.getDriverFee()).reduce(0L,Long::sum) |
| | | ); |
| | | |
| | | // 根据筛选出的订单主键查询已支付的逾期费用 |
| | | List<Integer> orderIds = orders.stream().map(Orders::getId).collect(Collectors.toList()); |
| | | Map<Integer, Long> overduePaidMap = new HashMap<>(); |
| | | if (!orderIds.isEmpty()) { |
| | | List<OtherOrders> overdueOrders = otherOrdersMapper.selectList(new QueryWrapper<OtherOrders>().lambda() |
| | | .eq(OtherOrders::getType, 2) |
| | | .eq(OtherOrders::getPayStatus, Constants.ONE) |
| | | .eq(OtherOrders::getDeleted, Constants.ZERO) |
| | | .in(OtherOrders::getOrderId, orderIds)); |
| | | for (OtherOrders oo : overdueOrders) { |
| | | long amt = oo.getPayAccount() != null ? oo.getPayAccount() : 0L; |
| | | overduePaidMap.merge(oo.getOrderId(), amt, Long::sum); |
| | | } |
| | | } |
| | | |
| | | // 按司机归集: key = acceptDriver |
| | | Map<Integer, long[]> map = new LinkedHashMap<>(); |
| | | for (Orders o : orders) { |
| | | if (o.getAcceptDriver() == null) continue; |
| | | long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; |
| | | long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; |
| | | long overdue = overduePaidMap.getOrDefault(o.getId(), 0L); |
| | | long dFee = o.getDriverFee() != null ? o.getDriverFee() : 0L; |
| | | long[] arr = map.computeIfAbsent(o.getAcceptDriver(), k -> new long[4]); // [count, revenue, fee, refundCount] |
| | | arr[0]++; |
| | | arr[1] += pay - refund + overdue; |
| | | arr[2] += dFee; |
| | | if (refund > 0) arr[3]++; |
| | | } |
| | | |
| | | // 批量查司机姓名 |
| | | Set<Integer> driverIds = map.keySet(); |
| | | Map<Integer, String> driverNameMap = new HashMap<>(); |
| | | if (!driverIds.isEmpty()) { |
| | | List<DriverInfo> drivers = driverInfoMapper.selectList(new QueryWrapper<DriverInfo>().lambda() |
| | | .in(DriverInfo::getId, driverIds) |
| | | .select(DriverInfo::getMemberId, DriverInfo::getName)); |
| | | for (DriverInfo d : drivers) { |
| | | driverNameMap.put(d.getMemberId(), d.getName()); |
| | | } |
| | | } |
| | | |
| | | BigDecimal hundred = BigDecimal.valueOf(100); |
| | | List<DriverTopVO> result = new ArrayList<>(); |
| | | for (Map.Entry<Integer, long[]> entry : map.entrySet()) { |
| | | long[] arr = entry.getValue(); |
| | | DriverTopVO vo = new DriverTopVO(); |
| | | vo.setDriverId(entry.getKey()); |
| | | vo.setDriverName(driverNameMap.getOrDefault(entry.getKey(), "未知")); |
| | | vo.setCompletedCount(arr[0]); |
| | | vo.setTotalRevenue(BigDecimal.valueOf(arr[1]).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setDriverFeeTotal(BigDecimal.valueOf(arr[2]).divide(hundred, 2, RoundingMode.HALF_UP)); |
| | | vo.setRefundCount(arr[3]); |
| | | vo.setPenaltyAmount(BigDecimal.ZERO); |
| | | result.add(vo); |
| | | } |
| | | result.sort((a, b) -> Long.compare(b.getCompletedCount(), a.getCompletedCount())); |
| | | return result.size() > 10 ? result.subList(0, 10) : result; |
| | | } |
| | | |
| | | // ========== 私有方法 ========== |
| | | |
| | | private QueryWrapper<Orders> buildOrderQueryWrapper(DataBoardQueryDTO query) { |
| | |
| | | Constants.OrderStatus.accepted.getKey(), |
| | | Constants.OrderStatus.delivering.getKey(), |
| | | Constants.OrderStatus.arrived.getKey(), |
| | | Constants.OrderStatus.overdue.getKey()); |
| | | Constants.OrderStatus.finished.getKey()); |
| | | if (query.getStartDate() != null) { |
| | | qw.lambda().ge(Orders::getCreateTime, query.getStartDate()); |
| | | } |
| | |
| | | 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(); |
| | | private QueryWrapper<Orders> buildFinanceOrderQueryWrapper(FinanceQueryDTO query) { |
| | | QueryWrapper<Orders> 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.finished.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())); |
| | | } |
| | | return qw; |
| | | } |
| | | |
| | | private TrendDateRange parseTrendDateRange(TrendQueryDTO query) { |
| | | Calendar now = Calendar.getInstance(); |
| | | TrendDateRange range = new TrendDateRange(); |
| | | |
| | | if (StringUtils.isNotBlank(query.getMonth())) { |
| | | // 按月查询:yyyy-MM |
| | | SimpleDateFormat parseSdf = new SimpleDateFormat("yyyy-MM"); |
| | | Date monthDate; |
| | | try { |
| | | monthDate = parseSdf.parse(query.getMonth()); |
| | | } catch (Exception e) { |
| | | throw new IllegalArgumentException("month 格式错误,应为 yyyy-MM"); |
| | | } |
| | | Calendar monthCal = Calendar.getInstance(); |
| | | monthCal.setTime(monthDate); |
| | | |
| | | // 当月1日起始 |
| | | monthCal.set(Calendar.DAY_OF_MONTH, 1); |
| | | monthCal.set(Calendar.HOUR_OF_DAY, 0); |
| | | monthCal.set(Calendar.MINUTE, 0); |
| | | monthCal.set(Calendar.SECOND, 0); |
| | | monthCal.set(Calendar.MILLISECOND, 0); |
| | | range.startDate = monthCal.getTime(); |
| | | |
| | | // 月末 |
| | | monthCal.set(Calendar.DAY_OF_MONTH, monthCal.getActualMaximum(Calendar.DAY_OF_MONTH)); |
| | | monthCal.set(Calendar.HOUR_OF_DAY, 23); |
| | | monthCal.set(Calendar.MINUTE, 59); |
| | | monthCal.set(Calendar.SECOND, 59); |
| | | monthCal.set(Calendar.MILLISECOND, 999); |
| | | Date monthEnd = monthCal.getTime(); |
| | | |
| | | // 不能超过当前时间 |
| | | range.endDate = monthEnd.after(now.getTime()) ? now.getTime() : monthEnd; |
| | | range.byMonth = true; |
| | | range.pattern = "MM-dd"; |
| | | } else if (StringUtils.isNotBlank(query.getYear())) { |
| | | // 按年查询:yyyy |
| | | int year = Integer.parseInt(query.getYear()); |
| | | Calendar yearStart = Calendar.getInstance(); |
| | | yearStart.set(Calendar.YEAR, year); |
| | | yearStart.set(Calendar.MONTH, Calendar.JANUARY); |
| | | yearStart.set(Calendar.DAY_OF_MONTH, 1); |
| | | yearStart.set(Calendar.HOUR_OF_DAY, 0); |
| | | yearStart.set(Calendar.MINUTE, 0); |
| | | yearStart.set(Calendar.SECOND, 0); |
| | | yearStart.set(Calendar.MILLISECOND, 0); |
| | | range.startDate = yearStart.getTime(); |
| | | |
| | | Calendar yearEnd = Calendar.getInstance(); |
| | | yearEnd.set(Calendar.YEAR, year); |
| | | yearEnd.set(Calendar.MONTH, Calendar.DECEMBER); |
| | | yearEnd.set(Calendar.DAY_OF_MONTH, 31); |
| | | yearEnd.set(Calendar.HOUR_OF_DAY, 23); |
| | | yearEnd.set(Calendar.MINUTE, 59); |
| | | yearEnd.set(Calendar.SECOND, 59); |
| | | yearEnd.set(Calendar.MILLISECOND, 999); |
| | | Date end = yearEnd.getTime(); |
| | | range.endDate = end.after(now.getTime()) ? now.getTime() : end; |
| | | range.byMonth = false; |
| | | range.pattern = "MM"; |
| | | } else { |
| | | throw new IllegalArgumentException("请传入 month 或 year 参数"); |
| | | } |
| | | return range; |
| | | } |
| | | |
| | | private static class TrendDateRange { |
| | | Date startDate; |
| | | Date endDate; |
| | | boolean byMonth; // true=按月查(按日返回),false=按年查(按月返回) |
| | | String pattern; // SimpleDateFormat 格式 |
| | | } |
| | | |
| | | @FunctionalInterface |
| | | private interface DailyVOBuilder<T> { |
| | | T build(String date); |
| | | private interface TrendVOBuilder<T> { |
| | | T build(String key, String label); |
| | | } |
| | | |
| | | private <T> List<T> buildDailyList(Date startDate, DailyVOBuilder<T> builder) { |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | private <T> List<T> buildTrendList(TrendDateRange range, TrendVOBuilder<T> builder) { |
| | | List<T> 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); |
| | | SimpleDateFormat sdf = new SimpleDateFormat(range.pattern); |
| | | |
| | | if (range.byMonth) { |
| | | // 按日循环 |
| | | Calendar loop = Calendar.getInstance(); |
| | | loop.setTime(range.startDate); |
| | | Calendar end = Calendar.getInstance(); |
| | | end.setTime(range.endDate); |
| | | while (!loop.after(end)) { |
| | | String dateStr = sdf.format(loop.getTime()); |
| | | result.add(builder.build(dateStr, dateStr)); |
| | | loop.add(Calendar.DAY_OF_MONTH, 1); |
| | | } |
| | | } else { |
| | | // 按月循环 1~12,不能超过 endDate 所在月 |
| | | Calendar endCal = Calendar.getInstance(); |
| | | endCal.setTime(range.endDate); |
| | | int endMonth = endCal.get(Calendar.MONTH); // 0-based |
| | | for (int m = 0; m <= endMonth; m++) { |
| | | String key = String.format("%02d", m + 1); |
| | | String label = (m + 1) + "月"; |
| | | result.add(builder.build(key, label)); |
| | | } |
| | | } |
| | | return result; |
| | | } |