修复定位的回调问题

This commit is contained in:
joywayer
2026-08-07 12:18:44 +08:00
parent 3f8b382ad3
commit 1203957924
12 changed files with 637 additions and 180 deletions
@@ -21,7 +21,7 @@
//
// ✅ Phase 4.E 微信:新版 SDK(2.x)无 static delegate API,逐次调用 handleOpenURL/sendAuthReq
// 时传入并被 SDK 弱引用;WeChatManager singleton 长生命周期,子游戏 pop 不析构,无需清理
// ✅ Phase 5 高德定位:`LocationService.shared.stop()`(已加,canImport 守卫,
// ✅ Phase 5 高德定位:`location.stop()`(子游戏私有实例,canImport 守卫,
// Xcode Embed AMapLocationKit 后激活)
// ⏳ Phase 3.B/C/D 录音 / 七牛上传:`AudioRecorder.shared.cancel()` 待 opencore-amr
// 接入后取消注释;`QiniuUploader.cancelInFlight()` 依赖 Task.cancel 传播无需显式调
@@ -34,7 +34,9 @@ import Foundation
public enum BackGameDataHandler {
public static func register(on bridge: any BridgeProtocol) {
/// - Parameter location: **子游戏自己的** LocationService(msext cleanUpAction
/// 只停 gameController 那一个 manager,不影响大厅)。
public static func register(on bridge: any BridgeProtocol, location: LocationService) {
// 【18】backgameData
// 入参 data(字符串,子游戏 H5 自定义透传给大厅 getWebdata)
// cb: "backgameData"
@@ -57,7 +59,7 @@ public enum BackGameDataHandler {
#if canImport(AMapLocationKit)
// msext gameController.m:2473-2478 cleanUpAction 等价
LocationService.shared.stop(caller: "backgameData")
location.stop(caller: "backgameData")
#endif
// Phase 3.C 录音 / Phase 3.B 七牛上传 清理(QiniuUploader.cancelInFlight 当前
@@ -25,46 +25,74 @@ private nonisolated let startLocLog = Logger(subsystem: "ylgamehall", category:
public enum StartLocationHandler {
/// - Parameter label: 容器标识(`lobby` / `subGame`),仅用于诊断日志。
public static func register(on bridge: any BridgeProtocol, label: String = "?") {
/// - Parameters:
/// - label: 容器标识(`lobby` / `subGame`),仅用于诊断日志。
/// - location: **本容器私有**的 LocationService。msext 大厅 / 子游戏各持一个
/// `AMapLocationManager`,不可共用(见 LocationService.init 注释)。
public static func register(on bridge: any BridgeProtocol,
label: String = "?",
location: LocationService) {
bridge.register("startlocation") { data, callback in
// 与 msext 行为一致:先回 cb 字面,不等定位完成
callback?(.string("startlocation"))
startLocLog.debug("[\(label, privacy: .public)] ← startlocation 入参 type=\(data?.asLooseString ?? "nil", privacy: .public)")
diagLog("[\(label)] ← startlocation 入参 type=\(data?.asLooseString ?? "nil")")
#if canImport(AMapLocationKit)
// SDK 已链接:拉起一次性定位 → 反向 callback 9 字段
// (持续定位 data == 1 暂未实现,业务实际只调一次性 — Phase 5.4 再扩展)
// msext NewRootVC.m:411-421 / gameController.m:533-543 的分支逐字等价:
// int tempinfo = [data intValue];
// if (tempinfo == 1) [self.locationManager startUpdatingLocation]; // 持续
// else [self reGeocodeAction]; // 单次
// 现网两个 H5 包(gamehall / jinxianmahjong)实测入参恒为 2,持续定位分支
// 是死代码;但 msext 有、我们缺就是原生侧的行为不一致,按 msext 补齐。
let isContinuous = Int(data?.asLooseString ?? "") == 1
Task { @MainActor in
do {
let payload = try await LocationService.shared.requestOnce(caller: label)
startLocLog.debug("[\(label, privacy: .public)] → getlocationinfo 成功 city=\(payload.city, privacy: .public) province=\(payload.province, privacy: .public)")
bridge.call("getlocationinfo", data: .object([
"address": .string(payload.address),
"city": .string(payload.city),
"cityCode": .string(payload.cityCode),
"country": .string(payload.country),
"district": .string(payload.district),
"latitude": .string(payload.latitude), // ← string
"longitude": .string(payload.longitude), // ← string
"province": .string(payload.province), // ← 小写 p
"street": .string(payload.street)
]), callback: nil)
} catch LocationError.authorizationDenied {
// 契约失败回包(msext gameController.m:2507 等价)
startLocLog.error("[\(label, privacy: .public)] → getlocationinfo 失败:authorizationDenied")
bridge.call("getlocationinfo", data: .object([
"errorCode": .number(12),
"errorMsg": .string("缺少定位权限")
]), callback: nil)
} catch {
// 其它失败也归一到 12(msext 行为)
startLocLog.error("[\(label, privacy: .public)] → getlocationinfo 失败:\(String(describing: error), privacy: .public)")
bridge.call("getlocationinfo", data: .object([
"errorCode": .number(12),
"errorMsg": .string("缺少定位权限")
]), callback: nil)
// 单次路径下一次请求可能产出两条 outcome(msext completionBlock 的原始
// 控制流);持续路径下每次定位更新都会回一条 .success。
let dispatch: @MainActor (LocationOutcome) -> Void = { outcome in
switch outcome {
case .failure:
// msext gameController.m:2507 等价
diagLog("[\(label)] → getlocationinfo errorCode=12")
bridge.call("getlocationinfo", data: .object([
"errorCode": .number(12),
"errorMsg": .string("缺少定位权限")
]), callback: nil)
case .success(let payload):
// msext gameController.m:2528 的 9 字段 + `errorCode: 0`。
//
// ⚠️ 契约影响(docs/H5-Native-Contract.md §3.2 表 B):
// msext 全工程只在**失败**分支发 `errorCode: 12`,成功分支
// 9 个字段里**没有** errorCode —— 这是原工程的遗漏。
// H5 侧以 `errorCode == 0` 作为"定位数据有效"的判据:
// 子游戏 jinxianmahjong 的 05_Func.js(2026-07-19)在
// `Func.startlocation` / `Func.getlocation` 的兜底桩里
// 自造的定位对象就带 `"errorCode":0`;而大厅 gamehall 的
// 05_Func.js(2026-02-04)整份文件都没有 errorCode。
// 这正是"大厅定位正常、子游戏拿不到"的原生侧成因:成功包缺
// errorCode 时子游戏 H5 不认这份数据。
// 按项目方决定补齐(此处是**有意偏离** msext 的一处,其余
// 定位链路仍严格对齐)。
diagLog("[\(label)] → getlocationinfo 成功 city=\(payload.city) province=\(payload.province)")
bridge.call("getlocationinfo", data: .object([
"errorCode": .number(0),
"address": .string(payload.address),
"city": .string(payload.city),
"cityCode": .string(payload.cityCode),
"country": .string(payload.country),
"district": .string(payload.district),
"latitude": .decimal(payload.latitude), // ← 数字(非字符串),6 位小数
"longitude": .decimal(payload.longitude), // ← 数字(非字符串),6 位小数
"province": .string(payload.province), // ← 小写 p
"street": .string(payload.street)
]), callback: nil)
}
}
if isContinuous {
location.startContinuous(caller: label, emit: dispatch)
} else {
location.requestOnce(caller: label, emit: dispatch)
}
}
#endif