design.md 10 KB

Design Document

Overview

本设计文档描述将 supermart-mini 微信小程序迁移到 React Native/Expo iOS 应用的技术方案。迁移遵循以下原则:

  1. 保持与小程序一致的视觉样式和交互体验
  2. 复用小程序的 CDN 图片资源(通过 Images 常量配置)
  3. 对接相同的后端 API 接口
  4. 使用 React Native 的 ImageBackground 组件实现背景图效果

Architecture

graph TB
    subgraph "React Native App"
        subgraph "Pages (app/)"
            Tabs["Tab Pages<br/>(tabs)"]
            Login["Login Page"]
            Address["Address Pages"]
            Orders["Order Pages"]
            Product["Product Pages"]
        end
        
        subgraph "Components"
            Home["Home Components"]
            ProductComp["Product Components"]
            Common["Common Components"]
        end
        
        subgraph "Services"
            UserService["user.ts"]
            MallService["mall.ts"]
            AwardService["award.ts"]
            AddressService["address.ts"]
            BaseService["base.ts"]
        end
        
        subgraph "Constants"
            Images["images.ts"]
            Theme["theme.ts"]
        end
        
        subgraph "Contexts"
            AuthContext["AuthContext"]
        end
    end
    
    subgraph "Backend API"
        API["REST API Server"]
    end
    
    Tabs --> Services
    Login --> UserService
    Address --> AddressService
    Orders --> MallService
    Product --> MallService
    Services --> API
    Pages --> Images
    Pages --> Components

Components and Interfaces

1. 页面组件结构

Login Page (app/login.tsx)

interface LoginScreenProps {}

// 状态
- phone: string           // 手机号
- verifyCode: string      // 验证码
- agreeFlag: boolean      // 协议同意状态
- loading: boolean        // 登录加载状态
- countdown: number       // 验证码倒计时

// 方法
- handleGetVerifyCode()   // 获取验证码
- handleLogin()           // 登录
- goBack()               // 返回

Mine Page (app/(tabs)/mine.tsx)

interface MineScreenProps {}

interface IndexData {
  couponCount?: number;
  inventoryCount?: number;
  magicBalance?: number;
  treasureBoxCount?: number;
}

// 状态
- userInfo: UserInfo | null
- indexData: IndexData | null

// 方法
- loadData()              // 加载用户数据
- handleLogin()           // 跳转登录
- handleCopy(text)        // 复制文本
- handleMenuPress(route)  // 菜单点击
- showNumber(key)         // 格式化数字显示

Address List Page (app/address/index.tsx)

interface Address {
  id: string;
  contactName: string;
  contactNo: string;
  province: string;
  city: string;
  district: string;
  address: string;
  defaultFlag: number;
}

// 方法
- loadData()              // 加载地址列表
- selectAddress(item)     // 选择地址
- setDefault(item)        // 设为默认
- handleDelete(item)      // 删除地址
- editAddress(item)       // 编辑地址
- addNewAddress()         // 新增地址

Orders Page (app/orders/index.tsx)

interface OrderItem {
  tradeNo: string;
  status: number;
  statusText: string;
  goodsName: string;
  goodsCover: string;
  quantity: number;
  paymentAmount: number;
  createTime: string;
}

// Tab 配置
const tabs = [
  { label: '全部', value: undefined },
  { label: '待付款', value: 1 },
  { label: '待发货', value: 2 },
  { label: '待收货', value: 3 },
  { label: '已完成', value: 4 },
];

2. 服务层接口

Address Service (services/address.ts)

// API 端点
const apis = {
  LIST: '/api/addressBook',
  ADD: '/api/addressBook/add',
  DELETE: '/api/addressBook/delete/{id}',
  DEFAULT: '/api/addressBook/getDefault',
  UPDATE: '/api/addressBook/update',
  GET_AREA: '/api/area',
};

// 方法
export const getAddressList: () => Promise<Address[]>
export const getDefaultAddress: () => Promise<Address | null>
export const addAddress: (params) => Promise<boolean>
export const updateAddress: (params) => Promise<boolean>
export const deleteAddress: (id) => Promise<boolean>
export const getArea: (pid) => Promise<AreaItem[]>

Award Service (services/award.ts)

// 新增方法
export const getMagicIndex: () => Promise<IndexData>
export const getPoolList: (params) => Promise<PoolListResponse>
export const getPoolDetail: (poolId) => Promise<PoolDetail>
export const getIPList: () => Promise<IPItem[]>

Data Models

User Info

interface UserInfo {
  id: string;
  nickname: string;
  avatar: string;
  phone?: string;
  realName?: string;
  idNum?: string;
  balance?: number;
}

