mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-03 19:16:21 +00:00
feat 驾驶舱接口
This commit is contained in:
21
README.md
21
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交流群
|
||||
|
||||
微信扫码加入微信交流群
|
||||
<p>
|
||||
<img alt="微信图片_20240226144703" height="500" src="https://jsd.cdn.zzko.cn/gh/xxm1995/picx-images-hosting@master/connect/微信图片_20240310224719.4jnuoluofy.webp" width="330"/>
|
||||
<img alt="微信图片_20240226144703" height="500" src="https://jsd.cdn.zzko.cn/gh/xxm1995/picx-images-hosting@master/connect/7ea3e91d78f07dad16a480f5f48f74e.70a3dbwew7.webp" width="330"/>
|
||||
</p>
|
||||
|
||||
## 🍻 鸣谢
|
||||
|
@@ -1,10 +1,15 @@
|
||||
|
||||
2.0.4:
|
||||
- [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单
|
||||
- [ ] 首页驾驶舱功能: 各通道收入和支付情况
|
||||
- [x] 第一排: (数字格式)显示今日收入、支出金额,支付总订单数量、退款总订单数, 时间分支分为: 今日金额/昨日金额/七天内金额
|
||||
- [x] 第二排: (折线图)显示各通道支付分为支付金额和退款,时间分为: 今日金额/昨日金额/七天内金额
|
||||
- [x] 第三排: (饼图)显示各通道各支付方式数量和占比, 时间分为: 今日金额/昨日金额/七天内金额
|
||||
- [ ] 报表功能
|
||||
- [ ] 各通道收入和支付情况
|
||||
- [ ] 微信新增V3版本接口
|
||||
- [ ] 增加转账功能
|
||||
- [ ] 云闪付支持对账功能
|
||||
- [ ] 结算台DEMO增加云闪付示例
|
||||
|
||||
2.0.x 版本内容
|
||||
- [ ] 统一关闭接口增加使用撤销关闭订单
|
||||
|
@@ -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<Integer> getPayAmount(@ParameterObject CockpitReportQuery query){
|
||||
ValidationUtil.validateParam(query);
|
||||
return Res.ok(cockpitReportService.getPayAmount(query));
|
||||
}
|
||||
|
||||
@Operation(summary = "退款金额(分)")
|
||||
@GetMapping("/getRefundAmount")
|
||||
public ResResult<Integer> getRefundAmount(@ParameterObject CockpitReportQuery query){
|
||||
ValidationUtil.validateParam(query);
|
||||
return Res.ok(cockpitReportService.getRefundAmount(query));
|
||||
}
|
||||
|
||||
@Operation(summary = "支付订单数量")
|
||||
@GetMapping("/getPayOrderCount")
|
||||
public ResResult<Integer> getPayOrderCount(@ParameterObject CockpitReportQuery query){
|
||||
ValidationUtil.validateParam(query);
|
||||
return Res.ok(cockpitReportService.getPayOrderCount(query));
|
||||
}
|
||||
|
||||
@Operation(summary = "退款订单数量")
|
||||
@GetMapping("/getRefundOrderCount")
|
||||
public ResResult<Integer> getRefundOrderCount(@ParameterObject CockpitReportQuery query){
|
||||
ValidationUtil.validateParam(query);
|
||||
return Res.ok(cockpitReportService.getRefundOrderCount(query));
|
||||
}
|
||||
|
||||
@Operation(summary = "支付通道折线图")
|
||||
@GetMapping("/getPayChannelLine")
|
||||
public ResResult<List<ChannelLineReport>> getPayChannelLine(@ParameterObject CockpitReportQuery query){
|
||||
ValidationUtil.validateParam(query);
|
||||
return Res.ok(cockpitReportService.getPayChannelLine(query));
|
||||
}
|
||||
|
||||
@Operation(summary = "退款通道折线图")
|
||||
@GetMapping("/getRefundChannelLine")
|
||||
public ResResult<List<ChannelLineReport>> getRefundChannelLine(@ParameterObject CockpitReportQuery query){
|
||||
ValidationUtil.validateParam(query);
|
||||
return Res.ok(cockpitReportService.getRefundChannelLine(query));
|
||||
}
|
||||
|
||||
}
|
@@ -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<ChannelOrderLine> getPayChannelLine(CockpitReportQuery query);
|
||||
|
||||
/**
|
||||
* 退款通道订单折线图
|
||||
*/
|
||||
List<ChannelOrderLine> getRefundChannelLine(CockpitReportQuery query);
|
||||
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
@@ -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<ChannelLineReport> getPayChannelLine(CockpitReportQuery query){
|
||||
Map<String, ChannelOrderLine> 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<ChannelLineReport> getRefundChannelLine(CockpitReportQuery query){
|
||||
Map<String, ChannelOrderLine> 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());
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
<?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="cn.bootx.platform.daxpay.service.core.report.dao.CockpitReportMapper">
|
||||
|
||||
<!-- 查询支付金额 -->
|
||||
<select id="getPayAmount" resultType="java.lang.Integer">
|
||||
SELECT
|
||||
sum(amount)
|
||||
FROM
|
||||
pay_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS IN ( 'success', 'refunding', 'partial_refund', 'refunded' );
|
||||
</select>
|
||||
|
||||
<!-- 查询退款金额 -->
|
||||
<select id="getRefundAmount" resultType="java.lang.Integer">
|
||||
SELECT
|
||||
sum(amount)
|
||||
FROM
|
||||
pay_refund_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS = 'success';
|
||||
</select>
|
||||
|
||||
<!-- 查询支付订单数量 -->
|
||||
<select id="getPayOrderCount" resultType="java.lang.Integer">
|
||||
SELECT
|
||||
count(*)
|
||||
FROM
|
||||
pay_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS IN ( 'success', 'refunding', 'partial_refund', 'refunded' );
|
||||
</select>
|
||||
|
||||
<!-- 查询退款订单数量 -->
|
||||
<select id="getRefundOrderCount" resultType="java.lang.Integer">
|
||||
SELECT
|
||||
count(*)
|
||||
FROM
|
||||
pay_refund_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS = 'success';
|
||||
</select>
|
||||
|
||||
<!-- 支付通道折线图 -->
|
||||
<select id="getPayChannelLine" resultType="cn.bootx.platform.daxpay.service.core.report.entity.ChannelOrderLine">
|
||||
SELECT
|
||||
channel,
|
||||
count(*) as count,
|
||||
sum(amount) as sum
|
||||
FROM
|
||||
pay_channel_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS IN ( 'success', 'refunding', 'partial_refund', 'refunded' )
|
||||
GROUP BY
|
||||
channel
|
||||
</select>
|
||||
|
||||
<!-- 退款通道折线图 -->
|
||||
<select id="getRefundChannelLine" resultType="cn.bootx.platform.daxpay.service.core.report.entity.ChannelOrderLine">
|
||||
SELECT
|
||||
channel,
|
||||
count(*) as count,
|
||||
sum(amount) as sum
|
||||
FROM
|
||||
pay_refund_channel_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS = 'success'
|
||||
GROUP BY
|
||||
channel
|
||||
</select>
|
||||
|
||||
<!-- 支付通道占比饼图 -->
|
||||
<select id="getPayChannelPie" resultType="cn.bootx.platform.daxpay.service.core.report.entity.ChannelProportionPie">
|
||||
SELECT
|
||||
channel,
|
||||
name,
|
||||
count(*) as count,
|
||||
sum(amount) as sum
|
||||
FROM
|
||||
pay_channel_order
|
||||
WHERE
|
||||
create_time BETWEEN #{startTime} AND #{endTime}
|
||||
AND STATUS IN ( 'success', 'refunding', 'partial_refund', 'refunded' )
|
||||
GROUP BY
|
||||
channel
|
||||
</select>
|
||||
|
||||
<!-- -->
|
||||
<select id="getRefundChannelPie" resultType="cn.bootx.platform.daxpay.service.core.report.entity.ChannelProportionPie">
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
Reference in New Issue
Block a user