Browse Source

rest服务接口访问完成

master
wenlele 2 years ago
parent
commit
515eafa4ef
  1. 63
      api/app/lib/controllers/dataService/index.js
  2. 63
      api/app/lib/controllers/latestMetadata/index.js
  3. 8
      api/app/lib/index.js
  4. 2
      api/app/lib/models/resource_consumption.js
  5. 2
      api/app/lib/models/restful_api.js
  6. 8
      api/app/lib/routes/dataService/index.js
  7. 177
      api/app/lib/routes/latestMetadata/index.js
  8. 4
      scripts/0.0.9/01_alter_t_restful_api.sql
  9. 12
      web/client/src/sections/dataService/actions/serviceManagement.js
  10. 88
      web/client/src/sections/dataService/components/editModal.js
  11. 118
      web/client/src/sections/dataService/components/fileModal.js
  12. 66
      web/client/src/sections/dataService/components/groupModal.js
  13. 79
      web/client/src/sections/dataService/components/resourceModal.js
  14. 104
      web/client/src/sections/dataService/components/ruleModal.js
  15. 92
      web/client/src/sections/dataService/containers/serviceManagement.js
  16. 120
      web/client/src/sections/dataService/containers/serviceView.js
  17. 8
      web/client/src/sections/dataService/nav-item.js
  18. 5
      web/client/src/sections/metadataManagement/components/releaseModal.js
  19. 1
      web/client/src/sections/resourceConsumption/containers/myApplication.js
  20. 1
      web/client/src/utils/webapi.js

63
api/app/lib/controllers/dataService/index.js

