467 lines
17 KiB
Java
467 lines
17 KiB
Java
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);
|
||
}
|
||
}
|