befly 3.9.38 → 3.9.40

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.
Files changed (155) hide show
  1. package/README.md +37 -38
  2. package/befly.config.ts +62 -40
  3. package/checks/checkApi.ts +16 -16
  4. package/checks/checkApp.ts +19 -25
  5. package/checks/checkTable.ts +42 -42
  6. package/docs/README.md +42 -35
  7. package/docs/{api.md → api/api.md} +223 -231
  8. package/docs/cipher.md +71 -69
  9. package/docs/database.md +143 -141
  10. package/docs/{examples.md → guide/examples.md} +181 -181
  11. package/docs/guide/quickstart.md +331 -0
  12. package/docs/hooks/auth.md +38 -0
  13. package/docs/hooks/cors.md +28 -0
  14. package/docs/{hook.md → hooks/hook.md} +140 -57
  15. package/docs/hooks/parser.md +19 -0
  16. package/docs/hooks/rateLimit.md +47 -0
  17. package/docs/{redis.md → infra/redis.md} +84 -93
  18. package/docs/plugins/cipher.md +61 -0
  19. package/docs/plugins/database.md +128 -0
  20. package/docs/{plugin.md → plugins/plugin.md} +83 -81
  21. package/docs/quickstart.md +26 -26
  22. package/docs/{addon.md → reference/addon.md} +46 -46
  23. package/docs/{config.md → reference/config.md} +32 -80
  24. package/docs/{logger.md → reference/logger.md} +52 -52
  25. package/docs/{sync.md → reference/sync.md} +32 -35
  26. package/docs/{table.md → reference/table.md} +1 -1
  27. package/docs/{validator.md → reference/validator.md} +57 -57
  28. package/hooks/auth.ts +8 -4
  29. package/hooks/cors.ts +13 -13
  30. package/hooks/parser.ts +37 -17
  31. package/hooks/permission.ts +26 -14
  32. package/hooks/rateLimit.ts +276 -0
  33. package/hooks/validator.ts +8 -8
  34. package/lib/asyncContext.ts +43 -0
  35. package/lib/cacheHelper.ts +212 -77
  36. package/lib/cacheKeys.ts +38 -0
  37. package/lib/cipher.ts +30 -30
  38. package/lib/connect.ts +28 -28
  39. package/lib/dbHelper.ts +183 -102
  40. package/lib/jwt.ts +16 -16
  41. package/lib/logger.ts +610 -19
  42. package/lib/redisHelper.ts +185 -44
  43. package/lib/sqlBuilder.ts +90 -91
  44. package/lib/validator.ts +59 -39
  45. package/loader/loadApis.ts +48 -44
  46. package/loader/loadHooks.ts +40 -14
  47. package/loader/loadPlugins.ts +16 -17
  48. package/main.ts +57 -47
  49. package/package.json +47 -45
  50. package/paths.ts +15 -14
  51. package/plugins/cache.ts +5 -4
  52. package/plugins/cipher.ts +3 -3
  53. package/plugins/config.ts +2 -2
  54. package/plugins/db.ts +9 -9
  55. package/plugins/jwt.ts +3 -3
  56. package/plugins/logger.ts +8 -12
  57. package/plugins/redis.ts +8 -8
  58. package/plugins/tool.ts +6 -6
  59. package/router/api.ts +85 -56
  60. package/router/static.ts +12 -12
  61. package/sync/syncAll.ts +12 -12
  62. package/sync/syncApi.ts +55 -52
  63. package/sync/syncDb/apply.ts +20 -19
  64. package/sync/syncDb/constants.ts +25 -23
  65. package/sync/syncDb/ddl.ts +35 -36
  66. package/sync/syncDb/helpers.ts +6 -9
  67. package/sync/syncDb/schema.ts +10 -9
  68. package/sync/syncDb/sqlite.ts +7 -8
  69. package/sync/syncDb/table.ts +37 -35
  70. package/sync/syncDb/tableCreate.ts +21 -20
  71. package/sync/syncDb/types.ts +23 -20
  72. package/sync/syncDb/version.ts +10 -10
  73. package/sync/syncDb.ts +43 -36
  74. package/sync/syncDev.ts +74 -65
  75. package/sync/syncMenu.ts +190 -55
  76. package/tests/api-integration-array-number.test.ts +282 -0
  77. package/tests/befly-config-env.test.ts +78 -0
  78. package/tests/cacheHelper.test.ts +135 -104
  79. package/tests/cacheKeys.test.ts +41 -0
  80. package/tests/cipher.test.ts +90 -89
  81. package/tests/dbHelper-advanced.test.ts +140 -134
  82. package/tests/dbHelper-all-array-types.test.ts +316 -0
  83. package/tests/dbHelper-array-serialization.test.ts +258 -0
  84. package/tests/dbHelper-columns.test.ts +56 -55
  85. package/tests/dbHelper-execute.test.ts +45 -44
  86. package/tests/dbHelper-joins.test.ts +124 -119
  87. package/tests/fields-redis-cache.test.ts +29 -27
  88. package/tests/fields-validate.test.ts +38 -38
  89. package/tests/getClientIp.test.ts +54 -0
  90. package/tests/integration.test.ts +69 -67
  91. package/tests/jwt.test.ts +27 -26
  92. package/tests/logger.test.ts +267 -34
  93. package/tests/rateLimit-hook.test.ts +477 -0
  94. package/tests/redisHelper.test.ts +187 -188
  95. package/tests/redisKeys.test.ts +6 -73
  96. package/tests/scanConfig.test.ts +144 -0
  97. package/tests/sqlBuilder-advanced.test.ts +217 -215
  98. package/tests/sqlBuilder.test.ts +92 -91
  99. package/tests/sync-connection.test.ts +29 -29
  100. package/tests/syncDb-apply.test.ts +97 -96
  101. package/tests/syncDb-array-number.test.ts +160 -0
  102. package/tests/syncDb-constants.test.ts +48 -47
  103. package/tests/syncDb-ddl.test.ts +99 -98
  104. package/tests/syncDb-helpers.test.ts +29 -28
  105. package/tests/syncDb-schema.test.ts +61 -60
  106. package/tests/syncDb-types.test.ts +60 -59
  107. package/tests/syncMenu-paths.test.ts +68 -0
  108. package/tests/util.test.ts +42 -41
  109. package/tests/validator-array-number.test.ts +310 -0
  110. package/tests/validator-default.test.ts +373 -0
  111. package/tests/validator.test.ts +271 -266
  112. package/tsconfig.json +4 -5
  113. package/types/api.d.ts +7 -12
  114. package/types/befly.d.ts +60 -13
  115. package/types/cache.d.ts +8 -4
  116. package/types/common.d.ts +17 -9
  117. package/types/context.d.ts +2 -2
  118. package/types/crypto.d.ts +23 -0
  119. package/types/database.d.ts +19 -19
  120. package/types/hook.d.ts +2 -2
  121. package/types/jwt.d.ts +118 -0
  122. package/types/logger.d.ts +30 -0
  123. package/types/plugin.d.ts +4 -4
  124. package/types/redis.d.ts +7 -3
  125. package/types/roleApisCache.ts +23 -0
  126. package/types/sync.d.ts +10 -10
  127. package/types/table.d.ts +50 -9
  128. package/types/validate.d.ts +69 -0
  129. package/utils/addonHelper.ts +90 -0
  130. package/utils/arrayKeysToCamel.ts +18 -0
  131. package/utils/calcPerfTime.ts +13 -0
  132. package/utils/configTypes.ts +3 -0
  133. package/utils/cors.ts +19 -0
  134. package/utils/fieldClear.ts +75 -0
  135. package/utils/genShortId.ts +12 -0
  136. package/utils/getClientIp.ts +45 -0
  137. package/utils/keysToCamel.ts +22 -0
  138. package/utils/keysToSnake.ts +22 -0
  139. package/utils/modules.ts +98 -0
  140. package/utils/pickFields.ts +19 -0
  141. package/utils/process.ts +56 -0
  142. package/utils/regex.ts +225 -0
  143. package/utils/response.ts +115 -0
  144. package/utils/route.ts +23 -0
  145. package/utils/scanConfig.ts +142 -0
  146. package/utils/scanFiles.ts +48 -0
  147. package/.prettierignore +0 -2
  148. package/.prettierrc +0 -12
  149. package/docs/1-/345/237/272/346/234/254/344/273/213/347/273/215.md +0 -35
  150. package/docs/2-/345/210/235/346/255/245/344/275/223/351/252/214.md +0 -64
  151. package/docs/3-/347/254/254/344/270/200/344/270/252/346/216/245/345/217/243.md +0 -46
  152. package/docs/4-/346/223/215/344/275/234/346/225/260/346/215/256/345/272/223.md +0 -172
  153. package/hooks/requestLogger.ts +0 -84
  154. package/types/index.ts +0 -24
  155. package/util.ts +0 -283
