mall + pay:

1. 优化订单管理的接口实现
This commit is contained in:
YunaiV 2023-07-19 09:50:24 +08:00
parent 888c488206
commit 5dcb3db5d7
6 changed files with 73 additions and 109 deletions

View File

@ -56,31 +56,20 @@ public class PayOrderController {
return success(PayOrderConvert.INSTANCE.convert(payOrderService.getOrder(id))); return success(PayOrderConvert.INSTANCE.convert(payOrderService.getOrder(id)));
} }
// TODO 芋艿看看怎么优化下
@GetMapping("/get-detail") @GetMapping("/get-detail")
@Operation(summary = "获得支付订单详情") @Operation(summary = "获得支付订单详情")
@Parameter(name = "id", description = "编号", required = true, example = "1024") @Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('pay:order:query')") @PreAuthorize("@ss.hasPermission('pay:order:query')")
public CommonResult<PayOrderDetailsRespVO> getOrderDetail(@RequestParam("id") Long id) { public CommonResult<PayOrderDetailsRespVO> getOrderDetail(@RequestParam("id") Long id) {
PayOrderDO order = payOrderService.getOrder(id); PayOrderDO order = payOrderService.getOrder(id);
if (ObjectUtil.isNull(order)) { if (order == null) {
return success(new PayOrderDetailsRespVO()); return success(null);
} }
PayAppDO appDO = appService.getApp(order.getAppId()); // 拼接返回
PayChannelEnum channelEnum = PayChannelEnum.getByCode(order.getChannelCode()); PayAppDO app = appService.getApp(order.getAppId());
PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
// TODO @aquan文案都是前端 format return success(PayOrderConvert.INSTANCE.convert(order, orderExtension, app));
PayOrderDetailsRespVO respVO = PayOrderConvert.INSTANCE.orderDetailConvert(order);
respVO.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
respVO.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
PayOrderExtensionDO extensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
if (ObjectUtil.isNotNull(extensionDO)) {
respVO.setPayOrderExtension(PayOrderConvert.INSTANCE.orderDetailExtensionConvert(extensionDO));
}
return success(respVO);
} }
@PostMapping("/submit") @PostMapping("/submit")
@ -90,6 +79,7 @@ public class PayOrderController {
return success(respVO); return success(respVO);
} }
// TODO 芋艿优化
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得支付订单分页") @Operation(summary = "获得支付订单分页")
@PreAuthorize("@ss.hasPermission('pay:order:query')") @PreAuthorize("@ss.hasPermission('pay:order:query')")
@ -116,6 +106,7 @@ public class PayOrderController {
return success(new PageResult<>(pageList, pageResult.getTotal())); return success(new PageResult<>(pageList, pageResult.getTotal()));
} }
// TODO 芋艿优化
@GetMapping("/export-excel") @GetMapping("/export-excel")
@Operation(summary = "导出支付订单Excel") @Operation(summary = "导出支付订单Excel")
@PreAuthorize("@ss.hasPermission('pay:order:export')") @PreAuthorize("@ss.hasPermission('pay:order:export')")

View File

