Files
medical-mall/services/admin/productService.uts
2026-02-13 17:29:50 +08:00

212 lines
5.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import supa from '@/components/supadb/aksupainstance.uts'
import { rpcOrNull, rpcOrValue } from '@/services/analytics/rpc.uts'
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
}
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 ProductPageResult = {
total: number
items: Array<AdminProduct>
}
/**
* 分页获取商品列表
*/
export async function fetchAdminProductPage(
page: number,
pageSize: number,
filters: {
name?: string,
status?: number,
categoryId?: string
}
): Promise<ProductPageResult> {
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<AdminProduct> }
}
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<AdminProduct>
}
}
/**
* 获取商品详情(包含 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<AdminProduct>, skus: AdminProductSku[]): Promise<boolean> {
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简单策略先删除旧的再插入新的或使用 upsert
// 注意:如果涉及订单关联,不能简单的物理删除旧 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<UTSJSONObject | null> {
return await rpcOrNull('rpc_admin_product_count_stats', {} as UTSJSONObject)
}
/**
* 获取商品概况统计指标
*/
export async function fetchAdminProductStats(startTime : string, endTime : string) : Promise<UTSJSONObject | null> {
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<Array<UTSJSONObject>> {
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<UTSJSONObject>) : [] as Array<UTSJSONObject>
}
/**
* 获取商品排行
*/
export async function fetchAdminProductRanking(startTime : string, endTime : string, sortBy : string = 'sales', limit : number = 10) : Promise<Array<UTSJSONObject>> {
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<UTSJSONObject>) : [] as Array<UTSJSONObject>
}
/**
* 更新商品状态 (上架/下架/回收站)
* @param status 1:上架 2:下架 3:草稿 4:删除
*/
export async function updateAdminProductStatus(productId: string, status: number): Promise<boolean> {
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
}
}