forum
This commit is contained in:
Binary file not shown.
@@ -11,6 +11,7 @@ from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiPara
|
||||
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.db import transaction
|
||||
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
|
||||
from django.contrib.auth.models import User
|
||||
from wechatpayv3 import WeChatPay, WeChatPayType
|
||||
@@ -998,7 +999,7 @@ def wechat_login(request):
|
||||
session_key = data.get('session_key')
|
||||
unionid = data.get('unionid')
|
||||
|
||||
# 3. 处理手机号 (尝试获取并合并 Web 用户)
|
||||
# 3. 处理手机号与用户合并逻辑
|
||||
user = None
|
||||
phone_number = None
|
||||
|
||||
@@ -1013,42 +1014,82 @@ def wechat_login(request):
|
||||
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}")
|
||||
|
||||
# 4. 创建或更新用户 (如果未通过手机号找到)
|
||||
if not user:
|
||||
defaults = {
|
||||
'session_key': session_key,
|
||||
'unionid': unionid
|
||||
}
|
||||
if phone_number:
|
||||
defaults['phone_number'] = phone_number
|
||||
try:
|
||||
with transaction.atomic():
|
||||
# 查找已存在的 OpenID 用户 (小程序用户)
|
||||
mp_user = WeChatUser.objects.select_for_update().filter(openid=openid).first()
|
||||
|
||||
user, created = WeChatUser.objects.update_or_create(
|
||||
openid=openid,
|
||||
defaults=defaults
|
||||
)
|
||||
else:
|
||||
# 如果找到了用户,且 OpenID 匹配(或刚被更新),更新 session_key
|
||||
if user.openid == openid:
|
||||
# 查找已存在的手机号用户 (可能是 Web 用户或已绑定的 MP 用户)
|
||||
phone_user = None
|
||||
if phone_number:
|
||||
phone_user = WeChatUser.objects.select_for_update().filter(phone_number=phone_number).first()
|
||||
|
||||
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.delete()
|
||||
user = mp_user
|
||||
|
||||
# 更新手机号
|
||||
if not user.phone_number:
|
||||
user.phone_number = phone_number
|
||||
else:
|
||||
# 同一个用户
|
||||
user = mp_user
|
||||
|
||||
elif phone_user:
|
||||
# 【绑定场景】: 只有手机号用户存在 (通常是 Web 用户) -> 升级为小程序用户
|
||||
user = phone_user
|
||||
# 只有当它是 Web 虚拟用户时,才覆盖 OpenID
|
||||
# 或者如果它就是目标用户,直接更新
|
||||
if user.openid.startswith('web_') or not user.openid:
|
||||
user.openid = openid
|
||||
elif user.openid != openid:
|
||||
# 冲突: 手机号已被另一个真实 OpenID 绑定,但当前登录的是新的 OpenID
|
||||
# 策略: 创建新用户,不合并 (避免安全风险)
|
||||
user = WeChatUser.objects.create(openid=openid)
|
||||
|
||||
elif mp_user:
|
||||
# 【更新场景】: 只有小程序用户存在 -> 更新手机号
|
||||
user = mp_user
|
||||
if phone_number:
|
||||
user.phone_number = phone_number
|
||||
|
||||
else:
|
||||
# 【新建场景】: 都不存在 -> 创建新用户
|
||||
user = WeChatUser.objects.create(openid=openid)
|
||||
if phone_number:
|
||||
user.phone_number = phone_number
|
||||
|
||||
# 统一更新会话信息
|
||||
user.session_key = session_key
|
||||
user.unionid = unionid
|
||||
user.save()
|
||||
created = False
|
||||
created = False # 简化处理
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return Response({'error': f'Login failed: {str(e)}'}, status=500)
|
||||
|
||||
# 生成 Token
|
||||
signer = TimestampSigner()
|
||||
|
||||
@@ -20,8 +20,70 @@ export default function UserIndex() {
|
||||
try { await Taro.chooseAddress() } catch(e) {}
|
||||
}
|
||||
|
||||
const login = () => {
|
||||
Taro.reLaunch({ url: '/pages/index/index' })
|
||||
const login = async () => {
|
||||
try {
|
||||
// 1. 获取微信登录 Code
|
||||
const { code } = await Taro.login()
|
||||
if (!code) throw new Error('登录失败:无法获取 Code')
|
||||
|
||||
// 2. 调用后端登录 (仅 Code)
|
||||
const res = await Taro.request({
|
||||
url: 'https://market.quant-speed.com/api/wechat/login/',
|
||||
method: 'POST',
|
||||
data: { code }
|
||||
})
|
||||
|
||||
console.log('code:', code)
|
||||
|
||||
if (res.statusCode === 200 && res.data.token) {
|
||||
Taro.setStorageSync('token', res.data.token)
|
||||
Taro.setStorageSync('userInfo', res.data)
|
||||
setUserInfo(res.data)
|
||||
Taro.showToast({ title: '登录成功', icon: 'success' })
|
||||
} else {
|
||||
throw new Error(res.data.error || '登录请求失败')
|
||||
}
|
||||
} catch (e) {
|
||||
Taro.showToast({ title: e.message || '登录失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const getPhoneNumber = async (e) => {
|
||||
const { code: phoneCode, errMsg } = e.detail
|
||||
if (errMsg !== "getPhoneNumber:ok") {
|
||||
Taro.showToast({ title: '获取手机号失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
Taro.showLoading({ title: '登录中...' })
|
||||
// 1. 获取登录 Code
|
||||
const { code: loginCode } = await Taro.login()
|
||||
|
||||
// 2. 调用后端登录 (Code + PhoneCode)
|
||||
const res = await Taro.request({
|
||||
url: 'https://market.quant-speed.com/api/wechat/login/',
|
||||
method: 'POST',
|
||||
data: {
|
||||
code: loginCode,
|
||||
phone_code: phoneCode
|
||||
}
|
||||
})
|
||||
|
||||
Taro.hideLoading()
|
||||
|
||||
if (res.statusCode === 200 && res.data.token) {
|
||||
Taro.setStorageSync('token', res.data.token)
|
||||
Taro.setStorageSync('userInfo', res.data)
|
||||
setUserInfo(res.data)
|
||||
Taro.showToast({ title: '授权登录成功', icon: 'success' })
|
||||
} else {
|
||||
throw new Error(res.data.error || '登录失败')
|
||||
}
|
||||
} catch(err) {
|
||||
Taro.hideLoading()
|
||||
Taro.showToast({ title: err.message || '系统异常', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const serviceGroups = [
|
||||
@@ -65,9 +127,24 @@ export default function UserIndex() {
|
||||
</View>
|
||||
<View className='info-col'>
|
||||
<Text className='nickname'>{userInfo?.nickname || '未登录用户'}</Text>
|
||||
<Text className='uid'>ID: {userInfo ? '888888' : '----'}</Text>
|
||||
<Text className='uid'>ID: {userInfo ? (userInfo.phone_number || userInfo.id || '----') : '----'}</Text>
|
||||
{!userInfo && (
|
||||
<Button className='btn-login' onClick={login}>立即登录 / 注册</Button>
|
||||
<View className='login-btns'>
|
||||
<Button
|
||||
className='btn-login'
|
||||
onClick={login}
|
||||
style={{ marginRight: '10px' }}
|
||||
>
|
||||
游客登录
|
||||
</Button>
|
||||
<Button
|
||||
className='btn-login primary'
|
||||
openType="getPhoneNumber"
|
||||
onGetPhoneNumber={getPhoneNumber}
|
||||
>
|
||||
手机号快捷登录
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View className='card-bg-effect' />
|
||||
|
||||
Reference in New Issue
Block a user