马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,950 @@
import 'package:ainoval/utils/logger.dart';
/// 小说模型
class Novel {
Novel({
required this.id,
required this.title,
this.coverUrl= '',
required this.createdAt,
required this.updatedAt,
this.acts = const [],
this.lastEditedChapterId,
this.author,
this.wordCount = 0,
this.readTime = 0,
this.version = 1,
this.contributors = const <String>[],
});
/// 从JSON创建Novel实例
factory Novel.fromJson(Map<String, dynamic> json) {
AppLogger.v(
'NovelModel', 'Parsing Novel from JSON: ${json['id']}'); // 添加日志确认进入
try {
// --- 这是关键部分 ---
List<Act> parsedActs = [];
// 处理acts数据 - 优先检查structure.acts路径
if (json.containsKey('structure') && json['structure'] is Map) {
final structure = json['structure'] as Map<String, dynamic>;
if (structure.containsKey('acts') && structure['acts'] is List) {
AppLogger.v('NovelModel',
'Found "structure.acts" list with ${(structure['acts'] as List).length} items.');
parsedActs = (structure['acts'] as List)
.map((actJson) {
if (actJson is Map<String, dynamic>) {
// 对列表中的每个元素调用 Act.fromJson
return Act.fromJson(actJson);
} else {
// 处理无效数据项
AppLogger.w('NovelModel',
'Invalid item in "structure.acts" list: $actJson');
return null; // 返回null让whereType过滤掉
}
})
.whereType<Act>() // 过滤掉可能的 null 值
.toList();
AppLogger.v('NovelModel',
'Successfully parsed ${parsedActs.length} acts from structure.acts.');
} else {
AppLogger.w('NovelModel',
'"structure.acts" field is missing, null, or not a list in JSON for Novel ${json['id']}');
}
}
// 如果在structure中没有找到有效的acts尝试直接从json的acts字段读取
else if (json.containsKey('acts') && json['acts'] is List) {
AppLogger.v('NovelModel',
'Found direct "acts" list with ${(json['acts'] as List).length} items.');
parsedActs = (json['acts'] as List)
.map((actJson) {
if (actJson is Map<String, dynamic>) {
return Act.fromJson(actJson);
} else {
AppLogger.w('NovelModel',
'Invalid item in direct "acts" list: $actJson');
return null;
}
})
.whereType<Act>()
.toList();
AppLogger.v('NovelModel',
'Successfully parsed ${parsedActs.length} acts from direct acts field.');
} else {
AppLogger.w('NovelModel',
'No valid acts field found in JSON for Novel ${json['id']}');
}
// --- 关键部分结束 ---
// 解析元数据
final metadata = json['metadata'] as Map<String, dynamic>? ?? {};
final wordCount = metadata['wordCount'] is int ? metadata['wordCount'] as int : 0;
final readTime = metadata['readTime'] is int ? metadata['readTime'] as int : 0;
final version = metadata['version'] is int ? metadata['version'] as int : 1;
// 处理contributors列表
List<String> contributors = [];
if (metadata.containsKey('contributors') && metadata['contributors'] is List) {
// 尝试转换每个元素为String
for (var item in metadata['contributors'] as List) {
if (item is String) {
contributors.add(item);
}
}
}
// 解析日期
DateTime createdAt;
DateTime updatedAt;
try {
createdAt = json.containsKey('createdAt') && json['createdAt'] is String
? DateTime.parse(json['createdAt'] as String)
: DateTime.now();
} catch (e) {
AppLogger.w('NovelModel', '解析createdAt失败使用当前时间', e);
createdAt = DateTime.now();
}
try {
updatedAt = json.containsKey('updatedAt') && json['updatedAt'] is String
? DateTime.parse(json['updatedAt'] as String)
: DateTime.now();
} catch (e) {
AppLogger.w('NovelModel', '解析updatedAt失败使用当前时间', e);
updatedAt = DateTime.now();
}
// 处理封面URL字段
String coverUrl = '';
if (json.containsKey('coverUrl') && json['coverUrl'] is String) {
coverUrl = json['coverUrl'] as String;
} else if (json.containsKey('coverImage') && json['coverImage'] is String) {
// 兼容后端可能使用coverImage字段
coverUrl = json['coverImage'] as String;
}
// 创建Novel对象
return Novel(
id: json['id'] as String? ?? 'unknown_${DateTime.now().millisecondsSinceEpoch}',
title: json['title'] as String? ?? '无标题',
coverUrl: coverUrl,
createdAt: createdAt,
updatedAt: updatedAt,
acts: parsedActs,
lastEditedChapterId: json['lastEditedChapterId'] as String?,
author: json['author'] != null
? Author.fromJson(json['author'] as Map<String, dynamic>)
: null,
wordCount: wordCount,
readTime: readTime,
version: version,
contributors: contributors,
);
} catch (e, stackTrace) {
AppLogger.e('NovelModel', 'Error parsing Novel from JSON: ${json['id']}',
e, stackTrace);
// 返回一个基本的空Novel对象避免应用崩溃
return Novel(
id: json['id'] as String? ?? 'error_${DateTime.now().millisecondsSinceEpoch}',
title: '解析错误 - ${json['title'] ?? '无标题'}',
coverUrl: '',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
acts: [],
wordCount: 0,
);
}
}
final String id;
final String title;
final String coverUrl;
final DateTime createdAt;
final DateTime updatedAt;
final List<Act> acts;
final String? lastEditedChapterId; // 上次编辑的章节ID
final Author? author; // 作者信息
final int wordCount; // 总字数(来自元数据)
final int readTime; // 估计阅读时间(分钟)
final int version; // 文档版本号
final List<String> contributors; // 贡献者列表
/// 计算小说总字数(如果需要动态计算)
int calculateWordCount() {
int totalWordCount = 0;
for (final act in acts) {
for (final chapter in act.chapters) {
for (final scene in chapter.scenes) {
totalWordCount += scene.wordCount;
}
}
}
return totalWordCount;
}
/// 计算小说总场景数(考虑 sceneIds 字段)
int getSceneCount() {
int totalSceneCount = 0;
//AppLogger.d('Novel', '开始计算场景总数');
for (final act in acts) {
int actSceneCount = 0;
for (final chapter in act.chapters) {
// 使用 sceneCount 属性,它会返回 scenes 和 sceneIds 中的较大值
int chapterSceneCount = chapter.sceneCount;
actSceneCount += chapterSceneCount;
//AppLogger.d('Novel', '章节 ${chapter.id} 场景数: scenes=${chapter.scenes.length}, sceneIds=${chapter.sceneIds.length}, 取较大值=${chapterSceneCount}');
}
totalSceneCount += actSceneCount;
//AppLogger.d('Novel', '卷 ${act.id} 场景总数: $actSceneCount');
}
//AppLogger.d('Novel', '小说场景总数: $totalSceneCount');
return totalSceneCount;
}
/// 计算小说总章节数
int getChapterCount() {
int totalChapterCount = 0;
for (final act in acts) {
totalChapterCount += act.chapters.length;
}
return totalChapterCount;
}
/// 计算小说总卷数
int getActCount() {
return acts.length;
}
/// 转换为JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'coverUrl': coverUrl,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
'acts': acts.map((act) => act.toJson()).toList(),
'lastEditedChapterId': lastEditedChapterId,
'author': author?.toJson(),
'metadata': {
'wordCount': wordCount,
'readTime': readTime,
'version': version,
'contributors': contributors,
},
};
}
/// 创建Novel的副本
Novel copyWith({
String? id,
String? title,
String? coverUrl,
DateTime? createdAt,
DateTime? updatedAt,
List<Act>? acts,
String? lastEditedChapterId,
Author? author,
int? wordCount,
int? readTime,
int? version,
List<String>? contributors,
}) {
return Novel(
id: id ?? this.id,
title: title ?? this.title,
coverUrl: coverUrl?? this.coverUrl,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
acts: acts ?? this.acts,
lastEditedChapterId: lastEditedChapterId ?? this.lastEditedChapterId,
author: author ?? this.author,
wordCount: wordCount ?? this.wordCount,
readTime: readTime ?? this.readTime,
version: version ?? this.version,
contributors: contributors ?? this.contributors,
);
}
/// 创建一个空的小说结构
static Novel createEmpty(String id, String title) {
final now = DateTime.now();
return Novel(
id: id,
title: title,
createdAt: now,
updatedAt: now,
acts: [],
);
}
/// 添加一个新的Act
Novel addAct(String title) {
final newAct = Act(
id: 'act_${DateTime.now().millisecondsSinceEpoch}',
title: title,
order: acts.length + 1,
chapters: [],
);
return copyWith(
acts: [...acts, newAct],
updatedAt: DateTime.now(),
);
}
/// 获取指定Act
Act? getAct(String actId) {
try {
return acts.firstWhere((act) => act.id == actId);
} catch (e) {
return null;
}
}
/// 获取指定Chapter
Chapter? getChapter(String actId, String chapterId) {
final act = getAct(actId);
if (act == null) return null;
try {
return act.chapters.firstWhere((chapter) => chapter.id == chapterId);
} catch (e) {
return null;
}
}
/// 根据章节ID直接获取章节不需要知道Act ID
Chapter? getChapterById(String chapterId) {
for (final act in acts) {
try {
final chapter =
act.chapters.firstWhere((chapter) => chapter.id == chapterId);
return chapter;
} catch (e) {
// 继续查找下一个act
}
}
return null;
}
/// 获取指定Scene
Scene? getScene(String actId, String chapterId, {String? sceneId}) {
final chapter = getChapter(actId, chapterId);
if (chapter == null) return null;
if (sceneId != null) {
// 如果提供了sceneId则获取特定Scene
return chapter.getScene(sceneId);
} else if (chapter.scenes.isNotEmpty) {
// 否则返回第一个Scene
return chapter.scenes.first;
}
return null;
}
/// 获取上下文章节前后n章
List<Chapter> getContextChapters(String chapterId, int n) {
// 提取所有章节
List<Chapter> allChapters = [];
for (final act in acts) {
allChapters.addAll(act.chapters);
}
// 按order排序
allChapters.sort((a, b) => a.order.compareTo(b.order));
// 找到当前章节的索引
int currentIndex =
allChapters.indexWhere((chapter) => chapter.id == chapterId);
if (currentIndex == -1) {
// 如果找不到当前章节返回前n章
return allChapters.take(n).toList();
}
// 计算前后n章的范围
int startIndex = (currentIndex - n) < 0 ? 0 : (currentIndex - n);
int endIndex = (currentIndex + n) >= allChapters.length
? allChapters.length - 1
: (currentIndex + n);
// 提取前后n章
return allChapters.sublist(startIndex, endIndex + 1);
}
/// 更新最后编辑的章节ID
Novel updateLastEditedChapter(String chapterId) {
return copyWith(
lastEditedChapterId: chapterId,
updatedAt: DateTime.now(),
);
}
}
/// 幕模型如Act 1, Act 2等
class Act {
Act({
required this.id,
required this.title,
required this.order,
this.chapters = const [],
});
/// 从JSON创建Act实例
factory Act.fromJson(Map<String, dynamic> json) {
List<Chapter> parsedChapters = [];
if (json['chapters'] != null && json['chapters'] is List) {
parsedChapters = (json['chapters'] as List<dynamic>)
.map((chapterJson) =>
Chapter.fromJson(chapterJson as Map<String, dynamic>))
.toList();
}
return Act(
id: json['id'] as String,
title: json['title'] as String,
order: json['order'] as int,
chapters: parsedChapters, // 使用解析后的列表
);
}
final String id;
final String title;
final int order;
final List<Chapter> chapters;
/// 计算Act的总字数
int get wordCount {
return chapters.fold(0, (sum, chapter) => sum + chapter.wordCount);
}
/// 转换为JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'order': order,
'chapters': chapters.map((chapter) => chapter.toJson()).toList(),
};
}
/// 创建Act的副本
Act copyWith({
String? id,
String? title,
int? order,
List<Chapter>? chapters,
}) {
return Act(
id: id ?? this.id,
title: title ?? this.title,
order: order ?? this.order,
chapters: chapters ?? this.chapters,
);
}
/// 添加一个新的Chapter
Act addChapter(String title) {
// 创建一个默认的Scene
final defaultScene = Scene.createEmpty();
final newChapter = Chapter(
id: 'chapter_${DateTime.now().millisecondsSinceEpoch}',
title: title,
order: chapters.length + 1,
scenes: [defaultScene], // 包含一个默认的Scene
);
return copyWith(
chapters: [...chapters, newChapter],
);
}
/// 获取指定Chapter
Chapter? getChapter(String chapterId) {
try {
return chapters.firstWhere((chapter) => chapter.id == chapterId);
} catch (e) {
return null;
}
}
}
/// 章节模型
class Chapter {
Chapter({
required this.id,
required this.title,
required this.order,
this.scenes = const [],
this.sceneIds = const [], // 添加 sceneIds 字段
});
/// 从JSON创建Chapter实例
factory Chapter.fromJson(Map<String, dynamic> json) {
List<Scene> parsedScenes = [];
List<String> parsedSceneIds = [];
// 解析场景列表
if (json['scenes'] != null && json['scenes'] is List) {
parsedScenes = (json['scenes'] as List<dynamic>)
.map((sceneJson) => Scene.fromJson(sceneJson as Map<String, dynamic>))
.toList();
}
// 解析场景ID列表
if (json['sceneIds'] != null && json['sceneIds'] is List) {
parsedSceneIds = (json['sceneIds'] as List<dynamic>)
.map((id) => id.toString())
.toList();
}
return Chapter(
id: json['id'] as String,
title: json['title'] as String,
order: json['order'] as int,
scenes: parsedScenes,
sceneIds: parsedSceneIds, // 保存场景ID列表
);
}
final String id;
final String title;
final int order;
final List<Scene> scenes;
final List<String> sceneIds; // 保存从后端返回的场景ID列表
/// 计算章节的总字数
int get wordCount {
return scenes.fold(0, (sum, scene) => sum + scene.wordCount);
}
/// 获取场景总数scenes列表或sceneIds列表中的较大值
int get sceneCount {
int scenesLength = scenes.length;
int sceneIdsLength = sceneIds.length;
int result = scenesLength > sceneIdsLength ? scenesLength : sceneIdsLength;
//AppLogger.d('Chapter', '章节 $id 场景计数: scenes=$scenesLength, sceneIds=$sceneIdsLength, 取较大值=$result');
return result;
}
/// 转换为JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'order': order,
'scenes': scenes.map((scene) => scene.toJson()).toList(),
'sceneIds': sceneIds, // 添加场景ID列表
};
}
/// 创建Chapter的副本
Chapter copyWith({
String? id,
String? title,
int? order,
List<Scene>? scenes,
List<String>? sceneIds, // 添加sceneIds参数
}) {
return Chapter(
id: id ?? this.id,
title: title ?? this.title,
order: order ?? this.order,
scenes: scenes ?? this.scenes,
sceneIds: sceneIds ?? this.sceneIds, // 设置sceneIds
);
}
/// 添加一个新的Scene
void addScene(Scene newScene) {
scenes.add(newScene);
}
/// 获取指定Scene
Scene? getScene(String sceneId) {
try {
return scenes.firstWhere((scene) => scene.id == sceneId);
} catch (e) {
return null;
}
}
/// 更新指定Scene
Chapter updateScene(String sceneId, Scene updatedScene) {
final updatedScenes = scenes.map((scene) {
if (scene.id == sceneId) {
return updatedScene;
}
return scene;
}).toList();
return copyWith(scenes: updatedScenes);
}
}
/// 场景模型
class Scene {
Scene({
required this.id,
required this.content,
required this.wordCount,
required this.summary,
required this.lastEdited,
this.title = '',
this.actId = '',
this.chapterId = '',
this.version = 1,
this.history = const [],
});
/// 从JSON创建Scene实例
factory Scene.fromJson(Map<String, dynamic> json) {
// 创建安全的Summary对象
Summary summaryObj;
try {
// 处理summary字段 - 可能是字符串(后端)或对象(前端)
if (json.containsKey('summary')) {
final summaryData = json['summary'];
if (summaryData is Map<String, dynamic>) {
// 如果是对象格式,直接解析
summaryObj = Summary.fromJson(summaryData);
} else if (summaryData is String) {
// 如果是字符串格式后端发送的创建Summary对象
final sceneId = json['id'] ?? DateTime.now().millisecondsSinceEpoch.toString();
summaryObj = Summary(
id: '${sceneId}_summary',
content: summaryData,
);
} else {
// 其他格式创建默认Summary
final sceneId = json['id'] ?? DateTime.now().millisecondsSinceEpoch.toString();
summaryObj = Summary(
id: '${sceneId}_summary',
content: '',
);
AppLogger.w('Scene.fromJson', '场景 $sceneId 的摘要字段类型不支持: ${summaryData.runtimeType}');
}
} else {
// 创建默认Summary
final sceneId = json['id'] ?? DateTime.now().millisecondsSinceEpoch.toString();
summaryObj = Summary(
id: '${sceneId}_summary',
content: '',
);
AppLogger.w('Scene.fromJson', '场景 $sceneId 缺少摘要字段,已创建默认摘要');
}
} catch (e) {
// 处理任何异常创建默认Summary
final sceneId = json['id'] ?? DateTime.now().millisecondsSinceEpoch.toString();
summaryObj = Summary(
id: '${sceneId}_summary',
content: '',
);
AppLogger.e('Scene.fromJson', '解析场景 $sceneId 的摘要时出错', e);
}
// 安全解析lastEdited字段支持多种日期格式
DateTime lastEditedDate;
try {
if (json.containsKey('lastEdited') && json['lastEdited'] != null) {
final lastEditedStr = json['lastEdited'].toString();
lastEditedDate = _parseDateTime(lastEditedStr);
} else if (json.containsKey('updatedAt') && json['updatedAt'] != null) {
// 兼容后端可能使用updatedAt字段
final updatedAtStr = json['updatedAt'].toString();
lastEditedDate = _parseDateTime(updatedAtStr);
} else {
lastEditedDate = DateTime.now();
AppLogger.w('Scene.fromJson', '场景 ${json['id']} 缺少时间字段,使用当前时间');
}
} catch (e) {
lastEditedDate = DateTime.now();
AppLogger.w('Scene.fromJson', '解析场景 ${json['id']} 的时间字段失败,使用当前时间', e);
}
return Scene(
id: json['id'] ?? DateTime.now().millisecondsSinceEpoch.toString(),
content: json['content'] ?? '',
wordCount: json['wordCount'] ?? 0,
summary: summaryObj,
lastEdited: lastEditedDate,
title: json['title'] ?? '',
actId: json['actId'] ?? '',
chapterId: json['chapterId'] ?? '',
version: json['version'] ?? 1,
history: [],
);
}
final String id;
final String content;
final int wordCount;
final Summary summary;
final DateTime lastEdited;
final String title;
final String actId;
final String chapterId;
final int version;
final List<HistoryEntry> history;
/// 解析多种日期格式的工具方法
static DateTime _parseDateTime(String dateTimeStr) {
if (dateTimeStr.isEmpty) {
return DateTime.now();
}
try {
// 尝试标准ISO格式
return DateTime.parse(dateTimeStr);
} catch (e1) {
try {
// 尝试处理带毫秒的格式 "yyyy-MM-dd'T'HH:mm:ss.SSS"
if (dateTimeStr.contains('T') && dateTimeStr.contains('.')) {
// 如果包含时区信息,先移除
String cleanStr = dateTimeStr;
if (cleanStr.endsWith('Z')) {
cleanStr = cleanStr.substring(0, cleanStr.length - 1);
}
if (cleanStr.contains('+') || cleanStr.lastIndexOf('-') > 10) {
// 移除时区偏移
final timeZoneIndex = cleanStr.lastIndexOf('+') > cleanStr.lastIndexOf('-')
? cleanStr.lastIndexOf('+')
: cleanStr.lastIndexOf('-');
if (timeZoneIndex > 10) {
cleanStr = cleanStr.substring(0, timeZoneIndex);
}
}
return DateTime.parse(cleanStr);
}
// 尝试其他常见格式
// 格式yyyy-MM-dd HH:mm:ss
if (dateTimeStr.contains(' ') && !dateTimeStr.contains('T')) {
final parts = dateTimeStr.split(' ');
if (parts.length == 2) {
final datePart = parts[0];
final timePart = parts[1];
final isoStr = '${datePart}T$timePart';
return DateTime.parse(isoStr);
}
}
throw e1; // 如果都失败了,抛出原始异常
} catch (e2) {
AppLogger.w('Scene._parseDateTime', '无法解析日期格式: $dateTimeStr,使用当前时间');
return DateTime.now();
}
}
}
/// 转换为JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'content': content,
'wordCount': wordCount,
'summary': summary.toJson(),
'lastEdited': lastEdited.toIso8601String(),
'title': title,
'actId': actId,
'chapterId': chapterId,
'version': version,
'history': history.map((entry) => entry.toJson()).toList(),
};
}
/// 创建Scene的副本
Scene copyWith({
String? id,
String? content,
int? wordCount,
Summary? summary,
DateTime? lastEdited,
String? title,
String? actId,
String? chapterId,
int? version,
List<HistoryEntry>? history,
}) {
return Scene(
id: id ?? this.id,
content: content ?? this.content,
wordCount: wordCount ?? this.wordCount,
summary: summary ?? this.summary,
lastEdited: lastEdited ?? this.lastEdited,
title: title ?? this.title,
actId: actId ?? this.actId,
chapterId: chapterId ?? this.chapterId,
version: version ?? this.version,
history: history ?? this.history,
);
}
/// 创建一个空的场景
static Scene createEmpty() {
const defaultContent = '{"ops":[{"insert":"\\n"}]}'; // <-- 确保是这个值
final now = DateTime.now();
return Scene(
id: DateTime.now().millisecondsSinceEpoch.toString(),
content: defaultContent,
wordCount: 0,
summary: Summary(
id: '${DateTime.now().millisecondsSinceEpoch}_summary',
content: '',
),
lastEdited: now,
title: '',
actId: '',
chapterId: '',
version: 1,
history: [],
);
}
/// 创建一个默认的场景
static Scene createDefault(String sceneIdBase) {
// 使用正确Quill Delta格式包含ops对象的内容
const defaultContent = '{"ops":[{"insert":"\\n"}]}';
final now = DateTime.now();
return Scene(
id: sceneIdBase,
content: defaultContent,
wordCount: 0,
summary: Summary(
id: '${sceneIdBase}_summary',
content: '',
),
lastEdited: now,
title: '新场景',
actId: '',
chapterId: '',
version: 1,
history: [],
);
}
}
/// 摘要模型
class Summary {
Summary({
required this.id,
required this.content,
});
/// 从JSON创建Summary实例
factory Summary.fromJson(Map<String, dynamic> json) {
return Summary(
id: json['id'] as String,
content: json['content'] as String,
);
}
final String id;
final String content;
/// 转换为JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'content': content,
};
}
/// 创建Summary的副本
Summary copyWith({
String? id,
String? content,
}) {
return Summary(
id: id ?? this.id,
content: content ?? this.content,
);
}
/// 创建一个空的摘要
static Summary createEmpty() {
return Summary(
id: 'summary_${DateTime.now().millisecondsSinceEpoch}',
content: '',
);
}
}
class HistoryEntry {
HistoryEntry({
this.content,
required this.updatedAt,
required this.updatedBy,
required this.reason,
});
factory HistoryEntry.fromJson(Map<String, dynamic> json) {
DateTime updatedAt;
try {
updatedAt = DateTime.parse(json['updatedAt']);
} catch (e) {
updatedAt = DateTime.now();
}
return HistoryEntry(
content: json['content'],
updatedAt: updatedAt,
updatedBy: json['updatedBy'] ?? 'unknown',
reason: json['reason'] ?? '',
);
}
final String? content;
final DateTime updatedAt;
final String updatedBy;
final String reason;
Map<String, dynamic> toJson() => {
'content': content,
'updatedAt': updatedAt.toIso8601String(),
'updatedBy': updatedBy,
'reason': reason,
};
}
/// 作者信息模型
class Author {
Author({
required this.id,
required this.username,
});
/// 从JSON创建Author实例
factory Author.fromJson(Map<String, dynamic> json) {
return Author(
id: json['id'] ?? '',
username: json['username'] ?? '未知作者',
);
}
final String id;
final String username;
/// 转换为JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'username': username,
};
}
/// 创建Author的副本
Author copyWith({
String? id,
String? username,
}) {
return Author(
id: id ?? this.id,
username: username ?? this.username,
);
}
}