diff --git a/src/core/navigation/adminNavigation.ts b/src/core/navigation/adminNavigation.ts index d675e3b2..08e58634 100644 --- a/src/core/navigation/adminNavigation.ts +++ b/src/core/navigation/adminNavigation.ts @@ -186,6 +186,7 @@ export const adminNavigation: AdminNavigationNode[] = [ title: '自营业务', icon: PrototypeSalesIcon, children: [ + { key: 'self-operated-overview', title: '经营总览', icon: PrototypeDashboardIcon, path: '/self-operated/overview', permission: 'platform:self-operated-analytics:list' }, { key: 'self-operated-sales', title: '销量分析', icon: ClipboardList, path: '/self-operated/sales', permission: 'platform:self-operated-analytics:list' }, { key: 'self-operated-inventory', title: '库存分析', icon: WarehouseIcon, path: '/self-operated/inventory', permission: 'platform:self-operated-analytics:list' }, { key: 'self-operated-collection', title: '数据采集', icon: Database, path: '/self-operated/collection', permission: 'platform:self-operated-analytics:list' }, diff --git a/src/modules/self-operated/AGENTS.md b/src/modules/self-operated/AGENTS.md index 9c430be3..0caa2c8e 100644 --- a/src/modules/self-operated/AGENTS.md +++ b/src/modules/self-operated/AGENTS.md @@ -1,6 +1,7 @@ # 自营经营分析 -- 模块入口 `routes.ts`;正式页面 `pages/SalesView.vue`、`InventoryView.vue`、`CollectionView.vue`。根路由、导航、IAM 由总控装配。 +- 模块入口 `routes.ts`;正式页面 `pages/OverviewView.vue`、`SalesView.vue`、`InventoryView.vue`、`CollectionView.vue`。根路由、导航、IAM 由总控装配。 +- 经营总览唯一设计入口 `docs/自营业务/经营总览图表设计.md`;`analyticsApi.dashboard` 独立于采集 `overview`。六项指标、日趋势、七桶和三榜共用一次服务端上下文;趋势范围不改变固定30日指标。`dashboardNavigation.ts` 校验下钻版本、日期、枚举、互斥条件和固定返回入口;库存严格下钻必须保留 `drilldownFrom=DASHBOARD`,过期禁止冒充原图结果。不得恢复货主独立筛选或面板。 - API 只经统一 `shared/api/http.ts` 消费 Lastleg 的平台自营统计合同;不得调用 Integration 或持有来源凭据。 - 北京时间 `Asia/Shanghai`,每 SKU 件数 `PCS`;货主不合并同名 SKU。未知数量保留空值,库存可售天数只用服务端结果。 - 经营查询固定服务端版本和 queryAsOf;显式刷新/切仓重新取上下文,轮询只读运行状态。请求取消和世代校验共同防迟到响应。 diff --git a/src/modules/self-operated/api/analytics.test.ts b/src/modules/self-operated/api/analytics.test.ts index 17a4a414..240110b5 100644 --- a/src/modules/self-operated/api/analytics.test.ts +++ b/src/modules/self-operated/api/analytics.test.ts @@ -5,6 +5,13 @@ const http = vi.hoisted(() => ({ get: vi.fn(), post: vi.fn() })) vi.mock('@/shared/api/http', () => ({ http, unwrapApiResponse: (value: { data: unknown }) => value.data })) beforeEach(() => { vi.clearAllMocks(); http.get.mockResolvedValue({ data: {} }); http.post.mockResolvedValue({ data: { taskNo: 'COL-001', status: 'QUEUED' } }) }) describe('self-operated platform transport contract', () => { + it('keeps dashboard business aggregation separate from sync-overview runtime polling', async () => { + const params = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', rangePreset: 'LAST_15_DAYS' as const, salesRunId: '11', inventoryRunId: '12', queryAsOf: '2026-10-13T00:00:00Z' } + await analyticsApi.dashboard(params) + expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/self-operated-analytics/dashboard', { params, signal: undefined }) + expect(params).not.toHaveProperty('customerCode') + expect(params).not.toHaveProperty('current') + }) it('uses local fixed-version owners, owner overview and inventory attention endpoints', async () => { const fixed = { sourceAccount: 'lingxing-primary', warehouseCode: 'WH-A', salesRunId: '11', inventoryRunId: '12', queryAsOf: '2026-10-12T01:00:00Z' } await analyticsApi.owners({ ...fixed, keyword: 'OWNER_', current: 2, size: 20 }) diff --git a/src/modules/self-operated/api/analytics.ts b/src/modules/self-operated/api/analytics.ts index 9e4f3b3c..1928d5e8 100644 --- a/src/modules/self-operated/api/analytics.ts +++ b/src/modules/self-operated/api/analytics.ts @@ -1,5 +1,5 @@ import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http' -import type { BusinessPage, BusinessQuery, DailyResponse, Metric, Overview, OwnerOption, OwnerOverview, OwnerOverviewQuery, OwnerQuery, RetryCommand, RetryResult, Run, RuntimePage, RuntimeQuery, SalesSummary, Scope, TaskDetail } from '../types' +import type { BusinessPage, BusinessQuery, Dashboard, DashboardQuery, DailyResponse, Metric, Overview, OwnerOption, OwnerOverview, OwnerOverviewQuery, OwnerQuery, RetryCommand, RetryResult, Run, RuntimePage, RuntimeQuery, SalesSummary, Scope, TaskDetail } from '../types' const base = '/platform/v1/last-leg/self-operated-analytics' async function get(path: string, params: object, signal?: AbortSignal): Promise { @@ -7,6 +7,7 @@ async function get(path: string, params: object, signal?: AbortSignal): Promi return unwrapApiResponse(response) } export const analyticsApi = { + dashboard: (query: DashboardQuery, signal?: AbortSignal) => get('dashboard', query, signal), owners: (query: OwnerQuery, signal?: AbortSignal) => get>('owners', query, signal), ownerOverview: (query: OwnerOverviewQuery, signal?: AbortSignal) => get('owner-overview', query, signal), summary: (query: BusinessQuery, signal?: AbortSignal) => get>('sku-sales-summary', query, signal), diff --git a/src/modules/self-operated/components/AnalyticsView.vue b/src/modules/self-operated/components/AnalyticsView.vue index 7aad677b..452fd124 100644 --- a/src/modules/self-operated/components/AnalyticsView.vue +++ b/src/modules/self-operated/components/AnalyticsView.vue @@ -1,6 +1,6 @@