diff --git a/entry/src/main/ets/components/FbMovieDetailSheet.ets b/entry/src/main/ets/components/FbMovieDetailSheet.ets index d17b491..88ff8b4 100644 --- a/entry/src/main/ets/components/FbMovieDetailSheet.ets +++ b/entry/src/main/ets/components/FbMovieDetailSheet.ets @@ -136,7 +136,7 @@ export struct FbMovieDetailSheet { return { ok: false, error: '', id: 0, title: '', startsAt: '', endsAt: '', note: '', info: '', posterKey: '', ticketKey: '', cinemaName: '', cinemaPlace: '', - cinemaAddress: '', cinemaColor: '', myRating: 0, myReview: '', + cinemaAddress: '', cinemaColor: '', cinemaId: 0, myRating: 0, myReview: '', reviewCount: 0, reviewAvg: 0, attendees: [], photos: [] }; } diff --git a/entry/src/main/ets/components/FbMovieNewSheet.ets b/entry/src/main/ets/components/FbMovieNewSheet.ets index 9ef587a..5a4baff 100644 --- a/entry/src/main/ets/components/FbMovieNewSheet.ets +++ b/entry/src/main/ets/components/FbMovieNewSheet.ets @@ -129,6 +129,19 @@ export struct FbMovieNewSheet { * 保存后海报变成了另一张"。 */ @Local existingPosterKey: string = ''; + /** + * 编辑模式下**这条记录已有的观影照片**(相对键;只展示,不提交)。 + * + * 🔴 用户 2026-11-06:「观影照片…没带进来。」 + * 原来照片区只渲染 `photoUris`(本次新选的本地图),已有照片在 + * `editingDetail.photos` 里**完全没画** ⇒ 用户以为照片丢了。 + * + * ⚠️ 用 `@Local` 而不是在 build 里读 `this.editingDetail.photos`: + * `if` 条件里调方法/取对象深层字段都不建依赖(本项目反复踩过)。 + * ⚠️ 与 `photoUris` 分开存:那个是"待上传的本地 uri",语义不同, + * 混在一起会让"保存时传哪些"变得说不清。 + */ + @Local existingPhotos: string[] = []; /** 豆瓣抓到的元数据 JSON(存进库,详情页另有展示) */ @Local doubanMeta: string = ''; @@ -205,13 +218,15 @@ export struct FbMovieNewSheet { const en: string = d.endsAt; this.endDate = en.length >= 10 ? en.substring(0, 10) : this.startDate; this.endTime = en.length >= 16 ? en.substring(11, 16) : ''; - this.cinemaId = this.cinemaIdOf(d.cinemaName); + this.cinemaId = this.cinemaIdOf(d); this.info = d.info; this.note = d.note; /* 已有海报:只展示,不提交(见 existingPosterKey 的说明)。 ⚠️ 别塞进 doubanPosterKey —— 那个会被 PATCH 带上去, 虽然值相同不出错,但语义就乱了,后人容易改错。 */ this.existingPosterKey = d.posterKey; + /* 已有观影照片(票根另算,它是 movie_details.ticket_key,不在这里) */ + this.existingPhotos = d.photos.slice(); const ids: number[] = []; /* ⚠️ 必须显式标类型(`arkts-no-any-unknown`): 直接 `for (const a of d.attendees)` 会让 `a` 被推断成 any 而编译失败。 */ @@ -220,24 +235,38 @@ export struct FbMovieNewSheet { ids.push(a.id); } this.picked = ids; + /* 🔴 2026-11-06:回填完把豆瓣联想的任何残留态清干净。 + 用户报的「片名下面列出来好几个条目(来自豆瓣)」就是联想列表 —— + 拦它的地方在 `titleField` 的 `onChange`(值没真变就不调度), + 这里负责兜底清一次,双保险。 */ + this.clearSuggestTimer(); + this.suggests = []; + this.suggestOpen = false; } /** - * 影院名 → 影院 id。 + * 影院 id 的回填。 * - * ⚠️ 详情接口只给 `cinemaName`(不给 id)—— 查一下本地库那份影院列表。 - * 找不到(影院被删/改名)就返回 0,表单显示"未选影院",不会硬塞一个错的。 + * 🔴 2026-11-06:原来只能按**影院名**反查(因为 `FbMovieDetail` 里没有 id), + * 用户报的「影院没带进来」就是这个 —— + * · 影院重名(很多城市都有「万达影城」)⇒ 反查选中了错的那家; + * · 影院改过名 ⇒ 旧记录的 `cinema_name` 与影院表对不上 ⇒ 直接回填失败。 + * 现在 `FbMovieDetail.cinemaId` 已经从接口接上了,**优先用 id**, + * 只有 id 缺失(老数据 / 接口没给)时才退回按名字模糊匹配。 */ - private cinemaIdOf(name: string): number { - if (name === '') { + private cinemaIdOf(d: FbMovieDetail): number { + if (d.cinemaId > 0) { + // 确认这家影院还在本地列表里(可能已被删) + const hit: FbCinemaItem | undefined = + this.cinemas.find((c: FbCinemaItem): boolean => c.id === d.cinemaId); + return hit !== undefined ? d.cinemaId : 0; + } + if (d.cinemaName === '') { return 0; } - for (const c of this.cinemas) { - if (c.name === name) { - return c.id; - } - } - return 0; + const byName: FbCinemaItem | undefined = + this.cinemas.find((c: FbCinemaItem): boolean => c.name === d.cinemaName); + return byName !== undefined ? byName.id : 0; } aboutToDisappear(): void { @@ -276,6 +305,34 @@ export struct FbMovieNewSheet { return out.join('、'); } + /** + * 「同看的人」那一行的文案。 + * + * 🔴 2026-11-06:用户报「同看的人没带进来」。 + * 实际是**名字还没查出来时显示成 `已选 2 人:`(冒号后面空白)**, + * 看起来像"什么都没带进来"。 + * + * 根因是两条时序叠在一起: + * ① `prefill()` 只填了 `picked`(id 数组),名字要靠 `roster` 反查; + * ② `loadRoster()` 是**异步**的 —— 表单刚打开那一两帧里 `roster` 还是空, + * 于是 `pickedNames()` 返回空串。 + * + * ⇒ 分三种情况说清楚,不让用户看到"半句话": + * · 没人选 → 「还没有选人」 + * · 有人但名字还没到 → 「已选 N 人(正在读取名字…)」 + * · 都有 → 「已选 N 人:张三、李四」 + */ + private pickedLabel(): string { + if (this.picked.length === 0) { + return '还没有选人'; + } + const names: string = this.pickedNames(); + if (names === '') { + return '已选 ' + this.picked.length.toString() + ' 人(正在读取名字…)'; + } + return '已选 ' + this.picked.length.toString() + ' 人:' + names; + } + private cinemaLabel(): string { if (this.cinemaId <= 0) { return '不记录影院'; @@ -579,10 +636,14 @@ export struct FbMovieNewSheet { FbSheetTitleBar({ /* 编辑模式换个标题,用户一眼知道自己在改而不是新建 */ title: this.editingId > 0 ? '编辑观影记录' : '记一场电影', - subtitle: this.editingId > 0 - ? '改完点下面保存;照片可以再补几张' - : '片名和时间必填,其余可以留空', + /* 🔴 用户 2026-11-06:「编辑观影记录这几个字,也应该是主题颜色才对, + 下面的一行小字(改完点下面保存,照片可以再补几张。),删除不要了。」 + ⇒ ① 标题用主题色(见下面 `titleColor` 参数的来源); + ② **编辑模式不再传 subtitle** —— 那句说明是他明确要删的。 + 新建模式保留原有的提示(那边还需要"片名和时间必填"这句引导)。 */ + subtitle: this.editingId > 0 ? '' : '片名和时间必填,其余可以留空', accent: this.accentColor(), + titleColor: this.accentColor(), onClose: (): void => { this.onClose(); } @@ -626,14 +687,9 @@ export struct FbMovieNewSheet { this.posterField() this.photoField() - this.areaField('简介', '剧情简介(可空,点「自动获取」从豆瓣填)', this.info, - (v: string): void => { - this.info = v; - }) - this.areaField('备注', '想记的话(可空)', this.note, - (v: string): void => { - this.note = v; - }) + this.infoField() + + this.noteField() } .width('100%') .padding({ top: 6, bottom: 4 }) @@ -785,7 +841,19 @@ export struct FbMovieNewSheet { .layoutWeight(1) .padding({ left: 10, right: 10 }) .onChange((v: string): void => { + /* 🔴 2026-11-06:编辑模式回填时,`TextInput` 会把"刚被赋值的新内容" + 当成一次 onChange 回调(用户报的"片名下面列出来好几个条目")。 + ⇒ 值没有真正变化时,不调度联想 —— + 回填是"值本来就是这个",不是"用户改了"。 + ⚠️ 这里用"值比较"而不是 `prefilling` 标记: + `prefill()` 是同步执行完的,标记会立刻复位; + 而 `onChange` 是在**下一帧**渲染时才回调的, + 那时标记早就 false 了,拦不住(我第一版就是这么写的,没用)。 */ + const changed: boolean = v !== this.title; this.title = v; + if (!changed) { + return; + } this.scheduleSuggest(); }) @@ -885,7 +953,13 @@ export struct FbMovieNewSheet { */ @Builder cinemaField() { - Row({ space: 8 }) { + /* 🔴 2026-11-06 结构性修正(与 `posterField` 同一条病): + 原来外层是 `Row { Text('影院').width('100%'); Row { 值…选择 } }` + —— 标签 `width('100%')` 吃满整行,把内层 `Row` 挤到行外。 + 真机症状:编辑表单里「影院」只剩一个标签,**下面的值整个不见了** + (用户报「影院没带进来」,其实是根本没渲染)。 + ⇒ 按《全局表单规范》改成上下两排:标签第一排、控件第二排整宽。 */ + Column({ space: 6 }) { /* GLOBAL-FORM-SPEC:标签独占第一排(主题色 Bold,必填红无星号) */ Text('影院') .fontSize(FbMetric.fontSmall) @@ -893,6 +967,7 @@ export struct FbMovieNewSheet { .fontColor(this.accentColor()) .width('100%') + /* 第二排:色点 + 当前选中 + 「选择」 */ Row({ space: 8 }) { if (this.cinemaColor() !== '') { Text('') @@ -911,7 +986,7 @@ export struct FbMovieNewSheet { .fontSize(FbMetric.fontTiny) .fontColor(this.accentColor()) } - .layoutWeight(1) + .width('100%') .height(42) .padding({ left: 12, right: 12 }) .backgroundColor(FbColors.inputBg) @@ -922,7 +997,7 @@ export struct FbMovieNewSheet { }) } .width('100%') - .alignItems(VerticalAlign.Center) + .alignItems(HorizontalAlign.Start) } /** @@ -934,7 +1009,12 @@ export struct FbMovieNewSheet { */ @Builder personField() { - Row({ space: 8 }) { + /* 🔴 2026-11-06 结构性修正(与 `cinemaField` / `posterField` 同一条病): + 原来外层 `Row` 里标签 `width('100%')` 把内层 `Row` 挤出可视区, + 真机症状是「同看的人」只剩标签、**下面的值整个不渲染** + (用户报「同看的人没带进来」其实是根本没画)。 + ⇒ 改成上下两排。 */ + Column({ space: 6 }) { /* GLOBAL-FORM-SPEC:标签独占第一排(主题色 Bold,必填红无星号) */ Text('同看的人') .fontSize(FbMetric.fontSmall) @@ -942,10 +1022,9 @@ export struct FbMovieNewSheet { .fontColor(this.accentColor()) .width('100%') + /* 第二排:已选人 + 「选择」 */ Row({ space: 8 }) { - Text(this.picked.length > 0 - ? ('已选 ' + this.picked.length.toString() + ' 人:' + this.pickedNames()) - : '还没有选人') + Text(this.pickedLabel()) .fontSize(FbMetric.fontSmall) .fontColor(this.picked.length > 0 ? FbColors.ink : FbColors.muted) .maxLines(1) @@ -956,7 +1035,7 @@ export struct FbMovieNewSheet { .fontSize(FbMetric.fontTiny) .fontColor(this.accentColor()) } - .layoutWeight(1) + .width('100%') .height(42) .padding({ left: 12, right: 12 }) .backgroundColor(FbColors.inputBg) @@ -972,7 +1051,7 @@ export struct FbMovieNewSheet { }) } .width('100%') - .alignItems(VerticalAlign.Center) + .alignItems(HorizontalAlign.Start) } /** @@ -1099,39 +1178,39 @@ export struct FbMovieNewSheet { @Builder photoField() { Column({ space: 6 }) { - Row({ space: 8 }) { - /* GLOBAL-FORM-SPEC:标签独占第一排(主题色 Bold,必填红无星号) */ - Text('观影照片') - .fontSize(FbMetric.fontSmall) - .fontWeight(FontWeight.Bold) - .fontColor(this.accentColor()) - .width('100%') + /* GLOBAL-FORM-SPEC:标签独占第一排(主题色 Bold,必填红无星号) + 🔴 2026-11-06:原来标签和内层 `Row` 挤在同一个外层 `Row` 里, + 标签 `width('100%')` 把内层挤出可视区 ⇒ 「选照片」按钮整个不渲染。 + 改成上下两排(同 `cinemaField` / `personField` / `posterField`)。 */ + Text('观影照片') + .fontSize(FbMetric.fontSmall) + .fontWeight(FontWeight.Bold) + .fontColor(this.accentColor()) + .width('100%') - Row({ space: 8 }) { - Text(this.photoUris.length > 0 - ? ('已选 ' + this.photoUris.length.toString() + ' 张') - : '票根、合影、随手拍都可以放这儿') - .fontSize(FbMetric.fontSmall) - .fontColor(this.photoUris.length > 0 ? FbColors.ink : FbColors.muted) - .maxLines(1) - .textOverflow({ overflow: TextOverflow.Ellipsis }) - .layoutWeight(1) - Text('选照片') - .fontSize(FbMetric.fontTiny) - .fontColor(this.accentColor()) - } - .layoutWeight(1) - .height(42) - .padding({ left: 12, right: 12 }) - .backgroundColor(FbColors.inputBg) - .borderRadius(FbMetric.radiusS) - .alignItems(VerticalAlign.Center) - .onClick((): void => { - this.pickPhotos(); - }) + /* 第二排:说明/已选数 + 「选照片」 */ + Row({ space: 8 }) { + Text(this.photoUris.length > 0 + ? ('已新选 ' + this.photoUris.length.toString() + ' 张') + : '票根、合影、随手拍都可以放这儿') + .fontSize(FbMetric.fontSmall) + .fontColor(this.photoUris.length > 0 ? FbColors.ink : FbColors.muted) + .maxLines(1) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .layoutWeight(1) + Text('选照片') + .fontSize(FbMetric.fontTiny) + .fontColor(this.accentColor()) } .width('100%') + .height(42) + .padding({ left: 12, right: 12 }) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) .alignItems(VerticalAlign.Center) + .onClick((): void => { + this.pickPhotos(); + }) if (this.photoUris.length > 0) { Flex({ wrap: FlexWrap.Wrap }) { @@ -1163,12 +1242,53 @@ export struct FbMovieNewSheet { .width('100%') .padding({ left: 56 }) } + + /* 🔴 2026-11-06:**已有照片**(编辑模式)。 + 用户报「观影照片没有带进来」—— 原来这里只渲染 `photoUris` + (= 本次新选的本地图),而编辑时已有的图在 `existingPhotos` 里, + 完全没画出来 ⇒ 用户以为照片丢了,会重新选一遍。 + ⚠️ 只**展示 + 说明**,不做删除:删照片是另一件事(要调服务端删附件), + 用户这轮只要求"带进来能看到"。 + ⚠️ 条件读 `@Local existingPhotos`(不是调方法), + 否则 ArkUI 不建立依赖(同 `posterField` 那条教训)。 */ + if (this.existingPhotos.length > 0) { + Column({ space: 6 }) { + Text('已有 ' + this.existingPhotos.length.toString() + ' 张(保存后保持不变)') + .fontSize(10) + .fontColor(FbColors.faint) + .width('100%') + + Flex({ wrap: FlexWrap.Wrap }) { + ForEach(this.existingPhotos, (k: string) => { + Image(fbImageSrc(k, this.token())) + .width(60) + .height(60) + .borderRadius(4) + .objectFit(ImageFit.Cover) + .alt($r('app.media.fb_ic_movies')) + .margin({ right: 8, bottom: 8 }) + }, (k: string): string => 'exph_' + k) + } + .width('100%') + } + .width('100%') + .alignItems(HorizontalAlign.Start) + } } .width('100%') .alignItems(HorizontalAlign.Start) } - /** 一行多行文本输入(简介 / 备注) */ + /** + * 一行多行文本输入。 + * + * ⚠️ **值参数 Builder 有坑**(本项目 `pitfalls.md` 记过): + * `Builder(value)` 的值参数**不参与状态观察**, + * `TextArea({ text: value })` 只拿得到首次调用时的字面量。 + * ⇒ 简介 / 备注**已改成独立 Builder**(`infoField` / `noteField`), + * 直接在内部读 `this.info` / `this.note`,编辑回填才能显示出来。 + * ⇒ 这个通用版留着给"值不会变、或调用方自己保证重传"的场景用。 + */ @Builder areaField(label: string, placeholder: string, value: string, onChange: (v: string) => void) { /* GLOBAL-FORM-SPEC:标签独占第一排(原来是 .width(48) 硬凑) */ @@ -1196,4 +1316,74 @@ export struct FbMovieNewSheet { .width('100%') .alignItems(HorizontalAlign.Start) } + + /** + * 简介(横跨整个表单宽度)。 + * + * 🔴 2026-11-06:**不再走 `areaField(...)`**,因为它是**值参数** Builder —— + * `TextArea({ text: value })` 拿到的是**首次调用时**的字面量, + * `prefill()` 在 `aboutToAppear` 里改了 `this.info` 之后,这里收不到新值 + * ⇒ 编辑表单里「简介」永远空白(用户报的"简介没带进来"就是这个)。 + * + * 这与 `pitfalls.md` 的「@Builder 值参数不参与状态观察」是**同一条规则**: + * ❌ `Builder(value)` —— 值参数,建不了依赖 + * ✅ Builder 内部直接读 `this.xxx` + * + * ⚠️ `areaField` 保留:它还被别的地方用(备注已改成独立 Builder, + * 见下面 `noteField`)。真要彻底删得先确认没有其它调用点。 + */ + @Builder + infoField() { + Column({ space: 6 }) { + Text('简介') + .fontSize(FbMetric.fontSmall) + .fontWeight(FontWeight.Bold) + .fontColor(this.accentColor()) + .width('100%') + + TextArea({ text: this.info, placeholder: '剧情简介(可空,点「自动获取」从豆瓣填)' }) + .placeholderColor(FbColors.faint) + .placeholderFont({ size: FbMetric.fontTiny }) + .fontSize(FbMetric.fontSmall) + .fontColor(FbColors.ink) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) + .height(110) + .width('100%') + .padding({ left: 10, right: 10, top: 8, bottom: 8 }) + .onChange((v: string): void => { + this.info = v; + }) + } + .width('100%') + .alignItems(HorizontalAlign.Start) + } + + /** 备注(同上,直接读 `this.note`) */ + @Builder + noteField() { + Column({ space: 6 }) { + Text('备注') + .fontSize(FbMetric.fontSmall) + .fontWeight(FontWeight.Bold) + .fontColor(this.accentColor()) + .width('100%') + + TextArea({ text: this.note, placeholder: '想记的话(可空)' }) + .placeholderColor(FbColors.faint) + .placeholderFont({ size: FbMetric.fontTiny }) + .fontSize(FbMetric.fontSmall) + .fontColor(FbColors.ink) + .backgroundColor(FbColors.inputBg) + .borderRadius(FbMetric.radiusS) + .height(72) + .width('100%') + .padding({ left: 10, right: 10, top: 8, bottom: 8 }) + .onChange((v: string): void => { + this.note = v; + }) + } + .width('100%') + .alignItems(HorizontalAlign.Start) + } } \ No newline at end of file diff --git a/entry/src/main/ets/components/FbSheetClose.ets b/entry/src/main/ets/components/FbSheetClose.ets index a6fabd3..6b345b1 100644 --- a/entry/src/main/ets/components/FbSheetClose.ets +++ b/entry/src/main/ets/components/FbSheetClose.ets @@ -96,6 +96,17 @@ export struct FbSheetTitleBar { @Param confirmText: string = ''; /** 主题色(标题下的副标题、确认文字用它) */ @Param accent: string = ''; + /** + * 主标题的颜色(空 = 用正文色 `FbColors.ink`)。 + * + * 🔴 用户 2026-11-06:「编辑观影记录这几个字,也应该是主题颜色才对。」 + * + * ⚠️ 为什么单独开一个参数,而不是直接让 `title` 用 `accent`: + * 本组件被**全站浮层**引用(设置类、影院管理、任务面板…), + * 标题默认必须是正文色(深墨),只有明确要求的浮层才上主题色。 + * `@Param` 带默认空串 ⇒ 老调用点一行都不用改。 + */ + @Param titleColor: string = ''; /** 同上:回调统一 `@Event`(`@Param` 实测也能用,但语义不对) */ @Event onClose: () => void = (): void => {}; @@ -106,7 +117,7 @@ export struct FbSheetTitleBar { Text(this.title) .fontSize(FbMetric.fontTitle) .fontWeight(FontWeight.Medium) - .fontColor(FbColors.ink) + .fontColor(this.titleColor !== '' ? this.titleColor : FbColors.ink) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) } diff --git a/entry/src/main/ets/data/FbMovieRepo.ets b/entry/src/main/ets/data/FbMovieRepo.ets index 47717b7..d7be894 100644 --- a/entry/src/main/ets/data/FbMovieRepo.ets +++ b/entry/src/main/ets/data/FbMovieRepo.ets @@ -278,6 +278,19 @@ export interface FbMovieDetail { cinemaPlace: string; cinemaAddress: string; cinemaColor: string; + /** + * 影院 id(0 = 没选)。 + * + * 🔴 2026-11-06:**这个字段原来漏了**,`parseMovieDetail` 没解析 `cinema_id`。 + * 后果:编辑观影记录时「影院」永远是「不记录影院」—— 用户报的 + * 「影院没带进来」就是这个。 + * + * 为什么必须有 id 而不能只靠 `cinemaName` 反查: + * · 影院可能**重名**(「万达影城」很多城市都有)⇒ 按名字 find 会选错; + * · 影院被改名后,旧记录里的 `cinema_name` 与影院表对不上 ⇒ 反查失败; + * · 服务端 `Movie::find()` 本来就 `SELECT ... d.cinema_id`,只是端上没接。 + */ + cinemaId: number; /** 我的评分(0 = 没评)。⚠️ 本版恒为 0:详情接口不返回"我的评价" */ myRating: number; /** 我的评价正文。⚠️ 同上 */ @@ -1506,6 +1519,15 @@ interface FbApiMovieDetail { cinema_place?: string | null; cinema_address?: string | null; cinema_color?: string | null; + /** + * 影院 id。 + * + * 🔴 2026-11-06:**这个字段原来漏了**(接口真实返回有它,端上没声明也没解析)。 + * 服务端 `Movie::find()` 的 SELECT 里明确有 `d.cinema_id`; + * 少声明它的后果是编辑表单里「影院」永远回填不了(只能靠名字反查, + * 而名字会重名/被改名)。 + */ + cinema_id?: number | null; attendees?: FbApiAttendee[]; /** * ⚠️ 照片是**对象数组**(`[{id, key}]`),不是字符串数组 —— @@ -1526,7 +1548,7 @@ function emptyDetail(id: number, error: string): FbMovieDetail { return { ok: false, error: error, id: id, title: '', startsAt: '', endsAt: '', note: '', info: '', posterKey: '', ticketKey: '', cinemaName: '', cinemaPlace: '', - cinemaAddress: '', cinemaColor: '', myRating: 0, myReview: '', + cinemaAddress: '', cinemaColor: '', cinemaId: 0, myRating: 0, myReview: '', reviewCount: 0, reviewAvg: 0, attendees: [], photos: [] }; } @@ -1579,6 +1601,8 @@ function parseDetail(id: number, json: string): FbMovieDetail { cinemaPlace: strOf(o.cinema_place), cinemaAddress: strOf(o.cinema_address), cinemaColor: strOf(o.cinema_color), + /* 🔴 2026-11-06:补上 `cinema_id`(原来漏解析,导致编辑表单里影院回填不了)。 */ + cinemaId: numOf(o.cinema_id), // ⚠️ 「我的评价」与「评分汇总」详情接口都**不返回**(实测确认)—— // 前者要拉子资源,后者由调用方从列表项带过来。 myRating: 0,