乐观锁解决库存超卖问题
public BaseResult creatOneOrder(FlightOrderServiceImpl orderService, List<Map<String, String>> passengers,Map<String, String> selectFlightMap,String account) throws Exception {
//如果是单程
//判断座位数是否>=1
//是的话就直接减库存
//不是就return 201,库存不够
int k=0;
FlightSeat seat = seatService.getById(selectFlightMap.get("seatId"));
if(seat.getRestNum()<1){
return new BaseResult(201,"库存不够了");
}
seat.setRestNum(seat.getRestNum()-1);
boolean updateResult = seatService.updateById(seat);
if(!updateResult){
//如果更新失败
//说明在更新前有其他人已经对该行剩余量进行了修改
//就再试一次,先判断库存
int maxRetries = 5; //最大重试次数
int retryCount = 0;
while (retryCount < maxRetries) {
// 获取最新的座位信息
FlightSeat updatedSeat = seatService.getById(selectFlightMap.get("seatId"));
// 检查座位是否仍然可用
if (updatedSeat.getRestNum() < 1) {
return new BaseResult(201, "库存不够了");
}
// 尝试更新座位信息
updatedSeat.setRestNum(updatedSeat.getRestNum() - 1);
boolean retryUpdateResult = seatService.updateById(updatedSeat);
// 检查更新操作的结果
if (retryUpdateResult) {
// 成功更新,生成订单,跳出循环
k=1;
break;
} else {
// 增加重试计数并进行下一次尝试
retryCount++;
}
if (retryCount == maxRetries) {
return new BaseResult(202, "更新失败,重试次数已达上限");
}
}
}
else {
k =1;
}
if(k==1){//k=1说明库存成功减少了
FlightOrder Order = new FlightOrder();
// 获取当前时间
LocalDateTime currentTime = LocalDateTime.now();
// 如果你想以特定格式输出当前时间,可以使用 DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedCurrentTime = currentTime.format(formatter);
Order.setDate(formattedCurrentTime);
// 设置超时时间为当前时间的15分钟后
LocalDateTime timeoutTime = currentTime.plusMinutes(15);
String formattedTimeoutTime = timeoutTime.format(formatter);
Order.setTimeout(formattedTimeoutTime);
Order.setOrderId(String.valueOf(new SnowUtil(0,0).getId()));
// redisTemplate.opsForValue().set(Order.getOrderId(), Order.getOrderId(), 15, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(Order.getOrderId(), Order.getOrderId(), 15, TimeUnit.MINUTES);
Flight flight = new Flight();
flight = flightService.getById(selectFlightMap.get("flightId"));
Order.setFlightCode(flight.getFlightCode());
Order.setStage("待支付");
Order.setWay("单程");
Order.setUserId(account);
Air air = airService.getById(flight.getAirId());
Order.setPrice(seat.getPrice());
Order.setAllPrice(seat.getPrice());
Order.setSeatType(seat.getSeatType());
Order.setDepCity(flight.getDepCity());
Order.setDepDate(flight.getDepDate());
Order.setDepTime(flight.getDepTime());
Order.setArrCity(flight.getArrCity());
Order.setArrDate(flight.getArrDate());
Order.setArrTime(flight.getArrTime());
Order.setDepAirport(flight.getDepAirport());
Order.setArrAirport(flight.getArrAirport());
Order.setAirlineName(flight.getAirlineName());
Order.setAirType(air.getType());
orderService.save(Order);
for (Map<String, String> pas : passengers) {
FlightPassenger passenger = new FlightPassenger();
passenger.setOrderId(Order.getOrderId());
passenger.setName(pas.get("name"));
passenger.setIdentityId(pas.get("sfz"));
passenger.setPhone(pas.get("phone"));
passengerService.save(passenger);
}
return new BaseResult(200,"支付成功",Order.getOrderId());
}
else return new BaseResult(202,"库存不够或者重试次数达到上限");
}
往返票
@Transactional
public BaseResult creatTwoOrder(FlightOrderServiceImpl orderService,List<Map<String, String>> passengers,Map<String, String> selectFlightMap,String account) throws Exception {
// 座位
int k1=0,k2=0;
FlightSeat seat = seatService.getById(selectFlightMap.get("goSeatId"));
FlightSeat seatback = seatService.getById(selectFlightMap.get("backSeatId"));
//先判断库存
if(seat.getRestNum()<1||seatback.getRestNum()<1){
return new BaseResult(201,"库存不够了");
}
//如果库存都够
seat.setRestNum(seat.getRestNum()-1);
boolean updateResult = seatService.updateById(seat);
if(!updateResult){//如果更新失败
int max =5;
int cnt=0;
while(cnt<max){
//先判断库存
FlightSeat newSeat = seatService.getById(selectFlightMap.get("goSeatId"));
if(newSeat.getRestNum()<1){
return new BaseResult(201,"库存不够了");
}
//如果库存足够
newSeat.setRestNum(newSeat.getRestNum()-1);
boolean updateSeatResult = seatService.updateById(newSeat);
if(updateSeatResult){
//成功
k1 =1;
break;
}
else {
//修改库存又失败了,有其他线程已经将这行记录的值进行了修改
cnt++;
}
if(cnt==max){
return new BaseResult(202,"重试次数已经达到上限");
}
}
}
else {
//一次成功
k1 =1;
}
//到这里的话就说明去程的票减库存已经成功了
//开始买返程的票
seatback.setRestNum(seatback.getRestNum()-1);
boolean backSeatResult = seatService.updateById(seatback);
if(!backSeatResult){
//如果更新失败
int max1 =5;
int cnt1 =0;
while (cnt1<max1){
FlightSeat newSeatback = seatService.getById(selectFlightMap.get("backSeatId"));
if(newSeatback.getRestNum()<1){
//回滚去程的票数
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new BaseResult(202,"库存不够了");
}
//如果库存足够
boolean result = seatService.updateById(newSeatback);
if(result){
k2=1;
break;
}
else {
cnt1++;
}
if(cnt1==max1){
//首先要回滚去程的库存
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new BaseResult(202,"达到尝试上限");
}
}
}
else {
k2=1;
}
if(k1==1&&k2==1){
FlightOrder Order = new FlightOrder();
// 获取当前时间
LocalDateTime currentTime = LocalDateTime.now();
// 如果你想以特定格式输出当前时间,可以使用 DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedCurrentTime = currentTime.format(formatter);
Order.setDate(formattedCurrentTime);
// 设置超时时间为当前时间的15分钟后
LocalDateTime timeoutTime = currentTime.plusMinutes(15);
String formattedTimeoutTime = timeoutTime.format(formatter);
Order.setTimeout(formattedTimeoutTime);
Order.setOrderId(String.valueOf(new SnowUtil(0,0).getId()));
redisTemplate.opsForValue().set(Order.getOrderId(), Order.getOrderId(), 15, TimeUnit.MINUTES);
// 获取数据
Flight flight1 = flightService.getById(selectFlightMap.get("goId"));
Air air1 = airService.getById(flight1.getAirId());
Order.setDepCity(flight1.getDepCity());
Order.setDepDate(flight1.getDepDate());
Order.setDepTime(flight1.getDepTime());
Order.setArrCity(flight1.getArrCity());
Order.setArrDate(flight1.getArrDate());
Order.setArrTime(flight1.getArrTime());
Order.setDepAirport(flight1.getDepAirport());
Order.setArrAirport(flight1.getArrAirport());
Order.setAirlineName(flight1.getAirlineName());
Order.setAirType(air1.getType());
// 订单对象
Order.setFlightCode(flight1.getFlightCode());
Order.setStage("待支付");
Order.setWay("往返");
Order.setUserId(account);
Order.setPrice(seat.getPrice());
Order.setSeatType(seat.getSeatType());
Flight flight2 = flightService.getById(selectFlightMap.get("backId"));
Air air2 = airService.getById(flight2.getAirId());
Order.setBackDepCity(flight2.getDepCity());
Order.setBackDepDate(flight2.getDepDate());
Order.setBackDepTime(flight2.getDepTime());
Order.setBackArrCity(flight2.getArrCity());
Order.setBackArrDate(flight2.getArrDate());
Order.setBackArrTime(flight2.getArrTime());
Order.setBackDepAirport(flight2.getDepAirport());
Order.setBackArrAirport(flight2.getArrAirport());
Order.setBackAirlineName(flight2.getAirlineName());
Order.setBackAirType(air2.getType());
Order.setBackPrice(seatback.getPrice());
Order.setAllPrice(seat.getPrice()+seatback.getPrice());
Order.setBackFlightCode(flight2.getFlightCode());
Order.setBackSeatType(seatback.getSeatType());
orderService.save(Order);
for (Map<String, String> pas : passengers) {
FlightPassenger passenger = new FlightPassenger();
passenger.setOrderId(Order.getOrderId());
passenger.setName(pas.get("name"));
passenger.setIdentityId(pas.get("sfz"));
passenger.setPhone(pas.get("phone"));
passengerService.save(passenger);
}
return new BaseResult(200,"支付成功",Order.getOrderId());
}
else {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new BaseResult(202, "库存不够或者达到重试次数上限");
}
}