feat:额外收费

This commit is contained in:
dzc committed 2026-03-26 11:04:13 +08:00
1 parent 2b956342f4
commit e730b6923c
17 files changed
+461 -153

No files matched your search

+8 -123
View File
@@ -1,7 +1,9 @@
import 'dart:async';
import 'dart:io';
import 'package:floor/floor.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
import 'package:pj_supermarket_cloud/common/services/database/databaseMigration.dart';
import 'package:sqflite/sqflite.dart' as sqflite;
@@ -32,11 +34,13 @@ const typeConverters = <Type>[
SysTypeConverter,
PrintStatusConverter,
SupermarketNatureConverter,
DiscountsTypeConverter,
ListDiscountsConverter,
ListIntConverter,
BoolConverter,
];
@Database(version: 3, entities: entities)
@Database(version: 5, entities: entities)
@TypeConverters(typeConverters)
abstract class AppDatabase extends FloorDatabase {
BroadcastDao get getBroadcastDao;
@@ -53,130 +57,11 @@ abstract class AppDatabase extends FloorDatabase {
FaceDao get getFaceDao;
SettingDao get getSettingDao;
// 获取数据库迁移列表
static List<Migration> _getDatabaseMigrations() {
return [
// 从版本1到版本2的迁移:添加productNamePy字段
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');
}
}),
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)",
);
}
}),
];
}
static Future<AppDatabase> getDataBase() async {
final dbPath = await AppFileTool.getDatabasePath();
final name = "$dbPath/${Constants.databaseName}";
final dataBase = await $FloorAppDatabase
.databaseBuilder("$dbPath/${Constants.databaseName}")
.databaseBuilder(name)
.addCallback(
Callback(
onCreate: (database, version) async {
@@ -190,7 +75,7 @@ abstract class AppDatabase extends FloorDatabase {
},
),
)
.addMigrations(_getDatabaseMigrations())
.addMigrations(DatabaseMigration.getDatabaseMigrations())
.build();
return dataBase;
}
+16 -2
View File
@@ -104,7 +104,7 @@ class _$AppDatabase extends AppDatabase {
Callback? callback,
]) async {
final databaseOptions = sqflite.OpenDatabaseOptions(
version: 3,
version: 5,
onConfigure: (database) async {
await database.execute('PRAGMA foreign_keys = ON');
await callback?.onConfigure?.call(database);
@@ -140,7 +140,7 @@ class _$AppDatabase extends AppDatabase {
await database.execute(
'CREATE TABLE IF NOT EXISTS `print_history` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `orderId` TEXT NOT NULL, `serviceOrderId` TEXT NOT NULL, `servicePrintId` TEXT NOT NULL, `invoiceUrl` TEXT NOT NULL, `status` TEXT NOT NULL, `printTime` TEXT, `syncTime` TEXT, `createTime` TEXT NOT NULL)');
await database.execute(
'CREATE TABLE IF NOT EXISTS `user_record` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `userId` TEXT NOT NULL, `nickname` TEXT NOT NULL, `mobile` TEXT, `avatar` TEXT, `walletCardNumber` TEXT, `walletId` TEXT, `walletTypeId` TEXT, `walletBalance` INTEGER, `canteenId` TEXT, `createTime` TEXT, `updateTime` TEXT)');
'CREATE TABLE IF NOT EXISTS `user_record` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `userId` TEXT NOT NULL, `nickname` TEXT NOT NULL, `mobile` TEXT, `avatar` TEXT, `walletCardNumber` TEXT, `walletId` TEXT, `walletTypeId` TEXT, `walletBalance` INTEGER, `canteenId` TEXT, `discountsList` TEXT, `discountsUpdateTime` TEXT, `createTime` TEXT, `updateTime` TEXT)');
await database.execute(
'CREATE TABLE IF NOT EXISTS `face` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `userId` TEXT NOT NULL, `sampleId` TEXT NOT NULL, `userName` TEXT NOT NULL, `deptId` TEXT, `deptName` TEXT, `imagePath` TEXT NOT NULL, `path` TEXT, `faceModel` TEXT, `createTime` TEXT, `updateTime` TEXT)');
await database.execute(
@@ -1504,6 +1504,10 @@ class _$UserDao extends UserDao {
'walletTypeId': item.walletTypeId,
'walletBalance': item.walletBalance,
'canteenId': item.canteenId,
'discountsList':
_listDiscountsConverter.encode(item.discountsList),
'discountsUpdateTime':
_dateTimeEmptyConverter.encode(item.discountsUpdateTime),
'createTime': _dateTimeEmptyConverter.encode(item.createTime),
'updateTime': _dateTimeEmptyConverter.encode(item.updateTime)
}),
@@ -1522,6 +1526,10 @@ class _$UserDao extends UserDao {
'walletTypeId': item.walletTypeId,
'walletBalance': item.walletBalance,
'canteenId': item.canteenId,
'discountsList':
_listDiscountsConverter.encode(item.discountsList),
'discountsUpdateTime':
_dateTimeEmptyConverter.encode(item.discountsUpdateTime),
'createTime': _dateTimeEmptyConverter.encode(item.createTime),
'updateTime': _dateTimeEmptyConverter.encode(item.updateTime)
}),
@@ -1540,6 +1548,10 @@ class _$UserDao extends UserDao {
'walletTypeId': item.walletTypeId,
'walletBalance': item.walletBalance,
'canteenId': item.canteenId,
'discountsList':
_listDiscountsConverter.encode(item.discountsList),
'discountsUpdateTime':
_dateTimeEmptyConverter.encode(item.discountsUpdateTime),
'createTime': _dateTimeEmptyConverter.encode(item.createTime),
'updateTime': _dateTimeEmptyConverter.encode(item.updateTime)
});
@@ -1946,5 +1958,7 @@ final _orderPayTypeConverter = OrderPayTypeConverter();
final _sysTypeConverter = SysTypeConverter();
final _printStatusConverter = PrintStatusConverter();
final _supermarketNatureConverter = SupermarketNatureConverter();
final _discountsTypeConverter = DiscountsTypeConverter();
final _listDiscountsConverter = ListDiscountsConverter();
final _listIntConverter = ListIntConverter();
final _boolConverter = BoolConverter();
@@ -0,0 +1,157 @@
import 'package:floor/floor.dart';
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 List<Migration> getDatabaseMigrations() {
return [migration1To2, migration2To3, migration3To4];
}
}
@@ -1,9 +1,62 @@
import 'package:common_utils/common_utils.dart';
import 'package:equatable/equatable.dart';
import 'package:floor/floor.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
import 'package:pj_supermarket_cloud/common/models/index.dart';
part 'user_entity.g.dart';
// 优惠
@JsonSerializable()
class DiscountsItem {
/// 优惠活动名称
String name;
/// 用餐时间段类型
@JsonKey(
fromJson: JsonTool.stringToProductHourType,
toJson: JsonTool.productHourTypeToString,
)
ProductHourType? hourType;
/// 优惠类型
@JsonKey(
fromJson: JsonTool.stringToDiscountsType,
toJson: JsonTool.discountsTypeToString,
)
DiscountsType? type;
/// 基础金额(分),超过了这个基数`extraPer`就生效
int basePrice;
/// 打折(8折->0.8)
double? extraPer;
DiscountsItem({
required this.name,
required this.hourType,
required this.type,
required this.basePrice,
required this.extraPer,
});
/// 计算优惠后的价格
/// [totalPrice] 总金额(分)
/// [return] 优惠的价格(分)
int computedDiscountsExtraPrice(int totalPrice) {
if (totalPrice <= basePrice) return 0;
final rate = extraPer ?? 1.0;
if (rate == 1.0) return 0;
return (totalPrice * (rate - 1)).round();
}
factory DiscountsItem.fromJson(Map<String, dynamic> json) =>
_$DiscountsItemFromJson(json);
Map<String, dynamic> toJson() => _$DiscountsItemToJson(this);
}
@JsonSerializable()
@Entity(tableName: "user_record")
class UserEntity extends Equatable {
@@ -38,6 +91,12 @@ class UserEntity extends Equatable {
/// 用户所属的商户id(主要用于机器的离线模式)
String? canteenId;
/// 优惠活动
List<DiscountsItem>? discountsList;
/// 优惠活动更新时间
DateTime? discountsUpdateTime;
/// 创建时间
DateTime? createTime;
@@ -59,6 +118,28 @@ class UserEntity extends Equatable {
this.updateTime,
});
/// 计算优惠后的价格
/// [totalPrice] 总金额(分)
/// [return] 优惠的价格(分)
int computedDiscountsPrice(int totalPrice) {
if (ObjectUtil.isEmpty(discountsList)) {
return 0;
}
var discountsPrice = 0;
for (final item in discountsList!) {
discountsPrice += item.computedDiscountsExtraPrice(totalPrice);
}
return discountsPrice;
}
/// 根据`discountsUpdateTime`来计算,最小更新时间30s
bool isUpdateDiscounts() {
if (ObjectUtil.isEmpty(discountsUpdateTime)) {
return true;
}
return DateTime.now().difference(discountsUpdateTime!).inSeconds > 30;
}
factory UserEntity.fromJson(Map<String, dynamic> json) =>
_$UserEntityFromJson(json);
@@ -6,6 +6,24 @@ part of 'user_entity.dart';
// JsonSerializableGenerator
// **************************************************************************
DiscountsItem _$DiscountsItemFromJson(Map<String, dynamic> json) =>
DiscountsItem(
name: json['name'] as String,
hourType: JsonTool.stringToProductHourType(json['hourType'] as String?),
type: JsonTool.stringToDiscountsType(json['type'] as String?),
basePrice: (json['basePrice'] as num).toInt(),
extraPer: (json['extraPer'] as num?)?.toDouble(),
);
Map<String, dynamic> _$DiscountsItemToJson(DiscountsItem instance) =>
<String, dynamic>{
'name': instance.name,
'hourType': JsonTool.productHourTypeToString(instance.hourType),
'type': JsonTool.discountsTypeToString(instance.type),
'basePrice': instance.basePrice,
'extraPer': instance.extraPer,
};
UserEntity _$UserEntityFromJson(Map<String, dynamic> json) => UserEntity(
id: (json['id'] as num?)?.toInt(),
userId: json['userId'] as String,
@@ -23,7 +41,13 @@ UserEntity _$UserEntityFromJson(Map<String, dynamic> json) => UserEntity(
updateTime: json['updateTime'] == null
? null
: DateTime.parse(json['updateTime'] as String),
);
)
..discountsList = (json['discountsList'] as List<dynamic>?)
?.map((e) => DiscountsItem.fromJson(e as Map<String, dynamic>))
.toList()
..discountsUpdateTime = json['discountsUpdateTime'] == null
? null
: DateTime.parse(json['discountsUpdateTime'] as String);
Map<String, dynamic> _$UserEntityToJson(UserEntity instance) =>
<String, dynamic>{
@@ -37,6 +61,8 @@ Map<String, dynamic> _$UserEntityToJson(UserEntity instance) =>
'walletTypeId': instance.walletTypeId,
'walletBalance': instance.walletBalance,
'canteenId': instance.canteenId,
'discountsList': instance.discountsList,
'discountsUpdateTime': instance.discountsUpdateTime?.toIso8601String(),
'createTime': instance.createTime?.toIso8601String(),
'updateTime': instance.updateTime?.toIso8601String(),
};
@@ -119,6 +119,18 @@ class SupermarketNatureConverter extends TypeConverter<ProductNature, String> {
}
}
class DiscountsTypeConverter extends TypeConverter<DiscountsType, String> {
@override
DiscountsType decode(String databaseValue) {
return DiscountsType.values.byName(databaseValue);
}
@override
String encode(DiscountsType value) {
return value.name;
}
}
class ListIntConverter extends TypeConverter<List<dynamic>?, String> {
@override
List<dynamic> decode(String databaseValue) {
@@ -137,6 +149,28 @@ class ListIntConverter extends TypeConverter<List<dynamic>?, String> {
}
}
class ListDiscountsConverter
extends TypeConverter<List<DiscountsItem>?, String> {
@override
List<DiscountsItem> decode(String databaseValue) {
if (databaseValue.isEmpty) {
return [];
}
return json
.decode(databaseValue)
.map((e) => DiscountsItem.fromJson(e))
.toList();
}
@override
String encode(List<DiscountsItem>? value) {
if (ObjectUtil.isEmptyList(value)) {
return '';
}
return json.encode(value);
}
}
class BoolConverter extends TypeConverter<bool, int> {
@override
bool decode(int databaseValue) {