# 🔧 前端与后端联调指南

## 📋 目录
1. [联调环境配置](#联调环境配置)
2. [本地开发环境搭建](#本地开发环境搭建)
3. [前端连接后端](#前端连接后端)
4. [调试工具和方法](#调试工具和方法)
5. [常见联调场景](#常见联调场景)
6. [问题排查](#问题排查)

---

## 一、联调环境配置

### 1.1 环境类型

#### 开发环境 (Development)
- **Supabase 本地实例**: Docker Compose 运行在 `192.168.0.150:8080`
- **Supabase 云服务**: 使用开发项目
- **前端**: uni-app-x 开发模式

#### 生产环境 (Production)
- **Supabase 云服务**: 生产项目
- **前端**: 编译后的应用

### 1.2 配置文件位置

#### 前端配置
**文件**: `ak/config.uts`

```typescript
// 开发环境 - 本地 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`

```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 (推荐)

```bash
# 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

```bash
# 1. 安装 Supabase CLI
npm install -g supabase

# 2. 初始化项目
supabase init

# 3. 启动本地实例
supabase start

# 4. 查看服务信息
supabase status
```

### 2.2 数据库初始化

```bash
# 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 前端开发环境

```bash
# 1. 安装依赖 (如果使用 npm)
npm install

# 2. 启动 uni-app-x 开发服务器
# 在 HBuilderX 中:
# - 运行 -> 运行到浏览器/手机模拟器
# - 或使用命令行工具

# 3. 配置开发环境
# 修改 ak/config.uts 中的 SUPA_URL 和 SUPA_KEY
```

---

## 三、前端连接后端

### 3.1 Supabase 客户端初始化

#### 全局单例模式
**文件**: `components/supadb/aksupainstance.uts`

```typescript
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
```

#### 在页面中使用

```typescript
// 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)

```typescript
// 简单查询
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)

```typescript
// 插入订单
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)

```typescript
// 更新商品状态
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)

```typescript
// 删除收藏
await supa.delete('ml_user_favorites', { id: favoriteId })

// 删除购物车商品
await supa.delete('ml_shopping_cart', {
    user_id: userId,
    product_id: productId
})
```

#### 3.2.5 调用数据库函数 (RPC)

```typescript
// 计算购物车总金额
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 链式查询构建器

```typescript
// 使用链式 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 浏览器开发者工具

#### 网络请求调试
1. **打开 Chrome DevTools** (F12)
2. **Network 标签页**
   - 查看所有 HTTP 请求
   - 检查请求 URL、Headers、Body
   - 查看响应状态码、数据

3. **Console 标签页**
   - 查看 `console.log()` 输出
   - 查看错误信息
   - 执行调试代码

#### 示例: 检查 API 请求

```typescript
// 在代码中添加日志
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

#### 实时查看数据
1. **Table Editor**
   - 查看表数据
   - 手动编辑数据
   - 验证数据是否正确

2. **SQL Editor**
   - 执行 SQL 查询
   - 测试数据库函数
   - 验证 RLS 策略

3. **API Logs**
   - 查看 API 请求日志
   - 检查错误信息
   - 分析性能问题

#### 示例: 测试查询

```sql
-- 在 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

```http
# 获取商品列表
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

```sql
-- 直接连接 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 调试工具
1. **控制台输出**
   - 查看 `console.log()` 输出
   - 查看错误堆栈

2. **网络请求监控**
   - 查看所有网络请求
   - 检查请求参数和响应

3. **断点调试**
   - 在代码中设置断点
   - 单步执行
   - 查看变量值

---

## 五、常见联调场景

### 5.1 场景一: 查询商品列表失败

#### 问题现象
```typescript
// 前端代码
const res = await supa.select('ml_products', null, { limit: 20 })
// res.success = false
// res.error = "relation 'ml_products' does not exist"
```

#### 排查步骤
1. **检查数据库表是否存在**
   ```sql
   SELECT table_name FROM information_schema.tables 
   WHERE table_schema = 'public' AND table_name = 'ml_products';
   ```

2. **检查表是否已创建**
   - 执行 `complete_mall_database.sql` 脚本
   - 验证脚本执行成功

3. **检查连接配置**
   ```typescript
   console.log('Supabase URL:', SUPA_URL)
   console.log('Supabase Key:', SUPA_KEY)
   ```

#### 解决方案
```bash
# 重新执行数据库脚本
psql -h localhost -U postgres -d postgres -f doc_mall/database/complete_mall_database.sql
```

### 5.2 场景二: RLS 策略阻止数据访问

#### 问题现象
```typescript
// 查询用户数据返回空
const res = await supa.select('ml_user_profiles', { user_id: userId })
// res.data = [] 或 null
```

#### 排查步骤
1. **检查用户是否已登录**
   ```typescript
   const session = await supa.getSession()
   console.log('当前用户:', session.user)
   ```

2. **检查 RLS 策略**
   ```sql
   -- 查看表的 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;
   ```

3. **检查 auth_id 关联**
   ```sql
   -- 验证 ak_users.auth_id 是否正确
   SELECT id, auth_id FROM ak_users WHERE id = 'user-uuid';
   ```

#### 解决方案
```typescript
// 确保用户已登录
await supa.signIn('user@example.com', 'password')

// 或检查 Token
const token = AkReq.getToken()
console.log('JWT Token:', token)
```

### 5.3 场景三: 插入数据失败

#### 问题现象
```typescript
// 插入订单失败
const res = await supa.insert('ml_orders', orderData)
// res.success = false
// res.error = "new row violates row-level security policy"
```

#### 排查步骤
1. **检查必填字段**
   ```typescript
   console.log('订单数据:', JSON.stringify(orderData, null, 2))
   ```

2. **检查外键约束**
   ```sql
   -- 验证 user_id 和 merchant_id 是否存在
   SELECT id FROM ak_users WHERE id IN ('user-id', 'merchant-id');
   ```

3. **检查 RLS INSERT 策略**
   ```sql
   SELECT * FROM pg_policies 
   WHERE tablename = 'ml_orders' AND cmd = 'INSERT';
   ```

#### 解决方案
```typescript
// 确保数据完整
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 场景四: 实时数据同步不工作

#### 问题现象
```typescript
// 订阅订单状态更新，但没有收到推送
supa.realtime.subscribe('ml_orders', {
    filter: `id=eq.${orderId}`,
    event: 'UPDATE',
    callback: (payload) => {
        console.log('订单更新:', payload)  // 没有触发
    }
})
```

#### 排查步骤
1. **检查 WebSocket 连接**
   ```typescript
   console.log('WebSocket URL:', WS_URL)
   ```

2. **检查表是否启用 Realtime**
   ```sql
   -- 在 Supabase Dashboard 中检查
   -- Database -> Replication -> 确保 ml_orders 表已启用
   ```

3. **检查网络连接**
   - 确保 WebSocket 连接没有被防火墙阻止
   - 检查浏览器控制台是否有 WebSocket 错误

#### 解决方案
```typescript
// 确保 WebSocket URL 正确
export const WS_URL: string = 'wss://your-project.supabase.co/realtime/v1/websocket'

// 在 Supabase Dashboard 中启用表的 Realtime
// Database -> Replication -> 找到 ml_orders -> 启用
```

### 5.5 场景五: 数据库函数调用失败

#### 问题现象
```typescript
// 调用 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"
```

#### 排查步骤
1. **检查函数是否存在**
   ```sql
   SELECT routine_name FROM information_schema.routines 
   WHERE routine_schema = 'public' AND routine_name = 'calculate_cart_total';
   ```

2. **检查函数参数**
   ```sql
   -- 查看函数定义
   \df calculate_cart_total
   ```

3. **测试函数**
   ```sql
   SELECT calculate_cart_total('user-uuid-here');
   ```

#### 解决方案
```bash
# 重新执行数据库脚本，确保函数已创建
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 常用调试命令

#### 前端调试
```typescript
// 打印完整请求信息
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
})
```

#### 数据库调试
```sql
-- 查看表结构
\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 日志收集

#### 前端日志
```typescript
// 创建日志工具
class DebugLogger {
    static log(module: string, action: string, data: any) {
        console.log(`[${module}] ${action}:`, data)
        // 可以发送到日志服务器
    }
}

// 使用
DebugLogger.log('MallAPI', '查询商品', { filter, options })
```

#### 后端日志
```sql
-- 启用 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 开发环境配置

1. **使用环境变量**
   ```typescript
   // 开发环境
   const isDev = process.env.NODE_ENV === 'development'
   export const SUPA_URL = isDev 
       ? 'http://192.168.0.150:8080' 
       : 'https://ak3.oulog.com'
   ```

2. **统一错误处理**
   ```typescript
   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
       }
   }
   ```

3. **请求重试机制**
   ```typescript
   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 联调流程

1. **数据库准备**
   - 执行数据库脚本
   - 插入测试数据
   - 验证表结构

2. **前端配置**
   - 配置 Supabase URL 和 Key
   - 测试连接
   - 验证认证

3. **功能测试**
   - 测试 CRUD 操作
   - 测试 RLS 策略
   - 测试实时同步

4. **问题排查**
   - 查看日志
   - 检查网络请求
   - 验证数据库数据

---

## 📚 相关文档

- [模块分析报告](./MODULE_ANALYSIS.md)
- [数据库创建报告](./database/database_creation_report.md)
- [完整部署指南](./database/complete_deployment_guide.md)
- [Supabase 官方文档](https://supabase.com/docs)

---

**生成时间**: 2025年1月  
**版本**: v1.0  
**状态**: ✅ 完整联调指南
