^9SAoXe
z>wZA*ZFt_QS6LhSv2e1j1h9z4eX
zyVDb`cV12Buwxq(o-cg-<%^|?Mxhc<*ih9R)oHM*24!>GvnMN5=17O@+%lf&7alJo;$`u
zrFR;hW#i|ljeb1o0is4DS9M`I7xKNW1c)f6*QAXMV-cys44|ySgv%5Z+(nx!lZpK8
zl10)#YE?(KCXvBjVj16G5;Iqv1;-Yh;q
zI<5E6f{A3HB4QS=i;G2gi826_mUGkPwK_jDUIAu=Or_R+;QC;$|~9
zM4O0+7lKWqu{y%u20bxD5VjWfKlXsZ&(fk1_BR_u8Nk*X
zt0$EQLg<4(2{XWFsr-pd94c~zjU)JVA&na()9nG@E9s`i~)H)#O^Kp^1_r!6egaBD(?j7=bpR
zUR=I@jKzaq*c9$pd;*h>)nGfgHNGaSicaqG0Br~>S)aw)a!#!kIOVOH-}VORv7u66
z1Q537BLJHI0ikvV=-R%&fL}2H4*vnW0Pyeq3mp26Y*dJB>h^Ae^=*8;WQ`1J5is2b
zoG>Sn&&~i?O7OmsUJQC}AS)AS;oT%J02uB60@4Nmi2Vn&0zl^93S1!)SyE(c3#k68
z*gZXgq`FFh1wy|iZltre&lQ@JK83HAOZ
za)>=iLX0L-!PG0$?UNCogm{51HoCA6iXl5}4L~*wp6)1gML+H@MF2ugM*6desw!I~
zXou9~d_%eg^`b$W#LMJ$%v??uE0E?ozJ->(<&v5z98<=A3#+3=6XU?qnN|)0223J<
z88G#UA?t1aGLR{9Mf(r_We{LEi`Y;9%V1Pi<|L9)eC#j6z$l)JPVOsZ2K-vxpRkJO
z*@6-Xid$&A=cd%_2JWe?6XRG1eQ+U{Mfed$?_BX6vdjLw%xXjaux23l;T|Nhlt#(%
zmWtzC^cvxt!2w(rU^tPbT1tHe*&cbvL(9q)g(J}o3lP7VroE!RRj
zs?7pc*RAbYhr5M9HU)#aXrq6F)~v(5e+N;zXu^MkL9F6^e+S8Q&^P}MF1WCY5BwdB
z)IneUH|WhO-v4({T?Z}rZ_tBPeDLpJhYtGEzd?W2sSd#qIxe+LV-
z(MkUXm07FC{|-87qig;Rso>k$=EqmuNWBoR;7(}`Zq`yJLmgvPzPD~Z;(57&h6iz7LrfsuROnF=N|tZ6gNb6
z{2T0zozwa^s1NFgzcXiJ=k)#!8XzP84K~HjJ^VMQha~$qcpM9&$RCl0jX>D_yF4Wp
zB!$0>n!2g9Wj3{r`59iI)1O!uJJ;q<=c|0g<|TnQ{6=M+JNN)_yPk*B#;VZ;>M9Wy*%EQM{4N=dr5VE3&pu4>CjQi
znJdnUm~Mk~jc((S*+AnB;|pBL+8+~U`>_g>$*F@d&h
z5=P*i!doml+LFb8$wborh{JwR3#Ixr=(c|
zKyPTYRUAs?{R>EAlL=kiy9EHivx=Xm4_=XokDvlU+q7Oa#nVw7=~iOH3BX^6IlQ7s
z89oEJM%b4zXd$R9`U{NKpb6Kz(=x%NcWcqH^uZOBykIYl{-Q;55WVLKp8+TYg2jV8
zl^t1;ZnZXWxc)_Ni_gRB8q4yJdZfAZw(Kh=YSpM+cHVc$b%xL)h^O(yiJCPCMvFj+#if?7!{jF;b8hC7e;@N&?VCQ;te-)`=REXd!gz+sE2y
z0RA^52WiTXY%QOZ;Pgi_JT0<>@JYAPhoMm{Ez#8Te?VNn7&Pd`N68DMzNJVlu`+6h
zXnCWg3Zx_HCU-1D&lRM>2|i@8ySzTexCS9<`D{xcEEygLHg_G30};lV+W;7o>0PIkZvET@DYQcQ^%ZF=vaw^M#Ag#zfE<$~nOh)~R2
zO`19Awv{-Kmd4C&|LYhzjv(Dsx^Dx-Y8VGP0?S7R9w1}~>u;%A_YJThC%FGy4S0Mj
z3bfHu`f&LNFh#>WL{*`C&LZr+*g4IG>hd_GTj?Sk9IxUkhAW7wynjds
zKmZG44`!Imx}kL+7ovqo>Cffn+*$<}vZP=U4ry3Z`#dy4)Ps+j0oc`tPClK-Cj0Q&Xd@iq=?g=>7~(Tf7g*v$cdO!FRF6
zbcF&tw3HEC#|O*!=4z{+j)rWC6xh(^21t!bD{URaa**MpNS()zYZ<(+QbNrD7sWW+v5_y
zp>gL(;bJ=D8|yJYoU^Q~x07kzF|cTfgW9+1nTlwcU8+o
zJP?uwu|baL&7jsjH}AcIrH_CH)zaX!i&_qXNl01*SS(AO3AmG+tG$75NO#{1^xbni
zj>;keX0G?bBxJ=doYkjhsT*E5T>x(RcspNEp9d>dVzVBDQimLb&07yX*)OyD)Guv-
zS-+O55`B?Qv?7Th>O!~3>6%`bU1aqs`g0}t?UJUGiP#nT;{uHMLT%0#hrPljV9F5D
z(x{nEo)$VjDH2d|i89XB=5Fbpe$p!sK(a5*;2q`(D7^pJx%?hRrwl1hLusxXwrbvffStxLadcJrAw`K`zVy
zUMr^z$m4c&(8J&nrxDCkP$$RsOsH+F8J2RC=M8C{O_cAlhP
zuemLJc|?vxGl@$mR_?9H-Nz$boJw;2^~?ol?X<646w-J~{g7*$CA$1}SMI}Cl$YbF
z&&vPzwtZwn;uQ{oxPJdWy$iDl>PnHbH*S2ta5pfC2!%L%=W!EW%G#>lWa&3k^1@75#*tnC(X7Nv{IFn*4@C$LL^X
z2q!{*WK|q{X^-+UPQ-P!+>X2!{yl3e8{;IET*w0|!{K0FTN8ra+WZj(e!y}&?WJ^3
z)c&@!YH=)iB`Xm2s+X4EI$)S#EVS`e4x*7U$d@LFKZB()Ay`-oP$~;G0sNL(Rcpkf
z%)Xr-+q*Z0KB_;%IH`|=z^`DDbrv&wrV!0Zg`OWFA3@er@OQ2cPbx=lfM2_!eLo)^
z2VId1;hYk09${LSXC86%w|P+ZoEaA6S!N3TkM*Zd#0ip~UI2*GBwZfEd4S|3wc~0;
zf=Ol@e?9Rb5GCk#qVABPZI|TBMR5S48BTrldQ8+Ef!x6)Yy5F^m0!l*>eKrN+7l589H|ydoDn&5lZMD_
z^9Oq#1N>{>oTU4V`&tlMT1RoR|Jo+LP^bwU@$3uqHl_GMiYyQqS_?qI&UQo;4mF>&D)v
zy);DT4tsZnh(xp`h2X{Pc4Q6?xDmOKM~%G^{dDVO%mVj=W+!tQ=wZHB)j*r@$L7ux
zPon&DA(~`A7Kiod{pHJ=Zo;(D%|`qVxFNZa*~XD4+eD9v6hnaJ6rh4(WvL7JYz;sq
zwV8U>znu3=3(p;LyQ;2_*ny>aE~k6VG8pTkb-?Lq-zWUfJw
z$$zN_ZjipQZa(&m4uRJv=}X0@Ys>U(uvl)`!^tahaW%o(%@X`mN{x4ZVlOX5D!U&x
z_mjl}-dgFucGL=tXn$a!#~S(WgeUws{V;`7eVi#wTsTo?%Y?vU|88*ynK#?5#NRw$
zU3kWTy*0tc-)}LEr#|=EoxtMyx%c|{zX_ervNY{=NmEo5<)D7R8q((K%BF8X3DZ4h31Ew
zXEFZL5Se1gtM3)sk}j}HvR-{}xGop>^vO8Q`e>unWtv|*Muxa9SYpsT)`s+d1y6Df
z)DbY9VZk{zG9=|O$N(@WB~kMIHS>kYb5Zlpo2wKt6zlC0)m{sufBmlEblEQS4;Z?CSaiYWY}EY0lR63vt@qh566*THb{jIO^bxbS
zV7EG5J$z#$ub%P{J?(GyWuNMovq+Hfa4#d|1iT9}auf^+e{AJGsiR(3V%8K=eqz?S
zrYrdt@a?&1gKHF0KJ%lbK+pMnE%fdjP-9L2#j$-JgF0AitG`_ovGK^{0&^<~CR1nMke@vDzB9+
z?K+Ps*sS@&gRkI7`pF6azNN)O=46YjmW%rh0>I8JfwTJQqb}GcbphRj%&pyi@Ix18
zE-P71(`%SklXEc9>KR>GFM_=A;Rlz7&fGr}o#xs>Gy@E&Z-Z*5md7R;s2<^>fBVpf
zVC^hR>q|j$6?apGvL+s|$72%J8VCDN=LC+;Zyze1(IGs1`a@uQNF|TK2B1sU3BIdw
zkN}QIhZ<`lG_@P-!|W#X1pm1q8|Ywi{jkdu4;8P-&_C*(-)j-#>Ca^AJq8xL`T99(>hllEI
z!^q&*jZ1x>Zm7#rwm;qOC#wUjeS3Q%eW$mcLINpgM5wUy7x&zm1=Io#oBGM>|G<3}
zvRCBFMJciKWJBi_fmr0Vg(K7Pz!97i{HC@EUPA)oI&C^EQ&)lk=?;d7Un!jhJ6Aw(
z*L2n!+o!vY!f!wV!odra*w~AEhJd(~t6v%QQ__7Kces%FzUQ!H!E06zD5I
zjvM*n`)nPINt$9+s+J8=hn%=FmmAQ*WyvI{`}td6CJ}w
zVL6K=@x2GJ#w;*>N|4K_138BtXN>pc%n$c9H)pE|sOR>Jjh1iDbKoCU4yzMRlnW%fub
zJVo0U7dgw$M8Z5O5VExpZ8mms~
z&3k$HTL`(z?~Vl>za>DPv$-d7Xyn>c+9UnjD_g+@au#&5u0cXX?=^7qAfIKF8;p<|
zO0qnBo)n=kdN!Y62a0x_8Ctk`aF|>70@fbealmcY)k_3@rlOdvcb=+^R;I*Z4v*dh
zW_Q5Gt3P*%vTOOllPUZUOH_p#2Ekc5z9FZlC9iNz{b7pTb($y2biw+vTLm>yWn_6_$i5S%VhI>`Vu-X+6Zgc
ztI?@|1JV`e976xjESboEJ(qFk`s?$&OVt|Gm6MUpd`khMutx_hPwye)gnO8Lpy<_<
zHOhLJh~Ep~7juoT>OK6U4Cv2MXj8@Ey8Re^$P-c7@8ikn;KA8OHaMW^bOAr%1xqZq
z9@4wrNBa2@tTp3v%V!r=SAP~Yv0VcsdZpD2f4WNfD(4yyDY
znIpgfRp#QvszL=C@>@UoqwT$$RRllX%wc;QRH!qXF}?{G)LLEbX>XBgVp9Wi+%X<~
z8m_)!Lt#))1|_J_dYb#s(X{oOv6&;5jcgi#A_~8{HXlcS>ndb4mFVIJnV|LJiij5O
z_;%Qv4g*_$apG4*1_QfVcKF>T9pu&&bRaU2)~{lwHt#xF6m0lTr#jImrarf;C5LAh
z@nBpr6xF`kxBIZi?nI5_C}oi@O)(9Kj1E#k5h6{n0Z)THtVww%Ps?8lu)J_!?@1!M
zxWUTkLxtu!NSO)~do^gy
zIth+oQMm=SQm5OVhG(kIoNha>X()$pW_(It3Az4ie1g1c4L;H>1Wxw*OqG$RAtccS
z29{6SO;f6egUUf^pap=Vvyqf7dnfonhz599EQg2miW}~jZGPh9nf1oRfiFrlDW6c$
zMtn?nApVeTtLe&-@(19yk%z;mpCZ8*VN%K1W)?HLSM>Y0o=-3WTV(92gmGwWUSTS^
zhvZh7WV03g<@sjqSI05G9|bU#PEAZ(j@wGyZdCFJlEV+5GGjG*rv{By0W}c8KpXC1
zkJc~;(F`41U+|P^NBMO=pL(?n^)v`1iwn(qw6``Y9FP@!E3Ts2aImsn0vSzg4#0PI
z|71ohi^T8k?3;jrY}M?)ezO4A7!7so`2p~X{PxQc6!49{)`?*U+OR(LE~%CekMwX&fnBI4bLm{)9wr&=5qKJrfMjO_1{wv>i<@)
zbC^~70H;ED{la>)G?=YQp!yu(ME@1Kj}d&Z+@`vBVA{my4~*x1;(>*ILO`N(JsHH3
z*FR=o#;aDQh0Bhj!86Y}_lt;;;HTj|)&pO@4Ghn$QNb1gKF6?I6pAUa?6MYc>j=
ztONFqMojrm+P4v@quREkVT=cdRZvCQgzGz?3)#(&E;hou0-!=wBn;_Sf6TV`xCfHD
zpRm}GJfgwj$9OmCeaqEv(|e;~E@3BpZXb^nVY3u-ug#Z7aPK`3Zs$~SXR2WVA#oVA
z%ze=<=>vO1qfFRJGf_rq^5usK#>*R7%Gs@3N3Nt3Kr}x{8kd~`>Hm>PXrxf+{0|~>
z|ML#37K3GB-&<{)y$7V36RPfCy2l2iqF3F|e+-?|9`AG+far#|MzB?X%o{wCn%yd}
zCMqYtIF#$X`C`KlqF@UA?$|+iVm-jyFMD_wObbX5O^9m6LNj!)Bz|)
zGwt{PB9)*KqDvA7CtxRSPs5F@;PccmnDNB=Dhj#IzcQ8!!@V@{7alv-w@Zr=(|PIc
zaIC?jc$;WB8?r49$48#p@$1nF7`8RFEOV)qqdo@sXO*j}$#!rLx82ECpF$*Z%S|q5
zic=nzToM^emh`t0@oNJjDM|Q`zV6W8j;eFcb^33|LJn^E3R6xqt;OJAjOd>@kc6cK;f
zSI{a+IQ|rXvtB*nXp2?WoObf3XEMP0Uthm`YdhI_%nWKn^H@O2Kqf*>pxO~^U4Qlx
z8j>WlcVcniTMMzyh8Rf=!%}LM5m<
zm7x^L?E34}UXWz*Ce)qBgVA+2yFSyOtY-y3j-MBl^iB*0$tBcoWZ^naBLC~-DjpSe
z@}g%Y$7Dwvu|yc^1_^w|Hi~CCC{#183{|C`1IT4o@!MiTY?koWxRN%zhqoO3QxVv6
z8rYNrx_bu?k-{V1L8Jn(Z5Jw5&c=@p9-5F;rUl(;6vg29K$dEL5~mMMPW~H^4%<`o
zL>Ox(fx3?US!#W|9;FBEC_8qXDp1y2lX(TyV(-BA5*E8T^$(3nPb77cQjPq5k3Xk1
zj&mCBh~$c60c;|O6WE`z{VNU=CC>zTkM{`rtpYfio)If#Ev2!s#&FDNl|&@+OHT^h
zo}+jiVLv$LVtNNSTzMH>t*siIboPG){!M!P>PLHW@ZH1-tBnmRV|8
z02!@)_qZ|jA8xymYcG5x35zYDEuoF46(SiPYZ4a{{{^j^_=Elop5t
zUKE4?KY#~m*wU>S_54sPIGQg17n7wJX`KY{c5+NsjF-t7?0V7hPw0mZpZla
zSi_|E{MWu6cE<_{_#Nx#vy5ViL~0{Uoz90|h-x^$fR;>H+DDSaFX`&=H*)~Dnd2(?Py>{%*Qu9z;=OwjH$RhT5-gJlW#QBD(U~8`+IP}en>nKs-?tk2dYSq+Ma=OkSB)p7LWHMAj?9)b
z6ie`JBNEB7xpYsNzn?<(0br&_MJi5T{SK9rM>H8;?JX7#_pwcp_W?kf4hcae*qR>(+R#f}Z
zxl3^^woWTA{l#oqBzVuqw0wwZfjQI4KV#rexMdjECv}IB_FoU};Sc`!Cv#ftFQ+VW
z8MJT(f@225t~E8wL!afUJ=ffq&y2~
zySMNE{^oYD0@X_a4u2cJwj{L}rb-P}zpqB9Vb
zkBqYX4ov)wBrawTnH~Wjv|wdkwr{c5zGZ}r2Ozwd;%_nTYPansv@(oQ~d|bwoNT}&&qy_AVd;sZzT0uV8jUnxy*%SQQ7rUaqg;80h(NaZ!Q_L>XS?93aA5J_OvEjl8sp8gfR^&MZuug>-#
zU3N5kn4f_*JeJb}>goRaq&lU@JK+WDl-o(<(~~~7hCR*l702wOq1GSEf#*?}%(rT5FdC(nFYz!8wr;Fa273y(A9C(@1w4O;4
zy9V=+z)4ejolC-xrDuEp
zTU%DY-?%8U?iVnZ^^AEoDlmKTs()8vKWpYAKx>HEB{UQ6_RNq14)=R0=NokUQ;MSM
zNI*g6RFr@A6gsta^s4s2sTFD4(6c(dE;QV*PZ2Wh=RY+l#;MniB4|5_7P+dQ)`^7;
zisT2^*GynVK;%vZTj3RSt}LZj^{)o2&`_v#s6zlLHUT?iCEZOHk
z`0E%b5!wi>T0Bp^$ItKdxz6=yXmD?V78MGn8R^YviK^)Z!R!%MLK{GViI7>S8fb`R
z$RAv&5;wM8@Wu^&o79nrH6OWl)q9;P6ihzC?dR!i(tuDGt6-Yso#fHHPVsWRR}hj=
zutwH3EM0h?o(52w!iRy13n^8>8r`F~{al@t@Elkx7Z`HR_A2fi<(;cVLA{#eM&iVt
zj20YEu5Xg<9#FM!>N=LFgqnfG(8q?sV?cK+H*dtHhUqAyp_L;X&m}mXVyqA>+TH6H
zjNWIipi}w;ykd-IJ-mQ$l&f1^%(`fm%r>?e^TuudR8X57Jl3%FCs9?Fb#qkMuT1e<
z2*F@tPEyAyi0}zB0dQZMm!ZHY{(9V4gM7bo0%PEu9QP(-DLo62POfUKx;kl
zr?;mp&aq@)s(1ixMe4v}&67LevfcHnu%@nA$9V+dIJ4;aqtbf*(zlJxL!sHd=6I1MO@bw89ME8E?O?
z`zqn;rPK=8x{uBLj8T!
z&uqm~TMGn(Gnk#SV%ngq6C+a7epve#t`)`_`u6|DrQL-)XRTen=>A=-o4a8{tBk#x
z%ghX%4~AfZ%m=g==<4K&2WdaDH;WG@6%Bo77Mg5iIxkzHk^hDgQ7;gCM~E%^NfIz@
zGenU6GAt^><{YvfB$}_@dY3djv_KAb1&wsEoi=ENe|%T_yo0j&uA+vh^0b#>@v`7-
zXGJ(SX$npgy3Hjae1f6#mGXYay+$>igZI)qXPm0)kY3y@%T{w~^|Ojhml+;h0z(#P
z9za6bU(wY$5N%)~jyr8#@&ku6Mhg~PTS-pH&QAlrUi=G3_tf{%^glE8@FTn{L6g^j
zdpTJX7BEubtGQ|ML#~sAf8{hYdJ_*)D%{zF(AV(Arsn(c!IkTL-6%COOU7YKxD>{a
z89-lkOf#V(lp}cV-hO4l?=VCn+t^_n*vFsacr4*THoHVJ{%yC1dWYTY{nstw&f(1`
zQy~b&1g{)^i;41xWcI|NgF2AEyKreX_-~^>hlnWo9OJN=Pv!sNMiB=fO6)QA;kuaY
z+_X0yDO+l(cPgz@z$9}<3(6O%k))EW4wP)pk6>=yeZS{fJdg#5%&ITA;P3P#KqtKk
zg}G^|>8u+CdD#`kwKMz0UyjBuj`eYDHM$H8``*v~yp}5R4?YXCDjb&p)X%_D&6xwY
zZqhXE(Y;SOfli68rbPfgNf?{f%TR6)mt1Q{UC1d2F1)vYm{R`ZI|d()20HcXLQ-i4
zcbH;Y1V;Bg_=mQN+Wg~;2kJW!r#fxE#s}Y#=x+bD<-9h;7*sah%Im?3dk-AXX~p`P
z2E$&2jh{tfZ@9JNPKhiVh$X^$cNoXxhd+NIta2Tt8aA+w8kh7{
zsA>h&kw91jJY+pwAF@qf$5P<|bM#ognS8ts+@v4ulTl#`PSR%DM^tjR$I)f|&_X*=
z3f6gUiB6r72wXA=NeRCyLhDBRQA_r&G-6IupZ|zGXnT3sh%3tTK;n%7&u=#6zT16a
zgf)exm6x(}wtp`u$iBF?*zRr4id)KRdncp9&JY1{2w$|$N=mYH=$ntytKPnkMo1gs^
zNZW=?y-$xj)kbE-DHBhb#9crH%>YP?b`Lc9A`23}+
zEob<5)yxWQ$k6Yr>zJnkda?h&SVp?nbS;>RqHI3rQ+nUH*!zC=t@4;EbL~bv4S6oF
z$VH`#{O_sP&2gdS*ZyrPGh@B2N-8ChI8ix_C&`k&mSs0Z@}+V9`y6S%@G(KE*D
zN-u|N1h^ZNP;o`kjkPNem4%m|`8U=~30*8F2B8|MUdQigMrqk@fzjvc=zr1V6T}`e
z`!x0Yn;+vHW<3X-&WGk}ANm
z{Lu<$G(v26`B`T
zaZFzO>MA|H9kFQHE!dU)<<8NI!>n-;ar$A5kWcOU4IrjjWE#!
z#<4}tM7Liwi7)Q*Eb4h(Z39i^&+9P6=HI{nCT=h(r`hCSJImmWV?*lU@um_wL~`sn
z!Nqo>VQYzeyL^v@u)_ED72>MHeY(Lj@5QTeH@c#)w@DnL~2g7
zH#sDFCXU(OvUN$gE3VWE@wBP^y5G{lp>)%gD$idTtFxaG@rO>-zSm;-3K|J>)B2h(
zrZ4I2f3Ia`QwXNU3pIZNSTzW)
zH^KEwna~m;VbwAdOA$3QUdV^jfL3(bj)VIxS($6}tu_NSX_vd5+0rt16
zTt}FK7`Axh3T4a2F?h>yJe5c8N~vD%=H->BGdu|VmXDJJ~{ct1_;!w
zE@rQR3*3KSrXElUvMbv$Y_-MQ-2>ylJR(PUGxHddNQu&Rkb-8+r04ZWI
zT?9Nz+O%zP&JSxIieXzaHXR<)wVACgAH06@3NCiiPxUzp$F9B7c8lTQV@mL|^Q36h
z%4xU%kF(+A {
+ return this.get<返回类型>('/endpoint');
+ }
+ }
+ ```
+
+3. **在services/index.ts注册**:
+ ```typescript
+ import {新服务类} from './新服务名';
+
+ const services = {
+ auth: AuthService,
+ 新服务名: 新服务类
+ };
+ ```
+
+**使用方法**
+
+```typescript
+import services from '@/lib/services';
+
+// 调用服务方法
+const 结果 = await services.新服务名.方法名(参数);
+```
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..32ed842f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,23 @@
+FROM alpine:3.23
+
+# set the time zone to Beijing Time in the Eastern 8th Time Zone
+ARG TZ=Asia/Shanghai
+RUN apk add --no-cache tzdata && \
+ cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
+ echo "${TZ}" > /etc/timezone
+
+WORKDIR /app
+
+# set build arg for platform
+ARG TARGETPLATFORM
+
+# copy the appropriate binary based on platform
+COPY credit-server-${TARGETPLATFORM#linux/} ./credit-server
+
+# copy docs
+COPY docs ./docs
+
+EXPOSE 8000
+
+# set entrypoint
+ENTRYPOINT ["./credit-server"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..1b5e2d67
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2025 linux.do
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..e43aa45e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+swagger:
+ scripts/swagger.sh
+
+tidy:
+ scripts/tidy.sh
+
+check_license:
+ scripts/license.sh
+
+pre_commit: tidy swagger check_license
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ec19b726
--- /dev/null
+++ b/README.md
@@ -0,0 +1,284 @@
+# LINUX DO Credit
+
+🚀 Linux Do Community Credit Service Platform
+
+[中文](./README_zh.md)
+
+[](https://opensource.org/licenses/Apache-2.0)
+[](https://golang.org/)
+[](https://nextjs.org/)
+[](https://reactjs.org/)
+
+[](https://github.com/linux-do/credit/releases)
+[](https://github.com/linux-do/credit/stargazers)
+[](https://github.com/linux-do/credit/network)
+[](https://github.com/linux-do/credit/issues)
+[](https://github.com/linux-do/credit/pulls)
+[](https://github.com/linux-do/credit/graphs/contributors)
+
+[](https://github.com/linux-do/credit/actions/workflows/build_backend.yml)
+[](https://github.com/linux-do/credit/actions/workflows/build_frontend.yml)
+[](https://github.com/linux-do/credit/actions/workflows/build_image.yml)
+[](https://github.com/linux-do/credit/actions/workflows/codeql.yml)
+[](https://github.com/linux-do/credit/actions/workflows/eslint.yml)
+
+## 📖 Introduction
+
+LINUX DO Credit is a credit service platform built for the Linux Do community, aimed at providing a series of credit-related services and offering a foundational framework for credit circulation for community developers.
+
+### ✨ Key Features
+
+- 🔐 **OAuth2 Authentication** - Integrated with Linux Do community account system
+- 🛡️ **Risk Control** - Comprehensive trust level and risk assessment system
+- 📊 **Real-time Monitoring** - Detailed distribution statistics and user behavior analysis
+- 🎨 **Modern Interface** - Responsive design based on Next.js 16 and React 19
+- ⚡ **High Performance** - Go Backend + Redis Cache + PostgreSQL Database
+
+## 🏗️ Architecture Overview
+
+```
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ Frontend │ │ Backend │ │ Database │
+│ (Next.js) │◄──►│ (Go) │◄──►│ (PostgreSQL) │
+│ │ │ │ │ │
+│ • React 19 │ │ • Gin Framework │ │ • PostgreSQL │
+│ • TypeScript │ │ • OAuth2 │ │ • Redis Cache │
+│ • Tailwind CSS │ │ • Session Store │ │ │
+│ • Shadcn UI │ │ • OpenTelemetry │ │ │
+│ │ │ • Swagger API │ │ │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+```
+
+## 🛠️ Tech Stack
+
+### Backend
+- **[Go 1.25.5](https://go.dev/doc)** - Primary development language
+- **[GIN](https://github.com/gin-gonic/gin)** - Web Framework
+- **[GORM](https://github.com/go-gorm/gorm)** - ORM Framework
+- **[Redis](https://github.com/redis/redis)** - Cache and session store
+- **[PostgreSQL](https://www.postgresql.org)** - Primary Database
+- **[OpenTelemetry](https://opentelemetry.io)** - Observability
+- **[Swagger](https://github.com/swaggo/swag)** - API Documentation
+
+### Frontend
+- **[Next.js 16](https://github.com/vercel/next.js)** - React Framework
+- **[React 19](https://github.com/facebook/react)** - UI Library
+- **[TypeScript](https://github.com/microsoft/TypeScript)** - Type Safety
+- **[Tailwind CSS 4](https://github.com/tailwindlabs/tailwindcss)** - Styling Framework
+- **[Shadcn UI](https://github.com/shadcn-ui/ui)** - Component Library
+- **[Lucide Icons](https://github.com/lucide-icons/lucide)** - Icon Library
+
+## 📋 Requirements
+
+- **Go** >= 1.25.5
+- **Node.js** >= 18.0
+- **PostgreSQL** >= 18
+- **Redis** >= 6.0
+- **pnpm** >= 8.0 (Recommended)
+
+## 🚀 Quick Start
+
+### 1. Clone the Project
+
+```bash
+git clone https://github.com/linux-do/credit.git
+cd credit
+```
+
+### 2. Configure Environment
+
+Copy the configuration file and edit it:
+
+```bash
+cp config.example.yaml config.yaml
+```
+
+Edit `config.yaml` to configure database connections, Redis, OAuth2, etc.
+
+### 3. Initialize Database
+
+```bash
+# Create database
+createdb -h -p 5432 -U postgres linux_do_credit
+
+# If you need to specify encoding, use:
+# psql -h -p 5432 -U postgres -c "CREATE DATABASE linux_do_credit WITH ENCODING 'UTF8' LC_COLLATE='zh_CN.UTF-8' LC_CTYPE='zh_CN.UTF-8' TEMPLATE template0;"
+
+# Run migrations (automatically executed when starting the backend)
+```
+
+### 4. Start Backend
+
+```bash
+# Install Go dependencies
+go mod tidy
+
+# Generate API documentation
+make swagger
+
+# Start backend service
+go run main.go api
+```
+
+### 5. Start Frontend
+
+```bash
+cd frontend
+
+# Install dependencies
+pnpm install
+
+# Start development server
+pnpm dev
+```
+
+### 6. Access Application
+
+- **Frontend Interface**: http://localhost:3000
+- **API Documentation**: http://localhost:8000/swagger/index.html
+- **Health Check**: http://localhost:8000/api/health
+
+## ⚙️ Configuration
+
+### Main Configuration Options
+
+| Option | Description | Example |
+|--------|-------------|---------|
+| `app.addr` | Backend service listening address | `:8000` |
+| `oauth2.client_id` | OAuth2 Client ID | `your_client_id` |
+| `database.host` | PostgreSQL database host | `127.0.0.1` |
+| `database.port` | PostgreSQL database port | `5432` |
+| `database.username` | PostgreSQL database username | `postgres` |
+| `database.password` | PostgreSQL database password | `password` |
+| `database.database` | PostgreSQL database name | `linux_do_credit` |
+| `database.ssl_mode` | PostgreSQL SSL mode | `disable` |
+| `database.application_name` | PostgreSQL application name | `credit-server` |
+| `database.search_path` | PostgreSQL search path | `public` |
+| `database.default_query_exec_mode` | SQL cache mode | `cache_statement` |
+| `redis.host` | Redis server address | `127.0.0.1` |
+
+For detailed configuration instructions, please refer to the `config.example.yaml` file.
+
+## 🔧 Development Guide
+
+### Backend Development
+
+```bash
+# Run API server
+go run main.go api
+
+# Run task scheduler
+go run main.go scheduler
+
+# Run worker queue
+go run main.go worker
+
+# Generate Swagger documentation
+make swagger
+
+# Format and check code
+make tidy
+```
+
+### Frontend Development
+
+```bash
+cd frontend
+
+# Development mode (using Turbopack)
+pnpm dev
+
+# Build production version
+pnpm build
+
+# Start production service
+pnpm start
+
+# Lint and format code
+pnpm lint
+pnpm format
+```
+
+## 📚 API Documentation
+
+API documentation is automatically generated by Swagger and can be accessed after starting the backend service:
+
+```
+http://localhost:8000/swagger/index.html
+```
+
+## 🧪 Testing
+
+```bash
+# Backend testing
+go test ./...
+
+# Frontend testing
+cd frontend
+pnpm test
+```
+
+## 🚀 Deployment
+
+### Docker Deployment
+
+```bash
+# Build image
+docker build -t linux-do-credit .
+
+# Run container
+docker run -d -p 8000:8000 linux-do-credit
+```
+
+### Production Environment Deployment
+
+1. Build frontend resources:
+ ```bash
+ cd frontend && pnpm build
+ ```
+
+2. Compile backend program:
+ ```bash
+ go build -o credit main.go
+ ```
+
+3. Configure `config.yaml` for production
+
+4. Start service:
+ ```bash
+ ./credit api
+ ```
+
+## 🤝 Contribution Guidelines
+
+We welcome community contributions! Please read the following before submitting code:
+
+- [Contribution Guidelines](CONTRIBUTING.md)
+- [Code of Conduct](CODE_OF_CONDUCT.md)
+- [Contributor License Agreement](CLA.md)
+
+### Submission Process
+
+1. Fork this repository
+2. Create a feature branch (`git checkout -b feature/your-feature`)
+3. Commit changes (`git commit -am 'Add your feature'`)
+4. Push to branch (`git push origin feature/your-feature`)
+5. Create Pull Request
+
+## 📄 License
+
+This project is open source under the [Apache2.0 License](LICENSE).
+
+## 🔗 Related Links
+
+- [Linux Do Community](https://linux.do)
+- [Issue Reporting](https://github.com/linux-do/credit/issues)
+- [Feature Request](https://github.com/linux-do/credit/issues/new?template=feature_request.md)
+
+## ❤️ Acknowledgements
+
+Thanks to all developers who contributed to this project and the support of the Linux Do Community!
+
+## 📈 Star History
+
+[](https://star-history.com/#linux-do/credit&Date)
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 00000000..58a0ed9e
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,284 @@
+# LINUX DO Credit
+
+🚀 Linux Do 社区 Credit 积分服务平台
+
+[English](./README.md)
+
+[](https://opensource.org/licenses/Apache-2.0)
+[](https://golang.org/)
+[](https://nextjs.org/)
+[](https://reactjs.org/)
+
+[](https://github.com/linux-do/credit/releases)
+[](https://github.com/linux-do/credit/stargazers)
+[](https://github.com/linux-do/credit/network)
+[](https://github.com/linux-do/credit/issues)
+[](https://github.com/linux-do/credit/pulls)
+[](https://github.com/linux-do/credit/graphs/contributors)
+
+[](https://github.com/linux-do/credit/actions/workflows/build_backend.yml)
+[](https://github.com/linux-do/credit/actions/workflows/build_frontend.yml)
+[](https://github.com/linux-do/credit/actions/workflows/build_image.yml)
+[](https://github.com/linux-do/credit/actions/workflows/codeql.yml)
+[](https://github.com/linux-do/credit/actions/workflows/eslint.yml)
+
+## 📖 项目简介
+
+LINUX DO Credit 是一个为 Linux Do 社区打造的积分服务平台,旨在提供一系列积分相关服务,为社区开发者提供积分流转基础框架。
+
+### ✨ 主要特性
+
+- 🔐 **OAuth2 认证** - 集成 Linux Do 社区账号系统
+- 🛡️ **风险控制** - 完善的信任等级和风险评估系统
+- 📊 **实时监控** - 详细的分发统计和用户行为分析
+- 🎨 **现代化界面** - 基于 Next.js 16 和 React 19 的响应式设计
+- ⚡ **高性能** - Go 后端 + Redis 缓存 + PostgreSQL 数据库
+
+## 🏗️ 架构概览
+
+```
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ Frontend │ │ Backend │ │ Database │
+│ (Next.js) │◄──►│ (Go) │◄──►│ (PostgreSQL) │
+│ │ │ │ │ │
+│ • React 19 │ │ • Gin Framework │ │ • PostgreSQL │
+│ • TypeScript │ │ • OAuth2 │ │ • Redis Cache │
+│ • Tailwind CSS │ │ • Session Store │ │ │
+│ • Shadcn UI │ │ • OpenTelemetry │ │ │
+│ │ │ • Swagger API │ │ │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+```
+
+## 🛠️ 技术栈
+
+### 后端
+- **[Go 1.25.5](https://go.dev/doc)** - 主要开发语言
+- **[GIN](https://github.com/gin-gonic/gin)** - Web 框架
+- **[GORM](https://github.com/go-gorm/gorm)** - ORM 框架
+- **[Redis](https://github.com/redis/redis)** - 缓存和会话存储
+- **[PostgreSQL](https://www.postgresql.org)** - 主数据库
+- **[OpenTelemetry](https://opentelemetry.io)** - 可观测性
+- **[Swagger](https://github.com/swaggo/swag)** - API 文档
+
+### 前端
+- **[Next.js 16](https://github.com/vercel/next.js)** - React 框架
+- **[React 19](https://github.com/facebook/react)** - UI 库
+- **[TypeScript](https://github.com/microsoft/TypeScript)** - 类型安全
+- **[Tailwind CSS 4](https://github.com/tailwindlabs/tailwindcss)** - 样式框架
+- **[Shadcn UI](https://github.com/shadcn-ui/ui)** - 组件库
+- **[Lucide Icons](https://github.com/lucide-icons/lucide)** - 图标库
+
+## 📋 环境要求
+
+- **Go** >= 1.25.5
+- **Node.js** >= 18.0
+- **PostgreSQL** >= 18
+- **Redis** >= 6.0
+- **pnpm** >= 8.0 (推荐)
+
+## 🚀 快速开始
+
+### 1. 克隆项目
+
+```bash
+git clone https://github.com/linux-do/credit.git
+cd credit
+```
+
+### 2. 配置环境
+
+复制配置文件并编辑:
+
+```bash
+cp config.example.yaml config.yaml
+```
+
+编辑 `config.yaml` 文件,配置数据库连接、Redis、OAuth2 等信息。
+
+### 3. 初始化数据库
+
+```bash
+# 创建数据库
+createdb -h <主机> -p 5432 -U postgres linux_do_credit
+
+# 如果需要指定字符集,可使用
+# psql -h <主机> -p 5432 -U postgres -c "CREATE DATABASE linux_do_credit WITH ENCODING 'UTF8' LC_COLLATE='zh_CN.UTF-8' LC_CTYPE='zh_CN.UTF-8' TEMPLATE template0;"
+
+# 运行迁移(启动后端时会自动执行)
+```
+
+### 4. 启动后端
+
+```bash
+# 安装 Go 依赖
+go mod tidy
+
+# 生成 API 文档
+make swagger
+
+# 启动后端服务
+go run main.go api
+```
+
+### 5. 启动前端
+
+```bash
+cd frontend
+
+# 安装依赖
+pnpm install
+
+# 启动开发服务器
+pnpm dev
+```
+
+### 6. 访问应用
+
+- **前端界面**: http://localhost:3000
+- **API 文档**: http://localhost:8000/swagger/index.html
+- **健康检查**: http://localhost:8000/api/health
+
+## ⚙️ 配置说明
+
+### 主要配置项
+
+| 配置项 | 说明 | 示例 |
+|--------|------|------|
+| `app.addr` | 后端服务监听地址 | `:8000` |
+| `oauth2.client_id` | OAuth2 客户端 ID | `your_client_id` |
+| `database.host` | PostgreSQL 数据库地址 | `127.0.0.1` |
+| `database.port` | PostgreSQL 数据库端口 | `5432` |
+| `database.username` | PostgreSQL 数据库用户名 | `postgres` |
+| `database.password` | PostgreSQL 数据库密码 | `password` |
+| `database.database` | PostgreSQL 数据库名称 | `linux_do_credit` |
+| `database.ssl_mode` | PostgreSQL SSL 模式 | `disable` |
+| `database.application_name` | PostgreSQL 应用标识 | `credit-server` |
+| `database.search_path` | PostgreSQL 搜索路径 | `public` |
+| `database.default_query_exec_mode` | SQL 缓存模式 | `cache_statement` |
+| `redis.host` | Redis 服务器地址 | `127.0.0.1` |
+
+详细配置说明请参考 `config.example.yaml` 文件。
+
+## 🔧 开发指南
+
+### 后端开发
+
+```bash
+# 运行 API 服务器
+go run main.go api
+
+# 运行任务调度器
+go run main.go scheduler
+
+# 运行工作队列
+go run main.go worker
+
+# 生成 Swagger 文档
+make swagger
+
+# 代码格式化和检查
+make tidy
+```
+
+### 前端开发
+
+```bash
+cd frontend
+
+# 开发模式(使用 Turbopack)
+pnpm dev
+
+# 构建生产版本
+pnpm build
+
+# 启动生产服务
+pnpm start
+
+# 代码检查和格式化
+pnpm lint
+pnpm format
+```
+
+## 📚 API 文档
+
+API 文档通过 Swagger 自动生成,启动后端服务后可访问:
+
+```
+http://localhost:8000/swagger/index.html
+```
+
+## 🧪 测试
+
+```bash
+# 后端测试
+go test ./...
+
+# 前端测试
+cd frontend
+pnpm test
+```
+
+## 🚀 部署
+
+### Docker 部署
+
+```bash
+# 构建镜像
+docker build -t linux-do-credit .
+
+# 运行容器
+docker run -d -p 8000:8000 linux-do-credit
+```
+
+### 生产环境部署
+
+1. 构建前端资源:
+ ```bash
+ cd frontend && pnpm build
+ ```
+
+2. 编译后端程序:
+ ```bash
+ go build -o credit main.go
+ ```
+
+3. 配置生产环境的 `config.yaml`
+
+4. 启动服务:
+ ```bash
+ ./credit api
+ ```
+
+## 🤝 贡献指南
+
+我们欢迎社区贡献!请在提交代码前阅读:
+
+- [贡献指南](CONTRIBUTING.md)
+- [行为准则](CODE_OF_CONDUCT.md)
+- [贡献者许可协议](CLA.md)
+
+### 提交流程
+
+1. Fork 本仓库
+2. 创建特性分支 (`git checkout -b feature/your-feature`)
+3. 提交更改 (`git commit -am 'Add your feature'`)
+4. 推送到分支 (`git push origin feature/your-feature`)
+5. 创建 Pull Request
+
+## 📄 许可证
+
+本项目基于 [Apache2.0 许可证](LICENSE) 开源。
+
+## 🔗 相关链接
+
+- [Linux Do 社区](https://linux.do)
+- [问题反馈](https://github.com/linux-do/credit/issues)
+- [功能请求](https://github.com/linux-do/credit/issues/new?template=feature_request.md)
+
+## ❤️ 致谢
+
+感谢所有为本项目做出贡献的开发者和 Linux Do 社区的支持!
+
+## 📈 项目趋势
+
+[](https://star-history.com/#linux-do/credit&Date)
diff --git a/config.example.yaml b/config.example.yaml
new file mode 100644
index 00000000..91176135
--- /dev/null
+++ b/config.example.yaml
@@ -0,0 +1,164 @@
+# LINUX DO Credit Config
+# Copy to config.yaml for runtime
+
+# App
+app:
+ app_name: "linux-do-credit"
+ env: "development" # development, testing, production
+ addr: ":8000"
+ node_id: 1 # 分布式节点 ID (0-1023),不同实例必须不同
+ graceful_shutdown_timeout: 30
+ session_cookie_name: "linux_do_credit_session_id" # change this in local dev env
+ session_secret: "" # you can't change this after first time start
+ session_domain: ".linux.do"
+ session_age: 86400
+ session_secure: false
+ session_http_only: false
+ api_prefix: "/api"
+ frontend_url: "http://localhost:3000"
+ frontend_pay_url: "http://localhost:3000/paying"
+
+# OAuth2/OIDC(优先)
+oauth2:
+ client_id: ""
+ client_secret: ""
+ redirect_uri: ""
+ issuer: "https://connect.linux.do/" # OIDC Issuer URL,用于自动发现端点
+ authorization_endpoint: "https://connect.linux.do/oauth2/authorize"
+ token_endpoint: "https://connect.linux.do/oauth2/token"
+ user_endpoint: "https://connect.linux.do/api/user"
+
+# DB
+# 支持两种模式:Standalone(单节点)、Primary-Replica(读写分离)
+database:
+ enabled: true
+ host: "127.0.0.1"
+ port: 5432
+ username: "postgres"
+ password: ""
+ database: "linux_do_credit"
+ max_idle_conn: 16
+ max_open_conn: 128
+ conn_max_lifetime: 1800
+ conn_max_idle_time: 600
+ log_level: "info" # error, warn, info, debug, silent
+ ssl_mode: "disable"
+ time_zone: "Asia/Shanghai"
+ application_name: "pay-server"
+ # pgb改为true,默认false
+ prefer_simple_protocol: false
+ search_path: "public"
+ statement_cache_capacity: 256
+ default_query_exec_mode: "cache_statement"
+ slow_threshold: 200ms
+ # replicas:
+ # - host: "replica1.db.local"
+ # port: 5432
+ # - host: "replica2.db.local"
+ # port: 5432
+
+# clickhouse
+clickhouse:
+ enabled: false
+ hosts:
+ - "127.0.0.1:9000"
+ username: "default"
+ password: ""
+ database: "linux_do_credit"
+ max_idle_conn: 10
+ max_open_conn: 100
+ conn_max_lifetime: 3600
+ dial_timeout: 5
+ block_buffer_size: 10
+
+# Redis
+# 支持三种模式:Standalone(单节点)、Sentinel(高可用)、Cluster(水平扩展)
+redis:
+ enabled: true
+ addrs:
+ - "127.0.0.1:6379"
+ username: ""
+ password: ""
+ db: 0 # Cluster 模式忽略此项
+ cluster_mode: false # true 启用 Cluster 模式
+ master_name: "" # 非空启用 Sentinel 模式
+ key_prefix: "credit:"
+ pool_size: 100
+ min_idle_conn: 10
+ dial_timeout: 5
+ read_timeout: 3
+ write_timeout: 3
+ max_retries: 3
+ pool_timeout: 4
+ conn_max_idle_time: 300
+
+# Log
+log:
+ level: "info" # debug, info, warn, error, fatal, panic
+ format: "json" # text, json
+ output: "stdout" # stdout, file
+ file_path: "./logs/app.log"
+ max_size: 100
+ max_age: 30
+ max_backups: 10
+ compress: true
+
+# Scheduler
+scheduler:
+ update_user_gamification_scores_task_cron: "0 2 * * *"
+ dispute_auto_refund_dispatch_interval_seconds: 3
+ auto_refund_expired_disputes_task_cron: "0 0 * * *"
+ sync_orders_to_clickhouse_task_cron: "10 0 * * *"
+ refund_expired_red_envelopes_task_cron: "0 1 * * *"
+ cleanup_unused_uploads_task_cron: "0 */2 * * *"
+ settle_pending_payments_task_cron: "0 * * * *"
+
+# Worker
+worker:
+ concurrency: 20
+ strict_priority: false
+ queues:
+ - name: webhook
+ priority: 10
+ - name: whitelist_only
+ priority: 5
+ - name: default
+ priority: 3
+ # 积分更新速率限制:rate 次/period 秒
+ gamification_score_rate_limit:
+ rate: 1 # 允许的请求次数
+ period: 3 # 时间周期(秒)
+
+# linuxDo
+linuxDo:
+ api_key: ""
+
+# OpenAPI Risk
+openapi_risk:
+ enabled: false
+ base_url: "https://audit.example.com"
+ username: ""
+ password: ""
+ cache_ttl_seconds: 3600
+ prompt_risk_levels: []
+ block_risk_levels: []
+
+# OpenTelemetry
+otel:
+ sampling_rate: 0.1 # 采样率 0.0-1.0
+
+# S3 Compatible Storage
+# 支持 AWS S3、MinIO、Cloudflare R2、腾讯 COS 等 S3 兼容存储
+s3:
+ enabled: true
+ endpoint: "https://.r2.cloudflarestorage.com"
+ region: "auto"
+ bucket: ""
+ access_key_id: ""
+ secret_access_key: ""
+ path_style: false # MinIO 等自托管服务设为 true
+ key_prefix: "" # 对象 key 前缀,如 "uploads/",可用于分目录存储
+ cdn_url: "" # CDN 域名(如 https://cdn.example.com),为空则直接读 S3
+ local_cache:
+ enabled: false
+ cache_dir: "./s3_cache"
diff --git a/docs/docs.go b/docs/docs.go
new file mode 100644
index 00000000..6c5a06cf
--- /dev/null
+++ b/docs/docs.go
@@ -0,0 +1,2566 @@
+// Package docs Code generated by swaggo/swag. DO NOT EDIT
+package docs
+
+import "github.com/swaggo/swag"
+
+const docTemplate = `{
+ "schemes": {{ marshal .Schemes }},
+ "swagger": "2.0",
+ "info": {
+ "description": "{{escape .Description}}",
+ "title": "{{.Title}}",
+ "contact": {},
+ "version": "{{.Version}}"
+ },
+ "host": "{{.Host}}",
+ "basePath": "{{.BasePath}}",
+ "paths": {
+ "/api.php": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "name": "act",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "name": "key",
+ "in": "query",
+ "required": true
+ },
+ {
+ "maxLength": 64,
+ "minLength": 1,
+ "type": "string",
+ "name": "out_trade_no",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "pid",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/payment.QueryMerchantOrderResponse"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "退款请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.RefundOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/payment.RefundMerchantOrderResponse"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/system-configs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/system_config.CreateSystemConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/system-configs/{key}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置键",
+ "name": "key",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置键",
+ "name": "key",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/system_config.UpdateSystemConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置键",
+ "name": "key",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/tasks/dispatch": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/task.DispatchTaskRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/tasks/types": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/user-pay-configs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user_pay_config.CreateUserPayConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/user-pay-configs/{id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user_pay_config.UpdateUserPayConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/users": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "minimum": 1,
+ "type": "integer",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "maximum": 100,
+ "minimum": 1,
+ "type": "integer",
+ "name": "page_size",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "name": "user_id",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "name": "username",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/users/{id}/status": {
+ "put": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "用户ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "状态",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user.updateUserStatusRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/config/public": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "config"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/dashboard/stats/daily": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "dashboard"
+ ],
+ "summary": "获取每日收支统计",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "查询天数,最大7天",
+ "name": "days",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/dashboard/stats/top-customers": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "dashboard"
+ ],
+ "summary": "获取Top客户",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "查询天数,最大7天",
+ "name": "days",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "返回数量,最大10",
+ "name": "limit",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/dashboard/stats/user-balance": {
+ "get": {
+ "description": "统计所有用户的AvailableBalance字段",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "dashboard"
+ ],
+ "summary": "获取用户余额统计",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/util.ResponseAny"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/definitions/dashboard.UserBalanceStatsResponse"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/health": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "health"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/leaderboard": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "leaderboard"
+ ],
+ "summary": "获取排行榜列表",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "页码",
+ "name": "page",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "每页数量",
+ "name": "page_size",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/leaderboard/me": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "leaderboard"
+ ],
+ "summary": "获取当前用户排名",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/leaderboard/users/{id}": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "leaderboard"
+ ],
+ "summary": "获取指定用户排名",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "用户ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api_key.CreateAPIKeyRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys/{id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api_key.UpdateAPIKeyRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys/{id}/payment-links": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "创建支付链接请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/link.PaymentLinkRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys/{id}/payment-links/{linkId}": {
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "Payment Link ID",
+ "name": "linkId",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "更新支付链接请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/link.PaymentLinkRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "Payment Link ID",
+ "name": "linkId",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "支付订单请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.PayOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment-links/pay": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "description": "支付请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/link.PayByLinkRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment-links/{token}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "支付链接 Token",
+ "name": "token",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment/order": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "订单号",
+ "name": "order_no",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/callback": {
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/oauth.CallbackRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/login": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/logout": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/user-info": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/dispute": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dispute.CreateDisputeRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/dispute/close": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dispute.CloseDisputeRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/disputes": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/dispute.ListDisputesRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/disputes/merchant": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/dispute.ListDisputesRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/refund-review": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dispute.RefundReviewRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/transactions": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/order.TransactionListRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/payment/transfer": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "转账请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.TransferRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/claim": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "description": "领取红包请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/redenvelope.ClaimRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/covers": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "封面类型 (cover/heterotypic)",
+ "name": "type",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/create": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "description": "创建红包请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/redenvelope.CreateRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/list": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "description": "列表请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/redenvelope.ListRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/{id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "红包ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/upload/redenvelope/cover": {
+ "post": {
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "upload"
+ ],
+ "parameters": [
+ {
+ "type": "file",
+ "description": "图片文件",
+ "name": "file",
+ "in": "formData",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "封面类型 (cover/heterotypic)",
+ "name": "type",
+ "in": "formData",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/user/pay-key": {
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user.UpdatePayKeyRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/f/{id}": {
+ "get": {
+ "produces": [
+ "application/octet-stream"
+ ],
+ "tags": [
+ "upload"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Upload ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/pay/distribute": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Basic Auth (base64(client_id:client_secret))",
+ "name": "Authorization",
+ "in": "header",
+ "required": true
+ },
+ {
+ "description": "分发请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.MerchantDistributeRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/pay/submit.php": {
+ "get": {
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.CreateOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.CreateOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "api_key.CreateAPIKeyRequest": {
+ "type": "object",
+ "required": [
+ "app_homepage_url",
+ "app_name",
+ "notify_url"
+ ],
+ "properties": {
+ "app_description": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_homepage_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_name": {
+ "type": "string",
+ "maxLength": 20
+ },
+ "notify_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "public_key": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "redirect_uri": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "test_mode": {
+ "type": "boolean"
+ }
+ }
+ },
+ "api_key.UpdateAPIKeyRequest": {
+ "type": "object",
+ "properties": {
+ "app_description": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_homepage_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_name": {
+ "type": "string",
+ "maxLength": 20
+ },
+ "notify_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "public_key": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "redirect_uri": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "test_mode": {
+ "type": "boolean"
+ }
+ }
+ },
+ "dashboard.UserBalanceStatsResponse": {
+ "type": "object",
+ "properties": {
+ "avg_amount": {
+ "type": "number"
+ },
+ "max_amount": {
+ "type": "number"
+ },
+ "median_amount": {
+ "type": "number"
+ },
+ "min_amount": {
+ "type": "number"
+ },
+ "std_dev": {
+ "type": "number"
+ },
+ "total_amount": {
+ "type": "number"
+ },
+ "total_count": {
+ "type": "integer"
+ }
+ }
+ },
+ "dispute.CloseDisputeRequest": {
+ "type": "object",
+ "required": [
+ "dispute_id"
+ ],
+ "properties": {
+ "dispute_id": {
+ "type": "string",
+ "example": "0"
+ }
+ }
+ },
+ "dispute.CreateDisputeRequest": {
+ "type": "object",
+ "required": [
+ "order_id",
+ "reason"
+ ],
+ "properties": {
+ "order_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "reason": {
+ "type": "string",
+ "maxLength": 100
+ }
+ }
+ },
+ "dispute.ListDisputesRequest": {
+ "type": "object",
+ "properties": {
+ "dispute_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "page_size": {
+ "type": "integer",
+ "maximum": 100,
+ "minimum": 1
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "disputing",
+ "refund",
+ "closed"
+ ]
+ }
+ }
+ },
+ "dispute.RefundReviewRequest": {
+ "type": "object",
+ "required": [
+ "dispute_id",
+ "status"
+ ],
+ "properties": {
+ "dispute_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "reason": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "refund",
+ "closed"
+ ]
+ }
+ }
+ },
+ "link.PayByLinkRequest": {
+ "type": "object",
+ "required": [
+ "pay_key",
+ "token"
+ ],
+ "properties": {
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "token": {
+ "type": "string"
+ }
+ }
+ },
+ "link.PaymentLinkRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "product_name"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "product_name": {
+ "type": "string",
+ "maxLength": 30
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "total_limit": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "user_limit": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ },
+ "model.OrderTransferStatus": {
+ "type": "string",
+ "enum": [
+ "pending",
+ "completed"
+ ],
+ "x-enum-varnames": [
+ "OrderTransferStatusPending",
+ "OrderTransferStatusCompleted"
+ ]
+ },
+ "model.PayLevel": {
+ "type": "integer",
+ "format": "int32",
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3
+ ],
+ "x-enum-varnames": [
+ "PayLevelFree",
+ "PayLevelBasic",
+ "PayLevelStandard",
+ "PayLevelPremium"
+ ]
+ },
+ "model.RedEnvelopeType": {
+ "type": "string",
+ "enum": [
+ "fixed",
+ "random"
+ ],
+ "x-enum-varnames": [
+ "RedEnvelopeTypeFixed",
+ "RedEnvelopeTypeRandom"
+ ]
+ },
+ "oauth.CallbackRequest": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string"
+ }
+ }
+ },
+ "order.TransactionListRequest": {
+ "type": "object",
+ "properties": {
+ "client_id": {
+ "type": "string"
+ },
+ "endTime": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string",
+ "example": "0"
+ },
+ "order_name": {
+ "type": "string"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "page_size": {
+ "type": "integer",
+ "maximum": 100,
+ "minimum": 1
+ },
+ "payee_transfer_status": {
+ "enum": [
+ "pending",
+ "completed"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/model.OrderTransferStatus"
+ }
+ ]
+ },
+ "payee_username": {
+ "type": "string"
+ },
+ "payer_username": {
+ "type": "string"
+ },
+ "startTime": {
+ "type": "string"
+ },
+ "statuses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "types": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "payment.CreateOrderRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "order_name"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "merchant_order_no": {
+ "type": "string",
+ "maxLength": 64,
+ "minLength": 1
+ },
+ "notify_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "order_name": {
+ "type": "string",
+ "maxLength": 64
+ },
+ "payment_type": {
+ "type": "string"
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "return_url": {
+ "type": "string",
+ "maxLength": 100
+ }
+ }
+ },
+ "payment.MerchantDistributeRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "user_id",
+ "username"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "out_trade_no": {
+ "type": "string",
+ "maxLength": 64,
+ "minLength": 1
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "user_id": {
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ }
+ }
+ },
+ "payment.PayOrderRequest": {
+ "type": "object",
+ "required": [
+ "order_no",
+ "pay_key"
+ ],
+ "properties": {
+ "order_no": {
+ "type": "string"
+ },
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ }
+ }
+ },
+ "payment.QueryMerchantOrderResponse": {
+ "type": "object",
+ "properties": {
+ "addtime": {
+ "type": "string",
+ "example": "2023-12-08 12:00:00"
+ },
+ "code": {
+ "type": "integer",
+ "example": 1
+ },
+ "endtime": {
+ "type": "string",
+ "example": "2023-12-08 12:05:00"
+ },
+ "money": {
+ "type": "string",
+ "example": "10.00"
+ },
+ "msg": {
+ "type": "string",
+ "example": "查询订单号成功!"
+ },
+ "name": {
+ "type": "string",
+ "example": "商品名称"
+ },
+ "out_trade_no": {
+ "type": "string",
+ "example": "M202312080001"
+ },
+ "pid": {
+ "type": "string",
+ "example": "1001"
+ },
+ "status": {
+ "type": "integer",
+ "example": 1
+ },
+ "trade_no": {
+ "type": "string",
+ "example": "123456"
+ },
+ "type": {
+ "type": "string",
+ "example": "epay"
+ }
+ }
+ },
+ "payment.RefundMerchantOrderResponse": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "example": 1
+ },
+ "msg": {
+ "type": "string",
+ "example": "退款成功"
+ }
+ }
+ },
+ "payment.RefundOrderRequest": {
+ "type": "object",
+ "required": [
+ "key",
+ "money",
+ "pid",
+ "trade_no"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "money": {
+ "type": "number"
+ },
+ "out_trade_no": {
+ "type": "string"
+ },
+ "pid": {
+ "type": "string"
+ },
+ "trade_no": {
+ "type": "integer"
+ }
+ }
+ },
+ "payment.TransferRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "pay_key",
+ "recipient_id",
+ "recipient_username"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ },
+ "recipient_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "recipient_username": {
+ "type": "string"
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ }
+ }
+ },
+ "redenvelope.ClaimRequest": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "example": "0"
+ }
+ }
+ },
+ "redenvelope.CreateRequest": {
+ "type": "object",
+ "required": [
+ "pay_key",
+ "total_amount",
+ "total_count",
+ "type"
+ ],
+ "properties": {
+ "cover_upload_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "greeting": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "heterotypic_upload_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "pay_key": {
+ "type": "string",
+ "maxLength": 10
+ },
+ "total_amount": {
+ "type": "number"
+ },
+ "total_count": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "type": {
+ "enum": [
+ "fixed",
+ "random"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/model.RedEnvelopeType"
+ }
+ ]
+ }
+ }
+ },
+ "redenvelope.ListRequest": {
+ "type": "object",
+ "required": [
+ "page",
+ "page_size"
+ ],
+ "properties": {
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "page_size": {
+ "type": "integer",
+ "maximum": 100,
+ "minimum": 1
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "sent",
+ "received"
+ ]
+ }
+ }
+ },
+ "system_config.CreateSystemConfigRequest": {
+ "type": "object",
+ "required": [
+ "key",
+ "value"
+ ],
+ "properties": {
+ "description": {
+ "type": "string",
+ "maxLength": 255
+ },
+ "key": {
+ "type": "string",
+ "maxLength": 64
+ },
+ "value": {
+ "type": "string",
+ "maxLength": 255
+ }
+ }
+ },
+ "system_config.UpdateSystemConfigRequest": {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "description": {
+ "type": "string",
+ "maxLength": 255
+ },
+ "value": {
+ "type": "string",
+ "maxLength": 255
+ }
+ }
+ },
+ "task.DispatchTaskRequest": {
+ "type": "object",
+ "required": [
+ "task_type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string"
+ },
+ "start_time": {
+ "type": "string"
+ },
+ "task_type": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "integer"
+ }
+ }
+ },
+ "user.UpdatePayKeyRequest": {
+ "type": "object",
+ "required": [
+ "pay_key"
+ ],
+ "properties": {
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ }
+ }
+ },
+ "user.updateUserStatusRequest": {
+ "type": "object",
+ "properties": {
+ "is_active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "user_pay_config.CreateUserPayConfigRequest": {
+ "type": "object",
+ "required": [
+ "distribute_rate",
+ "fee_rate",
+ "score_rate"
+ ],
+ "properties": {
+ "daily_limit": {
+ "type": "integer"
+ },
+ "distribute_rate": {
+ "type": "number"
+ },
+ "fee_rate": {
+ "type": "number"
+ },
+ "level": {
+ "$ref": "#/definitions/model.PayLevel"
+ },
+ "max_score": {
+ "type": "integer"
+ },
+ "min_score": {
+ "type": "integer"
+ },
+ "score_rate": {
+ "type": "number"
+ }
+ }
+ },
+ "user_pay_config.UpdateUserPayConfigRequest": {
+ "type": "object",
+ "required": [
+ "distribute_rate",
+ "fee_rate",
+ "score_rate"
+ ],
+ "properties": {
+ "daily_limit": {
+ "type": "integer"
+ },
+ "distribute_rate": {
+ "type": "number"
+ },
+ "fee_rate": {
+ "type": "number"
+ },
+ "max_score": {
+ "type": "integer"
+ },
+ "min_score": {
+ "type": "integer"
+ },
+ "score_rate": {
+ "type": "number"
+ }
+ }
+ },
+ "util.ResponseAny": {
+ "type": "object",
+ "properties": {
+ "data": {},
+ "error_msg": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ }
+ }
+}`
+
+// SwaggerInfo holds exported Swagger Info so clients can modify it
+var SwaggerInfo = &swag.Spec{
+ Version: "1.0.0",
+ Host: "",
+ BasePath: "",
+ Schemes: []string{},
+ Title: "LINUX DO Credit",
+ Description: "",
+ InfoInstanceName: "swagger",
+ SwaggerTemplate: docTemplate,
+ LeftDelim: "{{",
+ RightDelim: "}}",
+}
+
+func init() {
+ swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
+}
diff --git a/docs/swagger.json b/docs/swagger.json
new file mode 100644
index 00000000..98c4939f
--- /dev/null
+++ b/docs/swagger.json
@@ -0,0 +1,2539 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "LINUX DO Credit",
+ "contact": {},
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/api.php": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "name": "act",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "name": "key",
+ "in": "query",
+ "required": true
+ },
+ {
+ "maxLength": 64,
+ "minLength": 1,
+ "type": "string",
+ "name": "out_trade_no",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "string",
+ "name": "pid",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/payment.QueryMerchantOrderResponse"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "退款请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.RefundOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/payment.RefundMerchantOrderResponse"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/system-configs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/system_config.CreateSystemConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/system-configs/{key}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置键",
+ "name": "key",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置键",
+ "name": "key",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/system_config.UpdateSystemConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置键",
+ "name": "key",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/tasks/dispatch": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/task.DispatchTaskRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/tasks/types": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/user-pay-configs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user_pay_config.CreateUserPayConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/user-pay-configs/{id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user_pay_config.UpdateUserPayConfigRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "配置ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/users": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "minimum": 1,
+ "type": "integer",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "maximum": 100,
+ "minimum": 1,
+ "type": "integer",
+ "name": "page_size",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "name": "user_id",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "name": "username",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/admin/users/{id}/status": {
+ "put": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "用户ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "状态",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user.updateUserStatusRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/config/public": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "config"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/dashboard/stats/daily": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "dashboard"
+ ],
+ "summary": "获取每日收支统计",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "查询天数,最大7天",
+ "name": "days",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/dashboard/stats/top-customers": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "dashboard"
+ ],
+ "summary": "获取Top客户",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "查询天数,最大7天",
+ "name": "days",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "返回数量,最大10",
+ "name": "limit",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/dashboard/stats/user-balance": {
+ "get": {
+ "description": "统计所有用户的AvailableBalance字段",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "dashboard"
+ ],
+ "summary": "获取用户余额统计",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/util.ResponseAny"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/definitions/dashboard.UserBalanceStatsResponse"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/health": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "health"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/leaderboard": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "leaderboard"
+ ],
+ "summary": "获取排行榜列表",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "页码",
+ "name": "page",
+ "in": "query",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "每页数量",
+ "name": "page_size",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/leaderboard/me": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "leaderboard"
+ ],
+ "summary": "获取当前用户排名",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/leaderboard/users/{id}": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "leaderboard"
+ ],
+ "summary": "获取指定用户排名",
+ "parameters": [
+ {
+ "type": "integer",
+ "description": "用户ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api_key.CreateAPIKeyRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys/{id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api_key.UpdateAPIKeyRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys/{id}/payment-links": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "创建支付链接请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/link.PaymentLinkRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/api-keys/{id}/payment-links/{linkId}": {
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "Payment Link ID",
+ "name": "linkId",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "更新支付链接请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/link.PaymentLinkRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "API Key ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "Payment Link ID",
+ "name": "linkId",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "支付订单请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.PayOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment-links/pay": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "description": "支付请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/link.PayByLinkRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment-links/{token}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "merchant"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "支付链接 Token",
+ "name": "token",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/merchant/payment/order": {
+ "get": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "订单号",
+ "name": "order_no",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/callback": {
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/oauth.CallbackRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/login": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/logout": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/oauth/user-info": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "oauth"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/dispute": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dispute.CreateDisputeRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/dispute/close": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dispute.CloseDisputeRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/disputes": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/dispute.ListDisputesRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/disputes/merchant": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/dispute.ListDisputesRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/refund-review": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/dispute.RefundReviewRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/order/transactions": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "order"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/order.TransactionListRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/payment/transfer": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "转账请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.TransferRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/claim": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "description": "领取红包请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/redenvelope.ClaimRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/covers": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "封面类型 (cover/heterotypic)",
+ "name": "type",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/create": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "description": "创建红包请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/redenvelope.CreateRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/list": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "description": "列表请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/redenvelope.ListRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/redenvelope/{id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "redenvelope"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "红包ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/upload/redenvelope/cover": {
+ "post": {
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "upload"
+ ],
+ "parameters": [
+ {
+ "type": "file",
+ "description": "图片文件",
+ "name": "file",
+ "in": "formData",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "封面类型 (cover/heterotypic)",
+ "name": "type",
+ "in": "formData",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/user/pay-key": {
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/user.UpdatePayKeyRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/f/{id}": {
+ "get": {
+ "produces": [
+ "application/octet-stream"
+ ],
+ "tags": [
+ "upload"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Upload ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/pay/distribute": {
+ "post": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Basic Auth (base64(client_id:client_secret))",
+ "name": "Authorization",
+ "in": "header",
+ "required": true
+ },
+ {
+ "description": "分发请求",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.MerchantDistributeRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ },
+ "/pay/submit.php": {
+ "get": {
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.CreateOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ },
+ "post": {
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "payment"
+ ],
+ "parameters": [
+ {
+ "description": "request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/payment.CreateOrderRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/util.ResponseAny"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "api_key.CreateAPIKeyRequest": {
+ "type": "object",
+ "required": [
+ "app_homepage_url",
+ "app_name",
+ "notify_url"
+ ],
+ "properties": {
+ "app_description": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_homepage_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_name": {
+ "type": "string",
+ "maxLength": 20
+ },
+ "notify_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "public_key": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "redirect_uri": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "test_mode": {
+ "type": "boolean"
+ }
+ }
+ },
+ "api_key.UpdateAPIKeyRequest": {
+ "type": "object",
+ "properties": {
+ "app_description": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_homepage_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "app_name": {
+ "type": "string",
+ "maxLength": 20
+ },
+ "notify_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "public_key": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "redirect_uri": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "test_mode": {
+ "type": "boolean"
+ }
+ }
+ },
+ "dashboard.UserBalanceStatsResponse": {
+ "type": "object",
+ "properties": {
+ "avg_amount": {
+ "type": "number"
+ },
+ "max_amount": {
+ "type": "number"
+ },
+ "median_amount": {
+ "type": "number"
+ },
+ "min_amount": {
+ "type": "number"
+ },
+ "std_dev": {
+ "type": "number"
+ },
+ "total_amount": {
+ "type": "number"
+ },
+ "total_count": {
+ "type": "integer"
+ }
+ }
+ },
+ "dispute.CloseDisputeRequest": {
+ "type": "object",
+ "required": [
+ "dispute_id"
+ ],
+ "properties": {
+ "dispute_id": {
+ "type": "string",
+ "example": "0"
+ }
+ }
+ },
+ "dispute.CreateDisputeRequest": {
+ "type": "object",
+ "required": [
+ "order_id",
+ "reason"
+ ],
+ "properties": {
+ "order_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "reason": {
+ "type": "string",
+ "maxLength": 100
+ }
+ }
+ },
+ "dispute.ListDisputesRequest": {
+ "type": "object",
+ "properties": {
+ "dispute_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "page_size": {
+ "type": "integer",
+ "maximum": 100,
+ "minimum": 1
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "disputing",
+ "refund",
+ "closed"
+ ]
+ }
+ }
+ },
+ "dispute.RefundReviewRequest": {
+ "type": "object",
+ "required": [
+ "dispute_id",
+ "status"
+ ],
+ "properties": {
+ "dispute_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "reason": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "refund",
+ "closed"
+ ]
+ }
+ }
+ },
+ "link.PayByLinkRequest": {
+ "type": "object",
+ "required": [
+ "pay_key",
+ "token"
+ ],
+ "properties": {
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "token": {
+ "type": "string"
+ }
+ }
+ },
+ "link.PaymentLinkRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "product_name"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "product_name": {
+ "type": "string",
+ "maxLength": 30
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "total_limit": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "user_limit": {
+ "type": "integer",
+ "minimum": 1
+ }
+ }
+ },
+ "model.OrderTransferStatus": {
+ "type": "string",
+ "enum": [
+ "pending",
+ "completed"
+ ],
+ "x-enum-varnames": [
+ "OrderTransferStatusPending",
+ "OrderTransferStatusCompleted"
+ ]
+ },
+ "model.PayLevel": {
+ "type": "integer",
+ "format": "int32",
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3
+ ],
+ "x-enum-varnames": [
+ "PayLevelFree",
+ "PayLevelBasic",
+ "PayLevelStandard",
+ "PayLevelPremium"
+ ]
+ },
+ "model.RedEnvelopeType": {
+ "type": "string",
+ "enum": [
+ "fixed",
+ "random"
+ ],
+ "x-enum-varnames": [
+ "RedEnvelopeTypeFixed",
+ "RedEnvelopeTypeRandom"
+ ]
+ },
+ "oauth.CallbackRequest": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string"
+ }
+ }
+ },
+ "order.TransactionListRequest": {
+ "type": "object",
+ "properties": {
+ "client_id": {
+ "type": "string"
+ },
+ "endTime": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string",
+ "example": "0"
+ },
+ "order_name": {
+ "type": "string"
+ },
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "page_size": {
+ "type": "integer",
+ "maximum": 100,
+ "minimum": 1
+ },
+ "payee_transfer_status": {
+ "enum": [
+ "pending",
+ "completed"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/model.OrderTransferStatus"
+ }
+ ]
+ },
+ "payee_username": {
+ "type": "string"
+ },
+ "payer_username": {
+ "type": "string"
+ },
+ "startTime": {
+ "type": "string"
+ },
+ "statuses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "types": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "payment.CreateOrderRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "order_name"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "merchant_order_no": {
+ "type": "string",
+ "maxLength": 64,
+ "minLength": 1
+ },
+ "notify_url": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "order_name": {
+ "type": "string",
+ "maxLength": 64
+ },
+ "payment_type": {
+ "type": "string"
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "return_url": {
+ "type": "string",
+ "maxLength": 100
+ }
+ }
+ },
+ "payment.MerchantDistributeRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "user_id",
+ "username"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "out_trade_no": {
+ "type": "string",
+ "maxLength": 64,
+ "minLength": 1
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "user_id": {
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ }
+ }
+ },
+ "payment.PayOrderRequest": {
+ "type": "object",
+ "required": [
+ "order_no",
+ "pay_key"
+ ],
+ "properties": {
+ "order_no": {
+ "type": "string"
+ },
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ }
+ }
+ },
+ "payment.QueryMerchantOrderResponse": {
+ "type": "object",
+ "properties": {
+ "addtime": {
+ "type": "string",
+ "example": "2023-12-08 12:00:00"
+ },
+ "code": {
+ "type": "integer",
+ "example": 1
+ },
+ "endtime": {
+ "type": "string",
+ "example": "2023-12-08 12:05:00"
+ },
+ "money": {
+ "type": "string",
+ "example": "10.00"
+ },
+ "msg": {
+ "type": "string",
+ "example": "查询订单号成功!"
+ },
+ "name": {
+ "type": "string",
+ "example": "商品名称"
+ },
+ "out_trade_no": {
+ "type": "string",
+ "example": "M202312080001"
+ },
+ "pid": {
+ "type": "string",
+ "example": "1001"
+ },
+ "status": {
+ "type": "integer",
+ "example": 1
+ },
+ "trade_no": {
+ "type": "string",
+ "example": "123456"
+ },
+ "type": {
+ "type": "string",
+ "example": "epay"
+ }
+ }
+ },
+ "payment.RefundMerchantOrderResponse": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "example": 1
+ },
+ "msg": {
+ "type": "string",
+ "example": "退款成功"
+ }
+ }
+ },
+ "payment.RefundOrderRequest": {
+ "type": "object",
+ "required": [
+ "key",
+ "money",
+ "pid",
+ "trade_no"
+ ],
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "money": {
+ "type": "number"
+ },
+ "out_trade_no": {
+ "type": "string"
+ },
+ "pid": {
+ "type": "string"
+ },
+ "trade_no": {
+ "type": "integer"
+ }
+ }
+ },
+ "payment.TransferRequest": {
+ "type": "object",
+ "required": [
+ "amount",
+ "pay_key",
+ "recipient_id",
+ "recipient_username"
+ ],
+ "properties": {
+ "amount": {
+ "type": "number"
+ },
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ },
+ "recipient_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "recipient_username": {
+ "type": "string"
+ },
+ "remark": {
+ "type": "string",
+ "maxLength": 100
+ }
+ }
+ },
+ "redenvelope.ClaimRequest": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "example": "0"
+ }
+ }
+ },
+ "redenvelope.CreateRequest": {
+ "type": "object",
+ "required": [
+ "pay_key",
+ "total_amount",
+ "total_count",
+ "type"
+ ],
+ "properties": {
+ "cover_upload_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "greeting": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "heterotypic_upload_id": {
+ "type": "string",
+ "example": "0"
+ },
+ "pay_key": {
+ "type": "string",
+ "maxLength": 10
+ },
+ "total_amount": {
+ "type": "number"
+ },
+ "total_count": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "type": {
+ "enum": [
+ "fixed",
+ "random"
+ ],
+ "allOf": [
+ {
+ "$ref": "#/definitions/model.RedEnvelopeType"
+ }
+ ]
+ }
+ }
+ },
+ "redenvelope.ListRequest": {
+ "type": "object",
+ "required": [
+ "page",
+ "page_size"
+ ],
+ "properties": {
+ "page": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "page_size": {
+ "type": "integer",
+ "maximum": 100,
+ "minimum": 1
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "sent",
+ "received"
+ ]
+ }
+ }
+ },
+ "system_config.CreateSystemConfigRequest": {
+ "type": "object",
+ "required": [
+ "key",
+ "value"
+ ],
+ "properties": {
+ "description": {
+ "type": "string",
+ "maxLength": 255
+ },
+ "key": {
+ "type": "string",
+ "maxLength": 64
+ },
+ "value": {
+ "type": "string",
+ "maxLength": 255
+ }
+ }
+ },
+ "system_config.UpdateSystemConfigRequest": {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "description": {
+ "type": "string",
+ "maxLength": 255
+ },
+ "value": {
+ "type": "string",
+ "maxLength": 255
+ }
+ }
+ },
+ "task.DispatchTaskRequest": {
+ "type": "object",
+ "required": [
+ "task_type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string"
+ },
+ "start_time": {
+ "type": "string"
+ },
+ "task_type": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "integer"
+ }
+ }
+ },
+ "user.UpdatePayKeyRequest": {
+ "type": "object",
+ "required": [
+ "pay_key"
+ ],
+ "properties": {
+ "pay_key": {
+ "type": "string",
+ "maxLength": 6
+ }
+ }
+ },
+ "user.updateUserStatusRequest": {
+ "type": "object",
+ "properties": {
+ "is_active": {
+ "type": "boolean"
+ }
+ }
+ },
+ "user_pay_config.CreateUserPayConfigRequest": {
+ "type": "object",
+ "required": [
+ "distribute_rate",
+ "fee_rate",
+ "score_rate"
+ ],
+ "properties": {
+ "daily_limit": {
+ "type": "integer"
+ },
+ "distribute_rate": {
+ "type": "number"
+ },
+ "fee_rate": {
+ "type": "number"
+ },
+ "level": {
+ "$ref": "#/definitions/model.PayLevel"
+ },
+ "max_score": {
+ "type": "integer"
+ },
+ "min_score": {
+ "type": "integer"
+ },
+ "score_rate": {
+ "type": "number"
+ }
+ }
+ },
+ "user_pay_config.UpdateUserPayConfigRequest": {
+ "type": "object",
+ "required": [
+ "distribute_rate",
+ "fee_rate",
+ "score_rate"
+ ],
+ "properties": {
+ "daily_limit": {
+ "type": "integer"
+ },
+ "distribute_rate": {
+ "type": "number"
+ },
+ "fee_rate": {
+ "type": "number"
+ },
+ "max_score": {
+ "type": "integer"
+ },
+ "min_score": {
+ "type": "integer"
+ },
+ "score_rate": {
+ "type": "number"
+ }
+ }
+ },
+ "util.ResponseAny": {
+ "type": "object",
+ "properties": {
+ "data": {},
+ "error_msg": {
+ "type": "string",
+ "example": ""
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
new file mode 100644
index 00000000..d6268ff4
--- /dev/null
+++ b/docs/swagger.yaml
@@ -0,0 +1,1658 @@
+definitions:
+ api_key.CreateAPIKeyRequest:
+ properties:
+ app_description:
+ maxLength: 100
+ type: string
+ app_homepage_url:
+ maxLength: 100
+ type: string
+ app_name:
+ maxLength: 20
+ type: string
+ notify_url:
+ maxLength: 100
+ type: string
+ public_key:
+ maxLength: 100
+ type: string
+ redirect_uri:
+ maxLength: 100
+ type: string
+ test_mode:
+ type: boolean
+ required:
+ - app_homepage_url
+ - app_name
+ - notify_url
+ type: object
+ api_key.UpdateAPIKeyRequest:
+ properties:
+ app_description:
+ maxLength: 100
+ type: string
+ app_homepage_url:
+ maxLength: 100
+ type: string
+ app_name:
+ maxLength: 20
+ type: string
+ notify_url:
+ maxLength: 100
+ type: string
+ public_key:
+ maxLength: 100
+ type: string
+ redirect_uri:
+ maxLength: 100
+ type: string
+ test_mode:
+ type: boolean
+ type: object
+ dashboard.UserBalanceStatsResponse:
+ properties:
+ avg_amount:
+ type: number
+ max_amount:
+ type: number
+ median_amount:
+ type: number
+ min_amount:
+ type: number
+ std_dev:
+ type: number
+ total_amount:
+ type: number
+ total_count:
+ type: integer
+ type: object
+ dispute.CloseDisputeRequest:
+ properties:
+ dispute_id:
+ example: "0"
+ type: string
+ required:
+ - dispute_id
+ type: object
+ dispute.CreateDisputeRequest:
+ properties:
+ order_id:
+ example: "0"
+ type: string
+ reason:
+ maxLength: 100
+ type: string
+ required:
+ - order_id
+ - reason
+ type: object
+ dispute.ListDisputesRequest:
+ properties:
+ dispute_id:
+ example: "0"
+ type: string
+ page:
+ minimum: 1
+ type: integer
+ page_size:
+ maximum: 100
+ minimum: 1
+ type: integer
+ status:
+ enum:
+ - disputing
+ - refund
+ - closed
+ type: string
+ type: object
+ dispute.RefundReviewRequest:
+ properties:
+ dispute_id:
+ example: "0"
+ type: string
+ reason:
+ maxLength: 100
+ type: string
+ status:
+ enum:
+ - refund
+ - closed
+ type: string
+ required:
+ - dispute_id
+ - status
+ type: object
+ link.PayByLinkRequest:
+ properties:
+ pay_key:
+ maxLength: 6
+ type: string
+ remark:
+ maxLength: 100
+ type: string
+ token:
+ type: string
+ required:
+ - pay_key
+ - token
+ type: object
+ link.PaymentLinkRequest:
+ properties:
+ amount:
+ type: number
+ product_name:
+ maxLength: 30
+ type: string
+ remark:
+ maxLength: 100
+ type: string
+ total_limit:
+ minimum: 1
+ type: integer
+ user_limit:
+ minimum: 1
+ type: integer
+ required:
+ - amount
+ - product_name
+ type: object
+ model.OrderTransferStatus:
+ enum:
+ - pending
+ - completed
+ type: string
+ x-enum-varnames:
+ - OrderTransferStatusPending
+ - OrderTransferStatusCompleted
+ model.PayLevel:
+ enum:
+ - 0
+ - 1
+ - 2
+ - 3
+ format: int32
+ type: integer
+ x-enum-varnames:
+ - PayLevelFree
+ - PayLevelBasic
+ - PayLevelStandard
+ - PayLevelPremium
+ model.RedEnvelopeType:
+ enum:
+ - fixed
+ - random
+ type: string
+ x-enum-varnames:
+ - RedEnvelopeTypeFixed
+ - RedEnvelopeTypeRandom
+ oauth.CallbackRequest:
+ properties:
+ code:
+ type: string
+ state:
+ type: string
+ type: object
+ order.TransactionListRequest:
+ properties:
+ client_id:
+ type: string
+ endTime:
+ type: string
+ id:
+ example: "0"
+ type: string
+ order_name:
+ type: string
+ page:
+ minimum: 1
+ type: integer
+ page_size:
+ maximum: 100
+ minimum: 1
+ type: integer
+ payee_transfer_status:
+ allOf:
+ - $ref: '#/definitions/model.OrderTransferStatus'
+ enum:
+ - pending
+ - completed
+ payee_username:
+ type: string
+ payer_username:
+ type: string
+ startTime:
+ type: string
+ statuses:
+ items:
+ type: string
+ type: array
+ types:
+ items:
+ type: string
+ type: array
+ type: object
+ payment.CreateOrderRequest:
+ properties:
+ amount:
+ type: number
+ merchant_order_no:
+ maxLength: 64
+ minLength: 1
+ type: string
+ notify_url:
+ maxLength: 100
+ type: string
+ order_name:
+ maxLength: 64
+ type: string
+ payment_type:
+ type: string
+ remark:
+ maxLength: 100
+ type: string
+ return_url:
+ maxLength: 100
+ type: string
+ required:
+ - amount
+ - order_name
+ type: object
+ payment.MerchantDistributeRequest:
+ properties:
+ amount:
+ type: number
+ out_trade_no:
+ maxLength: 64
+ minLength: 1
+ type: string
+ remark:
+ maxLength: 100
+ type: string
+ user_id:
+ type: integer
+ username:
+ type: string
+ required:
+ - amount
+ - user_id
+ - username
+ type: object
+ payment.PayOrderRequest:
+ properties:
+ order_no:
+ type: string
+ pay_key:
+ maxLength: 6
+ type: string
+ required:
+ - order_no
+ - pay_key
+ type: object
+ payment.QueryMerchantOrderResponse:
+ properties:
+ addtime:
+ example: "2023-12-08 12:00:00"
+ type: string
+ code:
+ example: 1
+ type: integer
+ endtime:
+ example: "2023-12-08 12:05:00"
+ type: string
+ money:
+ example: "10.00"
+ type: string
+ msg:
+ example: 查询订单号成功!
+ type: string
+ name:
+ example: 商品名称
+ type: string
+ out_trade_no:
+ example: M202312080001
+ type: string
+ pid:
+ example: "1001"
+ type: string
+ status:
+ example: 1
+ type: integer
+ trade_no:
+ example: "123456"
+ type: string
+ type:
+ example: epay
+ type: string
+ type: object
+ payment.RefundMerchantOrderResponse:
+ properties:
+ code:
+ example: 1
+ type: integer
+ msg:
+ example: 退款成功
+ type: string
+ type: object
+ payment.RefundOrderRequest:
+ properties:
+ key:
+ type: string
+ money:
+ type: number
+ out_trade_no:
+ type: string
+ pid:
+ type: string
+ trade_no:
+ type: integer
+ required:
+ - key
+ - money
+ - pid
+ - trade_no
+ type: object
+ payment.TransferRequest:
+ properties:
+ amount:
+ type: number
+ pay_key:
+ maxLength: 6
+ type: string
+ recipient_id:
+ example: "0"
+ type: string
+ recipient_username:
+ type: string
+ remark:
+ maxLength: 100
+ type: string
+ required:
+ - amount
+ - pay_key
+ - recipient_id
+ - recipient_username
+ type: object
+ redenvelope.ClaimRequest:
+ properties:
+ id:
+ example: "0"
+ type: string
+ required:
+ - id
+ type: object
+ redenvelope.CreateRequest:
+ properties:
+ cover_upload_id:
+ example: "0"
+ type: string
+ greeting:
+ maxLength: 100
+ type: string
+ heterotypic_upload_id:
+ example: "0"
+ type: string
+ pay_key:
+ maxLength: 10
+ type: string
+ total_amount:
+ type: number
+ total_count:
+ minimum: 1
+ type: integer
+ type:
+ allOf:
+ - $ref: '#/definitions/model.RedEnvelopeType'
+ enum:
+ - fixed
+ - random
+ required:
+ - pay_key
+ - total_amount
+ - total_count
+ - type
+ type: object
+ redenvelope.ListRequest:
+ properties:
+ page:
+ minimum: 1
+ type: integer
+ page_size:
+ maximum: 100
+ minimum: 1
+ type: integer
+ type:
+ enum:
+ - sent
+ - received
+ type: string
+ required:
+ - page
+ - page_size
+ type: object
+ system_config.CreateSystemConfigRequest:
+ properties:
+ description:
+ maxLength: 255
+ type: string
+ key:
+ maxLength: 64
+ type: string
+ value:
+ maxLength: 255
+ type: string
+ required:
+ - key
+ - value
+ type: object
+ system_config.UpdateSystemConfigRequest:
+ properties:
+ description:
+ maxLength: 255
+ type: string
+ value:
+ maxLength: 255
+ type: string
+ required:
+ - value
+ type: object
+ task.DispatchTaskRequest:
+ properties:
+ end_time:
+ type: string
+ start_time:
+ type: string
+ task_type:
+ type: string
+ user_id:
+ type: integer
+ required:
+ - task_type
+ type: object
+ user.UpdatePayKeyRequest:
+ properties:
+ pay_key:
+ maxLength: 6
+ type: string
+ required:
+ - pay_key
+ type: object
+ user.updateUserStatusRequest:
+ properties:
+ is_active:
+ type: boolean
+ type: object
+ user_pay_config.CreateUserPayConfigRequest:
+ properties:
+ daily_limit:
+ type: integer
+ distribute_rate:
+ type: number
+ fee_rate:
+ type: number
+ level:
+ $ref: '#/definitions/model.PayLevel'
+ max_score:
+ type: integer
+ min_score:
+ type: integer
+ score_rate:
+ type: number
+ required:
+ - distribute_rate
+ - fee_rate
+ - score_rate
+ type: object
+ user_pay_config.UpdateUserPayConfigRequest:
+ properties:
+ daily_limit:
+ type: integer
+ distribute_rate:
+ type: number
+ fee_rate:
+ type: number
+ max_score:
+ type: integer
+ min_score:
+ type: integer
+ score_rate:
+ type: number
+ required:
+ - distribute_rate
+ - fee_rate
+ - score_rate
+ type: object
+ util.ResponseAny:
+ properties:
+ data: {}
+ error_msg:
+ example: ""
+ type: string
+ type: object
+info:
+ contact: {}
+ title: LINUX DO Credit
+ version: 1.0.0
+paths:
+ /api.php:
+ get:
+ consumes:
+ - application/json
+ parameters:
+ - in: query
+ name: act
+ type: string
+ - in: query
+ name: key
+ required: true
+ type: string
+ - in: query
+ maxLength: 64
+ minLength: 1
+ name: out_trade_no
+ required: true
+ type: string
+ - in: query
+ name: pid
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/payment.QueryMerchantOrderResponse'
+ tags:
+ - payment
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 退款请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/payment.RefundOrderRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/payment.RefundMerchantOrderResponse'
+ tags:
+ - payment
+ /api/v1/admin/system-configs:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/system_config.CreateSystemConfigRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/system-configs/{key}:
+ delete:
+ parameters:
+ - description: 配置键
+ in: path
+ name: key
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ get:
+ parameters:
+ - description: 配置键
+ in: path
+ name: key
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ put:
+ consumes:
+ - application/json
+ parameters:
+ - description: 配置键
+ in: path
+ name: key
+ required: true
+ type: string
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/system_config.UpdateSystemConfigRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/tasks/dispatch:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/task.DispatchTaskRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/tasks/types:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/user-pay-configs:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/user_pay_config.CreateUserPayConfigRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/user-pay-configs/{id}:
+ delete:
+ parameters:
+ - description: 配置ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ get:
+ parameters:
+ - description: 配置ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ put:
+ consumes:
+ - application/json
+ parameters:
+ - description: 配置ID
+ in: path
+ name: id
+ required: true
+ type: string
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/user_pay_config.UpdateUserPayConfigRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/users:
+ get:
+ parameters:
+ - in: query
+ minimum: 1
+ name: page
+ type: integer
+ - in: query
+ maximum: 100
+ minimum: 1
+ name: page_size
+ type: integer
+ - in: query
+ name: user_id
+ type: integer
+ - in: query
+ name: username
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/admin/users/{id}/status:
+ put:
+ parameters:
+ - description: 用户ID
+ in: path
+ name: id
+ required: true
+ type: integer
+ - description: 状态
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/user.updateUserStatusRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - admin
+ /api/v1/config/public:
+ get:
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - config
+ /api/v1/dashboard/stats/daily:
+ get:
+ consumes:
+ - application/json
+ parameters:
+ - description: 查询天数,最大7天
+ in: query
+ name: days
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ summary: 获取每日收支统计
+ tags:
+ - dashboard
+ /api/v1/dashboard/stats/top-customers:
+ get:
+ consumes:
+ - application/json
+ parameters:
+ - description: 查询天数,最大7天
+ in: query
+ name: days
+ required: true
+ type: integer
+ - description: 返回数量,最大10
+ in: query
+ name: limit
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ summary: 获取Top客户
+ tags:
+ - dashboard
+ /api/v1/dashboard/stats/user-balance:
+ get:
+ consumes:
+ - application/json
+ description: 统计所有用户的AvailableBalance字段
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ allOf:
+ - $ref: '#/definitions/util.ResponseAny'
+ - properties:
+ data:
+ $ref: '#/definitions/dashboard.UserBalanceStatsResponse'
+ type: object
+ summary: 获取用户余额统计
+ tags:
+ - dashboard
+ /api/v1/health:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - health
+ /api/v1/leaderboard:
+ get:
+ consumes:
+ - application/json
+ parameters:
+ - description: 页码
+ in: query
+ name: page
+ required: true
+ type: integer
+ - description: 每页数量
+ in: query
+ name: page_size
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ summary: 获取排行榜列表
+ tags:
+ - leaderboard
+ /api/v1/leaderboard/me:
+ get:
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ summary: 获取当前用户排名
+ tags:
+ - leaderboard
+ /api/v1/leaderboard/users/{id}:
+ get:
+ consumes:
+ - application/json
+ parameters:
+ - description: 用户ID
+ in: path
+ name: id
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ summary: 获取指定用户排名
+ tags:
+ - leaderboard
+ /api/v1/merchant/api-keys:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/api_key.CreateAPIKeyRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ /api/v1/merchant/api-keys/{id}:
+ delete:
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ get:
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ put:
+ consumes:
+ - application/json
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/api_key.UpdateAPIKeyRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ /api/v1/merchant/api-keys/{id}/payment-links:
+ get:
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ - description: 创建支付链接请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/link.PaymentLinkRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ /api/v1/merchant/api-keys/{id}/payment-links/{linkId}:
+ delete:
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ - description: Payment Link ID
+ format: int64
+ in: path
+ name: linkId
+ required: true
+ type: integer
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ put:
+ consumes:
+ - application/json
+ parameters:
+ - description: API Key ID
+ format: int64
+ in: path
+ name: id
+ required: true
+ type: integer
+ - description: Payment Link ID
+ format: int64
+ in: path
+ name: linkId
+ required: true
+ type: integer
+ - description: 更新支付链接请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/link.PaymentLinkRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ /api/v1/merchant/payment:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 支付订单请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/payment.PayOrderRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - payment
+ /api/v1/merchant/payment-links/{token}:
+ get:
+ parameters:
+ - description: 支付链接 Token
+ in: path
+ name: token
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ /api/v1/merchant/payment-links/pay:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 支付请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/link.PayByLinkRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - merchant
+ /api/v1/merchant/payment/order:
+ get:
+ consumes:
+ - application/json
+ parameters:
+ - description: 订单号
+ in: query
+ name: order_no
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - payment
+ /api/v1/oauth/callback:
+ post:
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/oauth.CallbackRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - oauth
+ /api/v1/oauth/login:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - oauth
+ /api/v1/oauth/logout:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - oauth
+ /api/v1/oauth/user-info:
+ get:
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - oauth
+ /api/v1/order/dispute:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/dispute.CreateDisputeRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - order
+ /api/v1/order/dispute/close:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/dispute.CloseDisputeRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - order
+ /api/v1/order/disputes:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ schema:
+ $ref: '#/definitions/dispute.ListDisputesRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - order
+ /api/v1/order/disputes/merchant:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ schema:
+ $ref: '#/definitions/dispute.ListDisputesRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - order
+ /api/v1/order/refund-review:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/dispute.RefundReviewRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - order
+ /api/v1/order/transactions:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ schema:
+ $ref: '#/definitions/order.TransactionListRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - order
+ /api/v1/payment/transfer:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 转账请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/payment.TransferRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - payment
+ /api/v1/redenvelope/{id}:
+ get:
+ parameters:
+ - description: 红包ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - redenvelope
+ /api/v1/redenvelope/claim:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 领取红包请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/redenvelope.ClaimRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - redenvelope
+ /api/v1/redenvelope/covers:
+ get:
+ parameters:
+ - description: 封面类型 (cover/heterotypic)
+ in: query
+ name: type
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - redenvelope
+ /api/v1/redenvelope/create:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 创建红包请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/redenvelope.CreateRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - redenvelope
+ /api/v1/redenvelope/list:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: 列表请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/redenvelope.ListRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - redenvelope
+ /api/v1/upload/redenvelope/cover:
+ post:
+ consumes:
+ - multipart/form-data
+ parameters:
+ - description: 图片文件
+ in: formData
+ name: file
+ required: true
+ type: file
+ - description: 封面类型 (cover/heterotypic)
+ in: formData
+ name: type
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - upload
+ /api/v1/user/pay-key:
+ put:
+ consumes:
+ - application/json
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/user.UpdatePayKeyRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - user
+ /f/{id}:
+ get:
+ parameters:
+ - description: Upload ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/octet-stream
+ responses:
+ "200":
+ description: OK
+ tags:
+ - upload
+ /pay/distribute:
+ post:
+ consumes:
+ - application/json
+ parameters:
+ - description: Basic Auth (base64(client_id:client_secret))
+ in: header
+ name: Authorization
+ required: true
+ type: string
+ - description: 分发请求
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/payment.MerchantDistributeRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - payment
+ /pay/submit.php:
+ get:
+ consumes:
+ - application/x-www-form-urlencoded
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/payment.CreateOrderRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - payment
+ post:
+ consumes:
+ - application/x-www-form-urlencoded
+ parameters:
+ - description: request body
+ in: body
+ name: request
+ required: true
+ schema:
+ $ref: '#/definitions/payment.CreateOrderRequest'
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/util.ResponseAny'
+ tags:
+ - payment
+swagger: "2.0"
diff --git a/frontend/.env.example b/frontend/.env.example
new file mode 100644
index 00000000..a855cd4f
--- /dev/null
+++ b/frontend/.env.example
@@ -0,0 +1,11 @@
+# 前端 URL
+LINUX_DO_CREDIT_FRONTEND_URL=http://localhost:3000
+
+# 后端 URL
+LINUX_DO_CREDIT_BACKEND_URL=http://localhost:8000
+
+# Session Cookie
+LINUX_DO_CREDIT_SESSION_COOKIE_NAME=linux_do_credit_session_id
+
+# 环境
+NODE_ENV=production
\ No newline at end of file
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
new file mode 100644
index 00000000..a411c7b6
--- /dev/null
+++ b/frontend/Dockerfile
@@ -0,0 +1,52 @@
+# ---- base stage ----
+FROM node:22-alpine AS base
+
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+
+RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
+
+# ---- build stage ----
+FROM base AS builder
+
+ENV NODE_ENV=production
+ENV NEXT_PUBLIC_LINUX_DO_CREDIT_BACKEND_URL=https://build-placeholder.invalid
+ENV LINUX_DO_CREDIT_SESSION_COOKIE_NAME=__LINUX_DO_CREDIT_SESSION_COOKIE_NAME__
+ENV LINUX_DO_CREDIT_RATE_LIMIT_ENABLED=__LINUX_DO_CREDIT_RATE_LIMIT_ENABLED__
+ENV LINUX_DO_CREDIT_BACKEND_URL=https://build-placeholder-2.invalid
+
+ARG VERSION=""
+ARG BUILD_DATE=""
+
+WORKDIR /app
+
+COPY package.json pnpm-lock.yaml ./
+
+RUN pnpm install --frozen-lockfile
+
+COPY . .
+
+RUN if [ -n "$VERSION" ] || [ -n "$BUILD_DATE" ]; then \
+ VERSION="$VERSION" BUILD_DATE="$BUILD_DATE" node -e "const fs = require('fs'); const pkg = require('./package.json'); const version = process.env.VERSION; const buildDate = process.env.BUILD_DATE; if (version) pkg.version = version; if (buildDate) pkg.buildDate = buildDate; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\\n')"; \
+fi
+
+RUN pnpm build
+
+RUN grep -rl \
+ -e "https://build-placeholder.invalid" \
+ -e "https://build-placeholder-2.invalid" \
+ -e "__LINUX_DO_CREDIT_SESSION_COOKIE_NAME__" \
+ -e "__LINUX_DO_CREDIT_RATE_LIMIT_ENABLED__" \
+ /app/.next > /app/.replace.files
+
+# ---- runner stage ----
+FROM base AS runner
+
+WORKDIR /app
+
+COPY --from=builder /app .
+
+EXPOSE 3000
+
+ENTRYPOINT ["./entrypoint.sh"]
+CMD ["pnpm", "start"]
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 00000000..412a6b4b
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,50 @@
+# Linux Do Credit Frontend
+
+Modern frontend application for the Linux Do Credit system.
+
+[中文](./README_zh.md) | English
+
+## Tech Stack
+
+- **Framework**: [Next.js 16](https://nextjs.org/)
+- **Styling**: [Tailwind CSS 4](https://tailwindcss.com/)
+- **UI Components**: [Radix UI](https://www.radix-ui.com/)
+- **Icons**: [Lucide React](https://lucide.dev/)
+- **Package Manager**: [pnpm](https://pnpm.io/)
+
+## Getting Started
+
+### Prerequisites
+
+- Node.js (Latest LTS recommended)
+- pnpm
+
+### Installation
+
+1. Install dependencies:
+ ```bash
+ pnpm install
+ ```
+
+2. Run the development server:
+ ```bash
+ pnpm dev
+ ```
+
+ Open [http://localhost:3000](http://localhost:3000) (or the port shown in your terminal) with your browser to see the result.
+
+## Project Structure
+
+- `app/`: Next.js App Router pages and layouts
+- `components/`: Reusable UI components
+ - `ui/`: Base UI components (buttons, inputs, etc.)
+ - `common/`: Shared business components
+- `lib/`: Utility functions and service definitions
+- `public/`: Static assets
+
+## Scripts
+
+- `pnpm dev`: Start development server with Turbopack
+- `pnpm build`: Build the application for production
+- `pnpm start`: Start production server
+- `pnpm lint`: Run ESLint
diff --git a/frontend/README_zh.md b/frontend/README_zh.md
new file mode 100644
index 00000000..33b0b12c
--- /dev/null
+++ b/frontend/README_zh.md
@@ -0,0 +1,50 @@
+# Linux Do Credit Frontend
+
+Linux Do Credit 系统的现代化前端应用。
+
+[English](./README.md) | 中文
+
+## 技术栈
+
+- **框架**: [Next.js 16](https://nextjs.org/)
+- **样式**: [Tailwind CSS 4](https://tailwindcss.com/)
+- **UI 组件**: [Radix UI](https://www.radix-ui.com/)
+- **图标**: [Lucide React](https://lucide.dev/)
+- **包管理器**: [pnpm](https://pnpm.io/)
+
+## 快速开始
+
+### 前置要求
+
+- Node.js (建议使用最新的 LTS 版本)
+- pnpm
+
+### 安装
+
+1. 安装依赖:
+ ```bash
+ pnpm install
+ ```
+
+2. 运行开发服务器:
+ ```bash
+ pnpm dev
+ ```
+
+ 在浏览器中打开 [http://localhost:3000](http://localhost:3000) (或终端中显示的端口) 查看结果。
+
+## 项目结构
+
+- `app/`: Next.js App Router 页面和布局
+- `components/`: 可复用 UI 组件
+ - `ui/`: 基础 UI 组件 (按钮, 输入框等)
+ - `common/`: 共享业务组件
+- `lib/`: 工具函数和服务定义
+- `public/`: 静态资源
+
+## 脚本
+
+- `pnpm dev`: 启动开发服务器 (使用 Turbopack)
+- `pnpm build`: 构建生产版本
+- `pnpm start`: 启动生产服务器
+- `pnpm lint`: 运行 ESLint
diff --git a/frontend/app/(auth)/login/page.tsx b/frontend/app/(auth)/login/page.tsx
new file mode 100644
index 00000000..5c3f1f52
--- /dev/null
+++ b/frontend/app/(auth)/login/page.tsx
@@ -0,0 +1,10 @@
+import { Suspense } from "react"
+import { LoginPage } from "@/components/auth/login-page"
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
diff --git a/frontend/app/(docs)/docs/api/page.tsx b/frontend/app/(docs)/docs/api/page.tsx
new file mode 100644
index 00000000..8ba38086
--- /dev/null
+++ b/frontend/app/(docs)/docs/api/page.tsx
@@ -0,0 +1,18 @@
+
+import { LegalPageLayout } from "@/components/common/docs/legal-page-layout";
+import { apiSections, DOCS_LAST_UPDATED } from "@/components/common/docs/api";
+
+export default function ApiDocPage() {
+ return (
+
+ LINUX DO Credit 提供简单、强大的 API 接口,支持多种编程语言和开发环境。通过标准化的 HTTP 接口,您可以轻松将积分服务集成到您的应用中。
+
+ }
+ />
+ );
+}
diff --git a/frontend/app/(docs)/docs/how-to-use/page.tsx b/frontend/app/(docs)/docs/how-to-use/page.tsx
new file mode 100644
index 00000000..0724d257
--- /dev/null
+++ b/frontend/app/(docs)/docs/how-to-use/page.tsx
@@ -0,0 +1,19 @@
+
+import { LegalPageLayout } from "@/components/common/docs/legal-page-layout";
+import { howToUseSections } from "@/components/common/docs/how-to-use";
+import { DOCS_LAST_UPDATED } from "@/components/common/docs/api";
+
+export default function HowToUsePage() {
+ return (
+
+ 欢迎使用 LINUX DO Credit。本文档将引导您快速了解平台的核心功能、角色定义及使用流程,帮助您更好地使用我们的服务。
+