befly-tpl 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.env.development +83 -0
  2. package/LICENSE +201 -0
  3. package/README.md +20 -0
  4. package/README.ts.md +175 -0
  5. package/addon-loader.example.ts +99 -0
  6. package/addons/_template/README.md +123 -0
  7. package/addons/_template/addon.config.json +17 -0
  8. package/addons/_template/apis/example.ts +33 -0
  9. package/addons/_template/checks/example.ts +23 -0
  10. package/addons/_template/config/default.ts +12 -0
  11. package/addons/_template/plugins/example.ts +39 -0
  12. package/addons/_template/tables/example.json +7 -0
  13. package/addons/_template/types/index.d.ts +21 -0
  14. package/addons/admin/README.md +179 -0
  15. package/addons/admin/addon.config.json +13 -0
  16. package/addons/admin/apis/adminDel.ts +37 -0
  17. package/addons/admin/apis/adminInfo.ts +50 -0
  18. package/addons/admin/apis/adminIns.ts +70 -0
  19. package/addons/admin/apis/adminList.ts +24 -0
  20. package/addons/admin/apis/adminRoleDetail.ts +38 -0
  21. package/addons/admin/apis/adminRoleSave.ts +40 -0
  22. package/addons/admin/apis/adminUpd.ts +54 -0
  23. package/addons/admin/apis/apiAll.ts +38 -0
  24. package/addons/admin/apis/cacheRefresh.ts +36 -0
  25. package/addons/admin/apis/dashboardAddonList.ts +16 -0
  26. package/addons/admin/apis/dashboardChangelog.ts +41 -0
  27. package/addons/admin/apis/dashboardConfigStatus.ts +56 -0
  28. package/addons/admin/apis/dashboardEnvironmentInfo.ts +48 -0
  29. package/addons/admin/apis/dashboardPerformanceMetrics.ts +25 -0
  30. package/addons/admin/apis/dashboardPermissionStats.ts +33 -0
  31. package/addons/admin/apis/dashboardServiceStatus.ts +84 -0
  32. package/addons/admin/apis/dashboardSystemInfo.ts +34 -0
  33. package/addons/admin/apis/dashboardSystemOverview.ts +34 -0
  34. package/addons/admin/apis/dashboardSystemResources.ts +119 -0
  35. package/addons/admin/apis/dictAll.ts +26 -0
  36. package/addons/admin/apis/dictDel.ts +27 -0
  37. package/addons/admin/apis/dictDetail.ts +28 -0
  38. package/addons/admin/apis/dictIns.ts +38 -0
  39. package/addons/admin/apis/dictList.ts +27 -0
  40. package/addons/admin/apis/dictUpd.ts +44 -0
  41. package/addons/admin/apis/login.ts +123 -0
  42. package/addons/admin/apis/logout.ts +23 -0
  43. package/addons/admin/apis/menuAll.ts +70 -0
  44. package/addons/admin/apis/menuDel.ts +40 -0
  45. package/addons/admin/apis/menuIns.ts +31 -0
  46. package/addons/admin/apis/menuList.ts +26 -0
  47. package/addons/admin/apis/menuUpd.ts +41 -0
  48. package/addons/admin/apis/register.ts +50 -0
  49. package/addons/admin/apis/roleApiDetail.ts +34 -0
  50. package/addons/admin/apis/roleApiSave.ts +44 -0
  51. package/addons/admin/apis/roleDel.ts +48 -0
  52. package/addons/admin/apis/roleDetail.ts +28 -0
  53. package/addons/admin/apis/roleIns.ts +40 -0
  54. package/addons/admin/apis/roleList.ts +18 -0
  55. package/addons/admin/apis/roleMenuDetail.ts +33 -0
  56. package/addons/admin/apis/roleMenuSave.ts +39 -0
  57. package/addons/admin/apis/roleSave.ts +45 -0
  58. package/addons/admin/apis/roleUpd.ts +53 -0
  59. package/addons/admin/apis/sendSmsCode.ts +36 -0
  60. package/addons/admin/checks/admin.ts +36 -0
  61. package/addons/admin/config/index.ts +45 -0
  62. package/addons/admin/config/menu.json +44 -0
  63. package/addons/admin/scripts/syncApi.ts +285 -0
  64. package/addons/admin/scripts/syncDev.ts +203 -0
  65. package/addons/admin/scripts/syncMenu.ts +210 -0
  66. package/addons/admin/tables/admin.json +14 -0
  67. package/addons/admin/tables/api.json +8 -0
  68. package/addons/admin/tables/dict.json +8 -0
  69. package/addons/admin/tables/menu.json +8 -0
  70. package/addons/admin/tables/role.json +8 -0
  71. package/addons/admin/types/index.ts +44 -0
  72. package/addons/admin/util.ts +266 -0
  73. package/addons/befly/addon.config.json +13 -0
  74. package/addons/befly/apis/health/info.ts +77 -0
  75. package/addons/befly/apis/tool/tokenCheck.ts +52 -0
  76. package/addons/demo/README.md +62 -0
  77. package/addons/demo/addon.config.json +13 -0
  78. package/addons/demo/apis/demoIns.ts +36 -0
  79. package/addons/demo/apis/demoList.ts +36 -0
  80. package/addons/demo/checks/demo.ts +30 -0
  81. package/addons/demo/config/default.ts +17 -0
  82. package/addons/demo/plugins/tool.ts +61 -0
  83. package/addons/demo/tables/todo.json +6 -0
  84. package/addons/demo/types/index.d.ts +56 -0
  85. package/apis/article/articleDel.ts +33 -0
  86. package/apis/article/articleDetail.ts +26 -0
  87. package/apis/article/articleIns.ts +47 -0
  88. package/apis/article/articleList.ts +47 -0
  89. package/apis/article/articleUpd.ts +55 -0
  90. package/apis/article/increment.ts +37 -0
  91. package/apis/test/hi.ts +9 -0
  92. package/apis/user/login.ts +56 -0
  93. package/apis/user/userList.ts +40 -0
  94. package/bun.lock +140 -0
  95. package/checks/demo.ts +25 -0
  96. package/logs/2025-08-22.0.log +197 -0
  97. package/logs/2025-08-23.0.log +151 -0
  98. package/logs/2025-08-24.0.log +296 -0
  99. package/logs/2025-08-25.0.log +162 -0
  100. package/logs/2025-08-26.0.log +19 -0
  101. package/logs/2025-08-27.0.log +63 -0
  102. package/logs/2025-08-28.0.log +286 -0
  103. package/logs/2025-08-30.0.log +1 -0
  104. package/logs/2025-09-01.0.log +296 -0
  105. package/logs/2025-09-02.0.log +298 -0
  106. package/logs/2025-10-11.0.log +2718 -0
  107. package/logs/2025-10-12.0.log +4374 -0
  108. package/logs/2025-10-13.0.log +759 -0
  109. package/logs/2025-10-14.0.log +2350 -0
  110. package/logs/2025-10-15.0.log +2386 -0
  111. package/logs/2025-10-16.0.log +2807 -0
  112. package/logs/2025-10-17.0.log +1143 -0
  113. package/logs/2025-10-18.0.log +1292 -0
  114. package/logs/2025-10-19.0.log +1752 -0
  115. package/logs/2025-10-20.0.log +722 -0
  116. package/logs/2025-10-21.0.log +1075 -0
  117. package/logs/2025-10-23.0.log +3291 -0
  118. package/logs/2025-10-24.0.log +2341 -0
  119. package/logs/2025-10-25.0.log +1367 -0
  120. package/logs/debug.0.log +25174 -0
  121. package/main.ts +9 -0
  122. package/package.json +29 -0
  123. package/pm2.config.cjs +85 -0
  124. package/tables/article.json +11 -0
  125. package/tables/user.json +8 -0
  126. package/temp/addon-route-prefix-migration.md +400 -0
  127. package/temp/api-route-conflict-analysis.md +441 -0
  128. package/temp/interactive-cli-guide.md +199 -0
  129. package/temp/missing-apis-fix.md +362 -0
  130. package/temp/remove-status-field.md +239 -0
  131. package/temp/roleid-to-rolecode-optimization.md +321 -0
  132. package/temp/status-to-state-migration-complete.md +176 -0
  133. package/temp/syncMenu-guide.md +235 -0
  134. package/temp/test-admin-menus-cache.ts +125 -0
  135. package/temp/test-admin-menus.ts +110 -0
  136. package/temp/test-interactive-cli.ps1 +14 -0
  137. package/tests/core.test.ts +13 -0
  138. package/tsconfig.json +23 -0
  139. package/types/api.ts +128 -0
  140. package/types/index.ts +6 -0
  141. package/types/models.example.ts +267 -0
  142. package/types/models.ts +67 -0