@ -13,32 +13,30 @@ import java.time.LocalDateTime;
@ToString(callSuper = true) @ToString(callSuper = true)
public class PayOrderDetailsRespVO extends PayOrderBaseVO { public class PayOrderDetailsRespVO extends PayOrderBaseVO {
@Schema(description = "支付订单编号") @Schema(description = "支付订单编号", required = true, example = "1024")
private Long id; private Long id;
@Schema(description = "应用名称") @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String appName; private String appName;
@Schema(description = "渠道编号名称") @Schema(description = "创建时间", required = true)
private String channelCodeName;
@Schema(description = "创建时间")
private LocalDateTime createTime; private LocalDateTime createTime;
/** /**
* 支付订单扩展 * 支付订单扩展
*/ */
private PayOrderExtension payOrderExtension; private PayOrderExtension extension;
@Data @Data
@Schema(description = "支付订单扩展") @Schema(description = "支付订单扩展")
public static class PayOrderExtension { public static class PayOrderExtension {
@Schema(description = "支付订单号") @Schema(description = "支付订单号", required = true, example = "1024")
private String no; private String no;
@Schema(description = "支付异步通知的内容") @Schema(description = "支付异步通知的内容")
private String channelNotifyData; private String channelNotifyData;
} }
} }

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO; import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
@ -30,9 +31,16 @@ public interface PayOrderConvert {
PayOrderRespDTO convert2(PayOrderDO order); PayOrderRespDTO convert2(PayOrderDO order);
PayOrderDetailsRespVO orderDetailConvert(PayOrderDO bean); default PayOrderDetailsRespVO convert(PayOrderDO order, PayOrderExtensionDO orderExtension, PayAppDO app) {
PayOrderDetailsRespVO respVO = convertDetail(order);
PayOrderDetailsRespVO.PayOrderExtension orderDetailExtensionConvert(PayOrderExtensionDO bean); respVO.setExtension(convert(orderExtension));
if (app != null) {
respVO.setAppName(app.getName());
}
return respVO;
}
PayOrderDetailsRespVO convertDetail(PayOrderDO bean);
PayOrderDetailsRespVO.PayOrderExtension convert(PayOrderExtensionDO bean);
List<PayOrderRespVO> convertList(List<PayOrderDO> list); List<PayOrderRespVO> convertList(List<PayOrderDO> list);

View File

@ -5,6 +5,7 @@ import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.config.PayProperties;
@ -161,7 +162,7 @@ public class PayOrderServiceImpl implements PayOrderService {
// 4. 如果调用直接支付成功则直接更新支付单状态为成功例如说付款码支付免密支付时就直接验证支付成功 // 4. 如果调用直接支付成功则直接更新支付单状态为成功例如说付款码支付免密支付时就直接验证支付成功
if (unifiedOrderResp != null) { if (unifiedOrderResp != null) {
notifyPayOrder(channel, unifiedOrderResp); getSelf().notifyPayOrder(channel, unifiedOrderResp);
// 如有渠道错误码则抛出业务异常提示用户 // 如有渠道错误码则抛出业务异常提示用户
if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) { if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) {
throw exception(ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(), throw exception(ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(),
@ -234,11 +235,17 @@ public class PayOrderServiceImpl implements PayOrderService {
// 校验支付渠道是否有效 // 校验支付渠道是否有效
PayChannelDO channel = channelService.validPayChannel(channelId); PayChannelDO channel = channelService.validPayChannel(channelId);
// 更新支付订单为已支付 // 更新支付订单为已支付
TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify)); TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyPayOrder(channel, notify));
} }
// TODO 芋艿事务问题 /**
private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { * 通知并更新订单的支付结果
*
* @param channel 支付渠道
* @param notify 通知
*/
@Transactional(rollbackFor = Exception.class) // 注意如果是方法内调用该方法需要通过 getSelf().notifyPayOrder(channel, notify) 调用否则事务不生效
public void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) {
// 情况一支付成功的回调 // 情况一支付成功的回调
if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) {
notifyOrderSuccess(channel, notify); notifyOrderSuccess(channel, notify);
@ -396,4 +403,13 @@ public class PayOrderServiceImpl implements PayOrderService {
} }
} }
/**
* 获得自身的代理对象解决 AOP 生效问题
*
* @return 自己
*/
private PayOrderServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
} }

View File

