From 1e13882df32cf3ecf79a382ff8c86dad650a4e17 Mon Sep 17 00:00:00 2001 From: jeremygan2021 Date: Thu, 12 Feb 2026 17:37:45 +0800 Subject: [PATCH] forum --- .../shop/__pycache__/views.cpython-312.pyc | Bin 67223 -> 68171 bytes backend/shop/views.py | 96 ++++++++++++------ miniprogram/src/pages/user/index.tsx | 2 + 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/backend/shop/__pycache__/views.cpython-312.pyc b/backend/shop/__pycache__/views.cpython-312.pyc index c2c010c766f4d885b3dddcde180994ab14ffe2c7..60cb3fa598de16963f3255030d1d53f9f6357bb7 100644 GIT binary patch delta 2875 zcmaJ?4Ny~87Jm1=BqosXV;rv{Ft%avQe1|e}k+9(1D45lpl4*~r(Z3=PgjL5yo+N%v1|)#6 zhfay=Cvmhy9JEJjVVXh;UClqG;+g^6wlyAxw3JZsuY8@8=9DhLHgGhu2RS82Z(c|c z$bW~Ut4wfPo)TWTs5DrOfxM1IQ&vmh6ekJm4336-T(lIqiQ_7;Jc^phDKW^Py-EXG zKyXT@+(p{cYf_=1DLs^KF$H2UB9czvl#>7;th6<0i+XE}Gis~G8NF5ORG|b>uzSEH z=jfJMQ4XHUVEleoi(YFrFK zzEa7c^I}Sat>KuerEu>Gtq2j8Vmef*s4 z-*Pm2o}z?p>w?v!SkSdw9H4I^{(eU|Kr^n+p{( z!YSP?b&1|r!0xsA@U}*oZhE84cAL>|IkURWwqyOaTk;dc0mo8-*YSV)x!_u-co5QiTdb7=L z@SkrRzWlpKUGEPaJv+o78NTpa|D}DwG_|UrDv0^J-u8EPW*XR5^etlF21EtHwjsB6 zI^z2GwLkc9Z>Ay0Hq?e8*x>JIA3oRops{JFzQeFH#9&(erp=yfLHnAC!bye)*BksF zod|Bj@2dCz;i|vB)qex}_aB(hLH*?{SbCWctH3K8u_-}n7=NfZUPOoNe{mTAu4V>Beb6vyA`?T7vUEKLb=Sp`> zZXca@KQh{_%JW9%kA{@N_d0jijYbgKL_w46(Ig9+g&xhqPKj6Z+UHEH+c2kfacgRK zSkKPSoPTllRJ(sx?yg{kinlx!Z@J5NjuON!u#rSV8CgX>!^2h*Z5$5>+(_E-psyy! z2txfPIjB!OAsP!I)Vec@;|hUE_AtqvG447hxtFmBOumQ7cNdqsnS3wviwDda53|O{ zBnnKbhe;Kf`5tC|XQrE(-^=6(%qkDF%3b(|n_1;$N_|>TwxjwDoOz!dA zQ~9G3LTwb7BoCA1Wz0T(;xQ3|3<-ihtyiDsOIv)*;?<^1&`)E!*4SYAGEy_BigRHq z%^b~Y$P$z=>>6FynD0aL+=kpE`MxOi(UOLevpM&oW{pXS^QCWkmF?29mQ@`TbE|Lg~AIqa)%e6uvI($8GP9_nd{ucqzWt2y{X>-f+R?wW& zl;NH}Pms^QC!havltRH5&q9#Ut%!R$wi~X!o>Nyu5r52HrYK4$dP3=9Gtr}8nO&@> z`m|Cs5hz(vDkaEOMM;!k1lUg!W1B83)>1;Is#rz&RAS`&^x|UEG+)9rr1F_%>r*J- zywvq3sy`kezuzQAww6J!Y3StTp$}U9H$S@n@o&)U<<(tXKs<=w8b5G@6N3?0bgLTK zKi^6M1Mu;!Eb%{4@Rxl0?Qm&?AGvG;exgIz`wAA^xhZ*s-G@B=*90K`H}dcC*>~4V zv@ei5BE{Uh$Rvr_xtKQ_n-{R%TMk~pn7#s#%5UylOM+a!Tlfo}k$%P=Fca6{>=3Bhw@30{; zUxJMSn`n6RYbNA5j$kvBAN-mG;5q-*HyI?j#cLn71CR(md!z+-_f0%*LNj7I&M)U1f2* z2Eta@>^8QtY+a>od!emnyp~saUqA8uHj9gPG4 delta 2079 zcmZux4NOy46n?kw^}mH`Z)wXbv;u_|D$oh)WHPJ>5@n*)xy@~i(W)@T_*z5aUZ*bE z(99p)b>cRrF3w1XG2laSn?G6h1IeDTX^^PO|g zJ@>x%-Cm}7ClmjjTCJe)M}1&tt@s|*5nq2)DaSUAf1^VRn~n~&1%MfCxNSnYYB`#B>;+(nSuEtWO@PbdAx0ao z<>70yC<=44--ax$PBA<-tqq`OniA7^Euo|#iUO1edQ#RVtkVZ{>kNT}b;f`mP`nOR z9M7UtA0{w3Ar%!LpGBulIMPtdgu{guI2J4E&wt1$iiaL+o9Lm%K<5@*$-a;*ykG8tBv(jT7`$)Z_Sk}b9PsYn}Jxk0}znbV!$6ruJhRZ$YPs5{4 z0t>K9mt~HX!%NQMwVZ0aQ8nlMUsLwUS#u|4@oG-`UoEe|%csVc=atw>dt|8|4ksHQ zVMf5{p~yEjYe4yz#Ds1Ck^sxIbxYCK`C6Ln;_{WmsR5H;9Fy@Tyt!p0%szG)n~gZ` z{x`jl3=r56BQ#7g24wz6BljfpGIT)D)ASApIxS2CU!VaYNooxw@`-ij9^kY|rbPWll0+f0>UAYn$;PSA;o`ja1ZZ}D5IB&Jz>)~#az)7O)#NbQ9 zCfZPygo3lhi36$zZ_S!&f7ROct844I0W3I!A4}g2MyUVH2c}-sXS7Os^*P&A{Yw3a zNUqLDqo2PlZdyDdTZq3ToHxFN#zvdd?oCvj+*TebAHI2zn_VwW$kBLC6#m4MA%dW<=SPref?eSt6|C0_%u6X76YE40cp;Hv6gf7DaW&=9PO^ z9xAz@%e;kE=@E8%h@Bo`^FnOi>5?Fu*UQe|Q+#=f;TL^!6VtCXM{Rjc#eL9<#?GeO zaA8Z9Ro_6onLL@t1vbj=2eXE#ftt_Ov zbD=wj>S4;P?o>KtmtmtV?p~KQRH%o_EG2y9Vw?i7X zNpV&5! zmBOwsc`+D5%I@_TU+=bqesrX}KztnsJ;Gpjyi9WyQ*IBrr-Ow3ikRRz$qhn>g`Qv< z5Z}i9gm5$TyabG)MbSiztD>th{t&GIae}$uEdqAI|I@cXLgDwVok1N}A#z`GO7id% z?yE()!*hghF1<^GV&S>Vr)W?m#1HPGb@5m{psra{yVk>b>#ID~{%THxP7KWe8&KBp z(}MMP2@RykalJt)!%~jCj5s+ue!U1N(I3|fK%dV2;rLzy`#n*xz?j0=^W1Kf8KFpcQDy z8-gRp%8}#aZZ~gX091}|ZM;-z^FanAfmgfEGGIHcFA)LhC{2mrXp&ka8c8Ph%?hUL JB^kH@{sH%9b5Q^Q diff --git a/backend/shop/views.py b/backend/shop/views.py index 6c998f4..6ffe8a3 100644 --- a/backend/shop/views.py +++ b/backend/shop/views.py @@ -1004,9 +1004,9 @@ def wechat_login(request): phone_number = None if phone_code: - access_token = get_access_token(config) - if access_token: - try: + try: + access_token = get_access_token(config) + if access_token: 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() @@ -1014,8 +1014,13 @@ def wechat_login(request): if phone_data.get('errcode') == 0: phone_info = phone_data.get('phone_info') phone_number = phone_info.get('purePhoneNumber') - except Exception as e: - print(f"获取手机号失败: {e}") + else: + print(f"获取手机号API返回错误: {phone_data}") + else: + print("获取 AccessToken 失败,无法解密手机号") + except Exception as e: + print(f"获取手机号异常: {str(e)}") + try: with transaction.atomic(): @@ -1029,29 +1034,41 @@ def wechat_login(request): if mp_user and phone_user: if mp_user != phone_user: - # 【合并场景】: 小程序用户 和 手机号用户 都存在且不同 -> 将手机号用户(Web)合并到小程序用户 - # 注意: 如果 phone_user 也是真实的 MP 用户(有 openid 且不是 web_), 则可能冲突,这里默认保留当前登录的 mp_user + # 【合并场景】: 小程序用户 和 手机号用户 都存在且不同 - # 1. 迁移订单 - Order.objects.filter(wechat_user=phone_user).update(wechat_user=mp_user) - # 2. 迁移社区数据 (延迟导入避免循环引用) - from community.models import ActivitySignup, Topic, Reply - ActivitySignup.objects.filter(user=phone_user).update(user=mp_user) - Topic.objects.filter(author=phone_user).update(author=mp_user) - Reply.objects.filter(author=phone_user).update(author=mp_user) - # 3. 迁移分销员 - if hasattr(phone_user, 'distributor') and not hasattr(mp_user, 'distributor'): - dist = phone_user.distributor - dist.user = mp_user - dist.save() + # 检查 phone_user 是否已经是真实的 MP 用户 (防止覆盖已绑定的其他微信账号) + # 规则: 如果 phone_user.openid 不是以 'web_' 开头,说明它已经是一个微信用户 + # 此时我们不能简单的合并,因为这意味着两个不同的微信账号绑定了同一个手机号(可能是异常或用户更换了微信号) + # 策略: 优先保留当前的 mp_user,提示用户手机号已被占用,或者这里简单处理为不合并手机号,只登录 mp_user - # 删除旧用户 - phone_user.delete() - user = mp_user - - # 更新手机号 - if not user.phone_number: - user.phone_number = phone_number + if not phone_user.openid.startswith('web_'): + print(f"冲突: 手机号 {phone_number} 已被用户 {phone_user.id} (OpenID: {phone_user.openid}) 绑定,无法合并到当前用户 {mp_user.id}") + # 这种情况下,我们让当前用户登录,但不更新手机号 (或者可以返回错误提示需人工解绑) + user = mp_user + # 也可以选择强制更新手机号到当前用户,并解绑旧用户(取决于业务规则,这里选择保守策略:不合并,仅登录) + else: + # 是 Web 虚拟用户,可以安全合并 + # 1. 迁移订单 + Order.objects.filter(wechat_user=phone_user).update(wechat_user=mp_user) + # 2. 迁移社区数据 (延迟导入避免循环引用) + from community.models import ActivitySignup, Topic, Reply + ActivitySignup.objects.filter(user=phone_user).update(user=mp_user) + Topic.objects.filter(author=phone_user).update(author=mp_user) + Reply.objects.filter(author=phone_user).update(author=mp_user) + # 3. 迁移分销员 + if hasattr(phone_user, 'distributor') and not hasattr(mp_user, 'distributor'): + dist = phone_user.distributor + dist.user = mp_user + dist.save() + + # 删除旧用户 + phone_user.delete() + user = mp_user + + # 更新手机号 + if not user.phone_number: + user.phone_number = phone_number + user.save() else: # 同一个用户 user = mp_user @@ -1059,31 +1076,46 @@ def wechat_login(request): elif phone_user: # 【绑定场景】: 只有手机号用户存在 (通常是 Web 用户) -> 升级为小程序用户 user = phone_user + # 只有当它是 Web 虚拟用户时,才覆盖 OpenID - # 或者如果它就是目标用户,直接更新 if user.openid.startswith('web_') or not user.openid: user.openid = openid + user.save() elif user.openid != openid: # 冲突: 手机号已被另一个真实 OpenID 绑定,但当前登录的是新的 OpenID # 策略: 创建新用户,不合并 (避免安全风险) - user = WeChatUser.objects.create(openid=openid) + # 检查 openid 是否已被其他用户占用 (理论上 mp_user 为 None 说明没有,但双重检查) + existing_openid_user = WeChatUser.objects.filter(openid=openid).first() + if existing_openid_user: + user = existing_openid_user + else: + user = WeChatUser.objects.create(openid=openid) + # 此时不绑定手机号,因为手机号被 phone_user 占用了 elif mp_user: # 【更新场景】: 只有小程序用户存在 -> 更新手机号 user = mp_user if phone_number: + # 检查手机号是否冲突 (理论上 phone_user 为 None 说明没有冲突) user.phone_number = phone_number + user.save() else: # 【新建场景】: 都不存在 -> 创建新用户 user = WeChatUser.objects.create(openid=openid) if phone_number: user.phone_number = phone_number + user.save() - # 统一更新会话信息 - user.session_key = session_key - user.unionid = unionid - user.save() + # 统一更新会话信息 (确保 user 对象是最新的) + # 重新获取对象以防状态不一致 (可选,但推荐) + # user.refresh_from_db() + + if user.openid == openid: + user.session_key = session_key + user.unionid = unionid + user.save() + created = False # 简化处理 except Exception as e: diff --git a/miniprogram/src/pages/user/index.tsx b/miniprogram/src/pages/user/index.tsx index ee9d18c..03fec4d 100644 --- a/miniprogram/src/pages/user/index.tsx +++ b/miniprogram/src/pages/user/index.tsx @@ -61,6 +61,8 @@ export default function UserIndex() { const { code: loginCode } = await Taro.login() // 2. 调用后端登录 (Code + PhoneCode) + console.log('loginCode:', loginCode) + console.log('phoneCode:', phoneCode) const res = await Taro.request({ url: 'https://market.quant-speed.com/api/wechat/login/', method: 'POST',