f
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
export * from './feature';
|
export * from './feature';
|
||||||
export * from './product';
|
export * from './product';
|
||||||
|
export * from './query-whitelist';
|
||||||
|
|||||||
74
apps/web-antd/src/api/product-manage/query-whitelist.ts
Normal file
74
apps/web-antd/src/api/product-manage/query-whitelist.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace QueryWhitelistApi {
|
||||||
|
export interface OpRequest {
|
||||||
|
name: string;
|
||||||
|
id_card: string;
|
||||||
|
api_codes: string[];
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OpResponse {
|
||||||
|
success: boolean;
|
||||||
|
tianyuan_code: number;
|
||||||
|
tianyuan_message: string;
|
||||||
|
transaction_id?: string;
|
||||||
|
entry_id?: string;
|
||||||
|
id_card_masked?: string;
|
||||||
|
entry_api_codes?: string[];
|
||||||
|
entry_status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OpLogItem {
|
||||||
|
id: number;
|
||||||
|
admin_user_id: number;
|
||||||
|
admin_user_name: string;
|
||||||
|
action: string;
|
||||||
|
name: string;
|
||||||
|
id_card: string;
|
||||||
|
id_card_masked: string;
|
||||||
|
api_codes: string[];
|
||||||
|
remark: string;
|
||||||
|
tianyuan_code: number;
|
||||||
|
tianyuan_message: string;
|
||||||
|
transaction_id: string;
|
||||||
|
entry_id: string;
|
||||||
|
entry_status: string;
|
||||||
|
entry_api_codes: string[];
|
||||||
|
create_time: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OpLogList {
|
||||||
|
total: number;
|
||||||
|
items: OpLogItem[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createQueryWhitelist(data: QueryWhitelistApi.OpRequest) {
|
||||||
|
return requestClient.post<QueryWhitelistApi.OpResponse>(
|
||||||
|
'/query-whitelist/create',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function appendQueryWhitelist(data: QueryWhitelistApi.OpRequest) {
|
||||||
|
return requestClient.post<QueryWhitelistApi.OpResponse>(
|
||||||
|
'/query-whitelist/append',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getQueryWhitelistOpLogList(params: Recordable<any>) {
|
||||||
|
return requestClient.get<QueryWhitelistApi.OpLogList>(
|
||||||
|
'/query-whitelist/op-log/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
appendQueryWhitelist,
|
||||||
|
createQueryWhitelist,
|
||||||
|
getQueryWhitelistOpLogList,
|
||||||
|
};
|
||||||
162
apps/web-antd/src/views/product-manage/query-whitelist/data.ts
Normal file
162
apps/web-antd/src/views/product-manage/query-whitelist/data.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { QueryWhitelistApi } from '#/api/product-manage/query-whitelist';
|
||||||
|
|
||||||
|
export function useConfigFormSchema(
|
||||||
|
featureOptions: Array<{ label: string; value: string }>,
|
||||||
|
): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'id_card',
|
||||||
|
label: '身份证号',
|
||||||
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
maxlength: 18,
|
||||||
|
placeholder: '18位中国大陆身份证号',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '姓名',
|
||||||
|
defaultValue: '*',
|
||||||
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '填 * 表示只按身份证匹配',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
fieldName: 'api_codes',
|
||||||
|
label: '产品编码',
|
||||||
|
rules: 'required',
|
||||||
|
componentProps: {
|
||||||
|
mode: 'multiple',
|
||||||
|
allowClear: true,
|
||||||
|
showSearch: true,
|
||||||
|
optionFilterProp: 'label',
|
||||||
|
options: featureOptions,
|
||||||
|
placeholder: '请选择产品编码',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Textarea',
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
componentProps: {
|
||||||
|
maxlength: 500,
|
||||||
|
rows: 2,
|
||||||
|
showCount: true,
|
||||||
|
placeholder: '可选备注',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'id_card',
|
||||||
|
label: '身份证号',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
fieldName: 'action',
|
||||||
|
label: '操作类型',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: [
|
||||||
|
{ label: '创建规则', value: 'create' },
|
||||||
|
{ label: '追加接口', value: 'append' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'InputNumber',
|
||||||
|
fieldName: 'tianyuan_code',
|
||||||
|
label: '天远返回码',
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'create_time',
|
||||||
|
title: '操作时间',
|
||||||
|
minWidth: 170,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'admin_user_name',
|
||||||
|
title: '操作人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'action',
|
||||||
|
title: '操作类型',
|
||||||
|
minWidth: 100,
|
||||||
|
formatter: ({ cellValue }) => (cellValue === 'append' ? '追加接口' : '创建规则'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'id_card',
|
||||||
|
title: '身份证号',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '姓名规则',
|
||||||
|
minWidth: 90,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'api_codes',
|
||||||
|
title: '提交编码',
|
||||||
|
minWidth: 220,
|
||||||
|
formatter: ({ cellValue }) =>
|
||||||
|
Array.isArray(cellValue) ? cellValue.join(', ') : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tianyuan_code',
|
||||||
|
title: '天远码',
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tianyuan_message',
|
||||||
|
title: '天远描述',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'entry_api_codes',
|
||||||
|
title: '规则编码',
|
||||||
|
minWidth: 220,
|
||||||
|
formatter: ({ cellValue }) =>
|
||||||
|
Array.isArray(cellValue) ? cellValue.join(', ') : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatOpResult(result: QueryWhitelistApi.OpResponse | null) {
|
||||||
|
if (!result) {
|
||||||
|
return '暂无调用结果';
|
||||||
|
}
|
||||||
|
const lines = [
|
||||||
|
`业务结果:${result.success ? '成功' : '失败'}`,
|
||||||
|
`天远码:${result.tianyuan_code}`,
|
||||||
|
`描述:${result.tianyuan_message || '-'}`,
|
||||||
|
`流水号:${result.transaction_id || '-'}`,
|
||||||
|
`规则ID:${result.entry_id || '-'}`,
|
||||||
|
`脱敏身份证:${result.id_card_masked || '-'}`,
|
||||||
|
`规则状态:${result.entry_status || '-'}`,
|
||||||
|
`规则编码:${result.entry_api_codes?.join(', ') || '-'}`,
|
||||||
|
];
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
184
apps/web-antd/src/views/product-manage/query-whitelist/list.vue
Normal file
184
apps/web-antd/src/views/product-manage/query-whitelist/list.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { FeatureApi, QueryWhitelistApi } from '#/api/product-manage';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { Button, Card, message, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import {
|
||||||
|
appendQueryWhitelist,
|
||||||
|
createQueryWhitelist,
|
||||||
|
getFeatureList,
|
||||||
|
getQueryWhitelistOpLogList,
|
||||||
|
} from '#/api/product-manage';
|
||||||
|
|
||||||
|
import { formatOpResult, useColumns, useConfigFormSchema, useGridFormSchema } from './data';
|
||||||
|
|
||||||
|
const featureOptions = ref<Array<{ label: string; value: string }>>([]);
|
||||||
|
const featureItems = ref<FeatureApi.FeatureItem[]>([]);
|
||||||
|
const lastResult = ref<QueryWhitelistApi.OpResponse | null>(null);
|
||||||
|
const submitting = ref(false);
|
||||||
|
|
||||||
|
const configFormSchema = ref(useConfigFormSchema([]));
|
||||||
|
|
||||||
|
const [ConfigForm, configFormApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
schema: configFormSchema,
|
||||||
|
showDefaultActions: false,
|
||||||
|
wrapperClass: 'grid-cols-1 md:grid-cols-2',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
submitOnChange: true,
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getQueryWhitelistOpLogList({
|
||||||
|
page: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
custom: true,
|
||||||
|
export: false,
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
zoom: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<QueryWhitelistApi.OpLogItem>,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadFeatureOptions() {
|
||||||
|
const { items } = await getFeatureList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 500,
|
||||||
|
});
|
||||||
|
featureItems.value = items || [];
|
||||||
|
featureOptions.value = featureItems.value.map((item) => ({
|
||||||
|
label: `${item.api_id}(${item.name})`,
|
||||||
|
value: item.api_id,
|
||||||
|
}));
|
||||||
|
configFormSchema.value = useConfigFormSchema(featureOptions.value);
|
||||||
|
configFormApi.setState({ schema: configFormSchema.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshLogs() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitAction(action: 'append' | 'create') {
|
||||||
|
const { valid } = await configFormApi.validate();
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = await configFormApi.getValues();
|
||||||
|
const apiCodes = Array.isArray(values.api_codes) ? values.api_codes : [];
|
||||||
|
if (apiCodes.length === 0) {
|
||||||
|
message.warning('请至少选择一个产品编码');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
name: values.name || '*',
|
||||||
|
id_card: values.id_card,
|
||||||
|
api_codes: apiCodes,
|
||||||
|
remark: values.remark || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
submitting.value = true;
|
||||||
|
try {
|
||||||
|
const result =
|
||||||
|
action === 'create'
|
||||||
|
? await createQueryWhitelist(payload)
|
||||||
|
: await appendQueryWhitelist(payload);
|
||||||
|
lastResult.value = result;
|
||||||
|
if (result.success) {
|
||||||
|
message.success(action === 'create' ? '创建规则成功' : '追加接口成功');
|
||||||
|
refreshLogs();
|
||||||
|
} else {
|
||||||
|
message.warning(result.tianyuan_message || '天远接口返回失败');
|
||||||
|
refreshLogs();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
submitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectJudicialFeatures() {
|
||||||
|
const judicialCodes = featureItems.value
|
||||||
|
.filter((item) => item.name.includes('司法'))
|
||||||
|
.map((item) => item.api_id);
|
||||||
|
if (judicialCodes.length === 0) {
|
||||||
|
message.info('当前模块列表中未找到名称包含「司法」的模块');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
configFormApi.setFieldValue('api_codes', judicialCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearApiCodes() {
|
||||||
|
configFormApi.setFieldValue('api_codes', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
await loadFeatureOptions();
|
||||||
|
} catch {
|
||||||
|
message.error('加载模块列表失败');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<Card title="查询白名单配置">
|
||||||
|
<ConfigForm />
|
||||||
|
<div class="mt-4 flex flex-wrap items-center justify-between gap-3">
|
||||||
|
<Space wrap>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:loading="submitting"
|
||||||
|
@click="submitAction('create')"
|
||||||
|
>
|
||||||
|
创建规则
|
||||||
|
</Button>
|
||||||
|
<Button :loading="submitting" @click="submitAction('append')">
|
||||||
|
追加接口
|
||||||
|
</Button>
|
||||||
|
<Button @click="selectJudicialFeatures">一键选中司法</Button>
|
||||||
|
<Button @click="clearApiCodes">清空编码</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title="最近一次天远 API 返回结果">
|
||||||
|
<pre class="whitespace-pre-wrap text-sm leading-6">{{ formatOpResult(lastResult) }}</pre>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Grid table-title="操作记录" />
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user