FunyMeeAI/docs/funymee_app_development_manual.md

480 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FunyMee 应用开发手册(完整版)
本文档是 **FunyMee 客户端** 的主开发手册:页面范围、启动与认证、`skin_config`、**`client_proxy_framework` 已封装 API**、分屏实施要点、合规与发布清单。
实现代理/V2 报文细节、字段别名全表时,需对照 [FunyMeeAI_client_guide.md](./FunyMeeAI_client_guide.md)从零搭工程、Adjust/Facebook 原生步骤可对照 [skin_app_development_guide.md](./skin_app_development_guide.md)。**按本手册顺序施工 + 上述两份文档作规格备查,即可闭环完整 App**(不含后端实现与商店审核商务材料)。
---
## 目录
1. [资料闭环与本手册怎么用](#1-资料闭环与本手册怎么用)
2. [页面与画板映射](#2-页面与画板映射)
3. [目标工程结构(建议)](#3-目标工程结构建议)
4. [依赖与原生能力](#4-依赖与原生能力)
5. [启动、配置与认证](#5-启动配置与认证)
6. [请求与字段映射(框架行为)](#6-请求与字段映射框架行为)
7. [API 与数据实体总表](#7-api-与数据实体总表)
8. [分屏实施规格](#8-分屏实施规格)
9. [生图状态机与轮询](#9-生图状态机与轮询)
10. [历史 24 小时与倒计时](#10-历史-24-小时与倒计时)
11. [积分流水与支付记录](#11-积分流水与支付记录)
12. [内购(积分购买)](#12-内购积分购买)
13. [举报(反馈)](#13-举报反馈)
14. [注销账号](#14-注销账号)
15. [主题、字体与资源](#15-主题字体与资源)
16. [权限与合规](#16-权限与合规)
17. [调试、环境与联调](#17-调试环境与联调)
18. [测试与上线检查清单](#18-测试与上线检查清单)
19. [导航关系](#19-导航关系)
20. [外部文档索引](#20-外部文档索引)
21. [修订记录](#21-修订记录)
---
## 1. 资料闭环与本手册怎么用
| 层级 | 内容 | 文档/代码 |
|------|------|-----------|
| **产品/UI** | 视觉与交互 | `desgin/funymee_home.pen`、同目录 PNG 切图 |
| **运行时配置** | BaseUrl、AES、代理键、归因、字段映射 | `assets/skin_config.json` |
| **框架 API** | 已封装的 REST 调用与实体 | 本地依赖 `../client_proxy_framework`,见下文 API 表 |
| **协议深度** | V2 包装、密文键名、错误码 | [FunyMeeAI_client_guide.md](./FunyMeeAI_client_guide.md) |
| **工程/Android/iOS/SDK** | 换皮脚手架、清单、Gradle | [skin_app_development_guide.md](./skin_app_development_guide.md)、`client_proxy_framework/docs/sdk_integration_guide.md` |
| **配置项语义** | 各 JSON 键说明 | [new_app_config_template.md](./new_app_config_template.md) |
**推荐阅读顺序**:本手册 §19 → 开始写壳与首页 → 对照 client_guide 联调接口 → skin_guide 处理 SDK/权限遗漏。
---
## 2. 页面与画板映射
来源:`desgin/funymee_home.pen` 根级 Frame`children` 顶层)。
| 顺序 | 画板名称 | Pencil 根 id | 说明 |
|------|----------|----------------|------|
| 1 | FunyMee Home | `bi8Au` | 首页 |
| 2 | 通用背景 · 黄→白渐变 | `suXxr` | 可复用渐变,非独立路由 |
| 3 | My History 页面 | `WBRp4` | 历史24h 提示与卡片时间见设计稿 |
| 4 | Credit Record 页面 | `QR6Gq` | 积分流水 Tab |
| 5 | Purchase Point 页面 | `ETbdo` | 购买积分 |
| 6 | 生成图片 页面 | `EYsUi` | 选图/发起任务前 |
| 7 | 生成图片 · 生成中 | `YoZaK` | 进行中 |
| 8 | 生成图片 · 已完成 | `c7R7z` | 完成态 |
| 9 | 生成图片 · 可下载 | `2SyyL` | 可下载 |
| 10 | 生成图片 · 单图 | `0iJjl` | 单图结果变体 |
| 11 | 个人中心 | `5J8Po` | 设置、积分入口、注销入口等 |
| 12 | 注销账户 · 步骤1 | `SYt0O` | |
| 13 | 注销账户 · 步骤2 | `yxwpg` | 二次确认 |
| 14 | 通用 · 加载中 | `8rpyo` | 全屏 Loading 组件 |
| 15 | 举报 | `Y9WlO` | **仅从生图完成后的结果页进入** |
补充切图(如 `desgin/订阅.png`)若单独成页,在本表追加一行并分配路由名。
---
## 3. 目标工程结构(建议)
在现有 `lib/` 上扩展(与 [skin_app_development_guide.md](./skin_app_development_guide.md) 一致):
```
lib/
├── main.dart
├── app.dart # MaterialApp + 路由入口
├── core/
│ ├── auth/auth_service.dart # 已有
│ ├── user/user_state.dart # 已有;可改为 ChangeNotifier
│ ├── theme/ # 色板、字体、圆角
│ ├── routing/ # 路由表、RoutePath 常量
│ └── widgets/ # 全屏 Loading、通用渐变背景等
└── features/
├── home/
├── generate/ # 选图 → 中 → 完成 → 下载 / 单图
├── history/ # My History + Credit RecordTab
├── purchase/
├── profile/ # 个人中心 + 注销两步
└── report/ # 举报(从结果页 push
```
验收:**每个画板至少对应一个 `Widget` 文件与一个路由路径常量**。
---
## 4. 依赖与原生能力
当前 `pubspec.yaml` 已含:`client_proxy_framework``http``encrypt``crypto``logger``shared_preferences``android_id``device_info_plus`
**按功能待增(在实现到对应章节时加入)**
| 能力 | 典型 package | 使用场景 |
|------|----------------|----------|
| 路由 | `go_router`(可选) | 深链、声明式路由 |
| 选图/拍照 | `image_picker` | 生成页上传 |
| 网络图 | `cached_network_image` | 历史缩略图、结果图 |
| 保存相册 | `gal``image_gallery_saver``photo_manager` | 下载落盘 |
| 时间/国际化 | `intl` | 创建时间格式化 |
| UUID/路径 | `path``mime` | 上传文件名 |
框架侧已依赖 `in_app_purchase`(经 `client_proxy_framework` 传递);**确保 Android/iOS 内购能力与商店元数据在商店后台配置完成**。
---
## 5. 启动、配置与认证
### 5.1 `main.dart` 顺序(与现码一致)
1. `WidgetsFlutterBinding.ensureInitialized()`
2. `ClientBootstrap.initFromAsset('assets/skin_config.json')`
- 内部:`SkinConfig.fromJson``ApiClient.init(skin)`**此后方能发代理请求**
3. `ClientBootstrap.initAnalytics()`Adjust / Facebook 配置来自 `skin_config.analytics`
4. `AnalyticsService.initAttribution()`(若需与归因桥接)
5. `runApp(App(...))`
6. `AuthService.init()``FrameworkAuthService.init` + `start()`(设备 ID、`fast_login``common_info`
### 5.2 `skin_config.json` 必用字段(顶层)
| JSON 路径 | 用途 |
|-----------|------|
| `app.name` / `app.id` / `app.packageName` | 展示名、业务 appId、包名请求映射 |
| `backend.iosAppType` / `backend.androidAppType` | 接口 query `app``HIOS` / `HAndroid` 等 |
| `api.preBaseUrl` / `api.prodBaseUrl` / `api.proxyPath` / `api.aesKey` | 环境与加解密 |
| `api.alwaysUsePreBaseUrl` / `api.debugBaseUrlOverride` | 开发期切环境 |
| `proxyKeys.*` / `v2.*` | 代理外框字段名与 V2 噪声 |
| `fieldMapping` | 业务 JSON 键 ↔ V2 伪装键(巨大见文件;**查具体接口时打开 client_guide** |
| `analytics.adjust` / `analytics.facebook` | SDK 初始化 |
| `adjustEvents.*` | 埋点 token`AnalyticsService.track*` 对齐 |
### 5.3 业务侧 `app` 参数
`UserApi` / `ImageApi` 等需要 `queryParams['app']` 处:
- iOS`ClientBootstrap.skin.backendAppTypeIOS`(或 `ApiClient.instance.config` 同等字段)
- Android`backendAppTypeAndroid`
`defaultTargetPlatform` 对齐,**不要用 appId 字符串顶替**。
### 5.4 登录与鉴权
- **匿名设备登录**`UserApi.fastLogin`(框架在 `FrameworkAuthService` 内调用;宿主实现 `AuthServiceCallbacks` 提供 `getDeviceId``computeSign`)。
- **Token**:成功后写入 `ApiClient`/代理层;之后 `ProxyClient` 自动带 `pkg` + `User_token`(除 fast_login
- **`common_info`**:拉取 `CommonInfoResponse`(积分、用户信息、`extConfig``t2IConfig``payCenterUrl``userToken` 等),在 `AppAuthCallbacks.onCommonInfoLoaded` 同步到 `UserState`
- **业务请求前**`await AuthService.loginComplete`(或框架提供的 `FrameworkAuthService.loginComplete`),避免空 token。
### 5.5 `extConfig``skin_config` + common_info框架内解析
1. **`skin_config.json``extConfig`**(可选):配置 **wire 键名列表**`keys` / `itemKeys`)及本地 **`defaults`**(与线上下发同键;`common_info` 成功后与服务器 **浅合并**,服务器覆盖同名顶层键)。见 `ExtConfigKeySchema``SkinExtConfigSection`,导出见 `package:client_proxy_framework/client_proxy_framework.dart`
2. **`common_info.extConfig`**:线上 JSON 字符串;若与 `defaults` 合并后仍为空对象,则得到空的 `ExtConfigData`
`AppConfig` 提供 `extConfigKeySchema``extConfigDefaults`;手写 `AppConfig` 时可覆盖;**换皮仅用 JSON 时在 `SkinConfig` 中已注入**。
| 导出 | 用途 |
|------|------|
| `ExtConfigRuntime.data` | `ValueNotifier<ExtConfigData?>`,监听后刷新首页 Tab / Grid |
| `ExtConfigRuntime.commonInfoSucceeded` | `true` / `false` / `null`:是否成功拉到 common_info**建议仅在为 `true` 时展示核心业务 UI** |
| `ExtConfigData` | `showVideoMenu``allowScreenshot``allowThirdPartyPayment``privacyUrl``agreementUrl``items` |
| `ExtConfigItem` | 单项:`image``image_fix``img_need``cost``title``params` / `detail``taskExt``params ?? detail` |
| `kExtConfigItemsCategoryId` | 固定 `-1`,作「静态 items Tab」分类 id |
| `mergeHomeTabsWithExtConfigItems<T>()` | `showVideoMenu == true` 时在 API Tab 列表 **末尾** 追加静态 Tab |
| `ClientBootstrap.skin.extConfigKeySchema` 等 | `SkinConfig` 从 JSON `extConfig.keys` 注入;宿主可只改配置不换代码 |
**默认 wire 键(可在 `skin_config.extConfig.keys` 中整体改写):**
| 语义 | 默认候选键(首个存在则生效) |
|------|------------------------------|
| 展示顶部 Video Tab 栏 + items 固定最后一格 | `go_run``need_wait` |
| 允许截屏 | `screen`;若无则看 `safe_area``true` ⇒ 不允许截屏) |
| 允许第三方支付 | `san_fang``lucky` |
| 隐私 / 协议 URL | `privacy``agreement` |
| items 数组 | `items` |
**`itemKeys` 默认(可省略 `imageFix`,仍解析 `image_fix`** `image``image_fix``img_need``cost``title``params``detail`
示例(与当前后端约定一致时可原样使用):
```json
{
"go_run": false,
"screen": false,
"san_fang": false,
"privacy": "https://example.com/privacy",
"agreement": "https://example.com/terms",
"items": [
{
"image": "https://cdn.example.com/a.png",
"image_fix": "https://cdn.example.com/a.png",
"img_need": 2,
"cost": 1,
"title": "BananaTask",
"params": "animal_expression"
}
]
}
```
首页逻辑建议(对齐 `app_client``await FrameworkAuthService.loginComplete` 后判断 `ExtConfigRuntime.commonInfoSucceeded.value == true` 再进入主页;`showVideoMenu == true` 时展示顶部 Tab分类列表用 `mergeHomeTabsWithExtConfigItems` 把静态 Tab 放在最后,**该 Tab 的 Grid 数据源为 `ExtConfigRuntime.data.value?.items`**。第三方支付入口用 `allowThirdPartyPayment`;截屏策略用 `shouldPreventCapture` 或自行根据 `allowScreenshot` 调用宿主侧防护(框架不强制依赖 `screen_secure`)。
---
## 6. 请求与字段映射(框架行为)
- 宿主**只使用** `UserApi``ImageApi``PaymentApi``FeedbackApi` 等,**Body/Query 使用业务原始字段名**(如 `deviceId``taskId`)。
- `ProxyClient` 负责:按 `skin_config.fieldMapping` 做键名映射、V2 包装、AES、噪音字段等。
- 查阅某一字段的密文键名:在 [FunyMeeAI_client_guide.md](./FunyMeeAI_client_guide.md) 或 `skin_config.json``fieldMapping` 中搜索原始键。
---
## 7. API 与数据实体总表
以下均定义于 `client_proxy_framework/lib/src/services/*.dart`,实体在 `entities/`
### 7.1 用户
| 方法 | Path | 说明 |
|------|------|------|
| `UserApi.fastLogin` | `POST /v1/user/fast_login` | 设备登录 |
| `UserApi.referrer` | `POST /v1/user/referrer` | 归因上报 |
| `UserApi.getCommonInfo` | `GET /v1/user/common_info` | 通用信息(首页配置、积分、扩展 JSON |
| `UserApi.getAppLanguage` | `GET /v1/config/app-language` | 语言配置 |
| `UserApi.getAccount` | `GET /v1/user/account` | 账户信息 |
| `UserApi.getCreditsPage` | `GET /v1/user/credits-page` | **积分分页流水**`page/size/type` |
| `UserApi.getUserPayments` | `GET /v1/user/payments` | **支付/订单记录列表** |
| `UserApi.getUnreadMessageCount` | `GET /v1/user/unread-message-count` | 未读消息 |
| `UserApi.deleteAccount` | `GET /v1/user/delete` | **注销** |
核心实体:`FastLoginResponse``CommonInfoResponse``AccountResponse``CreditsPageInfoResponse`(含 `CreditRecordItem`)、`UserPaymentsListResponse`
### 7.2 图片 / 任务
| 方法 | Path | 说明 |
|------|------|------|
| `ImageApi.getCategoryList` | `GET /v1/image/img2video/categories` | 分类 |
| `ImageApi.getImg2VideoTasks` | `GET /v1/image/img2video/tasks` | 图生视频任务模板列表 |
| `ImageApi.getPromptRecommends` | `GET /v1/image/prompt/recomends` | 推荐提示词 |
| `ImageApi.createTxt2Img` | `POST /v1/image/txt2img_create` | 文生图 |
| `ImageApi.createImg2VideoPose` | `POST /v1/image/img2video_pose_task` | 图生视频姿态任务 |
| `ImageApi.getProgress` | `GET /v1/image/progress` | **任务进度** |
| `ImageApi.getImg2VideoPoseTemplates` | `GET /v1/image/img2Video_pose_template` | 姿态模板 |
| `ImageApi.getUploadPresignedUrl` | `POST /v1/image/upload-presigned-url` | **用户图预签名上传** |
| `ImageApi.createTask` | `POST /v1/image/create-task` | **主创建任务**(多参数,与后端协定) |
| `ImageApi.getMyTasks` | `GET /v1/image/my-tasks` | **我的任务列表** |
核心实体:`CreateTaskResponse``ProgressResponse``status``progress``resultUrl`)、`MyTasksResponse` / `MyTaskItem``taskId``status``createTime``resultUrl` 等)、`UploadPresignedUrlResponse`
**与 app_client 对齐(框架内、无 UI**
- **上传图 ↔ 任务 id 本地缓存**:创建任务成功后调用 `TaskUploadCoverStore.saveAfterCreateTaskResponse(response:, source:)`(或 `saveAfterCreateTaskBody` / `saveForTask`)。文件在应用 support 目录下 `gallery_upload_covers/`,默认 25h 过期清理,文件名与数字 `taskId` 一致(如 `123.jpg`)。
- **我的任务列表**:仍用 `ImageApi.getMyTasks``MyTasksResponse` 同时兼容列表键 `tasks` / `intensify``hasNext` / `manifest``MyTaskItem` / `CreateTaskResponse` 中任务 id 兼容 `taskId` / `tree` / `id`(解密后的 business 字段)。
- **与 app_client Gallery 相同的行模型**`GalleryTaskItem` / `GalleryMediaItem``listingDisplayFromApi` 等见 `gallery_task_models.dart`;原始列表 Map 可用 `ImageTaskHistory.parseGalleryTasksFromData` 解析,本机封面路径用 `ImageTaskHistory.localCoverPathsForGalleryTasks`(或与 `MyTaskItem` 对应的 `localCoverPathsForMyTaskItems`)在刷新列表后合并。
**FunyMee 推荐直接调用的封装(框架已导出)**
- **`compressImageForUpload` / `CompressImageForUploadOptions`**:上传前压图。
- **`ImagePresignedUploadCreateTaskFlow.run`**:压图(可关)→ `getUploadPresignedUrl` → HTTP PUT → `createTask` → 可选 `TaskUploadCoverStore``UploadPresignedUrlResponse` 支持 `putHeaders` 等与 PUT 合并;若后端只要 `imgUrl` 可设 `createTaskUseImgUrlOnly: true`
- **`UserAccountRefresh.fetchAndNotify`**`getAccount` + 回调,无 UI 状态。
- **`AdjustService.obtainReferrerForUpload`**:返回 `ReferrerForUpload``digest` + `source`),供 `UserApi.referrer` 等与参考产品一致。
- **`ensureDeviceMemoryProfileInitialized`**(默认通道 `client_proxy_framework/device_memory`,插件已接 Android `ActivityManager.totalMem`)、`deviceGridMaxConcurrentVideos` 等。
- **`VideoThumbnailCache.instance`**:远程视频缩略图 / 海报帧缓存。
### 7.3 支付
| 方法 | Path | 说明 |
|------|------|------|
| `PaymentApi.getGooglePayActivities` | `GET /v1/payment/getGooglePayActivities` | Android 商品活动 |
| `PaymentApi.getApplePayActivities` | `GET /v1/payment/getApplePayActivities` | iOS 商品活动 |
| `PaymentApi.getPaymentMethods` | `POST /v1/payment/get-payment-methods` | 某活动支付方式 |
| `PaymentApi.createPayment` | `POST /v1/payment/createPayment` | 创建订单 |
| `PaymentApi.getPaymentDetailList` / `getOrderDetail` 等 | 见 `payment_api.dart` | 订单详情、列表 |
内购验证与 `PurchaseDetails` 流封装见 `PaymentService``client_proxy_framework`)。
### 7.4 反馈(举报)
| 方法 | Path | 说明 |
|------|------|------|
| `FeedbackApi.getUploadPresignedUrl` | `POST /v1/feedback/upload-presigned-url` | 截图/凭证上传 |
| `FeedbackApi.submit` | `POST /v1/feedback/submit` | 提交(`fileUrls``contentType``content` |
实体:`SubmitFeedbackResponse``FeedbackUploadPresignedUrlResponse`
---
## 8. 分屏实施规格
| 页面 | 主要 API / 数据源 | 关键实体与注意事项 |
|------|-------------------|-------------------|
| **FunyMee Home** | `getCommonInfo`框架内已拉Tab/静态列表用 **`ExtConfigRuntime` / `ExtConfigData`**;其它首页结构可解析 `t2IConfig` | 见 **§5.5**;静态 Tab id 使用 `kExtConfigItemsCategoryId` |
| **生成图片** | `getUploadPresignedUrl` + HTTP PUT 到 `uploadUrl``createTask``createTxt2Img` / 业务指定路径 | 传 `userId``app`;参数以后台为准 |
| **生成中** | `getProgress` 轮询 | `taskId` 来自创建响应;`status` / `progress` 与 client_guide 状态枚举对齐 |
| **已完成 / 可下载 / 单图** | 同一任务不同 UI 态;数据来自 `ProgressResponse``MyTaskItem` | 下载前校验 `resultUrl`**举报入口放此组页面** |
| **My History** | `getMyTasks`(分页 `page` / `pageSize` / `cursor` 按后端) | 展示 `createTime``resultUrl`、状态;无远程封面时可合并 `TaskUploadCoverStore`;过期逻辑见 §10 |
| **Credit Record** | `getCreditsPage` | `CreditRecordItem.createTime` 为整型时间戳(秒或毫秒需与后端确认) |
| **Purchase Point** | `getGooglePayActivities` / `getApplePayActivities``getPaymentMethods``createPayment` + 平台 IAB | 对齐 `PaymentService` 与订单恢复 |
| **个人中心** | `getAccount`;展示 `common_info` 冗余字段 | 跳转购买、注销;**无举报** |
| **注销** | 两步 UI + `deleteAccount` | 成功后清除本地 token、回首页或登录流 |
| **举报** | `FeedbackApi` 预签名上传 + `submit` | `contentType` 与后台枚举一致(见 client_guide |
| **全屏 Loading** | 无 API | 覆盖路由栈或全局 `Overlay` |
---
## 9. 生图状态机与轮询
推荐流程:
1. **创建**`createTask`(或文生图等)→ 取 `taskId`;若有用户上传的本地待传文件,成功后调用 `TaskUploadCoverStore.saveAfterCreateTaskResponse` 与任务 id 关联(见 §7.2)。
2. **轮询**:循环 `ImageApi.getProgress(app:, taskId:, userId:)`,间隔 13s带** backoff** 与**页面 dispose 取消**。
3. **状态映射**(字符串以**后端文档为准**,此处为占位):
- `pending` / `processing` → 生成中页
- `success` / `completed``resultUrl` 有效 → 可下载 / 单图
- `failed` → 错误态 UI + 重试/客服
4. **离开 App 再进入**:用 `getMyTasks` 或持久化 `taskId` 恢复轮询。
详细状态码表:在 **client_guide** 搜索 `progress` / `task` / `status`
---
## 10. 历史 24 小时与倒计时
- 列表项展示 **创建时间**:优先 `MyTaskItem.createTime`格式由后端决定ISO8601 或时间戳用 `intl`/自建解析)。
- **「剩余可下载时间」**:若接口**未**直接返回过期时间戳,则采用产品规则:
`deadline = createdAt + 24h`,剩余 = `deadline - DateTime.now()`;展示与 **My History** 画板一致(天/时/分)。
- 过期后:隐藏或置灰 Download可选调用后台是否仍返回 `resultUrl` 以决定客户端行为。
---
## 11. 积分流水与支付记录
- **积分流水Credit Record 页)**`UserApi.getCreditsPage(page:, size:, type:)`
**`type` 含义必须与后台确认**(过滤消费/充值等);分页用 `total/current/pages`
- **支付记录(若单独 Tab**`UserApi.getUserPayments(app:, userId:)`,实体 `UserPaymentsListResponse`
---
## 12. 内购(积分购买)
### 12.1 编排层(推荐,`package:client_proxy_framework/...` 已导出 `payment_flow.dart`
| 类型 | 作用 |
|------|------|
| `PaymentSettlementSink` | 宿主实现 `onPaymentSettled`**仅此一处**拉 `common_info` / 更新 `UserState` / 埋点 |
| `PaymentSettlement` / `PaymentFlowOutcomeType` | 成功 / 失败 / 取消 / 超时 等统一结果 |
| `PaymentFlowCatalog.loadStoreActivities` | 按平台调 `getGooglePayActivities` / `getApplePayActivities` |
| `PaymentApi.getPaymentMethods` | 仍直接调用;选完后走三方或内购 |
| `ThirdPartyCheckoutCoordinator.createOrder` | 封装 `createPayment`,得到 `orderId` + `payUrl` |
| `ThirdPartyCheckoutCoordinator.openPayUrlIfPresent` | 需传入宿主 `PaymentCheckoutUrlLauncher`WebView / `launchUrl` |
| `ThirdPartyPaymentWatch` | 宿主在打开收银台后调用 `start(orderId:)``stop()` 停止;终态只触发一次 `sink` |
| `NativeIapCoordinator.purchaseGooglePlay` | **仅 Android**`createPayment` → 拉起购买 → `googlepay` → consume`federation` 映射) |
**宿主策略**:根据 `ExtConfigRuntime.data``allowThirdPartyPayment`(或自研规则)选择调用 `ThirdPartyCheckoutCoordinator` + `ThirdPartyPaymentWatch`,或 `NativeIapCoordinator.purchaseGooglePlay`**不要在框架里写死分支**。
### 12.2 底层 API仍可直接用
1. 拉商品活动:`PaymentApi.getGooglePayActivities` / `getApplePayActivities`
2. 用户选档位 → `PaymentApi.getPaymentMethods(activityId: int)`
3. `PaymentApi.createPayment``PaymentApi.googlepay``PaymentService` 补单等:**编排类内部已组合**,单独对接时仍以 client_guide 为准。
---
## 13. 举报(反馈)
1. **入口**:仅从 **生图完成后的结果页**(已完成 / 可下载 / 单图)进入 `Y9WlO` 对应界面。
2. 可选图片:`FeedbackApi.getUploadPresignedUrl` → PUT 文件 → 得到 `fileUrl` 列表。
3. `FeedbackApi.submit(fileUrls:, content:, contentType:)``content` 可含任务 id、原因枚举等约定字段。
---
## 14. 注销账号
1. UI`SYt0O``yxwpg`
2. 调用 `UserApi.deleteAccount(app:, userId:)`GET**确认是否需额外 body/query**。
3. 清理:`ApiClient` 侧 token、本地 `SharedPreferences`、再 `runApp` 或导航到首页。
---
## 15. 主题、字体与资源
- 颜色/圆角:对齐 Pencil主强调色与 Tab 下划线可参考 `#c99304`
- 字体:`Inter` + `Bonheur Royale`(见历史卡片 Download 标签);在 `pubspec.yaml` 注册 `fonts:` 并放入 `assets/fonts/`
- 首页背景:设计使用 `首页.png`,可复制为 `assets/images/` 或完全用 `LinearGradient` 复刻 `suXxr`
---
## 16. 权限与合规
- **相册/存储**保存生成图、选图上传前申请权限iOS `NSPhotoLibraryUsageDescription` 等)。
- **网络**ATS / 明文规则按环境配置。
- **Adjust / Facebook**:按 skin_guide 填 `AndroidManifest.xml``Info.plist`
- **隐私政策 URL**:若 `common_info` / `h5UrlConfig` 下发,在个人中心打开。
---
## 17. 调试、环境与联调
- 切换预发布:`skin_config.api.alwaysUsePreBaseUrl: true``debugBaseUrlOverride`
- 抓包:注意请求体为代理包装后的 JSON**解密验证用 client_guide + 后端工具**。
- `Logger` / `AnalyticsService` 调试开关见 `skin_config.analytics.debugLogs`
---
## 18. 测试与上线检查清单
- [ ] 冷启动:无崩溃,`loginComplete` 后首屏可请求接口
- [ ] 生图全链路:创建 → 进度 → 结果 → 下载(或保存相册)
- [ ] 历史分页与 24h 展示正确
- [ ] 积分流水 `type`、分页正确
- [ ] 内购沙盒成功、积分刷新、Adjust 事件(若配置)
- [ ] 举报:仅结果页可见、提交成功
- [ ] 注销:账号不可用、本地状态清空
- [ ] 弱网 / 后台恢复 / 任务恢复
---
## 19. 导航关系
```mermaid
flowchart TB
Home[FunyMee Home]
GenFlow[生成图片→生成中]
GenResult[生图完成: 已完成/可下载/单图]
Hist[My History]
Credit[Credit Record]
Buy[Purchase Point]
Profile[个人中心]
Report[举报]
Del1[注销 步骤1]
Del2[注销 步骤2]
Home --> GenFlow
GenFlow --> GenResult
GenResult --> Report
Home --> Hist
Home --> Profile
Hist --- Credit
Profile --> Buy
Profile --> Del1 --> Del2
```
---
## 20. 外部文档索引
| 文档 | 用途 |
|------|------|
| [skin_app_development_guide.md](./skin_app_development_guide.md) | 换皮、Android/iOS、SDK、`AppAuthCallbacks` 模板 |
| [FunyMeeAI_client_guide.md](./FunyMeeAI_client_guide.md) | 代理/V2/接口细节、错误码 |
| [new_app_config_template.md](./new_app_config_template.md) | 配置模板说明 |
| `assets/skin_config.json` | 本应用实际配置 |
| `../client_proxy_framework/lib/src/services/*.dart` | API 源码与注释 |
---
## 21. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-04-07 | 初版:页面清单与启动流程 |
| 2026-04-07 | 举报入口:生图完成页 |
| 2026-04-07 | **完整版**API 总表、分屏规格、状态机、支付/举报/注销、`skin_config`、工程结构、测试清单;明确与 client_guide / skin_guide 的闭环关系 |
| 2026-04-07 | 框架增加 `ExtConfigData` / `ExtConfigRuntime`:解析 `go_run`/`screen`/`san_fang` 与旧键名common_info 成功后更新;手册 §5.5 |
| 2026-04-07 | `skin_config.extConfig``keys`/`itemKeys`/`defaults``ExtConfigKeySchema`;与 common_info 浅合并 |
| 2026-04-07 | 支付编排:`PaymentSettlementSink``ThirdPartyCheckoutCoordinator``ThirdPartyPaymentWatch``NativeIapCoordinator``CreatePaymentResponse.federation` |