基于Springboot3+微服务实现12306高性能售票系统

这篇具有很好参考价值的文章主要介绍了基于Springboot3+微服务实现12306高性能售票系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

💗博主介绍:全网CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者。
专注于Java、小程序技术领域和毕业项目实战💗

✌Java、SSM+Vue、SpringBoot+Vue、NodeJS+Vue、微信小程序、Python、大数据、安卓。

你想要的我都有,你没有的,本团队亲历亲为开发。
统信打造硬核“服务工具”,让客户省心、放心、舒心!

🌟文末获取源码+数据库🌟
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人


一、具体实现截图

简单介绍:
使用三端分离,会员端、控台端、服务端,便于开发和维护,同时将界面与功能逻辑分开,易于分配不同的资源。使用微服务生态组件:注册中心、配置中心、网关、限流降级、分布式事务、服务调用,保障服务高可用。将系统功能拆分成多个微服务应用,实现服务间低耦合,服务内功能高内聚。使用Redis、MQ等中间件,提升系统性能,保障服务高性能响应。

springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js
springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js
springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js
springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js
springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js
springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js

二、技术栈

①.SpringBoot3.0体系

自动配置与约定优于配置简化了项目配置,提供默认配置,减少了开发者的工作量。快速开发集成了大量常用功能,通过注解和约定,减少了样板代码,提高了开发效率。嵌入式服务器支持常见的嵌入式服务器,如Tomcat、Jetty,方便打包成可执行JAR文件。微服务支持提供了Spring Cloud等工具,支持微服务开发和管理。Spring生态系统整合可轻松整合Spring框架中的各种组件,如Spring Data、Spring Security等。广泛的社区支持拥有庞大的社区,提供了丰富的文档和解决方案。
Spring Boot的使用优势在于简化开发、提高效率、支持微服务等方面,使得Java开发更加便捷和灵活。如有新版本发布,请查阅最新文档以获取详细信息。

②.第三方组件集成

含:Mybatis、Redis、RocketMQ、Quartz;

③.设计亮点

WT单点登录、项目分层设计、分库分表、自制前后端代码生成器

④.前端Vue3

Vue CLI 5、Ref、Reactiev、Axios、Router、Vuex、Ant Design Vue、多环境配置、编译与部署

⑤.前高并发秒杀技术

高并发秒杀技术、缓存、限流、熔断、JWT令牌、令牌大闸、削峰填谷、分布式锁

三、为何选择我们?

①:强大、正规的团队

Ⅰ:专注全栈技术分享,汇总多年实战经验。拥有正规团队官网。

springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js

网站上传的项目均为博主自己收集和开发的,质量都可以得到保障。

适合自己懂一点程序开发的同学使用!

Ⅱ:(小程序端正在上线,敬请期待 > > >)

四:代码参考

只进行部分展示 -->

SpringBoot部分:

package com.company.train.business.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.company.train.business.req.ConfirmOrderDoReq;
import com.company.train.business.service.BeforeConfirmOrderService;
import com.company.train.business.service.ConfirmOrderService;
import com.company.train.common.exception.BusinessExceptionEnum;
import com.company.train.common.resp.CommonResp;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/confirm-order")
public class ConfirmOrderController {

    private static final Logger LOG = LoggerFactory.getLogger(ConfirmOrderController.class);

    @Resource
    private BeforeConfirmOrderService beforeConfirmOrderService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Value("${spring.profiles.active}")
    private String env;

    @Resource
    private ConfirmOrderService confirmOrderService;