@ -13,6 +13,10 @@ function getServiceManagement (opts) {
let option = {
where: {},
order: [["id", "desc"]],
include: [{
model: models.ResourceConsumption,
}],
distinct: true
}
if (keyword) {
option.where.name = { $iLike: `%${keyword}%` }
@ -45,9 +49,9 @@ function postServiceManagement (opts) {
try {
const models = ctx.fs.dc.models;
const { id, name, endbled } = ctx.request.body;
const { id, name, enabled } = ctx.request.body;
await models.RestfulApi.update({ name, endbled }, { where: { id: id } })
await models.RestfulApi.update({ name: name, enabled: enabled }, { where: { id: id } })
ctx.status = 204;
} catch (error) {
@ -80,9 +84,62 @@ function delServiceManagement (opts) {
}
}
//获取表字段
function getLookField (opts) {
return async function (ctx, next) {
const models = ctx.fs.dc.models;
const { table, fields } = ctx.query;
try {
let option = {
where: {
$or: []
// code: table,
// 'metadataDatabase.code': fields
},
order: [["id", "desc"]],
include: [{
model: models.MetadataDatabase,
include: [{
model: models.MetadataDatabase,
}],
distinct: true
}],
distinct: true
}
option.where.$or.push(
{
code: table,
}
)
option.where.$or.push(
{
'$metadataDatabase.code$': fields
},
)
let res = await models.MetadataDatabase.findOne(option)
ctx.status = 200;
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '获取表字段失败' }
}
}
}
module.exports = {
getServiceManagement,
postServiceManagement,
delServiceManagement
delServiceManagement,
getLookField
}

63
api/app/lib/controllers/latestMetadata/index.js

@ -527,12 +527,18 @@ async function getTagMetadata(ctx) {
//申请资源
async function postMetadataResourceApplications (ctx) {
try {
const { resourceName, applyBy, resourceType, resourceId } = ctx.request.body;
const { resourceName, applyBy, resourceType, resourceId, restServiceId } = ctx.request.body;
const models = ctx.fs.dc.models;
if (restServiceId) {
await models.ResourceConsumption.create({ applyAt: moment(), approveState: '审批中', ...ctx.request.body });
ctx.body = { message: '申请资源成功' }
ctx.status = 200;
} else {
if (!resourceName || !applyBy || !resourceType || !resourceId) {
ctx.status = 400;
ctx.body = { message: '参数不全,请重新申请资源' }
} else {
const models = ctx.fs.dc.models;
const postOne = await models.ResourceConsumption.findOne({
where: { applyBy: applyBy, resourceName: resourceName, resourceId, resourceType, approve_remarks: null }
});
@ -545,6 +551,8 @@ async function postMetadataResourceApplications(ctx) {
ctx.status = 200;
}
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
@ -828,34 +836,37 @@ async function listStructuredData(ctx) {
}
}
//发布REST服务
async function publishingServices (ctx) {
let message = "发布REST服务失败"
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
const { method, url } = data
// //发布REST服务
// function publishingServices (opts) {
// return async function (ctx, next) {
// let message = "发布REST服务失败"
// try {
// const models = ctx.fs.dc.models;
// const data = ctx.request.body;
// const { method, url } = data
const postOne = await models.RestfulApi.findOne({ where: { method: method, url: url } });
if (postOne) {
message = '路由和请求方式重复'
throw ''
}
// const postOne = await models.RestfulApi.findOne({ where: { method: method, url: url } });
// if (postOne) {
// message = '路由和请求方式重复'
// throw ''
// }
await models.RestfulApi.create(data)
// await models.RestfulApi.create(data)
ctx.body = { message: '发布REST服务成功' }
ctx.status = 200;
// ctx.body = { message: '发布REST服务成功' }
// ctx.status = 200;
// } catch (error) {
// ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
// ctx.status = 400;
// ctx.body = {
// "message": message
// }
// }
// }
// }
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": message
}
}
}
module.exports = {
getResourceCatalog,
postResourceCatalog,
@ -880,5 +891,5 @@ module.exports = {
putMetadataRestapis,
delMetadataRestapis,
listStructuredData,
publishingServices
// publishingServices
}

8
api/app/lib/index.js

@ -57,7 +57,7 @@ module.exports.models = function (dc) {
const {
DataSource, AcquisitionTask, Adapter, User, MetadataDatabase, MetadataFile, MetadataRestapi, AcquisitionLog, ResourceCatalog,
BusinessMetadataDatabase, BusinessMetadataFile, BusinessMetadataRestapi, ResourceConsumption, BusinessRule, StandardDoc, DbStatistics
, RestfulApi, RestfulApiRecord,
, RestfulApi, RestfulApiRecord
} = dc.models;
AcquisitionTask.belongsTo(DataSource, { foreignKey: 'dataSourceId', targetKey: 'id' });
@ -77,6 +77,8 @@ module.exports.models = function (dc) {
BusinessMetadataDatabase.belongsTo(MetadataDatabase, { foreignKey: 'metadataDatabaseId', targetKey: 'id' });
MetadataDatabase.hasMany(BusinessMetadataDatabase, { foreignKey: 'metadataDatabaseId', sourceKey: 'id' });
MetadataDatabase.hasMany(MetadataDatabase, { foreignKey: 'parent', sourceKey: 'id' });
BusinessMetadataFile.belongsTo(MetadataFile, { foreignKey: 'metadataFileId', targetKey: 'id' });
MetadataFile.hasMany(BusinessMetadataFile, { foreignKey: 'metadataFileId', sourceKey: 'id' });
@ -89,7 +91,7 @@ module.exports.models = function (dc) {
ResourceConsumption.belongsTo(User, { foreignKey: 'applyBy', targetKey: 'id', as: "applyUser" });
// ResourceConsumption.belongsTo(RestfulApi, { foreignKey: 'restServiceId', targetKey: 'id',});
RestfulApi.hasMany(ResourceConsumption, { foreignKey: 'restServiceId', targetKey: 'id',});
ResourceConsumption.belongsTo(User, { foreignKey: 'approveBy', targetKey: 'id', as: 'approveUser' });
@ -99,5 +101,5 @@ module.exports.models = function (dc) {
DbStatistics.belongsTo(DataSource, { foreignKey: 'sourceId', targetKey: 'id' });
DataSource.hasMany(DbStatistics, { foreignKey: 'sourceId', sourceKey: 'id' })
RestfulApi.belongsTo(RestfulApiRecord, { foreignKey: 'restServiceId', targetKey: 'id' });
RestfulApiRecord.belongsTo(RestfulApi, { foreignKey: 'restServiceId', targetKey: 'id' })
};

2
api/app/lib/models/resource_consumption.js

@ -18,7 +18,7 @@ module.exports = dc => {
},
resourceId: {
type: DataTypes.INTEGER,
allowNull: false,
allowNull: true,
defaultValue: null,
comment: "资源id",
primaryKey: false,

2
api/app/lib/models/restful_api.js

@ -53,7 +53,7 @@ module.exports = dc => {
autoIncrement: false
},
conditions: {
type: DataTypes.JSONB,
type: DataTypes.TEXT,
allowNull: true,
defaultValue: null,
comment: "数据库表查询条件",

8
api/app/lib/routes/dataService/index.js

@ -13,4 +13,12 @@ module.exports = function (app, router, opts, AuthCode) {
app.fs.api.logAttr['DEL/service-management/:id'] = { content: '删除REST服务', visible: true };
router.del('/service-management/:id', model.delServiceManagement(opts))
app.fs.api.logAttr['GET/lookField'] = { content: '获取表字段', visible: true };
router.get('/lookField', model.getLookField(opts));
};

177
api/app/lib/routes/latestMetadata/index.js

@ -1,4 +1,7 @@
'use strict';
const { Pool } = require('pg');
const moment = require('moment')
const latestMetadata = require('../../controllers/latestMetadata');
@ -72,7 +75,179 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/listStructuredData'] = { content: '获取对表的库与字段信息', visible: true };
router.get('/listStructuredData', latestMetadata.listStructuredData);
app.fs.api.logAttr['POST/publishing/services'] = { content: '发布REST服务', visible: true };
router.post('/publishing/services', latestMetadata.publishingServices);
router.post('/publishing/services', async (ctx) => {
const data = ctx.request.body;
opts.exclude.push({ p: data.url, o: 'GET' });
router.get(data.url, async (ctx) => {
let message = "获取库表元数据列表失败"
try {
const models = ctx.fs.dc.models;
const { token } = ctx.query;
if (!token) {
message = '缺少访问令牌'
throw ''
} else {
let tokens
let findOne = await models.RestfulApi.findOne({
where: { url: data.url, method: data.method },
order: [["id", "desc"]],
include: [{
model: models.ResourceConsumption,
}],
distinct: true
});
findOne && findOne.resourceConsumptions.map(s => {
if (!tokens && s.token) {
tokens = s.token
}
})
if (tokens && tokens == token) {
if (findOne.enabled) {
const pool = new Pool({
user: ctx.fs.dc.orm.config.username,
host: ctx.fs.dc.orm.config.host,
database: findOne.table,
password: ctx.fs.dc.orm.config.password,
port: ctx.fs.dc.orm.config.port,
})
const client = await pool.connect()
const res = await client.query(findOne.conditions, [])
await models.RestfulApiRecord.create({
restServiceId: findOne.id,
visitTime: moment().format('YYYY-MM-DD HH:mm:ss'),
token: tokens
})
ctx.status = 200;
ctx.body = res;
} else {
message = '该REST服务被禁用'
throw ''
}
} else {
message = '该服务令牌错误'
throw ''
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": message
}
}
});
let message = "发布REST服务失败"
try {
const models = ctx.fs.dc.models;
const data = ctx.request.body;
const { method, url } = data
const postOne = await models.RestfulApi.findOne({ where: { method: method, url: url } });
if (postOne) {
message = '路由和请求方式重复'
throw ''
}
await models.RestfulApi.create(data)
ctx.body = { message: '发布REST服务成功' }
ctx.status = 200;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": message
}
}
});
async function release (apps, opts) {
const models = apps.fs.dc.models;
const list = await models.RestfulApi.findAll({
order: [["id", "desc"]],
include: [{
model: models.ResourceConsumption,
}],
distinct: true
}) || []
list.map(v => {
opts.exclude.push({ p: v.url, o: 'GET' });
router.get(v.url, async (ctx) => {
let message = "获取库表元数据列表失败"
try {
const { token } = ctx.query;
if (!token) {
message = '缺少访问令牌'
throw ''
} else {
let tokens
v.resourceConsumptions.map(s => {
if (!tokens && s.token) {
tokens = s.token
}
})
if (tokens && tokens == token) {
if (v.enabled) {
const pool = new Pool({
user: ctx.fs.dc.orm.config.username,
host: ctx.fs.dc.orm.config.host,
database: v.table,
password: ctx.fs.dc.orm.config.password,
port: ctx.fs.dc.orm.config.port,
})
const client = await pool.connect()
const res = await client.query(v.conditions, [])
await models.RestfulApiRecord.create({
restServiceId: v.id,
visitTime: moment().format('YYYY-MM-DD HH:mm:ss'),
token: tokens
})
ctx.status = 200;
ctx.body = res;
} else {
message = '该REST服务被禁用'
throw ''
}
} else {
message = '该服务令牌错误'
throw ''
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": message
}
}
});
})
}
release(app, opts)
};

4
scripts/0.0.9/01_alter_t_restful_api.sql

@ -3,11 +3,11 @@ create table t_restful_api
id serial not null,
"table" varchar(255) not null,
fields varchar(255) not null,
conditions jsonb not null,
conditions text not null,
name varchar(255) not null,
method varchar(10) not null,
url varchar(255) not null,
enabled boolean default true not null
enabled boolean not null
);
comment on table t_restful_api is 'REST服务';

12
web/client/src/sections/dataService/actions/serviceManagement.js

@ -39,4 +39,14 @@ export function delServiceManagement (id) {
});
}
export function getLookField (query = {}) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'GET_LOOK_FIELD',
url: `${ApiTable.lookField}`,
msg: { error: '获取表字段失败' },
reducer: { name: '' }
});
}

88
web/client/src/sections/dataService/components/editModal.js

@ -0,0 +1,88 @@
import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux';
import moment from 'moment';
import { UploadLocal } from '$components';
import { Tabs, Form, Input, DatePicker, Button, Modal, Select, Tag } from 'antd';
function EditModal ({ loading, parent, user, actions, editData = {}, dispatch, close, success, viewDetails }) {
const { dataService } = actions
const [lookFiold, setLookFiold] = useState([]);
const [interfaceName, setInterfaceName] = useState()
useEffect(() => {
setInterfaceName(editData?.name);
dispatch(dataService.getLookField({ table: editData?.table, fields: editData?.fields })).then(res => {
if (res.success) {
setLookFiold(res.payload.data)
}
})
}, [])
return <>
<Modal title={viewDetails ? '查看REST服务详情' : "编辑REST服务"} open={true} width={600}
onOk={e => {
if (viewDetails) {
close()
} else {
dispatch(dataService.postServiceManagement({
id: editData.id, name: interfaceName, enabled: editData?.enabled
})).then(res => {
if (res.success) {
close()
success()
}
})
}
}}
onCancel={() => {
close()
}}
>
<div style={{ display: 'flex' }}>接口名称
<Input style={{ width: 180 }} value={interfaceName} disabled={viewDetails} onChange={e => {
setInterfaceName(e.target.value)
}} /></div>
<div style={{ display: 'flex', margin: '10px 0' }}>接口路由
<Input style={{ width: 180 }} value={editData?.url} disabled={true} />
</div>
<div style={{ margin: '10px 0 10px 0' }} >接口类型
<Input style={{ width: 180 }} value={editData?.method} disabled={true} />
</div>
<div style={{ display: 'flex' }}>返回值
<div style={{ display: 'flex', width: 400, flexDirection: 'column', alignItems: 'center' }}>
<div style={{ display: 'flex' }}>
<div style={{ width: 120, border: '1px solid #dcc', textAlign: 'center' }}>名称</div>
<div style={{ width: 120, border: '1px solid #dcc', textAlign: 'center' }}>意义</div>
<div style={{ width: 120, border: '1px solid #dcc', textAlign: 'center' }}>说明</div>
</div>
{lookFiold?.metadataDatabases?.map((s, index) => {
return <div key={'lookFiold' + index} style={{ display: 'flex' }}>
<div style={{ width: 120, border: '1px solid #dcc', textAlign: 'center' }}>{s.code}</div>
<div style={{ width: 120, border: '1px solid #dcc', textAlign: 'center' }}>{s.name}</div>
<div style={{ width: 120, border: '1px solid #dcc', textAlign: 'center' }}>{s.description}</div>
</div>
})}
</div>
</div>
</Modal >
</>
}
function mapStateToProps (state) {
const { global, auth, resourceCatalog } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
// resourceCatalog: resourceCatalog?.data || [],
// isRequesting: resourceCatalog.isRequesting
};
}
export default connect(mapStateToProps)(EditModal)

118
web/client/src/sections/dataService/components/fileModal.js

@ -1,118 +0,0 @@
import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux';
import moment from 'moment';
import { UploadLocal } from '$components';
import { Tabs, Form, Input, DatePicker, Button, Modal, Select, Tag } from 'antd';
function FileModal ({ loading, parent, user, actions, editData = {}, dispatch, close, success,remove }) {
const { dataQuality } = actions
const [tabsKey, setTabsKey] = useState("stay")
const [query, setQuery] = useState({ page: 0, limit: 10 });
const [proTableList, setProTableList] = useState({ rows: [], count: 0 });
const [approve, setApprove] = useState()
const [form] = Form.useForm();
const [editUrl, setEditUrl] = useState([]);
useEffect(() => {
}, [])
const vsjunct = (params) => {
if (params.length) {
let appendix = []
for (let p of params) {
appendix.push({
fName: p.name,
size: p.size,
fileSize: p.size,
storageUrl: p.storageUrl,//必须有storageUrl
})
}
setEditUrl(appendix)
} else {
setEditUrl([])
}
}
return <>
<Modal title="标准文档上传" open={true} width={600}
onOk={e => {
form.validateFields().then(v => {
dispatch(dataQuality.postStandardDocs({
...v,
path: v?.files[0]?.url, docName: v?.files[0]?.name,
folder: parent || null,
})).then(res => {
if (res.success) {
close()
success()
}
})
})
}}
onCancel={() => {
if (form.getFieldValue('files') && form.getFieldValue('files').length) {
remove(form.getFieldValue('files')[0]?.url)
}
close()
}}
>
<Form
style={{ marginLeft: 20 }}
form={form}
onValuesChange={v => {
}}
autoComplete="off"
labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}
>
<Form.Item label="标准类型" name="standardType" rules={[{ required: true, message: '请选择标准类型' }]}>
<Select style={{ width: 200, }} allowClear
options={[{ value: '国家标准', label: '国家标准', }, { value: '行业标准', label: '行业标准', }, { value: '地方标准', label: '地方标准', },]}
/>
</Form.Item>
<Form.Item label="标签" name="tags" >
<Input allowClear placeholder='请输入标签' maxLength={50} style={{ width: 300, marginRight: 16 }} />
</Form.Item>
<Form.Item
label='文件'
name='files'
key='files'
rules={[{ required: true, message: '文件不可为空' }]}>
<UploadLocal
// addNew={editData.add || !editData.record.files.length}
isLocal={true}
maxFilesNum={1}
maxFileSize={40}
onChange={vsjunct}
fileTypes={["jpg", "png", "gif", "txt", "doc", "docx", "pdf", "xls", "xlsx", "zip", "rar"]}
value={editUrl}
// fileList={editData.record.files || []}
/>
</Form.Item>
<Form.Item style={{ marginLeft: 42 }} key='tip'>
<Tag color="orange">文件大小不超过40MB开放资源包含多个文件建议将文件进行压缩形成压缩包再上传</Tag>
<Tag color="orange">支持的文件格式jpg,png,gif,txt,doc,docx,pdf,xsl,xlsx,zip,rar</Tag>
</Form.Item>
</Form>
</Modal >
</>
}
function mapStateToProps (state) {
const { global, auth, resourceCatalog } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
// resourceCatalog: resourceCatalog?.data || [],
// isRequesting: resourceCatalog.isRequesting
};
}
export default connect(mapStateToProps)(FileModal)

66
web/client/src/sections/dataService/components/groupModal.js

@ -1,66 +0,0 @@
import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux';
import moment from 'moment';
import { v4 as uuidv4 } from 'uuid'
import { Tabs, Form, Input, DatePicker, Button, Modal, Radio } from 'antd';
function GroupModal ({ loading, parent, user, actions, dispatch, close, success, }) {
const { dataQuality } = actions
const [form] = Form.useForm();
useEffect(() => {
}, [])
return <>
<Modal title="新建分组" open={true}
onOk={e => {
form.validateFields().then(v => {
dispatch(dataQuality.postStandardDocFolders({
...v,
parent: parent || null,
})).then(res => {
if (res.success) {
close()
success()
}
})
})
}}
onCancel={() => {
close()
}}
>
<Form
style={{ marginLeft: 20 }}
form={form}
onValuesChange={v => {
}}
autoComplete="off"
>
<Form.Item label="名称" name="name" rules={[{ required: true, message: '请输入分组名称,最多50字', max: 50 },]}>
<Input allowClear placeholder='请输入分组名称' style={{ width: 300, }} />
</Form.Item>
</Form>
</Modal >
</>
}
function mapStateToProps (state) {
const { global, auth, resourceCatalog } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
// resourceCatalog: resourceCatalog?.data || [],
// isRequesting: resourceCatalog.isRequesting
};
}
export default connect(mapStateToProps)(GroupModal)

