仿12306项目选座购票业务逻辑
12306项目选座购票业务逻辑
文章目录
- 12306项目选座购票业务逻辑
- 项目分享
- 选座逻辑
- 购票逻辑
- 更新余票逻辑
- 用户选座功能
- 服务器售票功能
- 0. 业务数据校验
- 1. 保存确认订单表,状态初始化
- 2. 查出余票记录,需要得到真是的库存
- 3. 扣减余票数量,并判断余票是否足够
- 4. 选座开始
- 计算相对第一个座位的偏移值
- 4.1 一个车厢一个车厢获取座位数据
- 4.2 挑选符合条件的座位,如果这个车厢不满足,则进入下一个车厢(多个选座应在一个车厢)
- 5. 选中座位后事务处理
- 5.1 座位表修改售卖情况为sell
- 5.2 余票详情表修改余票
- 5.3 为会员增加购票记录
- 5.4 更新确认订单为成功
项目分享
近期在跟视频做SpringBoot微服务项目12306,可点击查看项目。可私信分享项目资料。
此篇记录12306中的重点业务逻辑流程,选座购票业务逻辑。
选座逻辑
分成选座和不选座
- 不选座,以购买一等座为例:遍历一等座车厢,每个车厢从1号座位开始找,未被购买的,就选中它
- 选座,以购买两张一等座AB为例:遍历一等座车厢,每个车厢从1号座位开始找A列座位,未被购买的,就预选中它;再挑它旁边的B,如果也未被购买,则最终选中这两个座位,如果B已被购买,则回到第一步,继续找未被购买的A座。
从第二个座位开始,需要计算和第一个座位的偏移值,可以减少循环,提高选座效率
选择C1,D2,偏移值为[0, 5]
选择B1,D2,偏移值为[0, 7]
选择A1,B1,C1,则偏移值为[0,1,2]
由此可见,偏移值都是针对第一个选座位置的偏移量,并且第一个永远是0
购票逻辑
计算某座位在区间内是否可卖
例:
有站点ABCDEF,则设置sell=10001,1表示座位在区间已卖出,0表示未卖出
本次购买区间站1~4,则区间已售000,全部是0,表示这个区间可买;只要有1,就表示区间内已售过票
选中座位后,应完成:
选中后,要计算购票后的sell,比如原来是10001,本次购买区间站1~4
方案:构造本次购票造成的售卖信息01110,和原sell10001按位或,最终得到11111
以上转化为字符串的截取和位运算
更新余票逻辑
重点逻辑
购买一张票将会影响的库存:本次选座之前没卖过票的,和本次购买的区间有交集的区间
假设10个站,本次买4~7站
原售:001000001
购买:000011100
新售:001011101
影响:XXX11111X
Integer startIndex = 4;
Integer endIndex = 7;
Integer minStartIndex = startIndex - (往前碰到的最后一个0);
Integer maxStartIndex = endIndex - 1;
Integer minEndIndex = startIndex + 1;
Integer maxEndIndex = endIndex + (往后碰到的最后一个0);
因此要更新的余票为:
minStartIndex <= start_index <= maxStartIndex &&
minEndIndex <= end_index <= maxEndIndex
用户选座功能
前端传递数据:
{passengerId: 123,passengerType: "1",passengerName: "张三",passengerIdCard: "12323132132",seatTypeCode: "2",seat: "C1"}
用户分为选座和不选座,不选座则seat
字段为空,由后端自动分配座位;选座则seat字段为所选座位信息,一等座,则seatTypeCode
为1,二等座为2,自动提供两排备选座位。
C1表示选择第1排的C号座位。
服务器售票功能
0. 业务数据校验
主要目的是防止恶意请求,直接绕过前端访问后端接口
车次是否存在,余票是否存在,车次是否在有效期内,tickets条数>0,同乘客同车次是否已买过
1. 保存确认订单表,状态初始化
2. 查出余票记录,需要得到真是的库存
DailyTrainTicket dailyTrainTicket = dailyTrainTicketService.selectByUnique(date, trainCode, start, end);
3. 扣减余票数量,并判断余票是否足够
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);}}}}
4. 选座开始
计算相对第一个座位的偏移值
eg:
一等座选择C1,D2,则偏移值为[0,5]
二等座选择A1,B1,C1,则偏移值为[0,1,2]
根据订单中订票座位类型,构造出和前端两排选座一样的列表,用于作参考的座位列表,例如:
referSeatList = {A1, C1, D1, F1, A2, C2, D2, F2}
referSeatList = {A1, B1, C1, D1, F1, A2, B2, C2, D2, F2}
接着再根据订单中的座位,计算全部座位的绝对偏移值,即在referSeatList 数组中的下标位置,最后每一个座位减去首个座位的下标位置,可以得到偏移值数组。
// 计算相对第一个座位的偏移值// 比如选择的是C1,D2,则偏移值是:[0,5]// 比如选择的是A1,B1,C1,则偏移值是:[0,1,2]ConfirmOrderTicketReq ticketReq0 = tickets.get(0);if (StrUtil.isNotBlank(ticketReq0.getSeat())) {LOG.info("本次购票有选座");// 查出本次选座的座位类型都有哪些列,用于计算所选座位与第一个座位的偏离值List<SeatColEnum> colEnumList = SeatColEnum.getColsByType(ticketReq0.getSeatTypeCode());LOG.info("本次选座的座位类型包含的列:{}", colEnumList);// 组成和前端两排选座一样的列表,用于作参照的座位列表,例:referSeatList = {A1, C1, D1, F1, A2, C2, D2, F2}List<String> referSeatList = new ArrayList<>();for (int i = 1; i <= 2; i++) {for (SeatColEnum seatColEnum : colEnumList) {referSeatList.add(seatColEnum.getCode() + i);}}LOG.info("用于作参照的两排座位:{}", referSeatList);List<Integer> offsetList = new ArrayList<>();// 绝对偏移值,即:在参照座位列表中的位置List<Integer> aboluteOffsetList = new ArrayList<>();for (ConfirmOrderTicketReq ticketReq : tickets) {int index = referSeatList.indexOf(ticketReq.getSeat());aboluteOffsetList.add(index);}LOG.info("计算得到所有座位的绝对偏移值:{}", aboluteOffsetList);for (Integer index : aboluteOffsetList) {int offset = index - aboluteOffsetList.get(0);offsetList.add(offset);}LOG.info("计算得到所有座位的相对第一个座位的偏移值:{}", offsetList);getSeat(finalSeatList,date,trainCode,ticketReq0.getSeatTypeCode(),ticketReq0.getSeat().split("")[0], // 从A1得到AoffsetList,dailyTrainTicket.getStartIndex(),dailyTrainTicket.getEndIndex());}
打印结果:
4.1 一个车厢一个车厢获取座位数据
根据对应的车座类型,循环查找车次的对应车厢,如一等组车厢
再在每个车厢中,寻找符合偏移值条件的车座
4.2 挑选符合条件的座位,如果这个车厢不满足,则进入下一个车厢(多个选座应在一个车厢)
对于每一个未售卖且列号等于初始座位的座位,选中其。
接着判断各偏移量座位,只有全部偏移量座位都可选中,才能选中这些座位,否则要退出重选。
同时注意偏移量座位的座位号不能大于车厢座位数量,因为要保证在同一车厢。
/*** 挑座位,如果有选座,则一次性挑完,如果无选座,则一个一个挑* @param date* @param trainCode* @param seatType* @param column* @param offsetList*/private void getSeat(List<DailyTrainSeat> finalSeatList, Date date, String trainCode, String seatType, String column, List<Integer> offsetList, Integer startIndex, Integer endIndex) {List<DailyTrainSeat> getSeatList = new ArrayList<>();List<DailyTrainCarriage> carriageList = dailyTrainCarriageService.selectBySeatType(date, trainCode, seatType);LOG.info("共查出{}个符合条件的车厢", carriageList.size());// 一个车箱一个车箱的获取座位数据for (DailyTrainCarriage dailyTrainCarriage : carriageList) {LOG.info("开始从车厢{}选座", dailyTrainCarriage.getIndex());getSeatList = new ArrayList<>();List<DailyTrainSeat> seatList = dailyTrainSeatService.selectByCarriage(date, trainCode, dailyTrainCarriage.getIndex());LOG.info("车厢{}的座位数:{}", dailyTrainCarriage.getIndex(), seatList.size());for (int i = 0; i < seatList.size(); i++) {DailyTrainSeat dailyTrainSeat = seatList.get(i);Integer seatIndex = dailyTrainSeat.getCarriageSeatIndex();String col = dailyTrainSeat.getCol();// 判断当前座位不能被选中过boolean alreadyChooseFlag = false;for (DailyTrainSeat finalSeat : finalSeatList){if (finalSeat.getId().equals(dailyTrainSeat.getId())) {alreadyChooseFlag = true;break;}}if (alreadyChooseFlag) {LOG.info("座位{}被选中过,不能重复选中,继续判断下一个座位", seatIndex);continue;}// 判断column,有值的话要比对列号if (StrUtil.isBlank(column)) {LOG.info("无选座");} else {if (!column.equals(col)) {LOG.info("座位{}列值不对,继续判断下一个座位,当前列值:{},目标列值:{}", seatIndex, col, column);continue;}}boolean isChoose = calSell(dailyTrainSeat, startIndex, endIndex);if (isChoose) {LOG.info("选中座位");getSeatList.add(dailyTrainSeat);} else {continue;}// 根据offset选剩下的座位boolean isGetAllOffsetSeat = true;if (CollUtil.isNotEmpty(offsetList)) {LOG.info("有偏移值:{},校验偏移的座位是否可选", offsetList);// 从索引1开始,索引0就是当前已选中的票for (int j = 1; j < offsetList.size(); j++) {Integer offset = offsetList.get(j);// 座位在库的索引是从1开始// int nextIndex = seatIndex + offset - 1;int nextIndex = i + offset;// 有选座时,一定是在同一个车箱if (nextIndex >= seatList.size()) {LOG.info("座位{}不可选,偏移后的索引超出了这个车箱的座位数", nextIndex);isGetAllOffsetSeat = false;break;}DailyTrainSeat nextDailyTrainSeat = seatList.get(nextIndex);boolean isChooseNext = calSell(nextDailyTrainSeat, startIndex, endIndex);if (isChooseNext) {LOG.info("座位{}被选中", nextDailyTrainSeat.getCarriageSeatIndex());getSeatList.add(nextDailyTrainSeat);} else {LOG.info("座位{}不可选", nextDailyTrainSeat.getCarriageSeatIndex());isGetAllOffsetSeat = false;break;}}}if (!isGetAllOffsetSeat) {getSeatList = new ArrayList<>();continue;}// 保存选好的座位finalSeatList.addAll(getSeatList);return;}}}
计算某座位在区间内是否可卖:
/*** 计算某座位在区间内是否可卖* 例:sell=10001,本次购买区间站1~4,则区间已售000* 全部是0,表示这个区间可买;只要有1,就表示区间内已售过票** 选中后,要计算购票后的sell,比如原来是10001,本次购买区间站1~4* 方案:构造本次购票造成的售卖信息01110,和原sell 10001按位与,最终得到11111*/private boolean calSell(DailyTrainSeat dailyTrainSeat, Integer startIndex, Integer endIndex) {// 00001, 00000String sell = dailyTrainSeat.getSell();// 000, 000String 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);// 111, 111String curSell = sellPart.replace('0', '1');// 0111, 0111curSell = StrUtil.fillBefore(curSell, '0', endIndex);// 01110, 01110curSell = StrUtil.fillAfter(curSell, '0', sell.length());// 当前区间售票信息curSell 01110与库里的已售信息sell 00001按位与,即可得到该座位卖出此票后的售票详情// 15(01111), 14(01110 = 01110|00000)int newSellInt = NumberUtil.binaryToInt(curSell) | NumberUtil.binaryToInt(sell);// 1111, 1110String newSell = NumberUtil.getBinaryStr(newSellInt);// 01111, 01110newSell = StrUtil.fillBefore(newSell, '0', sell.length());LOG.info("座位{}被选中,原售票信息:{},车站区间:{}~{},即:{},最终售票信息:{}", dailyTrainSeat.getCarriageSeatIndex(), sell, startIndex, endIndex, curSell, newSell);dailyTrainSeat.setSell(newSell);return true;}}
5. 选中座位后事务处理
5.1 座位表修改售卖情况为sell
5.2 余票详情表修改余票
5.3 为会员增加购票记录
5.4 更新确认订单为成功
/*** 选中座位后事务处理:* 座位表修改售卖情况sell;* 余票详情表修改余票;* 为会员增加购票记录* 更新确认订单为成功*/// @Transactional// @GlobalTransactionalpublic void afterDoConfirm(DailyTrainTicket dailyTrainTicket, List<DailyTrainSeat> finalSeatList, List<ConfirmOrderTicketReq> tickets, ConfirmOrder confirmOrder) throws Exception {// LOG.info("se ata全局事务ID: {}", RootContext.getXID());for (int j = 0; j < finalSeatList.size(); j++) {DailyTrainSeat dailyTrainSeat = finalSeatList.get(j);DailyTrainSeat seatForUpdate = new DailyTrainSeat();seatForUpdate.setId(dailyTrainSeat.getId());seatForUpdate.setSell(dailyTrainSeat.getSell());.setUpdateTime(new Date());dailyTrainSeatMapper.updateByPrimaryKeySelective(seatForUpdate);// 计算这个站卖出去后,影响了哪些站的余票库存// 参照2-3节 如何保证不超卖、不少卖,还要能承受极高的并发 10:30左右// 影响的库存:本次选座之前没卖过票的,和本次购买的区间有交集的区间// 假设10个站,本次买4~7站// 原售:001000001// 购买:000011100// 新售:001011101// 影响:XXX11111X// Integer startIndex = 4;// Integer endIndex = 7;// Integer minStartIndex = startIndex - 往前碰到的最后一个0;// Integer maxStartIndex = endIndex - 1;// Integer minEndIndex = startIndex + 1;// Integer maxEndIndex = endIndex + 往后碰到的最后一个0;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);// 调用会员服务接口,为会员增加一张车票MemberTicketReq memberTicketReq = new MemberTicketReq();memberTicketReq.setMemberId(confirmOrder.getMemberId());memberTicketReq.setPassengerId(tickets.get(j).getPassengerId());memberTicketReq.setPassengerName(tickets.get(j).getPassengerName());memberTicketReq.setTrainDate(dailyTrainTicket.getDate());memberTicketReq.setTrainCode(dailyTrainTicket.getTrainCode());memberTicketReq.setCarriageIndex(dailyTrainSeat.getCarriageIndex());memberTicketReq.setSeatRow(dailyTrainSeat.getRow());memberTicketReq.setSeatCol(dailyTrainSeat.getCol());memberTicketReq.setStartStation(dailyTrainTicket.getStart());memberTicketReq.setStartTime(dailyTrainTicket.getStartTime());memberTicketReq.setEndStation(dailyTrainTicket.getEnd());memberTicketReq.setEndTime(dailyTrainTicket.getEndTime());memberTicketReq.setSeatType(dailyTrainSeat.getSeatType());CommonResp<Object> commonResp = memberFeign.save(memberTicketReq);LOG.info("调用member接口,返回:{}", commonResp);// 更新订单状态为成功ConfirmOrder confirmOrderForUpdate = new ConfirmOrder();confirmOrderForUpdate.setId(confirmOrder.getId());confirmOrderForUpdate.setUpdateTime(new Date());confirmOrderForUpdate.setStatus(ConfirmOrderStatusEnum.SUCCESS.getCode());confirmOrderMapper.updateByPrimaryKeySelective(confirmOrderForUpdate);}}
同时利用feign调用member模块的save接口,保存购票信息
@FeignClient(name = "member", url = "http://127.0.0.1:8001")
public interface MemberFeign {@GetMapping("/member/feign/ticket/save")CommonResp<Object> save(@RequestBody MemberTicketReq req);}
member模块中:
@RestController
@RequestMapping("/feign/ticket")
public class FeignTicketController {@Autowiredprivate TicketService ticketService;@PostMapping("/save")public CommonResp<Object> save(@Valid @RequestBody MemberTicketReq req) throws Exception {ticketService.save(req);return new CommonResp<>();}}
相关文章:
仿12306项目选座购票业务逻辑
12306项目选座购票业务逻辑 文章目录 12306项目选座购票业务逻辑项目分享选座逻辑购票逻辑更新余票逻辑用户选座功能服务器售票功能0. 业务数据校验1. 保存确认订单表,状态初始化2. 查出余票记录,需要得到真是的库存3. 扣减余票数量,并判断余…...
2024年面对不确定性
24年处在了十字路口,面对工作、家庭、生活的责任,一切变得不确定了,量子力学给了我们新的认识世界的角度,不确定性才是这个世界的底色,我们怎么选择? 不停的思考 霍金在大设计书中给出了深刻的哲学思想&a…...
Nginx的负载均衡
一、概述 Nginx负载均衡是一种通过将客户端请求分发到多个后端服务器的技术,旨在提高系统的吞吐量、可用性和容错性。 二、Nginx负载均衡工作原理 Nginx作为反向代理服务器,接收客户端的请求,并根据配置的负载均衡算法将请求转发到后端服务…...
vue3组件el-table报错
传给table标签的data不是数组就会报错, 摁着商品管理代码找了半天也没发现哪里错了,而且关闭报错表格数据能正常显示, 。。。 最后发现我还有个订单管理页面,这里面的data初始化成ref( )了,把这个组件注释掉…...
天聚地合:引领API数据流通服务,助力数字经济发展
天聚地合:引领API数据流通服务,助力数字经济发展 爱企猫01月24日消息:天聚地合(苏州)科技股份有限公司,成立于2010年,总部位于苏州,是一家综合性API数据流通服务商。公司旗下品牌‘聚合数据’已开发超过790个API,服务百万企业级客…...
AIGC的企业级解决方案架构及成本效益分析
AIGC的企业级解决方案架构及成本效益分析 一,企业级解决方案架构 AIGC(人工智能生成内容)的企业级解决方案架构是一个多层次、多维度的复杂系统,旨在帮助企业实现智能化转型和业务创新。以下是总结的企业级AIGC解决方案架构的主要组成部分: 1. 技术架构 企业级AIGC解决方…...
企业知识管理平台的对比分析与优化策略探讨
内容概要 随着信息技术的飞速发展,企业对知识管理的重视程度日益提高。知识管理不仅有助于知识的积累和传承,更在于提升企业整体运营效率和创新能力。为此,众多企业纷纷引入知识管理平台,以便更好地管理和利用其内部知识资源。 …...
分布式数据库与集中式数据库
分布式数据库 分布式数据库是在集中式数据库系统的基础上发展起来的,由多个相互连接并分布在不同物理位置的数据库组成。因此,可以独立于其他物理位置来管理存储在各个物理位置上的数据。因此,在不同物理位置的数据库之间的通信是由计算机网…...
STM32 OLED屏配置
1.OLED简介 OLED(Organic Light Emitting Diode):有机发光二极管 OLED显示屏:性能优异的新型显示屏,具有功耗低、相应速度快、宽视角、轻薄柔韧等特点 0.96寸OLED模块:小巧玲珑、占用接口少、简单易用&a…...
Spring Boot - 数据库集成04 - 集成Redis
Spring boot集成Redis 文章目录 Spring boot集成Redis一:redis基本集成1:RedisTemplate Jedis1.1:RedisTemplate1.2:实现案例1.2.1:依赖引入和属性配置1.2.2:redisConfig配置1.2.3:基础使用 2&…...
Day47:遍历元组
元组是 Python 中的一种有序集合类型,类似于列表,但与列表不同的是,元组是不可变的。这使得元组的元素一旦创建就不能更改。尽管元组是不可变的,我们仍然可以使用循环结构来遍历元组中的元素。 今天我们将学习如何遍历元组中的元…...
【PoCL】项目源码编译
PoCL 项目链接 本博文主要介绍了源码编译llvm和PoCL的过程 目录 0. 个人简介 && 授权须知1. 项目介绍2. 项目依赖3. 源码编译3.1 编译 LLVM 工程3.2 编译PoCL 工程 0. 个人简介 && 授权须知 📋 个人简介 💖 作者简介:大家好&…...
【数据分享】1929-2024年全球站点的逐月平均能见度(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标!说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2024年全球气象站点…...
免费GPU算力,不花钱部署DeepSeek-R1
在人工智能和大模型技术飞速发展的今天,越来越多的开发者和研究者希望能够亲自体验和微调大模型,以便更好地理解和应用这些先进的技术。然而,高昂的GPU算力成本往往成为了阻碍大家探索的瓶颈。幸运的是,腾讯云Cloud Studio提供了免…...
C语言的灵魂——指针(1)
指针是C语言的灵魂,有了指针C语言才能完成一些复杂的程序;没了指针就相当于C语言最精髓的部分被去掉了,可见指针是多么重要。废话不多讲我们直接开始。 指针 一,内存和地址二,编址三,指针变量和地址1&#…...
2000-2020年各省第三产业增加值占GDP比重数据
2000-2020年各省第三产业增加值占GDP比重数据 1、时间:2000-2020年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区名称、年份、第三产业增加值占GDP比重 4、范围:31省 5、指标解释:第三产业增加值占G…...
Linux MySQL离线安装
一、准备工作 1. 下载MySQL安装包 访问MySQL官方网站,选择适合您Linux系统的MySQL版本进行下载。通常推荐下载Generic Linux (glibc 2.12)版本的.tar.gz压缩包,例如mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz。将下载好的安装包拷贝到Linux服务器的某…...
ESP32服务器和PC客户端的Wi-Fi通信
ESP32客户端-服务器Wi-Fi通信 本指南将向您展示如何设置ESP32板作为服务端,PC作为客户端,通过HTTP通信,以通过Wi-Fi(无需路由器或互联网连接)交换数据。简而言之,您将学习如何使用HTTP请求将一个板的数据发…...
Linux文件权限
目录 1.Linux权限管理 1.1文件访问者的分类 1.2文件的类型和访问权限 1)文件类型 2)文件访问权限 3)chmod指令 4)chown指令 编辑 5)chgrp命令 1.3目录权限 1.4粘滞位 1.5umask(权限掩码&…...
UE骨骼模拟物理
此功能可以制作仿动物派对上半身模拟物理效果 Set all bodies below simulate physics 骨骼名称设置为 spine_01 这样上半身所有骨骼都会模拟物理 效果演示...
salesforce中如何获取一个profile的18位id
在 Salesforce 中,要获取一个 Profile 的 18 位 ID,可以通过以下几种方式实现: 方法 1:通过 Developer Console 登录 Salesforce。 点击右上角的 头像 或 设置齿轮,选择 “开发者控制台”(Developer Conso…...
为什么机器学习中梯度下降是减去斜率,而不是按照其数学意义减去斜率的倒数
做个简单假设,Loss函数的某一个参数的函数曲线是二次方程,其导数函数为 r 2 ∗ w r 2*w r2∗w 按照斜率意义来看,要减去斜率倒数 降低LOSS需要将w1更新为w2,所以更新公式为 w w − Δ L Δ w w w - \frac{\Delta L}{\Delta w…...
Vue 3 30天精进之旅:Day 04 - 计算属性与侦听器
引言 在前几天的学习中,我们已经了解了Vue实例的基本概念和使用方法。今天,我们将深入探讨两个重要的特性:计算属性(computed properties)和侦听器(watchers)。这两个特性使得我们能够更高效地…...
UE学习日志#11GAS--ASC源码简要分析9 AbilitySystemGlobals分析2 初始化相关
1 static UAbilitySystemGlobals& Get() 保证了是单例,IGameplayAbilitiesModule继承了IModuleInterface /** Gets the single instance of the globals object, will create it as necessary */static UAbilitySystemGlobals& Get(){return *IGameplayAbi…...
SQL在DBA手里-改写篇
背景 最近运营需要做月报汇总交易情况,之前一直是他们手工出的数据,他们想做成月初自动发送邮件,从而减轻他们的工作量。于是他们提供SQL我们在邮件服务器配置做定时发送任务。 表介绍(表及字段已做脱敏处理) trans…...
SQL Server查询计划操作符(7.3)——查询计划相关操作符(5)
7.3. 查询计划相关操作符 38)Flow Distinct:该操作符扫描其输入并对其去重。该操作符从其输入得到每行数据时即将其返回(除非其为重复数据行,此时,该数据行会被抛弃),而Distinct操作符在产生任何输出前将消费所有输入。该操作符为逻辑操作符。该操作符具体如图7.2-38中…...
Autogen_core:Agent and Agent Runtime
目录 1. 代码2. 代码解释第一部分:定义消息类型和代理第二部分:定义助手代理第三部分:注册和运行代理第四部分:发送和停止消息处理总结 3. 类似的例子 1. 代码 from dataclasses import dataclassfrom autogen_core import Agent…...
vue(33) : 安装组件出错解决
1. request to https://registry.npm.taobao.org/semver/download/semver-6.1.1.tgz?cache0&other_urlshttps%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.1.1.tgz failed, reason: certificate has expired 这个错误提示表明你在尝试从https://reg…...
论文阅读(五):乳腺癌中的高斯图模型和扩展网络推理
1.论文链接:Network Inference in Breast Cancer with Gaussian Graphical Models and Extensions 摘要: 具有高相关性的聚类基因将具有接近表达谱的基因分组,确定共表达基因的聚类。然而,这种相关性并没有提供任何关于基因间信息…...
定时器按键tim_key模版
低优先级放在高优先级内势必是程序卡死 把高优先级放到低优先级内,会使程序卡死 可修改 Debuger调试方法 Pwm rcc #include "my_main.h" uint8_t led_sta0x10; char text[30]; void LED_Disp(uint8_t dsLED) {HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPI…...
Effective Objective-C 2.0 读书笔记—— objc_msgSend
Effective Objective-C 2.0 读书笔记—— objc_msgSend 文章目录 Effective Objective-C 2.0 读书笔记—— objc_msgSend引入——静态绑定和动态绑定OC之中动态绑定的实现方法签名方法列表 其他方法objc_msgSend_stretobjc_msgSend_fpretobjc_msgSendSuper 尾调用优化总结参考文…...
机器学习 vs 深度学习
目录 一、机器学习 1、实现原理 2、实施方法 二、深度学习 1、与机器学习的联系与区别 2、神经网络的历史发展 3、神经网络的基本概念 一、机器学习 1、实现原理 训练(归纳)和预测(演绎) 归纳: 从具体案例中抽象一般规律…...
Vue中的动态组件是什么?如何动态切换组件?
什么是动态组件? 动态组件是 Vue.js 中的一项强大功能,它允许开发者根据程序的状态或用户的操作,动态地切换组件。动态组件的优势在于,开发者可以根据具体需求灵活地渲染不同的组件,从而提高应用的通用性和可维护性。…...
Spring IoC DI
目录 一. IoC & DI 入门 1. 重谈Spring 2. 容器 3. IoC ① 传统程序开发 ② IoC 程序开发 ③ IoC 的优势 4. DI 3. IoC & DI 使用 二. IoC & DI 详解 1. Bean的存储 2. Bean的重命名 3. 扫描路径 三. DI 详解 1. 属性注入 2. 构造方法注入 3. Se…...
【Linux】线程、线程控制、地址空间布局
⭐️个人主页:小羊 ⭐️所属专栏:Linux 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 1、Linux线程1.1 线程的优缺点1.2 线程异常和用途1.3 线程等待1.3 线程终止1.4 线程分离1.5 线程ID和地址空间布局1.6 线程栈 1、…...
【记录】日常|从零散记录到博客之星Top300的成长之路
文章目录 shandianchengzi 2024 年度盘点概述写作风格简介2024年的创作内容总结 shandianchengzi 2024 年度盘点 概述 2024年及2025年至今我创作了786即84篇文章,加上这篇就是85篇。 很荣幸这次居然能够入选博客之星Top300,这个排名在我之前的所有年份…...
网盘资源查找工具---AI功能
01 软件介绍 这是一款融入了ai技术的网盘搜索神器,可以让你更快,更精准的找到自己需要的文件,不管你是找影视,音乐,还是找软件或者学习资料都可以,欢迎前来使用。 02 功能展示 该软件非常简洁ÿ…...
LWJGL轻量级Java游戏库
LWJGL - Lightweight Java Game Library 基本介绍 LWJGL是一个Java库,它支持跨平台访问流行的本地api,这些api在图形(OpenGL, Vulkan)、音频(OpenAL)和并行计算(OpenCL)应用程序的…...
AI智能日志分析系统
文章目录 1.combinations-intelligent-analysis-starter1.目录结构2.pom.xml3.自动配置1.IntelligentAnalysisAutoConfiguration.java2.spring.factories 2.combinations-intelligent-analysis-starter-demo1.目录结构2.pom.xml3.application.yml4.IntelligentAnalysisApplicat…...
详解三种常用标准化:Batch Norm、Layer Norm和RMSNorm
在深度学习中,标准化技术是提升模型训练速度、稳定性和性能的重要手段。本文将详细介绍三种常用的标准化方法:Batch Normalization(批量标准化)、Layer Normalization(层标准化)和 RMS Normalization&#…...
数据压缩算法-差分编码(Delta Encoding)
Delta Encoding(差分编码)是一种数据压缩技术,其核心思想是存储数据之间的差异(delta),而不是原始数据本身。这种方法特别适用于数据序列中相邻元素之间变化较小的情况,可以显著减少存储空间或传…...
Nginx中部署多个前端项目
1,准备前端项目 tlias系统的前端资源 外卖项目的前端资源 2,nginx里面的html文件夹中新建,tlias和sky两个文件夹。 切记这是在nginx/html下创建的 mkdir sky mkdir tlias 把tlias和sky的资源都放到对应的文件夹中 3,编辑配置ngi…...
Level DB --- TableBuilder
TableBuilder是Level DB里面重要的类和模块,它描述了数据如何序列化到文件中,以及数据里面的格式逻辑。它里面包含了之前介绍的多个模块和类。 data block、filter block和index block block格式,之前已经介绍过Level DB --- BlockBuilder-…...
JVM堆空间
一、堆空间的核心概述 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。Java堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间。 堆内存的大小是可以调节的。堆可以处于物理上不连续的内存空间中ÿ…...
【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.13 降维打击:扁平化操作的六种武器
1.13 降维打击:扁平化操作的六种武器 目录 #mermaid-svg-bbLxDryjxBbXe3tu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bbLxDryjxBbXe3tu .error-icon{fill:#552222;}#mermaid-svg-bbLxDryjxBbXe3tu…...
Doris Schema Change 常见问题分析
1. 什么是 Schema Change Schema Change 是在数据库中修改表结构的一种操作,例如添加列、删除列、更改列类型等。 ⚠️Schema Change 限制⚠️ 一张表在同一时间只能有一个 Schema Change 作业在运行。分区列和分桶列不能修改。如果聚合表中有 REPLACE 方式聚合的…...
数据结构之堆排序
文章目录 堆排序版本一图文理解 版本二向下调整建堆向上调整建堆 排升/降序升序 堆排序 版本一 基于已有数组建堆取堆顶元素并删除堆顶元素重新建大根堆,完成排序版本。 图文理解 版本二 前提:必须提供有现成的数据结构堆 数组建堆,首尾…...
实现桌面动态壁纸(三)—— 视频播放的策略
关于动态壁纸这边,其实已经不需要再谈什么东西了,现有的各种文章都在介绍相关的技术。可以说现如今要去制作一个桌面动态壁纸应该不是什么难事。我考虑了很久,决定还是开一篇单独谈谈。可能我说的也不全部正确,您有什么建议随时可…...
C语言程序设计十大排序—希尔排序
文章目录 1.概念✅2.希尔排序🎈3.代码实现✅3.1 直接写✨3.2 函数✨ 4.总结✅ 1.概念✅ 排序是数据处理的基本操作之一,每次算法竞赛都很多题目用到排序。排序算法是计算机科学中基础且常用的算法,排序后的数据更易于处理和查找。在计算机发展…...
2023年版本IDEA复制项目并修改端口号和运行内存
2023年版本IDEA复制项目并修改端口号和运行内存 1 在idea中打开server面板,在server面板中选择需要复制的项目右键,点击弹出来的”复制配置…(Edit Configuration…)“。如果idea上没有server面板或者有server面板但没有springbo…...