diff --git a/order-service/src/main/java/cn/mayiming/Controller/OrderController.java b/order-service/src/main/java/cn/mayiming/Controller/OrderController.java index 97d5b1f..eeb477f 100644 --- a/order-service/src/main/java/cn/mayiming/Controller/OrderController.java +++ b/order-service/src/main/java/cn/mayiming/Controller/OrderController.java @@ -33,6 +33,17 @@ public class OrderController { @PostMapping("/create") public Result createOrder(@RequestBody Map jsonMap){ + Object businessIdObj = jsonMap.get("businessId"); + String businessId = null; + if (businessIdObj != null) { + businessId = businessIdObj.toString().trim(); // 转字符串并去除首尾空格 + } + + // 2. 判断businessId是否存在(核心逻辑) + if (businessId == null || businessId.isEmpty()) { + // businessId不存在/为空 → 返回参数错误 + return Result.fail(400, "businessId不能为空"); + } try { // 1. 安全获取userId:先转Number,再取int值(兼容Integer/Long) Number userIdNum = (Number) jsonMap.get("userId"); @@ -61,7 +72,7 @@ public class OrderController { stockDeductDTO.setDeductNum(deductNum); // 5. 调用服务 - int serviceResult = orderService.createOrder(stockDeductDTO, userId); + int serviceResult = orderService.createOrder(stockDeductDTO, userId,businessId); if (serviceResult != 0) { // 库存扣减/订单创建失败 return Result.fail("库存扣减失败,订单创建失败"); diff --git a/order-service/src/main/java/cn/mayiming/Mapper/OrderMapper.java b/order-service/src/main/java/cn/mayiming/Mapper/OrderMapper.java index 6b4c741..a0601fa 100644 --- a/order-service/src/main/java/cn/mayiming/Mapper/OrderMapper.java +++ b/order-service/src/main/java/cn/mayiming/Mapper/OrderMapper.java @@ -3,7 +3,9 @@ package cn.mayiming.Mapper; import cn.mayiming.entity.Order; import feign.Param; +import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; import java.util.List; @@ -30,4 +32,50 @@ public interface OrderMapper { "WHERE user_id = #{userId} " + "ORDER BY create_time DESC") List selectByUserId(@Param("userId") Integer userId); + + /** + * 判断businessId是否存在于订单表中 + * @param businessId 业务唯一标识(如幂等请求ID/外部订单号等) + * @return 存在返回true,不存在返回false + */ + @Select("SELECT COUNT(1) FROM idempotent_record WHERE business_id = #{businessId}") + Integer countByBusinessId(@Param("businessId") String businessId); + + /** + * 插入幂等记录(核心:利用唯一索引uk_request_id防重复) + * @param businessId 业务唯一标识(UUID) + * @param status 处理状态:0-处理中 1-处理成功 2-处理失败 + * @return 插入成功返回1,失败返回0(唯一索引冲突时) + */ + @Insert("INSERT INTO idempotent_record (business_id, status) " + + "VALUES (#{businessId}, #{status}) " + + "ON DUPLICATE KEY UPDATE update_time = CURRENT_TIMESTAMP") + Integer insertIdempotentRecord(@Param("businessId") String businessId, + @Param("status") Integer status); + + /** + * 更新幂等记录状态(业务成功/失败后调用) + * @param businessId 业务唯一标识(UUID) + * @param status 处理状态:1-处理成功 2-处理失败 + * @return 更新成功返回1,无匹配记录返回0 + */ + @Update("UPDATE idempotent_record SET status = #{status}, update_time = CURRENT_TIMESTAMP " + + "WHERE business_id = #{businessId}") + Integer updateIdempotentStatus(@Param("businessId") String businessId, + @Param("status") Integer status); + + /** + * 插入订单记录(核心业务操作) + * @param order 订单对象 + * @return 插入成功返回1,失败返回0 + */ + @Insert("INSERT INTO t_order (order_no, user_id, goods_id, order_amount, order_status) " + + "VALUES (#{orderNo}, #{userId}, #{goodsId}, #{orderAmount}, #{orderStatus})") + Integer insertOrder(Order order); + + // 可选:生成唯一订单号(也可在代码中生成) + default String generateOrderNo() { + // 简单生成规则:时间戳 + 6位随机数 + return System.currentTimeMillis() + "" + (int)(Math.random() * 900000 + 100000); + } } diff --git a/order-service/src/main/java/cn/mayiming/Service/OrderService.java b/order-service/src/main/java/cn/mayiming/Service/OrderService.java index 99bfcdc..dbb8e79 100644 --- a/order-service/src/main/java/cn/mayiming/Service/OrderService.java +++ b/order-service/src/main/java/cn/mayiming/Service/OrderService.java @@ -10,7 +10,9 @@ import jakarta.annotation.Resource; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.util.List; @Service @@ -56,15 +58,55 @@ public class OrderService { } } - public int createOrder(StockDeductDTO stockDeductDTO,int i) { - int res = 0; - if(stockFeignClient.deductstock(stockDeductDTO)==0){ - res ++; + @Transactional(rollbackFor = Exception.class) + public int createOrder(StockDeductDTO stockDeductDTO,int userId,String businessId) { + Integer count = orderMapper.countByBusinessId(businessId); + if (count != null && count > 0) { + return 1; // 已存在,直接返回失败 } - if(userFeignClient.GetUserByid(i)==null){ - res ++; + // 1.2 校验用户是否存在(远程只读,无事务管控) + if (userFeignClient.GetUserByid(userId) == null) { + return 1; // 用户不存在,直接返回失败 } - return res; + + try { + // 步骤1:调用库存服务扣减库存(远程操作,需库存服务自身保证事务) + int stockResult = stockFeignClient.deductstock(stockDeductDTO); + if (stockResult == 0) { // 假设返回0是失败,非0是成功(按你原有逻辑) + throw new RuntimeException("库存扣减失败,返回码:" + stockResult); + } + + // 步骤2:构建订单对象(适配t_order表) + Order order = new Order(); + order.setOrderNo(orderMapper.generateOrderNo()); // 生成唯一订单号 + order.setUserId(Long.valueOf(userId)); // 转换为Long适配表结构 + order.setGoodsId(stockDeductDTO.getId()); // 商品ID(从库存DTO获取) + order.setOrderAmount(new BigDecimal("99.99")); // 订单金额(实际需从商品服务获取) + order.setOrderStatus(0); // 0-待支付 + + // 步骤3:插入订单记录(核心!事务管控) + Integer orderInsertResult = orderMapper.insertOrder(order); + if (orderInsertResult == null || orderInsertResult == 0) { + throw new RuntimeException("订单插入失败"); + } + + // 步骤4:插入幂等记录(占坑,事务管控) + Integer idempotentInsertResult = orderMapper.insertIdempotentRecord(businessId, 1); // 直接标记为成功 + if (idempotentInsertResult == null || idempotentInsertResult == 0) { + throw new RuntimeException("幂等记录插入失败"); + } + + // 所有操作成功,返回0 + return 0; + + } catch (Exception e) { + // 任何异常触发事务回滚:订单插入、幂等插入都会回滚 + e.printStackTrace(); + // 可选:幂等记录标记为失败(方便排查) + orderMapper.updateIdempotentStatus(businessId, 2); + return 1; + } + } } diff --git a/order-service/src/main/java/cn/mayiming/entity/Order.java b/order-service/src/main/java/cn/mayiming/entity/Order.java index c484096..7ab065b 100644 --- a/order-service/src/main/java/cn/mayiming/entity/Order.java +++ b/order-service/src/main/java/cn/mayiming/entity/Order.java @@ -5,50 +5,13 @@ import lombok.Data; import java.math.BigDecimal; import java.util.Date; -@Data // Lombok 注解,自动生成get/set/toString +@Data public class Order { - /** 订单ID */ - private Long id; - /** 订单编号 */ - private String orderNo; - /** 关联用户ID */ - private Integer userId; - /** 商品名称 */ - private String productName; - /** 商品单价 */ - private BigDecimal productPrice; - /** 购买数量 */ - private Integer count; - /** 订单总金额 */ - private BigDecimal totalAmount; - /** 订单状态:0-待支付 1-已支付 2-已取消 3-已完成 */ - private Integer status; - /** 创建时间 */ - private Date createTime; - /** 更新时间 */ - private Date updateTime; - - // 可选:添加状态枚举,避免硬编码数字 - public enum OrderStatus { - PENDING_PAYMENT(0, "待支付"), - PAID(1, "已支付"), - CANCELLED(2, "已取消"), - COMPLETED(3, "已完成"); - - private final int code; - private final String desc; - - OrderStatus(int code, String desc) { - this.code = code; - this.desc = desc; - } - - public int getCode() { - return code; - } - - public String getDesc() { - return desc; - } - } + private Long id; // 主键ID + private String orderNo; // 订单号(唯一) + private Long userId; // 用户ID + private Long goodsId; // 商品ID(对应库存扣减的stockId) + private BigDecimal orderAmount; // 订单金额 + private Integer orderStatus; // 订单状态:0-待支付 1-已支付 2-已取消 + private Date createTime; // 创建时间 } \ No newline at end of file