学习到支付服务

This commit is contained in:
2026-03-03 20:10:22 +08:00
parent b0c2889ddf
commit 5d04837d46
6 changed files with 138 additions and 49 deletions

View File

@@ -32,59 +32,72 @@ public class OrderController {
}
@PostMapping("/create")
public Result<Object> createOrder(@RequestBody Map<String, Object> jsonMap){
public Result<Object> createOrder(@RequestBody Map<String, Object> jsonMap) {
// ========== 1. 校验businessId原有逻辑保留 ==========
Object businessIdObj = jsonMap.get("businessId");
String businessId = null;
if (businessIdObj != null) {
businessId = businessIdObj.toString().trim(); // 转字符串并去除首尾空格
businessId = businessIdObj.toString().trim();
}
// 2. 判断businessId是否存在核心逻辑
if (businessId == null || businessId.isEmpty()) {
// businessId不存在/为空 → 返回参数错误
return Result.fail(400, "businessId不能为空");
}
try {
// 1. 安全获取userId先转Number再取int值兼容Integer/Long
// ========== 2. 安全解析参数(原有逻辑保留,仅优化提示) ==========
// 2.1 解析userId
Number userIdNum = (Number) jsonMap.get("userId");
if (userIdNum == null) {
return Result.fail(400, "userId不能为空");
}
int userId = userIdNum.intValue();
// 2. 安全获取stockid先转Number再取long值兼容Integer/Long
// 2.2 解析stockid
Number stockIdNum = (Number) jsonMap.get("stockid");
if (stockIdNum == null) {
return Result.fail(400, "stockid不能为空");
}
Long stockId = stockIdNum.longValue();
// 3. 安全获取deductnum先转Number再取int值兼容Integer/Long
// 2.3 解析deductnum
Number deductNumNum = (Number) jsonMap.get("deductnum");
if (deductNumNum == null) {
return Result.fail(400, "deductnum不能为空");
}
int deductNum = deductNumNum.intValue();
// 4. 组装DTO
// ========== 3. 组装DTO ==========
StockDeductDTO stockDeductDTO = new StockDeductDTO();
stockDeductDTO.setId(stockId);
stockDeductDTO.setDeductNum(deductNum);
// 5. 调用服务
int serviceResult = orderService.createOrder(stockDeductDTO, userId,businessId);
if (serviceResult != 0) {
// 库存扣减/订单创建失败
return Result.fail("库存扣减失败,订单创建失败");
// ========== 4. 调用Service核心改动接收Map结果获取订单号 ==========
// 注意原Service返回int需改为返回Map包含success、msg、orderNo
Map<String, Object> serviceResult = orderService.createOrder(stockDeductDTO, userId, businessId);
// 4.1 解析Service返回结果
boolean isSuccess = (boolean) serviceResult.get("success");
if (!isSuccess) {
return Result.fail(500, serviceResult.get("msg").toString());
}
// 成功可以返回额外数据比如订单ID
return Result.success("库存扣减成功,订单创建成功");
} catch (Exception e) {
// 捕获所有异常,返回友好提示
// 4.2 成功:返回订单号(供前端轮询支付链接)
String orderNo = serviceResult.get("orderNo").toString();
return Result.success(
Map.of(
"msg", "订单创建成功,已触发支付流程",
"orderNo", orderNo, // 核心:返回订单号
"tips", "请轮询 /order/detail?orderNo=" + orderNo + " 获取支付链接"
)
);
} catch (ClassCastException e) {
// 补充类型转换异常处理(前端传参类型错误)
e.printStackTrace();
return Result.fail("订单创建异常:" + e.getMessage());
return Result.fail(400, "参数类型错误userId/stockid/deductnum需为数字");
} catch (Exception e) {
e.printStackTrace();
return Result.fail(500, "订单创建异常:" + e.getMessage());
}
}
}

View File

@@ -2,6 +2,7 @@ package cn.mayiming.Service;
import cn.mayiming.Mapper.OrderMapper;
import cn.mayiming.entity.Order;
import cn.mayiming.entity.PayTriggerMsgDTO;
import cn.mayiming.entity.StockDeductDTO;
import cn.mayiming.entity.User;
import cn.mayiming.feign.StockFeignClient;
@@ -13,7 +14,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class OrderService {
@@ -29,6 +32,8 @@ public class OrderService {
@Resource
private RocketMQTemplate rocketMQTemplate;
private static final String PAY_TRIGGER_TOPIC = "pay_topic";
public User SearchUserbyname (User user) {
return userFeignClient.selectByUsername(user);
}
@@ -59,54 +64,75 @@ public class OrderService {
}
@Transactional(rollbackFor = Exception.class)
public int createOrder(StockDeductDTO stockDeductDTO,int userId,String businessId) {
public Map<String, Object> createOrder(StockDeductDTO stockDeductDTO, int userId, String businessId) {
Map<String, Object> result = new HashMap<>();
// ========== 原有核心逻辑(幂等+用户+扣库存+插订单)==========
Integer count = orderMapper.countByBusinessId(businessId);
if (count != null && count > 0) {
return 1; // 已存在,直接返回失败
result.put("success", false);
result.put("msg", "重复请求,下单失败");
return result;
}
// 1.2 校验用户是否存在(远程只读,无事务管控)
if (userFeignClient.GetUserByid(userId) == null) {
return 1; // 用户不存在,直接返回失败
result.put("success", false);
result.put("msg", "用户不存在,下单失败");
return result;
}
Order order = null; // 声明订单对象,方便后续发送消息
try {
// 步骤1调用库存服务扣减库存远程操作需库存服务自身保证事务
int stockResult = stockFeignClient.deductstock(stockDeductDTO);
if (stockResult == 0) { // 假设返回0是失败非0是成功按你原有逻辑
throw new RuntimeException("库存扣减失败,返回码:" + stockResult);
if (stockResult == 0) {
throw new RuntimeException("库存不足,扣减失败");
}
// 步骤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-待支付
// 构建并插入订单
order = new Order();
order.setOrderNo(orderMapper.generateOrderNo());
order.setUserId(Long.valueOf(userId));
order.setGoodsId(stockDeductDTO.getId());
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); // 直接标记为成功
// 插入幂等记录
Integer idempotentInsertResult = orderMapper.insertIdempotentRecord(businessId, 1);
if (idempotentInsertResult == null || idempotentInsertResult == 0) {
throw new RuntimeException("幂等记录插入失败");
}
// 所有操作成功返回0
return 0;
// ========== 核心新增发送RocketMQ支付触发消息 ==========
PayTriggerMsgDTO payMsg = new PayTriggerMsgDTO();
payMsg.setOrderNo(order.getOrderNo());
payMsg.setUserId(order.getUserId());
payMsg.setPayAmount(order.getOrderAmount());
payMsg.setBusinessId(businessId);
payMsg.setGoodsId(order.getGoodsId());
// 发送异步消息(本地模拟用同步发送,确保消息投递成功)
try {
rocketMQTemplate.convertAndSend(PAY_TRIGGER_TOPIC, payMsg);
System.out.println("订单" + order.getOrderNo() + "支付触发消息已发送到RocketMQ");
} catch (Exception e) {
throw new RuntimeException("发送支付触发消息失败", e);
}
// 返回结果(包含订单号,前端后续用订单号调用模拟支付接口)
result.put("success", true);
result.put("msg", "订单创建成功,已触发支付流程");
result.put("orderNo", order.getOrderNo());
} catch (Exception e) {
// 任何异常触发事务回滚:订单插入、幂等插入都会回滚
e.printStackTrace();
// 可选:幂等记录标记为失败(方便排查)
orderMapper.updateIdempotentStatus(businessId, 2);
return 1;
result.put("success", false);
result.put("msg", "下单失败:" + e.getMessage());
}
return result;
}
}

View File

@@ -0,0 +1,14 @@
package cn.mayiming.entity;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class PayTriggerMsgDTO {
private String orderNo; // 订单号(核心关联)
private Long userId; // 用户ID
private BigDecimal payAmount; // 支付金额
private String businessId; // 幂等ID透传
private Long goodsId; // 商品ID用于关单恢复库存
}