    // 接口的资源名称不要和接口路径一致,会导致限流后走不到降级方法中
    @SentinelResource(value = "confirmOrderDo", blockHandler = "doConfirmBlock")
    @PostMapping("/do")
    public CommonResp<Object> doConfirm(@Valid @RequestBody ConfirmOrderDoReq req) {
        if (!env.equals("dev")) {
            // 图形验证码校验
            String imageCodeToken = req.getImageCodeToken();
            String imageCode = req.getImageCode();
            String imageCodeRedis = redisTemplate.opsForValue().get(imageCodeToken);
            LOG.info("从redis中获取到的验证码:{}", imageCodeRedis);
            if (ObjectUtils.isEmpty(imageCodeRedis)) {
                return new CommonResp<>(false, "验证码已过期", null);
            }
            // 验证码校验,大小写忽略,提升体验,比如Oo Vv Ww容易混
            if (!imageCodeRedis.equalsIgnoreCase(imageCode)) {
                return new CommonResp<>(false, "验证码不正确", null);
            } else {
                // 验证通过后,移除验证码
                redisTemplate.delete(imageCodeToken);
            }
        }

        Long id = beforeConfirmOrderService.beforeDoConfirm(req);
        return new CommonResp<>(String.valueOf(id));
    }

    @GetMapping("/query-line-count/{id}")
    public CommonResp<Integer> queryLineCount(@PathVariable Long id) {
        Integer count = confirmOrderService.queryLineCount(id);
        return new CommonResp<>(count);
    }

    @GetMapping("/cancel/{id}")
    public CommonResp<Integer> cancel(@PathVariable Long id) {
        Integer count = confirmOrderService.cancel(id);
        return new CommonResp<>(count);
    }

    /** 降级方法,需包含限流方法的所有参数和BlockException参数,且返回值要保持一致
     * @param req
     * @param e
     */
    public CommonResp<Object> doConfirmBlock(ConfirmOrderDoReq req, BlockException e) {
        LOG.info("ConfirmOrderController购票请求被限流:{}", req);
        // throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION);
        CommonResp<Object> commonResp = new CommonResp<>();
        commonResp.setSuccess(false);
        commonResp.setMessage(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION.getDesc());
        return commonResp;

    }

}

Vue端:

