from datetime import UTC, date, datetime import pytest from zbt.core.errors import AppError from zbt.domains.enrollment.models import Policy from zbt.domains.enrollment.repository import InMemoryEnrollmentRepository from zbt.domains.identity.models import H5User from zbt.domains.service_request.models import ServiceRequest from zbt.domains.service_request.repository import InMemoryServiceRequestRepository from zbt.domains.service_request.service import ServiceRequestService from zbt.domains.service_request.vision import MaterialExtraction class FakeVisionGateway: def recognize( self, *, content: bytes, media_type: str, material_type: str, ) -> MaterialExtraction: assert content == b"invoice-image" assert media_type == "image/jpeg" assert material_type == "MEDICAL_INVOICE" return MaterialExtraction( fields={ "invoice_no": "INV-20260727-001", "hospital_name": "成都市第一人民医院", "visit_date": "2026-07-20", "amount_yuan": "1280.50", }, confidence=0.94, warnings=("请人工核对票据号码",), ) def test_user_recognizes_confirms_and_submits_service_request() -> None: enrollments = InMemoryEnrollmentRepository() enrollments.save_policy(_policy()) repository = InMemoryServiceRequestRepository() service = ServiceRequestService( repository, enrollments, FakeVisionGateway(), lambda: datetime(2026, 7, 27, 6, 0, tzinfo=UTC), ) user = H5User( id="01USER00000000000000000001", mobile="13800138000", mobile_masked="138****8000", display_name="张先生", status="ACTIVE", created_at=datetime(2026, 7, 1, tzinfo=UTC), ) recognition = service.recognize_material( user, policy_id="01POLICY00000000000000001", material_type="MEDICAL_INVOICE", source_name="医疗发票.jpg", media_type="image/jpeg", content=b"invoice-image", ) request = service.submit_request( user, policy_id="01POLICY00000000000000001", request_type="MEDICAL_REIMBURSEMENT_ASSISTANCE", recognition_id=recognition["recognition_id"], confirmed_fields={ **recognition["fields"], "contact_mobile": "13800138000", }, ) assert recognition["status"] == "PENDING_CONFIRMATION" assert "confidence" not in recognition assert request["request_no"].startswith("ZBT-SVC-20260727-") assert request["status"] == "SUBMITTED" assert service.list_user_requests(user)["total"] == 1 assert "recognition" not in service.list_user_requests(user)["items"][0] admin_request = service.list_all_requests()["items"][0] assert admin_request["recognition"]["confidence"] == 0.94 assert admin_request["recognition"]["material_available"] is True material, media_type, source_name = service.get_request_material( request["request_id"] ) assert material.content == b"invoice-image" assert media_type == "image/jpeg" assert source_name == "医疗发票.jpg" reviewed = service.review_request( request["request_id"], decision="ACCEPTED", reviewer_id="01ADMIN000000000000000001", ) assert reviewed["status"] == "ACCEPTED" assert reviewed["reviewed_by"] == "01ADMIN000000000000000001" assert reviewed["review_note"] is None assert service.list_user_requests(user)["items"][0]["status"] == "ACCEPTED" def test_user_cannot_recognize_material_for_another_users_policy() -> None: enrollments = InMemoryEnrollmentRepository() enrollments.save_policy(_policy()) service = ServiceRequestService( InMemoryServiceRequestRepository(), enrollments, FakeVisionGateway(), lambda: datetime(2026, 7, 27, 6, 0, tzinfo=UTC), ) another_user = H5User( id="01OTHER0000000000000000001", mobile="13900139000", mobile_masked="139****9000", display_name="李女士", status="ACTIVE", created_at=datetime(2026, 7, 1, tzinfo=UTC), ) with pytest.raises(AppError) as caught: service.recognize_material( another_user, policy_id="01POLICY00000000000000001", material_type="MEDICAL_INVOICE", source_name="医疗发票.jpg", media_type="image/jpeg", content=b"invoice-image", ) assert caught.value.code == "POLICY_NOT_FOUND" def test_rejected_request_uses_standard_reason() -> None: repository = InMemoryServiceRequestRepository() submitted_at = datetime(2026, 7, 27, 6, 0, tzinfo=UTC) repository.save_request( ServiceRequest( id="01SERVICE00000000000000001", request_no="ZBT-SVC-20260727-0001", user_id="01USER00000000000000000001", policy_id="01POLICY00000000000000001", request_type="MEDICAL_REIMBURSEMENT_ASSISTANCE", recognition_id="01RECOGNITION000000000001", confirmed_fields={}, status="SUBMITTED", submitted_at=submitted_at, ) ) service = ServiceRequestService( repository, InMemoryEnrollmentRepository(), FakeVisionGateway(), lambda: submitted_at, ) reviewed = service.review_request( "01SERVICE00000000000000001", decision="REJECTED", reviewer_id="01ADMIN000000000000000001", ) assert reviewed["status"] == "REJECTED" assert reviewed["review_note"] == "不满足赔付条件" def _policy() -> Policy: return Policy( id="01POLICY00000000000000001", policy_no="POL-20260727-0001", order_id="01ORDER000000000000000001", user_id="01USER00000000000000000001", product_id="01PRODUCT0000000000000001", product_version_id="01VERSION0000000000000001", plan_id="01PLAN0000000000000000001", premium_cents=29900, currency="CNY", coverage_start=date(2026, 7, 28), coverage_end=date(2027, 7, 27), status="ACTIVE", issued_at=datetime(2026, 7, 27, 5, 0, tzinfo=UTC), )