@ -137,7 +137,7 @@ public class PayRefundServiceImpl implements PayRefundService {
.setOutRefundNo(refund.getNo()) .setOutRefundNo(refund.getNo())
.setNotifyUrl(genChannelRefundNotifyUrl(channel)) .setNotifyUrl(genChannelRefundNotifyUrl(channel))
.setReason(reqDTO.getReason()); .setReason(reqDTO.getReason());
PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // TODO 增加一个 channelErrorCodechannelErrorMsg 字段 PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO);
// 2.3 处理退款返回 // 2.3 处理退款返回
notifyRefund(channel, refundRespDTO); notifyRefund(channel, refundRespDTO);

View File

@ -11,7 +11,7 @@
<el-form-item label="渠道编码" prop="channelCode"> <el-form-item label="渠道编码" prop="channelCode">
<el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable <el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable
@clear="()=>{queryParams.channelCode = null}"> @clear="()=>{queryParams.channelCode = null}">
<el-option v-for="dict in payChannelCodeDictDatum" :key="dict.value" :label="dict.label" :value="dict.value"/> <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE)" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="商户订单编号" prop="merchantOrderId"> <el-form-item label="商户订单编号" prop="merchantOrderId">
@ -24,13 +24,13 @@
</el-form-item> </el-form-item>
<el-form-item label="支付状态" prop="status"> <el-form-item label="支付状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择支付状态" clearable size="small"> <el-select v-model="queryParams.status" placeholder="请选择支付状态" clearable size="small">
<el-option v-for="dict in payOrderDictDatum" :key="parseInt(dict.value)" <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_ORDER_STATUS)" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/> :label="dict.label" :value="parseInt(dict.value)"/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="退款状态" prop="refundStatus"> <el-form-item label="退款状态" prop="refundStatus">
<el-select v-model="queryParams.refundStatus" placeholder="请选择退款状态" clearable> <el-select v-model="queryParams.refundStatus" placeholder="请选择退款状态" clearable>
<el-option v-for="dict in payOrderRefundDictDatum" :key="parseInt(dict.value)" <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_ORDER_REFUND_STATUS)" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/> :label="dict.label" :value="parseInt(dict.value)"/>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -102,6 +102,7 @@
</template> </template>
</el-table-column> </el-table-column>
<!-- TODO 芋艿要放开 -->
<!-- <el-table-column label="退款状态" align="center" prop="refundStatus">--> <!-- <el-table-column label="退款状态" align="center" prop="refundStatus">-->
<!-- <template v-slot="scope">--> <!-- <template v-slot="scope">-->
<!-- <span>{{ getDictDataLabel(DICT_TYPE.PAY_ORDER_REFUND_STATUS, scope.row.refundStatus) }}</span>--> <!-- <span>{{ getDictDataLabel(DICT_TYPE.PAY_ORDER_REFUND_STATUS, scope.row.refundStatus) }}</span>-->
@ -124,7 +125,7 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope"> <template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-search" @click="handleQueryDetails(scope.row)" <el-button size="mini" type="text" icon="el-icon-search" @click="handleDetail(scope.row)"
v-hasPermi="['pay:order:query']">查看详情 v-hasPermi="['pay:order:query']">查看详情
</el-button> </el-button>
</template> </template>
@ -134,7 +135,7 @@
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/> @pagination="getList"/>
<!-- 对话框(添加 / 修改) --> <!-- 对话框(详情) -->
<el-dialog title="订单详情" :visible.sync="open" width="50%"> <el-dialog title="订单详情" :visible.sync="open" width="50%">
<el-descriptions :column="2" label-class-name="desc-label"> <el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item> <el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item>
@ -149,8 +150,8 @@
<el-tag class="tag-purple" size="small">{{ orderDetail.channelOrderNo }}</el-tag> <el-tag class="tag-purple" size="small">{{ orderDetail.channelOrderNo }}</el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="支付订单号"> <el-descriptions-item label="支付订单号">
<el-tag v-if="orderDetail.payOrderExtension.no !== ''" class="tag-pink" size="small"> <el-tag v-if="orderDetail.extension.no !== ''" class="tag-pink" size="small">
{{ orderDetail.payOrderExtension.no }} {{ orderDetail.extension.no }}
</el-tag> </el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="金额"> <el-descriptions-item label="金额">
@ -176,7 +177,9 @@
</el-descriptions> </el-descriptions>
<el-divider></el-divider> <el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label"> <el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付渠道">{{ orderDetail.channelCodeName }}</el-descriptions-item> <el-descriptions-item label="支付渠道">
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="orderDetail.channelCode" />
</el-descriptions-item>
<el-descriptions-item label="支付IP">{{ orderDetail.userIp }}</el-descriptions-item> <el-descriptions-item label="支付IP">{{ orderDetail.userIp }}</el-descriptions-item>
<el-descriptions-item label="退款状态"> <el-descriptions-item label="退款状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_REFUND_STATUS" :value="orderDetail.refundStatus" /> <dict-tag :type="DICT_TYPE.PAY_ORDER_REFUND_STATUS" :value="orderDetail.refundStatus" />
@ -194,7 +197,7 @@
{{ orderDetail.body }} {{ orderDetail.body }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="支付通道异步回调内容"> <el-descriptions-item label="支付通道异步回调内容">
{{ orderDetail.payOrderExtension.channelNotifyData }} {{ orderDetail.extension.channelNotifyData }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</el-dialog> </el-dialog>
@ -204,34 +207,6 @@
<script> <script>
import { getOrderDetail, getOrderPage, exportOrderExcel } from "@/api/pay/order"; import { getOrderDetail, getOrderPage, exportOrderExcel } from "@/api/pay/order";
import { DICT_TYPE, getDictDatas } from "@/utils/dict"; import { DICT_TYPE, getDictDatas } from "@/utils/dict";
import { getNowDateTime} from "@/utils/ruoyi";
const defaultOrderDetail = {
appName: '',
channelCodeName: '',
subject: '',
merchantOrderId: null,
channelOrderNo: '',
body: '',
price: null,
channelFeeRate: null,
channelFeePrice: null,
userIp: '',
status: null,
notifyUrl: '',
notifyStatus: null,
refundStatus: null,
refundTimes: '',
refundPrice: null,
createTime: '',
successTime: '',
notifyTime: '',
expireTime: '',
payOrderExtension: {
channelNotifyData: '',
no: ''
}
};
export default { export default {
name: "PayOrder", name: "PayOrder",
@ -278,47 +253,24 @@ export default {
notifyTime: [], notifyTime: [],
createTime: [] createTime: []
}, },
//
merchantLoading: false,
//
merchantList: null,
// //
appList: null, appList: [],
//
payChannelCodeDictDatum: getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE),
// //
payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS), payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
//
payOrderDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_STATUS), //
// 退 orderDetail: {
payOrderRefundDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_REFUND_STATUS), extension: {}
orderDetail: JSON.parse(JSON.stringify(defaultOrderDetail)), },
}; };
}, },
created() { created() {
//
this.initTime();
this.getList(); this.getList();
this.handleGetMerchantListByName(null);
}, },
methods: { methods: {
initTime(){
this.queryParams.createTime = [getNowDateTime("00:00:00"), getNowDateTime("23:59:59")];
},
/** 查询列表 */ /** 查询列表 */
getList() { getList() {
//
let oneMonthTime = 31 * 24 * 3600 * 1000;
if (this.queryParams.createTime == null){
this.initTime();
} else {
let minDateTime = new Date(this.queryParams.createTime[0]).getTime();
let maxDateTime = new Date(this.queryParams.createTime[1]).getTime()
if (maxDateTime - minDateTime > oneMonthTime) {
this.$message.error('时间范围最大为 31 天!');
return false;
}
}
this.loading = true; this.loading = true;
// //
getOrderPage(this.queryParams).then(response => { getOrderPage(this.queryParams).then(response => {
@ -339,23 +291,22 @@ export default {
/** 重置按钮操作 */ /** 重置按钮操作 */
resetQuery() { resetQuery() {
this.resetForm("queryForm"); this.resetForm("queryForm");
this.initTime();
this.handleQuery(); this.handleQuery();
}, },
/** /** 详情按钮操作 */
* 查看订单详情 handleDetail(row) {
*/ this.orderDetail = {};
handleQueryDetails(row) {
this.orderDetail = JSON.parse(JSON.stringify(defaultOrderDetail));
getOrderDetail(row.id).then(response => { getOrderDetail(row.id).then(response => {
//
this.orderDetail = response.data; this.orderDetail = response.data;
if (response.data.payOrderExtension === null) { if (!this.orderDetail.extension) {
this.orderDetail.payOrderExtension = Object.assign(defaultOrderDetail.payOrderExtension, {}); this.orderDetail.extension = {}
} }
//
this.open = true; this.open = true;
}); });
}, },
/** 导出按钮操作 */ /** 导出按钮操作 */
handleExport() { handleExport() {
// //