feat 订单相关问题排查和功能优化

This commit is contained in:
bootx
2024-08-17 22:58:14 +08:00
parent 790d6dddd6
commit 616c580c17
26 changed files with 582 additions and 122 deletions

View File

@@ -1,8 +0,0 @@
/**
* 支付通道
*/
export enum ChannelEnum {
ALI = 'ali_pay',
WECHAT = 'wechat_pay',
UNION_PAY = 'union_pay',
}

View File

@@ -0,0 +1,26 @@
/**
* 支付通道
*/
export enum PaymentEnum {
ALI = 'ali_pay',
WECHAT = 'wechat_pay',
UNION_PAY = 'union_pay',
}
/**
* 交易类型
*/
export enum TradeTypeEnum {
/**
* 支付
*/
PAY = 'pay',
/**
* 退款
*/
REFUND = 'refund',
/**
* 转账
*/
TRANSFER = 'transfer',
}

View File

@@ -1,7 +1,7 @@
/**
* 支付状态
*/
export enum PayStatusEnum{
export enum PayStatusEnum {
/**
* 支付中
*/
@@ -31,7 +31,7 @@ export enum PayStatusEnum{
/**
* 支付订单的退款状态
*/
export enum PayRefundStatusEnum{
export enum PayRefundStatusEnum {
/**
* 未退款
*/
@@ -53,8 +53,7 @@ export enum PayRefundStatusEnum{
/**
* 支付订单的分账状态
*/
export enum PayAllocStatusEnum{
export enum PayAllocStatusEnum {
/**
* 待分账
*/
@@ -68,7 +67,7 @@ export enum PayAllocStatusEnum{
/**
* 退款状态枚举
*/
export enum RefundStatusEnum{
export enum RefundStatusEnum {
/**
* 退款中
*/
@@ -90,7 +89,7 @@ export enum RefundStatusEnum{
/**
* 转账状态
*/
export enum TransferStatusEnum{
export enum TransferStatusEnum {
/** 转账中 */
PROGRESS = 'progress',
/** 转账成功 */

View File

@@ -4,7 +4,7 @@
</template>
<script setup lang="ts">
import { useMessage } from '@/hooks/web/useMessage'
import { ChannelEnum } from '@/enums/daxpay/ChannelEnum'
import { PaymentEnum } from '@/enums/daxpay/PaymentEnum'
import { ChannelConfig } from './ChannelConfig.api'
import AlipayConfigEdit from '@/views/daxpay/admin/channel/alipay/config/AlipayConfigEdit.vue'
import WechatPayConfigEdit from '@/views/daxpay/admin/channel/wechat/config/WechatPayConfigEdit.vue'
@@ -22,15 +22,15 @@
*/
function show(record: ChannelConfig) {
switch (record.channel) {
case ChannelEnum.ALI: {
case PaymentEnum.ALI: {
alipay.value.init(record)
break
}
case ChannelEnum.WECHAT: {
case PaymentEnum.WECHAT: {
wechat.value.init(record)
break
}
case ChannelEnum.UNION_PAY: {
case PaymentEnum.UNION_PAY: {
union.value.init(record)
break
}

View File

@@ -54,7 +54,7 @@
import { BasicDrawer } from '@/components/Drawer'
import { ref } from 'vue'
import { findAll, ChannelConfig } from './ChannelConfig.api'
import { ChannelEnum } from '@/enums/daxpay/ChannelEnum'
import { PaymentEnum } from '@/enums/daxpay/PaymentEnum'
import alipay from '@/assets/daxpay/alipay.svg'
import wechat from '@/assets/daxpay/wechat.svg'
import unionPay from '@/assets/daxpay/unionPay.svg'
@@ -112,11 +112,11 @@
*/
function getIcon(type: string) {
switch (type) {
case ChannelEnum.ALI:
case PaymentEnum.ALI:
return alipay
case ChannelEnum.WECHAT:
case PaymentEnum.WECHAT:
return wechat
case ChannelEnum.UNION_PAY:
case PaymentEnum.UNION_PAY:
return unionPay
default:
return ''

View File

@@ -61,7 +61,7 @@ export function getRecord(id) {
export interface CallbackTask extends MchEntity {
// 本地交易ID
tradeId?: string
// 本地交易号
// 平台交易号
tradeNo?: string
// 交易类型
tradeType?: string

View File

@@ -19,7 +19,7 @@
@sort-change="sortChange"
>
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="orderId" title="本地交易号" :min-width="230">
<vxe-column field="orderId" title="平台交易号" :min-width="230">
<template #default="{ row }">
<a-link @click="showOrder(row)">
{{ row.tradeNo }}
@@ -80,8 +80,8 @@
import ALink from '@/components/Link/Link.vue'
import { LabeledValue } from 'ant-design-vue/lib/select'
import CallbackTaskInfo from './CallbackTaskInfo.vue'
import PayOrderInfo from '../../order/pay/PayOrderInfo.vue'
import TransferOrderInfo from '../../order/transfer/TransferOrderInfo.vue'
import PayOrderInfo from '@/views/daxpay/admin/order/pay/PayOrderInfo.vue'
import TransferOrderInfo from '@/views/daxpay/admin/order/transfer/TransferOrderInfo.vue'
import CallbackRecordList from './CallbackRecordList.vue'
// 使用hooks
@@ -103,7 +103,7 @@
// 查询条件
const fields = computed(() => {
return [
{ field: 'tradeNo', type: STRING, name: '本地交易号', placeholder: '请输入完整本地交易号' },
{ field: 'tradeNo', type: STRING, name: '平台交易号', placeholder: '请输入完整平台交易号' },
{
field: 'noticeType',
type: LIST,

View File

@@ -61,7 +61,7 @@ export function getRecord(id) {
export interface NotifyTask extends MchEntity {
// 本地交易ID
tradeId?: string
// 本地交易号
// 平台交易号
tradeNo?: string
// 交易类型
tradeType?: string

View File

@@ -19,7 +19,7 @@
@sort-change="sortChange"
>
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="orderId" title="本地交易号" :min-width="230">
<vxe-column field="orderId" title="平台交易号" :min-width="230">
<template #default="{ row }">
<a-link @click="showOrder(row)">
{{ row.tradeNo }}
@@ -80,8 +80,8 @@
import ALink from '@/components/Link/Link.vue'
import { LabeledValue } from 'ant-design-vue/lib/select'
import NotifyTaskInfo from './NotifyTaskInfo.vue'
import PayOrderInfo from '../../order/pay/PayOrderInfo.vue'
import TransferOrderInfo from '../../order/transfer/TransferOrderInfo.vue'
import PayOrderInfo from '@/views/daxpay/admin/order/pay/PayOrderInfo.vue'
import TransferOrderInfo from '@/views/daxpay/admin/order/transfer/TransferOrderInfo.vue'
import NotifyRecordList from './NotifyRecordList.vue'
// 使用hooks
@@ -103,7 +103,7 @@
// 查询条件
const fields = computed(() => {
return [
{ field: 'tradeNo', type: STRING, name: '本地交易号', placeholder: '请输入完整本地交易号' },
{ field: 'tradeNo', type: STRING, name: '平台交易号', placeholder: '请输入完整平台交易号' },
{
field: 'noticeType',
type: LIST,

View File

@@ -38,22 +38,32 @@ export function getOrderByOrderNo(orderNo: string) {
}
/**
* 根据订单号同步支付状态
* 同步支付状态
*/
export function syncByOrderNo(orderNo: string) {
export function syncOrder(id: string) {
return defHttp.post<Result>({
url: '/order/pay/syncByOrderNo',
params: { orderNo },
url: '/order/pay/sync',
params: { id },
})
}
/**
* 关闭支付记录
*/
export function close(orderNo) {
export function close(id) {
return defHttp.post<Result<void>>({
url: '/order/pay/close',
params: { orderNo },
params: { id },
})
}
/**
* 关闭支付记录
*/
export function cancel(id) {
return defHttp.post<Result<void>>({
url: '/order/pay/cancel',
params: { id },
})
}

View File

@@ -43,10 +43,10 @@
<a-descriptions-item label="通知地址" :span="16">
{{ order.notifyUrl || '无' }}
</a-descriptions-item>
<a-descriptions-item label="错误编码" :span="8">
<a-descriptions-item label="错误编码" v-if="order.errorMsg" :span="8">
{{ order.errorCode || '' }}
</a-descriptions-item>
<a-descriptions-item label="错误信息" :span="16">
<a-descriptions-item label="错误信息" v-if="order.errorMsg" :span="16">
{{ order.errorMsg || '' }}
</a-descriptions-item>
<a-descriptions-item label="客户IP" :span="8">

View File

@@ -89,6 +89,9 @@
<a-menu-item v-if="[PayStatusEnum.PROGRESS].includes(row.status)">
<a-link @click="closeOrder(row)" danger>关闭</a-link>
</a-menu-item>
<a-menu-item v-if="[PayStatusEnum.PROGRESS].includes(row.status)">
<a-link @click="cancelOrder(row)" danger>撤销</a-link>
</a-menu-item>
<a-menu-item
v-if="
row.allocStatus === PayAllocStatusEnum.WAITING &&
@@ -99,7 +102,12 @@
</a-menu-item>
<a-menu-item
v-if="
[PayStatusEnum.SUCCESS].includes(row.status) && row.refundableBalance > 0
[PayStatusEnum.SUCCESS].includes(row.status) &&
row.refundableBalance > 0 &&
[
PayRefundStatusEnum.NO_REFUND,
PayRefundStatusEnum.PARTIAL_REFUND,
].includes(row.refundStatus)
"
>
<a-link @click="refund(row)" danger>退款</a-link>
@@ -132,8 +140,9 @@
close,
getTotalAmount,
page,
syncByOrderNo,
syncOrder,
cellStyle,
cancel,
} from './PayOrder.api'
import useTablePage from '@/hooks/bootx/useTablePage'
import PayOrderInfo from './PayOrderInfo.vue'
@@ -146,7 +155,11 @@
import ALink from '@/components/Link/Link.vue'
import { LabeledValue } from 'ant-design-vue/lib/select'
import { Icon } from '@/components/Icon'
import { PayAllocStatusEnum, PayStatusEnum } from '@/enums/daxpay/TradeStatusEnum'
import {
PayAllocStatusEnum,
PayRefundStatusEnum,
PayStatusEnum,
} from '@/enums/daxpay/TradeStatusEnum'
// 使用hooks
const {
@@ -264,10 +277,10 @@
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否同步支付信息',
content: '是否同步支付信息,只有状态支付中的订单才会对状态进行更新',
onOk: () => {
loading.value = true
syncByOrderNo(record.orderNo).then(() => {
syncOrder(record.id).then(() => {
createMessage.success('同步成功')
queryPage()
})
@@ -283,13 +296,29 @@
title: '警告',
content: '是否关闭支付订单',
onOk: () => {
close(record.orderNo).then(() => {
close(record.id).then(() => {
createMessage.success('关闭成功')
queryPage()
})
},
})
}
/**
* 撤销订单
*/
function cancelOrder(record) {
createConfirm({
iconType: 'warning',
title: '警告',
content: '只有部分类型的订单支持撤销操作,请注意!',
onOk: () => {
cancel(record.id).then(() => {
createMessage.success('撤销成功')
queryPage()
})
},
})
}
/**
* 退款
*/

View File

@@ -24,11 +24,12 @@
<a-form-item label="商户订单号">
<a-tag>{{ order.bizOrderNo }}</a-tag>
</a-form-item>
<a-form-item label="退款金额" name="amount">
<a-form-item label="退款金额(元)" name="amount">
<a-input-number
:min="0.01"
:max="order.refundableBalance"
:precision="2"
placeholder="请输入退款金额(元)"
v-model:value="form.amount"
/>
</a-form-item>

View File

@@ -45,19 +45,29 @@ export function refund(params) {
/**
* 退款信息同步
*/
export function syncByRefundNo(refundNo) {
export function syncOrder(id) {
return defHttp.post<Result<void>>({
url: '/order/refund/syncByRefundNo',
params: { refundNo },
url: '/order/refund/sync',
params: { id },
})
}
/**
* 退款重试
*/
export function resetRefund(id) {
export function retryRefund(id) {
return defHttp.post<Result<void>>({
url: '/order/refund/resetRefund',
url: '/order/refund/retry',
params: { id },
})
}
/**
* 退款关闭
*/
export function closeRefund(id) {
return defHttp.post<Result<void>>({
url: '/order/refund/close',
params: { id },
})
}

View File

@@ -40,7 +40,7 @@
{{ order.errorMsg }}
</a-descriptions-item>
<a-descriptions-item label="退款状态" :span="2">
<a-tag>{{ dictConvert('RefundStatus', order.status) || '空' }}</a-tag>
<a-tag>{{ dictConvert('refund_status', order.status) || '空' }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="退款终端ip">
{{ order.clientIp }}

View File

@@ -14,9 +14,7 @@
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }">
<template #buttons>
<span style="font-size: 18px"
>退款金额: {{ totalAmount ? totalAmount : 0 }}</span
>
<span style="font-size: 18px">退款金额: {{ totalAmount ? totalAmount : 0 }}</span>
</template>
</vxe-toolbar>
<div class="h-65vh">
@@ -62,14 +60,33 @@
</template>
</vxe-column>
<vxe-column field="createTime" title="创建时间" sortable :min-width="230" />
<vxe-column fixed="right" width="150" :showOverflow="false" title="操作">
<vxe-column fixed="right" width="130" :showOverflow="false" title="操作">
<template #default="{ row }">
<a-link @click="show(row)">查看</a-link>
<a-divider type="vertical" />
<!-- 钱包不可以同步 -->
<a-link @click="sync(row)">同步</a-link>
<a-divider type="vertical" />
<a-link :disabled="!(row.status === 'fail')" @click="reset(row)">重试</a-link>
<a-dropdown>
<a>
更多
<icon icon="ant-design:down-outlined" :size="12" />
</a>
<template #overlay>
<a-menu>
<a-menu-item
v-if="[RefundStatusEnum.PROGRESS, RefundStatusEnum.FAIL].includes(row.status)"
>
<a-link @click="sync(row)">同步</a-link>
</a-menu-item>
<a-menu-item
v-if="[RefundStatusEnum.PROGRESS, RefundStatusEnum.FAIL].includes(row.status)"
>
<a-link @click="reset(row)">重试</a-link>
</a-menu-item>
<a-menu-item v-if="[RefundStatusEnum.FAIL].includes(row.status)">
<a-link @click="closeOrder(row)" danger>关闭</a-link>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</vxe-column>
</vxe-table>
@@ -90,7 +107,7 @@
<script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'
import { getTotalAmount, page, resetRefund, syncByRefundNo } from './RefundOrder.api'
import { closeRefund, getTotalAmount, page, retryRefund, syncOrder } from './RefundOrder.api'
import useTablePage from '@/hooks/bootx/useTablePage'
import RefundOrderInfo from './RefundOrderInfo.vue'
import { VxeTable, VxeTableInstance, VxeToolbar, VxeToolbarInstance } from 'vxe-table'
@@ -101,6 +118,12 @@
import PayOrderInfo from '../pay/PayOrderInfo.vue'
import { LabeledValue } from 'ant-design-vue/lib/select'
import ALink from '@/components/Link/Link.vue'
import {
PayAllocStatusEnum,
PayStatusEnum,
RefundStatusEnum,
} from '@/enums/daxpay/TradeStatusEnum'
import { Icon } from '@/components/Icon'
// 使用hooks
const {
@@ -141,12 +164,11 @@
placeholder: '请输入通道订单号',
},
{ field: 'title', type: STRING, name: '原支付标题', placeholder: '请输入原支付标题' },
{ field: 'errorCode', name: '错误码', type: STRING },
{
field: 'status',
type: LIST,
name: '处理状态',
placeholder: '请选择处理状态',
name: '退款状态',
placeholder: '请选择退款状态',
selectList: refundStatusList.value,
},
{ field: 'channel', name: '支付通道', type: LIST, selectList: channelList.value },
@@ -206,8 +228,7 @@
content: '是否同步退款信息',
onOk: () => {
loading.value = true
syncByRefundNo(record.refundNo).then(({ data }) => {
// TODO 后期可以根据返回结果进行相应的处理
syncOrder(record.id).then(({ data }) => {
createMessage.success('同步成功')
console.log(data)
queryPage()
@@ -216,6 +237,24 @@
})
}
/**
* 退款关闭
*/
function closeOrder(record) {
createConfirm({
iconType: 'warning',
title: '警告',
content: '请确定该退款订单是否处理失败并结束!',
onOk: () => {
loading.value = true
closeRefund(record.id).then(() => {
createMessage.success('关闭成功')
queryPage()
})
},
})
}
/**
* 退款重试
*/
@@ -223,10 +262,10 @@
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否同步退款信息',
content: '是否重新发起退款请求',
onOk: () => {
loading.value = true
resetRefund(record.id).then(() => {
retryRefund(record.id).then(() => {
createMessage.success('提交成功')
queryPage()
})

View File

@@ -36,11 +36,8 @@
<a-descriptions-item label="收款人账号" :span="2">
{{ order.payeeAccount }}
</a-descriptions-item>
<a-descriptions-item label="转账类型" :span="2">
<a-tag>{{ dictConvert('TransferType', order.transferType) || '空' }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="状态" :span="2">
<a-tag>{{ dictConvert('TransferStatus', order.status) || '空' }}</a-tag>
<a-tag>{{ dictConvert('transfer_status', order.status) || '空' }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="转账时间" :span="2">
{{ order.createTime }}

View File

@@ -113,17 +113,16 @@
// 查询条件
const fields = computed(() => {
return [
{ field: 'bizTransferNo', type: STRING, name: '商户退款号', placeholder: '请输入商户退款号' },
{ field: 'transferNo', type: STRING, name: '退款号', placeholder: '请输入退款号' },
{ field: 'outTransferNo', type: STRING, name: '通道退款号', placeholder: '请输入通道退款号' },
{ field: 'bizTransferNo', type: STRING, name: '商户转账号', placeholder: '请输入商户转账号' },
{ field: 'transferNo', type: STRING, name: '转账号', placeholder: '请输入转账号' },
{ field: 'outTransferNo', type: STRING, name: '通道转账号', placeholder: '请输入通道转账号' },
{ field: 'title', type: STRING, name: '原支付标题', placeholder: '请输入转账标题' },
{ field: 'payeeName', type: STRING, name: '收款人姓名', placeholder: '请输入收款人姓名' },
{ field: 'errorCode', name: '错误码', type: STRING },
{
field: 'status',
type: LIST,
name: '处理状态',
placeholder: '请选择处理状态',
name: '转账状态',
placeholder: '请选择转账状态',
selectList: transferStatusList.value,
},
{ field: 'channel', name: '转账通道', type: LIST, selectList: channelList.value },
@@ -147,8 +146,8 @@
* 初始化数据
*/
async function initData() {
transferStatusList.value = await dictDropDown('TransferStatus')
channelList.value = await dictDropDown('PayChannel')
transferStatusList.value = await dictDropDown('transfer_status')
channelList.value = await dictDropDown('channel')
}
/**
@@ -176,13 +175,36 @@
* 信息同步
*/
function sync(record) {
createMessage.warn('下版本支持...')
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否同步退款信息',
onOk: () => {
loading.value = true
syncByTransferNo(record.transferNo).then(() => {
createMessage.success('同步成功')
queryPage()
})
},
})
}
/**
* 退款重试
* 转账重试
*/
function reset(record) {
createConfirm({
iconType: 'warning',
title: '警告',
content: '是否同步退款信息',
onOk: () => {
loading.value = true
resetTransfer(record.id).then(() => {
createMessage.success('提交成功')
queryPage()
})
},
})
createMessage.warn('下版本支持...')
}

View File

@@ -24,36 +24,41 @@
<vxe-column field="reconcileDate" title="对账日期" sortable :min-width="120" />
<vxe-column field="channel" title="通道" :min-width="120">
<template #default="{ row }">
<a-tag>{{ dictConvert('PayChannel', row.channel) }}</a-tag>
<a-tag>{{ dictConvert('channel', row.channel) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="tradeType" title="交易类型" :min-width="120">
<template #default="{ row }">
<a-tag>{{ dictConvert('PaymentType', row.tradeType) }}</a-tag>
<a-tag>{{ dictConvert('trade_type', row.tradeType) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="diffType" title="差异类型" :min-width="120">
<vxe-column field="discrepancyType" title="差异类型" :min-width="120">
<template #default="{ row }">
<a-tag>{{ dictConvert('ReconcileDiffType', row.diffType) }}</a-tag>
<a-tag>{{ dictConvert('reconcile_discrepancy_type', row.discrepancyType) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="tradeNo" title="本地交易号" :min-width="230">
<template #default="{ row }">
<a-link @click="showTrade(row)">{{ row.tradeNo }}</a-link>
</template>
</vxe-column>
<vxe-column field="outTradeNo" title="通道交易号" :min-width="230" />
<vxe-column field="amount" title="交易金额(元)" sortable :min-width="130">
<template #default="{ row }">
{{ row.amount ? (row.amount / 100).toFixed(2) : '无' }}
</template>
</vxe-column>
<vxe-column field="outAmount" title="通道交易金额(元)" sortable :min-width="150">
<template #default="{ row }">
{{ row.outAmount ? (row.outAmount / 100).toFixed(2) : '无' }}
</template>
</vxe-column>
<vxe-column field="tradeTime" title="交易时间" sortable :min-width="150" />
<vxe-colgroup title="平台信息">
<vxe-column field="tradeNo" title="交易号" :min-width="230">
<template #default="{ row }">
<a-link @click="showTrade(row)">{{ row.tradeNo }}</a-link>
</template>
</vxe-column>
<vxe-column field="amount" title="交易金额(元)" sortable :min-width="130">
<template #default="{ row }">
{{ row.amount ? (row.amount / 100).toFixed(2) : '无' }}
</template>
</vxe-column>
<vxe-column field="tradeTime" title="交易时间" sortable :min-width="150" />
<vxe-column field="tradeStatus" title="交易状态" :min-width="150" />
</vxe-colgroup>
<vxe-colgroup title="通道信息">
<vxe-column field="channelTradeNo" title="交易号" :min-width="230" />
<vxe-column field="channelTradeAmount" title="交易金额(元)" sortable :min-width="150" />
<vxe-column field="channelTradeTime" title="交易时间" sortable :min-width="150" />
<vxe-column field="channelTradeStatus" title="交易状态" :min-width="150" />
</vxe-colgroup>
<vxe-column field="reconcileNo" title="对账单号" :min-width="230">
<template #default="{ row }">
<a-link @click="showReconcile(row)">{{ row.reconcileNo }}</a-link>
@@ -78,7 +83,7 @@
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted } from 'vue'
import { computed, nextTick, onMounted, ref } from 'vue'
import { page } from './ReconcileDiscrepancy.api'
import useTablePage from '@/hooks/bootx/useTablePage'
import { VxeTable, VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
@@ -100,7 +105,6 @@
model,
loading,
} = useTablePage(queryPage)
const { notification, createMessage } = useMessage()
const { dictDropDown, dictConvert } = useDict()
let tradeTypeList = ref<LabeledValue[]>([])
@@ -133,7 +137,7 @@
placeholder: '请选择对账',
selectList: payChannelList,
},
{ field: 'tradeNo', type: STRING, name: '本地交易号', placeholder: '请输入本地交易号' },
{ field: 'tradeNo', type: STRING, name: '平台交易号', placeholder: '请输入平台交易号' },
{ field: 'outOrderNo', type: STRING, name: '通道交易号', placeholder: '请输入通道交易号' },
] as QueryField[]
})
@@ -146,7 +150,7 @@
const reconcileDetailInfo = ref<any>()
nextTick(() => {
xTable?.connect(xToolbar as VxeToolbarInstance)
xTable.value?.connect(xToolbar.value as VxeToolbarInstance)
})
onMounted(() => {
@@ -158,16 +162,16 @@
* 初始化基础数据
*/
async function initData() {
tradeTypeList = await dictDropDown('PaymentType')
diffTypeList = await dictDropDown('ReconcileDiffType')
payChannelList = await dictDropDown('PayChannel')
tradeTypeList.value = await dictDropDown('PaymentType')
diffTypeList.value = await dictDropDown('ReconcileDiffType')
payChannelList.value = await dictDropDown('PayChannel')
}
/**
* 入口
*/
function init() {
model.queryParam = {}
xTable?.clearSort()
xTable.value?.clearSort()
queryPage()
}
@@ -188,14 +192,14 @@
* 查看
*/
function show(record) {
reconcileDiffInfo.init(record)
reconcileDiffInfo.value.init(record)
}
/**
* 查看对账单
*/
function showReconcile(record) {
reconcileDiffInfo.init(record)
reconcileDiffInfo.value.init(record)
}
/**
@@ -203,16 +207,16 @@
*/
function showTrade(record) {
if (record.callbackType === 'pay') {
payOrderInfo.init(record.tradeNo)
payOrderInfo.value.init(record.tradeNo)
} else {
refundOrderInfo.init(record.tradeNo)
refundOrderInfo.value.init(record.tradeNo)
}
}
/**
* 查看通道交易信息
*/
function showOutTrade(record) {
reconcileDetailInfo.init(record.outTradeNo)
reconcileDetailInfo.value.init(record.outTradeNo)
}
defineExpose({
init,

View File

@@ -10,7 +10,7 @@
>
<a-spin :spinning="confirmLoading">
<a-descriptions bordered title="">
<a-descriptions-item label="本地交易号" :span="2">
<a-descriptions-item label="平台交易号" :span="2">
{{ form.tradeNo }}
</a-descriptions-item>
<a-descriptions-item label="通道交易号" :span="2">

View File

@@ -102,7 +102,7 @@
// 查询条件
const fields = computed(() => {
return [
{ field: 'tradeNo', type: STRING, name: '本地交易号', placeholder: '请输入本地交易号' },
{ field: 'tradeNo', type: STRING, name: '平台交易号', placeholder: '请输入平台交易号' },
{ field: 'outTradeNo', type: STRING, name: '通道交易号', placeholder: '请输入通道交易号' },
{
field: 'channel',

View File

@@ -16,7 +16,7 @@
<a-descriptions-item label="交易金额(元)" :span="2">
{{ record.amount || '无' }}
</a-descriptions-item>
<a-descriptions-item label="本地交易号" :span="4">
<a-descriptions-item label="平台交易号" :span="4">
{{ record.tradeNo }}
</a-descriptions-item>
<a-descriptions-item label="商户交易号" :span="4">

View File

@@ -45,7 +45,7 @@
<a-tag>{{ dictConvert('AsyncPayChannel', row.channel) || '无' }}</a-tag>
</template>
</vxe-column>
<vxe-column field="tradeNo" title="本地交易号" :min-width="230">
<vxe-column field="tradeNo" title="平台交易号" :min-width="230">
<template #default="{ row }">
<a @click="showOrder(row)">
{{ row.tradeNo }}
@@ -112,7 +112,7 @@
const fields = computed(() => {
return [
{ field: 'title', type: STRING, name: '订单标题', placeholder: '请输入订单标题' },
{ field: 'tradeNo', type: STRING, name: '本地交易号', placeholder: '请输入本地交易号' },
{ field: 'tradeNo', type: STRING, name: '平台交易号', placeholder: '请输入平台交易号' },
{ field: 'bizTradeNo', type: STRING, name: '商户交易号', placeholder: '请输入商户交易号' },
{ field: 'outTradeNo', type: STRING, name: '通道交易号', placeholder: '请输入通道交易号' },
{

View File

@@ -0,0 +1,53 @@
import { defHttp } from '@/utils/http/axios'
import { PageResult, Result } from '#/axios'
import { MchEntity } from '#/web'
/**
* 分页
*/
export function page(params) {
return defHttp.get<Result<PageResult<TradeSyncRecord>>>({
url: '/record/sync/page',
params,
})
}
/**
* 获取单条
*/
export function get(id) {
return defHttp.get<Result<TradeSyncRecord>>({
url: '/record/sync/findById',
params: { id },
})
}
/**
* 支付同步记录
*/
export interface TradeSyncRecord extends MchEntity {
// 平台交易号
tradeNo?: string
// 商户交易号
bizTradeNo?: string
// 通道交易号
outTradeNo?: string
// 通道返回的状态
outTradeStatus?: string
// 同步类型
tradeType?: string
// 同步通道
channel?: string
// 通知消息
syncInfo?: string
// 是否进行调整
adjust?: boolean
// 错误消息
errorCode?: string
// 错误消息
errorMsg?: string
// 同步时间
createTime?: string
// 终端ip
clientIp?: string
}

View File

@@ -0,0 +1,87 @@
<template>
<basic-modal
title="查看同步信息"
v-bind="$attrs"
:loading="confirmLoading"
:width="1200"
:open="visible"
:mask-closable="showable"
@cancel="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-descriptions title="" bordered>
<a-descriptions-item label="平台交易号" :span="4">
{{ form.tradeNo }}
</a-descriptions-item>
<a-descriptions-item label="商户交易号" :span="4">
{{ form.bizTradeNo }}
</a-descriptions-item>
<a-descriptions-item label="通道交易号" :span="4">
{{ form.outTradeNo || '无' }}
</a-descriptions-item>
<a-descriptions-item label="同步类型" :span="2">
<a-tag>{{ dictConvert('trade_type', form.tradeType) }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="通道状态" :span="2">
<a-tag> {{ form.outTradeStatus }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="同步通道" :span="2">
<a-tag> {{ dictConvert('channel', form.channel) }}</a-tag>
</a-descriptions-item>
<a-descriptions-item label="客户端IP" :span="2">
{{ form.clientIp }}
</a-descriptions-item>
<a-descriptions-item label="同步时间" :span="4">
{{ form.createTime }}
</a-descriptions-item>
<a-descriptions-item label="错误编码" v-if="form.errorMsg" :span="2">
{{ form.errorMsg }}
</a-descriptions-item>
<a-descriptions-item label="错误信息" v-if="form.errorMsg" :span="2">
{{ form.errorMsg }}
</a-descriptions-item>
<a-descriptions-item label="同步消息" :span="4">
<json-preview :data="XEUtils.toStringJSON(form.syncInfo || '{}')" />
</a-descriptions-item>
</a-descriptions>
</a-spin>
<template #footer>
<a-button key="cancel" @click="handleCancel">取消</a-button>
</template>
</basic-modal>
</template>
<script lang="ts" setup>
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { get, TradeSyncRecord } from './TradeSyncRecord.api'
import { BasicModal } from '@/components/Modal'
import { useDict } from '@/hooks/bootx/useDict'
import JsonPreview from '/@/components/CodeEditor/src/json-preview/JsonPreview.vue'
import XEUtils from 'xe-utils'
import { ref } from 'vue'
const { handleCancel, confirmLoading, visible, showable } = useFormEdit()
const { dictConvert } = useDict()
// 表单
let form = ref<TradeSyncRecord>({})
// 入口
function init(id) {
visible.value = true
confirmLoading.value = true
get(id).then(({ data }) => {
form.value = data
confirmLoading.value = false
})
}
defineExpose({
init,
})
</script>
<style lang="less" scoped>
:deep(.ant-descriptions-item-label) {
width: 170px;
}
</style>

View File

@@ -0,0 +1,191 @@
<template>
<div>
<div class="m-3 p-3 pt-5 bg-white">
<b-query
:query-params="model.queryParam"
:default-item-count="3"
:fields="fields"
@query="queryPage"
@reset="resetQueryParams"
/>
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }" />
<vxe-table row-id="id" ref="xTable" :data="pagination.records" :loading="loading">
<vxe-column type="seq" title="序号" width="60" />
<vxe-column field="tradeNo" title="平台交易号" :min-width="230">
<template #default="{ row }">
<a @click="showOrder(row)">
{{ row.tradeNo }}
</a>
</template>
</vxe-column>
<vxe-column field="bizTradeNo" title="商户交易号" :min-width="230" />
<vxe-column field="outTradeNo" title="通道交易号" :min-width="230" />
<vxe-column field="outTradeStatus" title="通道状态" :min-width="150" />
<vxe-column field="type" title="同步类型" :min-width="120">
<template #default="{ row }">
<a-tag>{{ dictConvert('trade_type', row.tradeType) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="channel" title="同步通道" :min-width="100">
<template #default="{ row }">
<a-tag>{{ dictConvert('channel', row.channel) }}</a-tag>
</template>
</vxe-column>
<vxe-column field="repairOrder" title="是否调整" width="170">
<template #default="{ row }">
<a-tag v-if="row.adjust" color="green"> 已调整 </a-tag>
<a-tag v-else>未调整</a-tag>
</template>
</vxe-column>
<vxe-column field="errorMsg" title="错误消息" :min-width="160" />
<vxe-column field="createTime" title="同步时间" :min-width="160" />
<vxe-column fixed="right" :min-width="60" :showOverflow="false" title="操作">
<template #default="{ row }">
<span>
<a-link @click="show(row)">查看</a-link>
</span>
</template>
</vxe-column>
</vxe-table>
<vxe-pager
size="medium"
:loading="loading"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
@page-change="handleTableChange"
/>
</div>
<TradeSyncRecordInfo ref="tradeSyncRecordInfo" />
<PayOrderInfo ref="payOrderInfo" />
<RefundOrderInfo ref="refundOrderInfo" />
<TransferOrderInfo ref="transferOrderInfo" />
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'
import { page, TradeSyncRecord } from './TradeSyncRecord.api'
import useTablePage from '@/hooks/bootx/useTablePage'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import BQuery from '@/components/Bootx/Query/BQuery.vue'
import { useMessage } from '@/hooks/web/useMessage'
import { LIST, QueryField, STRING } from '@/components/Bootx/Query/Query'
import { useDict } from '@/hooks/bootx/useDict'
import { TradeTypeEnum } from '@/enums/daxpay/PaymentEnum'
import { LabeledValue } from 'ant-design-vue/lib/select'
import ALink from '/@/components/Link/Link.vue'
import PayOrderInfo from '@/views/daxpay/admin/order/pay/PayOrderInfo.vue'
import TradeSyncRecordInfo from './TradeSyncRecordInfo.vue'
import RefundOrderInfo from '@/views/daxpay/admin/order/refund/RefundOrderInfo.vue'
import TransferOrderInfo from '@/views/daxpay/admin/order/transfer/TransferOrderInfo.vue'
// 使用hooks
const {
handleTableChange,
pageQueryResHandel,
resetQueryParams,
pagination,
pages,
model,
loading,
} = useTablePage(queryPage)
const { notification, createMessage, createConfirm } = useMessage()
const { dictConvert, dictDropDown } = useDict()
let syncStatusList = ref<LabeledValue[]>([])
let payChannelList = ref<LabeledValue[]>([])
let syncTypeList = ref<LabeledValue[]>([])
// 查询条件
const fields = computed(() => {
return [
{ field: 'tradeNo', type: STRING, name: '平台交易号', placeholder: '请输入平台交易号' },
{ field: 'bizTradeNo', type: STRING, name: '商户交易号', placeholder: '请输入商户交易号' },
{ field: 'outTradeNo', type: STRING, name: '通道交易号', placeholder: '请输入通道交易号' },
{
field: 'syncType',
type: LIST,
name: '同步类型',
placeholder: '请选择同步类型',
selectList: syncTypeList.value,
},
{
field: 'outTradeStatus',
type: LIST,
name: '同步结果',
placeholder: '请选择同步结果',
selectList: syncStatusList.value,
},
{
field: 'channel',
type: LIST,
name: '同步通道',
placeholder: '请选择同步通道',
selectList: payChannelList.value,
},
] as QueryField[]
})
const xTable = ref<VxeTableInstance>()
const xToolbar = ref<VxeToolbarInstance>()
const tradeSyncRecordInfo = ref<any>()
const payOrderInfo = ref<any>()
const refundOrderInfo = ref<any>()
const transferOrderInfo = ref<any>()
onMounted(() => {
init()
vxeBind()
queryPage()
})
function vxeBind() {
xTable.value?.connect(xToolbar.value as VxeToolbarInstance)
}
/**
* 初始化
*/
async function init() {
syncStatusList.value = await dictDropDown('PaySyncStatus')
payChannelList.value = await dictDropDown('channel')
syncTypeList.value = await dictDropDown('PaymentType')
}
/**
* 分页查询
*/
function queryPage() {
loading.value = true
page({
...model.queryParam,
...pages,
}).then(({ data }) => {
pageQueryResHandel(data)
})
return Promise.resolve()
}
/**
* 查看
*/
function show(record) {
tradeSyncRecordInfo.value.init(record.id)
}
/**
* 查看支付单信息
*/
function showOrder(record: TradeSyncRecord) {
if (record.tradeType === TradeTypeEnum.PAY) {
payOrderInfo.value.init(record.tradeNo)
} else if (record.tradeType === TradeTypeEnum.REFUND) {
refundOrderInfo.value.init(record.tradeNo)
} else if (record.tradeType === TradeTypeEnum.TRANSFER) {
transferOrderInfo.value.init(record.tradeNo)
}
}
</script>
<style lang="less" scoped></style>