feat: openid 即可区分用户 — openid-login 降级建档(CRMEB 查不到也建档)+ 用户中心微信授权登录按钮
This commit is contained in:
@@ -108,10 +108,13 @@ class OpenidRequest(BaseModel):
|
|||||||
|
|
||||||
@router.post("/openid-login")
|
@router.post("/openid-login")
|
||||||
async def openid_login(req: OpenidRequest) -> TokenResponse:
|
async def openid_login(req: OpenidRequest) -> TokenResponse:
|
||||||
"""仅传 openid → 调 CRMEB 获取用户信息 → 创建/更新 User → 返回 token"""
|
"""仅传 openid → 调 CRMEB 获取用户信息(查不到也降级建档)→ 创建/更新 User → 返回 token"""
|
||||||
if not settings.crmeb_base_url or not settings.crmeb_api_key:
|
if not settings.crmeb_base_url or not settings.crmeb_api_key:
|
||||||
raise HTTPException(status_code=500, detail="CRMEB 未配置")
|
raise HTTPException(status_code=500, detail="CRMEB 未配置")
|
||||||
|
|
||||||
|
# 尝试从 CRMEB 拉取用户资料;失败(新用户/网络)时降级为匿名建档,openid 即身份
|
||||||
|
nickname, phone, avatar = "", "", ""
|
||||||
|
try:
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
resp = await client.get(
|
resp = await client.get(
|
||||||
f"{settings.crmeb_base_url}/api/internal/user-info",
|
f"{settings.crmeb_base_url}/api/internal/user-info",
|
||||||
@@ -120,28 +123,31 @@ async def openid_login(req: OpenidRequest) -> TokenResponse:
|
|||||||
timeout=10,
|
timeout=10,
|
||||||
)
|
)
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
|
if data.get("code") == 200:
|
||||||
if data.get("code") != 200:
|
info = data.get("data") or {}
|
||||||
raise HTTPException(status_code=400, detail="获取用户信息失败")
|
nickname = (info.get("nickname") or "").strip()
|
||||||
|
phone = (info.get("phone") or "").strip()
|
||||||
info = data["data"]
|
avatar = (info.get("avatar") or "").strip()
|
||||||
|
except Exception:
|
||||||
|
pass # CRMEB 不可用时仍允许 openid 建档
|
||||||
|
|
||||||
# 查找或创建用户
|
# 查找或创建用户
|
||||||
user = await User.find_one(User.openid == req.openid)
|
user = await User.find_one(User.openid == req.openid)
|
||||||
if not user:
|
if not user:
|
||||||
user = User(
|
user = User(
|
||||||
openid=req.openid,
|
openid=req.openid,
|
||||||
name=info.get("nickname", "") or "",
|
name=nickname,
|
||||||
phone=info.get("phone") or None,
|
phone=phone or None,
|
||||||
avatar_url=info.get("avatar") or None,
|
avatar_url=avatar or None,
|
||||||
)
|
)
|
||||||
await user.insert()
|
await user.insert()
|
||||||
else:
|
else:
|
||||||
user.name = info.get("nickname", "") or user.name
|
if nickname:
|
||||||
if info.get("phone"):
|
user.name = nickname
|
||||||
user.phone = info["phone"]
|
if phone:
|
||||||
if info.get("avatar"):
|
user.phone = phone
|
||||||
user.avatar_url = info["avatar"]
|
if avatar:
|
||||||
|
user.avatar_url = avatar
|
||||||
await user.save()
|
await user.save()
|
||||||
|
|
||||||
token = _build_token(user)
|
token = _build_token(user)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { Button } from "../components/ui/button";
|
|||||||
import { Card } from "../components/ui/card";
|
import { Card } from "../components/ui/card";
|
||||||
import { Badge } from "../components/ui/badge";
|
import { Badge } from "../components/ui/badge";
|
||||||
import { useUser, getPregnancyStage } from "../context/UserContext";
|
import { useUser, getPregnancyStage } from "../context/UserContext";
|
||||||
|
import bridge from "../services/bridge";
|
||||||
|
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -287,6 +288,22 @@ export function ProfilePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* 微信授权登录:未绑定手机号时显示,点击跳小程序登录页完善资料 */}
|
||||||
|
{!user.phone && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
onClick={() => bridge.requireAuth()}
|
||||||
|
className="w-full py-3 rounded-2xl bg-white border border-morandi-200 text-morandi-500 text-sm font-medium flex items-center justify-center gap-2 shadow-sm hover:bg-morandi-50"
|
||||||
|
>
|
||||||
|
<Shield className="w-4 h-4" />
|
||||||
|
微信授权登录 · 完善资料
|
||||||
|
</button>
|
||||||
|
<p className="text-center text-xs text-gray-400 mt-2">
|
||||||
|
绑定手机号后可同步头像昵称,数据不丢失
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 基本信息 */}
|
{/* 基本信息 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user