<template>
  <div class="order-train">
    <span class="order-train-main">{{dailyTrainTicket.date}}</span>&nbsp;
    <span class="order-train-main">{{dailyTrainTicket.trainCode}}</span>&nbsp;
    <span class="order-train-main">{{dailyTrainTicket.start}}</span><span class="order-train-main">({{dailyTrainTicket.startTime}})</span>&nbsp;
    <span class="order-train-main">——</span>&nbsp;
    <span class="order-train-main">{{dailyTrainTicket.end}}</span><span class="order-train-main">({{dailyTrainTicket.endTime}})</span>&nbsp;

    <div class="order-train-ticket">
      <span v-for="item in seatTypes" :key="item.type">
        <span>{{item.desc}}</span><span class="order-train-ticket-main">{{item.price}}</span>&nbsp;
        <span class="order-train-ticket-main">{{item.count}}</span>&nbsp;张票&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      </span>
    </div>
  </div>
  <a-divider></a-divider>
  <b>勾选要购票的乘客:</b>&nbsp;
  <a-checkbox-group v-model:value="passengerChecks" :options="passengerOptions" />

  <div class="order-tickets">
    <a-row class="order-tickets-header" v-if="tickets.length > 0">
      <a-col :span="2">乘客</a-col>
      <a-col :span="6">身份证</a-col>
      <a-col :span="4">票种</a-col>
      <a-col :span="4">座位类型</a-col>
    </a-row>
    <a-row class="order-tickets-row" v-for="ticket in tickets" :key="ticket.passengerId">
      <a-col :span="2">{{ticket.passengerName}}</a-col>
      <a-col :span="6">{{ticket.passengerIdCard}}</a-col>
      <a-col :span="4">
        <a-select v-model:value="ticket.passengerType" style="width: 100%">
          <a-select-option v-for="item in PASSENGER_TYPE_ARRAY" :key="item.code" :value="item.code">
            {{item.desc}}
          </a-select-option>
        </a-select>
      </a-col>
      <a-col :span="4">
        <a-select v-model:value="ticket.seatTypeCode" style="width: 100%">
          <a-select-option v-for="item in seatTypes" :key="item.code" :value="item.code">
            {{item.desc}}
          </a-select-option>
        </a-select>
      </a-col>
    </a-row>
  </div>
  <div v-if="tickets.length > 0">
    <a-button type="primary" size="large" @click="finishCheckPassenger">提交订单</a-button>
  </div>

  <a-modal v-model:visible="visible" title="请核对以下信息"
           style="top: 50px; width: 800px"
           ok-text="确认" cancel-text="取消"
           @ok="showFirstImageCodeModal">
    <div class="order-tickets">
      <a-row class="order-tickets-header" v-if="tickets.length > 0">
        <a-col :span="3">乘客</a-col>
        <a-col :span="15">身份证</a-col>
        <a-col :span="3">票种</a-col>
        <a-col :span="3">座位类型</a-col>
      </a-row>
      <a-row class="order-tickets-row" v-for="ticket in tickets" :key="ticket.passengerId">
        <a-col :span="3">{{ticket.passengerName}}</a-col>
        <a-col :span="15">{{ticket.passengerIdCard}}</a-col>
        <a-col :span="3">
          <span v-for="item in PASSENGER_TYPE_ARRAY" :key="item.code">
            <span v-if="item.code === ticket.passengerType">
              {{item.desc}}
            </span>
          </span>
        </a-col>
        <a-col :span="3">
          <span v-for="item in seatTypes" :key="item.code">
            <span v-if="item.code === ticket.seatTypeCode">
              {{item.desc}}
            </span>
          </span>
        </a-col>
      </a-row>
      <br/>
      <div v-if="chooseSeatType === 0" style="color: red;">
        您购买的车票不支持选座
        <div>12306规则:只有全部是一等座或全部是二等座才支持选座</div>
        <div>12306规则:余票小于一定数量时,不允许选座(本项目以20为例)</div>
      </div>
      <div v-else style="text-align: center">
        <a-switch class="choose-seat-item" v-for="item in SEAT_COL_ARRAY" :key="item.code"
                  v-model:checked="chooseSeatObj[item.code + '1']" :checked-children="item.desc" :un-checked-children="item.desc" />
        <div v-if="tickets.length > 1">
          <a-switch class="choose-seat-item" v-for="item in SEAT_COL_ARRAY" :key="item.code"
                    v-model:checked="chooseSeatObj[item.code + '2']" :checked-children="item.desc" :un-checked-children="item.desc" />
        </div>
        <div style="color: #999999">提示:您可以选择{{tickets.length}}个座位</div>
      </div>
      <br>
      <div style="color: red">
        体验排队购票,加入多人一起排队购票:
        <a-input-number v-model:value="lineNumber" :min="0" :max="20" />
      </div>
      <!--<br/>-->
      <!--最终购票:{{tickets}}-->
      <!--最终选座:{{chooseSeatObj}}-->
    </div>
  </a-modal>

  <!-- 第二层验证码 后端 -->
  <a-modal v-model:visible="imageCodeModalVisible" :title="null" :footer="null" :closable="false"
           style="top: 50px; width: 400px">
    <p style="text-align: center; font-weight: bold; font-size: 18px">
      使用服务端验证码削弱瞬时高峰<br/>
      防止机器人刷票
    </p>
    <p>
      <a-input v-model:value="imageCode" placeholder="图片验证码">
        <template #suffix>
          <img v-show="!!imageCodeSrc" :src="imageCodeSrc" alt="验证码" v-on:click="loadImageCode()"/>
        </template>
      </a-input>
    </p>
    <a-button type="danger" block @click="handleOk">输入验证码后开始购票</a-button>
  </a-modal>

  <!-- 第一层验证码 纯前端 -->
  <a-modal v-model:visible="firstImageCodeModalVisible" :title="null" :footer="null" :closable="false"
           style="top: 50px; width: 400px">
    <p style="text-align: center; font-weight: bold; font-size: 18px">
      使用纯前端验证码削弱瞬时高峰<br/>
      减小后端验证码接口的压力
    </p>
    <p>
      <a-input v-model:value="firstImageCodeTarget" placeholder="验证码">
        <template #suffix>
          {{firstImageCodeSourceA}} + {{firstImageCodeSourceB}}
        </template>
      </a-input>
    </p>
    <a-button type="danger" block @click="validFirstImageCode">提交验证码</a-button>
  </a-modal>

  <a-modal v-model:visible="lineModalVisible" title="排队购票" :footer="null" :maskClosable="false" :closable="false"
           style="top: 50px; width: 400px">
    <div class="book-line">
      <div v-show="confirmOrderLineCount < 0">
        <loading-outlined /> 系统正在处理中...
      </div>
      <div v-show="confirmOrderLineCount >= 0">
        <loading-outlined /> 您前面还有{{confirmOrderLineCount}}位用户在购票,排队中,请稍候
      </div>
    </div>
    <br/>
    <a-button type="danger" @click="onCancelOrder">取消购票</a-button>
  </a-modal>
