137 lines
4.7 KiB
TypeScript
137 lines
4.7 KiB
TypeScript
import type { ClientRuntimeConfig, WechatLoginResult } 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 ShopSessionPayload } from './api';
|
||
|
||
export type ShopAccountProfile = {
|
||
id: string;
|
||
storeId: string;
|
||
name: string;
|
||
phone: string;
|
||
/** 是否已绑定微信(来自 /shop/auth/me account.hasWechat) */
|
||
hasWechat?: boolean;
|
||
wxOpenId?: string | null;
|
||
store?: { name: string };
|
||
};
|
||
|
||
export async function fetchClientConfig(): Promise<ClientRuntimeConfig> {
|
||
return request<ClientRuntimeConfig>('SHOP_H5', '/common/client-config');
|
||
}
|
||
|
||
export async function fetchShopAccount(): Promise<ShopAccountProfile> {
|
||
const me = await request<{
|
||
id?: string;
|
||
storeId?: string;
|
||
name?: string;
|
||
phone?: string;
|
||
account?: {
|
||
id?: string;
|
||
name?: string;
|
||
phone?: string;
|
||
hasWechat?: boolean;
|
||
};
|
||
store?: { name?: string } | null;
|
||
}>('SHOP_H5', '/shop/auth/me');
|
||
return {
|
||
id: String(me.account?.id ?? me.id ?? ''),
|
||
storeId: String(me.storeId ?? ''),
|
||
name: String(me.account?.name ?? me.name ?? ''),
|
||
phone: String(me.account?.phone ?? me.phone ?? ''),
|
||
hasWechat: !!me.account?.hasWechat,
|
||
store: me.store?.name ? { name: String(me.store.name) } : undefined,
|
||
};
|
||
}
|
||
|
||
export function needsWechatAuth(
|
||
profile: ShopAccountProfile | null,
|
||
config?: Pick<ClientRuntimeConfig, 'wxAuthorize'> | null,
|
||
): boolean {
|
||
if (config && !isWxAuthorizeEnabled(config)) return false;
|
||
const unbound = !!profile && !(profile.hasWechat || profile.wxOpenId);
|
||
return isWechatEnv() && unbound;
|
||
}
|
||
|
||
export async function checkNeedsWechatAuth(profile: ShopAccountProfile | null): Promise<boolean> {
|
||
const config = await fetchClientConfig();
|
||
return needsWechatAuth(profile, config);
|
||
}
|
||
|
||
export function sessionFromWechatLogin(result: WechatLoginResult): ShopSessionPayload | null {
|
||
if (!result.accessToken || !result.refreshToken) return null;
|
||
const store = result.store as Record<string, unknown> | undefined;
|
||
const stores = Array.isArray((result as { stores?: unknown }).stores)
|
||
? ((result as { stores: ShopSessionPayload['stores'] }).stores)
|
||
: undefined;
|
||
const account = (result as { account?: ShopSessionPayload['account'] }).account;
|
||
return {
|
||
accessToken: result.accessToken,
|
||
refreshToken: result.refreshToken,
|
||
stores,
|
||
account,
|
||
selectedStoreId: (result as { selectedStoreId?: string }).selectedStoreId,
|
||
store: store
|
||
? {
|
||
id: String(store.id ?? account?.id ?? ''),
|
||
storeId: String(store.storeId ?? ''),
|
||
name: String(store.name ?? account?.name ?? ''),
|
||
phone: String(store.phone ?? account?.phone ?? ''),
|
||
storeName: String(store.storeName ?? store.name ?? ''),
|
||
isPrimary: account?.isPrimary,
|
||
stores,
|
||
}
|
||
: undefined,
|
||
};
|
||
}
|
||
|
||
/** 处理微信登录/绑定结果,写入 7 天免登录 session */
|
||
export function handleShopWechatLoginResult(result: WechatLoginResult): ShopSessionPayload | null {
|
||
const session = sessionFromWechatLogin(result);
|
||
if (!session) return null;
|
||
saveWechatSession(session);
|
||
return session;
|
||
}
|
||
|
||
/** @deprecated 使用 handleShopWechatLoginResult */
|
||
export function saveShopWechatAuth(result: WechatLoginResult): boolean {
|
||
return !!handleShopWechatLoginResult(result);
|
||
}
|
||
|
||
export async function handleShopWechatCallback(): Promise<WechatLoginResult | null> {
|
||
if (!isWechatEnv()) return null;
|
||
const config = await fetchClientConfig();
|
||
if (!isWxAuthorizeEnabled(config)) return null;
|
||
return weixinSdk.handleOAuthCallback();
|
||
}
|
||
|
||
/**
|
||
* 微信一键登录(已绑定微信的门店账号免验证码)。
|
||
* 返回 session = 已登录;void = 已跳转授权页等待回调。
|
||
*/
|
||
export async function loginShopWithWechat(): Promise<ShopSessionPayload | null | void> {
|
||
const config = await fetchClientConfig();
|
||
if (!isWxAuthorizeEnabled(config)) return null;
|
||
if (!isWechatEnv()) {
|
||
throw new Error(WECHAT_INAPP_REQUIRED_MSG);
|
||
}
|
||
const result = await weixinSdk.login();
|
||
if (result) return handleShopWechatLoginResult(result);
|
||
}
|
||
|
||
/** 短信登录成功后于微信内自动发起 OAuth,绑定 openId 便于后续免登 */
|
||
export async function bindShopWechatAfterSmsLogin(): Promise<void> {
|
||
const config = await fetchClientConfig();
|
||
if (!isWxAuthorizeEnabled(config)) return;
|
||
if (!isWechatEnv()) return;
|
||
await weixinSdk.login();
|
||
}
|
||
|
||
export async function authorizeShopWechat(): Promise<WechatLoginResult | void> {
|
||
const config = await fetchClientConfig();
|
||
if (!isWxAuthorizeEnabled(config)) return;
|
||
if (!isWechatEnv()) {
|
||
throw new Error(WECHAT_INAPP_REQUIRED_MSG);
|
||
}
|
||
return weixinSdk.login();
|
||
}
|