车驾管服务系统详细设计说明书
1. 详细设计概述
1.1 文档说明
本文档是车驾管服务系统的详细设计说明书,基于需求文档和概要设计文档编写,详细描述系统的功能模块设计、类设计、接口设计、数据库设计、业务流程设计等,为系统开发提供详细的技术指导。
1.2 设计目标
- 功能完整性:详细描述所有功能模块的实现细节
- 逻辑严谨性:业务流程设计逻辑严谨,考虑各种边界情况
- 技术可行性:设计方案技术可行,符合实际开发需求
- 可维护性:代码结构清晰,易于维护和扩展
1.3 设计原则
- 单一职责原则:每个类、每个方法只负责一个功能
- 开闭原则:对扩展开放,对修改关闭
- 依赖倒置原则:依赖抽象而不是具体实现
- 接口隔离原则:使用多个专门的接口,而不是使用单一的总接口
- 里氏替换原则:子类可以替换父类
- 最小知识原则:一个对象应当对其他对象有尽可能少的了解
1.4 设计约束
- 技术约束:基于Spring Boot、MySQL、Redis等技术栈
- 性能约束:接口响应时间 ≤ 1秒,支持500+并发用户
- 安全约束:数据加密、接口签名、权限控制
- 兼容性约束:支持主流浏览器和移动端
2. 订单管理模块详细设计
2.1 模块概述
订单管理模块是系统的核心模块,负责订单的全生命周期管理,包括订单创建、状态流转、支付、分配、进度跟踪、材料管理等。
2.2 类设计
2.2.1 订单实体类 (Order)
/**
* 订单实体类
*/
public class Order {
/**
* 订单ID
*/
private Long orderId;
/**
* 订单号(唯一)
*/
private String orderNo;
/**
* 客户ID
*/
private Long customerId;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 服务类型ID
*/
private Long serviceId;
/**
* 订单类型(DRIVER-代驾订单, SELF-自驾订单)
*/
private String orderType;
/**
* 订单来源(CUSTOMER-客户直接下单, CHANNEL-渠道API下单, ADMIN-管理员手动创建)
*/
private String orderSource;
/**
* 渠道ID(渠道来源订单必填)
*/
private Long channelId;
/**
* 订单状态(PENDING_PAYMENT-待支付, PAID-已支付, PENDING_ASSIGN-待分配,
* PROCESSING-办理中, PENDING_CONFIRM-待确认, COMPLETED-已完成,
* CANCELLED-已取消, REFUNDED-已退款)
*/
private String status;
/**
* 订单金额
*/
private BigDecimal amount;
/**
* 实际支付金额
*/
private BigDecimal paidAmount;
/**
* 服务地址
*/
private String serviceAddress;
/**
* 服务时间
*/
private LocalDateTime serviceTime;
/**
* 代驾人员ID
*/
private Long driverId;
/**
* 接待员ID
*/
private Long receptionistId;
/**
* 检测站ID
*/
private Long stationId;
/**
* 订单备注
*/
private String remark;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
2.2.2 订单服务类 (OrderService)
/**
* 订单服务接口
*/
public interface OrderService {
/**
* 创建订单(客户下单)
* @param orderCreateDTO 订单创建DTO
* @return 订单信息
*/
OrderVO createOrder(OrderCreateDTO orderCreateDTO);
/**
* 创建订单(渠道API下单)
* @param channelOrderCreateDTO 渠道订单创建DTO
* @return 订单信息
*/
OrderVO createOrderFromChannel(ChannelOrderCreateDTO channelOrderCreateDTO);
/**
* 手动创建订单
* @param adminOrderCreateDTO 管理员订单创建DTO
* @return 订单信息
*/
OrderVO createOrderByAdmin(AdminOrderCreateDTO adminOrderCreateDTO);
/**
* 查询订单列表
* @param queryDTO 查询条件
* @return 订单列表
*/
PageResult<OrderVO> queryOrderList(OrderQueryDTO queryDTO);
/**
* 查询订单详情
* @param orderId 订单ID
* @return 订单详情
*/
OrderDetailVO getOrderDetail(Long orderId);
/**
* 更新订单状态
* @param orderId 订单ID
* @param newStatus 新状态
* @param operatorId 操作人ID
* @param reason 变更原因
*/
void updateOrderStatus(Long orderId, String newStatus, Long operatorId, String reason);
/**
* 取消订单
* @param orderId 订单ID
* @param reason 取消原因
*/
void cancelOrder(Long orderId, String reason);
/**
* 分配订单
* @param orderId 订单ID
* @param assignDTO 分配信息
*/
void assignOrder(Long orderId, OrderAssignDTO assignDTO);
}
2.2.3 订单状态机 (OrderStateMachine)
/**
* 订单状态机
* 管理订单状态的流转规则
*/
public class OrderStateMachine {
/**
* 订单状态流转规则
*/
private static final Map<String, Set<String>> STATUS_TRANSITION_MAP = new HashMap<>();
static {
// 待支付可以流转到:已支付、已取消
STATUS_TRANSITION_MAP.put("PENDING_PAYMENT",
Set.of("PAID", "CANCELLED"));
// 已支付可以流转到:待分配、已退款、已取消
STATUS_TRANSITION_MAP.put("PAID",
Set.of("PENDING_ASSIGN", "REFUNDED", "CANCELLED"));
// 待分配可以流转到:办理中、已取消
STATUS_TRANSITION_MAP.put("PENDING_ASSIGN",
Set.of("PROCESSING", "CANCELLED"));
// 办理中可以流转到:待确认、已取消
STATUS_TRANSITION_MAP.put("PROCESSING",
Set.of("PENDING_CONFIRM", "CANCELLED"));
// 待确认可以流转到:已完成、已取消
STATUS_TRANSITION_MAP.put("PENDING_CONFIRM",
Set.of("COMPLETED", "CANCELLED"));
// 已完成、已取消、已退款为终态,不能再流转
}
/**
* 验证状态流转是否合法
* @param currentStatus 当前状态
* @param newStatus 新状态
* @return 是否合法
*/
public boolean canTransition(String currentStatus, String newStatus) {
Set<String> allowedStatuses = STATUS_TRANSITION_MAP.get(currentStatus);
if (allowedStatuses == null) {
return false;
}
return allowedStatuses.contains(newStatus);
}
/**
* 获取可流转的状态列表
* @param currentStatus 当前状态
* @return 可流转的状态列表
*/
public Set<String> getAllowedStatuses(String currentStatus) {
return STATUS_TRANSITION_MAP.getOrDefault(currentStatus, Collections.emptySet());
}
}
2.3 业务流程设计
2.3.1 订单创建流程(客户下单)
流程说明:
- 客户在客户端选择服务类型、车辆、填写订单信息
- 系统验证订单数据(客户信息、车辆信息、服务类型可用性)
- 系统计算订单金额(根据服务类型、价格规则、优惠活动)
- 创建订单,状态为"待支付"
- 返回订单信息给客户端
详细步骤:
1. 接收订单创建请求
├─ 验证客户登录状态
├─ 验证订单数据完整性
│ ├─ 客户信息验证(客户是否存在、状态是否正常)
│ ├─ 车辆信息验证(车辆是否存在、是否属于该客户)
│ ├─ 服务类型验证(服务类型是否存在、是否启用)
│ └─ 订单信息验证(服务地址、服务时间等)
│
2. 计算订单金额
├─ 获取服务基础价格
├─ 根据价格规则计算价格(按车型、按地区等)
├─ 应用优惠活动(优惠券、积分等)
└─ 计算最终金额
3. 创建订单
├─ 生成订单号(唯一)
├─ 设置订单基本信息
├─ 设置订单状态为"待支付"
├─ 保存订单到数据库
├─ 记录订单创建日志
└─ 发送订单创建通知(可选)
4. 返回订单信息
└─ 返回订单号、订单金额、支付信息等
异常处理:
- 客户信息不存在:返回错误信息"客户信息不存在"
- 车辆信息不存在:返回错误信息"车辆信息不存在"
- 服务类型不可用:返回错误信息"服务类型不可用"
- 价格计算失败:记录错误日志,返回错误信息"价格计算失败,请稍后重试"
- 订单创建失败:记录错误日志,返回错误信息"订单创建失败,请稍后重试"
2.3.2 订单创建流程(渠道API下单)
流程说明:
- 渠道调用订单创建API
- 系统验证API签名和权限
- 系统验证订单数据
- 创建订单,状态根据渠道是否已支付设置为"待支付"或"已支付"
- 返回订单号给渠道
详细步骤:
1. 接收API请求
├─ 验证API签名(HMAC-SHA256)
├─ 验证API Key和Secret
├─ 验证IP白名单
├─ 验证限流(检查调用频率)
└─ 记录API调用日志
2. 验证订单数据
├─ 必填字段验证
│ ├─ 客户信息(姓名、手机号、身份证号)
│ ├─ 车辆信息(车牌号、品牌、型号)
│ ├─ 服务信息(服务类型、服务地址)
│ └─ 订单信息(订单类型、订单金额)
├─ 数据格式验证
│ ├─ 手机号格式验证
│ ├─ 身份证号格式验证
│ ├─ 车牌号格式验证
│ └─ 金额格式验证
└─ 业务规则验证
├─ 服务类型可用性验证
├─ 价格计算准确性验证
└─ 车辆信息有效性验证
3. 创建订单
├─ 生成订单号
├─ 设置订单来源为"渠道API下单"
├─ 关联渠道信息
├─ 根据渠道是否已支付设置订单状态
│ ├─ 渠道已支付 → 状态为"已支付"
│ └─ 渠道未支付 → 状态为"待支付"
├─ 保存订单到数据库
├─ 记录订单创建日志
└─ 如果状态为"已支付",触发订单分配流程
4. 返回订单信息
└─ 返回订单号、订单状态等
异常处理:
- API签名验证失败:返回401错误"签名验证失败"
- API Key无效:返回401错误"API Key无效"
- IP不在白名单:返回403错误"IP不在白名单"
- 限流触发:返回429错误"请求频率超限"
- 数据验证失败:返回400错误,包含具体错误信息
- 订单创建失败:返回500错误"订单创建失败",记录错误日志
2.3.3 订单状态流转流程
流程说明: 订单状态流转必须遵循状态机规则,不允许跨状态流转。每次状态变更都需要记录变更日志。
状态流转规则:
待支付 (PENDING_PAYMENT)
├─ 支付成功 → 已支付 (PAID)
└─ 客户取消 → 已取消 (CANCELLED)
已支付 (PAID)
├─ 分配完成 → 待分配 (PENDING_ASSIGN)
├─ 申请退款 → 已退款 (REFUNDED)
└─ 管理员取消 → 已取消 (CANCELLED)
待分配 (PENDING_ASSIGN)
├─ 材料审核通过 → 办理中 (PROCESSING)
└─ 订单取消 → 已取消 (CANCELLED)
办理中 (PROCESSING)
├─ 业务办理完成 → 待确认 (PENDING_CONFIRM)
└─ 订单取消 → 已取消 (CANCELLED)
待确认 (PENDING_CONFIRM)
├─ 客户确认 → 已完成 (COMPLETED)
└─ 订单取消 → 已取消 (CANCELLED)
已完成 (COMPLETED) - 终态
已取消 (CANCELLED) - 终态
已退款 (REFUNDED) - 终态
状态流转实现:
/**
* 更新订单状态
*/
public void updateOrderStatus(Long orderId, String newStatus, Long operatorId, String reason) {
// 1. 查询订单当前状态
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
String currentStatus = order.getStatus();
// 2. 验证状态流转是否合法
if (!orderStateMachine.canTransition(currentStatus, newStatus)) {
throw new BusinessException(
String.format("订单状态不能从 %s 流转到 %s", currentStatus, newStatus));
}
// 3. 使用分布式锁,防止并发修改
String lockKey = "order:status:lock:" + orderId;
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
// 4. 再次查询订单状态(双重检查)
order = orderMapper.selectById(orderId);
if (!currentStatus.equals(order.getStatus())) {
throw new BusinessException("订单状态已被修改,请刷新后重试");
}
// 5. 更新订单状态
order.setStatus(newStatus);
order.setUpdateTime(LocalDateTime.now());
orderMapper.updateById(order);
// 6. 记录状态变更日志
OrderStatusLog statusLog = new OrderStatusLog();
statusLog.setOrderId(orderId);
statusLog.setOldStatus(currentStatus);
statusLog.setNewStatus(newStatus);
statusLog.setOperatorId(operatorId);
statusLog.setReason(reason);
statusLog.setCreateTime(LocalDateTime.now());
orderStatusLogMapper.insert(statusLog);
// 7. 发送状态变更通知
sendStatusChangeNotification(order, currentStatus, newStatus);
// 8. 如果状态为"已支付",触发订单分配流程
if ("PAID".equals(newStatus)) {
triggerOrderAssign(order);
}
// 9. 如果状态为"已完成",触发后续流程
if ("COMPLETED".equals(newStatus)) {
triggerOrderComplete(order);
}
} else {
throw new BusinessException("订单正在处理中,请稍后重试");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("订单状态更新失败");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
2.4 订单分配详细设计
2.4.1 分配规则引擎
/**
* 订单分配规则引擎
*/
public class OrderAssignRuleEngine {
/**
* 代驾人员分配规则
*/
public List<Driver> filterDrivers(Order order, List<Driver> allDrivers) {
return allDrivers.stream()
.filter(driver -> checkDriverStatus(driver)) // 状态检查
.filter(driver -> checkDriverArea(driver, order)) // 区域检查
.filter(driver -> checkDriverWorkload(driver)) // 负载检查
.filter(driver -> checkDriverQualification(driver)) // 资质检查
.filter(driver -> checkDriverRating(driver)) // 评价检查
.collect(Collectors.toList());
}
/**
* 代驾人员排序(优先级排序)
*/
public List<Driver> sortDrivers(List<Driver> drivers, Order order) {
return drivers.stream()
.sorted(Comparator
.comparing((Driver d) -> calculateDistance(d, order)).thenComparing( // 距离最近
Comparator.comparing(Driver::getCurrentOrderCount)).thenComparing( // 负载最轻
Comparator.comparing(Driver::getAverageRating).reversed()).thenComparing( // 评价最高
Comparator.comparing(Driver::getAverageResponseTime))) // 响应最快
.collect(Collectors.toList());
}
/**
* 计算距离(使用高德地图API或数据库存储的距离信息)
*/
private double calculateDistance(Driver driver, Order order) {
// 实现距离计算逻辑
// 可以使用高德地图API计算实际距离
// 或使用数据库存储的距离信息
return 0.0;
}
}
2.4.2 自动分配流程
流程说明:
- 系统根据订单类型、服务地址等条件筛选可用的服务人员
- 按优先级排序,选择优先级最高的服务人员
- 分配订单,发送通知
- 如果分配失败,自动重新分配
详细步骤:
1. 触发订单分配
├─ 订单状态变为"已支付"
└─ 调用分配服务
2. 根据订单类型选择分配策略
├─ 代驾订单
│ ├─ 先分配代驾人员
│ ├─ 再分配接待员
│ └─ 最后分配检测站(如需要)
└─ 自驾订单
├─ 先分配接待员
└─ 再分配检测站(如需要)
3. 代驾人员分配
├─ 筛选符合条件的代驾人员
│ ├─ 状态:在线、非忙碌、非休息
│ ├─ 服务区域:包含订单服务地址
│ ├─ 工作负载:当前订单数 < 最大并发数
│ ├─ 资质:审核通过、证件有效
│ └─ 评价:好评率 >= 80%
├─ 按优先级排序
│ ├─ 第一优先级:距离最近
│ ├─ 第二优先级:工作负载最轻
│ ├─ 第三优先级:评价最高
│ └─ 第四优先级:响应最快
├─ 选择优先级最高的代驾人员
├─ 分配订单(使用分布式锁)
├─ 发送短信通知(使用模板:DRIVER_ORDER_ASSIGN)
└─ 设置超时时间(15分钟)
4. 代驾人员接单处理
├─ 代驾人员接受订单
│ ├─ 更新订单状态为"待分配"(代驾已分配)
│ └─ 更新代驾人员状态为"忙碌"
├─ 代驾人员拒绝订单
│ └─ 重新分配(选择下一个优先级)
└─ 超时未接单
└─ 重新分配(选择下一个优先级)
5. 接待员分配
├─ 筛选符合条件的接待员
│ ├─ 状态:启用、在线
│ ├─ 服务区域:包含订单服务地址
│ ├─ 工作负载:当前订单数 < 最大并发数
│ └─ 服务类型:具备该服务类型办理能力
├─ 按优先级排序
│ ├─ 第一优先级:工作负载最轻
│ ├─ 第二优先级:距离最近
│ └─ 第三优先级:历史完成率最高
├─ 选择优先级最高的接待员
└─ 分配订单
6. 检测站分配(如需要)
├─ 筛选符合条件的检测站
│ ├─ 状态:启用
│ ├─ 服务范围:包含订单服务地址
│ ├─ 服务类型:支持该服务类型
│ └─ 营业时间:当前时间在营业时间内
├─ 按优先级排序
│ ├─ 第一优先级:距离最近
│ ├─ 第二优先级:评价最高
│ └─ 第三优先级:当前订单量最少
└─ 分配订单
异常处理:
- 没有可用的代驾人员:记录日志,转为手动分配或等待代驾人员上线
- 连续3次分配失败:记录异常,转为手动分配,通知管理员
- 分配超时:自动重新分配,记录超时日志
2.5 数据库设计
2.5.1 订单表 (car_order)
CREATE TABLE `car_order` (
`order_id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`order_no` varchar(32) NOT NULL COMMENT '订单号(唯一)',
`customer_id` bigint NOT NULL COMMENT '客户ID',
`vehicle_id` bigint NOT NULL COMMENT '车辆ID',
`service_id` bigint NOT NULL COMMENT '服务类型ID',
`order_type` varchar(20) NOT NULL COMMENT '订单类型(DRIVER-代驾订单, SELF-自驾订单)',
`order_source` varchar(20) NOT NULL COMMENT '订单来源(CUSTOMER-客户直接下单, CHANNEL-渠道API下单, ADMIN-管理员手动创建)',
`channel_id` bigint DEFAULT NULL COMMENT '渠道ID(渠道来源订单必填)',
`status` varchar(20) NOT NULL DEFAULT 'PENDING_PAYMENT' COMMENT '订单状态',
`amount` decimal(10,2) NOT NULL COMMENT '订单金额',
`paid_amount` decimal(10,2) DEFAULT 0.00 COMMENT '实际支付金额',
`service_address` varchar(200) NOT NULL COMMENT '服务地址',
`service_time` datetime NOT NULL COMMENT '服务时间',
`driver_id` bigint DEFAULT NULL COMMENT '代驾人员ID',
`receptionist_id` bigint DEFAULT NULL COMMENT '接待员ID',
`station_id` bigint DEFAULT NULL COMMENT '检测站ID',
`remark` text COMMENT '订单备注',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`order_id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_customer_id` (`customer_id`),
KEY `idx_vehicle_id` (`vehicle_id`),
KEY `idx_service_id` (`service_id`),
KEY `idx_channel_id` (`channel_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`),
KEY `idx_service_time` (`service_time`),
KEY `idx_driver_id` (`driver_id`),
KEY `idx_receptionist_id` (`receptionist_id`),
KEY `idx_station_id` (`station_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表';
2.5.2 订单状态流转日志表 (car_order_status_log)
CREATE TABLE `car_order_status_log` (
`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`order_id` bigint NOT NULL COMMENT '订单ID',
`old_status` varchar(20) NOT NULL COMMENT '原状态',
`new_status` varchar(20) NOT NULL COMMENT '新状态',
`operator_id` bigint DEFAULT NULL COMMENT '操作人ID',
`operator_type` varchar(20) DEFAULT NULL COMMENT '操作人类型(ADMIN-管理员, CUSTOMER-客户, SYSTEM-系统)',
`reason` varchar(500) DEFAULT NULL COMMENT '变更原因',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`log_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单状态流转日志表';
2.5.3 订单材料表 (car_order_material)
CREATE TABLE `car_order_material` (
`material_id` bigint NOT NULL AUTO_INCREMENT COMMENT '材料ID',
`order_id` bigint NOT NULL COMMENT '订单ID',
`material_type` varchar(50) NOT NULL COMMENT '材料类型(ID_CARD-身份证, DRIVING_LICENSE-行驶证, VEHICLE_PHOTO-车辆照片等)',
`material_name` varchar(100) NOT NULL COMMENT '材料名称',
`file_url` varchar(500) NOT NULL COMMENT '文件URL',
`file_size` bigint DEFAULT NULL COMMENT '文件大小(字节)',
`audit_status` varchar(20) DEFAULT 'PENDING' COMMENT '审核状态(PENDING-待审核, PASSED-通过, REJECTED-不通过)',
`audit_comment` varchar(500) DEFAULT NULL COMMENT '审核意见',
`auditor_id` bigint DEFAULT NULL COMMENT '审核人ID',
`audit_time` datetime DEFAULT NULL COMMENT '审核时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`material_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_audit_status` (`audit_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单材料表';
2.6 接口设计
2.6.1 创建订单接口
接口地址:POST /api/v1/order/create
请求参数:
{
"serviceId": 1,
"vehicleId": 1,
"orderType": "DRIVER",
"serviceAddress": "重庆市渝北区xxx",
"serviceTime": "2024-01-15 10:00:00",
"remark": "备注信息"
}
响应参数:
{
"code": 200,
"message": "success",
"data": {
"orderId": 1,
"orderNo": "ORD202401150001",
"amount": 100.00,
"status": "PENDING_PAYMENT",
"createTime": "2024-01-15 09:00:00"
},
"timestamp": 1705280400000
}
错误码:
- 400:请求参数错误
- 401:未登录
- 403:无权限
- 500:服务器内部错误
2.6.2 查询订单列表接口
接口地址:GET /api/v1/order/list
请求参数:
- pageNum:页码(默认1)
- pageSize:每页数量(默认10)
- orderNo:订单号(可选)
- status:订单状态(可选)
- orderType:订单类型(可选)
- startTime:开始时间(可选)
- endTime:结束时间(可选)
响应参数:
{
"code": 200,
"message": "success",
"data": {
"total": 100,
"pageNum": 1,
"pageSize": 10,
"list": [
{
"orderId": 1,
"orderNo": "ORD202401150001",
"customerName": "张三",
"serviceName": "车辆年检",
"status": "PROCESSING",
"amount": 100.00,
"createTime": "2024-01-15 09:00:00"
}
]
},
"timestamp": 1705280400000
}
3. 渠道管理模块详细设计
3.1 模块概述
渠道管理模块负责管理渠道信息、API对接、订单接收、订单统计及结算。渠道通过API接口向系统发送订单,系统接收订单后进行后续处理。
3.2 类设计
3.2.1 渠道实体类 (Channel)
/**
* 渠道实体类
*/
public class Channel {
/**
* 渠道ID
*/
private Long channelId;
/**
* 渠道编码(唯一)
*/
private String channelCode;
/**
* 渠道名称
*/
private String channelName;
/**
* 联系方式
*/
private String contactPhone;
/**
* 地址
*/
private String address;
/**
* 负责人
*/
private String contactPerson;
/**
* 业务类型
*/
private String businessType;
/**
* 渠道状态(ENABLED-启用, DISABLED-停用, PENDING-审核中, BANNED-已禁用)
*/
private String status;
/**
* API Key
*/
private String apiKey;
/**
* API Secret(加密存储)
*/
private String apiSecret;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
3.2.2 API签名验证服务 (ApiSignatureService)
/**
* API签名验证服务
*/
@Service
public class ApiSignatureService {
/**
* 验证API签名
* @param request 请求对象
* @param apiKey API Key
* @param apiSecret API Secret
* @return 是否验证通过
*/
public boolean verifySignature(HttpServletRequest request, String apiKey, String apiSecret) {
// 1. 获取请求参数
String timestamp = request.getHeader("X-Timestamp");
String nonce = request.getHeader("X-Nonce");
String signature = request.getHeader("X-Signature");
// 2. 验证时间戳(防止重放攻击,时间差不超过5分钟)
if (!isValidTimestamp(timestamp)) {
return false;
}
// 3. 验证nonce(防止重复请求,使用Redis存储已使用的nonce)
if (!isValidNonce(nonce)) {
return false;
}
// 4. 构建签名字符串
String signString = buildSignString(request, timestamp, nonce);
// 5. 计算签名
String calculatedSignature = calculateSignature(signString, apiSecret);
// 6. 比较签名
return signature != null && signature.equals(calculatedSignature);
}
/**
* 构建签名字符串
*/
private String buildSignString(HttpServletRequest request, String timestamp, String nonce) {
// 获取请求方法、URI、参数等
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
String body = getRequestBody(request);
// 按字典序排序参数
Map<String, String> params = new TreeMap<>();
params.put("timestamp", timestamp);
params.put("nonce", nonce);
if (queryString != null) {
// 解析query参数
String[] pairs = queryString.split("&");
for (String pair : pairs) {
String[] kv = pair.split("=");
if (kv.length == 2) {
params.put(kv[0], kv[1]);
}
}
}
if (body != null && !body.isEmpty()) {
// 解析body参数(JSON格式)
// ...
}
// 构建签名字符串
StringBuilder sb = new StringBuilder();
sb.append(method).append("\n");
sb.append(uri).append("\n");
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 计算签名(HMAC-SHA256)
*/
private String calculateSignature(String signString, String apiSecret) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKeySpec);
byte[] hash = mac.doFinal(signString.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch (Exception e) {
throw new RuntimeException("签名计算失败", e);
}
}
/**
* 验证时间戳
*/
private boolean isValidTimestamp(String timestamp) {
try {
long ts = Long.parseLong(timestamp);
long currentTime = System.currentTimeMillis() / 1000;
long diff = Math.abs(currentTime - ts);
return diff <= 300; // 5分钟内有效
} catch (Exception e) {
return false;
}
}
/**
* 验证nonce
*/
private boolean isValidNonce(String nonce) {
// 使用Redis存储已使用的nonce,有效期5分钟
String key = "api:nonce:" + nonce;
Boolean exists = redisTemplate.hasKey(key);
if (exists != null && exists) {
return false; // nonce已使用
}
redisTemplate.opsForValue().set(key, "1", 5, TimeUnit.MINUTES);
return true;
}
}
3.2.3 API限流服务 (ApiRateLimitService)
/**
* API限流服务(令牌桶算法)
*/
@Service
public class ApiRateLimitService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 检查是否限流
* @param channelId 渠道ID
* @param limit 限流阈值(每分钟请求数)
* @return 是否允许请求
*/
public boolean checkRateLimit(Long channelId, int limit) {
String key = "api:ratelimit:" + channelId;
// 获取当前令牌数
String tokensStr = (String) redisTemplate.opsForValue().get(key);
int tokens = tokensStr != null ? Integer.parseInt(tokensStr) : limit;
if (tokens > 0) {
// 有令牌,允许请求,令牌数减1
redisTemplate.opsForValue().set(key, String.valueOf(tokens - 1), 60, TimeUnit.SECONDS);
return true;
} else {
// 无令牌,拒绝请求
return false;
}
}
/**
* 添加令牌(定时任务,每秒添加limit/60个令牌)
*/
@Scheduled(fixedRate = 1000)
public void addTokens() {
// 获取所有渠道的限流配置
List<Channel> channels = channelService.getAllChannels();
for (Channel channel : channels) {
if ("ENABLED".equals(channel.getStatus())) {
String key = "api:ratelimit:" + channel.getChannelId();
int limit = channel.getRateLimit(); // 每分钟限流数
int addTokens = limit / 60; // 每秒添加的令牌数
String tokensStr = (String) redisTemplate.opsForValue().get(key);
int tokens = tokensStr != null ? Integer.parseInt(tokensStr) : limit;
int newTokens = Math.min(tokens + addTokens, limit); // 不超过上限
redisTemplate.opsForValue().set(key, String.valueOf(newTokens), 60, TimeUnit.SECONDS);
}
}
}
}
3.3 业务流程设计
3.3.1 渠道API订单创建流程
详细步骤:
1. 接收API请求
├─ 从请求头获取API Key
├─ 根据API Key查询渠道信息
├─ 验证渠道状态(必须是启用状态)
├─ 验证API签名
│ ├─ 获取时间戳、随机数、签名
│ ├─ 验证时间戳有效性(5分钟内)
│ ├─ 验证nonce唯一性(防止重放攻击)
│ ├─ 构建签名字符串
│ ├─ 计算签名(HMAC-SHA256)
│ └─ 比较签名
├─ 验证IP白名单
│ └─ 检查请求IP是否在渠道的IP白名单中
└─ 验证限流
└─ 检查渠道的API调用频率是否超限
2. 验证订单数据
├─ 必填字段验证
│ ├─ 客户信息(姓名、手机号、身份证号)
│ ├─ 车辆信息(车牌号、品牌、型号)
│ ├─ 服务信息(服务类型、服务地址)
│ └─ 订单信息(订单类型、订单金额)
├─ 数据格式验证
│ ├─ 手机号格式:11位数字,1开头
│ ├─ 身份证号格式:18位,最后一位可能是X
│ ├─ 车牌号格式:符合中国车牌号规则
│ └─ 金额格式:大于0的数值
└─ 业务规则验证
├─ 服务类型是否存在且启用
├─ 价格计算是否准确(验证订单金额)
└─ 车辆信息是否有效
3. 创建订单
├─ 生成订单号(格式:ORD + 年月日 + 6位序号)
├─ 设置订单来源为"渠道API下单"
├─ 关联渠道信息
├─ 根据渠道是否已支付设置订单状态
│ ├─ 如果渠道已支付 → 状态为"已支付",触发订单分配
│ └─ 如果渠道未支付 → 状态为"待支付"
├─ 保存订单到数据库
├─ 记录订单创建日志
└─ 记录API调用日志
4. 返回订单信息
└─ 返回订单号、订单状态、创建时间等
异常处理:
- API Key不存在:返回401错误"API Key不存在"
- 渠道状态异常:返回403错误"渠道状态异常"
- 签名验证失败:返回401错误"签名验证失败"
- IP不在白名单:返回403错误"IP不在白名单"
- 限流触发:返回429错误"请求频率超限,请稍后重试"
- 数据验证失败:返回400错误,包含具体错误信息
- 订单创建失败:返回500错误"订单创建失败",记录错误日志
3.3.2 订单状态回调流程
流程说明: 系统订单状态变更时,自动回调渠道接口,通知渠道订单最新状态。
详细步骤:
1. 订单状态变更触发回调
├─ 订单状态变更时,检查订单是否来自渠道
├─ 如果是渠道订单,获取渠道的回调地址
└─ 异步调用回调接口
2. 构建回调数据
├─ 订单号
├─ 订单状态
├─ 状态变更时间
├─ 状态变更原因
└─ 签名(使用渠道的API Secret签名)
3. 发送回调请求
├─ HTTP POST请求
├─ 请求头:Content-Type: application/json
├─ 请求体:JSON格式的回调数据
└─ 设置超时时间(5秒)
4. 处理回调结果
├─ 回调成功(HTTP 200)
│ ├─ 记录回调成功日志
│ └─ 更新回调状态为"成功"
└─ 回调失败(非200或超时)
├─ 记录回调失败日志
├─ 更新回调状态为"失败"
└─ 加入重试队列(最多重试3次,间隔递增)
重试机制:
- 第1次重试:失败后立即重试
- 第2次重试:失败后等待1分钟重试
- 第3次重试:失败后等待5分钟重试
- 3次都失败:记录异常日志,通知管理员
3.4 数据库设计
3.4.1 渠道表 (car_channel)
CREATE TABLE `car_channel` (
`channel_id` bigint NOT NULL AUTO_INCREMENT COMMENT '渠道ID',
`channel_code` varchar(20) NOT NULL COMMENT '渠道编码(唯一)',
`channel_name` varchar(100) NOT NULL COMMENT '渠道名称',
`contact_person` varchar(50) NOT NULL COMMENT '负责人',
`contact_phone` varchar(11) NOT NULL COMMENT '联系电话',
`contact_email` varchar(100) DEFAULT NULL COMMENT '联系邮箱',
`address` varchar(200) DEFAULT NULL COMMENT '地址',
`business_type` varchar(50) DEFAULT NULL COMMENT '业务类型',
`api_key` varchar(64) NOT NULL COMMENT 'API Key',
`api_secret` varchar(128) NOT NULL COMMENT 'API Secret(加密存储)',
`rate_limit` int DEFAULT 100 COMMENT '限流阈值(每分钟请求数)',
`callback_url` varchar(500) DEFAULT NULL COMMENT '回调地址',
`ip_whitelist` text COMMENT 'IP白名单(JSON数组)',
`status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '状态(ENABLED-启用, DISABLED-停用, PENDING-审核中, BANNED-已禁用)',
`remark` text COMMENT '备注',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`channel_id`),
UNIQUE KEY `uk_channel_code` (`channel_code`),
UNIQUE KEY `uk_api_key` (`api_key`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='渠道表';
3.4.2 API调用日志表 (car_api_call_log)
CREATE TABLE `car_api_call_log` (
`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`channel_id` bigint NOT NULL COMMENT '渠道ID',
`api_path` varchar(200) NOT NULL COMMENT '接口路径',
`request_method` varchar(10) NOT NULL COMMENT '请求方法',
`request_params` text COMMENT '请求参数(JSON)',
`response_code` int DEFAULT NULL COMMENT '响应码',
`response_data` text COMMENT '响应数据(JSON)',
`response_time` int DEFAULT NULL COMMENT '响应时间(毫秒)',
`client_ip` varchar(50) DEFAULT NULL COMMENT '客户端IP',
`user_agent` varchar(500) DEFAULT NULL COMMENT 'User Agent',
`status` varchar(20) DEFAULT NULL COMMENT '调用状态(SUCCESS-成功, FAILED-失败)',
`error_message` varchar(500) DEFAULT NULL COMMENT '错误信息',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`log_id`),
KEY `idx_channel_id` (`channel_id`),
KEY `idx_create_time` (`create_time`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='API调用日志表';
3.5 接口设计
3.5.1 渠道订单创建接口
接口地址:POST /api/v1/channel/order/create
认证方式:API Key + Secret签名
请求头:
- X-API-Key: {apiKey}
- X-Timestamp: {timestamp}
- X-Nonce: {nonce}
- X-Signature: {signature}
- Content-Type: application/json
请求参数:
{
"customerName": "张三",
"customerPhone": "13800138000",
"customerIdCard": "500123199001011234",
"plateNumber": "渝A12345",
"brand": "大众",
"model": "朗逸",
"serviceId": 1,
"orderType": "DRIVER",
"serviceAddress": "重庆市渝北区xxx",
"serviceTime": "2024-01-15 10:00:00",
"amount": 100.00,
"paid": true
}
响应参数:
{
"code": 200,
"message": "success",
"data": {
"orderNo": "ORD202401150001",
"orderId": 1,
"status": "PAID",
"createTime": "2024-01-15 09:00:00"
},
"timestamp": 1705280400000
}
错误码:
- 400:请求参数错误
- 401:签名验证失败
- 403:IP不在白名单或渠道状态异常
- 429:请求频率超限
- 500:服务器内部错误
4. 支付管理模块详细设计
4.1 模块概述
支付管理模块负责订单支付、退款处理、支付记录管理等。支持微信支付、支付宝支付等多种支付方式。
4.2 类设计
4.2.1 支付服务类 (PaymentService)
/**
* 支付服务接口
*/
public interface PaymentService {
/**
* 创建支付订单
* @param orderId 订单ID
* @param paymentType 支付方式(WECHAT-微信, ALIPAY-支付宝)
* @return 支付信息(支付URL或支付参数)
*/
PaymentVO createPayment(Long orderId, String paymentType);
/**
* 处理支付回调
* @param paymentType 支付方式
* @param callbackData 回调数据
* @return 处理结果
*/
PaymentCallbackResult handlePaymentCallback(String paymentType, Map<String, String> callbackData);
/**
* 查询支付状态
* @param paymentNo 支付流水号
* @return 支付状态
*/
PaymentStatusVO queryPaymentStatus(String paymentNo);
/**
* 申请退款
* @param orderId 订单ID
* @param refundAmount 退款金额
* @param reason 退款原因
* @return 退款信息
*/
RefundVO applyRefund(Long orderId, BigDecimal refundAmount, String reason);
/**
* 处理退款回调
* @param paymentType 支付方式
* @param callbackData 回调数据
* @return 处理结果
*/
RefundCallbackResult handleRefundCallback(String paymentType, Map<String, String> callbackData);
}
4.2.2 支付流程设计
支付流程:
1. 创建支付订单
├─ 验证订单状态(必须是"待支付")
├─ 验证订单金额
├─ 创建支付记录(状态:待支付)
├─ 调用第三方支付接口
│ ├─ 微信支付:调用统一下单接口
│ └─ 支付宝支付:调用统一收单接口
├─ 获取支付参数(支付URL或支付二维码)
└─ 返回支付信息给客户端
2. 客户端完成支付
├─ 用户完成支付
└─ 第三方支付平台回调系统
3. 处理支付回调
├─ 验证回调签名
├─ 验证订单金额
├─ 更新支付记录状态(已支付)
├─ 更新订单状态(已支付)
├─ 触发订单分配流程
├─ 发送支付成功通知
└─ 返回成功响应给第三方平台
支付超时处理:
- 订单创建后30分钟内未支付,自动取消订单
- 订单创建后25分钟发送支付提醒
- 使用定时任务检查超时订单
4.3 退款流程设计
退款流程:
1. 申请退款
├─ 客户申请退款或管理员发起退款
├─ 验证订单状态(必须是"已支付")
├─ 验证退款金额(不能超过支付金额)
├─ 创建退款记录(状态:待审核)
└─ 通知管理员审核
2. 退款审核
├─ 管理员审核退款申请
├─ 审核通过
│ ├─ 更新退款记录状态(已审核)
│ └─ 调用第三方支付退款接口
└─ 审核拒绝
├─ 更新退款记录状态(已拒绝)
└─ 通知客户审核结果
3. 处理退款回调
├─ 第三方支付平台回调系统
├─ 验证回调签名
├─ 更新退款记录状态(退款成功/失败)
├─ 更新订单状态(已退款)
├─ 发送退款通知
└─ 返回成功响应给第三方平台
5. 订单分配模块详细设计
5.1 分配算法详细设计
5.1.1 代驾人员分配算法
算法流程:
/**
* 代驾人员分配算法
*/
public Driver assignDriver(Order order) {
// 1. 筛选符合条件的代驾人员
List<Driver> candidates = driverService.findAvailableDrivers(order);
if (candidates.isEmpty()) {
throw new BusinessException("没有可用的代驾人员");
}
// 2. 计算每个代驾人员的综合得分
List<DriverScore> scores = new ArrayList<>();
for (Driver driver : candidates) {
double score = calculateDriverScore(driver, order);
scores.add(new DriverScore(driver, score));
}
// 3. 按得分排序(得分越高优先级越高)
scores.sort((a, b) -> Double.compare(b.getScore(), a.getScore()));
// 4. 选择得分最高的代驾人员
Driver selectedDriver = scores.get(0).getDriver();
// 5. 分配订单
assignOrderToDriver(order, selectedDriver);
return selectedDriver;
}
/**
* 计算代驾人员综合得分
*/
private double calculateDriverScore(Driver driver, Order order) {
double score = 0.0;
// 距离得分(距离越近得分越高,权重40%)
double distance = calculateDistance(driver.getLocation(), order.getServiceAddress());
double distanceScore = 100.0 / (1.0 + distance / 10.0); // 距离每增加10km,得分减半
score += distanceScore * 0.4;
// 负载得分(负载越轻得分越高,权重30%)
int currentOrders = driver.getCurrentOrderCount();
int maxOrders = driver.getMaxConcurrentOrders();
double loadScore = 100.0 * (1.0 - (double)currentOrders / maxOrders);
score += loadScore * 0.3;
// 评价得分(评价越高得分越高,权重20%)
double rating = driver.getAverageRating();
double ratingScore = rating * 20.0; // 5星评价 = 100分
score += ratingScore * 0.2;
// 响应速度得分(响应越快得分越高,权重10%)
long avgResponseTime = driver.getAverageResponseTime(); // 秒
double responseScore = 100.0 / (1.0 + avgResponseTime / 60.0); // 响应时间每增加60秒,得分减半
score += responseScore * 0.1;
return score;
}
5.1.2 接待员分配算法
算法流程:
/**
* 接待员分配算法
*/
public Receptionist assignReceptionist(Order order) {
// 1. 筛选符合条件的接待员
List<Receptionist> candidates = receptionistService.findAvailableReceptionists(order);
if (candidates.isEmpty()) {
throw new BusinessException("没有可用的接待员");
}
// 2. 计算每个接待员的综合得分
List<ReceptionistScore> scores = new ArrayList<>();
for (Receptionist receptionist : candidates) {
double score = calculateReceptionistScore(receptionist, order);
scores.add(new ReceptionistScore(receptionist, score));
}
// 3. 按得分排序
scores.sort((a, b) -> Double.compare(b.getScore(), a.getScore()));
// 4. 选择得分最高的接待员
Receptionist selectedReceptionist = scores.get(0).getReceptionist();
// 5. 分配订单
assignOrderToReceptionist(order, selectedReceptionist);
return selectedReceptionist;
}
/**
* 计算接待员综合得分
*/
private double calculateReceptionistScore(Receptionist receptionist, Order order) {
double score = 0.0;
// 负载得分(权重50%)
int currentOrders = receptionist.getCurrentOrderCount();
int maxOrders = receptionist.getMaxConcurrentOrders();
double loadScore = 100.0 * (1.0 - (double)currentOrders / maxOrders);
score += loadScore * 0.5;
// 距离得分(权重30%)
double distance = calculateDistance(receptionist.getLocation(), order.getServiceAddress());
double distanceScore = 100.0 / (1.0 + distance / 10.0);
score += distanceScore * 0.3;
// 完成率得分(权重20%)
double completionRate = receptionist.getCompletionRate();
double completionScore = completionRate * 100.0;
score += completionScore * 0.2;
return score;
}
5.2 分配失败处理
处理流程:
1. 代驾人员15分钟内未接受订单
├─ 系统检测到超时
├─ 释放当前分配
├─ 重新筛选代驾人员(排除已分配的)
├─ 重新分配订单
└─ 记录重新分配日志
2. 代驾人员拒绝订单
├─ 接收拒绝请求
├─ 释放当前分配
├─ 重新筛选代驾人员(排除已拒绝的)
├─ 重新分配订单
└─ 记录拒绝日志
3. 连续3次分配失败
├─ 记录异常日志
├─ 标记订单为"分配异常"
├─ 通知管理员
└─ 转为手动分配或等待代驾人员上线
6. 数据模型详细设计
6.1 核心表结构设计
6.1.1 订单表详细设计
表名:car_order
字段说明:
| 字段名 | 类型 | 长度 | 是否必填 | 说明 |
|---|---|---|---|---|
| order_id | bigint | - | 是 | 订单ID,主键,自增 |
| order_no | varchar | 32 | 是 | 订单号,唯一,格式:ORD+年月日+6位序号 |
| customer_id | bigint | - | 是 | 客户ID,外键关联car_customer |
| vehicle_id | bigint | - | 是 | 车辆ID,外键关联car_vehicle |
| service_id | bigint | - | 是 | 服务类型ID,外键关联car_service |
| order_type | varchar | 20 | 是 | 订单类型:DRIVER-代驾订单,SELF-自驾订单 |
| order_source | varchar | 20 | 是 | 订单来源:CUSTOMER-客户直接下单,CHANNEL-渠道API下单,ADMIN-管理员手动创建 |
| channel_id | bigint | - | 否 | 渠道ID,外键关联car_channel,渠道来源订单必填 |
| status | varchar | 20 | 是 | 订单状态:PENDING_PAYMENT-待支付,PAID-已支付,PENDING_ASSIGN-待分配,PROCESSING-办理中,PENDING_CONFIRM-待确认,COMPLETED-已完成,CANCELLED-已取消,REFUNDED-已退款 |
| amount | decimal | 10,2 | 是 | 订单金额,大于0 |
| paid_amount | decimal | 10,2 | 否 | 实际支付金额,默认0 |
| service_address | varchar | 200 | 是 | 服务地址 |
| service_time | datetime | - | 是 | 服务时间 |
| driver_id | bigint | - | 否 | 代驾人员ID,外键关联car_driver |
| receptionist_id | bigint | - | 否 | 接待员ID,外键关联car_receptionist |
| station_id | bigint | - | 否 | 检测站ID,外键关联car_inspection_station |
| remark | text | - | 否 | 订单备注 |
| create_by | varchar | 64 | 否 | 创建者 |
| create_time | datetime | - | 是 | 创建时间 |
| update_by | varchar | 64 | 否 | 更新者 |
| update_time | datetime | - | 是 | 更新时间 |
索引设计:
- 主键索引:order_id
- 唯一索引:order_no
- 普通索引:customer_id, vehicle_id, service_id, channel_id, status, create_time, service_time, driver_id, receptionist_id, station_id
- 组合索引:(status, create_time) - 用于按状态和时间查询
6.1.2 支付记录表详细设计
表名:car_payment_record
字段说明:
| 字段名 | 类型 | 长度 | 是否必填 | 说明 |
|---|---|---|---|---|
| payment_id | bigint | - | 是 | 支付记录ID,主键,自增 |
| payment_no | varchar | 32 | 是 | 支付流水号,唯一 |
| order_id | bigint | - | 是 | 订单ID,外键关联car_order |
| payment_type | varchar | 20 | 是 | 支付方式:WECHAT-微信支付,ALIPAY-支付宝支付 |
| payment_amount | decimal | 10,2 | 是 | 支付金额 |
| payment_status | varchar | 20 | 是 | 支付状态:PENDING-待支付,PAID-已支付,FAILED-支付失败,REFUNDED-已退款 |
| third_party_order_no | varchar | 64 | 否 | 第三方支付平台订单号 |
| payment_time | datetime | - | 否 | 支付时间 |
| create_time | datetime | - | 是 | 创建时间 |
| update_time | datetime | - | 是 | 更新时间 |
7. 接口详细设计
7.1 内部接口设计
7.1.1 订单创建接口
接口地址:POST /api/v1/order/create
接口描述:客户创建订单
请求头:
- Authorization: Bearer {token}
- Content-Type: application/json
请求参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| serviceId | Long | 是 | 服务类型ID |
| vehicleId | Long | 是 | 车辆ID |
| orderType | String | 是 | 订单类型:DRIVER-代驾订单,SELF-自驾订单 |
| serviceAddress | String | 是 | 服务地址,长度1-200 |
| serviceTime | String | 是 | 服务时间,格式:yyyy-MM-dd HH:mm:ss |
| remark | String | 否 | 备注,长度0-500 |
请求示例:
{
"serviceId": 1,
"vehicleId": 1,
"orderType": "DRIVER",
"serviceAddress": "重庆市渝北区xxx街道xxx号",
"serviceTime": "2024-01-15 10:00:00",
"remark": "请准时到达"
}
响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Integer | 响应码:200-成功,其他-失败 |
| message | String | 响应消息 |
| data | Object | 响应数据 |
| data.orderId | Long | 订单ID |
| data.orderNo | String | 订单号 |
| data.amount | BigDecimal | 订单金额 |
| data.status | String | 订单状态 |
| data.createTime | String | 创建时间 |
响应示例:
{
"code": 200,
"message": "success",
"data": {
"orderId": 1,
"orderNo": "ORD202401150001",
"amount": 100.00,
"status": "PENDING_PAYMENT",
"createTime": "2024-01-15 09:00:00"
},
"timestamp": 1705280400000
}
错误响应示例:
{
"code": 400,
"message": "服务类型不存在",
"data": null,
"timestamp": 1705280400000
}
业务规则:
- 客户必须登录
- 服务类型必须存在且启用
- 车辆必须属于当前客户
- 服务时间必须大于当前时间
- 订单金额必须大于0
异常处理:
- 客户未登录:返回401错误
- 服务类型不存在:返回400错误"服务类型不存在"
- 车辆不存在:返回400错误"车辆不存在"
- 服务时间无效:返回400错误"服务时间必须大于当前时间"
- 价格计算失败:返回500错误"价格计算失败,请稍后重试"
7.2 外部接口设计(渠道API)
7.2.1 渠道订单创建接口
接口地址:POST /api/v1/channel/order/create
接口描述:渠道通过API创建订单
认证方式:API Key + Secret签名
请求头:
- X-API-Key: {apiKey}
- X-Timestamp: {timestamp} - Unix时间戳(秒)
- X-Nonce: {nonce} - 随机字符串,32位
- X-Signature: {signature} - HMAC-SHA256签名
- Content-Type: application/json
签名算法:
- 构建签名字符串:
- 请求方法(POST)
- 请求URI(/api/v1/channel/order/create)
- 请求参数(按字典序排序,key=value格式,用&连接)
- 时间戳(timestamp)
- 随机数(nonce)
- 使用HMAC-SHA256算法,以API Secret为密钥,对签名字符串进行签名
- 对签名结果进行Base64编码
请求参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| customerName | String | 是 | 客户姓名,长度1-50 |
| customerPhone | String | 是 | 客户手机号,11位数字 |
| customerIdCard | String | 否 | 客户身份证号,18位 |
| plateNumber | String | 是 | 车牌号 |
| brand | String | 是 | 品牌 |
| model | String | 是 | 型号 |
| serviceId | Long | 是 | 服务类型ID |
| orderType | String | 是 | 订单类型:DRIVER-代驾订单,SELF-自驾订单 |
| serviceAddress | String | 是 | 服务地址 |
| serviceTime | String | 是 | 服务时间,格式:yyyy-MM-dd HH:mm:ss |
| amount | BigDecimal | 是 | 订单金额,大于0 |
| paid | Boolean | 是 | 是否已支付:true-已支付,false-未支付 |
请求示例:
{
"customerName": "张三",
"customerPhone": "13800138000",
"customerIdCard": "500123199001011234",
"plateNumber": "渝A12345",
"brand": "大众",
"model": "朗逸",
"serviceId": 1,
"orderType": "DRIVER",
"serviceAddress": "重庆市渝北区xxx街道xxx号",
"serviceTime": "2024-01-15 10:00:00",
"amount": 100.00,
"paid": true
}
响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Integer | 响应码:200-成功,其他-失败 |
| message | String | 响应消息 |
| data | Object | 响应数据 |
| data.orderNo | String | 订单号 |
| data.orderId | Long | 订单ID |
| data.status | String | 订单状态 |
| data.createTime | String | 创建时间 |
响应示例:
{
"code": 200,
"message": "success",
"data": {
"orderNo": "ORD202401150001",
"orderId": 1,
"status": "PAID",
"createTime": "2024-01-15 09:00:00"
},
"timestamp": 1705280400000
}
错误码定义:
| 错误码 | 说明 |
|---|---|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 签名验证失败或API Key无效 |
| 403 | IP不在白名单或渠道状态异常 |
| 429 | 请求频率超限 |
| 500 | 服务器内部错误 |
8. 异常处理详细设计
8.1 异常分类
8.1.1 业务异常 (BusinessException)
使用场景:
- 业务规则验证失败
- 数据状态不符合业务要求
- 业务操作失败
异常示例:
- 订单状态不能流转
- 订单金额计算错误
- 代驾人员不可用
- 支付金额不匹配
处理方式:
- 返回明确的错误信息给用户
- 记录业务异常日志
- 不进行重试
8.1.2 系统异常 (SystemException)
使用场景:
- 系统内部错误
- 数据库操作失败
- 第三方服务调用失败
异常示例:
- 数据库连接失败
- Redis连接失败
- 第三方支付接口调用失败
处理方式:
- 记录系统异常日志
- 返回通用错误信息给用户
- 支持重试机制
8.1.3 参数异常 (ParameterException)
使用场景:
- 请求参数格式错误
- 必填参数缺失
- 参数值不符合要求
异常示例:
- 手机号格式错误
- 订单金额为负数
- 服务时间格式错误
处理方式:
- 返回具体的参数错误信息
- 不记录异常日志(参数错误是正常情况)
8.2 异常处理策略
8.2.1 统一异常处理
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException e) {
log.warn("业务异常:{}", e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理参数异常
*/
@ExceptionHandler(ParameterException.class)
public Result handleParameterException(ParameterException e) {
log.warn("参数异常:{}", e.getMessage());
return Result.error(400, e.getMessage());
}
/**
* 处理系统异常
*/
@ExceptionHandler(SystemException.class)
public Result handleSystemException(SystemException e) {
log.error("系统异常:", e);
return Result.error(500, "系统异常,请稍后重试");
}
/**
* 处理其他异常
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
log.error("未知异常:", e);
return Result.error(500, "系统异常,请稍后重试");
}
}
8.2.2 重试机制
使用场景:
- 第三方服务调用失败
- 网络超时
- 临时性错误
重试策略:
- 最大重试次数:3次
- 重试间隔:递增(1秒、3秒、5秒)
- 重试条件:网络超时、5xx错误
- 不重试:4xx错误、业务异常
实现示例:
/**
* 重试机制实现
*/
@Retryable(value = {SystemException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
public void callThirdPartyService() {
// 调用第三方服务
}
9. 性能优化详细设计
9.1 数据库优化
9.1.1 查询优化
优化策略:
使用索引:
- 为常用查询字段创建索引
- 为组合查询创建组合索引
- 定期分析慢查询,优化索引
分页查询:
- 使用LIMIT分页,避免一次性查询大量数据
- 使用游标分页(适用于大数据量)
避免全表扫描:
- 使用索引字段查询
- 避免在WHERE子句中使用函数
优化JOIN查询:
- 使用合适的JOIN类型
- 避免多表JOIN(超过3个表)
9.1.2 写入优化
优化策略:
- 批量插入:使用批量插入减少数据库交互
- 异步写入:非关键数据异步写入
- 分库分表:数据量大时进行分库分表
9.2 缓存优化
9.2.1 缓存策略
缓存数据类型:
热点数据:
- 服务列表
- 地区数据
- 字典数据
- 价格规则
用户数据:
- 登录用户信息
- 用户权限信息
订单数据:
- 订单状态(短期缓存)
- 订单分配规则
代驾人员数据:
- 代驾人员状态
- 代驾人员位置信息
缓存更新策略:
- Cache Aside:先更新数据库,再删除缓存
- Write Through:先更新缓存,再更新数据库
- Write Back:先更新缓存,异步更新数据库
9.2.2 缓存穿透防护
问题:查询不存在的数据,每次都查询数据库
解决方案:
- 布隆过滤器:使用布隆过滤器判断数据是否存在
- 空值缓存:查询不存在的数据时,缓存空值(设置较短的过期时间)
9.3 异步处理
9.3.1 消息队列使用
使用场景:
- 订单分配:异步分配订单,提升响应速度
- 短信发送:异步发送短信,避免阻塞
- 订单状态回调:异步回调渠道,提升性能
- 统计计算:异步计算统计数据
消息队列选型:RabbitMQ
消息格式:
{
"messageId": "msg_123456",
"messageType": "ORDER_ASSIGN",
"timestamp": 1705280400000,
"data": {
"orderId": 1,
"orderNo": "ORD202401150001"
}
}
10. 安全设计详细说明
10.1 数据加密
10.1.1 敏感数据加密
加密字段:
- 身份证号
- 银行卡号
- API Secret
加密算法:AES-256
加密实现:
/**
* 数据加密服务
*/
@Service
public class EncryptionService {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
/**
* 加密
*/
public String encrypt(String data) {
// 实现AES加密
}
/**
* 解密
*/
public String decrypt(String encryptedData) {
// 实现AES解密
}
}
10.2 接口安全
10.2.1 防重放攻击
实现方式:
- 时间戳验证:请求时间戳必须在5分钟内
- Nonce验证:使用Redis存储已使用的nonce,防止重复请求
10.2.2 防SQL注入
实现方式:
- 参数化查询:使用MyBatis的参数化查询
- 输入验证:对用户输入进行验证和过滤
11. 其他核心模块设计
11.1 结算管理模块
11.1.1 结算单生成流程
流程说明:
- 定时任务触发(按结算周期)
- 查询该周期内完成的订单
- 根据结算规则计算结算金额
- 生成结算单
- 通知财务人员审核
详细步骤:
1. 定时任务触发
├─ 每月1日0点触发(月结)
├─ 每周一0点触发(周结)
└─ 每天0点触发(日结)
2. 查询结算订单
├─ 根据结算对象查询订单
├─ 筛选订单状态为"已完成"
├─ 筛选订单时间在结算周期内
└─ 排除已结算的订单
3. 计算结算金额
├─ 根据结算规则计算
│ ├─ 按订单结算:订单数 × 固定金额
│ ├─ 按比例结算:订单金额 × 比例
│ └─ 混合结算:基础金额 + 按比例
├─ 计算总金额
└─ 生成结算明细
4. 生成结算单
├─ 创建结算单记录
├─ 关联结算订单
├─ 设置结算单状态为"待审核"
└─ 通知财务人员
11.2 评价管理模块
11.2.1 评价审核流程
流程说明:
- 客户提交评价
- 系统自动审核(敏感词过滤)
- 管理员审核
- 审核通过后展示评价
详细步骤:
1. 客户提交评价
├─ 验证订单状态(必须是"已完成")
├─ 验证评价时间(订单完成后7天内)
├─ 保存评价记录(状态:待审核)
└─ 触发自动审核
2. 系统自动审核
├─ 敏感词过滤
├─ 违规内容识别
├─ 如果通过 → 状态改为"待人工审核"
└─ 如果不通过 → 状态改为"已拒绝",通知客户
3. 管理员审核
├─ 查看待审核评价
├─ 审核评价内容
├─ 审核通过 → 状态改为"已通过",展示评价
└─ 审核拒绝 → 状态改为"已拒绝",通知客户
12. 附录
12.1 设计模式应用
- 状态机模式:订单状态流转
- 策略模式:支付方式、结算规则
- 工厂模式:支付服务创建
- 观察者模式:订单状态变更通知
- 模板方法模式:订单分配算法
12.2 技术选型说明
- Spring Boot:快速开发框架
- MyBatis Plus:ORM框架,简化数据库操作
- Redis:缓存和分布式锁
- RabbitMQ:消息队列
- Redisson:Redis客户端,支持分布式锁
13. 服务管理模块详细设计
13.1 模块概述
服务管理模块负责配置和管理可办理的服务类型、价格、流程及材料。系统支持多级服务分类、灵活的价格规则配置、服务流程定义等。
13.2 类设计
13.2.1 服务实体类 (Service)
/**
* 服务实体类
*/
public class Service {
/**
* 服务ID
*/
private Long serviceId;
/**
* 服务编码(唯一)
*/
private String serviceCode;
/**
* 服务名称
*/
private String serviceName;
/**
* 服务分类ID
*/
private Long categoryId;
/**
* 服务描述
*/
private String description;
/**
* 基础价格
*/
private BigDecimal basePrice;
/**
* 价格规则类型(FIXED-固定价格, BY_VEHICLE-按车型, BY_REGION-按地区, MIXED-混合规则)
*/
private String priceRuleType;
/**
* 价格规则配置(JSON格式)
*/
private String priceRuleConfig;
/**
* 服务状态(ENABLED-启用, DISABLED-停用)
*/
private String status;
/**
* 办理时长(小时)
*/
private Integer processingHours;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
13.2.2 价格计算服务 (PriceCalculationService)
/**
* 价格计算服务
*/
@Service
public class PriceCalculationService {
/**
* 计算订单价格
* @param serviceId 服务ID
* @param vehicleId 车辆ID
* @param regionId 地区ID
* @return 订单价格
*/
public BigDecimal calculatePrice(Long serviceId, Long vehicleId, Long regionId) {
// 1. 获取服务信息
Service service = serviceService.getById(serviceId);
if (service == null || !"ENABLED".equals(service.getStatus())) {
throw new BusinessException("服务不存在或已停用");
}
// 2. 获取基础价格
BigDecimal basePrice = service.getBasePrice();
// 3. 根据价格规则类型计算价格
String priceRuleType = service.getPriceRuleType();
BigDecimal finalPrice = basePrice;
switch (priceRuleType) {
case "FIXED":
// 固定价格
finalPrice = basePrice;
break;
case "BY_VEHICLE":
// 按车型计算
finalPrice = calculateByVehicle(basePrice, vehicleId, service.getPriceRuleConfig());
break;
case "BY_REGION":
// 按地区计算
finalPrice = calculateByRegion(basePrice, regionId, service.getPriceRuleConfig());
break;
case "MIXED":
// 混合规则
finalPrice = calculateByMixed(basePrice, vehicleId, regionId, service.getPriceRuleConfig());
break;
}
// 4. 应用优惠活动
finalPrice = applyDiscounts(finalPrice, serviceId);
return finalPrice;
}
/**
* 按车型计算价格
*/
private BigDecimal calculateByVehicle(BigDecimal basePrice, Long vehicleId, String config) {
// 解析配置:{"brand": "大众", "multiplier": 1.2}
// 根据车辆品牌、型号等计算价格
Vehicle vehicle = vehicleService.getById(vehicleId);
// 实现价格计算逻辑
return basePrice;
}
/**
* 按地区计算价格
*/
private BigDecimal calculateByRegion(BigDecimal basePrice, Long regionId, String config) {
// 解析配置:{"regionId": 1, "multiplier": 1.1}
// 根据地区计算价格
return basePrice;
}
/**
* 混合规则计算价格
*/
private BigDecimal calculateByMixed(BigDecimal basePrice, Long vehicleId, Long regionId, String config) {
// 综合车型和地区计算价格
return basePrice;
}
/**
* 应用优惠活动
*/
private BigDecimal applyDiscounts(BigDecimal price, Long serviceId) {
// 查询该服务的优惠活动
// 应用优惠折扣
return price;
}
}
13.3 业务流程设计
13.3.1 服务价格计算流程
流程说明:
- 获取服务基础价格
- 根据价格规则类型计算价格
- 应用优惠活动
- 返回最终价格
详细步骤:
1. 获取服务信息
├─ 根据服务ID查询服务
├─ 验证服务是否存在
└─ 验证服务是否启用
2. 获取基础价格
└─ 从服务信息中获取基础价格
3. 根据价格规则计算
├─ 固定价格:直接使用基础价格
├─ 按车型:根据车辆品牌、型号等计算
├─ 按地区:根据服务地区计算
└─ 混合规则:综合车型和地区计算
4. 应用优惠活动
├─ 查询该服务的优惠活动
├─ 检查优惠活动是否有效
├─ 应用优惠折扣
└─ 计算最终价格
5. 返回价格
└─ 返回计算后的价格(保留2位小数)
13.4 数据库设计
13.4.1 服务分类表 (car_service_category)
CREATE TABLE `car_service_category` (
`category_id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类ID',
`category_code` varchar(20) NOT NULL COMMENT '分类编码(唯一)',
`category_name` varchar(100) NOT NULL COMMENT '分类名称',
`parent_id` bigint DEFAULT 0 COMMENT '父分类ID,0表示顶级分类',
`level` int DEFAULT 1 COMMENT '分类层级',
`sort_order` int DEFAULT 0 COMMENT '排序顺序',
`icon` varchar(200) DEFAULT NULL COMMENT '图标URL',
`description` varchar(500) DEFAULT NULL COMMENT '分类描述',
`status` varchar(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态(ENABLED-启用, DISABLED-停用)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`category_id`),
UNIQUE KEY `uk_category_code` (`category_code`),
KEY `idx_parent_id` (`parent_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='服务分类表';
13.4.2 服务表 (car_service)
CREATE TABLE `car_service` (
`service_id` bigint NOT NULL AUTO_INCREMENT COMMENT '服务ID',
`service_code` varchar(20) NOT NULL COMMENT '服务编码(唯一)',
`service_name` varchar(100) NOT NULL COMMENT '服务名称',
`category_id` bigint NOT NULL COMMENT '服务分类ID',
`description` text COMMENT '服务描述',
`base_price` decimal(10,2) NOT NULL COMMENT '基础价格',
`price_rule_type` varchar(20) NOT NULL DEFAULT 'FIXED' COMMENT '价格规则类型',
`price_rule_config` text COMMENT '价格规则配置(JSON)',
`processing_hours` int DEFAULT NULL COMMENT '办理时长(小时)',
`status` varchar(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态(ENABLED-启用, DISABLED-停用)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`service_id`),
UNIQUE KEY `uk_service_code` (`service_code`),
KEY `idx_category_id` (`category_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='服务表';
14. 客户管理模块详细设计
14.1 模块概述
客户管理模块负责客户信息管理、客户车辆关联、客户订单查询等功能。
14.2 类设计
14.2.1 客户实体类 (Customer)
/**
* 客户实体类
*/
public class Customer {
/**
* 客户ID
*/
private Long customerId;
/**
* 客户姓名
*/
private String customerName;
/**
* 手机号(唯一)
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 身份证号(加密存储)
*/
private String idCard;
/**
* 地址
*/
private String address;
/**
* 性别
*/
private String gender;
/**
* 生日
*/
private LocalDate birthday;
/**
* 客户状态(ACTIVE-正常, DISABLED-禁用, BLACKLIST-黑名单)
*/
private String status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
14.3 业务流程设计
14.3.1 客户注册流程
流程说明:
- 客户输入手机号和验证码
- 系统验证验证码
- 创建客户账号
- 返回注册结果
详细步骤:
1. 接收注册请求
├─ 验证手机号格式
├─ 验证手机号是否已注册
└─ 验证验证码
2. 创建客户账号
├─ 生成客户ID
├─ 保存客户基本信息
├─ 设置默认状态为"正常"
└─ 记录注册日志
3. 返回注册结果
└─ 返回客户ID、注册时间等
15. 车辆管理模块详细设计
15.1 模块概述
车辆管理模块负责车辆信息管理、车辆证件管理、车辆办理记录等功能。
15.2 类设计
15.2.1 车辆实体类 (Vehicle)
/**
* 车辆实体类
*/
public class Vehicle {
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 客户ID
*/
private Long customerId;
/**
* 车牌号(唯一)
*/
private String plateNumber;
/**
* 品牌
*/
private String brand;
/**
* 型号
*/
private String model;
/**
* 颜色
*/
private String color;
/**
* VIN码(唯一)
*/
private String vinCode;
/**
* 发动机号
*/
private String engineNumber;
/**
* 车架号
*/
private String frameNumber;
/**
* 车辆状态(ACTIVE-正常, DELETED-已删除)
*/
private String status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
15.2.2 OCR识别服务 (OcrService)
/**
* OCR识别服务
*/
@Service
public class OcrService {
/**
* 识别身份证信息
* @param imageUrl 图片URL
* @return 身份证信息
*/
public IdCardInfo recognizeIdCard(String imageUrl) {
// 调用第三方OCR服务(阿里云/腾讯云)
// 解析识别结果
// 返回身份证信息
}
/**
* 识别行驶证信息
* @param imageUrl 图片URL
* @return 行驶证信息
*/
public DrivingLicenseInfo recognizeDrivingLicense(String imageUrl) {
// 调用第三方OCR服务
// 解析识别结果
// 返回行驶证信息
}
}
15.3 业务流程设计
15.3.1 车辆添加流程(带OCR识别)
流程说明:
- 客户上传行驶证照片
- 系统调用OCR服务识别行驶证信息
- 客户确认或修改识别结果
- 保存车辆信息
详细步骤:
1. 上传行驶证照片
├─ 验证图片格式(JPG、PNG)
├─ 验证图片大小(不超过5MB)
├─ 上传图片到OSS
└─ 返回图片URL
2. OCR识别
├─ 调用OCR服务识别行驶证
├─ 解析识别结果
│ ├─ 车牌号
│ ├─ 品牌型号
│ ├─ 车辆识别代号(VIN码)
│ ├─ 发动机号
│ └─ 注册日期
└─ 返回识别结果
3. 客户确认信息
├─ 展示识别结果
├─ 客户确认或修改
└─ 验证信息完整性
4. 保存车辆信息
├─ 验证车牌号是否已存在
├─ 验证VIN码是否已存在
├─ 保存车辆信息
├─ 关联客户信息
└─ 返回车辆ID
16. 代驾管理模块详细设计
16.1 模块概述
代驾管理模块负责代驾人员管理、代驾业务管理、代驾评价管理等功能。
16.2 类设计
16.2.1 代驾人员实体类 (Driver)
/**
* 代驾人员实体类
*/
public class Driver {
/**
* 代驾人员ID
*/
private Long driverId;
/**
* 姓名
*/
private String name;
/**
* 手机号(唯一)
*/
private String phone;
/**
* 身份证号(加密存储)
*/
private String idCard;
/**
* 驾驶证号
*/
private String drivingLicenseNo;
/**
* 驾驶证有效期
*/
private LocalDate drivingLicenseExpiry;
/**
* 车辆信息(JSON格式)
*/
private String vehicleInfo;
/**
* 服务区域(JSON数组,存储地区ID)
*/
private String serviceAreas;
/**
* 工作状态(ONLINE-在线, OFFLINE-离线, BUSY-忙碌, REST-休息, PENDING-审核中, DISABLED-已禁用)
*/
private String workStatus;
/**
* 审核状态(PENDING-待审核, PASSED-已通过, REJECTED-已拒绝)
*/
private String auditStatus;
/**
* 当前订单数
*/
private Integer currentOrderCount;
/**
* 最大并发订单数
*/
private Integer maxConcurrentOrders;
/**
* 平均评分
*/
private BigDecimal averageRating;
/**
* 好评率
*/
private BigDecimal goodRatingRate;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
16.2.2 代驾人员状态管理服务 (DriverStatusService)
/**
* 代驾人员状态管理服务
*/
@Service
public class DriverStatusService {
/**
* 更新代驾人员工作状态
* @param driverId 代驾人员ID
* @param newStatus 新状态
*/
public void updateWorkStatus(Long driverId, String newStatus) {
// 1. 验证状态流转是否合法
Driver driver = driverService.getById(driverId);
String currentStatus = driver.getWorkStatus();
if (!canTransitionStatus(currentStatus, newStatus)) {
throw new BusinessException("状态不能从 " + currentStatus + " 流转到 " + newStatus);
}
// 2. 更新状态
driver.setWorkStatus(newStatus);
driverService.updateById(driver);
// 3. 更新Redis缓存
String key = "driver:status:" + driverId;
redisTemplate.opsForValue().set(key, newStatus, 30, TimeUnit.MINUTES);
// 4. 如果状态为"在线",加入可用代驾人员列表
if ("ONLINE".equals(newStatus)) {
addToAvailableDrivers(driverId);
} else {
removeFromAvailableDrivers(driverId);
}
}
/**
* 根据订单状态自动更新代驾人员状态
*/
public void autoUpdateStatusByOrder(Long driverId, String orderStatus) {
if ("PROCESSING".equals(orderStatus) || "PENDING_ASSIGN".equals(orderStatus)) {
// 订单处理中,代驾人员状态为"忙碌"
updateWorkStatus(driverId, "BUSY");
} else if ("COMPLETED".equals(orderStatus) || "CANCELLED".equals(orderStatus)) {
// 订单完成或取消,检查是否还有其他订单
int currentOrders = driverService.getCurrentOrderCount(driverId);
if (currentOrders == 0) {
updateWorkStatus(driverId, "ONLINE");
}
}
}
}
16.3 业务流程设计
16.3.1 代驾人员注册与审核流程
流程说明:
- 代驾人员注册账号
- 上传资质材料(身份证、驾驶证、行驶证、保险等)
- 系统审核资质
- 审核通过后可以接单
详细步骤:
1. 代驾人员注册
├─ 手机号注册
├─ 设置密码
├─ 填写基本信息
└─ 创建账号(状态:待审核)
2. 上传资质材料
├─ 上传身份证(OCR识别)
├─ 上传驾驶证(OCR识别)
├─ 上传行驶证(OCR识别)
├─ 上传车辆照片
└─ 上传保险凭证
3. 系统审核
├─ 管理员审核资质材料
├─ 验证身份证、驾驶证、行驶证真实性
├─ 验证证件有效期
└─ 审核结果
├─ 审核通过 → 状态改为"已通过",可以接单
└─ 审核不通过 → 状态改为"已拒绝",通知代驾人员补充材料
17. 权限管理模块详细设计
17.1 模块概述
权限管理模块基于RBAC模型,实现用户、角色、权限的管理,支持菜单权限、按钮权限、数据权限。
17.2 类设计
17.2.1 权限验证服务 (PermissionService)
/**
* 权限验证服务
*/
@Service
public class PermissionService {
/**
* 验证用户是否有权限访问资源
* @param userId 用户ID
* @param resource 资源标识(如:order:create)
* @return 是否有权限
*/
public boolean hasPermission(Long userId, String resource) {
// 1. 获取用户角色
List<Role> roles = userRoleService.getRolesByUserId(userId);
// 2. 获取角色权限
Set<String> permissions = new HashSet<>();
for (Role role : roles) {
List<Permission> rolePermissions = rolePermissionService.getPermissionsByRoleId(role.getRoleId());
for (Permission permission : rolePermissions) {
permissions.add(permission.getPermissionCode());
}
}
// 3. 检查权限
return permissions.contains(resource);
}
/**
* 验证用户是否有权限访问菜单
* @param userId 用户ID
* @param menuId 菜单ID
* @return 是否有权限
*/
public boolean hasMenuPermission(Long userId, Long menuId) {
// 实现菜单权限验证逻辑
return true;
}
/**
* 获取用户的数据权限范围
* @param userId 用户ID
* @return 数据权限范围(部门ID列表等)
*/
public List<Long> getDataPermissionScope(Long userId) {
// 实现数据权限范围获取逻辑
return Collections.emptyList();
}
}
17.3 权限拦截器设计
/**
* 权限拦截器
*/
@Component
public class PermissionInterceptor implements HandlerInterceptor {
@Autowired
private PermissionService permissionService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 1. 获取请求路径和方法
String path = request.getRequestURI();
String method = request.getMethod();
// 2. 构建资源标识(如:order:create)
String resource = buildResource(path, method);
// 3. 获取当前用户ID
Long userId = getCurrentUserId(request);
// 4. 验证权限
if (!permissionService.hasPermission(userId, resource)) {
// 返回403错误
response.setStatus(403);
return false;
}
return true;
}
}
18. 通知管理模块详细设计
18.1 模块概述
通知管理模块负责系统通知和公告的管理,支持多种通知方式和推送渠道。
18.2 类设计
18.2.1 通知服务 (NotificationService)
/**
* 通知服务
*/
@Service
public class NotificationService {
/**
* 发送通知
* @param notification 通知对象
*/
public void sendNotification(Notification notification) {
// 1. 确定推送对象
List<Long> targetUserIds = determineTargetUsers(notification);
// 2. 根据推送方式发送通知
List<String> pushMethods = notification.getPushMethods();
for (String method : pushMethods) {
switch (method) {
case "STATION_MESSAGE":
// 站内信
sendStationMessage(notification, targetUserIds);
break;
case "SMS":
// 短信
sendSms(notification, targetUserIds);
break;
case "EMAIL":
// 邮件
sendEmail(notification, targetUserIds);
break;
case "APP_PUSH":
// APP推送
sendAppPush(notification, targetUserIds);
break;
}
}
}
/**
* 发送订单状态变更通知
*/
public void sendOrderStatusChangeNotification(Order order, String oldStatus, String newStatus) {
// 1. 构建通知内容
Notification notification = new Notification();
notification.setType("ORDER_STATUS_CHANGE");
notification.setTitle("订单状态变更");
notification.setContent(buildOrderStatusChangeContent(order, oldStatus, newStatus));
// 2. 确定推送对象(客户、代驾人员、接待员等)
List<Long> targetUserIds = new ArrayList<>();
targetUserIds.add(order.getCustomerId());
if (order.getDriverId() != null) {
targetUserIds.add(order.getDriverId());
}
if (order.getReceptionistId() != null) {
targetUserIds.add(order.getReceptionistId());
}
// 3. 发送通知
notification.setTargetUserIds(targetUserIds);
sendNotification(notification);
}
}
19. 短信管理模块详细设计
19.1 模块概述
短信管理模块负责短信模板管理、短信发送、第三方审核等功能。
19.2 类设计
19.2.1 短信模板实体类 (SmsTemplate)
/**
* 短信模板实体类
*/
public class SmsTemplate {
/**
* 模板ID
*/
private Long templateId;
/**
* 模板编码(唯一)
*/
private String templateCode;
/**
* 模板名称
*/
private String templateName;
/**
* 模板内容
*/
private String templateContent;
/**
* 模板类型
*/
private String templateType;
/**
* 使用场景
*/
private String usageScenario;
/**
* 模板状态(DRAFT-草稿, PENDING-待审核, APPROVED-已审核, REJECTED-已驳回, ENABLED-已启用, DISABLED-已停用)
*/
private String status;
/**
* 第三方审核状态(PENDING-待审核, APPROVING-审核中, APPROVED-已通过, REJECTED-已驳回)
*/
private String thirdPartyStatus;
/**
* 第三方模板ID(审核通过后返回)
*/
private String thirdPartyTemplateId;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
// getter/setter方法
}
19.2.2 短信发送服务 (SmsService)
/**
* 短信发送服务
*/
@Service
public class SmsService {
/**
* 发送短信
* @param templateCode 模板编码
* @param phone 手机号
* @param params 模板参数
*/
public void sendSms(String templateCode, String phone, Map<String, String> params) {
// 1. 获取短信模板
SmsTemplate template = smsTemplateService.getByCode(templateCode);
if (template == null || !"ENABLED".equals(template.getStatus())) {
throw new BusinessException("短信模板不存在或未启用");
}
// 2. 替换模板变量
String content = replaceTemplateVariables(template.getTemplateContent(), params);
// 3. 调用第三方短信服务
SmsResult result = callThirdPartySmsService(phone, content, template.getThirdPartyTemplateId());
// 4. 记录发送日志
SmsSendLog log = new SmsSendLog();
log.setTemplateCode(templateCode);
log.setPhone(phone);
log.setContent(content);
log.setStatus(result.isSuccess() ? "SUCCESS" : "FAILED");
log.setErrorMessage(result.getErrorMessage());
log.setCreateTime(LocalDateTime.now());
smsSendLogMapper.insert(log);
// 5. 如果发送失败,加入重试队列
if (!result.isSuccess()) {
addToRetryQueue(templateCode, phone, params);
}
}
/**
* 替换模板变量
*/
private String replaceTemplateVariables(String template, Map<String, String> params) {
String result = template;
for (Map.Entry<String, String> entry : params.entrySet()) {
result = result.replace("{" + entry.getKey() + "}", entry.getValue());
}
return result;
}
}
19.3 业务流程设计
19.3.1 短信模板审核流程
流程说明:
- 管理员创建短信模板
- 提交第三方审核
- 第三方审核通过后,模板可以启用
- 启用后可以用于发送短信
详细步骤:
1. 创建短信模板
├─ 填写模板信息(名称、编码、内容等)
├─ 设置模板变量(如:{客户姓名}、{订单号})
├─ 保存模板(状态:草稿)
└─ 预览模板效果
2. 提交第三方审核
├─ 调用第三方短信服务审核接口
├─ 提交模板内容
├─ 更新模板状态为"审核中"
└─ 等待第三方审核结果
3. 处理审核结果
├─ 第三方回调审核结果
├─ 审核通过
│ ├─ 更新模板状态为"已审核"
│ ├─ 保存第三方模板ID
│ └─ 可以启用模板
└─ 审核不通过
├─ 更新模板状态为"已驳回"
├─ 记录驳回原因
└─ 允许修改后重新提交
4. 启用模板
├─ 管理员启用模板
├─ 更新模板状态为"已启用"
└─ 可以用于发送短信
20. 数据一致性设计
20.1 事务管理
20.1.1 订单创建事务
/**
* 订单创建事务
*/
@Transactional(rollbackFor = Exception.class)
public OrderVO createOrder(OrderCreateDTO dto) {
// 1. 创建订单
Order order = new Order();
// ... 设置订单信息
orderMapper.insert(order);
// 2. 创建订单状态日志
OrderStatusLog statusLog = new OrderStatusLog();
statusLog.setOrderId(order.getOrderId());
statusLog.setOldStatus(null);
statusLog.setNewStatus("PENDING_PAYMENT");
orderStatusLogMapper.insert(statusLog);
// 3. 如果创建失败,事务回滚
return convertToVO(order);
}
20.1.2 支付回调事务
/**
* 支付回调事务
*/
@Transactional(rollbackFor = Exception.class)
public void handlePaymentCallback(String paymentType, Map<String, String> callbackData) {
// 1. 验证回调签名
if (!verifyCallbackSignature(paymentType, callbackData)) {
throw new BusinessException("回调签名验证失败");
}
// 2. 查询支付记录
String paymentNo = callbackData.get("paymentNo");
PaymentRecord payment = paymentRecordMapper.selectByPaymentNo(paymentNo);
// 3. 幂等性检查(防止重复处理)
if ("PAID".equals(payment.getPaymentStatus())) {
return; // 已处理,直接返回
}
// 4. 更新支付记录状态
payment.setPaymentStatus("PAID");
payment.setPaymentTime(LocalDateTime.now());
paymentRecordMapper.updateById(payment);
// 5. 更新订单状态
Order order = orderMapper.selectById(payment.getOrderId());
orderStateMachine.updateOrderStatus(order.getOrderId(), "PAID", null, "支付成功");
// 6. 如果任何一步失败,事务回滚
}
20.2 分布式锁设计
20.2.1 订单创建分布式锁
/**
* 订单创建(使用分布式锁防止重复创建)
*/
public OrderVO createOrderWithLock(OrderCreateDTO dto) {
// 使用客户ID+服务ID+时间戳作为锁的key
String lockKey = "order:create:lock:" + dto.getCustomerId() + ":" + dto.getServiceId();
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取锁,最多等待5秒,锁定10秒
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
// 再次检查是否已创建订单(防止重复提交)
Order existingOrder = checkExistingOrder(dto);
if (existingOrder != null) {
return convertToVO(existingOrder);
}
// 创建订单
return createOrder(dto);
} else {
throw new BusinessException("订单正在创建中,请稍后重试");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("订单创建失败");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
21. 监控与日志设计
21.1 日志设计
21.1.1 日志级别
- DEBUG:调试信息,开发环境使用
- INFO:一般信息,记录重要操作
- WARN:警告信息,记录异常但不影响系统运行
- ERROR:错误信息,记录系统错误
21.1.2 日志格式
[时间] [级别] [线程] [类名] [方法名] - [消息] [异常堆栈]
示例:
2024-01-15 09:00:00.123 [INFO] [http-nio-8080-exec-1] [OrderService] [createOrder] - 创建订单成功,订单号:ORD202401150001
2024-01-15 09:00:01.456 [ERROR] [http-nio-8080-exec-2] [PaymentService] [handlePaymentCallback] - 支付回调处理失败:签名验证失败
21.2 监控指标
21.2.1 业务监控指标
- 订单创建量(按小时、按天统计)
- 订单完成率
- 支付成功率
- 订单分配成功率
- 渠道API调用量
- 渠道API成功率
21.2.2 系统监控指标
- 接口响应时间
- 接口调用次数
- 接口错误率
- 数据库连接数
- Redis连接数
- 系统资源使用率(CPU、内存、磁盘)
22. 测试设计
22.1 单元测试设计
22.1.1 订单服务单元测试
/**
* 订单服务单元测试
*/
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
void testCreateOrder() {
// 1. 准备测试数据
OrderCreateDTO dto = new OrderCreateDTO();
dto.setServiceId(1L);
dto.setVehicleId(1L);
dto.setOrderType("DRIVER");
// ...
// 2. 执行测试
OrderVO result = orderService.createOrder(dto);
// 3. 验证结果
assertNotNull(result);
assertEquals("PENDING_PAYMENT", result.getStatus());
// ...
}
@Test
void testCreateOrderWithInvalidService() {
// 测试服务类型不存在的情况
OrderCreateDTO dto = new OrderCreateDTO();
dto.setServiceId(999L); // 不存在的服务ID
assertThrows(BusinessException.class, () -> {
orderService.createOrder(dto);
});
}
}
22.2 集成测试设计
22.2.1 订单创建集成测试
测试场景:
- 正常创建订单流程
- 订单创建后自动触发支付流程
- 支付成功后自动触发订单分配流程
测试步骤:
1. 创建测试客户和车辆
2. 调用订单创建接口
3. 验证订单创建成功
4. 调用支付接口
5. 验证支付成功
6. 验证订单状态变为"已支付"
7. 验证订单分配流程触发
23. 部署设计
23.1 部署架构
23.1.1 应用部署
- 应用服务器:多实例部署,使用Nginx负载均衡
- 数据库:主从复制,读写分离
- Redis:主从+哨兵模式
- 消息队列:RabbitMQ集群
23.1.2 配置文件管理
- 开发环境配置:application-dev.yml
- 测试环境配置:application-test.yml
- 生产环境配置:application-prod.yml
配置内容:
- 数据库连接配置
- Redis连接配置
- 第三方服务配置(支付、短信、OCR等)
- 日志配置
- 安全配置
23.2 数据迁移设计
23.2.1 数据库迁移
- 使用Flyway或Liquibase管理数据库版本
- 每次发布包含数据库迁移脚本
- 迁移脚本必须可重复执行(幂等性)
24. 附录
24.1 设计模式总结
| 设计模式 | 应用场景 | 说明 |
|---|---|---|
| 状态机模式 | 订单状态流转 | 管理订单状态的合法流转 |
| 策略模式 | 支付方式、结算规则 | 不同支付方式和结算规则的可替换实现 |
| 工厂模式 | 支付服务创建 | 根据支付类型创建对应的支付服务 |
| 观察者模式 | 订单状态变更通知 | 订单状态变更时通知相关角色 |
| 模板方法模式 | 订单分配算法 | 定义分配算法的骨架,具体实现由子类完成 |
| 责任链模式 | 权限验证 | 多个权限验证器组成责任链 |
| 单例模式 | 配置管理 | 系统配置单例管理 |
24.2 技术难点与解决方案
24.2.1 订单状态一致性
难点:多系统、多角色操作订单,保证状态一致性
解决方案:
- 使用状态机模式,严格控制状态流转
- 使用分布式锁,防止并发修改
- 使用事务,保证数据库操作原子性
24.2.2 订单分配性能
难点:大量订单需要快速分配,分配算法复杂
解决方案:
- 使用Redis缓存代驾人员状态和位置信息
- 使用消息队列异步分配
- 优化分配算法,减少计算时间
24.2.3 渠道API高并发
难点:多个渠道同时调用API,需要保证性能和安全性
解决方案:
- 使用限流控制,防止恶意调用
- 使用签名验证,保证接口安全
- 使用异步处理,提升响应速度
备注: 本详细设计说明书基于需求文档和概要设计文档编写,详细描述了系统的功能模块设计、类设计、接口设计、数据库设计、业务流程设计等,为系统开发提供详细的技术指导。后续可根据实际情况进行调整和优化。
