wok-server 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -17,7 +17,17 @@
|
|
|
17
17
|
|
|
18
18
|
## Hello world
|
|
19
19
|
|
|
20
|
+
安装:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
npm i wok-server --save
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
入口文件:
|
|
27
|
+
|
|
20
28
|
```ts
|
|
29
|
+
import { startWebServer } from 'wok-server'
|
|
30
|
+
|
|
21
31
|
startWebServer({
|
|
22
32
|
routers: {
|
|
23
33
|
'/': async exchange => exchange.respondText('Hello world !')
|
|
@@ -4,7 +4,7 @@ exports.ValidationException = void 0;
|
|
|
4
4
|
/**
|
|
5
5
|
* 校验异常.
|
|
6
6
|
*/
|
|
7
|
-
class ValidationException {
|
|
7
|
+
class ValidationException extends Error {
|
|
8
8
|
errMsg;
|
|
9
9
|
validator;
|
|
10
10
|
propertyPath;
|
|
@@ -26,19 +26,11 @@ class ValidationException {
|
|
|
26
26
|
* 值
|
|
27
27
|
*/
|
|
28
28
|
val) {
|
|
29
|
+
super(`Field "${propertyPath}" failed to validate by validator ${validator} ,value:${val},info:${errMsg}`);
|
|
29
30
|
this.errMsg = errMsg;
|
|
30
31
|
this.validator = validator;
|
|
31
32
|
this.propertyPath = propertyPath;
|
|
32
33
|
this.val = val;
|
|
33
34
|
}
|
|
34
|
-
/**
|
|
35
|
-
* 对错误的完整描述信息,用于日志记录.
|
|
36
|
-
*/
|
|
37
|
-
desc() {
|
|
38
|
-
return `Field "${this.propertyPath}" failed to validate by validator ${this.validator} ,value:${this.val},info:${this.errMsg}`;
|
|
39
|
-
}
|
|
40
|
-
get message() {
|
|
41
|
-
return this.desc();
|
|
42
|
-
}
|
|
43
35
|
}
|
|
44
36
|
exports.ValidationException = ValidationException;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# 工程化
|
|
2
|
+
|
|
3
|
+
当程序的规模大起来以后,我们就需要进行拆分,更好的组织代码文件,
|
|
4
|
+
可以方便的管理大型项目。
|
|
5
|
+
|
|
6
|
+
以下是推荐的做法。
|
|
7
|
+
|
|
8
|
+
## 目录结构
|
|
9
|
+
|
|
10
|
+
推荐的划分方式是按功能来划分,而不是传统的按层(service 层,controller 层)来划分,
|
|
11
|
+
因为这样可以将相关性高的文件放在一起,查找更方便,也更利于封装。
|
|
12
|
+
|
|
13
|
+
假设有用户(user)和标签(tag)两个业务功能,下面是项目的大致结构。
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
根目录
|
|
17
|
+
├──db-migration 数据库迁移文件,使用 mysql 时才有
|
|
18
|
+
├──src 源码目录
|
|
19
|
+
│ ├──auth 授权
|
|
20
|
+
│ │ ├──auth.ts 授权信实体配置
|
|
21
|
+
│ │ ├──auth-interceptor.ts 授权拦截器
|
|
22
|
+
│ │ ├──create-auth.ts 创建授权接口(/auth/create)
|
|
23
|
+
│ │ ├──ccu-task.ts 在线人数统计任务
|
|
24
|
+
│ │ └──index.ts 包入口文件,导出需要导出的模块
|
|
25
|
+
│ ├──user 用户业务功能目录
|
|
26
|
+
│ │ ├──user.ts 用户实体配置
|
|
27
|
+
│ │ ├──create-user.ts 创建用户接口(/user/create)
|
|
28
|
+
│ │ ├──delete-user.ts 删除用户接口(/user/delete)
|
|
29
|
+
│ │ ├──update-user.ts 更新用户接口(/user/update)
|
|
30
|
+
│ │ └──index.ts 包入口文件,导出需要导出的模块
|
|
31
|
+
│ ├──tag 标签业务功能目录
|
|
32
|
+
│ │ ├──tag.ts 标签实体配置
|
|
33
|
+
│ │ ├──create-tag.ts 创建标签接口(/tag/create)
|
|
34
|
+
│ │ ├──delete-tag.ts 删除标签接口(/tag/delete)
|
|
35
|
+
│ │ └──index.ts 包入口文件,导出需要导出的模块
|
|
36
|
+
│ └──main.ts 入口文件,启动服务,配置路由
|
|
37
|
+
├──.env 开发环境变量配置
|
|
38
|
+
├──package.json
|
|
39
|
+
├──tsconfig.json
|
|
40
|
+
└──.gitignore
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 文件说明
|
|
44
|
+
|
|
45
|
+
main.ts 示例:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { createAuth, authInterceptor, ccuTask } from './auth'
|
|
49
|
+
import { createUser, updateUser, deleteUser } from './user'
|
|
50
|
+
import { createTag, deleteTag } from './tag'
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
// 改写 date 原型,将日期序列化为数字
|
|
54
|
+
// 可选,一般来说为了国际化才会这样处理
|
|
55
|
+
Date.prototype.toJSON = function () {
|
|
56
|
+
return this.getTime() as any
|
|
57
|
+
}
|
|
58
|
+
// 激活 mysql
|
|
59
|
+
await enableMysql()
|
|
60
|
+
// 启动服务
|
|
61
|
+
await startWebServer({
|
|
62
|
+
// 静态文件配置
|
|
63
|
+
static: {
|
|
64
|
+
// 前端文件
|
|
65
|
+
'/': { dir: 'fe', cacheAge: 600 }
|
|
66
|
+
},
|
|
67
|
+
// 如果接口非常多,建议将这里的配置单独写文件
|
|
68
|
+
// 或再进行拆分,放入单独的 router 目录中
|
|
69
|
+
// 但是路由的配置必须集中起来,不能放在业务功能各自的目录下
|
|
70
|
+
// 集中起来是为了方便查找
|
|
71
|
+
routers: {
|
|
72
|
+
'/auth/create': createAuth,
|
|
73
|
+
'/user/create': createUser,
|
|
74
|
+
'/user/update': updateUser,
|
|
75
|
+
'/user/delete': deleteUser,
|
|
76
|
+
'/tag/create': createTag,
|
|
77
|
+
'/tag/delete': deleteTag
|
|
78
|
+
},
|
|
79
|
+
// 拦截器
|
|
80
|
+
interceptors: [authInterceptor]
|
|
81
|
+
})
|
|
82
|
+
// 周期性任务
|
|
83
|
+
// 每60秒统计一次在线人数
|
|
84
|
+
scheduleWithFixedDelay(60, 60, ccuTask)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
main().catch(console.error)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
下面以用户功能为例,来说明实体的配置和接口的编写。
|
|
91
|
+
|
|
92
|
+
user/user.ts 文件示例:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { Table } from 'wok-server'
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 用户
|
|
99
|
+
*/
|
|
100
|
+
export interface User {
|
|
101
|
+
id: string
|
|
102
|
+
/**
|
|
103
|
+
* 编号.
|
|
104
|
+
*/
|
|
105
|
+
code: string
|
|
106
|
+
/**
|
|
107
|
+
* 昵称
|
|
108
|
+
*/
|
|
109
|
+
nickname: string
|
|
110
|
+
/**
|
|
111
|
+
* 密码
|
|
112
|
+
*/
|
|
113
|
+
pwd: string
|
|
114
|
+
/**
|
|
115
|
+
* 状态.
|
|
116
|
+
*/
|
|
117
|
+
status: 'ACTIVATED' | 'DISABLED'
|
|
118
|
+
/**
|
|
119
|
+
* 创建时间.
|
|
120
|
+
*/
|
|
121
|
+
create_at?: Date
|
|
122
|
+
/**
|
|
123
|
+
* 更新时间.
|
|
124
|
+
*/
|
|
125
|
+
update_at?: Date
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 用户表
|
|
130
|
+
*/
|
|
131
|
+
export const tableUser: Table<User> = {
|
|
132
|
+
tableName: 'user',
|
|
133
|
+
id: 'id',
|
|
134
|
+
columns: ['code', 'nickname', 'pwd', 'status'],
|
|
135
|
+
createdDate: {
|
|
136
|
+
type: 'date',
|
|
137
|
+
column: 'create_at'
|
|
138
|
+
},
|
|
139
|
+
updatedDate: {
|
|
140
|
+
type: 'date',
|
|
141
|
+
column: 'update_at'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
user.ts 这个文件中是实体类的类型申明和表的配置信息,这里的示例是 mysql 的,mongo 略有区别。
|
|
147
|
+
|
|
148
|
+
我们的建议是每个接口单独写一个文件,将接口相关的请求和响应信息都放入文件中,仅导出接口的请求处理函数。
|
|
149
|
+
|
|
150
|
+
user/create-user.ts 示例:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { createJsonHandler, getMysqlManager, notBlank, length } from 'wok-server'
|
|
154
|
+
import { User, tableUser } from './user.ts'
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 请求发送的表单信息
|
|
158
|
+
*/
|
|
159
|
+
interface Form {
|
|
160
|
+
/**
|
|
161
|
+
* 编号.
|
|
162
|
+
*/
|
|
163
|
+
code: string
|
|
164
|
+
/**
|
|
165
|
+
* 昵称
|
|
166
|
+
*/
|
|
167
|
+
nickname: string
|
|
168
|
+
/**
|
|
169
|
+
* 密码
|
|
170
|
+
*/
|
|
171
|
+
pwd: string
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 响应信息
|
|
175
|
+
*/
|
|
176
|
+
interface Resp {
|
|
177
|
+
/**
|
|
178
|
+
* 用户id
|
|
179
|
+
*/
|
|
180
|
+
id: string
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 创建用户接口 /user/create
|
|
185
|
+
*/
|
|
186
|
+
export const createUser = createJsonHandler<Form, Resp>({
|
|
187
|
+
// 校验
|
|
188
|
+
validation: {
|
|
189
|
+
code: [notBlank(), length({ max: 64 })],
|
|
190
|
+
nickname: [notBlank(), length({ max: 64 })],
|
|
191
|
+
pwd: [notBlank(), length({ max: 20 })]
|
|
192
|
+
},
|
|
193
|
+
// 业务处理
|
|
194
|
+
async handle(body, exchange) {
|
|
195
|
+
const manager = getMysqlManager()
|
|
196
|
+
if (await manager.existsBy(tableUser, { code: body.code })) {
|
|
197
|
+
// 错误信息的响应也可以自定义异常,然后通过拦截器来统一处理,这里仅仅是简单的示例
|
|
198
|
+
exchage.respondErrMsg('编号已经存在')
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
const newUser = await manager.insert(tableUser, body)
|
|
202
|
+
return { id: newUser.id }
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wok-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"packageManager": "pnpm@8.9.0",
|
|
5
5
|
"description": "一个基于 NodeJs 和 TypeScript 的后端框架,轻量级、克制、简洁。A lightweight, restrained, and concise backend framework based on Node.js and TypeScript.",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
11
|
"web",
|
|
12
|
-
"
|
|
12
|
+
"http",
|
|
13
|
+
"app",
|
|
14
|
+
"server",
|
|
15
|
+
"framework"
|
|
13
16
|
],
|
|
14
17
|
"files": [
|
|
15
18
|
"types/",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 校验异常.
|
|
3
3
|
*/
|
|
4
|
-
export declare class ValidationException {
|
|
4
|
+
export declare class ValidationException extends Error {
|
|
5
5
|
/**
|
|
6
6
|
* 异常信息,如 “不能为空” 之类的.
|
|
7
7
|
*/
|
|
@@ -35,9 +35,4 @@ export declare class ValidationException {
|
|
|
35
35
|
* 值
|
|
36
36
|
*/
|
|
37
37
|
val: any);
|
|
38
|
-
/**
|
|
39
|
-
* 对错误的完整描述信息,用于日志记录.
|
|
40
|
-
*/
|
|
41
|
-
desc(): string;
|
|
42
|
-
get message(): string;
|
|
43
38
|
}
|