14 KiB
14 KiB
项目名称, 一级部门, 二级部门, DMP编码, 项目类型, 开发语言, 项目负责人, 运维负责人, 产品负责人, 项目成员, 项目状态, 项目级别, 核心分支, 代码仓库, 最后审计人, 最后审计时间, 告警群, 备注
| 项目名称 | 一级部门 | 二级部门 | DMP编码 | 项目类型 | 开发语言 | 项目负责人 | 运维负责人 | 产品负责人 | 项目成员 | 项目状态 | 项目级别 | 核心分支 | 代码仓库 | 最后审计人 | 最后审计时间 | 告警群 | 备注 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 91porn | 北斗 | 瑞升 | APP | Dart | DN5368 | DN9460,DN9651,DN4792 | YY1145,YY1208 | DN5368,DN1180 | 开发中 | 高 | main |
|
DN5368 | 2026-08-06 | tg://blibli-alert-group | Flutter 客户端(Android + iOS 双端),当前版本 1.0.0,Android 包名 com.noporn.newchatone_twelve_one(版本号变更需同步替换) |
1. 项目说明
91porn 移动客户端(Android + iOS 同一套代码),基于 Flutter 3.38.3 + Dart 3.10.0,面向 C 端用户的视频/社区/AI/暗网综合内容 APP。
核心模块:
- 首页 (
hj_page/home/):视频/短视频/漫画/动漫等多 Tab 内容流,含搜索、片库、排行 - 发现 (
hj_page/find/):榜单、分类、片单 - AI (
hj_page/mine/ai/):AI 换脸、AI 脱衣、文生图、图生视频、文生小说等付费功能 - 会员/支付 (
hj_page/mine/mine_vip/、hj_page/pre_sale/):VIP 购买、金币充值、代理充值、预售 - 个人中心 (
hj_page/user_center_page/、hj_page/mine/):用户主页、客服、QA、设置 - 社区 (
hj_page/community/):帖子、评论、互动 - IM (
im_manager/):自研私信/会话,无三方 IM SDK(会话列表、聊天详情、消息通知、聊天页广告位) - 暗网 (
hj_page/darkweb/):分类专区,独立入口与 VIP 体系 - 直播 (
hj_page/live/):直播入口 - 在线客服 (
hj_page/zaixiankefu/):H5 客服对接 - 播放器:基于 video_player + ffmpeg_kit_flutter_new_https + 自研 m3u8_downloader
- 视频下载:Android 走
m3u8_downloader原生 isolate;iOS 走IOSVideoDownloader(用 ffmpeg 拉远程 m3u8 转 mp4 自动入相册,命令-c copy -bsf:a aac_adtstoasc -movflags +faststart,不重编码)
技术栈:
- 状态管理:GetX (
GetxController+GetBuilder) + Provider(全局状态) - 网络:Dio + 自研
httpManager+ RSA 请求加密 - 屏幕适配:flutter_screenutil(设计稿 428×927)
- 路由:GetX 路由
- 本地存储:MMKV + shared_preferences
- 视频下载:
m3u8_downloader-master(本仓库内) - 调试工具:ume_core + ume_kit_dio / ume_kit_perf / ume_kit_ui(均取自 pub,不再本地维护)
项目目录结构:
hgdj/
├── lib/
│ ├── alert/ # 全局弹窗
│ ├── assets_tool/ # 颜色、图片、资源路径
│ ├── config/ # 配置常量(域名、版本、AB 等)
│ ├── extension/ # Dart 扩展方法
│ ├── hj_model/ # 数据模型(手写 fromJson)
│ ├── hj_page/ # 业务页面(按模块分目录)
│ ├── hj_utils/ # 工具方法 + API service
│ ├── im_manager/ # 自研 IM(会话/聊天页/通知)
│ ├── routers/ # 路由定义与跳转
│ ├── tools_base/ # 基础工具(网络/事件/loading/toast/refresh/缓存)
│ ├── track_event_manager/ # 设备信息 / 请求头 sid 会话
│ └── main.dart # 入口
├── android/ # Android 工程
├── ios/ # iOS 工程
├── assets/ # 静态资源(图片/flare/字体)
├── m3u8_downloader-master/ # 自研 m3u8 下载库
├── third_party/ # 三方原生库
├── dist/ # 打包产物输出目录
├── build_apk.sh # 双端打包脚本(apk / ipa)
└── pubspec.yaml
2. 基础设施与中间件
- 后端服务:91porn-server(Go,独立仓库)— 提供 APP API、Web 管理、Skd 任务
- CDN:视频/图片/动漫资源分发(Cloudfront + 自有域名多线路)
- 推送:Telegram Bot 客服群对接
- 客户端依赖:
- Dio:HTTP 网络层
- cached_network_image:图片缓存
- mmkv / shared_preferences:本地 KV
- ffmpeg_kit_flutter_new_https:视频处理(社区维护 fork,自托管二进制带 SSL,原 minimal 包二进制已下架)
- flutter_inappwebview:H5 嵌入(充值/客服/活动页)
- permission_handler:权限申请
- device_info_plus / package_info_plus:设备信息
- flutter_native_image / image_pickers:图片选择压缩
3. 运维部署
客户端通过 build_apk.sh 本地打包后分发,不走 K8S/CI 流水线。
发布流程:
-
切到
main分支,更新pubspec.yaml版本号 +Config.innerVersion+local.properties+ applicationId(按版本号转换规则) -
确认
Config.isDebug = false(开发分支常年为true,最容易漏) -
打包(脚本自动检测 FVM PATH,产物统一复制至
dist/):sh build_apk.sh <target> [mode] [export_method] [export_options_plist] [sign_mode] target a(android) | i(ios) | all mode release(默认) | debug | profile export_method (iOS) app-store(默认) | ad-hoc | development | enterprise export_options_plist ExportOptions.plist 路径,留空用默认 sign_mode (iOS) codesign(默认) | nocodesign常用:
sh build_apk.sh a(Android release,arm64 单 ABI)、sh build_apk.sh i release ad-hoc(iOS 内测包)、sh build_apk.sh all -
产物命名(
APP_NAME取自pubspec.yaml的name,即hgdj):- Android:
hgdj_<mode>_<version>_<MMdd-HHmm>.apk - iOS:
hgdj_<mode>_<exportMethod>_<version>_<MMdd-HHmm>.ipa,如hgdj_release_adhoc_1.0.0_0806-0925.ipa
- Android:
-
Android 发版前跑 VirusTotal 包毒检测
-
上传到分发平台(落地页 / 蒲公英 / 自有 CDN),Telegram 告警群通知
真机调试(不出正式包):flutter build ios --release && flutter install --release -d <deviceId>,Xcode 自动签名,Team NWP977LR25。
回滚方案:
- 仅客户端无服务端回滚概念
- 旧版本安装包保留在分发平台,用户可重新下载
- 服务端通过
Config.dns接口下发动态域名实现服务端切换
4. 风险说明
- 包名风险:
com.noporn.newchatone_twelve_one与版本号强绑定,每次升版需替换 applicationId(数字转英文单词规则),遗漏会导致 push/支付失败 - 加密密钥风险:
Config.encryptKey/ANTI_REPLAY_ATTACK_KEY/Config.appId = JHA-204泄漏会被刷接口 - 包毒检测:APK 容易被部分杀毒软件误报,需走 VirusTotal 检测后再分发
- 包体积:当前接入 ffmpeg_kit_flutter_new_https(带 SSL,比 minimal 大 +5~10MB)/ video_player / 多个三方 SDK,需定期
analyze-size控制体积 - iOS 最低系统版本:Podfile + Runner.xcodeproj 锁定 15.0(Xcode 16+ 要求 + ffmpeg 7.x 要求),低于此版本无法安装
- Android SDK 版本:minSdk 24 / targetSdk 36,targetSdk 升级需重新验证权限与后台限制
- 三方依赖:image_pickers / m3u8_downloader 为内部维护,Flutter 升级时需同步适配
- 原生插件升级:
flutter pub get+flutter analyze全过也不代表能用,含原生代码的插件必须真机 build 验证(audioplayers 升 v6 即栽在这里) - release-only 缺陷:混淆 + strip + 独立 manifest 让 release 有一整类 debug 不复现的问题(详见第 5 节),每次发版必须真机跑 release 包,不能只测 debug
- 域名兜底:
Config.dns = 91porn.zmnchs.com走运维下发,运维接口挂掉会导致 APP 启动失败 - Flutter 版本锁定:3.38.3 强制 fvm,避免系统 SDK 切换触发 flutter_ume / leak_tracker 冲突
- 暗网模块:
Config.darkWebEnable通过运维下发开关,下发异常会暴露入口 - DEBUG 标志:
Config.isDebug切换会影响lineList兜底线路与日志输出,正式包必须false(开发分支当前为true)
5. 历史事故(可选)
5.1 release 包专有(debug 一律不复现,最贵的一类)
debug 与 release 在混淆、符号 strip、manifest 合并三处行为不同,以下问题只能靠真机 release 包发现:
| 现象 | 根因 | 修复 |
|---|---|---|
| iOS 启动卡死 / 本地存储静默读不到 | FFI(MMKV)符号被 strip 掉,运行时找不到 | STRIP_INSTALLED_PRODUCT = NO(只设 STRIP_STYLE = non-global 不够) |
| Android 安装后直接闪退(SIGSEGV) | 含 native 方法的插件(ffmpeg-kit)被 R8 混淆,RegisterNatives 失败 |
proguard 补 -keep;新增带 JNI 的插件都要补 |
| 启动弹「线路检测失败」,host lookup errno=7 | INTERNET 权限只声明在 debug/profile 的 manifest,src/main 漏了 |
权限写进 src/main/AndroidManifest.xml,别指望三方插件替你声明 |
| 页面灰屏 / 列表静默空白 | 用 T.toString() == '类名' 做分支,混淆后类名变了必然失配 |
一律改 T == 类型(比较 Type 对象,不碰名字字符串) |
5.2 其他
- 视频下载进度不刷新(退出重进才更新):本仓库内
m3u8_downloader的callbackDispatcher缺@pragma('vm:entry-point'),后台 isolate 起不来。教训:本地插件不是黑盒,出问题要读它的源码 - 刷新控制器双重释放崩溃:
refreshCtr归CustomRefreshView释放,logic / 子 widget 又 dispose 一次 →headerMode!空断言崩在 unmount 阶段 → 中断整棵树的卸载并连带泄漏。规则:只释放自己 new 的 - dio 5.9 偶发
DioException[unknown] Null check operator:仅 CDN 接口偶发,根因不可复现(已证伪 adapter / FusedTransformer 两个方向),用幂等 GET 的RetryInterceptor自愈 - 截图保存「提示成功但相册没有」:
RepaintBoundary套在SingleChildScrollView内被裁剪,截出空图。改用Center + FittedBox完整渲染,且调用处必须await判断结果 - 设置锁屏密码当次不生效、重启后才生效:把内部可变 list 直接交给未
await的异步回调,回调在await之后读到的已是被 clear 的空 list(内存态空、持久化态有值)。传副本List<int>.of(x)修复 - audioplayers 无法升 v6:
pub get/analyze全过但 Android 侧 build 挂(5.2.1 与 platform_interface 7.1.1 错配 + Kotlin 2.1),已停在 v5.2.1 pull_to_refresh升级后回弹手感夸张(Flutter 3.16.8 → 3.38.3):改pull_refresh.dart参数解决;不迁移 EasyRefresh——logic 层有 305 处依赖 SmartRefresher 专有 API- 华为多任务卡片隐私遮盖失败:系统截快照早于 Flutter 生命周期回调,任何遮盖层(View / 独立 Window)都盖不住,也无法把黑屏换成自定义图。结论:只能用
FLAG_SECURE
6. 交接说明(可选)
- Flutter 版本:必须使用
fvm flutter,版本锁定 3.38.3(见.fvmrc),build_apk.sh已内置 FVM PATH 检测 - 打包:
build_apk.sh a打 Android,build_apk.sh i打 iOS,build_apk.sh all双端;完整参数见第 3 节 - applicationId 规则:
com.noporn.newchat+<major>_<minor>_<patch>(数字转英文),如 1.0.0 →com.noporn.newchatone_twelve_one - 版本号同步点:
pubspec.yaml/Config.innerVersion/android/local.properties/android/app/build.gradle四处必须一致 - 加密密钥:RSA 公私钥位于 Android 端 native 层 + Dart 层,调整时需双端同步
- AB 配置:
Config类中集中维护(aiSwitchConf/showAbTestAd等),新增 AB 需走运维下发 - 客服地址:
/customer/url接口动态下发完整 URL,前端不再拼baseHost - provider 注入:
main.dart中MultiProvider注入若干ChangeNotifier,新增全局状态走该处 - 图片资源:
assets_tool/统一管理路径扩展(.commonImgPath/.homePath/.darkWebPath等) - 域名错误兜底:DNS 接口失败时降级到
Config.LINE_LIST本地兜底 - 网络层异常约定:dio 异常已被网络层吞成
BaseRespBean(业务层不要写DioExceptioncatch),但请求前的签名加密会抛,需 try 兜底;拦截器只在「有响应但失败」时弹 toast,超时/断网(code 2001-2003)不弹,要各自兜底 - 列表分页:统一继承
tools_base/base_list_controller.dart的ListBaseLogic(配pullYsRefresh),别自己重写分页 - 支付下单:走
PayManager().buy / buyVip(含 loading、余额不足跳充值、回调、source 透传),不要直接调BuyService - 弹窗:提示类统一用
tools_base/widget/common_alert.dart;「停留 N 分钟后满足条件才弹」走TimedPopupManager(onClose必须cancel,否则泄漏) - 包毒检测:发版前必须用
/check-apk-virus跑 VirusTotal - 包体积分析:使用
apk-analyzerskill 跑flutter build apk --analyze-size