Files
todo-backend/src/main/java/com/example/building/service/impl/OrderServiceImpl.java
Agent 96ee3da7d0
All checks were successful
continuous-integration/drone/push Build is passing
feat: 订单状态0未完成+编辑+确认完成功能
2026-03-25 16:00:44 +00:00

467 lines
17 KiB
Java
Raw 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.
package com.example.building.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.building.dto.CreateOrderRequest;
import com.example.building.entity.*;
import com.example.building.mapper.*;
import com.example.building.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* 订单服务实现类
* 核心业务逻辑:
* 1. 订单原价(total_amount) = Σ(item.price × item.quantity)
* 2. 优惠金额(discount_amount) = total_amount × (100 - discount_rate) / 100
* 3. 实付金额(actual_amount) = total_amount - discount_amount
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private ProductMapper productMapper;
@Autowired
private StockMapper stockMapper;
@Autowired
private StockFlowMapper stockFlowMapper;
@Autowired
private CustomerMapper customerMapper;
/**
* 创建订单
* 核心:价格计算、库存扣减、客户累计消费更新
*/
@Override
@Transactional
public Order createOrder(CreateOrderRequest request, String operatorId, String operatorName) {
// 1. 准备订单数据
Order order = new Order();
order.setOrderId(UUID.randomUUID().toString());
order.setOrderNo(generateOrderNo());
order.setOperatorId(operatorId);
order.setOperatorName(operatorName);
order.setDiscountRate(request.getDiscountRate() != null ? request.getDiscountRate() : new BigDecimal("100"));
order.setRemark(request.getRemark());
order.setPaymentMethod(request.getPaymentMethod());
order.setStatus(0); // 未完成
// 2. 查询客户信息(如果指定了客户)
if (request.getCustomerId() != null) {
Customer customer = customerMapper.selectById(request.getCustomerId());
if (customer != null) {
order.setCustomerId(customer.getCustomerId());
order.setCustomerName(customer.getName());
order.setCustomerPhone(customer.getPhone());
order.setCustomerWechat(customer.getWechatOpenid());
}
}
// 3. 计算订单金额
BigDecimal totalAmount = BigDecimal.ZERO; // 原价
List<OrderItem> orderItems = new ArrayList<>();
for (CreateOrderRequest.OrderItemDTO itemDTO : request.getItems()) {
// 查询商品信息
Product product = productMapper.selectById(itemDTO.getProductId());
if (product == null) {
throw new RuntimeException("商品不存在: " + itemDTO.getProductId());
}
// 使用用户指定价格或商品标价
BigDecimal price = itemDTO.getPrice() != null ? itemDTO.getPrice() : product.getPrice();
// 计算小计
BigDecimal subtotal = price.multiply(new BigDecimal(itemDTO.getQuantity()))
.setScale(2, RoundingMode.HALF_UP);
// 累加原价
totalAmount = totalAmount.add(subtotal);
// 构建订单明细
OrderItem item = new OrderItem();
item.setItemId(UUID.randomUUID().toString());
item.setOrderId(order.getOrderId());
item.setProductId(product.getProductId());
item.setProductName(product.getName());
item.setProductSpec(product.getSpec());
item.setUnit(product.getUnit());
item.setPrice(price);
item.setQuantity(itemDTO.getQuantity());
item.setSubtotal(subtotal);
orderItems.add(item);
// 4. 暂时不扣减库存,等确认完成时再扣减
// decreaseStock(product.getProductId(), itemDTO.getQuantity(), order.getOrderId(), operatorId);
}
// 设置订单原价
order.setTotalAmount(totalAmount);
// 5. 计算优惠金额和实付金额
BigDecimal discountRate = order.getDiscountRate();
BigDecimal discountAmount = totalAmount.multiply(new BigDecimal("100").subtract(discountRate))
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
BigDecimal actualAmount = totalAmount.subtract(discountAmount);
order.setDiscountAmount(discountAmount);
order.setActualAmount(actualAmount);
// 6. 保存订单
orderMapper.insert(order);
// 7. 保存订单明细
for (OrderItem item : orderItems) {
orderItemMapper.insert(item);
}
// 8. 更新客户累计消费金额
if (request.getCustomerId() != null) {
Customer customer = customerMapper.selectById(request.getCustomerId());
if (customer != null) {
BigDecimal newTotal = customer.getTotalAmount().add(actualAmount);
customer.setTotalAmount(newTotal);
customerMapper.updateById(customer);
}
}
return order;
}
/**
* 获取订单列表
*/
@Override
public Page<Order> getOrders(String customerId, Integer status, String startDate, String endDate, Integer page, Integer pageSize) {
Page<Order> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
if (customerId != null) {
wrapper.eq(Order::getCustomerId, customerId);
}
if (status != null) {
wrapper.eq(Order::getStatus, status);
}
if (startDate != null) {
wrapper.ge(Order::getCreatedAt, startDate);
}
if (endDate != null) {
wrapper.le(Order::getCreatedAt, endDate);
}
wrapper.orderByDesc(Order::getCreatedAt);
return orderMapper.selectPage(pageParam, wrapper);
}
/**
* 获取订单详情(含明细)
* 顾客只能查看自己的订单
*/
@Override
public Map<String, Object> getOrderDetail(String orderId, String userId, String role) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 顾客只能查看自己的订单
if ("customer".equals(role) && !userId.equals(order.getCustomerId())) {
throw new RuntimeException("无权查看该订单");
}
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
Map<String, Object> result = new HashMap<>();
result.put("order", order);
result.put("items", items);
return result;
}
/**
* 取消订单
*/
@Override
@Transactional
public void cancelOrder(String orderId, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() != 0) {
throw new RuntimeException("订单状态不允许取消");
}
// 未完成订单没有扣减库存,不需要恢复
// 更新订单状态
order.setStatus(2); // 已取消
orderMapper.updateById(order);
}
/**
* 退款
*/
@Override
@Transactional
public void refundOrder(String orderId, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() != 1) {
throw new RuntimeException("订单状态不允许退款");
}
// 恢复库存
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
for (OrderItem item : items) {
increaseStock(item.getProductId(), item.getQuantity(), orderId, operatorId);
}
// 更新订单状态
order.setStatus(4); // 已退款
orderMapper.updateById(order);
}
/**
* 更新订单状态(确认完成/取消)
*/
@Override
@Transactional
public void updateOrderStatus(String orderId, Integer status, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 只有未完成状态可以操作
if (order.getStatus() != 0) {
throw new RuntimeException("订单状态不允许操作");
}
if (status == 1) {
// 确认完成:扣减库存
List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderId, orderId));
for (OrderItem item : items) {
decreaseStock(item.getProductId(), item.getQuantity(), orderId, operatorId);
}
}
// 取消不需要恢复库存(因为创建时没扣减)
order.setStatus(status);
orderMapper.updateById(order);
}
/**
* 更新订单(编辑)
*/
@Override
@Transactional
public Order updateOrder(String orderId, CreateOrderRequest request, String operatorId) {
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 只有未完成状态可以编辑
if (order.getStatus() != 0) {
throw new RuntimeException("订单状态不允许编辑");
}
// 更新客户信息
if (request.getCustomerId() != null) {
Customer customer = customerMapper.selectById(request.getCustomerId());
if (customer != null) {
order.setCustomerId(customer.getCustomerId());
order.setCustomerName(customer.getName());
order.setCustomerPhone(customer.getPhone());
order.setCustomerWechat(customer.getWechatOpenid());
}
} else {
order.setCustomerId(null);
order.setCustomerName(null);
order.setCustomerPhone(null);
order.setCustomerWechat(null);
}
// 更新折扣和备注
order.setDiscountRate(request.getDiscountRate() != null ? request.getDiscountRate() : new BigDecimal("100"));
order.setRemark(request.getRemark());
order.setPaymentMethod(request.getPaymentMethod());
// 重新计算金额
BigDecimal totalAmount = BigDecimal.ZERO;
// 删除旧的订单明细
orderItemMapper.delete(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderId));
// 重新创建订单明细
List<OrderItem> orderItems = new ArrayList<>();
for (CreateOrderRequest.OrderItemDTO itemDTO : request.getItems()) {
Product product = productMapper.selectById(itemDTO.getProductId());
if (product == null) {
throw new RuntimeException("商品不存在: " + itemDTO.getProductId());
}
BigDecimal price = itemDTO.getPrice() != null ? itemDTO.getPrice() : product.getPrice();
BigDecimal subtotal = price.multiply(new BigDecimal(itemDTO.getQuantity()))
.setScale(2, RoundingMode.HALF_UP);
totalAmount = totalAmount.add(subtotal);
OrderItem item = new OrderItem();
item.setItemId(UUID.randomUUID().toString());
item.setOrderId(orderId);
item.setProductId(product.getProductId());
item.setProductName(product.getName());
item.setProductSpec(product.getSpec());
item.setUnit(product.getUnit());
item.setPrice(price);
item.setQuantity(itemDTO.getQuantity());
item.setSubtotal(subtotal);
orderItems.add(item);
orderItemMapper.insert(item);
}
order.setTotalAmount(totalAmount);
BigDecimal discountRate = order.getDiscountRate();
BigDecimal discountAmount = totalAmount.multiply(new BigDecimal("100").subtract(discountRate))
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
BigDecimal actualAmount = totalAmount.subtract(discountAmount);
order.setDiscountAmount(discountAmount);
order.setActualAmount(actualAmount);
orderMapper.updateById(order);
return order;
}
/**
* 订单统计
*/
@Override
public Map<String, Object> getStatistics(String startDate, String endDate) {
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Order::getStatus, 1);
if (startDate != null && !startDate.isEmpty()) {
LocalDateTime start = LocalDate.parse(startDate, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
wrapper.ge(Order::getCreatedAt, start);
}
if (endDate != null && !endDate.isEmpty()) {
LocalDateTime end = LocalDate.parse(endDate, DateTimeFormatter.ISO_LOCAL_DATE).atTime(23, 59, 59);
wrapper.le(Order::getCreatedAt, end);
}
List<Order> orders = orderMapper.selectList(wrapper);
BigDecimal totalAmount = BigDecimal.ZERO; // 原价合计
BigDecimal actualAmount = BigDecimal.ZERO; // 实付合计
for (Order order : orders) {
totalAmount = totalAmount.add(order.getTotalAmount());
actualAmount = actualAmount.add(order.getActualAmount());
}
Map<String, Object> result = new HashMap<>();
result.put("orderCount", orders.size());
result.put("totalAmount", totalAmount);
result.put("actualAmount", actualAmount);
result.put("discountAmount", totalAmount.subtract(actualAmount));
return result;
}
/**
* 生成订单编号
* 规则: ORD + 年月日 + 6位序号
*/
private String generateOrderNo() {
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String key = "order:no:" + date;
// 这里简化处理实际应使用Redis自增
int seq = (int) (Math.random() * 1000000);
return "ORD" + date + String.format("%06d", seq);
}
/**
* 扣减库存
*/
private void decreaseStock(String productId, Integer quantity, String relatedId, String operatorId) {
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
if (stock == null) {
throw new RuntimeException("库存记录不存在");
}
if (stock.getQuantity() < quantity) {
throw new RuntimeException("库存不足");
}
int beforeQty = stock.getQuantity();
stock.setQuantity(beforeQty - quantity);
stockMapper.updateById(stock);
// 记录库存流水
saveStockFlow(productId, 2, -quantity, beforeQty, beforeQty - quantity, relatedId, "sale", operatorId);
}
/**
* 增加库存
*/
private void increaseStock(String productId, Integer quantity, String relatedId, String operatorId) {
Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>()
.eq(Stock::getProductId, productId));
if (stock == null) {
stock = new Stock();
stock.setStockId(UUID.randomUUID().toString());
stock.setProductId(productId);
stock.setQuantity(0);
stock.setLockedQuantity(0);
stockMapper.insert(stock);
}
int beforeQty = stock.getQuantity();
stock.setQuantity(beforeQty + quantity);
stockMapper.updateById(stock);
// 记录库存流水
saveStockFlow(productId, 1, quantity, beforeQty, beforeQty + quantity, relatedId, "cancel", operatorId);
}
/**
* 保存库存流水
*/
private void saveStockFlow(String productId, Integer type, Integer quantity,
Integer beforeQty, Integer afterQty, String relatedId,
String relatedType, String operatorId) {
StockFlow flow = new StockFlow();
flow.setFlowId(UUID.randomUUID().toString());
flow.setProductId(productId);
flow.setType(type);
flow.setQuantity(quantity);
flow.setBeforeQuantity(beforeQty);
flow.setAfterQuantity(afterQty);
flow.setRelatedId(relatedId);
flow.setRelatedType(relatedType);
flow.setOperatorId(operatorId);
stockFlowMapper.insert(flow);
}
}