chanjs 2.7.3 → 2.7.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 (93) hide show
  1. package/USAGE.md +533 -0
  2. package/config/index.js +37 -6
  3. package/core/App.js +166 -0
  4. package/core/Container.js +77 -0
  5. package/core/Controller.js +29 -0
  6. package/core/Database.js +93 -0
  7. package/core/Repository.js +327 -0
  8. package/core/Service.js +11 -0
  9. package/core/bootstrap/error-handler.js +104 -0
  10. package/core/bootstrap/hook-runner.js +64 -0
  11. package/core/bootstrap/middleware.js +35 -0
  12. package/core/bootstrap/router-loader.js +53 -0
  13. package/core/errors.js +224 -0
  14. package/core/loader.js +89 -0
  15. package/core/registry.js +17 -0
  16. package/doc/Cache.md +279 -106
  17. package/doc/Common.md +590 -134
  18. package/doc/Controller.md +166 -95
  19. package/doc/Help.md +299 -698
  20. package/doc/QuickStart.md +116 -0
  21. package/doc/Repository.md +560 -0
  22. package/doc/Service.md +201 -527
  23. package/index.js +75 -37
  24. package/middleware/body.js +17 -0
  25. package/middleware/cookie.js +7 -15
  26. package/middleware/cors.js +9 -27
  27. package/middleware/favicon.js +7 -17
  28. package/middleware/header.js +15 -16
  29. package/middleware/index.js +11 -11
  30. package/middleware/log.js +26 -56
  31. package/middleware/static.js +15 -28
  32. package/middleware/template.js +75 -115
  33. package/middleware/validate.js +79 -0
  34. package/middleware/waf.js +174 -197
  35. package/package.json +11 -3
  36. package/response/code.js +73 -0
  37. package/response/index.js +9 -6
  38. package/response/response.js +82 -236
  39. package/security/checker.js +26 -74
  40. package/security/index.js +4 -9
  41. package/security/jwt.js +69 -142
  42. package/security/keywords.js +32 -136
  43. package/security/rate-limit.js +38 -80
  44. package/security/sign.js +83 -176
  45. package/security/xss-filter.js +21 -53
  46. package/storage/cache.js +57 -196
  47. package/storage/index.js +3 -6
  48. package/storage/redis.js +123 -181
  49. package/storage/store.js +163 -188
  50. package/utils/data-parse.js +42 -186
  51. package/utils/file.js +73 -244
  52. package/utils/filter.js +22 -25
  53. package/utils/html.js +49 -33
  54. package/utils/index.js +21 -7
  55. package/utils/ip.js +31 -71
  56. package/utils/logger.js +117 -0
  57. package/utils/pages.js +55 -0
  58. package/utils/paths.js +18 -0
  59. package/utils/request.js +94 -136
  60. package/utils/signal.js +87 -0
  61. package/utils/time.js +33 -75
  62. package/utils/tree.js +112 -104
  63. package/App.js +0 -533
  64. package/base/Aop.js +0 -195
  65. package/base/Container.js +0 -161
  66. package/base/Controller.js +0 -65
  67. package/base/Database.js +0 -133
  68. package/base/Event.js +0 -61
  69. package/base/Repository.js +0 -644
  70. package/common/api.js +0 -35
  71. package/common/code.js +0 -52
  72. package/common/email.js +0 -191
  73. package/common/index.js +0 -5
  74. package/common/pages.js +0 -120
  75. package/common/utils.js +0 -73
  76. package/config/code.js +0 -166
  77. package/config/paths.js +0 -60
  78. package/doc/Aop.md +0 -269
  79. package/doc/Email.md +0 -114
  80. package/doc/Event.md +0 -232
  81. package/global/env.js +0 -11
  82. package/global/import.js +0 -39
  83. package/global/index.js +0 -8
  84. package/helper/index.js +0 -79
  85. package/loader/index.js +0 -6
  86. package/loader/loader.js +0 -138
  87. package/middleware/compress.js +0 -185
  88. package/middleware/setBody.js +0 -32
  89. package/realtime/index.js +0 -7
  90. package/realtime/sse.js +0 -424
  91. package/realtime/websocket.js +0 -540
  92. package/schedule/index.js +0 -6
  93. package/schedule/schedule.js +0 -491