</template>

<script>

import {defineComponent, ref, onMounted, watch, computed} from 'vue';
import axios from "axios";
import {notification} from "ant-design-vue";

export default defineComponent({
  name: "order-view",
  setup() {
    const passengers = ref([]);
    const passengerOptions = ref([]);
    const passengerChecks = ref([]);
    const dailyTrainTicket = SessionStorage.get(SESSION_ORDER) || {};
    console.log("下单的车次信息", dailyTrainTicket);

    const SEAT_TYPE = window.SEAT_TYPE;
    console.log(SEAT_TYPE)
    // 本车次提供的座位类型seatTypes,含票价,余票等信息,例:
    // {
    //   type: "YDZ",
    //   code: "1",
    //   desc: "一等座",
    //   count: "100",
    //   price: "50",
    // }
    // 关于SEAT_TYPE[KEY]:当知道某个具体的属性xxx时,可以用obj.xxx,当属性名是个变量时,可以使用obj[xxx]
    const seatTypes = [];
    for (let KEY in SEAT_TYPE) {
      let key = KEY.toLowerCase();
      if (dailyTrainTicket[key] >= 0) {
        seatTypes.push({
          type: KEY,
          code: SEAT_TYPE[KEY]["code"],
          desc: SEAT_TYPE[KEY]["desc"],
          count: dailyTrainTicket[key],
          price: dailyTrainTicket[key + 'Price'],
        })
      }
    }
    console.log("本车次提供的座位:", seatTypes)
    // 购票列表,用于界面展示,并传递到后端接口,用来描述:哪个乘客购买什么座位的票
    // {
    //   passengerId: 123,
    //   passengerType: "1",
    //   passengerName: "张三",
    //   passengerIdCard: "12323132132",
    //   seatTypeCode: "1",
    //   seat: "C1"
    // }
    const tickets = ref([]);
    const PASSENGER_TYPE_ARRAY = window.PASSENGER_TYPE_ARRAY;
    const visible = ref(false);
    const lineModalVisible = ref(false);
    const confirmOrderId = ref();
    const confirmOrderLineCount = ref(-1);
    const lineNumber = ref(5);

    // 勾选或去掉某个乘客时,在购票列表中加上或去掉一张表
    watch(() => passengerChecks.value, (newVal, oldVal)=>{
      console.log("勾选乘客发生变化", newVal, oldVal)
      // 每次有变化时,把购票列表清空,重新构造列表
      tickets.value = [];
      passengerChecks.value.forEach((item) => tickets.value.push({
        passengerId: item.id,
        passengerType: item.type,
        seatTypeCode: seatTypes[0].code,
        passengerName: item.name,
        passengerIdCard: item.idCard
      }))
    }, {immediate: true});

    // 0:不支持选座;1:选一等座;2:选二等座
    const chooseSeatType = ref(0);
    // 根据选择的座位类型,计算出对应的列,比如要选的是一等座,就筛选出ACDF,要选的是二等座,就筛选出ABCDF
    const SEAT_COL_ARRAY = computed(() => {
      return window.SEAT_COL_ARRAY.filter(item => item.type === chooseSeatType.value);
    });
    // 选择的座位
    // {
    //   A1: false, C1: true,D1: false, F1: false,
    //   A2: false, C2: false,D2: true, F2: false
    // }
    const chooseSeatObj = ref({});
    watch(() => SEAT_COL_ARRAY.value, () => {
      chooseSeatObj.value = {};
      for (let i = 1; i <= 2; i++) {
        SEAT_COL_ARRAY.value.forEach((item) => {
          chooseSeatObj.value[item.code + i] = false;
        })
      }
      console.log("初始化两排座位,都是未选中:", chooseSeatObj.value);
    }, {immediate: true});

    const handleQueryPassenger = () => {
      axios.get("/member/passenger/query-mine").then((response) => {
        let data = response.data;
        if (data.success) {
          passengers.value = data.content;
          passengers.value.forEach((item) => passengerOptions.value.push({
            label: item.name,
            value: item
          }))
        } else {
          notification.error({description: data.message});
        }
      });
    };

    const finishCheckPassenger = () => {
      console.log("购票列表:", tickets.value);

      if (tickets.value.length > 5) {
        notification.error({description: '最多只能购买5张车票'});
        return;
      }

      // 校验余票是否充足,购票列表中的每个座位类型,都去车次座位余票信息中,看余票是否充足
      // 前端校验不一定准,但前端校验可以减轻后端很多压力
      // 注意:这段只是校验,必须copy出seatTypesTemp变量来扣减,用原始的seatTypes去扣减,会影响真实的库存
      let seatTypesTemp = Tool.copy(seatTypes);
      for (let i = 0; i < tickets.value.length; i++) {
        let ticket = tickets.value[i];
        for (let j = 0; j < seatTypesTemp.length; j++) {
          let seatType = seatTypesTemp[j];
          // 同类型座位余票-1,这里扣减的是临时copy出来的库存,不是真正的库存,只是为了校验
          if (ticket.seatTypeCode === seatType.code) {
            seatType.count--;
            if (seatType.count < 0) {
              notification.error({description: seatType.desc + '余票不足'});
              return;
            }
          }
        }
      }
      console.log("前端余票校验通过");

      // 判断是否支持选座,只有纯一等座和纯二等座支持选座
      // 先筛选出购票列表中的所有座位类型,比如四张表:[1, 1, 2, 2]
      let ticketSeatTypeCodes = [];
      for (let i = 0; i < tickets.value.length; i++) {
        let ticket = tickets.value[i];
        ticketSeatTypeCodes.push(ticket.seatTypeCode);
      }
      // 为购票列表中的所有座位类型去重:[1, 2]
      const ticketSeatTypeCodesSet = Array.from(new Set(ticketSeatTypeCodes));
      console.log("选好的座位类型:", ticketSeatTypeCodesSet);
      if (ticketSeatTypeCodesSet.length !== 1) {
        console.log("选了多种座位,不支持选座");
        chooseSeatType.value = 0;
      } else {
        // ticketSeatTypeCodesSet.length === 1,即只选择了一种座位(不是一个座位,是一种座位)
        if (ticketSeatTypeCodesSet[0] === SEAT_TYPE.YDZ.code) {
          console.log("一等座选座");
          chooseSeatType.value = SEAT_TYPE.YDZ.code;
        } else if (ticketSeatTypeCodesSet[0] === SEAT_TYPE.EDZ.code) {
          console.log("二等座选座");
          chooseSeatType.value = SEAT_TYPE.EDZ.code;
        } else {
          console.log("不是一等座或二等座,不支持选座");
          chooseSeatType.value = 0;
        }

        // 余票小于20张时,不允许选座,否则选座成功率不高,影响出票
        if (chooseSeatType.value !== 0) {
          for (let i = 0; i < seatTypes.length; i++) {
            let seatType = seatTypes[i];
            // 找到同类型座位
            if (ticketSeatTypeCodesSet[0] === seatType.code) {
              // 判断余票,小于20张就不支持选座
              if (seatType.count < 20) {
                console.log("余票小于20张就不支持选座")
                chooseSeatType.value = 0;
                break;
              }
            }
          }
        }
      }

      // 弹出确认界面
      visible.value = true;

    };

    const handleOk = () => {
      if (Tool.isEmpty(imageCode.value)) {
        notification.error({description: '验证码不能为空'});
        return;
      }

      console.log("选好的座位:", chooseSeatObj.value);

      // 设置每张票的座位
      // 先清空购票列表的座位,有可能之前选了并设置座位了,但选座数不对被拦截了,又重新选一遍
      for (let i = 0; i < tickets.value.length; i++) {
        tickets.value[i].seat = null;
      }
      let i = -1;
      // 要么不选座位,要么所选座位应该等于购票数,即i === (tickets.value.length - 1)
      for (let key in chooseSeatObj.value) {
        if (chooseSeatObj.value[key]) {
          i++;
          if (i > tickets.value.length - 1) {
            notification.error({description: '所选座位数大于购票数'});
            return;
          }
          tickets.value[i].seat = key;
        }
      }
      if (i > -1 && i < (tickets.value.length - 1)) {
        notification.error({description: '所选座位数小于购票数'});
        return;
      }

      console.log("最终购票:", tickets.value);

      axios.post("/business/confirm-order/do", {
        dailyTrainTicketId: dailyTrainTicket.id,
        date: dailyTrainTicket.date,
        trainCode: dailyTrainTicket.trainCode,
        start: dailyTrainTicket.start,
        end: dailyTrainTicket.end,
        tickets: tickets.value,
        imageCodeToken: imageCodeToken.value,
        imageCode: imageCode.value,
        lineNumber: lineNumber.value
      }).then((response) => {
        let data = response.data;
        if (data.success) {
          // notification.success({description: "下单成功!"});
          visible.value = false;
          imageCodeModalVisible.value = false;
          lineModalVisible.value = true;
          confirmOrderId.value = data.content;
          queryLineCount();
        } else {
          notification.error({description: data.message});
        }
      });
    }

    /* ------------------- 定时查询订单状态 --------------------- */
    // 确认订单后定时查询
    let queryLineCountInterval;

    // 定时查询订单结果/排队数量
    const queryLineCount = () => {
      confirmOrderLineCount.value = -1;
      queryLineCountInterval = setInterval(function () {
        axios.get("/business/confirm-order/query-line-count/" + confirmOrderId.value).then((response) => {
          let data = response.data;
          if (data.success) {
            let result = data.content;
            switch (result) {
              case -1 :
                notification.success({description: "购票成功!"});
                lineModalVisible.value = false;
                clearInterval(queryLineCountInterval);
                break;
              case -2:
                notification.error({description: "购票失败!"});
                lineModalVisible.value = false;
                clearInterval(queryLineCountInterval);
                break;
              case -3:
                notification.error({description: "抱歉,没票了!"});
                lineModalVisible.value = false;
                clearInterval(queryLineCountInterval);
                break;
              default:
                confirmOrderLineCount.value = result;
            }
          } else {
            notification.error({description: data.message});
          }
        });
      }, 500);
    };

    /* ------------------- 第二层验证码 --------------------- */
    const imageCodeModalVisible = ref();
    const imageCodeToken = ref();
    const imageCodeSrc = ref();
    const imageCode = ref();
    /**
     * 加载图形验证码
     */
    const loadImageCode = () => {
      imageCodeToken.value = Tool.uuid(8);
      imageCodeSrc.value = process.env.VUE_APP_SERVER + '/business/kaptcha/image-code/' + imageCodeToken.value;
    };

    const showImageCodeModal = () => {
      loadImageCode();
      imageCodeModalVisible.value = true;
    };

    /* ------------------- 第一层验证码 --------------------- */
    const firstImageCodeSourceA = ref();
    const firstImageCodeSourceB = ref();
    const firstImageCodeTarget = ref();
    const firstImageCodeModalVisible = ref();

    /**
     * 加载第一层验证码
     */
    const loadFirstImageCode = () => {
      // 获取1~10的数:Math.floor(Math.random()*10 + 1)
      firstImageCodeSourceA.value = Math.floor(Math.random()*10 + 1) + 10;
      firstImageCodeSourceB.value = Math.floor(Math.random()*10 + 1) + 20;
    };

    /**
     * 显示第一层验证码弹出框
     */
    const showFirstImageCodeModal = () => {
      loadFirstImageCode();
      firstImageCodeModalVisible.value = true;
    };

    /**
     * 校验第一层验证码
     */
    const validFirstImageCode = () => {
      if (parseInt(firstImageCodeTarget.value) === parseInt(firstImageCodeSourceA.value + firstImageCodeSourceB.value)) {
        // 第一层验证通过
        firstImageCodeModalVisible.value = false;
        showImageCodeModal();
      } else {
        notification.error({description: '验证码错误'});
      }
    };

    /**
     * 取消排队
     */
    const onCancelOrder = () => {
      axios.get("/business/confirm-order/cancel/" + confirmOrderId.value).then((response) => {
        let data = response.data;
        if (data.success) {
          let result = data.content;
          if (result === 1) {
            notification.success({description: "取消成功!"});
            // 取消成功时,不用再轮询排队结果
            clearInterval(queryLineCountInterval);
            lineModalVisible.value = false;
          } else {
            notification.error({description: "取消失败!"});
          }
        } else {
          notification.error({description: data.message});
        }
      });
    };

    onMounted(() => {
      handleQueryPassenger();
    });

    return {
      passengers,
      dailyTrainTicket,
      seatTypes,
      passengerOptions,
      passengerChecks,
      tickets,
      PASSENGER_TYPE_ARRAY,
      visible,
      finishCheckPassenger,
      chooseSeatType,
      chooseSeatObj,
      SEAT_COL_ARRAY,
      handleOk,
      imageCodeToken,
      imageCodeSrc,
      imageCode,
      showImageCodeModal,
      imageCodeModalVisible,
      loadImageCode,
      firstImageCodeSourceA,
      firstImageCodeSourceB,
      firstImageCodeTarget,
      firstImageCodeModalVisible,
      showFirstImageCodeModal,
      validFirstImageCode,
      lineModalVisible,
      confirmOrderId,
      confirmOrderLineCount,
      onCancelOrder,
      lineNumber
    };
  },
});
</script>

