From 694a2ab9791f262be8d53490bcb12b7762008d2c Mon Sep 17 00:00:00 2001 From: QingGang Date: Sat, 20 Dec 2025 17:14:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E6=8E=89=E6=9D=82=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + backend/__pycache__/config.cpython-313.pyc | Bin 1137 -> 0 bytes backend/__pycache__/database.cpython-313.pyc | Bin 1696 -> 0 bytes backend/__pycache__/main.cpython-313.pyc | Bin 2818 -> 0 bytes backend/__pycache__/schemas.cpython-313.pyc | Bin 1715 -> 0 bytes backend/__pycache__/service.cpython-313.pyc | Bin 6586 -> 0 bytes backend/__pycache__/utils.cpython-313.pyc | Bin 1237 -> 0 bytes backend/database.py | 12 +- nodes/register.py | 136 ------------------- nodes/template.py | 106 --------------- 10 files changed, 13 insertions(+), 243 deletions(-) create mode 100644 .gitignore delete mode 100644 backend/__pycache__/config.cpython-313.pyc delete mode 100644 backend/__pycache__/database.cpython-313.pyc delete mode 100644 backend/__pycache__/main.cpython-313.pyc delete mode 100644 backend/__pycache__/schemas.cpython-313.pyc delete mode 100644 backend/__pycache__/service.cpython-313.pyc delete mode 100644 backend/__pycache__/utils.cpython-313.pyc delete mode 100644 nodes/register.py delete mode 100644 nodes/template.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..48e4ceb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +.venv \ No newline at end of file diff --git a/backend/__pycache__/config.cpython-313.pyc b/backend/__pycache__/config.cpython-313.pyc deleted file mode 100644 index 6505e503f5b51fea89af9c0a2c99dec40ce48a60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1137 zcmZuwO-vI(6rS1LmZg6XE2uRj{0OEoDSxdojWHIfKT!(ll*GfvVOf@DVc9LS+Ykr` zJV~Ng5|4N?9!&7$6;EDEJkX365>MPPjq&Qtb_dJ4~@e38bcUp~Scx~bV~ z`qBXb;Hz&7cS&x z@-#?vw@NIH&G>xr$@4B6?-;Q8!g)|UH!nzx&4xMttS{mKFh^ug>z>*yKPT80@+os9 zmLp&0Ool8t4{w2Fps>~3n#{U+r{ZjCcBMR#9UY}GOW{KbN0R~4!OkF^9QZFG5e_j1 zNr6g^ZZ1=?N=%3M-QQ0~@>yk$*ego=e3V61Th*;4MYFACqnxU2(#UkKl$*+-Jf1JU zpgpbYO0|c>>`3PsgB@_vJ+kk9-2Sk=Zyn?gmcE)_%!5~lE&^0 z;i$AwLuyT3tZC(A%|6oX+G3>^Ti+GyT~~HP4R#p#1PaL*l`*!|H63Fb!g$SIs+ycf zFkY{!rtfrNylfE1H4RI*Y>d58P|bk^OthzeSOfZk!;Lyu~U$Y1N%{Vg*-hh*M-_Da=^h4^a$A_A1s2=Zcvi^EJ z(PW8we4xn&P6E+0K?ua(YYo6Y4G{!m4IXG#;zg1o6>P^VMPw?mG3ua^MDMq h6#lH8n+(oG2;ooA`Bw_V_;0`=0U}r5W?#!*fq%o^4rc%W diff --git a/backend/__pycache__/database.cpython-313.pyc b/backend/__pycache__/database.cpython-313.pyc deleted file mode 100644 index 37a2b1251d28b45f68a30ed416d6964c2fa6a5f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1696 zcmbVMU1%It6h1SvGdtPr=BFkx2_c!b0lO$IR@Wx2iW`z5n zOEx_a_%gv)UFvz3Q*zB_lwjaSP7Mv&Cggz$IxtZen52t?z+$?DT-IYuDamQYS9#zoa?L@tlQ3Gesp%0!%%(1G6mzLvbU84Z ze`@M{{v}qql3%k{EMhWa&895BWSA?|Ddc%v4)f{iRbM5gv$9>Tm;N z9IYqEIH>0-Jn$wA|E;3BfY`RG$~{BE*?+3AI4&KjZ>uwOod3^pT00zem>&BdaatW! zg5$85jB<#B(GTDVi=EMoE-wfj@RXQoo?C+vyuyx<6-v#qC=}s2X#|QwTAB6RgJNRU zu_<_W88xqBSBwH#v%KZV8um)pic`V57fO_bT~iyzU4QssLwm5f_2Bxadtbi0|Iy98 zw?Ev!b7$|y?fuU`KCh)!#*fbU`B(>MU7U4ub_|){HuaiGtDaSHe7VXj$4iT>hZ_Xy z@}g-P>*b62=t&`iw zp4myBt0&L>`A|Z`A1)RF-poFcJp*^oBqqA0yW<@bN$IPkjB;8IG7Ym2a0s&JILxNw z{K7a!nc{^>5K^ubYBraXgsj#KyJ_hpq-ZhMv+>PzDul2;bPgfuCB*d%&oYVOdCXd> zdDJE37BGIK5IX)sG0u21y1aMZf?q^M9{IH^E{}x^xp_9N`U!W{Hf(d5mam2n3-<|1 zh)+euDO#oAsR_dQ?S&=0?6|#SQuY{M*xJ-rpAB<)o;{8R-i+>XWQdm_d=HsAWWIx; X-(p?D*w4V(D0Dvh&XqUXgAjiK<8yT8 diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc deleted file mode 100644 index ef786ffa830c7704885d9ea25deb9d6f27e69af0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2818 zcmd^B&2Jk;6rb7I4}Zj7w@KO(r_HvwByO9gt!h=EQfXDC2pdA1Rn$~5Sc|<0+t`jf zyKZSt1u7B#0HofC1c&58l2cn1QTaazF$lD31&C8G$jBEYIPhlIo7hnTst_ke+Ba|C z%zHEY-fw=h%}6AKpuI73$s7|9`h~stjc+N<1V-pnq#+GwP#ROLAdS;VhD#GlT%FJG zX@Lr9k&0=FO6dR%xOO2EOowPlL0NnRX`&{*jA=L<9)H1KVyo8zDOiOyEtnO~a$2Yl zUFTDt3DL+dUg0*c0C;tEctv;dinMu&;1%ugl6Ucv+q@+3igkF!X7(iaK4be$Vn@29 zTef=n^=Isx;9bsB{Z`SSIm@6o&AgH19eLcCF)iDmpv!-&Rs9HFfh z9YT#8`Bu8!3`-zCf~Wa4h!>Fp_m)71(Nv%{ge0U7AXMhsW|OI~KRUtr@lvflSlj?WvCOGlqSa61Y4j=?K-2gf zna0cf7-idXB(0jy8nsrE(7AZRcPH^=7yiu{uxw1nnn=TXD z#msy`w{-^_7Tdd`C{AMZmF#6YcRhOx5}j+6HT$lfpM~mWOS)M;S-I^5{AK6?@MGU< zy$RwOx`P@#FAP1A4}CA6*pN>wX)9Nkuh!+%0{=wrzjtLZxOBEIpIQ%``u$0`r-?9| z3-&L_qWZa5mj}NK4F1s|VE)rO1ra~Q>Op+>#A&q3l(mrBM;`U4y`&~%P-`&(thHW& z>3sxrK!IK;i&C$VzxQ zysF)wT$`-R7oNYe&C?JUDm#Slo;!~oG3Bd}dX&_}9#w%tVy4FhFxM1;=|>6Z^O2E? z#~^=G-d`c|e_P%OtYLQ4hTvgMhvTPlZ()tV8Q&@*rA1s*o(x# zhM>J{1}RxHbRTFIqaNT1#f6L0)e-Cnj4?j2-T~p^Mrd)t$c8+!O74f(!Vk2^*B)N0 z%dai)Kg0(X#m~-vVlKVCKsub7lwQ;yX7%kaofvhwd4CEcQo zl_|TEDZ7v;JByA`wapgqIYh5i9KJFK*B?Wv$8`*|?sqJsCaB26b>Mtyu23x+7wAc_ zu|EmxOAv4|Vf+-upQ6N%XtItbpCaXFF|ywC(l_GZ9r6?AKR)omfraCBysyCtc<7$o zM9?(};`Y0SMig-{G;1I2%1tJ7iF F@?T&r7zzLY diff --git a/backend/__pycache__/schemas.cpython-313.pyc b/backend/__pycache__/schemas.cpython-313.pyc deleted file mode 100644 index a6a884bd3bfc15b1d5be60c888d5ec1389db1000..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1715 zcmbVMU27Xh6rI_f{m{qCNt%$xsckt;F^fa#Lt8><18p!#ozw|l0xfJ=*3v}XdbM(9 zMoru&Lm;tVie3WlPw3AH0V4)VANu6Cg4OrlJ1Yf+D1mli@6MciHFM9IGdc-^DuL&h z`L(QJ5%L#KCXX{7ocs!d=OiK#y-n7spiLZGx2<(s*y~KNCV8J+B9T2$A{Nun?DBWQ zF{lHmJB7Li^#Jv!P|u(hK&w-zZ_pZ`^(nN{3BtxPZW2;m`8t*S+g_XJp{3cKOe*c) z-BDSuNb^?NNEydnk&`COpOcsf8WCYdRM;Ppn8i+Htw1KSh1+4Fqvtkx2Ug=^lRxZp zsg4q+vkpXTGtT#`JdP|&{WM+%V2Pa-N}R&!1q`^8IE?`p zD7$|TN9QI1w~;slhQX;PcZL@i_SW{V4sINLdBhgVUtoGR-%lX(cD}J2dt&0Jw|E;(ViXF4Sr#Z2F&4N4tCSY_5J#6WnCs^}$a|@pDO(eZ zFkm0@a|p)zPi_q_E=`Q_k}VAvmiKP%e>Z3y{P2=34_8+AzS)0#$PVxSR(mmb`G~ES z;^IQH#r>boITNKn^HQl-?lE3A?(u}t@IM&{Tb!u`9+AL+>r1@&69WaO2dgiF#Ur*@ zvc#f|u&Vu?-F8~QACRyC(XQIv!Lu?xH8;@%xV(DColTsYZC<~f+W(^bC%i7MKnaCc z-h}YCO)33@EdEKBM~u>7kc|j@My^E{2j7nfd`7-S&z9q}BcIVLgK$LPGpbv3>F~lo JP_ty3{ufpUQXl{T diff --git a/backend/__pycache__/service.cpython-313.pyc b/backend/__pycache__/service.cpython-313.pyc deleted file mode 100644 index 6d7cc5a09ff1af7c13a57bfec34dac10556a0d8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6586 zcmdU!d2kcg9mn5lwK{y;k}cs=J`y9qc3@1vDTEk@BOFeUP)x^$jV!H=KwiPyRY=;T zg-#Mo4zN23w$o;8rp-V)L&HpGQkY~)jvY_a_qMvs%iXk9M0+p{bn2k~L{rx<|$X?MWK(J5@ z(IIaz@H2rA)1WV1v--#yMIWJzn^2vbp{!e1NvPh<_Hpexx4x1b&{Kn(>od9yeWrH3 z+o;Bv)fiKs5n{}0Or9E(r{>95WAfbvFp5HFky6w}y+?uqbqn;#fKRw?fIj%J&zi$2 zVBzmZC3vrqUM-$@NHCLc5Qf<8)KL?pDms5Zj8G5rG9Y0Nhtj<1fxgFR=s@3*z`+3T zOSRb7@AVxNM1LPl6E@v?=xN2o!z2Y{o>y#(`_-x?7@>#5d`z7{XPIb^gGj653@S`F6unQwXlp@kPw%)1nDq7T-b1y(>8>Th-@#mr@GtJ zscu^>PwMF^SRBTX3pRDO)B5Ww%}yb(3OPq`x2=`ss8RR8RzkEkMZJf0mFLK%*K)XQ zx29?{BXS;YX*$^pz4vf#hJ&zM=Vn`V5^Q4pX~)b#nn>=}dY5Qz-2Hen{j1W-&Obdm zH`l?qYUX3Kx4WtN5KX;lhgU67XA<_5)XQEac$^0|Za*mnUL)`|qv4hp}IEcsS zHr;M@>d6YCt*0MO(SXiD>WBk=gZlMs9T{Mp=5RfQoyYD7mJd?Qe zPGb0t#Mze;FQ2|XKA9LEqj>H%J9Uat_DTo&fM3ZENW3T<;W69|05Q=}NJcs;#zTOK zfH?RTODmuhwSouSmA!IU+U+c%c=RZq8^s`qg8@-cxPAei6wklHVTF>y_!J$q!X4Q! zP=VSp#V9-__`<+!(|{oR_J^RL`OqL@8o;d>p7aL8f~1(KAcv_4?JL~jun-oUCR&Uw zs@*XAf`V66yQ%kuL{Tvh1i%1D@{p{Ugr@?Mj0H{Fz|;Xqh)GA<(Gu)RA%zZ0vOv}0 zE7e)_Ta=1tTWW{dKT57z>!%uH)_aG|(}ks{dQSA5+IM1Kys$o6SpR8Zv~Fe0zAA2C8?~>!XuWK^WV>8*spfLsrMh^>u4u=uh-Y84 zV_(e94?i+(s~US|^qHwgVz$<}ZB5j+CT8mx-a1=sn{HfwuJg=KE{3DFO>>2$s3}_5 zm@FgqYRo@VvShqzvU#F;vSp$rRI#b>yQBGn9JQ>Qy9YWLFKmQ=-$@pjy$c(wHaD>!m3MLQ_@ zVH7=VH_3Zm%qRC@& zp}GS=$-g*_tyoFQc7N1%|8#ZZw7q`1swrtC_BBbOx0lVCh{YK# zTY7QH`@57+W{GZmRw>OXIY)Zfa%J z+Vtz~-;@I{=2$qJNjoX69shZxczMLMd~ONE0KFDPekb8(JKNc^v4#E6ZbrW)Z{veT zg$o9T0`j6Gn-4kFBl{`%uc1PMh-~)*jw`$1kmjxvVUgu>9noCMK|5Hv4rXsg_uXbJ zciV1`1M6W>?fsAS;*QO700O92JLs9w<;2pGfnLSu=O zlZo>qpeVmO`RBx`(~H>e0L6Lz)nkc2zMjH8s!b*D5Doc&r6?sCid(6hB72>s*Z9*nGGyXJJ3s!U6qjDV36X+qrwJ6UbP2K;{wpD*;U5lrd1fKHZP&6 zNwYmg_L4zC=F_4#MfkcbL71HZt%tOWQAwXu3C>s+XtK$+iMDuUOSG~jW@(LE+M|~C znG);B?y;WHp0T~7d&l@uK3={eTE60fF*>hax^Jrg~Xdnfiz@)LZ#rafBI ze$g1M+3>#gitPj2m6{J~uGD={7w_C1?c5#j+!O8G6Zz@hSSLzY=v;ZlwTkM|fvFuM z1CffAH;wwzvbj>UIhCZ;Hm)E3MZ{F|pKnWvwUe34Pg|Y^N#9Ah1K#YiY^h*BT3fWG z)bLls#tjg-Qp%xUk+-GUs2IUShlBEB>4>LFwL`P8`+iJ2gtBo9v(*8@O25D+>0LSO zfOcYme5?yCW0vd9$i&;Q?)%?`BUQ|MKXu#KccBf3Wcdpy$pp9_JAA|6eFl;8Kk&k7KZu-KVASRW?yLN zP*9KsKV1jybStLR$GYHmTCy1qMn6q%To8&fT`~CfhsA@EN@ltVqc@|s1wG_;iuv=T zSLu)YQ(=RG=rk-~`9{pHviyF0LscQ+6Bug9|%xM4#HKC7~*+~=Rsj5nbH>ZEdaYpAB6iB ziXW5oA@p`&N;wZVH(oj51O0G0B=P)PgyOA%x~3Evs(QHC5|(;_e5+%)+N7bJ zTR&yFN#K!eE#_8Cu}K1tbL}_rk!&dDYR{=rwHLZDsy5kB&MlkjNlNQ}s#Ks8;PkkFdC7|2nWaufrBS+7>sA{GzSOb%(D{Xs#jiNz%`$RUfJPF#SZ6jBo~pQU0lN%+ogc3 zOH~G|rV4tK4Ojs>(Sa_Zx={rxs)P{!;MKG28z?ViSKLK6?|{AHG04u@&LZ;**=p!| z!C+;T8-5u|w&%WQd0YnQ#e7jUSVmPNCwhAwj^f6Kxn{0Es=Yimdg{)4t~POGbZz9; za)(A@-$)!7{r_b2&W+Kv(L=p&U+>#;4)p%o_=(=JCDpr+^`83jcK9K;oBKHNdFs>D zm;SvQ(+7HLPf49)Ws>5GQ!X$rmx3Z!aAj@ha;NZEJTcnOHZ(TL49!Ru9h9-#QfgO* zJIF$xPxW#n z{ne;6+!G<9(#7jy&r9V()nj8ImQs8{!5FGDnNoU69-orQLo#`$#OP~U56+0dt>8z5 X-@7Rt-jdG str: - """ - 标准化 URL,解决末尾斜杠、大小写、锚点造成的重复问题 - """ - if not url: - return "" - # 去除前后空格 - url = url.strip() - # 解析 URL - parsed = urlparse(url) - # 1. 协议和域名转小写 - scheme = parsed.scheme.lower() - netloc = parsed.netloc.lower() - # 2. 路径处理:去除末尾斜杠 - path = parsed.path - if path.endswith('/'): - path = path.rstrip('/') - # 3. 忽略 Query 参数排序或 Fragment (#) - # 这里保留 Query 参数,但丢弃 Fragment,因为锚点指向同一页面 - query = parsed.query - - # 重新拼接 - return urlunparse((scheme, netloc, path, parsed.params, query, "")) - -# --- 2. 数据库连接工厂 --- -def get_db_connection(db_url: str): - """ - 获取通用数据库连接,处理协议兼容性 - """ - if not db_url: - raise ValueError("数据库连接字符串 (db_url) 不能为空") - - # 修复常见协议头报错 - if db_url.startswith("postgres://"): - db_url = db_url.replace("postgres://", "postgresql+psycopg2://", 1) - elif db_url.startswith("postgresql://") and "+psycopg2" not in db_url: - db_url = db_url.replace("postgresql://", "postgresql+psycopg2://", 1) - - try: - # pool_pre_ping=True 用于在获取连接前检查有效性,防止超时断开 - return SQLDatabase.from_uri(db_url, engine_args={"pool_pre_ping": True}) - except Exception as e: - raise RuntimeError(f"数据库连接失败: {str(e)}") - -# --- 3. 核心业务逻辑 --- -def _logic_handler(db: SQLDatabase, inputs: dict): - """ - 业务逻辑:注册任务并初始化队列 - """ - engine = db._engine - metadata = MetaData() - - # 标准化输入 URL - raw_url = inputs.get("url", "") - if not raw_url: - raise ValueError("输入参数 'url' 缺失") - clean_url = normalize_url(raw_url) - - # 反射获取表结构(无需写SQL) - tasks_table = Table('crawl_tasks', metadata, autoload_with=engine) - queue_table = Table('crawl_queue', metadata, autoload_with=engine) - - with engine.begin() as conn: - # 1. 查询该 root_url 是否已存在 - find_stmt = select(tasks_table.c.id).where(tasks_table.c.root_url == clean_url) - existing_task = conn.execute(find_stmt).fetchone() - - if existing_task: - # 任务已存在,直接返回 - return { - "task_id": existing_task[0], - "is_new_task": 0, - "url": clean_url - } - - # 2. 任务不存在,创建新任务记录 - # .returning(tasks_table.c.id) 是 PostgreSQL 获取自增 ID 的标准写法 - insert_task_stmt = insert(tasks_table).values( - root_url=clean_url, - status='running' - ).returning(tasks_table.c.id) - - new_task_id = conn.execute(insert_task_stmt).fetchone()[0] - - # 3. 初始化任务队列:将根 URL 作为第一条待爬取数据 - # 确保根 URL 也经过标准化处理 - insert_queue_stmt = insert(queue_table).values( - task_id=new_task_id, - url=clean_url, - status='pending' - ) - conn.execute(insert_queue_stmt) - - return { - "task_id": new_task_id, - "is_new_task": 1, - "url": clean_url - } - -# --- 4. Dify 节点主入口 --- -def main(url: str, DB_URL: str): - """ - Dify 节点入口函数 - """ - ret = {"code": 0, "msg": "unknown", "data": None} - - # 从输入或环境变量获取数据库地址 - db_url = DB_URL - - try: - # 获取连接 - db = get_db_connection(db_url) - - # 处理逻辑 - result_data = _logic_handler(db, url) - - ret["code"] = 1 - ret["msg"] = "注册成功" - ret["data"] = result_data - - except Exception as e: - ret["code"] = 0 - ret["msg"] = str(e) - ret["data"] = None - - return { - "code": ret["code"], - "msg": ret["msg"], - "data": ret["data"] - } \ No newline at end of file diff --git a/nodes/template.py b/nodes/template.py deleted file mode 100644 index 79cfa3e..0000000 --- a/nodes/template.py +++ /dev/null @@ -1,106 +0,0 @@ -import json -from urllib.parse import urlparse, urlunparse -from langchain_community.utilities import SQLDatabase -from sqlalchemy import Table, MetaData, select, insert, update, delete, and_ - -# --- 工具函数:URL 标准化 --- -def normalize_url(url: str) -> str: - """ - 标准化 URL,确保末尾斜杠、大小写等不影响唯一性判定 - """ - if not url: - return url - - # 1. 解析 URL - parsed = urlparse(url.strip()) - - # 2. 转换协议和域名为小写 (Domain 是不区分大小写的) - scheme = parsed.scheme.lower() - netloc = parsed.netloc.lower() - - # 3. 处理路径:去除末尾的斜杠 - path = parsed.path - if path.endswith('/'): - path = path.rstrip('/') - - # 4. 去除 Fragment (#部分),保留 Query 参数 - # 如果需要忽略 Query 参数,可以将 query 设置为 "" - query = parsed.query - - # 5. 重新拼接 - normalized = urlunparse((scheme, netloc, path, parsed.params, query, "")) - return normalized - -# --- 数据库连接工厂 --- -def get_db_connection(db_url: str): - """ - 获取通用数据库连接,处理协议兼容性 - """ - if db_url.startswith("postgres://"): - db_url = db_url.replace("postgres://", "postgresql+psycopg2://", 1) - elif db_url.startswith("postgresql://") and "+psycopg2" not in db_url: - db_url = db_url.replace("postgresql://", "postgresql+psycopg2://", 1) - - try: - # engine_args 确保连接池在 Dify 高并发下更稳定 - return SQLDatabase.from_uri(db_url, engine_args={ - "pool_pre_ping": True, - "pool_recycle": 3600 - }) - except Exception as e: - raise RuntimeError(f"DB_CONNECT_ERROR: {str(e)}") - -# --- Dify 节点主入口 --- -def main(inputs: dict): - """ - Dify 节点主入口函数 - """ - ret = {"code": 0, "msg": "unknown", "data": None} - - # 预设数据库连接字符串 (建议在 Dify 环境变量中配置) - db_url = inputs.get("db_url") - - try: - # 1. 初始化数据库 - db = get_db_connection(db_url) - - # 2. 执行具体的业务逻辑 - result_data = _logic_handler(db, inputs) - - ret["code"] = 1 - ret["msg"] = "success" - ret["data"] = result_data - - except Exception as e: - ret["code"] = 0 - ret["msg"] = str(e) - ret["data"] = None - - return ret - -# ------------------------------------------------- -# 业务逻辑处理器:每个节点只需修改这里 -# ------------------------------------------------- -def _logic_handler(db: SQLDatabase, inputs: dict): - """ - 在这里编写具体的业务操作 - """ - engine = db._engine - metadata = MetaData() - - # 示例:获取并标准化 URL - raw_url = inputs.get("url", "") - clean_url = normalize_url(raw_url) - - # 反射获取表对象 - # tasks = Table('crawl_tasks', metadata, autoload_with=engine) - - # 使用 SQLAlchemy Core 进行操作(无需写原生SQL) - # with engine.begin() as conn: - # stmt = select(tasks).where(tasks.c.root_url == clean_url) - # result = conn.execute(stmt).fetchone() - - return { - "processed_url": clean_url, - "info": "逻辑已执行" - } \ No newline at end of file