From 31801ca384ddcecdce33434d0be83a777030244f Mon Sep 17 00:00:00 2001 From: Neo Date: Wed, 19 Nov 2025 05:05:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AD=A3=E5=BC=8F=E8=B5=B7=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E6=89=93=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__pycache__/config.cpython-313.pyc | Bin 0 -> 2192 bytes app/__pycache__/db.cpython-313.pyc | Bin 0 -> 2151 bytes app/__pycache__/main.cpython-313.pyc | Bin 0 -> 1720 bytes app/config.py | 48 +++++++++++++++++++++++ app/db.py | 42 ++++++++++++++++++++ app/main.py | 51 +++++++++++++++++++++++++ logs/llzq_api_8000.0.err.log | 1 + logs/llzq_api_8000.0.out.log | 1 + logs/llzq_api_8000.1.err.log | 1 + logs/llzq_api_8000.1.out.log | 1 + logs/llzq_api_8000.2.err.log | 1 + logs/llzq_api_8000.2.out.log | 1 + logs/llzq_api_8000.3.err.log | 1 + logs/llzq_api_8000.3.out.log | 1 + logs/llzq_api_8000.4.out.log | 1 + logs/llzq_api_8000.5.out.log | 1 + logs/llzq_api_8000.err.log | 0 logs/llzq_api_8000.out.log | 0 logs/llzq_api_8000.wrapper.log | 11 ++++++ 19 files changed, 162 insertions(+) create mode 100644 app/__pycache__/config.cpython-313.pyc create mode 100644 app/__pycache__/db.cpython-313.pyc create mode 100644 app/__pycache__/main.cpython-313.pyc create mode 100644 app/config.py create mode 100644 app/db.py create mode 100644 app/main.py create mode 100644 logs/llzq_api_8000.0.err.log create mode 100644 logs/llzq_api_8000.0.out.log create mode 100644 logs/llzq_api_8000.1.err.log create mode 100644 logs/llzq_api_8000.1.out.log create mode 100644 logs/llzq_api_8000.2.err.log create mode 100644 logs/llzq_api_8000.2.out.log create mode 100644 logs/llzq_api_8000.3.err.log create mode 100644 logs/llzq_api_8000.3.out.log create mode 100644 logs/llzq_api_8000.4.out.log create mode 100644 logs/llzq_api_8000.5.out.log create mode 100644 logs/llzq_api_8000.err.log create mode 100644 logs/llzq_api_8000.out.log create mode 100644 logs/llzq_api_8000.wrapper.log diff --git a/app/__pycache__/config.cpython-313.pyc b/app/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..257ba84a7c6735cad5f4066c9f2fa6e2867df85e GIT binary patch literal 2192 zcma)7|4&m_6u`)TLC_Eo|!dSBkT=bmr( zymLSIyp^gd7lPrL+mJeJMd%q{m_sTvD`8;95k^=ziYTAOqr!(G6=hV1nh>_sBP?}_ zC2MMlicP4q?24^X%T5v7>d}{yDWg(S2-=spMK~xhXPRb7JQg2R;$6!VY?x>V+VC4rJ##Bu%hd!Oj292L8xpN zh^u({;^MErEu9-%d@%ju{-mo-KG@q!y2GD@_KdyDk23Q$BX=^N-tfMbls0zsiGDgf3>Iik_vJ za9l9Huta!H7I;_w0_iw9QBg(81+55xfnp0-G)omU;UHeYre7b5RQ5JyfDd=5AE6jn zVGr!$G&MbTT(x@r#%!x`&8l6E3&dk3QE*+D*~>^`7Jvk^1A>n9;NxeU+dFo*zYouL zL)P09InJccojW>=WgqU2obja9cr4x4JE9M&8RiDh-h&u_+7szwh)EnwOycnR&shN( zbAlB4sJr#p%D``r4K;vgEv&~(hC{M{C zZ4hW!p;6c~8doy|sbu@m2y+mU%A|Bc*n0Ciwwt1A2%=pDGK!w~13!*@KQa}#*`4=y z%=!1s`uF7hp`7%cr%+KH&HMLo$rFG5wT|n1C-+W$He<^-?5>E-7~#BspJCXr(J=dB z<`9_Wq{oiBWgGIWyR`qp{)t_A_vZQLV6N+LkNmW`J=gWvBNqY<*8`J*seLond|+2j z8mnG`^bm7F5y_wuODHtH`g(HSxWv7{jH5mw?m-AZ$eq@VEuf85uI6i{d2usBpMV7e zs8HObn6w0?tO|Cng{ePz7rHP8ovUzvYT>6Fi<95T{cQ#qx--La@%-<_>B)s_ql@=) zg$ECz8x^nIF5I~dU98mOnmC4<9gAkckV9Ux@*df?&YHEVNd!F zCte^ykv4L^uEcNV{JTIj{^_zzn_yx!RMFJ*Fd(Lc99CC3>ouB9+_eM9C|dR)PwlLG z2Ya}jb%O=QywK+!|USJ_y)tf?VnAVoDjoysT~71VY@ z5^9_f+6+;cMbjyLk8?gkG(D!L;v}Z)G<7ly?VgZZ$jHp71m&ICAiND}-2Xg!DO#ui_@+5yqZe?htfl2~6wG@>* z(lwzS`%Be4KskVURvI^3wg`go3_1TnjjwDiLgP!sNw0uJ_t}PJQ4ne`53V4XRxC)Y KddUUG{{I7OmHmGJ literal 0 HcmV?d00001 diff --git a/app/__pycache__/db.cpython-313.pyc b/app/__pycache__/db.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9aaf5a1c1857648fb6a9b9ac5a2a0faf85fa7a4a GIT binary patch literal 2151 zcmah~{ZAWZ9DnYvFW2h}FB`bY%Z>rM62Q3^J2%u(AR;9!wj(j43n|C7MWL2z8mL+a6GYk9$tC^NuTwJoXz)!2GX36vizt6P?16+LW?z!)M zzAw*n-}`*Om$h0s1Y>8id|*n4&;#2-;eTM-X0MmO6E@KCaSsr;QkI`%LuwKTib8psyuio9ItFk8PLD-}7n4U-P>h|VA zY{|#5f`%g+Yzr`U^D@!fDa9s)L$Y{W9`c2J0TBbjz9oy;Cx>wtQM*OoVC#S&w?wfN z#>-UB%MuXfUP+YYflxqFjQxGRA>WXw7_cZuaY!Im+F(JbhATS?Xf@&?Cp{~AZI>ac zht?e2kkR#HJL;y~T4tY80!req8-S|cXDtk62f1~(O&eFm7DZ4I;Asn|j8%s@0FWvq!{n^FE*$a!OPd>W!Mf%EQ z`tpUPD>u^DMpL(94{yTxUC!pwt?|XrFQsP3Q&+F0KDz!OHp-=bI+GfkSem`Pe0FyE z z#L>SqEHYVh&_B{Eu&>c(v-1sV&Q|EGLvxjt&aKQ`lLh2?wg~X~t(@~k<9xM|Aa%vg zCT6~g0enrNlh4Dzl`RCCK3(M9XfGgSKoL<;EW~Qv=vgUxC`StHXebR5aozCHS}e7A zOzYI}s1*+>R}Vv4qDQyC9nw!jdxc}Q2sa`%=-K6Vh{+XEC27-Z;so^1_6q28UUoyY z9Ja!>I{DOx7gImpNPiVepT7t-=V|L~YdOrn%&E;-WoWP8CqVxlIbqY3e6xoiKA=#; zm^8sIyIy4#CjS6TUJ*YBlfwE%SqO$h8jo=qQIT~O28WNWFc?=5$O6n4-s=rQgO`K2 z2B7V5NdcIEqb%aK#Eo}N{7maYj%t@}r+N}r$Iaa`!inE2nVj$f>fCmND8@xPk*ClXn3d~>sdnXxyQ>*q`+ z5a-Id<~q~d3q~S4IH%rdFHpGrUutlm4-@t%26-d`{~kv7wz2yQd&m)=P>985GEOg6X@aMpBY b37%y%!%RI(RmH0_2yQbqe~}w*gz*0YH^TQi literal 0 HcmV?d00001 diff --git a/app/__pycache__/main.cpython-313.pyc b/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c98650fadf09bc26ae1117ce5c580ce6cd9f3aa GIT binary patch literal 1720 zcmZ`(T}&HC5Z=8XpMAE$#1IG|a)C&ZReoHFR87@Z0ue+YLTyf#s^G}F*atY6!`(Y0 z%0p$6R!!C7QYA2GMIs6nMw)VDr@c)&fS4^^-YPc?!{Rr}J}GX^DUN4nXW zoteGaZ)SGK?G_PG*LrOF+HWrr-DVp_563_4_clYo%y3OZUj*+?t(1L6|3W`MyPAY|XM)nlqPws^f*Z-VTR*aMnRik}x_ki8!-J-ER z))YDw-DrF5jA`pqm554Ap+k+UT14NXDfqDI8c>apt{O&E8_=m99#q3ap+QxN8H3}l zvuDr0F10J+A&430{msue`+bbb8&M;sn!>0`^yrAzJ1)M_L_M2MYolNmB5J>qh#4lY z#G;CBiesTzZ2ZlTs*Rd9q8bUJh4&D!uE+wX-_DD`rcvL%t#~I52k<%S@eBn}5rN}y zm&6NT7B`ktn~M*#-(Ajp`an8JOWIiaX7k>Bc6Ms>vx(gG&vP^HNFD7`W@_?D^0Me- z2$T<*r9{<7Vxwv(axtVS!z%2%>At;)kj{WAN#@axmxA=h7HiT~0#F2%csw|)M78Gl zTf_r?7NhZcEwCiowIfgM2X804(oXNXvtiZQFxS22Y+2!2cEFCt9W6>c+7gFK0EC4E zp2CUtuZ7cX5dIq{8EcHpXlxzo+sEqwC5;I%fTW%N?P&_204uZ2+;Maa^}*Z}NI*&j zLK@dc!Lca76%p6iqcANR*T(feS6KUTKY9gr`>~HpjDowEy`9Y6Pi7{kH^05Im7K|@ z7BU~r|MBCD*d%Q}x}Lc;oB8Ql=KaacR4Vt?VlK6qxqIcnbbog1=DVAZW+CdJSYR%k zn3*ZZa@Q@tZooOM-?2(VIgId9k#rzz|gQhU}9CLC6FkQL)6I8Vv#x!0Vkwi00w3D z6wB-=Nw%jw<;gR7j^%3}yN}#BvF5J7%H%~H?JuP`0i)ZOfZ+ z&<`&Z^gW?D654u%f^6EtBU*oSpir+Wx@{GWg`$!gs?rHFS$MuI{H=dIn~448;gLup zroKu}fHQqo^b|0_65||dTt$r;B>jeJf48~QVpZDZ-R4<=NwV86)ZDt%@r!GaOcyjrt{D}JpyI#;?@<=;jX*f!O$OP3uMF^BUE XNO^zJw{8trZ8IFMrN$$cWC8Inq-MK{ literal 0 HcmV?d00001 diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..bcc64cd --- /dev/null +++ b/app/config.py @@ -0,0 +1,48 @@ +# app/config.py +import os +from functools import lru_cache + + +class Settings: + """ + 后端配置: + - APP_ENV: dev / prod + - DB_*: 数据库连接参数 + - 默认库:dev 用 LLZQ-test;prod 用 LLZQ + """ + + def __init__(self) -> None: + self.app_env = os.getenv("APP_ENV", "dev").lower() + + self.db_host = os.getenv("DB_HOST", "127.0.0.1") + self.db_port = int(os.getenv("DB_PORT", "5432")) + self.db_user = os.getenv("DB_USER", "local-Python") + self.db_password = os.getenv("DB_PASSWORD", "") + + # 默认连接的库,根据 APP_ENV 决定 + if self.app_env == "prod": + self.db_name_default = "LLZQ" + else: + self.db_name_default = "LLZQ-test" + + self.db_schema = os.getenv("DB_SCHEMA", "XCX") + + def resolve_db_name(self, env_header: str | None) -> str: + """ + 根据前端传来的 X-LLZQ-Env 决定最终使用哪个 DB: + - "prod" => LLZQ + - 其它/为空 => LLZQ-test + """ + if env_header is None: + return self.db_name_default + + env_header = env_header.lower() + if env_header == "prod": + return "LLZQ" + else: + return "LLZQ-test" + + +@lru_cache() +def get_settings() -> Settings: + return Settings() \ No newline at end of file diff --git a/app/db.py b/app/db.py new file mode 100644 index 0000000..614d9ae --- /dev/null +++ b/app/db.py @@ -0,0 +1,42 @@ +# app/db.py +from contextlib import contextmanager +from typing import Iterator + +import psycopg2 +from psycopg2.extras import RealDictCursor + +from .config import get_settings + + +@contextmanager +def get_connection(db_name: str) -> Iterator[psycopg2.extensions.connection]: + """ + 简单连接管理:每次请求开一个连接,用完就关。 + 当前访问量不高,这种写法足够。 + """ + settings = get_settings() + conn = psycopg2.connect( + host=settings.db_host, + port=settings.db_port, + user=settings.db_user, + password=settings.db_password, + dbname=db_name, + ) + try: + # 设置 schema = XCX + with conn.cursor() as cur: + cur.execute("SET search_path TO %s;", (settings.db_schema,)) + yield conn + finally: + conn.close() + + +def check_health(db_name: str) -> bool: + """ + DB 健康检查:SELECT 1 + """ + with get_connection(db_name) as conn: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute("SELECT 1 AS v;") + row = cur.fetchone() + return row["v"] == 1 \ No newline at end of file diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..62cf7c4 --- /dev/null +++ b/app/main.py @@ -0,0 +1,51 @@ +# app/main.py +from fastapi import FastAPI, Header, Depends + +from .config import get_settings +from .db import check_health + +app = FastAPI( + title="LLZQ Backend", + version="0.1.0", +) + + +def resolve_db_name(x_llzq_env: str | None = Header(default=None, alias="X-LLZQ-Env")) -> str: + """ + 从请求头 X-LLZQ-Env 中解析要用的 DB 名。 + """ + settings = get_settings() + return settings.resolve_db_name(x_llzq_env) + + +@app.get("/api/ping") +def ping(db_name: str = Depends(resolve_db_name)): + """ + 最简单调通接口: + - 返回当前后端环境 + - 返回实际连接的 DB 名 + - 简单做一个 DB 健康检查 + """ + settings = get_settings() + ok = False + error_msg = None + + try: + ok = check_health(db_name) + except Exception as e: + error_msg = str(e) + + return { + "ok": ok, + "app_env": settings.app_env, + "db": db_name, + "error": error_msg, + } + + +@app.get("/api/healthz") +def healthz(): + """ + 给 Nginx / 监控用的探活接口,不查 DB。 + """ + return {"status": "ok"} \ No newline at end of file diff --git a/logs/llzq_api_8000.0.err.log b/logs/llzq_api_8000.0.err.log new file mode 100644 index 0000000..395d577 --- /dev/null +++ b/logs/llzq_api_8000.0.err.log @@ -0,0 +1 @@ +INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) diff --git a/logs/llzq_api_8000.0.out.log b/logs/llzq_api_8000.0.out.log new file mode 100644 index 0000000..25b182d --- /dev/null +++ b/logs/llzq_api_8000.0.out.log @@ -0,0 +1 @@ +INFO: 100.64.0.1:59692 - "GET /api/ping HTTP/1.1" 200 OK diff --git a/logs/llzq_api_8000.1.err.log b/logs/llzq_api_8000.1.err.log new file mode 100644 index 0000000..bca2c3e --- /dev/null +++ b/logs/llzq_api_8000.1.err.log @@ -0,0 +1 @@ +INFO: Application startup complete. diff --git a/logs/llzq_api_8000.1.out.log b/logs/llzq_api_8000.1.out.log new file mode 100644 index 0000000..deeb384 --- /dev/null +++ b/logs/llzq_api_8000.1.out.log @@ -0,0 +1 @@ +INFO: 100.64.0.1:59690 - "GET /api/healthz HTTP/1.1" 200 OK diff --git a/logs/llzq_api_8000.2.err.log b/logs/llzq_api_8000.2.err.log new file mode 100644 index 0000000..7f800df --- /dev/null +++ b/logs/llzq_api_8000.2.err.log @@ -0,0 +1 @@ +INFO: Waiting for application startup. diff --git a/logs/llzq_api_8000.2.out.log b/logs/llzq_api_8000.2.out.log new file mode 100644 index 0000000..420732f --- /dev/null +++ b/logs/llzq_api_8000.2.out.log @@ -0,0 +1 @@ +INFO: 100.64.0.1:60268 - "GET /api/ping HTTP/1.1" 200 OK diff --git a/logs/llzq_api_8000.3.err.log b/logs/llzq_api_8000.3.err.log new file mode 100644 index 0000000..8396257 --- /dev/null +++ b/logs/llzq_api_8000.3.err.log @@ -0,0 +1 @@ +INFO: Started server process [11432] diff --git a/logs/llzq_api_8000.3.out.log b/logs/llzq_api_8000.3.out.log new file mode 100644 index 0000000..1ccd2bf --- /dev/null +++ b/logs/llzq_api_8000.3.out.log @@ -0,0 +1 @@ +INFO: 100.64.0.1:60266 - "GET /api/healthz HTTP/1.1" 200 OK diff --git a/logs/llzq_api_8000.4.out.log b/logs/llzq_api_8000.4.out.log new file mode 100644 index 0000000..e3d21f8 --- /dev/null +++ b/logs/llzq_api_8000.4.out.log @@ -0,0 +1 @@ +INFO: 127.0.0.1:56463 - "GET /api/ping HTTP/1.1" 200 OK diff --git a/logs/llzq_api_8000.5.out.log b/logs/llzq_api_8000.5.out.log new file mode 100644 index 0000000..6e25fb7 --- /dev/null +++ b/logs/llzq_api_8000.5.out.log @@ -0,0 +1 @@ +INFO: 127.0.0.1:56463 - "GET /api/healthz HTTP/1.1" 200 OK diff --git a/logs/llzq_api_8000.err.log b/logs/llzq_api_8000.err.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/llzq_api_8000.out.log b/logs/llzq_api_8000.out.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/llzq_api_8000.wrapper.log b/logs/llzq_api_8000.wrapper.log new file mode 100644 index 0000000..2ee775f --- /dev/null +++ b/logs/llzq_api_8000.wrapper.log @@ -0,0 +1,11 @@ +2025-11-19 04:51:16,714 DEBUG - Starting WinSW in console mode +2025-11-19 04:51:16,728 INFO - Installing service 'LLZQ API (FastAPI 8000) (llzq-api-8000)'... +2025-11-19 04:51:16,755 INFO - Service 'LLZQ API (FastAPI 8000) (llzq-api-8000)' was installed successfully. +2025-11-19 04:51:16,909 DEBUG - Starting WinSW in console mode +2025-11-19 04:51:16,925 INFO - Starting service 'LLZQ API (FastAPI 8000) (llzq-api-8000)'... +2025-11-19 04:51:17,008 DEBUG - Starting WinSW in service mode +2025-11-19 04:51:17,017 INFO - Service 'LLZQ API (FastAPI 8000) (llzq-api-8000)' started successfully. +2025-11-19 04:51:17,024 INFO - Starting D:\LLZQ\LLZQ-server\.venv\Scripts\uvicorn.exe app.main:app --host 0.0.0.0 --port 8000 +2025-11-19 04:51:17,041 INFO - Started process 10844 +2025-11-19 04:51:17,047 DEBUG - Forwarding logs of the process System.Diagnostics.Process (uvicorn) to WinSW.SizeBasedRollingLogAppender +2025-11-19 04:51:18,533 DEBUG - Starting WinSW in console mode