befly 3.17.0 → 3.17.3
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 +6 -0
- package/apis/admin/cacheRefresh.js +122 -0
- package/apis/admin/del.js +34 -0
- package/apis/admin/detail.js +23 -0
- package/apis/admin/ins.js +69 -0
- package/apis/admin/list.js +28 -0
- package/apis/admin/upd.js +95 -0
- package/apis/api/all.js +24 -0
- package/apis/api/list.js +31 -0
- package/apis/auth/login.js +123 -0
- package/apis/auth/sendSmsCode.js +24 -0
- package/apis/dashboard/configStatus.js +39 -0
- package/apis/dashboard/environmentInfo.js +43 -0
- package/apis/dashboard/performanceMetrics.js +20 -0
- package/apis/dashboard/permissionStats.js +27 -0
- package/apis/dashboard/serviceStatus.js +75 -0
- package/apis/dashboard/systemInfo.js +19 -0
- package/apis/dashboard/systemOverview.js +30 -0
- package/apis/dashboard/systemResources.js +106 -0
- package/apis/dict/all.js +23 -0
- package/apis/dict/del.js +16 -0
- package/apis/dict/detail.js +27 -0
- package/apis/dict/ins.js +51 -0
- package/apis/dict/items.js +30 -0
- package/apis/dict/list.js +36 -0
- package/apis/dict/upd.js +74 -0
- package/apis/dictType/all.js +16 -0
- package/apis/dictType/del.js +38 -0
- package/apis/dictType/detail.js +20 -0
- package/apis/dictType/ins.js +37 -0
- package/apis/dictType/list.js +26 -0
- package/apis/dictType/upd.js +51 -0
- package/apis/email/config.js +25 -0
- package/apis/email/logList.js +23 -0
- package/apis/email/send.js +66 -0
- package/apis/email/verify.js +21 -0
- package/apis/loginLog/list.js +23 -0
- package/apis/menu/all.js +41 -0
- package/apis/menu/list.js +25 -0
- package/apis/operateLog/list.js +23 -0
- package/apis/role/all.js +21 -0
- package/apis/role/apiSave.js +43 -0
- package/apis/role/apis.js +22 -0
- package/apis/role/del.js +49 -0
- package/apis/role/detail.js +32 -0
- package/apis/role/ins.js +46 -0
- package/apis/role/list.js +27 -0
- package/apis/role/menuSave.js +42 -0
- package/apis/role/menus.js +22 -0
- package/apis/role/save.js +40 -0
- package/apis/role/upd.js +50 -0
- package/apis/sysConfig/all.js +16 -0
- package/apis/sysConfig/del.js +36 -0
- package/apis/sysConfig/get.js +49 -0
- package/apis/sysConfig/ins.js +50 -0
- package/apis/sysConfig/list.js +24 -0
- package/apis/sysConfig/upd.js +62 -0
- package/checks/api.js +55 -0
- package/checks/config.js +107 -0
- package/checks/hook.js +38 -0
- package/checks/menu.js +58 -0
- package/checks/plugin.js +38 -0
- package/checks/table.js +78 -0
- package/configs/beflyConfig.json +61 -0
- package/configs/beflyMenus.json +85 -0
- package/configs/constConfig.js +34 -0
- package/configs/regexpAlias.json +55 -0
- package/hooks/auth.js +34 -0
- package/hooks/cors.js +39 -0
- package/hooks/parser.js +90 -0
- package/hooks/permission.js +71 -0
- package/hooks/validator.js +43 -0
- package/index.js +334 -0
- package/lib/cacheHelper.js +483 -0
- package/lib/cacheKeys.js +42 -0
- package/lib/connect.js +120 -0
- package/lib/dbHelper/builders.js +698 -0
- package/lib/dbHelper/context.js +131 -0
- package/lib/dbHelper/dataOps.js +505 -0
- package/lib/dbHelper/execute.js +65 -0
- package/lib/dbHelper/index.js +27 -0
- package/lib/dbHelper/transaction.js +43 -0
- package/lib/dbHelper/util.js +58 -0
- package/lib/dbHelper/validate.js +549 -0
- package/lib/emailHelper.js +110 -0
- package/lib/logger.js +604 -0
- package/lib/redisHelper.js +684 -0
- package/lib/sqlBuilder/batch.js +113 -0
- package/lib/sqlBuilder/check.js +150 -0
- package/lib/sqlBuilder/compiler.js +347 -0
- package/lib/sqlBuilder/errors.js +60 -0
- package/lib/sqlBuilder/index.js +218 -0
- package/lib/sqlBuilder/parser.js +296 -0
- package/lib/sqlBuilder/util.js +260 -0
- package/lib/validator.js +303 -0
- package/package.json +19 -12
- package/paths.js +112 -0
- package/plugins/cache.js +16 -0
- package/plugins/config.js +11 -0
- package/plugins/email.js +27 -0
- package/plugins/logger.js +20 -0
- package/plugins/mysql.js +36 -0
- package/plugins/redis.js +34 -0
- package/plugins/tool.js +155 -0
- package/router/api.js +140 -0
- package/router/static.js +71 -0
- package/sql/admin.sql +18 -0
- package/sql/api.sql +12 -0
- package/sql/dict.sql +13 -0
- package/sql/dictType.sql +12 -0
- package/sql/emailLog.sql +20 -0
- package/sql/loginLog.sql +25 -0
- package/sql/menu.sql +12 -0
- package/sql/operateLog.sql +22 -0
- package/sql/role.sql +14 -0
- package/sql/sysConfig.sql +16 -0
- package/sync/api.js +93 -0
- package/sync/cache.js +13 -0
- package/sync/dev.js +171 -0
- package/sync/menu.js +99 -0
- package/tables/admin.json +56 -0
- package/tables/api.json +26 -0
- package/tables/dict.json +30 -0
- package/tables/dictType.json +24 -0
- package/tables/emailLog.json +61 -0
- package/tables/loginLog.json +86 -0
- package/tables/menu.json +24 -0
- package/tables/operateLog.json +68 -0
- package/tables/role.json +32 -0
- package/tables/sysConfig.json +43 -0
- package/utils/calcPerfTime.js +13 -0
- package/utils/cors.js +17 -0
- package/utils/deepMerge.js +78 -0
- package/utils/fieldClear.js +65 -0
- package/utils/formatYmdHms.js +23 -0
- package/utils/formatZodIssues.js +109 -0
- package/utils/getClientIp.js +47 -0
- package/utils/importDefault.js +51 -0
- package/utils/is.js +462 -0
- package/utils/loggerUtils.js +185 -0
- package/utils/processInfo.js +39 -0
- package/utils/regexpUtil.js +52 -0
- package/utils/response.js +114 -0
- package/utils/scanFiles.js +124 -0
- package/utils/scanSources.js +68 -0
- package/utils/sortModules.js +75 -0
- package/utils/toSessionTtlSeconds.js +14 -0
- package/utils/util.js +374 -0
- package/befly.js +0 -16413
- package/befly.min.js +0 -72
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
import { fieldClear } from "../../utils/fieldClear.js";
|
|
2
|
+
import { isFiniteNumber, isNonEmptyString, isNullable, isPlainObject, isString } from "../../utils/is.js";
|
|
3
|
+
import { arrayKeysToCamel, keysToCamel, keysToSnake, snakeCase } from "../../utils/util.js";
|
|
4
|
+
import { SqlBuilder } from "../sqlBuilder/index.js";
|
|
5
|
+
import { convertBigIntFields, quoteIdentMySql } from "./context.js";
|
|
6
|
+
import { getJoinMainQualifier, normalizeTableRef } from "./util.js";
|
|
7
|
+
import { assertNoExprField, validateAndClassifyFields, validateExcludeFieldsResult, validateQueryOptions, validateTimeIdValue } from "./validate.js";
|
|
8
|
+
|
|
9
|
+
export function clearDeep(value, options) {
|
|
10
|
+
const arrayObjectKeys = Array.isArray(options?.arrayObjectKeys) ? options.arrayObjectKeys : ["$or", "$and"];
|
|
11
|
+
const arrayObjectKeySet = new Set();
|
|
12
|
+
for (const key of arrayObjectKeys) {
|
|
13
|
+
arrayObjectKeySet.add(key);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const depthRaw = isFiniteNumber(options?.depth) ? Math.floor(options.depth) : 0;
|
|
17
|
+
const depth = depthRaw < 0 ? 0 : depthRaw;
|
|
18
|
+
|
|
19
|
+
const clearInternal = (input, remainingDepth) => {
|
|
20
|
+
if (!input || typeof input !== "object") {
|
|
21
|
+
return input;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Array.isArray(input)) {
|
|
25
|
+
return input;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const canRecurse = remainingDepth === 0 || remainingDepth > 1;
|
|
29
|
+
const childDepth = remainingDepth === 0 ? 0 : remainingDepth - 1;
|
|
30
|
+
const result = {};
|
|
31
|
+
|
|
32
|
+
for (const [key, item] of Object.entries(input)) {
|
|
33
|
+
if (isNullable(item)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (arrayObjectKeySet.has(key)) {
|
|
38
|
+
if (!Array.isArray(item)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const outList = [];
|
|
43
|
+
for (const child of item) {
|
|
44
|
+
if (!child || typeof child !== "object" || Array.isArray(child)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const cleaned = clearInternal(child, remainingDepth);
|
|
48
|
+
if (!cleaned || typeof cleaned !== "object" || Array.isArray(cleaned)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (Object.keys(cleaned).length === 0) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
outList.push(cleaned);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (outList.length > 0) {
|
|
58
|
+
result[key] = outList;
|
|
59
|
+
}
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (typeof item === "object" && !Array.isArray(item)) {
|
|
64
|
+
if (!canRecurse) {
|
|
65
|
+
result[key] = item;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const cleanedObj = clearInternal(item, childDepth);
|
|
70
|
+
if (!cleanedObj || typeof cleanedObj !== "object" || Array.isArray(cleanedObj)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (Object.keys(cleanedObj).length === 0) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
result[key] = cleanedObj;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
result[key] = item;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return clearInternal(value, depth);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function fieldsToSnake(table, fields, getTableColumns) {
|
|
90
|
+
if (!fields || !Array.isArray(fields)) {
|
|
91
|
+
return ["*"];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const classified = validateAndClassifyFields(fields);
|
|
95
|
+
if (classified.type === "all") {
|
|
96
|
+
return ["*"];
|
|
97
|
+
}
|
|
98
|
+
if (classified.type === "include") {
|
|
99
|
+
return classified.fields.map((field) => processJoinField(field));
|
|
100
|
+
}
|
|
101
|
+
if (classified.type === "exclude") {
|
|
102
|
+
const allColumns = await getTableColumns(table);
|
|
103
|
+
const excludeSnakeFields = classified.fields.map((f) => snakeCase(f));
|
|
104
|
+
const resultFields = allColumns.filter((col) => !excludeSnakeFields.includes(col));
|
|
105
|
+
validateExcludeFieldsResult(resultFields, table, excludeSnakeFields);
|
|
106
|
+
return resultFields;
|
|
107
|
+
}
|
|
108
|
+
return ["*"];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function orderByToSnake(orderBy) {
|
|
112
|
+
if (!orderBy || !Array.isArray(orderBy)) {
|
|
113
|
+
return orderBy;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return orderBy.map((item) => {
|
|
117
|
+
if (!isString(item) || !item.includes("#")) {
|
|
118
|
+
return item;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const parts = item.split("#");
|
|
122
|
+
if (parts.length !== 2) {
|
|
123
|
+
return item;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const field = parts[0];
|
|
127
|
+
const direction = parts[1];
|
|
128
|
+
if (!isString(field) || !isString(direction)) {
|
|
129
|
+
return item;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return `${snakeCase(field.trim())}#${direction.trim()}`;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function processJoinField(field) {
|
|
137
|
+
assertNoExprField(field);
|
|
138
|
+
if (field === "*" || field.startsWith("`")) {
|
|
139
|
+
return field;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (field.toUpperCase().includes(" AS ")) {
|
|
143
|
+
const parts = field.split(/\s+AS\s+/i);
|
|
144
|
+
const fieldPart = parts[0];
|
|
145
|
+
const aliasPart = parts[1];
|
|
146
|
+
if (!isString(fieldPart) || !isString(aliasPart)) {
|
|
147
|
+
return field;
|
|
148
|
+
}
|
|
149
|
+
return `${processJoinField(fieldPart.trim())} AS ${aliasPart.trim()}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (field.includes(".")) {
|
|
153
|
+
const parts = field.split(".");
|
|
154
|
+
if (parts.length < 2) {
|
|
155
|
+
return snakeCase(field);
|
|
156
|
+
}
|
|
157
|
+
if (parts.some((p) => !isNonEmptyString(p))) {
|
|
158
|
+
return field;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const fieldName = parts[parts.length - 1];
|
|
162
|
+
const tableName = parts.slice(0, parts.length - 1).join(".");
|
|
163
|
+
if (!isString(fieldName) || !isString(tableName)) {
|
|
164
|
+
return snakeCase(field);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const normalizedTableName = tableName
|
|
168
|
+
.split(".")
|
|
169
|
+
.map((item) => {
|
|
170
|
+
const trimmed = item.trim();
|
|
171
|
+
if (!trimmed) {
|
|
172
|
+
return trimmed;
|
|
173
|
+
}
|
|
174
|
+
return /[A-Z]/.test(trimmed) ? snakeCase(trimmed) : trimmed;
|
|
175
|
+
})
|
|
176
|
+
.join(".");
|
|
177
|
+
|
|
178
|
+
return `${normalizedTableName}.${snakeCase(fieldName)}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return snakeCase(field);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function processJoinWhereKey(key) {
|
|
185
|
+
if (key === "$or" || key === "$and") {
|
|
186
|
+
return key;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
assertNoExprField(key);
|
|
190
|
+
if (key.includes("$")) {
|
|
191
|
+
const lastDollarIndex = key.lastIndexOf("$");
|
|
192
|
+
const fieldPart = key.substring(0, lastDollarIndex);
|
|
193
|
+
const operator = key.substring(lastDollarIndex);
|
|
194
|
+
|
|
195
|
+
if (fieldPart.includes(".")) {
|
|
196
|
+
const parts = fieldPart.split(".");
|
|
197
|
+
if (parts.length < 2) {
|
|
198
|
+
return `${snakeCase(fieldPart)}${operator}`;
|
|
199
|
+
}
|
|
200
|
+
if (parts.some((p) => !isNonEmptyString(p))) {
|
|
201
|
+
return `${snakeCase(fieldPart)}${operator}`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const fieldName = parts[parts.length - 1];
|
|
205
|
+
const tableName = parts.slice(0, parts.length - 1).join(".");
|
|
206
|
+
if (!isString(fieldName) || !isString(tableName)) {
|
|
207
|
+
return `${snakeCase(fieldPart)}${operator}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const normalizedTableName = tableName
|
|
211
|
+
.split(".")
|
|
212
|
+
.map((item) => {
|
|
213
|
+
const trimmed = item.trim();
|
|
214
|
+
if (!trimmed) {
|
|
215
|
+
return trimmed;
|
|
216
|
+
}
|
|
217
|
+
return /[A-Z]/.test(trimmed) ? snakeCase(trimmed) : trimmed;
|
|
218
|
+
})
|
|
219
|
+
.join(".");
|
|
220
|
+
return `${normalizedTableName}.${snakeCase(fieldName)}${operator}`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return `${snakeCase(fieldPart)}${operator}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (key.includes(".")) {
|
|
227
|
+
const parts = key.split(".");
|
|
228
|
+
if (parts.length < 2) {
|
|
229
|
+
return snakeCase(key);
|
|
230
|
+
}
|
|
231
|
+
if (parts.some((p) => !isNonEmptyString(p))) {
|
|
232
|
+
return snakeCase(key);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const fieldName = parts[parts.length - 1];
|
|
236
|
+
const tableName = parts.slice(0, parts.length - 1).join(".");
|
|
237
|
+
if (!isString(fieldName) || !isString(tableName)) {
|
|
238
|
+
return snakeCase(key);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const normalizedTableName = tableName
|
|
242
|
+
.split(".")
|
|
243
|
+
.map((item) => {
|
|
244
|
+
const trimmed = item.trim();
|
|
245
|
+
if (!trimmed) {
|
|
246
|
+
return trimmed;
|
|
247
|
+
}
|
|
248
|
+
return /[A-Z]/.test(trimmed) ? snakeCase(trimmed) : trimmed;
|
|
249
|
+
})
|
|
250
|
+
.join(".");
|
|
251
|
+
return `${normalizedTableName}.${snakeCase(fieldName)}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return snakeCase(key);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function processJoinWhere(where) {
|
|
258
|
+
if (!where || typeof where !== "object") {
|
|
259
|
+
return where;
|
|
260
|
+
}
|
|
261
|
+
if (Array.isArray(where)) {
|
|
262
|
+
return where.map((item) => processJoinWhere(item));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const result = {};
|
|
266
|
+
for (const [key, value] of Object.entries(where)) {
|
|
267
|
+
const newKey = processJoinWhereKey(key);
|
|
268
|
+
if (key === "$or" || key === "$and") {
|
|
269
|
+
result[newKey] = Array.isArray(value) ? value.map((item) => processJoinWhere(item)) : [];
|
|
270
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
271
|
+
result[newKey] = processJoinWhere(value);
|
|
272
|
+
} else {
|
|
273
|
+
result[newKey] = value;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function processJoinOrderBy(orderBy) {
|
|
281
|
+
if (!orderBy || !Array.isArray(orderBy)) {
|
|
282
|
+
return orderBy;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return orderBy.map((item) => {
|
|
286
|
+
if (!isString(item) || !item.includes("#")) {
|
|
287
|
+
return item;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const parts = item.split("#");
|
|
291
|
+
if (parts.length !== 2) {
|
|
292
|
+
return item;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const field = parts[0];
|
|
296
|
+
const direction = parts[1];
|
|
297
|
+
if (!isString(field) || !isString(direction)) {
|
|
298
|
+
return item;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return `${processJoinField(field.trim())}#${direction.trim()}`;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function processJoinOn(on) {
|
|
306
|
+
if (!isString(on)) {
|
|
307
|
+
return String(on);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const raw = String(on);
|
|
311
|
+
if (!isNonEmptyString(raw)) {
|
|
312
|
+
return raw;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const replaceSegment = (segment) => {
|
|
316
|
+
return segment.replace(/([a-zA-Z_][a-zA-Z0-9_]*)(\s*\.\s*)([a-zA-Z_][a-zA-Z0-9_]*)/g, (_match, left, dot, right) => {
|
|
317
|
+
return `${snakeCase(left)}${dot}${snakeCase(right)}`;
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
let result = "";
|
|
322
|
+
let buffer = "";
|
|
323
|
+
let quote = null;
|
|
324
|
+
let prev = "";
|
|
325
|
+
|
|
326
|
+
for (const ch of raw) {
|
|
327
|
+
if (quote) {
|
|
328
|
+
result += ch;
|
|
329
|
+
if (ch === quote && prev !== "\\") {
|
|
330
|
+
quote = null;
|
|
331
|
+
}
|
|
332
|
+
prev = ch;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
337
|
+
if (buffer.length > 0) {
|
|
338
|
+
result += replaceSegment(buffer);
|
|
339
|
+
buffer = "";
|
|
340
|
+
}
|
|
341
|
+
quote = ch;
|
|
342
|
+
result += ch;
|
|
343
|
+
prev = ch;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
buffer += ch;
|
|
348
|
+
prev = ch;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (buffer.length > 0) {
|
|
352
|
+
result += replaceSegment(buffer);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function addDefaultStateFilter(where = {}, table, hasJoins = false) {
|
|
359
|
+
const hasStateCondition = Object.keys(where).some((key) => key.startsWith("state") || key.includes(".state"));
|
|
360
|
+
if (hasStateCondition) {
|
|
361
|
+
return where;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (hasJoins && table) {
|
|
365
|
+
const result = {};
|
|
366
|
+
for (const [key, value] of Object.entries(where)) {
|
|
367
|
+
result[key] = value;
|
|
368
|
+
}
|
|
369
|
+
result[`${table}.state$gt`] = 0;
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const result = {};
|
|
374
|
+
for (const [key, value] of Object.entries(where)) {
|
|
375
|
+
result[key] = value;
|
|
376
|
+
}
|
|
377
|
+
result.state$gt = 0;
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function whereKeysToSnake(where) {
|
|
382
|
+
if (!where || typeof where !== "object") {
|
|
383
|
+
return where;
|
|
384
|
+
}
|
|
385
|
+
if (Array.isArray(where)) {
|
|
386
|
+
return where.map((item) => whereKeysToSnake(item));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const result = {};
|
|
390
|
+
for (const [key, value] of Object.entries(where)) {
|
|
391
|
+
if (key === "$or" || key === "$and") {
|
|
392
|
+
result[key] = Array.isArray(value) ? value.map((item) => whereKeysToSnake(item)) : [];
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
assertNoExprField(key);
|
|
397
|
+
if (key.includes("$")) {
|
|
398
|
+
const lastDollarIndex = key.lastIndexOf("$");
|
|
399
|
+
const fieldName = key.substring(0, lastDollarIndex);
|
|
400
|
+
const operator = key.substring(lastDollarIndex);
|
|
401
|
+
result[snakeCase(fieldName) + operator] = value;
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const snakeKey = snakeCase(key);
|
|
406
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
407
|
+
result[snakeKey] = whereKeysToSnake(value);
|
|
408
|
+
} else {
|
|
409
|
+
result[snakeKey] = value;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return result;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function serializeArrayFields(data) {
|
|
417
|
+
const serialized = {};
|
|
418
|
+
for (const [key, value] of Object.entries(data)) {
|
|
419
|
+
if (isNullable(value)) {
|
|
420
|
+
serialized[key] = value;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (Array.isArray(value)) {
|
|
424
|
+
serialized[key] = JSON.stringify(value);
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
serialized[key] = value;
|
|
428
|
+
}
|
|
429
|
+
return serialized;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function deserializeArrayFields(data) {
|
|
433
|
+
if (!data) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
const deserialized = {};
|
|
437
|
+
for (const [key, value] of Object.entries(data)) {
|
|
438
|
+
deserialized[key] = value;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
for (const [key, value] of Object.entries(deserialized)) {
|
|
442
|
+
if (!isString(value)) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
446
|
+
try {
|
|
447
|
+
const parsed = JSON.parse(value);
|
|
448
|
+
if (Array.isArray(parsed)) {
|
|
449
|
+
deserialized[key] = parsed;
|
|
450
|
+
}
|
|
451
|
+
} catch {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return deserialized;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function cleanAndSnakeAndSerializeWriteData(data, excludeValues = [null, undefined]) {
|
|
461
|
+
const cleanData = fieldClear(data, { excludeValues: excludeValues });
|
|
462
|
+
const snakeData = keysToSnake(cleanData);
|
|
463
|
+
return serializeArrayFields(snakeData);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function stripSystemFieldsForWrite(data, options) {
|
|
467
|
+
const result = {};
|
|
468
|
+
for (const [key, value] of Object.entries(data)) {
|
|
469
|
+
if (key === "id") continue;
|
|
470
|
+
if (key === "created_at") continue;
|
|
471
|
+
if (key === "updated_at") continue;
|
|
472
|
+
if (key === "deleted_at") continue;
|
|
473
|
+
if (!options.allowState && key === "state") continue;
|
|
474
|
+
result[key] = value;
|
|
475
|
+
}
|
|
476
|
+
return result;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export function buildInsertRow(options) {
|
|
480
|
+
const serializedData = cleanAndSnakeAndSerializeWriteData(options.data);
|
|
481
|
+
const userData = stripSystemFieldsForWrite(serializedData, { allowState: false });
|
|
482
|
+
|
|
483
|
+
const result = {};
|
|
484
|
+
for (const [key, value] of Object.entries(userData)) {
|
|
485
|
+
result[key] = value;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (options.idMode === "timeId") {
|
|
489
|
+
validateTimeIdValue(options.id);
|
|
490
|
+
result["id"] = options.id;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
result["created_at"] = options.now;
|
|
494
|
+
result["updated_at"] = options.now;
|
|
495
|
+
result["state"] = 1;
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function buildUpdateRow(options) {
|
|
500
|
+
const serializedData = cleanAndSnakeAndSerializeWriteData(options.data);
|
|
501
|
+
const userData = stripSystemFieldsForWrite(serializedData, { allowState: options.allowState });
|
|
502
|
+
|
|
503
|
+
const result = {};
|
|
504
|
+
for (const [key, value] of Object.entries(userData)) {
|
|
505
|
+
result[key] = value;
|
|
506
|
+
}
|
|
507
|
+
result["updated_at"] = options.now;
|
|
508
|
+
return result;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export function buildPartialUpdateData(options) {
|
|
512
|
+
const serializedData = cleanAndSnakeAndSerializeWriteData(options.data);
|
|
513
|
+
return stripSystemFieldsForWrite(serializedData, { allowState: options.allowState });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export const builderMethods = {
|
|
517
|
+
createSqlBuilder() {
|
|
518
|
+
return new SqlBuilder({ quoteIdent: quoteIdentMySql });
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
createListBuilder(prepared, whereFiltered, limit, offset) {
|
|
522
|
+
const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(whereFiltered).limit(limit);
|
|
523
|
+
this.applyJoins(builder, prepared.joins);
|
|
524
|
+
if (!isNullable(offset)) {
|
|
525
|
+
builder.offset(offset);
|
|
526
|
+
}
|
|
527
|
+
if (prepared.orderBy && prepared.orderBy.length > 0) {
|
|
528
|
+
builder.orderBy(prepared.orderBy);
|
|
529
|
+
}
|
|
530
|
+
return builder;
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
async fetchCount(prepared, whereFiltered, alias) {
|
|
534
|
+
const builder = this.createSqlBuilder().selectRaw(alias).from(prepared.table).where(whereFiltered);
|
|
535
|
+
this.applyJoins(builder, prepared.joins);
|
|
536
|
+
const result = builder.toSelectSql();
|
|
537
|
+
const executeRes = await this.execute(result.sql, result.params);
|
|
538
|
+
const total = executeRes.data?.[0]?.total || executeRes.data?.[0]?.count || 0;
|
|
539
|
+
return {
|
|
540
|
+
total: total,
|
|
541
|
+
sql: executeRes.sql
|
|
542
|
+
};
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
normalizeRowData(row, bigintFields) {
|
|
546
|
+
if (!row) {
|
|
547
|
+
return {};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const camelRow = keysToCamel(row);
|
|
551
|
+
const deserialized = deserializeArrayFields(camelRow);
|
|
552
|
+
if (!deserialized) {
|
|
553
|
+
return {};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const convertedList = convertBigIntFields([deserialized], bigintFields);
|
|
557
|
+
if (convertedList && convertedList.length > 0) {
|
|
558
|
+
return convertedList[0];
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return deserialized;
|
|
562
|
+
},
|
|
563
|
+
|
|
564
|
+
normalizeListData(list, bigintFields) {
|
|
565
|
+
const camelList = arrayKeysToCamel(list);
|
|
566
|
+
const deserializedList = camelList.map((item) => deserializeArrayFields(item)).filter((item) => item !== null);
|
|
567
|
+
return convertBigIntFields(deserializedList, bigintFields);
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
normalizeJoinOptions(options, cleanWhere) {
|
|
571
|
+
const processedFields = (options.fields || []).map((field) => processJoinField(field));
|
|
572
|
+
const normalizedTableRef = normalizeTableRef(options.table);
|
|
573
|
+
const mainQualifier = getJoinMainQualifier(options.table);
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
table: normalizedTableRef,
|
|
577
|
+
tableQualifier: mainQualifier,
|
|
578
|
+
fields: processedFields.length > 0 ? processedFields : ["*"],
|
|
579
|
+
where: processJoinWhere(cleanWhere),
|
|
580
|
+
joins: options.joins,
|
|
581
|
+
orderBy: processJoinOrderBy(options.orderBy || []),
|
|
582
|
+
page: options.page || 1,
|
|
583
|
+
limit: options.limit || 10
|
|
584
|
+
};
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
async normalizeSingleTableOptions(options, cleanWhere) {
|
|
588
|
+
const snakeTable = snakeCase(options.table);
|
|
589
|
+
const processedFields = await fieldsToSnake(snakeTable, options.fields || [], this.getTableColumns.bind(this));
|
|
590
|
+
|
|
591
|
+
return {
|
|
592
|
+
table: snakeTable,
|
|
593
|
+
tableQualifier: snakeTable,
|
|
594
|
+
fields: processedFields,
|
|
595
|
+
where: whereKeysToSnake(cleanWhere),
|
|
596
|
+
joins: undefined,
|
|
597
|
+
orderBy: orderByToSnake(options.orderBy || []),
|
|
598
|
+
page: options.page || 1,
|
|
599
|
+
limit: options.limit || 10
|
|
600
|
+
};
|
|
601
|
+
},
|
|
602
|
+
|
|
603
|
+
buildQueryOptions(options, defaults) {
|
|
604
|
+
const output = {
|
|
605
|
+
table: options.table,
|
|
606
|
+
page: defaults.page,
|
|
607
|
+
limit: defaults.limit
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
if (options.fields !== undefined) output.fields = options.fields;
|
|
611
|
+
if (options.bigint !== undefined) output.bigint = options.bigint;
|
|
612
|
+
if (options.where !== undefined) output.where = options.where;
|
|
613
|
+
if (options.joins !== undefined) output.joins = options.joins;
|
|
614
|
+
if (options.orderBy !== undefined) output.orderBy = options.orderBy;
|
|
615
|
+
return output;
|
|
616
|
+
},
|
|
617
|
+
|
|
618
|
+
resolveFieldValue(record, field) {
|
|
619
|
+
if (!isPlainObject(record)) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (Object.hasOwn(record, field)) {
|
|
624
|
+
return record[field];
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const camelField = field.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
628
|
+
if (camelField !== field && Object.hasOwn(record, camelField)) {
|
|
629
|
+
return record[camelField];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const snakeField = field.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
633
|
+
if (snakeField !== field && Object.hasOwn(record, snakeField)) {
|
|
634
|
+
return record[snakeField];
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return null;
|
|
638
|
+
},
|
|
639
|
+
|
|
640
|
+
async getTableColumns(table) {
|
|
641
|
+
const quotedTable = quoteIdentMySql(table);
|
|
642
|
+
const executeRes = await this.execute(`SHOW COLUMNS FROM ${quotedTable}`, []);
|
|
643
|
+
const result = executeRes.data;
|
|
644
|
+
|
|
645
|
+
if (!result || result.length === 0) {
|
|
646
|
+
throw new Error(`表 ${table} 不存在或没有字段`, {
|
|
647
|
+
cause: null,
|
|
648
|
+
code: "runtime"
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const columnNames = [];
|
|
653
|
+
for (const row of result) {
|
|
654
|
+
const record = row;
|
|
655
|
+
const name = record["Field"];
|
|
656
|
+
if (isNonEmptyString(name)) {
|
|
657
|
+
columnNames.push(name);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return columnNames;
|
|
662
|
+
},
|
|
663
|
+
|
|
664
|
+
async prepareQueryOptions(options, label = "queryOptions") {
|
|
665
|
+
validateQueryOptions(options, label);
|
|
666
|
+
const cleanWhere = clearDeep(options.where || {});
|
|
667
|
+
const hasJoins = options.joins && options.joins.length > 0;
|
|
668
|
+
|
|
669
|
+
if (hasJoins) {
|
|
670
|
+
return this.normalizeJoinOptions(options, cleanWhere);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return await this.normalizeSingleTableOptions(options, cleanWhere);
|
|
674
|
+
},
|
|
675
|
+
|
|
676
|
+
applyJoins(builder, joins) {
|
|
677
|
+
if (!joins || joins.length === 0) return;
|
|
678
|
+
|
|
679
|
+
for (const join of joins) {
|
|
680
|
+
const processedTable = normalizeTableRef(join.table);
|
|
681
|
+
const processedOn = processJoinOn(join.on);
|
|
682
|
+
const type = join.type || "left";
|
|
683
|
+
|
|
684
|
+
switch (type) {
|
|
685
|
+
case "inner":
|
|
686
|
+
builder.innerJoin(processedTable, processedOn);
|
|
687
|
+
break;
|
|
688
|
+
case "right":
|
|
689
|
+
builder.rightJoin(processedTable, processedOn);
|
|
690
|
+
break;
|
|
691
|
+
case "left":
|
|
692
|
+
default:
|
|
693
|
+
builder.leftJoin(processedTable, processedOn);
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
};
|