package/common/code.js DELETED
@@ -1,52 +0,0 @@
1
- /**
2
- * 响应状态码常量定义(旧版对象形式,保留向后兼容)
3
- *
4
- * ============================================================
5
- * 重要说明
6
- * ============================================================
7
- * 业务侧推荐使用 config/code.js 的三层 code 体系(0/1xxx/5xxx/6xxx),
8
- * 该文件提供完整的 CODE 字典、CODE_RANGE 段位常量、语义化别名、DB_ERROR 映射。
9
- *
10
- * 本文件的 CODE 对象为旧版形式,仅用于兼容历史代码。
11
- * code 值已对齐新体系,不再使用 200/201/500。
12
- *
13
- * @deprecated 推荐使用 config/code.js 的新体系
14
- */
15
-
16
- export const CODE = {
17
- /**
18
- * 操作成功(code: 0,不再用 200)
19
- */
20
- SUCCESS: {
21
- code: 0,
22
- message: "操作成功",
23
- },
24
- /**
25
- * 操作失败(code: 1008 业务处理失败)
26
- */
27
- FAIL: {
28
- code: 1008,
29
- message: "操作失败",
30
- },
31
- /**
32
- * 系统错误(code: 5001 系统内部错误)
33
- */
34
- ERROR: {
35
- code: 5001,
36
- message: "系统错误",
37
- },
38
- /**
39
- * 未授权(code: 1001 认证失败)
40
- */
41
- UNAUTHORIZED: {
42
- code: 1001,
43
- message: "认证失败",
44
- },
45
- /**
46
- * 禁止访问(code: 1003 权限不足)
47
- */
48
- FORBIDDEN: {
49
- code: 1003,
50
- message: "权限不足",
51
- },
52
- };
package/common/email.js DELETED
@@ -1,191 +0,0 @@
1
- import nodemailer from "nodemailer";
2
- import Chan from "../App.js";
3
-
4
- /**
5
- * 邮件发送工具
6
- * 提供邮件发送和HTML模板生成功能
7
- * 通过 import Chan 获取应用配置(不依赖 global.Chan)
8
- */
9
-
10
- /**
11
- * 邮箱地址正则(基础校验,防注入和拼写错误)
12
- */
13
- const EMAIL_REGEX = /^[^\s@<>'"`]+@[^\s@<>'"`]+\.[^\s@<>'"`]+$/;
14
-
15
- /**
16
- * 模块级 transporter 缓存(避免每次发邮件都创建连接池)
17
- * 配置不变时复用同一个 transporter,提升性能
18
- */
19
- let _transporter = null;
20
- let _transporterKey = '';
21
-
22
- /**
23
- * HTML 特殊字符转义,防止邮件模板注入
24
- * @param {string} str - 待转义的字符串
25
- * @returns {string} 转义后的字符串
26
- */
27
- function escapeHtml(str) {
28
- if (str == null) return '';
29
- return String(str)
30
- .replace(/&/g, '&amp;')
31
- .replace(/</g, '&lt;')
32
- .replace(/>/g, '&gt;')
33
- .replace(/"/g, '&quot;')
34
- .replace(/'/g, '&#39;');
35
- }
36
-
37
- /**
38
- * 校验收件人邮箱格式(基础规则,拒绝含特殊字符的非法地址)
39
- * @param {string} email - 邮箱地址
40
- * @returns {boolean} 是否合法
41
- */
42
- function isValidEmail(email) {
43
- return typeof email === 'string' && EMAIL_REGEX.test(email) && email.length <= 254;
44
- }
45
-
46
- /**
47
- * 获取或创建 transporter(带缓存)
48
- * @param {Object} EMAIL - 邮件配置
49
- * @returns {Object} nodemailer transporter
50
- */
51
- function getTransporter(EMAIL) {
52
- // 用 host+port+user 作为缓存 key,配置变化时自动重建
53
- const key = `${EMAIL.HOST}:${EMAIL.PORT}:${EMAIL.USER}`;
54
- if (_transporter && _transporterKey === key) {
55
- return _transporter;
56
- }
57
-
58
- _transporter = nodemailer.createTransport({
59
- host: EMAIL.HOST,
60
- port: parseInt(EMAIL.PORT),
61
- secure: EMAIL.SECURE === "true",
62
- auth: {
63
- user: EMAIL.USER,
64
- pass: EMAIL.PASS,
65
- },
66
- // 连接池配置:避免每次发邮件都握手,降低 SMTP 服务器压力
67
- pool: true,
68
- maxConnections: 5,
69
- maxMessages: 100,
70
- // 超时控制,避免 SMTP 卡死拖垮业务
71
- connectionTimeout: 10 * 1000,
72
- greetingTimeout: 10 * 1000,
73
- socketTimeout: 30 * 1000,
74
- });
75
- _transporterKey = key;
76
- return _transporter;
77
- }
78
-
79
- /**
80
- * 发送邮件
81
- * @param {string} to - 收件人邮箱
82
- * @param {string} subject - 邮件主题
83
- * @param {string} text - 邮件文本内容
84
- * @param {string|null} html - 邮件HTML内容,默认为null
85
- * @returns {Promise<Object>} 发送结果
86
- * @throws {Error} 邮件服务未配置或发送失败时抛出异常
87
- * @description
88
- * - 收件人邮箱格式校验,拒绝非法地址
89
- * - transporter 模块级缓存,避免每次创建连接池
90
- */
91
- export const sendMail = async (to, subject, text, html = null) => {
92
- // 收件人邮箱格式校验
93
- if (!isValidEmail(to)) {
94
- throw new Error(`[email] 收件人邮箱格式非法: ${to}`);
95
- }
96
-
97
- const { EMAIL, APP_NAME } = Chan.config;
98
-
99
- const transporter = getTransporter(EMAIL);
100
-
101
- let res = await transporter.verify();
102
- if (!res) {
103
- throw new Error("邮件服务未配置");
104
- }
105
-
106
- const mailOptions = {
107
- from: EMAIL.FROM,
108
- to,
109
- subject,
110
- text,
111
- html: html || text,
112
- };
113
-
114
- try {
115
- const info = await transporter.sendMail(mailOptions);
116
- return info;
117
- } catch (error) {
118
- console.error("❌ 邮件发送失败:", error.message);
119
- throw error;
120
- }
121
- };
122
-
123
- /**
124
- * 生成注册验证码邮件HTML模板
125
- * @param {string} code - 验证码
126
- * @param {number} minutes - 有效期(分钟),默认10分钟
127
- * @returns {string} HTML字符串
128
- * @description
129
- * 安全改进:所有动态内容(code、APP_NAME)都经 escapeHtml 转义,防止 HTML 注入
130
- */
131
- export const genRegEmailHtml = (code, minutes = 10) => {
132
- const { APP_NAME } = Chan.config;
133
- // 转义防止 HTML 注入
134
- const safeCode = escapeHtml(code);
135
- const safeAppName = escapeHtml(APP_NAME);
136
- return `
137
- <div style="font-family: Arial, sans-serif; max-width: 750px; margin: auto; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;">
138
- <div style="background-color: #007bff; color: white; padding: 10px; text-align: center;">
139
- <h2>${safeAppName} 欢迎注册!</h2>
140
- </div>
141
- <div style="padding: 20px; line-height: 1.6;">
142
- <p>您好,感谢您注册我们的服务!</p>
143
- <p>您的注册验证码是:</p>
144
- <div style="font-size: 24px; font-weight: bold; color: #007bff; text-align: center; margin: 20px 0;">
145
- ${safeCode}
146
- </div>
147
- <p>请在 <strong>${escapeHtml(minutes)} 分钟内</strong> 输入该验证码,完成账户验证。</p>
148
- <p>如非本人操作,请忽略此邮件。</p>
149
- <p>祝您使用愉快!</p>
150
- </div>
151
- <div style="background-color: #f8f9fa; padding: 10px; text-align: center; font-size: 12px; color: #666;">
152
- &copy; ${new Date().getFullYear()} ${safeAppName}. 保留所有权利。
153
- </div>
154
- </div>
155
- `;
156
- };
157
-
158
- /**
159
- * 生成重置密码邮件HTML模板
160
- * @param {string} code - 验证码
161
- * @param {number} minutes - 有效期(分钟),默认10分钟
162
- * @returns {string} HTML字符串
163
- * @description
164
- * 安全改进:所有动态内容(code、APP_NAME)都经 escapeHtml 转义,防止 HTML 注入
165
- */
166
- export const genResetPasswordEmail = (code, minutes = 10) => {
167
- const { APP_NAME } = Chan.config;
168
- // 转义防止 HTML 注入
169
- const safeCode = escapeHtml(code);
170
- const safeAppName = escapeHtml(APP_NAME);
171
- return `
172
- <div style="font-family: Arial, sans-serif; max-width: 600px; margin: auto; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;">
173
- <div style="background-color: #28a745; color: white; padding: 20px; text-align: center;">
174
- <h2>重置密码_${safeAppName}</h2>
175
- </div>
176
- <div style="padding: 20px; line-height: 1.6;">
177
- <p>您好,我们收到了您重置密码的请求。</p>
178
- <p>您的重置密码验证码是:</p>
179
- <div style="font-size: 24px; font-weight: bold; color: #007bff; text-align: center; margin: 20px 0;">
180
- ${safeCode}
181
- </div>
182
- <p>请在 <strong>${escapeHtml(minutes)} 分钟内</strong> 输入该验证码,完成账户验证。</p>
183
- <p>如非本人操作,请忽略此邮件。</p>
184
- <p>祝您使用愉快!</p>
185
- </div>
186
- <div style="background-color: #f8f9fa; padding: 10px; text-align: center; font-size: 12px; color: #666;">
187
- &copy; ${new Date().getFullYear()} ${safeAppName}. 保留所有权利。
188
- </div>
189
- </div>
190
- `;
191
- };
package/common/index.js DELETED
@@ -1,5 +0,0 @@
1
- export { success, fail, error } from "./api.js";
2
- export { getChildrenId } from "./utils.js";
3
- export { CODE } from "./code.js";
4
- export { sendMail, genRegEmailHtml, genResetPasswordEmail } from "./email.js";
5
- export { pages, getHtmlFilesSync } from "./pages.js";
package/common/pages.js DELETED
@@ -1,120 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- /**
5
- * 分页工具函数
6
- * 提供分页HTML生成和文件遍历功能
7
- */
8
-
9
- /**
10
- * 生成分页HTML
11
- * @param {number} current - 当前页码
12
- * @param {number} total - 总记录数
13
- * @param {number} pageSize - 每页大小
14
- * @param {string} href - 链接基础路径
15
- * @param {string} query - 查询参数字符串,默认为空
16
- * @returns {string} 分页HTML字符串
17
- */
18
- export function pages(current, total, pageSize, href, query='') {
19
- let pageTemp = [];
20
-
21
- // 确保参数是数字
22
- current = parseInt(current) || 1;
23
- total = parseInt(total) || 0;
24
- pageSize = parseInt(pageSize) || 10;
25
-
26
- let totalPage = Math.ceil(total / pageSize);
27
-
28
- // 如果没有数据或只有一页,不显示分页
29
- if (totalPage <= 1 || total === 0) {
30
- return '';
31
- }
32
-
33
- // 确保当前页在有效范围内
34
- if (current < 1) current = 1;
35
- if (current > totalPage) current = totalPage;
36
-
37
- // 上一页
38
- if (current == 1) {
39
- pageTemp.push(`<li class="disabled">上一页</li>`);
40
- } else {
41
- pageTemp.push(`<li><a href='${href}${current - 1}.html${query}'>上一页</a></li>`);
42
- }
43
-
44
- // 页码逻辑
45
- if (totalPage <= 7) {
46
- // 总页数少于7页,全部显示
47
- for (let i = 1; i <= totalPage; i++) {
48
- if (current == i) {
49
- pageTemp.push(`<li class="current">${i}</li>`);
50
- } else {
51
- pageTemp.push(`<li><a href='${href}${i}.html${query}'>${i}</a></li>`);
52
- }
53
- }
54
- } else {
55
- // 总页数大于7页,需要显示省略号
56
- if (current <= 4) {
57
- // 当前页在前4页:显示 1 2 3 4 5 ... 最后一页
58
- for (let i = 1; i <= 5; i++) {
59
- if (current == i) {
60
- pageTemp.push(`<li class="current">${i}</li>`);
61
- } else {
62
- pageTemp.push(`<li><a href='${href}${i}.html${query}'>${i}</a></li>`);
63
- }
64
- }
65
- pageTemp.push(`<li class="disabled">...</li>`);
66
- pageTemp.push(`<li><a href='${href}${totalPage}.html${query}'>${totalPage}</a></li>`);
67
- } else if (current >= totalPage - 3) {
68
- // 当前页在后4页:显示 1 ... 最后5页
69
- pageTemp.push(`<li><a href='${href}1.html${query}'>1</a></li>`);
70
- pageTemp.push(`<li class="disabled">...</li>`);
71
- for (let i = totalPage - 4; i <= totalPage; i++) {
72
- if (current == i) {
73
- pageTemp.push(`<li class="current">${i}</li>`);
74
- } else {
75
- pageTemp.push(`<li><a href='${href}${i}.html${query}'>${i}</a></li>`);
76
- }
77
- }
78
- } else {
79
- // 当前页在中间:显示 1 ... 当前页前1页 当前页 当前页后1页 ... 最后一页
80
- pageTemp.push(`<li><a href='${href}1.html${query}'>1</a></li>`);
81
- pageTemp.push(`<li class="disabled">...</li>`);
82
- for (let i = current - 1; i <= current + 1; i++) {
83
- if (current == i) {
84
- pageTemp.push(`<li class="current">${i}</li>`);
85
- } else {
86
- pageTemp.push(`<li><a href='${href}${i}.html${query}'>${i}</a></li>`);
87
- }
88
- }
89
- pageTemp.push(`<li class="disabled">...</li>`);
90
- pageTemp.push(`<li><a href='${href}${totalPage}.html${query}'>${totalPage}</a></li>`);
91
- }
92
- }
93
-
94
- // 下一页
95
- if (current == totalPage) {
96
- pageTemp.push(`<li class="disabled">下一页</li>`);
97
- } else {
98
- pageTemp.push(`<li><a href='${href}${current + 1}.html${query}'>下一页</a></li>`);
99
- }
100
-
101
- return pageTemp.join("");
102
- }
103
-
104
- /**
105
- * 同步获取指定文件夹下的所有HTML文件
106
- * @param {string} folderPath - 文件夹路径
107
- * @returns {Array<string>} HTML文件名数组
108
- */
109
- export function getHtmlFilesSync(folderPath) {
110
- const files = fs.readdirSync(folderPath);
111
- const htmlFiles = [];
112
- files.forEach((file) => {
113
- const filePath = path.join(folderPath, file);
114
- const stats = fs.statSync(filePath);
115
- if (stats.isFile() && path.extname(file) === ".html") {
116
- htmlFiles.push(file);
117
- }
118
- });
119
- return htmlFiles;
120
- }
package/common/utils.js DELETED
@@ -1,73 +0,0 @@
1
- /**
2
- * 通用工具函数
3
- * 提供分类、HTML处理、设备检测和图片提取等功能
4
- */
5
-
6
- /**
7
- * 根据拼音或ID获取分类信息
8
- * @param {string|number} py - 拼音或ID
9
- * @param {Array} source - 分类数据源
10
- * @returns {Object} 包含分类对象和ID的对象
11
- */
12
- export function getChildrenId(py, source) {
13
- let cate = {};
14
- let id = "";
15
- source.forEach((item) => {
16
- if (item.pinyin == py || item.id == py) {
17
- cate = item;
18
- id = item.id;
19
- }
20
- });
21
- return { cate, id };
22
- }
23
-
24
- /**
25
- * 从HTML字符串中提取body内容
26
- * @param {string} str - HTML字符串
27
- * @returns {string} body内容,未找到则返回原字符串
28
- */
29
- export function filterBody(str) {
30
- const result = /<body[^>]*>([\s\S]*)<\/body>/.exec(str);
31
- if (result && result.length === 2) return result[1];
32
- return str;
33
- }
34
-
35
- /**
36
- * 检测是否为PC端
37
- * @param {string} str - User-Agent字符串
38
- * @returns {boolean} 是PC端返回true,否则返回false
39
- */
40
- export function pc(str) {
41
- if (
42
- str.match(
43
- /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
44
- )
45
- ) {
46
- return false;
47
- }
48
- return true;
49
- }
50
-
51
- /**
52
- * 从HTML字符串中提取所有图片URL
53
- * @param {string} str - HTML字符串
54
- * @returns {Array<string>} 图片URL数组
55
- */
56
- export function filterImgFromStr(str) {
57
- if (!str) {
58
- return [];
59
- }
60
- const imgReg = /<img.*?(?:>|\/>)/gi;
61
- const srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i;
62
- const arr = str.match(imgReg);
63
- const imgArr = [];
64
- if (arr) {
65
- for (let i = 0; i < arr.length; i++) {
66
- const src = arr[i].match(srcReg);
67
- if (src[1]) {
68
- imgArr.push(src[1]);
69
- }
70
- }
71
- }
72
- return imgArr;
73
- }
package/config/code.js DELETED
@@ -1,166 +0,0 @@
1
- /**
2
- * 业务状态码定义(统一三层 code 体系)
3
- *
4
- * ============================================================
5
- * 设计原则
6
- * ============================================================
7
- * 1. 业务 code 与 HTTP Status 彻底解耦
8
- * - HTTP Status 由网关/Express 控制(200/400/401/403/404/500)
9
- * - 业务 code 由业务层控制(0 / 1xxx / 5xxx / 6xxx)
10
- * - 前端 axios 先看 HTTP Status,再看业务 code
11
- *
12
- * 2. 0 表示成功(不再用 200,避免与 HTTP 200 混淆)
13
- *
14
- * 3. 错误按业务域分段
15
- * - 1xxx 通用业务错误(认证/权限/参数/资源)
16
- * - 2xxx CMS 业务专用错误(保留扩展段)
17
- * - 5xxx 系统错误
18
- * - 6xxx 数据库错误
19
- *
20
- * 4. 每个 code 都有默认 msg,业务可覆盖
21
- *
22
- * ============================================================
23
- * Code 段位定义
24
- * ============================================================
25
- * 0 → 成功
26
- * 1001 → 认证失败(token 无效/未登录)
27
- * 1002 → token 已过期
28
- * 1003 → 权限不足
29
- * 1004 → 资源不存在
30
- * 1005 → 资源已存在
31
- * 1006 → 参数无效
32
- * 1007 → 参数缺失
33
- * 1008 → 业务处理失败(通用兜底)
34
- * 1009 → 请求过于频繁(限流)
35
- * 1010 → 登录设备异常
36
- * 2xxx → CMS 业务专用错误(保留扩展段)
37
- * 5001 → 系统内部错误
38
- * 5002 → 服务繁忙,请稍后再试
39
- * 6001 → 数据库连接失败
40
- * 6002 → 数据库访问被拒绝
41
- * 6003 → 存在关联数据,操作失败
42
- * 6004 → 数据库字段错误
43
- * 6005 → 数据重复,违反唯一性约束
44
- * 6006 → 目标表不存在
45
- * 6007 → 数据库操作超时
46
- * 6008 → 数据库语法错误
47
- * 6009 → 数据库连接已关闭
48
- */
49
-
50
- /**
51
- * 业务状态码 → 默认消息
52
- * @type {Object<number, string>}
53
- */
54
- export const CODE = {
55
- // 成功
56
- 0: "操作成功",
57
-
58
- // 1xxx 通用业务错误
59
- 1001: "认证失败",
60
- 1002: "令牌已过期",
61
- 1003: "权限不足",
62
- 1004: "资源不存在",
63
- 1005: "资源已存在",
64
- 1006: "参数无效",
65
- 1007: "参数缺失",
66
- 1008: "业务处理失败",
67
- 1009: "请求过于频繁",
68
- 1010: "登录设备异常",
69
-
70
- // 2xxx CMS 业务专用错误(保留扩展段,业务方可自定义)
71
- // 例如:2001 文章不存在 / 2002 栏目不存在 等
72
-
73
- // 5xxx 系统错误
74
- 5001: "系统内部错误",
75
- 5002: "服务繁忙,请稍后再试",
76
-
77
- // 6xxx 数据库错误
78
- 6001: "数据库连接失败",
79
- 6002: "数据库访问被拒绝",
80
- 6003: "存在关联数据,操作失败",
81
- 6004: "数据库字段错误",
82
- 6005: "数据重复,违反唯一性约束",
83
- 6006: "目标表不存在",
84
- 6007: "数据库操作超时",
85
- 6008: "数据库语法错误,请检查查询语句",
86
- 6009: "数据库连接已关闭,请重试",
87
- };
88
-
89
- /**
90
- * 业务 code 段位常量
91
- * 便于业务方判断 code 归属段位
92
- * @example
93
- * if (code >= CODE_RANGE.SYSTEM[0] && code <= CODE_RANGE.SYSTEM[1]) { ... }
94
- */
95
- export const CODE_RANGE = {
96
- SUCCESS: [0, 0], // 成功
97
- BUSINESS: [1000, 1999], // 通用业务错误
98
- CMS: [2000, 2999], // CMS 业务错误(保留扩展段)
99
- SYSTEM: [5000, 5999], // 系统错误
100
- DATABASE: [6000, 6999], // 数据库错误
101
- };
102
-
103
- /**
104
- * 常用 code 语义化别名
105
- * 业务方可直接用 CODE_OK / CODE_AUTH_FAILED 等语义化常量,避免硬编码数字
106
- */
107
- export const CODE_OK = 0; // 成功
108
- export const CODE_AUTH_FAILED = 1001; // 认证失败
109
- export const CODE_TOKEN_EXPIRED = 1002; // token 过期
110
- export const CODE_FORBIDDEN = 1003; // 权限不足
111
- export const CODE_NOT_FOUND = 1004; // 资源不存在
112
- export const CODE_CONFLICT = 1005; // 资源已存在
113
- export const CODE_PARAM_INVALID = 1006; // 参数无效
114
- export const CODE_PARAM_MISSING = 1007; // 参数缺失
115
- export const CODE_BUSINESS_FAIL = 1008; // 业务处理失败(通用)
116
- export const CODE_RATE_LIMIT = 1009; // 限流
117
- export const CODE_DEVICE_ERROR = 1010; // 登录设备异常
118
- export const CODE_SYSTEM_ERROR = 5001; // 系统内部错误
119
- export const CODE_SERVICE_BUSY = 5002; // 服务繁忙
120
-
121
- /**
122
- * 数据库原生错误码 → 业务 code 映射
123
- * 将 MySQL/Knex 等数据库原生错误码转换为业务 code
124
- * @type {Object<string, number>}
125
- */
126
- export const DB_ERROR = {
127
- ECONNREFUSED: 6001,
128
- ER_ACCESS_DENIED_ERROR: 6002,
129
- ER_ROW_IS_REFERENCED_2: 6003,
130
- ER_BAD_FIELD_ERROR: 6004,
131
- ER_DUP_ENTRY: 6005,
132
- ER_NO_SUCH_TABLE: 6006,
133
- ETIMEOUT: 6007,
134
- ER_TABLE_EXISTS_ERROR: 1005, // 表已存在 → 资源已存在
135
- };
136
-
137
- /**
138
- * 判断 code 是否为成功
139
- * @param {number} code - 业务 code
140
- * @returns {boolean}
141
- */
142
- export function isSuccessCode(code) {
143
- return code === CODE_OK;
144
- }
145
-
146
- /**
147
- * 判断 code 所属段位
148
- * @param {number} code - 业务 code
149
- * @returns {string} 段位名:SUCCESS / BUSINESS / CMS / SYSTEM / DATABASE / UNKNOWN
150
- */
151
- export function getCodeRange(code) {
152
- for (const [name, [min, max]] of Object.entries(CODE_RANGE)) {
153
- if (code >= min && code <= max) return name;
154
- }
155
- return 'UNKNOWN';
156
- }
157
-
158
- /**
159
- * 获取 code 的默认消息
160
- * @param {number} code - 业务 code
161
- * @param {string} [fallback='操作失败'] - 兜底消息
162
- * @returns {string}
163
- */
164
- export function getCodeMsg(code, fallback = '操作失败') {
165
- return CODE[code] || fallback;
166
- }
package/config/paths.js DELETED
@@ -1,60 +0,0 @@
1
- import path from "path";
2
-
3
- /**
4
- * 路径配置
5
- * 提供项目中常用路径的访问器
6
- */
7
-
8
- export const Paths = {
9
- /**
10
- * 项目根目录路径
11
- */
12
- rootPath: process.cwd(),
13
- };
14
-
15
- Object.defineProperties(Paths, {
16
- /**
17
- * 应用目录路径
18
- */
19
- appPath: {
20
- get() { return path.join(this.rootPath, "app"); }
21
- },
22
- /**
23
- * 配置文件目录路径
24
- */
25
- configPath: {
26
- get() { return path.join(this.rootPath, "config"); }
27
- },
28
- /**
29
- * 公共资源目录路径
30
- */
31
- publicPath: {
32
- get() { return path.join(this.rootPath, "public"); }
33
- },
34
- /**
35
- * 模块目录路径
36
- */
37
- modulesPath: {
38
- get() { return path.join(this.appPath, "modules"); }
39
- },
40
- /**
41
- * 通用模块目录路径
42
- */
43
- commonPath: {
44
- get() { return path.join(this.appPath, "common"); }
45
- },
46
- /**
47
- * 辅助函数目录路径
48
- */
49
- helperPath: {
50
- get() { return path.join(this.appPath, "helper"); }
51
- },
52
- /**
53
- * 扩展目录路径
54
- */
55
- extendPath: {
56
- get() { return path.join(this.appPath, "extend"); }
57
- },
58
- });
59
-
60
- export default Paths;