feat: 增强日历功能并集成 AgentScope 代理服务

This commit is contained in:
qzl
2026-03-11 15:28:29 +08:00
parent e55e445906
commit e20e7d2a02
85 changed files with 5175 additions and 885 deletions
@@ -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);
}