79
web/client/src/sections/dataService/components/resourceModal.js

@ -0,0 +1,79 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Modal, Input, Form } from 'antd';
const { TextArea } = Input;
const ResourceModal = (props) => {
const { actions, dispatch, success, close, editData, user } = props;
const { dataService, metadataManagement } = actions
const [form] = Form.useForm();
useEffect(() => {
}, []);
const handleOk = () => {
form.validateFields().then(values => {
if (success) {
dispatch(metadataManagement.postMetadataResourceApplications({
...values,
resourceType: '数据服务',
applyBy: user?.id,
approveState: '审批中',
restServiceId: editData?.id
})).then(res => {
if (res.success) {
success()
close()
}
});
}
})
}
const validatorNull = (rule, value, getFieldValue, validateFields, label) => {
if (!value || !value.trim().length) {
return Promise.reject(new Error(`${label}不可空字符串`));
}
return Promise.resolve();
}
return (
<Modal title={'申请资源'} open={true} destroyOnClose
okText='确定' width={800}
onOk={() => handleOk(null)}
onCancel={()=>close()}>
<Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 18 }} initialValues={editData.record || {}}>
<Form.Item
label='资源名称'
name='resourceName'
rules={[{ required: true, message: '资源名称不可空' }]}>
<Input style={{ width: '90%' }} />
</Form.Item>
<Form.Item
label='申请人'
name='applyByName'
rules={[{ required: true, message: '申请人不可空' }]}>
<Input style={{ width: '90%' }} />
</Form.Item>
<Form.Item
label='需求描述'
name='requirements'
rules={[{ required: true, message: '' }, { max: 255, message: `需求描述不超过255个字符` },
({ getFieldValue, validateFields }) => ({
validator (_, value) { return validatorNull(_, value, getFieldValue, validateFields, '需求描述') }
})]}>
<TextArea rows={4} style={{ width: '90%' }} placeholder={`请输入需求描述`} />
</Form.Item>
</Form>
</Modal >
)
}
function mapStateToProps (state) {
const { global, auth, resourceCatalog } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
// resourceCatalog: resourceCatalog?.data || [],
// isRequesting: resourceCatalog.isRequesting
};
}
export default connect(mapStateToProps)(ResourceModal)

