update database

This commit is contained in:
Phuoc Nguyen
2025-10-24 11:31:48 +07:00
parent f95fa9d0a6
commit c4272f9a21
126 changed files with 23528 additions and 2234 deletions

View File

@@ -0,0 +1,300 @@
import 'dart:convert';
import 'package:hive_ce/hive.dart';
import 'package:worker/core/constants/storage_constants.dart';
import 'package:worker/core/database/models/enums.dart';
part 'user_model.g.dart';
/// User Model
///
/// Hive CE model for caching user data locally.
/// Maps to the 'users' table in the database.
///
/// Type ID: 0
@HiveType(typeId: HiveTypeIds.userModel)
class UserModel extends HiveObject {
UserModel({
required this.userId,
required this.phoneNumber,
this.passwordHash,
required this.fullName,
this.email,
required this.role,
required this.status,
required this.loyaltyTier,
required this.totalPoints,
this.companyInfo,
this.cccd,
this.attachments,
this.address,
this.avatarUrl,
this.referralCode,
this.referredBy,
this.erpnextCustomerId,
required this.createdAt,
this.updatedAt,
this.lastLoginAt,
});
/// User ID (Primary Key)
@HiveField(0)
final String userId;
/// Phone number (unique, used for login)
@HiveField(1)
final String phoneNumber;
/// Password hash (stored encrypted)
@HiveField(2)
final String? passwordHash;
/// Full name of the user
@HiveField(3)
final String fullName;
/// Email address
@HiveField(4)
final String? email;
/// User role (customer, distributor, admin, staff)
@HiveField(5)
final UserRole role;
/// Account status (active, inactive, suspended, pending)
@HiveField(6)
final UserStatus status;
/// Loyalty tier (bronze, silver, gold, platinum, diamond, titan)
@HiveField(7)
final LoyaltyTier loyaltyTier;
/// Total accumulated loyalty points
@HiveField(8)
final int totalPoints;
/// Company information (JSON encoded)
/// Contains: company_name, tax_id, business_type, etc.
@HiveField(9)
final String? companyInfo;
/// Citizen ID (CCCD/CMND)
@HiveField(10)
final String? cccd;
/// Attachments (JSON encoded list)
/// Contains: identity_card_images, business_license, etc.
@HiveField(11)
final String? attachments;
/// Address
@HiveField(12)
final String? address;
/// Avatar URL
@HiveField(13)
final String? avatarUrl;
/// Referral code for this user
@HiveField(14)
final String? referralCode;
/// ID of user who referred this user
@HiveField(15)
final String? referredBy;
/// ERPNext customer ID for integration
@HiveField(16)
final String? erpnextCustomerId;
/// Account creation timestamp
@HiveField(17)
final DateTime createdAt;
/// Last update timestamp
@HiveField(18)
final DateTime? updatedAt;
/// Last login timestamp
@HiveField(19)
final DateTime? lastLoginAt;
// =========================================================================
// JSON SERIALIZATION
// =========================================================================
/// Create UserModel from JSON
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
userId: json['user_id'] as String,
phoneNumber: json['phone_number'] as String,
passwordHash: json['password_hash'] as String?,
fullName: json['full_name'] as String,
email: json['email'] as String?,
role: UserRole.values.firstWhere(
(e) => e.name == (json['role'] as String),
orElse: () => UserRole.customer,
),
status: UserStatus.values.firstWhere(
(e) => e.name == (json['status'] as String),
orElse: () => UserStatus.pending,
),
loyaltyTier: LoyaltyTier.values.firstWhere(
(e) => e.name == (json['loyalty_tier'] as String),
orElse: () => LoyaltyTier.bronze,
),
totalPoints: json['total_points'] as int? ?? 0,
companyInfo: json['company_info'] != null
? jsonEncode(json['company_info'])
: null,
cccd: json['cccd'] as String?,
attachments: json['attachments'] != null
? jsonEncode(json['attachments'])
: null,
address: json['address'] as String?,
avatarUrl: json['avatar_url'] as String?,
referralCode: json['referral_code'] as String?,
referredBy: json['referred_by'] as String?,
erpnextCustomerId: json['erpnext_customer_id'] as String?,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'] as String)
: null,
lastLoginAt: json['last_login_at'] != null
? DateTime.parse(json['last_login_at'] as String)
: null,
);
}
/// Convert UserModel to JSON
Map<String, dynamic> toJson() {
return {
'user_id': userId,
'phone_number': phoneNumber,
'password_hash': passwordHash,
'full_name': fullName,
'email': email,
'role': role.name,
'status': status.name,
'loyalty_tier': loyaltyTier.name,
'total_points': totalPoints,
'company_info': companyInfo != null ? jsonDecode(companyInfo!) : null,
'cccd': cccd,
'attachments': attachments != null ? jsonDecode(attachments!) : null,
'address': address,
'avatar_url': avatarUrl,
'referral_code': referralCode,
'referred_by': referredBy,
'erpnext_customer_id': erpnextCustomerId,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'last_login_at': lastLoginAt?.toIso8601String(),
};
}
// =========================================================================
// HELPER METHODS
// =========================================================================
/// Get company info as Map
Map<String, dynamic>? get companyInfoMap {
if (companyInfo == null) return null;
try {
return jsonDecode(companyInfo!) as Map<String, dynamic>;
} catch (e) {
return null;
}
}
/// Get attachments as List
List<dynamic>? get attachmentsList {
if (attachments == null) return null;
try {
return jsonDecode(attachments!) as List<dynamic>;
} catch (e) {
return null;
}
}
/// Check if user is active
bool get isActive => status == UserStatus.active;
/// Check if user is admin or staff
bool get isStaff => role == UserRole.admin || role == UserRole.staff;
/// Get user initials for avatar
String get initials {
final parts = fullName.trim().split(' ');
if (parts.length >= 2) {
return '${parts.first[0]}${parts.last[0]}'.toUpperCase();
}
return fullName.isNotEmpty ? fullName[0].toUpperCase() : '?';
}
// =========================================================================
// COPY WITH
// =========================================================================
/// Create a copy with updated fields
UserModel copyWith({
String? userId,
String? phoneNumber,
String? passwordHash,
String? fullName,
String? email,
UserRole? role,
UserStatus? status,
LoyaltyTier? loyaltyTier,
int? totalPoints,
String? companyInfo,
String? cccd,
String? attachments,
String? address,
String? avatarUrl,
String? referralCode,
String? referredBy,
String? erpnextCustomerId,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastLoginAt,
}) {
return UserModel(
userId: userId ?? this.userId,
phoneNumber: phoneNumber ?? this.phoneNumber,
passwordHash: passwordHash ?? this.passwordHash,
fullName: fullName ?? this.fullName,
email: email ?? this.email,
role: role ?? this.role,
status: status ?? this.status,
loyaltyTier: loyaltyTier ?? this.loyaltyTier,
totalPoints: totalPoints ?? this.totalPoints,
companyInfo: companyInfo ?? this.companyInfo,
cccd: cccd ?? this.cccd,
attachments: attachments ?? this.attachments,
address: address ?? this.address,
avatarUrl: avatarUrl ?? this.avatarUrl,
referralCode: referralCode ?? this.referralCode,
referredBy: referredBy ?? this.referredBy,
erpnextCustomerId: erpnextCustomerId ?? this.erpnextCustomerId,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
lastLoginAt: lastLoginAt ?? this.lastLoginAt,
);
}
@override
String toString() {
return 'UserModel(userId: $userId, fullName: $fullName, role: $role, tier: $loyaltyTier, points: $totalPoints)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is UserModel && other.userId == userId;
}
@override
int get hashCode => userId.hashCode;
}