@@ -0,0 +1,26 @@
1
+ import { Yes, No } from 'befly';
2
+ import type { ApiRoute } from 'befly';
3
+
4
+ /**
5
+ * 获取所有字典(不分页)
6
+ */
7
+ export default {
8
+ name: '获取所有字典',
9
+ handler: async (befly, ctx) => {
10
+ try {
11
+ const dicts = await befly.db.getAll({
12
+ table: 'addon_admin_dict',
13
+ fields: ['id', 'name', 'code', 'value', 'sort', 'pid', 'description', 'state', 'created_at', 'updated_at'],
14
+ orderBy: [
15
+ { field: 'sort', direction: 'ASC' },
16
+ { field: 'id', direction: 'ASC' }
17
+ ]
18
+ });
19
+
20
+ return Yes('操作成功', dicts);
21
+ } catch (error) {
22
+ befly.logger.error('获取所有字典失败:', error);
23
+ return No('操作失败');
24
+ }
25
+ }
26
+ } as ApiRoute;
@@ -0,0 +1,27 @@
1
+ import { Fields, Yes, No } from 'befly';
2
+ import type { ApiRoute } from 'befly';
3
+
4
+ /**
5
+ * 删除字典
6
+ */
7
+ export default {
8
+ name: '删除字典',
9
+ fields: {
10
+ id: Fields._id
11
+ },
12
+ handler: async (befly, ctx) => {
13
+ try {
14
+ await befly.db.delData({
15
+ table: 'addon_admin_dict',
16
+ where: {
17
+ id: ctx.body.id
18
+ }
19
+ });
20
+
21
+ return Yes('操作成功');
22
+ } catch (error) {
23
+ befly.logger.error('删除字典失败:', error);
24
+ return No('操作失败');
25
+ }
26
+ }
27
+ } as ApiRoute;
@@ -0,0 +1,28 @@
1
+ import { Fields, Yes, No } from 'befly';
2
+ import type { ApiRoute } from 'befly';
3
+
4
+ /**
5
+ * 获取字典详情
6
+ */
7
+ export default {
8
+ name: '获取字典详情',
9
+ fields: {
10
+ id: Fields._id
11
+ },
12
+ handler: async (befly, ctx) => {
13
+ try {
14
+ const dict = await befly.db.getDetail({
15
+ table: 'addon_admin_dict',
16
+ fields: ['id', 'name', 'code', 'value', 'sort', 'pid', 'description', 'state', 'created_at', 'updated_at'],
17
+ where: {
18
+ id: ctx.body.id
19
+ }
20
+ });
21
+
22
+ return Yes('操作成功', dict);
23
+ } catch (error) {
24
+ befly.logger.error('获取字典详情失败:', error);
25
+ return No('操作失败');
26
+ }
27
+ }
28
+ } as ApiRoute;
@@ -0,0 +1,38 @@
1
+ import { Yes, No } from 'befly';
2
+ import type { ApiRoute } from 'befly';
3
+ import adminDictTable from '../tables/dict.json';
4
+
5
+ /**
6
+ * 添加字典
7
+ */
8
+ export default {
9
+ name: '添加字典',
10
+ fields: {
11
+ name: adminDictTable.name,
12
+ code: adminDictTable.code,
13
+ value: adminDictTable.value,
14
+ sort: adminDictTable.sort,
15
+ pid: adminDictTable.pid,
16
+ description: adminDictTable.description
17
+ },
18
+ handler: async (befly, ctx) => {
19
+ try {
20
+ const dictId = await befly.db.insData({
21
+ table: 'addon_admin_dict',
22
+ data: {
23
+ name: ctx.body.name,
24
+ code: ctx.body.code,
25
+ value: ctx.body.value,
26
+ sort: ctx.body.sort,
27
+ pid: ctx.body.pid,
28
+ description: ctx.body.description
29
+ }
30
+ });
31
+
32
+ return Yes('操作成功', { id: dictId });
33
+ } catch (error) {
34
+ befly.logger.error('添加字典失败:', error);
35
+ return No('操作失败');
36
+ }
37
+ }
38
+ } as ApiRoute;
@@ -0,0 +1,27 @@
1
+ import { Fields, Yes, No } from 'befly';
2
+ import type { ApiRoute } from 'befly';
3
+
4
+ /**
5
+ * 获取字典列表
6
+ */
7
+ export default {
8
+ name: '获取字典列表',
9
+ fields: {
10
+ page: Fields.page,
11
+ limit: Fields.limit
12
+ },
13
+ handler: async (befly, ctx) => {
14
+ const result = await befly.db.getList({
15
+ table: 'addon_admin_dict',
16
+ fields: ['id', 'name', 'code', 'value', 'sort', 'pid', 'description', 'state', 'created_at', 'updated_at'],
17
+ page: ctx.body.page,
18
+ limit: ctx.body.limit,
19
+ orderBy: [
20
+ { field: 'sort', direction: 'ASC' },
21
+ { field: 'id', direction: 'ASC' }
22
+ ]
23
+ });
24
+
25
+ return Yes('操作成功', result);
26
+ }
27
+ } as ApiRoute;
@@ -0,0 +1,44 @@
1
+ import { Fields, Yes, No } from 'befly';
2
+ import type { ApiRoute } from 'befly';
3
+ import adminDictTable from '../tables/dict.json';
4
+
5
+ /**
6
+ * 更新字典
7
+ */
8
+ export default {
9
+ name: '更新字典',
10
+ fields: {
11
+ id: Fields._id,
12
+ name: adminDictTable.name,
13
+ code: adminDictTable.code,
14
+ value: adminDictTable.value,
15
+ sort: adminDictTable.sort,
16
+ pid: adminDictTable.pid,
17
+ description: adminDictTable.description,
18
+ state: Fields.state
19
+ },
20
+ handler: async (befly, ctx) => {
21
+ try {
22
+ await befly.db.updData({
23
+ table: 'addon_admin_dict',
24
+ data: {
25
+ name: ctx.body.name,
26
+ code: ctx.body.code,
27
+ value: ctx.body.value,
28
+ sort: ctx.body.sort,
29
+ pid: ctx.body.pid,
30
+ description: ctx.body.description,
31
+ state: ctx.body.state
32
+ },
33
+ where: {
34
+ id: ctx.body.id
35
+ }
36
+ });
37
+
38
+ return Yes('操作成功');
39
+ } catch (error) {
40
+ befly.logger.error('更新字典失败:', error);
41
+ return No('操作失败');
42
+ }
43
+ }
44
+ } as ApiRoute;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * 管理员登录接口
3
+ */
4
+
5
+ import { Yes, No } from 'befly';
6
+ import { Crypto2 } from 'befly';
7
+ import { Jwt } from 'befly';
8
+ import adminTable from '../tables/admin.json';
9
+
10
+ export default {
11
+ name: '管理员登录',
12
+ auth: false,
13
+ fields: {
14
+ email: adminTable.email,
15
+ password: adminTable.password,
16
+ phone: adminTable.phone,
17
+ code: '验证码|string|4|6|null|0|null'
18
+ },
19
+ handler: async (befly, ctx) => {
20
+ let admin = null;
21
+
22
+ // 邮箱登录
23
+ if (ctx.body.email && ctx.body.password) {
24
+ // 查询管理员
25
+ admin = await befly.db.getOne({
26
+ table: 'addon_admin_admin',
27
+ where: { email: ctx.body.email }
28
+ });
29
+
30
+ if (!admin) {
31
+ return No('邮箱或密码错误');
32
+ }
33
+
34
+ // 验证密码
35
+ try {
36
+ const isValid = await Crypto2.verifyPassword(ctx.body.password, admin.password);
37
+ if (!isValid) {
38
+ return No('邮箱或密码错误');
39
+ }
40
+ } catch (error) {
41
+ befly.logger.error('密码验证失败:', {
42
+ error: error.message,
43
+ passwordLength: admin.password?.length,
44
+ passwordPrefix: admin.password?.substring(0, 10)
45
+ });
46
+ return No('密码格式错误,请重新设置密码');
47
+ }
48
+ }
49
+ // 手机号登录
50
+ else if (ctx.body.phone && ctx.body.code) {
51
+ // 验证验证码
52
+ if (befly.redis) {
53
+ const key = `sms_code:${ctx.body.phone}`;
54
+ const storedCode = await befly.redis.get(key);
55
+
56
+ if (!storedCode) {
57
+ return No('验证码已过期或不存在');
58
+ }
59
+
60
+ if (storedCode !== ctx.body.code) {
61
+ return No('验证码错误');
62
+ }
63
+
64
+ // 验证成功后删除验证码
65
+ await befly.redis.del(key);
66
+ } else {
67
+ // 没有 Redis 时的降级处理(开发环境)
68
+ if (ctx.body.code !== '123456') {
69
+ return No('验证码错误');
70
+ }
71
+ }
72
+
73
+ // 查询管理员
74
+ admin = await befly.db.getOne({
75
+ table: 'addon_admin_admin',
76
+ where: { phone: ctx.body.phone }
77
+ });
78
+
79
+ if (!admin) {
80
+ return No('该手机号未注册');
81
+ }
82
+ } else {
83
+ return No('请提供正确的登录信息');
84
+ }
85
+
86
+ // 检查账号状态(state=1 表示正常,state=2 表示禁用)
87
+ if (admin.state === 2) {
88
+ return No('账号已被禁用');
89
+ }
90
+
91
+ // 更新最后登录信息
92
+ await befly.db.updData({
93
+ table: 'addon_admin_admin',
94
+ where: { id: admin.id },
95
+ data: {
96
+ lastLoginTime: Date.now(),
97
+ lastLoginIp: ctx.ip || 'unknown'
98
+ }
99
+ });
100
+
101
+ // 生成 JWT Token(包含核心身份信息)
102
+ const token = await Jwt.sign(
103
+ {
104
+ id: admin.id,
105
+ email: admin.email,
106
+ nickname: admin.nickname,
107
+ roleCode: admin.roleCode,
108
+ roleType: admin.roleType
109
+ },
110
+ {
111
+ expiresIn: '7d'
112
+ }
113
+ );
114
+
115
+ // 返回用户信息(不包含密码)
116
+ const { password: _, ...userWithoutPassword } = admin;
117
+
118
+ return Yes('登录成功', {
119
+ token,
120
+ userInfo: userWithoutPassword
121
+ });
122
+ }
123
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 退出登录接口
3
+ */
4
+
5
+ import { Yes } from 'befly';
6
+
7
+ export default {
8
+ name: '退出登录',
9
+ handler: async (befly, ctx) => {
10
+ // JWT token 是无状态的,前端删除 token 即可
11
+ // 如果需要实现 token 黑名单,可以在这里将 token 加入 Redis 黑名单
12
+
13
+ const token = ctx.headers.authorization?.replace('Bearer ', '');
14
+
15
+ if (token && befly.redis) {
16
+ // 将 token 加入黑名单,有效期设置为 token 的剩余有效期
17
+ const key = `token_blacklist:${token}`;
18
+ await befly.redis.set(key, '1', 'EX', 7 * 24 * 60 * 60); // 7天
19
+ }
20
+
21
+ return Yes('退出成功');
22
+ }
23
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 获取当前用户的菜单权限
3
+ * 说明:
4
+ * 1. 从 Redis 缓存读取所有菜单(如果缓存不存在则从数据库查询并缓存)
5
+ * 2. 根据当前登录用户的角色过滤可访问的菜单
6
+ * 3. 返回一维数组(由前端构建树形结构)
7
+ * 4. 仅返回状态为启用的菜单
8
+ */
9
+
10
+ import { Yes, No } from 'befly';
11
+
12
+ export default {
13
+ name: '获取用户菜单',
14
+ auth: true,
15
+ handler: async (befly, ctx) => {
16
+ try {
17
+ // 2. 查询角色信息获取菜单权限(使用 roleCode 而非 roleId)
18
+ const role = await befly.db.getOne({
19
+ table: 'addon_admin_role',
20
+ where: { code: ctx.user.roleCode }
21
+ });
22
+
23
+ if (!role || !role.menus) {
24
+ return No('角色不存在', []);
25
+ }
26
+
27
+ // 3. 解析菜单ID列表(逗号分隔的字符串)
28
+ const menuIds = role.menus
29
+ .split(',')
30
+ .map((id: string) => parseInt(id.trim()))
31
+ .filter((id: number) => !isNaN(id));
32
+
33
+ if (menuIds.length === 0) {
34
+ return Yes('菜单为空', []);
35
+ }
36
+
37
+ // 4. 从 Redis 缓存读取所有菜单
38
+ let allMenus = await befly.redis.getObject<any[]>('menus:all');
39
+
40
+ // 如果缓存不存在,从数据库查询并缓存
41
+ if (!allMenus || allMenus.length === 0) {
42
+ befly.logger.info('菜单缓存未命中,从数据库查询');
43
+ allMenus = await befly.db.getAll({
44
+ table: 'addon_admin_menu',
45
+ fields: ['id', 'pid', 'name', 'path', 'icon', 'type', 'sort'],
46
+ orderBy: ['sort#ASC', 'id#ASC']
47
+ });
48
+
49
+ // 回写缓存
50
+ if (allMenus.length > 0) {
51
+ await befly.redis.setObject('menus:all', allMenus);
52
+ befly.logger.info(`已缓存 ${allMenus.length} 个菜单到 Redis`);
53
+ }
54
+ } else {
55
+ befly.logger.debug(`从 Redis 缓存读取 ${allMenus.length} 个菜单`);
56
+ // JSON.parse 会保持数字类型,无需额外转换
57
+ }
58
+
59
+ // 5. 根据角色权限过滤菜单
60
+ const menuIdSet = new Set(menuIds.map(String)); // 转为字符串 Set 方便比较
61
+ const authorizedMenus = allMenus.filter((menu: any) => menuIdSet.has(String(menu.id)));
62
+
63
+ // 6. 返回一维数组(由前端构建树形结构)
64
+ return Yes('获取菜单成功', authorizedMenus);
65
+ } catch (error) {
66
+ befly.logger.error('获取用户菜单失败:', error);
67
+ return No('获取菜单失败');
68
+ }
69
+ }
70
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 删除菜单
3
+ */
4
+
5
+ import { Yes, No, Fields } from 'befly';
6
+
7
+ export default {
8
+ name: '删除菜单',
9
+ fields: {
10
+ id: Fields._id
11
+ },
12
+ handler: async (befly, ctx) => {
13
+ try {
14
+ // 检查是否有子菜单(使用 getList 代替 getAll)
15
+ const childrenList = await befly.db.getList({
16
+ table: 'addon_admin_menu',
17
+ where: { pid: ctx.body.id }
18
+ });
19
+
20
+ if (childrenList.total > 0) {
21
+ return No('该菜单下有子菜单,无法删除');
22
+ }
23
+
24
+ // 删除菜单
25
+ await befly.db.delData({
26
+ table: 'addon_admin_menu',
27
+ where: { id: ctx.body.id }
28
+ });
29
+
30
+ // 注意:菜单权限现在存储在 role.menus 字段中
31
+ // 如果需要从角色权限中移除此菜单,需要额外处理
32
+ // 这里暂时不处理,由管理员在角色管理界面手动调整
33
+
34
+ return Yes('操作成功');
35
+ } catch (error) {
36
+ befly.logger.error('删除菜单失败:', error);
37
+ return No('操作失败');
38
+ }
39
+ }
40
+ };
@@ -0,0 +1,31 @@
1
+ import { Yes, No } from 'befly';
2
+ import adminMenuTable from '../tables/menu.json';
3
+
4
+ /**
5
+ * 创建菜单
6
+ */
7
+ export default {
8
+ name: '创建菜单',
9
+ fields: {
10
+ name: adminMenuTable.name,
11
+ path: adminMenuTable.path,
12
+ icon: adminMenuTable.icon,
13
+ sort: adminMenuTable.sort,
14
+ pid: adminMenuTable.pid,
15
+ type: adminMenuTable.type
16
+ // state 由框架自动设置为 1(正常)
17
+ },
18
+ handler: async (befly, ctx) => {
19
+ try {
20
+ const menuId = await befly.db.insData({
21
+ table: 'addon_admin_menu',
22
+ data: ctx.body
23
+ });
24
+
25
+ return Yes('操作成功', { id: menuId });
26
+ } catch (error) {
27
+ befly.logger.error('创建菜单失败:', error);
28
+ return No('操作失败');
29
+ }
30
+ }
31
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 获取所有菜单列表
3
+ * 说明:用于后台管理的菜单配置页面
4
+ */
5
+
6
+ import { Yes, No } from 'befly';
7
+ export default {
8
+ name: '获取菜单列表',
9
+ handler: async (befly, ctx) => {
10
+ try {
11
+ const menus = await befly.db.getAll({
12
+ table: 'addon_admin_menu',
13
+ fields: ['id', 'name', 'path', 'icon', 'sort', 'pid', 'type', 'state', 'created_at', 'updated_at'],
14
+ orderBy: [
15
+ { field: 'sort', direction: 'ASC' },
16
+ { field: 'id', direction: 'ASC' }
17
+ ]
18
+ });
19
+
20
+ return Yes('操作成功', menus);
21
+ } catch (error) {
22
+ befly.logger.error('获取菜单列表失败:', error);
23
+ return No('操作失败');
24
+ }
25
+ }
26
+ };
@@ -0,0 +1,41 @@
1
+ import { Yes, No, Fields } from 'befly';
2
+ import adminMenuTable from '../tables/menu.json';
3
+
4
+ /**
5
+ * 更新菜单
6
+ */
7
+ export default {
8
+ name: '更新菜单',
9
+ fields: {
10
+ id: Fields._id,
11
+ name: adminMenuTable.name,
12
+ path: adminMenuTable.path,
13
+ icon: adminMenuTable.icon,
14
+ sort: adminMenuTable.sort,
15
+ pid: adminMenuTable.pid,
16
+ type: adminMenuTable.type
17
+ // state 需要禁用时传 2,启用时传 1
18
+ },
19
+ handler: async (befly, ctx) => {
20
+ try {
21
+ await befly.db.updData({
22
+ table: 'addon_admin_menu',
23
+ where: { id: ctx.body.id },
24
+ data: {
25
+ name: ctx.body.name,
26
+ path: ctx.body.path,
27
+ icon: ctx.body.icon,
28
+ sort: ctx.body.sort,
29
+ pid: ctx.body.pid,
30
+ type: ctx.body.type
31
+ // state 字段不在此处更新,需要禁用/启用时单独处理
32
+ }
33
+ });
34
+
35
+ return Yes('操作成功');
36
+ } catch (error) {
37
+ befly.logger.error('更新菜单失败:', error);
38
+ return No('操作失败');
39
+ }
40
+ }
41
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 管理员注册接口
3
+ */
4
+
5
+ import { Yes, No } from 'befly';
6
+
7
+ import { Crypto2 } from 'befly';
8
+ import adminTable from '../tables/admin.json';
9
+
10
+ export default {
11
+ name: '管理员注册',
12
+ auth: false,
13
+ fields: {
14
+ name: adminTable.name,
15
+ email: adminTable.email,
16
+ password: adminTable.password
17
+ },
18
+ required: ['name', 'email', 'password'],
19
+ handler: async (befly, ctx) => {
20
+ // 检查邮箱是否已存在
21
+ const existingAdmin = await befly.db.getOne({
22
+ table: 'addon_admin_admin',
23
+ where: { email: ctx.body.email }
24
+ });
25
+
26
+ if (existingAdmin) {
27
+ return No('该邮箱已被注册');
28
+ }
29
+
30
+ // 加密密码
31
+ const hashedPassword = await Crypto2.hashPassword(ctx.body.password);
32
+
33
+ // 创建管理员
34
+ const adminId = await befly.db.insData({
35
+ table: 'addon_admin_admin',
36
+ data: {
37
+ name: ctx.body.name,
38
+ email: ctx.body.email,
39
+ password: hashedPassword,
40
+ role: 'user' // 默认为普通用户,state 由框架自动设置为 1
41
+ }
42
+ });
43
+
44
+ return Yes('注册成功', {
45
+ id: adminId,
46
+ name: ctx.body.name,
47
+ email: ctx.body.email
48
+ });
49
+ }
50
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 获取角色的接口权限
3
+ */
4
+
5
+ import { Yes, No, Fields } from 'befly';
6
+
7
+ export default {
8
+ name: '获取角色接口权限',
9
+ auth: true,
10
+ fields: {
11
+ roleId: Fields._id
12
+ },
13
+ handler: async (befly, ctx) => {
14
+ // 查询角色信息
15
+ const role = await befly.db.getOne({
16
+ table: 'addon_admin_role',
17
+ where: { id: ctx.body.roleId }
18
+ });
19
+
20
+ if (!role) {
21
+ return No('角色不存在');
22
+ }
23
+
24
+ // 解析接口ID列表(逗号分隔的字符串转为数组)
25
+ const apiIds = role.apis
26
+ ? role.apis
27
+ .split(',')
28
+ .map((id: string) => parseInt(id.trim()))
29
+ .filter((id: number) => !isNaN(id))
30
+ : [];
31
+
32
+ return Yes('操作成功', { apiIds });
33
+ }
34
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 保存角色的接口权限
3
+ */
4
+
5
+ import { Yes, No, Fields } from 'befly';
6
+ import { cacheRolePermissions } from '../util';
7
+ import adminRoleTable from '../tables/role.json';
8
+
9
+ export default {
10
+ name: '保存角色接口权限',
11
+ auth: true,
12
+ fields: {
13
+ roleId: Fields._id,
14
+ apiIds: adminRoleTable.apis
15
+ },
16
+ handler: async (befly, ctx) => {
17
+ // 查询角色是否存在
18
+ const role = await befly.db.getOne({
19
+ table: 'addon_admin_role',
20
+ where: { id: ctx.body.roleId }
21
+ });
22
+
23
+ if (!role) {
24
+ return No('角色不存在');
25
+ }
26
+
27
+ // 将数组转为逗号分隔的字符串存储
28
+ const apiIdsStr = Array.isArray(ctx.body.apiIds) ? ctx.body.apiIds.join(',') : '';
29
+
30
+ // 更新角色的接口权限
31
+ await befly.db.updData({
32
+ table: 'addon_admin_role',
33
+ where: { id: ctx.body.roleId },
34
+ data: {
35
+ apis: apiIdsStr
36
+ }
37
+ });
38
+
39
+ // 增量更新 Redis 缓存
40
+ await cacheRolePermissions(befly, role.code, apiIdsStr);
41
+
42
+ return Yes('操作成功');
43
+ }
44
+ };