明树Git Lab
Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jt_backend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zengfanpei
jt_backend
Commits
e71b822f
Commit
e71b822f
authored
Feb 08, 2026
by
zfp1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update
parent
ccd33ccf
Pipeline
#107112
passed with stage
in 3 seconds
Changes
8
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
2078 additions
and
6 deletions
+2078
-6
a.md
a.md
+1151
-0
b.md
b.md
+742
-0
cc.md
cc.md
+82
-0
projectQtController.js
controller/projectQtController.js
+86
-0
index.js
db/index.js
+3
-0
qtCbgl.js
db/model/jt/qtCbgl.js
+9
-0
projectRouter.js
router/projectRouter.js
+5
-5
resourceRouter.js
router/resourceRouter.js
+0
-1
No files found.
a.md
0 → 100644
View file @
e71b822f
# 项目代码优化建议报告
## 项目概况
本项目是一个基于 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
=
24
h
FILE_STORAGE_PATH
=
/uploadfile
s
```
#### 问题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.
**工程化**
: 缺少测试、文档、代码规范
建议按照优先级逐步优化,优先解决安全和稳定性问题,再逐步提升代码质量和工程化水平。
b.md
0 → 100644
View file @
e71b822f
# 项目优化建议
## 一、安全相关优化建议
### 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.
**开发体验**
:缺少文档、测试、规范检查
建议优先处理安全问题,然后逐步重构代码结构和优化性能。
cc.md
0 → 100644
View file @
e71b822f
# 「好停车」小程序功能设计方案
## 一、核心功能架构
```
好停车小程序(极简版)
├── 核心功能:快速找到最近距离的最便宜停车场
└── 辅助功能:停车导航、订单管理
```
## 二、详细页面设计(极简版)
### 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
controller/projectQtController.js
View file @
e71b822f
...
...
@@ -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
};
db/index.js
View file @
e71b822f
...
...
@@ -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
,
}
...
...
db/model/jt/qtCbgl.js
View file @
e71b822f
...
...
@@ -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
,
...
...
router/projectRouter.js
View file @
e71b822f
...
...
@@ -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
router/resourceRouter.js
View file @
e71b822f
...
...
@@ -26,5 +26,4 @@ router.post('/saveAllResourceInfo', resourceController.saveAllResourceInfo);
router
.
post
(
'/listResourceAll'
,
resourceController
.
listResourceAll
);
module
.
exports
=
router
;
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment