From 0b4c4eb23301d4b41c5da317dfdaf537f6223c33 Mon Sep 17 00:00:00 2001 From: Lxy Date: Sun, 15 Mar 2026 11:19:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=86=85=E9=83=A8=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=AE=8C=E6=AF=95=EF=BC=8C=E9=99=A4=5FInfoDa?= =?UTF-8?q?taInternal=E6=8E=A5=E5=8F=A3=E5=A4=96=EF=BC=8C=E5=85=B6?= =?UTF-8?q?=E4=BB=96=E6=8E=A5=E5=8F=A3=E5=B7=B2=E5=AE=8C=E6=88=90=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API_TEST_CATEGORIES.md | 175 +++++ .../basedata/adj_factor/adj_factor.h5 | Bin 0 -> 144824 bytes .../infodata/share_holder/share_holder.h5 | Bin 0 -> 1210008 bytes app/__pycache__/main.cpython-311.pyc | Bin 65147 -> 71873 bytes .../amazingdata_adapter.cpython-311.pyc | Bin 85938 -> 85920 bytes .../internal_data_service.cpython-311.pyc | Bin 22060 -> 22338 bytes app/adapters/amazingdata_adapter.py | 2 +- app/adapters/internal_data_service.py | 5 +- .../__pycache__/admin_routes.cpython-311.pyc | Bin 20877 -> 23756 bytes app/api/admin_routes.py | 54 ++ app/main.py | 137 ++++ .../__pycache__/test_service.cpython-311.pyc | Bin 20734 -> 34295 bytes app/services/test_service.py | 615 +++++++++++++----- test_adapters.py | 20 - test_adapters2.py | 19 - test_db.py | 46 -- test_klines_api.py | 112 ---- test_klines_extended_fields.py | 133 ---- test_sdk_output.txt | Bin 0 -> 178944 bytes test_source.py | 17 - .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 323 bytes 21 files changed, 834 insertions(+), 501 deletions(-) create mode 100644 API_TEST_CATEGORIES.md create mode 100644 amazing_data_cache/basedata/adj_factor/adj_factor.h5 create mode 100644 amazing_data_cache/infodata/share_holder/share_holder.h5 delete mode 100644 test_adapters.py delete mode 100644 test_adapters2.py delete mode 100644 test_db.py delete mode 100644 test_klines_api.py delete mode 100644 test_klines_extended_fields.py create mode 100644 test_sdk_output.txt delete mode 100644 test_source.py create mode 100644 venv/Lib/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc diff --git a/API_TEST_CATEGORIES.md b/API_TEST_CATEGORIES.md new file mode 100644 index 0000000..a0d01ea --- /dev/null +++ b/API_TEST_CATEGORIES.md @@ -0,0 +1,175 @@ +# 接口测试分类说明 + +## 一、接口分类架构 + +``` +接口测试 +├── 对外接口 (External APIs) +│ ├── 股票数据接口 +│ ├── 期货数据接口 +│ ├── 管理接口 +│ └── 数据同步接口 +│ +└── 对内接口 (Internal APIs - SDK封装层) + ├── 市场数据接口 (_market_data) + ├── 基础数据接口 (_base_data) + ├── 股本股东接口 (_info_data) + ├── 财务报表接口 (_info_data) + ├── 市场状态接口 (_info_data) + ├── 特色数据接口 (_info_data) + └── 基金可转债接口 (_info_data) +``` + +## 二、对外接口列表 (18个) + +### 1. 股票数据接口 (4个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| stock_klines | 查询股票K线 | GET | /v1/stock/klines/{symbol} | +| stock_symbols | 查询股票列表 | GET | /v1/stock/symbols | +| stock_batch | 批量查询股票K线 | POST | /v1/stock/klines/batch | +| stock_calendar | 查询股票交易日历 | GET | /v1/stock/trading-dates | + +### 2. 期货数据接口 (5个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| futures_klines | 查询期货K线 | GET | /v1/futures/klines/{symbol} | +| futures_symbols | 查询期货列表 | GET | /v1/futures/symbols | +| futures_batch | 批量查询期货K线 | POST | /v1/futures/klines/batch | +| futures_contracts | 查询合约列表 | GET | /v1/futures/contracts | +| futures_calendar | 查询期货交易日历 | GET | /v1/futures/trading-dates | + +### 3. 管理接口 (4个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| admin_health | 健康检查 | GET | /v1/admin/health | +| admin_source_status | 数据源状态 | GET | /v1/admin/source/status | +| admin_source_switch | 切换数据源 | POST | /v1/admin/source/switch | +| admin_system_status | 系统状态 | GET | /v1/admin/system/status | + +### 4. 数据同步接口 (5个) +| 测试ID | 名称 | 方法 | 路径 | +|--------|------|------|------| +| admin_data_sync_full | 全量数据同步 | POST | /v1/admin/data/sync | +| admin_data_sync_base | 同步基础K线数据 | POST | /v1/admin/data/sync | +| admin_data_sync_quote | 同步行情指标数据 | POST | /v1/admin/data/sync | +| admin_data_sync_finance | 同步财务数据 | POST | /v1/admin/data/sync | +| admin_data_sync_incremental | 增量数据同步 | POST | /v1/admin/data/sync/incremental | + +## 三、对内接口列表 (23个) + +### 1. 市场数据接口 _market_data (2个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_market_query_kline | query_kline | 查询K线数据 | +| internal_market_query_snapshot | query_snapshot | 查询快照数据 | + +### 2. 基础数据接口 _base_data (6个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_base_get_code_list | get_code_list | 获取股票代码列表 | +| internal_base_get_future_code_list | get_future_code_list | 获取期货代码列表 | +| internal_base_get_code_info | get_code_info | 获取代码信息 | +| internal_base_get_calendar | get_calendar | 获取交易日历 | +| internal_base_get_adj_factor | get_adj_factor | 获取复权因子 | +| internal_base_get_etf_pcf | get_etf_pcf | 获取ETF申赎数据 | + +### 3. 股本股东接口 _info_data (3个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_equity_structure | get_equity_structure | 获取股本结构 | +| internal_info_get_share_holder | get_share_holder | 获取股东数据 | +| internal_info_get_holder_num | get_holder_num | 获取股东户数 | + +### 4. 财务报表接口 _info_data (3个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_income | get_income | 获取利润表 | +| internal_info_get_balance_sheet | get_balance_sheet | 获取资产负债表 | +| internal_info_get_cash_flow | get_cash_flow | 获取现金流量表 | + +### 5. 市场状态接口 _info_data (3个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_history_stock_status | get_history_stock_status | 历史股票状态(涨停/跌停/ST/停牌) | +| internal_info_get_margin_summary | get_margin_summary | 融资融券汇总 | +| internal_info_get_margin_detail | get_margin_detail | 融资融券明细 | + +### 6. 特色数据接口 _info_data (4个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_long_hu_bang | get_long_hu_bang | 获取龙虎榜数据 | +| internal_info_get_block_trading | get_block_trading | 获取大宗交易数据 | +| internal_info_get_index_constituent | get_index_constituent | 获取指数成分股 | +| internal_info_get_index_weight | get_index_weight | 获取指数权重 | + +### 7. 基金可转债接口 _info_data (2个) +| 测试ID | SDK方法 | 说明 | +|--------|---------|------| +| internal_info_get_fund_share | get_fund_share | 获取基金份额 | +| internal_info_get_kzz_issuance | get_kzz_issuance | 获取可转债发行数据 | + +## 四、API端点 + +### 获取对外接口测试列表 +``` +GET /v1/admin/tests/api +``` + +### 获取对内接口测试列表 +``` +GET /v1/admin/tests/internal +``` + +### 执行对外接口测试 +``` +POST /v1/admin/tests/api/run +Body: {"id": "stock_klines", "params": {...}} +``` + +### 执行对内接口测试 +``` +POST /v1/admin/tests/internal/run +Body: {"id": "internal_info_get_equity_structure", "params": {...}} +``` + +## 五、调用关系说明 + +### 对外接口调用链 +``` +外部请求 + ↓ +API路由 (admin_routes.py) + ↓ +对外接口方法 (AmazingDataAdapter) + ↓ +内部接口层 (_MarketDataInternal/_BaseDataInternal/_InfoDataInternal) + ↓ +AmazingData SDK +``` + +### 对内接口调用链 +``` +测试请求 + ↓ +内部接口测试方法 (TestService.run_internal_test) + ↓ +内部接口层 (_MarketDataInternal/_BaseDataInternal/_InfoDataInternal) + ↓ +AmazingData SDK +``` + +## 六、设计优势 + +1. **分层清晰** - 对外接口面向用户,对内接口面向SDK +2. **独立测试** - 可以单独测试SDK封装层是否正确 +3. **便于定位** - 接口问题可以快速定位是外部API问题还是SDK问题 +4. **覆盖全面** - 所有SDK方法都有对应的测试用例 + +## 七、统计 + +| 类别 | 数量 | +|------|------| +| 对外接口 | 18个 | +| 对内接口 | 23个 | +| **总计** | **41个** | diff --git a/amazing_data_cache/basedata/adj_factor/adj_factor.h5 b/amazing_data_cache/basedata/adj_factor/adj_factor.h5 new file mode 100644 index 0000000000000000000000000000000000000000..598481152f1e900b55632cb30516139ea985d5e2 GIT binary patch literal 144824 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 z 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

