19 KiB
19 KiB
🔧 前端与后端联调指南
📋 目录
一、联调环境配置
1.1 环境类型
开发环境 (Development)
- Supabase 本地实例: Docker Compose 运行在
192.168.0.150:8080 - Supabase 云服务: 使用开发项目
- 前端: uni-app-x 开发模式
生产环境 (Production)
- Supabase 云服务: 生产项目
- 前端: 编译后的应用
1.2 配置文件位置
前端配置
文件: ak/config.uts
// 开发环境 - 本地 Supabase
export const SUPA_URL: string = 'http://192.168.0.150:8080'
export const SUPA_KEY: string = 'your-anon-key'
// 生产环境 - Supabase 云服务
export const SUPA_URL: string = 'https://ak3.oulog.com'
export const SUPA_KEY: string = 'your-anon-key'
// WebSocket 实时连接
export const WS_URL: string = 'wss://ak3.oulog.com/realtime/v1/websocket'
后端配置 (Docker)
文件: doc_chat/supa.env
# Supabase 本地配置
POSTGRES_HOST=db
POSTGRES_DB=postgres
POSTGRES_PORT=5432
POSTGRES_PASSWORD=your-password
# API 配置
KONG_HTTP_PORT=8000
KONG_HTTPS_PORT=8443
# Auth 配置
API_EXTERNAL_URL=http://localhost:8000
SITE_URL=http://localhost:3000
二、本地开发环境搭建
2.1 Supabase 本地实例启动
方式一: Docker Compose (推荐)
# 1. 进入 Supabase 目录
cd doc_chat
# 2. 启动 Supabase 服务
docker-compose -f supa-docker-compose.yml up -d
# 3. 检查服务状态
docker-compose -f supa-docker-compose.yml ps
# 4. 查看日志
docker-compose -f supa-docker-compose.yml logs -f
服务端口:
- API:
http://localhost:8000或http://192.168.0.150:8080 - PostgreSQL:
localhost:5432 - Dashboard:
http://localhost:3000
方式二: Supabase CLI
# 1. 安装 Supabase CLI
npm install -g supabase
# 2. 初始化项目
supabase init
# 3. 启动本地实例
supabase start
# 4. 查看服务信息
supabase status
2.2 数据库初始化
# 1. 执行商城数据库脚本
psql -h localhost -U postgres -d postgres -f doc_mall/database/complete_mall_database.sql
# 或使用 Supabase Dashboard SQL Editor
# 1. 打开 http://localhost:3000
# 2. 进入 SQL Editor
# 3. 复制粘贴 complete_mall_database.sql 内容
# 4. 执行脚本
# 2. 插入模拟数据 (可选)
psql -h localhost -U postgres -d postgres -f doc_mall/database/mock_data_insert.sql
# 3. 验证数据库
psql -h localhost -U postgres -d postgres -f doc_mall/database/validation_test.sql
2.3 前端开发环境
# 1. 安装依赖 (如果使用 npm)
npm install
# 2. 启动 uni-app-x 开发服务器
# 在 HBuilderX 中:
# - 运行 -> 运行到浏览器/手机模拟器
# - 或使用命令行工具
# 3. 配置开发环境
# 修改 ak/config.uts 中的 SUPA_URL 和 SUPA_KEY
三、前端连接后端
3.1 Supabase 客户端初始化
全局单例模式
文件: components/supadb/aksupainstance.uts
import AkSupa from './aksupa.uts'
import { SUPA_URL, SUPA_KEY } from '@/ak/config.uts'
// 创建全局 Supabase 客户端实例
const supa = new AkSupa(SUPA_URL, SUPA_KEY)
// 自动登录 (开发环境)
const supaReady: Promise<boolean> = (async () => {
try {
await supa.signIn('test@example.com', 'password')
return true
} catch (err) {
console.error('Supabase auto sign-in failed', err)
return false
}
})()
export { supaReady }
export default supa
在页面中使用
// pages/mall/consumer/index.uvue
<script setup lang="uts">
import supa from '@/components/supadb/aksupainstance.uts'
import { ProductType } from '@/types/mall-types.uts'
const products = ref<Array<ProductType>>([])
onMounted(async () => {
// 等待 Supabase 客户端就绪
await supaReady
// 查询商品列表
const res = await supa.select('ml_products', {
status: 1 // 只查询已上架商品
}, {
limit: 20,
order: 'created_at.desc'
})
if (res.success && res.data) {
products.value = res.data as Array<ProductType>
}
})
</script>
3.2 API 调用方式
3.2.1 查询数据 (SELECT)
// 简单查询
const res = await supa.select('ml_products', null, {
limit: 10,
order: 'created_at.desc'
})
// 带过滤条件
const res = await supa.select('ml_products', {
status: 1,
category_id: categoryId
}, {
limit: 20,
order: 'sale_count.desc'
})
// 复杂过滤 (PostgREST 操作符)
const res = await supa.select('ml_products', {
base_price: { gte: 100, lte: 500 },
name: { ilike: '%商品%' },
category_id: { in: [id1, id2, id3] }
}, {
limit: 20
})
// 单条记录
const res = await supa.select('ml_products', { id: productId }, {
single: true
})
// 选择特定字段
const res = await supa.select('ml_products', null, {
columns: 'id,name,base_price,main_image_url',
limit: 20
})
3.2.2 插入数据 (INSERT)
// 插入订单
const orderRes = await supa.insert('ml_orders', {
user_id: userId,
merchant_id: merchantId,
total_amount: 100.00,
order_status: 1,
payment_status: 1,
shipping_status: 1,
shipping_address: {
receiver_name: '张三',
receiver_phone: '13800138000',
address_detail: '北京市朝阳区xxx'
}
})
// 批量插入
const items = [
{ order_id: orderId, product_id: productId1, quantity: 2 },
{ order_id: orderId, product_id: productId2, quantity: 1 }
]
const itemsRes = await supa.insert('ml_order_items', items)
3.2.3 更新数据 (UPDATE)
// 更新商品状态
await supa.update('ml_products',
{ id: productId }, // 过滤条件
{
status: 2, // 下架
updated_at: new Date().toISOString()
}
)
// 更新订单状态
await supa.update('ml_orders',
{ id: orderId },
{
order_status: 2, // 待发货
updated_at: new Date().toISOString()
}
)
3.2.4 删除数据 (DELETE)
// 删除收藏
await supa.delete('ml_user_favorites', { id: favoriteId })
// 删除购物车商品
await supa.delete('ml_shopping_cart', {
user_id: userId,
product_id: productId
})
3.2.5 调用数据库函数 (RPC)
// 计算购物车总金额
const totalRes = await supa.rpc('calculate_cart_total', {
p_user_id: userId
})
// 生成订单号
const orderNoRes = await supa.rpc('generate_order_no')
// 获取用户默认地址
const addressRes = await supa.rpc('get_user_default_address', {
p_user_id: userId
})
3.3 链式查询构建器
// 使用链式 API
const res = await supa
.from('ml_products')
.eq('status', 1)
.gte('base_price', 100)
.lte('base_price', 500)
.like('name', '%商品%')
.order('created_at', { ascending: false })
.limit(20)
.select()
四、调试工具和方法
4.1 浏览器开发者工具
网络请求调试
-
打开 Chrome DevTools (F12)
-
Network 标签页
- 查看所有 HTTP 请求
- 检查请求 URL、Headers、Body
- 查看响应状态码、数据
-
Console 标签页
- 查看
console.log()输出 - 查看错误信息
- 执行调试代码
- 查看
示例: 检查 API 请求
// 在代码中添加日志
console.log('请求商品列表:', {
table: 'ml_products',
filter: { status: 1 },
options: { limit: 20 }
})
const res = await supa.select('ml_products', { status: 1 }, { limit: 20 })
console.log('API 响应:', {
success: res.success,
data: res.data,
error: res.error,
status: res.status
})
4.2 Supabase Dashboard
实时查看数据
-
Table Editor
- 查看表数据
- 手动编辑数据
- 验证数据是否正确
-
SQL Editor
- 执行 SQL 查询
- 测试数据库函数
- 验证 RLS 策略
-
API Logs
- 查看 API 请求日志
- 检查错误信息
- 分析性能问题
示例: 测试查询
-- 在 Supabase Dashboard SQL Editor 中执行
SELECT * FROM ml_products
WHERE status = 1
ORDER BY created_at DESC
LIMIT 20;
-- 测试 RLS 策略
SET ROLE authenticated;
SET request.jwt.claim.sub = 'user-uuid-here';
SELECT * FROM ml_user_profiles;
4.3 Postman / Insomnia
直接测试 Supabase API
# 获取商品列表
GET https://your-project.supabase.co/rest/v1/ml_products?status=eq.1&limit=20
Headers:
apikey: your-anon-key
Authorization: Bearer your-jwt-token
Content-Type: application/json
# 创建订单
POST https://your-project.supabase.co/rest/v1/ml_orders
Headers:
apikey: your-anon-key
Authorization: Bearer your-jwt-token
Content-Type: application/json
Prefer: return=representation
Body:
{
"user_id": "user-uuid",
"merchant_id": "merchant-uuid",
"total_amount": 100.00,
"order_status": 1
}
# 调用 RPC 函数
POST https://your-project.supabase.co/rest/v1/rpc/calculate_cart_total
Headers:
apikey: your-anon-key
Authorization: Bearer your-jwt-token
Content-Type: application/json
Body:
{
"p_user_id": "user-uuid"
}
4.4 数据库客户端工具
pgAdmin / DBeaver / DataGrip
-- 直接连接 PostgreSQL 数据库
-- Host: localhost (或 192.168.0.150)
-- Port: 5432
-- Database: postgres
-- User: postgres
-- Password: (从 supa.env 获取)
-- 查看表结构
SELECT * FROM information_schema.tables
WHERE table_schema = 'public' AND table_name LIKE 'ml_%';
-- 查看数据
SELECT * FROM ml_products LIMIT 10;
-- 查看 RLS 策略
SELECT * FROM pg_policies WHERE tablename = 'ml_products';
4.5 uni-app-x 调试
HBuilderX 调试工具
-
控制台输出
- 查看
console.log()输出 - 查看错误堆栈
- 查看
-
网络请求监控
- 查看所有网络请求
- 检查请求参数和响应
-
断点调试
- 在代码中设置断点
- 单步执行
- 查看变量值
五、常见联调场景
5.1 场景一: 查询商品列表失败
问题现象
// 前端代码
const res = await supa.select('ml_products', null, { limit: 20 })
// res.success = false
// res.error = "relation 'ml_products' does not exist"
排查步骤
-
检查数据库表是否存在
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'ml_products'; -
检查表是否已创建
- 执行
complete_mall_database.sql脚本 - 验证脚本执行成功
- 执行
-
检查连接配置
console.log('Supabase URL:', SUPA_URL) console.log('Supabase Key:', SUPA_KEY)
解决方案
# 重新执行数据库脚本
psql -h localhost -U postgres -d postgres -f doc_mall/database/complete_mall_database.sql
5.2 场景二: RLS 策略阻止数据访问
问题现象
// 查询用户数据返回空
const res = await supa.select('ml_user_profiles', { user_id: userId })
// res.data = [] 或 null
排查步骤
-
检查用户是否已登录
const session = await supa.getSession() console.log('当前用户:', session.user) -
检查 RLS 策略
-- 查看表的 RLS 策略 SELECT * FROM pg_policies WHERE tablename = 'ml_user_profiles'; -- 测试 RLS 策略 SET ROLE authenticated; SET request.jwt.claim.sub = 'auth-user-id'; SELECT * FROM ml_user_profiles; -
检查 auth_id 关联
-- 验证 ak_users.auth_id 是否正确 SELECT id, auth_id FROM ak_users WHERE id = 'user-uuid';
解决方案
// 确保用户已登录
await supa.signIn('user@example.com', 'password')
// 或检查 Token
const token = AkReq.getToken()
console.log('JWT Token:', token)
5.3 场景三: 插入数据失败
问题现象
// 插入订单失败
const res = await supa.insert('ml_orders', orderData)
// res.success = false
// res.error = "new row violates row-level security policy"
排查步骤
-
检查必填字段
console.log('订单数据:', JSON.stringify(orderData, null, 2)) -
检查外键约束
-- 验证 user_id 和 merchant_id 是否存在 SELECT id FROM ak_users WHERE id IN ('user-id', 'merchant-id'); -
检查 RLS INSERT 策略
SELECT * FROM pg_policies WHERE tablename = 'ml_orders' AND cmd = 'INSERT';
解决方案
// 确保数据完整
const orderData = {
user_id: userId, // 必须存在
merchant_id: merchantId, // 必须存在
total_amount: 100.00,
order_status: 1,
payment_status: 1,
shipping_status: 1,
shipping_address: addressData // 必须提供
}
// 确保用户有权限
await supa.signIn('user@example.com', 'password')
5.4 场景四: 实时数据同步不工作
问题现象
// 订阅订单状态更新,但没有收到推送
supa.realtime.subscribe('ml_orders', {
filter: `id=eq.${orderId}`,
event: 'UPDATE',
callback: (payload) => {
console.log('订单更新:', payload) // 没有触发
}
})
排查步骤
-
检查 WebSocket 连接
console.log('WebSocket URL:', WS_URL) -
检查表是否启用 Realtime
-- 在 Supabase Dashboard 中检查 -- Database -> Replication -> 确保 ml_orders 表已启用 -
检查网络连接
- 确保 WebSocket 连接没有被防火墙阻止
- 检查浏览器控制台是否有 WebSocket 错误
解决方案
// 确保 WebSocket URL 正确
export const WS_URL: string = 'wss://your-project.supabase.co/realtime/v1/websocket'
// 在 Supabase Dashboard 中启用表的 Realtime
// Database -> Replication -> 找到 ml_orders -> 启用
5.5 场景五: 数据库函数调用失败
问题现象
// 调用 RPC 函数失败
const res = await supa.rpc('calculate_cart_total', { p_user_id: userId })
// res.success = false
// res.error = "function calculate_cart_total does not exist"
排查步骤
-
检查函数是否存在
SELECT routine_name FROM information_schema.routines WHERE routine_schema = 'public' AND routine_name = 'calculate_cart_total'; -
检查函数参数
-- 查看函数定义 \df calculate_cart_total -
测试函数
SELECT calculate_cart_total('user-uuid-here');
解决方案
# 重新执行数据库脚本,确保函数已创建
psql -h localhost -U postgres -d postgres -f doc_mall/database/complete_mall_database.sql
六、问题排查
6.1 排查清单
连接问题
- Supabase URL 是否正确
- API Key 是否正确
- 网络连接是否正常
- 防火墙是否阻止连接
认证问题
- 用户是否已登录
- JWT Token 是否有效
- Token 是否过期
- auth_id 是否正确关联
数据问题
- 表是否存在
- 字段名是否正确
- 数据类型是否匹配
- 外键约束是否满足
权限问题
- RLS 策略是否正确
- 用户是否有权限
- 策略条件是否满足
6.2 常用调试命令
前端调试
// 打印完整请求信息
console.log('请求详情:', {
url: `${SUPA_URL}/rest/v1/ml_products`,
headers: {
apikey: SUPA_KEY,
Authorization: `Bearer ${token}`
},
filter: filter,
options: options
})
// 打印完整响应
console.log('响应详情:', {
success: res.success,
status: res.status,
data: res.data,
error: res.error,
raw: res
})
数据库调试
-- 查看表结构
\d ml_products
-- 查看索引
\di ml_products*
-- 查看触发器
\d+ ml_products
-- 查看 RLS 策略
SELECT * FROM pg_policies WHERE tablename = 'ml_products';
-- 测试查询性能
EXPLAIN ANALYZE SELECT * FROM ml_products WHERE status = 1;
6.3 错误码参考
| HTTP 状态码 | 含义 | 常见原因 |
|---|---|---|
| 200 | 成功 | - |
| 400 | 请求错误 | 参数错误、数据格式错误 |
| 401 | 未授权 | Token 无效、未登录 |
| 403 | 禁止访问 | RLS 策略阻止 |
| 404 | 未找到 | 表不存在、记录不存在 |
| 500 | 服务器错误 | 数据库错误、函数错误 |
6.4 日志收集
前端日志
// 创建日志工具
class DebugLogger {
static log(module: string, action: string, data: any) {
console.log(`[${module}] ${action}:`, data)
// 可以发送到日志服务器
}
}
// 使用
DebugLogger.log('MallAPI', '查询商品', { filter, options })
后端日志
-- 启用 PostgreSQL 日志
-- 在 postgresql.conf 中设置
log_statement = 'all'
log_duration = on
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
七、最佳实践
7.1 开发环境配置
-
使用环境变量
// 开发环境 const isDev = process.env.NODE_ENV === 'development' export const SUPA_URL = isDev ? 'http://192.168.0.150:8080' : 'https://ak3.oulog.com' -
统一错误处理
async function safeApiCall<T>(apiCall: () => Promise<AkReqResponse<T>>) { try { const res = await apiCall() if (!res.success) { console.error('API 调用失败:', res.error) uni.showToast({ title: '操作失败', icon: 'error' }) } return res } catch (error) { console.error('API 调用异常:', error) uni.showToast({ title: '网络错误', icon: 'error' }) throw error } } -
请求重试机制
async function retryApiCall<T>( apiCall: () => Promise<AkReqResponse<T>>, maxRetries = 3 ) { for (let i = 0; i < maxRetries; i++) { try { const res = await apiCall() if (res.success) return res } catch (error) { if (i === maxRetries - 1) throw error await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))) } } }
7.2 联调流程
-
数据库准备
- 执行数据库脚本
- 插入测试数据
- 验证表结构
-
前端配置
- 配置 Supabase URL 和 Key
- 测试连接
- 验证认证
-
功能测试
- 测试 CRUD 操作
- 测试 RLS 策略
- 测试实时同步
-
问题排查
- 查看日志
- 检查网络请求
- 验证数据库数据
📚 相关文档
生成时间: 2025年1月
版本: v1.0
状态: ✅ 完整联调指南