feat 平台配置优化和通道认证调试功能

This commit is contained in:
DaxPay
2024-09-25 18:59:39 +08:00
parent 3bfde816b1
commit 29029c8af7
14 changed files with 337 additions and 42 deletions

View File

@@ -59,6 +59,7 @@
"@vben/hooks": "workspace:*",
"@vue/shared": "^3.5.8",
"@vueuse/core": "^10.11.1",
"@vueuse/shared": "^10.11.1",
"@zxcvbn-ts/core": "^3.0.4",
"ant-design-vue": "^4.2.5",
"axios": "^1.7.7",

66
pnpm-lock.yaml generated
View File

@@ -23,6 +23,9 @@ importers:
'@vueuse/core':
specifier: ^10.11.1
version: 10.11.1(vue@3.5.8(typescript@5.6.2))
'@vueuse/shared':
specifier: ^10.11.1
version: 10.11.1(vue@3.5.8(typescript@5.6.2))
'@zxcvbn-ts/core':
specifier: ^3.0.4
version: 3.0.4
@@ -3132,15 +3135,6 @@ packages:
supports-color:
optional: true
debug@4.3.5:
resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
@@ -7025,7 +7019,7 @@ snapshots:
'@babel/traverse': 7.24.1
'@babel/types': 7.24.0
convert-source-map: 2.0.0
debug: 4.3.5
debug: 4.3.7
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -7384,7 +7378,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.25.6
'@babel/types': 7.24.0
debug: 4.3.5
debug: 4.3.7
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -7841,7 +7835,7 @@ snapshots:
'@eslint/eslintrc@2.1.4':
dependencies:
ajv: 6.12.6
debug: 4.3.5
debug: 4.3.7
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.1
@@ -7857,7 +7851,7 @@ snapshots:
'@humanwhocodes/config-array@0.11.14':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
debug: 4.3.5
debug: 4.3.7
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -8059,7 +8053,7 @@ snapshots:
'@pm2/pm2-version-check@1.0.4':
dependencies:
debug: 4.3.4
debug: 4.3.7
transitivePeerDependencies:
- supports-color
@@ -8068,8 +8062,8 @@ snapshots:
'@purge-icons/core@0.10.0':
dependencies:
'@iconify/iconify': 2.1.2
axios: 0.26.1(debug@4.3.5)
debug: 4.3.5
axios: 0.26.1(debug@4.3.7)
debug: 4.3.7
fast-glob: 3.3.2
fs-extra: 10.1.0
transitivePeerDependencies:
@@ -8494,7 +8488,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 7.7.0(typescript@5.6.2)
'@typescript-eslint/utils': 7.7.0(eslint@8.57.0)(typescript@5.6.2)
debug: 4.3.5
debug: 4.3.7
eslint: 8.57.0
ts-api-utils: 1.3.0(typescript@5.6.2)
optionalDependencies:
@@ -8508,7 +8502,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 7.7.0
'@typescript-eslint/visitor-keys': 7.7.0
debug: 4.3.5
debug: 4.3.7
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.4
@@ -9051,7 +9045,7 @@ snapshots:
'@vueuse/shared@10.9.0(vue@3.4.25(typescript@5.6.2))':
dependencies:
vue-demi: 0.14.7(vue@3.4.25(typescript@5.6.2))
vue-demi: 0.14.10(vue@3.4.25(typescript@5.6.2))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -9093,7 +9087,7 @@ snapshots:
agent-base@7.1.1:
dependencies:
debug: 4.3.4
debug: 4.3.7
transitivePeerDependencies:
- supports-color
@@ -9318,15 +9312,15 @@ snapshots:
dependencies:
possible-typed-array-names: 1.0.0
axios@0.26.1(debug@4.3.5):
axios@0.26.1(debug@4.3.7):
dependencies:
follow-redirects: 1.15.9(debug@4.3.5)
follow-redirects: 1.15.9(debug@4.3.7)
transitivePeerDependencies:
- debug
axios@1.7.7:
dependencies:
follow-redirects: 1.15.9(debug@4.3.5)
follow-redirects: 1.15.9(debug@4.3.7)
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
@@ -9932,10 +9926,6 @@ snapshots:
dependencies:
ms: 2.1.2
debug@4.3.5:
dependencies:
ms: 2.1.2
debug@4.3.7:
dependencies:
ms: 2.1.3
@@ -10676,9 +10666,9 @@ snapshots:
optionalDependencies:
debug: 4.3.4
follow-redirects@1.15.9(debug@4.3.5):
follow-redirects@1.15.9(debug@4.3.7):
optionalDependencies:
debug: 4.3.5
debug: 4.3.7
for-each@0.3.3:
dependencies:
@@ -10781,7 +10771,7 @@ snapshots:
dependencies:
basic-ftp: 5.0.5
data-uri-to-buffer: 6.0.2
debug: 4.3.4
debug: 4.3.7
fs-extra: 11.2.0
transitivePeerDependencies:
- supports-color
@@ -11029,14 +11019,14 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
debug: 4.3.7
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.4:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
debug: 4.3.7
transitivePeerDependencies:
- supports-color
@@ -12057,7 +12047,7 @@ snapshots:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.1
debug: 4.3.4
debug: 4.3.7
get-uri: 6.0.3
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
@@ -12554,7 +12544,7 @@ snapshots:
proxy-agent@6.3.1:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
debug: 4.3.7
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
lru-cache: 7.18.3
@@ -12650,7 +12640,7 @@ snapshots:
require-in-the-middle@5.2.0:
dependencies:
debug: 4.3.4
debug: 4.3.7
module-details-from-path: 1.0.3
resolve: 1.22.8
transitivePeerDependencies:
@@ -12949,7 +12939,7 @@ snapshots:
socks-proxy-agent@8.0.3:
dependencies:
agent-base: 7.1.1
debug: 4.3.4
debug: 4.3.7
socks: 2.8.3
transitivePeerDependencies:
- supports-color
@@ -13920,6 +13910,10 @@ snapshots:
vue-component-type-helpers@2.1.6: {}
vue-demi@0.14.10(vue@3.4.25(typescript@5.6.2)):
dependencies:
vue: 3.4.25(typescript@5.6.2)
vue-demi@0.14.10(vue@3.5.8(typescript@5.6.2)):
dependencies:
vue: 3.5.8(typescript@5.6.2)

