diff --git a/ak/config.uts b/ak/config.uts
index 7269882b..290e5606 100644
--- a/ak/config.uts
+++ b/ak/config.uts
@@ -1,12 +1,12 @@
// Supabase 配置
// 内网环境 - 本地部署的 Supabase
-// IP: 192.168.1.63
+// IP: 192.168.1.61 (Ubuntu服务器)
// Kong HTTP Port: 8000
-export const SUPA_URL: string = 'http://192.168.1.63:8000'
+export const SUPA_URL: string = 'http://192.168.1.61:8000'
export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzY4ODMwNjI0LCJleHAiOjE5MjY1MTA2MjR9.mDVl-kIOdRK9v6VTxo0TDF8r7X7xk3PZXazaavHyVvg'
// WebSocket 实时连接(内网使用 ws:// 而非 wss://)
-export const WS_URL: string = 'ws://192.168.1.63:8000/realtime/v1/websocket'
+export const WS_URL: string = 'ws://192.168.1.61:8000/realtime/v1/websocket'
// 备用配置(已注释,如需切换可取消注释)
// 开发环境 - 其他内网地址
diff --git a/doc_mall/SUPABASE_DATA_MIGRATION_GUIDE.md b/doc_mall/SUPABASE_DATA_MIGRATION_GUIDE.md
new file mode 100644
index 00000000..b0fa749e
--- /dev/null
+++ b/doc_mall/SUPABASE_DATA_MIGRATION_GUIDE.md
@@ -0,0 +1,247 @@
+# Supabase 数据迁移指南
+
+## 概述
+
+本指南将帮助您将当前使用模拟数据的 uni-app 项目迁移到使用 Supabase 数据库。您的项目已经配置了连接到 Ubuntu 服务器上的 Supabase(IP: 192.168.1.61),现在需要创建数据库表并插入测试数据,然后修改前端代码以使用真实数据。
+
+## 第一步:在 Supabase 中创建数据库表
+
+### 方法一:通过 Supabase Dashboard 执行 SQL
+
+1. **打开 Supabase Dashboard**
+ - 访问:http://192.168.1.61:3000
+ - 使用 Dashboard 用户名和密码登录(位于 `supabase_pro/.env` 中的 `DASHBOARD_USERNAME` 和 `DASHBOARD_PASSWORD`)
+
+2. **进入 SQL Editor**
+ - 在左侧菜单中点击 "SQL Editor"
+ - 点击 "New query" 创建新查询
+
+3. **执行建表脚本**
+ - 复制 `sql/001_create_tables.sql` 文件中的全部内容
+ - 粘贴到 SQL Editor 中
+ - 点击 "Run" 执行
+
+4. **执行插入数据脚本**
+ - 复制 `sql/002_insert_test_data.sql` 文件中的全部内容
+ - 粘贴到 SQL Editor 中
+ - 点击 "Run" 执行
+
+### 方法二:通过命令行执行(如果 Supabase 运行在 Ubuntu 服务器上)
+
+```bash
+# 登录到 Ubuntu 服务器
+ssh hfkj@192.168.1.61
+
+# 进入 Supabase 项目目录(假设 Supabase 安装在默认位置)
+cd ~/supabase
+
+# 使用 psql 连接到数据库执行 SQL 脚本
+# 注意:需要知道数据库密码(位于 supabase_pro/.env 中的 POSTGRES_PASSWORD)
+PGPASSWORD=yxyHINygZMLSq9jLddrZQBB-CoyGHSF5DwlwWmbrYXc psql -h localhost -U postgres -d postgres -f /path/to/001_create_tables.sql
+PGPASSWORD=yxyHINygZMLSq9jLddrZQBB-CoyGHSF5DwlwWmbrYXc psql -h localhost -U postgres -d postgres -f /path/to/002_insert_test_data.sql
+```
+
+## 第二步:验证数据表创建成功
+
+### 在 Supabase Dashboard 中验证
+
+1. **查看 Tables**
+ - 在左侧菜单中点击 "Table Editor"
+ - 应该能看到 `categories` 和 `products` 表
+
+2. **查看数据**
+ - 点击 `categories` 表,应该能看到 10 条分类数据
+ - 点击 `products` 表,应该能看到 18 条商品数据
+
+### 通过 API 验证
+
+1. **测试分类 API**
+ ```
+ GET http://192.168.1.61:8000/rest/v1/categories
+ Headers:
+ apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzY4ODMwNjI0LCJleHAiOjE5MjY1MTA2MjR9.mDVl-kIOdRK9v6VTxo0TDF8r7X7xk3PZXazaavHyVvg
+ ```
+
+2. **测试商品 API**
+ ```
+ GET http://192.168.1.61:8000/rest/v1/products?category_id=eq.cold
+ Headers:
+ apikey: [同上]
+ ```
+
+## 第三步:修改前端代码使用真实数据
+
+### 1. 已创建的服务文件
+
+我已经创建了 `utils/supabaseService.uts` 文件,提供了以下功能:
+- `getCategories()` - 获取所有分类
+- `getProductsByCategory()` - 获取指定分类的商品
+- `searchProducts()` - 搜索商品
+- `getProductById()` - 获取单个商品详情
+- `getHotProducts()` - 获取热销商品
+- `getRecommendedProducts()` - 获取推荐商品
+
+### 2. 修改分类页面 (`pages/mall/consumer/category.uvue`)
+
+需要将硬编码的模拟数据替换为从 Supabase 获取的数据:
+
+```typescript
+// 在 script 部分添加导入
+import supabaseService from '@/utils/supabaseService.uts'
+import type { Category, Product } from '@/utils/supabaseService.uts'
+
+// 替换 medicineCategories 的初始化
+// 删除原有的 medicineCategories 数组定义
+
+// 修改 onMounted 或创建新的生命周期函数
+onMounted(async () => {
+ await loadCategories()
+ await loadProducts()
+})
+
+// 添加加载分类的方法
+const loadCategories = async () => {
+ const categories = await supabaseService.getCategories()
+ if (categories.length > 0) {
+ primaryCategories.value = categories
+ // 设置默认选中第一个分类
+ if (!activePrimary.value && categories[0]) {
+ activePrimary.value = categories[0].id
+ }
+ }
+}
+
+// 修改 selectPrimaryCategory 方法
+const selectPrimaryCategory = async (categoryId: string) => {
+ activePrimary.value = categoryId
+
+ // 更新当前分类信息
+ const category = primaryCategories.value.find(cat => cat.id === categoryId)
+ if (category) {
+ currentCategoryName.value = category.name
+ currentCategoryDesc.value = category.description
+ }
+
+ // 加载对应商品
+ const response = await supabaseService.getProductsByCategory(categoryId)
+ productList.value = response.data
+ hasMore.value = response.hasmore
+}
+```
+
+### 3. 修改主页 (`pages/mall/consumer/index.uvue`)
+
+如果主页显示商品,也需要修改为从 Supabase 获取:
+
+```typescript
+import supabaseService from '@/utils/supabaseService.uts'
+
+// 获取热销商品
+const loadHotProducts = async () => {
+ hotProducts.value = await supabaseService.getHotProducts(6)
+}
+
+// 获取推荐商品
+const loadRecommendedProducts = async () => {
+ recommendedProducts.value = await supabaseService.getRecommendedProducts(6)
+}
+```
+
+## 第四步:测试连接和数据
+
+### 1. 测试 Supabase 连接
+
+创建一个测试页面或使用现有的页面测试连接:
+
+```typescript
+// 测试代码示例
+const testConnection = async () => {
+ try {
+ const categories = await supabaseService.getCategories()
+ console.log('连接成功,获取到分类数:', categories.length)
+ uni.showToast({
+ title: `连接成功,获取到 ${categories.length} 个分类`,
+ icon: 'success'
+ })
+ } catch (error) {
+ console.error('连接失败:', error)
+ uni.showToast({
+ title: '连接失败,请检查配置',
+ icon: 'error'
+ })
+ }
+}
+```
+
+### 2. 测试数据加载
+
+在分类页面测试:
+1. 打开分类页面
+2. 检查分类列表是否显示
+3. 点击不同分类,检查商品列表是否更新
+4. 检查商品图片、价格等信息是否正确显示
+
+### 3. 测试搜索功能
+
+如果项目有搜索页面,测试搜索功能:
+1. 输入关键字搜索
+2. 检查返回的商品是否相关
+
+## 第五步:处理图片 URL
+
+### 当前情况
+- 数据库中的 `image` 字段目前为空或使用本地路径
+- 实际项目中,图片应该存储在 Supabase Storage 或 CDN
+
+### 临时解决方案
+在显示图片时,如果数据库中没有图片 URL,使用默认图片:
+
+```typescript
+const getProductImage = (product: Product) => {
+ if (product.image && product.image.startsWith('http')) {
+ return product.image
+ }
+ return '/static/images/default-product.png'
+}
+```
+
+### 长期解决方案
+1. 将图片上传到 Supabase Storage
+2. 更新数据库中的 `image` 字段为完整 URL
+
+## 常见问题解决
+
+### 1. 连接超时或失败
+- 检查 Ubuntu 服务器上的 Supabase 是否正常运行
+- 检查防火墙设置,确保 8000 和 3000 端口可访问
+- 检查 `ak/config.uts` 中的 IP 地址是否正确
+
+### 2. 401 未授权错误
+- 检查 `SUPA_KEY` 是否正确(与 `supabase_pro/.env` 中的 `ANON_KEY` 一致)
+- 检查 Supabase 是否已启用匿名访问
+
+### 3. 表不存在错误
+- 确认已执行 SQL 脚本创建表
+- 检查表名是否拼写正确(区分大小写)
+
+### 4. 数据不显示
+- 检查浏览器控制台是否有错误
+- 检查网络请求是否成功
+- 确认数据库中有数据
+
+## 下一步优化建议
+
+1. **添加加载状态**:在数据加载时显示加载动画
+2. **错误处理**:添加更完善的错误处理和重试机制
+3. **数据缓存**:使用本地存储缓存常用数据,减少网络请求
+4. **分页加载**:实现滚动加载更多商品
+5. **图片优化**:使用图片懒加载和压缩
+
+## 总结
+
+通过以上步骤,您的项目将从使用模拟数据过渡到使用 Supabase 数据库数据。主要工作包括:
+1. 在 Supabase 中创建表和插入测试数据
+2. 修改前端代码使用新的服务层
+3. 测试连接和数据加载
+
+完成后,您的应用将具备完整的后端数据支持,为后续添加用户管理、购物车、订单等功能打下基础。
diff --git a/pages.json b/pages.json
index 41dc0f0d..30c70f87 100644
--- a/pages.json
+++ b/pages.json
@@ -1,11 +1,12 @@
{
"pages": [
{
- "path": "pages/user/login",
+ "path": "pages/mall/consumer/index",
"style": {
- "navigationBarTitleText": "用户登录",
- "navigationStyle": "custom"
- }
+ "navigationBarTitleText": "首页",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true
+ }
},
{
"path": "pages/user/boot",
@@ -44,11 +45,28 @@
}
},
{
- "path": "pages/mall/consumer/index",
+ "path": "pages/user/login",
"style": {
- "navigationBarTitleText": "首页",
- "navigationStyle": "custom",
- "enablePullDownRefresh": true
+ "navigationBarTitleText": "用户登录",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/user/change-password",
+ "style": {
+ "navigationBarTitleText": "修改密码"
+ }
+ },
+ {
+ "path": "pages/user/bind-phone",
+ "style": {
+ "navigationBarTitleText": "绑定手机"
+ }
+ },
+ {
+ "path": "pages/user/bind-email",
+ "style": {
+ "navigationBarTitleText": "绑定邮箱"
}
},
{
@@ -75,9 +93,7 @@
"style": {
"navigationBarTitleText": "我的"
}
- }
- ],
- "subPackages": [
+ },
{
"path": "pages/mall/consumer/search",
"style": {
@@ -195,8 +211,21 @@
"navigationBarTitleText": "客服聊天",
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/mall/consumer/settings",
+ "style": {
+ "navigationBarTitleText": "设置"
+ }
+ },
+ {
+ "path": "pages/mall/consumer/wallet",
+ "style": {
+ "navigationBarTitleText": "我的钱包"
+ }
}
],
+ "subPackages": [],
"tabBar": {
"color": "#999999",
"selectedColor": "#ff5000",
@@ -241,4 +270,4 @@
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8F8F8"
}
-}
\ No newline at end of file
+}
diff --git a/pages/mall/consumer/cart.uvue b/pages/mall/consumer/cart.uvue
index 5809cc95..25d48391 100644
--- a/pages/mall/consumer/cart.uvue
+++ b/pages/mall/consumer/cart.uvue
@@ -482,8 +482,18 @@ const goShopping = () => {
const navigateToProduct = (product: any) => {
// 使用productId(如果存在)作为跳转的商品ID,否则使用id
const productId = product.productId || product.id
+ // 传递完整的参数,确保商品详情页能正确加载
+ const params = new URLSearchParams()
+ params.append('id', productId)
+ params.append('productId', productId)
+ params.append('price', product.price?.toString() || '0')
+ // 商品详情页期望的参数名是originalPrice
+ params.append('originalPrice', (product.original_price || product.originalPrice || (product.price * 1.2).toFixed(2))?.toString())
+ params.append('name', encodeURIComponent(product.name || ''))
+ params.append('image', encodeURIComponent(product.image || '/static/product1.jpg'))
+
uni.navigateTo({
- url: `/pages/mall/consumer/product-detail?id=${productId}&name=${encodeURIComponent(product.name)}&price=${product.price}&image=${encodeURIComponent(product.image)}`
+ url: `/pages/mall/consumer/product-detail?${params.toString()}`
})
}
diff --git a/pages/mall/consumer/index.uvue b/pages/mall/consumer/index.uvue
index 8c6c10a6..854af7d0 100644
--- a/pages/mall/consumer/index.uvue
+++ b/pages/mall/consumer/index.uvue
@@ -346,6 +346,7 @@
\ No newline at end of file
diff --git a/pages/user/bind-email.uvue b/pages/user/bind-email.uvue
index 3fdd3ad5..dc7cadc1 100644
--- a/pages/user/bind-email.uvue
+++ b/pages/user/bind-email.uvue
@@ -1,544 +1,135 @@
-
-
-
-
-
- ✓
- 已绑定邮箱
- {{ userInfo.email }}
- 绑定时间:{{ formatTime(userInfo.emailBoundTime) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- ✓
- {{ successTitle }}
- {{ successMessage }}
-
-
-
-
+
+
+
+ 邮箱
+
+
+
+ 验证码
+
+ {{ counting ? `${count}s` : '获取验证码' }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/bind-phone.uvue b/pages/user/bind-phone.uvue
index c98e6406..f0e0e08d 100644
--- a/pages/user/bind-phone.uvue
+++ b/pages/user/bind-phone.uvue
@@ -1,548 +1,135 @@
-
-
-
-
-
- ✓
- 已绑定手机
- {{ formatPhone(userInfo.phone) }}
- 绑定时间:{{ formatTime(userInfo.phoneBoundTime) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- ✓
- {{ successTitle }}
- {{ successMessage }}
-
-
-
-
+
+
+
+ 手机号
+
+
+
+ 验证码
+
+ {{ counting ? `${count}s` : '获取验证码' }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/change-password.uvue b/pages/user/change-password.uvue
index 3b8584d3..2cc8b4fc 100644
--- a/pages/user/change-password.uvue
+++ b/pages/user/change-password.uvue
@@ -1,353 +1,103 @@
-
-
-
-
-
-
-
-
- 忘记密码?
-
-
-
-
-
-
- ✓
- 修改成功
- 密码已成功修改,请使用新密码登录
-
-
-
-
+
+
+
+ 旧密码
+
+
+
+ 新密码
+
+
+
+ 确认密码
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/test/DEBUG_SIGNUP.md b/pages/user/test/DEBUG_SIGNUP.md
index 39fc1359..30ac5940 100644
--- a/pages/user/test/DEBUG_SIGNUP.md
+++ b/pages/user/test/DEBUG_SIGNUP.md
@@ -41,7 +41,7 @@ docker-compose logs auth | grep -i error
### 4. 验证配置是否生效
-在 Supabase Dashboard (http://192.168.1.63:3000) 的 SQL Editor 中执行:
+在 Supabase Dashboard (http://192.168.1.61:3000) 的 SQL Editor 中执行:
```sql
-- 检查当前配置(需要访问 GoTrue 配置)
@@ -59,7 +59,7 @@ docker-compose logs auth | grep -i error
确认 `ak/config.uts` 中的配置正确:
```typescript
-export const SUPA_URL: string = 'http://192.168.1.63:8000'
+export const SUPA_URL: string = 'http://192.168.1.61:8000'
export const SUPA_KEY: string = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
```
diff --git a/pages/user/test/IMMEDIATE_FIX.md b/pages/user/test/IMMEDIATE_FIX.md
index 384778d3..4e072b39 100644
--- a/pages/user/test/IMMEDIATE_FIX.md
+++ b/pages/user/test/IMMEDIATE_FIX.md
@@ -58,7 +58,7 @@ grep ENABLE_EMAIL_AUTOCONFIRM supabase_pro/.env
## 🔍 验证用户是否创建
-在 Supabase Dashboard (http://192.168.1.63:3000) 的 SQL Editor 中执行:
+在 Supabase Dashboard (http://192.168.1.61:3000) 的 SQL Editor 中执行:
```sql
-- 检查最新注册的用户
diff --git a/pages/user/test/QUICK_FIX.md b/pages/user/test/QUICK_FIX.md
index bf2fea13..184941cd 100644
--- a/pages/user/test/QUICK_FIX.md
+++ b/pages/user/test/QUICK_FIX.md
@@ -16,7 +16,7 @@
**执行步骤**:
-1. **在 Supabase Dashboard (http://192.168.1.63:3000) 中打开 SQL Editor**
+1. **在 Supabase Dashboard (http://192.168.1.61:3000) 中打开 SQL Editor**
2. **执行 `USER_AUTH_SCHEMA.sql`**
- 创建 `ak_users` 表和 RLS 策略
diff --git a/pages/user/test/QUICK_FIX_SIGNUP_LOGIN.md b/pages/user/test/QUICK_FIX_SIGNUP_LOGIN.md
index 51d239d9..12172425 100644
--- a/pages/user/test/QUICK_FIX_SIGNUP_LOGIN.md
+++ b/pages/user/test/QUICK_FIX_SIGNUP_LOGIN.md
@@ -44,7 +44,7 @@ docker-compose restart auth
### 方法一:在 Supabase Dashboard 中手动确认
-1. 打开 Supabase Dashboard: http://192.168.1.63:3000
+1. 打开 Supabase Dashboard: http://192.168.1.61:3000
2. 进入 **Authentication** → **Users**
3. 找到对应的用户
4. 点击用户,在详情页中点击 **Confirm Email** 按钮
diff --git a/utils/supabaseService.uts b/utils/supabaseService.uts
new file mode 100644
index 00000000..0fd1cd5c
--- /dev/null
+++ b/utils/supabaseService.uts
@@ -0,0 +1,228 @@
+import { createClient } from '@/components/supadb/aksupa.uts'
+import { SUPA_URL, SUPA_KEY } from '@/ak/config.uts'
+import type { AkReqResponse } from '@/uni_modules/ak-req/index.uts'
+
+// 创建 Supabase 客户端
+const supa = createClient(SUPA_URL, SUPA_KEY)
+
+// 类型定义
+export interface Category {
+ id: string
+ name: string
+ icon: string
+ description: string
+ color: string
+ created_at?: string
+}
+
+export interface Product {
+ id: string
+ category_id: string
+ name: string
+ specification: string
+ price: number
+ original_price?: number
+ image?: string
+ manufacturer: string
+ sales: number
+ badge?: string
+ shop_id?: string
+ shop_name?: string
+ created_at?: string
+}
+
+export interface PaginatedResponse {
+ data: T[]
+ total: number
+ page: number
+ limit: number
+ hasmore: boolean
+}
+
+class SupabaseService {
+ // 获取所有分类
+ async getCategories(): Promise {
+ try {
+ const response = await supa
+ .from('categories')
+ .select('*')
+ .order('name', { ascending: true })
+ .execute()
+
+ if (response.error) {
+ console.error('获取分类失败:', response.error)
+ return []
+ }
+
+ return response.data as Category[]
+ } catch (error) {
+ console.error('获取分类异常:', error)
+ return []
+ }
+ }
+
+ // 获取指定分类的商品
+ async getProductsByCategory(
+ categoryId: string,
+ page: number = 1,
+ limit: number = 20
+ ): Promise> {
+ try {
+ const response = await supa
+ .from('products')
+ .select('*', { count: 'exact' })
+ .eq('category_id', categoryId)
+ .order('sales', { ascending: false })
+ .page(page)
+ .limit(limit)
+ .execute()
+
+ if (response.error) {
+ console.error('获取商品失败:', response.error)
+ return {
+ data: [],
+ total: 0,
+ page,
+ limit,
+ hasmore: false
+ }
+ }
+
+ return {
+ data: response.data as Product[],
+ total: response.total || 0,
+ page,
+ limit,
+ hasmore: response.hasmore || false
+ }
+ } catch (error) {
+ console.error('获取商品异常:', error)
+ return {
+ data: [],
+ total: 0,
+ page,
+ limit,
+ hasmore: false
+ }
+ }
+ }
+
+ // 搜索商品
+ async searchProducts(
+ keyword: string,
+ page: number = 1,
+ limit: number = 20
+ ): Promise> {
+ try {
+ const response = await supa
+ .from('products')
+ .select('*', { count: 'exact' })
+ .or(`name.ilike.%${keyword}%,manufacturer.ilike.%${keyword}%,specification.ilike.%${keyword}%`)
+ .order('sales', { ascending: false })
+ .page(page)
+ .limit(limit)
+ .execute()
+
+ if (response.error) {
+ console.error('搜索商品失败:', response.error)
+ return {
+ data: [],
+ total: 0,
+ page,
+ limit,
+ hasmore: false
+ }
+ }
+
+ return {
+ data: response.data as Product[],
+ total: response.total || 0,
+ page,
+ limit,
+ hasmore: response.hasmore || false
+ }
+ } catch (error) {
+ console.error('搜索商品异常:', error)
+ return {
+ data: [],
+ total: 0,
+ page,
+ limit,
+ hasmore: false
+ }
+ }
+ }
+
+ // 获取单个商品详情
+ async getProductById(productId: string): Promise {
+ try {
+ const response = await supa
+ .from('products')
+ .select('*')
+ .eq('id', productId)
+ .single()
+ .execute()
+
+ if (response.error) {
+ console.error('获取商品详情失败:', response.error)
+ return null
+ }
+
+ return response.data as Product
+ } catch (error) {
+ console.error('获取商品详情异常:', error)
+ return null
+ }
+ }
+
+ // 获取热销商品
+ async getHotProducts(limit: number = 10): Promise {
+ try {
+ const response = await supa
+ .from('products')
+ .select('*')
+ .order('sales', { ascending: false })
+ .limit(limit)
+ .execute()
+
+ if (response.error) {
+ console.error('获取热销商品失败:', response.error)
+ return []
+ }
+
+ return response.data as Product[]
+ } catch (error) {
+ console.error('获取热销商品异常:', error)
+ return []
+ }
+ }
+
+ // 获取推荐商品(带badge的商品)
+ async getRecommendedProducts(limit: number = 10): Promise {
+ try {
+ const response = await supa
+ .from('products')
+ .select('*')
+ .not('badge', 'is', null)
+ .order('sales', { ascending: false })
+ .limit(limit)
+ .execute()
+
+ if (response.error) {
+ console.error('获取推荐商品失败:', response.error)
+ return []
+ }
+
+ return response.data as Product[]
+ } catch (error) {
+ console.error('获取推荐商品异常:', error)
+ return []
+ }
+ }
+}
+
+// 导出单例实例
+export const supabaseService = new SupabaseService()
+
+// 默认导出
+export default supabaseService