├── .gitignore
├── LICENSE
├── README.md
├── db
└── node-server.sql
├── gulpfile.js
├── package-lock.json
├── package.json
└── src
├── application.js
├── main
├── modules
│ └── user
│ │ ├── controller
│ │ └── index.js
│ │ ├── dao
│ │ └── index.js
│ │ └── service
│ │ └── index.js
├── router
│ └── index.js
└── utils
│ ├── constant.js
│ ├── filter.js
│ ├── index.js
│ ├── mysql.js
│ ├── page.js
│ └── response.js
└── resources
├── env
├── development.js
├── index.js
└── production.js
└── static
├── files
└── slipper.jpeg
└── pages
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 GuMingChen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | ### 简介
17 | [node-server](https://github.com/gmingchen/node-server)主要目的在于学习使用 __`node`__ 写后端服务,是基于 ___`koa2`___ 、 ___`koa-router`___ 、 ___`mysql`___ 等实现的后端服务。并使用 ___`gulp`___ 和 ___`gulp-nodemon`___ 实现了热加载功能。后续还会加入参数验证等功能,持续更新。内置了单表的增删改查的例子,启动服务后访问 http://localhost:8000/pages 即可查看。
18 |
19 |
20 |
21 | ### 项目结构
22 |
23 | ```bash
24 | src
25 | ├─main
26 | │ ├─router # 路由主要配置
27 | │ ├─utils # 工具模块
28 | │ │ ├─constant.js # 常量
29 | │ │ ├─filter.js # 过滤器
30 | │ │ ├─mysql.js # 数据库工具 连接 处理
31 | │ │ ├─page.js # 分页结果封装类
32 | │ │ ├─response.js # 响应结果封装类
33 | │ │ └─index.js # 常用工具
34 | │ ├─modules # 主要业务模块
35 | │ │ ├─user # 用户
36 | │ │ │ ├─dao # 数据处理层
37 | │ │ │ ├─service # 服务层
38 | │ └─ └─ └─controller # 路由
39 | │
40 | ├─resources
41 | │ ├─env # 环境配置模块
42 | │ │ ├─development.js # 开发环境配置
43 | │ │ ├─production.js # 生产环境配置
44 | │ │ └─index.js # 环境配置出口
45 | │ ├─static # 静态资源
46 | │ │ ├─files # 文件上传路径
47 | │ └─ └─pages # Demo页面
48 | └─application.js # 主入口启动文件
49 | ```
50 |
51 | ### 开发
52 |
53 | ```bash
54 | # 克隆项目
55 | git clone https://github.com/gmingchen/node-server.git
56 |
57 | # 进入项目目录
58 | cd node-server
59 |
60 | # 安装依赖
61 | npm install
62 |
63 | # 安装数据库
64 |
65 | # 修改 src/resources/env 文件夹下的数据库相关配置
66 |
67 | # 启动服务
68 | npm run hot # 开发环境-热加载
69 | npm run dev # 开发环境
70 | npm run prod # 正式环境
71 |
72 | # http://localhost:8000/pages Demo页
73 | ```
74 |
75 | ### 联系方式
76 |
77 |
78 |
79 | 公众号 |
80 | QQ交流群 |
81 | 微信交流群 |
82 | 微信 |
83 | QQ |
84 |
85 |
86 |
87 |
88 | |
89 |
90 |
91 | |
92 |
93 |
94 | |
95 |
96 |
97 | |
98 |
99 |
100 | |
101 |
102 |
103 |
104 | ### 其它开源项目
105 |
106 | [vue3-element-plus-admin](https://github.com/gmingchen/vue3-element-plus-admin)
107 |
108 | 是一个管理后台基础功能框架,基于 [vue3](https://github.com/vuejs/vue-next) 、 [element-plus](https://github.com/element-plus/element-plus) 和 [typescript](https://github.com/microsoft/TypeScript) 实现。内置了 i18n 国际化,动态路由,权限验证。-[私活神器]
109 |
110 | [java-admin-base](https://github.com/gmingchen/java-admin-base)
111 |
112 | 是一个管理后台基础功能框架 [base-refactoring](https://github.com/gmingchen/vue3-element-plus-admin/tree/base-refactoring) 分支的后端代码,基于 __`java`__ 的 __`springboot`__
113 |
114 | [vue3-element-plus-im](https://github.com/gmingchen/vue3-element-plus-im)
115 |
116 | 是一个即时聊天系统,基于 [vue3](https://github.com/vuejs/vue-next) 、 [element-plus](https://github.com/element-plus/element-plus) 实现。内置了好友私聊功能。
117 |
118 | [java-im](https://github.com/gmingchen/java-im)
119 |
120 | 是[vue3-element-plus-im](https://github.com/gmingchen/vue3-element-plus-im)即时聊天系统的 __`java`__ 后端代码,__`springboot`__ 基于 [netty](https://github.com/netty/netty) 、 [shiro](https://github.com/apache/shiro) 实现。
121 |
--------------------------------------------------------------------------------
/db/node-server.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat Premium Data Transfer
3 |
4 | Source Server : Chen
5 | Source Server Type : MySQL
6 | Source Server Version : 80024
7 | Source Host : 139.196.182.46:3306
8 | Source Schema : node-server
9 |
10 | Target Server Type : MySQL
11 | Target Server Version : 80024
12 | File Encoding : 65001
13 |
14 | Date: 04/03/2022 16:18:15
15 | */
16 |
17 | SET NAMES utf8mb4;
18 | SET FOREIGN_KEY_CHECKS = 0;
19 |
20 | -- ----------------------------
21 | -- Table structure for user
22 | -- ----------------------------
23 | DROP TABLE IF EXISTS `user`;
24 | CREATE TABLE `user` (
25 | `id` int NOT NULL AUTO_INCREMENT,
26 | `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
27 | `password` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',
28 | `nickname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
29 | `mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '手机号',
30 | `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '邮箱',
31 | `sex` tinyint(1) NULL DEFAULT NULL COMMENT '性别:0-女 1-男 2-未知',
32 | `created_at` datetime NULL DEFAULT NULL COMMENT '创建时间',
33 | PRIMARY KEY (`id`) USING BTREE
34 | ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
35 |
36 | SET FOREIGN_KEY_CHECKS = 1;
37 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp')
2 | const nodemon = require('gulp-nodemon')
3 |
4 | gulp.task('default', () => {
5 | nodemon({
6 | exec: 'cross-env NODE_ENV=development node ./src/application.js',
7 | ext: 'js html css',
8 | env: {
9 | 'NODE_ENV': 'development'
10 | }
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-server",
3 | "version": "1.0.0",
4 | "description": "node 后端服务",
5 | "main": "./src/application.js",
6 | "scripts": {
7 | "hot": "gulp",
8 | "dev": "cross-env NODE_ENV=development node ./src/application.js",
9 | "prod": "cross-env NODE_ENV=production node ./src/application.js",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/gmingchen/node-server.git"
15 | },
16 | "author": "gumingchen",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/gmingchen/node-server/issues"
20 | },
21 | "homepage": "https://github.com/gmingchen/node-server#readme",
22 | "dependencies": {
23 | "cross-env": "^7.0.3",
24 | "koa": "^2.13.4",
25 | "koa-body": "^4.2.0",
26 | "koa-bodyparser": "^4.3.0",
27 | "koa-router": "^10.1.1",
28 | "koa-static": "^5.0.0",
29 | "koa2-cors": "^2.0.6",
30 | "mysql": "^2.18.1",
31 | "require-all": "^3.0.0"
32 | },
33 | "devDependencies": {
34 | "gulp": "^4.0.2",
35 | "gulp-nodemon": "^2.5.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/application.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa')
2 | const cors = require('koa2-cors');
3 | const Body = require('koa-body')
4 | const BodyParser = require('koa-bodyparser')
5 | const Static = require('koa-static')
6 |
7 | const { env, service, file } = require('./resources/env')
8 | const Router = require('./main/router')
9 | const Filter = require('./main/utils/filter')
10 |
11 | const app = new Koa()
12 |
13 | // 设置跨域
14 | app.use(cors({
15 | origin: function() {
16 | return '*';
17 | },
18 | maxAge: 5,
19 | credentials: true,
20 | allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
21 | }))
22 |
23 | // 设置静态文件夹
24 | app.use(Static(__dirname + '/resources/static'))
25 |
26 | // 设置请求体 文件大小
27 | app.use(Body({
28 | multipart: true,
29 | formidable: {
30 | maxFileSize: file.size
31 | }
32 | }))
33 |
34 | // 设置请求参数支持 json 格式
35 | app.use(new BodyParser())
36 |
37 | // 拦截处理
38 | Filter(app)
39 |
40 | // 使用路由 并启动服务
41 | app.use(Router.routes()).listen(service.port)
42 |
43 | console.log(new Date(), `Node started on port(s): ${ service.port } (http) with context path '${ service.contextPath }' - ${ env }`)
44 |
--------------------------------------------------------------------------------
/src/main/modules/user/controller/index.js:
--------------------------------------------------------------------------------
1 | const router = require('koa-router')()
2 | const R = require('../../../utils/response')
3 | const Service = require ('../service')
4 | const Constant = require('../../../utils/constant');
5 |
6 | // #region 分页列表
7 | /**
8 | * 分页列表
9 | *
10 | * @api {GET} /slipper/user/page page
11 | * @apiDescription 用户分页列表
12 | * @apiVersion 1.0.0
13 | * @apiGroup User
14 | * @apiName page
15 | * @apiParamExample 请求参数示例
16 | * {
17 | * page: 1, // 当前页
18 | * size: 10, // 页面大小
19 | * username: '', // 用户名
20 | * nickname: '', // 昵称
21 | * }
22 | * @apiSuccessExample 响应结果示例
23 | * {
24 | * code: 0,
25 | * message: '成功!',
26 | * data: {
27 | * current: 1, // 当前页
28 | * size: 1, // 页面大小
29 | * total: 1, // 总条数
30 | * pages: 1, // 总页数
31 | * list: [{
32 | * id: '', // ID
33 | * username: '', // 用户名
34 | * password: '', // 密码
35 | * nickname: '', // 昵称
36 | * mobile: '', // 手机号
37 | * email: '', // 邮箱
38 | * sex: '', // 性别:0-女 1-男 2-未知
39 | * created_at: '' // 创建时间
40 | * }]
41 | * }
42 | * }
43 | */
44 | //#endregion
45 | router.get('/page', async (ctx) => {
46 | const { current, size, username, nickname } = ctx.query
47 | await Service.page(current, size, username, nickname).then(r => {
48 | ctx.body = R.success(r)
49 | })
50 | })
51 |
52 | // #region 信息
53 | /**
54 | * 信息
55 | *
56 | * @api {GET} /slipper/user/info info
57 | * @apiDescription 用户信息
58 | * @apiVersion 1.0.0
59 | * @apiGroup User
60 | * @apiName info
61 | * @apiParamExample 请求参数示例
62 | * {
63 | * id: 1, // ID
64 | * }
65 | * @apiSuccessExample 响应结果示例
66 | * {
67 | * code: 0,
68 | * message: '成功!',
69 | * data: {
70 | * id: '', // ID
71 | * username: '', // 用户名
72 | * password: '', // 密码
73 | * nickname: '', // 昵称
74 | * mobile: '', // 手机号
75 | * email: '', // 邮箱
76 | * sex: '', // 性别:0-女 1-男 2-未知
77 | * created_at: '' // 创建时间
78 | * }
79 | * }
80 | */
81 | //#endregion
82 | router.get('/info', async (ctx) => {
83 | const { id } = ctx.query
84 | await Service.info(id).then(r => {
85 | ctx.body = R.success(r ? r : {})
86 | })
87 | })
88 |
89 | // #region 新增
90 | /**
91 | * 新增
92 | *
93 | * @api {POST} /slipper/user/create create
94 | * @apiDescription 用户新增
95 | * @apiVersion 1.0.0
96 | * @apiGroup User
97 | * @apiName create
98 | * @apiParamExample 请求参数示例
99 | * {
100 | * username: '', // 用户名
101 | * password: '', // 密码
102 | * nickname: '', // 昵称
103 | * mobile: '', // 手机号
104 | * email: '', // 邮箱
105 | * sex: '', // 性别:0-女 1-男 2-未知
106 | * }
107 | * @apiSuccessExample 响应结果示例
108 | * {
109 | * code: 0,
110 | * message: '成功!'
111 | * }
112 | */
113 | //#endregion
114 | router.post('/create', async (ctx) => {
115 | const user = ctx.request.body
116 | await Service.create(user).then(r => {
117 | ctx.body = R.success(r)
118 | })
119 | })
120 |
121 | // #region 编辑
122 | /**
123 | * 编辑
124 | *
125 | * @api {POST} /slipper/user/update update
126 | * @apiDescription 用户编辑
127 | * @apiVersion 1.0.0
128 | * @apiGroup User
129 | * @apiName update
130 | * @apiParamExample 请求参数示例
131 | * {
132 | * id: '', // ID
133 | * username: '', // 用户名
134 | * password: '', // 密码
135 | * nickname: '', // 昵称
136 | * mobile: '', // 手机号
137 | * email: '', // 邮箱
138 | * sex: '', // 性别:0-女 1-男 2-未知
139 | * }
140 | * @apiSuccessExample 响应结果示例
141 | * {
142 | * code: 0,
143 | * message: '成功!'
144 | * }
145 | */
146 | //#endregion
147 | router.post('/update', async (ctx) => {
148 | const user = ctx.request.body
149 | await Service.update(user).then(r => {
150 | ctx.body = R.success(r)
151 | })
152 | })
153 |
154 | // #region 删除
155 | /**
156 | * 删除
157 | *
158 | * @api {POST} /slipper/user/delete delete
159 | * @apiDescription 用户删除
160 | * @apiVersion 1.0.0
161 | * @apiGroup User
162 | * @apiName delete
163 | * @apiParamExample 请求参数示例
164 | * {
165 | * ids: [], // ID数组
166 | * }
167 | * @apiSuccessExample 响应结果示例
168 | * {
169 | * code: 0,
170 | * message: '成功!'
171 | * }
172 | */
173 | //#endregion
174 | router.post('/delete', async (ctx) => {
175 | const { ids } = ctx.request.body
176 | if (ids && ids.length) {
177 | await Service.deletion(ids).then(_r => {
178 | ctx.body = R.success()
179 | })
180 | } else {
181 | ctx.body = R.error(Constant.VALIDATE_ERROR_CODE, Constant.VALIDATE_ERROR_MESSAGE, 'ID不能为空')
182 | }
183 | })
184 |
185 | module.exports = router
--------------------------------------------------------------------------------
/src/main/modules/user/dao/index.js:
--------------------------------------------------------------------------------
1 | const Mysql = require('../../../utils/mysql')
2 |
3 | /**
4 | * 分页列表
5 | * @param {Number} current 当前页
6 | * @param {Number} size 分页大小
7 | * @param {String} username 用户名
8 | * @param {String} nickname 昵称
9 | * @returns
10 | */
11 | const page = (current, size, username, nickname) => {
12 | let sql = `select * from user`
13 | sql += ` where 1 = 1`
14 | sql += username ? ` and username like '%${ username }%'` : ''
15 | sql += nickname ? ` and nickname like '%${ nickname }%'` : ''
16 | sql += ` order by created_at desc`
17 | return Mysql.queryPage(sql, current, size)
18 | }
19 |
20 | /**
21 | * 信息
22 | * @param {Number} id ID
23 | * @returns
24 | */
25 | const info = (id) => {
26 | const sql = `select * from user where id = ${ id }`
27 | return Mysql.queryOne(sql)
28 | }
29 |
30 | /**
31 | * 新增
32 | * @param {Object} user 对象
33 | * @returns
34 | */
35 | const create = (user) => {
36 | const { columns, values } = Mysql.insertFormat(user, ['id'])
37 | const sql = `insert into user (${ columns.join(',') }) values (${ values.join(',') });`
38 | return Mysql.insert(sql)
39 | }
40 |
41 | /**
42 | * 编辑
43 | * @param {Object} user 对象
44 | * @returns
45 | */
46 | const update = (user) => {
47 | const columns = Mysql.updateFormat(user)
48 | const sql = `update user set ${ columns.join(',') } where id = ${ user.id };`
49 | return Mysql.update(sql)
50 | }
51 |
52 | /**
53 | * 删除
54 | * @param {Array} ids ID 数组
55 | * @returns
56 | */
57 | const deletion = (ids) => {
58 | const sql = `delete from user where id in (${ ids.join(',') })`
59 | return Mysql.deletion(sql)
60 | }
61 |
62 | module.exports = {
63 | page,
64 | info,
65 | create,
66 | update,
67 | deletion
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/modules/user/service/index.js:
--------------------------------------------------------------------------------
1 | const Dao = require('../dao');
2 | const { parseDate2Str } = require('../../../utils');
3 |
4 | /**
5 | * 分页列表
6 | * @param {Number} current 当前页
7 | * @param {Number} size 分页大小
8 | * @param {String} username 用户名
9 | * @param {String} nickname 昵称
10 | * @returns
11 | */
12 | const page = (current, size, username, nickname) => {
13 | return Dao.page(current, size, username, nickname)
14 | }
15 |
16 | /**
17 | * 信息
18 | * @param {Number} id ID
19 | * @returns
20 | */
21 | const info = (id) => {
22 | return Dao.info(id)
23 | }
24 |
25 | /**
26 | * 新增
27 | * @param {Object} user 对象
28 | * @returns
29 | */
30 | const create = async (user) => {
31 | user.created_at = parseDate2Str()
32 | return Dao.create(user)
33 | }
34 |
35 | /**
36 | * 编辑
37 | * @param {Object} user 对象
38 | * @returns
39 | */
40 | const update = async (user) => {
41 | return Dao.update(user)
42 | }
43 |
44 | /**
45 | * 删除
46 | * @param {Array} ids
47 | * @returns
48 | */
49 | const deletion = async (ids) => {
50 | return Dao.deletion(ids)
51 | }
52 |
53 | module.exports = {
54 | page,
55 | info,
56 | create,
57 | update,
58 | deletion
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/router/index.js:
--------------------------------------------------------------------------------
1 | const Router = require('koa-router')
2 |
3 | const { service, folder } = require('../../resources/env')
4 | const R = require('../utils/response');
5 |
6 | const router = new Router({
7 | prefix: service.contextPath
8 | })
9 |
10 | router.get('/', async (ctx) => {
11 | ctx.body = R.success()
12 | })
13 |
14 | // 获取 src / main / modules 下面的 模块
15 | const modules = require('require-all')({
16 | dirname: __dirname + '/../modules/',
17 | filter: /^[A-Za-z0-9]+\.js$/,
18 | })
19 |
20 | // 给模块中 controller 文件夹下面的 js 文件的路由统一添加模块名为路由
21 | for(const key in modules) {
22 | for(const name in modules[key][folder.controller]) {
23 | if (modules[key][folder.controller].hasOwnProperty(name)) {
24 | const moduleRouter = modules[key][folder.controller][name]
25 | router.use(`/${key}`, moduleRouter.routes(), moduleRouter.allowedMethods())
26 | }
27 | }
28 | }
29 |
30 | module.exports = router
31 |
--------------------------------------------------------------------------------
/src/main/utils/constant.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Description: 常量
3 | * @Author: gumingchen
4 | * @Email: 1240235512@qq.com
5 | * @Date: 2021-12-29 09:57:33
6 | */
7 | module.exports = class Constant {
8 | // 状态码
9 | static SUCCESS_CODE = 0
10 | static SUCCESS_MESSAGE = '成功!'
11 | static ERROR_CODE = 500
12 | static ERROR_MESSAGE = '未知异常!'
13 | static NOT_FOUND_CODE = 404
14 | static NOT_FOUND_MESSAGE = '路径不存在!'
15 | static VALIDATE_ERROR_CODE = 400;
16 | static VALIDATE_ERROR_MESSAGE = "参数校验异常!";
17 | static WARNING_CODE = 600
18 | }
--------------------------------------------------------------------------------
/src/main/utils/filter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Description: 过滤处理
3 | * @Author: gumingchen
4 | * @Email: 1240235512@qq.com
5 | * @Date: 2021-12-29 09:57:33
6 | */
7 | const R = require('./response');
8 | const Constant = require('./constant');
9 | const { parseDate2Str } = require('./index')
10 |
11 | module.exports = (app) => {
12 | app.use(async (ctx, next) => {
13 | console.log(new Date(), parseDate2Str(), ctx.request.header.host, ctx.request.url, ctx.request.method);
14 | console.log(new Date(), parseDate2Str(), ctx.query, ctx.body || {});
15 | try {
16 | await next()
17 | } catch (error) {
18 | if (error.code) {
19 | ctx.body = R.error(Constant.ERROR_CODE, 'SQL异常')
20 | } else if (error.message.split(':').length === 2) {
21 | const [code, message] = error.message.split(':')
22 | ctx.body = R.error(code, message)
23 | } else {
24 | ctx.body = R.error()
25 | }
26 | }
27 | switch (+ctx.status) {
28 | case 404:
29 | ctx.body = R.error(Constant.NOT_FOUND_CODE, Constant.NOT_FOUND_MESSAGE)
30 | break;
31 | }
32 | })
33 | }
--------------------------------------------------------------------------------
/src/main/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 日期转字符串
3 | * @param {*} time 日期 默认当前日期
4 | * @param {*} format 格式
5 | * @returns
6 | */
7 | function parseDate2Str(time = new Date(), format = '{y}-{M}-{d} {h}:{m}:{s}') {
8 | let result = ''
9 | let date = new Date()
10 | const type = typeof time
11 | if (type === 'object') {
12 | date = time
13 | } else if (type === 'number') {
14 | date = new Date(time)
15 | }
16 | const formatObj = {
17 | y: date.getFullYear(),
18 | M: date.getMonth() + 1,
19 | d: date.getDate(),
20 | h: date.getHours(),
21 | m: date.getMinutes(),
22 | s: date.getSeconds(),
23 | a: date.getDay()
24 | }
25 | result = format.replace(/\{[yMdhmsa]+\}/g, (val) => {
26 | const key = val.replace(/\{|\}/g, '')
27 | const value = formatObj[key]
28 | if (key === 'a') {
29 | return ['日', '一', '二', '三', '四', '五', '六'][value]
30 | }
31 | return value.toString().padStart(2, '0')
32 | })
33 | return result
34 | }
35 |
36 | module.exports = {
37 | parseDate2Str
38 | }
--------------------------------------------------------------------------------
/src/main/utils/mysql.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Description: mysql 处理封装
3 | * @Author: gumingchen
4 | * @Email: 1240235512@qq.com
5 | * @Date: 2021-12-29 09:57:33
6 | */
7 | const mysql = require('mysql')
8 | const { database } = require('../../resources/env')
9 | const { parseDate2Str } = require('.')
10 | const Page = require('./page')
11 |
12 | const pool = mysql.createPool({
13 | host: database.ip,
14 | port: database.port,
15 | database: database.name,
16 | user: database.username,
17 | password: database.password,
18 | multipleStatements: true,
19 | charset: 'UTF8MB4',
20 | dateStrings: true
21 | });
22 |
23 | /**
24 | * 查询列表
25 | * @param {String} sql sql语句
26 | * @returns
27 | */
28 | function queryList(sql) {
29 | return new Promise((resolve, reject) => {
30 | pool.getConnection((error, connection) => {
31 | if (error) {
32 | reject(error);
33 | return
34 | }
35 | // todo: 返回日期处理
36 | // sql = sql.replace(/,created_at/, `,DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%S') as created_at`)
37 | console.info(new Date(), parseDate2Str(), sql);
38 | connection.query(sql, (error, results) => {
39 | if (error) {
40 | reject(error);
41 | }
42 | resolve(results)
43 | })
44 | connection.release()
45 | })
46 | })
47 | }
48 |
49 | /**
50 | * 查询一条
51 | * @param {String} sql sql语句
52 | * @returns
53 | */
54 | function queryOne(sql) {
55 | return new Promise((resolve, reject) => {
56 | queryList(sql).then(results => {
57 | resolve(results && results.length > 0 ? results[0] : null)
58 | }).catch((error) => {
59 | reject(error)
60 | })
61 | })
62 | }
63 |
64 | /**
65 | * 查询分页 // todo: sql语句拼接的时候不需要加上 limit 这边会主动加上
66 | * @param {*} sql sql语句
67 | * @param {*} current 当前页
68 | * @param {*} size 分页大小
69 | * @returns
70 | */
71 | function queryPage(sql, current = 1, size = 10) {
72 | const countSql = sql.replace(/^select(.+?)from /, 'select count(*) from ').replace(/\s?$/, ';')
73 | sql = `${countSql} ${sql.replace(/\s?;$/, '')} limit ${ (current - 1) * size}, ${ size };`
74 | return new Promise((resolve, reject) => {
75 | queryList(sql).then(results => {
76 | let total = 0
77 | for (const key in results[0][0]) {
78 | total = results[0][0][key]
79 | }
80 | page = new Page(current, size, total, results[1])
81 | resolve(page)
82 | }).catch((error) => {
83 | reject(error)
84 | })
85 |
86 | })
87 | }
88 |
89 | /**
90 | * 插入数据
91 | * @param {String} sql sql语句
92 | * @returns
93 | */
94 | function insert(sql) {
95 | return new Promise((resolve, reject) => {
96 | pool.getConnection((error, connection) => {
97 | if (error) {
98 | reject(error);
99 | return
100 | }
101 | console.info(new Date(), parseDate2Str(), sql);
102 | connection.query(sql, (error, results) => {
103 | if (error) {
104 | reject(error);
105 | }
106 | resolve(results.insertId)
107 | })
108 | connection.release()
109 | })
110 | })
111 | }
112 |
113 | /**
114 | * 更新数据
115 | * @param {String} sql sql语句
116 | * @returns
117 | */
118 | function update(sql) {
119 | return new Promise((resolve, reject) => {
120 | pool.getConnection((error, connection) => {
121 | if (error) {
122 | reject(error);
123 | return
124 | }
125 | console.info(new Date(), parseDate2Str(), sql);
126 | connection.query(sql, (error, _results) => {
127 | if (error) {
128 | reject(error);
129 | }
130 | resolve(true)
131 | })
132 | connection.release()
133 | })
134 | })
135 | }
136 |
137 | /**
138 | * 删除数据
139 | * @param {String} sql sql语句
140 | * @returns
141 | */
142 | function deletion(sql) {
143 | return new Promise((resolve, reject) => {
144 | pool.getConnection((error, connection) => {
145 | if (error) {
146 | reject(error);
147 | return
148 | }
149 | console.info(new Date(), parseDate2Str(), sql);
150 | connection.query(sql, (error, _results) => {
151 | if (error) {
152 | reject(error);
153 | }
154 | resolve(true)
155 | })
156 | connection.release()
157 | })
158 | })
159 | }
160 |
161 | /**
162 | * 新增列名 列值 处理
163 | * @param {Array} keys 列
164 | * @param {Object} obj 对象
165 | * @returns
166 | */
167 | function insertFormat(obj, excludeKeys = []) {
168 | const columns = []
169 | const values = []
170 | for (const key in obj) {
171 | if (Object.hasOwnProperty.call(obj, key) && !excludeKeys.includes(key)) {
172 | columns.push(key)
173 | values.push(typeof obj[key] === 'string' ? `'${ obj[key] }'` : obj[key])
174 | }
175 | }
176 | return {
177 | columns,
178 | values
179 | }
180 | }
181 |
182 | /**
183 | * 更新列名 列值 处理
184 | * @param {Array} keys 列
185 | * @param {Object} obj 对象
186 | * @returns
187 | */
188 | function updateFormat(obj, excludeKeys = ['id']) {
189 | const result = []
190 | for (const key in obj) {
191 | if (Object.hasOwnProperty.call(obj, key) && !excludeKeys.includes(key)) {
192 | result.push(`${ key } = ${ typeof obj[key] === 'string' ? `'${ obj[key] }'` : obj[key] }`)
193 | }
194 | }
195 | return result
196 | }
197 |
198 | module.exports = {
199 | queryList,
200 | queryOne,
201 | queryPage,
202 | insert,
203 | update,
204 | deletion,
205 | insertFormat,
206 | updateFormat
207 | }
208 |
--------------------------------------------------------------------------------
/src/main/utils/page.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Description: 分页封装
3 | * @Author: gumingchen
4 | * @Email: 1240235512@qq.com
5 | * @Date: 2021-12-29 09:57:33
6 | */
7 | module.exports = class Page {
8 | constructor(current, size, total, list) {
9 | this.current = +current
10 | this.size = +size
11 | this.total = +total
12 | this.pages = Math.ceil(total / size)
13 | this.list = list
14 | }
15 | }
--------------------------------------------------------------------------------
/src/main/utils/response.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Description: 响应参数统一封装
3 | * @Author: gumingchen
4 | * @Email: 1240235512@qq.com
5 | * @Date: 2021-12-29 09:57:33
6 | */
7 | const Constant = require('./constant');
8 |
9 | module.exports = class Response {
10 | constructor(code, message, data) {
11 | this.code = code
12 | this.message = message
13 | this.data = data
14 | }
15 | static success(data) {
16 | return new Response(Constant.SUCCESS_CODE, Constant.SUCCESS_MESSAGE, data)
17 | }
18 | static error() {
19 | return new Response(Constant.ERROR_CODE, Constant.ERROR_MESSAGE)
20 | }
21 | static error(message) {
22 | return new Response(Constant.ERROR_CODE, message)
23 | }
24 | static error(code, message) {
25 | return new Response(code, message)
26 | }
27 | static error(code, message, data) {
28 | return new Response(code, message, data)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/resources/env/development.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: 'development',
3 | service: {
4 | port: 8000,
5 | contextPath: '/slipper'
6 | },
7 | database: {
8 | ip: 'localhost',
9 | port: 3306,
10 | name: 'node-server',
11 | username: 'root',
12 | password: 'root',
13 | },
14 | file: {
15 | path: '/src/resources/static/files',
16 | url: 'http://localhost:8888/files',
17 | size: 1024 * 1024 * 2
18 | },
19 | folder: {
20 | dao: 'dao',
21 | service: 'service',
22 | controller: 'controller'
23 | }
24 | }
--------------------------------------------------------------------------------
/src/resources/env/index.js:
--------------------------------------------------------------------------------
1 | const development = require('./development')
2 | const production = require('./production')
3 |
4 | module.exports = {
5 | development,
6 | production,
7 | }[process.env.NODE_ENV || 'development']
8 |
--------------------------------------------------------------------------------
/src/resources/env/production.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: 'production',
3 | service: {
4 | port: 8001,
5 | contextPath: '/slipper'
6 | },
7 | database: {
8 | ip: 'localhost',
9 | port: 3306,
10 | name: 'node-server',
11 | username: 'root',
12 | password: 'root',
13 | },
14 | file: {
15 | path: '/src/resources/static/files',
16 | url: 'http://localhost:8888/files',
17 | size: 1024 * 1024 * 2
18 | },
19 | folder: {
20 | dao: 'dao',
21 | service: 'service',
22 | controller: 'controller'
23 | }
24 | }
--------------------------------------------------------------------------------
/src/resources/static/files/slipper.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmingchen/node-server/caf27caac8ca0ca0acfa38b5904f79e88d3a8004/src/resources/static/files/slipper.jpeg
--------------------------------------------------------------------------------
/src/resources/static/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | index
8 |
9 |
10 |
11 |
12 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 搜索
47 | 重置
48 | 批量删除
49 |
50 |
51 |
新增
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 女
68 | 男
69 | 未知
70 |
71 |
72 |
73 |
74 |
75 | 编辑
76 | 删除
77 |
78 |
79 |
80 |
90 |
91 |
92 |
93 |
{{item.title}}
94 |
98 |
99 |
100 |
101 |
106 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | 女
146 | 男
147 | 未知
148 |
149 |
150 |
151 |
152 |
153 |
154 |
158 |
159 |
160 |
161 |
162 |
316 |
317 |
--------------------------------------------------------------------------------