View File

@@ -38,3 +38,15 @@ export enum NotifyContentTypeEnum {
/** 支付订单变动通知 */
TRANSFER = 'transfer',
}
/**
* 通道认证状态
*/
export enum ChannelAuthStatusEnum {
/** 获取中 */
WAITING = 'waiting',
/** 获取成功 */
SUCCESS = 'success',
/** 数据不存在 */
NOT_EXIST = 'not_exist',
}

View File

@@ -16,6 +16,14 @@ export function update(data: PlatformConfig) {
* 平台配置
*/
export interface PlatformConfig extends BaseEntity {
// 网关地址
// 网关服务地址
gatewayServiceUrl?: string
// 网关移动端是否为嵌入式
mobileEmbedded?: boolean
// 网关移动端地址
gatewayMobileUrl?: string
// 网关PC端是否为嵌入式
pcEmbedded?: boolean
// 网关PC端地址
gatewayPcUrl?: string
}

View File

@@ -19,6 +19,36 @@
placeholder="请输入网关地址"
/>
</a-form-item>
<a-form-item label="网关H5端部署方式" name="mobileEmbedded">
<a-switch
:disabled="!edit"
checked-children="嵌入"
un-checked-children="独立"
v-model:checked="form.mobileEmbedded"
/>
</a-form-item>
<a-form-item label="网关H5端地址" name="gatewayMobileUrl">
<a-input
:disabled="!edit"
v-model:value="form.gatewayMobileUrl"
placeholder="请输入网关H5端地址"
/>
</a-form-item>
<a-form-item label="网关PC端部署方式" name="pcEmbedded">
<a-switch
:disabled="!edit"
checked-children="嵌入"
un-checked-children="独立"
v-model:checked="form.pcEmbedded"
/>
</a-form-item>
<a-form-item label="网关PC端地址" name="gatewayPcUrl">
<a-input
:disabled="!edit"
v-model:value="form.gatewayPcUrl"
placeholder="请输入网关PC端地址"
/>
</a-form-item>
</a-form>
<div class="flex justify-center">
<a-button v-if="edit" @click="initData">取消</a-button>
@@ -42,11 +72,12 @@
const { createMessage } = useMessage()
const confirmLoading = ref(false)
const formRef = ref<FormInstance>()
const form = ref<PlatformConfig>({})
const form = ref<PlatformConfig>({
mobileEmbedded: false,
pcEmbedded: false,
})
const edit = ref<boolean>(false)
const rules = {
gatewayServiceUrl: [{ required: true, message: '请输入网关地址' }],
} as Record<string, Rule[]>
const rules = {} as Record<string, Rule[]>
onMounted(() => {
initData()

View File

@@ -0,0 +1,59 @@
import { defHttp } from '@/utils/http/axios'
import { Result } from '#/axios'
import { MchEntity } from '#/web'
/**
* 获取授权链接
*/
export function generateAuthUrl(params: GenerateAuthUrlParam) {
return defHttp.post<Result<AuthUrlResult>>({
url: '/assist/channel/auth/generateAuthUrl',
data: params,
})
}
/**
* 通过查询码获取认证结果
*/
export function queryAuthResult(queryCode) {
return defHttp.get<Result<AuthResult>>({
url: '/assist/channel/auth/queryAuthResult',
params: {
queryCode,
},
})
}
/**
* 授权链接和查询标识返回类
*/
export interface AuthUrlResult {
// 授权链接
authUrl?: string
// 查询标识
queryCode?: boolean
}
/**
* 生成授权链接参数
*/
export interface GenerateAuthUrlParam extends MchEntity {
// 通道
channel?: string
// 自定义授权重定向地址
authRedirectUrl?: string
}
/**
* 认证结果
*/
export interface AuthResult {
// OpenId
openId?: string
// 用户ID
userId?: string
// AccessToken
accessToken?: string
// 状态
status?: string
}

View File

@@ -0,0 +1,189 @@
<template>
<div
style="
display: flex;
margin: 20px;
flex-direction: column;
align-items: center;
background: white;
padding: 15px;
"
>
<a-alert style="width: 400px" message="请选择参数后生成授权链接进行扫码" type="info" />
<a-form class="small-from-item mt-15px mb-15px" ref="formRef" :model="form">
<a-form-item label="商户" name="mchNo">
<a-select
style="width: 320px"
:filter-option="search"
:options="merchantList"
v-model:value="form.mchNo"
placeholder="请选择商户"
@change="merchantChange"
/>
</a-form-item>
<a-form-item label="商户应用" name="appId" v-show="form.mchNo">
<a-select
:filter-option="search"
:options="mchAppList"
v-model:value="form.appId"
placeholder="请选择商户应用"
/>
</a-form-item>
<a-form-item label="对账通道" name="channel" v-show="form.appId">
<a-input-group compact>
<a-select
style="width: calc(100% - 60px)"
v-model:value="form.channel"
:options="channels"
placeholder="请选择对账通道"
/>
<a-button
:disabled="!form.channel"
style="margin-top: 15px"
type="primary"
@click="getUrl"
>生成链接</a-button
>
</a-input-group>
</a-form-item>
</a-form>
<qr-code
:options="{ margin: 0 }"
:width="250"
:value="authUrl.authUrl"
v-show="authUrl.authUrl"
/>
<a-form>
<a-form-item label="OpenId" v-show="authResult.openId">
<a-input v-model:value="authResult.openId" disabled style="width: 300px" />
<a-button type="primary" :disabled="!authResult.openId" @click="copy(authResult.openId)"
>复制</a-button
>
</a-form-item>
<a-form-item label="用户ID" v-show="authResult.userId">
<a-input v-model:value="authResult.userId" disabled style="width: 300px" />
<a-button type="primary" @click="copy(authResult.userId)">复制</a-button>
</a-form-item>
<a-form-item label="AccessToken" v-show="authResult.accessToken">
<a-input v-model:value="authResult.accessToken" disabled style="width: 300px" />
<a-button type="primary" @click="copy(authResult.accessToken)">复制</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useIntervalFn } from '@vueuse/shared'
import { FormInstance } from 'ant-design-vue/lib/form'
import { LabeledValue } from 'ant-design-vue/lib/select'
import QrCode from '@/components/Qrcode/src/Qrcode.vue'
import { copyText } from '@/utils/copyTextToClipboard'
import { useMessage } from '@/hooks/web/useMessage'
import {
AuthResult,
AuthUrlResult,
generateAuthUrl,
GenerateAuthUrlParam,
queryAuthResult,
} from './ChannelAuth.api'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { merchantDropdown } from '@/views/daxpay/admin/merchant/info/Merchant.api'
import { mchAppDropdown } from '@/views/daxpay/common/merchant/app/MchApp.api'
import { useDict } from '@/hooks/bootx/useDict'
import { ChannelAuthStatusEnum } from '@/enums/daxpay/ChannelEnum'
const { createMessage } = useMessage()
const { dictDropDown } = useDict()
const { search } = useFormEdit()
const channels = ref<LabeledValue[]>([])
const merchantList = ref<LabeledValue[]>([])
const mchAppList = ref<LabeledValue[]>([])
const formRef = ref<FormInstance>()
const form = ref<GenerateAuthUrlParam>({})
const authResult = ref<AuthResult>({})
const authUrl = ref<AuthUrlResult>({ authUrl: '' })
let pause: any = () => {}
onMounted(() => {
initData()
})
/**
* 初始化数据
*/
async function initData() {
// 通道
channels.value = await dictDropDown('channel')
// 商户
merchantDropdown().then(({ data }) => {
merchantList.value = data
})
}
/**
* 商户变动时刷新应用列表
*/
function merchantChange() {
form.value.appId = undefined
mchAppDropdown(form.value.mchNo).then(({ data }) => {
mchAppList.value = data
})
}
/**
* 复制
*/
function copy(value) {
copyText(value)
createMessage.info('已复制到剪贴板中')
}
/**
* 扫码授权链接
*/
function getUrl() {
pause()
createMessage.info('正在生成链接,请稍等.....')
generateAuthUrl(form.value).then((res) => {
authUrl.value = res.data
query()
})
}
/**
* 查询
*/
function query() {
pause()
let intervalFn = useIntervalFn(
() => {
queryAuthResult(authUrl.value.queryCode)
.then((res) => {
authResult.value = res.data
// 成功
if (authResult.value.status === ChannelAuthStatusEnum.SUCCESS) {
createMessage.success('获取授权结果成功')
pause()
}
if (authResult.value.status === ChannelAuthStatusEnum.NOT_EXIST) {
createMessage.error('获取授权结果失败')
pause()
}
})
.catch((err) => {
// 失败
createMessage.error(err.message)
pause()
})
},
1000 * 3,
{ immediate: false },
)
pause = intervalFn.pause
intervalFn.resume()
}
</script>
<style scoped lang="less"></style>

View File

@@ -122,7 +122,6 @@
// 商户
merchantDropdown().then(({ data }) => {
merchantList.value = data
console.log(merchantList)
})
}

2
types/web.d.ts vendored
View File

@@ -34,7 +34,9 @@ export interface BaseEntity {
* 商户应用基础实体对象
*/
export interface MchEntity extends BaseEntity {
// 商户号
mchNo?: string
// 应用号
appId?: string
}