104
web/client/src/sections/dataService/components/ruleModal.js

@ -1,104 +0,0 @@
import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux';
import moment from 'moment';
import { v4 as uuidv4 } from 'uuid'
import { Tabs, Form, Input, DatePicker, Button, Modal, Radio, Select, TreeSelect } from 'antd';
const { TextArea } = Input;
const { Option, OptGroup } = Select;
function RuleModal ({ loading, parent, user, actions, dispatch, close, success, treeList, editData }) {
const { dataQuality } = actions
const [tabsKey, setTabsKey] = useState("stay")
const [query, setQuery] = useState({ page: 0, limit: 10 });
const [proTableList, setProTableList] = useState({ rows: [], count: 0 });
const [approve, setApprove] = useState()
const [form] = Form.useForm();
useEffect(() => {
}, [])
return <>
<Modal title={editData?.id ? '编辑业务规则' : "新建业务规则"} open={true}
onOk={e => {
form.validateFields().then(v => {
// console.log(v);
dispatch(dataQuality.postBusinessRules({
...v, id: editData?.id
})).then(res => {
if (res.success) {
close()
success()
}
})
})
}}
onCancel={() => {
close()
}}
>
<Form
style={{ marginLeft: 20 }}
form={form}
onValuesChange={v => {
}}
autoComplete="off"
labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}
>
<Form.Item label="名称" name="name" initialValue={editData?.name} rules={[{ required: true, message: '请输入分组名称,最多50字', max: 50}]}>
<Input allowClear placeholder='请输入分组名称' style={{ width: 300 }} />
</Form.Item>
<Form.Item label="描述" name="description" initialValue={editData?.description} rules={[{ required: true, message: '请输入分组名称' }]}>
<TextArea allowClear autoSize={{ minRows: 2 }} placeholder='描述' style={{ width: 300, }} />
</Form.Item>
<Form.Item label="问题类型" name="problemType" initialValue={editData?.problemType} rules={[{ required: true, message: '请输入分组名称' }]}>
<Select style={{ width: 300, }} placeholder='问题类型'
options={[{ value: '一致性', label: '一致性', }, { value: '准确性', label: '准确性', }, { value: '完整性', label: '完整性', }, { value: '有效性', label: '有效性', }, { value: '及时性', label: '及时性', }, { value: '规范性', label: '规范性', },]}
/>
</Form.Item>
<Form.Item label="问题级别" name="problemLevel" initialValue={editData?.problemLevel} rules={[{ required: true, message: '请输入分组名称' }]}>
<Select style={{ width: 300, }} placeholder='问题级别'
options={[{ value: '一般', label: '一般', }, { value: '重要', label: '重要', }, { value: '严重', label: '严重', },]}
/>
</Form.Item>
<Form.Item label="制定依据" name="ruleBasis" initialValue={editData?.ruleBasis} rules={[{ required: true, message: '请输入分组名称' }]}>
<TreeSelect
showSearch
style={{
width: 300,
}}
// value={}
dropdownStyle={{
maxHeight: 300,
overflow: 'auto',
}}
placeholder=""
allowClear
treeDefaultExpandAll
// onChange={onChange}
treeData={treeList || []}
/>
</Form.Item>
</Form>
</Modal >
</>
}
function mapStateToProps (state) {
const { global, auth, resourceCatalog } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
// resourceCatalog: resourceCatalog?.data || [],
// isRequesting: resourceCatalog.isRequesting
};
}
export default connect(mapStateToProps)(RuleModal)

