mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
5.0
This commit is contained in:
56
sop-admin/sop-admin-frontend/src/api/apiTable.ts
Normal file
56
sop-admin/sop-admin-frontend/src/api/apiTable.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
import type { DescData } from '@arco-design/web-vue/es/descriptions/interface';
|
||||
|
||||
export interface PolicyRecord {
|
||||
id: string;
|
||||
number: number;
|
||||
name: string;
|
||||
contentType: 'img' | 'horizontalVideo' | 'verticalVideo';
|
||||
filterType: 'artificial' | 'rules';
|
||||
count: number;
|
||||
status: 'online' | 'offline';
|
||||
createdTime: string;
|
||||
}
|
||||
|
||||
export interface PolicyParams extends Partial<PolicyRecord> {
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface PolicyListRes {
|
||||
list: PolicyRecord[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export function queryPolicyList(params: PolicyParams) {
|
||||
return axios.get<PolicyListRes>('/api/page', {
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export interface ServiceRecord {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
name?: string;
|
||||
actionType?: string;
|
||||
icon?: string;
|
||||
data?: DescData[];
|
||||
enable?: boolean;
|
||||
expires?: boolean;
|
||||
}
|
||||
export function queryInspectionList() {
|
||||
return axios.get('/api/list/quality-inspection');
|
||||
}
|
||||
|
||||
export function queryTheServiceList() {
|
||||
return axios.get('/api/list/the-service');
|
||||
}
|
||||
|
||||
export function queryRulesPresetList() {
|
||||
return axios.get('/api/list/rules-preset');
|
||||
}
|
514
sop-admin/sop-admin-frontend/src/views/api/table/index.vue
Normal file
514
sop-admin/sop-admin-frontend/src/views/api/table/index.vue
Normal file
@@ -0,0 +1,514 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="['menu.list', 'menu.list.searchTable']" />
|
||||
<a-card class="general-card" :title="$t('menu.list.searchTable')">
|
||||
<a-row>
|
||||
<a-col :flex="1">
|
||||
<a-form
|
||||
:model="formModel"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
label-align="left"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="8">
|
||||
<a-form-item
|
||||
field="number"
|
||||
:label="$t('searchTable.form.number')"
|
||||
>
|
||||
<a-input
|
||||
v-model="formModel.number"
|
||||
:placeholder="$t('searchTable.form.number.placeholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="name" :label="$t('searchTable.form.name')">
|
||||
<a-input
|
||||
v-model="formModel.name"
|
||||
:placeholder="$t('searchTable.form.name.placeholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item
|
||||
field="contentType"
|
||||
:label="$t('searchTable.form.contentType')"
|
||||
>
|
||||
<a-select
|
||||
v-model="formModel.contentType"
|
||||
:options="contentTypeOptions"
|
||||
:placeholder="$t('searchTable.form.selectDefault')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item
|
||||
field="filterType"
|
||||
:label="$t('searchTable.form.filterType')"
|
||||
>
|
||||
<a-select
|
||||
v-model="formModel.filterType"
|
||||
:options="filterTypeOptions"
|
||||
:placeholder="$t('searchTable.form.selectDefault')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item
|
||||
field="createdTime"
|
||||
:label="$t('searchTable.form.createdTime')"
|
||||
>
|
||||
<a-range-picker
|
||||
v-model="formModel.createdTime"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item
|
||||
field="status"
|
||||
:label="$t('searchTable.form.status')"
|
||||
>
|
||||
<a-select
|
||||
v-model="formModel.status"
|
||||
:options="statusOptions"
|
||||
:placeholder="$t('searchTable.form.selectDefault')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-col>
|
||||
<a-divider style="height: 84px" direction="vertical" />
|
||||
<a-col :flex="'86px'" style="text-align: right">
|
||||
<a-space direction="vertical" :size="18">
|
||||
<a-button type="primary" @click="search">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
{{ $t('searchTable.form.search') }}
|
||||
</a-button>
|
||||
<a-button @click="reset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
{{ $t('searchTable.form.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider style="margin-top: 0" />
|
||||
<a-row style="margin-bottom: 16px">
|
||||
<a-col :span="12">
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
{{ $t('searchTable.operation.create') }}
|
||||
</a-button>
|
||||
<a-upload action="/">
|
||||
<template #upload-button>
|
||||
<a-button>
|
||||
{{ $t('searchTable.operation.import') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col
|
||||
:span="12"
|
||||
style="display: flex; align-items: center; justify-content: end"
|
||||
>
|
||||
<a-button>
|
||||
<template #icon>
|
||||
<icon-download />
|
||||
</template>
|
||||
{{ $t('searchTable.operation.download') }}
|
||||
</a-button>
|
||||
<a-tooltip :content="$t('searchTable.actions.refresh')">
|
||||
<div class="action-icon" @click="search"
|
||||
><icon-refresh size="18"
|
||||
/></div>
|
||||
</a-tooltip>
|
||||
<a-dropdown @select="handleSelectDensity">
|
||||
<a-tooltip :content="$t('searchTable.actions.density')">
|
||||
<div class="action-icon"><icon-line-height size="18" /></div>
|
||||
</a-tooltip>
|
||||
<template #content>
|
||||
<a-doption
|
||||
v-for="item in densityList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:class="{ active: item.value === size }"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-tooltip :content="$t('searchTable.actions.columnSetting')">
|
||||
<a-popover
|
||||
trigger="click"
|
||||
position="bl"
|
||||
@popup-visible-change="popupVisibleChange"
|
||||
>
|
||||
<div class="action-icon"><icon-settings size="18" /></div>
|
||||
<template #content>
|
||||
<div id="tableSetting">
|
||||
<div
|
||||
v-for="(item, index) in showColumns"
|
||||
:key="item.dataIndex"
|
||||
class="setting"
|
||||
>
|
||||
<div style="margin-right: 4px; cursor: move">
|
||||
<icon-drag-arrow />
|
||||
</div>
|
||||
<div>
|
||||
<a-checkbox
|
||||
v-model="item.checked"
|
||||
@change="
|
||||
handleChange($event, item as TableColumnData, index)
|
||||
"
|
||||
>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<div class="title">
|
||||
{{ item.title === '#' ? '序列号' : item.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-table
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:columns="(cloneColumns as TableColumnData[])"
|
||||
:data="renderData"
|
||||
:bordered="false"
|
||||
:size="size"
|
||||
@page-change="onPageChange"
|
||||
>
|
||||
<template #index="{ rowIndex }">
|
||||
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
||||
</template>
|
||||
<template #contentType="{ record }">
|
||||
<a-space>
|
||||
<a-avatar
|
||||
v-if="record.contentType === 'img'"
|
||||
:size="16"
|
||||
shape="square"
|
||||
>
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/581b17753093199839f2e327e726b157.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
<a-avatar
|
||||
v-else-if="record.contentType === 'horizontalVideo'"
|
||||
:size="16"
|
||||
shape="square"
|
||||
>
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/77721e365eb2ab786c889682cbc721c1.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
<a-avatar v-else :size="16" shape="square">
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/ea8b09190046da0ea7e070d83c5d1731.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
{{ $t(`searchTable.form.contentType.${record.contentType}`) }}
|
||||
</a-space>
|
||||
</template>
|
||||
<template #filterType="{ record }">
|
||||
{{ $t(`searchTable.form.filterType.${record.filterType}`) }}
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<span v-if="record.status === 'offline'" class="circle"></span>
|
||||
<span v-else class="circle pass"></span>
|
||||
{{ $t(`searchTable.form.status.${record.status}`) }}
|
||||
</template>
|
||||
<template #operations>
|
||||
<a-button v-permission="['admin']" type="text" size="small">
|
||||
{{ $t('searchTable.columns.operations.view') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, reactive, watch, nextTick } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { queryPolicyList, PolicyRecord, PolicyParams } from '@/api/apiTable';
|
||||
import { Pagination } from '@/types/global';
|
||||
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
|
||||
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
type SizeProps = 'mini' | 'small' | 'medium' | 'large';
|
||||
type Column = TableColumnData & { checked?: true };
|
||||
|
||||
const generateFormModel = () => {
|
||||
return {
|
||||
number: '',
|
||||
name: '',
|
||||
contentType: '',
|
||||
filterType: '',
|
||||
createdTime: [],
|
||||
status: '',
|
||||
};
|
||||
};
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const { t } = useI18n();
|
||||
const renderData = ref<PolicyRecord[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
const cloneColumns = ref<Column[]>([]);
|
||||
const showColumns = ref<Column[]>([]);
|
||||
|
||||
const size = ref<SizeProps>('medium');
|
||||
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
};
|
||||
const pagination = reactive({
|
||||
...basePagination,
|
||||
});
|
||||
const densityList = computed(() => [
|
||||
{
|
||||
name: t('searchTable.size.mini'),
|
||||
value: 'mini',
|
||||
},
|
||||
{
|
||||
name: t('searchTable.size.small'),
|
||||
value: 'small',
|
||||
},
|
||||
{
|
||||
name: t('searchTable.size.medium'),
|
||||
value: 'medium',
|
||||
},
|
||||
{
|
||||
name: t('searchTable.size.large'),
|
||||
value: 'large',
|
||||
},
|
||||
]);
|
||||
const columns = computed<TableColumnData[]>(() => [
|
||||
{
|
||||
title: t('searchTable.columns.index'),
|
||||
dataIndex: 'index',
|
||||
slotName: 'index',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.number'),
|
||||
dataIndex: 'number',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.name'),
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.contentType'),
|
||||
dataIndex: 'contentType',
|
||||
slotName: 'contentType',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.filterType'),
|
||||
dataIndex: 'filterType',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.count'),
|
||||
dataIndex: 'count',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.createdTime'),
|
||||
dataIndex: 'createdTime',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.status'),
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: t('searchTable.columns.operations'),
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations',
|
||||
},
|
||||
]);
|
||||
const contentTypeOptions = computed<SelectOptionData[]>(() => [
|
||||
{
|
||||
label: t('searchTable.form.contentType.img'),
|
||||
value: 'img',
|
||||
},
|
||||
{
|
||||
label: t('searchTable.form.contentType.horizontalVideo'),
|
||||
value: 'horizontalVideo',
|
||||
},
|
||||
{
|
||||
label: t('searchTable.form.contentType.verticalVideo'),
|
||||
value: 'verticalVideo',
|
||||
},
|
||||
]);
|
||||
const filterTypeOptions = computed<SelectOptionData[]>(() => [
|
||||
{
|
||||
label: t('searchTable.form.filterType.artificial'),
|
||||
value: 'artificial',
|
||||
},
|
||||
{
|
||||
label: t('searchTable.form.filterType.rules'),
|
||||
value: 'rules',
|
||||
},
|
||||
]);
|
||||
const statusOptions = computed<SelectOptionData[]>(() => [
|
||||
{
|
||||
label: t('searchTable.form.status.online'),
|
||||
value: 'online',
|
||||
},
|
||||
{
|
||||
label: t('searchTable.form.status.offline'),
|
||||
value: 'offline',
|
||||
},
|
||||
]);
|
||||
const fetchData = async (
|
||||
params: PolicyParams = { current: 1, pageSize: 20 }
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data } = await queryPolicyList(params);
|
||||
renderData.value = data.list;
|
||||
pagination.current = params.current;
|
||||
pagination.total = data.total;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const search = () => {
|
||||
fetchData({
|
||||
...basePagination,
|
||||
...formModel.value,
|
||||
} as unknown as PolicyParams);
|
||||
};
|
||||
const onPageChange = (current: number) => {
|
||||
fetchData({ ...basePagination, current });
|
||||
};
|
||||
|
||||
fetchData();
|
||||
const reset = () => {
|
||||
formModel.value = generateFormModel();
|
||||
};
|
||||
|
||||
const handleSelectDensity = (
|
||||
val: string | number | Record<string, any> | undefined,
|
||||
e: Event
|
||||
) => {
|
||||
size.value = val as SizeProps;
|
||||
};
|
||||
|
||||
const handleChange = (
|
||||
checked: boolean | (string | boolean | number)[],
|
||||
column: Column,
|
||||
index: number
|
||||
) => {
|
||||
if (!checked) {
|
||||
cloneColumns.value = showColumns.value.filter(
|
||||
(item) => item.dataIndex !== column.dataIndex
|
||||
);
|
||||
} else {
|
||||
cloneColumns.value.splice(index, 0, column);
|
||||
}
|
||||
};
|
||||
|
||||
const exchangeArray = <T extends Array<any>>(
|
||||
array: T,
|
||||
beforeIdx: number,
|
||||
newIdx: number,
|
||||
isDeep = false
|
||||
): T => {
|
||||
const newArray = isDeep ? cloneDeep(array) : array;
|
||||
if (beforeIdx > -1 && newIdx > -1) {
|
||||
// 先替换后面的,然后拿到替换的结果替换前面的
|
||||
newArray.splice(
|
||||
beforeIdx,
|
||||
1,
|
||||
newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
|
||||
);
|
||||
}
|
||||
return newArray;
|
||||
};
|
||||
|
||||
const popupVisibleChange = (val: boolean) => {
|
||||
if (val) {
|
||||
nextTick(() => {
|
||||
const el = document.getElementById('tableSetting') as HTMLElement;
|
||||
const sortable = new Sortable(el, {
|
||||
onEnd(e: any) {
|
||||
const { oldIndex, newIndex } = e;
|
||||
exchangeArray(cloneColumns.value, oldIndex, newIndex);
|
||||
exchangeArray(showColumns.value, oldIndex, newIndex);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => columns.value,
|
||||
(val) => {
|
||||
cloneColumns.value = cloneDeep(val);
|
||||
cloneColumns.value.forEach((item, index) => {
|
||||
item.checked = true;
|
||||
});
|
||||
showColumns.value = cloneDeep(cloneColumns.value);
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'SearchTable',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
:deep(.arco-table-th) {
|
||||
&:last-child {
|
||||
.arco-table-th-item-title {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.action-icon {
|
||||
margin-left: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.active {
|
||||
color: #0960bd;
|
||||
background-color: #e3f4fc;
|
||||
}
|
||||
.setting {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 200px;
|
||||
.title {
|
||||
margin-left: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,44 @@
|
||||
export default {
|
||||
'menu.list.searchTable': 'Search Table',
|
||||
'searchTable.form.number': 'Set Number',
|
||||
'searchTable.form.number.placeholder': 'Please enter Set Number',
|
||||
'searchTable.form.name': 'Set Name',
|
||||
'searchTable.form.name.placeholder': 'Please enter Set Name',
|
||||
'searchTable.form.contentType': 'Content Type',
|
||||
'searchTable.form.contentType.img': 'image-text',
|
||||
'searchTable.form.contentType.horizontalVideo': 'Horizontal short video',
|
||||
'searchTable.form.contentType.verticalVideo': 'Vertical short video',
|
||||
'searchTable.form.filterType': 'Filter Type',
|
||||
'searchTable.form.filterType.artificial': 'artificial',
|
||||
'searchTable.form.filterType.rules': 'Rules',
|
||||
'searchTable.form.createdTime': 'Create Date',
|
||||
'searchTable.form.status': 'Status',
|
||||
'searchTable.form.status.online': 'Online',
|
||||
'searchTable.form.status.offline': 'Offline',
|
||||
'searchTable.form.search': 'Search',
|
||||
'searchTable.form.reset': 'Reset',
|
||||
'searchTable.form.selectDefault': 'All',
|
||||
'searchTable.operation.create': 'Create',
|
||||
'searchTable.operation.import': 'Import',
|
||||
'searchTable.operation.download': 'Download',
|
||||
// columns
|
||||
'searchTable.columns.index': '#',
|
||||
'searchTable.columns.number': 'Set Number',
|
||||
'searchTable.columns.name': 'Set Name',
|
||||
'searchTable.columns.contentType': 'Content Type',
|
||||
'searchTable.columns.filterType': 'Filter Type',
|
||||
'searchTable.columns.count': 'Count',
|
||||
'searchTable.columns.createdTime': 'CreatedTime',
|
||||
'searchTable.columns.status': 'Status',
|
||||
'searchTable.columns.operations': 'Operations',
|
||||
'searchTable.columns.operations.view': 'View',
|
||||
// size
|
||||
'searchTable.size.mini': 'mini',
|
||||
'searchTable.size.small': 'small',
|
||||
'searchTable.size.medium': 'middle',
|
||||
'searchTable.size.large': 'large',
|
||||
// actions
|
||||
'searchTable.actions.refresh': 'refresh',
|
||||
'searchTable.actions.density': 'density',
|
||||
'searchTable.actions.columnSetting': 'columnSetting',
|
||||
};
|
@@ -0,0 +1,45 @@
|
||||
export default {
|
||||
'menu.list.searchTable': '查询表格',
|
||||
'searchTable.form.number': '集合编号',
|
||||
'searchTable.form.number.placeholder': '请输入集合编号',
|
||||
'searchTable.form.name': '集合名称',
|
||||
'searchTable.form.name.placeholder': '请输入集合名称',
|
||||
'searchTable.form.contentType': '内容体裁',
|
||||
'searchTable.form.contentType.img': '图文',
|
||||
'searchTable.form.contentType.horizontalVideo': '横版短视频',
|
||||
'searchTable.form.contentType.verticalVideo': '竖版小视频',
|
||||
'searchTable.form.filterType': '筛选方式',
|
||||
'searchTable.form.filterType.artificial': '人工筛选',
|
||||
'searchTable.form.filterType.rules': '规则筛选',
|
||||
'searchTable.form.createdTime': '创建时间',
|
||||
'searchTable.form.status': '状态',
|
||||
'searchTable.form.status.online': '已上线',
|
||||
'searchTable.form.status.offline': '已下线',
|
||||
'searchTable.form.search': '查询',
|
||||
'searchTable.form.reset': '重置',
|
||||
'searchTable.form.selectDefault': '全部',
|
||||
'searchTable.operation.create': '新建',
|
||||
'searchTable.operation.import': '批量导入',
|
||||
'searchTable.operation.download': '下载',
|
||||
// columns
|
||||
'searchTable.columns.index': '#',
|
||||
'searchTable.columns.number': '集合编号',
|
||||
'searchTable.columns.name': '集合名称',
|
||||
'searchTable.columns.contentType': '内容体裁',
|
||||
'searchTable.columns.filterType': '筛选方式',
|
||||
'searchTable.columns.count': '内容量',
|
||||
'searchTable.columns.createdTime': '创建时间',
|
||||
'searchTable.columns.status': '状态',
|
||||
'searchTable.columns.operations': '操作',
|
||||
'searchTable.columns.operations.view': '查看',
|
||||
|
||||
// size
|
||||
'searchTable.size.mini': '迷你',
|
||||
'searchTable.size.small': '偏小',
|
||||
'searchTable.size.medium': '中等',
|
||||
'searchTable.size.large': '偏大',
|
||||
// actions
|
||||
'searchTable.actions.refresh': '刷新',
|
||||
'searchTable.actions.density': '密度',
|
||||
'searchTable.actions.columnSetting': '列设置',
|
||||
};
|
@@ -36,16 +36,13 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- nacos注册中心
|
||||
<!-- nacos注册中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
|
||||
<version>0.2.1</version>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
@@ -80,6 +77,18 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-bom</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- 打包时跳过测试 -->
|
||||
|
@@ -1,7 +1 @@
|
||||
server.port=8082
|
||||
spring.application.name=story-service
|
||||
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
dubbo.application.qos-enable=false
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
@@ -0,0 +1,2 @@
|
||||
|
||||
dubbo.registry.address=nacos://localhost:8848
|
@@ -1 +1,9 @@
|
||||
spring.profiles.active=dev
|
||||
spring.profiles.active=test
|
||||
|
||||
server.port=8082
|
||||
spring.application.name=story-service
|
||||
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
dubbo.application.qos-enable=false
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
@@ -64,10 +64,10 @@
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- nacos注册中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
|
||||
<version>0.2.1</version>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -85,6 +85,12 @@
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
@@ -103,6 +109,13 @@
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@@ -153,14 +166,10 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<artifactId>dubbo-bom</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
|
||||
<version>0.2.1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@@ -12,53 +12,5 @@ public class SopConstants {
|
||||
|
||||
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||
public static final String UTF8 = "UTF-8";
|
||||
public static final String FORMAT_JSON = "json";
|
||||
public static final String DEFAULT_SIGN_METHOD = "md5";
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
|
||||
public static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path";
|
||||
|
||||
public static final String METADATA_SERVER_CONTEXT_PATH_COMPATIBILITY = "context-path";
|
||||
|
||||
/**
|
||||
* 在拦截器中调用获取参数:
|
||||
* String cachedBody = (String)exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY);
|
||||
*/
|
||||
public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
|
||||
|
||||
/**
|
||||
* 在拦截器中调用获取参数:
|
||||
* Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
|
||||
*/
|
||||
public static final String CACHE_REQUEST_BODY_FOR_MAP = "cacheRequestBodyForMap";
|
||||
|
||||
public static final String CACHE_API_PARAM = "cacheApiParam";
|
||||
|
||||
public static final String CACHE_UPLOAD_REQUEST = "cacheUploadRequest";
|
||||
|
||||
public static final String X_SERVICE_ERROR_CODE = "x-service-error-code";
|
||||
|
||||
public static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message";
|
||||
|
||||
public static final String X_SERVICE_ERROR_RESPONSE = "x-service-error-response";
|
||||
|
||||
public static final int BIZ_ERROR_STATUS = 4000;
|
||||
public static final int UNKNOWN_ERROR_STATUS = 5050;
|
||||
|
||||
public static final String UNKNOWN_SERVICE= "_sop_unknown_service_";
|
||||
public static final String UNKNOWN_METHOD = "_sop_unknown_method_";
|
||||
public static final String UNKNOWN_VERSION = "_sop_unknown_version_";
|
||||
|
||||
public static final String METADATA_ENV_KEY = "env";
|
||||
public static final String METADATA_ENV_PRE_VALUE = "pre";
|
||||
public static final String METADATA_ENV_GRAY_VALUE = "gray";
|
||||
|
||||
public static final String CACHE_ROUTE_INTERCEPTOR_CONTEXT = "cacheRouteInterceptorContext";
|
||||
public static final String TARGET_SERVICE = "sop-target-service";
|
||||
public static final String RESTFUL_REQUEST = "sop-restful-request";
|
||||
|
||||
public static final String METADATA_KEY_TIME_STARTUP = "server.startup-time";
|
||||
|
||||
public static final String CACHE_ROUTE_INFO = "cacheRouteInfo";
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
package com.gitee.sop.index.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 自定的扩展组件放这里
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Configuration
|
||||
public class CustomConfig {
|
||||
|
||||
|
||||
}
|
@@ -1,11 +1,18 @@
|
||||
package com.gitee.sop.index.config;
|
||||
|
||||
import com.gitee.sop.index.message.ErrorFactory;
|
||||
import com.gitee.sop.index.service.ParamExecutor;
|
||||
import com.gitee.sop.index.service.ParamExecutorImpl;
|
||||
import com.gitee.sop.index.service.RouteService;
|
||||
import com.gitee.sop.index.service.RouteServiceImpl;
|
||||
import com.gitee.sop.index.service.interceptor.internal.ResultRouteInterceptor;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalApiCacheManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.ApiManager;
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
import com.gitee.sop.index.service.manager.SecretManager;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalApiManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalIsvManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalSecretManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.RedisApiCacheManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.RedisApiManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.RedisIsvManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.RedisSecretManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -24,38 +31,38 @@ import javax.annotation.PostConstruct;
|
||||
public class IndexConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.api-cache", havingValue = "local", matchIfMissing = true)
|
||||
public LocalApiCacheManagerImpl localApiCacheManager() {
|
||||
return new LocalApiCacheManagerImpl();
|
||||
@ConditionalOnProperty(value = "manager.api", havingValue = "local", matchIfMissing = true)
|
||||
public ApiManager localApiManager() {
|
||||
return new LocalApiManagerImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.api-cache", havingValue = "redis")
|
||||
public RedisApiCacheManagerImpl redisApiCacheManager() {
|
||||
return new RedisApiCacheManagerImpl();
|
||||
@ConditionalOnProperty(value = "manager.api", havingValue = "redis")
|
||||
public ApiManager redisApiManager() {
|
||||
return new RedisApiManagerImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.isv", havingValue = "local", matchIfMissing = true)
|
||||
public LocalIsvManagerImpl localIsvManager() {
|
||||
public IsvManager localIsvManager() {
|
||||
return new LocalIsvManagerImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.isv", havingValue = "redis")
|
||||
public RedisIsvManagerImpl redisIsvManager() {
|
||||
public IsvManager redisIsvManager() {
|
||||
return new RedisIsvManagerImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.secret", havingValue = "local", matchIfMissing = true)
|
||||
public LocalSecretManagerImpl localSecretManager() {
|
||||
public SecretManager localSecretManager() {
|
||||
return new LocalSecretManagerImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.secret", havingValue = "redis")
|
||||
public RedisSecretManager redisSecretManager() {
|
||||
public SecretManager redisSecretManager() {
|
||||
return new RedisSecretManager();
|
||||
}
|
||||
|
||||
@@ -67,6 +74,17 @@ public class IndexConfig {
|
||||
return new ResultRouteInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ParamExecutor paramExecutor() {
|
||||
return new ParamExecutorImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RouteService routeService() {
|
||||
return new RouteServiceImpl();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
@@ -1,11 +1,10 @@
|
||||
package com.gitee.sop.index.controller;
|
||||
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.request.ApiRequestContextFactory;
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
import com.gitee.sop.index.service.ParamExecutor;
|
||||
import com.gitee.sop.index.service.RouteService;
|
||||
import com.gitee.sop.index.util.ResponseUtil;
|
||||
import com.gitee.sop.support.request.FileData;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -27,28 +26,39 @@ public class IndexController {
|
||||
@Resource
|
||||
private RouteService routeService;
|
||||
|
||||
@Resource
|
||||
private ParamExecutor<HttpServletRequest, HttpServletResponse> paramExecutor;
|
||||
|
||||
@GetMapping("/")
|
||||
public void home(HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("Open Platform");
|
||||
// 跳转到网站首页
|
||||
// response.sendRedirect("https://www.baidu.com");
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求入口
|
||||
*
|
||||
* @return 返回响应内容
|
||||
* @apiNote 参数描述
|
||||
<pre>
|
||||
参数 类型 是否必填 最大长度 描述 示例值
|
||||
app_id String 是 32 支付宝分配给开发者的应用ID 2014072300007148
|
||||
method String 是 128 接口名称 alipay.trade.fastpay.refund.query
|
||||
format String 否 40 仅支持JSON JSON
|
||||
charset String 是 10 请求使用的编码格式,如utf-8,gbk,gb2312等 utf-8
|
||||
sign_type String 是 10 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2
|
||||
sign String 是 344 商户请求参数的签名串,详见签名 详见示例
|
||||
timestamp String 是 19 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" 2014-07-24 03:07:50
|
||||
version String 是 3 调用的接口版本,固定为:1.0 1.0
|
||||
app_auth_token String 否 40 详见应用授权概述
|
||||
biz_content String 是 请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档
|
||||
</pre>
|
||||
*/
|
||||
@RequestMapping(value = "api", method = {RequestMethod.GET, RequestMethod.POST})
|
||||
@RequestMapping(value = "${index.path:/api}", method = {RequestMethod.GET, RequestMethod.POST})
|
||||
public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
ApiRequestContext apiRequestContext = ApiRequestContextFactory.build(request);
|
||||
ApiRequestContext apiRequestContext = paramExecutor.build(request);
|
||||
ApiResponse apiResponse = routeService.route(apiRequestContext);
|
||||
Object data = apiResponse.getData();
|
||||
if (data instanceof FileData) {
|
||||
FileData fileData = (FileData) data;
|
||||
ResponseUtil.writerFile(fileData, response);
|
||||
} else {
|
||||
// 此处还可以判断charset,返回xml格式
|
||||
ResponseUtil.writerJson(apiRequestContext, apiResponse, response);
|
||||
}
|
||||
paramExecutor.write(apiRequestContext, apiResponse, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -8,4 +8,8 @@ import com.gitee.sop.index.dao.entity.IsvKeys;
|
||||
*/
|
||||
public interface IsvKeysMapper extends BaseMapper<IsvKeys> {
|
||||
|
||||
default IsvKeys getByAppId(String appId) {
|
||||
return this.get(IsvKeys::getAppId, appId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,26 @@
|
||||
package com.gitee.sop.index.request;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RequestFormatEnum {
|
||||
NONE(""),
|
||||
JSON("json"),
|
||||
XML("xml"),
|
||||
;
|
||||
private final String value;
|
||||
|
||||
public static RequestFormatEnum of(String value) {
|
||||
for (RequestFormatEnum requestFormatEnum : RequestFormatEnum.values()) {
|
||||
if (requestFormatEnum.value.equalsIgnoreCase(value)) {
|
||||
return requestFormatEnum;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
package com.gitee.sop.index.service;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
import com.gitee.sop.index.util.CopyUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class ApiInfoService {
|
||||
|
||||
@Resource
|
||||
private ApiCacheManager apiCacheManager;
|
||||
|
||||
@Resource
|
||||
private ApiInfoMapper apiInfoMapper;
|
||||
|
||||
public ApiInfoDTO getApi(String apiName, String apiVersion) {
|
||||
return apiCacheManager.getOrElse(apiName, apiVersion, () -> {
|
||||
ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiName, apiVersion);
|
||||
return CopyUtil.copyBean(apiInfo, ApiInfoDTO::new);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.gitee.sop.index.service;
|
||||
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 参数处理
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public interface ParamExecutor<Req, Resp> {
|
||||
|
||||
/**
|
||||
* 构建请求参数上下文
|
||||
*
|
||||
* @param request request
|
||||
* @return 返回请求参数上下文
|
||||
*/
|
||||
ApiRequestContext build(Req request);
|
||||
|
||||
/**
|
||||
* 结果返回写入
|
||||
*
|
||||
* @param apiRequestContext 请求参数上下文
|
||||
* @param apiResponse 最终返回结果
|
||||
* @param response response
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
void write(ApiRequestContext apiRequestContext, ApiResponse apiResponse, Resp response) throws IOException;
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.gitee.sop.index.service;
|
||||
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.request.ApiRequestContextFactory;
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
import com.gitee.sop.index.util.ResponseUtil;
|
||||
import com.gitee.sop.support.request.FileData;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 请求参数默认实现
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public class ParamExecutorImpl implements ParamExecutor<HttpServletRequest, HttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public ApiRequestContext build(HttpServletRequest request) {
|
||||
return ApiRequestContextFactory.build(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ApiRequestContext apiRequestContext, ApiResponse apiResponse, HttpServletResponse response) throws IOException {
|
||||
Object data = apiResponse.getData();
|
||||
if (data instanceof FileData) {
|
||||
FileData fileData = (FileData) data;
|
||||
ResponseUtil.writerFile(fileData, response);
|
||||
} else {
|
||||
// 此处还可以判断charset,返回xml格式
|
||||
ResponseUtil.writerText(apiRequestContext, apiResponse, response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@ package com.gitee.sop.index.service;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
import com.gitee.sop.index.common.AttachmentNames;
|
||||
import com.gitee.sop.index.common.ParamInfoDTO;
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
@@ -12,6 +11,7 @@ import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.request.ApiRequest;
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.request.UploadContext;
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
import com.gitee.sop.index.service.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.index.service.validate.Validator;
|
||||
import com.gitee.sop.index.util.ClassUtil;
|
||||
@@ -22,7 +22,6 @@ import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcContextAttachment;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -34,7 +33,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -44,7 +42,6 @@ import java.util.Optional;
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class RouteServiceImpl implements RouteService {
|
||||
|
||||
@@ -204,7 +201,7 @@ public class RouteServiceImpl implements RouteService {
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (routeInterceptors == null) {
|
||||
routeInterceptors = Collections.emptyList();
|
||||
routeInterceptors = new ArrayList<>();
|
||||
}
|
||||
routeInterceptors.sort(Comparator.comparing(RouteInterceptor::getOrder));
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.common.StatusEnum;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
import com.gitee.sop.index.service.manager.ApiManager;
|
||||
import com.gitee.sop.index.util.CopyUtil;
|
||||
import com.gitee.sop.support.service.ApiRegisterService;
|
||||
import com.gitee.sop.support.service.dto.RegisterDTO;
|
||||
@@ -21,7 +21,7 @@ import javax.annotation.Resource;
|
||||
public class ApiRegisterServiceImpl implements ApiRegisterService {
|
||||
|
||||
@Resource
|
||||
private ApiCacheManager apiCacheManager;
|
||||
private ApiManager apiCacheManager;
|
||||
|
||||
@Resource
|
||||
private ApiInfoMapper apiInfoMapper;
|
||||
|
@@ -5,6 +5,6 @@ package com.gitee.sop.index.service.interceptor;
|
||||
*/
|
||||
public class RouteInterceptorOrders {
|
||||
|
||||
public static final int DOWNLOAD = -10;
|
||||
public static final int RESULT_INTERCEPTOR = -1000;
|
||||
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package com.gitee.sop.index.service.interceptor.internal;
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.service.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.index.service.interceptor.RouteInterceptorOrders;
|
||||
import com.gitee.sop.support.request.CommonFileData;
|
||||
import com.gitee.sop.support.request.FileData;
|
||||
|
||||
@@ -19,6 +20,10 @@ import java.util.Objects;
|
||||
public class ResultRouteInterceptor implements RouteInterceptor {
|
||||
|
||||
private static final String CLASS = "class";
|
||||
private static final String KEY_NAME = "name";
|
||||
private static final String KEY_ORIGINAL_FILENAME = "originalFilename";
|
||||
private static final String KEY_CONTENT_TYPE = "contentType";
|
||||
private static final String KEY_BYTES = "bytes";
|
||||
|
||||
@Override
|
||||
public Object afterRoute(ApiRequestContext context, ApiInfoDTO apiInfoDTO, Object result) {
|
||||
@@ -44,10 +49,10 @@ public class ResultRouteInterceptor implements RouteInterceptor {
|
||||
"empty": false
|
||||
*/
|
||||
CommonFileData commonFileData = new CommonFileData();
|
||||
commonFileData.setName(String.valueOf(map.get("name")));
|
||||
commonFileData.setOriginalFilename(String.valueOf(map.get("originalFilename")));
|
||||
commonFileData.setContentType(String.valueOf(map.get("contentType")));
|
||||
commonFileData.setData((byte[]) map.get("bytes"));
|
||||
commonFileData.setName(String.valueOf(map.get(KEY_NAME)));
|
||||
commonFileData.setOriginalFilename(String.valueOf(map.get(KEY_ORIGINAL_FILENAME)));
|
||||
commonFileData.setContentType(String.valueOf(map.get(KEY_CONTENT_TYPE)));
|
||||
commonFileData.setData((byte[]) map.get(KEY_BYTES));
|
||||
return commonFileData;
|
||||
}
|
||||
}
|
||||
@@ -61,6 +66,6 @@ public class ResultRouteInterceptor implements RouteInterceptor {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return RouteInterceptor.super.getOrder();
|
||||
return RouteInterceptorOrders.RESULT_INTERCEPTOR;
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import java.util.function.Supplier;
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface ApiCacheManager {
|
||||
public interface ApiManager {
|
||||
|
||||
void save(ApiInfoDTO apiInfoDTO);
|
||||
|
@@ -7,7 +7,18 @@ import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
*/
|
||||
public interface IsvManager {
|
||||
|
||||
/**
|
||||
* 获取isv信息
|
||||
*
|
||||
* @param appId appId
|
||||
* @return 返回isv信息, 没有返回null
|
||||
*/
|
||||
IsvDTO getIsv(String appId);
|
||||
|
||||
/**
|
||||
* 重新加载isv信息到内存中
|
||||
*
|
||||
* @param appId appId
|
||||
*/
|
||||
void reload(String appId);
|
||||
}
|
||||
|
@@ -15,5 +15,5 @@ public interface SecretManager {
|
||||
*/
|
||||
String getIsvPublicKey(String appId);
|
||||
|
||||
|
||||
String reload(String appId);
|
||||
}
|
||||
|
@@ -1,27 +0,0 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 本地存储接口信息.
|
||||
* @author 六如
|
||||
*/
|
||||
public class LocalApiCacheManagerImpl implements ApiCacheManager {
|
||||
|
||||
private static final Map<String, ApiInfoDTO> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void save(ApiInfoDTO apiInfoDTO) {
|
||||
String key = apiInfoDTO.buildApiNameVersion();
|
||||
CACHE.put(key, apiInfoDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiInfoDTO get(String apiName, String apiVersion) {
|
||||
return CACHE.get(apiName + apiVersion);
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
|
||||
import com.gitee.sop.index.service.manager.ApiManager;
|
||||
import com.gitee.sop.index.util.CopyUtil;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 本地存储接口信息.
|
||||
* @author 六如
|
||||
*/
|
||||
public class LocalApiManagerImpl implements ApiManager {
|
||||
|
||||
private static final Map<String, Optional<ApiInfoDTO>> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Resource
|
||||
protected ApiInfoMapper apiInfoMapper;
|
||||
|
||||
@Override
|
||||
public void save(ApiInfoDTO apiInfoDTO) {
|
||||
String key = apiInfoDTO.buildApiNameVersion();
|
||||
CACHE.put(key, Optional.of(apiInfoDTO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiInfoDTO get(String apiName, String apiVersion) {
|
||||
String key = apiName + apiVersion;
|
||||
return CACHE.computeIfAbsent(key, k-> {
|
||||
ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiName, apiVersion);
|
||||
return Optional.ofNullable(CopyUtil.copyBean(apiInfo, ApiInfoDTO::new));
|
||||
}).orElse(null);
|
||||
}
|
||||
}
|
@@ -7,23 +7,34 @@ import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
import com.gitee.sop.index.util.CopyUtil;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class LocalIsvManagerImpl implements IsvManager {
|
||||
|
||||
private static final Map<String, Optional<IsvDTO>> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@Resource
|
||||
private IsvInfoMapper isvInfoMapper;
|
||||
protected IsvInfoMapper isvInfoMapper;
|
||||
|
||||
@Override
|
||||
public IsvDTO getIsv(String appId) {
|
||||
return CACHE.computeIfAbsent(appId, k -> {
|
||||
IsvInfo isvInfo = isvInfoMapper.getByAppId(appId);
|
||||
return CopyUtil.copyBean(isvInfo, IsvDTO::new);
|
||||
return Optional.ofNullable(CopyUtil.copyBean(isvInfo, IsvDTO::new));
|
||||
}).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(String appId) {
|
||||
IsvInfo isvInfo = isvInfoMapper.getByAppId(appId);
|
||||
IsvDTO isvDTO = CopyUtil.copyBean(isvInfo, IsvDTO::new);
|
||||
CACHE.put(appId, Optional.ofNullable(isvDTO));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,19 +5,37 @@ import com.gitee.sop.index.dao.mapper.IsvKeysMapper;
|
||||
import com.gitee.sop.index.service.manager.SecretManager;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class LocalSecretManagerImpl implements SecretManager {
|
||||
|
||||
private static final Map<String, Optional<String>> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Resource
|
||||
private IsvKeysMapper isvKeysMapper;
|
||||
protected IsvKeysMapper isvKeysMapper;
|
||||
|
||||
@Override
|
||||
public String getIsvPublicKey(String appId) {
|
||||
return isvKeysMapper.query()
|
||||
return CACHE.computeIfAbsent(appId, k -> {
|
||||
String publicKey = isvKeysMapper.query()
|
||||
.eq(IsvKeys::getAppId, appId)
|
||||
.getValue(IsvKeys::getPublicKeyIsv);
|
||||
return Optional.ofNullable(publicKey);
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String reload(String appId) {
|
||||
String publicKey = isvKeysMapper.query()
|
||||
.eq(IsvKeys::getAppId, appId)
|
||||
.getValue(IsvKeys::getPublicKeyIsv);
|
||||
CACHE.put(appId, Optional.ofNullable(publicKey));
|
||||
return publicKey;
|
||||
}
|
||||
}
|
||||
|
@@ -1,24 +0,0 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
|
||||
/**
|
||||
* redis存储接口信息
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public class RedisApiCacheManagerImpl implements ApiCacheManager {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(ApiInfoDTO apiInfoDTO) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiInfoDTO get(String apiName, String apiVersion) {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
import com.gitee.sop.index.util.CopyUtil;
|
||||
import com.gitee.sop.index.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* redis存储接口信息
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
@Slf4j
|
||||
public class RedisApiManagerImpl extends LocalApiManagerImpl {
|
||||
|
||||
private static final String KEY_API = "sop:api";
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Override
|
||||
public void save(ApiInfoDTO apiInfoDTO) {
|
||||
String key = apiInfoDTO.buildApiNameVersion();
|
||||
stringRedisTemplate.opsForHash().put(KEY_API, key, JsonUtil.toJSONString(apiInfoDTO));
|
||||
}
|
||||
|
||||
protected void cache(String key, ApiInfo apiInfo) {
|
||||
ApiInfoDTO apiInfoDTO = CopyUtil.copyBean(apiInfo, ApiInfoDTO::new);
|
||||
stringRedisTemplate.opsForHash().put(KEY_API, key, JsonUtil.toJSONString(apiInfoDTO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiInfoDTO get(String apiName, String apiVersion) {
|
||||
String key = apiName + apiName;
|
||||
try {
|
||||
Object value = stringRedisTemplate.opsForHash().get(KEY_API, key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return JsonUtil.parseObject(String.valueOf(value), ApiInfoDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.error("redis访问失败", e);
|
||||
return super.get(apiName, apiVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("load apiInfo to redis");
|
||||
List<ApiInfo> apiInfos = this.apiInfoMapper.listAll();
|
||||
for (ApiInfo apiInfo : apiInfos) {
|
||||
String key = apiInfo.getApiName() + apiInfo.getApiVersion();
|
||||
this.cache(key, apiInfo);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,20 +1,61 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
import com.gitee.sop.index.dao.entity.IsvInfo;
|
||||
import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
import com.gitee.sop.index.util.CopyUtil;
|
||||
import com.gitee.sop.index.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class RedisIsvManagerImpl implements IsvManager {
|
||||
@Slf4j
|
||||
public class RedisIsvManagerImpl extends LocalIsvManagerImpl {
|
||||
|
||||
private static final String KEY_ISV = "sop:isv";
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
|
||||
@Override
|
||||
public IsvDTO getIsv(String appId) {
|
||||
return null;
|
||||
try {
|
||||
Object value = stringRedisTemplate.opsForHash().get(KEY_ISV, appId);
|
||||
if (value == null) {
|
||||
IsvInfo isvInfo = this.isvInfoMapper.getByAppId(appId);
|
||||
return this.cache(isvInfo);
|
||||
}
|
||||
return JsonUtil.parseObject(String.valueOf(value), IsvDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.error("操作redis失败", e);
|
||||
return super.getIsv(appId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(String appId) {
|
||||
IsvInfo isvInfo = isvInfoMapper.getByAppId(appId);
|
||||
this.cache(isvInfo);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("load isvInfo to redis");
|
||||
List<IsvInfo> isvInfos = this.isvInfoMapper.listAll();
|
||||
for (IsvInfo isvInfo : isvInfos) {
|
||||
this.cache(isvInfo);
|
||||
}
|
||||
}
|
||||
|
||||
protected IsvDTO cache(IsvInfo isvInfo) {
|
||||
IsvDTO isvDTO = CopyUtil.copyBean(isvInfo, IsvDTO::new);
|
||||
stringRedisTemplate.opsForHash().put(KEY_ISV, isvInfo.getAppId(), JsonUtil.toJSONString(isvDTO));
|
||||
return isvDTO;
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,59 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.service.manager.SecretManager;
|
||||
import com.gitee.sop.index.dao.entity.IsvKeys;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class RedisSecretManager implements SecretManager {
|
||||
@Slf4j
|
||||
public class RedisSecretManager extends LocalSecretManagerImpl {
|
||||
|
||||
private static final String KEY_ISV = "sop:sec";
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Override
|
||||
public String getIsvPublicKey(String appId) {
|
||||
return "";
|
||||
try {
|
||||
Object value = stringRedisTemplate.opsForHash().get(KEY_ISV, appId);
|
||||
if (value == null) {
|
||||
return this.reload(appId);
|
||||
}
|
||||
return String.valueOf(value);
|
||||
} catch (Exception e) {
|
||||
log.error("操作redis失败", e);
|
||||
return super.getIsvPublicKey(appId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String reload(String appId) {
|
||||
IsvKeys isvKeys = this.isvKeysMapper.getByAppId(appId);
|
||||
return this.cache(appId, isvKeys);
|
||||
}
|
||||
|
||||
|
||||
protected String cache(String appId, IsvKeys isvKeys) {
|
||||
String publicKey = Optional.ofNullable(isvKeys).map(IsvKeys::getPublicKeyIsv).orElse("");
|
||||
stringRedisTemplate.opsForHash().put(KEY_ISV, appId, publicKey);
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("load isvKey to redis");
|
||||
List<IsvKeys> isvKeys = this.isvKeysMapper.listAll();
|
||||
for (IsvKeys isvKey : isvKeys) {
|
||||
this.cache(isvKey.getAppId(), isvKey);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,15 +1,16 @@
|
||||
package com.gitee.sop.index.service.validate;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.request.ApiRequest;
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ParamNames;
|
||||
import com.gitee.sop.index.common.StatusEnum;
|
||||
import com.gitee.sop.index.config.ApiConfig;
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.request.ApiRequest;
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.request.RequestFormatEnum;
|
||||
import com.gitee.sop.index.request.UploadContext;
|
||||
import com.gitee.sop.index.service.ApiInfoService;
|
||||
import com.gitee.sop.index.service.manager.ApiManager;
|
||||
import com.gitee.sop.index.service.manager.IpBlacklistManager;
|
||||
import com.gitee.sop.index.service.manager.IsvApiPermissionManager;
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
@@ -27,7 +28,6 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import javax.annotation.Resource;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -41,9 +41,7 @@ import java.util.Locale;
|
||||
@Service
|
||||
public class ApiValidator implements Validator {
|
||||
|
||||
private static final int MILLISECOND_OF_ONE_SECOND = 1000;
|
||||
|
||||
private static final List<String> FORMAT_LIST = Arrays.asList("json", "xml");
|
||||
private static final long MILLISECOND_OF_ONE_SECOND = 1000;
|
||||
|
||||
private final Signer signer = new AlipaySigner();
|
||||
|
||||
@@ -63,7 +61,7 @@ public class ApiValidator implements Validator {
|
||||
private ApiConfig apiConfig;
|
||||
|
||||
@Resource
|
||||
private ApiInfoService apiInfoService;
|
||||
private ApiManager apiCacheManager;
|
||||
|
||||
@Resource
|
||||
private IpBlacklistManager ipBlacklistManager;
|
||||
@@ -94,7 +92,7 @@ public class ApiValidator implements Validator {
|
||||
checkIP(apiRequestContext);
|
||||
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
ApiInfoDTO apiInfo = apiInfoService.getApi(apiRequest.getMethod(), apiRequest.getVersion());
|
||||
ApiInfoDTO apiInfo = apiCacheManager.get(apiRequest.getMethod(), apiRequest.getVersion());
|
||||
// 检查接口信息
|
||||
checkApiInfo(apiRequestContext, apiInfo);
|
||||
|
||||
@@ -271,8 +269,10 @@ public class ApiValidator implements Validator {
|
||||
protected void checkFormat(ApiRequestContext apiRequestContext) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
String format = apiRequest.getFormat();
|
||||
boolean contains = FORMAT_LIST.contains(format.toLowerCase());
|
||||
if (!contains) {
|
||||
if (ObjectUtils.isEmpty(format)) {
|
||||
return;
|
||||
}
|
||||
if (RequestFormatEnum.of(format) == RequestFormatEnum.NONE) {
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_FORMAT, apiRequestContext.getLocale(),
|
||||
apiRequest.takeNameVersion(), format);
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package com.gitee.sop.index.util;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* json工具类,默认用fastjson2实现
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public class JsonUtil {
|
||||
|
||||
public static String toJSONString(Object object) {
|
||||
if (object == null) {
|
||||
return "null";
|
||||
}
|
||||
return JSON.toJSONString(object);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String value, Class<T> clazz) {
|
||||
if (Objects.equals(value, "null") || ObjectUtils.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parseObject(value, clazz);
|
||||
}
|
||||
|
||||
}
|
@@ -1,8 +1,10 @@
|
||||
package com.gitee.sop.index.util;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import com.gitee.sop.index.request.ApiRequest;
|
||||
import com.gitee.sop.index.request.ApiRequestContext;
|
||||
import com.gitee.sop.index.request.RequestFormatEnum;
|
||||
import com.gitee.sop.index.response.ApiResponse;
|
||||
import com.gitee.sop.support.request.FileData;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@@ -16,7 +18,7 @@ import java.io.InputStream;
|
||||
* @author 六如
|
||||
*/
|
||||
public class ResponseUtil {
|
||||
|
||||
private static final XmlMapper XML_MAPPER = new XmlMapper();
|
||||
|
||||
public static void writerFile(FileData fileData, HttpServletResponse response) throws IOException {
|
||||
InputStream inputStream = fileData.getInputStream();
|
||||
@@ -29,12 +31,20 @@ public class ResponseUtil {
|
||||
IOUtils.copy(inputStream, response.getOutputStream());
|
||||
}
|
||||
|
||||
public static void writerJson(ApiRequestContext apiRequestContext, ApiResponse apiResponse, HttpServletResponse response) throws IOException {
|
||||
public static void writerText(ApiRequestContext apiRequestContext, ApiResponse apiResponse, HttpServletResponse response) throws IOException {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
String charset = apiRequest.getCharset();
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(charset);
|
||||
response.getWriter().write(JSON.toJSONString(apiResponse));
|
||||
String format = apiRequest.getFormat();
|
||||
if (RequestFormatEnum.of(format) == RequestFormatEnum.XML) {
|
||||
response.setContentType(MediaType.APPLICATION_XML_VALUE);
|
||||
String xml = XML_MAPPER.writeValueAsString(apiResponse);
|
||||
response.getWriter().write(xml);
|
||||
} else {
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
String json = JSON.toJSONString(apiResponse);
|
||||
response.getWriter().write(json);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
||||
mybatis.print-sql=true
|
||||
|
||||
# mysql config
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.username=root
|
||||
|
20
sop-index/src/main/resources/application-test.properties
Normal file
20
sop-index/src/main/resources/application-test.properties
Normal file
@@ -0,0 +1,20 @@
|
||||
dubbo.registry.address=nacos://localhost:8848
|
||||
|
||||
mybatis.print-sql=true
|
||||
|
||||
# api manager,local/redis
|
||||
manager.api=redis
|
||||
# isv manager,local/redis
|
||||
manager.isv=redis
|
||||
# secret manger,local/redis
|
||||
manager.secret=redis
|
||||
|
||||
# mysql config
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.username=root
|
||||
mysql.password=root
|
||||
|
||||
# redis config
|
||||
spring.redis.host=localhost
|
||||
spring.redis.port=6379
|
||||
spring.redis.database=0
|
@@ -1,7 +1,18 @@
|
||||
spring.profiles.active=dev
|
||||
spring.profiles.active=test
|
||||
spring.application.name=sop-index
|
||||
server.port=8081
|
||||
|
||||
####### index config #######
|
||||
# request entry path
|
||||
index.path=/api
|
||||
# api manager,local/redis
|
||||
manager.api=local
|
||||
# isv manager,local/redis
|
||||
manager.isv=local
|
||||
# secret manger,local/redis
|
||||
manager.secret=local
|
||||
|
||||
|
||||
####### dubbo config #######
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
@@ -40,16 +51,19 @@ spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&cha
|
||||
spring.datasource.username=${mysql.username}
|
||||
spring.datasource.password=${mysql.password}
|
||||
|
||||
####### redis config #######
|
||||
spring.redis.host=localhost
|
||||
spring.redis.port=6379
|
||||
spring.redis.database=0
|
||||
|
||||
####### mybatis config #######
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillInsert=add_time
|
||||
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillUpdate=update_time
|
||||
# mybatis config file
|
||||
mybatis.config-location=classpath:mybatis/mybatisConfig.xml
|
||||
|
||||
|
||||
# print SQL
|
||||
logging.level.com.gitee.sop.index.dao=error
|
||||
logging.level.com.gitee.fastmybatis=debug
|
||||
mybatis.print-sql=true
|
||||
|
||||
# Need not register api
|
||||
open.register.enable=false
|
||||
logging.level.com.gitee.fastmybatis=info
|
||||
mybatis.print-sql=false
|
||||
|
@@ -68,6 +68,43 @@ public class AlipayClientPostTest extends TestBase {
|
||||
System.out.println(responseData);
|
||||
}
|
||||
|
||||
// 输出返回xml格式
|
||||
@Test
|
||||
public void testGetXml() throws Exception {
|
||||
|
||||
// 公共请求参数
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("app_id", appId);
|
||||
params.put("method", "story.get");
|
||||
params.put("format", "xml"); // xml
|
||||
params.put("charset", "utf-8");
|
||||
params.put("sign_type", "RSA2");
|
||||
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
params.put("version", "1.0");
|
||||
|
||||
// 业务参数
|
||||
Map<String, Object> bizContent = new HashMap<>();
|
||||
bizContent.put("id", "1");
|
||||
bizContent.put("name", "葫芦娃");
|
||||
|
||||
params.put("biz_content", JSON.toJSONString(bizContent));
|
||||
String content = AlipaySignature.getSignContent(params);
|
||||
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
|
||||
params.put("sign", sign);
|
||||
|
||||
System.out.println("----------- 请求信息 -----------");
|
||||
System.out.println("请求参数:" + buildParamQuery(params));
|
||||
System.out.println("商户秘钥:" + privateKey);
|
||||
System.out.println("待签名内容:" + content);
|
||||
System.out.println("签名(sign):" + sign);
|
||||
System.out.println("URL参数:" + buildUrlQuery(params));
|
||||
|
||||
System.out.println("----------- 返回结果 -----------");
|
||||
String responseData = postJson(url, params);// 发送请求
|
||||
// <ApiResponse><code>0</code><msg>success</msg><sub_code></sub_code><sub_msg></sub_msg><data><name>乌鸦喝水</name><id>1</id></data></ApiResponse>
|
||||
System.out.println(responseData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFind() throws Exception {
|
||||
|
||||
|
@@ -1,12 +1,10 @@
|
||||
package com.gitee.sop.test;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -101,7 +99,7 @@ public class AllInOneTest extends TestBase {
|
||||
Assert.assertEquals("isv.route-no-permissions", jsonObject.getString("sub_code"));
|
||||
});
|
||||
|
||||
client.execute(requestBuilder);
|
||||
//client.execute(requestBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user