Files
mp-java/docs/DELIVERY_ADDRESS_DESIGN.md

253 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 收货信息设计方案
## 概述
本文档详细说明了电商系统中收货信息的设计方案,采用**地址快照 + 地址引用混合模式**,确保订单收货信息的完整性和一致性。
## 设计原则
1. **数据一致性**:用户下单时保存收货地址快照,避免后续地址修改影响历史订单
2. **用户体验**:自动读取用户默认地址,减少用户输入
3. **灵活性**:支持用户在下单时临时修改收货信息
4. **可追溯性**保留地址ID引用关系便于数据分析和问题排查
## 数据库设计
### 1. 用户地址表 (shop_user_address)
```sql
CREATE TABLE `shop_user_address` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) DEFAULT NULL COMMENT '姓名',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号码',
`country` varchar(50) DEFAULT NULL COMMENT '所在国家',
`province` varchar(50) DEFAULT NULL COMMENT '所在省份',
`city` varchar(50) DEFAULT NULL COMMENT '所在城市',
`region` varchar(50) DEFAULT NULL COMMENT '所在辖区',
`address` varchar(500) DEFAULT NULL COMMENT '收货地址',
`full_address` varchar(500) DEFAULT NULL COMMENT '完整地址',
`lat` varchar(50) DEFAULT NULL COMMENT '纬度',
`lng` varchar(50) DEFAULT NULL COMMENT '经度',
`gender` int(11) DEFAULT NULL COMMENT '1先生 2女士',
`type` varchar(20) DEFAULT NULL COMMENT '家、公司、学校',
`is_default` tinyint(1) DEFAULT 0 COMMENT '默认收货地址',
`sort_number` int(11) DEFAULT NULL COMMENT '排序号',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_is_default` (`is_default`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收货地址';
```
### 2. 订单表收货信息字段 (shop_order)
```sql
-- 订单表中的收货信息字段
`address_id` int(11) DEFAULT NULL COMMENT '收货地址ID引用关系',
`address` varchar(500) DEFAULT NULL COMMENT '收货地址快照',
`real_name` varchar(100) DEFAULT NULL COMMENT '收货人姓名快照',
`address_lat` varchar(50) DEFAULT NULL COMMENT '地址纬度',
`address_lng` varchar(50) DEFAULT NULL COMMENT '地址经度',
```
## 业务流程设计
### 1. 下单时收货地址处理流程
```mermaid
flowchart TD
A[用户下单] --> B{前端是否传入完整地址?}
B -->|是| C[使用前端传入地址]
B -->|否| D{是否指定地址ID?}
D -->|是| E[根据地址ID获取地址]
E --> F{地址是否存在且属于当前用户?}
F -->|是| G[使用指定地址]
F -->|否| H[获取用户默认地址]
D -->|否| H[获取用户默认地址]
H --> I{是否有默认地址?}
I -->|是| J[使用默认地址]
I -->|否| K[获取用户第一个地址]
K --> L{是否有地址?}
L -->|是| M[使用第一个地址]
L -->|否| N[抛出异常:请先添加收货地址]
C --> O[创建地址快照]
G --> O
J --> O
M --> O
O --> P[保存订单]
```
### 2. 地址优先级
1. **前端传入的完整地址信息**(最高优先级)
2. **指定的地址ID对应的地址**
3. **用户默认收货地址**
4. **用户的第一个收货地址**
5. **无地址时抛出异常**
## 核心实现
### 1. 收货地址处理方法
```java
/**
* 处理收货地址信息
* 优先级:前端传入地址 > 指定地址ID > 用户默认地址
*/
private void processDeliveryAddress(ShopOrder shopOrder, OrderCreateRequest request, User loginUser) {
// 1. 如果前端已经传入了完整的收货地址信息,直接使用
if (isAddressInfoComplete(request)) {
return;
}
// 2. 如果指定了地址ID获取该地址信息
if (request.getAddressId() != null) {
ShopUserAddress userAddress = shopUserAddressService.getById(request.getAddressId());
if (userAddress != null && userAddress.getUserId().equals(loginUser.getUserId())) {
copyAddressToOrder(userAddress, shopOrder, request);
return;
}
}
// 3. 获取用户默认收货地址
ShopUserAddress defaultAddress = shopUserAddressService.getDefaultAddress(loginUser.getUserId());
if (defaultAddress != null) {
copyAddressToOrder(defaultAddress, shopOrder, request);
return;
}
// 4. 如果没有默认地址,获取用户的第一个地址
List<ShopUserAddress> userAddresses = shopUserAddressService.getUserAddresses(loginUser.getUserId());
if (!userAddresses.isEmpty()) {
copyAddressToOrder(userAddresses.get(0), shopOrder, request);
return;
}
// 5. 如果用户没有任何收货地址,抛出异常
throw new BusinessException("请先添加收货地址");
}
```
### 2. 地址快照创建
```java
/**
* 将用户地址信息复制到订单中(创建快照)
*/
private void copyAddressToOrder(ShopUserAddress userAddress, ShopOrder shopOrder, OrderCreateRequest request) {
// 保存地址ID引用关系
shopOrder.setAddressId(userAddress.getId());
request.setAddressId(userAddress.getId());
// 创建地址信息快照
if (request.getAddress() == null || request.getAddress().trim().isEmpty()) {
StringBuilder fullAddress = new StringBuilder();
if (userAddress.getProvince() != null) fullAddress.append(userAddress.getProvince());
if (userAddress.getCity() != null) fullAddress.append(userAddress.getCity());
if (userAddress.getRegion() != null) fullAddress.append(userAddress.getRegion());
if (userAddress.getAddress() != null) fullAddress.append(userAddress.getAddress());
shopOrder.setAddress(fullAddress.toString());
request.setAddress(fullAddress.toString());
}
// 复制收货人信息
if (request.getRealName() == null || request.getRealName().trim().isEmpty()) {
shopOrder.setRealName(userAddress.getName());
request.setRealName(userAddress.getName());
}
// 复制经纬度信息
if (request.getAddressLat() == null && userAddress.getLat() != null) {
shopOrder.setAddressLat(userAddress.getLat());
request.setAddressLat(userAddress.getLat());
}
if (request.getAddressLng() == null && userAddress.getLng() != null) {
shopOrder.setAddressLng(userAddress.getLng());
request.setAddressLng(userAddress.getLng());
}
}
```
## 前端集成建议
### 1. 下单页面地址选择
```javascript
// 获取用户地址列表
const getUserAddresses = async () => {
const response = await api.get('/api/shop/user-address/my');
return response.data;
};
// 获取默认地址
const getDefaultAddress = async () => {
const addresses = await getUserAddresses();
return addresses.find(addr => addr.isDefault) || addresses[0];
};
// 下单时的地址处理
const createOrder = async (orderData) => {
// 如果用户没有选择地址,使用默认地址
if (!orderData.addressId && !orderData.address) {
const defaultAddress = await getDefaultAddress();
if (defaultAddress) {
orderData.addressId = defaultAddress.id;
}
}
return api.post('/api/shop/order/create', orderData);
};
```
### 2. 地址选择组件
```vue
<template>
<div class="address-selector">
<div v-if="selectedAddress" class="selected-address">
<div class="address-info">
<span class="name">{{ selectedAddress.name }}</span>
<span class="phone">{{ selectedAddress.phone }}</span>
</div>
<div class="address-detail">{{ selectedAddress.fullAddress }}</div>
</div>
<button @click="showAddressList = true">选择收货地址</button>
</div>
</template>
```
## 优势分析
### 1. 数据一致性
- 订单创建时保存地址快照,确保历史订单信息不受用户后续地址修改影响
- 同时保留地址ID引用便于数据关联和分析
### 2. 用户体验
- 自动读取用户默认地址,减少用户操作步骤
- 支持临时修改收货信息,满足特殊需求
- 智能地址选择逻辑,确保总能找到合适的收货地址
### 3. 系统稳定性
- 完善的异常处理机制,避免因地址问题导致下单失败
- 详细的日志记录,便于问题排查和系统监控
### 4. 扩展性
- 支持多种地址类型(家、公司、学校等)
- 预留经纬度字段,支持地图定位功能
- 灵活的排序和默认地址设置
## 注意事项
1. **地址验证**:建议在前端和后端都进行地址完整性验证
2. **默认地址管理**:确保用户只能有一个默认地址
3. **地址数量限制**:建议限制用户地址数量,避免数据冗余
4. **隐私保护**:敏感信息如手机号需要适当脱敏处理
5. **性能优化**:对于高频查询的地址信息,可考虑适当缓存
## 总结
本设计方案通过地址快照机制确保了订单数据的一致性,通过智能地址选择提升了用户体验,通过完善的异常处理保证了系统稳定性。该方案已在 `OrderBusinessService` 中实现,可以直接投入使用。