befly-tpl 3.2.2 → 3.2.5

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 (40) hide show
  1. package/README.md +240 -14
  2. package/package.json +21 -3
  3. package/addon-loader.example.ts +0 -99
  4. package/bun.lock +0 -140
  5. package/logs/2025-08-22.0.log +0 -197
  6. package/logs/2025-08-23.0.log +0 -151
  7. package/logs/2025-08-24.0.log +0 -296
  8. package/logs/2025-08-25.0.log +0 -162
  9. package/logs/2025-08-26.0.log +0 -19
  10. package/logs/2025-08-27.0.log +0 -63
  11. package/logs/2025-08-28.0.log +0 -286
  12. package/logs/2025-08-30.0.log +0 -1
  13. package/logs/2025-09-01.0.log +0 -296
  14. package/logs/2025-09-02.0.log +0 -298
  15. package/logs/2025-10-11.0.log +0 -2718
  16. package/logs/2025-10-12.0.log +0 -4374
  17. package/logs/2025-10-13.0.log +0 -759
  18. package/logs/2025-10-14.0.log +0 -2350
  19. package/logs/2025-10-15.0.log +0 -2386
  20. package/logs/2025-10-16.0.log +0 -2807
  21. package/logs/2025-10-17.0.log +0 -1143
  22. package/logs/2025-10-18.0.log +0 -1292
  23. package/logs/2025-10-19.0.log +0 -1752
  24. package/logs/2025-10-20.0.log +0 -722
  25. package/logs/2025-10-21.0.log +0 -1075
  26. package/logs/2025-10-23.0.log +0 -3291
  27. package/logs/2025-10-24.0.log +0 -2341
  28. package/logs/2025-10-25.0.log +0 -1367
  29. package/logs/debug.0.log +0 -25174
  30. package/temp/addon-route-prefix-migration.md +0 -400
  31. package/temp/api-route-conflict-analysis.md +0 -441
  32. package/temp/interactive-cli-guide.md +0 -199
  33. package/temp/missing-apis-fix.md +0 -362
  34. package/temp/remove-status-field.md +0 -239
  35. package/temp/roleid-to-rolecode-optimization.md +0 -321
  36. package/temp/status-to-state-migration-complete.md +0 -176
  37. package/temp/syncMenu-guide.md +0 -235
  38. package/temp/test-admin-menus-cache.ts +0 -125
  39. package/temp/test-admin-menus.ts +0 -110
  40. package/temp/test-interactive-cli.ps1 +0 -14
