辽宁省住房和城乡建设厅官网/搜索引擎优化是什么

辽宁省住房和城乡建设厅官网,搜索引擎优化是什么,富德生命人寿保险公司官方网站,wordpress做个游戏基本预定车票功能的开发 对于乘客购票来说,需要有每一个车次的余票信息,展示给乘客,供乘客选择,因此首个功能是余票的初始化,之后是余票查询,这两个都是控台端。对于会员端的购票,需要有余票查询…

基本预定车票功能的开发

        对于乘客购票来说,需要有每一个车次的余票信息,展示给乘客,供乘客选择,因此首个功能是余票的初始化,之后是余票查询,这两个都是控台端。对于会员端的购票,需要有余票查询以及乘客的选择,不仅仅支持给自己买票,还可以给其他人买票,而且还可以选择座位类型,是一等座还是二等座,可以选择座位,最后是下单购票。

余票信息表

对于购票表来说,最为重要的字段是售卖字段,对于这个字段来说,将经过的车站用0和1拼接,如果是0表示可卖,1表示不可卖。例如有ABCD四个站,那么000表示这四个站都可以买,最终是可以通过售卖信息来计算出余票的信息。余票查询会显示还有多少张票,票数如果通过实时计算,会影响性能,所以应该另外做张表(余票信息表),直接存储余票数,这张表通过购票表的售卖字段定时的更新此表的信息。这张表是火车的一个子表,可以看作用余票的角度观察火车,因此需要包含id,日期,车次以及出发站和到达站的信息。对于出发站和到达站来说,重要的是这个站在整个车次是第几站,以及每一个站站记录它的余票信息。唯一键是日期,车次,出发站和终点站。

        为什么时日期,车次,出发站和终点站呢?首先是日期,同一个车次每天会运行一次,余票需要按天来划分,车次是列车的唯一标识,不同的车次余票应该进行隔离,对于出发站和终点站,举个例子,现在有100张票,有5个区间A->B->C->D->E,现在小刚买了A->C的票,他影响了A->C,A->B,B->C这三个区间,进行库存扣减,但是对于D->E并不影响,还是100张,这中间有座位复用的问题,因此需要加上出发站和终点站座位唯一键。

        构建余票表完成后,有两个问题,这张表应该如何初始化?初始化的数据从何而来?首先第一个问题,什么时候初始化?当一辆火车准备开始卖票时,就可以初始化了。对于车站数据来说,是一个嵌套循环,例如ABCD四个车站,用户可以查AB,AC,AD,BC,BD,CD,这样子就可以得到车次所有的出发站和到达站的站站组合。对于余票信息来说,可以查询座位数这张表,查询车次以及座位类型就可以得到余票的信息。

        落实到具体的代码上,首先是删除要生成某日车次的余票信息,使其支持重复生成,之后是查询这个车次的所有车站信息,根据车站的信息进行嵌套循环,先生成一个余票对象,然后根据车站进行数据的填充,最后将实体保存到数据库

