6.1 KiB
6.1 KiB
完成页「最终修正版」代码审查报告(禁止应用)
审查对象:基于零影响审查报告修正后的 CompletionView + VerticalScreenPlayerView 最终版(参数优先、错误 Toast、排序逻辑、接口兼容)。
结论:仅审查,不修改仓库内任何文件。核对三项修正与 6 条零影响条件,并说明应用时的合并范围。
一、三项修正落实情况
| 修正项 | 审查报告要求 | 本版代码 | 结论 |
|---|---|---|---|
| 参数优先权 | LessonPageView 传 courseTitle ?? mapData?.courseTitle |
courseTitle: self.courseTitle ?? mapData?.courseTitle(LessonPageView 与 CompletionView 均用) |
✅ 已落实 |
| 错误处理 | loadMapData 失败时 showToastMessage("加载失败") |
catch 内 self.showToastMessage("加载失败") |
✅ 已落实 |
| 排序逻辑 | isFirstNodeInChapter 用 validNodes.sorted { $0.order < $1.order } 再取 first |
validNodes = chapter.nodes.filter(...).sorted { $0.order < $1.order },再 validNodes.first?.id == nodeId |
✅ 已落实 |
二、6 条零影响条件核对
| # | 条件 | 本版代码 | 结论 |
|---|---|---|---|
| 1 | VerticalScreenPlayerView init 保持 6 参 | init(courseId, nodeId, initialScrollIndex, navigationPath?, isLastNode?, courseTitle?) 完整 |
✅ |
| 2 | 保留 CourseNavigation.completion 及三处 destination | 未改枚举与三 Tab;CompletionView 三参 | ✅ |
| 3 | CompletionView 保留三参 | courseId, courseTitle, completedLessonCount | ✅ |
| 4 | LessonPageView 传 courseTitle、navigationPath | self.courseTitle ?? mapData?.courseTitle,navigationPath 透传 |
✅ |
| 5 | 保留 loadError、toast、hideTabBar/showTabBar、handleBack(path 优先) | 错误态、toast、tabBar、handleBack 均保留;loadMapData 失败有 showToastMessage | ✅ |
| 6 | NoteTreeView / NoteListView 不修改 | init 未改,笔记流仍只传 3 参 | ✅ |
三、CompletionView 审查
- 接口:courseId / courseTitle / completedLessonCount 全保留,与 CourseNavigation.completion 及三处 destination 一致。✅
- 内部:纯 UI(粉紫勋章)+ handleReturnToRoot() → navStore.switchToGrowthTab(),无 navigationPath、无侧滑 Hack。✅
- 视觉:顶部「已完成」、底部「回到我的内容」淡蓝、无返回按钮。✅
结论:CompletionView 可直接全量替换 Views/CompletionView.swift。
四、VerticalScreenPlayerView 审查与合并范围
4.1 本版已包含且正确的部分
- Init 6 参、PlayerItem 枚举、allItems 数据源、loadMapData(realNodes + append .completion)、currentPositionProgress(仅 lesson)、handleBack(path 优先)、hideTabBar/showTabBar、showToastMessage、错误态 UI、toast、isFirstNodeInChapter(含 .sorted { $0.order < $1.order })、getChapterTitle。✅
4.2 应用时必须注意的合并范围(未应用,仅说明)
当前 VerticalScreenPlayerView.swift 文件中除 VerticalScreenPlayerView 结构体外,还包含:
- HeaderConfig、DuolingoProgressBar、CourseProgressNavBar(播放器依赖)
- CompletionPlaceholderPage 或 LastPageSwipeModifier(本方案中不再需要,可删除)
- LessonPageView、LessonSkeletonView、LessonErrorView 及后续所有类型与扩展
你提供的「最终修正版」只包含 VerticalScreenPlayerView 结构体 及 PlayerItem 枚举,未包含上述类型。因此:
- 不能用该片段整文件覆盖 VerticalScreenPlayerView.swift,否则会删掉 HeaderConfig、CourseProgressNavBar、LessonPageView 等,导致编译失败。
- 正确做法:在 VerticalScreenPlayerView.swift 内只做「局部替换」:
- 用本版的 VerticalScreenPlayerView 结构体(含 PlayerItem、body、loadMapData、handleBack、isFirstNodeInChapter 等)替换现有的 VerticalScreenPlayerView 结构体;
- 删除 CompletionPlaceholderPage(或占位页相关逻辑),不再添加 LastPageSwipeModifier;
- 保留 文件内 HeaderConfig、DuolingoProgressBar、CourseProgressNavBar、LessonPageView、LessonSkeletonView、LessonErrorView 及之后所有内容不变。
4.3 realNodes 的 .sorted 与当前行为差异(可选核对)
- 本版:
let realNodes = data.chapters.flatMap { $0.nodes }.filter { $0.status != .locked }.sorted { $0.order < $1.order }
即对 整课 的节点列表按node.order做一次全局排序。 - 当前实现:
allCourseNodes = data.chapters.flatMap { $0.nodes }.filter { $0.status != .locked }
即 不 对 flatMap 后的列表排序,顺序为:先 chapter 顺序,再各 chapter 内 nodes 的数组顺序。
若后端 node.order 是 按章节内 的(例如每章都是 0,1,2,…),则对整课 flat 列表只按 order 排序会 打乱章节顺序(例如把各章 order=0 的节点排在一起)。若希望与当前行为完全一致,可考虑:
- 要么 去掉 realNodes 的
.sorted { $0.order < $1.order },保持与当前一致的「章节顺序 + 章内数组顺序」; - 要么在确认后端
order为全局唯一或全局有序的前提下保留当前排序。
是否保留该 sort,可根据产品/后端约定决定;不影响三项修正与 6 条零影响条件。
五、审查结论汇总
| 项目 | 结论 |
|---|---|
| 三项修正 | 参数优先权、错误 Toast、isFirstNodeInChapter 排序均已落实。 |
| 6 条零影响 | 全部满足;对外接口与调用方无需改动。 |
| CompletionView | 可直接全量替换 ios/WildGrowth/WildGrowth/Views/CompletionView.swift。 |
| VerticalScreenPlayerView | 仅可替换 结构体 并删除占位页相关类型;必须保留同文件内 HeaderConfig、CourseProgressNavBar、LessonPageView 等所有其他类型,不能整文件覆盖。 |
| realNodes 排序 | 与当前实现存在差异,是否保留 .sorted { $0.order < $1.order } 需结合后端 order 语义决定。 |
未对仓库内任何文件进行修改。