View File

@@ -0,0 +1,98 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class UserModelAdapter extends TypeAdapter<UserModel> {
@override
final typeId = 0;
@override
UserModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return UserModel(
userId: fields[0] as String,
phoneNumber: fields[1] as String,
passwordHash: fields[2] as String?,
fullName: fields[3] as String,
email: fields[4] as String?,
role: fields[5] as UserRole,
status: fields[6] as UserStatus,
loyaltyTier: fields[7] as LoyaltyTier,
totalPoints: (fields[8] as num).toInt(),
companyInfo: fields[9] as String?,
cccd: fields[10] as String?,
attachments: fields[11] as String?,
address: fields[12] as String?,
avatarUrl: fields[13] as String?,
referralCode: fields[14] as String?,
referredBy: fields[15] as String?,
erpnextCustomerId: fields[16] as String?,
createdAt: fields[17] as DateTime,
updatedAt: fields[18] as DateTime?,
lastLoginAt: fields[19] as DateTime?,
);
}
@override
void write(BinaryWriter writer, UserModel obj) {
writer
..writeByte(20)
..writeByte(0)
..write(obj.userId)
..writeByte(1)
..write(obj.phoneNumber)
..writeByte(2)
..write(obj.passwordHash)
..writeByte(3)
..write(obj.fullName)
..writeByte(4)
..write(obj.email)
..writeByte(5)
..write(obj.role)
..writeByte(6)
..write(obj.status)
..writeByte(7)
..write(obj.loyaltyTier)
..writeByte(8)
..write(obj.totalPoints)
..writeByte(9)
..write(obj.companyInfo)
..writeByte(10)
..write(obj.cccd)
..writeByte(11)
..write(obj.attachments)
..writeByte(12)
..write(obj.address)
..writeByte(13)
..write(obj.avatarUrl)
..writeByte(14)
..write(obj.referralCode)
..writeByte(15)
..write(obj.referredBy)
..writeByte(16)
..write(obj.erpnextCustomerId)
..writeByte(17)
..write(obj.createdAt)
..writeByte(18)
..write(obj.updatedAt)
..writeByte(19)
..write(obj.lastLoginAt);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,185 @@
import 'package:hive_ce/hive.dart';
import 'package:worker/core/constants/storage_constants.dart';
part 'user_session_model.g.dart';
/// User Session Model
///
/// Hive CE model for caching user session data locally.
/// Maps to the 'user_sessions' table in the database.
///
/// Type ID: 1
@HiveType(typeId: HiveTypeIds.userSessionModel)
class UserSessionModel extends HiveObject {
UserSessionModel({
required this.sessionId,
required this.userId,
required this.deviceId,
this.deviceType,
this.deviceName,
this.ipAddress,
this.userAgent,
this.refreshToken,
required this.expiresAt,
required this.createdAt,
this.lastActivity,
});
/// Session ID (Primary Key)
@HiveField(0)
final String sessionId;
/// User ID (Foreign Key to users)
@HiveField(1)
final String userId;
/// Device ID (unique identifier for the device)
@HiveField(2)
final String deviceId;
/// Device type (android, ios, web, etc.)
@HiveField(3)
final String? deviceType;
/// Device name (e.g., "Samsung Galaxy S21")
@HiveField(4)
final String? deviceName;
/// IP address of the device
@HiveField(5)
final String? ipAddress;
/// User agent string
@HiveField(6)
final String? userAgent;
/// Refresh token for session renewal
@HiveField(7)
final String? refreshToken;
/// Session expiration timestamp
@HiveField(8)
final DateTime expiresAt;
/// Session creation timestamp
@HiveField(9)
final DateTime createdAt;
/// Last activity timestamp
@HiveField(10)
final DateTime? lastActivity;
// =========================================================================
// JSON SERIALIZATION
// =========================================================================
/// Create UserSessionModel from JSON
factory UserSessionModel.fromJson(Map<String, dynamic> json) {
return UserSessionModel(
sessionId: json['session_id'] as String,
userId: json['user_id'] as String,
deviceId: json['device_id'] as String,
deviceType: json['device_type'] as String?,
deviceName: json['device_name'] as String?,
ipAddress: json['ip_address'] as String?,
userAgent: json['user_agent'] as String?,
refreshToken: json['refresh_token'] as String?,
expiresAt: DateTime.parse(json['expires_at'] as String),
createdAt: DateTime.parse(json['created_at'] as String),
lastActivity: json['last_activity'] != null
? DateTime.parse(json['last_activity'] as String)
: null,
);
}
/// Convert UserSessionModel to JSON
Map<String, dynamic> toJson() {
return {
'session_id': sessionId,
'user_id': userId,
'device_id': deviceId,
'device_type': deviceType,
'device_name': deviceName,
'ip_address': ipAddress,
'user_agent': userAgent,
'refresh_token': refreshToken,
'expires_at': expiresAt.toIso8601String(),
'created_at': createdAt.toIso8601String(),
'last_activity': lastActivity?.toIso8601String(),
};
}
// =========================================================================
// HELPER METHODS
// =========================================================================
/// Check if session is expired
bool get isExpired => DateTime.now().isAfter(expiresAt);
/// Check if session is valid (not expired)
bool get isValid => !isExpired;
/// Get session duration
Duration get duration => DateTime.now().difference(createdAt);
/// Get time until expiration
Duration get timeUntilExpiration => expiresAt.difference(DateTime.now());
/// Get session age
Duration get age => DateTime.now().difference(createdAt);
/// Get time since last activity
Duration? get timeSinceLastActivity {
if (lastActivity == null) return null;
return DateTime.now().difference(lastActivity!);
}
// =========================================================================
// COPY WITH
// =========================================================================
/// Create a copy with updated fields
UserSessionModel copyWith({
String? sessionId,
String? userId,
String? deviceId,
String? deviceType,
String? deviceName,
String? ipAddress,
String? userAgent,
String? refreshToken,
DateTime? expiresAt,
DateTime? createdAt,
DateTime? lastActivity,
}) {
return UserSessionModel(
sessionId: sessionId ?? this.sessionId,
userId: userId ?? this.userId,
deviceId: deviceId ?? this.deviceId,
deviceType: deviceType ?? this.deviceType,
deviceName: deviceName ?? this.deviceName,
ipAddress: ipAddress ?? this.ipAddress,
userAgent: userAgent ?? this.userAgent,
refreshToken: refreshToken ?? this.refreshToken,
expiresAt: expiresAt ?? this.expiresAt,
createdAt: createdAt ?? this.createdAt,
lastActivity: lastActivity ?? this.lastActivity,
);
}
@override
String toString() {
return 'UserSessionModel(sessionId: $sessionId, userId: $userId, isValid: $isValid)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is UserSessionModel && other.sessionId == sessionId;
}
@override
int get hashCode => sessionId.hashCode;
}

View File

@@ -0,0 +1,71 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user_session_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class UserSessionModelAdapter extends TypeAdapter<UserSessionModel> {
@override
final typeId = 1;
@override
UserSessionModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return UserSessionModel(
sessionId: fields[0] as String,
userId: fields[1] as String,
deviceId: fields[2] as String,
deviceType: fields[3] as String?,
deviceName: fields[4] as String?,
ipAddress: fields[5] as String?,
userAgent: fields[6] as String?,
refreshToken: fields[7] as String?,
expiresAt: fields[8] as DateTime,
createdAt: fields[9] as DateTime,
lastActivity: fields[10] as DateTime?,
);
}
@override
void write(BinaryWriter writer, UserSessionModel obj) {
writer
..writeByte(11)
..writeByte(0)
..write(obj.sessionId)
..writeByte(1)
..write(obj.userId)
..writeByte(2)
..write(obj.deviceId)
..writeByte(3)
..write(obj.deviceType)
..writeByte(4)
..write(obj.deviceName)
..writeByte(5)
..write(obj.ipAddress)
..writeByte(6)
..write(obj.userAgent)
..writeByte(7)
..write(obj.refreshToken)
..writeByte(8)
..write(obj.expiresAt)
..writeByte(9)
..write(obj.createdAt)
..writeByte(10)
..write(obj.lastActivity);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserSessionModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}