| | |
| | | import com.doumee.dao.business.model.MemberRides; |
| | | import com.doumee.dao.business.model.MemberRidesTrack; |
| | | import com.doumee.dao.business.vo.BikeIncomeStatVO; |
| | | import com.doumee.dao.business.vo.BikeUsageStatVO; |
| | | import com.doumee.dao.business.vo.DashboardVO; |
| | | import com.doumee.dao.business.vo.IncomeDailyVO; |
| | | import com.doumee.dao.business.vo.IncomeStatVO; |
| | | import com.doumee.dao.business.vo.OperationCenterVO; |
| | |
| | | import com.doumee.dao.business.vo.OrderRideTrackVO; |
| | | import com.doumee.dao.business.vo.OrderRidesDetailVO; |
| | | import com.doumee.dao.business.vo.OverviewStatVO; |
| | | import com.doumee.dao.business.vo.PackageSourceStatVO; |
| | | import com.doumee.dao.business.web.request.BikeIncomeQueryDTO; |
| | | import com.doumee.dao.business.web.request.OperationOrderQueryDTO; |
| | | import com.doumee.service.business.ReportService; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Calendar; |
| | | import java.util.Collections; |
| | | import java.util.Comparator; |
| | | import java.util.Date; |
| | |
| | | |
| | | /** 百分比基数(增长率 = (本期 - 对比期) / 对比期 × 100) */ |
| | | private static final BigDecimal PERCENT_BASE = new BigDecimal("100"); |
| | | |
| | | /** 支付方式:抖音券核销(套餐销售来源识别用) */ |
| | | private static final int PAY_WAY_DOUYIN = 2; |
| | | |
| | | @Override |
| | | public OverviewStatVO overview() { |
| | |
| | | } |
| | | return "未知"; |
| | | } |
| | | |
| | | @Override |
| | | public IncomeStatVO incomeStat30() { |
| | | // 近30天(含今天共30天),复用 incomeStat 既有实现与口径 |
| | | BikeIncomeQueryDTO query = new BikeIncomeQueryDTO(); |
| | | query.setDateType(3); |
| | | return incomeStat(query); |
| | | } |
| | | |
| | | @Override |
| | | public BikeUsageStatVO bikeUsageStat() { |
| | | // status:0 空闲 / 1 使用中;禁用(3)不计入;type:0 自行车 / 1 电动车 |
| | | BikeUsageStatVO vo = new BikeUsageStatVO(); |
| | | vo.setBikeIdle(countBikeByStatus(Constants.ZERO, Constants.ZERO)); |
| | | vo.setBikeInUse(countBikeByStatus(Constants.ZERO, Constants.ONE)); |
| | | vo.setEleBikeIdle(countBikeByStatus(Constants.ONE, Constants.ZERO)); |
| | | vo.setEleBikeInUse(countBikeByStatus(Constants.ONE, Constants.ONE)); |
| | | return vo; |
| | | } |
| | | |
| | | /** |
| | | * 按车辆类型 + 状态统计未删除车辆数量。 |
| | | * |
| | | * @param type 车辆类型 0 自行车 / 1 电动车 |
| | | * @param status 车辆状态 0 空闲 / 1 使用中 |
| | | * @return 满足条件的车辆数 |
| | | */ |
| | | private long countBikeByStatus(Integer type, Integer status) { |
| | | return bikesMapper.selectCount( |
| | | new QueryWrapper<Bikes>().lambda() |
| | | .eq(Bikes::getType, type) |
| | | .eq(Bikes::getStatus, status) |
| | | .eq(Bikes::getIsdeleted, Constants.ZERO)); |
| | | } |
| | | |
| | | @Override |
| | | public PackageSourceStatVO packageSourceStat() { |
| | | // 本月/本年起止(pay_date 落在区间内);goodsorder type=1 套餐卡购买 + payStatus=1 已支付 |
| | | Calendar cal = Calendar.getInstance(); |
| | | Date monthStart = DateUtil.getStartOfDay(getFirstMs(cal, Calendar.MONTH)); |
| | | Date monthEnd = DateUtil.getEndOfDay(new Date()); |
| | | Date yearStart = DateUtil.getStartOfDay(getFirstMs(cal, Calendar.YEAR)); |
| | | Date yearEnd = DateUtil.getEndOfDay(new Date()); |
| | | |
| | | PackageSourceStatVO vo = new PackageSourceStatVO(); |
| | | vo.setMonth(countPackageSource(monthStart, monthEnd)); |
| | | vo.setYear(countPackageSource(yearStart, yearEnd)); |
| | | return vo; |
| | | } |
| | | |
| | | /** |
| | | * 统计指定时段内套餐销售来源数量。 |
| | | * <p>抖音兑换 = payWay 2,小程序购买 = payWay 0(微信)。 |
| | | * |
| | | * @param start 起始时间(含) |
| | | * @param end 结束时间(含) |
| | | * @return 抖音/小程序套餐数量 |
| | | */ |
| | | private PackageSourceStatVO.PeriodCount countPackageSource(Date start, Date end) { |
| | | List<Goodsorder> orders = goodsorderMapper.selectList( |
| | | new QueryWrapper<Goodsorder>().lambda() |
| | | .select(Goodsorder::getPayWay) |
| | | .eq(Goodsorder::getType, Constants.ONE) |
| | | .eq(Goodsorder::getPayStatus, Constants.ONE) |
| | | .eq(Goodsorder::getIsdeleted, Constants.ZERO) |
| | | .ge(Goodsorder::getPayDate, start) |
| | | .le(Goodsorder::getPayDate, end)); |
| | | long douyin = 0L; |
| | | long mini = 0L; |
| | | for (Goodsorder o : orders) { |
| | | if (o.getPayWay() == null) { |
| | | continue; |
| | | } |
| | | if (o.getPayWay() == PAY_WAY_DOUYIN) { |
| | | douyin++; |
| | | } else if (o.getPayWay() == Constants.ZERO) { |
| | | mini++; |
| | | } |
| | | } |
| | | PackageSourceStatVO.PeriodCount pc = new PackageSourceStatVO.PeriodCount(); |
| | | pc.setDouyinCount(douyin); |
| | | pc.setMiniCount(mini); |
| | | return pc; |
| | | } |
| | | |
| | | @Override |
| | | public DashboardVO dashboard() { |
| | | DashboardVO vo = new DashboardVO(); |
| | | |
| | | // 各时段起止:本月/昨日/今日 |
| | | Date now = new Date(); |
| | | Calendar cal = Calendar.getInstance(); |
| | | Date monthStart = DateUtil.getStartOfDay(getFirstMs(cal, Calendar.MONTH)); |
| | | Date monthEnd = DateUtil.getEndOfDay(now); |
| | | Date yesterdayStart = DateUtil.getStartOfDay(DateUtil.increaseDay(now, -1)); |
| | | Date yesterdayEnd = DateUtil.getEndOfDay(DateUtil.increaseDay(now, -1)); |
| | | Date todayStart = DateUtil.getStartOfDay(now); |
| | | Date todayEnd = DateUtil.getEndOfDay(now); |
| | | |
| | | // 收益(口径同 incomeStat:type=0 押金 + status=4 已结算 的 closeMoney),复用 sumClosedMoney |
| | | vo.setMonthIncome(sumClosedMoney(monthStart, monthEnd)); |
| | | vo.setYesterdayIncome(sumClosedMoney(yesterdayStart, yesterdayEnd)); |
| | | vo.setTodayIncome(sumClosedMoney(todayStart, todayEnd)); |
| | | |
| | | // 订单数:已支付(payStatus=1)订单计数 |
| | | vo.setMonthOrderCount(countPaidOrders(monthStart, monthEnd)); |
| | | vo.setYesterdayOrderCount(countPaidOrders(yesterdayStart, yesterdayEnd)); |
| | | vo.setTodayOrderCount(countPaidOrders(todayStart, todayEnd)); |
| | | |
| | | // 车辆:仅返回未删除总数 |
| | | vo.setTotalBikeCount((long) bikesMapper.selectCount( |
| | | new QueryWrapper<Bikes>().lambda().eq(Bikes::getIsdeleted, Constants.ZERO))); |
| | | |
| | | // 客户数:总会员=未删除全部;今日/昨日新增按 create_date 落在对应区间(与 overview 口径一致) |
| | | vo.setTotalMemberCount((long) memberMapper.selectCount( |
| | | new QueryWrapper<Member>().lambda().eq(Member::getIsdeleted, Constants.ZERO))); |
| | | vo.setYesterdayNewMember((long) memberMapper.selectCount( |
| | | new QueryWrapper<Member>().lambda() |
| | | .eq(Member::getIsdeleted, Constants.ZERO) |
| | | .ge(Member::getCreateDate, yesterdayStart) |
| | | .le(Member::getCreateDate, yesterdayEnd))); |
| | | vo.setTodayNewMember((long) memberMapper.selectCount( |
| | | new QueryWrapper<Member>().lambda() |
| | | .eq(Member::getIsdeleted, Constants.ZERO) |
| | | .ge(Member::getCreateDate, todayStart))); |
| | | return vo; |
| | | } |
| | | |
| | | /** |
| | | * 统计指定时段内已支付订单数量(payStatus=1)。 |
| | | * |
| | | * @param start 起始时间(含) |
| | | * @param end 结束时间(含) |
| | | * @return 已支付订单数 |
| | | */ |
| | | private long countPaidOrders(Date start, Date end) { |
| | | return goodsorderMapper.selectCount( |
| | | new QueryWrapper<Goodsorder>().lambda() |
| | | .eq(Goodsorder::getPayStatus, Constants.ONE) |
| | | .eq(Goodsorder::getIsdeleted, Constants.ZERO) |
| | | .ge(Goodsorder::getPayDate, start) |
| | | .le(Goodsorder::getPayDate, end)); |
| | | } |
| | | |
| | | /** |
| | | * 取本月/本年第一天的时间原值(时分秒归零前的毫秒),用于构造区间起始。 |
| | | * <p>用 Calendar 把「当月1日 00:00:00.000」或「当年1月1日 00:00:00.000」取出。 |
| | | * |
| | | * @param cal 日历(会被修改) |
| | | * @param field Calendar.MONTH 本月第一天 / Calendar.YEAR 本年第一天 |
| | | * @return 区间起始时间 |
| | | */ |
| | | private Date getFirstMs(Calendar cal, int field) { |
| | | cal.setTime(new Date()); |
| | | if (field == Calendar.MONTH) { |
| | | cal.set(Calendar.DAY_OF_MONTH, 1); |
| | | } else { |
| | | cal.set(Calendar.MONTH, Calendar.JANUARY); |
| | | cal.set(Calendar.DAY_OF_MONTH, 1); |
| | | } |
| | | cal.set(Calendar.HOUR_OF_DAY, 0); |
| | | cal.set(Calendar.MINUTE, 0); |
| | | cal.set(Calendar.SECOND, 0); |
| | | cal.set(Calendar.MILLISECOND, 0); |
| | | return cal.getTime(); |
| | | } |
| | | } |