明树Git Lab

Commit a38757d2 authored by zhangqi's avatar zhangqi

Merge branch 'dev' of gitlab.bridata.com:zengfanpei/jt_backend into dev

parents 300455a8 6ce89f36
function flattenTree(forest) {
function flattenTree(forest, idDes = 'id', parentIdDes = 'parentId') {
const result = [];
/**
* 递归处理节点
* @param {Object} node - 当前节点
......@@ -9,49 +9,82 @@ function flattenTree(forest) {
function traverse(node, parentId) {
// 创建节点副本避免修改原数据
const nodeCopy = { ...node };
// 添加parentId属性
nodeCopy.parentId = parentId;
nodeCopy[parentIdDes] = parentId;
// 移除children属性(使用正确拼写)
delete nodeCopy.children;
delete nodeCopy.children;
// 添加当前节点到结果集
result.push(nodeCopy);
result.push(nodeCopy);
// 处理子节点(兼容chilrden拼写错误)
const children = node.chilrden || node.children || [];
const children = node.chilrden || node.children || [];
for (const child of children) {
traverse(child, node.id);
traverse(child, node[idDes]);
}
}
// 遍历森林中的每棵树
for (const tree of forest) {
traverse(tree, null); // 根节点parentId为null
}
return result;
}
// 扩展测试:包含额外字段和标准children字段
const complexForest = [
let complexForest = [
{
xh: "1",
zb: "项目资本金",
dw: "万元",
children: [
{
xh: "1.1",
zb: "能建方出资",
dw: "万元"
},
{
xh: "1.2",
zb: "外部股东",
dw: "万元"
}
]
},
{
id: 'A',
name: 'Root A',
chilrden: [
{ id: 'A1', value: 100 },
{ id: 'A2', value: 200, children: [{ id: 'A2a' }] }
xh: "2",
zb: "贷款",
dw: "万元",
children: [
{
xh: "2.1",
zb: "其中:并非表项目我方贷款/投保额",
dw: "万元"
}
]
},
{
id: 'B',
name: 'Root B',
children: [{ id: 'B1' }]
xh: "3",
zb: "其他出资",
dw: "万元",
children: [
{
xh: "3.1",
zb: "其中:能建方出资",
dw: "万元"
}
]
},
{
xh: "",
zb: "批复总出资",
dw: "万元"
}
];
]
console.log(flattenTree(complexForest));
console.log(flattenTree(complexForest, 'xh', 'parentXh'));
/* 输出:
[
{ id: 'A', name: 'Root A', parentId: null },
......@@ -61,4 +94,38 @@ console.log(flattenTree(complexForest));
{ id: 'B', name: 'Root B', parentId: null },
{ id: 'B1', parentId: 'B' }
]
*/
\ No newline at end of file
*/
const _ = require('lodash');
function buildTree(nodes, keyid = 'id', keyParentId = 'parentId') {
// 创建一个映射,将节点ID映射到节点对象
const nodeMap = new Map(nodes.map(node => [String(node[keyid]), { ...node, children: [] }]));
// 遍历所有节点,并将它们添加到对应父节点的children数组中
nodes.forEach(node => {
const parentId = String(node[keyParentId]);
if (parentId !== null) {
// 确保父节点在映射中存在
if (nodeMap.has(parentId)) {
// 将当前节点添加到父节点的children数组中
nodeMap.get(parentId).children.push(nodeMap.get(String(node[keyid])));
nodeMap.get(parentId).children = _.orderBy(nodeMap.get(parentId).children, 'order')
}
}
});
// 从映射中提取顶级节点(parentId为null)
return _.orderBy(Array.from(nodeMap.values()).filter(node => node[keyParentId] == null), 'order');
}
console.log(JSON.stringify(buildTree([
{ xh: '1', zb: '项目资本金', dw: '万元', parentXh: null },
{ xh: '1.1', zb: '能建方出资', dw: '万元', parentXh: '1' },
{ xh: '1.2', zb: '外部股东', dw: '万元', parentXh: '1' },
{ xh: '2', zb: '贷款', dw: '万元', parentXh: null },
{ xh: '2.1', zb: '其中:并非表项目我方贷款/投保额', dw: '万元', parentXh: '2' },
{ xh: '3', zb: '其他出资', dw: '万元', parentXh: null },
{ xh: '3.1', zb: '其中:能建方出资', dw: '万元', parentXh: '3' },
{ xh: '', zb: '批复总出资', dw: '万元', parentXh: null }
], 'xh', 'parentXh')))
[{ "xh": "1", "zb": "项目资本金", "dw": "万元", "parentXh": null, "children": [{ "xh": "1.1", "zb": "能建方出资", "dw": "万元", "parentXh": "1", "children": [] }, { "xh": "1.2", "zb": "外部股东", "dw": "万元", "parentXh": "1", "children": [] }] }, { "xh": "2", "zb": "贷款", "dw": "万元", "parentXh": null, "children": [{ "xh": "2.1", "zb": "其中:并非表项目我方贷款/投保额", "dw": "万元", "parentXh": "2", "children": [] }] }, { "xh": "3", "zb": "其他出资", "dw": "万元", "parentXh": null, "children": [{ "xh": "3.1", "zb": "其中:能建方出资", "dw": "万元", "parentXh": "3", "children": [] }] }, { "xh": "", "zb": "批复总出资", "dw": "万元", "parentXh": null, "children": [] }]
\ No newline at end of file
const errorMessage = require("../utils/errorMessage");
const _ = require("lodash");
const { Op } = require('sequelize');
const sequelize = require('sequelize');
const userModule = require('../module/userModule');
......@@ -77,8 +78,10 @@ async function createProject(req, res, next) {
let projectInfo = _.omit(body, ['projectJsgms', 'projectGdxxs', 'projectXmtzzes', 'projectBjtjs',
'projectSpyjs', 'projectFxgls', 'projectTzzts', 'projectJczts', 'projectZqrzs', 'projectCwpjzbs', 'projectLcbjds',
'lxzl', 'lxpfwj', 'xgshcl']);
projectInfo.projectCreator = req.user.id;
projectInfo.projectLiuZhuanType = 1; // 草稿状态
let project = await DB.Project.create(projectInfo);
project = project.toJSON();
// 处理 projectJsgms
let projectJsgms = body.projectJsgms || [];
projectJsgms = projectJsgms.map(o => { o.projectId = project.id; return o });
......@@ -89,6 +92,7 @@ async function createProject(req, res, next) {
await DB.ProjectGdxx.bulkCreate(projectGdxxs);
// 处理 projectXmtzzes
let projectXmtzzes = body.projectXmtzzes || [];
projectXmtzzes = utils.flattenTree(projectXmtzzes, 'xh', 'parentXh');
projectXmtzzes = projectXmtzzes.map(o => { o.projectId = project.id; return o });
await DB.ProjectXmtzze.bulkCreate(projectXmtzzes);
// 处理 projectBjtjs
......@@ -136,6 +140,7 @@ async function createProject(req, res, next) {
xgshcl = xgshcl.map(o => { return { fileId: o.id, projectId: project.id, proFieldKey: 'xgshcl' } });
// 处理文件
let proFiles = _.concat(lxzl, lxpfwj, xgshcl);
console.log(proFiles, "=================")
await DB.ProjectFile.bulkCreate(proFiles);
/**
......@@ -156,17 +161,19 @@ async function createProject(req, res, next) {
});
let approvers = await userModule.getProjectApprover(req.user.id, 'xmgs_xmspr');
if (!(approvers && approvers.length > 0)) {
return res.sendError('项目已保存,请联系管理员为本公司添加【项目初审】角色人员');
return res.sendError({ code: '60001', msg: '项目已保存,请联系管理员为本公司添加【项目初审】角色人员' });
}
await DB.Message.createProjectMessage({
await DB.Message.create({
projectId: project.id,
creator: req.user.id,
receivers: approvers.map(o => o.id),
type: 2,
title: `项目【${project.name}】已发起,请尽快处理!`,
content: `项目【${project.name}】已发起,请尽快处理!`
});
await DB.Project.update({ projectLiuZhuanType: 3 }, { where: { id: project.id } });
}
console.log(project)
return res.sendData(project);
} catch (error) {
next(error);
......@@ -206,6 +213,7 @@ async function updateProject(req, res, next) {
await DB.ProjectGdxx.bulkCreate(newprojectGdxxs);
// 处理 projectXmtzzes
let projectXmtzzes = body.projectXmtzzes || [];
projectXmtzzes = utils.flattenTree(projectXmtzzes, 'xh', 'parentXh');
let pnids3 = [], newprojectXmtzzes = [];
projectXmtzzes.map(o => { if (!o.id) { o.projectId = project.id; newprojectXmtzzes.push(o); } else { pnids3.push(o.id) } return o });
await DB.ProjectXmtzze.bulkCreate(newprojectXmtzzes);
......@@ -296,12 +304,12 @@ async function updateProject(req, res, next) {
if (!(approvers && approvers.length > 0)) {
return res.sendError('项目已保存,请联系管理员为本公司添加【项目初审】角色人员');
}
await DB.Message.createProjectMessage({
await DB.Message.create({
projectId: project.id,
creator: req.user.id,
receivers: approvers.map(o => o.id),
type: 2,
content: `项目【${project.name}】已发起,请尽快处理!`
content: `项目【${project.name}】已发起初审,请尽快处理!`
});
projectInfo.projectLiuZhuanType = 3; // 待初审
}
......@@ -335,11 +343,40 @@ async function preJugProject(req, res, next) {
* 1. 更新项目状态 5 初审通过 初审不通过则回到状态1
*/
if (req.body.buttonType === 'approve') {
await DB.Project.update({ projectLiuZhuanType: 3 }, { where: { id } });
await DB.Project.update({ projectLiuZhuanType: 5 }, { where: { id } });
//处理项目流转记录 及消息发送TODO:
await DB.FlowRecord.create({
userId: req.user.id,
actionName: '项目初审通过,提交终审',
projectId: project.id,
});
let approvers = await userModule.getProjectApprover(req.user.id, 'jt_xmzs');
if (!(approvers && approvers.length > 0)) {
return res.sendError('项目已保存,请联系管理员为本公司添加【项目初审】角色人员');
}
await DB.Message.create({
projectId: project.id,
creator: req.user.id,
receivers: approvers.map(o => o.id),
type: 2,
content: `项目【${project.name}】已发起终审,请尽快处理!`
});
} else {
await DB.Project.update({ projectLiuZhuanType: 1 }, { where: { id } });
//处理项目流转记录 及消息发送TODO:
await DB.FlowRecord.create({
userId: req.user.id,
actionName: '项目初审不通过,退回处理',
projectId: project.id,
});
let approvers = [project.projectCreator]
await DB.Message.create({
projectId: project.id,
creator: req.user.id,
receivers: approvers.map(o => o.id),
type: 2,
content: `项目【${project.name}】已退回,请尽快处理!`
});
}
return res.sendData();
......@@ -355,25 +392,30 @@ async function preJugProject(req, res, next) {
* @returns
*/
async function finalJugProject(req, res, next) {
try {
let { id } = req.body;
if (!id) {
return res.sendError(errorMessage.resourceNotFound)
}
let project = await DB.Project.findOne({ where: { id } });
if (!project) {
return res.sendError(errorMessage.resourceNotFound)
}
/**
* 1. 更新项目状态 5 终审通过 初审不通过则回到状态1
*/
if (req.body.buttonType === 'approve') {
await DB.Project.update({ projectLiuZhuanType: 7 }, { where: { id } });
//处理项目流转记录 及消息发送TODO:
} else {
await DB.Project.update({ projectLiuZhuanType: 1 }, { where: { id } });
//处理项目流转记录 及消息发送TODO:
}
try {//TODO:逻辑有道商榷
// let { id } = req.body;
// if (!id) {
// return res.sendError(errorMessage.resourceNotFound)
// }
// let project = await DB.Project.findOne({ where: { id } });
// if (!project) {
// return res.sendError(errorMessage.resourceNotFound)
// }
// /**
// * 1. 更新项目状态 7 终审通过 终审不通过则回到状态1
// */
// if (req.body.buttonType === 'approve') {
// await DB.Project.update({ projectLiuZhuanType: 7 }, { where: { id } });
// //处理项目流转记录 及消息发送TODO:
// await DB.FlowRecord.create({
// userId: req.user.id,
// actionName: '项目终审通过,等待立项',
// projectId: project.id,
// });
// } else {
// await DB.Project.update({ projectLiuZhuanType: 1 }, { where: { id } });
// //处理项目流转记录 及消息发送TODO:
// }
return res.sendData();
} catch (error) {
......@@ -444,7 +486,7 @@ async function getProjectInfo(req, res, next) {
},
{
model: DB.ProjectTzzt,
as: 'projectTzzts',
as: 'projectTzzts',
// where: { del: 0 },
// attributes: [],
},
......@@ -465,9 +507,8 @@ async function getProjectInfo(req, res, next) {
// raw: true
});
project = project.toJSON();
//处理 projectXmtzzes 树结构
let projectXmtzzes = project.projectXmtzzes || [];
project.projectXmtzzes = utils.buildTree(projectXmtzzes, 'kid');
project.projectXmtzzes = utils.buildTree2(project.projectXmtzzes, 'xh', 'parentXh');
//处理文件 还原成项目各个字段拥有的文件
let files = project.files || [];
files = files.map(o => { o.proFieldKey = o.jt_project_file && o.jt_project_file.proFieldKey; delete o.jt_project_file; return o; });
......@@ -512,7 +553,7 @@ async function listProject(req, res, next) {
}
search.limit = limit;
search.offset = offset;
if(req.body.attributes.length) {
if(req.body.attributes && req.body.attributes.length) {
search.attributes = req.body.attributes;
}
let ret = await DB.Project.findAndCountAll(search);
......@@ -595,6 +636,57 @@ async function exportExcel(req, res, next) {
}
}
async function getOwnProjects(req, res, next) {
try {
let page = req.body.page || 1;
let limit = req.body.pageSize || 10;
let offset = (page - 1) * limit;
let search = { where: { del: 0 } };
//确定可见范围,结合当前人角色
let messs = await DB.Message.findAll({
//项目审批信息接收方
where: {
[Op.and]: [sequelize.where(
sequelize.fn('JSON_CONTAINS',
sequelize.col('receivers'),
sequelize.cast(req.user.id, 'JSON'), // 直接将数字转换为JSON
sequelize.literal("'$'")
),
true
),
{ projectId: { [Op.ne]: null } },
]
},
attributes: ['projectId']
});
console.log(messs)
let projectIds = messs.map(o => { return o && o.projectId });
//
let cArr = [
{ projectCreator: req.user.id },
{ id: { [Op.in]: projectIds } }
]
if (req.body.projectName) {
cArr.push({ projectName: { [Op.like]: `%${req.body.projectName}%` } });
}
search.where = {
[Op.or]: cArr
};
search.limit = limit;
search.offset = offset;
if (req.body.attributes && req.body.attributes.length) {
search.attributes = req.body.attributes;
}
console.log(search, "-==")
let ret = await DB.Project.findAndCountAll(search);
return res.sendData(ret);
} catch (error) {
next(error);
}
}
module.exports = {
getProjectFields,
createProject,
......@@ -605,4 +697,5 @@ module.exports = {
exportExcel,
preJugProject,
finalJugProject,
getOwnProjects,
}
\ No newline at end of file
......@@ -13,7 +13,7 @@ async function createResource(req, res, next) {
ret = ret.toJSON();
console.log(ret);
if (body.resourceInfos && body.resourceInfos.length && ret.id) {
if(type == 1){
if(body.type == 1){
let ris = [];
for (let index = 0; index < body.resourceInfos.length; index++) {
const element = body.resourceInfos[index];
......@@ -22,7 +22,7 @@ async function createResource(req, res, next) {
}
console.log(ris, "===")
await DB.ResourcesInfo.bulkCreate(ris);
} else if(type == 2) {
} else if(body.type == 2) {
//树结构
let ris = [];
let data = utils.flattenTreeIterative(body.resourceInfos);
......
......@@ -23,7 +23,11 @@ const Project = sequelize.define('Project', {
},
projectLiuZhuanType: {
type: DataTypes.STRING,
comment: "项目流转状态 1. 待初审、3 待初审、1初审退回、5待终审、1终审退回、7待立项审批、立项审批退回、已立项、已暂停、已结束等",
comment: "项目流转状态 1. 待提交、3 待初审、1初审退回、5待终审、1终审退回、7待立项审批、立项审批退回、已立项、已暂停、已结束等",
},
projectLiuZhuanTypeDes: {
type: DataTypes.STRING,
comment: "项目流转状态说明 待提交 待初审 初审退回 待终审 终审退回 待立项审批 立项审批退回 已立项 已结束等",
},
/**
* 项目基本信息
......@@ -1765,9 +1769,9 @@ const Project = sequelize.define('Project', {
// 同步模型到数据库(创建表)
Project.sync({
// force: false,
force: false,
// force: true ,//会删除已存在表并重新创建
alter: true
// alter: true
})
.then(() => {
console.log('Project 表同步成功');
......
......@@ -29,10 +29,10 @@ const ProjectXmtzze = sequelize.define('ProjectXmtzze', {
type: DataTypes.DECIMAL(20, 4),
comment: "人民币计价"
},
// parentId: {
// type: DataTypes.INTEGER,
// comment: "上级ID"
// },
parentXh: {
type: DataTypes.STRING,
comment: "上级ID"
},
projectId: {
type: DataTypes.INTEGER,
......@@ -67,9 +67,9 @@ const ProjectXmtzze = sequelize.define('ProjectXmtzze', {
// 同步模型到数据库(创建表)
ProjectXmtzze.sync({
force: false,
// force: false,
// force: true ,//会删除已存在表并重新创建
// alter: true
alter: true
})
.then(() => {
console.log('ProjectXmtzze 表同步成功');
......
......@@ -35,13 +35,13 @@ const flowRecord = sequelize.define('flowRecord', {
comment: "0 正常 1 删除"
},
}, {
tableName: 'system_user', // 指定表名(如果与模型名不同)
tableName: 'system_flowrecord', // 指定表名(如果与模型名不同)
timestamps: true, // 是否自动添加 createdAt 和 updatedAt 字段(覆盖全局设置)
});
// 同步模型到数据库(创建表)
flowRecord.sync({
// force: false,
force: false,
// force: true ,//会删除已存在表并重新创建
// alter: true
})
......
......@@ -26,8 +26,8 @@ const RoleMenu = sequelize.define('RoleMenu', {
// 同步模型到数据库(创建表)
RoleMenu.sync({
// force: false, // force: true 会删除已存在表并重新创建
alter: true
force: false, // force: true 会删除已存在表并重新创建
// alter: true
})
.then(() => {
console.log('RoleMenu 表同步成功');
......
......@@ -134,7 +134,7 @@ async function getProjectApprover(userId, roleCode) {
// 获取当前用户公司的 具体角色 的用户列表
let user = await DB.User.findOne({ where: { id: userId } });
return [];
return [user];
}
module.exports = {
......
......@@ -18,7 +18,9 @@ router.post('/getProjectFields', projectController.getProjectFields);
router.post('/exportExcel', projectController.exportExcel);
router.post('/preJugProject', projectController.preJugProject); //初审
router.post('/finalJugProject', projectController.finalJugProject); //终审
router.post('/finalJugProject', projectController.finalJugProject); //终审 --暂时没有
router.post('/getOwnProjects', projectController.getOwnProjects);
......
......@@ -68,22 +68,87 @@ function disTree(tree) {
// 迭代替代递归(万级以上节点)
function flattenTreeIterative(forest) {
const result = [];
const stack = [...forest.map(node => ({ node, parentId: null }))];
while (stack.length) {
const { node, parentId } = stack.pop();
const { chilrden, children, ...rest } = node;
result.push({ ...rest, parentId });
const kids = chilrden || children || [];
for (let i = kids.length - 1; i >= 0; i--) {
stack.push({ node: kids[i], parentId: node.id });
const result = [];
const stack = [...forest.map(node => ({ node, parentId: null }))];
while (stack.length) {
const { node, parentId } = stack.pop();
const { chilrden, children, ...rest } = node;
result.push({ ...rest, parentId });
const kids = chilrden || children || [];
for (let i = kids.length - 1; i >= 0; i--) {
stack.push({ node: kids[i], parentId: node.id });
}
}
return result;
}
/**
* 把带有chilrden的推平
* @param {*} forest
* @param {*} idDes
* @param {*} parentIdDes
* @returns
*/
function flattenTree(forest, idDes = 'id', parentIdDes = 'parentId') {
const result = [];
/**
* 递归处理节点
* @param {Object} node - 当前节点
* @param {string|null} parentId - 父节点ID
*/
function traverse(node, parentId) {
// 创建节点副本避免修改原数据
const nodeCopy = { ...node };
// 添加parentId属性
nodeCopy[parentIdDes] = parentId;
// 移除children属性(使用正确拼写)
delete nodeCopy.children;
// 添加当前节点到结果集
result.push(nodeCopy);
// 处理子节点(兼容chilrden拼写错误)
const children = node.chilrden || node.children || [];
for (const child of children) {
traverse(child, node[idDes]);
}
}
}
return result;
// 遍历森林中的每棵树
for (const tree of forest) {
traverse(tree, null); // 根节点parentId为null
}
return result;
}
//
function buildTree2(nodes, keyid = 'id', keyParentId = 'parentId') {
// 创建一个映射,将节点ID映射到节点对象
const nodeMap = new Map(nodes.map(node => [String(node[keyid]), { ...node, children: [] }]));
// 遍历所有节点,并将它们添加到对应父节点的children数组中
nodes.forEach(node => {
const parentId = String(node[keyParentId]);
if (parentId !== null) {
// 确保父节点在映射中存在
if (nodeMap.has(parentId)) {
// 将当前节点添加到父节点的children数组中
nodeMap.get(parentId).children.push(nodeMap.get(String(node[keyid])));
nodeMap.get(parentId).children = _.orderBy(nodeMap.get(parentId).children, 'order')
}
}
});
// 从映射中提取顶级节点(parentId为null)
return _.orderBy(Array.from(nodeMap.values()).filter(node => node[keyParentId] == null), 'order');
}
......@@ -92,6 +157,7 @@ module.exports = {
checkUserPassword,
buildTree,
disTree,
flattenTreeIterative
flattenTreeIterative,
flattenTree,
buildTree2,
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment