新增举报摄影师模块并接入首页路由,桌面应用名改为随心瞰商家版。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-01 16:51:22 +08:00
parent 19d681192c
commit fcb86d43f5
25 changed files with 5515 additions and 48 deletions

View File

@ -16,13 +16,14 @@ final class HomeAllFunctionsBuilderTests: XCTestCase {
topLevelPermissions: [
permission(name: "基本信息", uri: "basic_info"),
permission(name: "位置上报", uri: "location_report"),
permission(name: "举报摄影师", uri: "photographer_report"),
permission(name: "相册管理", uri: "album_list"),
permission(name: "我的钱包", uri: "wallet")
],
commonURIs: []
)
XCTAssertEqual(snapshot.allFunctions.map(\.uri), ["location_report", "wallet"])
XCTAssertEqual(snapshot.allFunctions.map(\.uri), ["location_report", "photographer_report", "wallet"])
}
/// API 使 preferredOrder

View File

@ -269,6 +269,22 @@ final class HomeCommonMenuStoreTests: XCTestCase {
XCTAssertEqual(uris, ["wallet", "message_center"])
}
/// photographer_report Android menuList
func testPhotographerReportIsWhitelistedAndCanBeAddedToCommon() {
XCTAssertTrue(HomeCommonMenuStore.androidHomeMenuURIs.contains("photographer_report"))
let store = HomeCommonMenuStore(defaults: makeIsolatedDefaults())
let uris = store.add(
"photographer_report",
current: ["location_report"],
topLevelPermissionURIs: ["location_report", "photographer_report"],
roleCode: "photographer",
accountScope: "scenic_user_photographer_report"
)
XCTAssertEqual(uris, ["location_report", "photographer_report"])
}
/// 4
func testDefaultCommonDoesNotBackfillAfterNonCatalogSlots() {
let uris = HomeCommonMenuStore.defaultCommonURIs(

View File

@ -22,52 +22,59 @@ final class HomeMenuRouterTests: XCTestCase {
///
func testMigratedRoutesResolveToDestinations() {
XCTAssertEqual(HomeMenuRouter.resolve(uri: "space_settings", title: ""), .destination(.profileSpace))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenicselection", title: ""), .destination(.scenicSelection))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "store", title: ""), .destination(.moreFunctions))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "wallet", title: ""), .destination(.wallet))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_collection", title: ""), .destination(.paymentCollection))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_qr", title: ""), .destination(.paymentCollection))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "payment_code", title: ""), .destination(.paymentCollection))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "permission_apply", title: ""), .destination(.permissionApply))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "permission_apply_status", title: ""), .destination(.permissionApplyStatus))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenicapplication", title: ""), .destination(.scenicApplication))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_management", title: ""), .destination(.taskManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_management_editor", title: ""), .destination(.taskManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "task_create", title: ""), .destination(.taskCreate))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cloud_management", title: ""), .destination(.cloudStorage))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cloud_storage_transit", title: ""), .destination(.cloudStorageTransit))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "asset_management", title: ""), .destination(.materialLibrary))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "material_upload", title: ""), .destination(.materialUpload))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_list", title: ""), .destination(.albumList))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "album_trailer", title: ""), .destination(.albumTrailer))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_management", title: ""), .destination(.sampleLibrary))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_upload", title: ""), .destination(.sampleUpload))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "checkin_points", title: ""), .destination(.punchPointList))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report", title: ""), .destination(.locationReport))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report_history", title: ""), .destination(.locationReportHistory))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm", title: ""), .destination(.projectManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "project_edit", title: ""), .destination(.projectManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm_manager", title: ""), .destination(.pmProjectManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "schedule_management", title: ""), .destination(.scheduleManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "registration_invitation", title: ""), .destination(.photographerInvite))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_invite", title: ""), .destination(.photographerInvite))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "invite_record", title: ""), .destination(.inviteRecord))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order_detail", title: ""), .destination(.depositOrders))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order", title: ""), .destination(.depositOrders))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "deposit_order_shooting_info", title: ""), .destination(.depositOrders))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "withdrawal_audit", title: ""), .destination(.withdrawalAudit))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "cooperation_order", title: ""), .destination(.cooperationOrder))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenic_settlement", title: ""), .destination(.scenicSettlement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "scenic_settlement_review", title: ""), .destination(.scenicSettlementReview))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "message_center", title: ""), .destination(.messageCenter))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "/scenic-queue", title: ""), .destination(.queueManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "queue_management", title: ""), .destination(.queueManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "live_stream_management", title: ""), .destination(.liveManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "live_album", title: ""), .destination(.liveAlbum))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "travel_album", title: ""), .destination(.travelAlbumEntry))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "operating-area", title: ""), .destination(.operatingArea))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pilot_cert", title: ""), .destination(.pilotCertification))
let expectations: [(String, HomeMenuResolvedRoute)] = [
("space_settings", .destination(.profileSpaceSettings)),
("scenicselection", .destination(.scenicSelection)),
("store", .destination(.moreFunctions)),
("wallet", .destination(.wallet)),
("payment_collection", .destination(.paymentCollection)),
("payment_qr", .destination(.paymentCollection)),
("payment_code", .destination(.paymentCollection)),
("permission_apply", .destination(.permissionApply)),
("permission_apply_status", .destination(.permissionApplyStatus)),
("scenicapplication", .destination(.scenicApplication)),
("task_management", .destination(.taskManagement)),
("task_management_editor", .destination(.taskManagement)),
("task_create", .destination(.taskCreate)),
("cloud_management", .destination(.cloudStorage)),
("cloud_storage_transit", .destination(.cloudStorageTransit)),
("asset_management", .destination(.materialLibrary)),
("material_upload", .destination(.materialUpload)),
("album_list", .destination(.albumList)),
("album_trailer", .destination(.albumTrailer)),
("sample_management", .destination(.sampleLibrary)),
("sample_upload", .destination(.sampleUpload)),
("checkin_points", .destination(.punchPointList)),
("location_report", .destination(.locationReport)),
("location_report_history", .destination(.locationReportHistory)),
("photographer_report", .destination(.photographerReport)),
("pm", .destination(.projectManagement)),
("project_edit", .destination(.projectManagement)),
("pm_manager", .destination(.pmProjectManagement)),
("schedule_management", .destination(.scheduleManagement)),
("registration_invitation", .destination(.photographerInvite)),
("photographer_invite", .destination(.photographerInvite)),
("invite_record", .destination(.inviteRecord)),
("deposit_order_detail", .destination(.depositOrders)),
("deposit_order", .destination(.depositOrders)),
("deposit_order_shooting_info", .destination(.depositOrders)),
("withdrawal_audit", .destination(.withdrawalAudit)),
("cooperation_order", .destination(.cooperationOrder)),
("scenic_settlement", .destination(.scenicSettlement)),
("scenic_settlement_review", .destination(.scenicSettlementReview)),
("message_center", .destination(.messageCenter)),
("/scenic-queue", .destination(.queueManagement)),
("queue_management", .destination(.queueManagement)),
("live_stream_management", .destination(.liveManagement)),
("live_album", .destination(.liveAlbum)),
("travel_album", .destination(.travelAlbumEntry)),
("operating-area", .destination(.operatingArea)),
("pilot_cert", .destination(.pilotCertification))
]
for (uri, expected) in expectations {
XCTAssertEqual(HomeMenuRouter.resolve(uri: uri, title: ""), expected, "URI \(uri) should resolve to migrated destination")
}
}
/// Tab HomeMenuRouter
@ -184,6 +191,7 @@ final class HomeMenuRouterTests: XCTestCase {
XCTAssertTrue(uris.contains("sample_management"))
XCTAssertTrue(uris.contains("checkin_points"))
XCTAssertTrue(uris.contains("location_report"))
XCTAssertTrue(uris.contains("photographer_report"))
XCTAssertTrue(uris.contains("pm"))
XCTAssertTrue(uris.contains("pm_manager"))
XCTAssertTrue(uris.contains("schedule_management"))
@ -201,6 +209,7 @@ final class HomeMenuRouterTests: XCTestCase {
XCTAssertEqual(HomeMenuRouter.resolve(uri: "sample_upload", title: ""), .destination(.sampleUpload))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "checkin_points", title: ""), .destination(.punchPointList))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "location_report", title: ""), .destination(.locationReport))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "photographer_report", title: ""), .destination(.photographerReport))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm", title: ""), .destination(.projectManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "pm_manager", title: ""), .destination(.pmProjectManagement))
XCTAssertEqual(HomeMenuRouter.resolve(uri: "schedule_management", title: ""), .destination(.scheduleManagement))

