From 236a2a87a58c03a58b942bcedf3834a011284ef8 Mon Sep 17 00:00:00 2001 From: jacy-dukang Date: Sun, 12 Jul 2026 21:18:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E4=BC=99=E4=BA=BA=E7=AB=AF=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=98=AF=E5=90=A6=E5=B7=B2=E7=BB=8F=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=EF=BC=8C=E5=90=A6=E5=88=99=E4=B8=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=BE=AE=E4=BF=A1=E4=B8=80=E9=94=AE=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/h5-partner/src/components/AuthGate.tsx | 2 +- apps/h5-partner/src/lib/storeDraft.ts | 6 ++++ apps/h5-partner/src/lib/wechat-auth.ts | 35 ++++++++++++++++++- apps/h5-partner/src/pages/LoginPage.tsx | 19 ++++++++-- apps/h5-partner/src/pages/StoreCreatePage.tsx | 17 ++++++--- packages/shared-types/src/partner.ts | 9 +++++ .../src/modules/iam/auth.service.ts | 1 + 7 files changed, 80 insertions(+), 9 deletions(-) diff --git a/apps/h5-partner/src/components/AuthGate.tsx b/apps/h5-partner/src/components/AuthGate.tsx index 862cebe..b01c2be 100644 --- a/apps/h5-partner/src/components/AuthGate.tsx +++ b/apps/h5-partner/src/components/AuthGate.tsx @@ -19,7 +19,7 @@ export default function AuthGate({ children }: { children: React.ReactNode }) { if (!authenticated && !PUBLIC_PATHS.has(location.pathname)) { const profile = getPartnerProfile(); - if (profile && hasPartnerWxSession()) { + if (profile && hasPartnerWxSession() && profile.hasWechat) { return ; } return ; diff --git a/apps/h5-partner/src/lib/storeDraft.ts b/apps/h5-partner/src/lib/storeDraft.ts index 233e7a0..4a5db73 100644 --- a/apps/h5-partner/src/lib/storeDraft.ts +++ b/apps/h5-partner/src/lib/storeDraft.ts @@ -111,6 +111,12 @@ export function clearStoreDraft(accountId?: string) { localStorage.removeItem(storeDraftKey(accountId)); } +/** 清除当前账号及遗留的全局录店草稿 */ +export function clearAllStoreDrafts(accountId?: string) { + clearStoreDraft(accountId); + localStorage.removeItem(STORE_DRAFT_KEY); +} + const PHONE_RE = /^1\d{10}$/; const BANK_RE = /^\d{16,19}$/; diff --git a/apps/h5-partner/src/lib/wechat-auth.ts b/apps/h5-partner/src/lib/wechat-auth.ts index 7f3d7ec..47e721a 100644 --- a/apps/h5-partner/src/lib/wechat-auth.ts +++ b/apps/h5-partner/src/lib/wechat-auth.ts @@ -1,8 +1,12 @@ import type { ClientRuntimeConfig, WechatLoginResult } from '@dukang/shared-types'; +import type { PartnerPhoneCheckResponse } from '@dukang/shared-types'; import { isWxAuthorizeEnabled } from '@dukang/shared-types'; import { WECHAT_INAPP_REQUIRED_MSG } from '@dukang/weixin-sdk'; import { isWechatEnv, weixinSdk } from './weixin'; -import { request, saveWechatSession, type PartnerSessionPayload } from './api'; +import { getPartnerProfile, request, saveWechatSession, type PartnerSessionPayload, type PartnerSessionProfile } from './api'; + +export const PARTNER_WECHAT_LOGIN_HINT = + '请先使用手机验证码登录,登录后将自动关联微信'; export type PartnerProfile = { id: string; @@ -13,6 +17,35 @@ export type PartnerProfile = { isPrimary?: boolean; }; +export function partnerHasWechatBinding( + profile: PartnerProfile | PartnerSessionProfile | null | undefined, +): boolean { + return !!profile?.hasWechat; +} + +/** 微信一键登录前:账号须已绑定 wxOpenId(本地缓存或手机号校验) */ +export async function canPartnerUseWechatLogin(options: { + profile?: PartnerProfile | PartnerSessionProfile | null; + phone?: string; +}): Promise { + const cached = options.profile ?? getPartnerProfile(); + if (partnerHasWechatBinding(cached)) return true; + + const normalized = options.phone?.trim() ?? ''; + if (!/^1\d{10}$/.test(normalized)) return false; + + try { + const res = await request('PARTNER_H5', '/partner/auth/phone/check', { + method: 'POST', + body: JSON.stringify({ phone: normalized }), + silent: true, + }); + return !!res.hasWechat; + } catch { + return false; + } +} + export async function fetchClientConfig(): Promise { return request('PARTNER_H5', '/common/client-config'); } diff --git a/apps/h5-partner/src/pages/LoginPage.tsx b/apps/h5-partner/src/pages/LoginPage.tsx index 3b49fa9..902404c 100644 --- a/apps/h5-partner/src/pages/LoginPage.tsx +++ b/apps/h5-partner/src/pages/LoginPage.tsx @@ -14,8 +14,10 @@ import { usePartnerSession } from '../contexts/PartnerSessionContext'; import { partnerHomePath } from '../lib/partnerAccess'; import { bindPartnerWechatAfterSmsLogin, + canPartnerUseWechatLogin, fetchClientConfig, loginPartnerWithWechat, + PARTNER_WECHAT_LOGIN_HINT, } from '../lib/wechat-auth'; import { isWechatEnv } from '../lib/weixin'; @@ -168,6 +170,14 @@ export default function LoginPage() { setMsg(WECHAT_INAPP_REQUIRED_MSG); return; } + const canUseWechat = await canPartnerUseWechatLogin({ + profile: savedProfile, + phone, + }); + if (!canUseWechat) { + setMsg(PARTNER_WECHAT_LOGIN_HINT); + return; + } setWxLoading(true); try { const session = await loginPartnerWithWechat(); @@ -183,7 +193,12 @@ export default function LoginPage() { } if (quick) { - const canWechatQuick = wxAuthorize && isWechatEnv() && hasPartnerWxSession() && !!savedProfile; + const canWechatQuick = + wxAuthorize && + isWechatEnv() && + hasPartnerWxSession() && + !!savedProfile && + savedProfile.hasWechat === true; return (
@@ -334,7 +349,7 @@ export default function LoginPage() {
- {hasPartnerWxSession() && savedProfile && ( + {hasPartnerWxSession() && savedProfile?.hasWechat && ( 微信快捷登录 )} diff --git a/apps/h5-partner/src/pages/StoreCreatePage.tsx b/apps/h5-partner/src/pages/StoreCreatePage.tsx index caa5eff..51d01cc 100644 --- a/apps/h5-partner/src/pages/StoreCreatePage.tsx +++ b/apps/h5-partner/src/pages/StoreCreatePage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; @@ -24,7 +24,7 @@ import { isSubAccount } from '../lib/partnerAccess'; import { - clearStoreDraft, + clearAllStoreDrafts, defaultStoreForm, @@ -114,6 +114,8 @@ export default function StoreCreatePage() { const [smsHint, setSmsHint] = useState(''); + const draftSaveDisabledRef = useRef(false); + function reportFormError(message: string) { setSubmitError(message); } @@ -137,9 +139,8 @@ export default function StoreCreatePage() { useEffect(() => { - + if (draftSaveDisabledRef.current) return; saveStoreDraft({ step, form }, accountId); - }, [step, form, accountId]); @@ -569,7 +570,13 @@ export default function StoreCreatePage() { }); - clearStoreDraft(accountId); + draftSaveDisabledRef.current = true; + clearAllStoreDrafts(accountId); + setForm(defaultStoreForm()); + setFieldErrors({}); + setSubmitError(''); + setSmsHint(''); + setParams({ step: '1' }, { replace: true }); toastSuccess('门店录入成功'); navigate(`/stores/${result.store.id}`); } catch (e) { diff --git a/packages/shared-types/src/partner.ts b/packages/shared-types/src/partner.ts index 61ea3de..847e132 100644 --- a/packages/shared-types/src/partner.ts +++ b/packages/shared-types/src/partner.ts @@ -73,6 +73,15 @@ export interface PartnerStorePhoneAvailableResponse { needConfirm?: boolean; } +/** 合伙人登录前手机号校验 */ +export interface PartnerPhoneCheckResponse { + ok: boolean; + maskedPhone: string; + name: string; + companyName?: string; + hasWechat?: boolean; +} + export interface PartnerWeeklyReportPeriod { startDate: string; endDate: string; diff --git a/server/dukang-api/src/modules/iam/auth.service.ts b/server/dukang-api/src/modules/iam/auth.service.ts index 3cc250f..cb22ad4 100644 --- a/server/dukang-api/src/modules/iam/auth.service.ts +++ b/server/dukang-api/src/modules/iam/auth.service.ts @@ -264,6 +264,7 @@ export class AuthService { maskedPhone: this.maskPhone(normalizedPhone), name: account.name, companyName: primary.companyName, + hasWechat: !!account.wxOpenId, }; }