@@ -1,441 +0,0 @@
1
- # API 路由冲突分析报告
2
-
3
- **日期**:2025-10-19
4
- **分析人**:AI Assistant
5
-
6
- ## 📋 问题描述
7
-
8
- 用户提出的问题:
9
-
10
- > 如果 admin 组件中有 list 接口,tpl 项目的 apis 目录下有 admin/list.ts,这2个接口路径会不会重复,会不会冲突?
11
-
12
- 具体场景:
13
-
14
- 1. **Addon API**:`packages/tpl/addons/admin/apis/list.ts`
15
- 2. **项目 API**:`packages/tpl/apis/admin/list.ts`
16
-
17
- ## 🔍 深度分析
18
-
19
- ### 1. 路由生成规则
20
-
21
- 根据 `packages/core/lifecycle/loader.ts` 的代码分析(Line 493-497):
22
-
23
- ```typescript
24
- // 构建路由:addon 接口添加前缀 /api/{addonName}/{apiPath}
25
- if (isAddon) {
26
- api.route = `${api.method.toUpperCase()}/api/${addonName}/${apiPath}`;
27
- } else {
28
- api.route = `${api.method.toUpperCase()}/api/${apiPath}`;
29
- }
30
- apiRoutes.set(api.route, api);
31
- ```
32
-
33
- **关键发现**:
34
-
35
- - **Addon API** 会添加 addon 名称前缀:`/api/{addonName}/{apiPath}`
36
- - **项目 API** 只有基础前缀:`/api/{apiPath}`
37
- - 路由以 `Map` 结构存储,key 为 `${METHOD}${PATH}`
38
-
39
- ### 2. 实际路径映射
40
-
41
- #### 场景 A:Addon 中的 list.ts
42
-
43
- **文件位置**:
44
-
45
- ```
46
- packages/tpl/addons/admin/apis/list.ts
47
- ```
48
-
49
- **生成的路由**:
50
-
51
- ```
52
- POST/api/admin/list
53
- ```
54
-
55
- **计算过程**:
56
-
57
- - `isAddon = true`
58
- - `addonName = 'admin'`
59
- - `apiPath = 'list'`(相对于 apis 目录的路径)
60
- - `method = 'POST'`(默认)
61
- - 结果:`POST/api/admin/list`
62
-
63
- #### 场景 B:项目中的 admin/list.ts
64
-
65
- **文件位置**:
66
-
67
- ```
68
- packages/tpl/apis/admin/list.ts
69
- ```
70
-
71
- **生成的路由**:
72
-
73
- ```
74
- POST/api/admin/list
75
- ```
76
-
77
- **计算过程**:
78
-
79
- - `isAddon = false`
80
- - `apiPath = 'admin/list'`(相对于 apis 目录的路径)
81
- - `method = 'POST'`(默认)
82
- - 结果:`POST/api/admin/list`
83
-
84
- ### 3. 冲突分析结论
85
-
86
- **⚠️ 会发生路由冲突!**
87
-
88
- 两个接口生成的路由完全相同:
89
-
90
- ```
91
- POST/api/admin/list
92
- ```
93
-
94
- ## 🚨 冲突后果
95
-
96
- ### 1. Map 覆盖问题
97
-
98
- ```typescript
99
- apiRoutes.set(api.route, api);
100
- ```
101
-
102
- - `apiRoutes` 是 `Map` 结构
103
- - 如果 key 相同,后加载的会**覆盖**先加载的
104
- - **最终只有一个接口生效**
105
-
106
- ### 2. 加载顺序
107
-
108
- 根据 `packages/core/lifecycle/lifecycle.ts` 的加载顺序(Line 70-120):
109
-
110
- ```typescript
111
- // 实际的加载顺序
112
- 1. 先加载所有 addon 的 APIs(按 addon 名称顺序)
113
- 2. 再加载项目(app)的 APIs
114
- ```
115
-
116
- **验证代码**:
117
-
118
- ```typescript
119
- // 1. 先加载 addon APIs
120
- for (const addon of addons) {
121
- if (hasApis) {
122
- await Loader.loadApis(addon, this.apiRoutes, {
123
- isAddon: true,
124
- addonName: addon
125
- });
126
- }
127
- }
128
-
129
- // 2. 后加载 app APIs
130
- await Loader.loadApis('app', this.apiRoutes);
131
- ```
132
-
133
- **如果发生冲突**:
134
-
135
- - Addon 的 `list.ts` 先注册 → `POST/api/admin/list`
136
- - 项目的 `admin/list.ts` 后注册 → **覆盖** addon 的接口
137
- - **结果**:addon 的接口失效,项目接口生效,且**没有任何警告**
138
-
139
- ### 3. 实际影响
140
-
141
- ❌ **严重问题**:
142
-
143
- - 用户无法预期哪个接口会生效
144
- - 调试困难,没有冲突警告
145
- - 可能导致接口行为异常
146
-
147
- ## 🎯 为什么会设计成这样?
148
-
149
- ### 当前的路径设计逻辑
150
-
151
- ```
152
- Addon API 路径格式:
153
- /api/{addonName}/{apiPath}
154
-
155
- 项目 API 路径格式:
156
- /api/{apiPath}
157
- ```
158
-
159
- **设计意图**:
160
-
161
- - Addon 用 addon 名称作为命名空间隔离
162
- - 项目 API 可以自由组织目录结构
163
-
164
- ### 存在的问题
165
-
166
- **场景 1**:Addon 名称与项目目录重名
167
-
168
- ```
169
- ✅ Addon: addons/admin/apis/list.ts → /api/admin/list
170
- ❌ 项目: apis/admin/list.ts → /api/admin/list
171
- ⚠️ 冲突!
172
- ```
173
-
174
- **场景 2**:Addon 名称与项目目录不重名
175
-
176
- ```
177
- ✅ Addon: addons/admin/apis/list.ts → /api/admin/list
178
- ✅ 项目: apis/user/list.ts → /api/user/list
179
- ✅ 不冲突
180
- ```
181
-
182
- **场景 3**:项目 API 在根目录
183
-
184
- ```
185
- ✅ Addon: addons/admin/apis/list.ts → /api/admin/list
186
- ✅ 项目: apis/list.ts → /api/list
187
- ✅ 不冲突
188
- ```
189
-
190
- ## 📊 实际案例验证
191
-
192
- ### 当前项目结构
193
-
194
- **Addon APIs**:
195
-
196
- ```
197
- packages/tpl/addons/admin/apis/
198
- ├── login.ts → POST/api/admin/login
199
- ├── register.ts → POST/api/admin/register
200
- ├── menuList.ts → POST/api/admin/menuList
201
- ├── roleList.ts → POST/api/admin/roleList
202
- └── ... (共16个文件)
203
- ```
204
-
205
- **项目 APIs**:
206
-
207
- ```
208
- packages/tpl/apis/
209
- ├── article/
210
- │ ├── list.ts → POST/api/article/list
211
- │ ├── create.ts → POST/api/article/create
212
- │ └── ...
213
- ├── user/
214
- │ ├── list.ts → POST/api/user/list
215
- │ └── login.ts → POST/api/user/login
216
- └── test/
217
- ```
218
-
219
- **当前状态**:
220
-
221
- - ✅ **没有冲突**:因为项目 API 使用 `article/`、`user/` 等目录
222
- - ✅ **addon 名称为 `admin`**,项目中没有 `admin/` 目录
223
-
224
- ### 潜在冲突场景
225
-
226
- **如果创建**:
227
-
228
- ```
229
- packages/tpl/apis/admin/list.ts
230
- ```
231
-
232
- **冲突分析**:
233
-
234
- ```
235
- Addon: addons/admin/apis/list.ts → POST/api/admin/list ❌
236
- 项目: apis/admin/list.ts → POST/api/admin/list ❌
237
- ⚠️ 路由冲突!
238
- ```
239
-
240
- ## 💡 解决方案建议
241
-
242
- ### 方案 1:修改 Addon 路由前缀(推荐)
243
-
244
- **修改 loader.ts**:
245
-
246
- ```typescript
247
- // 当前
248
- if (isAddon) {
249
- api.route = `${api.method.toUpperCase()}/api/${addonName}/${apiPath}`;
250
- }
251
-
252
- // 建议改为
253
- if (isAddon) {
254
- api.route = `${api.method.toUpperCase()}/api/addon/${addonName}/${apiPath}`;
255
- }
256
- ```
257
-
258
- **效果**:
259
-
260
- ```
261
- Addon: addons/admin/apis/list.ts → POST/api/addon/admin/list ✅
262
- 项目: apis/admin/list.ts → POST/api/admin/list ✅
263
- ✅ 不冲突!
264
- ```
265
-
266
- **优点**:
267
-
268
- - 完全隔离 addon 和项目 API
269
- - 路径更清晰,一看就知道是 addon 接口
270
- - 符合命名空间最佳实践
271
-
272
- **缺点**:
273
-
274
- - 需要修改现有接口路径(破坏性变更)
275
- - 前端调用需要更新
276
-
277
- ### 方案 2:添加冲突检测
278
-
279
- **修改 loader.ts**:
280
-
281
- ```typescript
282
- // 在注册路由前检查
283
- if (apiRoutes.has(api.route)) {
284
- const existing = apiRoutes.get(api.route);
285
- Logger.error(`路由冲突检测:${api.route}`);
286
- Logger.error(`已存在的接口:${existing.name}`);
287
- Logger.error(`新接口:${api.name}`);
288
- throw new Error(`路由冲突:${api.route} 已被注册`);
289
- }
290
- apiRoutes.set(api.route, api);
291
- ```
292
-
293
- **优点**:
294
-
295
- - 立即发现冲突
296
- - 不改变路由规则
297
- - 强制开发者解决冲突
298
-
299
- **缺点**:
300
-
301
- - 不解决根本问题
302
- - 开发者需要手动避免冲突
303
-
304
- ### 方案 3:项目规范约束(临时方案)
305
-
306
- **在 AGENTS.md 中添加规范**:
307
-
308
- ```markdown
309
- ## API 路由命名规范
310
-
311
- ### 禁止事项
312
-
313
- ❌ 禁止在项目 apis 目录下创建与 addon 名称相同的目录
314
-
315
- ### 允许的目录结构
316
-
317
- ✅ apis/article/
318
- ✅ apis/user/
319
- ✅ apis/product/
320
- ❌ apis/admin/ (admin 是 addon 名称)
321
- ❌ apis/befly/ (befly 是 addon 名称)
322
-
323
- ### Addon 名称清单
324
-
325
- - admin
326
- - befly
327
- - demo
328
- ```
329
-
330
- **优点**:
331
-
332
- - 不需要修改代码
333
- - 立即可用
334
-
335
- **缺点**:
336
-
337
- - 依赖开发者自觉
338
- - 容易被遗忘
339
- - 没有技术保障
340
-
341
- ### 方案 4:混合方案(最佳实践)
342
-
343
- 结合方案 1 和方案 2:
344
-
345
- 1. **立即**:添加冲突检测(方案 2)
346
- 2. **短期**:添加项目规范(方案 3)
347
- 3. **长期**:修改 addon 路由前缀为 `/api/addon/{name}/`(方案 1)
348
-
349
- ## 📋 影响评估
350
-
351
- ### 如果保持现状(不修改)
352
-
353
- **风险等级**:🔴 **高风险**
354
-
355
- **可能出现的问题**:
356
-
357
- 1. 开发者创建 `apis/admin/` 目录
358
- 2. 路由冲突,addon 接口失效
359
- 3. 前端调用报错,但日志没有明确提示
360
- 4. 调试困难,浪费时间
361
-
362
- **发生概率**:
363
-
364
- - ⚠️ **中高概率**:`admin` 是常见的目录名
365
-
366
- ### 如果添加冲突检测
367
-
368
- **风险等级**:🟡 **中等风险**
369
-
370
- **效果**:
371
-
372
- - ✅ 能立即发现冲突
373
- - ✅ 阻止错误部署
374
- - ⚠️ 开发者需要重命名目录
375
-
376
- ### 如果修改 addon 路由前缀
377
-
378
- **风险等级**:🟢 **低风险**
379
-
380
- **效果**:
381
-
382
- - ✅ 彻底解决冲突问题
383
- - ✅ 路径更清晰
384
- - ⚠️ 需要迁移现有接口
385
-
386
- ## 🎯 最终建议
387
-
388
- ### 立即执行(当前版本)
389
-
390
- **1. 添加冲突检测**:
391
-
392
- ```typescript
393
- // 在 loader.ts 的 apiRoutes.set() 之前
394
- if (apiRoutes.has(api.route)) {
395
- const existing = apiRoutes.get(api.route);
396
- throw new Error(`❌ 路由冲突:${api.route}\n` + `已存在:${existing.name} (${isAddon ? 'Addon' : '项目'})\n` + `新接口:${api.name} (${isAddon ? 'Addon' : '项目'})\n` + `解决方案:避免项目 apis 目录与 addon 名称重名`);
397
- }
398
- apiRoutes.set(api.route, api);
399
- ```
400
-
401
- **2. 更新 AGENTS.md**:
402
-
403
- - 添加 API 路由命名规范
404
- - 列出所有 addon 名称
405
- - 禁止在项目中使用同名目录
406
-
407
- ### 后续优化(下一个版本)
408
-
409
- **修改 addon 路由前缀**:
410
-
411
- - Addon API:`/api/addon/{name}/{path}`
412
- - 项目 API:`/api/{path}`
413
- - 完全隔离,永久解决冲突
414
-
415
- ## 📝 总结
416
-
417
- **问题答案**:
418
-
419
- > **会冲突!** 如果 addon admin 中有 `list.ts`,项目中又创建 `apis/admin/list.ts`,两个接口会生成相同的路由 `POST/api/admin/list`,后加载的会覆盖先加载的。
420
-
421
- **核心原因**:
422
-
423
- - Addon API 路径格式:`/api/{addonName}/{apiPath}`
424
- - 项目 API 路径格式:`/api/{apiPath}`
425
- - 当项目目录名与 addon 名称相同时,会产生冲突
426
-
427
- **解决方向**:
428
-
429
- 1. **短期**:添加冲突检测 + 项目规范
430
- 2. **长期**:修改 addon 路由前缀为 `/api/addon/{name}/`
431
-
432
- **当前状态**:
433
-
434
- - ✅ 项目中没有 `apis/admin/` 目录,暂时没有冲突
435
- - ⚠️ 需要预防未来可能的冲突
436
-
437
- ---
438
-
439
- **分析人**:AI Assistant
440
- **建议优先级**:🔴 **高优先级**(添加冲突检测)
441
- **最后更新**:2025-10-19
@@ -1,199 +0,0 @@
1
- # Befly CLI 交互式脚本管理器使用指南
2
-
3
- ## 📋 核心特性
4
-
5
- ### 1. 纯交互模式
6
-
7
- - 运行 `bunx befly` 进入交互模式
8
- - 通过数字选择要执行的脚本
9
- - 所有参数通过交互式问答添加
10
-
11
- ### 2. 脚本分类
12
-
13
- - **内置脚本**:来自 `core/scripts`(如 `syncDb`、`syncDev`)
14
- - **项目脚本**:来自 `tpl/scripts`(如 `syncDev`)
15
- - **组件脚本**:来自 `tpl/addons/*/scripts`(如 `initMenu`、`syncDev`)
16
-
17
- ### 3. 脚本执行规则
18
-
19
- - 选择哪个数字,就执行对应的脚本
20
- - 即使有同名脚本,也会精确执行选中的那个
21
- - 列表按来源分组:内置脚本 → 项目脚本 → 组件脚本
22
-
23
- 例如,有 3 个 `syncDev`:
24
-
25
- ```
26
- 1. syncDb (内置)
27
- 2. syncDev (内置)
28
- 3. syncDev (项目)
29
- 4. initMenu (组件)
30
- 5. syncDev (组件)
31
- ```
32
-
33
- - 选择 2 → 执行内置的 syncDev
34
- - 选择 3 → 执行项目的 syncDev
35
- - 选择 5 → 执行组件的 syncDev
36
-
37
- ## 🎯 使用方法
38
-
39
- ### 启动交互模式
40
-
41
- ```bash
42
- bunx befly
43
- # 或者
44
- bunx befly <任何参数> # 参数会被忽略,统一进入交互模式
45
- ```
46
-
47
- ### 显示效果
48
-
49
- ```
50
- 🚀 Befly CLI - 脚本管理器
51
-
52
- 📦 内置脚本:
53
- 1. syncDb
54
- 2. syncDev
55
-
56
- 📦 项目脚本:
57
- 3. syncDev
58
-
59
- 📦 组件脚本:
60
- 4. initMenu
61
- 5. syncDev
62
-
63
- 请输入脚本编号 (输入 0 或直接回车退出):
64
- ```
65
-
66
- ### 操作流程
67
-
68
- 1. **选择脚本**:输入数字(1-5),或输入 0/回车退出
69
- 2. **添加参数**:提示是否添加 `--plan` 参数(y/n,默认 n)
70
- 3. **确认执行**:提示是否执行(y/n,默认 y)
71
-
72
- ### 示例:选择脚本 1(syncDb)并添加 --plan
73
-
74
- ```bash
75
- bunx befly
76
-
77
- 请输入脚本编号 (输入 0 或直接回车退出): 1
78
-
79
- ✅ 已选择: syncDb
80
- 是否添加 --plan 参数? (y/n,直接回车默认为 n): y
81
- 是否执行? (y/n,直接回车默认为 y): y
82
-
83
- # 开始执行 syncDb --plan
84
- ```
85
-
86
- ## 开发说明
87
-
88
- ### 添加新脚本
89
-
90
- 1. **内置脚本**:在 `core/scripts/` 下创建 `.ts` 文件
91
- 2. **项目脚本**:在 `tpl/scripts/` 下创建 `.ts` 文件
92
- 3. **组件脚本**:在 `tpl/addons/<addonName>/scripts/` 下创建 `.ts` 文件
93
-
94
- CLI 会自动扫描这些目录,无需配置。
95
-
96
- ### 脚本命名规范
97
-
98
- - 使用小驼峰或下划线命名(如 `syncDb`、`initMenu`)
99
- - 文件扩展名为 `.ts`
100
- - 脚本名称会自动去除 `.ts` 后缀
101
-
102
- ### 脚本模板
103
-
104
- ```typescript
105
- #!/usr/bin/env bun
106
- import { Env, Logger } from 'befly';
107
-
108
- // 从命令行参数获取 --plan 标志
109
- const DRY_RUN = process.argv.includes('--plan');
110
-
111
- async function main() {
112
- if (DRY_RUN) {
113
- Logger.info('[计划] 这里描述脚本将要执行的操作');
114
- return;
115
- }
116
-
117
- // 实际执行逻辑
118
- Logger.info('开始执行...');
119
- // ...
120
- }
121
-
122
- main().catch((error) => {
123
- Logger.error('执行失败:', error);
124
- process.exit(1);
125
- });
126
- ```
127
-
128
- ## 🔍 常见问题
129
-
130
- ### Q: 如何知道执行了哪个来源的脚本?
131
-
132
- A: 在日志中会显示脚本路径,或者在脚本内添加日志标识:
133
-
134
- ```typescript
135
- Logger.info('【项目脚本】syncDev 开始执行...');
136
- ```
137
-
138
- ### Q: 为什么我的脚本没有显示?
139
-
140
- A: 检查以下几点:
141
-
142
- 1. 文件扩展名是否为 `.ts`
143
- 2. 文件是否在正确的目录下(`core/scripts`、`tpl/scripts` 或 `tpl/addons/*/scripts`)
144
- 3. 文件名是否符合命名规范
145
-
146
- ### Q: 如何执行特定来源的脚本?
147
-
148
- A: 使用数字选择即可精确执行:
149
-
150
- ```bash
151
- bunx befly
152
-
153
- # 列表显示:
154
- # 1. syncDb (内置)
155
- # 2. syncDev (内置)
156
- # 3. syncDev (项目)
157
- # 4. initMenu (组件)
158
- # 5. syncDev (组件)
159
-
160
- # 输入 2 → 执行内置的 syncDev
161
- # 输入 3 → 执行项目的 syncDev
162
- # 输入 5 → 执行组件的 syncDev
163
- ```
164
-
165
- ### Q: 如何快速执行脚本而不每次都要交互?
166
-
167
- A: 目前仅支持交互模式,这样可以:
168
-
169
- - 避免误执行错误的脚本
170
- - 清晰看到所有可用脚本
171
- - 灵活添加 --plan 等参数
172
- - 执行前再次确认
173
-
174
- 如果需要自动化执行,建议直接使用 `bun` 运行脚本文件:
175
-
176
- ```bash
177
- # 直接执行脚本文件
178
- bun packages/core/scripts/syncDb.ts --plan
179
- bun packages/tpl/scripts/syncDev.ts
180
- ```
181
-
182
- ### Q: --plan 参数有什么用?
183
-
184
- A: 用于预演模式,脚本会输出将要执行的操作但不实际执行,适合:
185
-
186
- - 查看脚本将做什么
187
- - 验证参数是否正确
188
- - 避免误操作
189
-
190
- ## 📊 技术实现
191
-
192
- - **脚本扫描**:使用 `Bun.Glob` 扫描目录
193
- - **脚本执行**:使用 `Bun.spawn` 执行子进程
194
- - **交互输入**:使用 Node.js `readline` 模块
195
- - **纯交互模式**:所有执行都通过数字选择,无传统命令行模式
196
-
197
- ---
198
-
199
- **最后更新**: 2025-10-19