View File

@ -0,0 +1,141 @@
//
// WildPhotographerReportTests.swift
// suixinkanTests
//
// Created by Codex on 2026/7/1.
//
import XCTest
@testable import suixinkan
/// Mock
@MainActor
final class WildPhotographerReportHomeViewModelTests: XCTestCase {
///
func testSeedDataLoadsReportsAndRiskPoints() {
let viewModel = WildPhotographerReportHomeViewModel()
XCTAssertFalse(viewModel.reports.isEmpty)
XCTAssertFalse(viewModel.riskPoints.isEmpty)
XCTAssertEqual(viewModel.reports.count, WildPhotographerReportMockStore.seedReports.count)
}
/// JB
func testSubmitReportGeneratesDynamicID() {
let viewModel = WildPhotographerReportHomeViewModel()
let beforeCount = viewModel.reports.count
let record = viewModel.submitReport(
reportType: .suspectedWild,
description: "测试举报说明",
location: "测试景区 · 测试点位",
contact: "13800138000",
imageCount: 1,
videoCount: 0
)
XCTAssertEqual(viewModel.reports.count, beforeCount + 1)
XCTAssertEqual(viewModel.reports.first?.id, record.id)
XCTAssertTrue(record.id.hasPrefix("JB"))
XCTAssertEqual(record.status, .pending)
XCTAssertEqual(record.reportType, .suspectedWild)
XCTAssertEqual(record.phoneNumber, "13800138000")
}
}
@MainActor
final class WildPhotographerReportSubmitViewModelTests: XCTestCase {
///
func testSubmitFailsWithoutEvidence() {
let homeViewModel = WildPhotographerReportHomeViewModel()
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
viewModel.images = []
viewModel.videos = []
viewModel.description = "有说明"
viewModel.reportLocation = "测试位置"
viewModel.locationConfirmed = true
viewModel.submitReport()
XCTAssertEqual(viewModel.validationMessage, "请至少上传一项现场证据。")
XCTAssertNil(viewModel.submitResult)
}
///
func testSubmitFailsWithoutDescription() {
let homeViewModel = WildPhotographerReportHomeViewModel()
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
viewModel.description = " "
viewModel.reportLocation = "测试位置"
viewModel.locationConfirmed = true
viewModel.submitReport()
XCTAssertEqual(viewModel.validationMessage, "请填写举报说明。")
XCTAssertNil(viewModel.submitResult)
}
///
func testSubmitFailsWithoutConfirmedLocation() {
let homeViewModel = WildPhotographerReportHomeViewModel()
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
viewModel.description = "测试说明"
viewModel.locationConfirmed = false
viewModel.submitReport()
XCTAssertEqual(viewModel.validationMessage, "请先更新举报位置后再提交举报。")
XCTAssertNil(viewModel.submitResult)
}
///
func testSubmitFailsWhileUpdatingLocation() {
let homeViewModel = WildPhotographerReportHomeViewModel()
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
viewModel.description = "测试说明"
viewModel.isUpdatingLocation = true
viewModel.locationConfirmed = true
viewModel.reportLocation = "测试位置"
viewModel.submitReport()
XCTAssertEqual(viewModel.validationMessage, "正在获取定位,请稍后再提交。")
XCTAssertNil(viewModel.submitResult)
}
/// Mock
func testSubmitSuccessCreatesRecord() {
let homeViewModel = WildPhotographerReportHomeViewModel()
let viewModel = WildPhotographerReportSubmitViewModel(homeViewModel: homeViewModel)
viewModel.description = "测试说明"
viewModel.reportLocation = "测试景区 · 测试点"
viewModel.locationConfirmed = true
viewModel.submitReport()
XCTAssertNil(viewModel.validationMessage)
XCTAssertNotNil(viewModel.submitResult)
XCTAssertEqual(homeViewModel.reports.first?.id, viewModel.submitResult?.record.id)
}
}
@MainActor
final class WildPhotographerReportListViewModelTests: XCTestCase {
///
func testFilterReturnsMatchingStatus() {
let homeViewModel = WildPhotographerReportHomeViewModel()
let viewModel = WildPhotographerReportListViewModel(homeViewModel: homeViewModel)
viewModel.selectFilter(.pending)
XCTAssertTrue(viewModel.filteredReports.allSatisfy { $0.status == .pending })
viewModel.selectFilter(.processing)
XCTAssertTrue(viewModel.filteredReports.allSatisfy { $0.status == .processing })
viewModel.selectFilter(.processed)
XCTAssertTrue(viewModel.filteredReports.allSatisfy { $0.status == .processed })
viewModel.selectFilter(.all)
XCTAssertEqual(viewModel.filteredReports.count, homeViewModel.reports.count)
}
}