From 2914b401718db7e70b3e68655f044ca0a4dbe4aa Mon Sep 17 00:00:00 2001 From: Pine Date: Tue, 25 Aug 2026 20:22:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(miniprogram):=20=E7=99=BB=E5=BD=95/?= =?UTF-8?q?=E8=B5=84=E6=96=99=E6=94=B9=E8=B5=B0=E5=B9=B3=E5=8F=B0=20/auth?= =?UTF-8?q?=EF=BC=88=E7=BB=9F=E4=B8=80=E8=B4=A6=E5=8F=B7=20+=20=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=20JWT=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 阶段2: - sendCode/phoneLogin/wxLogin/getMe/updateProfile 端点 /api/auth/*→/auth/*, pine_token 存平台 JWT;按平台 profile 契约映射 pine_user (username/name/avatar/phone/phoneBound/status/topics/source)。 - sendCode 兼容平台 stub_code→debugCode。 - bindPhone/wxPhoneBind 保留 /api/auth(阶段3 已使其读写平台 users + 验平台 JWT)。 - 手机绑定、业务(报名/签到/测评)使用平台 JWT 鉴权。 Co-Authored-By: Claude --- miniprogram/src/utils/auth.js | 97 ++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/miniprogram/src/utils/auth.js b/miniprogram/src/utils/auth.js index 7da1d82..9f0f557 100644 --- a/miniprogram/src/utils/auth.js +++ b/miniprogram/src/utils/auth.js @@ -1,23 +1,38 @@ import Taro from '@tarojs/taro'; import { request } from './api'; -/* 用户端认证:手机号 + 短信验证码(与 web 端一致,见 小程序同步约束.md) */ +/* 用户端认证(统一账号):登录/资料走平台 /auth(单一 users + 平台 JWT); + 手机绑定走培训 /api/auth(阶段3 后同样读写平台 users + 验平台 JWT)。 + 与 web 端一致,见 小程序同步约束.md */ + +/** pine_user 契约字段(各页面读取):username/name/avatar/phone/phoneBound/status/topics/source */ +function pickUser(d) { + return { + username: d.username, + name: d.name || d.nickname || d.username || '微信用户', + avatar: d.avatar || '', + phone: d.phone || '', + phoneBound: !!d.phoneBound, + status: d.status || '', + topics: Array.isArray(d.topics) ? d.topics : [], + source: d.source || '' + }; +} + +function saveUser(d) { + Taro.setStorageSync('pine_user', pickUser(d)); +} + export async function sendCode(phone) { - return request('POST', '/api/auth/send-code', { phone }); + const data = await request('POST', '/auth/send-code', { phone }); + return { ...data, debugCode: data.stub_code ?? data.debugCode }; } export async function phoneLogin(phone, code) { - const data = await request('POST', '/api/auth/phone-login', { phone, code }); + const data = await request('POST', '/auth/phone-login', { phone, code }); if (data.token) { Taro.setStorageSync('pine_token', data.token); - // 手机号登录账号即以手机号为账号,天然已绑定手机号(phoneBound=true),保持一致避免再次提示绑定 - Taro.setStorageSync('pine_user', { - username: data.username, - name: data.name || data.username, - avatar: '', - phone: data.username, - phoneBound: true - }); + saveUser(data); } return data; } @@ -35,23 +50,42 @@ export function logout() { Taro.removeStorageSync('pine_user'); } -/* ---------------- 微信登录 / 手机绑定 / 我的资料 ---------------- */ -/** 微信一键登录:wx.login 拿 code(演示后端用 code 哈希做伪 openid),可携带昵称/头像 */ +/* ---------------- 微信登录 / 我的资料(平台 /auth) ---------------- */ +/** 微信一键登录:wx.login 拿 code → 平台 /auth/wx-login(需后端配 PINEAGENTS_WECHAT_LOGIN=true) */ export async function wxLogin({ code, nickName, avatarUrl }) { - const data = await request('POST', '/api/auth/wx-login', { code, nickName, avatarUrl }); + const data = await request('POST', '/auth/wx-login', { code, nickName, avatarUrl }); if (data.token) { Taro.setStorageSync('pine_token', data.token); - Taro.setStorageSync('pine_user', { - username: data.username, - name: data.name || '微信用户', - avatar: data.avatar || '', - phone: '', - phoneBound: !!data.phoneBound - }); + saveUser(data); } return data; } +/** 拉取当前账号资料(刷新昵称/头像/手机绑定状态)——平台 /auth/me 返回扁平 profile */ +export async function getMe() { + const data = await request('GET', '/auth/me', {}, true); + if (data && data.username) { + const u = getUser() || {}; + const picked = pickUser(data); + Taro.setStorageSync('pine_user', { ...u, ...picked }); + return { ok: true, user: picked }; + } + return data; +} + +/** 更新当前用户资料(昵称/头像 + 报名资料 status/topics/source)——平台 /auth/update-profile */ +export async function updateProfile(body) { + const data = await request('POST', '/auth/update-profile', body, true); + if (data && data.username) { + const u = getUser() || {}; + const picked = pickUser(data); + Taro.setStorageSync('pine_user', { ...u, ...picked }); + return { ok: true, user: picked }; + } + return data; +} + +/* ---------------- 手机绑定(培训 /api/auth,阶段3 读写平台 users + 验平台 JWT) ---------------- */ /** 绑定手机:验证码校验后把手机号挂到当前账号 */ export async function bindPhone(phone, code) { const data = await request('POST', '/api/auth/bind-phone', { phone, code }, true); @@ -62,16 +96,6 @@ export async function bindPhone(phone, code) { return data; } -/** 拉取当前账号资料(刷新昵称/头像/手机绑定状态) */ -export async function getMe() { - const data = await request('GET', '/api/auth/me', {}, true); - if (data.ok && data.user) { - const u = getUser() || {}; - Taro.setStorageSync('pine_user', { ...u, ...data.user }); - } - return data; -} - /** 微信官方手机号一键绑定(getPhoneNumber 回调 code) */ export async function wxPhoneBind(code) { const data = await request('POST', '/api/auth/wx-phone', { code }, true); @@ -81,14 +105,3 @@ export async function wxPhoneBind(code) { } return data; } - -/** 更新当前用户资料(昵称/头像 + 报名资料 status/topics/source) */ -export async function updateProfile(data) { - const r = await request('POST', '/api/auth/update-profile', data, true); - if (r.ok && r.user) { - const u = getUser() || {}; - // 用后端返回的完整 user(含 nickname/avatar/phone/phoneBound/status/topics/source)覆盖内存资料 - Taro.setStorageSync('pine_user', { ...u, ...r.user }); - } - return r; -}