明树Git Lab

Commit e71b822f authored by zfp1's avatar zfp1

update

parent ccd33ccf
Pipeline #107112 passed with stage
in 3 seconds
# 项目代码优化建议报告
## 项目概况
本项目是一个基于 Express.js + Sequelize + MySQL 的投资项目管理后端系统,主要功能包括用户管理、项目管理、投中管理、投后管理、日常管理等模块。
---
## 一、数据库设计优化建议
### 1.1 数据模型结构问题
#### 问题1: Project模型字段过多(300+行)
**位置**: `db/model/jt/project.js:7-319`
**问题描述**:
- Project表包含了立项和决策两个阶段的所有字段,字段数量过多
- 字段命名采用拼音缩写(如 `jnw`, `sjnzjjw`, `gmjjhy`),可读性差
- 字段分组标记(`_mark: 'lixiang'` / `_mark: 'juece'`)表明应该拆分
**建议**:
```javascript
// 拆分为多个表
- ProjectBase (项目基础信息)
- ProjectLixiang (立项信息)
- ProjectJuece (决策信息)
- ProjectXmtzze (项目投资总额)
- ProjectCwpjzb (财务评价指标)
- ProjectJsgm (建设规模)
// 使用外键关联
```
#### 问题2: 表命名不一致
**问题描述**:
- 系统表: `system_user`, `system_role`
- 业务表: `jt_project`, `jt_tz_tzkz`, `tz_tzmbzrs`
- 有些带前缀,有些不带,命名不统一
**建议**: 统一采用 `jt_` 前缀或采用统一的命名规范
#### 问题3: 缺少索引定义
**问题描述**: 所有模型都没有定义索引,查询性能差
**建议**:
```javascript
// 在模型中添加索引定义
User.init({
// ... fields
}, {
indexes: [
{ unique: true, fields: ['mobile'] }, // 手机号唯一索引
{ fields: ['del'] }, // 删除标记索引
{ fields: ['enable'] }, // 启用状态索引
]
});
Project.init({
// ... fields
}, {
indexes: [
{ fields: ['projectCreator'] },
{ fields: ['projectLzType'] },
{ fields: ['createdAt'] },
{ fields: ['del'] },
]
});
```
#### 问题4: 缺少唯一约束
**问题描述**:
- User表mobile字段没有唯一约束
- Project项目编号可能重复
**建议**:
```javascript
User.init({
mobile: {
type: DataTypes.STRING(11),
allowNull: false,
unique: true, // 添加唯一约束
},
// ...
});
```
#### 问题5: timestamp getter使用moment可能影响性能
**位置**: `db/model/jt/project.js:300-315`, `db/model/system/user.js:44-59`
**问题描述**:
- 每次查询都会调用moment格式化,影响性能
- getter会覆盖Sequelize的原生时间处理
**建议**:
```javascript
// 方案1: 移除getter,在前端格式化
createdAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
}
// 方案2: 只在需要时格式化
// 使用Sequelize的getter只用于特殊逻辑
```
#### 问题6: 模型sync在模型文件中
**位置**: `db/model/jt/project.js:321-329`
**问题描述**:
- 每个模型都调用sync,开发时表结构变更混乱
- 生产环境不应使用sync
**建议**:
- 使用Sequelize Migration管理表结构变更
-`db/migrations` 目录下创建迁移文件
- 使用 `sequelize-cli` 工具管理迁移
#### 问题7: 缺少外键约束
**位置**: `db/index.js:188-296`
**问题描述**:
- 只定义了关联关系,没有数据库级别的外键约束
- 数据一致性无法保证
**建议**:
```javascript
Project.belongsTo(User, {
foreignKey: 'projectCreator',
as: 'creator',
onDelete: 'CASCADE', // 删除用户时级联删除
onUpdate: 'CASCADE',
});
```
---
## 二、业务代码设计优化建议
### 2.1 控制器层问题
#### 问题1: 控制器代码过长
**位置**: `controller/projectController.js` (约59KB)
**问题描述**:
- 单个控制器文件过大,难以维护
- createProject和updateProject有大量重复代码
**建议**:
```javascript
// 拆分为多个控制器
controller/project/
├── projectBaseController.js // 基础CRUD
├── projectLixiangController.js // 立项相关
├── projectJueceController.js // 决策相关
└── projectFlowController.js // 流程相关
// 提取公共逻辑
service/projectService.js // 处理项目业务逻辑
```
#### 问题2: 缺少Service层
**位置**: `controller/projectController.js`, `controller/userController.js`
**问题描述**:
- Controller直接操作DB模型,业务逻辑混在Controller中
- 代码复用性差
**建议**:
```
三层架构:
Controller (路由层) → Service (业务逻辑层) → Model (数据访问层)
示例结构:
service/
├── userService.js
├── projectService.js
└── roleService.js
controller/
├── userController.js // 只负责请求/响应处理
└── projectController.js // 只负责请求/响应处理
```
#### 问题3: 缺少事务处理
**位置**: `controller/projectController.js:44-164` (createProject)
**问题描述**:
- 创建项目时涉及多个表的批量插入,没有事务保护
- 任何一步失败会导致数据不一致
**建议**:
```javascript
async function createProject(req, res, next) {
const transaction = await sequelize.transaction();
try {
let project = await DB.Project.create(projectInfo, { transaction });
await DB.ProjectJsgm.bulkCreate(projectJsgms, { transaction });
await DB.ProjectGdxx.bulkCreate(projectGdxxs, { transaction });
// ...
await transaction.commit();
return res.sendData(project);
} catch (error) {
await transaction.rollback();
next(error);
}
}
```
#### 问题4: 输入验证被禁用
**位置**: `middleware/parameter.js:18-33`
**问题描述**:
- Joi验证schema已定义但被注释掉
- 没有对请求参数进行验证
**建议**:
```javascript
// 启用参数验证,并完善schema
module.exports = (req, res, next) => {
const schema = schemas[req.path];
if (schema) {
const { error } = schema.validate({
...req.body,
...req.query
});
if (error) {
return res.status(400).send({
code: 40004,
message: error.details[0].message
});
}
}
next();
};
// 完善schema
const schemas = {
'/api/project/createProject': Joi.object({
projectName: Joi.string().required().min(1).max(200),
projectCode: Joi.string().max(50),
xmjd: Joi.string().max(100),
// ... 其他字段
}),
};
```
#### 问题5: 错误处理不完善
**位置**: `server.js:52-70`
**问题描述**:
- 错误处理过于简单,没有区分错误类型
- console.log应该使用logger
- 生产环境不应返回详细错误信息
**建议**:
```javascript
// 错误分类处理
app.use(function (err, req, res, next) {
// 1. 记录错误日志
logger.error({
url: req.path,
method: req.method,
error: err.stack,
user: req.user?.id
});
// 2. 根据错误类型返回不同响应
if (err.name === 'SequelizeValidationError') {
return res.status(400).send({
code: 40004,
message: '参数验证失败'
});
}
if (err.name === 'SequelizeForeignKeyConstraintError') {
return res.status(400).send({
code: 40005,
message: '数据关联错误'
});
}
if (err.name === 'SequelizeUniqueConstraintError') {
return res.status(400).send({
code: 40003,
message: '数据重复'
});
}
// 生产环境隐藏详细错误
const isProduction = process.env.NODE_ENV === 'production';
return res.status(err.status || 500).send({
code: err.status || 500,
message: isProduction ? '服务器内部错误' : err.message
});
});
```
#### 问题6: 重复代码严重
**位置**: `controller/projectController.js:44-165`, `controller/projectController.js:168-260`
**问题描述**:
- createProject和xiangmulixianggengxin有大量重复逻辑
- userModule中的setUserRole/setUserDepart/setUserPosition逻辑几乎一样
**建议**:
```javascript
// 提取公共方法
function processRelatedData(mainData, relatedDataMap) {
// 统一处理关联数据的增删改
}
// userModule中使用泛型方法
async function setRelated(userId, Model, foreignKey, relatedIds, existingRecords) {
// 统一处理用户关联关系
}
```
---
### 2.2 路由设计问题
#### 问题1: 路由定义过于分散
**位置**: `router/projectRouter.js`
**问题描述**:
- 路由定义重复(createXxx, updateXxx, deleteXxx, getXxxList, getXxxInfo)
- 没有使用路由分组
**建议**:
```javascript
// 使用资源路由
router.route('/tzmbzrs')
.post(projectTzController.createTzmbzrs)
.get(projectTzController.getTzmbzrsList);
router.route('/tzmbzrs/:id')
.get(projectTzController.getTzmbzrsInfo)
.put(projectTzController.updateTzmbzrs)
.delete(projectTzController.deleteTzmbzrsInfo);
// 或使用Express Router
const tzmbzrsRouter = express.Router();
tzmbzrsRouter.route('/')
.post(create)
.get(list);
tzmbzrsRouter.route('/:id')
.get(get)
.put(update)
.delete(remove);
router.use('/tzmbzrs', tzmbzrsRouter);
```
#### 问题2: 缺少API版本控制
**建议**:
```
/api/v1/user/login
/api/v2/user/login // 未来版本
```
---
## 三、代码结构优化建议
### 3.1 目录结构优化
#### 当前结构问题
```
jt_backend/
├── controller/ # 控制器(过于臃肿)
├── router/ # 路由定义
├── module/ # 模块(职责不清)
├── middleware/ # 中间件
├── utils/ # 工具函数
├── db/
│ ├── model/ # 模型定义
│ ├── index.js # 数据库连接
│ └── redis.js # Redis连接
├── cron/ # 定时任务
├── config/ # 配置文件
└── server.js # 入口文件
```
#### 建议的新结构
```
jt_backend/
├── src/
│ ├── api/ # API路由层
│ │ ├── v1/
│ │ │ ├── user/
│ │ │ ├── project/
│ │ │ └── index.js
│ │ └── index.js
│ ├── controllers/ # 控制器(轻量级)
│ │ ├── userController.js
│ │ └── projectController.js
│ ├── services/ # 业务逻辑层
│ │ ├── userService.js
│ │ ├── projectService.js
│ │ └── authService.js
│ ├── models/ # 数据模型
│ │ ├── index.js
│ │ ├── system/
│ │ └── jt/
│ ├── repositories/ # 数据访问层(可选)
│ ├── middlewares/ # 中间件
│ │ ├── auth.js
│ │ ├── validation.js
│ │ └── errorHandler.js
│ ├── validators/ # 参数验证
│ │ ├── userValidator.js
│ │ └── projectValidator.js
│ ├── utils/ # 工具函数
│ │ ├── logger.js
│ │ ├── response.js
│ │ └── date.js
│ ├── config/ # 配置管理
│ │ ├── index.js
│ │ ├── database.js
│ │ └── redis.js
│ ├── constants/ # 常量定义
│ │ ├── errorCodes.js
│ │ ├── status.js
│ │ └── pagination.js
│ ├── jobs/ # 定时任务
│ │ └── index.js
│ └── app.js # Express应用实例
├── db/
│ ├── migrations/ # 数据库迁移文件
│ └── seeders/ # 测试数据
├── logs/ # 日志文件
├── tests/ # 测试文件
├── .env.example # 环境变量模板
├── .env.local # 本地环境变量
├── .eslintrc.js # ESLint配置
├── .prettierrc # Prettier配置
├── package.json
└── server.js # 入口文件
```
### 3.2 配置管理优化
#### 问题1: 环境变量管理不当
**位置**: `config/index.js`, `config/dev.json`
**问题描述**:
- 敏感信息(密码)直接写在配置文件中
- 不同环境配置切换不灵活
**建议**:
```javascript
// config/index.js
require('dotenv').config();
module.exports = {
env: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT, 10) || 3000,
mysql: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT, 10) || 3306,
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'gzbjt',
},
redis: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT, 10) || 6379,
password: process.env.REDIS_PASSWORD || '',
db: parseInt(process.env.REDIS_DB, 10) || 0,
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '24h',
},
file: {
storagePath: process.env.FILE_STORAGE_PATH || '/uploadfiles',
maxSize: parseInt(process.env.FILE_MAX_SIZE, 10) || 10485760,
allowedTypes: (process.env.FILE_ALLOWED_TYPES || '').split(','),
}
};
// .env.example
NODE_ENV=development
PORT=3000
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_NAME=gzbjt
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
JWT_SECRET=your-secret-key
JWT_EXPIRES_IN=24h
FILE_STORAGE_PATH=/uploadfiles
```
#### 问题2: 配置文件硬编码
**位置**: `db/model/index.js:5-18`
**问题描述**:
- 数据库连接配置使用默认值
- 生产环境和开发环境混在一起
**建议**: 使用环境变量配置
### 3.3 中间件优化
#### 问题1: 认证中间件路径硬编码
**位置**: `middleware/request.js:60-73`
**问题描述**:
- 白名单路径在中间件中硬编码
- 不够灵活
**建议**:
```javascript
// config/whitelist.js
const publicPaths = [
'/user/login',
'/user/regist',
'/template/getExcelTemplate',
'/file/download',
'/file/show',
];
module.exports = publicPaths;
// middleware/auth.js
const publicPaths = require('../config/whitelist');
const { default: got } = require('got');
module.exports = async (req, res, next) => {
if (publicPaths.includes(req.path)) {
return next();
}
// ... 认证逻辑
};
```
#### 问题2: 响应中间件日志记录可能失败
**位置**: `middleware/response.js:6-17`
**问题描述**:
- 如果response已经发送,更新日志会失败
- 没有错误处理
**建议**:
```javascript
module.exports = (req, res, next) => {
const originalSend = res.send;
const originalJson = res.json;
res.send = function(data) {
// 在发送前记录日志
requestLogModule.updateRequestLog({
reqid: req.reqid,
result: JSON.stringify(data),
msg: "success",
status: res.statusCode,
}).catch(err => {
console.error('Failed to log response:', err);
});
return originalSend.call(this, data);
};
next();
};
```
---
## 四、安全性优化建议
### 4.1 认证与授权问题
#### 问题1: JWT密钥硬编码
**位置**: `controller/userController.js:27`
**问题描述**:
```javascript
let encryptLogStr = CryptoJS.AES.decrypt(req.body.encryptLogStr, "GFG5w5AP0Ja2rNaa")
```
- 加密密钥硬编码在前端和后端
- 密钥过于简单
**建议**:
```javascript
// 使用环境变量
const ENCRYPT_KEY = process.env.ENCRYPT_KEY;
// 或使用JWT代替AES加密
const jwt = require('jsonwebtoken');
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '24h' });
```
#### 问题2: Token过期时间过长
**位置**: `config/dev.json:2`
**问题描述**:
```json
"tokenEx": 86400000000 // 1000
```
- Token过期时间过长,安全风险高
**建议**:
```json
"tokenEx": 86400000 // 24小时
```
#### 问题3: CORS配置过于宽松
**位置**: `server.js:28-33`
**问题描述**:
```javascript
res.header("Access-Control-Allow-Origin", "*");
```
- 允许所有来源,存在安全风险
**建议**:
```javascript
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.header("Access-Control-Allow-Origin", origin);
}
```
#### 问题4: 密码存储安全
**位置**: `controller/userController.js:158`, `utils/index.js:13-19`
**问题描述**:
```javascript
const { salt, passwordHash } = utils.saltHashPassword(req.body.password);
body.password = body.password || sysConfig.userDefaultPassword; // 默认密码固定
```
- 默认密码相同,所有初始账号密码一样
- HMAC-SHA256安全性较低
**建议**:
```javascript
// 使用bcrypt
const bcrypt = require('bcrypt');
const saltRounds = 10;
const passwordHash = await bcrypt.hash(password, saltRounds);
const isValid = await bcrypt.compare(password, hash);
// 或使用argon2 (更安全)
const argon2 = require('argon2');
const hash = await argon2.hash(password);
const isValid = await argon2.verify(hash, password);
```
#### 问题5: SQL注入风险
**位置**: 虽然使用Sequelize参数化查询,但存在动态查询风险
**建议**:
```javascript
// 避免动态字段拼接
const orderField = req.body.orderField || 'createdAt';
const allowedFields = ['createdAt', 'updatedAt', 'projectName'];
if (!allowedFields.includes(orderField)) {
return res.status(400).send({ message: '无效的排序字段' });
}
const result = await DB.Project.findAll({
order: [[orderField, 'DESC']]
});
```
---
## 五、性能优化建议
### 5.1 数据库查询优化
#### 问题1: 缺少分页优化
**位置**: `controller/userController.js:290-331`
**问题描述**:
- 使用findAndCountAll但缺少优化
- 没有limit和offset验证
**建议**:
```javascript
const page = Math.max(1, parseInt(req.body.page, 10) || 1);
const limit = Math.min(100, Math.max(1, parseInt(req.body.pageSize, 10) || 10));
const offset = (page - 1) * limit;
// 添加查询结果缓存
const cacheKey = `userList:${page}:${limit}:${JSON.stringify(req.body.name)}`;
const cached = await ioRedis.get(cacheKey);
if (cached) {
return res.sendData(JSON.parse(cached));
}
const ret = await DB.User.findAndCountAll({
// ... 查询条件
limit,
offset,
});
await ioRedis.setex(cacheKey, 300, JSON.stringify(ret)); // 缓存5分钟
```
#### 问题2: N+1查询问题
**位置**: `controller/userController.js:80-92`
**问题描述**:
```javascript
for (let index = 0; index < roles.length; index++) {
const element = roles[index];
menus = menus.concat(element.menus);
// ...
}
```
**建议**:
```javascript
// 使用include一次查询
const user = await DB.User.findOne({
where: { id: req.user.id },
include: [
{
model: DB.Role,
as: 'roles',
attributes: ["id", "name"],
through: { attributes: [] },
include: [{
model: DB.Menu,
as: 'menus',
through: { attributes: [] },
attributes: ['id', 'name', 'parentId', 'order'],
}]
}
]
});
```
#### 问题3: 连接池配置不合理
**位置**: `db/model/index.js:9-14`
**问题描述**:
```javascript
pool: {
max: 5, // 连接池太小
min: 0,
acquire: 30000,
idle: 10000
}
```
**建议**:
```javascript
pool: {
max: parseInt(process.env.DB_POOL_MAX, 10) || 20, // 根据并发量调整
min: parseInt(process.env.DB_POOL_MIN, 10) || 5,
acquire: 60000,
idle: 10000,
evict: 60000, // 60秒后检查空闲连接
}
```
### 5.2 Redis使用优化
#### 问题1: Token存储无压缩
**位置**: `controller/userController.js:75-76`
**问题描述**:
```javascript
let userStr = JSON.stringify(user);
await ioRedis.set(`token:${user.token}`, userStr, 'EX', sysConfig.tokenEx);
```
**建议**:
```javascript
// 压缩存储
const zlib = require('zlib');
const compressed = zlib.gzipSync(JSON.stringify(user)).toString('base64');
await ioRedis.set(`token:${user.token}`, compressed, 'EX', sysConfig.tokenEx);
// 读取时解压
const raw = await ioRedis.get(`token:${token}`);
const user = JSON.parse(zlib.gunzipSync(Buffer.from(raw, 'base64')).toString());
```
### 5.3 日志优化
#### 问题1: 缺少日志框架
**问题描述**:
- 使用console.log记录日志
- 没有日志级别
- 没有日志轮转
**建议**:
```javascript
// utils/logger.js
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' }),
],
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
module.exports = logger;
```
---
## 六、代码质量优化建议
### 6.1 命名规范
#### 问题1: 拼音命名
**位置**: 多处
**问题描述**:
```javascript
xmjd // 项目阶段
jnw // 境内外
sjnzjjw // 省级/洲际
gmjjhy // 国民经济行业
lxlx // 立项类型
lxzl // 立项资料
lxpfwj // 立项批复文件
```
**建议**: 统一使用英文命名
```javascript
projectStage // 项目阶段
regionType // 区域类型
provinceRegion // 省级区域
industryCategory // 行业类别
projectType // 项目类型
initiationDocs // 立项文档
approvalDoc // 批复文件
```
#### 问题2: 变量命名不一致
**位置**: `controller/projectController.js:176-180`
**问题描述**:
```javascript
let pnids1 = [], pns1 = [], newprojectJsgms = [];
let pnids2 = [], pns2 = [], newprojectGdxxs = [];
let pnids3 = [], pns3 = [], newprojectXmtzzes = [];
```
**建议**:
```javascript
const existingIds = [], existingRecords = [], newRecords = [];
```
### 6.2 代码重复消除
#### 问题1: userModule重复逻辑
**位置**: `module/userModule.js:9-98`
**建议**:
```javascript
// 泛型方法
async function setRelatedEntities(userId, relatedIds, existingRecords, Model, foreignKey) {
const existingIds = existingRecords.map(r => r.id || r[foreignKey]);
const idsToAdd = relatedIds.filter(id => !existingIds.includes(id));
const idsToRemove = existingIds.filter(id => !relatedIds.includes(id));
if (idsToAdd.length) {
await Model.bulkCreate(idsToAdd.map(id => ({ userId, [foreignKey]: id })));
}
if (idsToRemove.length) {
await Model.destroy({ where: { userId, [foreignKey]: { [Op.in]: idsToRemove } } });
}
}
// 使用
await setRelatedEntities(userId, roleIds, roles, DB.UserRole, 'roleId');
await setRelatedEntities(userId, departIds, departs, DB.UserDepart, 'departId');
await setRelatedEntities(userId, positionIds, positions, DB.UserPosition, 'posiId');
```
### 6.3 注释规范
#### 问题1: 过多注释代码
**位置**: 多处
**建议**: 删除或移到Git历史,保持代码整洁
#### 问题2: 中英文混用注释
**建议**: 统一使用英文或中文注释
---
## 七、工程化优化建议
### 7.1 缺少测试
**建议**:
```javascript
// tests/unit/user.test.js
const request = require('supertest');
const app = require('../src/app');
describe('User API', () => {
test('should login successfully', async () => {
const res = await request(app)
.post('/api/user/login')
.send({ mobile: '13800138000', password: 'test123' });
expect(res.statusCode).toBe(200);
});
});
```
### 7.2 缺少API文档
**建议**: 使用Swagger生成API文档
```javascript
// swagger.js
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: '交投项目管理系统API',
version: '1.0.0',
},
},
apis: ['./src/api/**/*.js'],
};
const specs = swaggerJSDoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
```
### 7.3 缺少ESLint和Prettier配置完善
**建议**:
```javascript
// .eslintrc.js
module.exports = {
extends: ['eslint:recommended', 'airbnb-base'],
rules: {
'no-console': 'warn',
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'max-len': ['error', { code: 120 }],
},
};
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "es5"
}
```
---
## 八、依赖管理优化建议
### 8.1 依赖版本问题
**位置**: `package.json`
#### 问题1: 依赖版本过旧
```json
"moment": "2.30.1" // 建议使用dayjs或date-fns(体积更小)
"lodash": "4.17.21" // 建议使用lodash-es或按需导入
```
**建议**:
```bash
# 使用更现代的替代方案
npm uninstall moment
npm install dayjs
# lodash按需导入
import { cloneDeep, orderBy } from 'lodash';
```
#### 问题2: 缺少必要依赖
**建议**:
```json
{
"dependencies": {
"helmet": "^7.1.0", // 安全头
"express-rate-limit": "^7.1.0", // 限流
"compression": "^1.7.4", // 已有,建议开启
"dotenv": "^16.3.1", // 环境变量管理
"winston": "^3.11.0", // 日志
"joi": "^17.11.0" // 已有,应启用
}
}
```
---
## 九、定时任务优化建议
### 9.1 定时任务问题
**位置**: `cron/index.js`
#### 问题1: 定时任务被注释且未实现
**位置**: `cron/index.js:15`
**问题描述**:
```javascript
// TODO:需要处理 多个server启动时,定时任务启动多次问题。
```
**建议**:
```javascript
// 使用分布式锁
const RedisLock = require('ioredis-lock');
nodeCron.schedule('1 */1 * * * *', async () => {
const lock = await RedisLock.acquire(redis, 'excel-cron-lock', { timeout: 60000 });
if (!lock) {
return; // 其他实例已在执行
}
try {
await excelCron.exportExcel();
} finally {
await lock.release();
}
}, { timezone: "Asia/Shanghai" });
// 或使用bull队列
const Queue = require('bull');
const excelQueue = new Queue('excel-queue', { redis: sysConfig.redis });
excelQueue.add('export', {}, { repeat: { every: 60000 } });
```
---
## 十、文件上传优化建议
### 10.1 文件存储问题
#### 问题1: 缺少文件类型和大小验证
**位置**: `router/fileRouter.js` (未显示,推断)
**建议**:
```javascript
const multer = require('multer');
const path = require('path');
const allowedTypes = ['.jpg', '.jpeg', '.png', '.pdf', '.doc', '.docx', '.xlsx'];
const maxSize = 10 * 1024 * 1024; // 10MB
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, process.env.FILE_STORAGE_PATH);
},
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
const filename = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}${ext}`;
cb(null, filename);
},
}),
fileFilter: (req, file, cb) => {
const ext = path.extname(file.originalname).toLowerCase();
if (allowedTypes.includes(ext)) {
cb(null, true);
} else {
cb(new Error('不支持的文件类型'));
}
},
limits: {
fileSize: maxSize,
},
});
```
#### 问题2: 缺少文件存储抽象
**建议**: 支持多种存储方式(本地、OSS、S3)
```javascript
// storage/index.js
class StorageStrategy {
upload(file) { throw new Error('Not implemented'); }
delete(fileId) { throw new Error('Not implemented'); }
}
class LocalStorage extends StorageStrategy { /* ... */ }
class AliyunOSS extends StorageStrategy { /* ... */ }
module.exports = {
strategy: process.env.STORAGE_TYPE === 'aliyun' ? new AliyunOSS() : new LocalStorage(),
};
```
---
## 优先级建议
### 高优先级(安全相关)
1. 修改Token过期时间(当前约1000天)
2. 修改默认密码机制
3. 修复CORS配置
4. 启用参数验证
5. 修改加密密钥管理
### 中优先级(稳定性相关)
1. 添加数据库事务处理
2. 添加数据库索引
3. 优化连接池配置
4. 添加错误处理中间件
5. 拆分大控制器
### 低优先级(代码质量相关)
1. 重构拼音命名
2. 消除代码重复
3. 添加单元测试
4. 添加API文档
5. 完善日志系统
---
## 总结
当前项目存在以下主要问题:
1. **数据库设计**: 表结构不合理、缺少索引、命名不规范
2. **业务逻辑**: 缺少Service层、代码重复严重、缺少事务处理
3. **代码结构**: 目录结构混乱、缺少分层设计
4. **安全性**: 认证机制薄弱、配置不当
5. **性能**: 查询未优化、缺少缓存
6. **工程化**: 缺少测试、文档、代码规范
建议按照优先级逐步优化,优先解决安全和稳定性问题,再逐步提升代码质量和工程化水平。
# 项目优化建议
## 一、安全相关优化建议
### 1.1 敏感信息泄露风险
**问题描述:**
- 配置文件中明文存储数据库密码和Redis密码:`config/production.json:14,21`
- 登录加密密钥硬编码在代码中:`controller/userController.js:27`
- 默认密码过于简单且明文存储:`config/production.json:3`
**优化建议:**
```javascript
// 1. 使用环境变量存储敏感信息
// 生产环境通过 .env 文件或 CI/CD 环境变量注入
// 不要将生产环境配置文件提交到版本控制
// 2. 使用更安全的加密方式
// - 建议使用 JWT (jsonwebtoken) 替代自定义 token
// - 加密密钥从环境变量读取
const JWT_SECRET = process.env.JWT_SECRET || crypto.randomBytes(32).toString('hex');
// 3. 密码策略
// - 强制用户首次登录修改默认密码
// - 实现密码复杂度验证
// - 支持密码过期和重置功能
```
### 1.2 SQL注入风险
**问题描述:**
- 存在原生SQL查询:`controller/projectController.js:894-904`
- 虽然使用了参数化查询,但应统一使用 ORM
**优化建议:**
```javascript
// 统一使用 Sequelize ORM 的查询方式
// 避免直接执行原生 SQL
await DB.Project.findAll({ where: { ... } });
```
### 1.3 XSS 和 CSRF 防护
**问题描述:**
- 缺少 XSS 防护中间件
- 缺少 CSRF token 机制
**优化建议:**
```javascript
// 添加安全中间件
const helmet = require('helmet');
const xss = require('xss-clean');
app.use(helmet());
app.use(xss());
```
---
## 二、代码设计优化建议
### 2.1 大量注释代码未清理
**问题描述:**
- `controller/projectController.js` 中有大量被注释的代码(约400行)
- `router/projectRouter.js` 中也有注释的路由
**优化建议:**
1. 删除所有不再使用的注释代码
2. 如需保留参考,移至单独文档或使用版本控制查看历史
3. 保持代码库清洁,提高可读性
### 2.2 全局变量滥用
**问题描述:**
- `server.js:21-22` 使用 global 挂载全局对象
- `global.utils``global.ioRedis` 被多处引用
**优化建议:**
```javascript
// 使用依赖注入或模块导出
// utils/index.js
module.exports = { utils };
// db/redis.js
module.exports = { ioRedis };
// 在需要的文件中导入
const { utils } = require('./utils');
const { ioRedis } = require('./db/redis');
```
### 2.3 代码重复严重
**问题描述:**
- `controller/projectController.js:168-258``386-526``xiangmulixianggengxin``xiangmujuecegengxin` 函数有大量重复逻辑
- 多处类似的数组处理模式重复
**优化建议:**
```javascript
// 提取公共逻辑到独立的 service 层
// services/projectUpdateService.js
async function updateProjectRelations(projectId, relations) {
const updates = [];
for (const [relation, data] of Object.entries(relations)) {
updates.push(processRelation(relation, projectId, data));
}
await Promise.all(updates);
}
// controller 中只需要调用
await updateProjectRelations(projectId, {
projectJsgms: body.projectJsgms,
projectGdxxs: body.projectGdxxs,
// ...
});
```
### 2.4 缺少参数验证
**问题描述:**
- `middleware/parameter.js:17-35` 中的参数验证逻辑完全被注释掉
- 接口没有对输入参数进行校验
**优化建议:**
```javascript
// 1. 启用并完善参数验证
const schemas = {
'/api/project/createProject': Joi.object({
projectName: Joi.string().required().max(200),
projectCode: Joi.string().max(50),
// ... 其他字段
}),
};
// 2. 添加自定义验证错误处理
if (error) {
return res.sendError({
code: 40004,
message: error.details[0].message
});
}
```
### 2.5 错误处理不统一
**问题描述:**
- 部分地方使用 `next(error)`,部分直接 `return res.sendError()`
- `server.js:52-70` 的错误处理逻辑可能覆盖业务错误
**优化建议:**
```javascript
// 统一错误处理中间件
// middleware/errorHandler.js
class AppError extends Error {
constructor(code, message, statusCode = 200) {
super(message);
this.code = code;
this.statusCode = statusCode;
}
}
// 业务错误
throw new AppError(40001, '登录失败');
// 全局错误处理
app.use((err, req, res, next) => {
if (err instanceof AppError) {
return res.status(err.statusCode).send({
code: err.code,
message: err.message
});
}
// 其他错误
console.error(err);
return res.status(500).send({
code: 50000,
message: '服务器内部错误'
});
});
```
---
## 三、数据库设计优化建议
### 3.1 软删除字段命名不统一
**问题描述:**
- 部分表使用 `del` 字段,部分使用 `del: 0` 作为软删除标记
- `db/model/system/user.js:37``db/model/jt/project.js:34`
**优化建议:**
```javascript
// 统一使用 deletedAt 字段(Sequelize 最佳实践)
deletedAt: {
type: DataTypes.DATE,
defaultValue: null,
paranoid: true // 启用软删除
}
// 查询时自动过滤已删除记录
Model.findAll({ paranoid: true }); // 自动排除 deletedAt 不为 null 的记录
```
### 3.2 缺少数据库索引
**问题描述:**
- 模型定义中未看到索引定义
- 查询性能可能存在隐患
**优化建议:**
```javascript
// 在模型中添加索引
const Project = sequelize.define('Project', {
// ... 字段定义
}, {
tableName: 'jt_project',
indexes: [
{
fields: ['projectLzType'],
name: 'idx_project_lz_type'
},
{
fields: ['projectCreator'],
name: 'idx_project_creator'
},
{
fields: ['del', 'projectLzType'],
name: 'idx_project_del_type'
}
]
});
```
### 3.3 JSON字段使用不当
**问题描述:**
- `db/model/jt/project.js:90,148` 使用 JSON 类型存储数组数据
- `controller/projectController.js:34-68` 中对 JSON 字段的查询逻辑复杂
**优化建议:**
```javascript
// 方案1: 使用关联表
// 创建中间表存储多对多关系
const ProjectIndustry = sequelize.define('ProjectIndustry', {
projectId: { type: DataTypes.INTEGER },
industryId: { type: DataTypes.INTEGER }
});
// 方案2: 如果必须用JSON,确保添加适当的索引
gmjjhy: {
type: DataTypes.JSON,
comment: "国民经济行业",
// MySQL 5.7+ 支持 JSON 索引
}
```
### 3.4 时间戳处理不一致
**问题描述:**
- `db/model/system/user.js:56``updatedAt` 的 getter 错误地使用了 `createdAt` 的值
- 会导致返回错误的更新时间
**优化建议:**
```javascript
// 修正错误
updatedAt: {
type: DataTypes.DATE,
defaultValue: new Date(),
get() {
const rawValue = this.getDataValue('updatedAt'); // 修正为 updatedAt
return rawValue ? moment(rawValue).format('YYYY-MM-DD HH:mm:ss') : '';
}
}
```
### 3.5 外键约束缺失
**问题描述:**
- 虽然定义了 references,但没有真正启用外键约束
- 可能导致数据一致性问题
**优化建议:**
```javascript
// 在 Sequelize 配置中启用外键
const sequelize = new Sequelize(/* ... */, {
define: {
timestamps: true,
freezeTableName: true,
underscored: false
},
// 启用外键约束
define: {
// ... 其他配置
}
});
```
---
## 四、性能优化建议
### 4.1 N+1 查询问题
**问题描述:**
- `controller/projectController.js:226-236` 中循环查询数据库
- `controller/projectController.js:82-92``getResourceInfoByIds` 的遍历查询
**优化建议:**
```javascript
// 1. 使用 include 一次性加载关联数据
const project = await DB.Project.findOne({
where: { id: projectId },
include: [
{ model: DB.ProjectBjtj, as: 'projectBjtjs' },
{ model: DB.ProjectCwpjzb, as: 'projectCwpjzbs' },
// ... 其他关联
]
});
// 2. 使用批量查询替代循环
const resourceIds = resouInfoIds.filter(id => id);
const resources = await DB.ResourcesInfo.findAll({
where: { key: { [Op.in]: resourceIds } }
});
const risMap = {};
resources.forEach(ris => {
risMap[ris.key] = ris.value || ris.name;
});
```
### 4.2 数据库连接池配置过小
**问题描述:**
- `db/index.js:10` 连接池最大连接数仅为5
- 高并发场景下可能成为瓶颈
**优化建议:**
```javascript
const sequelize = new Sequelize(/* ... */, {
pool: {
max: 20, // 根据服务器配置调整
min: 5,
acquire: 30000,
idle: 10000
}
});
```
### 4.3 缺少查询结果缓存
**问题描述:**
- 资源库数据查询频繁但无缓存
- 菜单、角色等基础数据每次都查询数据库
**优化建议:**
```javascript
// 使用 Redis 缓存热点数据
async function getResourceInfoMapByKey(key) {
const cacheKey = `resource:${key}`;
const cached = await ioRedis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const result = await DB.Resources.findOne({ where: { key }, raw: true });
const risMap = await buildResourcesMap(result);
// 缓存1小时
await ioRedis.set(cacheKey, JSON.stringify(risMap), 'EX', 3600);
return risMap;
}
```
---
## 五、代码规范优化建议
### 5.1 console.log 滥用
**问题描述:**
- 代码中大量使用 `console.log`,如 `controller/projectController.js:936,1020,1028,1159,265`
- 缺少统一的日志框架
**优化建议:**
```javascript
// 使用 winston 或 pino 等专业日志库
const logger = require('./utils/logger');
// 替换 console.log
logger.info('项目状态', { projectLzType: ret.projectLzType });
logger.debug('搜索条件', { search });
logger.error('错误信息', { error: error.message, stack: error.stack });
// 日志分级
// - error: 错误
// - warn: 警告
// - info: 重要信息
// - debug: 调试信息
```
### 5.2 变量命名不规范
**问题描述:**
- `controller/projectController.js:229-236` 中大量拼音缩写变量:`pnids1`, `pns1`, `newprojectJsgms`
- 可读性差
**优化建议:**
```javascript
// 使用语义化的英文命名
let existingIds = [];
let existingItems = [];
let newItems = [];
projectJsgms.forEach(item => {
if (!item.id && !_.isEmpty(item)) {
item.projectId = projectInfo.id;
newItems.push(item);
} else {
existingIds.push(item.id);
existingItems.push(item);
}
});
```
### 5.3 魔法数字和字符串
**问题描述:**
- 大量硬编码的状态值:`projectLzType: 1,3,5,7,8,9,11,12,13`
- 缺少常量定义
**优化建议:**
```javascript
// constants/projectStatus.js
module.exports = {
PROJECT_STATUS: {
DRAFT: 1,
LIXIANG_APPROVING: 3,
LIXIANG_APPROVED: 5,
DECISION_DRAFTING: 7,
DECISION_APPROVING: 8,
DECISION_APPROVED: 9,
RE_DECISION_DRAFTING: 11,
RE_DECISION_APPROVING: 12,
RE_DECISION_APPROVED: 13
}
};
// 使用
const { PROJECT_STATUS } = require('../constants/projectStatus');
projectInfo.projectLzType = PROJECT_STATUS.LIXIANG_APPROVING;
```
### 5.4 函数过长
**问题描述:**
- `controller/projectController.js:44-165``createProject` 函数超过100行
- `controller/projectController.js:386-526``xiangmujuecegengxin` 函数超过140行
**优化建议:**
```javascript
// 将长函数拆分为多个小函数
async function createProject(req, res, next) {
const transaction = await DB.sequelize.transaction();
try {
const projectInfo = prepareProjectInfo(req.body, req.user.id);
const project = await createMainProject(projectInfo, transaction);
await createProjectRelations(project.id, req.body, transaction);
await createProjectFiles(project.id, req.body, transaction);
await createFlowRecord(project.id, req.user.id, transaction);
await transaction.commit();
return res.sendData(project);
} catch (error) {
await transaction.rollback();
next(error);
}
}
// 单一职责的小函数
async function createMainProject(projectInfo, transaction) {
return DB.Project.create(projectInfo, { transaction });
}
async function createProjectRelations(projectId, body, transaction) {
// ...
}
```
---
## 六、架构设计优化建议
### 6.1 缺少分层架构
**问题描述:**
- 业务逻辑直接写在 controller 中
- 没有清晰的 service 层
**优化建议:**
```
建议目录结构:
├── controller/ # 控制器层:处理请求参数、调用service、返回响应
├── service/ # 服务层:处理业务逻辑
├── repository/ # 仓储层:处理数据库操作(如果需要)
├── model/ # 数据模型
├── middleware/ # 中间件
├── router/ # 路由定义
├── utils/ # 工具函数
├── constants/ # 常量定义
├── validators/ # 参数验证
└── types/ # TypeScript 类型定义(如使用TS)
```
### 6.2 缺少事务管理
**问题描述:**
- `controller/projectController.js:66-133` 创建项目时没有使用事务
- 可能导致数据不一致
**优化建议:**
```javascript
async function createProject(req, res, next) {
const transaction = await DB.sequelize.transaction();
try {
// 所有数据库操作使用事务
const project = await DB.Project.create(projectInfo, { transaction });
await DB.ProjectJsgm.bulkCreate(projectJsgms, { transaction });
await DB.ProjectGdxx.bulkCreate(projectGdxxs, { transaction });
// ... 其他操作
await transaction.commit();
return res.sendData(project);
} catch (error) {
await transaction.rollback();
next(error);
}
}
```
### 6.3 缺少限流和防护
**问题描述:**
- 没有 API 限流
- 没有防止暴力破解的保护
**优化建议:**
```javascript
const rateLimit = require('express-rate-limit');
// 登录接口限流
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 5, // 最多5次尝试
message: { code: 40005, message: '登录尝试次数过多,请15分钟后再试' }
});
app.post('/api/user/login', loginLimiter, login);
// 全局限流
const apiLimiter = rateLimit({
windowMs: 60 * 1000,
max: 100, // 每分钟100次请求
message: { code: 429, message: '请求过于频繁' }
});
app.use('/api', apiLimiter);
```
---
## 七、数据库连接优化建议
### 7.1 数据库配置问题
**问题描述:**
- `db/index.js:8` 设置 `logging: false`,但 `config/production.json:16` 设置 `logging: true`
- 配置不一致
**优化建议:**
```javascript
// 根据环境动态设置
const sequelize = new Sequelize(/* ... */, {
logging: process.env.NODE_ENV === 'development' ? console.log : false
});
```
### 7.2 缺少健康检查
**问题描述:**
- 没有数据库和 Redis 的健康检查接口
- 难以监控服务状态
**优化建议:**
```javascript
// router/healthRouter.js
router.get('/health', async (req, res) => {
const health = {
status: 'ok',
timestamp: new Date(),
database: 'unknown',
redis: 'unknown'
};
try {
await DB.sequelize.authenticate();
health.database = 'ok';
} catch (error) {
health.database = 'error';
health.status = 'degraded';
}
try {
await ioRedis.ping();
health.redis = 'ok';
} catch (error) {
health.redis = 'error';
health.status = 'degraded';
}
return res.status(health.status === 'ok' ? 200 : 503).send(health);
});
```
---
## 八、开发体验优化建议
### 8.1 缺少代码规范检查
**问题描述:**
- `package.json:10-11` 虽然配置了 ESLint,但代码中仍有大量不规范写法
- 没有强制执行
**优化建议:**
```json
// package.json
{
"scripts": {
"pre-commit": "eslint . --fix",
"pre-push": "eslint . && npm test",
"format": "prettier --write \"**/*.{js,json,md}\""
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
}
}
```
### 8.2 缺少 API 文档
**问题描述:**
- 没有 Swagger 或其他 API 文档
- 接口维护困难
**优化建议:**
```javascript
// 使用 swagger-jsdoc 和 swagger-ui-express
/**
* @swagger
* /api/project/createProject:
* post:
* summary: 创建项目
* tags: [Project]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* projectName:
* type: string
* description: 项目名称
* responses:
* 200:
* description: 创建成功
*/
```
### 8.3 缺少单元测试
**问题描述:**
- 项目中没有测试文件
- 缺少测试目录
**优化建议:**
```
tests/
├── unit/
│ ├── controller/
│ ├── service/
│ └── utils/
├── integration/
└── e2e/
```
---
## 九、性能监控优化建议
### 9.1 缺少性能监控
**问题描述:**
- 没有接口响应时间统计
- 没有慢查询监控
**优化建议:**
```javascript
// 添加性能监控中间件
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
// 记录慢接口
if (duration > 1000) {
logger.warn('Slow request', {
url: req.url,
method: req.method,
duration: `${duration}ms`
});
}
});
next();
});
```
---
## 十、优先级建议
### 高优先级(安全相关)
1. 移除配置文件中的明文密码,使用环境变量
2. 修正 `updatedAt` getter 的错误
3. 启用参数验证
4. 添加限流保护
### 中优先级(代码质量)
1. 清理注释代码
2. 消除全局变量
3. 提取重复代码
4. 添加事务管理
### 低优先级(体验提升)
1. 添加 API 文档
2. 添加单元测试
3. 添加性能监控
4. 优化日志系统
---
## 总结
该项目是一个基于 Express + Sequelize 的投资项目管理系统,主要存在以下方面的问题:
1. **安全隐患**:敏感信息明文存储、缺少基本的安全防护
2. **代码质量**:大量重复代码、函数过长、命名不规范
3. **架构设计**:缺少分层、事务管理、缓存机制
4. **数据库设计**:缺少索引、字段使用不当
5. **开发体验**:缺少文档、测试、规范检查
建议优先处理安全问题,然后逐步重构代码结构和优化性能。
# 「好停车」小程序功能设计方案
## 一、核心功能架构
```
好停车小程序(极简版)
├── 核心功能:快速找到最近距离的最便宜停车场
└── 辅助功能:停车导航、订单管理
```
## 二、详细页面设计(极简版)
### 1. **首页(唯一核心页面)**
- **当前位置自动获取**
- 首次打开申请位置权限
- 自动显示"您在这里"
- **极简地图展示**
- 只显示周边3km内停车场
- 停车场标记用颜色区分:
- 🟢 最近+最便宜(主推)
- 🟡 次优选择
- 🔴 其他选择
- **智能排序算法**
- 综合排序 = 距离权重(70%) + 价格权重(30%)
- 自动推荐最佳选择,无需手动筛选
---
### 2. **停车场卡片(点击地图标记弹出)**
- **只显示核心信息**
```
名称:XX停车场
距离:500米(步行5分钟)
价格:4元/小时
剩余:23/120个车位
```
- **两个核心按钮**
- 🚀 导航去这里(调起高德/百度地图)
- ✅ 我要停这里(记录选择,可选)
---
### 3. **我(用户中心)**
- **极简菜单**
- 常用车牌(1-3个)
- 停车记录(最近10条)
- 关于
---
### ❌ 已删除的复杂功能(非核心)
- ❌ 预约车位功能(增加决策成本,停车场有空位即可)
- ❌ 用户评价系统(停车是刚需,评价参考价值低)
- ❌ 积分会员体系(过度设计)
- ❌ 收藏停车场(智能推荐即可)
- ❌ 实时停车计时/费用计算(由停车场系统完成)
- ❌ 在线支付(由停车场出入口收费完成)
- ❌ 充值/优惠券/月卡(简化流程)
---
## 三、关键交互流程(3步完成)
### 找停车场流程
1. 打开小程序 → 自动定位 + 加载地图(1秒)
2. 查看地图 → 绿色标记是最佳选择(已智能排序)
3. 点击导航 → 调起高德/百度地图出发
完成!
---
## 四、技术建议(最小化实现)
- **地图服务**:高德地图SDK(定位+显示+导航跳转)
- **数据接口**
- 获取周边停车场列表(按距离+价格排序)
- 停车场实时车位数据
- **本地存储**:缓存常用车牌号
- **无需**:支付系统、订单系统、实时推送
\ No newline at end of file
......@@ -16,3 +16,89 @@ const _ = require("lodash");
const { Op } = require('sequelize');
const projectModule = require('../module/projectModule');
// ---------------- 其他 - 成本管理 (QtCbgl)
// 创建成本管理
async function createCbgl(req, res, next) {
try {
// 如果需要记录创建者,可以启用下面一行
// req.body.creator = req.user.id;
let ret = await DB.QtCbgl.create(req.body);
return res.sendData(ret);
} catch (error) {
next(error);
}
}
// 更新成本管理
async function updateCbgl(req, res, next) {
try {
if (!req.body.id) return res.sendError(errorMessage.paramsError);
let ret = await DB.QtCbgl.findOne({ where: { id: req.body.id }, raw: true });
if (!(ret && ret.id)) return res.sendError(errorMessage.resourceNotFound);
await DB.QtCbgl.update(req.body, { where: { id: req.body.id } });
return res.sendData({});
} catch (error) {
next(error);
}
}
// 获取单个成本管理
async function getCbgl(req, res, next) {
try {
if (!req.body.id) return res.sendError(errorMessage.paramsError);
let info = await DB.QtCbgl.findOne({ where: { id: req.body.id, del: 0 }, raw: true });
if (!(info && info.id)) return res.sendError(errorMessage.resourceNotFound);
return res.sendData(info);
} catch (error) {
next(error);
}
}
// 获取成本管理列表
async function getCbglList(req, res, next) {
try {
let page = req.body.page || 1;
let limit = req.body.pagesize || req.body.pageSize || 10;
let offset = (page - 1) * limit;
let search = {};
search.order = [['createdAt', 'DESC']];
search.limit = limit;
search.offset = offset;
let where = { del: 0 };
if (req.body.projectName) {
where.projectName = { [Op.like]: `%${req.body.projectName}%` };
}
if (req.body.projectId) {
where.projectId = req.body.projectId;
}
search.where = where;
if (req.body.attributes && req.body.attributes.length) {
search.attributes = req.body.attributes;
}
let ret = await DB.QtCbgl.findAndCountAll(search);
return res.sendData(ret);
} catch (error) {
next(error);
}
}
// 删除成本管理(逻辑删除)
async function deleteCbgl(req, res, next) {
try {
await DB.QtCbgl.update({ del: 1 }, { where: { id: req.body.id } });
return res.sendData({});
} catch (error) {
next(error);
}
}
module.exports = {
createCbgl,
updateCbgl,
getCbgl,
getCbglList,
deleteCbgl
};
......@@ -90,6 +90,8 @@ const RcTwhgl = require('./model/jt/rcTwhgl');
const RcTzjh = require('./model/jt/rcTzjh');
const RcXxhjs = require('./model/jt/rcXxhjs');
const RcTzdagl = require('./model/jt/rcTzdagl');
const QtCbgl = require('./model/jt/qtCbgl');
/**
* 业务表
*/
......@@ -177,6 +179,7 @@ global.DB = {
RcTzjh,
RcXxhjs,
RcTzdagl,
QtCbgl,
}
......
......@@ -14,6 +14,15 @@ const QtCbgl = sequelize.define('QtCbgl', {
comment: "项目名称",
},
yjzb: {
type: DataTypes.JSON,//{ztlx: 主题类型,zbmc:指标名称,zbkhnr: 指标考核内容,jyz: 建议值,sjz: 实际值}
comment: "一级指标",
},
ejzb: {
type: DataTypes.JSON,
comment: "二级指标",
},
projectId: {
type: DataTypes.INTEGER,
......
......@@ -211,10 +211,10 @@ router.post('/deleteXxhjs', projectRcController.deleteXxhjs);
*/
//5.1 成本管理
// router.post('/createCbgl', projectQtController.createCbgl);
// router.post('/updateCbgl', projectQtController.updateCbgl);
// router.post('/getCbgl', projectQtController.getCbgl);
// router.post('/getCbglList', projectQtController.getCbglList);
// router.post('/deleteCbgl', projectQtController.deleteCbgl);
router.post('/createCbgl', projectQtController.createCbgl);
router.post('/updateCbgl', projectQtController.updateCbgl);
router.post('/getCbgl', projectQtController.getCbgl);
router.post('/getCbglList', projectQtController.getCbglList);
router.post('/deleteCbgl', projectQtController.deleteCbgl);
module.exports = router;
\ No newline at end of file
......@@ -26,5 +26,4 @@ router.post('/saveAllResourceInfo', resourceController.saveAllResourceInfo);
router.post('/listResourceAll', resourceController.listResourceAll);
module.exports = router;
\ 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