109 lines
3.6 KiB
Plaintext
109 lines
3.6 KiB
Plaintext
import { computeDateRange, toDateOnly } from './dateRange.uts'
|
|
import { rpcOrEmptyArray, rpcOrNull } from './rpc.uts'
|
|
|
|
export type ProductOverview = {
|
|
total_products: number
|
|
product_growth: number
|
|
hot_products: number
|
|
turnover_rate: number
|
|
turnover_growth: number
|
|
avg_stock: number
|
|
stock_growth: number
|
|
}
|
|
|
|
export type ProductRank = { id: string; rank: number; name: string; sales: number; growth: number }
|
|
|
|
export type ProductTrendRow = { date: string; gmv: number; qty: number; orders: number }
|
|
|
|
function safeNumber(v: any): number {
|
|
const n = Number(v)
|
|
return isFinite(n) ? n : 0
|
|
}
|
|
|
|
export async function fetchProductOverview(period: string): Promise<ProductOverview> {
|
|
const { startIso, endIso } = computeDateRange(period)
|
|
const row = await rpcOrNull('rpc_product_insights_overview', {
|
|
p_start: toDateOnly(startIso),
|
|
p_end: toDateOnly(endIso)
|
|
} as any)
|
|
|
|
const obj: any = row != null ? row : ({} as any)
|
|
return {
|
|
total_products: safeNumber(obj.getAny?.('total_products') ?? 0),
|
|
product_growth: safeNumber(obj.getAny?.('product_growth') ?? 0),
|
|
hot_products: safeNumber(obj.getAny?.('hot_products') ?? 0),
|
|
turnover_rate: safeNumber(obj.getAny?.('turnover_rate') ?? 0),
|
|
turnover_growth: safeNumber(obj.getAny?.('turnover_growth') ?? 0),
|
|
avg_stock: safeNumber(obj.getAny?.('avg_stock') ?? 0),
|
|
stock_growth: safeNumber(obj.getAny?.('stock_growth') ?? 0)
|
|
}
|
|
}
|
|
|
|
export async function fetchTopProducts(period: string, limit: number = 10): Promise<Array<ProductRank>> {
|
|
const { startIso, endIso } = computeDateRange(period)
|
|
const rows = await rpcOrEmptyArray('rpc_analytics_top_products', {
|
|
p_start_date: toDateOnly(startIso),
|
|
p_end_date: toDateOnly(endIso),
|
|
p_limit: limit
|
|
} as any)
|
|
|
|
const list: Array<ProductRank> = []
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const r: any = rows[i]
|
|
list.push({
|
|
id: `${r.getAny?.('id') ?? i}`,
|
|
rank: i + 1,
|
|
name: `${r.getAny?.('name') ?? '未知商品'}`,
|
|
sales: safeNumber(r.getAny?.('sales') ?? 0),
|
|
growth: safeNumber(r.getAny?.('growth') ?? 0)
|
|
})
|
|
}
|
|
return list
|
|
}
|
|
|
|
export async function fetchProductTrend(period: string, productId: string): Promise<Array<ProductTrendRow>> {
|
|
const { startIso, endIso } = computeDateRange(period)
|
|
const rows = await rpcOrEmptyArray('rpc_analytics_product_trend', {
|
|
p_start_date: toDateOnly(startIso),
|
|
p_end_date: toDateOnly(endIso),
|
|
p_product_id: productId
|
|
} as any)
|
|
|
|
const out: Array<ProductTrendRow> = []
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const r: any = rows[i]
|
|
const date = `${r.getAny?.('date') ?? ''}`
|
|
out.push({
|
|
date,
|
|
gmv: safeNumber(r.getAny?.('gmv') ?? 0),
|
|
qty: safeNumber(r.getAny?.('qty') ?? 0),
|
|
orders: safeNumber(r.getAny?.('orders') ?? 0)
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
export async function fetchCategorySales(period: string): Promise<Array<UTSJSONObject>> {
|
|
const { startIso, endIso } = computeDateRange(period)
|
|
return await rpcOrEmptyArray('rpc_analytics_category_sales', {
|
|
p_start_date: toDateOnly(startIso),
|
|
p_end_date: toDateOnly(endIso)
|
|
} as any)
|
|
}
|
|
|
|
export async function fetchStockInsights(period: string): Promise<Array<UTSJSONObject>> {
|
|
return await rpcOrEmptyArray('rpc_product_insights_stock', {} as any)
|
|
}
|
|
|
|
export async function fetchPriceTrend(period: string): Promise<Array<UTSJSONObject>> {
|
|
const { startIso, endIso } = computeDateRange(period)
|
|
return await rpcOrEmptyArray('rpc_analytics_price_trend', {
|
|
p_start: startIso,
|
|
p_end: endIso
|
|
} as any)
|
|
}
|
|
|
|
export async function fetchReviewInsights(): Promise<Array<UTSJSONObject>> {
|
|
return await rpcOrEmptyArray('rpc_product_insights_reviews', {} as any)
|
|
}
|