diff --git a/apps/mini-user/src/pages/home/index.tsx b/apps/mini-user/src/pages/home/index.tsx index dd9c7e5..a4ef76d 100644 --- a/apps/mini-user/src/pages/home/index.tsx +++ b/apps/mini-user/src/pages/home/index.tsx @@ -110,16 +110,6 @@ export default function HomePage() { }); }, []); - useDidShow(() => { - syncTabBarSelected(0); - void capturePromoSceneAndTouchScan(); - void loadMiniHome(); - void resolveUserCity().then((resolved) => { - setDisplayCity(resolved.displayCity); - setCityCode(getCityCodeForCatalog(resolved)); - }); - }); - const loadProducts = useCallback(() => { setLoading(true); return request(`/catalog/products?cityCode=${encodeURIComponent(cityCode)}`) @@ -130,6 +120,18 @@ export default function HomePage() { .finally(() => setLoading(false)); }, [cityCode]); + useDidShow(() => { + syncTabBarSelected(0); + void capturePromoSceneAndTouchScan(); + void loadMiniHome(); + // 白名单商品按登录手机号过滤;登录后 switchTab 回首页不会卸载页面,须重新拉列表 + void loadProducts(); + void resolveUserCity().then((resolved) => { + setDisplayCity(resolved.displayCity); + setCityCode(getCityCodeForCatalog(resolved)); + }); + }); + useEffect(() => { void loadProducts(); }, [loadProducts]); diff --git a/apps/mini-user/src/pages/product-detail/index.tsx b/apps/mini-user/src/pages/product-detail/index.tsx index 0d58126..a480ee7 100644 --- a/apps/mini-user/src/pages/product-detail/index.tsx +++ b/apps/mini-user/src/pages/product-detail/index.tsx @@ -1,6 +1,12 @@ -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { View, Text, Image } from '@tarojs/components'; -import Taro, { usePageScroll, useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro'; +import Taro, { + useDidShow, + usePageScroll, + useRouter, + useShareAppMessage, + useShareTimeline, +} from '@tarojs/taro'; import type { ProductDetailContentDto } from '@dukang/shared-types'; import PageShell from '../../components/PageShell'; import PageNavBar from '../../components/PageNavBar'; @@ -51,13 +57,32 @@ export default function ProductDetailPage() { setHeaderSolid(scrollTop > 100); }); - useEffect(() => { + const loadProduct = useCallback(() => { if (!productId) return; - request(`/catalog/products/${productId}`) - .then((p) => setProduct(normalizeFulfillmentFlags(p))) - .catch((e) => toast(e instanceof Error ? e.message : '加载失败')); + request(`/catalog/products/${productId}`) + .then((p) => { + if (!p) { + setProduct(null); + toast('商品不存在或暂未开放'); + return; + } + setProduct(normalizeFulfillmentFlags(p)); + }) + .catch((e) => { + setProduct(null); + toast(e instanceof Error ? e.message : '加载失败'); + }); }, [productId]); + useEffect(() => { + loadProduct(); + }, [loadProduct]); + + // 登录后返回详情须带 token 重拉,否则白名单商品会一直空白 + useDidShow(() => { + loadProduct(); + }); + const sharePayload = useMemo( () => ({ title: product?.name || DEFAULT_SHARE_TITLE, diff --git a/server/dukang-api/src/common/guards/optional-jwt-auth.guard.ts b/server/dukang-api/src/common/guards/optional-jwt-auth.guard.ts index 1eb5103..2beb65a 100644 --- a/server/dukang-api/src/common/guards/optional-jwt-auth.guard.ts +++ b/server/dukang-api/src/common/guards/optional-jwt-auth.guard.ts @@ -7,6 +7,15 @@ import { JwtService } from '@nestjs/jwt'; import { CLIENT_APP_ACTOR_MAP, ClientApp } from '@dukang/shared-types'; import type { AuthUser } from './jwt-auth.guard'; +/** C 端 H5 / 小程序同属 USER,目录白名单只认 actorId→手机号,允许端间 token 兼容 */ +const USER_CLIENT_APPS = new Set([ClientApp.USER_MINI, ClientApp.USER_H5]); + +function clientAppCompatible(headerApp: ClientApp | undefined, payloadApp: unknown): boolean { + if (!headerApp || payloadApp == null) return false; + if (headerApp === payloadApp) return true; + return USER_CLIENT_APPS.has(headerApp) && USER_CLIENT_APPS.has(payloadApp as ClientApp); +} + @Injectable() export class OptionalJwtAuthGuard implements CanActivate { constructor(private readonly jwtService: JwtService) {} @@ -20,7 +29,7 @@ export class OptionalJwtAuthGuard implements CanActivate { try { const payload = this.jwtService.verify(auth.slice(7)); const clientApp = req.headers['x-client-app'] as ClientApp; - if (!clientApp || payload.clientApp !== clientApp) return true; + if (!clientAppCompatible(clientApp, payload.clientApp)) return true; const expectedActor = CLIENT_APP_ACTOR_MAP[clientApp]; if (payload.actorType !== expectedActor) return true; req.user = { diff --git a/server/dukang-api/src/modules/catalog/catalog.service.ts b/server/dukang-api/src/modules/catalog/catalog.service.ts index 2430397..c64b9c7 100644 --- a/server/dukang-api/src/modules/catalog/catalog.service.ts +++ b/server/dukang-api/src/modules/catalog/catalog.service.ts @@ -81,11 +81,9 @@ export class CatalogService { return serializeBigInt( visible.map((p) => { const media = mapProductMedia(p, resourceMap.get(p.id.toString()) ?? []); - const { visibilityPhones, ...rest } = p; + const { visibilityPhones: _phones, visibilityWhitelistEnabled: _wl, ...rest } = p; return { ...rest, - visibilityWhitelistEnabled: p.visibilityWhitelistEnabled, - visibilityPhones: visibilityPhones.map((row) => row.phone), benefitAmount: p.benefitAmount ?? p.price, price: Number(p.price), benefitDisplay: Number(p.benefitAmount ?? p.price), @@ -119,11 +117,9 @@ export class CatalogService { }); const media = mapProductMedia(product, resources); - const { visibilityPhones, ...rest } = product; + const { visibilityPhones: _phones, visibilityWhitelistEnabled: _wl, ...rest } = product; return serializeBigInt({ ...rest, - visibilityWhitelistEnabled: product.visibilityWhitelistEnabled, - visibilityPhones: visibilityPhones.map((row) => row.phone), benefitAmount: product.benefitAmount ?? product.price, price: Number(product.price), ...media,