Address

interface Address {
  id: string;
  contactName: string;
  contactNo: string;
  location?: string;
  province: string;
  city: string;
  district: string;
  address: string;
  defaultFlag: number;  // 0: 非默认, 1: 默认
}

Order

interface OrderItem {
  tradeNo: string;
  status: number;
  statusText: string;
  goodsName: string;
  goodsCover: string;
  quantity: number;
  paymentAmount: number;
  createTime: string;
}

interface OrderDetail extends OrderItem {
  address?: {
    contactName: string;
    contactNo: string;
    province: string;
    city: string;
    district: string;
    address: string;
  };
  expressInfo?: ExpressInfo[];
}

Pool (奖池)

interface PoolItem {
  id: string;
  name: string;
  cover: string;
  price: number;
  mode: string;
}

interface PoolDetail extends PoolItem {
  description?: string;
  prizes?: PrizeItem[];
  boxCount?: number;
}

Correctness Properties

A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.

Property 1: Login API call with correct parameters For any valid phone number and verify code combination, when the login function is called, it SHALL send a request with loginWay, mobile, and verifycode parameters to the login API endpoint. Validates: Requirements 1.3

Property 2: Navigation after login success For any successful login response, the system SHALL navigate back if needInfo is false, or navigate to user-info page if needInfo is true. Validates: Requirements 1.4

Property 3: User profile data display For any user info object with avatar, nickname, id, and phone fields, the mine page SHALL render all these fields in the appropriate UI elements. Validates: Requirements 2.2

Property 4: Statistics data display For any index data object, the mine page SHALL display couponCount, inventoryCount, magicBalance, and treasureBoxCount values. Validates: Requirements 2.3

Property 5: Function entry navigation For any function entry tap with a valid route, the system SHALL trigger navigation to that route. Validates: Requirements 2.4, 2.5

Property 6: Address list rendering For any list of addresses, the address list page SHALL render each address with contactName, contactNo, and full address (province + city + district + address). Validates: Requirements 3.2

Property 7: Address save API call For any valid address form submission, the system SHALL call the add or update API and navigate back on success. Validates: Requirements 3.5

Property 8: Order item rendering For any order item, the orders page SHALL display tradeNo, statusText, goodsCover, goodsName, paymentAmount, quantity, and createTime. Validates: Requirements 4.2

Property 9: Order navigation For any order item tap, the system SHALL navigate to the order detail page with the correct tradeNo. Validates: Requirements 4.3

Property 10: Order action handling For any order with status 1 (待付款), the pay action SHALL be available; for status 3 (待收货), the confirm receipt action SHALL be available. Validates: Requirements 4.4

Property 11: Welfare room navigation For any room entry tap with type 0, 1, or 2, the system SHALL navigate to the corresponding welfare page (room, catchDoll, or wish). Validates: Requirements 6.3

Property 12: Image constants consistency For any image key used in the app, the Images constant SHALL provide a valid CDN URL matching the ossurl configuration. Validates: Requirements 8.1, 8.2

Property 13: API service function mapping For any service function call, the request SHALL be sent to the correct API endpoint matching the mini-program service configuration. Validates: Requirements 9.1

Property 14: API error handling For any failed API call, the system SHALL catch the error and not crash, optionally displaying an error message. Validates: Requirements 9.2

Property 15: Authentication token inclusion For any authenticated API request, the HTTP headers SHALL include the authorization token. Validates: Requirements 9.3

Error Handling

API 错误处理

// HTTP 请求封装中的错误处理
try {
  const response = await fetch(url, options);
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  const data = await response.json();
  if (data.code === '401') {
    // Token 过期,跳转登录
    router.push('/login');
    return { success: false, code: '401' };
  }
  return data;
} catch (error) {
  console.error('API Error:', error);
  return { success: false, error };
}

页面错误处理

  • 加载失败时显示重试按钮
  • 网络错误时显示提示信息
  • 表单验证失败时显示具体错误

Testing Strategy

单元测试

  • 使用 Jest 进行单元测试
  • 测试服务层函数的参数处理和返回值
  • 测试工具函数(如数字格式化)

属性测试

  • 使用 fast-check 进行属性测试
  • 每个属性测试运行至少 100 次迭代
  • 测试标注格式:**Feature: miniapp-to-rn-migration, Property {number}: {property_text}**

测试覆盖范围

  1. 服务层 API 调用测试
  2. 页面组件渲染测试
  3. 导航逻辑测试
  4. 表单验证测试
  5. 错误处理测试