@Transactionalpublic void genDaily(Date date, String trainCode) {LOG.info("生成日期【{}】车次【{}】的余票信息开始", DateUtil.formatDate(date), trainCode);// 删除某日某车次的余票信息DailyTrainTicketExample dailyTrainTicketExample = new DailyTrainTicketExample();dailyTrainTicketExample.createCriteria().andDateEqualTo(date).andTrainCodeEqualTo(trainCode);dailyTrainTicketMapper.deleteByExample(dailyTrainTicketExample);// 查出某车次的所有车站信息List<TrainStation> stationList = trainStationService.selectByTrainCode(trainCode);if(CollUtil.isEmpty(stationList)) {LOG.info("该车次信息没有车站基础数据,生成该车次的余票信息失败");return;}DateTime now = DateTime.now();for (int i = 0; i < stationList.size(); i++) {// 得到出发站TrainStation trainStationStart = stationList.get(i);for (int j = i + 1; j < stationList.size(); j++) {TrainStation trainStationEnd = stationList.get(j);DailyTrainTicket dailyTrainTicket = new DailyTrainTicket();dailyTrainTicket.setId(SnowUtil.getSnowflakeNextId());dailyTrainTicket.setDate(date);dailyTrainTicket.setTrainCode(trainCode);dailyTrainTicket.setStart(trainStationStart.getName());dailyTrainTicket.setStartPinyin(trainStationStart.getNamePinyin());dailyTrainTicket.setStartTime(trainStationStart.getOutTime());dailyTrainTicket.setStartIndex(trainStationStart.getIndex());dailyTrainTicket.setEnd(trainStationEnd.getName());dailyTrainTicket.setEndPinyin(trainStationEnd.getNamePinyin());dailyTrainTicket.setEndTime(trainStationEnd.getInTime());dailyTrainTicket.setEndIndex(trainStationEnd.getIndex());dailyTrainTicket.setYdz(0);dailyTrainTicket.setYdzPrice(BigDecimal.ZERO);dailyTrainTicket.setEdz(0);dailyTrainTicket.setEdzPrice(BigDecimal.ZERO);dailyTrainTicket.setRw(0);dailyTrainTicket.setRwPrice(BigDecimal.ZERO);dailyTrainTicket.setYw(0);dailyTrainTicket.setYwPrice(BigDecimal.ZERO);dailyTrainTicket.setCreateTime(now);dailyTrainTicket.setUpdateTime(now);dailyTrainTicketMapper.insert(dailyTrainTicket);}}LOG.info("生成日期【{}】车次【{}】的余票信息结束", DateUtil.formatDate(date), trainCode);}

现在发现,对于座位类型的个数和票价还是未知,因此接下来解决这个方面的信息。求某个车次的某类型的票数,需要知道的是座位数。因此应该在每日座位表中查某个日期,某个车次,某个座位类型的票数。由于在每日座位表已经有了这些信息,因此只需要填写好信息去查询就可以得到初始化的余票信息,对于无票的时候,如果设置为0,用户可以以为卖光了,其实想要表达的是改车次没有这种类型的票,因此可以设置为-1.

public int countSeat(Date date, String trainCode,String seatType){DailyTrainSeatExample example = new DailyTrainSeatExample();example.createCriteria().andDateEqualTo(date).andTrainCodeEqualTo(trainCode).andSeatTypeEqualTo(seatType);long l = dailyTrainSeatMapper.countByExample(example);if(l == 0L) {return -1;}return (int)l;}

接下来是计算票价,票价和火车类型以及座位类型有关:票价=里程之和*座位类型的票价*车次类型系数。计算里程时从初始站加上每一次到达站的距离,即

sumKM = sumKM.add(trainStationEnd.getKm());

最终的总计算公式如下:

// 票价=里程之和*座位类型的票价*车次类型系数
String trainType = dailyTrain.getType();
// 计算票价系数:TrainTypeEnum.priceRate
BigDecimal priceRate = EnumUtil.getFieldBy(TrainTypeEnum::getPriceRate,TrainTypeEnum::getCode,trainType);

余票查询

对于余票的查询,设置查询条件为日期,火车车次,起始站和终点站。对于会员端的车票界面来说,它不支持增加和修改和删除,只是支持查询,因此后端对于会员端增加车票查询的接口。

@RestController
@RequestMapping("/daily-train-ticket")
public class DailyTrainTicketController {@Resourceprivate DailyTrainTicketService dailyTrainTicketService;@GetMapping("/query-list")public CommonResp<PageResp<DailyTrainTicketQueryResp>> queryList(@Valid DailyTrainTicketQueryReq req) {PageResp<DailyTrainTicketQueryResp> list = dailyTrainTicketService.queryList(req);return new CommonResp<>(list);}
}

由于进行查询时需要选择车站以及火车车次,而且为了进行分离,之前在控制台界面统一增加了admin,为了使会员端实现相同的查询,因此需要重新写controller层的车次和车票查询,它和控台的功能相同,只是url不同。至此,会员端和控制台界面开发完毕。

选座功能

        首先是预定的按钮,在点击按钮之后需要跳转到order界面,因此需要在router增加一个路由,那用什么取传递参数呢?session就是一个很好的选择,在order界面打开时执行setup(),定义一个参数dailyTrainTicket从缓存中获取dailyTrainTicket,如果没有,给一个空对象,避免空指针异常,之后进行返回,在html部分进行显示出来。在点击时,利用自定义的toOrder,首先是把record放入Session中,之后进行路由跳转。现在是每次查询之后返回,选择框不会保存之前选择的值,为了增强用户体验,可以为余票查询页面缓存查询参数,方便用户使用,将session key写成常量,方便统一维护,可以避免多个功能使用同一个key。当用户选择之后,将用户的选择缓存到一个session key中,然后在公共区添加不同的session key,避免混用。之后修改onMounted(),它表示页面打开,先进性缓存的获取,之后不为空是进行查询。

// order.vue<template><div>{{dailyTrainTicket}}</div>
</template><script>import {defineComponent} from 'vue';export default defineComponent({name: "order-view",setup() {const dailyTrainTicket = SessionStorage.get("dailyTrainTicket") || {};console.log("下单的车次信息", dailyTrainTicket);return {dailyTrainTicket,};},
});
</script>// ticket.vue// 保存查询参数
SessionStorage.set(SESSION_TICKET_PARAMS, params.value);onMounted(() => {params.value = SessionStorage.get(SESSION_TICKET_PARAMS) || {};if(Tool.isNotEmpty(params.value)) {handleQuery({page: 1,pageSize: pagination.value.pageSize});}});

最后的是在order界面的展示优化,得到从出发站到终点站以及座位类型和价格以及票数的展示。

        接下来是真正的选择座位功能,首先是后端去查找我的所有乘客接口,在order界面调用接口,在搜索的service层,需要获取当前登录者的id,然后根据登录者的id去库中搜索出为那些乘客购票,如果乘客太多,可以增加一个功能,当乘客数量大于50时就不拿增加乘客了,controller直接增加接口查询即可。对于前端,增加一个响应式变量passenger,增加一个handleQueryPassen-ger,方法,这个方法调用后端接口,得到后给响应式变量赋值,即初始化时直接查询。

        对于选择乘客,在js部分增加了const passengerOptions = ref([]); const passengerChecks = ref([]);表示选项和选择,由于乘客带有的属性过多,因此可以在handleQueryPassenger方法中增加lable和value,分别表示看到的值以及实际操作的值。勾选完乘客后,需要为乘客构造购票数据。由于一次不仅仅勾选一个乘客,因此可以引入watch,实时监控勾选的变化,用来显示购票的界面。

// 购票列表,用于界面展示,并传递到后端接口,用来描述:哪个乘客购买什么座位的票const tickets = ref([]);// 勾选或去掉某个乘客时,在购票列表中加上或去掉一张表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});

最后选择是勾选乘客后提交,显示购票列表确认框进行最后的核对。此时,购票的选座展示效果完毕。


选座规则

  • 只有全部是一等座或全部是二等座才支持选座
  • 余票小于20张时,不允许选座

选座效果

显示两排,一等座每排4个,二等座每排5个,为什么是两排,只是一个自定义的规则,可以3排进行显示,由自己规定。每排的座位是由枚举座位类型得到的,对于1,2等座的划分,根据枚举中的type值即可进行得到。之后构造两个响应式变量chooseSeatType和chooseSeatObj,其中chooseSeatType是表示是否支持选座以及选择的类型,chooseSeatObj表示用户选择的座位是那些,默认为false,选择之后为true,通过读这个对象就知道用户选择了什么座位。经过选座,就可以得到tickets,其中有乘客id,乘客类型,座位类型,乘客姓名,身份证,实际座位。当没有选座时,实际座位为空,由系统来分配,从一号开始找,未被购买,就选座。选座,以购买两张一等座AC为例:遍历一等座车厢,每个车厢从1号座位开始找A列座位,未被购买的,就预选中它;再挑它旁边的C,如果也未被购买,则最终选中这两个座位,如果B已被购买,则回到第一步,继续找未被购买的A座。再挑它旁边的C,这个应该怎么写?可以从第二个座位开始,需要计算和第一个座位的偏移值,不需要再从1位置开始找,提高选座效率。

前端的选座效果

首先是要考虑这个车票能不能选,例如现在还有5张票,共有7个人来买票,这肯定是不行的,因此可以在前端增加一层校验,来检验余票是否足够,可以减小后端的压力。这步是预扣减库存,只是用来校验,所有拷贝出临时变量来扣减,即点击提交是预扣减库存,实际提交才是真正扣减库存

      // 校验余票是否充足,购票列表中的每个座位类型,都去车次座位余票信息中,看余票是否充足// 前端校验不一定准,但前端校验可以减轻后端很多压力// 注意:这段只是校验,必须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("前端余票校验通过");

开始选座

响应式变量chooseSeatType首先是0,表示不支持选座,然后根据座位类型选择对应的列,赋值给SEAT_COL_ARRAY,之后对两排的座位进行初始化,赋值为false;由于规定不能同时选择1和2等座,所有开始选座之前先进行去重,如果多于1中返回选座不成功,否则的话根据类型进行选座。最后进行界面优化,增加选座的按钮,这里注意,如果是选择一个人进行购票,这里采用只显示一排按钮。回到选择的函数中来,增加一个约定,余票小于20张时,不允许选座。最后提交时,计算出每个用户的座位选择,代码如下:

const handleOk = () => {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);}

