diff --git a/suixinkan/Features/Location/LocationProvider.swift b/suixinkan/Features/Location/LocationProvider.swift index 1c94c3e..e872f2d 100644 --- a/suixinkan/Features/Location/LocationProvider.swift +++ b/suixinkan/Features/Location/LocationProvider.swift @@ -20,12 +20,12 @@ final class LocationProvider: NSObject, LocationProviding { permissionManager.desiredAccuracy = kCLLocationAccuracyBest } - func requestSnapshot() async throws -> HomeLocationSnapshot { + func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot { try AMapBootstrap.requireConfigured() try await ensureLocationPermission() let manager = makeLocationManager( - desiredAccuracy: kCLLocationAccuracyBest, + desiredAccuracy: desiredAccuracy, locatingWithReGeocode: true ) return try await withCheckedThrowingContinuation { continuation in diff --git a/suixinkan/Features/Location/LocationProviding.swift b/suixinkan/Features/Location/LocationProviding.swift index 45b3990..182c2b7 100644 --- a/suixinkan/Features/Location/LocationProviding.swift +++ b/suixinkan/Features/Location/LocationProviding.swift @@ -15,8 +15,8 @@ struct HomeLocationSnapshot: Sendable, Equatable { /// 定位服务协议,便于 ViewModel 注入与单元测试 mock。 protocol LocationProviding: Sendable { - /// 请求当前坐标与地址。 - func requestSnapshot() async throws -> HomeLocationSnapshot + /// 按指定精度请求当前坐标与地址。 + func requestSnapshot(desiredAccuracy: CLLocationAccuracy) async throws -> HomeLocationSnapshot /// 按指定精度请求当前坐标。 func requestCoordinate(desiredAccuracy: CLLocationAccuracy) async throws -> CLLocationCoordinate2D /// 逆地理编码。 @@ -24,6 +24,11 @@ protocol LocationProviding: Sendable { } extension LocationProviding { + /// 请求当前坐标与地址,默认使用最高精度以保持既有业务行为。 + func requestSnapshot() async throws -> HomeLocationSnapshot { + try await requestSnapshot(desiredAccuracy: kCLLocationAccuracyBest) + } + /// 请求当前坐标,默认使用最高精度以保持既有业务行为。 func requestCoordinate() async throws -> CLLocationCoordinate2D { try await requestCoordinate(desiredAccuracy: kCLLocationAccuracyBest) diff --git a/suixinkan/Features/LocationReport/Services/LocationReportService.swift b/suixinkan/Features/LocationReport/Services/LocationReportService.swift index 49d6ca9..7974114 100644 --- a/suixinkan/Features/LocationReport/Services/LocationReportService.swift +++ b/suixinkan/Features/LocationReport/Services/LocationReportService.swift @@ -3,6 +3,7 @@ // suixinkan // +import CoreLocation import Foundation /// 位置上报成功结果。 @@ -16,6 +17,7 @@ struct LocationReportSuccessResult: Equatable { final class LocationReportService { static let operationIntervalMillis: Int64 = 30_000 + static let manualReportDesiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyHundredMeters private(set) var lastOnlineStatusSwitchTime: Int64 = 0 private(set) var lastLocationReportTime: Int64 = 0 @@ -87,7 +89,8 @@ final class LocationReportService { longitude: nil, address: nil, type: 1, - showSuccessToast: true + showSuccessToast: true, + desiredAccuracy: Self.manualReportDesiredAccuracy ) } @@ -98,7 +101,8 @@ final class LocationReportService { longitude: Double?, address: String?, type: Int, - showSuccessToast: Bool = true + showSuccessToast: Bool = true, + desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest ) async throws -> LocationReportSuccessResult { if type != 3 { let now = currentTimestampMillis() @@ -136,7 +140,7 @@ final class LocationReportService { address: resolvedAddress ) } else { - snapshot = try await locationProvider.requestSnapshot() + snapshot = try await locationProvider.requestSnapshot(desiredAccuracy: desiredAccuracy) } let locationText = snapshot.address.isEmpty diff --git a/suixinkan/UI/Home/HomeViewController.swift b/suixinkan/UI/Home/HomeViewController.swift index 9e78ca4..166c816 100644 --- a/suixinkan/UI/Home/HomeViewController.swift +++ b/suixinkan/UI/Home/HomeViewController.swift @@ -183,7 +183,11 @@ final class HomeViewController: BaseViewController { ) as! HomeLocationReportCell cell.cardView.onReportTap = { [weak self] in guard let self else { return } - Task { await self.viewModel.manualReportLocation(api: self.homeAPI) } + Task { @MainActor in + self.showLoading() + defer { self.hideLoading() } + await self.viewModel.manualReportLocation(api: self.homeAPI) + } } cell.apply( address: HomeViewModel.locationReportPromptText, diff --git a/suixinkanTests/LocationReportServiceTests.swift b/suixinkanTests/LocationReportServiceTests.swift index 697db8b..7cbf251 100644 --- a/suixinkanTests/LocationReportServiceTests.swift +++ b/suixinkanTests/LocationReportServiceTests.swift @@ -67,6 +67,27 @@ final class LocationReportServiceTests: XCTestCase { XCTAssertTrue(stateStore.isOnline) } + func testManualReportUsesLowerLocationAccuracy() async throws { + let reportJSON = """ + {"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}} + """.data(using: .utf8)! + let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [reportJSON]))) + let stateStore = HomeLocationStateStore(store: appStore) + let locationProvider = MockLocationProvider() + let service = LocationReportService( + appStore: appStore, + locationStateStore: stateStore, + locationProvider: locationProvider + ) + + _ = try await service.manualReport(api: api) + + XCTAssertEqual( + locationProvider.requestedSnapshotAccuracies, + [LocationReportService.manualReportDesiredAccuracy] + ) + } + func testTooFrequentManualReportThrows() async throws { let reportJSON = """ {"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}} diff --git a/suixinkanTests/MockLocationProvider.swift b/suixinkanTests/MockLocationProvider.swift index 1fdd3d1..e0e7572 100644 --- a/suixinkanTests/MockLocationProvider.swift +++ b/suixinkanTests/MockLocationProvider.swift @@ -13,9 +13,11 @@ final class MockLocationProvider: LocationProviding, @unchecked Sendable { var reverseGeocodeResult = "北京市东城区" var requestSnapshotError: Error? var requestCoordinateError: Error? + var requestedSnapshotAccuracies: [CLLocationAccuracy] = [] var requestedAccuracies: [CLLocationAccuracy] = [] - func requestSnapshot() async throws -> HomeLocationSnapshot { + func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot { + requestedSnapshotAccuracies.append(desiredAccuracy) if let requestSnapshotError { throw requestSnapshotError } return snapshot } diff --git a/suixinkanTests/PunchPointViewModelTests.swift b/suixinkanTests/PunchPointViewModelTests.swift index ae8dfda..bc92edc 100644 --- a/suixinkanTests/PunchPointViewModelTests.swift +++ b/suixinkanTests/PunchPointViewModelTests.swift @@ -245,7 +245,7 @@ private final class MockPunchPointLocationProvider: LocationProviding, @unchecke self.reverseAddress = reverseAddress } - func requestSnapshot() async throws -> HomeLocationSnapshot { + func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot { snapshot } diff --git a/suixinkanTests/WildPhotographerReport/WildPhotographerReportTests.swift b/suixinkanTests/WildPhotographerReport/WildPhotographerReportTests.swift index 7c028cf..7082d3a 100644 --- a/suixinkanTests/WildPhotographerReport/WildPhotographerReportTests.swift +++ b/suixinkanTests/WildPhotographerReport/WildPhotographerReportTests.swift @@ -1465,7 +1465,7 @@ private final class MockRiskMapLocationProvider: LocationProviding, @unchecked S self.onCoordinateRequest = onCoordinateRequest } - func requestSnapshot() async throws -> HomeLocationSnapshot { + func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot { switch result { case .success(let latitude, let longitude): return HomeLocationSnapshot(latitude: latitude, longitude: longitude, address: "测试地址")