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.
Files changed (155) hide show
  1. package/README.md +38 -39
  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} +225 -235
  8. package/docs/cipher.md +71 -69
  9. package/docs/database.md +155 -153
  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} +7 -7
  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 +15 -7
  34. package/lib/asyncContext.ts +43 -0
  35. package/lib/cacheHelper.ts +212 -81
  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 +211 -109
  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 +53 -47
  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 -54
  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 -66
  75. package/sync/syncMenu.ts +190 -57
  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,32 +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', 'roleId'],
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',
1017
- where: { id: ctx.body.roleId },
1018
- columns: ['code']
1009
+ table: "addon_admin_role",
1010
+ where: { code: ctx.body.roleCode }
1019
1011
  });
1020
1012
 
1021
- if (!role?.code) {
1022
- return befly.tool.No('角色不存在');
1013
+ if (!role) {
1014
+ return befly.tool.No("角色不存在");
1023
1015
  }
1024
1016
 
1025
1017
  // 加密密码
@@ -1027,17 +1019,16 @@ export default {
1027
1019
 
1028
1020
  // 创建管理员
1029
1021
  const adminId = await befly.db.insData({
1030
- table: 'addon_admin_admin',
1022
+ table: "addon_admin_admin",
1031
1023
  data: {
1032
1024
  username: ctx.body.username,
1033
1025
  password: hashedPassword,
1034
1026
  nickname: ctx.body.nickname,
1035
- roleId: ctx.body.roleId,
1036
1027
  roleCode: role.code
1037
1028
  }
1038
1029
  });
1039
1030
 
1040
- return befly.tool.Yes('添加成功', {
1031
+ return befly.tool.Yes("添加成功", {
1041
1032
  id: adminId
1042
1033
  });
1043
1034
  }
@@ -1048,33 +1039,33 @@ export default {
1048
1039
 
1049
1040
  ```typescript
1050
1041
  // apis/admin/upd.ts
1051
- import adminTable from '../../tables/admin.json';
1042
+ import adminTable from "../../tables/admin.json";
1052
1043
 
1053
1044
  export default {
1054
- name: '更新管理员',
1045
+ name: "更新管理员",
1055
1046
  fields: adminTable,
1056
- required: ['id'],
1047
+ required: ["id"],
1057
1048
  handler: async (befly, ctx) => {
1058
1049
  const { id, ...updateData } = ctx.body;
1059
1050
 
1060
1051
  // 检查管理员是否存在
1061
1052
  const admin = await befly.db.getOne({
1062
- table: 'addon_admin_admin',
1053
+ table: "addon_admin_admin",
1063
1054
  where: { id: id }
1064
1055
  });
1065
1056
 
1066
1057
  if (!admin?.id) {
1067
- return befly.tool.No('管理员不存在');
1058
+ return befly.tool.No("管理员不存在");
1068
1059
  }
1069
1060
 
1070
1061
  // 更新管理员信息
1071
1062
  await befly.db.updData({
1072
- table: 'addon_admin_admin',
1063
+ table: "addon_admin_admin",
1073
1064
  data: updateData,
1074
1065
  where: { id: id }
1075
1066
  });
1076
1067
 
1077
- return befly.tool.Yes('更新成功');
1068
+ return befly.tool.Yes("更新成功");
1078
1069
  }
1079
1070
  };
1080
1071
  ```
@@ -1084,32 +1075,32 @@ export default {
1084
1075
  ```typescript
1085
1076
  // apis/admin/del.ts
1086
1077
  export default {
1087
- name: '删除管理员',
1078
+ name: "删除管理员",
1088
1079
  fields: {},
1089
- required: ['id'],
1080
+ required: ["id"],
1090
1081
  handler: async (befly, ctx) => {
1091
1082
  // 检查管理员是否存在
1092
1083
  const admin = await befly.db.getOne({
1093
- table: 'addon_admin_admin',
1084
+ table: "addon_admin_admin",
1094
1085
  where: { id: ctx.body.id }
1095
1086
  });
1096
1087
 
1097
1088
  if (!admin) {
1098
- return befly.tool.No('管理员不存在');
1089
+ return befly.tool.No("管理员不存在");
1099
1090
  }
1100
1091
 
1101
1092
  // 业务检查:不能删除开发者账号
1102
- if (admin.roleCode === 'dev') {
1103
- return befly.tool.No('不能删除开发者账号');
1093
+ if (admin.roleCode === "dev") {
1094
+ return befly.tool.No("不能删除开发者账号");
1104
1095
  }
1105
1096
 
1106
1097
  // 删除管理员
1107
1098
  await befly.db.delData({
1108
- table: 'addon_admin_admin',
1099
+ table: "addon_admin_admin",
1109
1100
  where: { id: ctx.body.id }
1110
1101
  });
1111
1102
 
1112
- return befly.tool.Yes('删除成功');
1103
+ return befly.tool.Yes("删除成功");
1113
1104
  }
1114
1105
  };
1115
1106
  ```
@@ -1119,29 +1110,29 @@ export default {
1119
1110
  ```typescript
1120
1111
  // apis/admin/detail.ts
1121
1112
  export default {
1122
- name: '获取用户信息',
1113
+ name: "获取用户信息",
1123
1114
  handler: async (befly, ctx) => {
1124
1115
  const userId = ctx.user?.id;
1125
1116
 
1126
1117
  if (!userId) {
1127
- return befly.tool.No('未授权');
1118
+ return befly.tool.No("未授权");
1128
1119
  }
1129
1120
 
1130
1121
  // 查询用户信息
1131
1122
  const admin = await befly.db.getOne({
1132
- table: 'addon_admin_admin',
1123
+ table: "addon_admin_admin",
1133
1124
  where: { id: userId }
1134
1125
  });
1135
1126
 
1136
1127
  if (!admin) {
1137
- return befly.tool.No('用户不存在');
1128
+ return befly.tool.No("用户不存在");
1138
1129
  }
1139
1130
 
1140
1131
  // 查询角色信息
1141
1132
  let roleInfo = null;
1142
1133
  if (admin.roleCode) {
1143
1134
  roleInfo = await befly.db.getOne({
1144
- table: 'addon_admin_role',
1135
+ table: "addon_admin_role",
1145
1136
  where: { code: admin.roleCode }
1146
1137
  });
1147
1138
  }
@@ -1149,7 +1140,7 @@ export default {
1149
1140
  // 返回用户信息(不包含密码)
1150
1141
  const { password: _, ...userWithoutPassword } = admin;
1151
1142
 
1152
- return befly.tool.Yes('获取成功', {
1143
+ return befly.tool.Yes("获取成功", {
1153
1144
  ...userWithoutPassword,
1154
1145
  role: roleInfo
1155
1146
  });
@@ -1162,27 +1153,27 @@ export default {
1162
1153
  ```typescript
1163
1154
  // apis/article/search.ts
1164
1155
  export default {
1165
- name: '搜索文章',
1166
- method: 'GET,POST', // 同时支持 GET 和 POST
1156
+ name: "搜索文章",
1157
+ method: "GET,POST", // 同时支持 GET 和 POST
1167
1158
  auth: false,
1168
1159
  fields: {
1169
1160
  keyword: {
1170
- name: '关键词',
1171
- type: 'string',
1161
+ name: "关键词",
1162
+ type: "string",
1172
1163
  min: 1,
1173
1164
  max: 100
1174
1165
  }
1175
1166
  },
1176
- required: ['keyword'],
1167
+ required: ["keyword"],
1177
1168
  handler: async (befly, ctx) => {
1178
1169
  const result = await befly.db.getList({
1179
- table: 'article',
1170
+ table: "article",
1180
1171
  where: {
1181
1172
  title: { $like: `%${ctx.body.keyword}%` }
1182
1173
  }
1183
1174
  });
1184
1175
 
1185
- return befly.tool.Yes('搜索成功', result);
1176
+ return befly.tool.Yes("搜索成功", result);
1186
1177
  }
1187
1178
  };
1188
1179
  ```
@@ -1192,8 +1183,8 @@ export default {
1192
1183
  ```typescript
1193
1184
  // apis/webhook/wechat.ts
1194
1185
  export default {
1195
- name: '微信回调',
1196
- method: 'POST',
1186
+ name: "微信回调",
1187
+ method: "POST",
1197
1188
  auth: false,
1198
1189
  rawBody: true, // 不过滤字段,保留完整请求体
1199
1190
  handler: async (befly, ctx) => {
@@ -1201,9 +1192,9 @@ export default {
1201
1192
  const { ToUserName, FromUserName, MsgType, Content } = ctx.body;
1202
1193
 
1203
1194
  // 处理微信消息
1204
- befly.logger.info({ msg: Content }, '收到微信消息');
1195
+ befly.logger.info({ msg: Content }, "收到微信消息");
1205
1196
 
1206
- return befly.tool.Yes('处理成功');
1197
+ return befly.tool.Yes("处理成功");
1207
1198
  }
1208
1199
  };
1209
1200
  ```
@@ -1262,7 +1253,7 @@ export default {
1262
1253
  ```typescript
1263
1254
  // 在 Hook 中中断
1264
1255
  if (!ctx.user?.id) {
1265
- ctx.response = ErrorResponse(ctx, '未登录');
1256
+ ctx.response = ErrorResponse(ctx, "未登录");
1266
1257
  return; // 后续 Hook 和 handler 不会执行
1267
1258
  }
1268
1259
  ```
@@ -1349,10 +1340,10 @@ befly.db.cleanFields<T>(
1349
1340
  ```typescript
1350
1341
  // 默认排除 null 和 undefined
1351
1342
  const cleanData = befly.db.cleanFields({
1352
- name: 'John',
1343
+ name: "John",
1353
1344
  age: null,
1354
1345
  email: undefined,
1355
- phone: ''
1346
+ phone: ""
1356
1347
  });
1357
1348
  // 结果: { name: 'John', phone: '' }
1358
1349
  ```
@@ -1361,7 +1352,7 @@ const cleanData = befly.db.cleanFields({
1361
1352
 
1362
1353
  ```typescript
1363
1354
  // 同时排除 null、undefined 和空字符串
1364
- 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, ""]);
1365
1356
  // 结果: { name: 'John' }
1366
1357
  ```
1367
1358
 
@@ -1369,7 +1360,7 @@ const cleanData = befly.db.cleanFields({ name: 'John', phone: '', age: null }, [
1369
1360
 
1370
1361
  ```typescript
1371
1362
  // 即使值在排除列表中,也保留 status 字段的 null 值
1372
- 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 });
1373
1364
  // 结果: { name: 'John', status: null, count: 0 }
1374
1365
  ```
1375
1366
 
@@ -1407,7 +1398,7 @@ fields: {
1407
1398
  ```typescript
1408
1399
  // ✅ 推荐:直接使用
1409
1400
  const result = await befly.db.insData({
1410
- table: 'user',
1401
+ table: "user",
1411
1402
  data: {
1412
1403
  username: ctx.body.username,
1413
1404
  email: ctx.body.email
@@ -1423,7 +1414,7 @@ const { username, email } = ctx.body;
1423
1414
  ```typescript
1424
1415
  // ✅ 推荐:明确每个字段
1425
1416
  await befly.db.insData({
1426
- table: 'user',
1417
+ table: "user",
1427
1418
  data: {
1428
1419
  username: ctx.body.username,
1429
1420
  email: ctx.body.email,
@@ -1433,7 +1424,7 @@ await befly.db.insData({
1433
1424
 
1434
1425
  // ❌ 避免:扩展运算符
1435
1426
  await befly.db.insData({
1436
- table: 'user',
1427
+ table: "user",
1437
1428
  data: { ...ctx.body } // 危险!可能写入未预期的字段
1438
1429
  });
1439
1430
  ```
@@ -1445,12 +1436,12 @@ handler: async (befly, ctx) => {
1445
1436
  try {
1446
1437
  // 业务逻辑
1447
1438
  const result = await someOperation();
1448
- return befly.tool.Yes('成功', result);
1439
+ return befly.tool.Yes("成功", result);
1449
1440
  } catch (error: any) {
1450
1441
  // 记录错误日志
1451
- befly.logger.error({ err: error }, '操作失败');
1442
+ befly.logger.error({ err: error }, "操作失败");
1452
1443
  // 返回友好错误信息
1453
- return befly.tool.No('操作失败,请稍后重试');
1444
+ return befly.tool.No("操作失败,请稍后重试");
1454
1445
  }
1455
1446
  };
1456
1447
  ```
@@ -1460,7 +1451,7 @@ handler: async (befly, ctx) => {
1460
1451
  ```typescript
1461
1452
  // ✅ 推荐:使用 Date.now()
1462
1453
  await befly.db.updData({
1463
- table: 'user',
1454
+ table: "user",
1464
1455
  data: {
1465
1456
  lastLoginTime: Date.now(), // number 类型
1466
1457
  lastLoginIp: ctx.ip
@@ -1480,7 +1471,7 @@ lastLoginTime: new Date(); // 类型不一致
1480
1471
 
1481
1472
  ```typescript
1482
1473
  export default {
1483
- name: '公开接口',
1474
+ name: "公开接口",
1484
1475
  auth: false, // 设置为 false
1485
1476
  handler: async (befly, ctx) => {
1486
1477
  // ...
@@ -1496,7 +1487,7 @@ handler: async (befly, ctx) => {
1496
1487
  const roleCode = ctx.user?.roleCode;
1497
1488
 
1498
1489
  if (!userId) {
1499
- return befly.tool.No('未登录');
1490
+ return befly.tool.No("未登录");
1500
1491
  }
1501
1492
  };
1502
1493
  ```
@@ -1511,12 +1502,12 @@ handler: async (befly, ctx) => {
1511
1502
 
1512
1503
  ```typescript
1513
1504
  // tpl/hooks/requestLog.ts
1514
- import type { Hook } from 'befly/types/hook';
1505
+ import type { Hook } from "befly/types/hook";
1515
1506
 
1516
1507
  const hook: Hook = {
1517
1508
  order: 100, // 执行顺序
1518
1509
  handler: async (befly, ctx) => {
1519
- befly.logger.info({ route: ctx.route }, '请求开始');
1510
+ befly.logger.info({ route: ctx.route }, "请求开始");
1520
1511
  }
1521
1512
  };
1522
1513
  export default hook;
@@ -1540,42 +1531,42 @@ export default hook;
1540
1531
  ```typescript
1541
1532
  // apis/order/create.ts
1542
1533
  export default {
1543
- name: '创建订单',
1534
+ name: "创建订单",
1544
1535
  fields: {
1545
1536
  productId: {
1546
- name: '商品ID',
1547
- type: 'number',
1537
+ name: "商品ID",
1538
+ type: "number",
1548
1539
  min: 1
1549
1540
  },
1550
1541
  quantity: {
1551
- name: '数量',
1552
- type: 'number',
1542
+ name: "数量",
1543
+ type: "number",
1553
1544
  min: 1,
1554
1545
  max: 999
1555
1546
  }
1556
1547
  },
1557
- required: ['productId', 'quantity'],
1548
+ required: ["productId", "quantity"],
1558
1549
  handler: async (befly, ctx) => {
1559
1550
  // 使用事务确保库存扣减和订单创建的原子性
1560
1551
  const result = await befly.db.transaction(async (trx) => {
1561
1552
  // 1. 查询商品信息(带锁)
1562
1553
  const product = await trx.getOne({
1563
- table: 'product',
1554
+ table: "product",
1564
1555
  where: { id: ctx.body.productId },
1565
1556
  forUpdate: true // 行锁
1566
1557
  });
1567
1558
 
1568
1559
  if (!product) {
1569
- throw new Error('商品不存在');
1560
+ throw new Error("商品不存在");
1570
1561
  }
1571
1562
 
1572
1563
  if (product.stock < ctx.body.quantity) {
1573
- throw new Error('库存不足');
1564
+ throw new Error("库存不足");
1574
1565
  }
1575
1566
 
1576
1567
  // 2. 扣减库存
1577
1568
  await trx.updData({
1578
- table: 'product',
1569
+ table: "product",
1579
1570
  data: {
1580
1571
  stock: product.stock - ctx.body.quantity
1581
1572
  },
@@ -1584,19 +1575,19 @@ export default {
1584
1575
 
1585
1576
  // 3. 创建订单
1586
1577
  const orderId = await trx.insData({
1587
- table: 'order',
1578
+ table: "order",
1588
1579
  data: {
1589
1580
  userId: ctx.user.id,
1590
1581
  productId: ctx.body.productId,
1591
1582
  quantity: ctx.body.quantity,
1592
1583
  totalPrice: product.price * ctx.body.quantity,
1593
- status: 'pending'
1584
+ status: "pending"
1594
1585
  }
1595
1586
  });
1596
1587
 
1597
1588
  // 4. 创建订单明细
1598
1589
  await trx.insData({
1599
- table: 'order_item',
1590
+ table: "order_item",
1600
1591
  data: {
1601
1592
  orderId: orderId,
1602
1593
  productId: ctx.body.productId,
@@ -1609,7 +1600,7 @@ export default {
1609
1600
  return { orderId: orderId };
1610
1601
  });
1611
1602
 
1612
- return befly.tool.Yes('订单创建成功', result);
1603
+ return befly.tool.Yes("订单创建成功", result);
1613
1604
  }
1614
1605
  };
1615
1606
  ```
@@ -1621,22 +1612,22 @@ export default {
1621
1612
  ```typescript
1622
1613
  // apis/user/batchImport.ts
1623
1614
  export default {
1624
- name: '批量导入用户',
1615
+ name: "批量导入用户",
1625
1616
  rawBody: true, // 保留原始请求体
1626
1617
  handler: async (befly, ctx) => {
1627
1618
  const users = ctx.body.users;
1628
1619
 
1629
1620
  if (!Array.isArray(users) || users.length === 0) {
1630
- return befly.tool.No('用户列表不能为空');
1621
+ return befly.tool.No("用户列表不能为空");
1631
1622
  }
1632
1623
 
1633
1624
  if (users.length > 100) {
1634
- return befly.tool.No('单次导入不能超过100条');
1625
+ return befly.tool.No("单次导入不能超过100条");
1635
1626
  }
1636
1627
 
1637
1628
  // 批量插入
1638
1629
  const result = await befly.db.batchInsert({
1639
- table: 'user',
1630
+ table: "user",
1640
1631
  data: users.map((user: any) => ({
1641
1632
  username: user.username,
1642
1633
  email: user.email,
@@ -1645,7 +1636,7 @@ export default {
1645
1636
  }))
1646
1637
  });
1647
1638
 
1648
- return befly.tool.Yes('导入成功', {
1639
+ return befly.tool.Yes("导入成功", {
1649
1640
  total: users.length,
1650
1641
  inserted: result.affectedRows
1651
1642
  });
@@ -1658,25 +1649,25 @@ export default {
1658
1649
  ```typescript
1659
1650
  // apis/article/batchUpdate.ts
1660
1651
  export default {
1661
- name: '批量更新文章状态',
1652
+ name: "批量更新文章状态",
1662
1653
  rawBody: true,
1663
1654
  handler: async (befly, ctx) => {
1664
1655
  const { ids, state } = ctx.body;
1665
1656
 
1666
1657
  if (!Array.isArray(ids) || ids.length === 0) {
1667
- return befly.tool.No('文章ID列表不能为空');
1658
+ return befly.tool.No("文章ID列表不能为空");
1668
1659
  }
1669
1660
 
1670
1661
  // 批量更新
1671
1662
  const result = await befly.db.updData({
1672
- table: 'article',
1663
+ table: "article",
1673
1664
  data: { state: state },
1674
1665
  where: {
1675
1666
  id: { $in: ids }
1676
1667
  }
1677
1668
  });
1678
1669
 
1679
- return befly.tool.Yes('更新成功', {
1670
+ return befly.tool.Yes("更新成功", {
1680
1671
  updated: result.affectedRows
1681
1672
  });
1682
1673
  }
@@ -1688,24 +1679,24 @@ export default {
1688
1679
  ```typescript
1689
1680
  // apis/log/batchDelete.ts
1690
1681
  export default {
1691
- name: '批量删除日志',
1682
+ name: "批量删除日志",
1692
1683
  rawBody: true,
1693
1684
  handler: async (befly, ctx) => {
1694
1685
  const { ids } = ctx.body;
1695
1686
 
1696
1687
  if (!Array.isArray(ids) || ids.length === 0) {
1697
- return befly.tool.No('日志ID列表不能为空');
1688
+ return befly.tool.No("日志ID列表不能为空");
1698
1689
  }
1699
1690
 
1700
1691
  // 批量软删除
1701
1692
  const result = await befly.db.delData({
1702
- table: 'operate_log',
1693
+ table: "operate_log",
1703
1694
  where: {
1704
1695
  id: { $in: ids }
1705
1696
  }
1706
1697
  });
1707
1698
 
1708
- return befly.tool.Yes('删除成功', {
1699
+ return befly.tool.Yes("删除成功", {
1709
1700
  deleted: result.affectedRows
1710
1701
  });
1711
1702
  }
@@ -1767,25 +1758,25 @@ export default {
1767
1758
  ```typescript
1768
1759
  // apis/article/listWithAuthor.ts
1769
1760
  export default {
1770
- name: '文章列表(含作者)',
1761
+ name: "文章列表(含作者)",
1771
1762
  handler: async (befly, ctx) => {
1772
1763
  const result = await befly.db.getList({
1773
- table: 'article',
1764
+ table: "article",
1774
1765
  joins: [
1775
1766
  {
1776
- type: 'LEFT',
1777
- table: 'user',
1778
- alias: 'author',
1779
- on: { 'article.authorId': 'author.id' }
1767
+ type: "LEFT",
1768
+ table: "user",
1769
+ alias: "author",
1770
+ on: { "article.authorId": "author.id" }
1780
1771
  }
1781
1772
  ],
1782
- columns: ['article.id', 'article.title', 'article.createdAt', 'author.nickname AS authorName'],
1773
+ columns: ["article.id", "article.title", "article.createdAt", "author.nickname AS authorName"],
1783
1774
  page: ctx.body.page || 1,
1784
1775
  limit: ctx.body.limit || 10,
1785
- orderBy: ['article.createdAt#DESC']
1776
+ orderBy: ["article.createdAt#DESC"]
1786
1777
  });
1787
1778
 
1788
- return befly.tool.Yes('获取成功', result);
1779
+ return befly.tool.Yes("获取成功", result);
1789
1780
  }
1790
1781
  };
1791
1782
  ```
@@ -1795,30 +1786,29 @@ export default {
1795
1786
  ```typescript
1796
1787
  // apis/config/getSiteConfig.ts
1797
1788
  export default {
1798
- name: '获取站点配置',
1789
+ name: "获取站点配置",
1799
1790
  auth: false,
1800
- cache: 300, // 缓存 5 分钟
1801
1791
  handler: async (befly, ctx) => {
1802
1792
  // 先从缓存获取
1803
- const cacheKey = 'site:config';
1793
+ const cacheKey = "site:config";
1804
1794
  let config = await befly.redis.get(cacheKey);
1805
1795
 
1806
1796
  if (!config) {
1807
1797
  // 缓存不存在,从数据库查询
1808
1798
  const result = await befly.db.getAll({
1809
- table: 'sys_config',
1799
+ table: "sys_config",
1810
1800
  where: { state: 1 }
1811
1801
  });
1812
1802
 
1813
1803
  config = result.lists; // 获取配置列表
1814
1804
 
1815
1805
  // 写入缓存
1816
- await befly.redis.set(cacheKey, JSON.stringify(config), 'EX', 300);
1806
+ await befly.redis.set(cacheKey, JSON.stringify(config), "EX", 300);
1817
1807
  } else {
1818
1808
  config = JSON.parse(config);
1819
1809
  }
1820
1810
 
1821
- return befly.tool.Yes('获取成功', config);
1811
+ return befly.tool.Yes("获取成功", config);
1822
1812
  }
1823
1813
  };
1824
1814
  ```
@@ -1828,22 +1818,22 @@ export default {
1828
1818
  ```typescript
1829
1819
  // apis/task/execute.ts
1830
1820
  export default {
1831
- name: '执行定时任务',
1821
+ name: "执行定时任务",
1832
1822
  handler: async (befly, ctx) => {
1833
1823
  const lockKey = `lock:task:${ctx.body.taskId}`;
1834
1824
 
1835
1825
  // 尝试获取锁(30秒超时)
1836
- 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");
1837
1827
 
1838
1828
  if (!locked) {
1839
- return befly.tool.No('任务正在执行中,请稍后');
1829
+ return befly.tool.No("任务正在执行中,请稍后");
1840
1830
  }
1841
1831
 
1842
1832
  try {
1843
1833
  // 执行任务逻辑
1844
1834
  await executeTask(ctx.body.taskId);
1845
1835
 
1846
- return befly.tool.Yes('任务执行成功');
1836
+ return befly.tool.Yes("任务执行成功");
1847
1837
  } finally {
1848
1838
  // 释放锁
1849
1839
  await befly.redis.del(lockKey);
@@ -1857,27 +1847,27 @@ export default {
1857
1847
  ```typescript
1858
1848
  // apis/report/exportUsers.ts
1859
1849
  export default {
1860
- name: '导出用户数据',
1850
+ name: "导出用户数据",
1861
1851
  handler: async (befly, ctx) => {
1862
1852
  // 查询所有用户(不分页,注意上限 10000 条)
1863
1853
  const result = await befly.db.getAll({
1864
- table: 'user',
1865
- columns: ['id', 'username', 'nickname', 'email', 'phone', 'createdAt'],
1854
+ table: "user",
1855
+ columns: ["id", "username", "nickname", "email", "phone", "createdAt"],
1866
1856
  where: { state: 1 },
1867
- orderBy: ['createdAt#DESC']
1857
+ orderBy: ["createdAt#DESC"]
1868
1858
  });
1869
1859
 
1870
1860
  // 转换为 CSV 格式
1871
- const headers = ['ID', '用户名', '昵称', '邮箱', '手机', '注册时间'];
1861
+ const headers = ["ID", "用户名", "昵称", "邮箱", "手机", "注册时间"];
1872
1862
  const rows = result.lists.map((u: any) => [u.id, u.username, u.nickname, u.email, u.phone, new Date(u.createdAt).toLocaleString()]);
1873
1863
 
1874
- 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");
1875
1865
 
1876
1866
  // 返回 CSV 文件(注意:如果 total > 10000,只会导出前 10000 条)
1877
1867
  return new Response(csv, {
1878
1868
  headers: {
1879
- 'Content-Type': 'text/csv; charset=utf-8',
1880
- 'Content-Disposition': 'attachment; filename="users.csv"'
1869
+ "Content-Type": "text/csv; charset=utf-8",
1870
+ "Content-Disposition": 'attachment; filename="users.csv"'
1881
1871
  }
1882
1872
  });
1883
1873
  }
@@ -1889,17 +1879,17 @@ export default {
1889
1879
  ```typescript
1890
1880
  // apis/file/download.ts
1891
1881
  export default {
1892
- name: '文件下载',
1893
- required: ['fileId'],
1882
+ name: "文件下载",
1883
+ required: ["fileId"],
1894
1884
  handler: async (befly, ctx) => {
1895
1885
  // 查询文件信息
1896
1886
  const file = await befly.db.getOne({
1897
- table: 'file',
1887
+ table: "file",
1898
1888
  where: { id: ctx.body.fileId }
1899
1889
  });
1900
1890
 
1901
1891
  if (!file) {
1902
- return befly.tool.No('文件不存在');
1892
+ return befly.tool.No("文件不存在");
1903
1893
  }
1904
1894
 
1905
1895
  // 读取文件并返回流
@@ -1907,9 +1897,9 @@ export default {
1907
1897
 
1908
1898
  return new Response(fileStream, {
1909
1899
  headers: {
1910
- 'Content-Type': file.mimeType,
1911
- 'Content-Disposition': `attachment; filename="${encodeURIComponent(file.name)}"`,
1912
- 'Content-Length': String(file.size)
1900
+ "Content-Type": file.mimeType,
1901
+ "Content-Disposition": `attachment; filename="${encodeURIComponent(file.name)}"`,
1902
+ "Content-Length": String(file.size)
1913
1903
  }
1914
1904
  });
1915
1905
  }