feat: 增强日历功能并集成 AgentScope 代理服务
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import 'package:social_app/core/api/i_api_client.dart';
|
||||
|
||||
import 'models/schedule_item_model.dart';
|
||||
|
||||
class CalendarApi {
|
||||
final IApiClient _client;
|
||||
static const _prefix = '/api/v1/schedule-items';
|
||||
|
||||
CalendarApi(this._client);
|
||||
|
||||
Future<List<ScheduleItemModel>> listByRange({
|
||||
required DateTime startAt,
|
||||
required DateTime endAt,
|
||||
}) async {
|
||||
final response = await _client.get(
|
||||
'$_prefix?start_at=${Uri.encodeQueryComponent(startAt.toUtc().toIso8601String())}&end_at=${Uri.encodeQueryComponent(endAt.toUtc().toIso8601String())}',
|
||||
);
|
||||
final data = response.data;
|
||||
if (data is! List) {
|
||||
return const [];
|
||||
}
|
||||
return data
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ScheduleItemModel.fromJson)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> getById(String id) async {
|
||||
final response = await _client.get('$_prefix/$id');
|
||||
return ScheduleItemModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> create(ScheduleItemModel request) async {
|
||||
final response = await _client.post(_prefix, data: request.toCreateJson());
|
||||
return ScheduleItemModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> update(ScheduleItemModel request) async {
|
||||
final response = await _client.patch(
|
||||
'$_prefix/${request.id}',
|
||||
data: request.toUpdateJson(),
|
||||
);
|
||||
return ScheduleItemModel.fromJson(response.data as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<void> delete(String id) async {
|
||||
await _client.delete('$_prefix/$id');
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ class ScheduleItemModel {
|
||||
final ScheduleSourceType sourceType;
|
||||
final ScheduleStatus status;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
|
||||
ScheduleItemModel({
|
||||
required this.id,
|
||||
@@ -27,7 +28,9 @@ class ScheduleItemModel {
|
||||
this.sourceType = ScheduleSourceType.manual,
|
||||
this.status = ScheduleStatus.active,
|
||||
DateTime? createdAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now();
|
||||
DateTime? updatedAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now(),
|
||||
updatedAt = updatedAt ?? DateTime.now();
|
||||
|
||||
ScheduleItemModel copyWith({
|
||||
String? id,
|
||||
@@ -40,6 +43,7 @@ class ScheduleItemModel {
|
||||
ScheduleSourceType? sourceType,
|
||||
ScheduleStatus? status,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return ScheduleItemModel(
|
||||
id: id ?? this.id,
|
||||
@@ -52,45 +56,222 @@ class ScheduleItemModel {
|
||||
sourceType: sourceType ?? this.sourceType,
|
||||
status: status ?? this.status,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
factory ScheduleItemModel.fromJson(Map<String, dynamic> json) {
|
||||
return ScheduleItemModel(
|
||||
id: json['id'] as String,
|
||||
title: json['title'] as String,
|
||||
description: json['description'] as String?,
|
||||
startAt: DateTime.parse(json['start_at'] as String).toLocal(),
|
||||
endAt: json['end_at'] != null
|
||||
? DateTime.parse(json['end_at'] as String).toLocal()
|
||||
: null,
|
||||
timezone: (json['timezone'] as String?) ?? 'UTC',
|
||||
metadata: json['metadata'] is Map<String, dynamic>
|
||||
? ScheduleMetadata.fromJson(json['metadata'] as Map<String, dynamic>)
|
||||
: null,
|
||||
sourceType: _sourceTypeFromApi(json['source_type'] as String?),
|
||||
status: _statusFromApi(json['status'] as String?),
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String).toLocal()
|
||||
: DateTime.now(),
|
||||
updatedAt: json['updated_at'] != null
|
||||
? DateTime.parse(json['updated_at'] as String).toLocal()
|
||||
: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toCreateJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'start_at': startAt.toUtc().toIso8601String(),
|
||||
'end_at': endAt?.toUtc().toIso8601String(),
|
||||
'timezone': timezone,
|
||||
'metadata': metadata?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toUpdateJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'start_at': startAt.toUtc().toIso8601String(),
|
||||
'end_at': endAt?.toUtc().toIso8601String(),
|
||||
'timezone': timezone,
|
||||
'metadata': metadata?.toJson(),
|
||||
'status': _statusToApi(status),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduleMetadata {
|
||||
final String? color;
|
||||
final String? location;
|
||||
final String? notes;
|
||||
final List<Attachment>? attachments;
|
||||
final List<Attachment> attachments;
|
||||
final int version;
|
||||
final Map<String, dynamic> raw;
|
||||
|
||||
ScheduleMetadata({this.color, this.location, this.notes, this.attachments});
|
||||
ScheduleMetadata({
|
||||
this.color,
|
||||
this.location,
|
||||
this.notes,
|
||||
List<Attachment>? attachments,
|
||||
this.version = 1,
|
||||
Map<String, dynamic>? raw,
|
||||
}) : attachments = attachments ?? const [],
|
||||
raw = raw ?? const {};
|
||||
|
||||
ScheduleMetadata copyWith({
|
||||
String? color,
|
||||
String? location,
|
||||
String? notes,
|
||||
List<Attachment>? attachments,
|
||||
int? version,
|
||||
Map<String, dynamic>? raw,
|
||||
}) {
|
||||
return ScheduleMetadata(
|
||||
color: color ?? this.color,
|
||||
location: location ?? this.location,
|
||||
notes: notes ?? this.notes,
|
||||
attachments: attachments ?? this.attachments,
|
||||
version: version ?? this.version,
|
||||
raw: raw ?? this.raw,
|
||||
);
|
||||
}
|
||||
|
||||
factory ScheduleMetadata.fromJson(Map<String, dynamic> json) {
|
||||
final rawAttachments = json['attachments'];
|
||||
final attachments = rawAttachments is List
|
||||
? rawAttachments
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(Attachment.fromJson)
|
||||
.toList()
|
||||
: <Attachment>[];
|
||||
return ScheduleMetadata(
|
||||
color: json['color'] as String?,
|
||||
location: json['location'] as String?,
|
||||
notes: json['notes'] as String?,
|
||||
attachments: attachments,
|
||||
version: (json['version'] as int?) ?? 1,
|
||||
raw: Map<String, dynamic>.from(json),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'color': color,
|
||||
'location': location,
|
||||
'notes': notes,
|
||||
'attachments': attachments.map((item) => item.toJson()).toList(),
|
||||
'version': version,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Attachment {
|
||||
final String name;
|
||||
final List<String> visibleTo;
|
||||
final String? url;
|
||||
final String? note;
|
||||
final String? content;
|
||||
final String type;
|
||||
|
||||
Attachment({
|
||||
required this.name,
|
||||
this.visibleTo = const [],
|
||||
this.url,
|
||||
this.note,
|
||||
this.content,
|
||||
this.type = 'document',
|
||||
});
|
||||
|
||||
Attachment copyWith({
|
||||
String? name,
|
||||
List<String>? visibleTo,
|
||||
String? url,
|
||||
String? note,
|
||||
String? content,
|
||||
String? type,
|
||||
}) {
|
||||
return Attachment(
|
||||
name: name ?? this.name,
|
||||
visibleTo: visibleTo ?? this.visibleTo,
|
||||
url: url ?? this.url,
|
||||
note: note ?? this.note,
|
||||
content: content ?? this.content,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
}
|
||||
|
||||
factory Attachment.fromJson(Map<String, dynamic> json) {
|
||||
final rawVisibleTo = json['visible_to'];
|
||||
final visibleTo = rawVisibleTo is List
|
||||
? rawVisibleTo.map((item) => item.toString()).toList()
|
||||
: <String>[];
|
||||
return Attachment(
|
||||
name: (json['name'] as String?) ?? '',
|
||||
visibleTo: visibleTo,
|
||||
url: json['url'] as String?,
|
||||
note: json['note'] as String?,
|
||||
content: json['content'] as String?,
|
||||
type: (json['type'] as String?) ?? 'document',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'name': name,
|
||||
'visible_to': visibleTo,
|
||||
'url': url,
|
||||
'note': note,
|
||||
'content': content,
|
||||
'type': type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ScheduleSourceType _sourceTypeFromApi(String? raw) {
|
||||
switch (raw) {
|
||||
case 'imported':
|
||||
return ScheduleSourceType.imported;
|
||||
case 'agent_generated':
|
||||
return ScheduleSourceType.agentGenerated;
|
||||
case 'manual':
|
||||
default:
|
||||
return ScheduleSourceType.manual;
|
||||
}
|
||||
}
|
||||
|
||||
ScheduleStatus _statusFromApi(String? raw) {
|
||||
switch (raw) {
|
||||
case 'completed':
|
||||
return ScheduleStatus.completed;
|
||||
case 'canceled':
|
||||
return ScheduleStatus.canceled;
|
||||
case 'archived':
|
||||
return ScheduleStatus.archived;
|
||||
case 'active':
|
||||
default:
|
||||
return ScheduleStatus.active;
|
||||
}
|
||||
}
|
||||
|
||||
String _statusToApi(ScheduleStatus status) {
|
||||
switch (status) {
|
||||
case ScheduleStatus.active:
|
||||
return 'active';
|
||||
case ScheduleStatus.completed:
|
||||
return 'completed';
|
||||
case ScheduleStatus.canceled:
|
||||
return 'canceled';
|
||||
case ScheduleStatus.archived:
|
||||
return 'archived';
|
||||
}
|
||||
}
|
||||
|
||||
const defaultColors = [
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:social_app/core/api/i_api_client.dart';
|
||||
|
||||
import '../calendar_api.dart';
|
||||
import '../models/schedule_item_model.dart';
|
||||
|
||||
class MockCalendarService {
|
||||
@@ -58,47 +60,70 @@ class MockCalendarService {
|
||||
class CalendarService {
|
||||
final IApiClient? _apiClient;
|
||||
final MockCalendarService _mock = MockCalendarService();
|
||||
CalendarApi? _calendarApi;
|
||||
|
||||
CalendarService({IApiClient? apiClient}) : _apiClient = apiClient;
|
||||
|
||||
List<ScheduleItemModel> getEventsForDay(DateTime date) {
|
||||
if (_apiClient != null) {
|
||||
throw UnimplementedError('Real API not implemented');
|
||||
CalendarApi get _api {
|
||||
final api = _calendarApi;
|
||||
if (api != null) {
|
||||
return api;
|
||||
}
|
||||
return _mock.getEventsForDay(date);
|
||||
final client = _apiClient;
|
||||
if (client == null) {
|
||||
throw StateError('Real API client not configured');
|
||||
}
|
||||
final created = CalendarApi(client);
|
||||
_calendarApi = created;
|
||||
return created;
|
||||
}
|
||||
|
||||
List<ScheduleItemModel> getEventsForRange(DateTime start, DateTime end) {
|
||||
Future<List<ScheduleItemModel>> getEventsForDay(DateTime date) async {
|
||||
if (_apiClient == null) {
|
||||
return _mock.getEventsForDay(date);
|
||||
}
|
||||
final start = DateTime(date.year, date.month, date.day);
|
||||
final end = DateTime(date.year, date.month, date.day, 23, 59, 59);
|
||||
return getEventsForRange(start, end);
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getEventsForRange(
|
||||
DateTime start,
|
||||
DateTime end,
|
||||
) async {
|
||||
if (_apiClient != null) {
|
||||
throw UnimplementedError('Real API not implemented');
|
||||
return _api.listByRange(startAt: start, endAt: end);
|
||||
}
|
||||
return _mock.getEventsForRange(start, end);
|
||||
}
|
||||
|
||||
ScheduleItemModel? getEventById(String id) {
|
||||
Future<ScheduleItemModel?> getEventById(String id) async {
|
||||
if (_apiClient != null) {
|
||||
throw UnimplementedError('Real API not implemented');
|
||||
return _api.getById(id);
|
||||
}
|
||||
return _mock.getEventById(id);
|
||||
}
|
||||
|
||||
void addEvent(ScheduleItemModel event) {
|
||||
Future<ScheduleItemModel> addEvent(ScheduleItemModel event) async {
|
||||
if (_apiClient != null) {
|
||||
throw UnimplementedError('Real API not implemented');
|
||||
return _api.create(event);
|
||||
}
|
||||
_mock.addEvent(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
void updateEvent(ScheduleItemModel event) {
|
||||
Future<ScheduleItemModel> updateEvent(ScheduleItemModel event) async {
|
||||
if (_apiClient != null) {
|
||||
throw UnimplementedError('Real API not implemented');
|
||||
return _api.update(event);
|
||||
}
|
||||
_mock.updateEvent(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
void deleteEvent(String id) {
|
||||
Future<void> deleteEvent(String id) async {
|
||||
if (_apiClient != null) {
|
||||
throw UnimplementedError('Real API not implemented');
|
||||
await _api.delete(id);
|
||||
return;
|
||||
}
|
||||
_mock.deleteEvent(id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user