befly 3.9.37 → 3.9.39
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 +38 -39
- package/befly.config.ts +62 -40
- package/checks/checkApi.ts +16 -16
- package/checks/checkApp.ts +19 -25
- package/checks/checkTable.ts +42 -42
- package/docs/README.md +42 -35
- package/docs/{api.md → api/api.md} +225 -235
- package/docs/cipher.md +71 -69
- package/docs/database.md +155 -153
- package/docs/{examples.md → guide/examples.md} +181 -181
- package/docs/guide/quickstart.md +331 -0
- package/docs/hooks/auth.md +38 -0
- package/docs/hooks/cors.md +28 -0
- package/docs/{hook.md → hooks/hook.md} +140 -57
- package/docs/hooks/parser.md +19 -0
- package/docs/hooks/rateLimit.md +47 -0
- package/docs/{redis.md → infra/redis.md} +84 -93
- package/docs/plugins/cipher.md +61 -0
- package/docs/plugins/database.md +128 -0
- package/docs/{plugin.md → plugins/plugin.md} +83 -81
- package/docs/quickstart.md +26 -26
- package/docs/{addon.md → reference/addon.md} +46 -46
- package/docs/{config.md → reference/config.md} +32 -80
- package/docs/{logger.md → reference/logger.md} +52 -52
- package/docs/{sync.md → reference/sync.md} +32 -35
- package/docs/{table.md → reference/table.md} +7 -7
- package/docs/{validator.md → reference/validator.md} +57 -57
- package/hooks/auth.ts +8 -4
- package/hooks/cors.ts +13 -13
- package/hooks/parser.ts +37 -17
- package/hooks/permission.ts +26 -14
- package/hooks/rateLimit.ts +276 -0
- package/hooks/validator.ts +15 -7
- package/lib/asyncContext.ts +43 -0
- package/lib/cacheHelper.ts +212 -81
- package/lib/cacheKeys.ts +38 -0
- package/lib/cipher.ts +30 -30
- package/lib/connect.ts +28 -28
- package/lib/dbHelper.ts +211 -109
- package/lib/jwt.ts +16 -16
- package/lib/logger.ts +610 -19
- package/lib/redisHelper.ts +185 -44
- package/lib/sqlBuilder.ts +90 -91
- package/lib/validator.ts +59 -39
- package/loader/loadApis.ts +53 -47
- package/loader/loadHooks.ts +40 -14
- package/loader/loadPlugins.ts +16 -17
- package/main.ts +57 -47
- package/package.json +47 -45
- package/paths.ts +15 -14
- package/plugins/cache.ts +5 -4
- package/plugins/cipher.ts +3 -3
- package/plugins/config.ts +2 -2
- package/plugins/db.ts +9 -9
- package/plugins/jwt.ts +3 -3
- package/plugins/logger.ts +8 -12
- package/plugins/redis.ts +8 -8
- package/plugins/tool.ts +6 -6
- package/router/api.ts +85 -56
- package/router/static.ts +12 -12
- package/sync/syncAll.ts +12 -12
- package/sync/syncApi.ts +55 -54
- package/sync/syncDb/apply.ts +20 -19
- package/sync/syncDb/constants.ts +25 -23
- package/sync/syncDb/ddl.ts +35 -36
- package/sync/syncDb/helpers.ts +6 -9
- package/sync/syncDb/schema.ts +10 -9
- package/sync/syncDb/sqlite.ts +7 -8
- package/sync/syncDb/table.ts +37 -35
- package/sync/syncDb/tableCreate.ts +21 -20
- package/sync/syncDb/types.ts +23 -20
- package/sync/syncDb/version.ts +10 -10
- package/sync/syncDb.ts +43 -36
- package/sync/syncDev.ts +74 -66
- package/sync/syncMenu.ts +190 -57
- package/tests/api-integration-array-number.test.ts +282 -0
- package/tests/befly-config-env.test.ts +78 -0
- package/tests/cacheHelper.test.ts +135 -104
- package/tests/cacheKeys.test.ts +41 -0
- package/tests/cipher.test.ts +90 -89
- package/tests/dbHelper-advanced.test.ts +140 -134
- package/tests/dbHelper-all-array-types.test.ts +316 -0
- package/tests/dbHelper-array-serialization.test.ts +258 -0
- package/tests/dbHelper-columns.test.ts +56 -55
- package/tests/dbHelper-execute.test.ts +45 -44
- package/tests/dbHelper-joins.test.ts +124 -119
- package/tests/fields-redis-cache.test.ts +29 -27
- package/tests/fields-validate.test.ts +38 -38
- package/tests/getClientIp.test.ts +54 -0
- package/tests/integration.test.ts +69 -67
- package/tests/jwt.test.ts +27 -26
- package/tests/logger.test.ts +267 -34
- package/tests/rateLimit-hook.test.ts +477 -0
- package/tests/redisHelper.test.ts +187 -188
- package/tests/redisKeys.test.ts +6 -73
- package/tests/scanConfig.test.ts +144 -0
- package/tests/sqlBuilder-advanced.test.ts +217 -215
- package/tests/sqlBuilder.test.ts +92 -91
- package/tests/sync-connection.test.ts +29 -29
- package/tests/syncDb-apply.test.ts +97 -96
- package/tests/syncDb-array-number.test.ts +160 -0
- package/tests/syncDb-constants.test.ts +48 -47
- package/tests/syncDb-ddl.test.ts +99 -98
- package/tests/syncDb-helpers.test.ts +29 -28
- package/tests/syncDb-schema.test.ts +61 -60
- package/tests/syncDb-types.test.ts +60 -59
- package/tests/syncMenu-paths.test.ts +68 -0
- package/tests/util.test.ts +42 -41
- package/tests/validator-array-number.test.ts +310 -0
- package/tests/validator-default.test.ts +373 -0
- package/tests/validator.test.ts +271 -266
- package/tsconfig.json +4 -5
- package/types/api.d.ts +7 -12
- package/types/befly.d.ts +60 -13
- package/types/cache.d.ts +8 -4
- package/types/common.d.ts +17 -9
- package/types/context.d.ts +2 -2
- package/types/crypto.d.ts +23 -0
- package/types/database.d.ts +19 -19
- package/types/hook.d.ts +2 -2
- package/types/jwt.d.ts +118 -0
- package/types/logger.d.ts +30 -0
- package/types/plugin.d.ts +4 -4
- package/types/redis.d.ts +7 -3
- package/types/roleApisCache.ts +23 -0
- package/types/sync.d.ts +10 -10
- package/types/table.d.ts +50 -9
- package/types/validate.d.ts +69 -0
- package/utils/addonHelper.ts +90 -0
- package/utils/arrayKeysToCamel.ts +18 -0
- package/utils/calcPerfTime.ts +13 -0
- package/utils/configTypes.ts +3 -0
- package/utils/cors.ts +19 -0
- package/utils/fieldClear.ts +75 -0
- package/utils/genShortId.ts +12 -0
- package/utils/getClientIp.ts +45 -0
- package/utils/keysToCamel.ts +22 -0
- package/utils/keysToSnake.ts +22 -0
- package/utils/modules.ts +98 -0
- package/utils/pickFields.ts +19 -0
- package/utils/process.ts +56 -0
- package/utils/regex.ts +225 -0
- package/utils/response.ts +115 -0
- package/utils/route.ts +23 -0
- package/utils/scanConfig.ts +142 -0
- package/utils/scanFiles.ts +48 -0
- package/.prettierignore +0 -2
- package/.prettierrc +0 -12
- package/docs/1-/345/237/272/346/234/254/344/273/213/347/273/215.md +0 -35
- package/docs/2-/345/210/235/346/255/245/344/275/223/351/252/214.md +0 -64
- package/docs/3-/347/254/254/344/270/200/344/270/252/346/216/245/345/217/243.md +0 -46
- package/docs/4-/346/223/215/344/275/234/346/225/260/346/215/256/345/272/223.md +0 -172
- package/hooks/requestLogger.ts +0 -84
- package/types/index.ts +0 -24
- package/util.ts +0 -283
package/docs/database.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> 本文档详细介绍 Befly 框架的数据库操作 API,包括 CRUD 操作、事务、条件查询等。
|
|
4
4
|
|
|
5
|
+
> 本文档已迁移到:[`./plugins/database.md`](./plugins/database.md)
|
|
6
|
+
|
|
5
7
|
## 目录
|
|
6
8
|
|
|
7
9
|
- [Befly 数据库操作指南](#befly-数据库操作指南)
|
|
@@ -73,7 +75,7 @@
|
|
|
73
75
|
// 在 API handler 中使用
|
|
74
76
|
handler: async (befly, ctx) => {
|
|
75
77
|
const user = await befly.db.getOne({
|
|
76
|
-
table:
|
|
78
|
+
table: "user",
|
|
77
79
|
where: { id: 1 }
|
|
78
80
|
});
|
|
79
81
|
};
|
|
@@ -100,13 +102,13 @@ handler: async (befly, ctx) => {
|
|
|
100
102
|
```typescript
|
|
101
103
|
// 用户提交的数据可能部分字段为空
|
|
102
104
|
await befly.db.insData({
|
|
103
|
-
table:
|
|
105
|
+
table: "user",
|
|
104
106
|
data: {
|
|
105
|
-
username:
|
|
106
|
-
email:
|
|
107
|
+
username: "john",
|
|
108
|
+
email: "john@example.com",
|
|
107
109
|
phone: undefined, // ❌ 自动忽略,不会写入
|
|
108
110
|
avatar: null, // ❌ 自动忽略,不会写入
|
|
109
|
-
nickname:
|
|
111
|
+
nickname: "" // ✅ 空字符串会写入(不是 null/undefined)
|
|
110
112
|
}
|
|
111
113
|
});
|
|
112
114
|
// 实际 SQL: INSERT INTO user (username, email, nickname, ...) VALUES ('john', 'john@example.com', '', ...)
|
|
@@ -117,7 +119,7 @@ await befly.db.insData({
|
|
|
117
119
|
```typescript
|
|
118
120
|
// 只更新用户提交的字段
|
|
119
121
|
await befly.db.updData({
|
|
120
|
-
table:
|
|
122
|
+
table: "user",
|
|
121
123
|
data: {
|
|
122
124
|
nickname: ctx.body.nickname, // 如果用户传了值,会更新
|
|
123
125
|
avatar: ctx.body.avatar, // 如果为 undefined,自动忽略
|
|
@@ -133,7 +135,7 @@ await befly.db.updData({
|
|
|
133
135
|
```typescript
|
|
134
136
|
// 条件筛选:用户可能只传部分筛选条件
|
|
135
137
|
const result = await befly.db.getList({
|
|
136
|
-
table:
|
|
138
|
+
table: "article",
|
|
137
139
|
where: {
|
|
138
140
|
categoryId: ctx.body.categoryId, // 如果未传,值为 undefined,自动忽略
|
|
139
141
|
status: ctx.body.status, // 如果未传,值为 undefined,自动忽略
|
|
@@ -150,19 +152,19 @@ const result = await befly.db.getList({
|
|
|
150
152
|
```typescript
|
|
151
153
|
// API: 用户列表(带可选筛选条件)
|
|
152
154
|
export default {
|
|
153
|
-
name:
|
|
155
|
+
name: "用户列表",
|
|
154
156
|
fields: {
|
|
155
|
-
keyword: { name:
|
|
156
|
-
|
|
157
|
-
state: { name:
|
|
157
|
+
keyword: { name: "关键词", type: "string", max: 50 },
|
|
158
|
+
status: { name: "状态", type: "number" },
|
|
159
|
+
state: { name: "状态", type: "number" }
|
|
158
160
|
},
|
|
159
161
|
handler: async (befly, ctx) => {
|
|
160
162
|
// 直接使用请求参数,无需判断是否存在
|
|
161
163
|
// null/undefined 的条件会被自动过滤
|
|
162
164
|
const result = await befly.db.getList({
|
|
163
|
-
table:
|
|
165
|
+
table: "user",
|
|
164
166
|
where: {
|
|
165
|
-
|
|
167
|
+
status: ctx.body.status, // 未传时为 undefined,自动忽略
|
|
166
168
|
state: ctx.body.state, // 未传时为 undefined,自动忽略
|
|
167
169
|
username$like: ctx.body.keyword ? `%${ctx.body.keyword}%` : undefined // 无关键词时忽略
|
|
168
170
|
},
|
|
@@ -170,7 +172,7 @@ export default {
|
|
|
170
172
|
limit: ctx.body.limit || 10
|
|
171
173
|
});
|
|
172
174
|
|
|
173
|
-
return befly.tool.Yes(
|
|
175
|
+
return befly.tool.Yes("查询成功", result);
|
|
174
176
|
}
|
|
175
177
|
};
|
|
176
178
|
```
|
|
@@ -182,23 +184,23 @@ export default {
|
|
|
182
184
|
```typescript
|
|
183
185
|
// 默认排除 null 和 undefined
|
|
184
186
|
const cleanData = befly.db.cleanFields({
|
|
185
|
-
name:
|
|
187
|
+
name: "John",
|
|
186
188
|
age: null,
|
|
187
189
|
email: undefined,
|
|
188
|
-
phone:
|
|
190
|
+
phone: ""
|
|
189
191
|
});
|
|
190
192
|
// 结果: { name: 'John', phone: '' }
|
|
191
193
|
|
|
192
194
|
// 自定义排除值(如同时排除空字符串)
|
|
193
195
|
const cleanData2 = befly.db.cleanFields(
|
|
194
|
-
{ name:
|
|
195
|
-
[null, undefined,
|
|
196
|
+
{ name: "John", phone: "", age: null },
|
|
197
|
+
[null, undefined, ""] // 排除这些值
|
|
196
198
|
);
|
|
197
199
|
// 结果: { name: 'John' }
|
|
198
200
|
|
|
199
201
|
// 保留特定字段的特定值(即使在排除列表中)
|
|
200
202
|
const cleanData3 = befly.db.cleanFields(
|
|
201
|
-
{ name:
|
|
203
|
+
{ name: "John", status: null, count: 0 },
|
|
202
204
|
[null, undefined], // 排除 null 和 undefined
|
|
203
205
|
{ status: null } // 但保留 status 字段的 null 值
|
|
204
206
|
);
|
|
@@ -217,16 +219,16 @@ const cleanData3 = befly.db.cleanFields(
|
|
|
217
219
|
```typescript
|
|
218
220
|
// 写入时使用小驼峰
|
|
219
221
|
await befly.db.insData({
|
|
220
|
-
table:
|
|
222
|
+
table: "user",
|
|
221
223
|
data: {
|
|
222
|
-
userName:
|
|
224
|
+
userName: "John", // → user_name
|
|
223
225
|
createdBy: 1 // → created_by
|
|
224
226
|
}
|
|
225
227
|
});
|
|
226
228
|
|
|
227
229
|
// 查询返回小驼峰
|
|
228
230
|
const user = await befly.db.getOne({
|
|
229
|
-
table:
|
|
231
|
+
table: "user",
|
|
230
232
|
where: { userId: 1 } // → WHERE user_id = 1
|
|
231
233
|
});
|
|
232
234
|
// 返回: { userId: 1, userName: 'John', createdBy: 1 }
|
|
@@ -253,21 +255,21 @@ interface QueryOptions {
|
|
|
253
255
|
```typescript
|
|
254
256
|
// 基础查询
|
|
255
257
|
const user = await befly.db.getOne({
|
|
256
|
-
table:
|
|
258
|
+
table: "user",
|
|
257
259
|
where: { id: 1 }
|
|
258
260
|
});
|
|
259
261
|
|
|
260
262
|
// 指定字段
|
|
261
263
|
const user = await befly.db.getOne({
|
|
262
|
-
table:
|
|
263
|
-
fields: [
|
|
264
|
+
table: "user",
|
|
265
|
+
fields: ["id", "username", "email"],
|
|
264
266
|
where: { id: 1 }
|
|
265
267
|
});
|
|
266
268
|
|
|
267
269
|
// 排除字段(使用 ! 前缀)
|
|
268
270
|
const user = await befly.db.getOne({
|
|
269
|
-
table:
|
|
270
|
-
fields: [
|
|
271
|
+
table: "user",
|
|
272
|
+
fields: ["!password", "!token"],
|
|
271
273
|
where: { id: 1 }
|
|
272
274
|
});
|
|
273
275
|
```
|
|
@@ -300,7 +302,7 @@ interface ListResult<T> {
|
|
|
300
302
|
```typescript
|
|
301
303
|
// 基础分页
|
|
302
304
|
const result = await befly.db.getList({
|
|
303
|
-
table:
|
|
305
|
+
table: "user",
|
|
304
306
|
page: 1,
|
|
305
307
|
limit: 10
|
|
306
308
|
});
|
|
@@ -308,10 +310,10 @@ const result = await befly.db.getList({
|
|
|
308
310
|
|
|
309
311
|
// 带条件和排序
|
|
310
312
|
const result = await befly.db.getList({
|
|
311
|
-
table:
|
|
312
|
-
fields: [
|
|
313
|
+
table: "user",
|
|
314
|
+
fields: ["id", "username", "createdAt"],
|
|
313
315
|
where: { state: 1 },
|
|
314
|
-
orderBy: [
|
|
316
|
+
orderBy: ["createdAt#DESC", "id#ASC"],
|
|
315
317
|
page: 2,
|
|
316
318
|
limit: 20
|
|
317
319
|
});
|
|
@@ -329,16 +331,16 @@ const result = await befly.db.getList({
|
|
|
329
331
|
```typescript
|
|
330
332
|
// 查询所有
|
|
331
333
|
const result = await befly.db.getAll({
|
|
332
|
-
table:
|
|
334
|
+
table: "user"
|
|
333
335
|
});
|
|
334
336
|
// result: { lists: [...], total: 实际总数 }
|
|
335
337
|
|
|
336
338
|
// 带条件
|
|
337
339
|
const activeResult = await befly.db.getAll({
|
|
338
|
-
table:
|
|
339
|
-
fields: [
|
|
340
|
+
table: "user",
|
|
341
|
+
fields: ["id", "username"],
|
|
340
342
|
where: { state: 1 },
|
|
341
|
-
orderBy: [
|
|
343
|
+
orderBy: ["sort#ASC"]
|
|
342
344
|
});
|
|
343
345
|
|
|
344
346
|
// 访问数据
|
|
@@ -362,12 +364,12 @@ console.log(activeResult.total); // 真实总数(如 100000)
|
|
|
362
364
|
```typescript
|
|
363
365
|
// 查询总数
|
|
364
366
|
const count = await befly.db.getCount({
|
|
365
|
-
table:
|
|
367
|
+
table: "user"
|
|
366
368
|
});
|
|
367
369
|
|
|
368
370
|
// 条件计数
|
|
369
371
|
const activeCount = await befly.db.getCount({
|
|
370
|
-
table:
|
|
372
|
+
table: "user",
|
|
371
373
|
where: { state: 1 }
|
|
372
374
|
});
|
|
373
375
|
```
|
|
@@ -378,8 +380,8 @@ const activeCount = await befly.db.getCount({
|
|
|
378
380
|
|
|
379
381
|
```typescript
|
|
380
382
|
const hasAdmin = await befly.db.exists({
|
|
381
|
-
table:
|
|
382
|
-
where: { roleCode:
|
|
383
|
+
table: "user",
|
|
384
|
+
where: { roleCode: "admin" }
|
|
383
385
|
});
|
|
384
386
|
|
|
385
387
|
if (hasAdmin) {
|
|
@@ -394,15 +396,15 @@ if (hasAdmin) {
|
|
|
394
396
|
```typescript
|
|
395
397
|
// 查询用户名
|
|
396
398
|
const username = await befly.db.getFieldValue({
|
|
397
|
-
table:
|
|
398
|
-
field:
|
|
399
|
+
table: "user",
|
|
400
|
+
field: "username",
|
|
399
401
|
where: { id: 1 }
|
|
400
402
|
});
|
|
401
403
|
|
|
402
404
|
// 查询余额
|
|
403
405
|
const balance = await befly.db.getFieldValue<number>({
|
|
404
|
-
table:
|
|
405
|
-
field:
|
|
406
|
+
table: "account",
|
|
407
|
+
field: "balance",
|
|
406
408
|
where: { userId: 1 }
|
|
407
409
|
});
|
|
408
410
|
```
|
|
@@ -436,24 +438,24 @@ interface InsertOptions {
|
|
|
436
438
|
```typescript
|
|
437
439
|
// 插入用户
|
|
438
440
|
const userId = await befly.db.insData({
|
|
439
|
-
table:
|
|
441
|
+
table: "user",
|
|
440
442
|
data: {
|
|
441
|
-
username:
|
|
442
|
-
email:
|
|
443
|
+
username: "john",
|
|
444
|
+
email: "john@example.com",
|
|
443
445
|
password: hashedPassword,
|
|
444
|
-
|
|
446
|
+
categoryId: 1
|
|
445
447
|
}
|
|
446
448
|
});
|
|
447
449
|
// 返回新记录的 ID
|
|
448
450
|
|
|
449
451
|
// 系统字段不可覆盖
|
|
450
452
|
await befly.db.insData({
|
|
451
|
-
table:
|
|
453
|
+
table: "user",
|
|
452
454
|
data: {
|
|
453
455
|
id: 999, // ❌ 会被忽略,自动生成
|
|
454
456
|
createdAt: 0, // ❌ 会被忽略,自动生成
|
|
455
457
|
state: 2, // ❌ 会被忽略,强制设为 1
|
|
456
|
-
username:
|
|
458
|
+
username: "john" // ✅ 正常写入
|
|
457
459
|
}
|
|
458
460
|
});
|
|
459
461
|
```
|
|
@@ -464,10 +466,10 @@ await befly.db.insData({
|
|
|
464
466
|
|
|
465
467
|
```typescript
|
|
466
468
|
// 批量插入
|
|
467
|
-
const ids = await befly.db.insBatch(
|
|
468
|
-
{ username:
|
|
469
|
-
{ username:
|
|
470
|
-
{ username:
|
|
469
|
+
const ids = await befly.db.insBatch("user", [
|
|
470
|
+
{ username: "user1", email: "user1@example.com" },
|
|
471
|
+
{ username: "user2", email: "user2@example.com" },
|
|
472
|
+
{ username: "user3", email: "user3@example.com" }
|
|
471
473
|
]);
|
|
472
474
|
// 返回: [id1, id2, id3]
|
|
473
475
|
```
|
|
@@ -489,10 +491,10 @@ interface UpdateOptions {
|
|
|
489
491
|
```typescript
|
|
490
492
|
// 更新用户
|
|
491
493
|
const affected = await befly.db.updData({
|
|
492
|
-
table:
|
|
494
|
+
table: "user",
|
|
493
495
|
data: {
|
|
494
|
-
nickname:
|
|
495
|
-
email:
|
|
496
|
+
nickname: "新昵称",
|
|
497
|
+
email: "new@example.com"
|
|
496
498
|
},
|
|
497
499
|
where: { id: 1 }
|
|
498
500
|
});
|
|
@@ -500,9 +502,9 @@ const affected = await befly.db.updData({
|
|
|
500
502
|
|
|
501
503
|
// 批量更新
|
|
502
504
|
await befly.db.updData({
|
|
503
|
-
table:
|
|
505
|
+
table: "user",
|
|
504
506
|
data: { state: 2 },
|
|
505
|
-
where: {
|
|
507
|
+
where: { status: 5 }
|
|
506
508
|
});
|
|
507
509
|
```
|
|
508
510
|
|
|
@@ -519,7 +521,7 @@ await befly.db.updData({
|
|
|
519
521
|
```typescript
|
|
520
522
|
// 软删除
|
|
521
523
|
const affected = await befly.db.delData({
|
|
522
|
-
table:
|
|
524
|
+
table: "user",
|
|
523
525
|
where: { id: 1 }
|
|
524
526
|
});
|
|
525
527
|
```
|
|
@@ -531,7 +533,7 @@ const affected = await befly.db.delData({
|
|
|
531
533
|
```typescript
|
|
532
534
|
// 硬删除
|
|
533
535
|
const affected = await befly.db.delForce({
|
|
534
|
-
table:
|
|
536
|
+
table: "temp_data",
|
|
535
537
|
where: { expiredAt$lt: Date.now() }
|
|
536
538
|
});
|
|
537
539
|
```
|
|
@@ -545,7 +547,7 @@ const affected = await befly.db.delForce({
|
|
|
545
547
|
```typescript
|
|
546
548
|
// 禁用用户
|
|
547
549
|
await befly.db.disableData({
|
|
548
|
-
table:
|
|
550
|
+
table: "user",
|
|
549
551
|
where: { id: 1 }
|
|
550
552
|
});
|
|
551
553
|
```
|
|
@@ -557,7 +559,7 @@ await befly.db.disableData({
|
|
|
557
559
|
```typescript
|
|
558
560
|
// 启用用户
|
|
559
561
|
await befly.db.enableData({
|
|
560
|
-
table:
|
|
562
|
+
table: "user",
|
|
561
563
|
where: { id: 1 }
|
|
562
564
|
});
|
|
563
565
|
```
|
|
@@ -572,10 +574,10 @@ await befly.db.enableData({
|
|
|
572
574
|
|
|
573
575
|
```typescript
|
|
574
576
|
// 阅读数 +1
|
|
575
|
-
await befly.db.increment(
|
|
577
|
+
await befly.db.increment("article", "viewCount", { id: 1 });
|
|
576
578
|
|
|
577
579
|
// 阅读数 +10
|
|
578
|
-
await befly.db.increment(
|
|
580
|
+
await befly.db.increment("article", "viewCount", { id: 1 }, 10);
|
|
579
581
|
```
|
|
580
582
|
|
|
581
583
|
### decrement - 自减
|
|
@@ -584,10 +586,10 @@ await befly.db.increment('article', 'viewCount', { id: 1 }, 10);
|
|
|
584
586
|
|
|
585
587
|
```typescript
|
|
586
588
|
// 库存 -1
|
|
587
|
-
await befly.db.decrement(
|
|
589
|
+
await befly.db.decrement("product", "stock", { id: 1 });
|
|
588
590
|
|
|
589
591
|
// 余额 -100
|
|
590
|
-
await befly.db.decrement(
|
|
592
|
+
await befly.db.decrement("account", "balance", { userId: 1 }, 100);
|
|
591
593
|
```
|
|
592
594
|
|
|
593
595
|
---
|
|
@@ -600,14 +602,14 @@ await befly.db.decrement('account', 'balance', { userId: 1 }, 100);
|
|
|
600
602
|
// 转账示例
|
|
601
603
|
const result = await befly.db.trans(async (tx) => {
|
|
602
604
|
// 扣除转出方余额
|
|
603
|
-
await tx.decrement(
|
|
605
|
+
await tx.decrement("account", "balance", { userId: 1 }, 100);
|
|
604
606
|
|
|
605
607
|
// 增加转入方余额
|
|
606
|
-
await tx.increment(
|
|
608
|
+
await tx.increment("account", "balance", { userId: 2 }, 100);
|
|
607
609
|
|
|
608
610
|
// 记录转账日志
|
|
609
611
|
await tx.insData({
|
|
610
|
-
table:
|
|
612
|
+
table: "transfer_log",
|
|
611
613
|
data: {
|
|
612
614
|
fromUserId: 1,
|
|
613
615
|
toUserId: 2,
|
|
@@ -620,9 +622,9 @@ const result = await befly.db.trans(async (tx) => {
|
|
|
620
622
|
|
|
621
623
|
// 事务中抛出异常会自动回滚
|
|
622
624
|
await befly.db.trans(async (tx) => {
|
|
623
|
-
await tx.updData({ table:
|
|
625
|
+
await tx.updData({ table: "user", data: { balance: 0 }, where: { id: 1 } });
|
|
624
626
|
|
|
625
|
-
throw new Error(
|
|
627
|
+
throw new Error("业务校验失败"); // 自动回滚
|
|
626
628
|
});
|
|
627
629
|
```
|
|
628
630
|
|
|
@@ -639,23 +641,23 @@ DbHelper 的查询方法(getOne、getList、getAll、getCount)支持通过 `
|
|
|
639
641
|
```typescript
|
|
640
642
|
// 单条联查
|
|
641
643
|
const order = await befly.db.getOne({
|
|
642
|
-
table:
|
|
643
|
-
joins: [{ table:
|
|
644
|
-
fields: [
|
|
645
|
-
where: {
|
|
644
|
+
table: "order",
|
|
645
|
+
joins: [{ table: "user", on: "order.user_id = user.id" }],
|
|
646
|
+
fields: ["order.id", "order.totalAmount", "order.status", "user.username", "user.nickname"],
|
|
647
|
+
where: { "order.id": orderId }
|
|
646
648
|
});
|
|
647
649
|
// 返回: { id: 1, totalAmount: 100, status: 'paid', username: 'john', nickname: '张三' }
|
|
648
650
|
|
|
649
651
|
// 分页联查
|
|
650
652
|
const result = await befly.db.getList({
|
|
651
|
-
table:
|
|
653
|
+
table: "order",
|
|
652
654
|
joins: [
|
|
653
|
-
{ table:
|
|
654
|
-
{ table:
|
|
655
|
+
{ table: "user", on: "order.userId = user.id" },
|
|
656
|
+
{ table: "product", on: "order.productId = product.id" }
|
|
655
657
|
],
|
|
656
|
-
fields: [
|
|
657
|
-
where: {
|
|
658
|
-
orderBy: [
|
|
658
|
+
fields: ["order.id", "order.totalAmount", "user.username", "product.name AS productName"],
|
|
659
|
+
where: { "order.status": "paid" },
|
|
660
|
+
orderBy: ["order.createdAt#DESC"],
|
|
659
661
|
page: 1,
|
|
660
662
|
limit: 10
|
|
661
663
|
});
|
|
@@ -663,18 +665,18 @@ const result = await befly.db.getList({
|
|
|
663
665
|
|
|
664
666
|
// 联查计数
|
|
665
667
|
const count = await befly.db.getCount({
|
|
666
|
-
table:
|
|
667
|
-
joins: [{ table:
|
|
668
|
-
where: {
|
|
668
|
+
table: "order",
|
|
669
|
+
joins: [{ table: "user", on: "order.userId = user.id" }],
|
|
670
|
+
where: { "order.state": 1, "user.state": 1 }
|
|
669
671
|
});
|
|
670
672
|
|
|
671
673
|
// 联查全部
|
|
672
674
|
const allOrders = await befly.db.getAll({
|
|
673
|
-
table:
|
|
674
|
-
joins: [{ table:
|
|
675
|
-
fields: [
|
|
676
|
-
where: {
|
|
677
|
-
orderBy: [
|
|
675
|
+
table: "order",
|
|
676
|
+
joins: [{ table: "user", on: "order.userId = user.id" }],
|
|
677
|
+
fields: ["order.id", "user.username"],
|
|
678
|
+
where: { "order.state": 1 },
|
|
679
|
+
orderBy: ["order.id#DESC"]
|
|
678
680
|
});
|
|
679
681
|
// 返回: { lists: [...最多10000条], total: 真实总数 }
|
|
680
682
|
```
|
|
@@ -684,7 +686,7 @@ const allOrders = await befly.db.getAll({
|
|
|
684
686
|
```typescript
|
|
685
687
|
interface JoinOption {
|
|
686
688
|
/** JOIN 类型:'left' | 'right' | 'inner',默认 'left' */
|
|
687
|
-
type?:
|
|
689
|
+
type?: "left" | "right" | "inner";
|
|
688
690
|
/** 表名(不支持别名) */
|
|
689
691
|
table: string;
|
|
690
692
|
/** JOIN 条件(如 'order.user_id = user.id') */
|
|
@@ -696,10 +698,10 @@ interface JoinOption {
|
|
|
696
698
|
|
|
697
699
|
```typescript
|
|
698
700
|
joins: [
|
|
699
|
-
{ table:
|
|
700
|
-
{ type:
|
|
701
|
-
{ type:
|
|
702
|
-
{ type:
|
|
701
|
+
{ table: "user", on: "order.userId = user.id" }, // LEFT JOIN(默认)
|
|
702
|
+
{ type: "left", table: "product", on: "order.productId = product.id" }, // LEFT JOIN
|
|
703
|
+
{ type: "inner", table: "category", on: "product.categoryId = category.id" }, // INNER JOIN
|
|
704
|
+
{ type: "right", table: "warehouse", on: "product.warehouseId = warehouse.id" } // RIGHT JOIN
|
|
703
705
|
];
|
|
704
706
|
```
|
|
705
707
|
|
|
@@ -714,43 +716,43 @@ joins: [
|
|
|
714
716
|
|
|
715
717
|
```typescript
|
|
716
718
|
export default {
|
|
717
|
-
name:
|
|
719
|
+
name: "订单列表",
|
|
718
720
|
fields: {
|
|
719
|
-
keyword: { name:
|
|
720
|
-
status: { name:
|
|
721
|
+
keyword: { name: "关键词", type: "string", max: 50 },
|
|
722
|
+
status: { name: "状态", type: "string" },
|
|
721
723
|
page: Fields.page,
|
|
722
724
|
limit: Fields.limit
|
|
723
725
|
},
|
|
724
726
|
handler: async (befly, ctx) => {
|
|
725
727
|
const where: any = {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
+
"order.state": 1,
|
|
729
|
+
"user.state": 1
|
|
728
730
|
};
|
|
729
731
|
|
|
730
732
|
// 关键词搜索
|
|
731
733
|
if (ctx.body.keyword) {
|
|
732
|
-
where.$or = [{
|
|
734
|
+
where.$or = [{ "user.username$like": `%${ctx.body.keyword}%` }, { "user.nickname$like": `%${ctx.body.keyword}%` }, { "product.name$like": `%${ctx.body.keyword}%` }];
|
|
733
735
|
}
|
|
734
736
|
|
|
735
737
|
// 状态过滤
|
|
736
738
|
if (ctx.body.status) {
|
|
737
|
-
where[
|
|
739
|
+
where["order.status"] = ctx.body.status;
|
|
738
740
|
}
|
|
739
741
|
|
|
740
742
|
const result = await befly.db.getList({
|
|
741
|
-
table:
|
|
743
|
+
table: "order",
|
|
742
744
|
joins: [
|
|
743
|
-
{ table:
|
|
744
|
-
{ table:
|
|
745
|
+
{ table: "user", on: "order.userId = user.id" },
|
|
746
|
+
{ table: "product", on: "order.productId = product.id" }
|
|
745
747
|
],
|
|
746
|
-
fields: [
|
|
748
|
+
fields: ["order.id", "order.quantity", "order.totalAmount", "order.status", "order.createdAt", "user.username", "user.nickname", "product.name AS productName", "product.price AS productPrice"],
|
|
747
749
|
where: where,
|
|
748
|
-
orderBy: [
|
|
750
|
+
orderBy: ["order.createdAt#DESC"],
|
|
749
751
|
page: ctx.body.page,
|
|
750
752
|
limit: ctx.body.limit
|
|
751
753
|
});
|
|
752
754
|
|
|
753
|
-
return befly.tool.Yes(
|
|
755
|
+
return befly.tool.Yes("查询成功", result);
|
|
754
756
|
}
|
|
755
757
|
};
|
|
756
758
|
```
|
|
@@ -778,7 +780,7 @@ const usersWithOrderCount = await befly.db.query(
|
|
|
778
780
|
);
|
|
779
781
|
|
|
780
782
|
// 需要手动转换字段名
|
|
781
|
-
import { arrayKeysToCamel } from
|
|
783
|
+
import { arrayKeysToCamel } from "befly/lib/arrayKeysToCamel";
|
|
782
784
|
const list = arrayKeysToCamel(usersWithOrderCount);
|
|
783
785
|
```
|
|
784
786
|
|
|
@@ -794,8 +796,8 @@ where: { id: 1 }
|
|
|
794
796
|
// → WHERE id = 1
|
|
795
797
|
|
|
796
798
|
// 多条件(AND)
|
|
797
|
-
where: { state: 1,
|
|
798
|
-
// → WHERE state = 1 AND
|
|
799
|
+
where: { state: 1, status: 2 }
|
|
800
|
+
// → WHERE state = 1 AND status = 2
|
|
799
801
|
```
|
|
800
802
|
|
|
801
803
|
### 比较操作符
|
|
@@ -832,7 +834,7 @@ where: {
|
|
|
832
834
|
```typescript
|
|
833
835
|
// 用户名或邮箱匹配
|
|
834
836
|
where: {
|
|
835
|
-
$or: [{ username:
|
|
837
|
+
$or: [{ username: "admin" }, { email: "admin@example.com" }];
|
|
836
838
|
}
|
|
837
839
|
// → WHERE (username = 'admin' OR email = 'admin@example.com')
|
|
838
840
|
```
|
|
@@ -842,9 +844,9 @@ where: {
|
|
|
842
844
|
```typescript
|
|
843
845
|
// 显式 AND(通常不需要,多条件默认就是 AND)
|
|
844
846
|
where: {
|
|
845
|
-
$and: [{ state: 1 }, {
|
|
847
|
+
$and: [{ state: 1 }, { status: 2 }];
|
|
846
848
|
}
|
|
847
|
-
// → WHERE state = 1 AND
|
|
849
|
+
// → WHERE state = 1 AND status = 2
|
|
848
850
|
```
|
|
849
851
|
|
|
850
852
|
**组合使用**
|
|
@@ -867,12 +869,12 @@ where: {
|
|
|
867
869
|
|
|
868
870
|
```typescript
|
|
869
871
|
where: {
|
|
870
|
-
|
|
872
|
+
categoryId$in: [1, 2, 3];
|
|
871
873
|
}
|
|
872
|
-
// → WHERE
|
|
874
|
+
// → WHERE category_id IN (1, 2, 3)
|
|
873
875
|
|
|
874
876
|
where: {
|
|
875
|
-
status$in: [
|
|
877
|
+
status$in: ["pending", "processing"];
|
|
876
878
|
}
|
|
877
879
|
// → WHERE status IN ('pending', 'processing')
|
|
878
880
|
```
|
|
@@ -936,19 +938,19 @@ where: {
|
|
|
936
938
|
```typescript
|
|
937
939
|
// 包含
|
|
938
940
|
where: {
|
|
939
|
-
username$like:
|
|
941
|
+
username$like: "%admin%";
|
|
940
942
|
}
|
|
941
943
|
// → WHERE username LIKE '%admin%'
|
|
942
944
|
|
|
943
945
|
// 以...开头
|
|
944
946
|
where: {
|
|
945
|
-
email$like:
|
|
947
|
+
email$like: "test%";
|
|
946
948
|
}
|
|
947
949
|
// → WHERE email LIKE 'test%'
|
|
948
950
|
|
|
949
951
|
// 以...结尾
|
|
950
952
|
where: {
|
|
951
|
-
phone$like:
|
|
953
|
+
phone$like: "%1234";
|
|
952
954
|
}
|
|
953
955
|
// → WHERE phone LIKE '%1234'
|
|
954
956
|
```
|
|
@@ -957,7 +959,7 @@ where: {
|
|
|
957
959
|
|
|
958
960
|
```typescript
|
|
959
961
|
where: {
|
|
960
|
-
username$notLike:
|
|
962
|
+
username$notLike: "%test%";
|
|
961
963
|
}
|
|
962
964
|
// → WHERE username NOT LIKE '%test%'
|
|
963
965
|
```
|
|
@@ -978,7 +980,7 @@ fields: undefined;
|
|
|
978
980
|
### 指定字段
|
|
979
981
|
|
|
980
982
|
```typescript
|
|
981
|
-
fields: [
|
|
983
|
+
fields: ["id", "username", "email"];
|
|
982
984
|
// → SELECT id, username, email
|
|
983
985
|
```
|
|
984
986
|
|
|
@@ -987,7 +989,7 @@ fields: ['id', 'username', 'email'];
|
|
|
987
989
|
使用 `!` 前缀排除字段(查询除指定字段外的所有字段)。
|
|
988
990
|
|
|
989
991
|
```typescript
|
|
990
|
-
fields: [
|
|
992
|
+
fields: ["!password", "!token", "!salt"];
|
|
991
993
|
// → SELECT id, username, email, created_at, ... (除了 password, token, salt)
|
|
992
994
|
```
|
|
993
995
|
|
|
@@ -1001,11 +1003,11 @@ fields: ['!password', '!token', '!salt'];
|
|
|
1001
1003
|
|
|
1002
1004
|
```typescript
|
|
1003
1005
|
// 单字段排序
|
|
1004
|
-
orderBy: [
|
|
1006
|
+
orderBy: ["createdAt#DESC"];
|
|
1005
1007
|
// → ORDER BY created_at DESC
|
|
1006
1008
|
|
|
1007
1009
|
// 多字段排序
|
|
1008
|
-
orderBy: [
|
|
1010
|
+
orderBy: ["sort#ASC", "id#DESC"];
|
|
1009
1011
|
// → ORDER BY sort ASC, id DESC
|
|
1010
1012
|
```
|
|
1011
1013
|
|
|
@@ -1047,19 +1049,19 @@ orderBy: ['sort#ASC', 'id#DESC'];
|
|
|
1047
1049
|
|
|
1048
1050
|
```typescript
|
|
1049
1051
|
// 默认查询:state > 0,包含正常和禁用数据
|
|
1050
|
-
getOne({ table:
|
|
1052
|
+
getOne({ table: "user", where: { id: 1 } });
|
|
1051
1053
|
// → WHERE id = 1 AND state > 0
|
|
1052
1054
|
|
|
1053
1055
|
// 只查询正常状态的数据
|
|
1054
|
-
getList({ table:
|
|
1056
|
+
getList({ table: "user", where: { state: 1 } });
|
|
1055
1057
|
// → WHERE state = 1
|
|
1056
1058
|
|
|
1057
1059
|
// 只查询禁用状态的数据
|
|
1058
|
-
getList({ table:
|
|
1060
|
+
getList({ table: "user", where: { state: 2 } });
|
|
1059
1061
|
// → WHERE state = 2
|
|
1060
1062
|
|
|
1061
1063
|
// 查询所有状态(包括软删除)
|
|
1062
|
-
getOne({ table:
|
|
1064
|
+
getOne({ table: "user", where: { id: 1, state$gte: 0 } });
|
|
1063
1065
|
// → WHERE id = 1 AND state >= 0
|
|
1064
1066
|
```
|
|
1065
1067
|
|
|
@@ -1072,10 +1074,10 @@ getOne({ table: 'user', where: { id: 1, state$gte: 0 } });
|
|
|
1072
1074
|
```typescript
|
|
1073
1075
|
// 用户列表
|
|
1074
1076
|
export default {
|
|
1075
|
-
name:
|
|
1077
|
+
name: "用户列表",
|
|
1076
1078
|
fields: {
|
|
1077
|
-
keyword: { name:
|
|
1078
|
-
|
|
1079
|
+
keyword: { name: "关键词", type: "string", max: 50 },
|
|
1080
|
+
departmentId: { name: "部门ID", type: "number" },
|
|
1079
1081
|
page: Fields.page,
|
|
1080
1082
|
limit: Fields.limit
|
|
1081
1083
|
},
|
|
@@ -1087,21 +1089,21 @@ export default {
|
|
|
1087
1089
|
where.$or = [{ username$like: `%${ctx.body.keyword}%` }, { nickname$like: `%${ctx.body.keyword}%` }, { email$like: `%${ctx.body.keyword}%` }];
|
|
1088
1090
|
}
|
|
1089
1091
|
|
|
1090
|
-
//
|
|
1091
|
-
if (ctx.body.
|
|
1092
|
-
where.
|
|
1092
|
+
// 部门过滤
|
|
1093
|
+
if (ctx.body.departmentId) {
|
|
1094
|
+
where.departmentId = ctx.body.departmentId;
|
|
1093
1095
|
}
|
|
1094
1096
|
|
|
1095
1097
|
const result = await befly.db.getList({
|
|
1096
|
-
table:
|
|
1097
|
-
fields: [
|
|
1098
|
+
table: "user",
|
|
1099
|
+
fields: ["!password", "!token"],
|
|
1098
1100
|
where: where,
|
|
1099
|
-
orderBy: [
|
|
1101
|
+
orderBy: ["createdAt#DESC"],
|
|
1100
1102
|
page: ctx.body.page,
|
|
1101
1103
|
limit: ctx.body.limit
|
|
1102
1104
|
});
|
|
1103
1105
|
|
|
1104
|
-
return befly.tool.Yes(
|
|
1106
|
+
return befly.tool.Yes("查询成功", result);
|
|
1105
1107
|
}
|
|
1106
1108
|
};
|
|
1107
1109
|
```
|
|
@@ -1110,47 +1112,47 @@ export default {
|
|
|
1110
1112
|
|
|
1111
1113
|
```typescript
|
|
1112
1114
|
export default {
|
|
1113
|
-
name:
|
|
1115
|
+
name: "创建订单",
|
|
1114
1116
|
fields: {
|
|
1115
|
-
productId: { name:
|
|
1116
|
-
quantity: { name:
|
|
1117
|
+
productId: { name: "商品ID", type: "number" },
|
|
1118
|
+
quantity: { name: "数量", type: "number", min: 1 }
|
|
1117
1119
|
},
|
|
1118
|
-
required: [
|
|
1120
|
+
required: ["productId", "quantity"],
|
|
1119
1121
|
handler: async (befly, ctx) => {
|
|
1120
1122
|
const result = await befly.db.trans(async (tx) => {
|
|
1121
1123
|
// 1. 查询商品
|
|
1122
1124
|
const product = await tx.getOne({
|
|
1123
|
-
table:
|
|
1125
|
+
table: "product",
|
|
1124
1126
|
where: { id: ctx.body.productId }
|
|
1125
1127
|
});
|
|
1126
1128
|
|
|
1127
1129
|
if (!product) {
|
|
1128
|
-
throw new Error(
|
|
1130
|
+
throw new Error("商品不存在");
|
|
1129
1131
|
}
|
|
1130
1132
|
|
|
1131
1133
|
if (product.stock < ctx.body.quantity) {
|
|
1132
|
-
throw new Error(
|
|
1134
|
+
throw new Error("库存不足");
|
|
1133
1135
|
}
|
|
1134
1136
|
|
|
1135
1137
|
// 2. 扣减库存
|
|
1136
|
-
await tx.decrement(
|
|
1138
|
+
await tx.decrement("product", "stock", { id: ctx.body.productId }, ctx.body.quantity);
|
|
1137
1139
|
|
|
1138
1140
|
// 3. 创建订单
|
|
1139
1141
|
const orderId = await tx.insData({
|
|
1140
|
-
table:
|
|
1142
|
+
table: "order",
|
|
1141
1143
|
data: {
|
|
1142
1144
|
userId: ctx.user.id,
|
|
1143
1145
|
productId: ctx.body.productId,
|
|
1144
1146
|
quantity: ctx.body.quantity,
|
|
1145
1147
|
totalAmount: product.price * ctx.body.quantity,
|
|
1146
|
-
status:
|
|
1148
|
+
status: "pending"
|
|
1147
1149
|
}
|
|
1148
1150
|
});
|
|
1149
1151
|
|
|
1150
1152
|
return { orderId: orderId };
|
|
1151
1153
|
});
|
|
1152
1154
|
|
|
1153
|
-
return befly.tool.Yes(
|
|
1155
|
+
return befly.tool.Yes("订单创建成功", result);
|
|
1154
1156
|
}
|
|
1155
1157
|
};
|
|
1156
1158
|
```
|
|
@@ -1160,14 +1162,14 @@ export default {
|
|
|
1160
1162
|
```typescript
|
|
1161
1163
|
// 查询最近7天内,状态为正常或待审核的文章
|
|
1162
1164
|
const articles = await befly.db.getList({
|
|
1163
|
-
table:
|
|
1164
|
-
fields: [
|
|
1165
|
+
table: "article",
|
|
1166
|
+
fields: ["id", "title", "authorId", "viewCount", "createdAt"],
|
|
1165
1167
|
where: {
|
|
1166
1168
|
createdAt$gte: Date.now() - 7 * 24 * 60 * 60 * 1000,
|
|
1167
|
-
$or: [{ status:
|
|
1169
|
+
$or: [{ status: "published" }, { status: "pending" }],
|
|
1168
1170
|
categoryId$in: [1, 2, 3]
|
|
1169
1171
|
},
|
|
1170
|
-
orderBy: [
|
|
1172
|
+
orderBy: ["viewCount#DESC", "createdAt#DESC"],
|
|
1171
1173
|
page: 1,
|
|
1172
1174
|
limit: 20
|
|
1173
1175
|
});
|