diff --git a/api/app/lib/controllers/alarm/app.js b/api/app/lib/controllers/alarm/app.js index 3faa464..edbaf63 100644 --- a/api/app/lib/controllers/alarm/app.js +++ b/api/app/lib/controllers/alarm/app.js @@ -108,10 +108,10 @@ async function notedInspection (ctx) { try { const models = ctx.fs.dc.models; const { inspectionId } = ctx.request.body - const { userId } = ctx.fs.api + const { userId, pepUserId } = ctx.fs.api await models.AppInspection.update({ - notedPepUserId: userId, + notedPepUserId: pepUserId, notedTime: moment().format() }, { where: { diff --git a/api/app/lib/controllers/project/bind.js b/api/app/lib/controllers/project/bind.js index 5bed3bd..8a0981a 100644 --- a/api/app/lib/controllers/project/bind.js +++ b/api/app/lib/controllers/project/bind.js @@ -1,74 +1,148 @@ 'use strict'; +const moment = require('moment') async function bindAnxin2pep (ctx) { const transaction = await ctx.fs.dc.orm.transaction(); try { - // const models = ctx.fs.dc.models; - // const { clickHouse } = ctx.app.fs - // const { bindId, name, pepProjectId, anxinProjectId = [], app = [] } = ctx.request.body + const models = ctx.fs.dc.models; + const { clickHouse } = ctx.app.fs + const { userId, pepUserId } = ctx.fs.api + const { bindId, name, pepProjectId, anxinProjectId = [], app = [] } = ctx.request.body - // let bindId_ = bindId - // const existRes = await models.ProjectCorrelation.findOne({ - // where: { - // pepProjectId: pepProjectId - // } - // }) + let bindId_ = bindId + const now = moment() + const existRes = await models.ProjectCorrelation.findOne({ + where: { + pepProjectId: pepProjectId + }, + include: { + model: models.ProjectApp + } + }) - // let storageData = { - // name, pepProjectId, anxinProjectId, - // } - // if (bindId_) { - // if (!existRes) { - // throw '尚无已绑定的项企项目' - // } - // // 修改 - // await models.ProjectCorrelation.update(storageData, { - // where: { - // pepProjectId: pepProjectId - // }, - // transaction - // }) - // } else { - // // 新增 - // if (existRes) { - // // 但是有之前的数据 - // if (existRes.del) { - // // 不过之前的删除了 + let storageData = { + name, pepProjectId, anxinProjectId, + del: false, + } + // 已经创建过的 app + let existApp = [] + // 新增的 + let createAppData = [] + // url 相同需要更新的 解除 lock 状态 + let updateAppData = [] + // 没有出现但存在的 叠加 lock 状态 + let lockAppData = [] + + if (bindId_) { + if (!existRes) { + throw '尚无已绑定的项企项目' + } + // 修改 + await models.ProjectCorrelation.update(storageData, { + where: { + pepProjectId: pepProjectId + }, + transaction + }) + existApp = existRes.projectApps + } else { + // 新增 + if (existRes) { + // 但是有之前的数据 + if (existRes.del) { + // 不过之前的删除了 + await models.ProjectCorrelation.update(storageData, { + where: { + pepProjectId: pepProjectId + }, + transaction + }) + existApp = existRes.projectApps + } else { + // 没有删除 重复添加 + throw '当前项企项目已绑定' + } + } else { + storageData.createUser = userId; + storageData.createTime = now.format() + const createRes = await models.ProjectCorrelation.create(storageData, { + transaction + }) + bindId_ = createRes.id + } + } + + app.forEach((a, i, arr) => { + if (!a.name || !a.url) { + throw `${a.name} ${a.url} 缺少必要参数` + } + let curUrlArr = a.url.split('://') + if (curUrlArr.length < 2) { + throw `${a.name} ${a.url} url 地址错误` + } + curUrlArr.shift() + let curUrl = curUrlArr.join('://') + // 先判断传过来的数据有没有重复 + for (let ii = i + 1; ii < arr.length; ii++) { + let curForUrlArr = arr[ii].url.split('://') + curForUrlArr.shift() + if (curUrl == curForUrlArr.join('://')) { + throw `${a.name} ${a.url} 与 ${arr[ii].name} ${arr[ii].url} 地址重复` + } + } - // } else { - // // 没有删除 重复添加 - // throw '当前项企项目已绑定' - // } - // } else { - // const createRes = await models.ProjectCorrelation.create(storageData, { - // transaction - // }) - // bindId_ = createRes.id + // 再判断和已有的有没有重复 + let existSameApp = existApp.find(ea => { + let curForUrlArr = ea.url.split('://') + curForUrlArr.shift() + return curUrl == curForUrlArr.join('://') + }) + if (existSameApp) { + updateAppData.push({ + id: existSameApp.id, + name: a.name, + url: a.url, + projectId: bindId_, + lock: false, + }) + existSameApp.addAgain = true + } else { + createAppData.push({ + name: a.name, + url: a.url, + projectId: bindId_, + lock: false, + }) + } + lockAppData = existApp.filter(esa => !esa.addAgain).map(esa => { + return { + id: esa.id, + // name: esa.name, + // url: esa.url, + // projectId: bindId_, + lock: true, + } + }) + }) - // await models.ProjectApp.bulkCreate(app.map((a, i, arr) => { - // if (!a.name || !a.url) { - // throw `${a.name} ${a.url} 缺少必要参数` - // } - // let curUrlArr = a.url.split('://') - // if(curUrlArr.length < 2){ - // throw `${a.name} ${a.url} url 地址错误` - // } - // for (let ii = i; ii < arr.length; ii++) { - // let - // } + createAppData.length ? + await models.ProjectApp.bulkCreate(createAppData, { + transaction + }) : null - // return { - // name: a.name, - // url: a.url, - // projectId: bindId_, - // lock: false, - // } - // })) - // } - // } + updateAppData.length || lockAppData.length ? + await Promise.all(updateAppData.concat(lockAppData).map(ud => { + return models.ProjectApp.update(ud, { + where: { + id: ud.id + }, + transaction + }) + })) + : null - // await transaction.commit(); - // ctx.status = 204; + await transaction.commit(); + ctx.status = 204; } catch (error) { await transaction.rollback(); ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); @@ -79,7 +153,31 @@ async function bindAnxin2pep (ctx) { } } +async function del (ctx) { + try { + const models = ctx.fs.dc.models; + const { bindId } = ctx.query + + await models.ProjectCorrelation.update({ + del: true + }, { + where: { + id: bindId + } + }) + + ctx.status = 20; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: error`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + module.exports = { - bindAnxin2pep + bindAnxin2pep, + del }; \ No newline at end of file diff --git a/api/app/lib/controllers/project/index.js b/api/app/lib/controllers/project/index.js index 7f55f79..73df7cd 100644 --- a/api/app/lib/controllers/project/index.js +++ b/api/app/lib/controllers/project/index.js @@ -24,13 +24,14 @@ async function pomsProject (ctx) { try { const models = ctx.fs.dc.models; const { clickHouse } = ctx.app.fs - const { userId } = ctx.fs.api + const { userId, pepUserId } = ctx.fs.api const { limit, page } = ctx.query let findOption = { where: { del: false }, + distinct: true, include: { model: models.ProjectApp, where: { @@ -49,14 +50,11 @@ async function pomsProject (ctx) { findOption.offset = page * limit } - const proRes = await models.ProjectCorrelation.findAll(findOption) - delete findOption.limit - delete findOption.offset - const proCount = await models.ProjectCorrelation.count(findOption) + const proRes = await models.ProjectCorrelation.findAndCountAll(findOption) let pepProjectIds = new Set() let anxinProjectIds = new Set() - for (let p of proRes) { + for (let p of proRes.rows) { pepProjectIds.add(p.pepProjectId) for (let ap of p.anxinProjectId) { anxinProjectIds.add(ap) @@ -71,7 +69,7 @@ async function pomsProject (ctx) { [] - for (let p of proRes) { + for (let p of proRes.rows) { const corPro = pepProjectRes.find(pp => pp.id == p.pepProjectId) p.dataValues.pepProjectName = corPro.project_name let nextAnxinProject = anxinProjectRes.filter(ap => p.anxinProjectId.includes(ap.id)) @@ -79,10 +77,7 @@ async function pomsProject (ctx) { delete p.dataValues.anxinProjectId } ctx.status = 200; - ctx.body = { - count: proCount, - rows: proRes - } + ctx.body = proRes } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: error`); ctx.status = 400; diff --git a/api/app/lib/controllers/push/config.js b/api/app/lib/controllers/push/config.js index 3cbcdb0..f4cba4f 100644 --- a/api/app/lib/controllers/push/config.js +++ b/api/app/lib/controllers/push/config.js @@ -48,7 +48,7 @@ async function list (ctx) { async function edit (ctx) { try { const models = ctx.fs.dc.models; - const { userId } = ctx.fs.api + const { userId, pepUserId } = ctx.fs.api const { pushId, name, pepProjectId = [], alarmType = [], receiverPepUserId = [], timeType = [], disable } = ctx.request.body let storageData = { diff --git a/api/app/lib/middlewares/authenticator.js b/api/app/lib/middlewares/authenticator.js index ac08b3c..617f596 100644 --- a/api/app/lib/middlewares/authenticator.js +++ b/api/app/lib/middlewares/authenticator.js @@ -76,11 +76,17 @@ let authorizeToken = async function (ctx, token) { }) const { userInfo, expired } = authorizeRes; if (expired && moment().valueOf() <= moment(expired).valueOf()) { + const pomsUser = await ctx.app.fs.dc.models.User.findOne({ + where: { + pepUserId: userInfo.id + } + }) rslt = { 'authorized': userInfo.authorized, 'resources': (userInfo || {}).resources || [], }; - ctx.fs.api.userId = userInfo.id; + ctx.fs.api.userId = pomsUser.id; + ctx.fs.api.pepUserId = userInfo.id; ctx.fs.api.userInfo = userInfo; ctx.fs.api.token = token; } diff --git a/api/app/lib/routes/project/index.js b/api/app/lib/routes/project/index.js index ec27061..241aaf3 100644 --- a/api/app/lib/routes/project/index.js +++ b/api/app/lib/routes/project/index.js @@ -10,6 +10,9 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['POST/project/bind'] = { content: '绑定安心云、项目管理项目', visible: true }; router.post('/project/bind', projectBind.bindAnxin2pep); + app.fs.api.logAttr['DEL/project/bind/:bindId'] = { content: '删除安心云、项目管理项目绑定关系', visible: true }; + router.delete('/project/bind/:bindId', projectBind.del); + app.fs.api.logAttr['GET/project/anxincloud'] = { content: '获取安心云项目', visible: true }; router.get('/project/anxincloud', project.projectAnxincloud); diff --git a/web/client/src/sections/install/actions/roles.js b/web/client/src/sections/install/actions/roles.js index 6081910..ff4b638 100644 --- a/web/client/src/sections/install/actions/roles.js +++ b/web/client/src/sections/install/actions/roles.js @@ -30,7 +30,7 @@ export function putOrganizationUser (data) {//更新成员状态 let msg = '' if (data) { pomsUserId = data.pomsUserId - msg=data.msg + msg = data.msg } return (dispatch) => basicAction({ @@ -47,7 +47,7 @@ export function putOrganizationUser (data) {//更新成员状态 export function postOrganizationUser (data) {//添加/编辑成员 let msg = '' if (data) { - msg=data.msg + msg = data.msg } return (dispatch) => basicAction({ @@ -60,20 +60,53 @@ export function postOrganizationUser (data) {//添加/编辑成员 reducer: { name: "" }, }); } -export function deteleOrganizationAdmin(data) { +export function deteleOrganizationAdmin (data) {//删除管理员 let pomsUserId = '' let msg = '' if (data) { pomsUserId = data.id - msg=data.msg + msg = data.msg } return (dispatch) => - basicAction({ - type: "del", + basicAction({ + type: "del", + dispatch: dispatch, + actionType: "DEL_ORGANIZATION_ADMIN", + url: `${ApiTable.deteleOrganizationAdmin.replace("{pomsUserId}", pomsUserId)}`, + msg: { option: msg }, //删除管理员 + reducer: {}, + }); +} +export function getProjectPoms (query) {//获取已绑定项目 + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_POMS", + query: query, + url: `${ApiTable.getProjectPoms}`, + msg: { option: "获取已绑定项目" }, + reducer: { name: "ProjectPoms", params: { noClear: true } }, + }); +} +export function getProjectAnxincloud (query) {//获取安心云项目 + return (dispatch) => basicAction({ + type: "get", dispatch: dispatch, - actionType: "DEL_ORGANIZATION_ADMIN", - url: `${ApiTable.deteleOrganizationAdmin.replace("{pomsUserId}", pomsUserId)}`, - msg: { option: msg }, //删除管理员 - reducer: {}, - }); - } \ No newline at end of file + actionType: "GET_PROJECT_ANXINCLOUD", + query: query, + url: `${ApiTable.getProjectAnxincloud}`, + msg: { option: "获取安心云项目" }, + reducer: { name: "ProjectPoms", params: { noClear: true } }, + }); +} +export function getProjectPmanage (query) {//获取PEP项目管理项目 + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_PMANAGE", + query: query, + url: `${ApiTable.getProjectPmanage}`, + msg: { option: "获取PEP项目管理项目" }, + reducer: { name: "ProjectPoms", params: { noClear: true } }, + }); +} diff --git a/web/client/src/sections/install/components/systemModal.jsx b/web/client/src/sections/install/components/systemModal.jsx new file mode 100644 index 0000000..6a03bf5 --- /dev/null +++ b/web/client/src/sections/install/components/systemModal.jsx @@ -0,0 +1,170 @@ +import React, { useState, useRef, useEffect } from "react"; +import { connect } from "react-redux"; +import { Modal, Form } from "@douyinfe/semi-ui"; +import { IconAlertCircle } from '@douyinfe/semi-icons'; + + +function adminModal (props) { + const { + close, + visible, + dispatch, + pepList, + actions, + adminEdit,//是否是编辑 + editObj, + } = props; + const { install } = actions; + const form = useRef();//表单 + const [disablePeople, setDisablePeople] = useState(true); //页码信息 + const [peopleList, setPeopleList] = useState([]); //人员List + const [departmentId, setDepartmentId] = useState(); //部门id + const [peopleId, setPeopleId] = useState(); //人员id + //初始化 + useEffect(() => { + if (editObj.id) { + let departmentList = [] + for (let i = 0; i < pepList.length; i++) { + if (pepList[i].id == editObj.departments[0].id) { + departmentList = pepList[i].users + } + } + setPeopleList(departmentList) + setDepartmentId(editObj.departments[0].id) + setPeopleId(editObj.pepUserId) + setDisablePeople(false) + } + }, []); + + function handleOk () { + //点击弹框确定 右边按钮 + form.current + .validate() + .then((values) => { + if (adminEdit) { + dispatch(install.deteleOrganizationAdmin({id:editObj.id,msg:''})).then( + dispatch(install.postOrganizationUser({ role: ['admin'], pepUserId: values.pepUserId, msg: '修改管理员' })).then((res) => {//获取项企(PEP)全部部门及其下用户 + if (res.success) { + close(); + } + }) + ) + } + else { + dispatch(install.postOrganizationUser({ role: ['admin'], pepUserId: values.pepUserId, msg: '新增管理员' })).then((res) => {//获取项企(PEP)全部部门及其下用户 + if (res.success) { + close(); + } + }) + } + }) + } + function handleCancel () { + close(); + //点击弹框取消 左边按钮 + } + return ( + <> + +
+
+
+
成员成为管理员后,拥有平台所有权限和项目,成员的普通角色会被禁用。
+
+
{ + for (var key in field) { + if (key == 'department') { + if (values.department >= 0) { + let departmentList = [] + for (let i = 0; i < pepList.length; i++) { + if (pepList[i].id == values.department) { + departmentList = pepList[i].users + } + } + setPeopleList(departmentList) + setDisablePeople(false) + form.current.setValue('pepUserId', undefined); + } + else { + setPeopleList([]) + setDisablePeople(true) + form.current.setValue('pepUserId', undefined); + } + } + } + }} + getFormApi={(formApi) => (form.current = formApi)} + > +
+ + { + pepList.map((item, index) => { + return ( + + {item.name} + + ) + }) + } + +
+
+ + { + peopleList.map((item, index) => { + return ( + + {item.name} + + ) + }) + } + +
+
+
+
+ + ); +} +function mapStateToProps (state) { + const { auth, global, members } = state; + return { + // loading: members.isRequesting, + user: auth.user, + actions: global.actions, + // members: members.data, + }; +} + +export default connect(mapStateToProps)(adminModal); diff --git a/web/client/src/sections/install/containers/roles.jsx b/web/client/src/sections/install/containers/roles.jsx index 71333b5..714ae0e 100644 --- a/web/client/src/sections/install/containers/roles.jsx +++ b/web/client/src/sections/install/containers/roles.jsx @@ -266,7 +266,7 @@ const Roles = (props) => { }, ]) // const [data, setdata] = useState([])//表格数据 - const tableData = useRef([]); //每页实际条数 + const tableData = useRef([]); //表格数据 const page = useRef(query.page);//哪一页 const [limits, setLimits] = useState()//每页实际条数 const mylimits = useRef(); //每页实际条数 diff --git a/web/client/src/sections/install/containers/system.jsx b/web/client/src/sections/install/containers/system.jsx index a31d01f..72b0436 100644 --- a/web/client/src/sections/install/containers/system.jsx +++ b/web/client/src/sections/install/containers/system.jsx @@ -1,16 +1,142 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { connect } from 'react-redux'; -import { Spin, Button } from '@douyinfe/semi-ui'; +import { Skeleton, Button, Pagination, Table, Popconfirm } from '@douyinfe/semi-ui'; +import { SkeletonScreen, } from "$components"; import '../style.less' const Example = (props) => { const { dispatch, actions, user, loading, socket } = props - + const { install } = actions; + const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 + const [limits, setLimits] = useState()//每页实际条数 + const mylimits = useRef(); //每页实际条数 + const [selected, setSelected] = useState([]) //表格被勾选项 + const tableData = useRef([]); //表格数据 useEffect(() => { - // dispatch(actions.example.getMembers(user.orgId)) + // getProjectAnxincloud//获取安心云项目 + // getProjectPmanage//获取PEP项目管理项目 + dispatch(install.getProjectAnxincloud(query)).then((res) => {//获取安心云项目 + console.log('getProjectAnxincloud',res.payload.data); + }) + dispatch(install.getProjectPmanage(query)).then((res) => {//获取PEP项目管理项目 + console.log('getProjectPmanage',res.payload.data); + }) }, []) - - + useEffect(() => { + getProjectPomsList(); + }, [query]); + function getProjectPomsList () { + dispatch(install.getProjectPoms(query)).then((res) => {//获取成员列表 + if (res.success) { + let mytableData = res.payload.data.rows; + for (let index = 0; index < mytableData.length; index++) { + mytableData[index].key = mytableData[index].id + } + tableData.current = mytableData; + setLimits(res.payload.data.count) + mylimits.current = res.payload.data.rows.length + } + }) + } + const [columns, setColumns] = useState([//表格属性 + { + title: "序号", + dataIndex: "index", + render: (text, r, index) => { + return index + 1; + }, + }, + // { + // title: '成员', + // render: (_, row) => { + // let departmentsArr = [] + // for (let i = 0; i < row.departments.length; i++) { + // departmentsArr.push(row.departments[i].name) + // } + // return ( + //
+ //
+ // {row.name} + //
+ //
+ // {row.departments[0].name} + //
+ // { + // row.departments.length > 1 ? ( + // + //
+ // +{row.departments.length - 1} + //
+ //
+ // ) : ('') + // } + //
+ // ) + // } + // }, + { + title: '关联时间', + dataIndex: "createTime", + render: (_, row) => { + return ( +
+ {row.createTime} +
+ ) + } + }, + { + title: "管理", + width: "20%", + dataIndex: "text", + render: (_, row) => { + return ( +
+ + { + dispatch(install.putOrganizationUser({ pomsUserId: row?.id, deleted: true, msg: '删除成员' })).then(() => { + if (page.current > 0 && mylimits.current < 2) { + setQuery({ limit: 10, page: page.current - 1 }) + } else { + setQuery({ limit: 10, page: page.current }) + } + }) + }} + > + + +
+ ); + }, + }, + ]) + const rowSelection = { + selectedRowKeys: selected, + onSelect: (record, selected) => { + console.log(`select row: ${selected}`, record); + }, + onSelectAll: (selected, selectedRows) => { + console.log(`select all rows: ${selected}`, selectedRows); + }, + onChange: (selectedRowKeys, selectedRows) => { + setSelected(selectedRows.map(v => v.key)) + console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); + }, + } return ( <>
@@ -20,7 +146,7 @@ const Example = (props) => {
系统映射
ROLE ASSIGNMENT
-
+
-
- 111 +
+ + { + if (index % 1 === 0) { + return { style: { background: '#FAFCFF' } } + } + }} + rowSelection={rowSelection} + /> + +
+
+
勾选{selected.length}条问题
+ + +
+
+ + 共{limits}个问题 + + { + setQuery({ limit: pageSize, page: currentPage - 1 }); + page.current = currentPage - 1 + }} + /> +
+
diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index f58fae4..8c5b9d2 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -16,12 +16,19 @@ export const ApiTable = { login: "login", logout: "logout", + //设置-鉴权管理 getOrganizationDeps: 'organization/deps',//获取项企(PEP)全部部门及其下用户 getOrganizationUser: 'organization/user',//获取成员列表 putOrganizationUser: 'organization/user/{pomsUserId}',//更新成员状态 postOrganizationUser: 'organization/user',//添加/编辑成员 deteleOrganizationAdmin: 'organization/admin/{pomsUserId}',//删除管理员 + //设置-关系映射 + getProjectPoms:'project/poms',//获取已绑定项目列表 + getProjectAnxincloud:'project/anxincloud',//获取安心云项目 + getProjectPmanage:'project/pmanage',//获取PEP项目管理项目 + + //告警 getProjectPoms: 'project/poms', //获取已绑定项目