00f37d7e19
- 新增 ReminderActionExecutor 处理取消/稍后提醒操作 - 新增 ReminderOutboxStore 本地存储待处理操作 - 重构 LocalNotificationService 支持聚合提醒和交互操作 - 新增 event_color_resolver 工具类统一颜色解析 - 新增 CalendarService.archiveEvent 归档方法 - 增强 ModelTracking 支持缓存命中、推理token和成本追踪 - 添加 qwen3.5-35b-a3b 模型配置 - 更新 AndroidManifest 全屏intent权限 - 补充相关单元测试和文档
175 lines
4.4 KiB
Dart
175 lines
4.4 KiB
Dart
class ReminderPayload {
|
|
final String eventId;
|
|
final String title;
|
|
final DateTime startAt;
|
|
final DateTime? endAt;
|
|
final String timezone;
|
|
final String? location;
|
|
final String? notes;
|
|
final String? color;
|
|
final ReminderPayloadMode mode;
|
|
final List<String> aggregateIds;
|
|
final int version;
|
|
|
|
const ReminderPayload({
|
|
required this.eventId,
|
|
required this.title,
|
|
required this.startAt,
|
|
required this.timezone,
|
|
this.endAt,
|
|
this.location,
|
|
this.notes,
|
|
this.color,
|
|
this.mode = ReminderPayloadMode.single,
|
|
this.aggregateIds = const [],
|
|
this.version = 1,
|
|
});
|
|
|
|
ReminderPayload copyWith({
|
|
String? eventId,
|
|
String? title,
|
|
DateTime? startAt,
|
|
DateTime? endAt,
|
|
String? timezone,
|
|
String? location,
|
|
String? notes,
|
|
String? color,
|
|
ReminderPayloadMode? mode,
|
|
List<String>? aggregateIds,
|
|
int? version,
|
|
}) {
|
|
return ReminderPayload(
|
|
eventId: eventId ?? this.eventId,
|
|
title: title ?? this.title,
|
|
startAt: startAt ?? this.startAt,
|
|
endAt: endAt ?? this.endAt,
|
|
timezone: timezone ?? this.timezone,
|
|
location: location ?? this.location,
|
|
notes: notes ?? this.notes,
|
|
color: color ?? this.color,
|
|
mode: mode ?? this.mode,
|
|
aggregateIds: aggregateIds ?? this.aggregateIds,
|
|
version: version ?? this.version,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'eventId': eventId,
|
|
'title': title,
|
|
'startAt': startAt.toIso8601String(),
|
|
'endAt': endAt?.toIso8601String(),
|
|
'timezone': timezone,
|
|
'location': location,
|
|
'notes': notes,
|
|
'color': color,
|
|
'mode': mode.value,
|
|
'aggregateIds': aggregateIds,
|
|
'version': version,
|
|
};
|
|
}
|
|
|
|
factory ReminderPayload.fromJson(Map<String, dynamic> json) {
|
|
final eventId = (json['eventId'] as String?) ?? '';
|
|
if (eventId.isEmpty) {
|
|
throw const FormatException('eventId is required');
|
|
}
|
|
|
|
final startAtRaw = json['startAt'] as String?;
|
|
if (startAtRaw == null || startAtRaw.isEmpty) {
|
|
throw const FormatException('startAt is required');
|
|
}
|
|
final parsedStartAt = DateTime.parse(startAtRaw);
|
|
|
|
final mode = ReminderPayloadMode.fromValue(
|
|
(json['mode'] as String?) ?? 'single',
|
|
);
|
|
final aggregateIds = (json['aggregateIds'] as List<dynamic>? ?? const [])
|
|
.map((item) => item.toString())
|
|
.toList();
|
|
if (mode == ReminderPayloadMode.aggregate && aggregateIds.length < 2) {
|
|
throw const FormatException('aggregateIds must contain at least 2 items');
|
|
}
|
|
|
|
return ReminderPayload(
|
|
eventId: eventId,
|
|
title: (json['title'] as String?) ?? '',
|
|
startAt: parsedStartAt,
|
|
endAt: json['endAt'] != null
|
|
? DateTime.parse(json['endAt'] as String)
|
|
: null,
|
|
timezone: (json['timezone'] as String?) ?? 'UTC',
|
|
location: json['location'] as String?,
|
|
notes: json['notes'] as String?,
|
|
color: json['color'] as String?,
|
|
mode: mode,
|
|
aggregateIds: aggregateIds,
|
|
version: (json['version'] as int?) ?? 1,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) {
|
|
return true;
|
|
}
|
|
return other is ReminderPayload &&
|
|
other.eventId == eventId &&
|
|
other.title == title &&
|
|
other.startAt == startAt &&
|
|
other.endAt == endAt &&
|
|
other.timezone == timezone &&
|
|
other.location == location &&
|
|
other.notes == notes &&
|
|
other.color == color &&
|
|
other.mode == mode &&
|
|
_listEquals(other.aggregateIds, aggregateIds) &&
|
|
other.version == version;
|
|
}
|
|
|
|
@override
|
|
int get hashCode {
|
|
return Object.hash(
|
|
eventId,
|
|
title,
|
|
startAt,
|
|
endAt,
|
|
timezone,
|
|
location,
|
|
notes,
|
|
color,
|
|
mode,
|
|
Object.hashAll(aggregateIds),
|
|
version,
|
|
);
|
|
}
|
|
}
|
|
|
|
enum ReminderPayloadMode {
|
|
single('single'),
|
|
aggregate('aggregate');
|
|
|
|
const ReminderPayloadMode(this.value);
|
|
|
|
final String value;
|
|
|
|
static ReminderPayloadMode fromValue(String raw) {
|
|
return ReminderPayloadMode.values.firstWhere(
|
|
(item) => item.value == raw,
|
|
orElse: () => ReminderPayloadMode.single,
|
|
);
|
|
}
|
|
}
|
|
|
|
bool _listEquals(List<String> left, List<String> right) {
|
|
if (left.length != right.length) {
|
|
return false;
|
|
}
|
|
for (var i = 0; i < left.length; i++) {
|
|
if (left[i] != right[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|