befly 3.9.0 → 3.9.2
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/package.json +2 -2
- package/sync/syncDb/ddl.ts +27 -4
- package/sync/syncDb/table.ts +21 -5
- package/sync/syncDb/types.ts +4 -4
- package/tests/syncDb-types.test.ts +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.2",
|
|
4
4
|
"description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"pino": "^10.1.0",
|
|
74
74
|
"pino-roll": "^4.0.0"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "a311a415ccf02fb5089040883049a02a54f5ec60",
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"typescript": "^5.9.3"
|
|
79
79
|
}
|
package/sync/syncDb/ddl.ts
CHANGED
|
@@ -60,16 +60,39 @@ export function buildIndexSQL(tableName: string, indexName: string, fieldName: s
|
|
|
60
60
|
return `DROP INDEX IF EXISTS ${indexQuoted}`;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 获取单个系统字段的列定义(用于 ADD COLUMN 或 CREATE TABLE)
|
|
65
|
+
*
|
|
66
|
+
* @param fieldName - 系统字段名(id, created_at, updated_at, deleted_at, state)
|
|
67
|
+
* @returns 列定义字符串,如果不是系统字段则返回 null
|
|
68
|
+
*/
|
|
69
|
+
export function getSystemColumnDef(fieldName: string): string | null {
|
|
70
|
+
const mysqlDefs: Record<string, string> = {
|
|
71
|
+
id: '`id` BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT "主键ID"',
|
|
72
|
+
created_at: '`created_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "创建时间"',
|
|
73
|
+
updated_at: '`updated_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "更新时间"',
|
|
74
|
+
deleted_at: '`deleted_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "删除时间"',
|
|
75
|
+
state: '`state` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "状态字段"'
|
|
76
|
+
};
|
|
77
|
+
const pgDefs: Record<string, string> = {
|
|
78
|
+
id: '"id" INTEGER PRIMARY KEY',
|
|
79
|
+
created_at: '"created_at" INTEGER NOT NULL DEFAULT 0',
|
|
80
|
+
updated_at: '"updated_at" INTEGER NOT NULL DEFAULT 0',
|
|
81
|
+
deleted_at: '"deleted_at" INTEGER NOT NULL DEFAULT 0',
|
|
82
|
+
state: '"state" INTEGER NOT NULL DEFAULT 0'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const defs = isMySQL() ? mysqlDefs : pgDefs;
|
|
86
|
+
return defs[fieldName] || null;
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
/**
|
|
64
90
|
* 构建系统字段列定义
|
|
65
91
|
*
|
|
66
92
|
* @returns 系统字段的列定义数组
|
|
67
93
|
*/
|
|
68
94
|
export function buildSystemColumnDefs(): string[] {
|
|
69
|
-
|
|
70
|
-
return ['`id` BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT "主键ID"', '`created_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "创建时间"', '`updated_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "更新时间"', '`deleted_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "删除时间"', '`state` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "状态字段"'];
|
|
71
|
-
}
|
|
72
|
-
return ['"id" INTEGER PRIMARY KEY', '"created_at" INTEGER NOT NULL DEFAULT 0', '"updated_at" INTEGER NOT NULL DEFAULT 0', '"deleted_at" INTEGER NOT NULL DEFAULT 0', '"state" INTEGER NOT NULL DEFAULT 0'];
|
|
95
|
+
return [getSystemColumnDef('id')!, getSystemColumnDef('created_at')!, getSystemColumnDef('updated_at')!, getSystemColumnDef('deleted_at')!, getSystemColumnDef('state')!];
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
/**
|
package/sync/syncDb/table.ts
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
import { snakeCase } from 'es-toolkit/string';
|
|
11
11
|
import { Logger } from '../../lib/logger.js';
|
|
12
|
-
import { isMySQL, isPG, CHANGE_TYPE_LABELS, getTypeMapping } from './constants.js';
|
|
12
|
+
import { isMySQL, isPG, CHANGE_TYPE_LABELS, getTypeMapping, SYSTEM_INDEX_FIELDS } from './constants.js';
|
|
13
13
|
import { logFieldChange, resolveDefaultValue, generateDefaultSql, isStringOrArrayType } from './helpers.js';
|
|
14
|
-
import { generateDDLClause, isPgCompatibleTypeChange } from './ddl.js';
|
|
14
|
+
import { generateDDLClause, isPgCompatibleTypeChange, getSystemColumnDef } from './ddl.js';
|
|
15
15
|
import { getTableColumns, getTableIndexes } from './schema.js';
|
|
16
16
|
import { compareFieldDefinition, applyTablePlan } from './apply.js';
|
|
17
17
|
import type { TablePlan } from '../../types/sync.js';
|
|
@@ -130,10 +130,26 @@ export async function modifyTable(sql: SQL, tableName: string, fields: Record<st
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
//
|
|
134
|
-
|
|
133
|
+
// 检查并添加缺失的系统字段(created_at, updated_at, deleted_at, state)
|
|
134
|
+
// 注意:id 是主键,不会缺失;这里只处理可能缺失的其他系统字段
|
|
135
|
+
const systemFieldNames = ['created_at', 'updated_at', 'deleted_at', 'state'];
|
|
136
|
+
for (const sysFieldName of systemFieldNames) {
|
|
137
|
+
if (!existingColumns[sysFieldName]) {
|
|
138
|
+
const colDef = getSystemColumnDef(sysFieldName);
|
|
139
|
+
if (colDef) {
|
|
140
|
+
Logger.debug(` + 新增系统字段 ${sysFieldName}`);
|
|
141
|
+
addClauses.push(`ADD COLUMN ${colDef}`);
|
|
142
|
+
changed = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 检查系统字段索引(字段存在或即将被添加时才创建索引)
|
|
148
|
+
for (const sysField of SYSTEM_INDEX_FIELDS) {
|
|
135
149
|
const idxName = `idx_${sysField}`;
|
|
136
|
-
|
|
150
|
+
// 字段已存在或刚添加到 addClauses 中
|
|
151
|
+
const fieldWillExist = existingColumns[sysField] || systemFieldNames.includes(sysField);
|
|
152
|
+
if (fieldWillExist && !existingIndexes[idxName]) {
|
|
137
153
|
indexActions.push({ action: 'create', indexName: idxName, fieldName: sysField });
|
|
138
154
|
changed = true;
|
|
139
155
|
}
|
package/sync/syncDb/types.ts
CHANGED
|
@@ -65,8 +65,9 @@ export function getSqlType(fieldType: string, fieldMax: number | null, unsigned:
|
|
|
65
65
|
* resolveDefaultValue(null, 'string') // => ''
|
|
66
66
|
* resolveDefaultValue(null, 'number') // => 0
|
|
67
67
|
* resolveDefaultValue('null', 'number') // => 0
|
|
68
|
-
* resolveDefaultValue(null, '
|
|
68
|
+
* resolveDefaultValue(null, 'array_string') // => '[]'
|
|
69
69
|
* resolveDefaultValue(null, 'text') // => 'null'
|
|
70
|
+
* resolveDefaultValue(null, 'array_text') // => 'null' (TEXT 不支持默认值)
|
|
70
71
|
* resolveDefaultValue('admin', 'string') // => 'admin'
|
|
71
72
|
* resolveDefaultValue(0, 'number') // => 0
|
|
72
73
|
*/
|
|
@@ -82,12 +83,11 @@ export function resolveDefaultValue(fieldDefault: any, fieldType: string): any {
|
|
|
82
83
|
return 0;
|
|
83
84
|
case 'string':
|
|
84
85
|
return '';
|
|
85
|
-
case 'array':
|
|
86
86
|
case 'array_string':
|
|
87
|
-
case 'array_text':
|
|
88
87
|
return '[]';
|
|
89
88
|
case 'text':
|
|
90
|
-
|
|
89
|
+
case 'array_text':
|
|
90
|
+
// text/array_text 类型不设置默认值(MySQL TEXT 不支持),保持 'null'
|
|
91
91
|
return 'null';
|
|
92
92
|
default:
|
|
93
93
|
return fieldDefault;
|
|
@@ -63,14 +63,18 @@ describe('resolveDefaultValue', () => {
|
|
|
63
63
|
expect(resolveDefaultValue('null', 'number')).toBe(0);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
test('null 值 +
|
|
67
|
-
expect(resolveDefaultValue(null, '
|
|
66
|
+
test('null 值 + array_string 类型 => "[]"', () => {
|
|
67
|
+
expect(resolveDefaultValue(null, 'array_string')).toBe('[]');
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
test('null 值 + text 类型 => "null"', () => {
|
|
71
71
|
expect(resolveDefaultValue(null, 'text')).toBe('null');
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
test('null 值 + array_text 类型 => "null"(TEXT 不支持默认值)', () => {
|
|
75
|
+
expect(resolveDefaultValue(null, 'array_text')).toBe('null');
|
|
76
|
+
});
|
|
77
|
+
|
|
74
78
|
test('有实际值时直接返回', () => {
|
|
75
79
|
expect(resolveDefaultValue('admin', 'string')).toBe('admin');
|
|
76
80
|
expect(resolveDefaultValue(100, 'number')).toBe(100);
|