# 读书 EPUB 阅读器 — 数据结构与 API 协议定义 > 基于 WeRead v10.0.3 (Build 79) --- ## 一、关键数据结构定义 (Header Files) ### 1.1 EPUB 解析数据结构 ```objc // ============================================================ #pragma mark - WREpubParser (EPUB 文件解析器) // ============================================================ @interface WREpubParser : NSObject @property (nonatomic, copy) NSString *epubFilePath; // EPUB 文件路径 @property (nonatomic, copy) NSString *opfFilePath; // content.opf 路径 @property (nonatomic, strong) NSError *lastError; // 最近错误 @property (nonatomic, weak) UIViewController *hostVC; // 宿主控制器 @property (nonatomic, strong) WRBook *book; // 书籍模型 @property (nonatomic, strong) WHAlbumInfo *albumInfo; // 专辑信息 // 解析 EPUB 结构 - (BOOL)parseEpubAtPath:(NSString *)path error:(NSError **)error; // 获取章节目录 (NCX) - (NSArray *)tableOfContents; // 获取资源文件映射 (manifest) - (NSDictionary *)resourceManifest; // 获取 XHTML 章节文件路径 - (NSString *)filePathForChapterWithId:(NSString *)chapterId; @end // ============================================================ #pragma mark - WRBook (书籍模型) // ============================================================ @interface WRBook : NSObject @property (nonatomic, copy) NSString *bookId; // 书籍 ID @property (nonatomic, copy) NSString *title; // 书名 @property (nonatomic, copy) NSString *author; // 作者 @property (nonatomic, copy) NSString *cover; // 封面 URL @property (nonatomic, assign) WRBookFormat format; // epub/pdf/mp @property (nonatomic, strong) NSArray *chapters; // 章节列表 @property (nonatomic, copy) NSString *encryptKey; // 加密密钥 @property (nonatomic, assign) BOOL isFinished; // 是否读完 @property (nonatomic, assign) BOOL isVIP; // 是否 VIP 书籍 typedef NS_ENUM(NSInteger, WRBookFormat) { WRBookFormatEpub = 0, WRBookFormatPDF = 1, WRBookFormatMP = 2, // 公众号文章 }; @end // ============================================================ #pragma mark - WRChapter (章节模型) // ============================================================ @interface WRChapter : NSObject @property (nonatomic, copy) NSString *chapterUid; // 章节唯一 ID @property (nonatomic, assign) NSInteger chapterIdx; // 章节索引 @property (nonatomic, copy) NSString *title; // 章节标题 @property (nonatomic, assign) WRChapterFormat format; // 章节格式 @property (nonatomic, copy) NSString *fileId; // 文件 ID @property (nonatomic, assign) NSUInteger filePosition; // 文件位置 @property (nonatomic, assign) NSUInteger fileSize; // 文件大小 @property (nonatomic, assign) BOOL isPaid; // 是否付费章节 @property (nonatomic, assign) BOOL isAvailable; // 是否可用 typedef NS_ENUM(NSInteger, WRChapterFormat) { WRChapterFormatXHTML = 0, WRChapterFormatHTML = 1, WRChapterFormatTXT = 2, }; @end ``` ### 1.2 排版数据结构 ```objc // ============================================================ #pragma mark - DTHTMLElement (HTML 元素模型, 38 ivars) // ============================================================ @interface DTHTMLElement : NSObject // 树结构 @property (nonatomic, weak) DTHTMLElement *parent; // 父元素 @property (nonatomic, strong) NSMutableArray *children; // 子元素 @property (nonatomic, copy) NSString *tagName; // 标签名 (p, h1, img...) @property (nonatomic, copy) NSString *elementId; // id 属性 @property (nonatomic, copy) NSString *className; // class 属性 // 样式 @property (nonatomic, strong) DTCoreTextFontDescriptor *fontDescriptor; // 字体 @property (nonatomic, strong) DTCoreTextParagraphStyle *paragraphStyle; // 段落 @property (nonatomic, strong) UIColor *textColor; // 文字颜色 @property (nonatomic, strong) UIColor *backgroundColor; // 背景色 @property (nonatomic, assign) CGFloat textScale; // 文字缩放 @property (nonatomic, assign) CGFloat letterSpacing; // 字间距 // 附件 @property (nonatomic, strong) DTTextAttachment *textAttachment; // 附件 (图片/视频) // 链接 @property (nonatomic, strong) NSURL *linkURL; // 超链接地址 // CSS 样式字典 @property (nonatomic, strong) NSDictionary *cssStyles; // 应用的 CSS 样式 // 边框与背景 @property (nonatomic, assign) DTBorderStyle borderTop; // 上边框 @property (nonatomic, assign) DTBorderStyle borderBottom; // 下边框 @property (nonatomic, strong) UIColor *borderColor; // 边框颜色 @property (nonatomic, strong) DTBackgroundImageStyle *backgroundImage; // 背景图 // 表格 @property (nonatomic, strong) DTTableStyle *tableStyle; // 表格样式 // 读书自定义属性 @property (nonatomic, assign) NSInteger verticalCenterStyle; // wr-vertical-center-style @property (nonatomic, assign) BOOL pageRelate; // weread-page-relate @property (nonatomic, assign) BOOL avoidPageBreakInside; // 断页保护 @property (nonatomic, assign) BOOL pageBreakAfter; // 元素后分页 @property (nonatomic, assign) BOOL pageBreakBefore; // 元素前分页 @property (nonatomic, strong) UIColor *pageBackgroundColor; // 页面背景色 @property (nonatomic, strong) NSURL *pageBackgroundImage; // 页面背景图 // 翻译 @property (nonatomic, assign) BOOL isTranslateTag; // 翻译标签 @property (nonatomic, assign) BOOL isTranslateNoStyle; // 无翻译样式 // 核心方法 - (void)applyStyleDictionary:(NSDictionary *)styles isLatinLanguageBook:(BOOL)isLatin; - (NSAttributedString *)attributedString; - (void)interpretAttributes; @end // ============================================================ #pragma mark - DTCoreTextFontDescriptor (字体描述) // ============================================================ @interface DTCoreTextFontDescriptor : NSObject @property (nonatomic, copy) NSString *fontFamily; // 字体族 (如 "Source Han Serif CN") @property (nonatomic, copy) NSString *fontName; // 字体名 (如 "SourceHanSerifCN-Medium") @property (nonatomic, assign) CGFloat pointSize; // 字号 (pt) @property (nonatomic, assign) BOOL bold; // 粗体 @property (nonatomic, assign) BOOL italic; // 斜体 @property (nonatomic, assign) uint32_t symbolicTraits; // 符号特征 // 匹配系统字体 - (CTFontRef)matchedFontDescriptor; @end // ============================================================ #pragma mark - DTCoreTextParagraphStyle (段落样式) // ============================================================ @interface DTCoreTextParagraphStyle : NSObject @property (nonatomic, assign) CTTextAlignment alignment; // 对齐方式 @property (nonatomic, assign) CGFloat lineSpacing; // 行间距 @property (nonatomic, assign) CGFloat paragraphSpacing; // 段间距 @property (nonatomic, assign) CGFloat paragraphSpacingBefore; // 段前间距 @property (nonatomic, assign) CGFloat firstLineHeadIndent; // 首行缩进 @property (nonatomic, assign) CGFloat headIndent; // 左缩进 @property (nonatomic, assign) CGFloat tailIndent; // 右缩进 @property (nonatomic, assign) CGFloat minimumLineHeight; // 最小行高 @property (nonatomic, assign) CGFloat maximumLineHeight; // 最大行高 @property (nonatomic, assign) CGFloat lineHeightMultiple; // 行高倍数 // 读书扩展 @property (nonatomic, assign) CGFloat defaultTabInterval; // 默认制表位 @end // ============================================================ #pragma mark - DTTextAttachment (文本附件) // ============================================================ @interface DTTextAttachment : NSObject @property (nonatomic, assign) DTTextAttachmentType contentType; // 附件类型 @property (nonatomic, strong) NSData *contents; // 内容数据 @property (nonatomic, strong) NSURL *contentURL; // 内容 URL @property (nonatomic, assign) CGSize displaySize; // 显示尺寸 @property (nonatomic, assign) CGSize originalSize; // 原始尺寸 @property (nonatomic, assign) CGFloat verticalAlignment; // 垂直对齐 typedef NS_ENUM(NSInteger, DTTextAttachmentType) { DTTextAttachmentTypeImage = 0, DTTextAttachmentTypeVideo = 1, DTTextAttachmentTypeIframe = 2, DTTextAttachmentTypeObject = 3, }; @end // ============================================================ #pragma mark - DTCSSStylesheet (CSS 样式表) // ============================================================ @interface DTCSSStylesheet : NSObject @property (nonatomic, strong) NSDictionary *rules; // rules 格式: { "selector": { "property": "value", ... }, ... } // 从 CSS 文本解析 + (DTCSSStylesheet *)styleSheetWithCSSString:(NSString *)cssString; // 合并另一个样式表 (后者覆盖前者) - (void)mergeStylesheet:(DTCSSStylesheet *)other; // 获取匹配选择器的样式 - (NSDictionary *)stylesForElement:(DTHTMLElement *)element; @end ``` ### 1.3 渲染数据结构 ```objc // ============================================================ #pragma mark - WRChapterData (章节数据模型, 12 ivars) // ============================================================ @interface WRChapterData : NSObject // 排版结果 @property (nonatomic, strong) NSMutableAttributedString *attributedString; // 排版后富文本 @property (nonatomic, strong) WRCoreTextLayouter *layouter; // 排版器 // 分页 @property (nonatomic, strong) NSArray *pageRanges; // 每页 NSRange // 标注 @property (nonatomic, strong) NSArray *highlights; // 高亮列表 @property (nonatomic, strong) NSArray *underlines; // 下划线列表 @property (nonatomic, strong) NSArray *marks; // 书签标注 @property (nonatomic, strong) NSArray *reviews; // 书评列表 @property (nonatomic, strong) NSArray *tempHighlights; // 临时高亮 @property (nonatomic, strong) NSArray *tempUnderlines; // 临时下划线 // 目录与搜索 @property (nonatomic, strong) NSArray *outlineContents; // 目录 @property (nonatomic, strong) NSArray *searchResults; // 搜索结果 // 书签 @property (nonatomic, strong) NSSet *bookmarkSet; // 书签集合 // 原始数据 @property (nonatomic, strong) NSAttributedString *originalString; // 原始富文本 // 核心方法 - (NSRange)rangeOfPage:(NSInteger)pageIndex; - (void)addHighlightInRange:(NSRange)range key:(NSString *)key itemId:(NSString *)itemId color:(UIColor *)color; - (void)addReviewUnderlineInRange:(NSRange)range itemId:(NSString *)itemId type:(WRReviewType)type; - (void)deleteReviewUnderlineInRange:(NSRange)range type:(WRReviewType)type; - (NSArray *)generateOutlineContents; - (NSInteger)freeTrialChapterCutOffRealStringLocation; + (void)addUnderLineToAttributedString:(NSMutableAttributedString *)attrStr range:(NSRange)range itemId:(NSString *)itemId style:(WRUnderlineStyle)style color:(UIColor *)color; + (NSInteger)freeTrialChapterCutOffStringLocaionWithAttributedString: (NSAttributedString *)attrStr book:(WRBook *)book; @end // ============================================================ #pragma mark - WRChapterPageCount (分页计算, 4 ivars) // ============================================================ @interface WRChapterPageCount : NSObject @property (nonatomic, copy) NSString *bookId; // 书籍 ID @property (nonatomic, copy) NSString *chapterUid; // 章节 ID @property (nonatomic, copy) NSString *cacheKey; // 缓存键 @property (nonatomic, strong) NSArray *pageRanges; // 页范围 + (NSString *)currentCacheKeyWithBookId:(NSString *)bookId; + (NSValue *)rangeValueWithPageInfo:(NSDictionary *)pageInfo; @end ``` ### 1.4 标注数据结构 ```objc // ============================================================ #pragma mark - WRPageHighlight (页面高亮) // ============================================================ @interface WRPageHighlight : NSObject @property (nonatomic, assign) NSRange range; // 高亮范围 @property (nonatomic, copy) NSString *highlightKey; // 高亮唯一键 @property (nonatomic, copy) NSString *itemId; // 标注 ID @property (nonatomic, strong) UIColor *color; // 高亮颜色 @property (nonatomic, assign) BOOL isTemporary; // 是否临时 @end // ============================================================ #pragma mark - WRPageUnderline (页面下划线) // ============================================================ @interface WRPageUnderline : NSObject @property (nonatomic, assign) NSRange range; // 下划线范围 @property (nonatomic, copy) NSString *itemId; // 标注 ID @property (nonatomic, assign) WRUnderlineStyle style; // 下划线样式 @property (nonatomic, strong) UIColor *color; // 颜色 typedef NS_ENUM(NSInteger, WRUnderlineStyle) { WRUnderlineStyleSolid = 0, // 实线 WRUnderlineStyleDashed = 1, // 虚线 WRUnderlineStyleWavy = 2, // 波浪线 WRUnderlineStyleDotted = 3, // 点线 }; @end // ============================================================ #pragma mark - WRPageMark (页面标注, 25 methods) // ============================================================ @interface WRPageMark : NSObject @property (nonatomic, assign) NSRange range; // 标注范围 @property (nonatomic, assign) WRMarkType type; // 标注类型 @property (nonatomic, copy) NSString *content; // 标注内容 @property (nonatomic, copy) NSString *itemId; // 标注 ID @property (nonatomic, strong) NSDate *createTime; // 创建时间 @property (nonatomic, strong) UIColor *color; // 颜色 typedef NS_ENUM(NSInteger, WRMarkType) { WRMarkTypeHighlight = 0, // 高亮 WRMarkTypeUnderline = 1, // 下划线 WRMarkTypeBookmark = 2, // 书签 WRMarkTypeNote = 3, // 笔记 WRMarkTypeReview = 4, // 书评 }; @end // ============================================================ #pragma mark - WRBookmark (书签模型, 25 ivars) // ============================================================ @interface WRBookmark : NSObject @property (nonatomic, copy) NSString *bookId; // 书籍 ID @property (nonatomic, copy) NSString *chapterUid; // 章节 ID @property (nonatomic, copy) NSString *itemId; // 书签 ID @property (nonatomic, assign) NSUInteger rangeLocation; // 范围起始 @property (nonatomic, assign) NSUInteger rangeLength; // 范围长度 @property (nonatomic, copy) NSString *abstractText; // 摘要文本 @property (nonatomic, assign) WRBookmarkType type; // 书签类型 @property (nonatomic, strong) UIColor *color; // 颜色 @property (nonatomic, assign) WRUnderlineStyle style; // 下划线样式 @property (nonatomic, copy) NSString *reviewId; // 关联书评 ID @property (nonatomic, strong) NSDate *createTime; // 创建时间 @property (nonatomic, strong) NSDate *updateTime; // 更新时间 typedef NS_ENUM(NSInteger, WRBookmarkType) { WRBookmarkTypeHighlight = 0, WRBookmarkTypeUnderline = 1, WRBookmarkTypeBookmark = 2, WRBookmarkTypeNote = 3, WRBookmarkTypeReview = 4, }; @end ``` ### 1.5 附件数据结构 ```objc // ============================================================ #pragma mark - WRPageImageAttachment (图片附件) // ============================================================ @interface WRPageImageAttachment : NSObject @property (nonatomic, copy) NSString *imageURL; // 图片 URL @property (nonatomic, strong) UIImage *image; // 图片对象 @property (nonatomic, assign) CGSize displaySize; // 显示尺寸 @property (nonatomic, assign) CGSize originalSize; // 原始尺寸 @property (nonatomic, assign) NSInteger position; // 在文本中的位置 @property (nonatomic, assign) NSInteger verticalCenterStyle; // 居中方式 @property (nonatomic, assign) BOOL hasWhiteBackground; // 是否白底 @end // ============================================================ #pragma mark - WRPageHyperlinksAttachment (超链接附件) // ============================================================ @interface WRPageHyperlinksAttachment : NSObject @property (nonatomic, strong) NSURL *url; // 链接 URL @property (nonatomic, assign) NSRange range; // 文本范围 @property (nonatomic, copy) NSString *displayText; // 显示文本 @end // ============================================================ #pragma mark - WRPageChapterToolAttachment (章节工具附件) // ============================================================ @interface WRPageChapterToolAttachment : NSObject @property (nonatomic, assign) WRChapterToolType type; // 工具类型 @property (nonatomic, strong) NSDictionary *data; // 工具数据 typedef NS_ENUM(NSInteger, WRChapterToolType) { WRChapterToolTypeShare = 0, // 分享 WRChapterToolTypeReview = 1, // 书评 WRChapterToolTypeBookmark = 2, // 书签 WRChapterToolTypeNext = 3, // 下一章 }; @end // ============================================================ #pragma mark - WRPageFlyleafAttachment (扉页附件) // ============================================================ @interface WRPageFlyleafAttachment : NSObject @property (nonatomic, assign) WRFlyleafType type; // 扉页类型 @property (nonatomic, strong) NSDictionary *data; // 扉页数据 typedef NS_ENUM(NSInteger, WRFlyleafType) { WRFlyleafTypeCover = 0, // 封面 WRFlyleafTypeTitle = 1, // 标题页 WRFlyleafTypeAuthor = 2, // 作者页 WRFlyleafTypeIntro = 3, // 简介页 WRFlyleafTypeCatalog = 4, // 目录页 }; @end // ============================================================ #pragma mark - WRPageCodeAttachment (代码块附件) // ============================================================ @interface WRPageCodeAttachment : NSObject @property (nonatomic, copy) NSString *codeString; // 代码内容 @property (nonatomic, copy) NSString *language; // 编程语言 @property (nonatomic, assign) NSInteger lineNumber; // 起始行号 @end // ============================================================ #pragma mark - WRPageTableAttachment (表格附件) // ============================================================ @interface WRPageTableAttachment : NSObject @property (nonatomic, assign) NSInteger rows; // 行数 @property (nonatomic, assign) NSInteger columns; // 列数 @property (nonatomic, strong) NSArray *> *cells; // 单元格数据 @end ``` --- ## 二、API 与网络协议控制矩阵 ### 2.1 书籍信息 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork loadBookInfoWithBookId: /api/book/info GET │ │ source:callback:] ?bookId=&source= │ │ │ │ +[WRBookNetwork loadBookReadInfoWithBookId: /api/book/readInfo GET │ │ callback:] ?bookId= │ │ │ │ +[WRBookNetwork loadBookReadInfoWithBookIdAndVid: /api/book/readInfo GET │ │ vid:callback:] ?bookId=&vid= │ │ │ │ +[WRBookNetwork loadBookReadDetailInfoWithBookId: /api/book/readDetail GET │ │ callback:] ?bookId= │ │ │ │ +[WRBookNetwork loadBookLectureAuthors: /api/book/authors GET │ │ callback:] ?bookId= │ │ │ │ +[WRBookNetwork loadArticleBookDetailWithBookId: /api/article/detail GET │ │ callback:] ?bookId= │ │ │ │ +[WRBookNetwork fetchLockInfoWithBookId: /api/book/lockInfo GET │ │ callback:] ?bookId= │ └────────────────────────────────────────────────────────────────────────────────┘ 响应格式 (推断): { "bookId": "string", "title": "string", "author": "string", "cover": "string (URL)", "format": 0, "chapters": [ { "chapterUid": "string", "chapterIdx": 0, "title": "string", "fileId": "string", "filePosition": 0, "fileSize": 0, "isPaid": false } ], "encryptKey": "string", "isFinished": false, "isVIP": false } ``` ### 2.2 章节内容 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork loadTarForEpubBookId:chapter: /api/book/tar GET │ │ isPreload:] ?bookId=&chapterUid= │ │ &isPreload= │ │ 响应: 加密 ZIP 文件 (application/octet-stream) │ │ 文件名: {bookId}_DECRYPT.zip │ │ │ │ +[WRBookNetwork loadTarForEpubBookId:chapters: /api/book/tar GET │ │ isPreload:] ?bookId=&chapterUids= │ │ &isPreload= │ │ 批量下载: chapters 逗号分隔 │ │ │ │ +[WRBookNetwork loadChapterContentWithParam: /api/book/chapter GET │ │ callback:] ?bookId=&chapterUid= │ │ 参数: NSDictionary (bookId, chapterUid, format) │ │ │ │ +[WRBookNetwork fileContentWithChapter:book: 本地文件读取 - │ │ shouldRemoveHtmlTags:filterTranslateContent:] 从 plainBookDirectory 读取 │ │ │ │ +[WRBookNetwork chaptersInfoFromFile: 本地文件解析 - │ │ checkTranslate:] 解析 OPF/NCX │ └────────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.3 预加载管理 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork savePreloadInfoWithDownloadParam: 本地存储 - │ │ chaptersStr:timeFlag:tmpFilePath:encryptKey:] NSUserDefaults │ │ │ │ +[WRBookNetwork clearPreloadKVWithBookId: 本地清理 - │ │ chapterUid:zipPath:] NSUserDefaults │ │ │ │ +[WRBookNetwork storeTarEpubImageToDiskWith 本地存储 - │ │ BookId:chapterUid:untarDirectory:] epubImage/ 目录 │ │ │ │ +[WRBookNetwork processEncryptedBookFileAtPath: 本地处理 - │ │ encryptKey:book:chapterUid:isFromReview:] 解密 + 存储 │ │ │ │ +[WRBookNetwork processChapterInfosFrom 本地处理 - │ │ ChapterDownload:bookId:] 解析章节信息 │ └────────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.4 书签/标注 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork loadBookmarkListWithBookId: /api/book/bookmarks GET │ │ syncKey:callback:] ?bookId=&syncKey= │ │ │ │ +[WRBookNetwork addReview:shareToWechat: /api/review/add POST │ │ audioArticleId:outlineContent: body: {bookId, chapterUid, │ │ audioColumnId:callback:] content, range, type, ...} │ │ │ │ +[WRBookNetwork likeReviewById:isUnlike: /api/review/like POST │ │ withParams:callback:] body: {reviewId, isUnlike} │ │ │ │ +[WRBookNetwork dislikeReviewById:isDislike: /api/review/dislike POST │ │ withParams:callback:] body: {reviewId, isDislike} │ │ │ │ +[WRBookNetwork repostReview:reposted: /api/review/repost POST │ │ callback:] body: {reviewId, reposted} │ │ │ │ +[WRBookNetwork rewardReviewForId:price: /api/review/reward POST │ │ timestamp:callback:] body: {reviewId, price, ts} │ │ │ │ +[WRBookNetwork postReviewHideWithBookId: /api/review/hide POST │ │ hide:callback:] body: {bookId, hide} │ │ │ │ +[WRBookNetwork loadTopicReviewlist: /api/review/topic GET │ │ callback:] ?topicId= │ │ │ │ +[WRBookNetwork loadRelatedBooksForReviewDetail: /api/review/related GET │ │ callback:] ?reviewId= │ └────────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.5 阅读进度 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork uploadBookProgressAndReadingTime: /api/book/progress POST │ │ callback:offlineCallback:] body: {bookId, chapterUid, │ │ progress, readingTime, ...} │ │ │ │ +[WRBookNetwork setFinishReading:withBookId: /api/book/finish POST │ │ callback:] body: {bookId, isFinished} │ │ │ │ +[WRBookNetwork setIsStartReading:withBookId: /api/book/start POST │ │ callback:] body: {bookId} │ │ │ │ +[WRBookNetwork markReadingStatus:bookIds: /api/book/status POST │ │ isCancel:callback:] body: {bookIds[], isCancel} │ │ │ │ +[WRBookNetwork markReadingStatus:withBookId: /api/book/status POST │ │ isCancel:withFinishInfo:callback:] body: {bookId, isCancel, │ │ finishInfo} │ │ │ │ +[WRBookNetwork automaticallyMarkFinishReading /api/book/autoFinish POST │ │ WithBookId:callback:] body: {bookId} │ │ │ │ +[WRBookNetwork addMileStone:callback:] /api/book/milestone POST │ │ body: {bookId, milestone} │ └────────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.6 搜索 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork searchResultsForBook: /api/search GET │ │ chapterUid:searchString:posBeg: ?bookId=&chapterUid= │ │ posEnd:mode:callback:] &keyword=&posBeg= │ │ &posEnd=&mode= │ │ mode: 0=精确 1=模糊 2=正则 │ │ │ │ +[WRBookNetwork searchResultsForLocalBook: 本地搜索 - │ │ chapterUid:searchString:posBeg: 遍历 NSAttributedString │ │ posEnd:mode:callback:] │ └────────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.7 翻译 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork pollingChapterTranslateStatus /api/book/translate GET │ │ WithBookId:isFreeTrialActive: ?bookId=&chapterUid= │ │ referenceLocationDict:chapterTranslations: &isFreeTrial= │ │ from:] │ │ │ │ +[WRBookNetwork _removeTranslateHtml:] 本地处理 - │ │ 移除翻译 HTML 标签 │ └────────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.8 付费/会员 API ``` ┌────────────────────────────────────────────────────────────────────────────────┐ │ 方法签名 推断端点 HTTP 方法 │ ├────────────────────────────────────────────────────────────────────────────────┤ │ +[WRBookNetwork fetchLockInfoWithBookId: /api/book/lock GET │ │ callback:] ?bookId= │ │ │ │ +[WRBookNetwork resetChapterPaidIfNeeded /api/book/resetPaid POST │ │ WithBookId:chapterUid:] body: {bookId, chapterUid} │ │ │ │ +[WRBookNetwork loadReadTimeWelfareAction /api/welfare/readTime GET │ │ WithBookId:opt:secretKey:firstEnter:] ?bookId=&opt=&secretKey= │ │ │ │ +[WRBookNetwork checkFMCards:callback:] /api/fm/cards GET │ │ │ │ +[WRBookNetwork loadFMCardsWithBookId: /api/fm/cards GET │ │ withSynckey:withListType:withFilterType: ?bookId=&synckey= │ │ withMaxIdx:withCount:] &listType=&filterType= │ └────────────────────────────────────────────────────────────────────────────────┘ ``` --- ## 三、本地存储 Schema ### 3.1 文件系统布局 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ App 沙箱 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ Documents/ │ │ ├── {bookId}/ │ │ │ ├── plainBookDirectory/ ← 解密后的 EPUB XHTML 文件 │ │ │ │ ├── META-INF/container.xml │ │ │ │ ├── OEBPS/content.opf │ │ │ │ ├── OEBPS/toc.ncx │ │ │ │ ├── OEBPS/Text/chapter1.xhtml │ │ │ │ ├── OEBPS/Text/chapter2.xhtml │ │ │ │ └── ... │ │ │ ├── epubImage/ ← 书籍图片缓存 │ │ │ │ ├── image1.jpg │ │ │ │ ├── image2.png │ │ │ │ └── ... │ │ │ └── {bookId}_DECRYPT.zip ← 下载的加密 ZIP (可能已删除) │ │ │ │ │ └── ... (其他书籍) │ │ │ │ Library/ │ │ ├── {cachePath}/ │ │ │ └── epubImage/ ← SDWebImage 缓存 │ │ │ └── com.hackemist.SDWebImageCache.epubImage/ │ │ ├── Caches/ │ │ │ └── chapterCache/ ← 章节排版缓存 │ │ └── Preferences/ │ │ └── com.tencent.weread.plist ← NSUserDefaults │ │ │ │ tmp/ │ │ └── {临时解压目录} ← 下载解压临时目录 │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` ### 3.2 NSUserDefaults Keys ```objc // 阅读进度 NSString *const kWRReadingProgressKey = @"WRReadingProgress_{bookId}"; // 格式: {chapterUid, chapterIdx, filePosition, stringLocation, pageOfChapter, lastReadDate} // 字体设置 NSString *const kWRFontFamilyKey = @"WRFontFamily"; // 字体族名 NSString *const kWRFontSizeKey = @"WRFontSize"; // 字号 (默认 18) NSString *const kWRLineHeightKey = @"WRLineHeight"; // 行高 (默认 1.8) NSString *const kWRParagraphSpacingKey = @"WRParagraphSpacing"; // 段间距 NSString *const kWRFirstIndentKey = @"WRFirstIndent"; // 首行缩进 // 主题设置 NSString *const kWRThemeKey = @"WRTheme"; // 主题 (light/dark/sepia) NSString *const kWRBrightnessKey = @"WRBrightness"; // 亮度 // 翻页设置 NSString *const kWRPageTurningStyleKey = @"WRPageTurningStyle"; // 翻页样式 (curl/scroll) NSString *const kWRAutoReadKey = @"WRAutoRead"; // 自动阅读 // 繁简转换 NSString *const kWRCht2sKey = @"WRCht2s_{bookId}"; // 繁简转换状态 // 预加载 NSString *const kWRPreloadSettingKey = @"WRPreloadSetting"; // 预加载设置 NSString *const kWRPreloadEncryptKeyKey = @"WRPreloadEncryptKey_{bookId}_{chapterUid}"; NSString *const kWRPreloadFileNameKey = @"WRPreloadFileName_{bookId}_{key}"; ``` ### 3.3 Keychain Keys ```objc // 每本书的加密密钥 NSString *const kWRBookEncryptKeyKey = @"com.weread.encrypt.{bookId}"; // 存储在 Keychain 中, 标记为 kSecAttrAccessibleAfterFirstUnlock // 用户凭证 NSString *const kWRUserTokenKey = @"com.weread.user.token"; NSString *const kWRUserVidKey = @"com.weread.user.vid"; ``` ### 3.4 NSAttributedString 自定义属性键 ```objc // 读书在 NSAttributedString 中注入的自定义属性 // 用于在排版结果中传递页面布局元数据 NSString *const DTPageBackgroundColorAttribute = @"DTPageBackgroundColor"; NSString *const DTPageBackgroundImageAttribute = @"DTPageBackgroundImage"; NSString *const DTPageBackgroundImagePathAttribute = @"DTPageBackgroundImagePath"; NSString *const DTPageBreakAfterAttribute = @"DTPageBreakAfter"; NSString *const DTPageBreakBeforeAttribute = @"DTPageBreakBefore"; NSString *const DTPageBreakInsideAvoidAttribute = @"DTPageBreakInsideAvoid"; NSString *const DTPageRelateAttribute = @"DTPageRelate"; NSString *const DTPageSize = @"DTPageSize"; NSString *const DTPageFlippingStyle = @"DTPageFlippingStyle"; NSString *const DTHTMLVerticalCenterAttribute = @"DTHTMLVerticalCenter"; NSString *const DTHTMLTranslateTagAttribute = @"DTHTMLTranslateTag"; NSString *const DTHTMLTranslateNoStyleAttribute = @"DTHTMLTranslateNoStyle"; // 标注相关属性 NSString *const WRHighlightAttributeKey = @"com.weread.highlight"; NSString *const WRUnderlineAttributeKey = @"com.weread.underline"; NSString *const WRBookmarkAttributeKey = @"com.weread.bookmark"; NSString *const WRReviewAttributeKey = @"com.weread.review"; // 附件属性 NSString *const DTTextAttachmentAttribute = @"DTTextAttachment"; NSString *const WRChapterToolAttachmentAttribute = @"WRChapterToolAttachment"; NSString *const WRFlyleafAttachmentAttribute = @"WRFlyleafAttachment"; ```