明树Git Lab

Commit 1d6b5e7c authored by zfp1's avatar zfp1

初始化架子

parents
Pipeline #102337 canceled with stages
module.exports = {
env: {
node: true,
es2021: true,
jest: true
},
extends: [
'eslint:recommended'
],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module'
},
rules: {
'indent': ['error', 2],
'linebreak-style': ['error', 'unix'],
'quotes': ['error', 'single'],
'semi': ['error', 'always'],
'no-console': 'warn',
'no-unused-vars': 'warn'
}
};
\ No newline at end of file
node_modules/
.git
package-lock.json
.vscode
test/
test.js
public/avatar
\ No newline at end of file
registry=https://registry.npmmirror.com
\ No newline at end of file
mindmap
root((投资管理系统))
管理层功能
监控驾驶舱
► 投资指标可视化(数量/金额/完成率)
► 运营指标可视化(路段分布/交通量/收入成本)
► 安全告警实时监控
► 五年趋势分析图
项目库查询
► 阶段分类查询(开发/建设/运营)
► 商业模式过滤
► 产业类型检索
► 退出项目状态查询
项目阶段监管
► 执行建设监控(投运时点/建设期)
► 运营监控(月报生成/计划下发)
► 资产经营监控(处置流程跟踪)
风险预警管理
► 风险因素识别分析
► 风险模拟推演引擎
► 阈值告警机制
► 风险应对措施库
同业对标管理
数据治理中心
专业层功能
个人工作台
► 待办任务处理
► 重要事项提醒
► 督办消息管理
► 消息统计分析
投资决策管理
► 项目信息申报
► 可行性研究管理
► 竞标预审核
► 项目公司设立(工商/产权)
项目信息库
► 全周期档案管理
► 立项/合同信息库
► 投资评价数据池
► 合规性验证
计划管理
► 多维计划填报(投资/融资/进度)
► 计划变更控制
► 执行偏差分析
► 自动上报批复
建设实施管理
► 概预算控制
► 合同生命周期管理
► 进度甘特图监控
► 质量安全双体系
► 竣工结算管理
运营回收管理
► 机电设备台账
► 养护计划执行
► 收费成本联动
► 财务数据分析
还本付息管理
► 还款计划关联
► 利息计算引擎
► 实际还款记录
投资退出管理
► 资产处置流程
► 股权转让管理
► 历史项目追溯
报表统计中心
► 投资明细多维查询
► 自动报告生成(月/季/年)
► 数据上报配置器
► 经营分析仪表盘
风险预警模块
► 指标阈值配置
► 异常定位看板
综合管理
► 标准制度发布
► 督导任务分发
► 偏差/超期/风险三督导
系统集成
纵向集成
► 管理层-专业层数据通道
► 计划下达/执行上报
横向集成
► 财务系统对接(资金流)
► 统一门户单点登录
► 身份认证同步
外部对接
► 第三方支付网关
► 电子签章系统
\ No newline at end of file
{
"tokenEx": 86400000000,
"dbURI": "mongodb://127.0.0.1:27017/letian?authSource=admin",
"file": {
"storagePath": "/uploadfiles"
},
"cron": {
},
"mysql": {
"host": "localhost",
"port": 3306,
"username": "root",
"password": "123456",
"database": "test",
"logging": true
},
"dmdb": {
"host": "localhost",
"port": 5236,
"username": "SYSDBA",
"password": "SYSDBA",
"database": "letian",
"logging": true,
"connectString": "dm://SYSDBA:SYSDBA@localhost:5236"
},
"redis": {
"host": "localhost",
"port": 6379,
"password": "",
"db": 0
}
}
\ No newline at end of file
const config = require(`./${process.env.NODE_ENV}.json`);
global.sysConfig = config;
\ No newline at end of file
{
"tokenEx": 86400,
"dbURI": "mongodb://root:letian2024.@119.29.111.186:27017/letian?authSource=admin",
"file": {
"storagePath": "/mnt/vdb1/uploadfiles"
},
"cron": {
}
}
\ No newline at end of file
{
"tokenEx": 86400,
"dbURI": "mongodb://root:letian2024.@127.0.0.1:27017/letian?authSource=admin",
"file": {
"storagePath": "/mnt/vdb1/uploadfiles"
},
"cron": {
}
}
\ No newline at end of file
const crypto = require('crypto');
const _ = require('lodash');
const CryptoJS = require('crypto-js');
const userModule = require('../module/userModule');
const errorMessage = require('../utils/errorMessage');
async function regist(req, res, next) {
try {
const body = req.body;
const ret = await userModule.create(body);
return res.sendData(ret);
} catch (error) {
next(error);
}
}
module.exports = {
regist
}
\ No newline at end of file
const nodeCron = require('node-cron');
const config = require('../config');
/**
根据config cron 配置 定时处理
# ┌────────────── second (optional)
# │ ┌──────────── minute
# │ │ ┌────────── hour
# │ │ │ ┌──────── day of month
# │ │ │ │ ┌────── month
# │ │ │ │ │ ┌──── day of week
# │ │ │ │ │ │
# │ │ │ │ │ │
# * * * * * *
TODO:需要处理 多个server启动时,定时任务启动多次问题。
*/
const cron = () => {
/*-----------------------------采集设备信息-------------------------------------*/
// 1 土壤温湿度、土壤氮磷钾, 2 土壤ph、土壤电导率, 3 气象站
// if (sysConfig && sysConfig.cron && sysConfig.cron.getDeviceList) {
// console.log("getDeviceList:", new Date());
// // 每天10点30分 处理 1 30 10 * * *
// // new nodeCron.schedule('1 30 10 * * *', async () => {
// new nodeCron.schedule('1 * * * * *', async () => {
// await deviceCron.getDeviceList();
// }, { timezone: "Asia/Shanghai" });
// }
}
module.exports = cron;
\ No newline at end of file
// 模型初始化
const User = require("./model/user");
const RequestLog = require("./model/requestLog");
global.DB = {
User,
RequestLog
}
/**
* 关联关系定义
*/
User.hasMany(RequestLog, { foreignKey: 'userId' });
RequestLog.belongsTo(User, { foreignKey: 'userId' });
// db.js
const { Sequelize } = require('sequelize');
// 创建 Sequelize 实例,连接数据库
const sequelize = new Sequelize('gzbjt', 'root', '123456', {
host: 'localhost',
dialect: 'mysql',
logging: console.log, // 显示日志(可选)
pool: {
max: 5, // 连接池最大连接数
min: 0,
acquire: 30000,
idle: 10000
},
define: {
timestamps: true, // 默认添加 createdAt 和 updatedAt 字段
freezeTableName: true // 禁止 Sequelize 修改表名(默认会加复数)
}
});
// 测试连接
sequelize.authenticate()
.then(() => {
console.log(' 数据库连接成功');
})
.catch(err => {
console.error(' 连接失败:', err);
});
module.exports = sequelize;
\ No newline at end of file
// models/User.js
const { DataTypes } = require('sequelize');
const sequelize = require('./index');
const RequestLog = sequelize.define('User', {
// 定义字段
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
reqId: {
type: DataTypes.STRING,
comment: "给每一个请求分配一个id"
},
userId: {
type: DataTypes.INTEGER,
comment:"关联查询用"
},
user: {
type: DataTypes.STRING,
comment: "记录用户姓名等可见信息"
},
url: {
type: DataTypes.STRING,
comment: "记录请求接口地址"
},
params: {
type: DataTypes.TEXT,
comment: "请求参数"
},
headers: {
type: DataTypes.TEXT,
comment: "请求头"
},
method: {
type: DataTypes.STRING,
comment: "请求方法"
},
result: {
type: DataTypes.TEXT,
comment: "返回结果"
},
status: {
type: DataTypes.STRING,
comment: "http请求状态"
},
msg: {
type: DataTypes.TEXT,
comment: "http请求错误信息"
},
type: {
type: DataTypes.STRING,
comment: "日志类型 系统内部请求日志:system",
default: "system"
},
}, {
tableName: 'system_request_log', // 指定表名(如果与模型名不同)
timestamps: true, // 是否自动添加 createdAt 和 updatedAt 字段(覆盖全局设置)
});
// 同步模型到数据库(创建表)
RequestLog.sync({ force: true }) // force: true 会删除已存在表并重新创建
.then(() => {
console.log('RequestLog 表同步成功');
});
module.exports = RequestLog;
\ No newline at end of file
// models/User.js
const { DataTypes } = require('sequelize');
const sequelize = require('./index');
const User = sequelize.define('User', {
// 定义字段
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING(50),
allowNull: true
},
password: {
type: DataTypes.STRING(200),
allowNull: true
},
salt: {
type: DataTypes.STRING(100),
allowNull: true
},
name: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
tableName: 'system_user', // 指定表名(如果与模型名不同)
timestamps: true, // 是否自动添加 createdAt 和 updatedAt 字段(覆盖全局设置)
});
// 同步模型到数据库(创建表)
User.sync({ force: false }) // force: true 会删除已存在表并重新创建
.then(() => {
console.log('User 表同步成功');
});
module.exports = User;
\ No newline at end of file
const Redis = require('ioredis');
const redis = new Redis({
port: 6379,
host: "127.0.0.1",
// password: "Mingshu20170706",
db: 0,
});
module.exports = redis;
\ No newline at end of file
let curDate = new Date();
let curDateStr = curDate.getFullYear() + "" + String(curDate.getMonth() + 1).padStart(2, '0') + "" + curDate.getDate();
module.exports = {
apps: [{
name: "letian",
script: 'server.js',
watch: true,
ignore_watch: ['node_modules', 'logs'],
env_dev: {
NODE_ENV: "dev",
},
env_production: {
NODE_ENV: "production",
PORT:3000
},
log_date_format: "YYYY-MM-DD HH:mm:ss",
out_file: `/srv/letian/logs/${curDateStr}.log`,
error_file: `/srv/letian/logs/${curDateStr}.log`,
}]
};
const createError = require('http-errors');
const requestLogModule = require("../module/requestLogModule");
const mongoose = require('mongoose');
//
module.exports = async (req, res, next) => {
req.reqId = new mongoose.Types.ObjectId();
/* --------------3. 记录所有进来的日志,是否记录响应的值有待 */
requestLogModule.createRequestLog({
url: req.baseUrl + req.path,
params: JSON.stringify({
...req.body,
...req.query,
...req.params,
}),
reqId: req.reqId,
user: req.user,
method: req.method,
headers: JSON.stringify(req.headers),
});
next();
}
const Joi = require('joi');
/**
* 参数校验 https://joi.dev/api/?v=17.13.0
* @param {*} req
* @param {*} res
* @param {*} next
* @returns
*/
module.exports = (req, res, next) => {
console.log(req.headers, req.url, req.method, req.path, req.baseUrl, req.originalUrl);
// const schema = schemas[req.originalUrl];
// if (schema) {
// const { error } = schema.validate({
// ...req.body,
// ...req.query
// });
// console.log(error); //eslint-disable-line
// if (error) {
// return res.status(400).send({
// code: 402,
// message: error.details[0].message,
// success: false
// });
// }
// }
next();
}
const schemas = {
//用户
'/api/user/register': Joi.object({
phone: Joi.string().regex(/^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$/).required(),
password: Joi.string().regex(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,}$/).required(),
full_name: Joi.string(),
department: Joi.string(),
position: Joi.string()
}),
}
const createError = require('http-errors');
const crypto = require('crypto');
const requestLogModule = require("../module/requestModule");
//
module.exports = async (req, res, next) => {
req.reqId = crypto.randomUUID();
/* 1. 处理token */
if (!checkPath(req.path)) {
if (!(req.headers.authorization || req.headers.Authorization)) {
next(createError(401));
}
req.headers.authorization = req.headers.authorization || req.headers.Authorization;
const userStr = await ioRedis.get(`token:${req.headers.authorization}`);
// console.log(userStr)
if (userStr) {
try {
req.user = JSON.parse(userStr);
// 刷新token时间
await ioRedis.expire(`token:${req.headers.authorization}`, sysConfig.tokenEx)
} catch (error) {
//解析有误
next(createError(401));
}
} else {
//过期
next(createError(401));
}
}
/* --------------3. 记录所有进来的日志,是否记录响应的值有待 */
requestLogModule.createRequestLog({
url: req.baseUrl + req.path,
params: JSON.stringify({
...req.body,
...req.query,
...req.params,
}),
reqId: req.reqId,
userId: req.user.id,
user: JSON.stringify(req.user),
method: req.method,
headers: JSON.stringify(req.headers),
});
next();
}
function checkPath(path) {
if (['/user/login', '/user/regist',].includes(path)) {
return true;
} else {
return false;
}
}
\ No newline at end of file
const requestLogModule = require("../module/requestModule");
module.exports = (req, res, next) => {
res['sendData'] = (data) => {
requestLogModule.updateRequestLog({
reqId: req.reqId,
result: JSON.stringify(data),
msg: "success",
status: 200,
});
return res.status(200).send({
code: 0,
data: data,
message: 'success',
});
};
res['sendError'] = (error) => {
requestLogModule.updateRequestLog({
reqId: req.reqId,
result: JSON.stringify(error.message),
msg: "error",
status: 200,
});
// 除404 500以外 其他均归类为业务错误 以http 200返回,具体错误在返回内容里面根据code来区分。
return res.status(200).send({
code: error.code,
message: error.message,
});
};
next();
}
\ No newline at end of file
async function createRequestLog(params) {
return DB.RequestLog.create(params);
}
async function updateRequestLog(params) {
return DB.RequestLog.update(params, {where: {reqId: params.reqId}});
}
module.exports = {
createRequestLog,
updateRequestLog
}
\ No newline at end of file
async function create(params) {
return DB.User.create(params);
}
module.exports = {
create
}
\ No newline at end of file
{
"name": "letian",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"production": "cross-env NODE_ENV=production node server.js",
"dev": "cross-env NODE_ENV=dev nodemon server.js",
"local": "cross-env NODE_ENV=local nodemon server.js",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "1.7.4",
"compression": "1.7.4",
"cors": "2.8.5",
"crypto": "1.0.1",
"crypto-js": "^4.2.0",
"dmdb": "^1.0.0",
"eslint": "9.4.0",
"exceljs": "4.4.0",
"express": "4.19.2",
"form-data": "4.0.0",
"ioredis": "5.4.1",
"joi": "17.13.1",
"lodash": "4.17.21",
"moment": "2.30.1",
"morgan": "1.10.0",
"multer": "1.4.5-lts.1",
"mysql2": "^3.15.3",
"node-cron": "3.0.3",
"sequelize": "^6.37.7"
},
"devDependencies": {
"cross-env": "^7.0.3",
"nodemon": "^3.0.2"
}
}
const express = require('express');
const router = express.Router();
const userRouter = require("./userRouter");
router.use('/user', userRouter); // 用户 角色
module.exports = router;
\ No newline at end of file
const express = require('express');
const router = express.Router();
const userController = require('../controller/userController');
router.post('/regist', userController.regist);
// router.post('/login', userController.login);
// router.post('/checkUserToken', userController.checkUserToken);
// router.post('/changePassword', userController.changePassword);
// router.post('/manage/list', userController.listUser);
// router.post('/manage/create', userController.addUser);
// router.post('/manage/update', userController.updateUser); //更新
// router.post('/manage/info', userController.getUser);
// router.post('/manage/delete', userController.deleteUser); // 删除
// router.post('/manage/changePwd', userController.changePwd); // 删除
// /**
// * 角色
// */
// router.post('/role/create', userController.createRole);
// router.post('/role/update', userController.updateRole);
// router.post('/role/delete', userController.deleteRole);
// router.post('/role/list', userController.listRole);
// router.post('/role/info', userController.getRole);
module.exports = router;
\ No newline at end of file
const express = require('express');
const createError = require('http-errors');
const compress = require('compression');
const router = require('./router');
const path = require('path');
const logger = require('morgan');
const cors = require('cors');
//中间件
const parameter = require('./middleware/parameter');
const request = require('./middleware/request');
const response = require('./middleware/response');
// const requestLogModule = require("./module/requestLogModule");
//系统数据库
require('./config');
require('./cron')();
require('./db');
global.utils = require('./utils');
global.ioRedis = require('./db/redis');
const app = express();
app.use(cors());
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With, Range");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
next();
});
app.use(compress());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static('public'));
app.use(logger('dev'));
app.use('/api', request, response, router);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
console.log(err.code, err.status)
// 记录下错误日志
// requestLogModule.createRequestLog({
// url: req.path,
// params: JSON.stringify({
// ...req.body,
// ...req.query,
// ...req.params,
// }),
// user: req.user,
// status: err.status || err.code || 500,
// msg: err.message || 'server error'
// });
if(err.code && err.message) {
return res.sendError(err);
}
return res.status(err.status || 500).send({code: err.status ||500, msg: err.message || 'server error'});
});
const server = require('http').createServer(app);
server.listen(process.env.PORT || 3000, function () {
console.log(`****** server is listening : ${process.env.PORT} || 3000`);
});
module.exports = {
loginError: {
code: 40001,
message: "登录失败"
},
resourceNotFound: {
code: 40002,
message: "资源不存在"
},
nameDuplicated: {
code: 40003,
message: "名称重复"
},
agrMatDuplicated: {
code: 40004,
message: "入库物品种类重复,请检查!"
},
agrMatHaveFlow: {
code: 40005,
message: "入库物品种类重复,请检查!"
},
databaseQueryError: {
code: 50001,
message: "数据库操作错误"
}
}
\ No newline at end of file
const axios = require('axios');
const requestLogModule = require("../module/requestLogModule");
async function httpRequest ({url, method, data, params, headers = {}, responseType}) {
try {
let opt = {
url,
method,
params: params || {},
data: data || {},
headers,
}
if(responseType) {
opt.responseType = responseType;
}
const ret = await axios(opt);
requestLogModule.createRequestLog({
url,
method,
params: JSON.stringify(params) + JSON.stringify((headers["Content-Type"] == "mutipart/form-data" ? "" : data)),
headers: JSON.stringify(headers),
type: "device",
status: ret && ret.status,
result: responseType == 'stream' ? "" : JSON.stringify(ret.data)
}).catch(error => {
console.log(error);
});
if(ret && ret.status == 200) {
return {data: ret.data, status: true};
} else {
return {data: null, status: false};
}
} catch (error) {
console.log(error);
requestLogModule.createRequestLog({
url,
method,
params: JSON.stringify(params) + JSON.stringify((headers["Content-Type"] == "mutipart/form-data" ? "" : data)),
headers: JSON.stringify(headers),
type: "device",
msg: error.message,
}).catch(error => {
console.log(error);
});
return {data: null, status: false};
}
}
module.exports = httpRequest;
\ No newline at end of file
const crypto = require('crypto');
const _ = require('lodash');
function genRandomString(length) {
return crypto.randomBytes(Math.ceil(length / 2))
.toString('hex')
.slice(0, length);
}
function saltHashPassword(password) {
const salt = genRandomString(16);
const passwordHash = crypto.createHmac('sha256', salt).update(password).digest('hex');
return {
salt,
passwordHash
}
}
function checkUserPassword({ reqPw, salt, userPw }) {
const passwordHash = crypto.createHmac('sha256', salt).update(reqPw).digest('hex');
if (userPw == passwordHash) {
return true;
} else {
return false;
}
}
function buildTree(nodes) {
// 创建一个映射,将节点ID映射到节点对象
const nodeMap = new Map(nodes.map(node => [String(node._id), { ...node, children: [] }]));
// 遍历所有节点,并将它们添加到对应父节点的children数组中
nodes.forEach(node => {
const parentId = String(node.parentId);
if (parentId !== null) {
// 确保父节点在映射中存在
if (nodeMap.has(parentId)) {
// 将当前节点添加到父节点的children数组中
nodeMap.get(parentId).children.push(nodeMap.get(String(node._id)));
nodeMap.get(parentId).children = _.orderBy(nodeMap.get(parentId).children, 'order')
}
}
});
// 从映射中提取顶级节点(parentId为null)
return _.orderBy(Array.from(nodeMap.values()).filter(node => node.parentId === null), 'order');
}
function disTree(tree) {
let parallel = [];
function addToParallel(nodes) {
nodes.forEach(node => {
parallel.push({
...node,
children: undefined
});
if (node.children && node.children.length) {
addToParallel(node.children);
}
});
}
addToParallel(tree)
return parallel
}
function genTracSourceCode({EnterpriseCode, type, date, batchNum, logisticsNum}) {
// 企业代码(9) MA4W271Y8 + 产品代码【种植、初加工、深加工】(6)+ 生产/出厂日期(8)) + 批次号(8)+ 物流码 + 校验码(8)
//MA4W271Y8
date = date && moment().format('YYYYMMDD');
return `${EnterpriseCode}${type}${date}${batchNum}${logisticsNum}`;
}
module.exports = {
saltHashPassword,
checkUserPassword,
buildTree,
disTree,
}
\ 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