<style>
.order-train .order-train-main {
  font-size: 18px;
  font-weight: bold;
}
.order-train .order-train-ticket {
  margin-top: 15px;
}
.order-train .order-train-ticket .order-train-ticket-main {
  color: red;
  font-size: 18px;
}

.order-tickets {
  margin: 10px 0;
}
.order-tickets .ant-col {
  padding: 5px 10px;
}
.order-tickets .order-tickets-header {
  background-color: cornflowerblue;
  border: solid 1px cornflowerblue;
  color: white;
  font-size: 16px;
  padding: 5px 0;
}
.order-tickets .order-tickets-row {
  border: solid 1px cornflowerblue;
  border-top: none;
  vertical-align: middle;
  line-height: 30px;
}

.order-tickets .choose-seat-item {
  margin: 5px 5px;
}
</style>

⚠ 源码获取

微信号Xiaojiacoding 👇🏻扫码获取联系方式👇🏻

springboot3+微服务实战12306高性能售票系统,Java精选案例,微服务,java,spring boot,java-rocketmq,javascript,vue.js文章来源地址https://www.toymoban.com/news/detail-828252.html

到了这里,关于基于Springboot3+微服务实现12306高性能售票系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springBoot + netty搭建高性能 websocket 服务 & 性能测试(包含python 测试脚本)

    1、如果我们的app类似于股票这种,数据很多很快,之前用的tomcat自带的 websocket 又或者 spring-boot-starter-websocke 集成,但是性能在数据并发很大时就会存在问题。 2、我前面写的一篇关于 springBoot+webosket的,没有使用netty的文章 springBoot使用webSocket的几种方式以及在高并发出现的

    2024年02月04日
    浏览(62)
  • 基于.NetCore高性能敏感词检测开源库

    🏆作者:科技、互联网行业优质创作者 🏆专注领域:.Net技术、软件架构、人工智能、数字化转型、DeveloperSharp、微服务、工业互联网、智能制造 🏆欢迎关注我(Net数字智慧化基地),里面有很多 高价值 技术文章, 是你刻苦努力也积累不到的经验 ,能助你快速成长。升职

    2024年02月21日
    浏览(44)
  • 基于WebCodecs的网页端高性能视频截帧

    web投稿页是B站的主要投稿来源,有很多高粉UP主使用web端进行投稿。 封面部分是投稿过程中耗时占比较高的步骤,因此在过去,web投稿页已上线了自动的封面截取推荐功能,有效提升了用户体验。同时在此过程中有了一定的技术积累。 自动封面功能依赖于对用户上传视频进

    2024年02月21日
    浏览(48)
  • 基于高性能计算环境的HPC算力编程模式

    摘要 【目的】 随着云计算、大数据、人工智能等技术的兴起和广泛应用,促进了基于多元算力的融合计算发展。在国家“东数西算”战略的指引下,充分发挥HPC算力优势,提供新型HPC算力编程模式,是新一代计算基础设施可编程能力的重要变革。 【方法】 分析了高性能计算环境

    2024年02月02日
    浏览(60)
  • 字节跳动开源 Shmipc:基于共享内存的高性能 IPC

    动手点关注 干货不迷路 CloudWeGo - Shmipc 是字节跳动服务框架团队研发的 高性能进程间通讯库 ,它基于 共享内存 构建,具有 零拷贝 的特点,同时它引入的同步机制具有 批量收割 IO 的能力,相对于其他进程间通讯方式能明显提升性能。在字节内部,Shmipc 应用于 Service Mesh 场

    2023年04月08日
    浏览(48)
  • 基于lvs+keepalived+nginx的web高性能集群项目

    模拟企业里的web项目需求,最终目的是构建一个高性能高可用的web集群系统。部署lvs负载均衡系统和keepalived高可用软件,后端使用nginx做web服务器,同时搭建内部的一套基于prometheus的监控系统。使用ansible实现整个集群系统的自动化运维工作。 ceotos7 nginx1.25.3 node_exporter-1.4.0

    2024年02月20日
    浏览(55)
  • 网易NDH基于Impala的高性能SQL引擎建设实践

    导读:本文将从四个方面来进行介绍。首先是分析在网易NDH中使用 Impala 过程遇到的一些痛点;第二个部分是基于这些痛点问题,我们提出了建设高性能SQL引擎的方案,以及这些方案是基于什么原则来创建的;第三个是基于这些原则,我们做了哪些的优化实践的尝试;最后会

    2024年02月09日
    浏览(47)
  • 基于k8s的高性能高可用的web集群

    模拟公司里的k8s生产环境,部署web,MySQL,nfs,harbor,Prometheus,Jenkins等应用,构建一个高性能高可用的web集群 CentOS7,k8s,docker,Prometheus,nfs,jumpserver,harbor,ansible,Jenkins等 k8s-master:192.168.121.101 k8s-node1:192.168.121.102 k8s-node2:192.168.121.103 nfs:192.168.121.104 harbor:192.168.121

    2024年04月28日
    浏览(44)
  • 002. 使用最小堆实现高性能定时器实现

    定时器原理 – 任务的容器(要求:数据结构有序或相对有序;能快速查找最近触发的定时任务) + 触发条件(可以通过带有time_out的系统函数,如epoll_wait的最后一个参数); 最小堆的特点 – 是一颗完全二叉树; – 某个节点的值总是小于等于它子节点的值(即定位到最小值的时间

    2024年02月07日
    浏览(53)
  • 【大数据】Doris:基于 MPP 架构的高性能实时分析型数据库

    Apache Doris 是一个基于 MPP ( Massively Parallel Processing , 大规模并行处理 )架构的高性能、实时的分析型数据库,以极速易用的特点被人们所熟知,仅需亚秒级响应时间即可返回海量数据下的查询结果,不仅可以支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。基于

    2024年02月11日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包