92
web/client/src/sections/dataService/containers/serviceManagement.js

@ -6,34 +6,28 @@ import { RouteTable } from '$utils'
import { Tabs, Form, Input, Space, Button, Table, Popconfirm } from 'antd';
const { Search } = Input;
import { CreditCardFilled, FilePdfOutlined } from '@ant-design/icons';
import { agent } from 'superagent';
import RuleModal from '../components/ruleModal';
import EditModal from '../components/editModal';
function ServiceManagement ({ loading, clientHeight, actions, dispatch, }) {
const { dataService } = actions
const [query, setQuery] = useState({ page: 0, limit: 10 });
const [ruleModal, setRuleModal] = useState(false)
const [editModal, setEditModal] = useState(false)
const [editData, setEditData] = useState({})
const [keyword, setKeyword] = useState()
const [tableList, setTableList] = useState({ rows: [], count: 0 });
const [treeList, setTreeLista] = useState([])
const [viewDetails, setViewDetails] = useState(false)
useEffect(() => {
resourceData()
// dispatch(dataQuality.getServiceManagement()).then(res => {
// if (res.success) {
// setTreeLista(res.payload.data)
// }
// })
}, [])
let resourceData = (data) => {
let params = data || query
dispatch(dataService.getServiceManagement({ keyword: keyword, ...params, })).then(res => {
dispatch(dataService.getServiceManagement({ ...query, keyword: keyword, ...params, })).then(res => {
if (res.success) {
setTableList({ rows: res.payload.data?.rows, count: res.payload.data?.count })
@ -46,30 +40,35 @@ function ServiceManagement ({ loading, clientHeight, actions, dispatch, }) {
dataIndex: 'name',
}, {
title: '接口路由',
dataIndex: 'description',
dataIndex: 'url',
}, {
title: '接口类型',
dataIndex: 'problemType',
dataIndex: 'method',
}, {
title: '状态',
dataIndex: 'problemLevel'
dataIndex: 'enabled',
render: (text, record) => text ? '启用' : '禁用'
}, {
title: '操作',
dataIndex: 'handle',
width: '250px',
// ellipsis: true,
render: (text, record) => <div style={{ width: 126 }}>
<Button type="link" onClick={() => {
// setEditData(record)
// setRuleModal(true);
}}>查看详情</Button>
<Button type="link" onClick={() => {
// setEditData(record)
// setRuleModal(true);
}}>编辑</Button>
render: (text, record) => <div style={{ width: 200, display: 'flex', justifyContent: 'space-evenly' }}>
<a onClick={() => {
setEditData(record)
setEditModal(true)
setViewDetails(true)
}}>查看详情</a>
<a onClick={() => {
setEditData(record)
setEditModal(true)
}}>编辑</a>
{/* {record?.enabled ? */}
<Popconfirm
title="是否确认删除该业务规则?"
onConfirm={() => {
dispatch(dataQuality.delBusinessRules(record.id)).then(res => {
dispatch(dataService.delBusinessRules(record.id)).then(res => {
if (res.success) {
setQuery({ limit: 10, page: 0 });
resourceData({ limit: 10, page: 0, keyword })
@ -77,12 +76,36 @@ function ServiceManagement ({ loading, clientHeight, actions, dispatch, }) {
})
}}
>
<Button type="link">删除</Button>
<a >删除</a>
</Popconfirm>
<Button type="link" onClick={() => {
// setEditData(record)
// setRuleModal(true);
}}>禁用</Button>
{/* // } */}
{record?.enabled ?
<Popconfirm
title="禁用后该服务将不可用"
onConfirm={() => {
dispatch(dataService.postServiceManagement({
id: record.id, name: record?.name, enabled: false
})).then(res => {
if (res.success) {
resourceData({ keyword })
}
})
}}
>
<a>禁用</a>
</Popconfirm>
: <a onClick={() => {
dispatch(dataService.postServiceManagement({
id: record.id, name: record?.name, enabled: true
})).then(res => {
if (res.success) {
resourceData({ keyword })
}
})
}}>启用</a>
}
</div >
},
];
@ -113,7 +136,7 @@ function ServiceManagement ({ loading, clientHeight, actions, dispatch, }) {
<Table
columns={columns}
dataSource={tableList?.rows || []}
scroll={{ scrollToFirstRowOnChange: true, y: clientHeight - 260 }}
scroll={{ y: clientHeight - 260 }}
pagination={{
current: query?.page + 1,
pageSize: query?.limit,
@ -129,13 +152,14 @@ function ServiceManagement ({ loading, clientHeight, actions, dispatch, }) {
/>
{
ruleModal ?
<RuleModal
treeList={treeList}
editModal ?
<EditModal
editData={editData}
viewDetails={viewDetails}
close={() => {
setRuleModal(false);
setEditModal(false);
setEditData({})
setViewDetails(false)
}}
success={() => {
resourceData({ limit: 10, page: 0, keyword })

120
web/client/src/sections/dataService/containers/serviceView.js

@ -4,41 +4,40 @@ import moment from 'moment';
import { RouteRequest } from '@peace/utils';
import { RouteTable } from '$utils'
import { Tabs, Form, Input, Space, Button, Table, Popconfirm } from 'antd';
import { Tabs, Form, Input, Space, Button, Table, message } from 'antd';
const { Search } = Input;
import { CreditCardFilled, FilePdfOutlined } from '@ant-design/icons';
import { agent } from 'superagent';
import RuleModal from '../components/ruleModal';
import EditModal from '../components/editModal';
import ResourceModal from '../components/resourceModal';
import { If } from 'react-if';
function ServiceView ({ loading, clientHeight, actions, dispatch, }) {
const { dataService } = actions
const { dataService, metadataManagement } = actions
const [query, setQuery] = useState({ page: 0, limit: 10 });
const [ruleModal, setRuleModal] = useState(false)
const [editModal, setEditModal] = useState(false)
const [editData, setEditData] = useState({})
const [keyword, setKeyword] = useState()
const [tableList, setTableList] = useState({ rows: [], count: 0 });
const [treeList, setTreeLista] = useState([])
const [viewDetails, setViewDetails] = useState(false)
const [resourceModal, setResourceModal] = useState(false)
useEffect(() => {
resourceData()
// dispatch(dataQuality.getRegularBasis()).then(res => {
// if (res.success) {
// setTreeLista(res.payload.data)
// }
// })
}, [])
let resourceData = (data) => {
// let params = data || query
// dispatch(dataService.getBusinessRules({ keyword: keyword, ...params, })).then(res => {
// if (res.success) {
// setTableList({ rows: res.payload.data?.rows, count: res.payload.data?.count })
let params = data || query
dispatch(dataService.getServiceManagement({ ...query, keyword: keyword, ...params, })).then(res => {
if (res.success) {
setTableList({ rows: res.payload.data?.rows, count: res.payload.data?.count })
// }
// })
}
})
}
const columns = [{
@ -46,48 +45,53 @@ function ServiceView ({ loading, clientHeight, actions, dispatch, }) {
dataIndex: 'name',
}, {
title: '接口路由',
dataIndex: 'description',
dataIndex: 'url',
}, {
title: '接口类型',
dataIndex: 'problemType',
}, {
title: '状态',
dataIndex: 'problemLevel'
dataIndex: 'method',
}, {
title: '操作',
dataIndex: 'handle',
width: '250px',
// ellipsis: true,
render: (text, record) => <div style={{ width: 126 }}>
<Button type="link" onClick={() => {
// setEditData(record)
// setRuleModal(true);
}}>查看详情</Button>
<Button type="link" onClick={() => {
// setEditData(record)
// setRuleModal(true);
}}>编辑</Button>
<Popconfirm
title="是否确认删除该业务规则?"
onConfirm={() => {
dispatch(dataQuality.delBusinessRules(record.id)).then(res => {
if (res.success) {
setQuery({ limit: 10, page: 0 });
resourceData({ limit: 10, page: 0, keyword })
render: (text, record) => <div style={{ width: 200, display: 'flex', justifyContent: 'space-evenly' }}>
<a onClick={() => {
setEditData(record)
setEditModal(true)
setViewDetails(true)
}}>查看详情</a>
<a onClick={() => {
let result
record?.resourceConsumptions?.map(v => {
if (result != '已有申请成功的资源' && result != '资源审批中') {
if (v.token) {
result = '已有申请成功的资源'
} else if (v.approveState == '审批中') {
result = '资源审批中'
}
}
})
}}
>
<Button type="link">删除</Button>
</Popconfirm>
<Button type="link" onClick={() => {
// setEditData(record)
// setRuleModal(true);
}}>禁用</Button>
if (result) {
message.warning({
duration: 1,
content: result,
})
} else {
setEditData(record)
setResourceModal(true)
}
}}>申请资源</a>
</div >
},
];
return <>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<div style={{ display: 'flex', }}>
<Input
@ -111,7 +115,7 @@ function ServiceView ({ loading, clientHeight, actions, dispatch, }) {
<Table
columns={columns}
dataSource={tableList?.rows || []}
scroll={{ scrollToFirstRowOnChange: true, y: clientHeight - 260 }}
scroll={{ y: clientHeight - 260 }}
pagination={{
current: query?.page + 1,
pageSize: query?.limit,
@ -127,12 +131,28 @@ function ServiceView ({ loading, clientHeight, actions, dispatch, }) {
/>
{
ruleModal ?
<RuleModal
treeList={treeList}
editModal ?
<EditModal
editData={editData}
viewDetails={viewDetails}
close={() => {
setEditModal(false);
setEditData({})
setViewDetails(false)
}}
success={() => {
resourceData({ limit: 10, page: 0, keyword })
}
}
/> : ""
}
{
resourceModal ?
<ResourceModal
editData={editData}
close={() => {
setRuleModal(false);
setResourceModal(false);
setEditData({})
}}
success={() => {

8
web/client/src/sections/dataService/nav-item.js

@ -8,12 +8,12 @@ export function getNavItem (user) {
return (
<SubMenu key="dataService" icon={<CarryOutOutlined />} title='数据服务'>
<Menu.Item key="serviceManagement">
{user?.role == '系统管理员' && <Menu.Item key="serviceManagement">
<Link to="/dataService/serviceManagement">服务管理</Link>
</Menu.Item>
<Menu.Item key="serviceView">
</Menu.Item>}
{user?.role != '系统管理员' && <Menu.Item key="serviceView">
<Link to="/dataService/serviceView">服务查看</Link>
</Menu.Item>
</Menu.Item>}
</ SubMenu >
);
}

5
web/client/src/sections/metadataManagement/components/releaseModal.js

@ -150,7 +150,6 @@ const ReleaseModal = ({ actions, dispatch, onConfirm, onCancel, editData = {} })
onClick={() => {
let whereOption = []
let integrity = true
console.log(fromData);
fromData.map(u => {
if (integrity) {
if (u.field && u.condition && u.value) {
@ -208,7 +207,7 @@ const ReleaseModal = ({ actions, dispatch, onConfirm, onCancel, editData = {} })
return <div key={'fieldData' + index} style={{ display: 'flex' }}>
<div style={{ width: 100, border: '1px solid #dcc', textAlign: 'center' }}>{s.code}</div>
<div style={{ width: 100, border: '1px solid #dcc', textAlign: 'center' }}>{s.name}</div>
<div style={{ width: 100, border: '1px solid #dcc', textAlign: 'center' }}>{s.type}</div>
<div style={{ width: 100, border: '1px solid #dcc', textAlign: 'center' }}>{s.description}</div>
</div>
})}
</div>
@ -252,7 +251,7 @@ const ReleaseModal = ({ actions, dispatch, onConfirm, onCancel, editData = {} })
dispatch(metadataManagement.publishingServices({
table: database?.code,
fields: editData?.code,
conditions: fromData,
conditions: sql,
name: interfaceName,
method: interfaceMode,
url: interfaceUrl,

1
web/client/src/sections/resourceConsumption/containers/myApplication.js

@ -44,6 +44,7 @@ function MyApplication ({ loading, clientHeight, actions, dispatch, user }) {
}, {
title: '数据类型',
dataIndex: 'resourceType',
render: (text, record) => text == '数据服务' ? 'REST服务' : text || '--'
}, {
title: '令牌',
dataIndex: 'token',

1
web/client/src/utils/webapi.js

@ -113,6 +113,7 @@ export const ApiTable = {
//REST服务
serviceManagement: 'service-management',
delServiceManagement: 'service-management/{id}',
lookField:'lookField',
};

Loading…
Cancel
Save