From bdab80b2bd746f7abfccf4db6e8ceafe50cd8774 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 2 Apr 2026 18:40:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E6=96=B0=E7=9A=84API=E4=BC=98=E5=8C=96=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/.gradle/8.9/checksums/checksums.lock | Bin 17 -> 17 bytes .../.gradle/8.9/checksums/md5-checksums.bin | Bin 18547 -> 21997 bytes .../.gradle/8.9/checksums/sha1-checksums.bin | Bin 18605 -> 31997 bytes .../executionHistory/executionHistory.lock | Bin 0 -> 17 bytes docs/README.md | 119 ++++++++++--- docs/payment_flow.md | 15 +- lib/src/api/proxy_client.dart | 16 +- lib/src/config/default_field_mapping.dart | 5 + lib/src/entities/image_entities.dart | 97 ++++++++++- lib/src/entities/payment_entities.dart | 89 +++++++++- lib/src/entities/user_entities.dart | 164 ++++++++++++++++++ lib/src/services/auth_service.dart | 11 +- lib/src/services/feedback_api.dart | 4 +- lib/src/services/image_api.dart | 31 +++- lib/src/services/payment_api.dart | 133 ++++++++++++-- lib/src/services/user_api.dart | 136 ++++++++++++--- 16 files changed, 730 insertions(+), 90 deletions(-) create mode 100644 android/.gradle/8.9/executionHistory/executionHistory.lock diff --git a/android/.gradle/8.9/checksums/checksums.lock b/android/.gradle/8.9/checksums/checksums.lock index e9a7e47bead15e03d030a37d9d627d6125ffc960..8a22d9ccaac0f9ef6aa11a312774dc6d8224dc29 100644 GIT binary patch literal 17 VcmZQxs<=cg`){9nLiJz zlY1iSD5uD+2+5_3Nt>>|4p3lr@|JM5Mwb!2KvG+zAO;LJ+ zY_$J2x&OY!?{ES*0h|C%04IPGzzN_4Z~{01oB&P$Cx8>c3E%{90yqJj08RiWfD`zC zNx%*aL<|l_QAdz?BXy8QTQm*4*%VZqBgXUV>VO|xI-~y|9Di9XSAI3A2l6ee5s$oa zV8G*8Qw8KUR}epYQ@bI})4v>Y^J>J;c`i#AwTxIy?q3V>3yP`_Sv_1&a{qORCmEG; zmhkpRLe4Ei{L+fD`o^}`E|7D+A%4Yl^JV?cw;agrG!Vb~^S32cdU<;ww|a;8HI=yI z^k>=UAvZJr&d2YmeKA)mf!xL)@!ZzYjDngBg&UpF{k1WaGJC z*niE2e9KRW-<{!^9ra2r0&?!Jh?jk7XJ(C7%!J%x81cs<%%sDTX2dZ9%`O7*8pri5 zmz6nLkZ%!3ygs38scGFtVaVAEh&Pm+@SXcM#~5;J6~tRabb_sR{t*N@s}b>+M}COi zI{qaEa?TFK+iNY-vshMskaM*W?>SZ9exc{A>ejGUZgW`M0 zZ4(h!U$1xJWNy_FazA#6>vx1&(~CzTrm@x|zNl>?=&XOf0sMfqAMxcaznytq)YAYt zXEEX{gKb7ne=3cJoP8Z}Mq{SJec>%m04IPGzzN_4 zZ~{01oB&P$Cx8>c3E%{90yqJj08RiWfD^z8-~@02I02jhP5>u>6Tk`J1aJa40h|C% z04IPGzzN_4Z~{01oWTD|fVi1g3;fL`{-o7vCk8*e>)mKJ8MPJ~6bEpJVy?+}$)gjt4@+}@~*D1W+7F=-y8f!G5VMyFl&SxZMB$XY%EgZY7J?zAUat62y zUf@DQ*18qYkWQi+r)O^n8=iJQV_mXNrJ7$FxbJz=@PdW{ai2S1jjo1r=k~lPm0Ovg zIVA)31Ee7ijfJ16M#$EOWv}A1e)F_k8g<5OaT_$&!+W>|R?MdwPH_&OGF{wcwMR_u z2=B5u0}POr!z@^X8BaBq{wN{mI?ugnq0*Apww~fc&~SVU4UuB1(GsE|Du3N46#w=n=aXYJcs7IRl@rk$&?Ikz4WIEWVyUY{ zwAh??JsI1t{%Nol8gXQ=7OOo;J$mJhI1ZK0X%gg0XBR! z7N3kYR7?yrP3|o0?|!zP2@Mwc#D`QD)u{Jh#&au*XEZQQ@64`z(FqOCLZSkVCjK03 z_-g3WS#pny_XmC$m(fql$~J-qo7gvK!o(crGvpd>_&hxBs~{`LQAlczeE|(ikO0wy zE`bf7vFF~vf<^<$o73bX7c+zZiGzld>7+mtDWV!axqU_5SL))wHdmYfwr%M{XmH7U zwT0EFMx}1^=1~J(3F}{cPh}3Zfw@Yg%OZ{gXv`_jsC`S?3R&$>GX&>`X(~Q9sf31o zEi~wj;FWJ25@Ef^4nK%3nb`H*JbYhJC^Q_%S;7pX8eA<^SCNhj^EZdoq!$GwUW5i4 zWJWX*aNhIR*m%!tuk<&afFPYN`871b~tZ5!$y9uqt`z4?rYiVApc zfEYQOIM$#EC4vp#I8LfNwO5^g;;%6>t}UqBdyK3>o*xWie(@O^)#ts^?pJ-B5nLa! zHKAq>G&tll2lN7}Q5e`dVmhz;r~_~QrYpnCq@m$NKK(+^r5X-lY{Rblm{YCHh;%k9 z94xQIIP5=zZ-Pe80UN$vC zn;#QWU3l5Z%KzUrW|UHm9;KI7$Bn+$8F@J_@J}xzS0AT8i3;F6Hv}8L8U|Ub#AO7E zc~ZhnDO_=Ta^#$ClLAe8FV$E$bTpNdVKURqV9CYnHk-g)CC1@?78){-s7A!g-+R^t zbaaojecZZt-#_xCL7q`kLsY}&fbTI`=cvTY)i+KRblu+#jqL-lhN>miP;K7W*J!L< zn<^M`zn4MN0S35oTJ=MN304aJk!u#MsY%xAJ#lfOMb)xG&;%N+c4&xtQ;iREj+B3q zd6S{~J}l@%cCsBbEW)9|oMOF7UtVJD)vxt&w7qjyjCMA7nrpIG-Oyk}P-{qe=k%^`)2t-WBm!(DJIA#rm_XesDFQ6o>Br~p># zDURTp*63wTT+P<@kW9wT@~0P|@n_JaK%;{s_O_z$tOx+beKSUmm_IF=R z*7ynyMiSMCnZKxk8@JM^<$drOnexFI|L&Dg8r4`m#uNC<@0RP}hf9AOcE;F{2KoC+ z7#valaZFsAA69c`+}p+IwPr}}sQ_{uaF)>MQydT3psT%FBS8tf+P_eLMqn?mxMQ1-d@pzwjF?w!6=*PesD`<#=GtFsH__`BDO*H_41s-5 z8d}hx^Qgv~!Qqz+^tq?~A1EY<#^r{-plPi8^q>?r5b*8 zkAjB%UE=%T$~nb2!s>4~%nqV8y$XHet-}le$C{|&6f`N&n2O+)uZBkcItNE(y*s6U zuS<6?;l{cQs-reQZAGFlWE(l~q7~8=LK{N(&9pN)l2llrP z=GZHTJ3O8qJX1mj7+@wk5o-u&VpAM*`9_DV#$w-eG4n+-I)984t0*vBRzgGeGipW1d zGJj%bfSCbi2ACOOW`LOiW(JrUU}k`s0cHl68DM6BnE_@7m>FPZfSCbi2ACOOW`LQ2 z|1TM^0S)1SjS-ihf&9R~jY3&I6a4TimVdbEKzL#-qEaXk8rbuL*zzMPeWQLIP!EX4 z^@>|OJA!-PK;1$T(|0>|CaQAiK8Jb;JErfsRao6K_0$ULQ7v=rlS8H|D&<7are+iFI_21pHWY1NoyNO|X zQi4-p@}TGh)UCE+`U%lf&f%16DwNYXPPwx36{MbNAzdy2U+A zKb0@noar6@0O}?dxc){;LhvZRDAbK_;QG&Vb!l?LQ&101#q<d^L$XZeTwVH zc~0(JSBlohs0Gtgy;ZWfjSr%_`7KOO6P-L<9b^l}6H*MyaNYg6mg?HYmz|eomQ1-!8WJ9n`&-;(BAk+#$|4T~K$6!}RmT zMVk~}96AVfGb*NEQmdl1yng2bb@xO}&wMvE=Vo%wR;Y&xVR}}PkNz?#89}ITVa4>z z1y2^poLZ&}^sTiisa}G*@p@d3 z7+YDltZ*~bt#@L2w&aO8wx*kBpl;}l>y^<%V=`U7P!FoY_22gweK(Z23-yghFg+*A zuh29t6U{>)K7Vt*&QE%Kk#iP|$EVBG%~Nx26zV1$q3*vKyH5VvL)$L07xY6t@))KU zbWOa;ykPnY>TV^Nep`0MlIm@RGXRH-U}`VPFF<%M(fQr68p5A|)uSbX{CAGXUI4vwSxZcM*>WOa9&x7a7BN1NdK zS-D{ui$}Ro_pQM6N@4GthkF*G{p2IaYa4cX*EJPZQ(?gJ=)+q3(~*#}`}U1E2Ji+CzQI zRxG|rq$bdci*+^BUBfY*cDi?!&dUv)P`AVPm*%?%f)@7QQir71;Cbk3%|ErjN-_fK zR`~kxYNuD?#^2x3apH#ed-n^YtUP^_U>NT{iTS>{H&}4y!)HsV8%JS!kBPQ`=`GtN zsD~@!df2v5G4uWrsC)Qf`r8GOX%hRqe?Z*{ABS(RE%Cb(@M8?>(fE9PmoV2pcenos zsQd26uJi8E{0CjqJ?OgaUWe(utYuMSf1Q?q@qV(H{(k$x?Z*Tp+@WskiR;y>CBN%z zl%ei?4A+|kf<Tz8?0aoEJEMB`*l-0ex6}Uslxj=3fqq zQ1^Dm^p7qI*6x&UbbU6%*Q)^|b!U5}So-OQLfQTXi~qDWVYwFn7a@3^C?8D!%pD{? z_5Lxsj#-Rg`qw*pB4PUj(edUyj_HH5*ng`zna+dP2|S4DL$XtIGQ70Wbvx_t53<4Zk$k748oNT;pze4L(|@Fh$qiH=Sqb%UeBu7l zqbF~>g@W#DMj2TAPnIp=iF4aGz<3LMo&33>gKdfP?YB_(nT5sw>Kyg159~nK2MfGi ztm&Wu76`dvWv@}(yhsNOgPxnZE$JyVY9ubP^ zlZu`(^7r|twoK+f4t2XLxZXIYWL<1xE7X1P>-?UX zyTMb$pAG8UK4bB}4Z0uQd@1}C>b}F6KDD<-q4;(SnrCZ0TrZB5PaAw*3*)1|Vmc+a z<++meT6EtrkH+*FjWaIyH=alP#WWeyS&a`bIm;sw4zFX*kLetm1wx1Tw;qFffDWc} z>ge9@Yx;z)CqDSP!CBN2xcAH}^nAFb1B>VVOLf+>`w>0xI*uB+uC6nqS}92p>RYmK zeb~6vCu8L>)SW~yedglM!;Rtdqo5xA6w~>f>M zo`3fB7pyY|ZbLob7N*aZ+ws{x`VhMB*et+x-=~Xxo-TO?<0Cg>y3q56^M7?erb68Y zKPLzuy-z8Ly;uiz>jErZbVTkGrP3TdC!65?E5`2@68@$QUEel0VDaLC?u(w)2BG87 zD<0D&!dj1%KbeEB!&Z3S=3dHQe5K$tH(H;cSp3|aebI`+>dT-Wo`&i3_V><>y(~k6 zde{+6mrPQ$x^>9tBGjGmeM<6k>xmumztQuaF+NY{uL-g}I?;`;8=K{@>&%Y|v{G{$ zc?qv$h{;p)kJni{h&sH7`X+pRSiqmXFvYI|ofqDZFkr#@@ApbO4i3V2=f$`l(XnK% zU=tc|iRVq~NJ;aoUtV`${Pu7xUg}8optuoD5b9=axSrGyGvOtUo|jzGaec&&M?FGm z35?$=i0O;9mz_OSP>1eQR%)2OL`Q0qs+LwHjCaH5PC0k@vBQ3#&5)Z71B5De&f0Yoi8@{`mgvV-juCk0)|ucwXy3je?1j@ zD!fDq{z5+&(^qg8NC#)TqV?H==SPXlKd5N?(GnQHqYjH#l8WP7a_$9c;A?^D%Jm8d zE)+GJz<5tyOjj}2xe+Wrgnq2xeFD=}J5x@+EAMND@vd($U2RGuH^6lD`T1pK)i(R#^*7>qUQ^lV{(N5>x^4vC!{RmC z-?!GSKZ#z)5U;1kFODy23u}JC>zKM?@oQszoNkvawuZVHULVcFTXb72pP>7i%L*)B zOL4g5bekqR{!RAdx`pXg!K&V~@H(5_aXnn}+qF##(EFeWeEeun1jopRt<`|>=6zWF zdcNQp>rSO?gnG~dOxMXQ70o$#9i12YZ!ukO#pTB99b#8uyi+lz>r?CJOz`b1gSr8} zj_Lo*XbNPR5exNbF)ZFt(7<@|O>Z02BVObBROd(jmm%mr>xqvyv)m8W%L2I9!+7T! zEZ*$X!kL`uuh4a1hcu>}JEVPnH*2RUjJNB;bPMyOjC^sSA*cu7`=F&Fw_^Mz z)I$ew-E};Ja{Xo{)VJ=$_2Xqv)s~7Lg?iW+raMHtYz(~XfcB$N0IuKnxqguM0XiR@ zH)6Wu#~D{H9?$!M`W9lk6VF4%y+RXcypb}dJ6o#ky)>hv0jlyfClUM8P6X=JgJ%_~ms_G_(^fbpjFn7(E5t=a8`Ch}0%$9;puqC6i{YAm51qK?G}xut4E^q)b` z1AfVv9_-irTl(P?I#2EK@e>l4F^hLzEjPSQ&`&HrOx8pCi{_zps0Vvudbr9(QDKSN zLZ}Ch;JVum0nt_8Goilm0j5WM?K|da*0K!hc6dD_>-M%zWJI9j(=il_k8EKdxU<-Jhf)Pn0}!kXsXg6O{KiLam01A^^5hp(aguT=*Yzw@kPH1GU) zZ+IP3SzK?*{#!Jx3mxZM^)Njq(P(LuY74p!SmNtH_InCNY&X0X^OczaW(JrUU}k`s z0cHl68DM6BnE_@7m>FPZfSCbi2ACOOW`LOiW(JrUU}k`s0cHl68Th};0P-+I0sKQA z!buB9XbTBXHJsM|Hky39HQ?#%(pgg36behaEca>)SchfEQ^=M7krEOyBB%TZ%p8WC z+9uX@PxgKAPu89)w~dBgYtbiy%E&h=8NA3SDlJGCdJ$1YLXJ%>9tYU2Uwcw&#&c6B zD^*DVc%5y|(?Y*J^Y`F%FA^#pc$q*iBI@Gyi>8?>%{-sa?R~g2DL=7gwcG{N3w`n@ zfIKN|(+)N2!@#MGyIkx+USt%N7LATxB1&SFOj7sQb z-w(aGDyDmpQB>NlE9BIsa~)h9i(7Tt?>KudeckftPEroYIMOcLFOcFuo;EU!4Kj*K z+k-v>CZdkdpu7{)t=%WE;o6!j(HxeqU+)1=)DbTSBzK_jA-&DuMMeQHDTE=WTH4EX zY2O;RzcqRA(4%Ncu&Dl8FxC;T6KD?EUjmfDi;MzZ=n6nY&FNjwdQV?&EUd61?A;!< z2!XP9;0ZS3<%m9M=9x9!OY4K`!;1w6x>>U_D%a)Iu((=rg_+YBkIzv&Q#w|zQARzuJ(IY|9U4FQEd(f^Yg{zDPf z(DvQg{Pad!J>KQIXQVrIV@7rPz#K-hVTNP@6i#HuFl2*_0$%7dcp}PqJaX)ApV_CJ zWtKNa3>N+sVy2i2y^J;KRutAt)4j+j;AKNjP1X&WeK8O-bzGtLVdBA!o{-r`PC_pS z9Y}GNPxm6DfL8!HCEymDlPsQIVG21F3l^J(A6YY zdOl#a-A~q=S<2AM_64NaTEII)yT~ZuHB3&ajeS);y6;%tywG{OP705f`rhyj*FER>viP9lL>UC(ghwb>UXPbBC>u!E>mTlj&3*g%> zNDi_6iyc`_8NA3S;DxRmMATf*oZL@(hm{?ag}Be_vPZ`RMP7hjR@ShFY^Bq^$SB~2 zes7G38e|`r6`zdD{QG`z?U(UekK%a5!MCN5b~&Kmnqn)M?nOpXX$Qv98WK=H{mc35 zxv$e}f~-UrjsX(Hwq5saw;%$SB}7NlqP6(K)>@LCSB{Iw{NL9GuJU2??s8 zUSK>Tg^l10Kt@q%2U*FfP}{um^5jE@kA$CT);WLnWZG|zJm_V-5mNjy;GJP?kWs)( zmYj0Bxa?-B_*LzstKV!ZN>itF)@}4g`^z3u+=>ti~&Doj%y^(uxGC)i^c+eJnJFR;QPg@~FxdEb|-$8Epl)>B@Z zi;H%rzw!ZN0cn?&6Ql&TfOm!*l2O15J$(^T3zgSrdf&fkZXVQNCFSqSHt%{LI5#0) z?x&!a;FIZIWE7Ql9E?Y#5K&RbCw`4Pe$2h{IO~h%vwU`Y&5JUq7rIyS{{SdMyT~Zu z1@7LELPU+0%HC@W-qX8oeRo||fvG6(z{3vc#f^SW=p*s-anD|cq-$3^$&1XL$YBE?pKg9?=E;@pnQ!L#7mYwy2k6Ll-#K$ zwD3l8GWf4GAe823XOBb5Xg{Pl2u`rK4u9z3 z+GMElTtNMFin*t6i^ruJ)C-Ieq;P;ehOve_e|DGWUY^-Jeowo1)rVyHZ!>0mg_QOI zIz*9}Go3mrH96;NywkJaq61aDnqSjPUxooFXB<%wImEH=yh}uQcU;mbsv%K-x#k+}F)`n!d1a=x zesX=a8B4J)Sd)-(r~_9377^q;#V~qFDByK2pPmguYFj;bow%gdyrQv>57G)${_=Od z`WAW_zk@m4-!R>aghISZ$f?b)%?p>$v(V_TOv$(4DwGv^Un~H;z&d{o>|;pbhy^bU z?INS7w2bcvLrkq~$m!oP9BZY2h*ZY zjUwx0uQK$MLNrszDB!gWdJ$0-XEx+H z4O}QazcjdL^G%k3%)2h&oQrtfGe~2?f@}su|=|y!&Zsf$wbwB@QX);vRqbOE%xD8ng?n^JcP; zVnd!bY9Y!Vl%Np)I26ISHZyg zV>If8{zVcya=K*jBB2nkM|3L+5!J?By{i3l;iruquSX?b?d+X8c^9lnNV}Z3p%ii- z#^6Ol0k38ja_Y%|;WWnz#~{@VRvo0s1cI0UNKt7c$miFf5K&+HtIp`?wb(Z_oBHQe zSe02k?~j6B?YfXs2X}FdUSt&TI!I2DWdl(A=@el$*61EHlJDAkW!+gf-3N2OuB>k{DX`>xuOFKc?c z$SC0DMXw>3^p{yW;6ygwz&+;pM^NVVz$JPS|<2Do~TIyjhESJ%rSybJM-ZDBzVyPHj3j z>+ZEmcX=^O6+2af=>AhnFM~UIdb_|rh7>h0a~QKhMggyjbcm25>n}ioT>_Lp>MuaS zPi_hOtMO6#@^;O5qgNWCX4#LqwJ0g?Dqy?5uYq2gm%uwiFU!5P;*HdcRDPe{n5-_A zTh&oh3-&E~Hjq1BP_)ay%YUS(%@kyP2Ze|tt0AC}l^SG&sD>Hyf49&6HS=*z(hjw} zHMdx6Bp(^U8h(YJ&T(Ft4OoU6`j@ur*!*(aHd4t85D|>_nXU=;dCiUFPRUuyN_y6`v+%ugbt0A?S%@c6~V}qcE zWZC$KBFY9?yZ)j6q+Ni5y90%&U4BkKXRq|w@+q^`EM$fDsr;bF^8{fw-XJ3hlxl)C zii`qYu?Ry<)y1wd%nrJ=M#r!@Mb>5Z(qo@R!G4U)$}Y5*9mW94&|hQ}@Pexig@|e$ z4XrYE@(v3~iJd1Om}kD3R|DLWBVMm0=oO@_;+XD5MgcFlMiElJogOm55x4bCo_MBc z+VB6ANt@aSyuf+>;77U_OE7ZoU})F)!i~=keJhT?diQ0KfWpH$l<$5MD23dufggL2 z&k-3Y60dY>Gv^#g{ec%k!OsB*y*A~^&-9%v>PpzvohFhhqFH-C8{7jS?aJ$>do3wI z?k^c?*vuvoBXXXrNYf*5v#^BtP^D3(IiNu12TsBozP9}D6sRFQnX?d4BsE0Zh0I0@ zg|LQ)^;}}FE?Xn35w+~v#lQ5b)!6PMnFlp|15f6ZFUYBxpH= z`TH-0i)|c1KOx!p-~y>ESWJj_B}&3w}rEI)Wz-MggK0R3TtQ*4&E8EK|+BV@`{mDUqANMr{BCk*ZcPY z(QENa?c8<+>h#R>@C(x^t{iYz!01Ir0k5;HYdy!C}zxb5MsoJdMT8n|j zCe8kj=3UUKky(@z3uXo~ztZ4IkK=U2bg#)5bEQSDF8LZo6_m`C=I6B4R|NYpLLE9l z_hOkpIh`W$LTYG5r~bqXp}=_x6hg1}RS^w&g^g5;Dyad<$-TW#yk7dj8YXDaDM|=3 zvKYoZ359rVBBvyD`+hoBxjP!(s~lBLJz+9YF@&zYpOA9~C~|AS3xgLK1-y`Za!`n< zS4UU+q}rz5Q1;P!YAb)YL*La7teVJ}AFiiEEbP+YoxzKY0$x$%l+d7Z*U?>i{G-DQ z{dskjR_y<_c?5cW@}*N0b!3HO@FJst*G_V3kUetmemmox0T(s;J?|YZ$Q<&zO!wju z5~Wj|bMQJepH>vtp}+(A}qGwK1Ae&slu_5g6<9n4o>%qJ8Q+t zB64+&ZKGcI#7slAN|x>ikUE@7_o7sT{fx1OBwk=d&E}(1EPvpIQ0vIO#M_Yazk9K|@cxUCvaeB?8&#ibv@fc`NNO8jg8qwM eVE*)n~z5T delta 44 vcmezSlX2}t#tkMCj7*d7s!L4f)sx`)4+RVi42~NWe~521xFJ5-(IW={$&D0A diff --git a/android/.gradle/8.9/executionHistory/executionHistory.lock b/android/.gradle/8.9/executionHistory/executionHistory.lock new file mode 100644 index 0000000000000000000000000000000000000000..9c651a73aad413b89c24ab60f2a1bacc6fe9b557 GIT binary patch literal 17 ScmZQJv=!m0@Zj0O00jUTLjo26 literal 0 HcmV?d00001 diff --git a/docs/README.md b/docs/README.md index 977212e..caa7361 100644 --- a/docs/README.md +++ b/docs/README.md @@ -77,7 +77,7 @@ void main() { ``` 调用层 (原始字段) ↓ -ProxyClient.request() +ProxyClient.request()(自动加 `pkg`;默认加 `User_token`) ↓ 字段映射 (原始 → V2) ↓ @@ -86,6 +86,10 @@ AES 加密 发送请求 ``` +**请求头**:`ProxyClient` 会将 [AppConfig.packageName] 写入映射后的包名字段(原始名 `pkg`);若已设置用户 token,默认还会写入 `User_token`。**`UserApi.fast_login` 等无需登录态接口**内部使用 `includeUserTokenInHeader: false`,避免把旧 token 打进 `filter_type`。其余请求也可在直接调用 `ProxyClient.request` 时传入该参数。 + +**请求体**:各 `*Api` 方法使用与《客户端指南》解密表一致的**原始字段名**(如 `referer`、`deviceId`、`fileUrls` / `contentType` / `content`)。 + ### 3.2 响应流程 ``` @@ -131,9 +135,10 @@ AES 解密 final res = await UserApi.fastLogin( deviceId: '设备ID', sign: 'MD5(deviceId)大写', + app: 'HAndroid', // 必填:HIOS / HAndroid referer: '归因来源', // 可选 ch: '渠道号', // 可选 - type: '类型', // 可选 + type: 'fb', // 可选;未传时默认 fb ); if (res.isSuccess) { @@ -157,9 +162,17 @@ if (res.isSuccess) { #### 5.1.2 getCommonInfo - 获取用户通用信息 ```dart +final config = ApiClient.instance.config; +final backendApp = defaultTargetPlatform == TargetPlatform.iOS + ? config.backendAppTypeIOS + : config.backendAppTypeAndroid; + final res = await UserApi.getCommonInfo( - app: '应用ID', - userId: '用户ID', // 可选 + app: backendApp, // 必填:HIOS / HAndroid 等与后端约定 + pkg: config.packageName, // 必填:包名 + userId: '用户ID', // 可选 + deviceId: '设备ID', // 可选 + // client / ch / inviteBy / clientId 可选 ); if (res.isSuccess) { @@ -173,7 +186,7 @@ if (res.isSuccess) { ```dart final res = await UserApi.getAccount( - app: '应用ID', + app: 'HAndroid', userId: '用户ID', // 可选 ); @@ -191,6 +204,46 @@ if (res.isSuccess) { - `isVip`: 是否 VIP - `freeTimes`: 免费次数 +#### 5.1.4 getAppLanguage - App 语言配置 + +`GET /v1/config/app-language`(需登录) + +```dart +final res = await UserApi.getAppLanguage( + pkg: ApiClient.instance.config.packageName, + lang: 'en', // 可选 +); +``` + +**返回实体**: `AppLanguageConfigResponse`(`config` 为语言配置 JSON 字符串) + +#### 5.1.5 getCreditsPage - 积分分页流水 + +与 `ImageApi.getCreditsPageInfo` 相同,路径 `GET /v1/user/credits-page`。 + +```dart +final res = await UserApi.getCreditsPage(page: '1', size: '10', type: '1'); +``` + +#### 5.1.6 getUserPayments - 用户支付记录 + +```dart +final res = await UserApi.getUserPayments( + app: backendApp, + userId: '用户ID', +); +``` + +**返回实体**: `UserPaymentsListResponse`(`items`: `UserPaymentRecord` 列表) + +#### 5.1.7 getUnreadMessageCount - 未读消息数 + +```dart +final res = await UserApi.getUnreadMessageCount(types: '类型列表'); // types 可选 +``` + +**返回实体**: `UnreadMessageCountResponse` + --- ### 5.2 PaymentApi - 支付相关 @@ -199,7 +252,8 @@ if (res.isSuccess) { ```dart final res = await PaymentApi.getGooglePayActivities( - app: '应用ID', // 可选 + app: 'HAndroid', // 可选;默认 `AppConfig.backendAppTypeAndroid` + client: '客户端', // 可选 country: '国家', // 可选 ); @@ -226,7 +280,7 @@ if (res.isSuccess) { ```dart final res = await PaymentApi.getPaymentMethods( - activityId: '活动ID', + activityId: 12345, // int,与接口一致 country: '国家', // 可选 ); @@ -252,7 +306,7 @@ if (res.isSuccess) { ```dart final res = await PaymentApi.createPayment( - app: '应用ID', + app: 'HAndroid', // HIOS / HAndroid userId: '用户ID', activityId: '活动ID', paymentMethod: '支付方式', @@ -271,7 +325,24 @@ if (res.isSuccess) { - `payUrl`: 支付链接 - `status`: 状态 -#### 5.2.4 getOrderDetail - 获取订单详情 +#### 5.2.4 getPaymentDetailList - 支付订单列表 + +`GET /v1/payment/getPaymentDetailList` + +```dart +final res = await PaymentApi.getPaymentDetailList( + app: 'HAndroid', + userId: '用户ID', + paymentMethod: 'GOOGLEPAY', // 可选 + filterStatus: 'SUCCESS', // 可选 +); +``` + +**返回实体**: `PaymentOrderListResponse`(`orders`: `PaymentOrderSummary` 列表) + +#### 5.2.5 getOrderDetail - 获取订单详情 + +Query 使用原始字段 `id`(值为订单/支付 ID)。 ```dart final res = await PaymentApi.getOrderDetail( @@ -290,7 +361,7 @@ if (res.isSuccess) { - `status`: 状态 - `amount`: 金额 -#### 5.2.5 googlepay - Google Pay 回调 +#### 5.2.6 googlepay - Google Pay 回调 ```dart final res = await PaymentApi.googlepay( @@ -410,26 +481,29 @@ if (res.isSuccess) { #### 5.3.6 getCreditsPageInfo - 获取积分页面信息 +对应 `GET /v1/user/credits-page`(与 `UserApi.getCreditsPage` 相同)。 + ```dart final res = await ImageApi.getCreditsPageInfo( - app: '应用ID', - userId: '用户ID', // 可选 - ch: '渠道', // 可选 + page: '1', + size: '10', + type: '1', ); +// 或:UserApi.getCreditsPage(page: '1', size: '10', type: '1'); + if (res.isSuccess) { final info = res.data!; - final credits = info.credits; - final freeTimes = info.freeTimes; - final isVip = info.isVip; + final total = info.total; + final records = info.records; + final credits = info.credits; // 若后端仍返回单屏字段则可用 } ``` **返回实体**: `CreditsPageInfoResponse` -- `credits`: 积分 -- `freeTimes`: 免费次数 -- `isVip`: 是否 VIP -- `vipExpireTime`: VIP 过期时间 +- `total` / `current` / `pages` / `size` / `records`: 分页与流水(新接口) +- `credits` / `freeTimes` / `isVip` / `vipExpireTime`: 兼容旧字段 +- `records` 项见 `CreditRecordItem` --- @@ -587,6 +661,7 @@ class AuthService { final res = await UserApi.fastLogin( deviceId: deviceId, sign: sign, + app: 'HAndroid', // 或 AppConfig.backendAppTypeAndroid / IOS referer: 'organic', ); @@ -613,7 +688,7 @@ class PaymentService { } // 2. 获取支付方式 - static Future> getPaymentMethods(String activityId) async { + static Future> getPaymentMethods(int activityId) async { final res = await PaymentApi.getPaymentMethods(activityId: activityId); return res.data?.paymentMethods ?? []; } @@ -625,7 +700,7 @@ class PaymentService { required String paymentMethod, }) async { final res = await PaymentApi.createPayment( - app: ApiClient.instance.config.appId, + app: ApiClient.instance.config.backendAppTypeAndroid, userId: userId, activityId: activityId, paymentMethod: paymentMethod, diff --git a/docs/payment_flow.md b/docs/payment_flow.md index 0de55d2..29025ad 100644 --- a/docs/payment_flow.md +++ b/docs/payment_flow.md @@ -47,15 +47,13 @@ ### 4.1 接口 ```dart -// Android +// Android(app 默认 backendAppTypeAndroid,可传 client) final res = await PaymentApi.getGooglePayActivities( - app: '应用ID', // 可选 country: '国家', // 可选 ); -// iOS +// iOS(app 默认 backendAppTypeIOS) final res = await PaymentApi.getApplePayActivities( - app: '应用ID', // 可选 country: '国家', // 可选 ); ``` @@ -85,7 +83,7 @@ class PaymentProductItem { ```dart final res = await PaymentApi.getPaymentMethods( - activityId: '活动ID', + activityId: 12345, // int country: '国家', // 可选 ); @@ -104,7 +102,7 @@ if (res.isSuccess) { ```dart final res = await PaymentApi.createPayment( - app: '应用ID', + app: 'HAndroid', // HIOS / HAndroid userId: '用户ID', activityId: '活动ID', paymentMethod: '支付方式', @@ -132,7 +130,7 @@ if (res.isSuccess) { ```dart // 1. 创建订单(直接走谷歌支付) final createRes = await PaymentApi.createPayment( - app: '应用ID', + app: 'HAndroid', userId: '用户ID', activityId: '活动ID', paymentMethod: 'GooglePay', @@ -232,6 +230,7 @@ class GooglePayCallbackResponse { | `getApplePayActivities` | GET | `PaymentProductsResponse` | | `getPaymentMethods` | POST | `PaymentMethodsResponse` | | `createPayment` | POST | `CreatePaymentResponse` | +| `getPaymentDetailList` | GET | `PaymentOrderListResponse` | | `getOrderDetail` | GET | `OrderDetailResponse` | | `googlepay` | POST | `GooglePayCallbackResponse` | @@ -250,7 +249,7 @@ class GooglePayCallbackResponse { | 活动 ID | activityId | - | | 支付方式 | paymentMethod | - | | 支付子类型 | paymentType | - | -| 订单 ID | orderId | orderId | +| 订单 / 支付 ID(详情 query) | `id`(Dart 仍用参数名 orderId) | orderId | | 购买签名 | signature | - | | 购买数据 | purchaseData | - | | 商品 ID | - | productId | diff --git a/lib/src/api/proxy_client.dart b/lib/src/api/proxy_client.dart index cabd0d3..23ff287 100644 --- a/lib/src/api/proxy_client.dart +++ b/lib/src/api/proxy_client.dart @@ -102,12 +102,20 @@ class ProxyClient { /// [headers]、[queryParams]、[body] 使用**原始字段名**, /// 框架会按 [AppConfig.fieldMapping] 转为 V2 字段名后发送。 /// 响应 data 会自动从 V2 转回原始字段名。 + /// + /// **请求头(自动注入)** + /// - [AppConfig.packageName] → 原始字段名 `pkg`(经映射后的请求头键,如 `stakeholder`) + /// - 若已 [userToken] 且 [includeUserTokenInHeader] 为 true,则注入 `User_token` + /// + /// 与文档一致:**设备快速登录等无需登录态接口**应传 `includeUserTokenInHeader: false`, + /// 避免历史 token 进入 `filter_type`。 Future request({ required String path, required String method, Map? headers, Map? queryParams, Map? body, + bool includeUserTokenInHeader = true, }) async { final pk = config.proxyKeys; final mapping = config.fieldMapping; @@ -116,7 +124,9 @@ class ProxyClient { if (config.packageName.isNotEmpty) { headersMap[mapping.headerPackageNameField] = config.packageName; } - if (userToken != null && userToken!.isNotEmpty) { + if (includeUserTokenInHeader && + userToken != null && + userToken!.isNotEmpty) { headersMap[mapping.headerUserTokenField] = userToken!; } headersMap = mapping.mapRequest(headersMap); @@ -168,6 +178,8 @@ class ProxyClient { /// /// [headers]、[queryParams]、[body] 使用**原始字段名**。 /// [entityFactory] 用于将映射后的 data 转换为实体对象。 + /// + /// 参见 [request] 的 [includeUserTokenInHeader] 说明。 Future> requestEntity({ required String path, required String method, @@ -175,6 +187,7 @@ class ProxyClient { Map? headers, Map? queryParams, Map? body, + bool includeUserTokenInHeader = true, }) async { final response = await request( path: path, @@ -182,6 +195,7 @@ class ProxyClient { headers: headers, queryParams: queryParams, body: body, + includeUserTokenInHeader: includeUserTokenInHeader, ); if (response.isSuccess && response.data is Map) { diff --git a/lib/src/config/default_field_mapping.dart b/lib/src/config/default_field_mapping.dart index 028f367..3c99a69 100644 --- a/lib/src/config/default_field_mapping.dart +++ b/lib/src/config/default_field_mapping.dart @@ -28,6 +28,9 @@ const FieldMapping petsHeroAIFieldMapping = FieldMapping({ // === 支付 === 'activityId': 'warrior', 'country': 'vambrace', + 'client': 'filter', + 'id': 'timing', + 'filterStatus': 'quest', 'orderId': 'federation', 'payUrl': 'convert', 'productId': 'helm', @@ -51,6 +54,8 @@ const FieldMapping petsHeroAIFieldMapping = FieldMapping({ 'cursor': 'platoon', 'declaration': 'declaration', 'quest': 'quest', + 'imgCount': 'indicator', + 'aspectRatio': 'caption', 'ext': 'nexus', 'imgUrl': 'congregation', 'poseId': 'profit', diff --git a/lib/src/entities/image_entities.dart b/lib/src/entities/image_entities.dart index a28671d..8ff5f26 100644 --- a/lib/src/entities/image_entities.dart +++ b/lib/src/entities/image_entities.dart @@ -368,13 +368,67 @@ class MyTasksResponse extends Entity { }; } -/// 积分页面信息响应 +/// 积分流水单条(`/v1/user/credits-page` 的 records) +class CreditRecordItem extends Entity { + CreditRecordItem({ + this.subBusinessType, + this.credits, + this.createTime, + this.businessId, + this.type, + this.businessType, + }); + + final String? subBusinessType; + final int? credits; + final int? createTime; + final String? businessId; + final int? type; + final String? businessType; + + static int? _toInt(dynamic v) { + if (v == null) return null; + if (v is int) return v; + if (v is num) return v.toInt(); + if (v is String && v.isNotEmpty) return int.tryParse(v); + return null; + } + + @override + factory CreditRecordItem.fromJson(Map json) { + return CreditRecordItem( + subBusinessType: json['subBusinessType'] as String?, + credits: _toInt(json['credits']), + createTime: _toInt(json['createTime']), + businessId: json['businessId'] as String?, + type: _toInt(json['type']), + businessType: json['businessType'] as String?, + ); + } + + @override + Map toJson() => { + 'subBusinessType': subBusinessType, + 'credits': credits, + 'createTime': createTime, + 'businessId': businessId, + 'type': type, + 'businessType': businessType, + }; +} + +/// 积分页面信息响应(兼容旧版单块字段 + `/v1/user/credits-page` 分页结构) class CreditsPageInfoResponse extends Entity { CreditsPageInfoResponse({ this.credits, this.freeTimes, this.vipExpireTime, this.isVip, + this.total, + this.current, + this.pages, + this.size, + this.records, }); final int? credits; @@ -382,13 +436,43 @@ class CreditsPageInfoResponse extends Entity { final String? vipExpireTime; final bool? isVip; + /// 分页:总条数 + final int? total; + + /// 分页:当前页 + final int? current; + + /// 分页:总页数 + final int? pages; + + /// 分页:每页大小 + final int? size; + + final List? records; + + static int? _toInt(dynamic v) { + if (v == null) return null; + if (v is int) return v; + if (v is num) return v.toInt(); + if (v is String && v.isNotEmpty) return int.tryParse(v); + return null; + } + @override factory CreditsPageInfoResponse.fromJson(Map json) { + final recList = json['records'] as List?; return CreditsPageInfoResponse( - credits: json['credits'] as int?, - freeTimes: json['freeTimes'] as int?, + credits: _toInt(json['credits']), + freeTimes: _toInt(json['freeTimes']), vipExpireTime: json['vipExpireTime'] as String?, - isVip: json['isVip'] as bool?, + isVip: json['isVip'] is bool ? json['isVip'] as bool? : null, + total: _toInt(json['total']), + current: _toInt(json['current']), + pages: _toInt(json['pages']), + size: _toInt(json['size']), + records: recList + ?.map((e) => CreditRecordItem.fromJson(e as Map)) + .toList(), ); } @@ -398,5 +482,10 @@ class CreditsPageInfoResponse extends Entity { 'freeTimes': freeTimes, 'vipExpireTime': vipExpireTime, 'isVip': isVip, + 'total': total, + 'current': current, + 'pages': pages, + 'size': size, + 'records': records?.map((e) => e.toJson()).toList(), }; } diff --git a/lib/src/entities/payment_entities.dart b/lib/src/entities/payment_entities.dart index cf65e2a..e05b991 100644 --- a/lib/src/entities/payment_entities.dart +++ b/lib/src/entities/payment_entities.dart @@ -169,10 +169,11 @@ class OrderDetailResponse extends Entity { @override factory OrderDetailResponse.fromJson(Map json) { + final idRaw = json['orderId'] ?? json['id']; return OrderDetailResponse( - orderId: json['orderId'] as String?, + orderId: idRaw == null ? null : idRaw.toString(), status: json['status'] as String?, - amount: json['amount'] as String?, + amount: json['amount'] == null ? null : json['amount'].toString(), ); } @@ -198,8 +199,9 @@ class GooglePayCallbackResponse extends Entity { @override factory GooglePayCallbackResponse.fromJson(Map json) { + final idRaw = json['orderId'] ?? json['id']; return GooglePayCallbackResponse( - orderId: json['orderId'] as String?, + orderId: idRaw == null ? null : idRaw.toString(), status: json['status'] as String?, creditsAdded: json['creditsAdded'] as bool?, ); @@ -212,3 +214,84 @@ class GooglePayCallbackResponse extends Entity { 'creditsAdded': creditsAdded, }; } + +/// `getPaymentDetailList` 返回的订单摘要项 +class PaymentOrderSummary extends Entity { + PaymentOrderSummary({ + this.id, + this.status, + this.currency, + this.count, + this.activityId, + this.addCredits, + this.redirectUrl, + }); + + final String? id; + final String? status; + final String? currency; + final String? count; + final String? activityId; + final bool? addCredits; + final String? redirectUrl; + + static String? _str(dynamic v) { + if (v == null) return null; + if (v is String) return v; + return v.toString(); + } + + @override + factory PaymentOrderSummary.fromJson(Map json) { + return PaymentOrderSummary( + id: _str(json['id']), + status: json['status'] as String?, + currency: json['currency'] as String?, + count: _str(json['count']), + activityId: _str(json['activityId']), + addCredits: json['addCredits'] is bool ? json['addCredits'] as bool? : null, + redirectUrl: json['redirectUrl'] as String?, + ); + } + + @override + Map toJson() => { + 'id': id, + 'status': status, + 'currency': currency, + 'count': count, + 'activityId': activityId, + 'addCredits': addCredits, + 'redirectUrl': redirectUrl, + }; +} + +/// `/v1/payment/getPaymentDetailList` data 为数组时的包装 +class PaymentOrderListResponse extends Entity { + PaymentOrderListResponse({required this.orders}); + + final List orders; + + static PaymentOrderListResponse fromDataList(List list) { + return PaymentOrderListResponse( + orders: [ + for (final e in list) + PaymentOrderSummary.fromJson(e as Map), + ], + ); + } + + @override + factory PaymentOrderListResponse.fromJson(Map json) { + final raw = json['orders'] as List?; + if (raw == null) { + return PaymentOrderListResponse(orders: const []); + } + return PaymentOrderListResponse.fromDataList(raw); + } + + @override + Map toJson() => { + 'orders': orders.map((e) => e.toJson()).toList(), + }; +} diff --git a/lib/src/entities/user_entities.dart b/lib/src/entities/user_entities.dart index 624f7bf..782c3f2 100644 --- a/lib/src/entities/user_entities.dart +++ b/lib/src/entities/user_entities.dart @@ -290,3 +290,167 @@ class AccountResponse extends Entity { 'freeTimes': freeTimes, }; } + +/// `/v1/config/app-language` 响应 data +class AppLanguageConfigResponse extends Entity { + AppLanguageConfigResponse({this.config}); + + /// 语言配置 JSON 字符串 + final String? config; + + @override + factory AppLanguageConfigResponse.fromJson(Map json) { + return AppLanguageConfigResponse( + config: json['config'] as String?, + ); + } + + @override + Map toJson() => {'config': config}; +} + +/// `/v1/user/payments` 单条支付记录 +class UserPaymentRecord extends Entity { + UserPaymentRecord({ + this.amount, + this.payTime, + this.payMethod, + this.currency, + this.payId, + }); + + final int? amount; + final int? payTime; + final String? payMethod; + final String? currency; + final String? payId; + + static int? _toInt(dynamic v) { + if (v == null) return null; + if (v is int) return v; + if (v is num) return v.toInt(); + if (v is String && v.isNotEmpty) return int.tryParse(v); + return null; + } + + @override + factory UserPaymentRecord.fromJson(Map json) { + return UserPaymentRecord( + amount: _toInt(json['amount']), + payTime: _toInt(json['payTime']), + payMethod: json['payMethod'] as String?, + currency: json['currency'] as String?, + payId: json['payId'] as String?, + ); + } + + @override + Map toJson() => { + 'amount': amount, + 'payTime': payTime, + 'payMethod': payMethod, + 'currency': currency, + 'payId': payId, + }; +} + +/// `/v1/user/payments` 列表包装 +class UserPaymentsListResponse extends Entity { + UserPaymentsListResponse({required this.items}); + + final List items; + + static UserPaymentsListResponse fromDataList(List list) { + return UserPaymentsListResponse( + items: [ + for (final e in list) + UserPaymentRecord.fromJson(e as Map), + ], + ); + } + + @override + factory UserPaymentsListResponse.fromJson(Map json) { + final raw = json['items'] as List?; + if (raw == null) { + return UserPaymentsListResponse(items: const []); + } + return UserPaymentsListResponse.fromDataList(raw); + } + + @override + Map toJson() => { + 'items': items.map((e) => e.toJson()).toList(), + }; +} + +/// 未读数按类型 +class UnreadCountItem extends Entity { + UnreadCountItem({this.count, this.type}); + + final int? count; + final String? type; + + static int? _toInt(dynamic v) { + if (v == null) return null; + if (v is int) return v; + if (v is num) return v.toInt(); + if (v is String && v.isNotEmpty) return int.tryParse(v); + return null; + } + + @override + factory UnreadCountItem.fromJson(Map json) { + return UnreadCountItem( + count: _toInt(json['count']), + type: json['type'] as String?, + ); + } + + @override + Map toJson() => { + 'count': count, + 'type': type, + }; +} + +/// `/v1/user/unread-message-count` 响应 data +class UnreadMessageCountResponse extends Entity { + UnreadMessageCountResponse({ + this.unreadCountList, + this.totalUnreadCount, + this.lastUpdateTime, + }); + + final List? unreadCountList; + final int? totalUnreadCount; + final int? lastUpdateTime; + + static int? _toInt(dynamic v) { + if (v == null) return null; + if (v is int) return v; + if (v is num) return v.toInt(); + if (v is String && v.isNotEmpty) return int.tryParse(v); + return null; + } + + @override + factory UnreadMessageCountResponse.fromJson(Map json) { + final raw = json['unreadCountList'] as List?; + return UnreadMessageCountResponse( + unreadCountList: raw + ?.map((e) => UnreadCountItem.fromJson(e as Map)) + .toList(), + totalUnreadCount: _toInt(json['totalUnreadCount']), + lastUpdateTime: _toInt(json['lastUpdateTime']), + ); + } + + @override + Map toJson() => { + 'unreadCountList': + unreadCountList?.map((e) => e.toJson()).toList(), + 'totalUnreadCount': totalUnreadCount, + 'lastUpdateTime': lastUpdateTime, + }; +} diff --git a/lib/src/services/auth_service.dart b/lib/src/services/auth_service.dart index 4ccc019..105cb90 100644 --- a/lib/src/services/auth_service.dart +++ b/lib/src/services/auth_service.dart @@ -201,6 +201,9 @@ abstract class FrameworkAuthService { if (uid.isEmpty) return; final config = ApiClient.instance.config; + final backendApp = defaultTargetPlatform == TargetPlatform.iOS + ? config.backendAppTypeIOS + : config.backendAppTypeAndroid; // 上报 Adjust 归因 final adjustReferer = await AttributionService.getAdjustReferrer(); @@ -210,7 +213,7 @@ abstract class FrameworkAuthService { : 'android_adjust'; try { final rAdjust = await UserApi.referrer( - app: config.appId, + app: backendApp, userId: uid, referer: adjustReferer, deviceId: deviceId, @@ -232,7 +235,7 @@ abstract class FrameworkAuthService { if (playReferrer != null && playReferrer.isNotEmpty) { try { final rGg = await UserApi.referrer( - app: config.appId, + app: backendApp, userId: uid, referer: playReferrer, deviceId: deviceId, @@ -252,8 +255,10 @@ abstract class FrameworkAuthService { // 获取通用信息 try { final commonRes = await UserApi.getCommonInfo( - app: config.appId, + app: backendApp, + pkg: config.packageName, userId: uid, + deviceId: deviceId, ); if (commonRes.isSuccess && commonRes.data != null) { _callbacks?.onCommonInfoLoaded(commonRes.data!); diff --git a/lib/src/services/feedback_api.dart b/lib/src/services/feedback_api.dart index 2a458d8..797b955 100644 --- a/lib/src/services/feedback_api.dart +++ b/lib/src/services/feedback_api.dart @@ -3,6 +3,8 @@ import '../api/proxy_client.dart'; import '../entities/feedback_entities.dart'; /// 举报/反馈相关 API(使用原始字段名) +/// +/// **Body**(`submit`):`fileUrls`、`contentType`、`content`(与文档顺序一致)。 abstract final class FeedbackApi { static ProxyClient get _client => ApiClient.instance.proxy; @@ -31,8 +33,8 @@ abstract final class FeedbackApi { entityFactory: SubmitFeedbackResponse.fromJson, body: { 'fileUrls': fileUrls, - 'content': content, 'contentType': contentType, + 'content': content, }, ); } diff --git a/lib/src/services/image_api.dart b/lib/src/services/image_api.dart index 2d005df..d0e32cf 100644 --- a/lib/src/services/image_api.dart +++ b/lib/src/services/image_api.dart @@ -3,6 +3,8 @@ import '../api/proxy_client.dart'; import '../entities/image_entities.dart'; /// 图片/视频生成相关 API(使用原始字段名) +/// +/// **请求头**:需登录接口自动附带 `pkg` 与 `User_token`(参见 [ProxyClient.request])。 abstract final class ImageApi { static ProxyClient get _client => ApiClient.instance.proxy; @@ -47,13 +49,20 @@ abstract final class ImageApi { } /// 创建文生图任务 + /// + /// **Body**(原始字段):`imgCount`(默认 1)、`aspectRatio`(可选)、`prompt`(必填)。 static Future> createTxt2Img({ required String app, required String prompt, String? ch, String? userId, + int imgCount = 1, + String? aspectRatio, + + /// 兼容旧调用,等价于 [aspectRatio]。 String? quest, }) async { + final ratio = aspectRatio ?? quest; return _client.requestEntity( path: '/v1/image/txt2img_create', method: 'POST', @@ -64,8 +73,8 @@ abstract final class ImageApi { if (userId != null) 'userId': userId, }, body: { - 'declaration': 1, - if (quest != null) 'quest': quest, + 'imgCount': imgCount, + if (ratio != null && ratio.isNotEmpty) 'aspectRatio': ratio, 'prompt': prompt, }, ); @@ -209,21 +218,27 @@ abstract final class ImageApi { ); } - /// 获取积分页面信息 + /// 获取积分页面信息(与 `UserApi.getCreditsPage` 相同,走 `GET /v1/user/credits-page`) + /// + /// 保留此方法以兼容旧调用;[app]、[userId]、[ch] 不再参与请求(接口只吃分页参数)。 static Future> getCreditsPageInfo({ - required String app, + String page = '1', + String size = '10', + String type = '1', + String? app, String? userId, String? ch, }) async { return _client.requestEntity( - path: '/v1/image/getCreditsPageInfo', + path: '/v1/user/credits-page', method: 'GET', entityFactory: CreditsPageInfoResponse.fromJson, queryParams: { - 'app': app, - if (userId != null) 'userId': userId, - if (ch != null) 'ch': ch, + 'page': page, + 'size': size, + 'type': type, }, ); } } + diff --git a/lib/src/services/payment_api.dart b/lib/src/services/payment_api.dart index d793aaf..282c88a 100644 --- a/lib/src/services/payment_api.dart +++ b/lib/src/services/payment_api.dart @@ -3,53 +3,61 @@ import '../api/proxy_client.dart'; import '../entities/payment_entities.dart'; /// 支付相关 API(使用原始字段名) +/// +/// **请求头**:需登录接口自动附带 `pkg` 与 `User_token`。 abstract final class PaymentApi { static ProxyClient get _client => ApiClient.instance.proxy; /// 获取 Google 商品列表(Android) + /// + /// 默认 `app` 为 [AppConfig.backendAppTypeAndroid];可选 [client]、[country]。 static Future> getGooglePayActivities({ String? app, - String? shield, + String? client, String? country, String? pkg, }) async { + final cfg = ApiClient.instance.config; return _client.requestEntity( path: '/v1/payment/getGooglePayActivities', method: 'GET', entityFactory: PaymentProductsResponse.fromJson, queryParams: { - 'app': app ?? ApiClient.instance.config.appId, - 'pkg': pkg ?? ApiClient.instance.config.packageName, - if (shield != null) 'shield': shield, - if (country != null) 'country': country, + 'app': app ?? cfg.backendAppTypeAndroid, + 'pkg': pkg ?? cfg.packageName, + if (client != null && client.isNotEmpty) 'client': client, + if (country != null && country.isNotEmpty) 'country': country, }, ); } /// 获取 Apple 商品列表(iOS) + /// + /// 默认 `app` 为 [AppConfig.backendAppTypeIOS]。 static Future> getApplePayActivities({ String? app, - String? shield, + String? client, String? country, String? pkg, }) async { + final cfg = ApiClient.instance.config; return _client.requestEntity( path: '/v1/payment/getApplePayActivities', method: 'GET', entityFactory: PaymentProductsResponse.fromJson, queryParams: { - 'app': app ?? ApiClient.instance.config.backendAppTypeIOS, - 'pkg': pkg ?? ApiClient.instance.config.packageName, - if (shield != null) 'shield': shield, - if (country != null) 'country': country, + 'app': app ?? cfg.backendAppTypeIOS, + 'pkg': pkg ?? cfg.packageName, + if (client != null && client.isNotEmpty) 'client': client, + if (country != null && country.isNotEmpty) 'country': country, }, ); } - /// 获取支付方式列表 + /// 获取支付方式列表(body 中 [activityId] 为 int,与接口一致) static Future> getPaymentMethods({ - required String activityId, + required int activityId, String? country, }) async { return _client.requestEntity( @@ -64,6 +72,13 @@ abstract final class PaymentApi { } /// 创建支付订单 + /// + /// **Query**:`app`、`userId`。 + /// **Body**:必填字段 + 文档中的可选字段(以下为原始名,非空才写入): + /// `lastName`、`country`、`expireMonth`、`accountName`、`userInfoType`、 + /// `automaticRenewal`、`channel`、`cvcCode`、`channelType`、`firstName`、 + /// `subPaymentMethod`、`phone`、`tgOrderId`、`tgId`、`name`、`expireYear`、 + /// `card`、`status`;另保留换皮用的 `lineage`、`armor`(视 [FieldMapping] 而定)。 static Future> createPayment({ required String app, required String userId, @@ -72,6 +87,24 @@ abstract final class PaymentApi { String? paymentType, String? lineage, String? armor, + String? lastName, + String? country, + String? expireMonth, + String? accountName, + String? userInfoType, + bool? automaticRenewal, + String? channel, + String? cvcCode, + String? channelType, + String? firstName, + String? subPaymentMethod, + String? phone, + String? tgOrderId, + String? tgId, + String? name, + String? expireYear, + String? card, + String? status, }) async { return _client.requestEntity( path: '/v1/payment/createPayment', @@ -85,13 +118,72 @@ abstract final class PaymentApi { 'paymentMethod': paymentMethod, if (paymentType != null && paymentType.isNotEmpty) 'paymentType': paymentType, - if (lineage != null) 'lineage': lineage, - if (armor != null) 'armor': armor, + if (lineage != null && lineage.isNotEmpty) 'lineage': lineage, + if (armor != null && armor.isNotEmpty) 'armor': armor, + if (lastName != null && lastName.isNotEmpty) 'lastName': lastName, + if (country != null && country.isNotEmpty) 'country': country, + if (expireMonth != null && expireMonth.isNotEmpty) + 'expireMonth': expireMonth, + if (accountName != null && accountName.isNotEmpty) + 'accountName': accountName, + if (userInfoType != null && userInfoType.isNotEmpty) + 'userInfoType': userInfoType, + if (automaticRenewal != null) 'automaticRenewal': automaticRenewal, + if (channel != null && channel.isNotEmpty) 'channel': channel, + if (cvcCode != null && cvcCode.isNotEmpty) 'cvcCode': cvcCode, + if (channelType != null && channelType.isNotEmpty) + 'channelType': channelType, + if (firstName != null && firstName.isNotEmpty) 'firstName': firstName, + if (subPaymentMethod != null && subPaymentMethod.isNotEmpty) + 'subPaymentMethod': subPaymentMethod, + if (phone != null && phone.isNotEmpty) 'phone': phone, + if (tgOrderId != null && tgOrderId.isNotEmpty) 'tgOrderId': tgOrderId, + if (tgId != null && tgId.isNotEmpty) 'tgId': tgId, + if (name != null && name.isNotEmpty) 'name': name, + if (expireYear != null && expireYear.isNotEmpty) 'expireYear': expireYear, + if (card != null && card.isNotEmpty) 'card': card, + if (status != null && status.isNotEmpty) 'status': status, }, ); } - /// 获取订单详情 + /// 支付订单列表(data 为数组) + static Future> + getPaymentDetailList({ + required String app, + String? userId, + String? paymentMethod, + String? filterStatus, + }) async { + final response = await _client.request( + path: '/v1/payment/getPaymentDetailList', + method: 'GET', + queryParams: { + 'app': app, + if (userId != null && userId.isNotEmpty) 'userId': userId, + if (paymentMethod != null && paymentMethod.isNotEmpty) + 'paymentMethod': paymentMethod, + if (filterStatus != null && filterStatus.isNotEmpty) + 'filterStatus': filterStatus, + }, + ); + if (response.isSuccess && response.data is List) { + return EntityResponse( + code: response.code, + msg: response.msg, + data: PaymentOrderListResponse.fromDataList( + response.data! as List, + ), + ); + } + return EntityResponse( + code: response.code, + msg: response.msg, + data: null, + ); + } + + /// 获取订单详情(query 使用原始字段 `id` 表示订单/支付 ID) static Future> getOrderDetail({ required String userId, required String orderId, @@ -102,30 +194,35 @@ abstract final class PaymentApi { entityFactory: OrderDetailResponse.fromJson, queryParams: { 'userId': userId, - 'orderId': orderId, + 'id': orderId, }, ); } /// Google 支付结果回调 + /// + /// **Query**:`app`、`userId`。 + /// **Body**(顺序与文档一致):`signature`、`purchaseData`、`id`、`userId`。 static Future> googlepay({ required String signature, required String purchaseData, required String orderId, required String userId, + String? app, }) async { + final cfg = ApiClient.instance.config; return _client.requestEntity( path: '/v1/payment/googlepay', method: 'POST', entityFactory: GooglePayCallbackResponse.fromJson, queryParams: { - 'app': ApiClient.instance.config.appId, + 'app': app ?? cfg.backendAppTypeAndroid, 'userId': userId, }, body: { 'signature': signature, 'purchaseData': purchaseData, - 'orderId': orderId, + 'id': orderId, 'userId': userId, }, ); diff --git a/lib/src/services/user_api.dart b/lib/src/services/user_api.dart index b444bfc..238197f 100644 --- a/lib/src/services/user_api.dart +++ b/lib/src/services/user_api.dart @@ -1,26 +1,30 @@ import '../api/api_client.dart'; import '../api/api_response.dart'; import '../api/proxy_client.dart'; +import '../entities/image_entities.dart'; import '../entities/user_entities.dart'; /// 用户相关 API(使用原始字段名) +/// +/// **请求头**:除 [UserApi.fastLogin] 外,需登录接口均由 [ProxyClient] 自动附带 +/// `pkg`(包名)与 `User_token`(已设置 token 时)。fast_login 仅带 `pkg`,不带 token。 +/// +/// **请求体**:与《客户端指南》一致,使用解密后的**原始字段名**(如 `referer`、`deviceId`)。 abstract final class UserApi { static ProxyClient get _client => ApiClient.instance.proxy; /// 设备快速登录 - /// [deviceId] 设备ID (必填) - /// [sign] 签名,MD5(deviceId)大写32位 (必填) - /// [referer] 归因信息 - /// [ch] 渠道 - /// [type] referrer类型 (af/fb/gg/ios_adjust/android_adjust) - /// [app] 应用类型 (iOS: HIOS / Android: HAndroid) + /// + /// **请求头**:仅 `pkg`(无 `User_token`)。 + /// **Query**:`app`、`type`(默认 `fb`)、`pkg`、`ch`(可选)。 + /// **Body**:`referer`、`sign`、`deviceId`。 static Future> fastLogin({ required String deviceId, required String sign, + required String app, String? referer, String? ch, String? type, - String? app, }) async { final config = ApiClient.instance.config; return _client.requestEntity( @@ -28,20 +32,25 @@ abstract final class UserApi { method: 'POST', entityFactory: FastLoginResponse.fromJson, queryParams: { - if (ch != null) 'ch': ch, + if (ch != null && ch.isNotEmpty) 'ch': ch, 'pkg': config.packageName, - if (type != null) 'type': type, - if (app != null) 'app': app, + 'type': type ?? 'fb', + 'app': app, }, body: { 'referer': referer ?? '', 'sign': sign, 'deviceId': deviceId, }, + includeUserTokenInHeader: false, ); } /// 归因上报 + /// + /// **请求头**:`pkg`、`User_token`。 + /// **Query**:`app`、`userId`、`type`(默认 `fb`)、`pkg`。 + /// **Body**:`referer`、`deviceId`。 static Future referrer({ required String app, required String userId, @@ -56,7 +65,7 @@ abstract final class UserApi { queryParams: { 'app': app, 'userId': userId, - if (type != null) 'type': type, + 'type': type ?? 'fb', 'pkg': pkg ?? ApiClient.instance.config.packageName, }, body: { @@ -67,15 +76,20 @@ abstract final class UserApi { } /// 获取用户通用信息 + /// + /// 与当前接口约定一致(原始 query 字段名,会经 [AppConfig.fieldMapping] 映射): + /// - [app] 必填:应用渠道标识(常见 iOS `HIOS` / Android `HAndroid`,与 fast_login 一致) + /// - [pkg] 必填:应用包名 + /// - 其余可选:[client]、[userId]、[ch]、[inviteBy]、[deviceId]、[clientId] static Future> getCommonInfo({ required String app, - String? shield, + required String pkg, + String? client, String? userId, String? ch, - String? item, + String? inviteBy, String? deviceId, - String? gauntlet, - String? pkg, + String? clientId, }) async { return _client.requestEntity( path: '/v1/user/common_info', @@ -83,13 +97,29 @@ abstract final class UserApi { entityFactory: CommonInfoResponse.fromJson, queryParams: { 'app': app, - if (shield != null) 'shield': shield, - if (userId != null) 'userId': userId, - if (ch != null) 'ch': ch, - if (item != null) 'item': item, - if (deviceId != null) 'deviceId': deviceId, - if (gauntlet != null) 'gauntlet': gauntlet, - if (pkg != null) 'pkg': pkg, + 'pkg': pkg, + if (client != null && client.isNotEmpty) 'client': client, + if (userId != null && userId.isNotEmpty) 'userId': userId, + if (ch != null && ch.isNotEmpty) 'ch': ch, + if (inviteBy != null && inviteBy.isNotEmpty) 'inviteBy': inviteBy, + if (deviceId != null && deviceId.isNotEmpty) 'deviceId': deviceId, + if (clientId != null && clientId.isNotEmpty) 'clientId': clientId, + }, + ); + } + + /// 获取 App 语言配置(需登录态) + static Future> getAppLanguage({ + required String pkg, + String? lang, + }) async { + return _client.requestEntity( + path: '/v1/config/app-language', + method: 'GET', + entityFactory: AppLanguageConfigResponse.fromJson, + queryParams: { + 'pkg': pkg, + if (lang != null && lang.isNotEmpty) 'lang': lang, }, ); } @@ -110,6 +140,68 @@ abstract final class UserApi { ); } + /// 积分分页流水(`GET /v1/user/credits-page`) + static Future> getCreditsPage({ + String page = '1', + String size = '10', + String type = '1', + }) async { + return _client.requestEntity( + path: '/v1/user/credits-page', + method: 'GET', + entityFactory: CreditsPageInfoResponse.fromJson, + queryParams: { + 'page': page, + 'size': size, + 'type': type, + }, + ); + } + + /// 用户支付记录列表(`GET /v1/user/payments`,data 为数组) + static Future> getUserPayments({ + required String app, + String? userId, + }) async { + final response = await _client.request( + path: '/v1/user/payments', + method: 'GET', + queryParams: { + 'app': app, + if (userId != null && userId.isNotEmpty) 'userId': userId, + }, + ); + if (response.isSuccess && response.data is List) { + return EntityResponse( + code: response.code, + msg: response.msg, + data: UserPaymentsListResponse.fromDataList( + response.data! as List, + ), + ); + } + return EntityResponse( + code: response.code, + msg: response.msg, + data: null, + ); + } + + /// 未读消息数(`types` 可为逗号分隔或后端约定的枚举串) + static Future> + getUnreadMessageCount({ + String? types, + }) async { + return _client.requestEntity( + path: '/v1/user/unread-message-count', + method: 'GET', + entityFactory: UnreadMessageCountResponse.fromJson, + queryParams: { + if (types != null && types.isNotEmpty) 'types': types, + }, + ); + } + /// 删除账号 static Future deleteAccount({ required String app,