befly 2.1.0 → 2.2.0
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.
- package/README.md +1 -1
- package/apis/health/info.js +1 -1
- package/apis/tool/tokenCheck.js +1 -1
- package/bunfig.toml +3 -0
- package/checks/table.js +3 -43
- package/config/env.js +0 -2
- package/main.js +64 -35
- package/package.json +5 -18
- package/plugins/db.js +7 -587
- package/plugins/logger.js +2 -1
- package/plugins/redis.js +7 -64
- package/plugins/tool.js +3 -40
- package/scripts/syncDb.js +8 -16
- package/utils/api.js +1 -1
- package/utils/{util.js → index.js} +90 -32
- package/utils/jwt.js +63 -13
- package/utils/logger.js +1 -1
- package/utils/redisHelper.js +74 -0
- package/utils/sqlManager.js +471 -0
- package/utils/tool.js +31 -0
- package/utils/validate.js +2 -92
- package/.gitignore +0 -94
- package/libs/jwt.js +0 -97
- /package/utils/{curd.js → sqlBuilder.js} +0 -0
- /package/{libs → utils}/xml.js +0 -0
package/plugins/db.js
CHANGED
|
@@ -1,29 +1,9 @@
|
|
|
1
|
-
import { SQL } from 'bun';
|
|
2
1
|
import { Env } from '../config/env.js';
|
|
3
2
|
import { Logger } from '../utils/logger.js';
|
|
4
|
-
import {
|
|
3
|
+
import { createSqlClient } from '../utils/index.js';
|
|
4
|
+
import { SqlManager } from '../utils/sqlManager.js';
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
async function createSqlClient() {
|
|
8
|
-
return new Promise(async (resolve, reject) => {
|
|
9
|
-
const sql = new SQL(Env.MYSQL_URL);
|
|
10
|
-
|
|
11
|
-
// 触发一次简单查询来确保连接可用;
|
|
12
|
-
try {
|
|
13
|
-
const result = await sql`SELECT VERSION() AS version`;
|
|
14
|
-
Logger.info(`数据库连接成功,MySQL 版本: ${result?.[0]?.version}`);
|
|
15
|
-
resolve(sql);
|
|
16
|
-
} catch (error) {
|
|
17
|
-
Logger.error('数据库连接测试失败:', error);
|
|
18
|
-
try {
|
|
19
|
-
await client.close();
|
|
20
|
-
} catch (e) {
|
|
21
|
-
Logger.error('关闭失败(创建阶段):', e);
|
|
22
|
-
}
|
|
23
|
-
reject(error);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
}
|
|
6
|
+
// 统一使用 utils/index.js 提供的 createSqlClient
|
|
27
7
|
|
|
28
8
|
export default {
|
|
29
9
|
after: ['_redis'],
|
|
@@ -35,569 +15,8 @@ export default {
|
|
|
35
15
|
// 创建 Bun SQL 客户端(内置连接池),并确保连接验证成功后再继续
|
|
36
16
|
sql = await createSqlClient();
|
|
37
17
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
// 私有属性
|
|
41
|
-
#sql;
|
|
42
|
-
|
|
43
|
-
constructor(client) {
|
|
44
|
-
this.#sql = client;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 原始连接池访问
|
|
48
|
-
get pool() {
|
|
49
|
-
// 兼容旧接口,返回占位对象
|
|
50
|
-
return {
|
|
51
|
-
// 这些方法在 Bun SQL 中不可用,这里提供空实现以避免调用错误
|
|
52
|
-
activeConnections: () => 0,
|
|
53
|
-
totalConnections: () => 0,
|
|
54
|
-
idleConnections: () => 0,
|
|
55
|
-
taskQueueSize: () => 0
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 创建查询构造器
|
|
60
|
-
query() {
|
|
61
|
-
return createQueryBuilder();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 私有方法:通用数据处理函数 - 自动添加ID、时间戳和状态
|
|
65
|
-
async #processDataForInsert(data) {
|
|
66
|
-
const now = Date.now();
|
|
67
|
-
|
|
68
|
-
if (Array.isArray(data)) {
|
|
69
|
-
return await Promise.all(
|
|
70
|
-
data.map(async (item) => ({
|
|
71
|
-
...item,
|
|
72
|
-
id: await befly.redis.genTimeID(),
|
|
73
|
-
state: 0,
|
|
74
|
-
created_at: now,
|
|
75
|
-
updated_at: now
|
|
76
|
-
}))
|
|
77
|
-
);
|
|
78
|
-
} else {
|
|
79
|
-
return {
|
|
80
|
-
...data,
|
|
81
|
-
id: await befly.redis.genTimeID(),
|
|
82
|
-
state: 0,
|
|
83
|
-
created_at: now,
|
|
84
|
-
updated_at: now
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// 私有方法:添加默认的state过滤条件
|
|
90
|
-
#addDefaultStateFilter(where = {}) {
|
|
91
|
-
// 检查是否已有state相关条件
|
|
92
|
-
const hasStateCondition = Object.keys(where).some((key) => key === 'state' || key.startsWith('state$'));
|
|
93
|
-
|
|
94
|
-
// 如果没有state条件,添加默认过滤
|
|
95
|
-
if (!hasStateCondition) {
|
|
96
|
-
return { ...where, state$ne: 2 };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return where;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 将 '?' 占位符转换为 Bun SQL 使用的 $1, $2 ...
|
|
103
|
-
#toDollarParams(query, params) {
|
|
104
|
-
if (!params || params.length === 0) return query;
|
|
105
|
-
let i = 0;
|
|
106
|
-
return query.replace(/\?/g, () => `$${++i}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// 私有方法:执行 SQL(支持传入事务连接对象)
|
|
110
|
-
async #executeWithConn(query, params = [], conn = null) {
|
|
111
|
-
if (!query || typeof query !== 'string') {
|
|
112
|
-
throw new Error('SQL 语句是必需的');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const isSelectLike = /^\s*(with|select|show|desc|explain)\b/i.test(query);
|
|
116
|
-
const isWriteLike = /^\s*(insert|update|delete|replace)\b/i.test(query);
|
|
117
|
-
const client = conn || this.#sql;
|
|
118
|
-
try {
|
|
119
|
-
if (Env.MYSQL_DEBUG === 1) {
|
|
120
|
-
Logger.debug('执行SQL:', { sql: query, params });
|
|
121
|
-
}
|
|
122
|
-
// 读查询,直接返回行
|
|
123
|
-
if (isSelectLike) {
|
|
124
|
-
if (params && params.length > 0) {
|
|
125
|
-
const q = this.#toDollarParams(query, params);
|
|
126
|
-
return await client.unsafe(q, params);
|
|
127
|
-
} else {
|
|
128
|
-
return await client.unsafe(query);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 写查询需要返回受影响行数/插入ID,必须保证同连接
|
|
133
|
-
if (isWriteLike) {
|
|
134
|
-
const runOn = conn ? client : await this.#sql.reserve();
|
|
135
|
-
try {
|
|
136
|
-
if (params && params.length > 0) {
|
|
137
|
-
const q = this.#toDollarParams(query, params);
|
|
138
|
-
await runOn.unsafe(q, params);
|
|
139
|
-
} else {
|
|
140
|
-
await runOn.unsafe(query);
|
|
141
|
-
}
|
|
142
|
-
const [{ affectedRows }] = await runOn`SELECT ROW_COUNT() AS affectedRows`;
|
|
143
|
-
const [{ insertId }] = await runOn`SELECT LAST_INSERT_ID() AS insertId`;
|
|
144
|
-
return { affectedRows: Number(affectedRows) || 0, insertId: Number(insertId) || 0 };
|
|
145
|
-
} finally {
|
|
146
|
-
if (!conn && runOn && typeof runOn.release === 'function') runOn.release();
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 其他(DDL等),直接执行并返回空数组以与旧行为尽可能兼容
|
|
151
|
-
if (params && params.length > 0) {
|
|
152
|
-
const q = this.#toDollarParams(query, params);
|
|
153
|
-
return await client.unsafe(q, params);
|
|
154
|
-
} else {
|
|
155
|
-
return await client.unsafe(query);
|
|
156
|
-
}
|
|
157
|
-
} catch (error) {
|
|
158
|
-
Logger.error('SQL 执行失败:', { sql: query, params, error: error.message });
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// 私有方法:获取单条记录详情(支持传入连接对象)
|
|
164
|
-
async #getDetailWithConn(table, options = {}, conn = null) {
|
|
165
|
-
if (!table || typeof table !== 'string') {
|
|
166
|
-
throw new Error('表名是必需的');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const { where = {}, fields = '*', leftJoins = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
// 添加默认的state过滤条件
|
|
173
|
-
const filteredWhere = this.#addDefaultStateFilter(where);
|
|
174
|
-
const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere).limit(1);
|
|
175
|
-
|
|
176
|
-
// 添加 LEFT JOIN
|
|
177
|
-
leftJoins.forEach((join) => {
|
|
178
|
-
if (typeof join === 'string') {
|
|
179
|
-
const parts = join.split(' ON ');
|
|
180
|
-
if (parts.length === 2) {
|
|
181
|
-
builder.leftJoin(parts[0].trim(), parts[1].trim());
|
|
182
|
-
}
|
|
183
|
-
} else if (join && typeof join === 'object' && join.table && join.on) {
|
|
184
|
-
builder.leftJoin(join.table, join.on);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const { sql: q, params } = builder.toSelectSql();
|
|
189
|
-
const result = await this.#executeWithConn(q, params, conn);
|
|
190
|
-
return result[0] || null;
|
|
191
|
-
} catch (error) {
|
|
192
|
-
Logger.error('getDetail 执行失败:', error);
|
|
193
|
-
throw error;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 私有方法:获取列表(支持传入连接对象)
|
|
198
|
-
async #getListWithConn(table, options = {}, conn = null) {
|
|
199
|
-
if (!table || typeof table !== 'string') {
|
|
200
|
-
throw new Error('表名是必需的');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const { where = {}, fields = '*', leftJoins = [], orderBy = [], groupBy = [], having = [], page = 1, pageSize = 10 } = options;
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
// 添加默认的state过滤条件
|
|
207
|
-
const filteredWhere = this.#addDefaultStateFilter(where);
|
|
208
|
-
const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere);
|
|
209
|
-
|
|
210
|
-
// 添加 LEFT JOIN
|
|
211
|
-
leftJoins.forEach((join) => {
|
|
212
|
-
if (typeof join === 'string') {
|
|
213
|
-
const parts = join.split(' ON ');
|
|
214
|
-
if (parts.length === 2) {
|
|
215
|
-
builder.leftJoin(parts[0].trim(), parts[1].trim());
|
|
216
|
-
}
|
|
217
|
-
} else if (join && typeof join === 'object' && join.table && join.on) {
|
|
218
|
-
builder.leftJoin(join.table, join.on);
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// 添加其他子句
|
|
223
|
-
if (Array.isArray(groupBy) && groupBy.length > 0) {
|
|
224
|
-
builder.groupBy(groupBy);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (Array.isArray(having) && having.length > 0) {
|
|
228
|
-
having.forEach((h) => builder.having(h));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (Array.isArray(orderBy) && orderBy.length > 0) {
|
|
232
|
-
builder.orderBy(orderBy);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// 分页处理
|
|
236
|
-
const numPage = parseInt(page) || 1;
|
|
237
|
-
const numPageSize = parseInt(pageSize) || 10;
|
|
238
|
-
|
|
239
|
-
if (numPage > 0 && numPageSize > 0) {
|
|
240
|
-
const offset = (numPage - 1) * numPageSize;
|
|
241
|
-
builder.limit(numPageSize, offset);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const { sql: q, params } = builder.toSelectSql();
|
|
245
|
-
const rows = await this.#executeWithConn(q, params, conn);
|
|
246
|
-
|
|
247
|
-
// 获取总数(如果需要分页)
|
|
248
|
-
let total = 0;
|
|
249
|
-
if (numPage > 0 && numPageSize > 0) {
|
|
250
|
-
const countBuilder = createQueryBuilder().from(table).where(filteredWhere);
|
|
251
|
-
|
|
252
|
-
// 计算总数时也要包含 JOIN
|
|
253
|
-
leftJoins.forEach((join) => {
|
|
254
|
-
if (typeof join === 'string') {
|
|
255
|
-
const parts = join.split(' ON ');
|
|
256
|
-
if (parts.length === 2) {
|
|
257
|
-
countBuilder.leftJoin(parts[0].trim(), parts[1].trim());
|
|
258
|
-
}
|
|
259
|
-
} else if (join && typeof join === 'object' && join.table && join.on) {
|
|
260
|
-
countBuilder.leftJoin(join.table, join.on);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
const { sql: countSql, params: countParams } = countBuilder.toCountSql();
|
|
265
|
-
const countResult = await this.#executeWithConn(countSql, countParams, conn);
|
|
266
|
-
total = countResult[0]?.total || 0;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
rows: Array.isArray(rows) ? rows : [],
|
|
271
|
-
total,
|
|
272
|
-
page: numPage,
|
|
273
|
-
pageSize: numPageSize
|
|
274
|
-
};
|
|
275
|
-
} catch (error) {
|
|
276
|
-
Logger.error('getList 执行失败:', error);
|
|
277
|
-
throw error;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// 私有方法:获取所有记录(支持传入连接对象)
|
|
282
|
-
async #getAllWithConn(table, options = {}, conn = null) {
|
|
283
|
-
if (!table || typeof table !== 'string') {
|
|
284
|
-
throw new Error('表名是必需的');
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const { where = {}, fields = '*', leftJoins = [], orderBy = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
// 添加默认的state过滤条件
|
|
291
|
-
const filteredWhere = this.#addDefaultStateFilter(where);
|
|
292
|
-
const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere);
|
|
293
|
-
|
|
294
|
-
// 添加 LEFT JOIN
|
|
295
|
-
leftJoins.forEach((join) => {
|
|
296
|
-
if (typeof join === 'string') {
|
|
297
|
-
const parts = join.split(' ON ');
|
|
298
|
-
if (parts.length === 2) {
|
|
299
|
-
builder.leftJoin(parts[0].trim(), parts[1].trim());
|
|
300
|
-
}
|
|
301
|
-
} else if (join && typeof join === 'object' && join.table && join.on) {
|
|
302
|
-
builder.leftJoin(join.table, join.on);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
if (Array.isArray(orderBy) && orderBy.length > 0) {
|
|
307
|
-
builder.orderBy(orderBy);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const { sql: q, params } = builder.toSelectSql();
|
|
311
|
-
const result = await this.#executeWithConn(q, params, conn);
|
|
312
|
-
return Array.isArray(result) ? result : [];
|
|
313
|
-
} catch (error) {
|
|
314
|
-
Logger.error('getAll 执行失败:', error);
|
|
315
|
-
throw error;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// 私有方法:插入数据(支持传入连接对象)
|
|
320
|
-
async #insDataWithConn(table, data, conn = null) {
|
|
321
|
-
if (!table || typeof table !== 'string') {
|
|
322
|
-
throw new Error('表名是必需的');
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (!data) {
|
|
326
|
-
throw new Error('插入数据是必需的');
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
try {
|
|
330
|
-
const processedData = await this.#processDataForInsert(data);
|
|
331
|
-
const builder = createQueryBuilder();
|
|
332
|
-
const { sql: q, params } = builder.toInsertSql(table, processedData);
|
|
333
|
-
return await this.#executeWithConn(q, params, conn);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
Logger.error('insData 执行失败:', error);
|
|
336
|
-
throw error;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// 私有方法:更新数据(支持传入连接对象)
|
|
341
|
-
async #updDataWithConn(table, data, where, conn = null) {
|
|
342
|
-
if (!table || typeof table !== 'string') {
|
|
343
|
-
throw new Error('表名是必需的');
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (!data || typeof data !== 'object') {
|
|
347
|
-
throw new Error('更新数据是必需的');
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (!where) {
|
|
351
|
-
throw new Error('更新操作需要 WHERE 条件');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
// 剔除 undefined 值和敏感字段
|
|
356
|
-
const filteredData = Object.fromEntries(Object.entries(data).filter(([key, value]) => value !== undefined && !['id', 'created_at', 'deleted_at'].includes(key)));
|
|
357
|
-
|
|
358
|
-
// 自动添加 updated_at
|
|
359
|
-
const updateData = {
|
|
360
|
-
...filteredData,
|
|
361
|
-
updated_at: Date.now()
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const builder = createQueryBuilder().where(where);
|
|
365
|
-
const { sql: q, params } = builder.toUpdateSql(table, updateData);
|
|
366
|
-
return await this.#executeWithConn(q, params, conn);
|
|
367
|
-
} catch (error) {
|
|
368
|
-
Logger.error('updData 执行失败:', error);
|
|
369
|
-
throw error;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// 私有方法:删除数据(支持传入连接对象)
|
|
374
|
-
async #delDataWithConn(table, where, conn = null) {
|
|
375
|
-
if (!table || typeof table !== 'string') {
|
|
376
|
-
throw new Error('表名是必需的');
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (!where) {
|
|
380
|
-
throw new Error('删除操作需要 WHERE 条件');
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
try {
|
|
384
|
-
const builder = createQueryBuilder().where(where);
|
|
385
|
-
const { sql: q, params } = builder.toDeleteSql(table);
|
|
386
|
-
return await this.#executeWithConn(q, params, conn);
|
|
387
|
-
} catch (error) {
|
|
388
|
-
Logger.error('delData 执行失败:', error);
|
|
389
|
-
throw error;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// 私有方法:软删除数据(支持传入连接对象)
|
|
394
|
-
async #delData2WithConn(table, where, conn = null) {
|
|
395
|
-
if (!table || typeof table !== 'string') {
|
|
396
|
-
throw new Error('表名是必需的');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (!where) {
|
|
400
|
-
throw new Error('软删除操作需要 WHERE 条件');
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
try {
|
|
404
|
-
// 软删除:将 state 设置为 2,同时更新 updated_at
|
|
405
|
-
const updateData = {
|
|
406
|
-
state: 2,
|
|
407
|
-
updated_at: Date.now()
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const builder = createQueryBuilder().where(where);
|
|
411
|
-
const { sql: q, params } = builder.toUpdateSql(table, updateData);
|
|
412
|
-
return await this.#executeWithConn(q, params, conn);
|
|
413
|
-
} catch (error) {
|
|
414
|
-
Logger.error('delData2 执行失败:', error);
|
|
415
|
-
throw error;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// 私有方法:批量插入(支持传入连接对象)
|
|
420
|
-
async #insBatchWithConn(table, dataArray, conn = null) {
|
|
421
|
-
if (!table || typeof table !== 'string') {
|
|
422
|
-
throw new Error('表名是必需的');
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (!Array.isArray(dataArray) || dataArray.length === 0) {
|
|
426
|
-
throw new Error('批量插入数据不能为空');
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
const processedDataArray = await this.#processDataForInsert(dataArray);
|
|
431
|
-
const builder = createQueryBuilder();
|
|
432
|
-
const { sql: q, params } = builder.toInsertSql(table, processedDataArray);
|
|
433
|
-
return await this.#executeWithConn(q, params, conn);
|
|
434
|
-
} catch (error) {
|
|
435
|
-
Logger.error('insBatch 执行失败:', error);
|
|
436
|
-
throw error;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// 私有方法:获取记录总数(支持传入连接对象)
|
|
441
|
-
async #getCountWithConn(table, options = {}, conn = null) {
|
|
442
|
-
if (!table || typeof table !== 'string') {
|
|
443
|
-
throw new Error('表名是必需的');
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const { where = {}, leftJoins = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
|
|
447
|
-
|
|
448
|
-
try {
|
|
449
|
-
// 添加默认的state过滤条件
|
|
450
|
-
const filteredWhere = this.#addDefaultStateFilter(where);
|
|
451
|
-
const builder = createQueryBuilder().from(table).where(filteredWhere);
|
|
452
|
-
|
|
453
|
-
// 添加 LEFT JOIN
|
|
454
|
-
leftJoins.forEach((join) => {
|
|
455
|
-
if (typeof join === 'string') {
|
|
456
|
-
const parts = join.split(' ON ');
|
|
457
|
-
if (parts.length === 2) {
|
|
458
|
-
builder.leftJoin(parts[0].trim(), parts[1].trim());
|
|
459
|
-
}
|
|
460
|
-
} else if (join && typeof join === 'object' && join.table && join.on) {
|
|
461
|
-
builder.leftJoin(join.table, join.on);
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
const { sql: q, params } = builder.toCountSql();
|
|
466
|
-
const result = await this.#executeWithConn(q, params, conn);
|
|
467
|
-
return result[0]?.total || 0;
|
|
468
|
-
} catch (error) {
|
|
469
|
-
Logger.error('getCount 执行失败:', error);
|
|
470
|
-
throw error;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// 执行原始 SQL - 核心方法
|
|
475
|
-
async execute(sql, params = []) {
|
|
476
|
-
return await this.#executeWithConn(sql, params);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// 获取单条记录详情
|
|
480
|
-
async getDetail(table, options = {}) {
|
|
481
|
-
return await this.#getDetailWithConn(table, options);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// 获取列表(支持分页)
|
|
485
|
-
async getList(table, options = {}) {
|
|
486
|
-
return await this.#getListWithConn(table, options);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// 获取所有记录
|
|
490
|
-
async getAll(table, options = {}) {
|
|
491
|
-
return await this.#getAllWithConn(table, options);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// 插入数据 - 增强版,自动添加 ID 和时间戳
|
|
495
|
-
async insData(table, data) {
|
|
496
|
-
return await this.#insDataWithConn(table, data);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// 更新数据 - 增强版,自动添加 updated_at,过滤敏感字段
|
|
500
|
-
async updData(table, data, where) {
|
|
501
|
-
return await this.#updDataWithConn(table, data, where);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// 删除数据
|
|
505
|
-
async delData(table, where) {
|
|
506
|
-
return await this.#delDataWithConn(table, where);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// 软删除数据 - 将 state 设置为 2
|
|
510
|
-
async delData2(table, where) {
|
|
511
|
-
return await this.#delData2WithConn(table, where);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// 批量插入 - 增强版,自动添加 ID 和时间戳
|
|
515
|
-
async insBatch(table, dataArray) {
|
|
516
|
-
return await this.#insBatchWithConn(table, dataArray);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// 获取记录总数
|
|
520
|
-
async getCount(table, options = {}) {
|
|
521
|
-
return await this.#getCountWithConn(table, options);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// 事务处理
|
|
525
|
-
async trans(callback) {
|
|
526
|
-
if (typeof callback !== 'function') {
|
|
527
|
-
throw new Error('事务回调函数是必需的');
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
try {
|
|
531
|
-
const result = await this.#sql.begin(async (tx) => {
|
|
532
|
-
// 为回调函数提供连接对象和高级方法(基于事务连接)
|
|
533
|
-
const txMethods = {
|
|
534
|
-
// 原始SQL执行方法
|
|
535
|
-
query: async (query, params = []) => this.#executeWithConn(query, params, tx),
|
|
536
|
-
execute: async (query, params = []) => this.#executeWithConn(query, params, tx),
|
|
537
|
-
|
|
538
|
-
// 高级数据操作方法 - 直接调用私有方法,传入事务连接
|
|
539
|
-
getDetail: async (table, options = {}) => this.#getDetailWithConn(table, options, tx),
|
|
540
|
-
getList: async (table, options = {}) => this.#getListWithConn(table, options, tx),
|
|
541
|
-
getAll: async (table, options = {}) => this.#getAllWithConn(table, options, tx),
|
|
542
|
-
insData: async (table, data) => this.#insDataWithConn(table, data, tx),
|
|
543
|
-
updData: async (table, data, where) => this.#updDataWithConn(table, data, where, tx),
|
|
544
|
-
delData: async (table, where) => this.#delDataWithConn(table, where, tx),
|
|
545
|
-
delData2: async (table, where) => this.#delData2WithConn(table, where, tx),
|
|
546
|
-
getCount: async (table, options = {}) => this.#getCountWithConn(table, options, tx),
|
|
547
|
-
insBatch: async (table, dataArray) => this.#insBatchWithConn(table, dataArray, tx)
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
return await callback(txMethods);
|
|
551
|
-
});
|
|
552
|
-
return result;
|
|
553
|
-
} catch (error) {
|
|
554
|
-
// Bun SQL 会自动回滚
|
|
555
|
-
Logger.info('事务已回滚');
|
|
556
|
-
throw error;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// 获取连接池状态
|
|
561
|
-
getPoolStatus() {
|
|
562
|
-
return {
|
|
563
|
-
activeConnections: 0,
|
|
564
|
-
totalConnections: 0,
|
|
565
|
-
idleConnections: 0,
|
|
566
|
-
taskQueueSize: 0
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// 关闭连接池
|
|
571
|
-
async close() {
|
|
572
|
-
if (this.#sql) {
|
|
573
|
-
try {
|
|
574
|
-
await this.#sql.close();
|
|
575
|
-
Logger.info('数据库连接已关闭');
|
|
576
|
-
} catch (error) {
|
|
577
|
-
Logger.error('关闭数据库连接失败:', error);
|
|
578
|
-
throw error;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// 创建数据库管理器实例
|
|
585
|
-
const dbManager = new DatabaseManager(sql);
|
|
586
|
-
|
|
587
|
-
// 监听进程退出事件,确保连接池正确关闭
|
|
588
|
-
const gracefulShutdown = async (signal) => {
|
|
589
|
-
Logger.info(`收到 ${signal} 信号,正在关闭数据库连接池...`);
|
|
590
|
-
try {
|
|
591
|
-
await dbManager.close();
|
|
592
|
-
} catch (error) {
|
|
593
|
-
Logger.error('优雅关闭数据库失败:', error);
|
|
594
|
-
}
|
|
595
|
-
process.exit(0);
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
process.on('SIGINT', gracefulShutdown);
|
|
599
|
-
process.on('SIGTERM', gracefulShutdown);
|
|
600
|
-
process.on('SIGUSR2', gracefulShutdown); // nodemon 重启
|
|
18
|
+
// 创建数据库管理器实例(迁移到 utils/sqlManager.js)
|
|
19
|
+
const dbManager = new SqlManager(sql, befly);
|
|
601
20
|
|
|
602
21
|
return dbManager;
|
|
603
22
|
} else {
|
|
@@ -620,7 +39,8 @@ export default {
|
|
|
620
39
|
}
|
|
621
40
|
}
|
|
622
41
|
|
|
623
|
-
|
|
42
|
+
// 插件内禁止直接退出进程,抛出异常交由主流程统一处理
|
|
43
|
+
throw error;
|
|
624
44
|
}
|
|
625
45
|
}
|
|
626
46
|
};
|