From 1bb97f78fe49a0479530b436e47aa4c2a8c41baf Mon Sep 17 00:00:00 2001 From: QCQCQC <1220204124@zust.edu.cn> Date: Tue, 6 May 2025 11:53:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=BF=94=E5=9B=9E=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception_handlers.cpython-310.pyc | Bin 0 -> 1209 bytes app/__pycache__/main.cpython-310.pyc | Bin 564 -> 1005 bytes app/__pycache__/router.cpython-310.pyc | Bin 3825 -> 4596 bytes app/exception_handlers.py | 39 ++++++++++++++++++ app/main.py | 13 ++++++ app/router.py | 27 ++++++++++++ .../__pycache__/response.cpython-310.pyc | Bin 0 -> 555 bytes app/schemas/response.py | 9 ++++ .../response_wrapper.cpython-310.pyc | Bin 0 -> 809 bytes app/utils/response_wrapper.py | 16 +++++++ 10 files changed, 104 insertions(+) create mode 100644 app/__pycache__/exception_handlers.cpython-310.pyc create mode 100644 app/exception_handlers.py create mode 100644 app/schemas/__pycache__/response.cpython-310.pyc create mode 100644 app/schemas/response.py create mode 100644 app/utils/__pycache__/response_wrapper.cpython-310.pyc create mode 100644 app/utils/response_wrapper.py diff --git a/app/__pycache__/exception_handlers.cpython-310.pyc b/app/__pycache__/exception_handlers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62efad7188ee76c37324114c5692123cee26fab5 GIT binary patch literal 1209 zcmZ`&&2G~`5cb+@JHIUrX;1`072=XZ^8g4T4s8TTgsQ1HM3ExbyKU00f7snb1@!;| ziBsR9T#)h{yv1HQafS;B31*zQ1WIk~WagW7_M2~JlWMhO!4tl3j($T6= z3#0%@iIoAVERZT76;=ggg{}0g*6J5H+iDTx@A4NDt~9unC%tFgU9Qq3RvhBSBK15F zV;1Of5^qbHNXS$l?d|PspM*R`6oLG+rwQZ2_;UWI#^EPnXyZ|f2mJ!9Pq5S-FmH$k z1vFtcb56;*{Q)9I&*-@Wo_l5kSFbz%NG$8s+8-htonS0D>&&QeB6VndH3>tml%YU0 zFf;@jGuxDv4)jC~F*JUd#G1#tMWq7=OAn0kIF_VLDMI2yHi^=iEq%-*&6y?Z@bjay zb#Ji6r(7iIU`HlLJk)BiAE?phC`pb7Q-~9}xgUhbJZ6I+O$Si;T%F-4h?(G0wbPg8 z*7QQl;iVmT;MWEi`?XE-*8`9Kq^|c}V(Mx!??M*EJS9g&+h83KGK-iEa#E1f$>gNn zjx54vxdNu;&c#?oV6F(`8pM?~M6RPj1?FZQJkuCyonB^-TUE}UBI~RMnzL_0{%ZFB zjsjNjmi%uNzTqgWKZaJyI1n2>E~i{>WRtmE*K(5`rRCN2G!J>qWgsq>bQ=r5f#xQd zZfn)}2LX&!I+i%cW^EC*<>Jz)OiWYD@rxEKiI?8g;bSSd*1WxxaxvBD<*C2!mZ`%q zwN*IcQJ^l)$vTzIgG5p9uZ{^t048Fcp%&HM>@Blz#*I C4lZK= literal 0 HcmV?d00001 diff --git a/app/__pycache__/main.cpython-310.pyc b/app/__pycache__/main.cpython-310.pyc index 423f7fdd7e84b3da4746cf561c77cb4619250013..18c49171379de048a70a533ae75840dded73777a 100644 GIT binary patch literal 1005 zcmZuv&2G~`5cb+}Y$wiNX(P4jAxCl`DK~@=0?{J4R28jySVfB5?2x!!d(G}TK(0mH zcn8W29)<_-l@qT}F3h@3TNKvv_?th^H!~|5jhcn$=jYAYH^;Jm8RhQrP4T;!?N76<$p|>Lot)lYj=L&E>VEPU|JE@-S)8Cb57pkNGTM zHCAWg7nio6^|cLk@Gflh8r%f`26rI1#%thQAP0=N#~K#|T-Ic*Pu4j(Cv=^)53PRZ zH}bXmr0`xwN^CNl58wNWg=D3cZ%(K#A6oeSfpe4FoCQ@GYsS) zi|$P8>_7I+DWo7HzUsm{-8b+q2c?g1{=6rpFbo>6j*brYmQ@y=W0+_mdxsFo$*hNb z7w*Hs&R}QZ4w;y!MRh0v!J;t~8KhAb4^EXx`)=XtSaT@6IY@=fsc*u^x#Tn`GfG&$ zPBO|R8BWY>?j9SJ7x*e41tZLk3C_^jFT4blil(sGULn|vRZr$=8mH6mmTY3;t+;%& z`*^eqbKoKy9Y}GC-Ko(@q-GChLY$4}Ww#GbqRAPgY!qeLD2d{9kbNvFQV6|exENBt zBis1d?{84=EiMupm&Q(Om@~NAr^I_}(Jf=hb_FdVc=n8Vq#*1LAKvq92`u1i~#KiERg^J diff --git a/app/__pycache__/router.cpython-310.pyc b/app/__pycache__/router.cpython-310.pyc index db4f25afeef17fdf9c3efddbf573f7c232133118..1ace9126eba881bf5feb35fb2c3d8f547e3c71ff 100644 GIT binary patch delta 2161 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 diff --git a/app/exception_handlers.py b/app/exception_handlers.py new file mode 100644 index 0000000..af60670 --- /dev/null +++ b/app/exception_handlers.py @@ -0,0 +1,39 @@ +# app/exception_handlers.py +from fastapi import Request +from fastapi.responses import JSONResponse +from fastapi.exceptions import RequestValidationError +from starlette.exceptions import HTTPException as StarletteHTTPException +from app.schemas.response import ResponseModel + +async def validation_exception_handler(request: Request, exc: RequestValidationError): + return JSONResponse( + status_code=422, + content=ResponseModel( + msg="Validation Failed", + success=False, + data=exc.errors(), # 可视情况换成 None 或 str(exc) + code=422 + ).model_dump() + ) + +async def http_exception_handler(request: Request, exc: StarletteHTTPException): + return JSONResponse( + status_code=exc.status_code, + content=ResponseModel( + msg=exc.detail, + success=False, + data=None, + code=exc.status_code + ).model_dump() + ) + +async def general_exception_handler(request: Request, exc: Exception): + return JSONResponse( + status_code=500, + content=ResponseModel( + msg="Internal Server Error", + success=False, + data=str(exc), + code=500 + ).model_dump() + ) diff --git a/app/main.py b/app/main.py index 521e565..39810ea 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,13 @@ from fastapi import FastAPI from app.router import router +from app.utils.response_wrapper import standard_response +from app.exception_handlers import ( + validation_exception_handler, + http_exception_handler, + general_exception_handler +) +from fastapi.exceptions import RequestValidationError +from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI( title="Vector Search API", @@ -9,8 +17,13 @@ app = FastAPI( openapi_url="/openapi.json" ) +# 注册自定义异常处理器 +app.add_exception_handler(RequestValidationError, validation_exception_handler) +app.add_exception_handler(StarletteHTTPException, http_exception_handler) +app.add_exception_handler(Exception, general_exception_handler) app.include_router(router) @app.get("/") +@standard_response def root(): return {"message": "Vector Search API is running"} diff --git a/app/router.py b/app/router.py index 47c3367..e2c01b7 100644 --- a/app/router.py +++ b/app/router.py @@ -8,6 +8,7 @@ from app.dependencies import ( id_to_index, index_to_id ) import faiss +from app.utils.response_wrapper import standard_response router = APIRouter() aggregate_index = None @@ -21,6 +22,7 @@ class ErrorQuery(BaseModel): top_n: int @router.post("/insert", summary="插入数据并重新建立索引") +@standard_response def insert_errors(errors: List[ErrorInsert]): global aggregate_index, id_to_index, index_to_id @@ -40,10 +42,12 @@ def insert_errors(errors: List[ErrorInsert]): return {"inserted": [e.db_id for e in errors]} @router.get("/list", summary="获取已建立索引的所有 db_id") +@standard_response def list_db_ids(): return list(error_memory.keys()) @router.delete("/delete", summary="删除指定 db_id 的数据") +@standard_response def delete_errors(ids: List[str] = Query(...)): global aggregate_index, id_to_index, index_to_id deleted = [] @@ -57,6 +61,7 @@ def delete_errors(ids: List[str] = Query(...)): return {"deleted": deleted} @router.put("/update", summary="更新指定 db_id 的数据") +@standard_response def update_error(error: ErrorInsert): if error.db_id not in error_memory: raise HTTPException(status_code=404, detail="db_id not found") @@ -77,7 +82,29 @@ def update_error(error: ErrorInsert): aggregate_index, id_to_index, index_to_id = rebuild_index(error_memory) return {"updated": error.db_id} +@router.get("/page", summary="分页获取错误信息") +@standard_response +def get_error_page(page: int = Query(1, ge=1), page_size: int = Query(10, gt=0)): + if not error_memory: + raise HTTPException(status_code=404, detail="数据库为空") + + total = len(error_memory) + start = (page - 1) * page_size + end = start + page_size + + if start >= total: + raise HTTPException(status_code=404, detail="页码超出范围") + + paginated_errors = list(error_memory.items())[start:end] + return { + "total": total, + "page": page, + "page_size": page_size, + "data": [{"db_id": db_id, "error": entry["error"]} for db_id, entry in paginated_errors] + } + @router.post("/search", summary="搜索 top_n 个最相似的错误") +@standard_response def search_error(query: ErrorQuery): if not error_memory: raise HTTPException(status_code=404, detail="数据库为空") diff --git a/app/schemas/__pycache__/response.cpython-310.pyc b/app/schemas/__pycache__/response.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30adef2d8f21bc3336678944dd1d217c30cfaab3 GIT binary patch literal 555 zcmYjOO-lnY5KT7Ox~(mK;HCHrdZ>7oQdA0_tfF{X7DBoi+S=V@O}0YM3jP-VQf}hO zzu?J9Td)K3GB24V?EPBVD0-1+S9;}>yQ-cWEsH#~>;^+}iRp$2 zKvJqm8a)wUz!e=3h;fVtcwO3nJxIC)IbZW8^46Nifd>J;^8l_gdV9NPrJMRax6E^J zZtS^|we&nkWxai$2%%*OLim;trBTZw)NLVNma^EcFjrfDAcWM~)G}Cb*uy8r6gEM@ zHoTY(9&8iAv$?K6i9HGv0e(lLi);k1P?##ax8@o0+GP{zX8oBli|iG-vHgk67ob%p zt15H(3`*%TyLEC_t!#`(g>>QjS|p(fJ)|deJwHakzc(7f&az86kh{*wR zKl~`6QYA>7_tx<{C1gHKsZCg!<-pM$ba;YLlLa}W7i3HY73@4XqjNf=*vA>BxP50f z$wU8%4nu_Rv?f<{L9VIpFUX8!WJZ6{AM`CA=M9;&(!aktXEn18W=uGOU-G$AJ2U5s zUlZY8+IHU1_|T-O1o(9O8|#PMI0YPQ+thH8)QRCK9taQUr>d-FS%yk@p2%EP@dvFwV=IV{5}52vl$ypTI9Gb-C`qTX6me2jF*3*@UY8o#ORA(ER9}q0 ziUTABT?F>6!?x)Ldq_9v*Jn^CrAV|Gt#b#vxk{59_6Dm&ZEr7RdJ6~r*uo