日/周/月营收统计

This commit is contained in:
yanglei 2025-11-28 14:28:42 +08:00
parent 107b7eb94b
commit 148896c5a6
7 changed files with 203 additions and 29 deletions

View File

@ -5,6 +5,7 @@ import com.cdzy.common.model.response.JsonResult;
import com.cdzy.user.model.dto.EbikeUserCyclingDto;
import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
import com.cdzy.user.model.entity.EbikeOrder;
import com.cdzy.user.model.vo.EbikeRevenueStatisticsVo;
import com.cdzy.user.model.vo.EbikeUserAllOrdersVo;
import com.cdzy.user.service.EbikeOrderService;
import com.ebike.feign.model.dto.FeignEbikeDto;
@ -120,4 +121,13 @@ public class EbikeOrderController {
return JsonResult.success(userAllOrders);
}
/**
* 查询营收统计
*/
@GetMapping("getRevenueStatistics")
public JsonResult<?> getRevenueStatistics() {
List<EbikeRevenueStatisticsVo> revenueStatistics = ebikeOrderService.getRevenueStatistics();
return JsonResult.success(revenueStatistics);
}
}

View File

@ -1,8 +1,11 @@
package com.cdzy.user.mapper;
import com.cdzy.user.model.entity.EbikeOrder;
import com.cdzy.user.model.vo.EbikeRevenueStatisticsVo;
import com.mybatisflex.core.BaseMapper;
import java.util.List;
/**
* 用户订单 映射层
*
@ -11,4 +14,11 @@ import com.mybatisflex.core.BaseMapper;
*/
public interface EbikeOrderMapper extends BaseMapper<EbikeOrder> {
/**
* 获取营收统计
*
* @return 营收统计
*/
List<EbikeRevenueStatisticsVo> selectRevenueComparison();
}

View File

@ -39,6 +39,6 @@ public class EbikeUserCyclingDto {
@Column(typeHandler = PGpointTypeHandler.class)
@JsonSerialize(using = PGpointSerializer.class)
@JsonDeserialize(using = PGpointDeserializer.class)
@NotNull(message = "骑行结束点不能为空")
@NotNull(message = "骑行开始点不能为空")
private PGpoint startPoint;
}

View File

@ -0,0 +1,65 @@
package com.cdzy.user.model.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* 营收统计
*
* @author yanglei
* @since 2025-11-28 09:21
*/
@Data
public class EbikeRevenueStatisticsVo {
/**
* 运营商ID
*/
private Long operatorId;
/**
* 当日营收
*/
private BigDecimal todayRevenue;
/**
* 昨日营收
*/
private BigDecimal yesterdayRevenue;
/**
* 当日比昨日比率
*/
private String todayGrowth;
/**
* 这周营收
*/
private BigDecimal thisWeekRevenue;
/**
* 上周营收
*/
private BigDecimal lastWeekRevenue;
/**
* 这周比上周比率
*/
private String weekGrowth;
/**
* 这月营收
*/
private BigDecimal thisMonthRevenue;
/**
* 上月营收
*/
private BigDecimal lastMonthRevenue;
/**
* 这月比上月比率
*/
private String monthGrowth;
}

View File

@ -4,6 +4,7 @@ import com.cdzy.common.model.request.PageParam;
import com.cdzy.user.model.dto.EbikeUserCyclingDto;
import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
import com.cdzy.user.model.entity.EbikeOrder;
import com.cdzy.user.model.vo.EbikeRevenueStatisticsVo;
import com.cdzy.user.model.vo.EbikeUserAllOrdersVo;
import com.ebike.feign.model.dto.FeignEbikeDto;
import com.ebike.feign.model.dto.FeignEbikeUserBikeInfo;
@ -119,4 +120,11 @@ public interface EbikeOrderService extends IService<EbikeOrder> {
* @return 用户订单
*/
Page<EbikeUserAllOrdersVo> getUserAllOrder(Long userId, Integer orderStatus, PageParam page);
/**
* 获取营收统计
*
* @return 营收统计
*/
List<EbikeRevenueStatisticsVo> getRevenueStatistics();
}

View File

