新闻详情

新闻详情

首页 / 资讯中心 / 详情

苍穹外卖用户下单:从表结构设计到防重复提交全解析

发布时间:2026/10/1 3:37:14来源:尧图网络
苍穹外卖用户下单:从表结构设计到防重复提交全解析
从下单这个功能开始苍穹外卖项目算是真正进入了核心业务区。day08这一节看似只是一个“用户下单”的接口但牵扯到订单表结构设计、购物车数据读取、地址薄校验、菜品口味快照、库存扣减、事务回滚、甚至高并发下的重复下单问题每一步都有坑。这篇文章我根据自己做苍穹外卖的完整过程把用户下单这个模块的拆解思路、代码实现、踩坑记录都梳理出来希望能给正在做到这个环节的同学一点参考。1. 内容整体设计与思路拆解1.1 下单功能在整个苍穹外卖项目中的定位苍穹外卖是一个典型的移动端外卖C端项目核心链路无非是“用户浏览菜品 → 加入购物车 → 确认订单 → 提交订单 → 支付 → 商家接单 → 配送给用户”。day08要完成的“用户下单”是整个交易闭环中最关键的一环。前面七天都在做员工端、分类、菜品、套餐的管理本质上是给商家用的后台功能数据操作多是单表CRUD业务逻辑相对直白。到了用户下单这里才开始真正把多个业务流程串起来用户要先有收货地址购物车里要有菜品下单时要校验菜品是否还在售卖、库存是否充足订单生成后还要清空购物车并且在极端情况下要防止同一用户疯狂点击导致生成重复订单。从课程进度安排来看day08的内容安排是合理的。它没有一上来就直接怼支付而是先让用户把订单提交逻辑走通生成一条待支付订单这样后续接支付、接订单状态流转时才有一个稳固的数据支撑。1.2 为什么下单流程需要这样的顺序设计很多同学在做这个功能时容易把思路局限在“插入一条订单记录”上但实际上一个完整的下单接口在苍穹外卖项目里至少要跑通这么几步接收前端传来的参数地址簿id、购物车数据或者直接从购物车表查、备注、预计送达时间等查询用户当前购物车列表校验购物车是否为空遍历购物车中的每个菜品/套餐查询最新的菜品状态、价格重新计算订单金额而不是直接信任前端传过来的总价构造Order实体插入订单主表获取订单id遍历购物车明细构造OrderDetail列表批量插入订单明细表清空当前用户的购物车返回订单id给前端用于后续支付。为什么要这样设计核心原因只有一个不能信任前端数据。外卖平台的菜品价格、库存是实时变化的用户在购物车停留的几分钟内菜品可能已经下架或者涨价了。如果直接把前端传过来的总价写入订单相当于把钱袋子交给用户来填这在真实业务里是不可接受的。所以后端必须要主动重新查一遍购物车数据、重新计算金额这是做交易类功能的基本素养。另外先插入订单主表获取自增id再插入明细表是因为明细表需要外键关联订单主表的id。虽然也可以用业务订单号来关联但用自增主键更简单直接也是大多数项目采用的方式。1.3 基于常见实践的方案选型补充如果你是在跟着苍穹外卖的课程敲代码会发现它用的是Spring Boot MyBatis MySQL Redis这套经典组合。下单这部分没怎么用Redis但是在优化重复下单问题时可以用Redis做分布式锁或者幂等控制我在后文会专门讲。事务管理用的是Spring的Transactional注解简单够用但对于交易核心链路我建议你理解一下事务的传播行为和回滚机制避免出现“订单主表插入了明细表插入失败数据不一致”这种尴尬情况。2. 核心细节解析与实操要点2.1 订单表与订单明细表的设计思路先看两张表的字段设计这是理解下单代码的前提。苍穹外卖的订单表orders主要字段有字段名类型说明idbigint主键自增numbervarchar订单号statusint订单状态1待付款 2待接单 3已接单 4派送中 5已完成 6已取消user_idbigint下单用户idaddress_book_idbigint地址簿idorder_timedatetime下单时间checkout_timedatetime结账时间pay_methodint支付方式1微信支付 2支付宝amountdecimal实收金额remarkvarchar备注phonevarchar联系电话addressvarchar收货地址user_namevarchar用户名consigneevarchar收货人cancel_reasonvarchar取消原因delivery_statusint配送状态estimated_delivery_timedatetime预计送达时间pack_amountdecimal打包费tableware_numberint餐具数量tableware_statusint餐具状态这里我特别想提醒的一点是订单表里冗余了phone、address、consignee、user_name这些字段。为什么不在下单时直接关联地址簿表和用户表而要把这些信息复制一份到订单表因为订单是交易快照。用户下单后地址簿可能被修改用户名可能被修改甚至地址被删除但订单里记录的必须是下单那一刻的收货信息后续商家配送、售后对账都要以订单里的快照为准。在实际开发中这种“冗余字段”不是设计缺陷而是为了防止历史数据被后续变更污染。订单明细表order_detail字段相对简单字段名类型说明idbigint主键namevarchar菜品名称imagevarchar菜品图片order_idbigint订单iddish_idbigint菜品idsetmeal_idbigint套餐iddish_flavorvarchar菜品口味numberint数量amountdecimal单价同样这里的name、image、dish_flavor也是快照。菜品名称、价格、图片都是可以改的但用户下单时看到的是什么订单明细里就必须记录什么。比如用户点了一份“鱼香肉丝”口味选“微辣”几分钟后商家把“鱼香肉丝”改名为“鱼香肉丝Plus”价格也涨了但订单里依然要显示鱼香肉丝、微辣、下单时的价格。这就是明细表不直接关联dish表的根本原因。2.2 购物车表与订单实体之间的数据来源下单的数据来源是购物车表shopping_cart它的设计也很典型id、name、image、user_id、dish_id、setmeal_id、dish_flavor、number、amount、create_time注意这里同样冗余了name、image、amount。购物车里存这些字段是为了在购物车列表页直接展示不用每次都去联表查菜品表。但下单时不能直接用购物车里的amount作为订单金额因为购物车里的价格可能已经过期了。也就是说购物车表只是“用户预选清单”真正下单时要以菜品表的当前价格为准重新计算。唯一的例外是套餐套餐表里也有price需要单独查询。所以你在写Service时遍历购物车列表时对每个菜品要重新查一次dish表拿到最新的status和price对套餐要重新查setmeal表。这个细节如果不做等订单生成后复盘发现金额和菜品管理端的最新价格对不上就会很被动。2.3 订单号生成规则苍穹外卖课程里生成订单号的方式是System.currentTimeMillis() 随机数或者用时间戳拼接用户id。我更推荐一种实际项目中更稳妥的生成方式使用时间戳 随机数 用户标记保证在同一毫秒内不同用户不会冲突。当然如果真正做高并发订单号需要引入雪花算法但苍穹外卖这个项目并发量完全不需要用UUID或者时间戳加随机序列就够了。课程里用到的生成方式通常是类似这样String number String.valueOf(System.currentTimeMillis()) RandomUtil.randomNumbers(4);这里有个小坑如果同一毫秒内两个用户同时下单随机数又恰巧相同订单号会重复吗概率极低但为了保险可以在订单号里加上用户id后几位或者直接用UUID去掉横线转大写。对于学习项目用时间戳随机数即可但如果你要做简历项目建议把这个订单号设计讲清楚能加分。3. 实操过程与核心环节实现3.1 查看接口文档明确前端传参格式day08的需求文档里前端提交订单的接口路径一般是POST /user/order/submit请求参数大致长这样{ addressBookId: 123, payMethod: 1, remark: 不要辣多放葱, estimatedDeliveryTime: 1737000000000, deliveryStatus: 1, tablewareNumber: 2, tablewareStatus: 1, packAmount: 2, amount: 45.5 }注意前端会把计算好的总金额amount也传过来但后端绝不能直接信任这个值。下单时后端会重新计算下面会看到。对应的返回结果需要包含订单id和订单号方便前端跳转到支付页面{ code: 1, msg: success, data: { id: 123, orderNumber: 17369999999991234, orderAmount: 45.5 } }3.2 Controller层参数接收与基础校验Controller层写起来很简单但要注意路径安全和用户身份获取。在苍穹外卖项目中用户登录后会把用户id存在ThreadLocal中通过BaseContext.getCurrentId()来拿。这是一个非常关键的设计点后面很多地方都要用到。PostMapping(/submit) ApiOperation(用户下单) public ResultOrderSubmitVO submit(RequestBody OrdersSubmitDTO ordersSubmitDTO) { log.info(用户下单参数{}, ordersSubmitDTO); OrderSubmitVO orderSubmitVO orderService.submitOrder(ordersSubmitDTO); return Result.success(orderSubmitVO); }DTO里要加参数校验注解比如NotNull标注addressBookId和payMethod避免空指针。很多同学在Controller层不做校验结果Service层一取地址簿id直接getById查出来是null然后继续getPhone直接炸出一堆难看的异常。所以Controller层做基础防呆是必要的。3.3 Service层事务控制下的完整下单逻辑Service层是下单功能的核心直接上代码这是一个基于苍穹外卖项目常规方案的实现我加上了比较完整的注释Override Transactional public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) { // 处理异常情况地址簿不存在、购物车为空 AddressBook addressBook addressBookMapper.getById(ordersSubmitDTO.getAddressBookId()); if (addressBook null) { throw new OrderBusinessException(地址簿为空); } Long userId BaseContext.getCurrentId(); ShoppingCart shoppingCart new ShoppingCart(); shoppingCart.setUserId(userId); ListShoppingCart shoppingCartList shoppingCartMapper.list(shoppingCart); if (shoppingCartList null || shoppingCartList.isEmpty()) { throw new OrderBusinessException(购物车为空); } // 构造订单主表数据 Orders orders new Orders(); orders.setNumber(String.valueOf(System.currentTimeMillis()) RandomUtil.randomNumbers(4)); orders.setStatus(Orders.PENDING_PAYMENT); // 待付款 orders.setUserId(userId); orders.setAddressBookId(ordersSubmitDTO.getAddressBookId()); orders.setOrderTime(LocalDateTime.now()); orders.setPayMethod(ordersSubmitDTO.getPayMethod()); orders.setRemark(ordersSubmitDTO.getRemark()); orders.setPhone(addressBook.getPhone()); orders.setAddress(addressBook.getDetail()); orders.setConsignee(addressBook.getConsignee()); orders.setEstimatedDeliveryTime(ordersSubmitDTO.getEstimatedDeliveryTime()); orders.setDeliveryStatus(ordersSubmitDTO.getDeliveryStatus()); orders.setTablewareNumber(ordersSubmitDTO.getTablewareNumber()); orders.setTablewareStatus(ordersSubmitDTO.getTablewareStatus()); orders.setPackAmount(ordersSubmitDTO.getPackAmount()); // 计算订单金额遍历购物车时用最新菜品价格 double totalAmount 0.0; int totalNumber 0; ListOrderDetail orderDetailList new ArrayList(); for (ShoppingCart cart : shoppingCartList) { OrderDetail orderDetail new OrderDetail(); orderDetail.setName(cart.getName()); orderDetail.setImage(cart.getImage()); orderDetail.setDishFlavor(cart.getDishFlavor()); orderDetail.setNumber(cart.getNumber()); // 如果是菜品查询最新价格和状态 if (cart.getDishId() ! null) { Dish dish dishMapper.getById(cart.getDishId()); if (dish null || dish.getStatus() 0) { throw new OrderBusinessException(菜品 cart.getName() 已下架); } orderDetail.setDishId(cart.getDishId()); orderDetail.setAmount(dish.getPrice()); } // 如果是套餐 else if (cart.getSetmealId() ! null) { Setmeal setmeal setmealMapper.getById(cart.getSetmealId()); if (setmeal null || setmeal.getStatus() 0) { throw new OrderBusinessException(套餐 cart.getName() 已停售); } orderDetail.setSetmealId(cart.getSetmealId()); orderDetail.setAmount(setmeal.getPrice()); } totalAmount orderDetail.getAmount() * cart.getNumber(); totalNumber cart.getNumber(); orderDetailList.add(orderDetail); } orders.setAmount(totalAmount ordersSubmitDTO.getPackAmount()); orders.setNumber(orderDetailList.size()); // 这里其实应该存总份数还是总种类数要注意 orderMapper.insert(orders); // 批量插入订单明细 if (!orderDetailList.isEmpty()) { for (OrderDetail od : orderDetailList) { od.setOrderId(orders.getId()); } orderDetailMapper.insertBatch(orderDetailList); } // 清空购物车 shoppingCartMapper.cleanByUserId(userId); // 封装返回VO OrderSubmitVO orderSubmitVO OrderSubmitVO.builder() .id(orders.getId()) .orderNumber(orders.getNumber()) .orderAmount(orders.getAmount()) .orderTime(orders.getOrderTime()) .build(); return orderSubmitVO; }这段代码里有几个小地方特别容易踩坑我一个个说。第一个坑orders.setNumber(orderDetailList.size())这句是我故意写出来的错误示范。Orders实体里有个number字段是订单号用的String类型而OrderDetail里也有number是数量。但Orders类中可能还有一个字段表示总份数和总种类数。很多同学会搞混把明细数量直接set到订单主表的某个int属性上。实际上在苍穹外卖的Orders实体里没有“总种类数”这个字段但有一个totalNumber之类的字段如果你在开发中给订单表扩展了total_number字段就应该把购物车中所有菜品的数量累加totalNumber变量存进去而不是存orderDetailList.size()。上面代码我注释已经提醒过了别踩。第二个坑订单主表插入后要立刻拿到自增主键id。这要求Mapper的insert方法上必须加Options(useGeneratedKeys true, keyProperty id)或者XML中配置keyPropertyid。如果忘了配orders.getId()永远返回null下面明细表设置orderId时全是null数据库直接报错。苍穹外卖的Mapper接口中一般已经加了这个注解但如果是你自己写的一定要检查。第三个坑插入订单明细时如果明细列表很大用单条insert循环插入性能很差。MyBatis支持foreach批量插入但要注意批量插入的SQL长度限制MySQL默认max_allowed_packet可能有限制。外卖订单一般就几个菜没太大问题但如果是B端大订单建议分批插入每批几百条。3.4 Mapper层批量插入与清空购物车的SQL实现OrderDetailMapper中的批量插入常见写法如下insert idinsertBatch insert into order_detail (name, image, order_id, dish_id, setmeal_id, dish_flavor, number, amount) values foreach collectionorderDetailList itemod separator, (#{od.name}, #{od.image}, #{od.orderId}, #{od.dishId}, #{od.setmealId}, #{od.dishFlavor}, #{od.number}, #{od.amount}) /foreach /insert注意这里的collecton名称必须和Mapper接口方法的参数名一致。如果接口方法签名是void insertBatch(ListOrderDetail orderDetailList);XML里的collection就是orderDetailList。如果加了Param注解就要用Param里的名字。这个细节报错时特别隐蔽很多人搞半天发现是参数名不一致。清空购物车的SQL很简单delete idcleanByUserId delete from shopping_cart where user_id #{userId} /delete这里有个并发问题后面详细说如果用户提交订单后又在另一个设备上往购物车加了菜那么清空购物车时会把加的新菜一起删掉吗理论上会有这个逻辑瑕疵。更严谨的做法是只删除“本次下单涉及的购物车记录”按dish_id和setmeal_id、dish_flavor等条件精准删除。但课程里为了简单直接按userId清空。你如果做项目优化建议改成按明细条件删除。3.5 金额计算与精度问题再单独说一下金额计算。代码里用double计算在财务上是禁区。因为double有精度丢失问题比如0.1 0.2可能等于0.30000000000000004订单金额一旦出现这种数字入库后对账就会崩。在学习阶段用double问题不大但如果想写进简历或者做真实项目订单金额应该用BigDecimal并且在数据库里用decimal类型。我在做苍穹外卖时把所有金额都给改成了BigDecimalService层也用BigDecimal计算。改完后发现一个好处当你把订单金额和支付回调的金额做对比时直接用equals比较都不会出问题如果用double还要设置精度比较非常麻烦。具体改造点是DTO里的amount、packAmount字段改成BigDecimal实体类里的amount、packAmount改成BigDecimalMapper的resultMap和insert语句不要动因为MyBatis会自动处理decimal到BigDecimal的映射。计算时BigDecimal totalAmount BigDecimal.ZERO; totalAmount totalAmount.add(orderDetail.getAmount().multiply(BigDecimal.valueOf(cart.getNumber()))); orders.setAmount(totalAmount.add(ordersSubmitDTO.getPackAmount()));建议你从一开始就用BigDecimal省得后面回踩。4. 高并发下的重复下单与事务边界问题如果你只是跟着课程把代码敲一遍跑通一次普通下单其实不算难。难的是要想清楚一个问题用户手一抖点了十次提交按钮会生成十张订单吗大多数同学写出来的代码都会。下面重点说说这个。4.1 重复提交的三种常见场景重复下单有三种典型场景用户快速连点提交按钮同一次业务意图发出多个请求用户在不同页面重复操作比如先提交了一次然后返回购物车又提交一次但其实第一次已经下单了网络重试导致同一请求被发送多次比如前端超时后自动重试或者后端重试机制误触。对于三种场景前端的按钮置灰可以挡住一大半问题但后端必须兜底。因为专业的测试或攻击者可以绕过前端直接调接口。4.2 基于数据库的唯一约束做幂等最简单的方案是引入“业务订单号唯一约束”。比如在下单请求中让前端生成一个唯一的token或者用userId 一个业务维度比如同用户同购物车同一时间段作为唯一维度。在order表里增加一个唯一索引比如uk_user_biz_token插入时如果冲突数据库会报DuplicateKeyException此时捕获异常返回“订单已提交”即可。但苍穹外卖的订单表没有预留这样的字段。所以实际项目里更常见的是在Redis里做分布式锁或幂等标记。4.3 用Redis做下单防重的实操方案在用户点击提交订单时可以先根据userId生成一个唯一的锁key例如order:submit:userId:123使用Redis的SETNX命令设置过期时间比如15秒。如果设置成功说明该用户当前没有正在提交的订单请求可以继续执行下单流程如果设置失败说明用户正在提交直接返回“正在提交中请勿重复操作”。伪代码如下String lockKey order:submit: userId; Boolean success redisTemplate.opsForValue().setIfAbsent(lockKey, 1, Duration.ofSeconds(15)); if (!Boolean.TRUE.equals(success)) { throw new OrderBusinessException(订单正在提交中请勿重复操作); } try { // 执行下单逻辑 } finally { redisTemplate.delete(lockKey); }这个方案能解决同一用户的重复点击问题。但它也有一个问题如果下单逻辑执行超过15秒锁提前过期后续请求又可以进来了。所以过期时间要设置合理或者使用Redisson的看门狗机制自动续期。对学习项目来说15秒足够而且外卖下单接口正常情况下几百毫秒就返回了。这里补充一个更精简的方案如果不想引入Redis锁可以在数据库层面用乐观锁比如给购物车表加一个版本号或者交易状态字段下单前先更新状态只有更新成功的那个请求才能往下走。但这个改动比较大学习阶段不推荐。4.4 事务边界与异常回滚Transactional注解默认只回滚RuntimeException而自定义异常OrderBusinessException一般继承RuntimeException所以可以正常触发回滚。但要注意如果你在Service里catch了异常然后自己处理Spring事务就感知不到异常也就不会回滚。我在第一次写下单时就犯过这个错误在循环中判断菜品状态时catch了一个Exception并跳过结果订单主表依然插入成功但明细少了几条数据直接错了。正确的做法是所有业务校验失败都直接抛异常交给事务管理器统一回滚。如果你想捕获异常做日志记录也要在catch块里手动设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()强制回滚。还要注意事务的边界下单涉及orderMapper.insert、orderDetailMapper.insertBatch、shoppingCartMapper.cleanByUserId三个写操作任何一步失败都要回滚所以Transactional必须加在Service层的公开方法上。为什么不加在Controller层因为Controller不是Spring管理的bean而且事务粒度太大也不好。4.5 数据一致性的最终检查下单完成后应该做一次数据一致性检查订单金额是否等于所有明细金额之和加上打包费订单状态是否是待付款购物车是否已清空。这些检查不一定都要写在业务代码里但写完功能后你可以打开数据库手工核对一下。我强烈建议你在开发阶段写一个简单的JUnit测试模拟下单流程然后断言三张表的数据。测试能帮你尽早发现问题比手动点接口高效得多。5. 常见问题与排查技巧实录5.1 下单后订单表有数据但订单明细为空这个问题的原因十有八九是orderDetailMapper.insertBatch没有执行成功但事务没有被正确触发回滚。排查步骤先看控制台SQL日志打印出MyBatis的执行SQL然后检查批量插入的Mapper XML看collection名称是否和接口参数名一致再看明细实体中orderId有没有拿到值。如果orders.getId()返回null就是insert主键回填配置漏了。5.2 下单时报“购物车为空”但前端明明有数据这个问题一般不是代码逻辑错误而是用户id获取错了。BaseContext.getCurrentId()是从ThreadLocal中取用户id如果你在拦截器里没有正确解析JWT并放入ThreadLocal或者Service中取到的不是当前登录用户那购物车自然查不到数据。排查时先确认前端是否携带token再确认拦截器是否放行了/user/order/submit这个路径最后在Service里打个日志把userId打出来对比数据库里的user_id。5.3 金额计算为0或小数位不对如果订单金额始终是0大概率是购物车列表为空或者amount字段为null。另一个常见问题是double计算导致的科学计数法显示比如2.0E-5之类的。如果你用double接受前端金额又做加减乘除很容易出现这种诡异数字。我建议直接全部换成BigDecimal别犹豫。5.4 下单成功后购物车里的菜还在清空购物车的SQL没生效。检查一下cleanByUserId方法的XML看user_id条件是否拼对了另外确认事务提交了吗。如果你在Service里先清空购物车然后后续代码抛异常事务回滚后购物车就还是满的。这其实是正常的说明回滚生效了。但如果你在清空购物车之后没有执行业务操作直接返回购物车还是满的那就要检查Mapper接口是否真的执行了delete。还有一种情况用户id类型不一致比如购物车表的user_id是bigint但你传了一个LongMyBatis会自动转换一般不会出错但如果你传的是String类型的id就可能查不到。5.5 地址簿id存在但是查出来是null这个错误通常是因为Controller层接收的参数名与前端不一致。比如前端传addressBookId后端DTO字段写的address_book_id或者没有加JsonProperty注解导致Spring无法绑定。解决方法是前端抓包看真实请求参数然后统一字段命名风格推荐使用驼峰命名并在application.yml中开启map-underscore-to-camel-case: true。5.6 下单接口报500日志里有空指针最常见的空指针出现在addressBook.getPhone()这一行。虽然前面判断了addressBook ! null但如果数据库里address_book表的phone字段是NULL那就会在设置订单phone时出现空指针。建议实体字段都设置默认值或者在下单时做兜底phone为空就返回“请完善收货人电话”。真实项目中地址簿的校验逻辑会更严格不只是查存在性还要查字段完整性。5.7 订单号重复引发数据库唯一索引冲突如果你给order表的number字段加了唯一索引而生成本地订单号的方式是时间戳4位随机数在并发稍微大一点时就有可能撞号。解决方法是把随机数位数扩展到6位以上或者用雪花算法。课程里用的订单号是字符串类型一般不会强制唯一索引但你在做项目扩展时如果加了就要考虑并发。6. 实操总结与个人体会写完用户下单苍穹外卖这条主链路算是串通了一半。我在做day08时最深刻的体会是一个看似简单的提交接口真正做好要比想象中多考虑好几层。从Controller的参数校验到Service的金额重算再到事务回滚、防重提交每一步都隐藏着真实业务里会遇到的痛点。给你一个实用建议在做下单功能时先不要急着写代码而是拿一张纸画出“请求进来 → 校验地址 → 校验购物车 → 查询菜品 / 套餐 → 计算金额 → 插入订单 → 插入明细 → 清空购物车 → 返回结果”这条时间线然后逐个节点去思考可能出现的数据问题、并发问题、异常问题。这个习惯能让你在这个过程中少走很多弯路。另外建议你把day08之后要做的“订单支付”环节也提前想一下。下单生成了待支付订单支付回调回来时怎么更新订单状态如果支付成功但订单已经被取消怎么办这些问题现在脑子里有个轮廓后面做起来会顺畅得多。我个人的经验是把这个模块完整的业务闭环跑通之后再去回头看表结构设计会比单纯跟着视频敲代码理解深刻得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

计算机小白必看!收藏这8大就业方向,核心技能+岗位一次说清,助你告别迷茫! 2026/10/1 7:44:01

计算机小白必看!收藏这8大就业方向,核心技能+岗位一次说清,助你告别迷茫!

计算机小白必看!收藏这8大就业方向,核心技能岗位一次说清,助你告别迷茫! 文章整理了计算机就业最主流的8大方向,详细介绍了每个方向的核心技能和对应的岗位,旨在帮助迷茫的计算机小白找到适合自己的职业方向…

阅读更多 →
deepflood签到_deepflood_checkin - 青龙面板自动签到脚本 2026/10/1 7:44:01

deepflood签到_deepflood_checkin - 青龙面板自动签到脚本

自动签到、消息推送、阅读任务。每日签到是获取平台福利最简单的方式,但每天手动操作容易忘记。这款自动签到脚本帮你每日准时完成签到,再也不用担心漏签。功能介绍 「deepflood签到_deepflood_checkin」脚本支持以下功能: • 自动完成每日签…

阅读更多 →
【教程】IDEA 中 GitHub Copilot 插件登录报错 Sign in failed 的排查与配置修复 2026/10/1 7:44:01

【教程】IDEA 中 GitHub Copilot 插件登录报错 Sign in failed 的排查与配置修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
远程 MCP 实战:把高德地图、FileSystem、Chrome DevTools 的 endpoint 改到 TaoToken 统一通道 2026/10/1 7:44:01

远程 MCP 实战:把高德地图、FileSystem、Chrome DevTools 的 endpoint 改到 TaoToken 统一通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
LIMS实验室管理系统:方案设计、模块拆解与落地实施 2026/10/1 7:44:01

LIMS实验室管理系统:方案设计、模块拆解与落地实施

做过实验室信息化的人都清楚,实验室管理系统这四个字听着挺唬人,真正落地的时候,你会发现它压根不是一个"软件安装完就能用"的东西。它更像是一场对实验室现有流程的彻底体检——把样品从进入实验室那一刻起,到最后报告…

阅读更多 →
Windows 安装部署 OpenClaw:用 PowerShell 配好 Node.js 与 Git 后把 Base URL 改到 TaoToken 2026/10/1 7:43:55

Windows 安装部署 OpenClaw:用 PowerShell 配好 Node.js 与 Git 后把 Base URL 改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