diff --git a/README.md b/README.md index 69bb4366..d09e9ef9 100644 --- a/README.md +++ b/README.md @@ -163,24 +163,9 @@ public class SimplePayOrderTest { ## 🛣️ 路线图 > 当前处于功能开发阶段,部分功能可能会有调整,`V2.1.0`时将作为正式生产可用版本进行发布,之后会保证系统版本非大版本升级时,API接口和数据接口向前兼容 -[**开发进度和任务池**](/_doc/Task.md) - -[**更新记录**](/_doc/ChangeLog.md) - -### 2.0.X版本: -- [x] 对账比对功能实现 -- [ ] 支持转账、分账操作 -- [ ] 云闪付支付支持 -- [ ] 支付宝和微信增加V3版本接口支持 -- [ ] 消息通知支持消息中间件模式 - -### 2.1.X版本: -- [ ] 增加账户金额表 -- [ ] 增加统计管理 -- [ ] 支持微信消息通知 -- [ ] 支持钉钉消息通知 -- [ ] 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况 +[**当前开发进度和任务池**](/_doc/Task.md) +[**历史更新记录**](/_doc/ChangeLog.md) ## 🥂 Bootx 项目合集 - Bootx-Platform:单体版脚手架 [Gitee地址](https://gitee.com/bootx/bootx-platform) @@ -197,7 +182,7 @@ QQ扫码加入QQ交流群 微信扫码加入微信交流群

-微信图片_20240226144703 +微信图片_20240226144703

## 🍻 鸣谢 diff --git a/_doc/Task.md b/_doc/Task.md index 3f322585..66f6c9be 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -1,10 +1,15 @@ - 2.0.4: - [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单 - [ ] 首页驾驶舱功能: 各通道收入和支付情况 + - [x] 第一排: (数字格式)显示今日收入、支出金额,支付总订单数量、退款总订单数, 时间分支分为: 今日金额/昨日金额/七天内金额 + - [x] 第二排: (折线图)显示各通道支付分为支付金额和退款,时间分为: 今日金额/昨日金额/七天内金额 + - [x] 第三排: (饼图)显示各通道各支付方式数量和占比, 时间分为: 今日金额/昨日金额/七天内金额 +- [ ] 报表功能 + - [ ] 各通道收入和支付情况 - [ ] 微信新增V3版本接口 - [ ] 增加转账功能 - [ ] 云闪付支持对账功能 +- [ ] 结算台DEMO增加云闪付示例 2.0.x 版本内容 - [ ] 统一关闭接口增加使用撤销关闭订单 diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/report/CockpitReportController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/report/CockpitReportController.java new file mode 100644 index 00000000..526dd9f1 --- /dev/null +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/report/CockpitReportController.java @@ -0,0 +1,75 @@ +package cn.bootx.platform.daxpay.admin.controller.report; + +import cn.bootx.platform.common.core.annotation.IgnoreAuth; +import cn.bootx.platform.common.core.rest.Res; +import cn.bootx.platform.common.core.rest.ResResult; +import cn.bootx.platform.common.core.util.ValidationUtil; +import cn.bootx.platform.daxpay.service.core.report.service.CockpitReportService; +import cn.bootx.platform.daxpay.service.dto.report.ChannelLineReport; +import cn.bootx.platform.daxpay.service.param.report.CockpitReportQuery; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 驾驶舱接口 + * @author xxm + * @since 2024/3/17 + */ +@IgnoreAuth// TODO 测试用, 后续删除 +@Tag(name = "驾驶舱接口") +@RestController +@RequestMapping("/report/cockpit") +@RequiredArgsConstructor +public class CockpitReportController { + private final CockpitReportService cockpitReportService; + + @Operation(summary = "支付金额(分)") + @GetMapping("/getPayAmount") + public ResResult getPayAmount(@ParameterObject CockpitReportQuery query){ + ValidationUtil.validateParam(query); + return Res.ok(cockpitReportService.getPayAmount(query)); + } + + @Operation(summary = "退款金额(分)") + @GetMapping("/getRefundAmount") + public ResResult getRefundAmount(@ParameterObject CockpitReportQuery query){ + ValidationUtil.validateParam(query); + return Res.ok(cockpitReportService.getRefundAmount(query)); + } + + @Operation(summary = "支付订单数量") + @GetMapping("/getPayOrderCount") + public ResResult getPayOrderCount(@ParameterObject CockpitReportQuery query){ + ValidationUtil.validateParam(query); + return Res.ok(cockpitReportService.getPayOrderCount(query)); + } + + @Operation(summary = "退款订单数量") + @GetMapping("/getRefundOrderCount") + public ResResult getRefundOrderCount(@ParameterObject CockpitReportQuery query){ + ValidationUtil.validateParam(query); + return Res.ok(cockpitReportService.getRefundOrderCount(query)); + } + + @Operation(summary = "支付通道折线图") + @GetMapping("/getPayChannelLine") + public ResResult> getPayChannelLine(@ParameterObject CockpitReportQuery query){ + ValidationUtil.validateParam(query); + return Res.ok(cockpitReportService.getPayChannelLine(query)); + } + + @Operation(summary = "退款通道折线图") + @GetMapping("/getRefundChannelLine") + public ResResult> getRefundChannelLine(@ParameterObject CockpitReportQuery query){ + ValidationUtil.validateParam(query); + return Res.ok(cockpitReportService.getRefundChannelLine(query)); + } + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/dao/CockpitReportMapper.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/dao/CockpitReportMapper.java new file mode 100644 index 00000000..0654abcd --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/dao/CockpitReportMapper.java @@ -0,0 +1,48 @@ +package cn.bootx.platform.daxpay.service.core.report.dao; + +import cn.bootx.platform.daxpay.service.core.report.entity.ChannelOrderLine; +import cn.bootx.platform.daxpay.service.param.report.CockpitReportQuery; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 查询报表 + * @author xxm + * @since 2024/3/17 + */ +@Mapper +public interface CockpitReportMapper { + + /** + * 获取支付金额 + */ + Integer getPayAmount(CockpitReportQuery query); + + /** + * 获取退款金额 + */ + Integer getRefundAmount(CockpitReportQuery query); + + /** + * 获取支付订单数量 + */ + Integer getPayOrderCount(CockpitReportQuery query); + + /** + * 获取退款订单数量 + */ + Integer getRefundOrderCount(CockpitReportQuery query); + + /** + * 支付通道订单折线图 + */ + List getPayChannelLine(CockpitReportQuery query); + + /** + * 退款通道订单折线图 + */ + List getRefundChannelLine(CockpitReportQuery query); + + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/entity/ChannelOrderLine.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/entity/ChannelOrderLine.java new file mode 100644 index 00000000..aefdc54e --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/entity/ChannelOrderLine.java @@ -0,0 +1,25 @@ +package cn.bootx.platform.daxpay.service.core.report.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 通道订单折线图 + * @author xxm + * @since 2024/3/17 + */ +@Data +@Accessors(chain = true) +@Schema(title = "通道订单折线图") +public class ChannelOrderLine { + + @Schema(description = "通道编码") + private String channel; + + @Schema(description = "订单金额") + private Integer count; + + @Schema(description = "订单数量") + private Integer sum; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/entity/ChannelProportionPie.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/entity/ChannelProportionPie.java new file mode 100644 index 00000000..2d0762fa --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/entity/ChannelProportionPie.java @@ -0,0 +1,29 @@ +package cn.bootx.platform.daxpay.service.core.report.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 支付通道占比饼图 + * @author xxm + * @since 2024/3/17 + */ +@Data +@Accessors(chain = true) +@Schema(title = "支付通道占比饼图") +public class ChannelProportionPie { + + @Schema(description = "通道编码") + private String code; + + @Schema(description = "通道名称") + private String name; + + @Schema(description = "占比") + private Double proportion; + + @Schema(description = "数量") + private Integer count; + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/service/CockpitReportService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/service/CockpitReportService.java new file mode 100644 index 00000000..3432a730 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/report/service/CockpitReportService.java @@ -0,0 +1,108 @@ +package cn.bootx.platform.daxpay.service.core.report.service; + +import cn.bootx.platform.common.core.function.CollectorsFunction; +import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.service.core.report.dao.CockpitReportMapper; +import cn.bootx.platform.daxpay.service.core.report.entity.ChannelOrderLine; +import cn.bootx.platform.daxpay.service.dto.report.ChannelLineReport; +import cn.bootx.platform.daxpay.service.param.report.CockpitReportQuery; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 驾驶舱接口 + * @author xxm + * @since 2024/3/15 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CockpitReportService { + + private final CockpitReportMapper cockpitReportMapper; + + /** + * 支付金额(分) + */ + public Integer getPayAmount(CockpitReportQuery query){ + // 获取支付成功的订单 + return cockpitReportMapper.getPayAmount(query); + } + + /** + * 退款金额(分) + */ + public Integer getRefundAmount(CockpitReportQuery query){ + return cockpitReportMapper.getRefundAmount(query); + } + + /** + * 支付订单数量 + */ + public Integer getPayOrderCount(CockpitReportQuery query){ + return cockpitReportMapper.getPayOrderCount(query); + } + + /** + * 退款订单数量 + */ + public Integer getRefundOrderCount(CockpitReportQuery query){ + return cockpitReportMapper.getRefundOrderCount(query); + } + + /** + * (折线图)显示各通道支付分为支付金额和订单数, + */ + public List getPayChannelLine(CockpitReportQuery query){ + Map lineMap = cockpitReportMapper.getPayChannelLine(query) + .stream() + .collect(Collectors.toMap(ChannelOrderLine::getChannel, Function.identity(), CollectorsFunction::retainLatest)); + // 根据系统中有的通道编码,获取对应的通道名称 + return Arrays.stream(PayChannelEnum.values()) + .map(e->{ + ChannelOrderLine channelOrderLine = lineMap.get(e.getCode()); + ChannelLineReport channelLineReport = new ChannelLineReport() + .setChannelCode(e.getCode()) + .setChannelName(e.getName()); + if (Objects.isNull(channelOrderLine)){ + channelLineReport.setOrderAmount(0).setOrderCount(0); + } else { + channelLineReport.setOrderAmount(channelOrderLine.getCount()) + .setOrderCount(channelOrderLine.getSum()); + } + return channelLineReport; + }).collect(Collectors.toList()); + } + + /** + * (折线图)显示各通道退款金额和订单数, + */ + public List getRefundChannelLine(CockpitReportQuery query){ + Map lineMap = cockpitReportMapper.getRefundChannelLine(query) + .stream() + .collect(Collectors.toMap(ChannelOrderLine::getChannel, Function.identity(), CollectorsFunction::retainLatest)); + // 根据系统中有的通道编码,获取对应的通道名称 + return Arrays.stream(PayChannelEnum.values()) + .map(e->{ + ChannelOrderLine channelOrderLine = lineMap.get(e.getCode()); + ChannelLineReport channelLineReport = new ChannelLineReport() + .setChannelCode(e.getCode()) + .setChannelName(e.getName()); + if (Objects.isNull(channelOrderLine)){ + channelLineReport.setOrderAmount(0).setOrderCount(0); + } else { + channelLineReport.setOrderAmount(channelOrderLine.getCount()) + .setOrderCount(channelOrderLine.getSum()); + } + return channelLineReport; + }).collect(Collectors.toList()); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/report/ChannelLineReport.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/report/ChannelLineReport.java new file mode 100644 index 00000000..10c7e96f --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/report/ChannelLineReport.java @@ -0,0 +1,29 @@ +package cn.bootx.platform.daxpay.service.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 支付通道折线图报表 + * @author xxm + * @since 2024/3/17 + */ +@Data +@Accessors(chain = true) +@Schema(title = "支付通道折线图报表") +public class ChannelLineReport { + + @Schema(description = "支付通道编码") + private String channelCode; + + @Schema(description = "支付通道名称") + private String channelName; + + @Schema(description = "订单金额") + private Integer orderAmount; + + @Schema(description = "订单数量") + private Integer orderCount; + +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/report/CockpitReportQuery.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/report/CockpitReportQuery.java new file mode 100644 index 00000000..0c59ce38 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/report/CockpitReportQuery.java @@ -0,0 +1,35 @@ +package cn.bootx.platform.daxpay.service.param.report; + +import cn.hutool.core.date.DatePattern; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +/** + * 驾驶舱报表查询参数 + * @author xxm + * @since 2024/3/15 + */ +@Data +@Accessors(chain = true) +@Schema(title = "驾驶舱报表查询参数") +public class CockpitReportQuery { + + @NotNull(message = "开始时间不得为空") + @Schema(description = "开始时间") + @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + private LocalDateTime startTime; + + @NotNull(message = "开始时间不得为空") + @Schema(description = "开始时间") + @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + private LocalDateTime endTime; + +} diff --git a/daxpay-single/daxpay-single-service/src/main/resources/mapper/report/CockpitReportMapper.xml b/daxpay-single/daxpay-single-service/src/main/resources/mapper/report/CockpitReportMapper.xml new file mode 100644 index 00000000..ffba7400 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/resources/mapper/report/CockpitReportMapper.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +