Files
medical-mall/services/admin/productService.uts
2026-02-15 16:37:37 +08:00

287 lines
7.0 KiB
Plaintext

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<AdminProduct>
}
/**
* 获取运费模板列表
*/
export async function fetchShippingTemplates() : Promise<ShippingTemplate[]> {
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<ShippingTemplate>) : Promise<boolean> {
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<boolean> {
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<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
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
}
}