@@ -123,24 +123,23 @@ addonAdmin/apis/
123
123
  ### 基础结构
124
124
 
125
125
  ```typescript
126
- import type { ApiRoute } from 'befly/types/api';
126
+ import type { ApiRoute } from "befly/types/api";
127
127
 
128
128
  export default {
129
129
  // 必填字段
130
- name: '接口名称', // 接口描述,用于日志和文档
130
+ name: "接口名称", // 接口描述,用于日志和文档
131
131
  handler: async (befly, ctx) => {
132
132
  // 处理逻辑
133
- return befly.tool.Yes('成功', { data });
133
+ return befly.tool.Yes("成功", { data });
134
134
  },
135
135
 
136
136
  // 可选字段
137
- method: 'POST', // HTTP 方法,默认 POST
137
+ method: "POST", // HTTP 方法,默认 POST
138
138
  auth: true, // 是否需要认证,默认 true
139
139
  fields: {}, // 字段定义(验证规则)
140
140
  required: [], // 必填字段列表
141
- rawBody: false, // 是否保留原始请求体
142
- cache: undefined, // 缓存时间(秒)
143
- rateLimit: undefined // 限流配置
141
+ rawBody: false // 是否保留原始请求体
142
+ // 缓存/限流:已统一迁移为 Hook 能力(见 hook 文档/配置),不再挂载在接口定义上
144
143
  } as ApiRoute;
145
144
  ```
146
145
 
@@ -155,7 +154,7 @@ interface ApiRoute<T = any, R = any> {
155
154
  handler: ApiHandler<T, R>;
156
155
 
157
156
  /** HTTP 方法(可选,默认 POST,支持逗号分隔多个方法) */
158
- method?: 'GET' | 'POST' | 'GET,POST' | 'POST,GET';
157
+ method?: "GET" | "POST" | "GET,POST" | "POST,GET";
159
158
 
160
159
  /** 认证类型(可选,默认 true)
161
160
  * - true: 需要登录
@@ -175,12 +174,6 @@ interface ApiRoute<T = any, R = any> {
175
174
  */
176
175
  rawBody?: boolean;
177
176
 
178
- /** 缓存配置(可选,单位:秒) */
179
- cache?: number;
180
-
181
- /** 限流配置(可选,格式:次数/秒,如 "10/60" 表示 60秒内10次) */
182
- rateLimit?: string;
183
-
184
177
  /** 路由路径(运行时生成,无需手动设置) */
185
178
  route?: string;
186
179
  }
