From 5a7b2032c4dfecea78c4d6ad604f4a231fb2a59f Mon Sep 17 00:00:00 2001 From: jeremygan2021 Date: Thu, 12 Feb 2026 17:13:30 +0800 Subject: [PATCH] forum --- .../__pycache__/settings.cpython-313.pyc | Bin 6264 -> 6984 bytes .../shop/__pycache__/admin.cpython-312.pyc | Bin 16764 -> 16782 bytes .../shop/__pycache__/admin.cpython-313.pyc | Bin 17059 -> 17208 bytes .../shop/__pycache__/models.cpython-313.pyc | Bin 26754 -> 26986 bytes .../__pycache__/serializers.cpython-313.pyc | Bin 15858 -> 15914 bytes backend/shop/__pycache__/urls.cpython-313.pyc | Bin 1630 -> 1972 bytes .../shop/__pycache__/views.cpython-312.pyc | Bin 63677 -> 65593 bytes .../shop/__pycache__/views.cpython-313.pyc | Bin 52009 -> 65713 bytes backend/shop/admin.py | 6 +- backend/shop/utils.py | 43 ++++++++++ backend/shop/views.py | 78 +++++++++++++++--- frontend/src/components/CreateTopicModal.jsx | 3 +- frontend/src/components/LoginModal.jsx | 1 + frontend/src/pages/ForumDetail.jsx | 10 ++- 14 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 backend/shop/utils.py diff --git a/backend/config/__pycache__/settings.cpython-313.pyc b/backend/config/__pycache__/settings.cpython-313.pyc index 505eb1346157a73b291f7d1294e76bff96610fa1..f54023d00e79dd3854d77717426dedcb03a28220 100644 GIT binary patch delta 1528 zcmb7^Pf#0G9LM)x5=clI(}X|@X`n46P<8_;h_$q(LM|$AdFqFCKmSfU?mZ>f6c7+t2s+ z{r!G#_a%Raf9MH5wcD)}9$$a*S%wW!)D!(?d$_k2PZ3uP{;0(1Sj)Dw^Bu#ke-H3L zZ8x(?ktgm6ZUu-kMS=H!+x+Gqn2 zW(ZAG&?KY?J#VA}N7_x}5*Hvt^ovFvW?_!dYz37R$PqkWfiD3fG+#kwSRnMWkqRbK z_&r?s3M>+R$*9Bo@ByJ88mYkOF6Tle4HIC#(QA+2g&S&Q2(tXnfR?#d2C#Ft~!uV2E4^OV1 zrN+%DJ~_kBh>6*hl+5$k7 zb!S(^5HL0x8`rq*Ly@kK8STdB3+aC=evKo}s%JJ%Z${(vaO5s=)gBXltmJdtXL@UJ z{O@0UzxL_3Yd0RP74K+O2{Ah_X42A9tC-AYq{E3^HoG8Y;)hdsqI4PU`Zrkm)yZmkbUj2h;*(ww)zp&O{U7f2=*NU}hU2zm0_ZA-p z%g)Gih`V+rT8yrY6h|KPebrueA6<2NE5lmK&dv?HrNLYnx|R8ft6TR|)w>=$C)ZiV zWG-FxL>PN`HPH5R;NatqbLGH<`pDZ=)9&-RHY|R-WBoX-{^9Mb9b#qLYu&J1p`GT9 U)?voV6wK?>>XEu$?iSwKKc-Db4*&oF delta 815 zcmY+C%TE(g6vpSyqYW)>NK4;P+JdxInHf3W&<5_^NLd@Imz!6x|dP6BFac z%+7@?|ASeX@CO(dtXR1-EZkDMF>&LW3ntS$nUgcW@80j6%(>r7f6PK75b#N~mU=$U zf5=PHhV$`%g?EyMu6!!2)eu3{WE5i}#zjn+%usn4N#2eWk3i#5dSmF|@v;}4eSS&e z3Chw=W-y*ammwJvGP=T3$RdYsXy`E*#a-g*)mwN681mztV`-kwNGPC)-Kg!sUi3kS z0TcVskNr4+0hDl1_fsnx262euVH_!^a}tgYu;jox#3i9mxpe@S1-;Tlr*T!#Yc5(- za2+=UzUjh>p^ARXRN@wH3;m8;$6eeL^nMe4fEhs_Hql3z6?D!`w=2wHo=QB%6S3i` zdjpD{v}@NGM06fXJ_T&Yx|4( zp0R+{>sht8JTr>S!*;_L(OAFT()L-le}{gt)+rjsPnB0Hi^ZI^-o9b|hLZz2%fELq=@L(!UCVKHCb YTV-vYdVW;)$t#}CX{$9gs4kP+KThbS$^ZZW diff --git a/backend/shop/__pycache__/admin.cpython-312.pyc b/backend/shop/__pycache__/admin.cpython-312.pyc index 5bcffbb16d6464eefd3c66fe8f82756609eb0630..0d97ef11b5270d6ebb18322439e7c2b63dbd957d 100644 GIT binary patch delta 119 zcmey<#Msx&$a|WXmx}=i;%4?{-q^_7WF)|Oiz6>HIXf>gH?^n@C^LDMk&7^M(LW&b z7F&KnYF=gvSbXyvqh(BdthZQ7i&Klh(nW2XXPcTb3N8mKZDIJp!XV+QS8U{MGUDdA#gUhpoSm1Ln>u;EkqaMF(LbR4Ew=oE z)V$1;&EJfcF>$ioVks?7Eh_rAd6lUtBi|CBf)<7c!j%m^H$=21e=sv)blR+Hp2-CO D+TtSN diff --git a/backend/shop/__pycache__/admin.cpython-313.pyc b/backend/shop/__pycache__/admin.cpython-313.pyc index f0692ff012a69e1d990d8402deef7cae5d433d84..2d1d4eac20393d6cd9a773890a9db7b64baaa478 100644 GIT binary patch delta 697 zcmYL_-%nCu7{||h7(W6f6+0lPrL`RR3j>sMQ0CgYppiFcOE;(Hkc@s{dyzH03EQOB zwA`L-thubk1vi4ut%%arf8n^P3pjAm294WRy)V#uFTUsdKIi*P zV~yB`8y1ab6O?TFHdR&?4EXK@=kNKm%0Or?v^Rm%|Spo};mLBH;#-&7~oqd|yn)i>Wm; zTcuW~gaz_bdh@uL+|Q*mQtX)&+m)V1q@7gZMM8S=VX)R%`n-|fkLGt0h4^uPBa=(- z|%n}wrf?#o+gEoGKvl~&(UIJk^g{_k9k)i(GS!zQ|92bE5k9+}qY zsr^lu*Z^NgdUbJPqs({RO2m88YKdGsY4O0Hk@05Ea$=hNDVG`5Wkz;NQ&xe?>4qZL z0PD^@Gz&KVR;QJ)%rd-1X)NW|-)w;TLHNjqJHoQ-RLIV z6^76dycW)q`mxZfs5h1beG_HlOH|BMEWo)ur0<5yq3)vvDzmWb+GYC?boCoxYIFgu z!qMof`V1aIV;4{aVq-o<9f90~h=(C}x7tY&R90ceJ%gNwpWI7GQ3X4}I^Kc2hZEx! a#GOZqt=kobd@3@AZ7;LLm9N6&W$#}HiPe+< delta 619 zcmdnd#<;kZk?%7vFBbz4%Yyl1hIFT!~8J1)-2CYCCbCmS1ka|iPoFq$w0^9KtA z3kD16F#*}a!Q8O`d7`g;9EP zpPAa^8Kz>BPnjk8$%B+h1Bp@|pxP&Ud!O%J_hkRVXN~JMnQyTt=a=S{6jdTj1gm4LQDP4=^rW>*6-|1eB$w3V3LVP(ar zHuBJcjXBBw>#WY!p)Fua8YwJRc z1Sti1@>$=Cr#tsUozGOH53|6CoAeo zOnz&V!dNyr$aWUgJr{J+QChOWIvq6oRW~a{vilm}{lh;Yha+!meAfjyYC%afi z^UVSF`HU)H)4@`PKvxy%fCw!haf`zyH$SB`C)KXVd-G$5&x~U7jBFFKJ~IHR&&&)= QG9Q?LtPf6;-#GaI03pn?WdHyG diff --git a/backend/shop/__pycache__/models.cpython-313.pyc b/backend/shop/__pycache__/models.cpython-313.pyc index fa1ef35ac4aebe75dc1f31ee93432ab532b6ab75..4f3cd77a529cba7feb16e71d645d97bdb94d0f29 100644 GIT binary patch delta 5928 zcmbtY3s98T73MCug1kXomR*+RA+W3u1mY8QaRDX1fJGw;uIuuHtFViA7b7;U7;AkJ zAIBu7CNUaYvFQ-aCNXM~HZyHAZIb$$Cgg9^q}D}A(=?fwNoP8l={fi3p$L++j(l?; z=broc?sx9__t7it)2l4%tH{VPLHO@RbGs|E{A|>_Dz+YC=TC$mrs@3Y!G~Cw&Zf5| z&rtD1_*;_NzvnXAPw-iP+kGu81x}-nCGjVmcF> zIgMOmGV%hdJU}p&U>eK~v#@Mv3rk{Ia5U_pTEXgj_Z zuf5LYZEke5auarV8G=!qDW~1Z@x?B$kGs~k_&j`)9fxqceK!0#W(u1GvDQK;i;Y$B zSg483+$s*1=i{isCn1i=iyapH>nA6T$eBD8IVty$Lrx13(wsjMUu zf-L{_xI(5D7q1eE3bg*&WAm7*rvx@8Xjv8PO(==+k#RLb4{4N3@JWKESmB52BnD2Z z;mZit5x5YlTGWVvlRLZp9ee||HWC;KdU>4U)I!xbU-k-W8z58dX!5kUefJUwUyReR zqw3?zdIX5nz>wfNaz8j0YmY^QK z)YmeHe`&If&Do1BCY2oGb~H)ysAleRO7~KNHgbH4tvo-^>T%b*8u~P~5k4_w2$pi8 zdRjz$%$+tA+KcwqBV%0T!*+zcg(XW3BK)`f@$pOKw0AkH^j3=eyfZbw9VW=fn~I6eMT7z@CBM zXXw}o2xJ^HyV1@RERSyHp1KyNPi%-UCJ*<7J2*RD$9BMb1lJW^aW1)Evj2g>TIx zMz~8j!Lu|;3&CJE#*V;-g$B{W_`3umyltjZD**{$p}72VtXD!l>gBB$bf5ACkp~(&rD5nOxd0N;%WpnjD zYWoAhI|!x_Io#`Ll)M-=ydJl5S|ZvvlgT~u!$vv5>S=0ndA%-=dy%I*D7lGF=)C(wzIjm63zRl|F>RxTtWGnG5MH~(>2qxwLQiNRYaWuU z>m0svN9)~B)Fn7ReGnK_oGk`)=k0ub2vUPK1bZjA$1eElwO{` zT!Jz9>%2S>5a$QGLKv*Fri>rq7R6!WkCw-*3HQ8AS>IW0{m+xE#-NAU4X2n$l@{)m z#B(EuZftS5@e`_5L`gZ^=kYlj?Mk*}q1{gv+Z;-+q2w(5VabWN)82v(c{Hw?TWJ)4 ztGH^~fMm<5@&56Zh~@+z`fnG1+na54iKISe{x}s2gRHU?%@H(EH2QRVV9#-U&~BbO zgDS2q%gK7CuP<=)P0T=y*~=;ynfM86rTcKRv+c&kz5D`nmQ7bHR|P&ROJWT$`oY!A z0QC=k5c4$J@nZxE|FC%B^aYt%Kh1_oQ?O5twn=;w=9hhp>+l@DqKnoR)D<1Sf6MMI zmqWE&`9cd@6oqpoF3QLl;R$E5_#H1ho1`MQ%A`cNMM+jvYBH7@- zB`5vK2|rI&B|QXrQC{IoRQD4MFn52_48%A=n18|Ig>1A~s)0d~HqrmmlK(K44xQ!c zsNAdN)u`MF6{!OZ20BmRj}rY^NnOPR76}I{64(YfRblL7=)DSk##&mElOTpbM<8C$ zP@3LF)i8*xOuARJQ=IF2b53zN<VVnedvllyEX@VUHesY} ze+I5*F0E)5t!OZ7!+%8&{xB1VDxS)z~Oa#38fW`Rho4~ryRkh zjV&(xLX+G+uR`fK8ap6fl0N^KV1V22pvp<)g&^I3tV+{M@Fa!cKP`JtrB-8~s~9>~ z?8F$FTb-bJ3g-|Cz4hXoH#_$BtFERxMJ=c@z_#ketOhPsCm1Mf3vu4+d@1nSt`>?Z z%dJX&KQlvR%= z4M_PpvOP{PAnI8pYMw^B8#08)EVo)knC#PZJ(*?@C^}wW6U|((rDnu$z!DteqH$Sb zFik7oL`BQ#!)jTMw=P{)X)Cc;RhO6C{R4}Iob-KY1ml+m7;AO-1506TE;|cNwHo}q z+fh43-7D?aYt8?Iw24?wQ0z}%{kvY~)e7^WYcwk62IJanly=$LJe2k$YtwRNT9=5> z6mR>KO^t~Jk?5?7=ab(JR5!pM*P4F&SZmi=IES{RN zWG2pGNDdPf220%B)7VJL=;LxXDx6X$%C#5PY1lq+){Us{l{ncxn(+Trw>Vd!Zn1m| z?T>b&;#P=}v`(St(M^)qOW%k?3AmDmtRVPaQm&%K6S)2RBoo8^zgpiqUpU)N&P2K( zzAeRTe~sF%6Lb-Xpd_M!*nn_GS6GR2X4uqF5Tj@L2gW_AKd8SQgvHx);6lT1LMRUY E7dy#*C;$Ke delta 6010 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 delta 1507 zcmaKsZA_b06vuno@;r2Xp-iD`p|oQI+7)P62Ew+A+DNK#$Wr4=RS??JR#;nanJ<|e zL=6GaWpEb4bQ_8bb2wkfJhc2i${)^>)cmAwSORYr&7^Vk@TP26P!t!-G7158RIxMlals;~Q_1 zf|MG>n6UzaB6ZsMx11{&NDTBx@uVpYk19n)CqUmrc`ia6*G!wF5Di1PY~BeW#aw(0 zAgRo498F$qa(ovR4CC>osz_qf7o-)`ggyZ;FxdUdDq4EYWkCP%|;-WKn-fk;B z#l;M8+?GH0FeMG+n*9M7=Es@E%N6Z%8yDNyDEbTme8~}%CMXGU)v*~SQFeC1+e)u9 z41;3xcPZ#0OcCfD?m~8g#?u_Rf;3Bnqf92@B;hr}>x3B&x5T_OeuHprLvbB(ScwOm z^bF-sOp9{%V3a+Hfy!T7W@+7f|1Hj6m8MmM(}XuUuD|mfiF26T@(^(9toYNGE4&TM zTSJh>;%!dssB*!F_)OIW-cNt^R){I_>Q*QxFR+7z|FwSzKdEVi=kTwZyQCK=V-^Fh z3h84S=KH=t!v%cN)dLHnsN_Bi0bX=BUUqL5g#++8hPGX}ah+CAGq3ZsXGe*sBaEA& zy~WM*wU%hUgg<)jgeBfot8!~?CrH#)Hi9#CZWzTc>ZYY5B#h$|^{p_0^Yx{?xu4bt z?KzUm2;UOMIPP<+SuIT_u-W@Ce61Yyeg@JqDMv8U5Qp#ZVnZuTqj7u9zp}D(yI<2x ze;IcJW#-J8f$o7sxGS-*I~MKQ&-&RR+F+0nBZ%`X(&!`QT;rH#FkgU#6@r#P7HMDS zH})z;j&rzm!gP82y-(^o-x*1}W!2-kf> zvUtmO(C8B3d%_O{;bqa3Rhs@xAn)G5m{|&EadFpMkj2M$A8?B=pZKPUK8tV1FeM2K r3rmS!XVK)dZ}^Qz=+(~rf{nfwQ{~jIOJz-8l{H;eX`vDi`Of|WLDz$4 diff --git a/backend/shop/__pycache__/urls.cpython-313.pyc b/backend/shop/__pycache__/urls.cpython-313.pyc index dc0bc4576910db096ef62121cccafc2836190176..48d61922078e9c5562b817b5bd2858fead21e506 100644 GIT binary patch delta 739 zcmZvZ&ubGw6vy{x+U{nv*(ST2Hqqusq1CvqpcR55VsBAI9Bn~{hSe-dFeYJ=P}ob% zNiU*h5Q2X}k2!eu?k#)MAYOX+VE+MUXOr#4ft~k0GvD`pZ<*imuj=l(qDTx`$}nP;F_WcC&!l!2M2H&4D4_#k{M?2xcD zx`LNa73#0$n%5WXXAYS3a$_v+J1{7$3#6rOr8K6Q1B!O1p zVLL43Y4r<_1M3IHBgAC2)SbL zleh3+wzMLlY@>8#8|S#&tcC&it0MQB-HuypcN?wFTzc8+)NAx)t{uAjO=s}D@1ld2 zdkoycJU;BTyH35U>?z?GHXXzE7E@UiJ*^kawj-3-9DXQ_HKdc5 zK}N8d!Ir__g|TFjlqFyPK7JRA1>I@>Hy+iz8$*~fpxik+Lho+`d@ w@29XjTgy&foVI=84uy?bwmh|c;Q^udTa#<2N4{_u--wlDT~@iVjLM|LKfXt_6#xJL delta 369 zcmdnOe~(AwGcPX}0}w3l?9N=p!octt#DM`GDC6@7#)%qnA_@#KtV#@~P{AUR7?B|D ziK`TuLDV@#bsz=Ph@gYnW4KJ2Abg;Hpqj~Ij9@L3t)O%qqhdXFz1%TE!Q8<-mdrq% z%t{QwyfM7Ne8K#d%wV2C3}3Jyn8yf|6N=#v7KZagVg!Ok;XJVz!C-MXPa=jhSQ5^Y zieXY>U;#QzXR;rYGCO;bc)GNv%;X9tGe+LY>zVFMKFXXh*@C5H@+%hk$!e?`j1rUm zSsN!mVXfm*2YQPUh>Mvf=ddYEp2$`;S&rS3Q}Tke_6GfnEKZZ#*p<2YIry0xxr
Hvi_LUI5A diff --git a/backend/shop/__pycache__/views.cpython-312.pyc b/backend/shop/__pycache__/views.cpython-312.pyc index 97bf4b11eb420d183a8773291df969d1319abe52..477e8b80d7934bb9ffe46fc7a1ef1b02863747e5 100644 GIT binary patch delta 12165 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 ZPrQ=_eYdmmxuZ-GeE0h=xu`Ob`@hy{e4_vW delta 10529 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 CiH%bL diff --git a/backend/shop/__pycache__/views.cpython-313.pyc b/backend/shop/__pycache__/views.cpython-313.pyc index 794b194558f5c8c8250d991970ef63d4963511b0..e85c4832e6fd4f09fce02d7a18c8d46b6b732fc3 100644 GIT binary patch delta 20746 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

D3RC|)RB^ym>A)XFkX0)LdjDLVG&e9 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≦eKAnFGLj&^-5T@6)5t zy&zPxH>})k?7+Y1fNtW_E%2uticvkr?=s=*VL2F~9Ap>p0Gw(_j^%KF`V8oKw*?(x zb4yFXOsvXi`-&f)ff%^YJf<)`6ha5mr@-EK2q>L zO29k%Sy5&J`ZZ(30@ski4EGUBd&DpdVl9c;)ob?~K>skAd3B@;s;T&}Ks1P4d@<2G z;g^t2cqzdvDvP!3TebCM1yaaE(0I3@zSDxMXc5*WHUet5+|>bBuF~Bm_&XRZN{W4! z?6$CM76!Z<{OiJUqQ}sS9>tIpmz=G>H9jP(sa?5@7c@*%V!#q9{ArD5_7N%z>uH93 zavv5dJzedj>>tSAgjjxdfp+36?r$hza}V5?njM=xop2FBM?Y~z{)m7Q z=hfV~`Tmdux(u;b_M!QG&|G*$4`=l62}JJ2T47Gj!Zhu$RBz%rx&^Jk8JI6JBp-h8 z;DZ6Z#jm&YCxJfMyW(?=5fn`CUBYa%rhvxc*H{9YLcgYPU`0Sv;n!4zEs=A4=E@6T z9<1>xYk=Kqbcd%NoElK4`qil?=A5oNSv8`z4rKe)MZHUc@!2osy*lm1Y2PU7&m5Et z^!W4@y(^%RHZ7pZ`iUm1U-{G0@@cq1VM<>}<0>=_Wl7}dvkf2T%N}f~$UfCa9VLE>(C2aX_=_Jk) z+$B#*_^+=J-04u#m&@@pZK6f-95RGUJ@6rAcS{e%e#g<*F6J2ZSf~jXG9on4&o8FY zgzuT)gX^i^%l8UjV>2$xE`AnhL9$sh=pEuu@H;$@AjH}26~#B1f*>COW5w)xXvNoQ z2nqqf_d4A!_~Qpfm^Q{DkHPH_`X#uuWKK90-1f;1>wcyUv^ip}6HvH!OQgq#L{(3&INz-J}b3NI4%IAU*BVoC`=7 z6hmE+2QPgPsl;)=5*7YRRO1)b{7SUom!hIyiYk67Dxu!*S4w^<%B92K|DkCLHyebK z*mNt`AQs=ugV*u;p<_R|$nh(soQ*p&Ctxh_8w+lW*y|sflzE(K9ByD@eNdYlG(31k znR+GL>YKa9Z*B-qE%hzh;?Hb@Ah5JJFi59bPAK6owq&SpN|ZYJ%>;VtN14_Xomf1c zE)uKzX7v?}aK>@*CXQ1GNMu0S%}jhBUnHUb`lAx>xS0)A8Z@|IsGuqRigo&h62E0l zFxxs@=r`9y$0`^|@|nvZunUJ%>Ez={qPX;+)^sIhaWJ(om_9X_nl-MH8RN!f$~g6S z0+gOuF|gh@Yq_szg?{-5{hZSu$?BixGZc?-CF5cBq3fv% delta 9988 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 diff --git a/backend/shop/admin.py b/backend/shop/admin.py index 624e39c..fa15012 100644 --- a/backend/shop/admin.py +++ b/backend/shop/admin.py @@ -311,8 +311,8 @@ class OrderAdmin(ModelAdmin): @admin.register(WeChatUser) class WeChatUserAdmin(ModelAdmin): - list_display = ('nickname', 'is_star', 'title', 'avatar_display', 'gender_display', 'province', 'city', 'created_at') - search_fields = ('nickname', 'openid') + list_display = ('nickname', 'phone_number', 'is_star', 'title', 'avatar_display', 'gender_display', 'province', 'city', 'created_at') + search_fields = ('nickname', 'openid', 'phone_number') list_filter = ('is_star', 'gender', 'province', 'city', 'created_at') readonly_fields = ('openid', 'unionid', 'session_key', 'created_at', 'updated_at') @@ -329,7 +329,7 @@ class WeChatUserAdmin(ModelAdmin): fieldsets = ( ('基本信息', { - 'fields': ('user', 'nickname', 'avatar_url', 'gender') + 'fields': ('user', 'nickname', 'phone_number', 'avatar_url', 'gender') }), ('专家认证', { 'fields': ('is_star', 'title'), diff --git a/backend/shop/utils.py b/backend/shop/utils.py new file mode 100644 index 0000000..7bf8209 --- /dev/null +++ b/backend/shop/utils.py @@ -0,0 +1,43 @@ +import requests +from django.core.cache import cache +from .models import WeChatPayConfig + +def get_access_token(config=None): + """ + 获取微信接口调用凭证 (client_credential) + """ + # 尝试从缓存获取 + cache_key = 'wechat_access_token' + if config: + cache_key = f'wechat_access_token_{config.app_id}' + + token = cache.get(cache_key) + if token: + return token + + if not config: + # 优先查找指定 AppID + config = WeChatPayConfig.objects.filter(app_id='wxdf2ca73e6c0929f0').first() + if not config: + config = WeChatPayConfig.objects.filter(is_active=True).first() + + if not config or not config.app_id or not config.app_secret: + return None + + url = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={config.app_id}&secret={config.app_secret}" + try: + response = requests.get(url, timeout=10) + data = response.json() + + if 'access_token' in data: + token = data['access_token'] + expires_in = data.get('expires_in', 7200) + # 缓存 Token,留出 200 秒缓冲时间 + cache.set(cache_key, token, expires_in - 200) + return token + else: + print(f"获取 AccessToken 失败: {data}") + except Exception as e: + print(f"获取 AccessToken 异常: {str(e)}") + + return None diff --git a/backend/shop/views.py b/backend/shop/views.py index 9d04602..25eb913 100644 --- a/backend/shop/views.py +++ b/backend/shop/views.py @@ -10,6 +10,7 @@ from django.http import HttpResponse from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiExample from .models import ESP32Config, Order, WeChatPayConfig, Service, VCCourse, ServiceOrder, Salesperson, CommissionLog, WeChatUser, Distributor, Withdrawal, CourseEnrollment from .serializers import ESP32ConfigSerializer, OrderSerializer, ServiceSerializer, VCCourseSerializer, ServiceOrderSerializer, WeChatUserSerializer, DistributorSerializer, WithdrawalSerializer, CommissionLogSerializer, CourseEnrollmentSerializer +from .utils import get_access_token from django.core.signing import TimestampSigner, BadSignature, SignatureExpired from django.contrib.auth.models import User from wechatpayv3 import WeChatPay, WeChatPayType @@ -953,9 +954,13 @@ def get_current_wechat_user(request): @extend_schema( summary="微信小程序登录", + description="支持通过 code 登录,以及可选的 phone_code 用于直接获取手机号并合并 Web 用户账号", request={ 'application/json': { - 'properties': {'code': {'type': 'string', 'description': 'wx.login获取的code'}}, + 'properties': { + 'code': {'type': 'string', 'description': 'wx.login获取的code'}, + 'phone_code': {'type': 'string', 'description': 'getPhoneNumber获取的code (可选)'} + }, 'required': ['code'] } }, @@ -964,14 +969,21 @@ def get_current_wechat_user(request): @api_view(['POST']) def wechat_login(request): code = request.data.get('code') + phone_code = request.data.get('phone_code') + if not code: return Response({'error': 'Code is required'}, status=400) - config = WeChatPayConfig.objects.filter(is_active=True).first() + # 1. 获取配置 (优先使用指定 AppID) + target_app_id = 'wxdf2ca73e6c0929f0' + config = WeChatPayConfig.objects.filter(app_id=target_app_id).first() + if not config: + config = WeChatPayConfig.objects.filter(is_active=True).first() + if not config or not config.app_id or not config.app_secret: return Response({'error': 'WeChat config missing'}, status=500) - # 换取 OpenID + # 2. 换取 OpenID url = f"https://api.weixin.qq.com/sns/jscode2session?appid={config.app_id}&secret={config.app_secret}&js_code={code}&grant_type=authorization_code" try: res = requests.get(url, timeout=10) @@ -985,26 +997,70 @@ def wechat_login(request): openid = data.get('openid') session_key = data.get('session_key') unionid = data.get('unionid') + + # 3. 处理手机号 (尝试获取并合并 Web 用户) + user = None + phone_number = None + + if phone_code: + access_token = get_access_token(config) + if access_token: + try: + phone_url = f"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={access_token}" + phone_res = requests.post(phone_url, json={'code': phone_code}, timeout=5) + phone_data = phone_res.json() + + if phone_data.get('errcode') == 0: + phone_info = phone_data.get('phone_info') + phone_number = phone_info.get('purePhoneNumber') + + if phone_number: + # 查找已存在的用户 (Web 用户或已绑定手机的 MP 用户) + existing_user = WeChatUser.objects.filter(phone_number=phone_number).first() + if existing_user: + user = existing_user + # 如果是 Web 虚拟账号 (openid 以 web_ 开头),更新为真实 OpenID + if user.openid.startswith('web_') or not user.openid: + user.openid = openid + user.session_key = session_key + user.unionid = unionid + user.save() + # 如果已是真实账号但 OpenID 不匹配,可能是不同 AppID,暂不处理(避免覆盖) + except Exception as e: + print(f"获取手机号失败: {e}") - # 创建或更新用户 - user, created = WeChatUser.objects.update_or_create( - openid=openid, - defaults={ + # 4. 创建或更新用户 (如果未通过手机号找到) + if not user: + defaults = { 'session_key': session_key, 'unionid': unionid } - ) + if phone_number: + defaults['phone_number'] = phone_number + + user, created = WeChatUser.objects.update_or_create( + openid=openid, + defaults=defaults + ) + else: + # 如果找到了用户,且 OpenID 匹配(或刚被更新),更新 session_key + if user.openid == openid: + user.session_key = session_key + user.unionid = unionid + user.save() + created = False # 生成 Token signer = TimestampSigner() - token = signer.sign(openid) + token = signer.sign(user.openid) return Response({ 'token': token, 'id': user.id, - 'openid': openid, + 'openid': user.openid, 'is_new': created, - 'nickname': user.nickname + 'nickname': user.nickname, + 'phone_number': user.phone_number }) @extend_schema( diff --git a/frontend/src/components/CreateTopicModal.jsx b/frontend/src/components/CreateTopicModal.jsx index bcf7fe8..143688e 100644 --- a/frontend/src/components/CreateTopicModal.jsx +++ b/frontend/src/components/CreateTopicModal.jsx @@ -175,7 +175,8 @@ const CreateTopicModal = ({ visible, onClose, onSuccess, initialValues, isEditMo footer={null} destroyOnClose width={1000} - style={{ top: 20 }} + centered + maskClosable={false} >

{ onCancel={onClose} footer={null} destroyOnClose + centered > { backdropFilter: 'blur(10px)', marginBottom: 30 }} - bodyStyle={{ padding: '30px' }} + styles={{ body: { padding: '30px' } }} >
{topic.is_pinned && 置顶} @@ -353,11 +353,13 @@ const ForumDetail = () => { {/* Edit Modal */} setEditModalVisible(false)} + onClose={() => { + setEditModalVisible(false); + // Workaround for scroll issue: Force reload page on close + window.location.reload(); + }} onSuccess={() => { fetchTopic(); - // setEditModalVisible(false) is called in modal's submit handler wrapper? - // CreateTopicModal calls onSuccess then onClose. So we just need to refresh here. }} initialValues={topic} isEditMode={true}