补充数据库数据,修改分类栏内容
This commit is contained in:
@@ -175,6 +175,8 @@ function parseProductFromRaw(item: any): Product {
|
||||
const result: Product = {
|
||||
id: safeGetString(itemObj, 'id'),
|
||||
name: safeGetString(itemObj, 'name'),
|
||||
short_title: safeGetString(itemObj, 'short_title'),
|
||||
subtitle: safeGetString(itemObj, 'subtitle'),
|
||||
description: safeGetString(itemObj, 'description'),
|
||||
base_price: safeGetNumber(itemObj, 'base_price'),
|
||||
price: safeGetNumber(itemObj, 'base_price'),
|
||||
@@ -193,6 +195,12 @@ function parseProductFromRaw(item: any): Product {
|
||||
is_featured: safeGetBoolean(itemObj, 'is_featured'),
|
||||
is_new: safeGetBoolean(itemObj, 'is_new'),
|
||||
is_hot: safeGetBoolean(itemObj, 'is_hot'),
|
||||
card_tags: safeGetStringArray(itemObj, 'card_tags'),
|
||||
service_tags: safeGetStringArray(itemObj, 'service_tags'),
|
||||
selling_points: safeGetStringArray(itemObj, 'selling_points'),
|
||||
display_sales_text: safeGetString(itemObj, 'display_sales_text'),
|
||||
compliance_type: safeGetString(itemObj, 'compliance_type'),
|
||||
device_class: safeGetString(itemObj, 'device_class'),
|
||||
specification: safeGetString(itemObj, 'specification'),
|
||||
usage: safeGetString(itemObj, 'usage'),
|
||||
side_effects: safeGetString(itemObj, 'side_effects'),
|
||||
@@ -256,6 +264,12 @@ export type Category = {
|
||||
parent_id?: string
|
||||
level?: number
|
||||
slug?: string
|
||||
sort_order?: number
|
||||
image_url?: string
|
||||
scene?: string
|
||||
category_type?: string
|
||||
compliance_type?: string
|
||||
is_active?: boolean
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
@@ -290,6 +304,13 @@ export type Product = {
|
||||
shop_id?: string
|
||||
tags?: string
|
||||
attributes?: string
|
||||
short_title?: string
|
||||
card_tags?: string[]
|
||||
service_tags?: string[]
|
||||
selling_points?: string[]
|
||||
display_sales_text?: string
|
||||
compliance_type?: string
|
||||
device_class?: string
|
||||
specification?: string
|
||||
usage?: string
|
||||
side_effects?: string
|
||||
@@ -491,6 +512,192 @@ function emptyProductPage(page: number, limit: number): PaginatedResponse<Produc
|
||||
}
|
||||
}
|
||||
|
||||
function buildAnyStringList(values: string[]): any[] {
|
||||
const result: any[] = []
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (values[i] != '') {
|
||||
result.push(values[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function parseMedicalMallCategory(item: UTSJSONObject): Category {
|
||||
const icon = safeGetString(item, 'icon')
|
||||
const imageUrl = safeGetString(item, 'image_url')
|
||||
const color = safeGetString(item, 'color')
|
||||
return {
|
||||
id: safeGetString(item, 'id'),
|
||||
name: safeGetString(item, 'name'),
|
||||
icon: icon != '' ? icon : imageUrl,
|
||||
description: safeGetString(item, 'description'),
|
||||
color: color != '' ? color : '#16a085',
|
||||
parent_id: safeGetString(item, 'parent_id'),
|
||||
level: safeGetNumber(item, 'level'),
|
||||
slug: safeGetString(item, 'category_type'),
|
||||
sort_order: safeGetNumber(item, 'sort_order'),
|
||||
image_url: imageUrl,
|
||||
scene: safeGetString(item, 'scene'),
|
||||
category_type: safeGetString(item, 'category_type'),
|
||||
compliance_type: safeGetString(item, 'compliance_type'),
|
||||
is_active: safeGetBoolean(item, 'is_active'),
|
||||
created_at: safeGetString(item, 'created_at')
|
||||
} as Category
|
||||
}
|
||||
|
||||
function getMedicalMallCategoryKeywords(categoryId: string): string[] {
|
||||
if (categoryId == 'med_device') return ['医疗器械', '血压计', '血糖仪', '血氧仪', '体温计', '制氧机', '雾化器', '轮椅', '拐杖', '护理床']
|
||||
if (categoryId == 'blood_pressure_monitor') return ['血压计', '血压监测', '电子血压计']
|
||||
if (categoryId == 'blood_glucose_meter') return ['血糖仪', '血糖测试', '采血笔']
|
||||
if (categoryId == 'oximeter') return ['血氧仪', '血氧']
|
||||
if (categoryId == 'thermometer') return ['体温计', '额温枪', '测温']
|
||||
if (categoryId == 'oxygen_concentrator') return ['制氧机', '吸氧']
|
||||
if (categoryId == 'nebulizer') return ['雾化器', '雾化']
|
||||
if (categoryId == 'wheelchair_crutch') return ['轮椅', '拐杖', '助行器']
|
||||
if (categoryId == 'nursing_bed') return ['护理床', '病床']
|
||||
|
||||
if (categoryId == 'otc_medicine') return ['感冒药', '退烧药', '止咳', '咽喉', '肠胃', '皮肤', '止痛', '跌打', '儿童用药', '家庭常备药']
|
||||
if (categoryId == 'cold_fever') return ['感冒药', '退烧', '发烧', '退热贴', '感冒']
|
||||
if (categoryId == 'cough_throat') return ['咳嗽', '咽喉', '润喉']
|
||||
if (categoryId == 'stomach_medicine') return ['肠胃', '胃药', '益生菌']
|
||||
if (categoryId == 'skin_external') return ['皮肤', '外用', '软膏', '喷剂']
|
||||
if (categoryId == 'eye_ear_nose') return ['眼药', '滴眼', '鼻炎', '口腔', '耳']
|
||||
if (categoryId == 'pain_relief') return ['止痛', '镇痛']
|
||||
if (categoryId == 'trauma_sprain') return ['跌打', '扭伤', '损伤', '喷雾']
|
||||
if (categoryId == 'child_medicine') return ['儿童', '小儿']
|
||||
|
||||
if (categoryId == 'rehab_care') return ['康复', '护理', '敷料', '护具', '支具', '理疗', '热敷', '训练']
|
||||
if (categoryId == 'postop_rehab') return ['术后', '恢复带', '康复']
|
||||
if (categoryId == 'wound_dressing' || categoryId == 'wound_care') return ['伤口', '敷料', '纱布', '创可贴', '护理包']
|
||||
if (categoryId == 'brace_support') return ['护具', '支具', '护腰', '护膝', '支撑带']
|
||||
if (categoryId == 'rehab_training') return ['康复训练', '训练器']
|
||||
if (categoryId == 'physiotherapy_hot') return ['理疗', '热敷', '暖贴', '热敷贴']
|
||||
if (categoryId == 'mobility_training') return ['行动训练', '步行训练']
|
||||
|
||||
if (categoryId == 'chronic_monitor') return ['慢病', '血压', '血糖', '心脑血管', '呼吸', '体脂', '家庭检测']
|
||||
if (categoryId == 'hypertension') return ['高血压', '血压']
|
||||
if (categoryId == 'diabetes') return ['糖尿病', '血糖']
|
||||
if (categoryId == 'cardiovascular') return ['心脑血管', '心率', '心电']
|
||||
if (categoryId == 'respiratory_health') return ['呼吸', '雾化', '氧', '制氧']
|
||||
if (categoryId == 'weight_bodyfat') return ['体重', '体脂']
|
||||
if (categoryId == 'home_testing') return ['检测', '试纸']
|
||||
|
||||
if (categoryId == 'elderly_aid') return ['适老', '长者', '防滑', '扶手', '助浴', '失禁', '生活辅助']
|
||||
if (categoryId == 'anti_slip') return ['防滑', '防跌']
|
||||
if (categoryId == 'elderly_bathroom') return ['卫浴', '扶手', '浴室']
|
||||
if (categoryId == 'eating_aid') return ['助餐', '餐具']
|
||||
if (categoryId == 'bathing_aid') return ['助浴', '洗澡椅', '沐浴']
|
||||
if (categoryId == 'incontinence_care') return ['失禁', '护理垫', '纸尿裤']
|
||||
if (categoryId == 'daily_living_aid') return ['生活辅助', '起身', '坐便']
|
||||
|
||||
if (categoryId == 'nutrition_health') return ['维生素', '钙片', '蛋白粉', '营养', '益生菌', '免疫']
|
||||
if (categoryId == 'vitamin') return ['维生素', '维C', '维D']
|
||||
if (categoryId == 'calcium') return ['钙片', '钙', '钙铁锌', '锌', '硒']
|
||||
if (categoryId == 'protein') return ['蛋白', '蛋白粉']
|
||||
if (categoryId == 'elderly_nutrition') return ['老人营养', '中老年营养']
|
||||
if (categoryId == 'gut_health') return ['益生菌', '肠道']
|
||||
if (categoryId == 'immune_support') return ['免疫', '免疫支持']
|
||||
|
||||
if (categoryId == 'protection_disinfection') return ['口罩', '消毒', '酒精', '湿巾', '手套', '急救包', '棉签', '纱布']
|
||||
if (categoryId == 'mask') return ['口罩']
|
||||
if (categoryId == 'disinfectant') return ['消毒液', '消毒', '酒精']
|
||||
if (categoryId == 'alcohol_wipes') return ['酒精湿巾', '湿巾']
|
||||
if (categoryId == 'gloves') return ['手套']
|
||||
if (categoryId == 'first_aid') return ['急救包']
|
||||
if (categoryId == 'dressing_tools' || categoryId == 'nursing_consumables') return ['棉签', '纱布', '耗材']
|
||||
|
||||
if (categoryId == 'tcm_health') return ['中医', '艾灸', '拔罐', '养生茶', '贴敷', '药膳', '按摩', '泡脚']
|
||||
if (categoryId == 'moxibustion') return ['艾灸', '拔罐', '艾条', '温灸']
|
||||
if (categoryId == 'herbal_drink') return ['养生茶', '茶饮', '草本']
|
||||
if (categoryId == 'tcm_patch') return ['贴敷', '理疗贴', '草本贴']
|
||||
if (categoryId == 'medicated_diet') return ['药膳', '滋补']
|
||||
if (categoryId == 'massage_tools') return ['按摩', '理疗', '刮痧']
|
||||
if (categoryId == 'foot_bath') return ['泡脚', '足浴']
|
||||
|
||||
if (categoryId == 'home_care_daily') return ['护理', '伤口', '口腔', '皮肤', '清洁', '耗材', '健康工具']
|
||||
if (categoryId == 'oral_care') return ['口腔', '牙', '漱口']
|
||||
if (categoryId == 'skin_care') return ['皮肤护理', '修护', '护肤']
|
||||
if (categoryId == 'cleaning_care') return ['清洁护理', '清洁']
|
||||
if (categoryId == 'home_health_tool') return ['健康工具', '家庭健康']
|
||||
|
||||
if (categoryId == 'all_medical' || categoryId == 'recommend') return ['血压计', '血糖仪', '血氧仪', '体温计', '制氧机', '雾化器', '轮椅', '护理床', '感冒药', '退热贴', '止咳', '胃药', '创可贴', '敷料', '纱布', '护具', '康复', '理疗', '慢病', '口罩', '消毒', '酒精', '急救包', '艾灸', '拔罐', '维生素', '钙片', '蛋白粉', '益生菌', '老人营养', '适老', '助浴', '护理']
|
||||
return [] as string[]
|
||||
}
|
||||
|
||||
function containsMedicalMallKeyword(source: string, keywords: string[]): boolean {
|
||||
if (source == '' || keywords.length == 0) {
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
if (keywords[i] != '' && source.indexOf(keywords[i]) != -1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function buildMedicalMallProductText(item: UTSJSONObject): string {
|
||||
const tags = safeGetStringArray(item, 'tags')
|
||||
let tagText = ''
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
if (tags[i] != '') {
|
||||
tagText += ' ' + tags[i]
|
||||
}
|
||||
}
|
||||
return (
|
||||
safeGetString(item, 'name') + ' ' +
|
||||
safeGetString(item, 'subtitle') + ' ' +
|
||||
safeGetString(item, 'description') + ' ' +
|
||||
safeGetString(item, 'category_name') + ' ' +
|
||||
safeGetString(item, 'specification') + ' ' +
|
||||
safeGetString(item, 'usage') + ' ' +
|
||||
tagText
|
||||
)
|
||||
}
|
||||
|
||||
function isRxHiddenProduct(item: UTSJSONObject): boolean {
|
||||
return safeGetString(item, 'compliance_type') == 'rx_hidden'
|
||||
}
|
||||
|
||||
function matchesMedicalMallFallback(item: UTSJSONObject, categoryId: string): boolean {
|
||||
if (isRxHiddenProduct(item)) {
|
||||
return false
|
||||
}
|
||||
const sourceText = buildMedicalMallProductText(item)
|
||||
return containsMedicalMallKeyword(sourceText, getMedicalMallCategoryKeywords(categoryId))
|
||||
}
|
||||
|
||||
function sortProductsByIdOrder(products: Product[], orderedIds: string[]): Product[] {
|
||||
const productMap = new Map<string, Product>()
|
||||
for (let i = 0; i < products.length; i++) {
|
||||
productMap.set(products[i].id, products[i])
|
||||
}
|
||||
const sorted: Product[] = []
|
||||
for (let i = 0; i < orderedIds.length; i++) {
|
||||
const item = productMap.get(orderedIds[i])
|
||||
if (item != null) {
|
||||
sorted.push(item)
|
||||
}
|
||||
}
|
||||
return sorted
|
||||
}
|
||||
|
||||
function dedupeProducts(products: Product[]): Product[] {
|
||||
const deduped: Product[] = []
|
||||
const seenIds: string[] = []
|
||||
for (let i = 0; i < products.length; i++) {
|
||||
const productId = products[i].id
|
||||
if (productId != '' && seenIds.indexOf(productId) != -1) {
|
||||
continue
|
||||
}
|
||||
if (productId != '') {
|
||||
seenIds.push(productId)
|
||||
}
|
||||
deduped.push(products[i])
|
||||
}
|
||||
return deduped
|
||||
}
|
||||
|
||||
export type ShopOrderResponse = {
|
||||
success: boolean
|
||||
orderIds: string[]
|
||||
@@ -946,6 +1153,373 @@ class SupabaseService {
|
||||
}
|
||||
}
|
||||
|
||||
async getMedicalMallParentCategories(): Promise<Category[]> {
|
||||
try {
|
||||
if (!logConsumerQueryStart('getMedicalMallParentCategories', 'medical_mall_categories', '*')) {
|
||||
return []
|
||||
}
|
||||
const response = await supa
|
||||
.from('medical_mall_categories')
|
||||
.select('*')
|
||||
.is('parent_id', null)
|
||||
.eq('is_active', true)
|
||||
.is('deleted_at', null)
|
||||
.order('sort_order', { ascending: true })
|
||||
.execute()
|
||||
|
||||
if (response.error != null || response.data == null) {
|
||||
logConsumerQueryFailure('getMedicalMallParentCategories', 'medical_mall_categories', response.error)
|
||||
return []
|
||||
}
|
||||
|
||||
const rows = response.data as UTSJSONObject[]
|
||||
const categories: Category[] = []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
categories.push(parseMedicalMallCategory(JSON.parse(JSON.stringify(rows[i])) as UTSJSONObject))
|
||||
}
|
||||
logConsumerQuerySuccess('getMedicalMallParentCategories', 'medical_mall_categories', categories.length)
|
||||
return categories
|
||||
} catch (error) {
|
||||
logConsumerQueryFailure('getMedicalMallParentCategories', 'medical_mall_categories', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async getMedicalMallCategoryById(categoryId: string): Promise<Category | null> {
|
||||
try {
|
||||
if (categoryId == '') {
|
||||
return null
|
||||
}
|
||||
const response = await supa
|
||||
.from('medical_mall_categories')
|
||||
.select('*')
|
||||
.eq('id', categoryId)
|
||||
.eq('is_active', true)
|
||||
.is('deleted_at', null)
|
||||
.limit(1)
|
||||
.execute()
|
||||
|
||||
if (response.error != null || response.data == null) {
|
||||
logConsumerQueryFailure('getMedicalMallCategoryById', 'medical_mall_categories', response.error)
|
||||
return null
|
||||
}
|
||||
|
||||
const rows = response.data as UTSJSONObject[]
|
||||
if (rows.length == 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return parseMedicalMallCategory(JSON.parse(JSON.stringify(rows[0])) as UTSJSONObject)
|
||||
} catch (error) {
|
||||
logConsumerQueryFailure('getMedicalMallCategoryById', 'medical_mall_categories', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async getMedicalMallSubCategories(parentId: string): Promise<Category[]> {
|
||||
try {
|
||||
if (!logConsumerQueryStart('getMedicalMallSubCategories', 'medical_mall_categories', '*')) {
|
||||
return []
|
||||
}
|
||||
const response = await supa
|
||||
.from('medical_mall_categories')
|
||||
.select('*')
|
||||
.eq('parent_id', parentId)
|
||||
.eq('is_active', true)
|
||||
.is('deleted_at', null)
|
||||
.order('sort_order', { ascending: true })
|
||||
.execute()
|
||||
|
||||
if (response.error != null || response.data == null) {
|
||||
logConsumerQueryFailure('getMedicalMallSubCategories', 'medical_mall_categories', response.error)
|
||||
return []
|
||||
}
|
||||
|
||||
const rows = response.data as UTSJSONObject[]
|
||||
const categories: Category[] = []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
categories.push(parseMedicalMallCategory(JSON.parse(JSON.stringify(rows[i])) as UTSJSONObject))
|
||||
}
|
||||
logConsumerQuerySuccess('getMedicalMallSubCategories', 'medical_mall_categories', categories.length)
|
||||
return categories
|
||||
} catch (error) {
|
||||
logConsumerQueryFailure('getMedicalMallSubCategories', 'medical_mall_categories', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async getMedicalMallCategoryChildrenIds(parentId: string): Promise<string[]> {
|
||||
try {
|
||||
let response: any
|
||||
if (parentId == 'all_medical') {
|
||||
response = await supa
|
||||
.from('medical_mall_categories')
|
||||
.select('id')
|
||||
.eq('is_active', true)
|
||||
.is('deleted_at', null)
|
||||
.eq('level', 2)
|
||||
.order('sort_order', { ascending: true })
|
||||
.execute()
|
||||
} else {
|
||||
response = await supa
|
||||
.from('medical_mall_categories')
|
||||
.select('id')
|
||||
.eq('parent_id', parentId)
|
||||
.eq('is_active', true)
|
||||
.is('deleted_at', null)
|
||||
.order('sort_order', { ascending: true })
|
||||
.execute()
|
||||
}
|
||||
|
||||
if (response.error != null || response.data == null) {
|
||||
return []
|
||||
}
|
||||
|
||||
const rows = response.data as UTSJSONObject[]
|
||||
const ids: string[] = []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = JSON.parse(JSON.stringify(rows[i])) as UTSJSONObject
|
||||
const id = safeGetString(row, 'id')
|
||||
if (id != '') {
|
||||
ids.push(id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
} catch (error) {
|
||||
console.error('获取医疗商城子分类 ID 失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async getMedicalMallProductsByCategory(categoryId: string, page: number = 1, limit: number = 20): Promise<PaginatedResponse<Product>> {
|
||||
try {
|
||||
if (categoryId == 'recommend') {
|
||||
return await this.getMedicalMallSmartRecommendations(page, limit)
|
||||
}
|
||||
if (!logConsumerQueryStart('getMedicalMallProductsByCategory', 'medical_mall_product_categories', '*')) {
|
||||
return emptyProductPage(page, limit)
|
||||
}
|
||||
|
||||
const targetCategoryIds: string[] = []
|
||||
if (categoryId == 'all_medical') {
|
||||
const allChildren = await this.getMedicalMallCategoryChildrenIds('all_medical')
|
||||
for (let i = 0; i < allChildren.length; i++) {
|
||||
targetCategoryIds.push(allChildren[i])
|
||||
}
|
||||
} else {
|
||||
const categoryResponse = await supa
|
||||
.from('medical_mall_categories')
|
||||
.select('id, level')
|
||||
.eq('id', categoryId)
|
||||
.eq('is_active', true)
|
||||
.is('deleted_at', null)
|
||||
.limit(1)
|
||||
.execute()
|
||||
|
||||
targetCategoryIds.push(categoryId)
|
||||
if (categoryResponse.error == null && categoryResponse.data != null) {
|
||||
const categoryRows = categoryResponse.data as UTSJSONObject[]
|
||||
if (categoryRows.length > 0) {
|
||||
const categoryObj = JSON.parse(JSON.stringify(categoryRows[0])) as UTSJSONObject
|
||||
if (safeGetNumber(categoryObj, 'level') == 1) {
|
||||
const childIds = await this.getMedicalMallCategoryChildrenIds(categoryId)
|
||||
for (let i = 0; i < childIds.length; i++) {
|
||||
if (targetCategoryIds.indexOf(childIds[i]) == -1) {
|
||||
targetCategoryIds.push(childIds[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const linkedIds: string[] = []
|
||||
if (targetCategoryIds.length > 0) {
|
||||
const linkResponse = await supa
|
||||
.from('medical_mall_product_categories')
|
||||
.select('product_id, sort_order, created_at')
|
||||
.in('category_id', buildAnyStringList(targetCategoryIds))
|
||||
.is('deleted_at', null)
|
||||
.order('sort_order', { ascending: true })
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(500)
|
||||
.execute()
|
||||
|
||||
if (linkResponse.error == null && linkResponse.data != null) {
|
||||
const linkRows = linkResponse.data as UTSJSONObject[]
|
||||
for (let i = 0; i < linkRows.length; i++) {
|
||||
const row = JSON.parse(JSON.stringify(linkRows[i])) as UTSJSONObject
|
||||
const productId = safeGetString(row, 'product_id')
|
||||
if (productId != '' && linkedIds.indexOf(productId) == -1) {
|
||||
linkedIds.push(productId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let finalProducts: Product[] = []
|
||||
if (linkedIds.length > 0) {
|
||||
const productResponse = await supa
|
||||
.from('ml_products_detail_view')
|
||||
.select('*')
|
||||
.in('id', buildAnyStringList(linkedIds))
|
||||
.eq('status', '1')
|
||||
.execute()
|
||||
|
||||
if (productResponse.error == null && productResponse.data != null) {
|
||||
const rows = productResponse.data as any[]
|
||||
const parsed: Product[] = []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const rowObj = JSON.parse(JSON.stringify(rows[i])) as UTSJSONObject
|
||||
if (isRxHiddenProduct(rowObj)) {
|
||||
continue
|
||||
}
|
||||
parsed.push(parseProductFromRaw(rows[i]))
|
||||
}
|
||||
finalProducts = sortProductsByIdOrder(parsed, linkedIds)
|
||||
}
|
||||
}
|
||||
|
||||
if (finalProducts.length == 0) {
|
||||
const fallbackResponse = await supa
|
||||
.from('ml_products_detail_view')
|
||||
.select('*')
|
||||
.eq('status', '1')
|
||||
.order('sale_count', { ascending: false })
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(300)
|
||||
.execute()
|
||||
|
||||
if (fallbackResponse.error == null && fallbackResponse.data != null) {
|
||||
const fallbackRows = fallbackResponse.data as any[]
|
||||
for (let i = 0; i < fallbackRows.length; i++) {
|
||||
const rowObj = JSON.parse(JSON.stringify(fallbackRows[i])) as UTSJSONObject
|
||||
if (!matchesMedicalMallFallback(rowObj, categoryId)) {
|
||||
continue
|
||||
}
|
||||
finalProducts.push(parseProductFromRaw(fallbackRows[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deduped = dedupeProducts(finalProducts)
|
||||
const startIndex = (page - 1) * limit
|
||||
const endIndex = startIndex + limit
|
||||
return {
|
||||
data: deduped.slice(startIndex, endIndex),
|
||||
total: deduped.length,
|
||||
page,
|
||||
limit,
|
||||
hasmore: deduped.length > endIndex
|
||||
}
|
||||
} catch (error) {
|
||||
logConsumerQueryFailure('getMedicalMallProductsByCategory', 'medical_mall_product_categories', error)
|
||||
console.error('获取医疗商城分类商品失败:', error)
|
||||
return emptyProductPage(page, limit)
|
||||
}
|
||||
}
|
||||
|
||||
async getMedicalMallSmartRecommendations(page: number = 1, limit: number = 10): Promise<PaginatedResponse<Product>> {
|
||||
try {
|
||||
if (!logConsumerQueryStart('getMedicalMallSmartRecommendations', 'medical_mall_product_categories', '*')) {
|
||||
return emptyProductPage(page, limit)
|
||||
}
|
||||
|
||||
const linkedIds: string[] = []
|
||||
const linkResponse = await supa
|
||||
.from('medical_mall_product_categories')
|
||||
.select('product_id, is_primary, sort_order, created_at')
|
||||
.is('deleted_at', null)
|
||||
.order('is_primary', { ascending: false })
|
||||
.order('sort_order', { ascending: true })
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(500)
|
||||
.execute()
|
||||
|
||||
if (linkResponse.error == null && linkResponse.data != null) {
|
||||
const linkRows = linkResponse.data as UTSJSONObject[]
|
||||
for (let i = 0; i < linkRows.length; i++) {
|
||||
const row = JSON.parse(JSON.stringify(linkRows[i])) as UTSJSONObject
|
||||
const productId = safeGetString(row, 'product_id')
|
||||
if (productId != '' && linkedIds.indexOf(productId) == -1) {
|
||||
linkedIds.push(productId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const merged: Product[] = []
|
||||
const addedIds = new Set<string>()
|
||||
|
||||
if (linkedIds.length > 0) {
|
||||
const linkedProductResponse = await supa
|
||||
.from('ml_products_detail_view')
|
||||
.select('*')
|
||||
.in('id', buildAnyStringList(linkedIds))
|
||||
.eq('status', '1')
|
||||
.execute()
|
||||
|
||||
if (linkedProductResponse.error == null && linkedProductResponse.data != null) {
|
||||
const rows = linkedProductResponse.data as any[]
|
||||
const parsed: Product[] = []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const rowObj = JSON.parse(JSON.stringify(rows[i])) as UTSJSONObject
|
||||
if (isRxHiddenProduct(rowObj)) {
|
||||
continue
|
||||
}
|
||||
parsed.push(parseProductFromRaw(rows[i]))
|
||||
}
|
||||
const ordered = sortProductsByIdOrder(parsed, linkedIds)
|
||||
for (let i = 0; i < ordered.length; i++) {
|
||||
if (!addedIds.has(ordered[i].id)) {
|
||||
merged.push(ordered[i])
|
||||
addedIds.add(ordered[i].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (merged.length < page * limit + limit) {
|
||||
const fallbackResponse = await supa
|
||||
.from('ml_products_detail_view')
|
||||
.select('*')
|
||||
.eq('status', '1')
|
||||
.order('sale_count', { ascending: false })
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(300)
|
||||
.execute()
|
||||
|
||||
if (fallbackResponse.error == null && fallbackResponse.data != null) {
|
||||
const rows = fallbackResponse.data as any[]
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const rowObj = JSON.parse(JSON.stringify(rows[i])) as UTSJSONObject
|
||||
if (!matchesMedicalMallFallback(rowObj, 'recommend')) {
|
||||
continue
|
||||
}
|
||||
const product = parseProductFromRaw(rows[i])
|
||||
if (!addedIds.has(product.id)) {
|
||||
merged.push(product)
|
||||
addedIds.add(product.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const startIndex = (page - 1) * limit
|
||||
const endIndex = startIndex + limit
|
||||
return {
|
||||
data: merged.slice(startIndex, endIndex),
|
||||
total: merged.length,
|
||||
page,
|
||||
limit,
|
||||
hasmore: merged.length > endIndex
|
||||
}
|
||||
} catch (error) {
|
||||
logConsumerQueryFailure('getMedicalMallSmartRecommendations', 'medical_mall_product_categories', error)
|
||||
console.error('获取医疗商城推荐商品失败:', error)
|
||||
return emptyProductPage(page, limit)
|
||||
}
|
||||
}
|
||||
|
||||
// 根据商品ID获取SKU列表
|
||||
async getProductSkus(productId: string): Promise<ProductSku[]> {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user