From fad583ee7c26400a9683d46b9482d39d9ba692fa Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Thu, 14 May 2026 15:42:22 +0000 Subject: [PATCH] =?UTF-8?q?feat(server):=20=E6=96=B0=E5=A2=9E=E7=BC=96?= =?UTF-8?q?=E6=8E=92=E5=99=A8=E5=92=8C=E6=8A=A5=E9=94=80=E5=8D=95API?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=89=A9=E5=B1=95=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=BC=96=E6=8E=92=E5=92=8C=E6=8A=A5=E9=94=80=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=AB=AF=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/api/v1/endpoints/orchestrator.py | 2 + .../app/api/v1/endpoints/reimbursements.py | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/server/src/app/api/v1/endpoints/orchestrator.py b/server/src/app/api/v1/endpoints/orchestrator.py index 185dfbd..9fbbfe4 100644 --- a/server/src/app/api/v1/endpoints/orchestrator.py +++ b/server/src/app/api/v1/endpoints/orchestrator.py @@ -49,12 +49,14 @@ def get_latest_conversation( user_id: Annotated[str, Query(min_length=1, description="当前用户 ID。")], db: DbSession, session_type: Annotated[str | None, Query(description="会话类型,例如 expense / knowledge。")] = None, + prefer_recoverable: Annotated[bool, Query(description="是否优先返回最近一条可恢复的会话。")] = False, ) -> ConversationLookupResponse: service = AgentConversationService(db) conversation = service.get_latest_conversation_for_user( user_id=user_id, source="user_message", session_type=session_type, + prefer_recoverable=prefer_recoverable, ) if conversation is None: return ConversationLookupResponse(found=False, conversation=None) diff --git a/server/src/app/api/v1/endpoints/reimbursements.py b/server/src/app/api/v1/endpoints/reimbursements.py index 226399a..2994952 100644 --- a/server/src/app/api/v1/endpoints/reimbursements.py +++ b/server/src/app/api/v1/endpoints/reimbursements.py @@ -272,6 +272,43 @@ def get_expense_claim_item_attachment_meta( return ExpenseClaimAttachmentRead(**payload) +@router.get( + "/claims/{claim_id}/items/{item_id}/attachment/preview", + response_class=FileResponse, + summary="读取费用明细附件预览资源", + description="优先返回票据预览图;若无单独预览图,则回退到原附件内容。", + responses={ + status.HTTP_404_NOT_FOUND: { + "model": ErrorResponse, + "description": "报销单、费用明细或附件预览不存在。", + }, + }, +) +def get_expense_claim_item_attachment_preview( + claim_id: str, + item_id: str, + db: DbSession, + current_user: CurrentUser, +) -> FileResponse: + service = ExpenseClaimService(db) + try: + payload = service.get_claim_item_attachment_preview_content( + claim_id=claim_id, + item_id=item_id, + current_user=current_user, + ) + except LookupError as error: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(error)) from error + except FileNotFoundError as error: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(error)) from error + + if payload is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Claim not found") + + file_path, media_type, filename = payload + return FileResponse(file_path, media_type=media_type, filename=filename) + + @router.get( "/claims/{claim_id}/items/{item_id}/attachment", response_class=FileResponse,