befly 3.22.9 → 3.23.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/lib/dbParse.js CHANGED
@@ -39,10 +39,6 @@ function validateWhereObject(where, label, required = false) {
39
39
  }
40
40
 
41
41
  const visitWhereMeta = (currentWhere, currentLabel) => {
42
- if (!isPlainObject(currentWhere)) {
43
- return;
44
- }
45
-
46
42
  for (const [key, value] of Object.entries(currentWhere)) {
47
43
  if (key === "$exclude") {
48
44
  if (isNullable(value)) {
@@ -198,42 +194,44 @@ function validateQueryOptions(options, label) {
198
194
  });
199
195
  }
200
196
 
201
- if (Array.isArray(options.table)) {
202
- if (options.table.length === 0) {
203
- throw new Error(`${label}.table 不能为空数组`, {
204
- cause: null,
205
- code: "validation"
206
- });
207
- }
197
+ const tableList = Array.isArray(options.table) ? options.table : [options.table];
198
+
199
+ if (Array.isArray(options.table) && tableList.length === 0) {
200
+ throw new Error(`${label}.table 不能为空数组`, {
201
+ cause: null,
202
+ code: "validation"
203
+ });
204
+ }
208
205
 
209
- for (let i = 0; i < options.table.length; i++) {
210
- assertNonEmptyString(options.table[i], `${label}.table[${i}]`);
211
- parseTableRef(options.table[i]);
206
+ for (let i = 0; i < tableList.length; i++) {
207
+ if (Array.isArray(options.table)) {
208
+ assertNonEmptyString(tableList[i], `${label}.table[${i}]`);
209
+ } else {
210
+ assertNonEmptyString(tableList[i], `${label}.table`);
212
211
  }
213
212
 
213
+ parseTableRef(tableList[i]);
214
+ }
215
+
216
+ if (Array.isArray(options.table)) {
214
217
  if (Array.isArray(options.leftJoin) && options.leftJoin.length > 0) {
215
- if (options.table.length !== options.leftJoin.length + 1) {
218
+ if (tableList.length !== options.leftJoin.length + 1) {
216
219
  throw new Error(`${label}.table 与 ${label}.leftJoin 数量不匹配,要求 table.length = leftJoin.length + 1`, {
217
220
  cause: null,
218
221
  code: "validation"
219
222
  });
220
223
  }
221
- } else if (options.table.length > 1) {
224
+ } else if (tableList.length > 1) {
222
225
  throw new Error(`${label}.table 为数组时必须配合 leftJoin 使用`, {
223
226
  cause: null,
224
227
  code: "validation"
225
228
  });
226
229
  }
227
- } else {
228
- assertNonEmptyString(options.table, `${label}.table`);
229
- parseTableRef(options.table);
230
-
231
- if (Array.isArray(options.leftJoin) && options.leftJoin.length > 0) {
232
- throw new Error(`${label}.leftJoin 启用时,${label}.table 必须是数组`, {
233
- cause: null,
234
- code: "validation"
235
- });
236
- }
230
+ } else if (Array.isArray(options.leftJoin) && options.leftJoin.length > 0) {
231
+ throw new Error(`${label}.leftJoin 启用时,${label}.table 必须是数组`, {
232
+ cause: null,
233
+ code: "validation"
234
+ });
237
235
  }
238
236
 
239
237
  if (!isNullable(options.where)) {
@@ -739,6 +737,18 @@ function applyDefaultStateFilter(where = {}, table, hasLeftJoin = false, beflyMo
739
737
  return result;
740
738
  }
741
739
 
740
+ function isBeflySystemTable(table) {
741
+ return parseTableRef(table).table.startsWith("befly");
742
+ }
743
+
744
+ function resolveDeleteMode(table, beflyMode = "auto") {
745
+ if (beflyMode === "manual" && isBeflySystemTable(table)) {
746
+ return "auto";
747
+ }
748
+
749
+ return beflyMode;
750
+ }
751
+
742
752
  function parseLeftJoinItem(joinTable, joinItem) {
743
753
  const parts = joinItem.split(" ");
744
754
  return {
@@ -834,25 +844,6 @@ export class DbParse {
834
844
  return this._prepareSingleTableWhere(options.table, options.where, true, false, "exists");
835
845
  }
836
846
 
837
- async parseFieldValue(options) {
838
- validateNoLeftJoinReadOptions(options, "getFieldValue", "getFieldValue 不支持 leftJoin(请使用 getOne/getList 并自行取字段)");
839
- assertSafeFieldName(options.field);
840
-
841
- const readOptions = {
842
- table: options.table,
843
- fields: [options.field]
844
- };
845
- if (options.where !== undefined) readOptions.where = options.where;
846
- if (options.orderBy !== undefined) readOptions.orderBy = options.orderBy;
847
- if (options.page !== undefined) readOptions.page = options.page;
848
- if (options.limit !== undefined) readOptions.limit = options.limit;
849
-
850
- return {
851
- field: options.field,
852
- prepared: await this.parseRead(readOptions, "one")
853
- };
854
- }
855
-
856
847
  parseInsert(options) {
857
848
  assertNonEmptyString(options.table, "insData.table");
858
849
  assertPlainObjectValue(options.data, "insData.data");
@@ -889,11 +880,20 @@ export class DbParse {
889
880
  const label = force ? "delForce" : "delData";
890
881
  assertNonEmptyString(options.table, `${label}.table`);
891
882
  validateWhereObject(options.where, `${label}.where`, true);
892
- return {
883
+ const deleteMode = resolveDeleteMode(options.table, this.beflyMode);
884
+ const result = {
893
885
  table: options.table,
894
886
  snakeTable: snakeCase(options.table),
895
- where: this._prepareSingleTableWhere(options.table, options.where, !force, true, label).where
887
+ where: this._prepareSingleTableWhere(options.table, options.where, !force, true, label, deleteMode).where,
888
+ deleteMode: deleteMode
896
889
  };
890
+
891
+ if (!force && deleteMode === "manual") {
892
+ assertPlainObjectValue(options.data, "delData.data");
893
+ result.data = options.data;
894
+ }
895
+
896
+ return result;
897
897
  }
898
898
 
899
899
  parseDelForceBatch(table, ids) {
@@ -989,13 +989,13 @@ export class DbParse {
989
989
  };
990
990
  }
991
991
 
992
- _prepareSingleTableWhere(table, where, useDefaultStateFilter, required, label) {
992
+ _prepareSingleTableWhere(table, where, useDefaultStateFilter, required, label, beflyMode = this.beflyMode) {
993
993
  const snakeTable = snakeCase(table);
994
994
  const normalizedWhere = whereKeysToSnake(clearDeep(where || {}));
995
995
  if (required) {
996
996
  assertEffectiveWhere(normalizedWhere, label);
997
997
  }
998
- const whereWithDefault = useDefaultStateFilter ? applyDefaultStateFilter(normalizedWhere, snakeTable, false, this.beflyMode) : normalizedWhere;
998
+ const whereWithDefault = useDefaultStateFilter ? applyDefaultStateFilter(normalizedWhere, snakeTable, false, beflyMode) : normalizedWhere;
999
999
  return {
1000
1000
  snakeTable: snakeTable,
1001
1001
  where: parseWhereObject(whereWithDefault)
package/lib/dbUtil.js CHANGED
@@ -34,7 +34,7 @@ export function parseTableRef(tableRef) {
34
34
  });
35
35
  }
36
36
 
37
- const parts = trimmed.split(/\s+/).filter((part) => part.length > 0);
37
+ const parts = trimmed.split(/\s+/);
38
38
  if (parts.length > 2) {
39
39
  throw new Error(`不支持的表引用格式(包含过多片段)。请使用最简形式:table 或 table alias 或 schema.table 或 schema.table alias (tableRef: ${trimmed})`, {
40
40
  cause: null,
@@ -42,27 +42,7 @@ export function parseTableRef(tableRef) {
42
42
  });
43
43
  }
44
44
 
45
- const namePart = parts[0];
46
- if (!isNonEmptyString(namePart)) {
47
- throw new Error(`tableRef 解析失败:缺少表名 (tableRef: ${trimmed})`, {
48
- cause: null,
49
- code: "validation"
50
- });
51
- }
52
-
53
- let aliasPart = null;
54
- if (parts.length === 2) {
55
- const alias = parts[1];
56
- if (!isNonEmptyString(alias)) {
57
- throw new Error(`tableRef 解析失败:缺少 alias (tableRef: ${trimmed})`, {
58
- cause: null,
59
- code: "validation"
60
- });
61
- }
62
- aliasPart = alias;
63
- }
64
-
65
- const nameSegments = namePart.split(".");
45
+ const nameSegments = parts[0].split(".");
66
46
  if (nameSegments.length > 2) {
67
47
  throw new Error(`不支持的表引用格式(schema 层级过深) (tableRef: ${trimmed})`, {
68
48
  cause: null,
@@ -71,32 +51,38 @@ export function parseTableRef(tableRef) {
71
51
  }
72
52
 
73
53
  if (nameSegments.length === 2) {
74
- const schema = nameSegments[0];
75
- const table = nameSegments[1];
76
- if (!isNonEmptyString(schema)) {
54
+ if (!nameSegments[0]) {
77
55
  throw new Error(`tableRef 解析失败:schema 为空 (tableRef: ${trimmed})`, {
78
56
  cause: null,
79
57
  code: "validation"
80
58
  });
81
59
  }
82
- if (!isNonEmptyString(table)) {
60
+ if (!nameSegments[1]) {
83
61
  throw new Error(`tableRef 解析失败:table 为空 (tableRef: ${trimmed})`, {
84
62
  cause: null,
85
63
  code: "validation"
86
64
  });
87
65
  }
88
- return { schema: schema, table: table, alias: aliasPart };
66
+
67
+ return {
68
+ schema: nameSegments[0],
69
+ table: nameSegments[1],
70
+ alias: parts[1] || null
71
+ };
89
72
  }
90
73
 
91
- const table = nameSegments[0];
92
- if (!isNonEmptyString(table)) {
74
+ if (!nameSegments[0]) {
93
75
  throw new Error(`tableRef 解析失败:table 为空 (tableRef: ${trimmed})`, {
94
76
  cause: null,
95
77
  code: "validation"
96
78
  });
97
79
  }
98
80
 
99
- return { schema: null, table: table, alias: aliasPart };
81
+ return {
82
+ schema: null,
83
+ table: nameSegments[0],
84
+ alias: parts[1] || null
85
+ };
100
86
  }
101
87
 
102
88
  function isQuotedIdentPaired(value) {
package/lib/logger.js CHANGED
@@ -100,12 +100,12 @@ class LogFileSink {
100
100
  }
101
101
 
102
102
  async flushNow() {
103
- this.cancelScheduledFlush();
103
+ this.clearScheduledFlush();
104
104
  await this.flush();
105
105
  }
106
106
 
107
107
  async shutdown() {
108
- this.cancelScheduledFlush();
108
+ this.clearScheduledFlush();
109
109
  await this.flush();
110
110
  await this.closeStream();
111
111
  }
@@ -120,24 +120,22 @@ class LogFileSink {
120
120
  }, this.flushDelayMs);
121
121
  }
122
122
 
123
- cancelScheduledFlush() {
123
+ clearScheduledFlush() {
124
124
  if (!this.scheduledTimer) return;
125
125
  clearTimeout(this.scheduledTimer);
126
126
  this.scheduledTimer = null;
127
127
  }
128
128
 
129
129
  async flush() {
130
- if (this.flushing) return;
130
+ if (this.flushing || this.pending.length === 0) return;
131
131
  this.flushing = true;
132
132
 
133
133
  try {
134
134
  while (this.pending.length > 0) {
135
135
  const batch = shiftBatchFromPending(this.pending, this.maxBatchBytes);
136
- const chunk = batch.chunk;
137
- const chunkBytes = Buffer.byteLength(chunk);
138
- this.pendingBytes = this.pendingBytes - chunkBytes;
136
+ this.pendingBytes = this.pendingBytes - batch.bytes;
139
137
 
140
- const ok = await this.writeChunk(chunk, chunkBytes);
138
+ const ok = await this.writeChunk(batch.chunk, batch.bytes);
141
139
  if (!ok) {
142
140
  // writer 已禁用/失败:清空剩余 pending
143
141
  this.pending = [];
@@ -267,13 +265,13 @@ export async function flush() {
267
265
  // 测试场景:mock logger 不需要 flush
268
266
  if (mockInstance) return;
269
267
 
270
- const sinks = [];
271
- if (appFileSink) sinks.push({ flush: () => (appFileSink ? appFileSink.flushNow() : Promise.resolve()) });
272
- if (errorFileSink) sinks.push({ flush: () => (errorFileSink ? errorFileSink.flushNow() : Promise.resolve()) });
268
+ for (const sink of [appFileSink, errorFileSink]) {
269
+ if (!sink) {
270
+ continue;
271
+ }
273
272
 
274
- for (const item of sinks) {
275
273
  try {
276
- await item.flush();
274
+ await sink.flushNow();
277
275
  } catch {
278
276
  // ignore
279
277
  }
@@ -289,13 +287,13 @@ export async function shutdown() {
289
287
  const currentAppFileSink = appFileSink;
290
288
  const currentErrorFileSink = errorFileSink;
291
289
 
292
- const sinks = [];
293
- if (currentAppFileSink) sinks.push({ shutdown: () => currentAppFileSink.shutdown() });
294
- if (currentErrorFileSink) sinks.push({ shutdown: () => currentErrorFileSink.shutdown() });
290
+ for (const sink of [currentAppFileSink, currentErrorFileSink]) {
291
+ if (!sink) {
292
+ continue;
293
+ }
295
294
 
296
- for (const item of sinks) {
297
295
  try {
298
- await item.shutdown();
296
+ await sink.shutdown();
299
297
  } catch {
300
298
  // ignore
301
299
  }
package/lib/sqlBuilder.js CHANGED
@@ -45,73 +45,46 @@ export class SqlBuilder {
45
45
  _compileOperatorNode(node) {
46
46
  const escapedField = escapeField(node.field, this._quoteIdent);
47
47
 
48
- if (node.operator === "$not") {
49
- return { sql: `${escapedField} != ?`, params: [node.value] };
48
+ switch (node.operator) {
49
+ case "$not":
50
+ return { sql: `${escapedField} != ?`, params: [node.value] };
51
+ case "$in":
52
+ return {
53
+ sql: `${escapedField} IN (${node.value.map(() => "?").join(",")})`,
54
+ params: node.value.slice()
55
+ };
56
+ case "$notIn":
57
+ return {
58
+ sql: `${escapedField} NOT IN (${node.value.map(() => "?").join(",")})`,
59
+ params: node.value.slice()
60
+ };
61
+ case "$like":
62
+ return { sql: `${escapedField} LIKE ?`, params: [`%${String(node.value)}%`] };
63
+ case "$leftLike":
64
+ return { sql: `${escapedField} LIKE ?`, params: [`%${String(node.value)}`] };
65
+ case "$rightLike":
66
+ return { sql: `${escapedField} LIKE ?`, params: [`${String(node.value)}%`] };
67
+ case "$notLike":
68
+ return { sql: `${escapedField} NOT LIKE ?`, params: [node.value] };
69
+ case "$gt":
70
+ return { sql: `${escapedField} > ?`, params: [node.value] };
71
+ case "$gte":
72
+ return { sql: `${escapedField} >= ?`, params: [node.value] };
73
+ case "$lt":
74
+ return { sql: `${escapedField} < ?`, params: [node.value] };
75
+ case "$lte":
76
+ return { sql: `${escapedField} <= ?`, params: [node.value] };
77
+ case "$between":
78
+ return { sql: `${escapedField} BETWEEN ? AND ?`, params: [node.value[0], node.value[1]] };
79
+ case "$notBetween":
80
+ return { sql: `${escapedField} NOT BETWEEN ? AND ?`, params: [node.value[0], node.value[1]] };
81
+ case "$null":
82
+ return { sql: `${escapedField} IS NULL`, params: [] };
83
+ case "$notNull":
84
+ return { sql: `${escapedField} IS NOT NULL`, params: [] };
85
+ default:
86
+ return { sql: `${escapedField} = ?`, params: [node.value] };
50
87
  }
51
-
52
- if (node.operator === "$in") {
53
- return {
54
- sql: `${escapedField} IN (${node.value.map(() => "?").join(",")})`,
55
- params: node.value.slice()
56
- };
57
- }
58
-
59
- if (node.operator === "$notIn") {
60
- return {
61
- sql: `${escapedField} NOT IN (${node.value.map(() => "?").join(",")})`,
62
- params: node.value.slice()
63
- };
64
- }
65
-
66
- if (node.operator === "$like") {
67
- return { sql: `${escapedField} LIKE ?`, params: [`%${String(node.value)}%`] };
68
- }
69
-
70
- if (node.operator === "$leftLike") {
71
- return { sql: `${escapedField} LIKE ?`, params: [`%${String(node.value)}`] };
72
- }
73
-
74
- if (node.operator === "$rightLike") {
75
- return { sql: `${escapedField} LIKE ?`, params: [`${String(node.value)}%`] };
76
- }
77
-
78
- if (node.operator === "$notLike") {
79
- return { sql: `${escapedField} NOT LIKE ?`, params: [node.value] };
80
- }
81
-
82
- if (node.operator === "$gt") {
83
- return { sql: `${escapedField} > ?`, params: [node.value] };
84
- }
85
-
86
- if (node.operator === "$gte") {
87
- return { sql: `${escapedField} >= ?`, params: [node.value] };
88
- }
89
-
90
- if (node.operator === "$lt") {
91
- return { sql: `${escapedField} < ?`, params: [node.value] };
92
- }
93
-
94
- if (node.operator === "$lte") {
95
- return { sql: `${escapedField} <= ?`, params: [node.value] };
96
- }
97
-
98
- if (node.operator === "$between") {
99
- return { sql: `${escapedField} BETWEEN ? AND ?`, params: [node.value[0], node.value[1]] };
100
- }
101
-
102
- if (node.operator === "$notBetween") {
103
- return { sql: `${escapedField} NOT BETWEEN ? AND ?`, params: [node.value[0], node.value[1]] };
104
- }
105
-
106
- if (node.operator === "$null") {
107
- return { sql: `${escapedField} IS NULL`, params: [] };
108
- }
109
-
110
- if (node.operator === "$notNull") {
111
- return { sql: `${escapedField} IS NOT NULL`, params: [] };
112
- }
113
-
114
- return { sql: `${escapedField} = ?`, params: [node.value] };
115
88
  }
116
89
 
117
90
  _compileWhereNode(node) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.22.9",
4
- "gitHead": "6ce876182a5b05c9d18168740c92959535205f10",
3
+ "version": "3.23.0",
4
+ "gitHead": "323ac9336929e2beaab435e9865194e22a884c82",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
7
7
  "keywords": [
@@ -18,7 +18,6 @@
18
18
  "homepage": "https://chensuiyi.me",
19
19
  "license": "Apache-2.0",
20
20
  "author": "chensuiyi <bimostyle@qq.com>",
21
- "main": "./index.js",
22
21
  "files": [
23
22
  "apis",
24
23
  "checks",
@@ -38,6 +37,7 @@
38
37
  "README.md"
39
38
  ],
40
39
  "type": "module",
40
+ "main": "./index.js",
41
41
  "exports": {
42
42
  ".": {
43
43
  "import": "./index.js",
@@ -52,8 +52,8 @@
52
52
  "test": "bun test"
53
53
  },
54
54
  "dependencies": {
55
- "fast-xml-parser": "^5.6.0",
56
- "nodemailer": "^8.0.5",
55
+ "fast-xml-parser": "^5.7.2",
56
+ "nodemailer": "^8.0.6",
57
57
  "pathe": "^2.0.3",
58
58
  "ua-parser-js": "^2.0.9",
59
59
  "zod": "^4.0.0"
@@ -7,7 +7,7 @@ import { toSyncDbFieldDef } from "./transform.js";
7
7
 
8
8
  const SYNC_DB_MANAGED_FIELD_NAMES = new Set(["id", "created_at", "updated_at", "deleted_at", "state"]);
9
9
  const SYNC_DB_HEAD_FIELD_NAME = "id";
10
- const SYNC_DB_TAIL_FIELD_NAMES = ["createdAt", "updatedAt", "deletedAt", "state"];
10
+ const SYNC_DB_TAIL_FIELD_NAMES = ["created_at", "updated_at", "deleted_at", "state"];
11
11
  const SYNC_DB_TAIL_FIELD_NAME_SET = new Set(SYNC_DB_TAIL_FIELD_NAMES);
12
12
 
13
13
  function shouldSkipSyncDbColumn(columnMeta, beflyMode) {
@@ -38,7 +38,7 @@ function sortSyncDbObjectKeys(source) {
38
38
  if (key === SYNC_DB_HEAD_FIELD_NAME) {
39
39
  continue;
40
40
  }
41
- if (SYNC_DB_TAIL_FIELD_NAME_SET.has(key)) {
41
+ if (SYNC_DB_TAIL_FIELD_NAME_SET.has(snakeCase(key))) {
42
42
  continue;
43
43
  }
44
44
 
@@ -46,11 +46,13 @@ function sortSyncDbObjectKeys(source) {
46
46
  }
47
47
 
48
48
  for (const fieldName of SYNC_DB_TAIL_FIELD_NAMES) {
49
- if (!Object.hasOwn(source, fieldName)) {
50
- continue;
51
- }
49
+ for (const [key, value] of Object.entries(source)) {
50
+ if (snakeCase(key) !== fieldName) {
51
+ continue;
52
+ }
52
53
 
53
- output[fieldName] = source[fieldName];
54
+ output[key] = value;
55
+ }
54
56
  }
55
57
 
56
58
  return output;
@@ -118,19 +120,32 @@ export function buildSyncDbDiff(groupedDbColumns, existingTableMap, beflyMode =
118
120
  }
119
121
 
120
122
  const existingFields = isPlainObject(existing.tableDef) ? existing.tableDef : {};
123
+ const existingFieldNameByDbColumnName = new Map();
124
+
125
+ for (const fieldName of Object.keys(existingFields)) {
126
+ if (shouldSkipSyncDbFieldName(fieldName, beflyMode)) {
127
+ continue;
128
+ }
129
+
130
+ existingFieldNameByDbColumnName.set(snakeCase(fieldName), fieldName);
131
+ }
132
+
121
133
  const missingFields = [];
122
134
  const dbFieldNameSet = new Set();
123
135
  for (const columnMeta of filteredColumns) {
124
136
  const fieldInfo = toSyncDbFieldDef(columnMeta);
125
- dbFieldNameSet.add(fieldInfo.fieldName);
126
- if (Object.hasOwn(existingFields, fieldInfo.fieldName)) {
137
+ const dbColumnName = String(columnMeta.columnName || "");
138
+ const normalizedDbColumnName = dbColumnName.toLowerCase();
139
+
140
+ dbFieldNameSet.add(normalizedDbColumnName);
141
+ if (existingFieldNameByDbColumnName.has(normalizedDbColumnName)) {
127
142
  continue;
128
143
  }
129
144
 
130
145
  missingFields.push({
131
146
  tableName: tableName,
132
147
  tableFileName: tableFileName,
133
- dbColumnName: columnMeta.columnName,
148
+ dbColumnName: dbColumnName,
134
149
  dbDataType: columnMeta.dataType,
135
150
  dbColumnType: columnMeta.columnType,
136
151
  dbCharLength: columnMeta.charLength,
@@ -154,7 +169,7 @@ export function buildSyncDbDiff(groupedDbColumns, existingTableMap, beflyMode =
154
169
  if (shouldSkipSyncDbFieldName(fieldName, beflyMode)) {
155
170
  continue;
156
171
  }
157
- if (dbFieldNameSet.has(fieldName)) {
172
+ if (dbFieldNameSet.has(snakeCase(fieldName))) {
158
173
  continue;
159
174
  }
160
175
 
@@ -1,6 +1,6 @@
1
1
  import { DECIMAL_KIND_TYPES, ENUM_KIND_TYPES, FLOAT_KIND_TYPES, INT_KIND_TYPES, JSON_KIND_TYPES, STRING_KIND_TYPES, TEXT_KIND_TYPES } from "../../configs/constConfig.js";
2
2
  import { isNonEmptyString, isNumber } from "../../utils/is.js";
3
- import { camelCase } from "../../utils/util.js";
3
+ import { camelCaseKeepUnderscore } from "../../utils/util.js";
4
4
 
5
5
  function resolveSyncDbEnumInput(columnType) {
6
6
  const matched = /^enum\((.*)\)$/.exec(columnType);
@@ -90,7 +90,7 @@ function resolveSyncDbMaxByColumn(columnMeta) {
90
90
  }
91
91
 
92
92
  export function toSyncDbFieldDef(columnMeta) {
93
- const fieldName = camelCase(String(columnMeta.columnName || ""));
93
+ const fieldName = camelCaseKeepUnderscore(columnMeta.columnName);
94
94
  const fieldDisplayName = isNonEmptyString(columnMeta.columnComment) ? String(columnMeta.columnComment).trim() : fieldName;
95
95
  const fieldInput = resolveSyncDbInputByColumn(columnMeta);
96
96
  const fieldMax = resolveSyncDbMaxByColumn(columnMeta);
package/sync/api.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { buildPathSyncLists } from "./syncUtil.js";
2
+
1
3
  const API_TABLE_NAME = "beflyApi";
2
4
 
3
5
  const getApiParentPath = (apiPath) => {
@@ -22,64 +24,45 @@ export async function syncApi(ctx, apis) {
22
24
  where: { state$gte: 0 }
23
25
  });
24
26
 
25
- // 给接口根据接口路径进行映射
26
- const dbLists = allDbApis.data.lists || [];
27
- const existingApiMap = new Map();
28
- for (const item of dbLists) {
29
- if (!existingApiMap.has(item.path)) {
30
- existingApiMap.set(item.path, item);
31
- }
32
- }
33
-
34
- const insList = [];
35
- const updList = [];
36
- const scannedPathSet = new Set();
27
+ const apiDefs = [];
37
28
  for (const api of apis) {
38
- scannedPathSet.add(api.apiPath);
39
-
40
- const auth = api.auth === false ? "否" : Array.isArray(api.auth) ? api.auth.join(",") : "是";
41
- const parentPath = getApiParentPath(api.apiPath);
42
- const existing = existingApiMap.get(api.apiPath);
43
-
44
- if (existing) {
45
- updList.push({
46
- id: existing.id,
47
- data: {
48
- name: api.name,
49
- path: api.apiPath,
50
- method: api.method,
51
- parentPath: parentPath,
52
- auth: auth
53
- }
54
- });
55
- continue;
56
- }
57
-
58
- insList.push({
29
+ apiDefs.push({
59
30
  name: api.name,
60
31
  path: api.apiPath,
61
32
  method: api.method,
62
- parentPath: parentPath,
63
- auth: auth
33
+ parentPath: getApiParentPath(api.apiPath),
34
+ auth: api.auth === false ? "否" : Array.isArray(api.auth) ? api.auth.join(",") : "是"
64
35
  });
65
36
  }
66
37
 
67
- const delIdList = [];
68
- for (const item of dbLists) {
69
- if (!scannedPathSet.has(item.path)) {
70
- delIdList.push(item.id);
71
- }
72
- }
38
+ const syncLists = buildPathSyncLists(
39
+ allDbApis.data.lists || [],
40
+ apiDefs,
41
+ (def) => ({
42
+ name: def.name,
43
+ path: def.path,
44
+ method: def.method,
45
+ parentPath: def.parentPath,
46
+ auth: def.auth
47
+ }),
48
+ (def) => ({
49
+ name: def.name,
50
+ path: def.path,
51
+ method: def.method,
52
+ parentPath: def.parentPath,
53
+ auth: def.auth
54
+ })
55
+ );
73
56
 
74
- if (updList.length > 0) {
75
- await ctx.mysql.updBatch(API_TABLE_NAME, updList);
57
+ if (syncLists.updList.length > 0) {
58
+ await ctx.mysql.updBatch(API_TABLE_NAME, syncLists.updList);
76
59
  }
77
60
 
78
- if (insList.length > 0) {
79
- await ctx.mysql.insBatch(API_TABLE_NAME, insList);
61
+ if (syncLists.insList.length > 0) {
62
+ await ctx.mysql.insBatch(API_TABLE_NAME, syncLists.insList);
80
63
  }
81
64
 
82
- if (delIdList.length > 0) {
83
- await ctx.mysql.delForceBatch(API_TABLE_NAME, delIdList);
65
+ if (syncLists.delIds.length > 0) {
66
+ await ctx.mysql.delForceBatch(API_TABLE_NAME, syncLists.delIds);
84
67
  }
85
68
  }