diff --git a/apps/h5-partner/src/lib/leaderboard.ts b/apps/h5-partner/src/lib/leaderboard.ts index bc00108..ff02c5b 100644 --- a/apps/h5-partner/src/lib/leaderboard.ts +++ b/apps/h5-partner/src/lib/leaderboard.ts @@ -5,6 +5,7 @@ export function fetchPartnerLeaderboard(period: PartnerLeaderboardPeriod = 'tota return request( 'PARTNER_H5', `/partner/dashboard/leaderboard?period=${period}`, + { silent: true }, ); } diff --git a/apps/h5-partner/src/lib/partnerAccess.ts b/apps/h5-partner/src/lib/partnerAccess.ts index e5de491..d0646e7 100644 --- a/apps/h5-partner/src/lib/partnerAccess.ts +++ b/apps/h5-partner/src/lib/partnerAccess.ts @@ -19,6 +19,33 @@ export function hasPartnerPermission( return account.permissions?.includes(permission) ?? false; } +export function hasAnyPartnerPermission( + account: PartnerMe | null | undefined, + permissions: PartnerPermissionKey[], +): boolean { + return permissions.some((p) => hasPartnerPermission(account, p)); +} + +/** 与后端 GET /partner/dashboard 权限点一致 */ +export function canAccessPartnerDashboard(account: PartnerMe | null | undefined): boolean { + return hasAnyPartnerPermission(account, [ + 'store:create', + 'store:manage', + 'order:view', + 'warehouse:manage', + ]); +} + +/** 门店列表/拓店统计所需权限 */ +export function canAccessPartnerStores(account: PartnerMe | null | undefined): boolean { + return hasAnyPartnerPermission(account, ['store:create', 'store:manage']); +} + +/** 与后端 GET /partner/orders 权限点一致 */ +export function canAccessPartnerOrders(account: PartnerMe | null | undefined): boolean { + return hasAnyPartnerPermission(account, ['order:view', 'warehouse:manage']); +} + /** 仓库管理员:warehouse:manage,或仅有 order:view(无门店权限) */ export function isWarehouseStaff(account: PartnerMe | null | undefined): boolean { if (!account || isPrimaryAccount(account)) return false; diff --git a/apps/h5-partner/src/pages/HomePage.tsx b/apps/h5-partner/src/pages/HomePage.tsx index c961441..c7be2fb 100644 --- a/apps/h5-partner/src/pages/HomePage.tsx +++ b/apps/h5-partner/src/pages/HomePage.tsx @@ -3,7 +3,13 @@ import { Link, useNavigate } from 'react-router-dom'; import type { PartnerLeaderboardEntry } from '@dukang/shared-types'; import { isLoggedIn, request } from '../lib/api'; import { fetchPartnerLeaderboard } from '../lib/leaderboard'; -import { getPartnerNavKind, hasWarehouseAccess } from '../lib/partnerAccess'; +import { + canAccessPartnerDashboard, + canAccessPartnerOrders, + canAccessPartnerStores, + getPartnerNavKind, + hasWarehouseAccess, +} from '../lib/partnerAccess'; import { usePartnerSession } from '../contexts/PartnerSessionContext'; function fmtMoney(n: number) { @@ -136,6 +142,9 @@ export default function HomePage() { const isPrimary = navKind === 'primary'; const isWarehouse = navKind === 'warehouse_staff'; const warehouseOk = hasWarehouseAccess(account); + const canOrders = canAccessPartnerOrders(account) && warehouseOk && (isPrimary || isWarehouse); + const canDashboard = canAccessPartnerDashboard(account) && !isWarehouse; + const canStores = canAccessPartnerStores(account) && !isWarehouse; const [dash, setDash] = useState | null>(null); const [stores, setStores] = useState>>([]); @@ -148,20 +157,34 @@ export default function HomePage() { useEffect(() => { if (!isLoggedIn()) { navigate('/login'); return; } - if ((isWarehouse || isPrimary) && warehouseOk) { - request<{ list: Array> }>('PARTNER_H5', '/partner/orders') + // 等 session 带上账号后再按权限发请求,避免无权限接口弹错 + if (!account) return; + + if (canOrders) { + request<{ list: Array> }>('PARTNER_H5', '/partner/orders', { silent: true }) .then((data) => setOrders(Array.isArray(data.list) ? data.list : [])) .catch(() => setOrders([])); } else { setOrders([]); } - if (!isWarehouse) { - request>('PARTNER_H5', '/partner/dashboard').then(setDash).catch(() => {}); - request>>('PARTNER_H5', '/partner/stores').then(setStores).catch(() => []); + + if (canDashboard) { + request>('PARTNER_H5', '/partner/dashboard', { silent: true }) + .then(setDash) + .catch(() => setDash(null)); } else { - request>('PARTNER_H5', '/partner/dashboard').then(setDash).catch(() => {}); + setDash(null); } - // 主账号与全部子账号均可查看同团队贡献榜 + + if (canStores) { + request>>('PARTNER_H5', '/partner/stores', { silent: true }) + .then((data) => setStores(Array.isArray(data) ? data : [])) + .catch(() => setStores([])); + } else { + setStores([]); + } + + // 主账号与全部子账号均可查看同团队贡献榜(后端不校验业务权限点) fetchPartnerLeaderboard('month') .then((data) => { setLeaderboardEntries(data.list.slice(0, LEADERBOARD_PREVIEW_LIMIT)); @@ -169,7 +192,7 @@ export default function HomePage() { .catch(() => { setLeaderboardEntries([]); }); - }, [navigate, isWarehouse, isPrimary, warehouseOk]); + }, [navigate, account, canOrders, canDashboard, canStores]); const storeCount = Number(dash?.storeCount || stores.length || 0); const orderStats = useMemo(() => summarizeOrders(orders), [orders]); @@ -248,7 +271,7 @@ export default function HomePage() {

订单管理

)} - {(isPrimary || isWarehouse) && warehouseOk && ( + {canOrders && ( )} - {!isWarehouse && ( + {canStores && ( <>
diff --git a/server/dukang-api/src/modules/store/store.controller.ts b/server/dukang-api/src/modules/store/store.controller.ts index 30a19e8..efa3d67 100644 --- a/server/dukang-api/src/modules/store/store.controller.ts +++ b/server/dukang-api/src/modules/store/store.controller.ts @@ -79,18 +79,17 @@ export class PartnerStoreController { @Controller('partner/dashboard') @UseGuards(JwtAuthGuard, PartnerPermissionGuard) -@RequirePartnerPermissions('store:create', 'store:manage', 'order:view', 'warehouse:manage') export class PartnerDashboardController { constructor(private readonly storeService: StoreService) {} @Get() + @RequirePartnerPermissions('store:create', 'store:manage', 'order:view', 'warehouse:manage') dashboard(@CurrentUser() user: AuthUser) { return this.storeService.partnerDashboard(user.actorId); } - @Get('leaderboard') /** 任意合伙人子账号可看同主账号排行(激励),不校验业务权限点 */ - @RequirePartnerPermissions() + @Get('leaderboard') leaderboard( @CurrentUser() user: AuthUser, @Query('period') period?: string,