93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
"""服务号绑定模块(手机号验证方式)。
|
||
|
||
用户关注服务号后,openid 存入 pending_service_binding 表。
|
||
用户在小程序中输入手机号,后端匹配系统用户并完成绑定。
|
||
"""
|
||
|
||
import logging
|
||
|
||
from fastapi import APIRouter, Depends
|
||
from pydantic import BaseModel
|
||
from sqlalchemy import select
|
||
from sqlalchemy.orm import Session
|
||
|
||
from backend.app.core.config import get_settings
|
||
from backend.app.core.error_codes import ErrorCode
|
||
from backend.app.core.exceptions import AppException
|
||
from backend.app.db import get_db_session
|
||
from backend.app.models.system import PendingServiceBinding, User
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter(tags=["h5-bind-service"])
|
||
|
||
|
||
class VerifyPhoneRequest(BaseModel):
|
||
"""手机号验证绑定请求"""
|
||
phone: str
|
||
|
||
|
||
@router.get("/api/h5/bind-service-qrcode")
|
||
def get_bind_qrcode():
|
||
"""返回服务号关注引导信息。"""
|
||
settings = get_settings()
|
||
if not settings.wechat_sa_app_id:
|
||
return {"configured": False, "message": "服务号未配置"}
|
||
return {
|
||
"configured": True,
|
||
"account_name": "特氟龙胶带大表哥",
|
||
"message": "请扫码关注服务号「特氟龙胶带大表哥」,或在微信中搜索同名服务号,关注后返回小程序输入手机号完成绑定。",
|
||
}
|
||
|
||
|
||
@router.post("/api/h5/verify-phone")
|
||
def verify_phone(
|
||
payload: VerifyPhoneRequest,
|
||
session: Session = Depends(get_db_session),
|
||
):
|
||
"""手机号验证绑定接口。
|
||
|
||
接收手机号,匹配系统用户,从 pending_service_binding 取最近的未绑定记录,
|
||
完成 service_open_id 的绑定。
|
||
"""
|
||
phone = payload.phone.strip()
|
||
if not phone:
|
||
raise AppException(code=ErrorCode.PARAM_ERROR, message="请输入手机号", status_code=400)
|
||
|
||
# 1. 按手机号匹配系统用户
|
||
user = session.execute(
|
||
select(User).where(User.mobile == phone, User.status == 1)
|
||
).scalar_one_or_none()
|
||
if user is None:
|
||
raise AppException(code=ErrorCode.NOT_FOUND, message="该手机号未注册系统账号", status_code=404)
|
||
|
||
# 2. 检查是否已绑定
|
||
if user.service_open_id:
|
||
return {"success": True, "message": f"{user.real_name},您的账号已绑定服务号通知", "already_bound": True}
|
||
|
||
# 3. 取最近的未绑定 pending binding
|
||
pending = session.execute(
|
||
select(PendingServiceBinding)
|
||
.where(PendingServiceBinding.bound == False)
|
||
.order_by(PendingServiceBinding.id.desc())
|
||
.limit(1)
|
||
).scalar_one_or_none()
|
||
|
||
if pending is None:
|
||
raise AppException(
|
||
code=ErrorCode.NOT_FOUND,
|
||
message="未找到待绑定记录。请先关注服务号,然后再试。",
|
||
status_code=404,
|
||
)
|
||
|
||
# 4. 完成绑定
|
||
user.service_open_id = pending.service_open_id
|
||
pending.bound = True
|
||
session.add(user)
|
||
session.add(pending)
|
||
session.commit()
|
||
|
||
logger.info("用户 %s (id=%s) 通过手机号验证绑定服务号 openid=%s", user.real_name, user.id, pending.service_open_id)
|
||
|
||
return {"success": True, "message": f"{user.real_name},绑定成功!您将在微信中收到订单提醒"}
|