"""公开 API:附件上传、反馈/建言提交、热线/二维码读取(无需登录)。""" from typing import Annotated, Optional from fastapi import APIRouter, Depends, File, Form, UploadFile from sqlalchemy.orm import Session from app.core import storage from app.core.database import get_db from app.core.response import AppHTTPError, success_response from app.core.storage import StorageError from app.core.storage import _normalize_folder from app.core.upload_validate import validate_upload from app.crud import attachment as attachment_crud from app.schemas.business import FeedbackCreateBody, SuggestionCreateBody from app.services import config_service, feedback_service, suggestion_service router = APIRouter(prefix="/public", tags=["public"]) PUBLIC_UPLOAD_FOLDERS = frozenset({"feedback", "suggestion"}) @router.post("/attachment/upload") async def upload_attachment( db: Annotated[Session, Depends(get_db)], file: UploadFile = File(...), folder: Optional[str] = Form(""), ): if not file or not file.filename: raise AppHTTPError(400, "请选择文件") try: folder_norm = _normalize_folder(folder or "") except StorageError as exc: raise AppHTTPError(400, exc.message) from exc if folder_norm not in PUBLIC_UPLOAD_FOLDERS: raise AppHTTPError(400, "不允许的上传目录") content = await file.read() validate_upload(file.filename, content) try: stored = storage.upload_file(content, file.filename, folder_norm) except StorageError as exc: raise AppHTTPError(400, exc.message) from exc row = attachment_crud.create_attachment( db, file_name=file.filename, object_key=stored["objectKey"], url=stored["url"], size=len(content), ) return success_response( { "id": row.id, "url": row.url, "fileName": row.file_name, "size": row.size, } ) @router.get("/hotline") def get_hotline(db: Annotated[Session, Depends(get_db)]): return success_response(config_service.get_hotline_payload(db)) @router.get("/qrcode") def get_qrcode(db: Annotated[Session, Depends(get_db)]): return success_response(config_service.get_qrcode_payload(db)) @router.post("/feedback") def submit_feedback( body: FeedbackCreateBody, db: Annotated[Session, Depends(get_db)], ): data = feedback_service.submit_feedback( db, fb_type=body.fb_type, content=body.content, target_name=body.target_name, target_dept=body.target_dept, target_post=body.target_post, reporter_name=body.reporter_name, reporter_unit=body.reporter_unit, reporter_post=body.reporter_post, reporter_phone=body.reporter_phone, attachment_urls=body.attachment_urls, ) return success_response(data) @router.post("/suggestion") def submit_suggestion( body: SuggestionCreateBody, db: Annotated[Session, Depends(get_db)], ): data = suggestion_service.submit_suggestion( db, content=body.content, contact_name=body.contact_name, contact_unit=body.contact_unit, contact_post=body.contact_post, contact_phone=body.contact_phone, attachment_urls=body.attachment_urls, ) return success_response(data)