后端选座接口

        前面的余票信息表是根据购票表来进行得到的,但是,购买完成票之后还需要进行落表,无论是否成功。需要哪一个人购的票,表示当前访问这个接口的是哪一个会员,还需要日期,车次,出发站和到达站以及这些基础信息,这些信息就可以唯一定位到余票信息这张表,就可以判断它的一等座,二等座等的余票信息了。余票id字段也可以和上面的余票表进行关联。由于订单状态不一定成功,因此需要订单的状态,车票可以做成json,也可以做成子表。

        开发接口的话,那么传入的参数是什么,可以参考设计的表,接口进来之后,就应该数据落表,那么表中的数据如何来?其中member_id可以从线程本地变量获取,日期,车次,出发站,到达站和车票都可以从前端获取,其中车票可以把json映射成Java类,这样子操作更加的方便。因为需要车票进行选座,用json操作不太方便。订单状态是由程序根据不同的步骤进行落库,因此不用管。首先要做的是添加车票类(ConfrimOrderTicketReq),即购买车票的信息,接受前端传来的对象。之后要构造一个订单类,方便入库。之后在controller层增加doConfirm接口,最后在service层增加相关保存购票信息的方法。最后前端调用后端接口。

        重点的话是会员模块的service层的保存订单方法doConfirm如何实现。

  • 保存确认订单表,状态初始,对于id,直接使用雪花算法,时间使用当前的时间,memberid使用登录人的id,像traincode,date,start和end一次ticket都是从前端获取的,之前保存到了ConfirmOrderReq,从这里直接获取即可,注意,对于ticket来说,需要将json字符串转化为车票类
  • 查出余票记录,得到真实的库存。由于唯一键是日期,车次,起始和终点站,这样子构造号条件,进行查询。
    public DailyTrainTicket selectByUnique(Date date, String trainCode,String start,String end) {DailyTrainTicketExample dailyTrainTicketExample = new DailyTrainTicketExample();dailyTrainTicketExample.createCriteria().andDateEqualTo(date).andTrainCodeEqualTo(trainCode).andStartEqualTo(start).andEndEqualTo(end);List<DailyTrainTicket> list = dailyTrainTicketMapper.selectByExample(dailyTrainTicketExample);if(CollUtil.isNotEmpty(list)) {return list.get(0);}else {return null;}}
  • 进行票数的预扣减,由于前端的是实时显示到界面上,因此需要一个变量,而这里只要不更新到数据库,怎么扣减都可以,因此可以之间操作查出的库存记录。一张票一张票的循环进行扣减,由于选择的可能是不同的座位类型(不同的人),因此不能按照列表进行扣票,应该按照不同的座位类型进行扣票。
     private static void reduceTickets(ConfirmOrderDoReq req, DailyTrainTicket dailyTrainTicket) {for (ConfirmOrderTicketReq ticketReq : req.getTickets()) {String seatTypeCode = ticketReq.getSeatTypeCode();SeatTypeEnum seatTypeEnum = EnumUtil.getBy(SeatTypeEnum::getCode, seatTypeCode);switch (seatTypeEnum) {case YDZ -> {int countLeft = dailyTrainTicket.getYdz() - 1;if(countLeft < 0) {throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);}dailyTrainTicket.setYdz(countLeft);}case EDZ -> {int countLeft = dailyTrainTicket.getEdz() - 1;if(countLeft < 0) {throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);}dailyTrainTicket.setEdz(countLeft);}case RW -> {int countLeft = dailyTrainTicket.getRw() - 1;if(countLeft < 0) {throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);}dailyTrainTicket.setRw(countLeft);}case YW -> {int countLeft = dailyTrainTicket.getYw() - 1;if(countLeft < 0) {throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);}dailyTrainTicket.setYw(countLeft);}}}}
  • 偏移值的计算,这里的偏移值指的是相对于第一个选座位置的偏移值,这样子可以一次循环找到所有位置的值(假设一个车厢50个座位,A,B,C,D,E各10个,现在A全部被买了,如果现在选座A,C那么不会选座成功)。对于第一个购票来说,要么是成功,要么不成功,因此就可以根据第一个张票成功与否的情况判断后面的票是否能选,即本次购票是否有选座。对于有选座的功能,还需要偏移值进行计算,得到的结果有空位算是选座成功。由于一等座和二等座每排位置不同,还需要知道座位的类型,之后组成和前端两排选座一样的列表,用于作参照的座位列表,需要两次循环,因为有两排,这一步就是座位的初始化。之后计算绝对的偏移值,再根据第一个位置计算相对的偏移值
  • 挑车厢:对于购票没有选座的来说,比较简单,只要这个座位是可选的即可,需要一张票一张票的去挑选。在买票之前,首先要知道购买票的类型在哪一个车厢中,因此需要是要写一个寻找车厢的方法,根据日期,车次,和车票类型,然后把上面的当作传入的参数就可以得到符合条件的车厢了,之后在得到的车厢列表中一个个的寻找。

  • 根据车厢挑座位:根据获取车厢的方法获得符合条件的车厢之后,现在得到的车厢都是一种票型了,先获取起始车厢,以及进入车厢前的座位列表,座位列表可以根据日期,车次以及车厢位置来获取,因为一个车厢的座位类型是相同的,然后就一个车厢一个车厢的寻找。下面的选座就是调用了本段写的getSeat方法,在有选座的情况下,需要知道第一个选座的实际列,以及得到的偏移值,根据这两个参数进行选座。在没有选座的情况,并没有特定的列号,也没有偏移值,那就传null,只是一个座位一个座位的选座。对于座位还应该进行排序,根据座位索引进行排序。现如今插入车厢的座位后,第一次需要一个座位一个座位的挑选,写个循环,看每个座位是否可卖,可卖的话之间返回,否则跳过。

  • 判断是否可卖:1表示在这个区间买过票了,就不能够售票了。0表示在这个给区间没有卖票。例如:sell=10001,本次购买的区间是1~4,则区间应该售000,这里是10001中间的三个0,000要变成111,那么之后可以根据或运算变成11111,即10001|01110==11111,如果sell=00001,那么按位或得到01111,之后转为数字是15,但再转成二进制是1111,不是01111,因此需要补0。

    private boolean calSell(DailyTrainSeat dailyTrainSeat,Integer startIndex,Integer endIndex) {String sell = dailyTrainSeat.getSell();String sellPart = sell.substring(startIndex, endIndex);if(Integer.parseInt(sellPart) > 0) {LOG.info("座位{}在本车站区间{}--{}已售过票,不可选中该座位",dailyTrainSeat.getCarriageSeatIndex(),startIndex,endIndex);return false;}else {LOG.info("座位{}在本车站区间{}--{}未售过票,可选中该座位",dailyTrainSeat.getCarriageSeatIndex(),startIndex,endIndex);// 111String curSell = sellPart.replace('0', '1');// 0111curSell = StrUtil.fillBefore(curSell,'0', endIndex);curSell = StrUtil.fillAfter(curSell,'0',sell.length());// 当前区间售票信息curSell与库里的已售信息sell按位与,即可得到该座位卖出此票后的售票详情// 32int newSellInt = NumberUtil.binaryToInt(curSell) | NumberUtil.binaryToInt(sell);// 11111String newSell = NumberUtil.getBinaryStr(newSellInt);newSell = StrUtil.fillBefore(newSell,'0',sell.length());LOG.info("座位{}在本车站区间{}--{}卖出该票后,最终售票详情:{}",dailyTrainSeat.getCarriageSeatIndex(),startIndex,endIndex,newSell);dailyTrainSeat.setSell(newSell);return true;}
  • 优化getSeat方法:首先判断传入的column和拿到的col,进行比较,如果无值,表示无选座,有值需要判断是否可以匹配,不匹配的话跳过。现在选完第一个座位后,接下来根据偏移值选则后面几人的座位,偏移值可能有多个,从索引1开始(0是以及选完的第一个座位)进行循环,下一个位置是索引+偏移量,然后根据是否超卖判断是否可选。还要注意,选座是在同一个车厢,根据下一个索引是否大于车厢座位数

  • 保存最终的选座结果,但不是更新到数据库中。此时用一个临时变量保存选择的座位,当下一个选座时,应该看这个选座的结果以及最终的选座结果(这个最终结果是之前的结果,如果此次选座成功,那么就更新最终选座),否则就出现挑选同一个座位的情况。例如系统分配座位的时候,先分配座位3,由于同一个人为不同人买多张票,在进行第二张票购买前,没有更新到数据库中,导致下一个分配的还是3号座位,这显然是不合理的,因此需要对每一次选择的座位进行保存,为了让下一次正确的挑选座位。对于选座的情况,由于第一次选座成功的情况下,可能之后根据偏移值选择的其他座位不成功,如果直接保存的最终结果,可能导致结果和符合的不一致,因此也是需要保存到临时变量中,注意没有成功选座时清空临时变量,当两种情况都成功选座后,才真正的保存到最终选座的变量中。

  • 根据售卖信息更新座位售卖情况,就是更新数据库的售卖信息,例如从000更新到101.就是选好座位后,将座位信息更新到日常座位表中。由于后面还需要涉及到修改余票,为会员增加购票记录,更新订单状态,因此可以把他们称为选座后的事务处理。可以在最后修改数据库的时候增加事务,这样子占用事务的时间较少,否则的话占用大量的数据库资源。由于本垒方法间的调用,事务不生效,因此增加一个类,专门进行处理。根据最终选票结果来处理。此时需要更新的是id以及售卖票的信息还要更新时间,不必要更新表中所有的字段。

    @Transactionalpublic void afterDoConfirm(List<DailyTrainSeat> finalSeatList) {for (DailyTrainSeat dailyTrainSeat: finalSeatList){DailyTrainSeat seatForUpdate = new DailyTrainSeat();seatForUpdate.setId(dailyTrainSeat.getId());seatForUpdate.setSell(dailyTrainSeat.getSell());seatForUpdate.setUpdateTime(new Date());dailyTrainSeatMapper.updateByPrimaryKeySelective(seatForUpdate);}}
  • 扣减库存(很重要):售卖一张票影响的是多个区间的库存,就是本次选座之前没卖过票的,并且本次购买的区间有交集的区间,怎么理解,如下图:

由于座位区间2没有卖过票,而且AD和AE与购买的CD区间有交集,因此减库存。 

由于 座位区间1已经卖过票了,因此不需要减库存。

因此先计算区间,然后根据区间来进行扣减库存。需要计算出最大最小开始结束的影响区间(4个参数)。

 这里买了4(startIndex)到7(endIndex)站的票,购买区间下标7不更新是因为在第七站下车,不会影响第七站的购票,首先看最小开始影响的下标,这里是3,因此minStartIndex = startIndex - 往前碰0到的最后一个0,由于这里购买7往后不会前面的影响库存,因此maxStratIndex = endIndex - 1,表示在[minStartIndex,maxStartIndex]这个区间开始的都会影响其他库存。如果是在3下标结束不会影响其他库存,但是4下标开始有影响,即minEndIndex= startIndex+1,同理maxEndIndex=endIndex+ 往后碰0到的最后一个0,表示在[minEndIndex,maxEndIndex]这个区间结束都会影响库存。

Integer startIndex = dailyTrainTicket.getStartIndex();
Integer endIndex = dailyTrainTicket.getEndIndex();
char[] chars = seatForUpdate.getSell().toCharArray();
Integer maxStartIndex = endIndex - 1;
Integer minEndIndex = startIndex + 1;
Integer minStartIndex = 0;
for (int i = startIndex - 1; i >= 0; i--) {char aChar = chars[i];if(aChar == '1') {minStartIndex = i + 1;break;}
}
LOG.info("影响出发站区间:"+minStartIndex+"-"+maxStartIndex);
Integer maxEndIndex = seatForUpdate.getSell().length();
for (int i = endIndex; i < seatForUpdate.getSell().length(); i++) {char aChar = chars[i];if(aChar == '1') {maxEndIndex = i;break;}
}
LOG.info("影响结束站区间:"+minEndIndex+"-"+maxEndIndex);
dailyTrainTicketMapperCust.updateCountBySell(dailyTrainSeat.getDate(),dailyTrainSeat.getTrainCode(),dailyTrainSeat.getSeatType(),minStartIndex,maxStartIndex,minEndIndex,maxEndIndex);

之后根据这四个值进行库存的更新。

  • 对乘客增加车票表,由于之前的购票表订单状态由可能为失败,而且信息不容易搜索,因此新建一张表,用来保存乘客购买车票成功的信息,由于每个会员经常查自己购买的车票,因此可以把会员id当作索引。由于生成的表在member模块,购票业务在business模块,当business模块购票成功后调用member模块的接口,把数据传入,这里使用的了feign。对于会员车票参数来说,在business模块需要调用它进行车票的构造,number模块需要调用它进行入库,因此可以放到common模块。
  • 要启用feign,需要在调用方buiness增加依赖,之后在启动类配置路径,表示哪个包是属于feign的,之后在相关的路径增加一个接口,路径配置是调用的哪个包下的接口。

之后在business模块就可以调用member的接口,为会员(乘客)增加一张票。之后可以为会员段增加我的车票,方便查看车票。但这里需要根据会员的id查看,只能够查看自己买的车票。

最后更新订单状态为成功,根据id更新 更新时间以及状态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/72725.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十二届蓝桥杯 异或数列

原题&#xff1a; https://www.acwing.com/problem/content/3424/ 题目大意&#xff1a; A、B两人的数初始值均为0&#xff0c;他们轮流从X数组中取数&#xff0c;可以将该数与自己的数或对方的数进行异或操作&#xff0c;A先手&#xff0c;当X中的数被取完的时候谁的数大谁…

高效编程指南:PyCharm与DeepSeek的完美结合

DeepSeek接入Pycharm 前几天DeepSeek的充值窗口又悄悄的开放了&#xff0c;这也就意味着我们又可以丝滑的使用DeepSeek的API进行各种辅助性工作了。本文我们来聊聊如何在代码编辑器中使用DeepSeek自动生成代码。 注&#xff1a;本文适用于所有的JetBrains开发工具&#xff0c…

项目中同时使用Redis(lettuce)和Redisson的报错

温馨提示&#xff1a;图片有点小&#xff0c;可以放大页面进行查看... 问题1&#xff1a;版本冲突 直接上图&#xff0c;这个错表示依赖版本不匹配问题&#xff0c;我本地SpringBoot用的是2.7&#xff0c;但是Redisson版本用的3.32.5。 我们通过点击 artifactId跟进去 发现它…

clickhouse安装路径

《ClickHouse安装路径指南》 大家好&#xff0c;今天我们将一起学习如何在电脑上找到和理解ClickHouse的安装路径。这将帮助学生、科研人员以及任何对数据库技术感兴趣的人更好地管理他们的数据查询工作。 ClickHouse是一款列式存储数据库管理系统&#xff08;DBMS&#xff09…

时序数据库 InfluxDB 3.0 版本性能实测报告:写入吞吐量提升效果验证

亮点总结&#xff1a; TSBS 测试表明&#xff0c;对于少于 100 万台设备的数据集&#xff0c;InfluxDB OSS 3.0 的数据写入速度实际上比 InfluxDB OSS 1.8 更慢。 对于 100 万台及以上设备的数据集&#xff0c;InfluxDB OSS 3.0 的数据写入性能才开始超过 InfluxDB OSS 1.8。…

AS32X601双核锁步MCU技术优势分析

AS32X601是国科安芯公司研制的一系列基于32位RISC-V指令集车规级MCU处理器芯片。主频高达180MHz&#xff0c;支持双核锁步架构&#xff0c;基于软错误防护技术加持&#xff0c;显著提高芯片安全性能。产品具有高安全、低失效、多IO、低成本、抗辐照等特点。 一、功能安全与可靠…

基于 LeNet 网络的 MNIST 数据集图像分类

1.LeNet的原始实验数据集MNIST 名称&#xff1a;MNIST手写数字数据集 数据类型&#xff1a;灰度图 &#xff08;一通道&#xff09; 图像大小&#xff1a;28*28 类别数&#xff1a;10类&#xff08;数字0-9&#xff09; 1.通过torchvision.datasets.MNIST下载并保存到本地…

电池综合测试仪:科技赋能,精准守护能源安全

在当今这个科技日新月异的时代&#xff0c;电池作为众多电子设备的心脏&#xff0c;其性能的稳定与高效直接关系到设备的运行质量与使用安全。随着电动汽车、可穿戴设备、储能系统等领域的快速发展&#xff0c;对电池性能的检测与评估提出了更高要求。在此背景下&#xff0c;电…

实战案例分享:Android WLAN Hal层移植(MTK+QCA6696)

本文将详细介绍基于MTK平台&#xff0c;适配高通&#xff08;Qualcomm&#xff09;QCA6696芯片的Android WLAN HAL层的移植过程&#xff0c;包括HIDL接口定义、Wi-Fi驱动移植以及wpa_supplicant适配过程&#xff0c;涵盖STA与AP模式的常见问题与解决方法。 1. HIDL接口简介 HID…

Greenplum6.19集群搭建

一&#xff0c;安装说明 1.1环境说明 1、首先确定部署的环境&#xff0c;确定下服务器的端口&#xff0c;一般默认是22的端口&#xff1b; 2、当前这份文档是服务器处于10022端口下部署的&#xff08;现场生产环境要求&#xff0c;22端口在生产环境存在安全隐患&#xff09;&…

原生android 打包.aar到uniapp使用

1.原生安卓里面引入uniapp官方提供的包文件&#xff1a; uniapp-v8-release.aar 2.提供uniapp调用的接口&#xff0c;新建类文件继承UniModule&#xff0c; package com.dermandar.panoramal;import com.scjt.lib.certlib;import io.dcloud.feature.uniapp.annotation.UniJSM…

基于Spring Boot的高校就业招聘系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

deepseek 本地部署

deepseek 本地部署 纯新手教学&#xff0c;手把手5分钟带你在本地部署一个私有的deepseek&#xff0c;再也不用受网络影响。流畅使用deepseek&#xff01;&#xff01;&#xff01; 如果不想看文章&#xff0c;指路&#xff1a;Deep seek R1本地部署 小白超详细教程 &#xff0…

HTML学习笔记(全)

基本结构 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title></title> </head> <body></body> </html> 基本标签元素 标题&#xff08;heading&#xff09; <h1>一级标题</h1> &…

腾讯云对象存储服务(COS)

腾讯云对象存储服务&#xff08;COS&#xff09; 安全、可扩展、低成本的云存储解决方案 腾讯云 对象存储服务&#xff08;COS&#xff0c;Cloud Object Storage&#xff09; 是一种高可靠、高性能、可扩展的云存储服务&#xff0c;专为海量非结构化数据&#xff08;如图片、…

数字孪生对于新基建的价值浅析,算是抛砖引玉。

数字孪生&#xff08;Digital Twin&#xff09;作为一项融合物理世界与数字世界的关键技术&#xff0c;在新基建中扮演着虚实协同、智能决策、全生命周期管理的核心角色&#xff0c;其价值贯穿于基础设施的设计、建设、运维到优化全流程。 一、核心价值&#xff1a;虚实映射与智…

从零开始实现大语言模型(十三):预训练大语言模型GPTModel

1. 前言 使用梯度下降算法通过下一个token预测任务预训练大语言模型GPTModel&#xff0c;前向传播流程每次会输入一个batch的长度均为context_len的训练样本&#xff0c;执行 batch_size context_len \text{batch\_size}\times\text{context\_len} batch_sizecontext_len次下…

QWQ大模型评测榜单

评测榜单说明 在数学推理基准AIME24上&#xff0c;QwQ-32B达到了79.5分&#xff0c;几乎与DeepSeek-R1-617B的79.8分持平&#xff0c;远超OpenAI o1-mini的63.6分&#xff0c;及相同尺寸的R1蒸馏模型。 在编程能力方面&#xff0c;QwQ-32B 在LiveCodeBench上获得了63.4分&…

超图(Hypergraph)

超图&#xff08;Hypergraph&#xff09;是图结构学习&#xff08;Graph Learning&#xff09;中的一种扩展形式&#xff0c;它比传统图&#xff08;Graph&#xff09;更具表达能力&#xff0c;适用于建模复杂的多元关系。 超图是一种由 超节点&#xff08;Hypernodes&#xff…

鸿蒙生态日日新,夸克、顺丰速运、驾校一点通等多款应用功能更新

3月5日鸿蒙生态日日新PLOG&#xff1a;吉事办、健康甘肃等政务服务App上架原生鸿蒙应用市场&#xff1b;夸克、顺丰速运、驾校一点通等多款应用功能更新。