ReadViewSDK/Doc/WXRead/analysis/03_数据结构与API协议定义.md
2026-05-21 19:40:51 +08:00

45 KiB

微信读书 EPUB 阅读器 — 数据结构与 API 协议定义

基于 WeRead v10.0.3 (Build 79) 逆向工程 数据结构来源: __objc_methtype + class_ro_t ivar 段 API 来源: WRBookNetwork 44 个 class method 签名


一、关键数据结构定义 (Header Files)

1.1 EPUB 解析数据结构

// ============================================================
#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<NSDictionary *> *)tableOfContents;

// 获取资源文件映射 (manifest)
- (NSDictionary<NSString *, NSString *> *)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<WRChapter *> *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 排版数据结构

// ============================================================
#pragma mark - DTHTMLElement (HTML 元素模型, 38 ivars)
// ============================================================

@interface DTHTMLElement : NSObject

// 树结构
@property (nonatomic, weak)   DTHTMLElement *parent;       // 父元素
@property (nonatomic, strong) NSMutableArray<DTHTMLElement *> *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<NSString *, NSDictionary *> *rules;
// rules 格式: { "selector": { "property": "value", ... }, ... }

// 从 CSS 文本解析
+ (DTCSSStylesheet *)styleSheetWithCSSString:(NSString *)cssString;

// 合并另一个样式表 (后者覆盖前者)
- (void)mergeStylesheet:(DTCSSStylesheet *)other;

// 获取匹配选择器的样式
- (NSDictionary *)stylesForElement:(DTHTMLElement *)element;

@end

1.3 渲染数据结构

// ============================================================
#pragma mark - WRChapterData (章节数据模型, 12 ivars)
// ============================================================

@interface WRChapterData : NSObject

// 排版结果
@property (nonatomic, strong) NSMutableAttributedString *attributedString;  // 排版后富文本
@property (nonatomic, strong) WRCoreTextLayouter *layouter;                 // 排版器

// 分页
@property (nonatomic, strong) NSArray<NSValue *> *pageRanges;   // 每页 NSRange

// 标注
@property (nonatomic, strong) NSArray<WRPageHighlight *> *highlights;     // 高亮列表
@property (nonatomic, strong) NSArray<WRPageUnderline *> *underlines;     // 下划线列表
@property (nonatomic, strong) NSArray<WRPageMark *>     *marks;           // 书签标注
@property (nonatomic, strong) NSArray<NSDictionary *>   *reviews;         // 书评列表
@property (nonatomic, strong) NSArray *tempHighlights;                     // 临时高亮
@property (nonatomic, strong) NSArray *tempUnderlines;                     // 临时下划线

// 目录与搜索
@property (nonatomic, strong) NSArray<NSDictionary *> *outlineContents;   // 目录
@property (nonatomic, strong) NSArray<NSDictionary *> *searchResults;     // 搜索结果

// 书签
@property (nonatomic, strong) NSSet<NSString *> *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<NSDictionary *> *)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<NSValue *> *pageRanges;  // 页范围

+ (NSString *)currentCacheKeyWithBookId:(NSString *)bookId;
+ (NSValue *)rangeValueWithPageInfo:(NSDictionary *)pageInfo;

@end

1.4 标注数据结构

// ============================================================
#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 附件数据结构

// ============================================================
#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<NSArray<NSString *> *> *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

// 阅读进度
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

// 每本书的加密密钥
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 自定义属性键

// 微信读书在 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";