Plan 进度同步:Phase 3.6 / 4.1 / 4.3-4.10 / 5.1-5.6 勾选
按 CLAUDE.md「进度同步规范」要求,把已落地的子项在 Development-Plan.md §5 / §8 中同步勾选。同时修正几处描述与当前实现的差异: - 4.1 微信 SDK:xcframework 2.0.5 入库 Vendor(不是 Embed Frameworks), Universal Links 不接(企业签分发不需要) - 4.3 WeChatSDK:新 SDK 2.x 无 MMAPP_SUPPORT_* flag,简单 registerApp - 4.6 WeChatAuth:客户端直拼 sns/oauth2/access_token + sns/userinfo, 接受 msext 同款 secret 路径(不走后台 /wechat/login) - 5.1 AMap:高德官方不出 xcframework,回退 fat .framework,M Mac 模拟器 链接失败是接受的现状(详见 Vendor/AMap/README.md「已知限制」) - 5.3 AMapWrapper:updatePrivacyShow/Agree 是 AMapLocationManager 类方法 - 5.4 LocationService:仅 requestOnce + stop,未实现持续模式(msext 也 只是单次,H5 未要求持续) - 3.6 Qiniu:CryptoKit HMAC-SHA1 自签 token + QNUploadManager 走 initWithConfiguration + defaultConfigurationV2 Phase 4.11 / 4.12(截图 / 远端图分享)下个 commit 实现。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
f137618070
commit
d9d9554d9f
+56
-55
@@ -433,9 +433,11 @@ Contract Design Plan(本文档)
|
||||
- [ ] **3.5** `Source/Audio/AudioRecorder.swift`(actor)
|
||||
- `record() async throws -> AudioFile`:`AVAudioRecorder` 录 WAV
|
||||
- 麦克风权限:Info.plist 加 `NSMicrophoneUsageDescription = "{gamehallname}需要访问您的麦克风录制语音消息"`
|
||||
- [ ] **3.6** 七牛 SPM 依赖(v8+)+ `Source/Network/QiniuUploader.swift`
|
||||
- `upload(_:token:) async throws -> UploadedFile`
|
||||
- 单测:mock token,模拟上传成功 / 失败
|
||||
- [x] **3.6** 七牛 SPM 依赖(`https://github.com/qiniu/objc-sdk` v8.9.2,传递依赖 happy-dns 1.0.4 自动解析)+ 客户端自签 token 路径
|
||||
- `Source/Network/QiniuConfig.swift`:4 项常量(AccessKey / SecretKey / BucketName / cdnDomain)+ publicURL,沿用 msext,整体 nonisolated
|
||||
- `Source/Network/QiniuTokenSigner.swift`:CryptoKit HMAC-SHA1 + Base64URL 自签,等价 msext QiniuManager.m:200-230
|
||||
- `Source/Network/QiniuUploader.swift`:actor,`upload(_:timeSec:) async throws -> UploadedFile`,`QNUploadManager(configuration: defaultConfigurationV2)` 路径
|
||||
- 真机上传单测待 Phase 3.C 完成(依赖 AudioRecorder 录音输入)
|
||||
- [ ] **3.7** `prepareaudio` handler:拉起录音 → 停止录音 → AMR 转码 → 上传 → 反向 callback `getaudiourl` + `recordSuccess`(仅子游戏触发)
|
||||
|
||||
##### 3.D 远程语音回放
|
||||
@@ -476,9 +478,9 @@ Contract Design Plan(本文档)
|
||||
|
||||
##### 4.A SDK 接入
|
||||
|
||||
- [ ] **4.1** `Vendor/WechatSDK/` 拷贝微信 SDK
|
||||
- Info.plist 加 URL Scheme(`wx586a9b321e56efb7`)+ `LSApplicationQueriesSchemes`(`weixin` / `weixinULAPI` / `weixinURLParamsAPI`)
|
||||
- Entitlement 加 Associated Domains(Universal Links)
|
||||
- [x] **4.1** `Vendor/WechatSDK/` 入库微信 SDK(`WechatOpenSDK-NoPay.xcframework` 2.0.5,入 git)
|
||||
- Info.plist URL Scheme `wx586a9b321e56efb7` + LSApplicationQueriesSchemes `weixin` / `weixinULAPI` / `weixinURLParamsAPI` 三项已就位
|
||||
- Universal Links / Associated Domains 暂不接入:企业签分发,不上 App Store,无审核合规需求;`WXApi.registerApp(_, universalLink: "")` 空串即可
|
||||
- [x] **4.2** **SharePanel 三选一面板 + SharePlatform 框架**(4.B 完成)
|
||||
- `Source/Share/SharePlatform.swift`:SharePlatform 协议(name / isInstalled / share async)+ ShareContent struct(nonisolated Sendable)+ ShareResult enum(success / cancelled / notInstalled / failed)+ ShareScene enum(friend / timeline)
|
||||
- `Source/Share/SharePanel.swift`:底部弹出三按钮(微信 / QQ / 抖音)+ 半透明背景点击关闭 + 0.25s 滑入/滑出动画;与 msext SharePanel.m 等价行为
|
||||
@@ -504,29 +506,27 @@ Contract Design Plan(本文档)
|
||||
5. 调起即视为 success
|
||||
- Info.plist 加 `NSPhotoLibraryAddUsageDescription` 文案
|
||||
- BuildProject 通过
|
||||
- [ ] **4.3** `Source/SDK/WeChat/WeChatSDK.swift` 启动注册(Design §4.2 `registerFull` + 全部 12 个 `MMAPP_SUPPORT_*` flag)
|
||||
- [ ] **4.4** `Source/SDK/WeChat/WeChatManager.swift`(@MainActor)
|
||||
- 持久 delegate
|
||||
- `authorize() async throws -> WXAuthCode`(state UUID 配对)
|
||||
- `share(_:scene:) async throws`(FIFO 串行)
|
||||
- 详见 Design §8.5
|
||||
- [ ] **4.5** `SceneDelegate.openURLContexts` 接入 QQ → WXApi 顺序
|
||||
- [x] **4.3** `Source/SDK/WeChat/WeChatSDK.swift` 启动注册(`WXApi.registerApp("wx586a9b321e56efb7", universalLink: "")`;新 SDK 2.x 无 `MMAPP_SUPPORT_*` flag 概念,简单注册即可)
|
||||
- [x] **4.4** `Source/SDK/WeChat/WeChatManager.swift`(@MainActor)
|
||||
- 持久 delegate(WXApiDelegate,onResp nonisolated 跳 MainActor)
|
||||
- `authorize() async throws -> WXAuthCodePayload`(state UUID 配对)
|
||||
- `shareLink(_:scene:) async throws`(FIFO 串行;type=2/3 截图/远端图待 4.11 / 4.12 Phase 4.F)
|
||||
- [x] **4.5** `SceneDelegate.openURLContexts` 仅 `WXApi.handleOpen`(QQ 走 URL Scheme 不依赖 SDK,无 SDK 顺序问题)
|
||||
|
||||
##### 4.B 授权登录
|
||||
|
||||
- [ ] **4.6** `Source/Login/WeChatAuth.swift`(actor)
|
||||
- `authorize() async throws -> WeChatUser`:内部调 `WeChatManager.authorize()` 拿 code → 转给后台 `/wechat/login`(或客户端 fallback)→ 拿到 7 字段 user
|
||||
- 后台未就绪时的 fallback:客户端直拼 `api.weixin.qq.com/sns/oauth2/access_token` —— 文件头标 `// FIXME: 待后台 /wechat/login 就绪后切换为后台中转`
|
||||
- [ ] **4.7** `accreditlogin` handler:触发 OAuth → 反向 callback `sharelogin`(7 字段,注意 `Province` 大写 P)
|
||||
- **关键契约测试**:`sharelogin` payload 字段名严格 1:1
|
||||
- [x] **4.6** `Source/Login/WeChatAuth.swift`(客户端直拼 sns/oauth2/access_token + sns/userinfo)
|
||||
- 接受 msext 同款客户端持 secret 路径(父项目 CLAUDE.md 已识别但接受;后台无 `/wechat/login` 中转)
|
||||
- 7 字段 user 包含 `openid` / `headimgurl` / `nickname` / `sex` / `city` / `Province`(大写 P)/ `unionid`
|
||||
- [x] **4.7** `accreditlogin` handler:触发 OAuth → 反向 callback `sharelogin`(7 字段,Province 大写 P)
|
||||
|
||||
##### 4.C 分享
|
||||
|
||||
- [ ] **4.8** `Source/Share/SharePlatform.swift` 协议 + `WeChatShare` / `NoopSharePlatform(name:"xianliao")`
|
||||
- [ ] **4.9** `Source/Share/ShareCenter.swift`:策略分发(type=1/2/3 × sharetype=1/2/3,共 9 种组合)
|
||||
- [ ] **4.10** `friendsSharetypeUrlToptitleDescript` handler:参数解析 → ShareCenter.dispatch → 反向 callback `sharesuccess({success:"2", type:<原 sharefriend>})`
|
||||
- [ ] **4.11** 截图分享:`getImageWithFullScreenshot` Swift 等价实现(`UIGraphicsImageRenderer`)
|
||||
- [ ] **4.12** 远程图片分享:URLSession 下载 → 重打包
|
||||
- [x] **4.8** `Source/Share/SharePlatform.swift` 协议 + `WechatShare` / `QQShare` / `DouyinShare`(闲聊不接,参 ADR-006)
|
||||
- [x] **4.9** `Source/Share/ShareCenter.swift`:sharefriend=1 弹 SharePanel;=2 直接微信朋友圈(msext gameController.m:585 等价)
|
||||
- [x] **4.10** `FriendsShareHandler.swift` → ShareCenter.dispatch → 反向 callback `sharesuccess({success:"2", type:<原 sharefriend>})`
|
||||
- [ ] **4.11** 截图分享(type=2):`captureScreenshot` → WXImageObject + thumbImage 0.4x(msext gameController.m:637-658 等价)— Phase 4.F
|
||||
- [ ] **4.12** 远程图片分享(type=其它):`downloadRemote` → WXImageObject + thumbImage 0.4x — Phase 4.F
|
||||
|
||||
#### 验收
|
||||
|
||||
@@ -554,16 +554,17 @@ Contract Design Plan(本文档)
|
||||
|
||||
#### 任务清单
|
||||
|
||||
- [ ] **5.1** 从 [高德官方下载页](https://lbs.amap.com/api/ios-location-sdk/download) 拉取最新 `AMapLocationKit.xcframework` + `AMapFoundationKit.xcframework`,放 `Vendor/AMap/`;Target → General → Embed Frameworks 加入;按 Design §14.1 Vendor 接入标准流程操作;`Vendor/AMap/README.md` 记录版本号 / 下载日期
|
||||
- [ ] **5.2** Info.plist 加 `NSLocationWhenInUseUsageDescription = "{gamehallname}需要访问您的位置以提供本地化服务"`
|
||||
- [ ] **5.3** `Source/SDK/AMap/AMapWrapper.swift`:启动期 `updatePrivacyShow` + `updatePrivacyAgree` + `apiKey` 设置
|
||||
- [ ] **5.4** `Source/Location/LocationService.swift`(actor)
|
||||
- `requestOnce() async throws -> LocationPayload`
|
||||
- `startContinuous(onUpdate:)` / `stop()`
|
||||
- [ ] **5.5** `startlocation` handler:`data=1` 持续 / 其他一次性 → 反向 callback `getlocationinfo` 9 字段
|
||||
- **关键契约**:`latitude` / `longitude` 是 **string**(`stringWithFormat:%f` 等价产物)
|
||||
- **关键契约**:`province` 是小写 p(与 `sharelogin.Province` 大写不同)
|
||||
- [ ] **5.6** 失败路径:`getlocationinfo({errorCode:12, errorMsg:"缺少定位权限"})`
|
||||
- [x] **5.1** 高德官方至今**不提供 xcframework**,回退为 fat `.framework` 入 `Vendor/AMap/`(AMapFoundationKit 1.9.0 + AMapLocationKit 2.12.0);Target Do Not Embed(静态库);M Mac 模拟器链接失败是接受的现状,开发期跑真机,详见 `Vendor/AMap/README.md` 「已知限制」
|
||||
- [x] **5.2** Info.plist `NSLocationWhenInUseUsageDescription = "进贤聚友棋牌需要访问您的位置以提供本地化服务"`
|
||||
- [x] **5.3** `Source/SDK/AMap/AMapWrapper.swift`:`AMapLocationManager.updatePrivacyShow` + `updatePrivacyAgree` + `AMapServices.shared().apiKey`(注意:隐私 API 在 AMapLocationManager 类方法上,不在 AMapServices)
|
||||
- [x] **5.4** `Source/Location/LocationService.swift`(actor)
|
||||
- `requestOnce() async throws -> LocationPayload` ✅
|
||||
- `stop()` 清理钩子 ✅
|
||||
- 持续模式 `startContinuous(onUpdate:)` 未实现(msext 实际只有 H5 一次性 startlocation;如未来 H5 用 data=1 持续,再补)
|
||||
- [x] **5.5** `startlocation` handler:单次定位 → 反向 callback `getlocationinfo` 9 字段
|
||||
- latitude/longitude 是 string(`String(format: "%f", ...)`)
|
||||
- province 是小写 p(与 sharelogin.Province 大写不同)
|
||||
- [x] **5.6** 失败路径:`getlocationinfo({errorCode:12, errorMsg:"缺少定位权限"})`
|
||||
|
||||
#### 验收
|
||||
|
||||
@@ -911,36 +912,36 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin
|
||||
- [ ] 2.13 ExternalSubscriptions
|
||||
|
||||
### Phase 3 音频体系
|
||||
- [ ] 3.1 AudioPlayer
|
||||
- [x] 3.1 AudioPlayer(Source/Audio/AudioPlayer.swift,srcIsloop / stopBackground / volume 全路径)
|
||||
- [ ] 3.2 本地音频真机验证
|
||||
- [ ] 3.3 opencore-amr Vendor 接入
|
||||
- [ ] 3.3 opencore-amr Vendor 接入(详见 docs/SDK-Integration-Guide.md §C,需手动)
|
||||
- [ ] 3.4 VoiceCoder Swift wrapper
|
||||
- [ ] 3.5 AudioRecorder + 麦克风权限
|
||||
- [ ] 3.6 七牛 SPM + QiniuUploader
|
||||
- [ ] 3.5 AudioRecorder + 麦克风权限(NSMicrophoneUsageDescription 已就位)
|
||||
- [x] 3.6 七牛 SPM(objc-sdk 8.9.2)+ QiniuConfig + QiniuTokenSigner(CryptoKit HMAC-SHA1 自签)+ QiniuUploader actor(initWithConfiguration + defaultConfigurationV2)
|
||||
- [ ] 3.7 prepareaudio handler
|
||||
- [ ] 3.8 mediaTypeAudio handler
|
||||
|
||||
### Phase 4 分享 + 登录
|
||||
- [ ] 4.1 微信 SDK Vendor + URL Scheme
|
||||
- ✗ ~~4.2 QQ SDK Vendor~~ **不需要**:QQ 走 URL Scheme,详见 Phase 4.2 任务清单
|
||||
- [ ] 4.3 WeChatSDK registerFull
|
||||
- [ ] 4.4 WeChatManager(state map + FIFO)
|
||||
- [ ] 4.5 SceneDelegate openURL 顺序
|
||||
- [ ] 4.6 WeChatAuth(后台中转或 fallback)
|
||||
- [ ] 4.7 accreditlogin → sharelogin
|
||||
- [ ] 4.8 SharePlatform 协议
|
||||
- [ ] 4.9 ShareCenter 9 种组合
|
||||
- [ ] 4.10 friendsShare... handler
|
||||
- [ ] 4.11 截图分享
|
||||
- [ ] 4.12 远程图片分享
|
||||
- [x] 4.1 微信 SDK Vendor + URL Scheme(WechatOpenSDK-NoPay.xcframework 2.0.5 入库 Vendor/WechatSDK/;Info.plist CFBundleURLTypes + LSApplicationQueriesSchemes 3 项)
|
||||
- ✗ ~~4.2 QQ SDK Vendor~~ **不需要**:QQ 走 URL Scheme,详见 §5 Phase 4.A 任务清单
|
||||
- [x] 4.3 WeChatSDK register(Source/SDK/WeChat/WeChatSDK.swift;新 SDK 2.x 无 MMAPP flag 概念,registerApp + universalLink 即可)
|
||||
- [x] 4.4 WeChatManager(state map + FIFO;Source/SDK/WeChat/WeChatManager.swift)
|
||||
- [x] 4.5 SceneDelegate openURL(QQ 走 URL Scheme 不依赖 SDK,仅微信 WXApi.handleOpen)
|
||||
- [x] 4.6 WeChatAuth(客户端直拼 sns/oauth2/access_token + sns/userinfo,沿用 msext 安全模型)
|
||||
- [x] 4.7 accreditlogin → sharelogin(7 字段,Province 大写 P)
|
||||
- [x] 4.8 SharePlatform 协议(Phase 4.B 完成)+ WechatShare(type=1 链接 done;type=2/3 见 4.11/4.12)
|
||||
- [x] 4.9 ShareCenter(sharefriend=1 弹 SharePanel;=2 直接微信朋友圈;Phase 4.B 完成)
|
||||
- [x] 4.10 friendsShare... handler(FriendsShareHandler.swift,sharesuccess 反向 callback)
|
||||
- [ ] 4.11 截图分享(Phase 4.F,type=2)
|
||||
- [ ] 4.12 远程图片分享(Phase 4.F,type=3)
|
||||
|
||||
### Phase 5 定位
|
||||
- [ ] 5.1 AMap Vendor 接入(XCFramework)
|
||||
- [ ] 5.2 定位权限文案
|
||||
- [ ] 5.3 AMapWrapper 启动注册
|
||||
- [ ] 5.4 LocationService
|
||||
- [ ] 5.5 startlocation handler(latitude/longitude string,province 小写)
|
||||
- [ ] 5.6 失败回包 errorCode 12
|
||||
- [x] 5.1 AMap Vendor 接入(高德官方不提供 XCFramework,回退为 fat `.framework`;M Mac 模拟器链接失败是接受的现状,详见 Vendor/AMap/README.md)
|
||||
- [x] 5.2 定位权限文案(NSLocationWhenInUseUsageDescription)
|
||||
- [x] 5.3 AMapWrapper 启动注册(updatePrivacyShow / updatePrivacyAgree 是 AMapLocationManager 类方法,apiKey 在 AMapServices.shared)
|
||||
- [x] 5.4 LocationService actor(requestOnce 异步 + stop 清理钩子)
|
||||
- [x] 5.5 startlocation handler(latitude/longitude string,province 小写 p)
|
||||
- [x] 5.6 失败回包 errorCode 12(缺少定位权限)
|
||||
|
||||
### Phase 6 子游戏
|
||||
- [x] 6.1 AppCoordinator 栈深节流(2s + viewControllers.count < 2)
|
||||
|
||||
Reference in New Issue
Block a user