# 一平node.js脚手架
**Repository Path**: zero_0001/yiping-nodejs-cli
## Basic Information
- **Project Name**: 一平node.js脚手架
- **Description**: 一平node.js脚手架是一平软件推出的基于阿里midway框架的后端服务框架。提供了基础的后台功能,包括员工管理、部门管理、角色管理、菜单管理、商品、订单管理等。
- **Primary Language**: TypeScript
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 7
- **Forks**: 1
- **Created**: 2025-01-01
- **Last Updated**: 2026-05-12
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 一平nodejs脚手架
#### 介绍
一平nodejs脚手架是一平软件架构团队,基于渐进式理念研发的 Node.js 企业级后端框架。通过自研的业务代码,搭配Midway.js的依赖注入容器及各种上层模块,组合出适用于不同场景的解决方案。提供了完整的企业级后台功能,包括用户权限管理、商品订单管理、库存采购管理、内容管理等核心业务模块。致力于为用户提供简单、易用、可靠的 Node.js 服务端研发体验。
本系统适配的前端web系统为: 一平react脚手架 https://gitee.com/zero_0001/yiping-react-cli
#### 预览
地址: http://test.zero9.work/public/index.html
用户名: admin
密码: aaaa1111
#### 作者
#### 技术栈
- **核心框架**: Midway.js 3.x (基于Koa的企业级Node.js框架)
- **编程语言**: TypeScript
- **数据库**: MySQL + TypeORM
- **缓存**: Redis
- **认证**: JWT (JSON Web Token)
- **文件上传**: Midway Upload组件
- **API文档**: Swagger
- **定时任务**: Midway Cron
- **WebSocket**: Midway WS
- **消息队列**: RabbitMQ + Kafka
- **支付集成**: 微信支付、支付宝支付
- **测试框架**: Jest
#### 核心特性
- **多角色权限系统**: 支持买家(buyer)和员工(staff)两种角色,每种角色支持多种设备类型
- **分层架构**: Controller-Service-Entity三层架构,代码结构清晰
- **统一响应格式**: 通过FormatMiddleware中间件统一API响应格式
- **基础服务封装**: BaseService提供通用的分页、排序、唯一性验证等功能
- **树形结构支持**: 内置树形数据结构的代码生成和查询功能
- **拖拽排序**: 支持前端拖拽排序的后端实现
- **多设备适配**: 支持uni-app移动端、Web PC端、大屏端等多种设备
- **完整的电商功能**: 商品管理、订单管理、支付、物流、售后等
- **企业级功能**: 部门管理、角色权限、操作日志、内容管理等
#### 软件架构
软件目录结构基于midway.js确定的基础结构。
业务代码处于src目录中。
**config** -- 基础配置
**controller** -- 接收并处理请求
**controller/buyer/uni** -- 接收并处理uni-app端会员请求
**controller/staff/web** -- 接收并处理web后台员工端请求
**entity** -- 实体model类(TypeORM实体)
**job** -- 定时任务
**module** -- 非midway框架的约定目录,为本项目自约定目录,放公共模块
**service** -- 服务层,会被controller层调用
**socket** -- webSocket请求层
**middleware** -- 中间件层
**filter** -- 过滤器层
**strategy** -- 策略层
**consumer** -- 消息消费者
#### 代码对应的用户角色
buyer -- 买家用户、消费者用户、软件直接面对的终端用户
staff -- 管理员用户、卖方用户,是一般系统中不可替代的基本角色
#### controller层结构
用户角色->使用设备->业务场景代码
例如:
用户角色(buyer)->使用设备(uni)->业务场景(frontPage用户不需要登录可以看到的界面)
#### 使用设备层区分
使用设备一般分为
uni: 移动端(手机、平板设备等)
web: PC端
screen: 大屏
#### 使用说明
1. 下载项目代码
2. cnpm i 安装依赖
3. 编写业务代码
4. npm run dev在开发模式下运行项目
5. 开发完成后,npm run build可以在生产环境下进行构建
6. pm2 start ./bootstrap.js 可以在pm2中运行
#### 开发指南
##### 环境要求
- Node.js >= 12.0.0
- MySQL >= 5.7
- Redis >= 3.0
##### 快速开始
1. 下载项目代码
2. 安装依赖:`npm install` 或 `yarn install`
3. 配置数据库:修改 `src/config/config.default.ts` 中的数据库连接信息
4. 启动开发服务器:`npm run dev` (端口: 7098)
5. 构建生产版本:`npm run build`
6. 启动生产服务器:`npm run start` 或使用PM2:`pm2 start ./bootstrap.js`
##### 开发命令
- `npm run dev` - 开发模式启动(热重载)
- `npm run build` - 构建生产版本
- `npm run start` - 生产模式启动
- `npm run test` - 运行测试
- `npm run lint` - 代码检查
- `npm run lint:fix` - 自动修复代码格式
- `npm run cov` - 生成测试覆盖率报告
##### 项目结构详解
```
src/
├── config/ # 配置文件
│ ├── config.default.ts # 默认配置
│ └── config.unittest.ts # 测试环境配置
├── controller/ # 控制器层
│ ├── buyer/ # 买家端控制器
│ │ ├── uni/ # 移动端(uni-app)
│ │ └── web/ # Web端
│ └── staff/ # 员工端控制器
│ └── web/ # Web后台管理
├── entity/ # TypeORM实体类
├── middleware/ # 中间件
│ ├── format.middleware.ts # 统一响应格式中间件
│ ├── jwt.passport.middleware.ts # JWT认证中间件
│ └── local.passport.middleware.ts # 本地认证中间件
├── module/ # 业务模块
│ ├── common/ # 公共模块
│ │ ├── model/ # 数据模型
│ │ ├── service/ # 基础服务
│ │ └── utils/ # 工具类
│ ├── auth/ # 权限认证模块
│ ├── trade/ # 交易模块
│ └── ... # 其他业务模块
├── service/ # 全局服务
├── socket/ # WebSocket处理
└── configuration.ts # 应用配置入口
```
##### API结构说明
- **基础URL**: `http://localhost:7098/api`
- **买家端API**: `/api/buyer/{device}/{module}/{action}`
- **员工端API**: `/api/staff/{device}/{module}/{action}`
- **设备类型**: `uni`(移动端)、`web`(PC端)、`screen`(大屏端)
- **响应格式**: 统一使用Result模型包装
```json
{
"code": 0,
"message": "",
"data": {},
"msg": ""
}
```
##### 核心服务类
- **BaseService**: 所有业务服务的基类,提供通用功能
- `pageBase()`: 分页查询
- `getByIdBase()`: 根据ID查询
- `arrBase()`: 数组查询
- `sortOrder()`: 拖拽排序
- `getCode()`: 树形结构代码生成
- `unique()`: 唯一性验证
##### 数据库设计规范
- 所有表必须有 `id varchar(128)` 主键(UUID格式)
- 表名、字段名使用小写字母和下划线
- 禁止使用外键,在应用层处理关联关系
- 支持树形结构,使用 `code` 字段存储层级编码
##### 错误处理
- 使用 `Zero0Error` 自定义错误类
- 通过 `FormatMiddleware` 统一处理异常响应
- 业务逻辑中直接抛出异常,不需要返回success标识
##### FormatMiddleware 中间件详解
`FormatMiddleware` 是本项目的核心中间件,承担着统一API响应格式的重要职责:
**核心作用:**
1. **统一响应格式**:将所有API响应包装成标准的Result格式
2. **异常处理**:自动捕获和处理业务异常,返回统一的错误响应
3. **简化开发**:开发者无需手动包装返回数据,专注于业务逻辑
**工作原理:**
- 拦截所有 `/api/buyer` 和 `/api/staff` 路径的请求
- 执行业务逻辑后,将返回数据自动包装到Result对象中
- 捕获异常时,提取错误码和错误信息,返回错误响应
**响应格式:**
```typescript
// 成功响应
{
"code": 0, // 固定为0表示成功
"message": "", // 成功消息
"data": {}, // 实际返回数据
"msg": "" // 备用消息
}
// 异常响应
{
"code": "ERROR_CODE", // 错误码
"message": "错误信息", // 错误描述
"data": null, // 异常时数据为null
"msg": "" // 备用消息
}
```
**开发优势:**
- **一致性**:所有API响应格式统一,前端处理更简单
- **安全性**:异常信息不会直接暴露,避免敏感信息泄露
- **效率**:减少重复的响应包装代码,提高开发效率
- **可维护性**:响应格式的修改只需在中间件中统一处理
**使用示例:**
```typescript
// Controller中直接返回数据或抛出异常
@Get('/user/info')
async getUserInfo() {
// 成功情况:直接返回数据
return { name: '张三', age: 25 };
// 异常情况:抛出异常
throw new Zero0Error('用户不存在', 'USER_NOT_FOUND');
}
```
**注意:** 由于FormatMiddleware的存在,所有Controller和Service中:
- ✅ 成功时直接返回业务数据
- ❌ 不要返回 `{ success: true, data: ... }` 格式
- ✅ 异常时直接抛出 `Zero0Error` 或其他异常
- ❌ 不要手动构造错误响应对象
#### module中内置模块
**area** -- 地区管理
**auth** -- 权限、菜单管理
**bpm** -- 工作流
**common** -- 基本、通用功能
**content** -- 内容管理(文章、栏目)
**dataSource** -- 数据源管理
**dict** -- 字典管理
**errorCode** -- 错误码管理
**file** -- 文件管理--已作废
**form** -- 表单管理
**inventory** -- 库存管理
**job** -- 任务管理
**log** -- 日志管理
**mobile** -- 移动端管理
**notice** -- 站内信管理
**oa** -- 部门、员工管理
**partyApi** -- 第三方API
**purchase** -- 采购管理
**question** -- 题库管理
**signIn** -- 签到管理
**socket** -- webSocket管理
**tag** -- 标签管理
**timeRes** -- 时间预约管理
**trade** -- 交易模块(商品、订单等)
**webUi** -- Web界面管理
**wxPay** -- 微信支付
module中各个model对象,因为和数据库表结构相关,原本是放置在module各个模块中,但出现了在npm run build后,在pm2运行时,typeorm无法加载相关entity的问题,所以后续统一放置在了src/entity目录下。
核心模块主要分为如下几个部分:
1. 用户、角色、权限、部门、岗位管理
2. 内容管理(简化版cms),文章、栏目管理
3. 商品、订单管理
4. 物料采购、库存、领用管理
/api/buyer/uni/frontPage/goods/goods/page.json 获得商品分页列表
/api/buyer/uni/userCenter/trade/tradeOrder/buy.json?data=%7B%22data%22:%7B%22goodsId%22:%221%22,%22selectedNum%22:1%7D%7D&shopId=1 商品下单
data=
{"data":{"goodsId":"1","selectedNum":1}} 进行encodeURIComponent 编码
goodsId: 商品id
selectedNum: 购买数量
shopId写死为1
/api/buyer/uni/userCenter/trade/tradeOrder/wxpayTransactionsNative.json?id=XXX 此订单,在微信支付端形成微信订单,返回格式如下:
"code":0,"message":"","data":{"status":200,"data":{"code_url":"weixin://wxpay/bizpayurl?pr=uC3LmsNz1"}},"msg":""}
其中,需要把code_url,在页面中形成二维码进行显示
/api/buyer/uni/userCenter/trade/tradeOrder/getById.json?id=XXX 查询一个订单的状态
其中data.tradeState === 'SUCCESS' 是订单支付成功的状态
```json
{
"code": 0,
"message": "",
"data": {
"id": "2025103117290984815890",
"trade_state": "SUCCESS",
...
},
"msg": ""
}
```
/api/staff/web/userCenter/trade/tradeOrder/auditRefund.json?id=XXX 对一个订单进行全额退款
#### 部署配置
##### 生产环境配置
1. **数据库配置**
```typescript
// src/config/config.prod.ts
typeorm: {
dataSource: {
default: {
type: "mysql",
host: "your-prod-host",
port: 3306,
username: "your-prod-user",
password: "your-prod-password",
database: "your-prod-database",
synchronize: false, // 生产环境关闭自动同步
logging: false,
}
}
}
```
2. **安全配置**
- 修改JWT密钥:`jwt.secret`
- 修改Cookie签名密钥:`keys`
- 配置CORS域名限制
- 启用HTTPS
3. **Redis配置**
```typescript
redis: {
client: {
port: 6379,
host: "your-redis-host",
password: "your-redis-password",
db: 0,
}
}
```
##### Docker部署
```dockerfile
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build
EXPOSE 7098
CMD ["npm", "start"]
```
##### PM2配置
```javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'yiping-nodejs-cli',
script: './bootstrap.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
}
}]
}
```
#### 常见问题
##### Q: 如何添加新的业务模块?
A: 在 `src/module/` 下创建新模块目录,包含service、model等文件,然后在controller中调用。
##### Q: 如何自定义响应格式?
A: 修改 `src/middleware/format.middleware.ts` 中的Result模型处理逻辑。
##### Q: 数据库实体如何管理?
A: 所有TypeORM实体统一放在 `src/entity/` 目录下,在 `config.default.ts` 中注册。
##### Q: 如何处理文件上传?
A: 使用Midway的Upload组件,配置在 `config.default.ts` 的upload部分。
##### Q: JWT认证如何配置?
A: 通过 `jwt.passport.middleware.ts` 中间件处理,支持多种认证策略。
##### Q: 如何添加定时任务?
A: 在 `src/job/` 目录下创建定时任务类,使用 `@Init` 和 `@Cron` 装饰器。
##### Q: WebSocket如何使用?
A: 在 `src/socket/` 目录下创建WebSocket处理类,参考现有socket模块实现。
开发规范:
任何标识符, 默认只由大小写英文字母及数字组成, 不包含其它文本. 数据库中的标识符命名允许出现_下划线.
代码中的命名严禁使用拼音与英文混合的方式, 更不允许直接使用中文的方式
标识符默认使用lowerCamelCase风格, 必须遵从驼峰形式. 有特别约定的除外. 单词默认使用单数形式.
常量命名全部大写, 单词间用下划线隔开
不允许出现任何魔法值(即未经定义的常量)直接出现在代码中
不要使用一个常量类维护所有常量, 应该按常量功能进行归类, 分开维护
尽量不要在接口里定义变量, 如果一定要定义变量, 肯定是与接口方法相关, 并且是整个应用的基础常量
接口类中的方法和属性不要加任何修饰符号(public 也不要加), 保持代码的简洁性, 并加上有效的注释
可变参数必须放置在参数列表的最后.
IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式, 不要使用windows格式
方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行
所有的枚举类型字段必须要有注释, 说明每个数据项的用途
专有名词与关键字保持英文原文即可
注释掉的代码尽量要配合说明, 而不是简单的注释掉
好的命名、代码结构是自解释的, 注释力求精简准确、表达到位
数据库规约
表名、字段名必须使用小写字母或数字;禁止出现数字开头, 禁止两个下划线中间只出现数字
表名不使用复数名词
禁用保留字
库名与应用名称尽量一致
表必备一字段:id varchar(128) uuid
如果修改字段含义或对字段表示的状态追加时, 需要及时更新字段注释
字段允许适当冗余, 以提高性能, 但是必须考虑数据同步的情况
单表行数超过500万行或者单表容量超过2GB, 才推荐进行分库分表
合适的字符存储长度, 不但节约数据库表空间、节约索引存储, 更重要的是提升检索速度
如果有order by的场景, 请注意利用索引的有序性
不要使用count(列名)或count(常量)来替代count(*)
在代码中写分页查询逻辑时, 若count为0应直接返回, 避免执行后面的分页语句
不得使用外键, 一切外键概念必须在应用层解决
禁止使用存储过程, 存储过程难以调试和扩展, 更没有移植性
推荐尽量少用else, if-else的方式可以改写成: if(condition){ … return obj; } // 接着写else的业务逻辑代码; 如果非得使用if()…else if()…else…方式表达逻辑, 请勿超过3层, 超过请使用状态设计模式
循环体中的语句要考量性能, 以下操作尽量移至循环体外处理, 如获取数据库连接, 进行不必要的try-catch操作 这一条值得说一下, 因为有些代码会走得比较深, 写着写着就忘了它处于循环体的内部了. 所以保持一个谨慎的心态比较重要.
方法中需要进行参数校验的场景: 1) 调用频次低的方法. 2) 执行时间开销很大的方法, 参数校验时间几乎可以忽略不计, 但如果因为参数错误导致中间执行回退, 或者错误, 那得不偿失. 3) 需要极高稳定性和可用性的方法. 4) 对外提供的开放接口, 不管是RPC/API/HTTP接口. 5) 敏感权限入口.
方法中不需要参数校验的场景: 1) 极有可能被循环调用的方法, 不建议对参数进行校验. 但在方法说明里必须注明外部参数检查 2) 底层的方法调用频度都比较高, 一般不校验. 毕竟是像纯净水过滤的最后一道, 参数错误不太可能到底层才会暴露问题. 3) 被声明成private只会被自己代码所调用的方法, 如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题, 此时可以不校验参数
如代码中涉及到目录路径,统一使用"/",而不使用"\"
controller:
page.json: 读取分页列表数据
所有的controller, 在向外提供服务以前, 都会经过中间件FormatMiddleware的再包装. 正常返回的数据, code为0, 异常时, code为对应的异常码. 所以在controller或者service中, 不需要再返回success: true等数据项代表操作成功. 如成功, 直接返回对应数据结果, 如失败, 则抛出异常. 抛出异常的代码示例见/src/module/notice/notice.service.ts第238行代码