L29acG*JeB*{y_oyp*6|*L#m&{ey z-=wgtu5(B!jnm5)SKdBNU9+qjGd<+MSzp-gP*7Y{Twd!^Nry;#p)5*_lr&kCn|s=1 zQ)tfnqAUuHbR{B-a-$|s7Dd<0oLOBK&8Tq{z#o7mb7tQQ%ROmvB3)FijAqnQrA~mw!S)|UHBa0HFCz!pZ z5fdy!q0th|UTE|LvzMD!g4v6%*$lEzFnfunv7YS8J`wCi$B=3E3bsh!J1@OrMa8V! z>e_1+tG!nH+DTO$eXnINF`uNewKQ^4SqqJtRJKC%Nh({pW;@OG>V(XMwxXjYm90e6 z*oaAGD>{~>vQ@A}dd>XG)l({0Evzf4Iu^7oY7>%Fx$LPXZ{bW}OCDBpjeFyOz0l}+ zWiK~kUS(CNWLGxtO6iT%Ug#md6R8!W)G@z$J0-oQG(By)dDkPuiF6jVm<=~lmf1TH zCClt3n#M-TGJDZ6WSPB!Y_hCFVTamT)-jh|S4fuG3e6|WY~@DIvMdVCJiKej;v&>d>ELmo&AVZcFIrpx$E$kSQWzqL#84Hb=Wf{wjl4aQz znp2i#EHsuZ%UFxqaC6VHj78_1Wf@B}jm4>oiOko5Rg-{(YJL zYg?iDWSOnp$XS*}p}A+7t|c3W-HM&He#09ijF1AY!z6utmXo! z?}ltAOA3n$+QxN*X-#>N(ko*tYc3w-e}idFTborpv|POjQ+40S^n?{zUEIppP?-gC zNZ5=;#*nTV>oD7YuF0CQ;J7xj8)@MOB>O#`*96siM3l_w$T6XEj=lN)9% zF;W6$QEtTjlfC=7XHfPn%^`=fC^UvF%A%I2d6Y%b)=bLc?>*aoHlIw$qD0eKPby_K ziAvGMh3fMS!L%xjeicJ!maWiyGRszOUec$xjY%m=XJ=?nx_&jE#H-mUI{O}V z%vbBOh69w>Wet0w(bLXeZp5_9s?c21E>oeA(#~FJTxn;oC9ky0P_%5xkD>6EsW zo<;VlI<|>Wat*S3PUib~tmihfEC@24HRW2GJ>7CEE$2MTt@!K`EXOjl%BWyQkR6eq#V)^oP*@X1m>)g|5cc+}5w@Q6Dw_I&mTa~B{ zwc??X&Uvd+tID5;^7HZ%T{h&s6HG%A>aB63yRv+KGrNM=$P>%w%O#Lwe+m= zFECwL3(PI;tVQLVb{R^`G3~6S<)3!e;v=S=z07E7XD!pYXE42JTE&F2jWgHfkYUzB z)y`~^y7Z;?D;2X>sT6kZ4z7K}eI3c$M&0BQay_Idiz7%?;+{1-v+CX0$E70!6NJ5v ztWv6uU7Ee*47nAl%*;s^vDi%67OmLI@&`Ap7@uA;tzuP~Y;WFsR3mK^5pp?FRwq)-%*~PKFfJ1htdl_(z0)sdFE?zQOJdsHrX9~W}C0JI`Yd` zTgg%L)mCO6`D!mVW4`Waoe`~tN6lJWp>bxdt)9HI)>?QrS?lCwbo`o)(Al>I9{Ezo zeA!lAh6y<$&E_FVs>JM)(q2bCNog;+#v}B+u#OqfUS{N^%v5Z~qzs?0?Zw8IIre(; z%p7~+G41d62wYic8FD^oZ%C{3l$$DUe7Ne?nd$o`S3Gc++Q+sjD5#h`zGCLm?2m}; zb<}LLx#V!M^A2F}9SnJzPfdv-pUcT1hi!G_m&4YQYYt@eqtDXUPp(Pko93j|Q+ZS$ z>dFmU$$4b5z1YZ^Y%4a-Ot#gNcP3j4k7FO$w!l_}T(haX_XhR8%!W0yHkMAz^O}vl zzMAaHAz$4y)LQwfR;%OVW|eEFsSX!|_-dS=#{a5|DS#_V;q8rpB3#%_t6TCAqWBARb5aea^AN$>pB|_)Av3fy!%Z*|i_LS3wzoN^eN*`SWv@D(y^+0+ zC|h^dC3@b$kQ-kjj&S@rZ|_4cH_}(7P(NfA*Vq6n}tqxDCb4puVnZH3A z)igW&*f;Dwq4ouB3qp>j&6A>XPs|JjMor92#YRj_d$IW^roH?`6VqPxZt1a$)p3_| zcCqdj_iaOUJ4)s1G3rgly52vum)NXo()|@T-dH*HzRI%E@`&IpE@9gXjelonudgPB zvOmDhy2KpjlJ;tc5q!Ff+F_W7K}AVRYk|?z(q3%jw6ql)M_Ss-R}VmT-lwZtr=FiZ zC;K;J>|>U2uG{)^gnC-Hx(`78Z^O#9mGjFh?|iuOfwlRcE8068c|Ndp<*(Sy&rp}7u}U!`C!E|xzj?Ohi(Ww zN?je7h3#8A6tpP{K0(GwV)ukN!ij2z0;48zreY%|vAx**lh|JVb`{g_teC$tz32{S z)7OvauWP?%FQ`>|LJ(aT1a~AGWXVl*lX(O ztx4U~wsB5rdf{B>CWmE9)6-!|c(kf4}C(4!-c*i~0^~esbS)`VVV*F8tt z8rr;a`rP!bcdKtCsCr;Ui;cI>u2^y3fx$}`;k(H8VYlcyJ;?6ja1-YXXUdnjH!?Zz zajB=*sC}$@I$~kSHDXn6mKU9)m76({BbA$f0@^fknkQ@pWs%9zy6>{8FE3K}M`gG^ z7OevJ09jSz9#ZCd0BPUa;+iRJ^ih-AR_3wt{*^iWRX4wOIn5ucL zDrPTnPBip)Hf>mSM|#G{x~7z3_3@XGbKDMv>cePR?Pcp2>kQ>a&QW{0d&xF*!_ul5 zw^ZJ{GW+S;UTzNAX)h>xcG}DJ9lN^62(CbcBL#a+t}>|IWSrd<-6ZdPkRizhp|J} zC$PTak9aBw*0`}qPB|Sns4lN#B({*T1PC?K*#XwfW`4%2ej8B z_s^>LLY*&{wjRRQb+Oc5LBwRY7n*dk+iQ<8T{2Y^+iA7EqKLDKz0f${!N^dPy3wZc z=9|-Hlbv@k^wmfA?i&RYTzHg1$OIIIKxl!GwTDkg8^`XqjN8k23{+QL)lW13dUwf!- zsOcE%zCJ@ONhW@Vs-q=-hC<^`{0!Aa&e06zMw?JF6dHTtXQ=N0xi47Vt+rxidcvJ* z(os*gt$jaB9dmA`((}8MC`0LqrM&ulVy*W9R9p1wmhLXKAhu@>lgeM(acOy?awn7t_bZpfn@yUz~TS?^k$kvY}<|A8u2jo3t*h)`q z#;{cy-!-PXH-Prem!Ic6O@v%{&QL{;mmR|250ue#UalWRtctW?2ls=bR%ikf zf~~rQCInlRkq<>|g(fi}*y_uFLa>#d*o4s0c`q_>6O*&Yb)IVx(}ZBFBCZL+R!71U zg01Sv6N0VK1SSMqbqP%fwkjh}2)06#m=J9B6A)P>)~_Jwk9VV8${b*sF+V zLa^77=!9UeI^u+2FEsWE!Cqa&?6eme@<<)^Q8w!%b=2=|8*3u-mc7at&)w}6C6@hR zj}LJ^Qs{i}H>T{jRS{SA+v-R-`)yT6&VE~=31q*my2#mYD>Pd6+e%C*`)yVJE=d{x zQB7e%amWW7)FjZs`8Z?FlR#E)XxQtFZC0@KE9qImQg!rM!BS`vvx23%=(B>Q(1^2w zrNpFW1xuC5%?g%E_dh%ypKmYkqVe zx1qWe_T{TiPm{Y_(OzdysbuW07$-|v&>Y74iu!@V?)V@nWz7001 zqYGP|aZMn$Iuf2hY*lYjHT~xF6m^gPobt)_)Quovx8tb;KFgp)o`S4}swaDISap;8 z6=xMw?@$NwSv)3JZMU{UV@eHM6$wozwkq?VPOPQp_e|@;HtOBY(1h+7>3znm-eR!V z8B;=A`;}}$TdVdY^aV|~NTtFv+FC@^jJ6jVF{7=8#*@+3Dw4@)Yn6Ftw6*k|)MqZ$ z(SODL<2S5OU!ZXBh7}VlC*FKaT^Bvts`GvIG{fM1gYs3-$?TEsb>^7BVS6-t1&L;_ zz3Qy8*Iq=#?6nsfOQPAUh;_TnH-+p?O=M26SJ^Du^r^nIF6?TZvs%5ymsIxJ>Wn3O zZ51S(y|${e%wAg&k+auUXiV8_t0L#@wH20s_S#BMD0^*HM$X>QGkrBvBzHMYeGz?8 z?T4KfN}aJ}udRZFv)5L2Jv42FszKQ2OPpEV1(A{E!*Rk4K>x?65 ztraAjwAQMlCaty5?2^`6MBGVhtt{81wH6jn(psyCl(e~**w#J`v{TRJ3c29iHsZ(H zvf8`a>x>~$?G~s$h=lXgR(0h3v=tib=4z`bG;K3Z1UZ*jlxe4A zE3spG`EBa14fQ}z{bhaA)swE&Gd(8f&@Ct9wx4r}8S^ve67$-+LwjAEPrf9P@pYKA za~@Bf%o{y*=BKVR$^6t6{65L0tIls~&_%?ZpSsW%>9UCxD<)M;npySG7 zVJ$+#ELx&&uTv^+>biF>S1r4B!|K)PvG=5>t%wE_b^fuoHza9upB8ehA@^y)T0@j+ zp}o3JtR8X_ysxoclGlmtb;gjh_6qXKS$olm=B&Nyta8>~gqAls>pfV`K6C9AM9f@! zp&^-@b6(|KL(X%9y@qI++cxAYmkL!y$z4HiudZ6F^XTAgA$7)*xwZ=O%v@X131_aY z>R8gwRzc*nvlW`dESUE$aT@lzofY zN4?b$S;AdMJJoU)~e%3U~2_Y6WCg) zCxI>LYb_#%B(s*E*Ysd5Iqsct2X%5B@}a->NuC_r>x?0n?G@ye%l4uZ&1HMlG32tn zf{3|nFSO=Mc`NR{b>o6n?mIWxKL)^Fdj4r{FF&6&x0f8lURr$#dQr$>wyHO|lV`Wi znH$EX^iQp`XsJAw6u0#*&lI;6op6fVs*WYaZ52dLaa*DJr?{>3{8G?X^1kjj7Dqco zwkKaK*cqY7I%7#{TLpP0wXNucliF5wEJ{y>4nOwqkmTFT9jtZ6k=51;^2=&#(aC1Dwdy#s+FC)>thN`r z@%GsjEACTymtM4_@}aWIB{ydCEH3*Nl=oC&ZE}8lX?xN6%tFQLyt-ZOw~;y|d0uU= zGlopISCCgG+lx*#lkHVEi=i_u9x@gBz=LY{xpBq1^pacTfqio(Zk%0a zztzQFXkH0yFS@wmhNaF8cdJ&V?_1}-GgbS}C3)kj)rJ*!IWL?ru5#7Fy56}`-=P!s z(FoOwd-&r zDIdiV&$L66+ge2&$!)D5zvQ+SoosSjtB#u7)_sO!W7w;XA-U}pL`?2Xg+@wld!ex> zx4pWeidi$9FX&pkcEgHE>hnYGa7NpzjwPdQ6-3Txd!Z3C+E!?Zx<;)YC8GXUF>%($^0KORH@9t5 zF=yV!yJuD0H@0H-jP!#`-H)a@*XznAsQYWwht=Gd+wW4fXWZ`I3iM}D#xGN)x_7PC zoy6*ty7o2kjxnB4cU0eVU3V3+B(bf6$VqG~G|wcq6`gPr+p3NwiER}`PGVc3`>Ll4 zR9^z$ux8|j6*svLTgc{pGW*7vWGdUb6#Bue49{ihs1E8w?k(#WY+^SIYqQH0bjDo~%@2)XdpF}`!-3{^x+ z-3*21pSl?;NHTRZRGm-iW+*!U)Xh*qwA9T|Xg;Z%q3E!TD(ql=09b|+VOJ}H( zJ}*`27VVoLLas=wvTRjEPF-7}v81l8f`n7oR&}1KYb!dI)U{O*IdyG?=9#*-qNAm* zt;9G}H}nu(rCYR5JBOyOwTh^zYb`X6)U8#4``z${CfVezRk`!6(x&SClGj>v9LZ~~ zAZqej3(YTitwl#lUTcZ1)J>b}sRvafZ`!bCR^TBX-dlNWA6%q5+-tHLk)?PuPS!=I4udKBf9YfaID~Oo2_Clkimc7Kh<^g-ryQ`E{ zPu;DYdS7MPXy+p&H$PIMZnw6+C8RPa^Z>jt##35*6_JzF*6vu6)K)>lNouP)&m^@K zT~K|$K-G*}D(_vH$JB4D<^c5tyXmou)&8X7;oBqaSZt+7o(OD(7E6<7j#FdPLgUC}YX!+>vbE~`GTB;me3_hOHEO?BwQOD<-jK5{)hWk%J*liZIpspIL&+ZI>f$`$feA7Pi5ib5ZNT~rw3nXtWzhzV^kG=_w> zSCD8z+pEqiq3uP-meBTUB4(Ps&={}k+AE5Z()JQ#PwBQrwIAD8q}mYI{m`l`TNRO0 z+E!>RDQ&AD;gq&joo7nhijFa*ZPi3hXr@pF+*Pss{x$^#8&)k+_rj^GM(H)B73FKy z?OyIT#k=nad%q?=+NKtDuCjI|#wo;FPn0R7ZRq2;RYJyhrfshxhUB(a5HY#!g(jNZ z_NwzrZhO&5<($3Nh&g93G`^g(*AwG>Xs>9Cs?iIoCXY(bm{MNnm-`j9Rj0bOztLT- zj_+*RUPUa~Z0kehY_=7ea5mej&NG{BMJJcdwpu$^->aT+r>!16UG>m%wGYpJ?`_}u zBF`kY4#hc>*y`z+o;5MOB+f&fwxQ4IR=i_u7wm;5IxpC(&TC$<7oF6+V6QdVdBI+H{PRMl`r@7!?Dge7FX-om zv@2*EcC?`CitT-Qdlj+F3bsB(o)v6`COj+Hs?Kv(uoa!$tYE7(=~=;6cM`LLy}tNo z1zUZQXNBUTHns1|YZv1cg?8$F{kp4&Wmd3N5P4Rx6`Js@V5>UMS;1Cxa_3ikTqpA~HNMV%GG-<50^Ll`3`qqkih8TDY;kyQ0Y^dNI|& z4XNOAgH#j!_e~n6QtE%|r<4BgrfSJ4p?-b0poXcYdVPa>OZKi(HFm+Vdl9NwXzuUe^Dz&MKZW*amNza}or>W(d*E_Fnm^!6%&y$bq z-c7&WX}fuSQR}v9$$5Qq9qZ}xH%aw6u2FA^V<6Qf8KI8*X175wd9oUJ!`jP z(1?qMT(17EajIC#ZKVF)Fg1K=-$BEM3?A0+T)jHAg9>h2TrJbCn%G$X{%KiJPu%L& zb61#gsEMxg_Nv@%)c@;t|6nV(1+Cm5|Ha*fsM&rT!G@`U0|x04bGC=g)Jx3x3>!Xl zz@YQJ+lBl3L47ak=al4(gW7hgU*AnSPKnw5G*Efy{Oz{Qk5F5O^V2ES zwo$6>uab!Q=KZ!ZUM*z4?IPn`Q|^v>yVO3?AJxmD1NsejrX4+RZLXr}-_U;NkGQb! zP_=BIPt5jZVgpr+Q--r0Z>jzt+^%Zo@vXL2zg9oaj8^mWTW?qYt@C)&^GS_(e*bdD z*!eNrO^+Ko>3^E_X1jRzv0Z%Y&Hm@>PoCF*Z(c8(Z|eT^BHwyvpY8n2di*>c&Ff_| zPj!0QozcvCXIplDjyU{49nE?-_fn}rJMHCLzlB~(r6$kb$+zD8UHhTV7VU1+PBi^( z`uTiczf6DsRY!mSa>@(NpX=OkV%+_`FwkGq&cXNGr<-Q_W7;|Wy}$a}Z^q@g$?bgW zO*<=J-`uy}j7y)E7dhRj?w1*tw|^?}tvBQH<9|B%)|+wJXOBI7>&>`))TNnkz3E5Q zVY~a*>&B@y_kFSUzpB5fW$k?JjJv-lwd<&d&bXNI-Tv@qJ_j?t`;Hjq8%Hy~U2Zze zx896zL0`&G-)g^S-|IrXOd1ySHz>8Q+B$ z4Ao6AuQUDK|BAzOSIm0T-!7k2_#8}s%i3P;>2LeDkN2%N?d^4se|^;cK7C@^*DurGrK57|Z?IpczrlW){?3}?oK4lt z1E#;d>UH(4H~pReczAzzo%H*RZ(aQjj*IDU)sim0ewqGWb^afH>rH>RJiWbdy&2yF zUc6iXsd=5*o;xl--M5{a?fIT>|Loh&&G^3c!bAEcrW~_9@4MN=(CvBgq|bfrG2?qp z{UN^froU%y=AY+He-FL+FTU5C`e&W!ANT(&{jD0?C$zsGclB@o^j9J{!^>^U5C;A-zuWZkC^|zo&pV0mezr3HG(#*VO=I7H+Jv(%M?s)p$q4RTx z$FKISH}i9F-ZsY@!Fk)v&%t@y%+G6kTr6J~!h_e>G$3%^U8|X?%m@ zXvX)AL+|h%k7XL)PM2M+hr*1L8Q^*`q5oC=?Xb<_(BrGP`a9ybO%v^J z^T99r^4e?{GxvSUx8CG$w?B5!n~Zs#nXmp=`up@hSNr;9`mxui_xaZU-u)do^<`h4 zn)x|6Z=3uL=C#S+U|yU4-oN|e-=)8+o^KwyJs&V)W}^KK?x%k5@ePin8QF0O%tvCG*x|#l-a_hUX^taLXr~CG^CVxx5 zKhn3}`wKfj{(_tAz$zJ8hh zzW4sYzV(^rH?E`1x(V^`^Z+AN``=pubgpKl8Q6^f%~W`WtjG{e7U(-+kr8-QO!V z`N8-8f*Id!R#f@M(TwjaM}&{>AEs>Sd%fvzaDFrWZJGYk*DurG^_zzGxBR*{e6Kg- zd(G4ree3mt8s8p$-`!b->i4g8 z?@8nL^u6BnxADzf^o<3^#f)#4vA^o7%z87vbDOu-T`}v;`2LjM;2U?--%iV0`0~Px@A?Cu_xYId z?J)ic-+I&Exo2(WTW|7r))(R9JHO0-|A`sjy88R{7w6^F-{z@jeEl;0?Qz3nzIK}a z&Ku&t&&2e1|4DoJUT^x_Y0|%a+o$R8CzbwuH{rH>Z{xW z{rg9K>y5+p-(2EbZye6=_Ag(5jYDvJjl&b;-u1mcQ-Aw>G0%5=V)8oZVDdVc?`As> zZvUpgR}R|LmlvkLof`S`!u0p!8Rz>POn;AU^MkKnroTal-@CuXPyNT&e$(IYe)W$# z{dJBaQmOy7@jY>K|Nh8~@3$xWw_7v5pEPdl+a64R)6;+cpV*#vT>7@J{h9hZX;t`s zDwwAxe>Z#9e?DUJclX9?N}cV)yiPYlEhjx+P2W4bzvor8^*NaF{pRoWeCy5l{$BlU zc3L~%@mQw$dC#?P_}XvA_vVfl`_`NO?%%?HKAx$+6)pbe+s@-2-@5u+f2{vLMzcS9 zb*~3}{WAScHNDrj-t_nHU-b5^H~oES#vgp^&31a;2><(Bard`lqluy8dvDnF*Es{P29(e3ja{&A#6C=J;x> zYcAf(yWSjMoxf9wZi1=LrHtv^WuA< z<2&o~w|94bVP2>I*crea$9Le<+v*}rIhn@y(XK!G+G+axr!IYb>rH>xtT@28-t_m0 z&i?hL{`Fh$5nmM?jBFa5ztv);^C=d7Hs zSDE#uzYYF8F?7D_v}f0ZW<6ZR2bH4Ra`@8M*Q$xo$IPPYAN6efQI=-(x-@(^@ zGhaP=UZHQj>F`23vf_Wa?X-m&y|&mF?g_g{P5KX04< zo^;L(U%yO$Qzz}`TW|W?Z>#Y6xx*%fH-+Z!oaSxA^0#EgZTf?Grd*T1kFGnzx898J z)eeD#N-ZoY9eyxR!4-M?%%Tv?e;Cj>FppWVAGgIF2^~>}(c-=eh{(kV(-}L8) zO?{dAyFPV(PW=r!nEv*zx6-$rnEHbbQTw~RUFj~~aWvx_9Cy>-ueNCHb1>WUna4k( zn`YW)#y7bAoAIryzrppUyr9Ex)!zxl%^P`rGWGYK5zp(PGwqJr-&c1Y;T!j;{cW@7 zHTpp&rahU)H#m-_zm0p`;p>+f-zF_q>wlW^qxN^Nla_||chbv8{p9rBl&_QD8ITE6 z^atL|di}?0b(6W*>ksIf^`^hORUGPjztwEd4fp@Ju~VFRz3K0fGiLh6-Sjtg@@=90 zz3=s&x=2%w$=|nkJlMD1{Qj*G&;3vL_v&W%`}$@28+0)J{pb(h|5x?*qt54rj_=!* zXXs79wA*aYt@dA(Q-A;4#y8jg2FKBiZ?Ip+DY)LuSGnfz&KG{t+?$V4^Y`q}cgkse zgYEyV`upY~ll2pCjAPvWeRk7hbXQFIW_u29=f72due-9t9=VL~F<(Biqxbb@eh$vt zW_}KiyXo(z$3LiPjf3g$le-P|%}eI@zx=Vlw?69j{Q2=4eEXeDt}uWt+w zov)UB{B7v*bNhoY@~w~B-zOeBHS~D0uKn+)=UwZ&4?pVozIxi{ze|4$uAiAxe+M0P zSS;h4Yk!Y9YhWznyY;$zLi-zZ(1W28H#qK@#`on7SLu{Bd1&%C*Z!_9Ul2=wKR#>u zLEibtoEh5R{x2Wr zTW{v8=Z@)qxC3EaG>TgO_PD2edB;)zv0Bk^!~@58*PHSE@!*nOz3a{P931yd+w+>| zZVcW3J~?ks{imj#W`A_lVat5|GQU5zrT=}XO#R*Sn!8W)w*R;4@0_apa_aA^oj(Ze z@5iU!;#;4ozdv`LaL)fif4AS_F5h-;#y8jgw)nVy@Biig9x|~gr}2H@wmtg)FZ8$J z?5%vqnPz$&*Iv4+NUO*H{W5>G`T+^HsiaG5g;&_5IhQ%zX7}WpCf> zO?w-i(ZshtQ~qxC+&ujt3G?@H_qTM>I^T60bH1=;-(7s;W%?U*F#Rn(-v7RmX>ZUc zQ-9w%VAbd(x96Q-ywW$%nErN{TIgG!slUN~nf_igwR2A6`}r*chbP(J;~x7o^nAZf zw?}>LjN0GRRz4njd{tNeu9;B3f0F%8-Jzc5s(#FPnDGs6x2C@jFQ4giFxzuo<6AWB zmz?^0Pt^f^z3tJJsTFa}S9RrYUH#o*fd6{3>F;+xEb=)RzoSp??^~ZKfBPOYYe16y z4fe~7Z?OGld^frBVqg1Bf5%LF#W#+ozn`2m*S9`Xf8X15*vU!u_ppv@eC;>=4LX?q z_PN7&-~bZ6su`prvA3P@?M{h>2J`%lsE9|bWY!Xfu zuSYIu5l4TU4fvz)dWGrl+%3=XIhg(i9ZY|N4yM0*UfCy>{=U`v_5rHua_4h9g4?Q4` z@jYb!OGEqHf6biG{!YCADqp`$f1hpJ(zibD{@&VUp!2mF)#o+3GPRuR_1H#tAFG>Y z+GoZ$)$-KP?RmlCvwi2qroa7l_PY)>{codxKCS+Ju)h}{?v(gm_3ss?5`Cw7 zIqCN4>JR^hgo*oyjxko2h-zKj-{J-~NK_ahE@; zZof|76*c*nYy0xek6?Yld~oNFzgM@{^?$v3IcdLK+vm<7ul?7w&mHf#tJ~ww*KbuX z2mM_Cn)%Q5u6z5_e}7)x^IxuhdUvqo&HtMA1^xcp+m~tk`>n?}=oei6%-P+2x>xo7 z)tygws$O=tpPJ*3@AUM$b@lJv{pA+b%X{h-HS=@u_(LCFd{~nY!SQtW?={=s|62Q8 zubrzsf_|+oa&4cx|4Sv-zD4Tj_RU+HSC^k_ z``rECPt`uT`nmIGMfKmi`I)X>c71pM-P-3KPi?4fkDEU=#}CQ0?}zI8a`klQm-yP} z*8iXC_PE<~&HR#U`-1tBj9*RrGUadZ`0Br{eeQf*S?wQepU&Tp3#;=ZxE!4Cg87=s zCse0@D({;XERN?bp^++@(*LrdKAw=bO6m%jVl#U=j!q*AV*&Y$%cln*S))IN9q z)cf00*S|Tc#Jzs;t5bf%$95i7lBxY}`##gY^-mlk?bXZk_dae&iF>@JuZPT^|IA=1 zUoZFheBR&^x4i|``Rrddew)khRjb^ePAW9~vSM_Jdwi&`N6kO@hmj@j{@k3Oq*88w z{;AvN{HgnU?f24{M~0TT+l$-&?q6Ii{(AYVIgecIZEtG7pN9n6Tf2Ske4*RZ@R+iZ zB~G2GlwPj-<*eH6bLWrmboccCfeR_K-xbwTq#aAykpSPKBxvaq^ zm%HtKPidc8{D|M0jHPkWm5df*!Qd)=P( zkAHo2iF>_HFE`)*kgI*|IcN5jCHivXncBPb{#HkoxbwMQ*6r8hkw^R7>u=htwAqu_ zWNfdSA8vbBmtE~`-v_Urbw$SZx&3wh`aj=8USF@1^1Ez(=kZ>@vR|GVDf#km$M>xP zy-HF@TP z9FHx3T_E|tnX`P^sF%vce^Y1qvm0m3knP7k{$JI3qHO=#Gu3UtIH_MRujtrvoRqJ9 zzWTPP)Hi=`yyhzJe3{yEiz~c-Pwn4fnfzXlZ`pg#&#%=!cYNLRrFvbadbhvQ2X+`I z^OxTKz8`bt*b;ZX|Hc{rf|EWg6>s%INR6sk*4En zwbzp$m+jnOtmKEge?0w+Qt$q;N%!8h+o$KR3Y{O9uD^Vln-9~azwUx7dsYkP}bIW^bKWAD0 zxUX;0NiFE{clTfV{TR>o>7K7R>hW(n@37G_f9n0= zkhU|-`0M?j9&dO55nub<{ef=Z`i@_W_qK28uga75Iq#3vJ-(j(Z~sGE_`K%NJ9M<{ zZ@zT$yT|!`eDh6@X3r!)zMk{t#ig&;Zl8O6p|`jBi?6KR9?$V)pZP7Xmi=pIXZzRr z;T}Kl;VgT8^n9)J!`=S%{H*iCJ$`kM|D61A^I4w{OxWS&ak9VE$IAsr-7!+;dwu+0 z_Ttv1a{T(S-D0j!r9Zkoix*8C<85E@oD;^#_VAX|-ag;Hd$rr&U5@LdPN%h>d%n7h zT27T-x65NvUVUd-`+1J1-zeK>k^Ekdul94>r|WNc-Yetfd`pkl;v?&u?NQhN!4rp% z^tPu(>qAG%{QaiWzSJd4WqbHP)IK*qYP{Uz8GSr*;pBC*Wqa56kCZ-AIbG(D_Z^Q} zEw&%$-M$)5ylj-u>zfx!<#!}w&ypKyzQI+^=4ya|MR-jpCK(~jPkT^ z{dL3AU&PtICXLHxiighMcTd`VhPS=nciv~R)c1~V&sisp_xhH7eZK5}^!69tK6iiE zpp)v4dX%2=tM4ppPZiaVo}WKCXvYOIAL#oJ<_}ss(YxHR{dE&%|E9P96`!ph?e$8X zaMURAf7xkopDW+<=5NOKsYl;sZl8Pn@vc+;_w%dF{!TB?-~WyA-h7>~>&;{2eB?Ff z^{EL5ne+cwo#hq-efisI)!h@n@r`e{CeOTy7XNVDSZ{l0?eNnW*Yl|^ol+^ceeU&#rfNBr za@yy5)OVJ3d)(s#ef>i{Qtkn1zdnA^?NhH)KYIRi{~1_z^Iy+@y8Z6)m~NkbrF*@@ zU3Si=_3>^F?Q^dm>f@{R({8-CR{K2jbE?6d>Ec^=d%fGkmQPRdZI4caGquli{Y^c( zZDlP__xMU*ul!*9ruWG2b-t`9zv8x9?bk>a-(Y+5ZJ+1*N|TE_-CnDGZu|B8ed?z- z-I~$Uz5by0hqKl$zAoeVy4#CBzE_V%TT#o;?Qe~jXMfUkez#j?``5?&o#uZwH)H$U z{lA_M8%`WG#nZl}+r99|@8o)pn;(xK^Py3=<(~GyIszo^!Y^pd4p%m{HWWvY*tzA_PO(e-oFI>_PF=2zvMl> zncC->fAshU+Y?-_$={`+?Q`pM?(ZmV)PBVJjN6|s?^xIG?N+t@g7s%w&eT3PpY-)p z_2{hCIk(UA{#|*a$qQ?ZcfRd&=Qo|d>QPsZ)M}r*J?rbM>d{sY)yn6fr@Q~u_xIj& z@blMu_ZJ2KD7#MDua6JD`uE6*-uwt1Uw3<{uf{i}9!+KD6TPhG7x#La-XE(+QDttQ z+aJBZ`}vGlXUg@qSDfROA$#00Ue5PkaF&;zHej@;ee;_hzU^Q|q0dPU`>a(NX65tDdj*d>Hg|um9=u^RDMVbYtG_b1F}z^z}6LD45#qbL-RX zQ;&Me?3Y9P3cfi#v!CaBu6k6{N}2ELZm(y5r~Taa==-@BKJ&wT@v7U;-JV`@#`mq; z8jsC5zHWXw*Plx-zVD1D@@}8!`qXEw|9PVv@9N{hvX2%{^N#li6E2hM!}|CyeEW0z zudk1Pa!67B?epAEbYY{eH+qks=U-eg$$R|V>AG%{Jbr!JU-3!3Bi-u*dnm8e)!P-l z?#-7YZ@K4X@BE@ZBC|^N=Q@A&_UYMw1jqNX*@xUJUOIo9UVF_fZ+n`4v|x%HFY5WF z`0Nk8$5(FqRJ+uV^Ldc<;eKNV0 zpL;&Jmug??+HF62EAjTZ=QH~HLf3s4-;l9=`uJyHbEkdJu2}!(Z{9xl_)DMvUUpo$ zcmLrX-@R4)N-rF+`P+%N&prOv*ISyO+rG@ZziZL)G4Ju8+djQNpSzFKzKeVHc{lI& zdG2Q_IN~MW_W!|->ioi;AN2n1)FXbKB*&|I{yX)pQS$zno?qPdx$i$Z@4t0w@Xzz# z&$)fx`-QImWv=&nREshF%Dnj+Y;UU9-jlugtJ`05eC4*!UEW`{ug_0MU;07L?Q{1x z`ufY6hkZFyuAl1b8}AlBS#E2eZjWdC`)XLr+Vh{@zwEQG<9B7{uJ7gCKF|G6*N=Jg z4sUyAUA*N~?{d&jx6d6vef;wDv-^3kAGm(H{2%rFa>&9|?)l*!Z|V2f)cgIn*J_V@ zJf!E}rH|ZOdpte+-?G{N^zPr>_PNKSdVV?h$K!IJ|AO0JFh4xY^Sg|m;%)!Z7Y~@? z&6nmk9a($-q381>_jbm2*0$&6?&qmrkDq5**YD2%+OPRr*Inm59&LVAxw)U>dFS}0 z$Fy&}*RNbZ*WbB+y3?hPzWZ9l?N49N`{0=meLlhME%k>TC)e`wY=64F!TfcPr=080 zrN`Hw_Das}^USwHrZlKMpLjg`{C%KrKGf}1ZtBOK|2{pquI+FP`gn zA8eRq%~#zX&v-YPa{qO5KZM@D&HwJ8+VhL&`lLRda_{eQKL1d<=|7ufKECq!1jn<_ zDJ^~Z8rnWj{(j%=*4lo-_POU<`tz%|KicbsERX;6@lv1HMose`e-@l@WNw~)KdPO- z!S=Zx`tvmlcH8R7jP3KBZ=brR>2&Y>5%ko4?)jY_&pwy_XzoYR%cWP(t-b#Zw$I() zG*aW6y6dcVk7sP3Tc5uEbJ?OFW_hpIUv|*s%@*T3?^h)6>4%HSY>#oykkAJ52x$~K`{gt-fV*I7@I=P;xv$svhqoU0@U7nKePwrEHb?fC z`uf_rTO8qQ-)paTuid_%$JI-Hf3&Jy{nT><|3v@+1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R;YEJ9I1n0000m$lrPl`wJlh z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* f1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfPpm-t_N`* literal 0 HcmV?d00001 diff --git a/app/__pycache__/main.cpython-311.pyc b/app/__pycache__/main.cpython-311.pyc index 1d43a2be177e77c2fd4db4768f887604e9cfc126..a00d6091e15945704c46f0609a0c656658b1f8be 100644 GIT binary patch delta 937 zcmezUhxy<}7XIbDyj%=G@K<(Q=4WxnjrAW3LPkMDAJN(x?54CkMpnalkZAZj4#Z;R!Wm z@;qlLZm=m(mHN>-lNI8{C%K3s)JVC+PQGBy%9EL5s{}X2E+J!cX`BEX56q~L)Z&uK z0t+QJH>NCLocuafP#&gMT~i?^KQRSap-*OU$!5_sKXFud=GH18+}U4if@-+IBGJk7 zKiKh=0BtJPhnYS(u|RUN{Hxl@eA~4VnvbVDp=t)YY;wRtSA>e0_4%kOK*mcg=9#RJ z$&XMKS0y>wJM#%bAi5C}*am(klLK7ECjXn}F!@cEsD`-71qCtXL+;WFKV9O)bgDPnoQkvknp3md!E< z+dk%WAUHL-9lS7K>w*Gda^Gqf?xNB>SjbKe%X2{}pPT2(4_984T3nh_GI?&E)a3iu zcqa$6c1*r^l7DjF9sbEX^B2N3Z7ytUWtx2964zvbtGtsp+!O^TEQc4ulLNZ#CTA2U zOn%pG4@wM^9;Bo-y+7V9YFmFDE=C>Uwh+UOS)rA}6~v)w$iw2K819_RbTCU0nAo9t9233uk? zxT+bbk=K7xY_f2*Dyy-9fu`nU!7bX8*G~{c7*cRTVshSU&dC!dN+1mRJ7GP7^PpOD wa`Z&r$xkLO+MHZlSH;A}Hu>@QvrIf}n>YRV%fgu;#>djY^}$Vd@{YfU0e9_vp#T5? delta 92 zcmV-i0HgoGu>|}40}rPS4GI7N008tJwP_^%0I?5|3j?m}0JEbDMK-fjLX8}=EMX7^ yvt(zS0kb@5LKm~CmwtKzeFc{(5&_Kug9W$v5&``L30D*j1b_ajv&1E|2 zj6mi+UEj&3dRmPBlcV(<80T%CqvyfMD}PnpMWzdIGKy@D(J5zS z6rFrU*O$G>A81Zd)?_(7-N^xZHjE25_v(2t3dmiN)VVHcc}ddpqNMc|N$ciE`r9Ar IGwzcI0E^%$;{X5v diff --git a/app/adapters/__pycache__/internal_data_service.cpython-311.pyc b/app/adapters/__pycache__/internal_data_service.cpython-311.pyc index 782601282ea2daae71471af92192e3c0c5f10397..7cc299fda924d2e48ed2cd708ab2fabf05ed23ee 100644 GIT binary patch delta 1125 zcmZuwZD>Hx}X`> zToYifwE>hJqF;4WB;+=K%PU4&NM2pt5#l7QxuDJK);#hj!&)%A8ta9E_D5}u$ffTf z9VDk!$H*XciO?z8wM^h82WCWgDb)j$JoPSd(Gc!W4yChwkz_u(sEua789)nWB!}SA z!E>p(Wl>1V>?p5f4h}h_-CSMFjQ(!;C`qk3;Pxm{YE3%ZveEgf`YhBnTs2%YTrgcW z{SsZUIADXf3x?fo&AA3%@KgIX@<1PUsAer;yK2r%xjAPS!w&Fne+Z9v>0zQR0nelW zd~Exa{t3>{e})!_wDGthgyJI|6bzsFzU>{sXLAgn|lfaKG|eCLGv z4Ca1T>ORq>hBIME^F~h_*{ht?nOkPZaXBpjN)%xP7Yk|QGwx*9)TxvywcdNAygGNA;9v!TyfKZ zZ$*(MI5SMfJhT>mkLC5_C%=>N delta 770 zcmZvZT}V@57{~VvMVgx$qR?jQg_bKlQ(MzSX?~Qbd3NK+)jT_H$9=TUYhOFA$>a8+QlIY#=hwabJ|OGFn?5Z=}X zI>v+6l@s~bOOv@gf_`Tmm0`kFiF;1nk;cyDa5lH1-BnNZTB=W{@vbeHo0K)P4OiPO z#T{fYC?RcA#ix$9zaW~%(G@#+)as7!lxr)nbT$-Eo@lx|$MRt!dp}>g%rv8Z>nbwl zW@fY#s!-}S(<-XmPbjIT+9JujHo1m}ylnO(^4p71N!59W=+4|Ou2Wkd3}`@*%FqUb8|vC9#4w0U&^4ir&3M!zsWDO1a8nlzcDem zV)ALzb|cT*t0C7|Q8d(a!^?z|4D8vIAVY*9${;gLGbjuRh8c!ghIxhshDC-Y?ADY6 zLo0ahNp252jo|sX>hy#P3-*m-0Ba)-D#h+d!nDll3}$#n+h`dNNBtUqjh>}PI5oD{ W94Ms9KZgJFZ}$Ju$&`nA-1QGoq416X diff --git a/app/adapters/amazingdata_adapter.py b/app/adapters/amazingdata_adapter.py index ab0f8bb..022e439 100644 --- a/app/adapters/amazingdata_adapter.py +++ b/app/adapters/amazingdata_adapter.py @@ -184,7 +184,7 @@ class AmazingDataAdapter(DataSourceAdapter): # 初始化数据类 self._base_data = self._ad.BaseData() self._info_data = self._ad.InfoData() - self._calendar = self._internal.base.get_calendar() + self._calendar = self._base_data.get_calendar() self._market_data = self._ad.MarketData(self._calendar) # 初始化内部数据服务层 diff --git a/app/adapters/internal_data_service.py b/app/adapters/internal_data_service.py index 4596666..d6aa893 100644 --- a/app/adapters/internal_data_service.py +++ b/app/adapters/internal_data_service.py @@ -195,7 +195,10 @@ class _InfoDataInternal: ) -> Dict[str, pd.DataFrame]: """获取股东数据""" try: - return self._info_data.get_share_holder( + # 创建新的 InfoData 实例以避免 SDK 内部状态问题 + import AmazingData as ad + info_data = ad.InfoData() + return info_data.get_share_holder( code_list=code_list, local_path=local_path, is_local=is_local, diff --git a/app/api/__pycache__/admin_routes.cpython-311.pyc b/app/api/__pycache__/admin_routes.cpython-311.pyc index c228081c5d11161b967481290164160a77a09d4e..65db501e943bcc1bbfb5735da0f3634940d222a1 100644 GIT binary patch delta 2737 zcma)8eQXow8Nc`J^PQbIc5HkhAs-Ip%MDFZ6PA$Ad4!M*LA%m0HBebs-;p>3o4t1- z3^{e7vza;ET0dwfb(-wxrbL#ab<><>f7R8r32l?M_dVxg z0{LUF-}~M3KF{xY-jBQIIsfP?_}vE}?}pWCp>Vrf_RHW?zRA4J^aOXO<_{($(>!vr zk1Rtvf_OX_s#hdgk?VsYMS>w=7@gU&(Aa>Ztz39Nb@7eVD=+`;rK#l$@1!nX zU4HL}E4N=yjla3__S9D&jvwsWmzp`Va`olZowHwkcpiPPxOg*EuxFAVkQBcj&`;u{ zctIihwAfj50ox&gO@N|g2@5Y$X^W9*Te59guzBZg-h{1DwKbye6g`O!l$e?dH(X^s z90>*1js!>H@bwbD{;n7(Y99sAL{&BVuieg#(Ggl6Ru}{-3ilf#h9PXvv?1e~#OTtP zk!(P3F_SJmc#jyb}PH55{d6vg>4# zyJ8w+B6RG5o$5ACzDm|J+Nk&$}fnx{YcI91sG7(rMoJz;uCd=xA_Y1WF<#*#@f# z)L=O6>Rl}uZj#?_3BoXJiMsyH546Ga;fdNT|6J%t{ zP?B~(o-8Bjd5)S&(E`<78@4KCTq& zYY`+bJMkJn>{O@v-a=&ZgnvW#TT>fW?- z&e2XPI^Y+B0R`3)mz@NDia{m`?baJCC;Ff0;3jqOpBPr~57)xna;7ciC|__i&N~`s zzMF8gtB&?@cG1SCDb{=#q+Df_M_xNJ-o04lzEYJOL%i+TINzV(`&GU_ZtwqxiLw_= zjK*vUTZ3wANK;nxAuz|P&HFRrl8wh!E@AViHcy($jamF-z zqPseCFWz?LfLdI?sNc7GuGOhqTNkVAuDzgE?@3$PG6&vgnPVa+ZKn!K-wa<0$Brd< zugZJl_KkZgv+!r)d|zFH?^F4{xV;Me?JuP!D_E=8`C|V$lR|#l;TC|KM zi-*hTgWi&yG!}8%Qu5Q>?4NpXn>H6yblw8Up9lGIhi9%P4)POVw+eQn$$fQ$T>wjI zngeZVUgJ6Dfg|p$n>!T;js)mYK@UzBN8v<4ys$Qrzg^AWo@S~ro5HYYE=U{j{mY!Z ZtWvXlZJ_909J(h>6Y>#&CZGDx{{S_$+j9T_ delta 657 zcmX@Jld*R(Bj0jfUM>b8h%Von8DcS!PlD+R`$mncY?BpP#3l=~YjFysil^|Vs;03_ zc4Bvy=S(q6F<&FLjER9^H4sAp6GJLzibaa$8nMX_n3YYLQ>;>XQdK2k(hMoqsj6Tz z*ivj-SeF6CSHtC^tWr3F88mG-@8R%N+x)_!h?D&mYidzZe$nK3r!dCg$=jTM%S{I= zFPaS^<^YLe79gRZpwLjH2ohQ_`Mk3dW6b1F&O|9povi12nqwwV=Srrcxsw^)tQqqr z+qyML%>o&=1V|KvJf@%kF&*UE$#QOTjQN{?xT!HJO#rD%1`!j1#4Vxp)ROqbhy$=Rhm zK%o~~Tb}G%{(R%^ryZ+{c7v2}+&ssRhe@Ch#O?o4q38&RI665u(1P_6klD0(RiFnWqxI&` sL3WG+2SJiOAOhs*qDzy#Lu9${0GW(HT)dQhb8*OeHb%9{7oz_F0K3Ps8UO$Q diff --git a/app/api/admin_routes.py b/app/api/admin_routes.py index e183e12..2b323f4 100644 --- a/app/api/admin_routes.py +++ b/app/api/admin_routes.py @@ -273,6 +273,60 @@ def get_test_history( raise HTTPException(status_code=500, detail=str(e)) +@admin_router.get("/admin/tests/internal", response_model=Response) +def get_internal_test_list( + token: str = Depends(verify_admin_token) +): + """获取内部接口测试列表(SDK封装层)""" + try: + data = test_service.get_internal_test_list() + return Response(code=0, message="success", data=data) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@admin_router.post("/admin/tests/internal/run", response_model=Response) +async def run_internal_test( + req: APITestRequest, + token: str = Depends(verify_admin_token) +): + """执行内部接口测试(SDK封装层)""" + try: + # 获取股票adapter + adapter = adapter_service.get_active_adapter("stock") + if not adapter: + # 尝试连接adapter + config = get_config() + active_name = config.sources.stock.active + await adapter_service._connect_adapter(active_name) + adapter = adapter_service.get_active_adapter("stock") + if not adapter: + raise HTTPException(status_code=500, detail="Stock adapter not available") + + # 确保adapter已连接 + if not hasattr(adapter, '_is_logged_in') or not adapter._is_logged_in: + if hasattr(adapter, 'config') and adapter.config: + # 使用已保存的配置重新连接 + config_dict = { + 'username': adapter.config.username, + 'password': adapter.config.password, + 'host': adapter.config.host, + 'port': adapter.config.port, + 'local_path': adapter.config.local_path, + 'use_local_cache': adapter.config.use_local_cache + } + await adapter.connect(config_dict) + else: + raise HTTPException(status_code=500, detail="Adapter not configured") + + data = await test_service.run_internal_test(adapter, req) + return Response(code=0, message="success", data=data) + except Exception as e: + import traceback + error(f"Internal test error: {e}\n{traceback.format_exc()}") + raise HTTPException(status_code=500, detail=str(e)) + + # ============================================ # 数据同步接口 diff --git a/app/main.py b/app/main.py index e93f21f..ff0977f 100644 --- a/app/main.py +++ b/app/main.py @@ -459,6 +459,7 @@ ADMIN_HTML = '''

快速测试
API 测试套件
+
对内接口测试
WebSocket 测试
测试历史
@@ -566,6 +567,15 @@ ADMIN_HTML = '''
加载中...
+ +
+
+ + +
+
加载中...
+
+
@@ -599,6 +609,7 @@ ADMIN_HTML = ''' if (pageName === 'tests') { loadAPITestList(); + loadInternalTestList(); loadWSTestList(); } else if (pageName === 'config') { loadConfigList(); @@ -611,6 +622,7 @@ ADMIN_HTML = ''' switchTestTab = function(tab) { _originalSwitchTestTab(tab); if (tab === 'history') loadTestHistory(); + if (tab === 'internal') loadInternalTestList(); }; // ============ API 测试 ============ @@ -736,6 +748,131 @@ ADMIN_HTML = ''' }); } + // ============ 对内接口测试 ============ + let internalTestCases = []; + + async function loadInternalTestList() { + try { + const response = await fetch('/v1/admin/tests/internal', { + headers: { 'X-Admin-Token': apiKey } + }); + const data = await response.json(); + if (data.code === 0) { + internalTestCases = data.data.categories || []; + renderInternalTestList(); + } + } catch (e) { + document.getElementById('internal-test-list').innerHTML = '加载失败: ' + e.message; + } + } + + function renderInternalTestList() { + let html = ''; + internalTestCases.forEach(cat => { + html += `
+
${cat.name}
`; + cat.items.forEach(item => { + html += `
+
+
+ SDK + ${item.name} +
+
${item.description}
+
${item.method}
+
+
+ +
+
+
`; + }); + html += '
'; + }); + document.getElementById('internal-test-list').innerHTML = html; + } + + async function runInternalTest(testId) { + const btn = document.getElementById(`btn-internal-${testId}`); + const resultDiv = document.getElementById(`internal-result-${testId}`); + + btn.disabled = true; + btn.innerHTML = ''; + resultDiv.className = 'test-result'; + resultDiv.classList.add('show'); + resultDiv.innerHTML = '运行中...'; + + try { + const response = await fetch('/v1/admin/tests/internal/run', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Admin-Token': apiKey + }, + body: JSON.stringify({ id: testId }) + }); + const data = await response.json(); + + if (data.code === 0 && data.data) { + const result = data.data; + const isSuccess = result.success; + + resultDiv.className = 'test-result show ' + (isSuccess ? 'success' : 'error'); + resultDiv.innerHTML = ` +
+ ${isSuccess ? '✅ 测试通过' : '❌ 测试失败'} + ${result.latency}ms +
+
+ 接口: ${result.interface} +
+
+ 参数:
${JSON.stringify(result.params, null, 2)}
+
+
+ 响应: +
${typeof result.response === 'object' ? JSON.stringify(result.response, null, 2) : result.response}
+
+ ${result.error ? `
错误: ${result.error}
` : ''} + `; + + btn.innerHTML = isSuccess ? '✓ 通过' : '✗ 失败'; + btn.className = isSuccess ? 'btn btn-success btn-sm' : 'btn btn-danger btn-sm'; + } else { + throw new Error(data.message || '测试执行失败'); + } + } catch (e) { + resultDiv.className = 'test-result show error'; + resultDiv.innerHTML = `
❌ 执行错误
${e.message}
`; + btn.innerHTML = '✗ 失败'; + btn.className = 'btn btn-danger btn-sm'; + } + + btn.disabled = false; + } + + async function runAllInternalTests() { + const allIds = []; + internalTestCases.forEach(cat => cat.items.forEach(item => allIds.push(item.id))); + + for (const id of allIds) { + await runInternalTest(id); + await new Promise(r => setTimeout(r, 300)); + } + } + + function clearAllInternalResults() { + document.querySelectorAll('[id^="internal-result-"]').forEach(el => { + el.className = 'test-result'; + el.innerHTML = ''; + }); + document.querySelectorAll('[id^="btn-internal-"]').forEach(el => { + el.innerHTML = '测试'; + el.className = 'btn btn-primary btn-sm'; + el.disabled = false; + }); + } + // ============ WebSocket 测试 ============ let wsTestCases = []; diff --git a/app/services/__pycache__/test_service.cpython-311.pyc b/app/services/__pycache__/test_service.cpython-311.pyc index b37c5138ab6f4ab9a0d9f732fb5b3660a6b6954e..a4dba86d25ae36c4b1ea7c9cad2f465d4eff4b88 100644 GIT binary patch literal 34295 zcmeHwYj7K7mT2o~NtP_j@2C8B;yCdue&ijT_lpE@5{Q8q#VtE>Y}vFVLOe1X-i8fe zX2RkyW-v?$W(bUTcZLZtv%v1Xv)sLV?@B6FrEgcIDz`vRa{sI-l3I#;f82Avuhr_7 zOoVt ziKI>MPA5lmw*`*+!Hgkmw{&K=6@&Kt__&L1l1 zE*L88E*vWAE+X{O28)MEx=V&iyGvD!n%T*)hG!VoctOoDAHYA|y6vpGi>WjDTHt!_ z_kVf)x3A4jy%c%z?ebOSk>7q2`Sk40;cIU^<8phQoZB(j=y7t7xq6+AUZ=;~&_8?w zsqC*)3kKHVb$VSxPQi$fbq;zRa9g|2){`nj z;o1z(ERfa&=NW7|!MDP_Ou#q8JsaR>!F6_@mbJij4x0hA)T|ZGbH(#aIM0LgOt^1@ z^L#c7syD*^0o6tW|KG6yUT3_rXBXhk2k=ifKhq;U>u2Cf zQaEyjG{{D-VoeDe2w>`B&sYYyQ}4fldDZE;G&l&_K~Nqc-7(_oIZi7|$c*WxbjNhk zBb}Q?v>uOZ)CuaR^*9InJjh%54|}$)d&n_3a@^5#a+o{e8FBPF9~wF3JwEL28FFwZ zoZcQ#YDbT#@((#iMjjH+Jr8+7WU(g=Bc}vIPmjy(^7izM=E$Vk5W8Cd8G5jZoo24& z6u!Fi?9OrR86)4+GOi693i!4TJeKkeO>o=Fmz3dUVSw!4K`KVjb55_1b1P~i4<3-( zC;`n=)&_PTtmXnd{XmlSm`Yw>1VfvEq4rAH48!WYQeD86o=sy7tdTXb>3-F;3=jFE z8*9D*lKnucdB1vTc&vrZV69WSi(~`{oHq+w4{}+k9#^ufAom8gaY^;q#5O}cwy>+&6>RJOlX`4p+kxK>b`86hUB|9Z zmc2r@8`zD2y@}m;$0eJp)^!Wk`ep@Zcd?zo*%o%Ii&?OKwz1m*ZwI?`5xiaOZm5|( z>|S;sdpEnEJuszNusz(v-V1c^W4n^+E~32$73Da@9)^7HXCHu;eWYK_cC!z%53vuk zN1^{%s6FWy@l4^jYI9mJC)Ibqc1+_n#`<4|@oU(Ti;pUM0(gsZzGZ-O{pCMIUimBdZldq^$oT8m z-=6u`KaS&f-ub_pd->hSE5H8NKfd7lU+}8EfgSt}d~4sp?$~Ds@b&2liu(2GSFb<$`2NJyG_xO!O}I5TbajvFYmU?m)v$uP znH4mT_B~ps6ZDjnp!0YgoLA5|-K?PPog|4Nc9wf7w8 zIyjma&sY>DO<0nUFmnEl$Sc2zSA{6X77$~dR!9?><{BUw*NC7A0DN_=#`9JLZ3x;C zbN~>v$A;NcLI!2F*D(l!c5puY&XIHa&8O$*CVw&a`n!>rpBv4O^XlastjpcM3P%l| zI(0nX_thjfLFe*1hdfDYH#hax_20dxP`iviAAA`(J;{nkkx_L z(R7iC@oS%6Q0iX#)+8tLo3OY7)e$-S_FmCk={m3(x^~7qO9^h?b=6gUz>O?)|)xlA(y-7xYIG{ zJx)eNktg4ceENsEU!MlfM>DaI1f@|#A*xRv(g8F@zxLwCbElsavMDvsu#f9?_CVj{ z^?7_&lnCuXKb@e6w2e@@6d8I-f?N#9v^$CX=@*d~UP?%qn8QgIb|08SWc>NLm)?=k zQ{_OB>m=mJ_^j9xV%))7!ZG9+g_^)25xgV>jbm^S24eA>JL)uW?eV~{sdo^L&=r%? zLJxY%-P?1_(R-rL1!-cUIOTyB9uozQI9w~{*8{)6TK)Ck zy#sa@kmabcy{W0Kc`Y61M-Y%)a}%ZcsBhToB+ZQyrUC#Jo11=~Qud*Gi#)ygJoMO&cXw{x z);Ra^%=JH`pd&Pd{6x}yF1N$o>!fu>s+5XVB#Y${$zFOYa{7;r@CKZly4V;Q ze}C@d3qqQhTqsVY>vH#U&LOAU>k#$eonIyDL0P;Dko2HYo~{Zq7qx+;ri_3d%uSuX z{`sq`nIW8?(uPhP}%qy#^DDZho(ru;PG;OXkgNsp1d#|CDA z)66YBlV_h~r#q(~4d&K`bL+-USF(#Iw@p_}-y6(c7S3KaZU7uh37^{*$tmND>-f?} zzO0%rUBg#zzgpYO*EC+PSre*RbEzO$vn5=!g|Di=T-6$?YMp%`ShXo!wTZ7@&ezsQ z)Ai*=Q3ip#7fktvjZOpEM}w(;3k4wMTBxaU)n+VXy>apU0t+r zQJn^hg}`D$asX~3i0)Q3=4VB9EtZUL82~pC-0D-wL>?{+$ta{2DB*$kg6-0c65CW7GXIS0l-ZJx9*cGPBW1$ zWD9kCRMP&sMt(&rzr2O7Z-K^!93V*9*nj}7b|7#U5SSk0AvLs9iGU>y2_Ud!U=aY^ zL~zSaiqJypi*nE@5@?F2UL^Zg8UL~DTb7)9O)NF^EW3Go-OTzxX-7gWMt!O+DwN`O z6^aqzO#-9C+J`lHrg77)C-h8a+02SdT)?^|Xx$RFZV4E+aQG3eFvsS=4eYMJ3qhxw zXpU8w4+Pc0?dF-Or&g@OO=S*kA_efsKIxUWUBzLnypy`4^5a_G+`#= z_3Iau#|ni!>e-b*XBC(ut*KQqxl4?gpcw7R`pqb*I2X zqoS5Qz}}OZ<|6#vtDpsDN1%0(J+w%khrzI@1=He@Mexw*sHJ8{wkNqwD)N4WbpY-$ zwwFDc3Kxu@3$(i&6Y7O-E3Jpx3M(Dsw>)2KL6m_v!Y=v zHOs_UwGG(wA>WEBAj8sxDj;JJ9M{MzfZW7ELhmr^>>=ZaEK(Nby@dsjM94@(B0`jc zDi*a+e)HL{uT4K0H&7>4fKVxkSmKEqd1R~=N~|S4YF|*;5{HTDWQ>ZYrbQWzYp=v8 zDK}0c7)=l!xpzCl!vr$ZJ{8+HpT9l#!v!->=k6(%#}^!D|P z^!ACp4002@vubIqi0D4-FQ|fVKe*%C*}uL1`IfZtr2k~&GOd@@6! zPSzU8O~UA-K9~0t*w;8;FWSdPN3E>+bRNLniu0BlghL(WmFBvJ9k$^~^+Vw__8 zswI`B#xl`YnA{bYC3A4hF^E>V9?x;7(>t0cF`~S`dTIP963JQ;n0mkY^u6o9dn`u4;%;pOKdulx;tM^lzTiNQ!4d2*@| z4YhFSI@kz@YcHIE&!m(ZE|e#i2)@Po!P(;R4Z+!|(R`^a2@hJARtjY**-QKSuU-PJ z0T>y-H22rf&z=4(HH#@!TBON3y$;u)Or@3gm#ET;MDy`C`SP{To=eGJk`i5e;gjnx z{7u3a98q}s1sDGym0H2zu)DwKxDWg??tWS4tGKy! z(#LR)UytajlSx{6e~FS-B$7!wa{j%@N3V;X!Uf(BRmsZg0Xx>2&oU_sS|= zaeIlCE>9zS!HK-S`SgwFUrNcS5B=o{qYvmc!4G`>PZyylPt^yecuDtxZx~)L@Btxv zNy~07@g*HkAu9v8ynv%`KKs*+civ1{hE$?GF*@4g@_2ms-jj(|aeIlPm8bEcgH0TB z$3z6~Mf9M}ueIE9NW{4S)M?_*BkD3z6PFQn4&n5km}@8xKN=-vkNbxz6a7QUIC_}y z>3JHAp2SIn_VaDTeh%hW@LGTyXg_!@_!VtmH}r%38>L*8=7i!&{7NFCW2clh{=9SEKBeW(JWIK%*CuWqfZL44y81!i&)fpOljSn zY}D8 z=GXYU_|mn6?ZZT(%VvrLWo-$KOC%`I7E~xDYIJo4R(3U3Hh`N5ZtbCpxScdarOM3L zEaZ6|PNDEAntSws{vqm?@z3j);Z8uuCTL*RU$4xO!eqeRr{Vv*Zg8IOWmMp#@iWtK zBYC?3x8l(ZV;WX9ruA!}sfpV*=HcowonJSAT`1Y$PTrW_uOE9Jg4#;0~bnlG=!8%qN^(lf5e zpEiv#6iJ&LOG!b+_*Fn3ry7uL6yTTv=t!j&ACQAmW+0E14*}koam?g5Ov~n|#?!DG zzwwt0t9?`B&5hsno0MEA4bfwUGpm3nNdl}+l9-m2IWh8e(_h-6b@wK7jb$lA{|hnd$k&uPapj1VNo znp~46_tD`Yod-hMC66xJjE*mTcxII8v0gJ7)9Zj9PK? z-e%s@IIl2iQ|7A7540xU2WbZiV~JUtKXU*lM3Jpm{kZxB>Cc9BY}N^K&#mr-K1q({ zx5?`ZYlF?6(f|g|*CJbLT|gJJvfWPOjO3(^%~SHu<@htFOOw#xhO)%a8ZrlPwk+9} zQim7wFF+skfz%I;WkKIBYeO+w&^oh}Jb^^AdNG(2{*+~U8t%q)TLsZ-RgCwpIGt*S z`+6+1aHyX=+B714|NpVlAPBM>c zjxZ-xRZKG@uXB&~q?$faF-PEg0>f^>QCj4~x7pka@4+aI224mX7yEz6#qm@*m=B%h z?qP2^_;1|ox^hAFGeI@R)j_%+R2v1=<{wnGKNufa1qoN}b)Ndcj03V&FmrF!3FoOB zsQ3^ykOni4!F28s(r`xLu+Ph(gBGUYY6m>SZf+7TxN`tTa}K#ZzLAk(&g*2$X~@I6 z@*CHom$>m2Ja?^xegH;_ZE!AVVBR6O1yh-E?lzb=R^cpDGNc^Mgc!&{7fe-zXx>K5 zq|Qhp1cWp!P>+kHllRj+zTRF4s>N6aoV-kYazff5OmTGgo)XgF!;Hl68erP~2-s1? zpkY8B8rku_fd(D?3V5KA2XEoo+X&u4fWqa@1E{OuhVgs=!FB`~tss~V!<m7L>QQfBT=L{*&-RF4Vi)CUbwEnW{PU2NVk3EI~8k}^@9`8DQ(^2VrB5+tp1 zdhqp(6G;;{I;qNpP)ib$;mvdHxa#jR$R)%v{ zjvKG$6pS1B>=Kwx3bWFTc?67a%vpStWtejk8_9I}8WdgISxxS(^gp zO)z~mD{sOT&Z`aDmW6H0#`S!9#+h|M$XLspvtGIT#k(h~rd9;aHDPm2z>L{g^Ct_Z z?7@s>;~GAv==FU+-}mbNv->ec3jr@|8}A%{l+Vh2z5eI*lLOPulLPqoVbzS|{o0GQ z!K~(RR`dAQ@vT71oEuIrp7e#%s{`rPGkE~`qLT5we1ZKf^Bd-K8B-ZiMr~P5z^mE` z)kHI&T{@|nSciYpN6sG&WUq#u&ndf{Qya>uovD}}ogSTWAPnZTgmYQ~IW6DwR*<2} zvYOA#p72a|z4gEw4@{p3mbQdTTY~v5!OWHk4e(M@Hnk>D+&Jzz^Z5AVvqye+bX-d& z0H1y6)uU&Rj+-Lc1>x-KV0LXdyEe+KHrf{kGqq85i^a|t*Un@FiaI9Ju3{BeP5VOG zD+Adp`I`Foy5H%Z)m*y&t7FsM!J2!+HTMR}68DK+6TA3adpNgdCNGrR7|3nJgb%*+ zAgIT5^=#S~Sy2sB+x#t~u7x>>06$_=9;Mu z=Qd4kx?H?ERJ=M^+!iiw3+A_n^V=byduj(?SUF{fv*|VAs*XU(M%Y6HUaF!;dJn7x z;qq3#r0QyAeYg@VVwIrVFi*4;%7MVdG_MUe-F3NXTc~N<7e@n!?hiIS5N>+la#L@p zsW-qr7Hm2hZaNud82euJEv+f*P7JlURtW?X)$&M?)2b>P_q-S?l(s(Xqwf6mxp+_ysg&nry# z*qb*uiKEZx7XQ^Cc*tk=TxK$Z^91Nj;}FEZUzofcT80+`yA@*)fcsVM~)6c}WN zRAU;IB5*%G7*T%0k7E|Q5NmM0$B!hY4oq7;%TRnRWJgOZ7xI8vG}?to;p?!QxT zU-FZ1pVFLiN4W1%a9{e9a4#8oKeQk0-dEJ25&M~?GS6-z5WD*FG$-UQp+ejay$kzNMI#3;> zFF)R{6jQv@RPO(&;J#Mme(gLGtdZv*J7<4ljd@L9F z=z_b8wFy{;D&f1_IFA&U$+?c5`;9;L3&s7v6};E~B)l(=@hd;x0p6AMzf$CV%{&rB z19Q@b-&VKPnv=fq+}#QZu3ChP6-g|>4YuJ&bCK`8KTer#l=GC*4R|DuEsuYiZ^Y`? z5;q_Q(j;xlu`Sz_!VPHh!}!&w8cX*+98d1od5^}gypP1s`_#%-!R2^+<4^rM<+m`a zIzdL#W9jb71o~_|)c_jE$(BId08<%SW6#XsME6pUAi3qeO*u{DDnUt`QbJ1ZqFEO=Tmb9C2a*M1%nZ)SwQ)Y;Xa0mG7GeBuCXPpM90wea z{|2uA+pM)8tKI)TH3k;)OVShX$+)cj7NsUHskS#II5BUlwtLm!#MF*ip0IR@$GC&C zZZnxSEV>?(Vl-hgE6h}uhhuUuLx+K7QL~a~DUargF_Xkk2|g{ivlsk_`#EeMbm=SX z$6X&@ZdJKHMvw{bXIM2z&s56LXid z*T+G8(n0V#!nFnbY2>N2wF_|cFrOZlYvoQM2})Qn!7Os{#*u~H z3@%R(x#Y-98)I8Dtha-6r0Z0ai=QDuKY}p?PayaU1g8-^2>_O2A^iR`-uQ0_;+#K) z&(&P~(bM?+83fNFcn-ny2*we-fZ#<0e+gjJ*bLmXt_E&KOAfhDfVa^tMM#vRmybA$ z*Oha>1Z=@5O}`}`SwYqFgK8Z&1&?l!{MR6o31|I-)71xF$+VsxSevb<2VHN>Xy*Rm za(q}$my>m}b*7Zw)@eYNxm`km?8}|(ckZcNNBfUz7IGA*ER9gQa+G>ZWeKk(rjezzv8v%z$6Qq2^q?W zS`OXV+%ZJ6;5_EI2Bvi#Jg{|dPp6=J486ia4n9r{>lE}m4jnvn-*#DIjFS+|gqFC> zn2>>2@z_E^4{f5))lceOu=>FNO;xVf(R&=0@k4Ae{EtJ!BF#E6hEB*tJbCyU%_kl` zCYTU|24&F&d=vpuBG0k-C7}SZQ-$#eS@=YZJP{*pC_P0q3S~$h(jjIFg_EK(gj~u~ zLLdZ9D~}AIg^ACyrx@}H?i_v>aDRi~Z&6Anm&XN>6*!Hadk=4DG0a%dfD#Mp5mqp6 z1NGejOEWrQ3GSg0?sgLu%Rp0VoG|v3;Jr07QPXv^Zcq|XF$0}a!X_G*{Jr2##nSUyC|H!9KF38 zuH;n)s@4Vb)`#=f2Qt@#hqt&4UCSloyRTZa&O8<~ zqZv$A?%BLR@%oT$eZaPUF{;}`_U$sN(KM!P2awbvNdSbTStPk8WLp!kt>H6w2b6n^ z%T|%g%7$=dd$@7~IE4v=YNQ5$P&0_sHivAR1GdczQbV^dkJQ@2mFtn(9^fq{wL8mO zN66L@uyx4F)fuvPew7ojcLweI!}k4HlB>2H6!hAVZEe7|c0oadV77+rTSYrv#}<`a2)$kraPwaXZ}D`dZmFIzD=gyQI&PaHAfE~Hby6p=60sbr>5 z)+zO1DXIm;WvZ1-d|oB{NyXU~vb6E^`&&tUp`j%) zxlkV&V=_*M*7rPg=B@9$LbkgCHi#Z6pXz?==o?38cFsN+EbR=Jc21aNLfjLw?}6}= z$-z`i?p4(HM0zyVk;e+V2yxng=OM0az%t^o1{{|F?fT}CfU@h8(TOn`r$hOfhfX3t zw=$Ugia^D#FEsy24+q$TdAq}T`09$}6~?9v&Fl#lw_h&W9xB=%EZP|^+WA#=uxNiU z??5>3!1zvhSr=5zR0r}K#&=z@7EacGsQr+g_6M!4VQVYShikq9lUaTiEUph1*UvaY z#f=k&3Bwgz@#NkQvo4kdZS7%OJAMT~Xxxf$SyQ06X~Gc6t-O+*_uAuU{jWZ8_KC~+ zjiLO;V19Erzd4xQ63%YH8FzX89}X0rnq2v(onPz?+}j=8@L+hu zgMsD8KGe+Y4K803UcM#(yW?1(uorjVDc)Wi$lEl*POyQMo30d9POUuGFx3#vP0xcs zBLJ{AUS7udj%X2Mv0XOThRn6oj|I&QVRJ*k+`wlSj^F)##99z#EJm2)76Vt7D(5SA zOeCcWF#rRT6|eT4?TeSlX2cQ!xQPHtlrwLM#_x{(!7QKeqs34xIC{{t_~x-HSb^?q zm8oB?`8s{;%D#=7xegUv%x%;GRIE-1-;H#2x_96tadkTUu@G1&9>Kf`9clSHv!1FKEpkWk$GFc&3p1uU3EZ6iy<(#Y5QoyWRh^x^c% zmzol?;AdVuzGJv#`!QTo88FB)u-89&=f>o-bddtO(kxj5nBI=O^qRB|dgP;D&At9H zMS%r%W2DI9PB7j_TPdBp+W}{=eta)Zd#20p<|y)}<4L+8HqC~v4kYG|PADn&goU={ zJa9WPA7!zI;Kz+z^hbHfg58vkw3aTgPf9DU;7yhY2MfpwWc@A#NRn6zV7|Bx=Lo~P z%)mP?F01BnXu??lz&Yv@2R}r`cwm?T0~R_+A)`~kmu4G}mdNM!;wVu5v<~Xk^D3mL zk-+(U_A)-VjxX83=U4KD)sq%jZW3b#0l>Q%Vg@r93n0}Iq%8!gcG8*}2`E+ZCGCh* z&>0|m0%Ei*&t@9e-D+Yo?bF+5D+AWHptUV*Z3`IM$a0OG3CgFK$A+!qY54!nuZzuN z`w=05dCHJLur8x4$tHS4w0>PY01(3d(qhlS|AAqD5`HRw1w7Zr@Hq?)ldec`m~=I! zSI(sKVik*?MlRNAfBI;^Y4^$VGYTiAAeepR0FILuB16v5L_kI zhhNW{rqmEDX`IfKszP>>I+QOdSQ0cmK5J4QCI_=Jm&M7k2_c9Ou6cGW9V9FB5DrK) z)PSdyv%?a~ZiYFCpurzVS}e~Wd>U!-d1R@>+Pv83iW{4uD6upvEYSn96rH(fRnAIN zYA=Lr{sn|?$vm|2@HX(JK)@~r_qesOFdz&BTR0TVZCgB4$UO{sqPbJtAg5#CwUIOh zLK7w3fZ$C-3{NTZJE)nNa?SLvkL3q*FB5zmfG5q$A^}z4i*OqtSPG7DU&OrOb!myx z_otEfJ!%{ETqL~j_j9knBS8ai8d&k1yq13jH%M@eU>Ksm-sj<7!+U=P0KEnfQNua# z*BdZEikQw1!b?sMt%ELJUjukIpw9u+H?WC(wg4wh>tp+Gv; zH#FiAbR7Aqi!=v@8@YxB!$}AHwpveGIrZV3`dCO%PU+bLFxJ6{ zAOPdrcx(2VlMszl8?u%MtmS-3`Gk=M`{qOPr`ic74 zx^TKsHbEvR6ShzY|xSEcCz(k5x(wzLWJFeu}&knxo zKI`Ul^I@_0EECv3vrJ$E%`%Pa;nM~li_tPsZHck+N`6H+zmCt$gHhPb{(yZwtO#EK zA434Z%1{8oJ$V=ru{eMzrJO4j)$blZKlrx$ynA*uSlfBI=AKZ^J;9p$!Zr5=4jl>B z90^u*hby`#^k*~piW)q?FtTiU*ba-8*Fw&)eggvlf_)53e&Or=Kkt8&of??QJ~uoy z9L#G7=QRW}Q|ysK`{bkN3Z`I?WwZge046%4YD2F5N?lX9ZtbPY&zF6=?321b)_rwv zpzc7h?m)QiK%gpdpEN*J)0FjcK|`pZ;lopWMg5hEn)5|7yMh&M;fl5^wGFgv5ctwm z1m>cqBCrFKv%+FHD=da!7!iCAu^c5;;o?RZ;pPHIF~Ap;PUVNcH`&2tz>>HQ<$S^Sth-3aH#%Q^0C7UTC@hkZa$yhWUrqEtXxinm?;M*YDh<{l|?excJ9S)?K!= zuPhok|0+|3=QeVllLaZhsx|FuO8crwgXgPNc-~6R*O>NHYyOM6vtUn|=AX)RaM3G$ z4M2W+_{VPYI|w`=%Wn+81>M@<9SgIN;6B|(;2k@rh6Noo@UMj@5*-eHfHHo?s>N^X zOg|H!x#L%-Gpy!WbzzJg(2=J~prMt~kiMf+(9p`ito<6l_M%St#RGxG8vIkVY3*!LK8D z4Y-C}UM>qTxnj7%;mmI?8vu-E$@uOiys;dg*CJR3z=JPA;-8X@_{eZid{HEi_~cKY zz|Q-7AaR#O)xi62h#;Umz4+N7Hj51vRWO(%)L zx+XGg`EzJADtV)k|Bi2}&t4kwjvu=zR2yUA)if6V51&O|oR*^C;m6=$8Oi;im8iE= zSIO7Rzu_f*TfQ$EB))`mxj};a7;Y#vl6==-s}S||Wz@+34rr1_YK*%HW+D}2hK+;H-=n%*&&@qOkNO;g z;uDyu#t!%P^l%j*1F~2pUEbpiUj7C_Is!DpasPk-)+(arYdSbbYY9h2zBow3=CC5(XQ)BA9Pj4G09qhCZ^7f!(jTwQ3uyDATAi*Oeo89_GMpOSR@L<8 zC__$CANHp`f><-wC__$CA9kcYtkT(MU{wb^&K{-5uPpSK0zRSc(dde1VT}nqelbRm zDd3j0B3=7*LzIC-B3RR*%bU?e892`Z*WIU#TKAL2TFqQe5Y!95TBr0Ed*)ZVYStjaV=Q8g$U4sLMIz);Y`qj z6$gH>lEV*N4hJ5vQp#}-EJAn_4e#7X2y!5)0KfJNzhCR&@Jm)O;j~6jQshA?=a_VAX6SWG7XSUqa-S~8kw%dH01z(Fl?X=V-vR) z9>C6c1_8nVR;A*ZH34P~&t#k?e>{_Zn*8z1@<6JcXDR}zcAhB?DEF(3`84_C8CxK> s^GtRiwuA1g)KDM)UjQ?sf&wo~-lO_`87kD9`48XHXEzr>A+_;;0bm*mqW}N^ delta 5707 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 APITestListData: - """获取API测试列表""" + """获取API测试列表(对外接口)""" # 固定交易时间:2026年3月2日到2026年3月6日 test_start = datetime(2026, 3, 2) test_end = datetime(2026, 3, 6) categories = [ APITestCategory( - name="股票接口", + name="【对外】股票数据接口", items=[ APITestCase( id="stock_klines", @@ -73,7 +75,7 @@ class TestService: ), APITestCase( id="stock_calendar", - name="查询交易日历", + name="查询股票交易日历", method="GET", path="/v1/stock/trading-dates", description="查询股票交易日历", @@ -85,7 +87,7 @@ class TestService: ] ), APITestCategory( - name="期货接口", + name="【对外】期货数据接口", items=[ APITestCase( id="futures_klines", @@ -143,7 +145,7 @@ class TestService: ] ), APITestCategory( - name="管理接口", + name="【对外】管理接口", items=[ APITestCase( id="admin_health", @@ -181,75 +183,10 @@ class TestService: description="获取系统运行状态和资源使用情况", params={} ), - APITestCase( - id="admin_config_list", - name="查询配置列表", - method="GET", - path="/v1/admin/config", - description="获取所有配置项列表", - params={} - ), - APITestCase( - id="admin_config_update", - name="更新配置", - method="PUT", - path="/v1/admin/config", - description="更新系统配置", - body={ - "key": "server.mode", - "value": "debug", - "description": "服务器运行模式" - } - ), - APITestCase( - id="admin_reload_config", - name="热加载配置", - method="POST", - path="/v1/admin/system/reload", - description="重新加载配置文件", - body={} - ), ] ), APITestCategory( - name="适配器管理", - items=[ - APITestCase( - id="admin_adapters_list", - name="适配器列表", - method="GET", - path="/v1/admin/adapters", - description="获取所有数据源适配器列表", - params={} - ), - APITestCase( - id="admin_adapter_toggle", - name="切换适配器状态", - method="POST", - path="/v1/admin/adapters/toggle", - description="启用或禁用适配器", - body={ - "name": "amazingdata", - "enable": True - } - ), - APITestCase( - id="admin_adapter_config", - name="更新适配器配置", - method="PUT", - path="/v1/admin/adapters/config", - description="更新适配器配置参数", - body={ - "name": "amazingdata", - "config": { - "timeout": "60" - } - } - ), - ] - ), - APITestCategory( - name="数据同步接口", + name="【对外】数据同步接口", items=[ APITestCase( id="admin_data_sync_full", @@ -270,7 +207,7 @@ class TestService: name="同步基础K线数据", method="POST", path="/v1/admin/data/sync", - description="仅同步基础K线数据(OHLCV)", + description="仅同步OHLCV基础数据", body={ "symbols": ["000001.SZ"], "sync_type": "base", @@ -285,7 +222,7 @@ class TestService: name="同步行情指标数据", method="POST", path="/v1/admin/data/sync", - description="同步行情指标数据(均线/MACD/涨跌幅)", + description="同步均线/MACD/涨跌幅", body={ "symbols": ["000001.SZ"], "sync_type": "quote", @@ -299,7 +236,7 @@ class TestService: name="同步财务数据", method="POST", path="/v1/admin/data/sync", - description="同步财务数据(市值/股本/利润)", + description="同步市值/股本/利润", body={ "symbols": ["000001.SZ"], "sync_type": "finance", @@ -316,41 +253,241 @@ class TestService: description="触发增量同步(最近30天)", body=["000001.SZ", "600519.SH"] ), + ] + ), + ] + + return APITestListData(categories=categories, base_url="") + + def get_internal_test_list(self) -> APITestListData: + """获取内部接口测试列表(对内接口 - SDK封装层)""" + categories = [ + APITestCategory( + name="【对内】市场数据接口 (_market_data)", + items=[ APITestCase( - id="admin_data_sync_futures", - name="期货数据同步", - method="POST", - path="/v1/admin/data/sync", - description="同步期货数据", - body={ - "symbols": ["CU2504.SHFE"], - "sync_type": "full", - "start_date": "20240301", - "end_date": "20240310", - "asset_class": "futures" - } + id="internal_market_query_kline", + name="SDK: query_kline", + method="INTERNAL", + path="AmazingDataAdapter._internal.market.query_kline", + description="查询K线数据(内部SDK调用)", + params={"symbol": "000001.SZ", "period": "1d"} + ), + APITestCase( + id="internal_market_query_snapshot", + name="SDK: query_snapshot", + method="INTERNAL", + path="AmazingDataAdapter._internal.market.query_snapshot", + description="查询快照数据(内部SDK调用)", + params={"symbol": "000001.SZ"} ), ] ), APITestCategory( - name="测试管理", + name="【对内】基础数据接口 (_base_data)", items=[ APITestCase( - id="admin_test_history", - name="测试历史", - method="GET", - path="/v1/admin/tests/history", - description="获取测试执行历史记录", - params={"type": "api", "limit": "20"} + id="internal_base_get_code_list", + name="SDK: get_code_list", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_code_list", + description="获取股票代码列表(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_future_code_list", + name="SDK: get_future_code_list", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_future_code_list", + description="获取期货代码列表(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_code_info", + name="SDK: get_code_info", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_code_info", + description="获取代码信息(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_calendar", + name="SDK: get_calendar", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_calendar", + description="获取交易日历(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_adj_factor", + name="SDK: get_adj_factor", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_adj_factor", + description="获取复权因子(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_base_get_etf_pcf", + name="SDK: get_etf_pcf", + method="INTERNAL", + path="AmazingDataAdapter._internal.base.get_etf_pcf", + description="获取ETF申赎数据(内部SDK调用)", + params={} + ), + ] + ), + APITestCategory( + name="【对内】股本股东接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_equity_structure", + name="SDK: get_equity_structure", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_equity_structure", + description="获取股本结构(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_share_holder", + name="SDK: get_share_holder", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_share_holder", + description="获取股东数据(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_holder_num", + name="SDK: get_holder_num", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_holder_num", + description="获取股东户数(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + ] + ), + APITestCategory( + name="【对内】财务报表接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_income", + name="SDK: get_income", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_income", + description="获取利润表(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_balance_sheet", + name="SDK: get_balance_sheet", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_balance_sheet", + description="获取资产负债表(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_cash_flow", + name="SDK: get_cash_flow", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_cash_flow", + description="获取现金流量表(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + ] + ), + APITestCategory( + name="【对内】市场状态接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_history_stock_status", + name="SDK: get_history_stock_status", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_history_stock_status", + description="获取历史股票状态(涨停/跌停/ST/停牌)(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + APITestCase( + id="internal_info_get_margin_summary", + name="SDK: get_margin_summary", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_margin_summary", + description="获取融资融券汇总(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_margin_detail", + name="SDK: get_margin_detail", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_margin_detail", + description="获取融资融券明细(内部SDK调用)", + params={"symbol": "000001.SZ"} + ), + ] + ), + APITestCategory( + name="【对内】特色数据接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_long_hu_bang", + name="SDK: get_long_hu_bang", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_long_hu_bang", + description="获取龙虎榜数据(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_block_trading", + name="SDK: get_block_trading", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_block_trading", + description="获取大宗交易数据(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_index_constituent", + name="SDK: get_index_constituent", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_index_constituent", + description="获取指数成分股(内部SDK调用)", + params={"index": "000300.SH"} + ), + APITestCase( + id="internal_info_get_index_weight", + name="SDK: get_index_weight", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_index_weight", + description="获取指数权重(内部SDK调用)", + params={"index": "000300.SH"} + ), + ] + ), + APITestCategory( + name="【对内】基金可转债接口 (_info_data)", + items=[ + APITestCase( + id="internal_info_get_fund_share", + name="SDK: get_fund_share", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_fund_share", + description="获取基金份额(内部SDK调用)", + params={} + ), + APITestCase( + id="internal_info_get_kzz_issuance", + name="SDK: get_kzz_issuance", + method="INTERNAL", + path="AmazingDataAdapter._internal.info.get_kzz_issuance", + description="获取可转债发行数据(内部SDK调用)", + params={} ), ] ), ] return APITestListData(categories=categories, base_url="") + async def run_api_test(self, base_url: str, req: APITestRequest) -> APITestResult: - """执行API测试""" + """执行对外API测试""" # 获取测试用例 test_list = self.get_api_test_list() @@ -444,6 +581,231 @@ class TestService: self._add_api_history(result) return result + async def run_internal_test(self, adapter, req: APITestRequest) -> APITestResult: + """执行内部接口测试(SDK封装层)""" + from app.adapters.amazingdata_adapter import AmazingDataAdapter + + start_time = datetime.now() + + try: + # 确保已连接 + if not adapter._is_logged_in: + raise RuntimeError("Adapter not connected") + + # 根据测试ID调用对应的内部接口 + result_data = None + error_msg = None + + if req.id == "internal_market_query_kline": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.market.query_kline( + code_list=[symbol], + begin_date=20240301, + end_date=20240310, + period=10000 # SDK只支持10000(日线) + ) + + elif req.id == "internal_market_query_snapshot": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.market.query_snapshot( + code_list=[symbol], + begin_date=20240301, + end_date=20240301 + ) + + elif req.id == "internal_base_get_code_list": + from app.adapters.amazingdata_adapter import SecurityType + result_data = adapter._internal.base.get_code_list( + security_type=SecurityType.STOCK_A.value + ) + + elif req.id == "internal_base_get_future_code_list": + from app.adapters.amazingdata_adapter import SecurityType + result_data = adapter._internal.base.get_future_code_list( + security_type=SecurityType.FUTURE.value + ) + + elif req.id == "internal_base_get_code_info": + from app.adapters.amazingdata_adapter import SecurityType + result_data = adapter._internal.base.get_code_info( + security_type=SecurityType.STOCK_A.value + ) + + elif req.id == "internal_base_get_calendar": + result_data = adapter._internal.base.get_calendar(market="SH") + + elif req.id == "internal_base_get_adj_factor": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.base.get_adj_factor( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_base_get_etf_pcf": + result_data = adapter._internal.base.get_etf_pcf( + code_list=["510050.SH"] + ) + + elif req.id == "internal_info_get_equity_structure": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_equity_structure( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_share_holder": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_share_holder( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_holder_num": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_holder_num( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_income": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_income( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_balance_sheet": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_balance_sheet( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_cash_flow": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_cash_flow( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_history_stock_status": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_history_stock_status( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache, + begin_date=20240301, + end_date=20240310 + ) + + elif req.id == "internal_info_get_margin_summary": + result_data = adapter._internal.info.get_margin_summary( + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_margin_detail": + symbol = req.params.get("symbol", "000001.SZ") + result_data = adapter._internal.info.get_margin_detail( + code_list=[symbol], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_long_hu_bang": + result_data = adapter._internal.info.get_long_hu_bang( + code_list=["000001.SZ"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_block_trading": + result_data = adapter._internal.info.get_block_trading( + code_list=["000001.SZ"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_index_constituent": + index_code = req.params.get("index", "000300.SH") + result_data = adapter._internal.info.get_index_constituent( + code_list=[index_code], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_index_weight": + index_code = req.params.get("index", "000300.SH") + result_data = adapter._internal.info.get_index_weight( + code_list=[index_code], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_fund_share": + result_data = adapter._internal.info.get_fund_share( + code_list=["510050.SH"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + elif req.id == "internal_info_get_kzz_issuance": + result_data = adapter._internal.info.get_kzz_issuance( + code_list=["110043.SH"], + local_path=adapter.config.local_path, + is_local=adapter.config.use_local_cache + ) + + else: + raise ValueError(f"Unknown internal test case: {req.id}") + + latency = int((datetime.now() - start_time).total_seconds() * 1000) + + # 检查结果 + has_data = result_data is not None + if isinstance(result_data, (dict, list)): + has_data = len(result_data) > 0 + elif isinstance(result_data, pd.DataFrame): + has_data = not result_data.empty + + result = APITestResult( + id=int(datetime.now().timestamp()), + case_id=req.id, + name=req.id.replace("internal_", "").replace("_", ":"), + success=has_data, + status_code=200 if has_data else 204, + latency=latency, + request={"params": req.params}, + response={"data_count": len(result_data) if hasattr(result_data, '__len__') else 1} if has_data else None, + error=None if has_data else "No data returned", + timestamp=datetime.now() + ) + + self._add_internal_history(result) + return result + + except Exception as e: + latency = int((datetime.now() - start_time).total_seconds() * 1000) + result = APITestResult( + id=int(datetime.now().timestamp()), + case_id=req.id, + name=req.id.replace("internal_", "").replace("_", ":"), + success=False, + latency=latency, + request={"params": req.params}, + error=str(e), + timestamp=datetime.now() + ) + self._add_internal_history(result) + return result + def get_ws_test_list(self) -> WSTestListData: """获取WebSocket测试列表""" cases = [ @@ -468,66 +830,12 @@ class TestService: action="subscribe", symbols=["000001.SZ", "000002.SZ", "CU2504.SHFE"] ), - WSTestCase( - id="ws_subscribe_many", - name="压力测试-大量订阅", - description="订阅大量标的测试性能", - action="subscribe", - symbols=[ - "000001.SZ", "000002.SZ", "000063.SZ", "000333.SZ", - "000538.SZ", "000568.SZ", "000651.SZ", "000725.SZ", - "000768.SZ", "000858.SZ" - ] - ), - WSTestCase( - id="ws_unsubscribe", - name="取消订阅", - description="取消订阅标的", - action="unsubscribe", - symbols=["000001.SZ"] - ), - WSTestCase( - id="ws_unsubscribe_all", - name="取消全部订阅", - description="取消所有已订阅标的", - action="unsubscribe", - symbols=["000001.SZ", "000002.SZ", "CU2504.SHFE"] - ), - WSTestCase( - id="ws_heartbeat", - name="心跳检测", - description="测试WebSocket连接心跳", - action="subscribe", - symbols=["000001.SZ"] - ), - WSTestCase( - id="ws_invalid_symbol", - name="无效标的测试", - description="测试订阅无效标的的错误处理", - action="subscribe", - symbols=["INVALID.CODE"] - ), - WSTestCase( - id="ws_empty_symbols", - name="空订阅测试", - description="测试空标的列表的处理", - action="subscribe", - symbols=[] - ), - WSTestCase( - id="ws_resubscribe", - name="重新订阅", - description="取消后重新订阅同一标的", - action="subscribe", - symbols=["000001.SZ"] - ), ] return WSTestListData(cases=cases, ws_url="") async def run_ws_test(self, ws_url: str, req: WSTestRequest) -> WSTestResult: """执行WebSocket测试""" - # 获取测试用例 test_list = self.get_ws_test_list() test_case = None @@ -539,7 +847,6 @@ class TestService: if not test_case: raise ValueError(f"Test case not found: {req.id}") - # 使用自定义标的 symbols = req.symbols if req.symbols else test_case.symbols result = WSTestResult( @@ -549,7 +856,6 @@ class TestService: messages=[] ) - # 连接WebSocket start_time = datetime.now() try: @@ -560,14 +866,12 @@ class TestService: result.latency = int((datetime.now() - start_time).total_seconds() * 1000) result.success = True - # 发送订阅消息 msg = { "action": test_case.action, "symbols": symbols } await ws.send(json.dumps(msg)) - # 等待响应(最多3条消息) for _ in range(3): try: msg_data = await asyncio.wait_for(ws.recv(), timeout=5) @@ -610,6 +914,13 @@ class TestService: if len(self.api_history) > self.history_size: self.api_history = self.api_history[-self.history_size:] + def _add_internal_history(self, result: APITestResult): + """添加内部接口测试历史""" + with self.lock: + self.internal_history.append(result) + if len(self.internal_history) > self.history_size: + self.internal_history = self.internal_history[-self.history_size:] + def _add_ws_history(self, result: WSTestResult): """添加WebSocket测试历史""" with self.lock: diff --git a/test_adapters.py b/test_adapters.py deleted file mode 100644 index 75054ab..0000000 --- a/test_adapters.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -import urllib.request -import json - -req = urllib.request.Request( - 'http://localhost:8080/v1/admin/adapters', - headers={'X-Admin-Token': ''} -) - -try: - response = urllib.request.urlopen(req, timeout=10) - data = json.loads(response.read().decode()) - print('✓ Success!') - print(f"Code: {data['code']}") - print(f"Message: {data['message']}") - print(f"Adapters count: {len(data['data']['adapters'])}") - for adapter in data['data']['adapters']: - print(f" - {adapter['name']}: {adapter['status']} ({adapter['type']})") -except Exception as e: - print(f'✗ Error: {e}') diff --git a/test_adapters2.py b/test_adapters2.py deleted file mode 100644 index e46de90..0000000 --- a/test_adapters2.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -import urllib.request -import json - -req = urllib.request.Request( - 'http://localhost:8080/v1/admin/adapters', - headers={'X-Admin-Token': ''} -) - -try: - response = urllib.request.urlopen(req, timeout=10) - data = json.loads(response.read().decode()) - print('Success!') - print("Code:", data['code']) - print("Adapters count:", len(data['data']['adapters'])) - for adapter in data['data']['adapters']: - print(" -", adapter['name'] + ":", adapter['status']) -except Exception as e: - print('Error:', e) diff --git a/test_db.py b/test_db.py deleted file mode 100644 index 4e13b0c..0000000 --- a/test_db.py +++ /dev/null @@ -1,46 +0,0 @@ -"""测试数据库连接""" -import os -import sys - -# 添加项目根目录到路径 -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from app.repositories.database import init_db, engine -from sqlalchemy import text - -print("=" * 50) -print("数据库连接测试") -print("=" * 50) - -try: - # 测试连接 - with engine.connect() as conn: - result = conn.execute(text("SELECT version()")) - version = result.scalar() - print(f"✅ 数据库连接成功") - print(f" PostgreSQL 版本: {version}") - - # 初始化数据库(创建表) - print("\n正在初始化数据库表...") - init_db() - print("✅ 数据库表创建成功") - - # 显示所有表 - from sqlalchemy import inspect - inspector = inspect(engine) - tables = inspector.get_table_names() - print(f"\n已创建的表 ({len(tables)} 个):") - for table in sorted(tables): - print(f" - {table}") - - print("\n" + "=" * 50) - print("数据库初始化完成!") - print("=" * 50) - -except Exception as e: - print(f"❌ 错误: {e}") - print("\n请检查:") - print("1. Docker 是否已启动: docker ps") - print("2. 数据库端口是否正确: netstat -ano | findstr 5432") - print("3. 数据库密码是否正确") - sys.exit(1) diff --git a/test_klines_api.py b/test_klines_api.py deleted file mode 100644 index 8850997..0000000 --- a/test_klines_api.py +++ /dev/null @@ -1,112 +0,0 @@ -"""测试股票K线接口返回的字段""" -import requests -import json - -# API 配置 -BASE_URL = "http://localhost:8080/v1" -API_KEY = "" - -# 测试获取股票K线 -def test_stock_klines(): - """测试股票K线接口返回的字段""" - url = f"{BASE_URL}/stock/klines/000001.SZ" - headers = {"X-API-Key": API_KEY} - params = { - "start": "20260301", - "end": "20260310", - "freq": "1d" - } - - print(f"\n{'='*60}") - print(f"测试接口: GET {url}") - print(f"{'='*60}") - - try: - response = requests.get(url, headers=headers, params=params) - data = response.json() - - if data.get("code") == 0: - kline_data = data.get("data", {}) - items = kline_data.get("items", []) - - print(f"\n标的: {kline_data.get('symbol')}") - print(f"周期: {kline_data.get('freq')}") - print(f"数据条数: {len(items)}") - print(f"\n{'='*60}") - - if items: - # 显示第一条数据的完整字段 - first_item = items[0] - print("\n第一条数据详情:") - print(f"{'-'*60}") - - # 基础字段 - print(f"时间戳: {first_item.get('time')}") - print(f"开盘价: {first_item.get('open')}") - print(f"最高价: {first_item.get('high')}") - print(f"最低价: {first_item.get('low')}") - print(f"收盘价: {first_item.get('close')}") - print(f"成交量: {first_item.get('volume')}") - print(f"成交额: {first_item.get('amount')}") - - # 扩展字段 - print(f"\n扩展字段:") - print(f" 交易日: {first_item.get('trade_date')}") - print(f" 是否涨停: {first_item.get('is_limit_up')}") - print(f" 是否跌停: {first_item.get('is_limit_down')}") - print(f" 总市值: {first_item.get('total_market_cap')}") - print(f" 流通市值: {first_item.get('float_market_cap')}") - print(f" 机构持仓占比: {first_item.get('inst_holding_ratio')}") - print(f" 可交易日数: {first_item.get('trading_days')}") - print(f" 创建时间: {first_item.get('created_at')}") - - # 验证所有字段是否存在 - expected_fields = [ - 'symbol', 'time', 'open', 'high', 'low', 'close', - 'volume', 'amount', 'trade_date', 'is_limit_up', - 'is_limit_down', 'total_market_cap', 'float_market_cap', - 'inst_holding_ratio', 'trading_days', 'created_at' - ] - - print(f"\n{'='*60}") - print("字段完整性检查:") - print(f"{'-'*60}") - - missing_fields = [] - for field in expected_fields: - if field in first_item: - print(f" ✓ {field}") - else: - print(f" ✗ {field} (缺失)") - missing_fields.append(field) - - if missing_fields: - print(f"\n缺失字段: {', '.join(missing_fields)}") - else: - print(f"\n所有字段都存在!") - - return True - else: - print("没有获取到数据") - return False - else: - print(f"请求失败: {data.get('message')}") - return False - - except Exception as e: - print(f"请求异常: {e}") - return False - -if __name__ == "__main__": - print("\n" + "="*60) - print("股票K线接口字段测试") - print("="*60) - - success = test_stock_klines() - - print(f"\n{'='*60}") - if success: - print("测试完成!") - else: - print("测试失败!") - print("="*60 + "\n") diff --git a/test_klines_extended_fields.py b/test_klines_extended_fields.py deleted file mode 100644 index ecf6926..0000000 --- a/test_klines_extended_fields.py +++ /dev/null @@ -1,133 +0,0 @@ -"""测试K线数据扩展字段获取""" -import asyncio -import os -import sys - -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from app.adapters.amazingdata_adapter import AmazingDataAdapter -from datetime import datetime - - -async def test_klines_with_extended_fields(): - """测试获取带有扩展字段的K线数据""" - print("\n" + "="*60) - print("测试K线数据扩展字段") - print("="*60) - - adapter = AmazingDataAdapter() - - # 连接配置(请根据实际情况修改) - config = { - "username": os.getenv("AMAZINGDATA_USERNAME", ""), - "password": os.getenv("AMAZINGDATA_PASSWORD", ""), - "host": os.getenv("AMAZINGDATA_HOST", "140.206.44.234"), - "port": int(os.getenv("AMAZINGDATA_PORT", "8600")), - "local_path": "./amazing_data_cache/", - "use_local_cache": True - } - - try: - # 连接适配器 - print("\n[1/3] 正在连接 AmazingData...") - await adapter.connect(config) - print("✓ 连接成功") - - # 获取K线数据 - symbol = "000001.SZ" # 平安银行 - start_date = "20260301" - end_date = "20260310" - - print(f"\n[2/3] 正在获取 {symbol} 的K线数据 ({start_date} ~ {end_date})...") - klines = await adapter.fetch_klines(symbol, start_date, end_date, "1d") - print(f"✓ 获取到 {len(klines)} 条K线数据") - - # 显示第一条数据的完整信息 - if klines: - print(f"\n[3/3] 数据字段验证") - print("-"*60) - - k = klines[0] - print(f"\n标的代码: {k.symbol}") - print(f"交易日: {k.trade_date}") - print(f"时间戳: {datetime.fromtimestamp(k.time)}") - - print(f"\n基础行情:") - print(f" 开盘价: {k.open}") - print(f" 最高价: {k.high}") - print(f" 最低价: {k.low}") - print(f" 收盘价: {k.close}") - print(f" 成交量: {k.volume}") - print(f" 成交额: {k.amount}") - - print(f"\n扩展字段:") - print(f" 是否涨停: {k.is_limit_up} {'✓' if k.is_limit_up is not None else '✗'}") - print(f" 是否跌停: {k.is_limit_down} {'✓' if k.is_limit_down is not None else '✗'}") - print(f" 总市值: {k.total_market_cap:,.0f} 元" if k.total_market_cap else " 总市值: None ✗") - print(f" 流通市值: {k.float_market_cap:,.0f} 元" if k.float_market_cap else " 流通市值: None ✗") - print(f" 机构持仓占比: {k.inst_holding_ratio}%" if k.inst_holding_ratio else " 机构持仓占比: None") - print(f" 可交易日数: {k.trading_days} {'✓' if k.trading_days else '✗'}") - - # 验证字段完整性 - print(f"\n{'='*60}") - print("字段完整性检查:") - print("-"*60) - - checks = [ - ("symbol", k.symbol is not None), - ("time", k.time > 0), - ("open", k.open > 0), - ("high", k.high > 0), - ("low", k.low > 0), - ("close", k.close > 0), - ("volume", k.volume > 0), - ("amount", k.amount > 0), - ("trade_date", k.trade_date is not None), - ("is_limit_up", k.is_limit_up is not None), - ("is_limit_down", k.is_limit_down is not None), - ("total_market_cap", k.total_market_cap is not None and k.total_market_cap > 0), - ("float_market_cap", k.float_market_cap is not None and k.float_market_cap > 0), - ("trading_days", k.trading_days is not None and k.trading_days > 0), - ] - - passed = 0 - for field, check in checks: - status = "✓" if check else "✗" - print(f" {status} {field}") - if check: - passed += 1 - - print(f"\n通过: {passed}/{len(checks)}") - - # 显示涨跌停判断逻辑验证 - print(f"\n{'='*60}") - print("涨跌停判断示例:") - print("-"*60) - for k in klines[:3]: # 显示前3条 - limit_status = "" - if k.is_limit_up: - limit_status = "📈 涨停" - elif k.is_limit_down: - limit_status = "📉 跌停" - else: - limit_status = "—" - print(f" {k.trade_date}: 收盘{k.close} {limit_status}") - - # 断开连接 - await adapter.close() - print(f"\n{'='*60}") - print("测试完成!") - print("="*60 + "\n") - - return True - - except Exception as e: - print(f"\n✗ 测试失败: {e}") - import traceback - traceback.print_exc() - return False - - -if __name__ == "__main__": - success = asyncio.run(test_klines_with_extended_fields()) - sys.exit(0 if success else 1) diff --git a/test_sdk_output.txt b/test_sdk_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5fe2667ff2f7bbd323448e1a08146820dde4cc87 GIT binary patch literal 178944 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 aF0ze`0_Vsl#WnFYwdk`u%4uSreE$I;avJLZ literal 0 HcmV?d00001 diff --git a/test_source.py b/test_source.py deleted file mode 100644 index b504ff3..0000000 --- a/test_source.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 -import urllib.request -import json - -# Test source status -req = urllib.request.Request( - 'http://localhost:8080/v1/admin/source/status', - headers={'X-API-Key': ''} -) - -try: - response = urllib.request.urlopen(req, timeout=10) - data = json.loads(response.read().decode()) - print('Source Status:') - print(json.dumps(data, indent=2, ensure_ascii=False)) -except Exception as e: - print('Error:', e) diff --git a/venv/Lib/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc b/venv/Lib/site-packages/uvicorn/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06c0b19829d663cf3359774094265b809be5d08d GIT binary patch literal 323 zcmZ3^%ge<81UfI)WZD7g#~=<2FhLogZGepF3@HpLj5!QZ45^GMOxa+$B0(mGRE8{= zI)-IHb*o{z85n`;gTYvn`6Woy?-oaVd~RZ9UVOYJ^DXw$vdrZCqCAjbUSe))eEcmI zkmO2+&mhIWB3!Iu5_1YN664GBi?WLg5|dM73Mxx7^7DXZ6lJHD#HS>dB*qt~76Hvk zjVVjbD~s{TOo}PaEJ@V`s?JVKPc4ptS`q`ZSg)Y+7l%!5eoARhs$CJt?~FiPYy>1e yFf%eT-eBNs0K*5&+#OYym?bYTOWt4+XmD@wY4mIHYXHL!%nU42K#3v_pf&(#XjoYQ literal 0 HcmV?d00001