feat: add payment card grouping and analysis

This commit is contained in:
JiWoong Sul
2025-11-14 16:53:41 +09:00
parent cba7d082bd
commit 132ae758de
40 changed files with 2846 additions and 522 deletions

View File

@@ -0,0 +1,33 @@
import 'package:hive/hive.dart';
part 'payment_card_model.g.dart';
@HiveType(typeId: 2)
class PaymentCardModel extends HiveObject {
@HiveField(0)
String id;
@HiveField(1)
String issuerName;
@HiveField(2)
String last4;
@HiveField(3)
String colorHex;
@HiveField(4)
String iconName;
@HiveField(5)
bool isDefault;
PaymentCardModel({
required this.id,
required this.issuerName,
required this.last4,
required this.colorHex,
required this.iconName,
this.isDefault = false,
});
}

View File

@@ -0,0 +1,56 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'payment_card_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class PaymentCardModelAdapter extends TypeAdapter<PaymentCardModel> {
@override
final int typeId = 2;
@override
PaymentCardModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return PaymentCardModel(
id: fields[0] as String,
issuerName: fields[1] as String,
last4: fields[2] as String,
colorHex: fields[3] as String,
iconName: fields[4] as String,
isDefault: fields[5] as bool,
);
}
@override
void write(BinaryWriter writer, PaymentCardModel obj) {
writer
..writeByte(6)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.issuerName)
..writeByte(2)
..write(obj.last4)
..writeByte(3)
..write(obj.colorHex)
..writeByte(4)
..write(obj.iconName)
..writeByte(5)
..write(obj.isDefault);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PaymentCardModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,14 @@
/// SMS 스캔 등에서 추출한 결제수단 정보 제안
class PaymentCardSuggestion {
final String issuerName;
final String? last4;
final String? source; // 예: SMS, OCR 등
const PaymentCardSuggestion({
required this.issuerName,
this.last4,
this.source,
});
bool get hasLast4 => last4 != null && last4!.length == 4;
}

View File

@@ -1,3 +1,5 @@
import 'payment_card_suggestion.dart';
class Subscription {
final String id;
final String serviceName;
@@ -10,6 +12,8 @@ class Subscription {
final DateTime? lastPaymentDate;
final String? websiteUrl;
final String currency;
final String? paymentCardId;
final PaymentCardSuggestion? paymentCardSuggestion;
Subscription({
required this.id,
@@ -23,6 +27,8 @@ class Subscription {
this.lastPaymentDate,
this.websiteUrl,
this.currency = 'KRW',
this.paymentCardId,
this.paymentCardSuggestion,
});
Map<String, dynamic> toMap() {
@@ -38,6 +44,10 @@ class Subscription {
'lastPaymentDate': lastPaymentDate?.toIso8601String(),
'websiteUrl': websiteUrl,
'currency': currency,
'paymentCardId': paymentCardId,
'paymentCardSuggestionIssuer': paymentCardSuggestion?.issuerName,
'paymentCardSuggestionLast4': paymentCardSuggestion?.last4,
'paymentCardSuggestionSource': paymentCardSuggestion?.source,
};
}
@@ -56,6 +66,14 @@ class Subscription {
: null,
websiteUrl: map['websiteUrl'] as String?,
currency: map['currency'] as String? ?? 'KRW',
paymentCardId: map['paymentCardId'] as String?,
paymentCardSuggestion: map['paymentCardSuggestionIssuer'] != null
? PaymentCardSuggestion(
issuerName: map['paymentCardSuggestionIssuer'] as String,
last4: map['paymentCardSuggestionLast4'] as String?,
source: map['paymentCardSuggestionSource'] as String?,
)
: null,
);
}

View File

@@ -49,6 +49,9 @@ class SubscriptionModel extends HiveObject {
@HiveField(14)
double? eventPrice; // 이벤트 기간 중 가격
@HiveField(15)
String? paymentCardId; // 연결된 결제수단의 ID
SubscriptionModel({
required this.id,
required this.serviceName,
@@ -65,6 +68,7 @@ class SubscriptionModel extends HiveObject {
this.eventStartDate,
this.eventEndDate,
this.eventPrice,
this.paymentCardId,
});
// 주기적 결제 여부 확인

View File

@@ -32,13 +32,14 @@ class SubscriptionModelAdapter extends TypeAdapter<SubscriptionModel> {
eventStartDate: fields[12] as DateTime?,
eventEndDate: fields[13] as DateTime?,
eventPrice: fields[14] as double?,
paymentCardId: fields[15] as String?,
);
}
@override
void write(BinaryWriter writer, SubscriptionModel obj) {
writer
..writeByte(15)
..writeByte(16)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -68,7 +69,9 @@ class SubscriptionModelAdapter extends TypeAdapter<SubscriptionModel> {
..writeByte(13)
..write(obj.eventEndDate)
..writeByte(14)
..write(obj.eventPrice);
..write(obj.eventPrice)
..writeByte(15)
..write(obj.paymentCardId);
}
@override