@ -12,6 +12,7 @@ import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
import com.cdzy.user.model.entity.EbikeOrder;
import com.cdzy.user.model.entity.EbikeOrderDetail;
import com.cdzy.user.model.entity.EbikePayment;
import com.cdzy.user.model.vo.EbikeRevenueStatisticsVo;
import com.cdzy.user.model.vo.EbikeUserAllOrdersVo;
import com.cdzy.user.service.EbikeOrderDetailService;
import com.cdzy.user.service.EbikeOrderService;
@ -33,6 +34,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
@ -80,10 +82,12 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
throw new EbikeException("请完成未支付订单后再试");
}
}
FeignEbikeUserBikeInfo bikeInfo = queryBikeInfo(orderDto.getBikeCode());
// 创建订单
EbikeOrder order = EbikeOrder.builder()
.userId(userId)
.bikeCode(orderDto.getBikeCode())
.operatorId(bikeInfo.getOperatorId())
.orderType(OrderType.ONCE)
.startLocation(orderDto.getStartPoint())
.startTime(LocalDateTime.now())
@ -214,9 +218,7 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
order.setNoParkingZoneFee(bikeInfo.getNoParkingZoneFee());
order.setOutOfParkingAreaFee(bikeInfo.getOutOfParkingAreaFee());
order.setOutOfServiceAreaFee(bikeInfo.getOutOfServiceAreaFee());
order.setNoParkingZoneFee(bikeInfo.getMaxFeeAmount());
order.setOrderStatus(OrderStatus.PENDING_PAYMENT);
order.setMaxFeeAmount(bikeInfo.getMaxFeeAmount());
// 关锁
JsonResult<FeignEbikeUserLockDto> jsonResult = operationsFeignClient.lock(endDto.getBikeCode());
if (jsonResult.getCode() != Code.SUCCESS) {
@ -234,6 +236,7 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
updateById(order);
return order.getOrderId();
}
order.setOrderStatus(OrderStatus.PENDING_PAYMENT);
order.setTotalAmount(totalAmount);
order.setActualAmount(totalAmount);
updateById(order);
@ -272,38 +275,58 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
public Page<EbikeUserAllOrdersVo> getUserAllOrder(Long userId, Integer orderStatus, PageParam page) {
QueryWrapper queryWrapper = QueryWrapper.create()
.select(
EBIKE_ORDER.ORDER_ID,
EBIKE_REFUND.REFUND_ID,
EBIKE_ORDER.OPERATOR_ID,
EBIKE_ORDER.BIKE_CODE,
EBIKE_ORDER.ORDER_TYPE,
EBIKE_ORDER.ORDER_STATUS,
EBIKE_ORDER.TOTAL_AMOUNT,
EBIKE_ORDER.ACTUAL_AMOUNT,
EBIKE_ORDER.START_TIME,
EBIKE_ORDER.END_TIME,
EBIKE_ORDER.PAYMENT_TIME,
EBIKE_ORDER.PAYMENT_METHOD,
EBIKE_ORDER.BASE_FEE,
EBIKE_ORDER.DURATION_FEE,
EBIKE_ORDER.FREE_DURATION_MINUTES,
EBIKE_ORDER.CHARGE_DURATION_MINUTES,
EBIKE_ORDER.NO_PARKING_ZONE_FEE,
EBIKE_ORDER.OUT_OF_PARKING_AREA_FEE,
EBIKE_ORDER.OUT_OF_SERVICE_AREA_FEE,
EBIKE_ORDER.MAX_FEE_AMOUNT,
EBIKE_ORDER.HELMET_MANAGEMENT_FEE,
EBIKE_ORDER.START_LOCATION,
EBIKE_ORDER.END_LOCATION,
EBIKE_ORDER.CREATE_TIME
EBIKE_ORDER.ALL_COLUMNS,
EBIKE_REFUND.REFUND_ID
)
.where(EBIKE_ORDER.USER_ID.eq(userId))
.and(EBIKE_ORDER.ORDER_STATUS.eq(orderStatus,Objects.nonNull(orderStatus)))
.and(EBIKE_ORDER.ORDER_STATUS.eq(orderStatus, Objects.nonNull(orderStatus)))
.leftJoin(EBIKE_REFUND).on(EBIKE_REFUND.ORDER_ID.eq(EBIKE_ORDER.ORDER_ID));
queryWrapper.orderBy(EBIKE_ORDER.CREATE_TIME.desc());
return this.mapper.paginateAs(page.getPage(), queryWrapper, EbikeUserAllOrdersVo.class);
}
@Override
public List<EbikeRevenueStatisticsVo> getRevenueStatistics() {
List<EbikeRevenueStatisticsVo> list = this.mapper.selectRevenueComparison();
// 每个 operator 计算增长率
for (EbikeRevenueStatisticsVo vo : list) {
vo.setTodayGrowth(formatGrowthRate(vo.getTodayRevenue(), vo.getYesterdayRevenue()));
vo.setWeekGrowth(formatGrowthRate(vo.getThisWeekRevenue(), vo.getLastWeekRevenue()));
vo.setMonthGrowth(formatGrowthRate(vo.getThisMonthRevenue(), vo.getLastMonthRevenue()));
}
return list;
}
/**
* 计算增长率并格式化为百分比字符串
*
* @param current 当前周期营收
* @param previous 上一周期营收
* @return 格式化字符串 "13.64%""0.00%"""
*/
private String formatGrowthRate(BigDecimal current, BigDecimal previous) {
if (current == null) {
current = BigDecimal.ZERO;
}
if (previous == null) {
previous = BigDecimal.ZERO;
}
// previous 0则视为 1
BigDecimal denominator = previous.compareTo(BigDecimal.ZERO) == 0
? BigDecimal.ONE
: previous;
// 计算增长率(current - previous) / denominator * 100%
BigDecimal growth = current.subtract(previous)
.divide(denominator, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
return growth.setScale(2, RoundingMode.HALF_UP) + "%";
}
private List<EbikeOrderDetail> buildOrderDetails(EbikeOrder order) {
List<EbikeOrderDetail> details = new ArrayList<>();

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cdzy.user.mapper.EbikeOrderMapper">
<resultMap id="EbikeRevenueStatisticsVoMap" type="com.cdzy.user.model.vo.EbikeRevenueStatisticsVo">
<id column="operator_id" property="operatorId"/>
<result column="today_revenue" property="todayRevenue" javaType="java.math.BigDecimal"/>
<result column="yesterday_revenue" property="yesterdayRevenue" javaType="java.math.BigDecimal"/>
<result column="this_week_revenue" property="thisWeekRevenue" javaType="java.math.BigDecimal"/>
<result column="last_week_revenue" property="lastWeekRevenue" javaType="java.math.BigDecimal"/>
<result column="this_month_revenue" property="thisMonthRevenue" javaType="java.math.BigDecimal"/>
<result column="last_month_revenue" property="lastMonthRevenue" javaType="java.math.BigDecimal"/>
</resultMap>
<select id="selectRevenueComparison" resultMap="EbikeRevenueStatisticsVoMap">
SELECT operator_id,
COALESCE(SUM(CASE
WHEN payment_time &gt;= DATE_TRUNC('day', CURRENT_TIMESTAMP)
AND payment_time &lt; CURRENT_TIMESTAMP
THEN actual_amount END), 0) AS today_revenue,
COALESCE(SUM(CASE
WHEN payment_time &gt;= DATE_TRUNC('day', CURRENT_TIMESTAMP - INTERVAL '1 day')
AND payment_time &lt; DATE_TRUNC('day', CURRENT_TIMESTAMP)
THEN actual_amount END), 0) AS yesterday_revenue,
COALESCE(SUM(CASE
WHEN payment_time &gt;= DATE_TRUNC('week', CURRENT_TIMESTAMP)
AND payment_time &lt; CURRENT_TIMESTAMP
THEN actual_amount END), 0) AS this_week_revenue,
COALESCE(SUM(CASE
WHEN payment_time &gt;= DATE_TRUNC('week', CURRENT_TIMESTAMP - INTERVAL '7 days')
AND payment_time &lt; DATE_TRUNC('week', CURRENT_TIMESTAMP)
THEN actual_amount END), 0) AS last_week_revenue,
COALESCE(SUM(CASE
WHEN payment_time &gt;= DATE_TRUNC('month', CURRENT_TIMESTAMP)
AND payment_time &lt; CURRENT_TIMESTAMP
THEN actual_amount END), 0) AS this_month_revenue,
COALESCE(SUM(CASE
WHEN payment_time &gt;= DATE_TRUNC('month', CURRENT_TIMESTAMP - INTERVAL '1 month')
AND payment_time &lt; DATE_TRUNC('month', CURRENT_TIMESTAMP)
THEN actual_amount END), 0) AS last_month_revenue
FROM ebike_order
WHERE order_status = 3
AND payment_time IS NOT NULL
AND is_deleted = false
AND operator_id IS NOT NULL
GROUP BY operator_id
ORDER BY operator_id;
</select>
</mapper>