日/周/月营收统计
This commit is contained in:
parent
107b7eb94b
commit
148896c5a6
@ -5,6 +5,7 @@ import com.cdzy.common.model.response.JsonResult;
|
|||||||
import com.cdzy.user.model.dto.EbikeUserCyclingDto;
|
import com.cdzy.user.model.dto.EbikeUserCyclingDto;
|
||||||
import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
|
import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
|
||||||
import com.cdzy.user.model.entity.EbikeOrder;
|
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.model.vo.EbikeUserAllOrdersVo;
|
||||||
import com.cdzy.user.service.EbikeOrderService;
|
import com.cdzy.user.service.EbikeOrderService;
|
||||||
import com.ebike.feign.model.dto.FeignEbikeDto;
|
import com.ebike.feign.model.dto.FeignEbikeDto;
|
||||||
@ -120,4 +121,13 @@ public class EbikeOrderController {
|
|||||||
return JsonResult.success(userAllOrders);
|
return JsonResult.success(userAllOrders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询营收统计。
|
||||||
|
*/
|
||||||
|
@GetMapping("getRevenueStatistics")
|
||||||
|
public JsonResult<?> getRevenueStatistics() {
|
||||||
|
List<EbikeRevenueStatisticsVo> revenueStatistics = ebikeOrderService.getRevenueStatistics();
|
||||||
|
return JsonResult.success(revenueStatistics);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package com.cdzy.user.mapper;
|
package com.cdzy.user.mapper;
|
||||||
|
|
||||||
import com.cdzy.user.model.entity.EbikeOrder;
|
import com.cdzy.user.model.entity.EbikeOrder;
|
||||||
|
import com.cdzy.user.model.vo.EbikeRevenueStatisticsVo;
|
||||||
import com.mybatisflex.core.BaseMapper;
|
import com.mybatisflex.core.BaseMapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户订单 映射层
|
* 用户订单 映射层
|
||||||
*
|
*
|
||||||
@ -11,4 +14,11 @@ import com.mybatisflex.core.BaseMapper;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public interface EbikeOrderMapper extends BaseMapper<EbikeOrder> {
|
public interface EbikeOrderMapper extends BaseMapper<EbikeOrder> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取营收统计
|
||||||
|
*
|
||||||
|
* @return 营收统计
|
||||||
|
*/
|
||||||
|
List<EbikeRevenueStatisticsVo> selectRevenueComparison();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,6 @@ public class EbikeUserCyclingDto {
|
|||||||
@Column(typeHandler = PGpointTypeHandler.class)
|
@Column(typeHandler = PGpointTypeHandler.class)
|
||||||
@JsonSerialize(using = PGpointSerializer.class)
|
@JsonSerialize(using = PGpointSerializer.class)
|
||||||
@JsonDeserialize(using = PGpointDeserializer.class)
|
@JsonDeserialize(using = PGpointDeserializer.class)
|
||||||
@NotNull(message = "骑行结束点不能为空")
|
@NotNull(message = "骑行开始点不能为空")
|
||||||
private PGpoint startPoint;
|
private PGpoint startPoint;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import com.cdzy.common.model.request.PageParam;
|
|||||||
import com.cdzy.user.model.dto.EbikeUserCyclingDto;
|
import com.cdzy.user.model.dto.EbikeUserCyclingDto;
|
||||||
import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
|
import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
|
||||||
import com.cdzy.user.model.entity.EbikeOrder;
|
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.model.vo.EbikeUserAllOrdersVo;
|
||||||
import com.ebike.feign.model.dto.FeignEbikeDto;
|
import com.ebike.feign.model.dto.FeignEbikeDto;
|
||||||
import com.ebike.feign.model.dto.FeignEbikeUserBikeInfo;
|
import com.ebike.feign.model.dto.FeignEbikeUserBikeInfo;
|
||||||
@ -119,4 +120,11 @@ public interface EbikeOrderService extends IService<EbikeOrder> {
|
|||||||
* @return 用户订单
|
* @return 用户订单
|
||||||
*/
|
*/
|
||||||
Page<EbikeUserAllOrdersVo> getUserAllOrder(Long userId, Integer orderStatus, PageParam page);
|
Page<EbikeUserAllOrdersVo> getUserAllOrder(Long userId, Integer orderStatus, PageParam page);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取营收统计
|
||||||
|
*
|
||||||
|
* @return 营收统计
|
||||||
|
*/
|
||||||
|
List<EbikeRevenueStatisticsVo> getRevenueStatistics();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import com.cdzy.user.model.dto.EbikeUserCyclingEndDto;
|
|||||||
import com.cdzy.user.model.entity.EbikeOrder;
|
import com.cdzy.user.model.entity.EbikeOrder;
|
||||||
import com.cdzy.user.model.entity.EbikeOrderDetail;
|
import com.cdzy.user.model.entity.EbikeOrderDetail;
|
||||||
import com.cdzy.user.model.entity.EbikePayment;
|
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.model.vo.EbikeUserAllOrdersVo;
|
||||||
import com.cdzy.user.service.EbikeOrderDetailService;
|
import com.cdzy.user.service.EbikeOrderDetailService;
|
||||||
import com.cdzy.user.service.EbikeOrderService;
|
import com.cdzy.user.service.EbikeOrderService;
|
||||||
@ -33,6 +34,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -80,10 +82,12 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
|
|||||||
throw new EbikeException("请完成未支付订单后再试");
|
throw new EbikeException("请完成未支付订单后再试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FeignEbikeUserBikeInfo bikeInfo = queryBikeInfo(orderDto.getBikeCode());
|
||||||
// 创建订单
|
// 创建订单
|
||||||
EbikeOrder order = EbikeOrder.builder()
|
EbikeOrder order = EbikeOrder.builder()
|
||||||
.userId(userId)
|
.userId(userId)
|
||||||
.bikeCode(orderDto.getBikeCode())
|
.bikeCode(orderDto.getBikeCode())
|
||||||
|
.operatorId(bikeInfo.getOperatorId())
|
||||||
.orderType(OrderType.ONCE)
|
.orderType(OrderType.ONCE)
|
||||||
.startLocation(orderDto.getStartPoint())
|
.startLocation(orderDto.getStartPoint())
|
||||||
.startTime(LocalDateTime.now())
|
.startTime(LocalDateTime.now())
|
||||||
@ -214,9 +218,7 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
|
|||||||
order.setNoParkingZoneFee(bikeInfo.getNoParkingZoneFee());
|
order.setNoParkingZoneFee(bikeInfo.getNoParkingZoneFee());
|
||||||
order.setOutOfParkingAreaFee(bikeInfo.getOutOfParkingAreaFee());
|
order.setOutOfParkingAreaFee(bikeInfo.getOutOfParkingAreaFee());
|
||||||
order.setOutOfServiceAreaFee(bikeInfo.getOutOfServiceAreaFee());
|
order.setOutOfServiceAreaFee(bikeInfo.getOutOfServiceAreaFee());
|
||||||
order.setNoParkingZoneFee(bikeInfo.getMaxFeeAmount());
|
order.setMaxFeeAmount(bikeInfo.getMaxFeeAmount());
|
||||||
order.setOrderStatus(OrderStatus.PENDING_PAYMENT);
|
|
||||||
|
|
||||||
// 关锁
|
// 关锁
|
||||||
JsonResult<FeignEbikeUserLockDto> jsonResult = operationsFeignClient.lock(endDto.getBikeCode());
|
JsonResult<FeignEbikeUserLockDto> jsonResult = operationsFeignClient.lock(endDto.getBikeCode());
|
||||||
if (jsonResult.getCode() != Code.SUCCESS) {
|
if (jsonResult.getCode() != Code.SUCCESS) {
|
||||||
@ -234,6 +236,7 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
|
|||||||
updateById(order);
|
updateById(order);
|
||||||
return order.getOrderId();
|
return order.getOrderId();
|
||||||
}
|
}
|
||||||
|
order.setOrderStatus(OrderStatus.PENDING_PAYMENT);
|
||||||
order.setTotalAmount(totalAmount);
|
order.setTotalAmount(totalAmount);
|
||||||
order.setActualAmount(totalAmount);
|
order.setActualAmount(totalAmount);
|
||||||
updateById(order);
|
updateById(order);
|
||||||
@ -272,38 +275,58 @@ public class EbikeOrderImpl extends ServiceImpl<EbikeOrderMapper, EbikeOrder> im
|
|||||||
public Page<EbikeUserAllOrdersVo> getUserAllOrder(Long userId, Integer orderStatus, PageParam page) {
|
public Page<EbikeUserAllOrdersVo> getUserAllOrder(Long userId, Integer orderStatus, PageParam page) {
|
||||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
.select(
|
.select(
|
||||||
EBIKE_ORDER.ORDER_ID,
|
EBIKE_ORDER.ALL_COLUMNS,
|
||||||
EBIKE_REFUND.REFUND_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
|
|
||||||
)
|
)
|
||||||
.where(EBIKE_ORDER.USER_ID.eq(userId))
|
.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));
|
.leftJoin(EBIKE_REFUND).on(EBIKE_REFUND.ORDER_ID.eq(EBIKE_ORDER.ORDER_ID));
|
||||||
queryWrapper.orderBy(EBIKE_ORDER.CREATE_TIME.desc());
|
queryWrapper.orderBy(EBIKE_ORDER.CREATE_TIME.desc());
|
||||||
return this.mapper.paginateAs(page.getPage(), queryWrapper, EbikeUserAllOrdersVo.class);
|
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) {
|
private List<EbikeOrderDetail> buildOrderDetails(EbikeOrder order) {
|
||||||
List<EbikeOrderDetail> details = new ArrayList<>();
|
List<EbikeOrderDetail> details = new ArrayList<>();
|
||||||
|
|||||||
58
ebike-user/src/main/resources/mapper/EbikeOrderMapper.xml
Normal file
58
ebike-user/src/main/resources/mapper/EbikeOrderMapper.xml
Normal 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 >= DATE_TRUNC('day', CURRENT_TIMESTAMP)
|
||||||
|
AND payment_time < CURRENT_TIMESTAMP
|
||||||
|
THEN actual_amount END), 0) AS today_revenue,
|
||||||
|
|
||||||
|
COALESCE(SUM(CASE
|
||||||
|
WHEN payment_time >= DATE_TRUNC('day', CURRENT_TIMESTAMP - INTERVAL '1 day')
|
||||||
|
AND payment_time < DATE_TRUNC('day', CURRENT_TIMESTAMP)
|
||||||
|
THEN actual_amount END), 0) AS yesterday_revenue,
|
||||||
|
|
||||||
|
COALESCE(SUM(CASE
|
||||||
|
WHEN payment_time >= DATE_TRUNC('week', CURRENT_TIMESTAMP)
|
||||||
|
AND payment_time < CURRENT_TIMESTAMP
|
||||||
|
THEN actual_amount END), 0) AS this_week_revenue,
|
||||||
|
|
||||||
|
COALESCE(SUM(CASE
|
||||||
|
WHEN payment_time >= DATE_TRUNC('week', CURRENT_TIMESTAMP - INTERVAL '7 days')
|
||||||
|
AND payment_time < DATE_TRUNC('week', CURRENT_TIMESTAMP)
|
||||||
|
THEN actual_amount END), 0) AS last_week_revenue,
|
||||||
|
|
||||||
|
COALESCE(SUM(CASE
|
||||||
|
WHEN payment_time >= DATE_TRUNC('month', CURRENT_TIMESTAMP)
|
||||||
|
AND payment_time < CURRENT_TIMESTAMP
|
||||||
|
THEN actual_amount END), 0) AS this_month_revenue,
|
||||||
|
|
||||||
|
COALESCE(SUM(CASE
|
||||||
|
WHEN payment_time >= DATE_TRUNC('month', CURRENT_TIMESTAMP - INTERVAL '1 month')
|
||||||
|
AND payment_time < 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user