@@ -318,26 +311,26 @@ befly.tool.Raw(ctx: RequestContext, data: Record<string, any> | string, options?
318
311
 
319
312
  ```typescript
320
313
  // JSON 响应(自动)
321
- return befly.tool.Raw(ctx, { code: 'SUCCESS', message: '成功' });
314
+ return befly.tool.Raw(ctx, { code: "SUCCESS", message: "成功" });
322
315
 
323
316
  // 纯文本响应(自动)- 支付宝回调
324
- return befly.tool.Raw(ctx, 'success');
317
+ return befly.tool.Raw(ctx, "success");
325
318
 
326
319
  // XML 响应(自动判断)
327
- return befly.tool.Raw(ctx, '<xml><return_code>SUCCESS</return_code></xml>');
320
+ return befly.tool.Raw(ctx, "<xml><return_code>SUCCESS</return_code></xml>");
328
321
 
329
322
  // XML 响应(手动指定)
330
- return befly.tool.Raw(ctx, xmlString, { contentType: 'application/xml' });
323
+ return befly.tool.Raw(ctx, xmlString, { contentType: "application/xml" });
331
324
 
332
325
  // 自定义状态码
333
- return befly.tool.Raw(ctx, { error: 'Not Found' }, { status: 404 });
326
+ return befly.tool.Raw(ctx, { error: "Not Found" }, { status: 404 });
334
327
 
335
328
  // 自定义响应头
336
329
  return befly.tool.Raw(
337
330
  ctx,
338
- { code: 'SUCCESS' },
331
+ { code: "SUCCESS" },
339
332
  {
340
- headers: { 'X-Custom-Header': 'value' }
333
+ headers: { "X-Custom-Header": "value" }
341
334
  }
342
335
  );
343
336
  ```
@@ -347,33 +340,33 @@ return befly.tool.Raw(
347
340
  ```typescript
348
341
  // 微信支付回调
349
342
  export default {
350
- name: '微信支付回调',
343
+ name: "微信支付回调",
351
344
  auth: false,
352
345
  rawBody: true,
353
346
  handler: async (befly, ctx) => {
354
347
  if (!befly.weixin) {
355
- return befly.tool.Raw(ctx, { code: 'SYSTEM_ERROR', message: 'weixin 插件未配置' });
348
+ return befly.tool.Raw(ctx, { code: "SYSTEM_ERROR", message: "weixin 插件未配置" });
356
349
  }
357
350
 
358
351
  // 处理成功
359
- return befly.tool.Raw(ctx, { code: 'SUCCESS', message: '' });
352
+ return befly.tool.Raw(ctx, { code: "SUCCESS", message: "" });
360
353
  }
361
354
  };
362
355
 
363
356
  // 支付宝回调
364
357
  export default {
365
- name: '支付宝回调',
358
+ name: "支付宝回调",
366
359
  auth: false,
367
360
  rawBody: true,
368
361
  handler: async (befly, ctx) => {
369
362
  // 支付宝要求返回纯文本 "success"
370
- return befly.tool.Raw(ctx, 'success');
363
+ return befly.tool.Raw(ctx, "success");
371
364
  }
372
365
  };
373
366
 
374
367
  // 微信公众号 XML 回调
375
368
  export default {
376
- name: '微信公众号回调',
369
+ name: "微信公众号回调",
377
370
  auth: false,
378
371
  rawBody: true,
379
372
  handler: async (befly, ctx) => {
@@ -397,10 +390,10 @@ export default {
397
390
  在 Hook 中使用,用于提前拦截请求:
398
391
 
399
392
  ```typescript
400
- import { ErrorResponse } from 'befly/util';
393
+ import { ErrorResponse } from "befly/utils/response";
401
394
 
402
395
  // 在 Hook 中使用
403
- ctx.response = ErrorResponse(ctx, '未授权', 1, null);
396
+ ctx.response = ErrorResponse(ctx, "未授权", 1, null);
404
397
  ```
405
398
 
406
399
  ### FinalResponse - 最终响应
@@ -419,33 +412,33 @@ ctx.response = ErrorResponse(ctx, '未授权', 1, null);
419
412
 
420
413
  ```typescript
421
414
  const PRESET_FIELDS = {
422
- '@id': {
423
- name: 'ID',
424
- type: 'number',
415
+ "@id": {
416
+ name: "ID",
417
+ type: "number",
425
418
  min: 1,
426
419
  max: null
427
420
  },
428
- '@page': {
429
- name: '页码',
430
- type: 'number',
421
+ "@page": {
422
+ name: "页码",
423
+ type: "number",
431
424
  min: 1,
432
425
  max: 9999
433
426
  },
434
- '@limit': {
435
- name: '每页数量',
436
- type: 'number',
427
+ "@limit": {
428
+ name: "每页数量",
429
+ type: "number",
437
430
  min: 1,
438
431
  max: 100
439
432
  },
440
- '@keyword': {
441
- name: '关键词',
442
- type: 'string',
433
+ "@keyword": {
434
+ name: "关键词",
435
+ type: "string",
443
436
  min: 1,
444
437
  max: 50
445
438
  },
446
- '@state': {
447
- name: '状态',
448
- type: 'number',
439
+ "@state": {
440
+ name: "状态",
441
+ type: "number",
449
442
  min: 0,
450
443
  max: 2
451
444
  }
@@ -497,14 +490,14 @@ fields: {
497
490
  ```typescript
498
491
  // apis/article/list.ts
499
492
  export default {
500
- name: '文章列表',
493
+ name: "文章列表",
501
494
  auth: true,
502
495
  fields: {
503
- page: '@page',
504
- limit: '@limit',
505
- keyword: '@keyword',
506
- state: '@state',
507
- categoryId: { name: '分类ID', type: 'number', min: 0 }
496
+ page: "@page",
497
+ limit: "@limit",
498
+ keyword: "@keyword",
499
+ state: "@state",
500
+ categoryId: { name: "分类ID", type: "number", min: 0 }
508
501
  },
509
502
  handler: async (befly, ctx) => {
510
503
  const { page, limit, keyword, categoryId } = ctx.body;
@@ -514,15 +507,15 @@ export default {
514
507
  if (keyword) where.title = { $like: `%${keyword}%` };
515
508
 
516
509
  const result = await befly.db.getList({
517
- table: 'article',
518
- columns: ['id', 'title', 'summary', 'createdAt'],
510
+ table: "article",
511
+ columns: ["id", "title", "summary", "createdAt"],
519
512
  where: where,
520
513
  page: page || 1,
521
514
  limit: limit || 10,
522
- orderBy: { id: 'desc' }
515
+ orderBy: { id: "desc" }
523
516
  });
524
517
 
525
- return befly.tool.Yes('获取成功', result);
518
+ return befly.tool.Yes("获取成功", result);
526
519
  }
527
520
  } as ApiRoute;
528
521
  ```
@@ -532,23 +525,23 @@ export default {
532
525
  ```typescript
533
526
  // apis/article/detail.ts
534
527
  export default {
535
- name: '文章详情',
528
+ name: "文章详情",
536
529
  auth: false,
537
530
  fields: {
538
- id: '@id'
531
+ id: "@id"
539
532
  },
540
- required: ['id'],
533
+ required: ["id"],
541
534
  handler: async (befly, ctx) => {
542
535
  const article = await befly.db.getDetail({
543
- table: 'article',
536
+ table: "article",
544
537
  where: { id: ctx.body.id, state: 1 }
545
538
  });
546
539
 
547
540
  if (!article?.id) {
548
- return befly.tool.No('文章不存在');
541
+ return befly.tool.No("文章不存在");
549
542
  }
550
543
 
551
- return befly.tool.Yes('获取成功', article);
544
+ return befly.tool.Yes("获取成功", article);
552
545
  }
553
546
  } as ApiRoute;
554
547
  ```
@@ -556,19 +549,19 @@ export default {
556
549
  ```typescript
557
550
  // apis/article/delete.ts
558
551
  export default {
559
- name: '删除文章',
552
+ name: "删除文章",
560
553
  auth: true,
561
554
  fields: {
562
- id: '@id'
555
+ id: "@id"
563
556
  },
564
- required: ['id'],
557
+ required: ["id"],
565
558
  handler: async (befly, ctx) => {
566
559
  await befly.db.delData({
567
- table: 'article',
560
+ table: "article",
568
561
  where: { id: ctx.body.id }
569
562
  });
570
563
 
571
- return befly.tool.Yes('删除成功');
564
+ return befly.tool.Yes("删除成功");
572
565
  }
573
566
  } as ApiRoute;
574
567
  ```
@@ -579,20 +572,20 @@ export default {
579
572
 
580
573
  ```typescript
581
574
  export default {
582
- name: '大数据列表',
575
+ name: "大数据列表",
583
576
  fields: {
584
- page: '@page',
577
+ page: "@page",
585
578
  // 覆盖默认的 @limit,允许更大的分页
586
579
  limit: {
587
- name: '每页数量',
588
- type: 'number',
580
+ name: "每页数量",
581
+ type: "number",
589
582
  min: 1,
590
583
  max: 500 // 修改最大值为 500
591
584
  }
592
585
  },
593
586
  handler: async (befly, ctx) => {
594
587
  // ctx.body.limit 最大可以是 500
595
- return befly.tool.Yes('获取成功');
588
+ return befly.tool.Yes("获取成功");
596
589
  }
597
590
  } as ApiRoute;
598
591
  ```
@@ -723,8 +716,8 @@ export default {
723
716
  ```typescript
724
717
  // API 定义
725
718
  export default {
726
- name: '微信支付回调',
727
- method: 'POST',
719
+ name: "微信支付回调",
720
+ method: "POST",
728
721
  auth: false,
729
722
  rawBody: true, // 跳过解析,保留原始请求
730
723
  handler: async (befly, ctx) => {
@@ -768,8 +761,8 @@ export default {
768
761
  ```typescript
769
762
  // apis/webhook/wechatPayV3.ts
770
763
  export default {
771
- name: '微信支付V3回调',
772
- method: 'POST',
764
+ name: "微信支付V3回调",
765
+ method: "POST",
773
766
  auth: false,
774
767
  rawBody: true, // 跳过解析,保留原始请求
775
768
  handler: async (befly, ctx) => {
@@ -777,15 +770,15 @@ export default {
777
770
  const rawBody = await ctx.req.text();
778
771
 
779
772
  // 2. 获取微信签名头
780
- const signature = ctx.req.headers.get('Wechatpay-Signature');
781
- const timestamp = ctx.req.headers.get('Wechatpay-Timestamp');
782
- const nonce = ctx.req.headers.get('Wechatpay-Nonce');
783
- const serial = ctx.req.headers.get('Wechatpay-Serial');
773
+ const signature = ctx.req.headers.get("Wechatpay-Signature");
774
+ const timestamp = ctx.req.headers.get("Wechatpay-Timestamp");
775
+ const nonce = ctx.req.headers.get("Wechatpay-Nonce");
776
+ const serial = ctx.req.headers.get("Wechatpay-Serial");
784
777
 
785
778
  // 3. 验证签名
786
779
  const verifyMessage = `${timestamp}\n${nonce}\n${rawBody}\n`;
787
780
  if (!verifyRsaSign(verifyMessage, signature, serial)) {
788
- return { code: 'FAIL', message: '签名验证失败' };
781
+ return { code: "FAIL", message: "签名验证失败" };
789
782
  }
790
783
 
791
784
  // 4. 解析并解密数据
@@ -795,9 +788,9 @@ export default {
795
788
  const payResult = JSON.parse(decrypted);
796
789
 
797
790
  // 5. 处理支付结果
798
- if (payResult.trade_state === 'SUCCESS') {
791
+ if (payResult.trade_state === "SUCCESS") {
799
792
  await befly.db.updData({
800
- table: 'order',
793
+ table: "order",
801
794
  where: { orderNo: payResult.out_trade_no },
802
795
  data: {
803
796
  payStatus: 1,
@@ -807,7 +800,7 @@ export default {
807
800
  });
808
801
  }
809
802
 
810
- return { code: 'SUCCESS', message: '' };
803
+ return { code: "SUCCESS", message: "" };
811
804
  }
812
805
  };
813
806
  ```
@@ -816,11 +809,11 @@ export default {
816
809
 
817
810
  ```typescript
818
811
  // apis/webhook/github.ts
819
- import { createHmac } from 'crypto';
812
+ import { createHmac } from "crypto";
820
813
 
821
814
  export default {
822
- name: 'GitHub Webhook',
823
- method: 'POST',
815
+ name: "GitHub Webhook",
816
+ method: "POST",
824
817
  auth: false,
825
818
  rawBody: true,
826
819
  handler: async (befly, ctx) => {
@@ -828,30 +821,30 @@ export default {
828
821
  const rawBody = await ctx.req.text();
829
822
 
830
823
  // 获取 GitHub 签名
831
- const signature = ctx.req.headers.get('X-Hub-Signature-256');
824
+ const signature = ctx.req.headers.get("X-Hub-Signature-256");
832
825
  if (!signature) {
833
- return befly.tool.No('缺少签名');
826
+ return befly.tool.No("缺少签名");
834
827
  }
835
828
 
836
829
  // 验证 HMAC 签名
837
830
  const secret = process.env.GITHUB_WEBHOOK_SECRET;
838
- const hmac = createHmac('sha256', secret);
839
- const expectedSignature = 'sha256=' + hmac.update(rawBody).digest('hex');
831
+ const hmac = createHmac("sha256", secret);
832
+ const expectedSignature = "sha256=" + hmac.update(rawBody).digest("hex");
840
833
 
841
834
  if (signature !== expectedSignature) {
842
- return befly.tool.No('签名验证失败');
835
+ return befly.tool.No("签名验证失败");
843
836
  }
844
837
 
845
838
  // 解析数据
846
839
  const payload = JSON.parse(rawBody);
847
- const event = ctx.req.headers.get('X-GitHub-Event');
840
+ const event = ctx.req.headers.get("X-GitHub-Event");
848
841
 
849
842
  // 处理不同事件
850
- if (event === 'push') {
851
- befly.logger.info({ ref: payload.ref }, 'GitHub Push 事件');
843
+ if (event === "push") {
844
+ befly.logger.info({ ref: payload.ref }, "GitHub Push 事件");
852
845
  }
853
846
 
854
- return befly.tool.Yes('处理成功');
847
+ return befly.tool.Yes("处理成功");
855
848
  }
856
849
  };
857
850
  ```
@@ -861,7 +854,7 @@ export default {
861
854
  ```typescript
862
855
  // apis/secure/receive.ts
863
856
  export default {
864
- name: '接收加密数据',
857
+ name: "接收加密数据",
865
858
  auth: false,
866
859
  rawBody: true,
867
860
  handler: async (befly, ctx) => {
@@ -875,7 +868,7 @@ export default {
875
868
  // 处理解密后的数据
876
869
  // ...
877
870
 
878
- return befly.tool.Yes('处理成功');
871
+ return befly.tool.Yes("处理成功");
879
872
  }
880
873
  };
881
874
  ```
@@ -887,17 +880,17 @@ export default {
887
880
  ```typescript
888
881
  // apis/user/batchImport.ts
889
882
  export default {
890
- name: '批量导入用户',
883
+ name: "批量导入用户",
891
884
  // 不定义 fields,或 fields: {},会保留所有请求参数
892
885
  handler: async (befly, ctx) => {
893
886
  const { users } = ctx.body; // 正常解析
894
887
 
895
888
  if (!Array.isArray(users) || users.length === 0) {
896
- return befly.tool.No('用户列表不能为空');
889
+ return befly.tool.No("用户列表不能为空");
897
890
  }
898
891
 
899
892
  // 批量插入...
900
- return befly.tool.Yes('导入成功');
893
+ return befly.tool.Yes("导入成功");
901
894
  }
902
895
  };
903
896
  ```
@@ -918,39 +911,39 @@ export default {
918
911
 
919
912
  ```typescript
920
913
  // apis/auth/login.ts
921
- import adminTable from '../../tables/admin.json';
914
+ import adminTable from "../../tables/admin.json";
922
915
 
923
916
  export default {
924
- name: '管理员登录',
917
+ name: "管理员登录",
925
918
  auth: false, // 公开接口
926
919
  fields: {
927
920
  account: {
928
- name: '账号',
929
- type: 'string',
921
+ name: "账号",
922
+ type: "string",
930
923
  min: 3,
931
924
  max: 100
932
925
  },
933
926
  password: adminTable.password
934
927
  },
935
- required: ['account', 'password'],
928
+ required: ["account", "password"],
936
929
  handler: async (befly, ctx) => {
937
930
  // 查询用户
938
931
  const admin = await befly.db.getOne({
939
- table: 'addon_admin_admin',
932
+ table: "addon_admin_admin",
940
933
  where: {
941
934
  $or: [{ username: ctx.body.account }, { email: ctx.body.account }]
942
935
  }
943
936
  });
944
937
 
945
938
  if (!admin?.id) {
946
- return befly.tool.No('账号或密码错误');
939
+ return befly.tool.No("账号或密码错误");
947
940
  }
948
941
 
949
942
  // 验证密码
950
943
  const isValid = await befly.cipher.verifyPassword(ctx.body.password, admin.password);
951
944
 
952
945
  if (!isValid) {
953
- return befly.tool.No('账号或密码错误');
946
+ return befly.tool.No("账号或密码错误");
954
947
  }
955
948
 
956
949
  // 生成 Token
@@ -959,7 +952,7 @@ export default {
959
952
  roleCode: admin.roleCode
960
953
  });
961
954
 
962
- return befly.tool.Yes('登录成功', {
955
+ return befly.tool.Yes("登录成功", {
963
956
  token: token,
964
957
  userInfo: admin
965
958
  });
@@ -972,20 +965,20 @@ export default {
972
965
  ```typescript
973
966
  // apis/admin/list.ts
974
967
  export default {
975
- name: '获取管理员列表',
968
+ name: "获取管理员列表",
976
969
  // auth: true, // 默认需要认证
977
970
  handler: async (befly, ctx) => {
978
971
  const result = await befly.db.getList({
979
- table: 'addon_admin_admin',
972
+ table: "addon_admin_admin",
980
973
  page: ctx.body.page || 1,
981
974
  limit: ctx.body.limit || 10,
982
975
  where: {
983
- roleCode: { $ne: 'dev' }
976
+ roleCode: { $ne: "dev" }
984
977
  },
985
- orderBy: ['createdAt#DESC']
978
+ orderBy: ["createdAt#DESC"]
986
979
  });
987
980
 
988
- return befly.tool.Yes('获取成功', result);
981
+ return befly.tool.Yes("获取成功", result);
989
982
  }
990
983
  };
991
984
  ```
@@ -994,31 +987,31 @@ export default {
994
987
 
995
988
  ```typescript
996
989
  // apis/admin/ins.ts
997
- import adminTable from '../../tables/admin.json';
990
+ import adminTable from "../../tables/admin.json";
998
991
 
999
992
  export default {
1000
- name: '添加管理员',
993
+ name: "添加管理员",
1001
994
  fields: adminTable,
1002
- required: ['username', 'password', 'roleCode'],
995
+ required: ["username", "password", "roleCode"],
1003
996
  handler: async (befly, ctx) => {
1004
997
  // 检查用户名是否已存在
1005
998
  const existing = await befly.db.getOne({
1006
- table: 'addon_admin_admin',
999
+ table: "addon_admin_admin",
1007
1000
  where: { username: ctx.body.username }
1008
1001
  });
1009
1002
 
1010
1003
  if (existing) {
1011
- return befly.tool.No('用户名已被使用');
1004
+ return befly.tool.No("用户名已被使用");
1012
1005
  }
1013
1006
 
1014
1007
  // 查询角色信息
1015
1008
  const role = await befly.db.getOne({
1016
- table: 'addon_admin_role',
1009
+ table: "addon_admin_role",
1017
1010
  where: { code: ctx.body.roleCode }
1018
1011
  });
1019
1012
 
1020
1013
  if (!role) {
1021
- return befly.tool.No('角色不存在');
1014
+ return befly.tool.No("角色不存在");
1022
1015
  }
1023
1016
 
1024
1017
  // 加密密码
@@ -1026,7 +1019,7 @@ export default {
1026
1019
 
1027
1020
  // 创建管理员
1028
1021
  const adminId = await befly.db.insData({
1029
- table: 'addon_admin_admin',
1022
+ table: "addon_admin_admin",
1030
1023
  data: {
1031
1024
  username: ctx.body.username,
1032
1025
  password: hashedPassword,
@@ -1035,7 +1028,7 @@ export default {
1035
1028
  }
1036
1029
  });
1037
1030
 
1038
- return befly.tool.Yes('添加成功', {
1031
+ return befly.tool.Yes("添加成功", {
1039
1032
  id: adminId
1040
1033
  });
1041
1034
  }
@@ -1046,33 +1039,33 @@ export default {
1046
1039
 
1047
1040
  ```typescript
1048
1041
  // apis/admin/upd.ts
1049
- import adminTable from '../../tables/admin.json';
1042
+ import adminTable from "../../tables/admin.json";
1050
1043
 
1051
1044
  export default {
1052
- name: '更新管理员',
1045
+ name: "更新管理员",
1053
1046
  fields: adminTable,
1054
- required: ['id'],
1047
+ required: ["id"],
1055
1048
  handler: async (befly, ctx) => {
1056
1049
  const { id, ...updateData } = ctx.body;
1057
1050
 
1058
1051
  // 检查管理员是否存在
1059
1052
  const admin = await befly.db.getOne({
1060
- table: 'addon_admin_admin',
1053
+ table: "addon_admin_admin",
1061
1054
  where: { id: id }
1062
1055
  });
1063
1056
 
1064
1057
  if (!admin?.id) {
1065
- return befly.tool.No('管理员不存在');
1058
+ return befly.tool.No("管理员不存在");
1066
1059
  }
1067
1060
 
1068
1061
  // 更新管理员信息
1069
1062
  await befly.db.updData({
1070
- table: 'addon_admin_admin',
1063
+ table: "addon_admin_admin",
1071
1064
  data: updateData,
1072
1065
  where: { id: id }
1073
1066
  });
1074
1067
 
1075
- return befly.tool.Yes('更新成功');
1068
+ return befly.tool.Yes("更新成功");
1076
1069
  }
1077
1070
  };
1078
1071
  ```
@@ -1082,32 +1075,32 @@ export default {
1082
1075
  ```typescript
1083
1076
  // apis/admin/del.ts
1084
1077
  export default {
1085
- name: '删除管理员',
1078
+ name: "删除管理员",
1086
1079
  fields: {},
1087
- required: ['id'],
1080
+ required: ["id"],
1088
1081
  handler: async (befly, ctx) => {
1089
1082
  // 检查管理员是否存在
1090
1083
  const admin = await befly.db.getOne({
1091
- table: 'addon_admin_admin',
1084
+ table: "addon_admin_admin",
1092
1085
  where: { id: ctx.body.id }
1093
1086
  });
1094
1087
 
1095
1088
  if (!admin) {
1096
- return befly.tool.No('管理员不存在');
1089
+ return befly.tool.No("管理员不存在");
1097
1090
  }
1098
1091
 
1099
1092
  // 业务检查:不能删除开发者账号
1100
- if (admin.roleCode === 'dev') {
1101
- return befly.tool.No('不能删除开发者账号');
1093
+ if (admin.roleCode === "dev") {
1094
+ return befly.tool.No("不能删除开发者账号");
1102
1095
  }
1103
1096
 
1104
1097
  // 删除管理员
1105
1098
  await befly.db.delData({
1106
- table: 'addon_admin_admin',
1099
+ table: "addon_admin_admin",
1107
1100
  where: { id: ctx.body.id }
1108
1101
  });
1109
1102
 
1110
- return befly.tool.Yes('删除成功');
1103
+ return befly.tool.Yes("删除成功");
1111
1104
  }
1112
1105
  };
1113
1106
  ```
@@ -1117,29 +1110,29 @@ export default {
1117
1110
  ```typescript
1118
1111
  // apis/admin/detail.ts
1119
1112
  export default {
1120
- name: '获取用户信息',
1113
+ name: "获取用户信息",
1121
1114
  handler: async (befly, ctx) => {
1122
1115
  const userId = ctx.user?.id;
1123
1116
 
1124
1117
  if (!userId) {
1125
- return befly.tool.No('未授权');
1118
+ return befly.tool.No("未授权");
1126
1119
  }
1127
1120
 
1128
1121
  // 查询用户信息
1129
1122
  const admin = await befly.db.getOne({
1130
- table: 'addon_admin_admin',
1123
+ table: "addon_admin_admin",
1131
1124
  where: { id: userId }
1132
1125
  });
1133
1126
 
1134
1127
  if (!admin) {
1135
- return befly.tool.No('用户不存在');
1128
+ return befly.tool.No("用户不存在");
1136
1129
  }
1137
1130
 
1138
1131
  // 查询角色信息
1139
1132
  let roleInfo = null;
1140
1133
  if (admin.roleCode) {
1141
1134
  roleInfo = await befly.db.getOne({
1142
- table: 'addon_admin_role',
1135
+ table: "addon_admin_role",
1143
1136
  where: { code: admin.roleCode }
1144
1137
  });
1145
1138
  }
@@ -1147,7 +1140,7 @@ export default {
1147
1140
  // 返回用户信息(不包含密码)
1148
1141
  const { password: _, ...userWithoutPassword } = admin;
1149
1142
 
1150
- return befly.tool.Yes('获取成功', {
1143
+ return befly.tool.Yes("获取成功", {
1151
1144
  ...userWithoutPassword,
1152
1145
  role: roleInfo
1153
1146
  });
@@ -1160,27 +1153,27 @@ export default {
1160
1153
  ```typescript
1161
1154
  // apis/article/search.ts
1162
1155
  export default {
1163
- name: '搜索文章',
1164
- method: 'GET,POST', // 同时支持 GET 和 POST
1156
+ name: "搜索文章",
1157
+ method: "GET,POST", // 同时支持 GET 和 POST
1165
1158
  auth: false,
1166
1159
  fields: {
1167
1160
  keyword: {
1168
- name: '关键词',
1169
- type: 'string',
1161
+ name: "关键词",
1162
+ type: "string",
1170
1163
  min: 1,
1171
1164
  max: 100
1172
1165
  }
1173
1166
  },
1174
- required: ['keyword'],
1167
+ required: ["keyword"],
1175
1168
  handler: async (befly, ctx) => {
1176
1169
  const result = await befly.db.getList({
1177
- table: 'article',
1170
+ table: "article",
1178
1171
  where: {
1179
1172
  title: { $like: `%${ctx.body.keyword}%` }
1180
1173
  }
1181
1174
  });
1182
1175
 
1183
- return befly.tool.Yes('搜索成功', result);
1176
+ return befly.tool.Yes("搜索成功", result);
1184
1177
  }
1185
1178
  };
1186
1179
  ```
@@ -1190,8 +1183,8 @@ export default {
1190
1183
  ```typescript
1191
1184
  // apis/webhook/wechat.ts
1192
1185
  export default {
1193
- name: '微信回调',
1194
- method: 'POST',
1186
+ name: "微信回调",
1187
+ method: "POST",
1195
1188
  auth: false,
1196
1189
  rawBody: true, // 不过滤字段,保留完整请求体
1197
1190
  handler: async (befly, ctx) => {
@@ -1199,9 +1192,9 @@ export default {
1199
1192
  const { ToUserName, FromUserName, MsgType, Content } = ctx.body;
1200
1193
 
1201
1194
  // 处理微信消息
1202
- befly.logger.info({ msg: Content }, '收到微信消息');
1195
+ befly.logger.info({ msg: Content }, "收到微信消息");
1203
1196
 
1204
- return befly.tool.Yes('处理成功');
1197
+ return befly.tool.Yes("处理成功");
1205
1198
  }
1206
1199
  };
1207
1200
  ```
@@ -1260,7 +1253,7 @@ export default {
1260
1253
  ```typescript
1261
1254
  // 在 Hook 中中断
1262
1255
  if (!ctx.user?.id) {
1263
- ctx.response = ErrorResponse(ctx, '未登录');
1256
+ ctx.response = ErrorResponse(ctx, "未登录");
1264
1257
  return; // 后续 Hook 和 handler 不会执行
1265
1258
  }
1266
1259
  ```
@@ -1347,10 +1340,10 @@ befly.db.cleanFields<T>(
1347
1340
  ```typescript
1348
1341
  // 默认排除 null 和 undefined
1349
1342
  const cleanData = befly.db.cleanFields({
1350
- name: 'John',
1343
+ name: "John",
1351
1344
  age: null,
1352
1345
  email: undefined,
1353
- phone: ''
1346
+ phone: ""
1354
1347
  });
1355
1348
  // 结果: { name: 'John', phone: '' }
1356
1349
  ```
@@ -1359,7 +1352,7 @@ const cleanData = befly.db.cleanFields({
1359
1352
 
1360
1353
  ```typescript
1361
1354
  // 同时排除 null、undefined 和空字符串
1362
- const cleanData = befly.db.cleanFields({ name: 'John', phone: '', age: null }, [null, undefined, '']);
1355
+ const cleanData = befly.db.cleanFields({ name: "John", phone: "", age: null }, [null, undefined, ""]);
1363
1356
  // 结果: { name: 'John' }
1364
1357
  ```
1365
1358
 
@@ -1367,7 +1360,7 @@ const cleanData = befly.db.cleanFields({ name: 'John', phone: '', age: null }, [
1367
1360
 
1368
1361
  ```typescript
1369
1362
  // 即使值在排除列表中,也保留 status 字段的 null 值
1370
- const cleanData = befly.db.cleanFields({ name: 'John', status: null, count: 0 }, [null, undefined], { status: null });
1363
+ const cleanData = befly.db.cleanFields({ name: "John", status: null, count: 0 }, [null, undefined], { status: null });
1371
1364
  // 结果: { name: 'John', status: null, count: 0 }
1372
1365
  ```
1373
1366
 
@@ -1405,7 +1398,7 @@ fields: {
1405
1398
  ```typescript
1406
1399
  // ✅ 推荐:直接使用
1407
1400
  const result = await befly.db.insData({
1408
- table: 'user',
1401
+ table: "user",
1409
1402
  data: {
1410
1403
  username: ctx.body.username,
1411
1404
  email: ctx.body.email
@@ -1421,7 +1414,7 @@ const { username, email } = ctx.body;
1421
1414
  ```typescript
1422
1415
  // ✅ 推荐:明确每个字段
1423
1416
  await befly.db.insData({
1424
- table: 'user',
1417
+ table: "user",
1425
1418
  data: {
1426
1419
  username: ctx.body.username,
1427
1420
  email: ctx.body.email,
@@ -1431,7 +1424,7 @@ await befly.db.insData({
1431
1424
 
1432
1425
  // ❌ 避免:扩展运算符
1433
1426
  await befly.db.insData({
1434
- table: 'user',
1427
+ table: "user",
1435
1428
  data: { ...ctx.body } // 危险!可能写入未预期的字段
1436
1429
  });
1437
1430
  ```
@@ -1443,12 +1436,12 @@ handler: async (befly, ctx) => {
1443
1436
  try {
1444
1437
  // 业务逻辑
1445
1438
  const result = await someOperation();
1446
- return befly.tool.Yes('成功', result);
1439
+ return befly.tool.Yes("成功", result);
1447
1440
  } catch (error: any) {
1448
1441
  // 记录错误日志
1449
- befly.logger.error({ err: error }, '操作失败');
1442
+ befly.logger.error({ err: error }, "操作失败");
1450
1443
  // 返回友好错误信息
1451
- return befly.tool.No('操作失败,请稍后重试');
1444
+ return befly.tool.No("操作失败,请稍后重试");
1452
1445
  }
1453
1446
  };
1454
1447
  ```
@@ -1458,7 +1451,7 @@ handler: async (befly, ctx) => {
1458
1451
  ```typescript
1459
1452
  // ✅ 推荐:使用 Date.now()
1460
1453
  await befly.db.updData({
1461
- table: 'user',
1454
+ table: "user",
1462
1455
  data: {
1463
1456
  lastLoginTime: Date.now(), // number 类型
1464
1457
  lastLoginIp: ctx.ip
@@ -1478,7 +1471,7 @@ lastLoginTime: new Date(); // 类型不一致
1478
1471
 
1479
1472
  ```typescript
1480
1473
  export default {
1481
- name: '公开接口',
1474
+ name: "公开接口",
1482
1475
  auth: false, // 设置为 false
1483
1476
  handler: async (befly, ctx) => {
1484
1477
  // ...
@@ -1494,7 +1487,7 @@ handler: async (befly, ctx) => {
1494
1487
  const roleCode = ctx.user?.roleCode;
1495
1488
 
1496
1489
  if (!userId) {
1497
- return befly.tool.No('未登录');
1490
+ return befly.tool.No("未登录");
1498
1491
  }
1499
1492
  };
1500
1493
  ```
@@ -1509,12 +1502,12 @@ handler: async (befly, ctx) => {
1509
1502
 
1510
1503
  ```typescript
1511
1504
  // tpl/hooks/requestLog.ts
1512
- import type { Hook } from 'befly/types/hook';
1505
+ import type { Hook } from "befly/types/hook";
1513
1506
 
1514
1507
  const hook: Hook = {
1515
1508
  order: 100, // 执行顺序
1516
1509
  handler: async (befly, ctx) => {
1517
- befly.logger.info({ route: ctx.route }, '请求开始');
1510
+ befly.logger.info({ route: ctx.route }, "请求开始");
1518
1511
  }
1519
1512
  };
1520
1513
  export default hook;
@@ -1538,42 +1531,42 @@ export default hook;
1538
1531
  ```typescript
1539
1532
  // apis/order/create.ts
1540
1533
  export default {
1541
- name: '创建订单',
1534
+ name: "创建订单",
1542
1535
  fields: {
1543
1536
  productId: {
1544
- name: '商品ID',
1545
- type: 'number',
1537
+ name: "商品ID",
1538
+ type: "number",
1546
1539
  min: 1
1547
1540
  },
1548
1541
  quantity: {
1549
- name: '数量',
1550
- type: 'number',
1542
+ name: "数量",
1543
+ type: "number",
1551
1544
  min: 1,
1552
1545
  max: 999
1553
1546
  }
1554
1547
  },
1555
- required: ['productId', 'quantity'],
1548
+ required: ["productId", "quantity"],
1556
1549
  handler: async (befly, ctx) => {
1557
1550
  // 使用事务确保库存扣减和订单创建的原子性
1558
1551
  const result = await befly.db.transaction(async (trx) => {
1559
1552
  // 1. 查询商品信息(带锁)
1560
1553
  const product = await trx.getOne({
1561
- table: 'product',
1554
+ table: "product",
1562
1555
  where: { id: ctx.body.productId },
1563
1556
  forUpdate: true // 行锁
1564
1557
  });
1565
1558
 
1566
1559
  if (!product) {
1567
- throw new Error('商品不存在');
1560
+ throw new Error("商品不存在");
1568
1561
  }
1569
1562
 
1570
1563
  if (product.stock < ctx.body.quantity) {
1571
- throw new Error('库存不足');
1564
+ throw new Error("库存不足");
1572
1565
  }
1573
1566
 
1574
1567
  // 2. 扣减库存
1575
1568
  await trx.updData({
1576
- table: 'product',
1569
+ table: "product",
1577
1570
  data: {
1578
1571
  stock: product.stock - ctx.body.quantity
1579
1572
  },
@@ -1582,19 +1575,19 @@ export default {
1582
1575
 
1583
1576
  // 3. 创建订单
1584
1577
  const orderId = await trx.insData({
1585
- table: 'order',
1578
+ table: "order",
1586
1579
  data: {
1587
1580
  userId: ctx.user.id,
1588
1581
  productId: ctx.body.productId,
1589
1582
  quantity: ctx.body.quantity,
1590
1583
  totalPrice: product.price * ctx.body.quantity,
1591
- status: 'pending'
1584
+ status: "pending"
1592
1585
  }
1593
1586
  });
1594
1587
 
1595
1588
  // 4. 创建订单明细
1596
1589
  await trx.insData({
1597
- table: 'order_item',
1590
+ table: "order_item",
1598
1591
  data: {
1599
1592
  orderId: orderId,
1600
1593
  productId: ctx.body.productId,
@@ -1607,7 +1600,7 @@ export default {
1607
1600
  return { orderId: orderId };
1608
1601
  });
1609
1602
 
1610
- return befly.tool.Yes('订单创建成功', result);
1603
+ return befly.tool.Yes("订单创建成功", result);
1611
1604
  }
1612
1605
  };
1613
1606
  ```
@@ -1619,22 +1612,22 @@ export default {
1619
1612
  ```typescript
1620
1613
  // apis/user/batchImport.ts
1621
1614
  export default {
1622
- name: '批量导入用户',
1615
+ name: "批量导入用户",
1623
1616
  rawBody: true, // 保留原始请求体
1624
1617
  handler: async (befly, ctx) => {
1625
1618
  const users = ctx.body.users;
1626
1619
 
1627
1620
  if (!Array.isArray(users) || users.length === 0) {
1628
- return befly.tool.No('用户列表不能为空');
1621
+ return befly.tool.No("用户列表不能为空");
1629
1622
  }
1630
1623
 
1631
1624
  if (users.length > 100) {
1632
- return befly.tool.No('单次导入不能超过100条');
1625
+ return befly.tool.No("单次导入不能超过100条");
1633
1626
  }
1634
1627
 
1635
1628
  // 批量插入
1636
1629
  const result = await befly.db.batchInsert({
1637
- table: 'user',
1630
+ table: "user",
1638
1631
  data: users.map((user: any) => ({
1639
1632
  username: user.username,
1640
1633
  email: user.email,
@@ -1643,7 +1636,7 @@ export default {
1643
1636
  }))
1644
1637
  });
1645
1638
 
1646
- return befly.tool.Yes('导入成功', {
1639
+ return befly.tool.Yes("导入成功", {
1647
1640
  total: users.length,
1648
1641
  inserted: result.affectedRows
1649
1642
  });
@@ -1656,25 +1649,25 @@ export default {
1656
1649
  ```typescript
1657
1650
  // apis/article/batchUpdate.ts
1658
1651
  export default {
1659
- name: '批量更新文章状态',
1652
+ name: "批量更新文章状态",
1660
1653
  rawBody: true,
1661
1654
  handler: async (befly, ctx) => {
1662
1655
  const { ids, state } = ctx.body;
1663
1656
 
1664
1657
  if (!Array.isArray(ids) || ids.length === 0) {
1665
- return befly.tool.No('文章ID列表不能为空');
1658
+ return befly.tool.No("文章ID列表不能为空");
1666
1659
  }
1667
1660
 
1668
1661
  // 批量更新
1669
1662
  const result = await befly.db.updData({
1670
- table: 'article',
1663
+ table: "article",
1671
1664
  data: { state: state },
1672
1665
  where: {
1673
1666
  id: { $in: ids }
1674
1667
  }
1675
1668
  });
1676
1669
 
1677
- return befly.tool.Yes('更新成功', {
1670
+ return befly.tool.Yes("更新成功", {
1678
1671
  updated: result.affectedRows
1679
1672
  });
1680
1673
  }
@@ -1686,24 +1679,24 @@ export default {
1686
1679
  ```typescript
1687
1680
  // apis/log/batchDelete.ts
1688
1681
  export default {
1689
- name: '批量删除日志',
1682
+ name: "批量删除日志",
1690
1683
  rawBody: true,
1691
1684
  handler: async (befly, ctx) => {
1692
1685
  const { ids } = ctx.body;
1693
1686
 
1694
1687
  if (!Array.isArray(ids) || ids.length === 0) {
1695
- return befly.tool.No('日志ID列表不能为空');
1688
+ return befly.tool.No("日志ID列表不能为空");
1696
1689
  }
1697
1690
 
1698
1691
  // 批量软删除
1699
1692
  const result = await befly.db.delData({
1700
- table: 'operate_log',
1693
+ table: "operate_log",
1701
1694
  where: {
1702
1695
  id: { $in: ids }
1703
1696
  }
1704
1697
  });
1705
1698
 
1706
- return befly.tool.Yes('删除成功', {
1699
+ return befly.tool.Yes("删除成功", {
1707
1700
  deleted: result.affectedRows
1708
1701
  });
1709
1702
  }
@@ -1765,25 +1758,25 @@ export default {
1765
1758
  ```typescript
1766
1759
  // apis/article/listWithAuthor.ts
1767
1760
  export default {
1768
- name: '文章列表(含作者)',
1761
+ name: "文章列表(含作者)",
1769
1762
  handler: async (befly, ctx) => {
1770
1763
  const result = await befly.db.getList({
1771
- table: 'article',
1764
+ table: "article",
1772
1765
  joins: [
1773
1766
  {
1774
- type: 'LEFT',
1775
- table: 'user',
1776
- alias: 'author',
1777
- on: { 'article.authorId': 'author.id' }
1767
+ type: "LEFT",
1768
+ table: "user",
1769
+ alias: "author",
1770
+ on: { "article.authorId": "author.id" }
1778
1771
  }
1779
1772
  ],
1780
- columns: ['article.id', 'article.title', 'article.createdAt', 'author.nickname AS authorName'],
1773
+ columns: ["article.id", "article.title", "article.createdAt", "author.nickname AS authorName"],
1781
1774
  page: ctx.body.page || 1,
1782
1775
  limit: ctx.body.limit || 10,
1783
- orderBy: ['article.createdAt#DESC']
1776
+ orderBy: ["article.createdAt#DESC"]
1784
1777
  });
1785
1778
 
1786
- return befly.tool.Yes('获取成功', result);
1779
+ return befly.tool.Yes("获取成功", result);
1787
1780
  }
1788
1781
  };
1789
1782
  ```
@@ -1793,30 +1786,29 @@ export default {
1793
1786
  ```typescript
1794
1787
  // apis/config/getSiteConfig.ts
1795
1788
  export default {
1796
- name: '获取站点配置',
1789
+ name: "获取站点配置",
1797
1790
  auth: false,
1798
- cache: 300, // 缓存 5 分钟
1799
1791
  handler: async (befly, ctx) => {
1800
1792
  // 先从缓存获取
1801
- const cacheKey = 'site:config';
1793
+ const cacheKey = "site:config";
1802
1794
  let config = await befly.redis.get(cacheKey);
1803
1795
 
1804
1796
  if (!config) {
1805
1797
  // 缓存不存在,从数据库查询
1806
1798
  const result = await befly.db.getAll({
1807
- table: 'sys_config',
1799
+ table: "sys_config",
1808
1800
  where: { state: 1 }
1809
1801
  });
1810
1802
 
1811
1803
  config = result.lists; // 获取配置列表
1812
1804
 
1813
1805
  // 写入缓存
1814
- await befly.redis.set(cacheKey, JSON.stringify(config), 'EX', 300);
1806
+ await befly.redis.set(cacheKey, JSON.stringify(config), "EX", 300);
1815
1807
  } else {
1816
1808
  config = JSON.parse(config);
1817
1809
  }
1818
1810
 
1819
- return befly.tool.Yes('获取成功', config);
1811
+ return befly.tool.Yes("获取成功", config);
1820
1812
  }
1821
1813
  };
1822
1814
  ```
@@ -1826,22 +1818,22 @@ export default {
1826
1818
  ```typescript
1827
1819
  // apis/task/execute.ts
1828
1820
  export default {
1829
- name: '执行定时任务',
1821
+ name: "执行定时任务",
1830
1822
  handler: async (befly, ctx) => {
1831
1823
  const lockKey = `lock:task:${ctx.body.taskId}`;
1832
1824
 
1833
1825
  // 尝试获取锁(30秒超时)
1834
- const locked = await befly.redis.set(lockKey, ctx.requestId, 'EX', 30, 'NX');
1826
+ const locked = await befly.redis.set(lockKey, ctx.requestId, "EX", 30, "NX");
1835
1827
 
1836
1828
  if (!locked) {
1837
- return befly.tool.No('任务正在执行中,请稍后');
1829
+ return befly.tool.No("任务正在执行中,请稍后");
1838
1830
  }
1839
1831
 
1840
1832
  try {
1841
1833
  // 执行任务逻辑
1842
1834
  await executeTask(ctx.body.taskId);
1843
1835
 
1844
- return befly.tool.Yes('任务执行成功');
1836
+ return befly.tool.Yes("任务执行成功");
1845
1837
  } finally {
1846
1838
  // 释放锁
1847
1839
  await befly.redis.del(lockKey);
@@ -1855,27 +1847,27 @@ export default {
1855
1847
  ```typescript
1856
1848
  // apis/report/exportUsers.ts
1857
1849
  export default {
1858
- name: '导出用户数据',
1850
+ name: "导出用户数据",
1859
1851
  handler: async (befly, ctx) => {
1860
1852
  // 查询所有用户(不分页,注意上限 10000 条)
1861
1853
  const result = await befly.db.getAll({
1862
- table: 'user',
1863
- columns: ['id', 'username', 'nickname', 'email', 'phone', 'createdAt'],
1854
+ table: "user",
1855
+ columns: ["id", "username", "nickname", "email", "phone", "createdAt"],
1864
1856
  where: { state: 1 },
1865
- orderBy: ['createdAt#DESC']
1857
+ orderBy: ["createdAt#DESC"]
1866
1858
  });
1867
1859
 
1868
1860
  // 转换为 CSV 格式
1869
- const headers = ['ID', '用户名', '昵称', '邮箱', '手机', '注册时间'];
1861
+ const headers = ["ID", "用户名", "昵称", "邮箱", "手机", "注册时间"];
1870
1862
  const rows = result.lists.map((u: any) => [u.id, u.username, u.nickname, u.email, u.phone, new Date(u.createdAt).toLocaleString()]);
1871
1863
 
1872
- const csv = [headers.join(','), ...rows.map((r: any[]) => r.join(','))].join('\n');
1864
+ const csv = [headers.join(","), ...rows.map((r: any[]) => r.join(","))].join("\n");
1873
1865
 
1874
1866
  // 返回 CSV 文件(注意:如果 total > 10000,只会导出前 10000 条)
1875
1867
  return new Response(csv, {
1876
1868
  headers: {
1877
- 'Content-Type': 'text/csv; charset=utf-8',
1878
- 'Content-Disposition': 'attachment; filename="users.csv"'
1869
+ "Content-Type": "text/csv; charset=utf-8",
1870
+ "Content-Disposition": 'attachment; filename="users.csv"'
1879
1871
  }
1880
1872
  });
1881
1873
  }
@@ -1887,17 +1879,17 @@ export default {
1887
1879
  ```typescript
1888
1880
  // apis/file/download.ts
1889
1881
  export default {
1890
- name: '文件下载',
1891
- required: ['fileId'],
1882
+ name: "文件下载",
1883
+ required: ["fileId"],
1892
1884
  handler: async (befly, ctx) => {
1893
1885
  // 查询文件信息
1894
1886
  const file = await befly.db.getOne({
1895
- table: 'file',
1887
+ table: "file",
1896
1888
  where: { id: ctx.body.fileId }
1897
1889
  });
1898
1890
 
1899
1891
  if (!file) {
1900
- return befly.tool.No('文件不存在');
1892
+ return befly.tool.No("文件不存在");
1901
1893
  }
1902
1894
 
1903
1895
  // 读取文件并返回流
@@ -1905,9 +1897,9 @@ export default {
1905
1897
 
1906
1898
  return new Response(fileStream, {
1907
1899
  headers: {
1908
- 'Content-Type': file.mimeType,
1909
- 'Content-Disposition': `attachment; filename="${encodeURIComponent(file.name)}"`,
1910
- 'Content-Length': String(file.size)
1900
+ "Content-Type": file.mimeType,
1901
+ "Content-Disposition": `attachment; filename="${encodeURIComponent(file.name)}"`,
1902
+ "Content-Length": String(file.size)
1911
1903
  }
1912
1904
  });
1913
1905
  }