import supa from '@/components/supadb/aksupainstance.uts' import { rpcOrNull, rpcOrValue } from '@/services/analytics/rpc.uts' /** * 商品主模型 (SPU) */ export type AdminProduct = { id: string name: string image: string price: number stock: number sales: number status: number created_at: string category_id: string category_name: string subtitle: string | null description: string | null product_code: string | null unit: string | null image_urls: string[] | null video_urls: string[] | null tags: string[] | null attributes: any | null // 扩展字段 (Step 3-6) shipping_template_id: string | null give_integral: number stock_warning: number virtual_sales: number sort_order: number } /** * 商品 SKU 模型 */ export type AdminProductSku = { id ?: string product_id ?: string sku_code : string specifications : any price : number stock : number status : number image_url : string | null } /** * 运费模板模型 */ export type ShippingTemplate = { id : string merchant_id ?: string name : string calc_method : string // piece, weight, volume is_free_shipping : boolean sort_order : number created_at ?: string updated_at ?: string } export type ProductPageResult = { total : number items : Array } /** * 获取运费模板列表 */ export async function fetchShippingTemplates() : Promise { const { data, error } = await supa .from('ak_shipping_templates') .select('*') .order('sort_order', { ascending: true }) .execute() if (error != null) { console.error('获取运费模板失败:', error) return [] as ShippingTemplate[] } return (data ?? []) as ShippingTemplate[] } /** * 保存运费模板(新增/更新) */ export async function saveShippingTemplate(tpl : Partial) : Promise { const session = supa.getSession() const uid = session?.user?.getString('id') if (uid == null) return false const { error } = await supa .from('ak_shipping_templates') .upsert({ ...tpl, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除运费模板 */ export async function deleteShippingTemplate(id : string) : Promise { const { error } = await supa .from('ak_shipping_templates') .delete() .eq('id', id) .execute() return error == null } /** * 分页获取商品列表 */ export async function fetchAdminProductPage( page: number, pageSize: number, filters: { name?: string, status?: number, categoryId?: string } ): Promise { const res = await rpcOrNull('rpc_admin_product_list', { p_page: page, p_page_size: pageSize, p_name: filters.name ?? null, p_status: filters.status ?? null, p_category_id: filters.categoryId ?? null } as UTSJSONObject) if (res == null) { return { total: 0, items: [] as Array } } const anyTotal = (res as any).total const anyItems = (res as any).items return { total: typeof anyTotal === 'number' ? anyTotal : 0, items: Array.isArray(anyItems) ? anyItems : [] as Array } } /** * 获取商品详情(包含 SKU 列表) */ export async function fetchAdminProductDetail(productId: string): Promise<{ product: AdminProduct | null, skus: AdminProductSku[] }> { const { data: productData, error: productError } = await supa .from('ml_products') .select('*, ml_categories(name)') .eq('id', productId) .single() .execute() if (productError != null) { console.error('获取商品详情失败:', productError) return { product: null, skus: [] } } const { data: skuData, error: skuError } = await supa .from('ml_product_skus') .select('*') .eq('product_id', productId) .execute() const product = productData as any if (product != null && product.ml_categories != null) { product.category_name = (product.ml_categories as any).name } return { product: product as AdminProduct, skus: (skuData ?? []) as AdminProductSku[] } } /** * 保存商品(新增或更新,含 SKU 关联保存) */ export async function saveAdminProduct(product: Partial, skus: AdminProductSku[]): Promise { const session = supa.getSession() const uid = session?.user?.getString('id') if (uid == null) return false // 1. 保存商品主表 const { data: savedProduct, error: productError } = await supa .from('ml_products') .upsert({ ...product, merchant_id: uid, updated_at: new Date().toISOString() }) .select() .single() .execute() if (productError != null || savedProduct == null) { console.error('保存商品主表失败:', productError) return false } const savedProductId = (savedProduct as any).id as string // 2. 保存 SKU const skuPayload = skus.map(s => ({ ...s, product_id: savedProductId, updated_at: new Date().toISOString() })) const { error: skuError } = await supa .from('ml_product_skus') .upsert(skuPayload as any) .execute() if (skuError != null) { console.error('保存 SKU 失败:', skuError) return false } return true } /** * 获取商品状态汇总统计 */ export async function fetchAdminProductCountStats() : Promise { return await rpcOrNull('rpc_admin_product_count_stats', {} as UTSJSONObject) } /** * 获取商品概况统计指标 */ export async function fetchAdminProductStats(startTime : string, endTime : string) : Promise { return await rpcOrNull('rpc_admin_product_stats', { p_start_time: startTime, p_end_time: endTime } as UTSJSONObject) } /** * 获取商品营业趋势数据 */ export async function fetchAdminProductTrend(startTime : string, endTime : string) : Promise> { const res = await rpcOrValue('rpc_admin_product_trend', { p_start_time: startTime, p_end_time: endTime } as UTSJSONObject) return Array.isArray(res) ? (res as Array) : [] as Array } /** * 获取商品排行 */ export async function fetchAdminProductRanking(startTime : string, endTime : string, sortBy : string = 'sales', limit : number = 10) : Promise> { const res = await rpcOrValue('rpc_admin_product_ranking', { p_start_time: startTime, p_end_time: endTime, p_sort_by: sortBy, p_limit: limit } as UTSJSONObject) return Array.isArray(res) ? (res as Array) : [] as Array } /** * 更新商品状态 (上架/下架/回收站) * @param status 1:上架 2:下架 3:草稿 4:删除 */ export async function updateAdminProductStatus(productId: string, status: number): Promise { try { const ok = await rpcOrValue('rpc_admin_product_update_status', { p_product_id: productId, p_status: status } as UTSJSONObject) return ok === true } catch (e: any) { console.error('更新商品状态失败:', e) return false } }