Files
biz_xfj_cloud/lib/common/services/database/database_migration.dart
T
2026-08-06 20:55:09 +08:00

310 lines
11 KiB
Dart

import 'package:floor/floor.dart';
/// 收银终端 SQLite 连续版本迁移清单,禁止跳过中间版本或在原生层重复执行结构 SQL。
abstract class DatabaseMigration {
/// 从版本1到版本2的迁移:添加productNamePy字段
static Migration migration1To2 = Migration(1, 2, (database) async {
// 先检查列是否存在,不存在才添加
final result = await database.rawQuery(
"PRAGMA table_info(supermarket_spu)",
);
bool columnExists = false;
for (var column in result) {
if (column['name'] == 'productNamePy') {
columnExists = true;
break;
}
}
if (!columnExists) {
await database
.execute('ALTER TABLE supermarket_spu ADD COLUMN productNamePy TEXT');
}
});
/// 从版本2到版本3的迁移:添加orderListJson字段
static Migration migration2To3 = Migration(2, 3, (database) async {
// 如果表不存在则创建,存在则校验列并补齐缺失项
final tables = await database.rawQuery(
"SELECT name FROM sqlite_master WHERE type='table' AND name='order_hold_bill'",
);
if (tables.isEmpty) {
await database.execute('''
CREATE TABLE IF NOT EXISTS order_hold_bill (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sysType TEXT NOT NULL,
carListJson TEXT,
userJson TEXT,
price INTEGER DEFAULT 0 NOT NULL,
extraPrice INTEGER DEFAULT 0 NOT NULL,
payPrice INTEGER DEFAULT 0 NOT NULL,
createTime TEXT NOT NULL
)
''');
await database.execute(
"CREATE INDEX IF NOT EXISTS idx_order_hold_bill_sysType ON order_hold_bill(sysType)",
);
await database.execute(
"CREATE INDEX IF NOT EXISTS idx_order_hold_bill_createTime ON order_hold_bill(createTime)",
);
} else {
final columns =
await database.rawQuery("PRAGMA table_info(order_hold_bill)");
bool hasSysType = false;
bool hasCarListJson = false;
bool hasOrderListJson = false;
bool hasUserJson = false;
bool hasPrice = false;
bool hasExtraPrice = false;
bool hasPayPrice = false;
bool hasCreateTime = false;
for (final c in columns) {
final name = c['name'];
if (name == 'sysType') hasSysType = true;
if (name == 'carListJson') hasCarListJson = true;
if (name == 'orderListJson') hasOrderListJson = true;
if (name == 'userJson') hasUserJson = true;
if (name == 'price') hasPrice = true;
if (name == 'extraPrice') hasExtraPrice = true;
if (name == 'payPrice') hasPayPrice = true;
if (name == 'createTime') hasCreateTime = true;
}
if (!hasSysType) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN sysType TEXT',
);
}
if (!hasCarListJson) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN carListJson TEXT',
);
if (hasOrderListJson) {
// 旧列迁移到新列
await database.execute(
"UPDATE order_hold_bill SET carListJson = orderListJson WHERE carListJson IS NULL",
);
}
}
if (!hasUserJson) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN userJson TEXT',
);
}
if (!hasPrice) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN price INTEGER DEFAULT 0 NOT NULL',
);
}
if (!hasExtraPrice) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN extraPrice INTEGER DEFAULT 0 NOT NULL',
);
}
if (!hasPayPrice) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN payPrice INTEGER DEFAULT 0 NOT NULL',
);
}
if (!hasCreateTime) {
await database.execute(
'ALTER TABLE order_hold_bill ADD COLUMN createTime TEXT DEFAULT "" NOT NULL',
);
}
await database.execute(
"CREATE INDEX IF NOT EXISTS idx_order_hold_bill_sysType ON order_hold_bill(sysType)",
);
await database.execute(
"CREATE INDEX IF NOT EXISTS idx_order_hold_bill_createTime ON order_hold_bill(createTime)",
);
}
});
/// 从版本3到版本4的迁移:添加为user_record表添加discountsList字段
static Migration migration3To4 = Migration(3, 4, (database) async {
final columns = await database.rawQuery("PRAGMA table_info(user_record)");
bool hasDiscountsList = false;
for (final c in columns) {
if (c['name'] == 'discountsList') {
hasDiscountsList = true;
break;
}
}
if (!hasDiscountsList) {
await database
.execute("ALTER TABLE user_record ADD COLUMN discountsList TEXT");
await database.execute(
"UPDATE user_record SET discountsList = '[]' WHERE discountsList IS NULL",
);
}
});
static Migration migration4To5 = Migration(4, 5, (database) async {
final columns = await database.rawQuery("PRAGMA table_info(user_record)");
bool hasDiscountsUpdateTime = false;
for (final c in columns) {
if (c['name'] == 'discountsUpdateTime') {
hasDiscountsUpdateTime = true;
break;
}
}
if (!hasDiscountsUpdateTime) {
await database.execute(
"ALTER TABLE user_record ADD COLUMN discountsUpdateTime TEXT");
}
});
static Migration migration5To6 = Migration(5, 6, (database) async {
final columns = await database.rawQuery("PRAGMA table_info(canteen_spu)");
bool hasIsPriceIncreased = false;
for (final c in columns) {
if (c['name'] == 'isPriceIncreased') {
hasIsPriceIncreased = true;
break;
}
}
if (!hasIsPriceIncreased) {
await database.execute(
"ALTER TABLE canteen_spu ADD COLUMN isPriceIncreased INTEGER NOT NULL DEFAULT 0",
);
}
});
static Migration migration6To7 = Migration(6, 7, (database) async {
final columns = await database.rawQuery("PRAGMA table_info(shop_card)");
bool hasIsPriceIncreased = false;
for (final c in columns) {
if (c['name'] == 'isPriceIncreased') {
hasIsPriceIncreased = true;
break;
}
}
if (!hasIsPriceIncreased) {
await database.execute(
"ALTER TABLE shop_card ADD COLUMN isPriceIncreased INTEGER NOT NULL DEFAULT 0",
);
}
});
/// 版本8将调度配置、执行日志和文件上传状态从 sysJson 收敛为可查询的结构化表。
static Migration migration7To8 = Migration(7, 8, (database) async {
await database.execute('''
CREATE TABLE IF NOT EXISTS scheduled_task (
taskCode TEXT NOT NULL PRIMARY KEY,
enabled INTEGER NOT NULL,
cronExpression TEXT NOT NULL,
idleOnly INTEGER NOT NULL,
lastRunAt TEXT,
lastSuccessAt TEXT,
lastStatus TEXT NOT NULL,
lastMessage TEXT NOT NULL,
createTime TEXT NOT NULL,
updateTime TEXT NOT NULL
)
''');
await database.execute('''
CREATE TABLE IF NOT EXISTS scheduled_task_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
taskCode TEXT NOT NULL,
taskName TEXT NOT NULL,
triggerType TEXT NOT NULL,
sourceType TEXT NOT NULL,
sourceName TEXT NOT NULL,
status TEXT NOT NULL,
message TEXT NOT NULL,
errorType TEXT,
errorMessage TEXT,
errorStack TEXT,
startedAt TEXT NOT NULL,
finishedAt TEXT,
durationMs INTEGER NOT NULL,
scannedCount INTEGER,
skippedCount INTEGER,
successCount INTEGER,
failureCount INTEGER
)
''');
await database.execute(
'CREATE INDEX IF NOT EXISTS index_scheduled_task_log_taskCode_startedAt '
'ON scheduled_task_log(taskCode, startedAt)',
);
await database.execute(
'CREATE INDEX IF NOT EXISTS index_scheduled_task_log_sourceType_status_startedAt '
'ON scheduled_task_log(sourceType, status, startedAt)',
);
await database.execute('''
CREATE TABLE IF NOT EXISTS file_upload_record (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hardId TEXT NOT NULL,
bizType TEXT NOT NULL,
relativePath TEXT NOT NULL,
originalName TEXT NOT NULL,
sourceSize INTEGER NOT NULL,
sourceModifiedTime TEXT NOT NULL,
sanitizedHash TEXT NOT NULL,
sanitizedSize INTEGER NOT NULL,
status TEXT NOT NULL,
serverFileId TEXT,
attemptCount INTEGER NOT NULL,
lastAttemptAt TEXT,
uploadedAt TEXT,
lastError TEXT,
createTime TEXT NOT NULL,
updateTime TEXT NOT NULL
)
''');
await database.execute(
'CREATE UNIQUE INDEX IF NOT EXISTS index_file_upload_record_hardId_bizType_relativePath '
'ON file_upload_record(hardId, bizType, relativePath)',
);
await database.execute(
'CREATE INDEX IF NOT EXISTS index_file_upload_record_bizType_status_updateTime '
'ON file_upload_record(bizType, status, updateTime)',
);
});
/// 版本9增加商品类型并将套餐组成拆分为可查询的一对多明细表。
static Migration migration8To9 = Migration(8, 9, (database) async {
final columns = await database.rawQuery("PRAGMA table_info(canteen_spu)");
final hasSpuType = columns.any((column) => column['name'] == 'spuType');
if (!hasSpuType) {
await database.execute(
'ALTER TABLE canteen_spu ADD COLUMN spuType INTEGER NOT NULL DEFAULT 1',
);
}
await database.execute('''
CREATE TABLE IF NOT EXISTS canteen_spu_combo_item (
id INTEGER PRIMARY KEY AUTOINCREMENT,
parentServiceUniqueCode TEXT NOT NULL,
saleDate TEXT NOT NULL,
childServiceUniqueCode TEXT NOT NULL,
name TEXT NOT NULL,
image TEXT,
quantity INTEGER NOT NULL,
sort INTEGER NOT NULL,
price INTEGER NOT NULL
)
''');
await database.execute(
'CREATE INDEX IF NOT EXISTS index_canteen_spu_combo_item_parentServiceUniqueCode_saleDate '
'ON canteen_spu_combo_item(parentServiceUniqueCode, saleDate)',
);
await database.execute(
'CREATE UNIQUE INDEX IF NOT EXISTS index_canteen_spu_combo_item_parentServiceUniqueCode_saleDate_childServiceUniqueCode '
'ON canteen_spu_combo_item(parentServiceUniqueCode, saleDate, childServiceUniqueCode)',
);
});
/// 按起始版本升序返回全部迁移,Floor 打开旧数据库时据此连续升级到当前版本。
static List<Migration> getDatabaseMigrations() {
return [
migration1To2,
migration2To3,
migration3To4,
migration4To5,
migration5To6,
migration6To7,
migration7To8,
migration8To9,
];
}
}