新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于Spring Boot的超市收银与进销存一体化系统设计与实践

发布时间:2026/10/1 4:27:40来源:尧图网络
基于Spring Boot的超市收银与进销存一体化系统设计与实践
把“基于Java的超市收银管理系统”这个题目拿到手的时候很多同学第一反应是“这不就是个增删改查吗”。我当时也是这样想的但真做完一版基于Spring Boot的商超POS收银与进销存一体化平台之后才意识到这个题目远不止商品表和订单表那么简单。前台收银要处理扫码、折扣、挂单、小票后台还压着采购入库、盘点报损、成本核算和一堆报表东西全串起来才叫一套系统。这篇文章写的就是我完整做一遍这类系统的思路和实操过程从需求拆解到数据库设计从前台POS到后台进销存再做一遍避坑总结适合正在准备Java毕业设计、特别是拿到超市收银或者门店管理系统题目的同学参考想练Spring Boot项目的新人也能从中找到一条可以复现的路线。1. 先想清楚再动手把“超市收银管理系统”拆成四个真实业务域做毕设最容易犯的错就是一上来写代码。拿到这个题目第一步应该是把标题里藏着的业务域拆出来否则你会陷入“东补一块西补一块”的泥潭。我当时把整个系统拆成了四个部分商品中心、前台收银、库存进销存、后台管理与报表。1.1 前台收银不只是“算账”POS终端的真实操作流前台收银表面上是把商品扫进来、算个总价、收钱找零实际上它是一连串事件的开端。一个顾客拿着一瓶可乐到收银台收银员扫码系统查出商品、校验价格和促销规则点击结算生成订单订单里的商品项触发库存扣减同时记录销售流水和会员积分。这意味着收银台不仅要展示商品信息还要承载折扣计算、支付方式、挂单取单、小票打印这些细节。所以在设计阶段我建议把收银台当成“事件的发起端”而不是“一个表单页面”。前端界面可以做得简单但后端服务必须把这个事件涉及的模型全部打通购物车、订单头、订单明细、支付流水、库存变动记录。后面写代码时你会发现真正花时间的不是页面而是这些模型之间的关系。1.2 进销存才是系统的“中台”采购、库存、成本三条线进销存听起来很业务化其实核心就是三条数据流采购入库、销售出库、盘点调整。采购员下一张采购单供应商送货后审核入库库存增加并产生一笔入库流水顾客买走商品库存减少并产生一笔出库流水盘点时发现实物和账面不一致生成盘盈盘亏单再调整库存。超市收银系统和普通电商系统的最大区别就在于电商可以允许“库存暂时不对没关系”但超市每天结账要日结库存必须对得上。所以进销存模块不仅仅是“商品管理”加“库存管理”它是一整套有单据、有审核、有流水的业务流程。设计时如果忽视“单据—审核—流水”这个链路后面报表和盘点一定会出问题。1.3 后台管理到底管什么商品档案、员工权限、会员与营销后台管理看着像纯粹的CRUD但它是整个系统的“配置中心”。商品档案决定前台能扫出什么员工账号决定谁能操作收银和退款会员等级决定结账时打几折营销规则决定满减和优惠券怎么生效。这一层虽然简单但它有一个很重要的特点所有后台配置都会直接影响前台收银的行为。比如后台把某件商品从“上架”改成“下架”前台扫码立刻就不能售卖后台给某会员升了级下一次结算就按新折扣计算。所以做后台管理时不要只做增删改查要在每次变更后想清楚这个变更会影响到哪些下游流程。1.4 为什么这个题天然适合Spring Boot技术选型的理由超市收银系统这种业务最大的特点就是模块多、接口多、事务贯穿多个表而且需要快速开发。Spring Boot在这类项目里的优势非常明显自动配置帮我把数据源、事务、JSON序列化都处理好了spring-boot-starter-web一加就能对外提供REST接口配合MyBatis或者MyBatis Plus写持久层增删改查开发速度非常快。加上Spring家族里的事务管理Transactional一键就能把“扣库存写订单记流水”包成原子操作。很多人纠结要不要用Spring Cloud甚至微服务我的建议是没必要。超市收银系统这个体量单体应用完全够用反而更容易把事务和业务逻辑讲清楚答辩时也更好说明白。前端你可以选Vue或直接用模板引擎核心后端用Spring Boot数据库选MySQL一套组合足够撑起整个项目。2. 数据库设计是毕设项目的生命线核心表结构与容易踩的坑我做了这么多管理系统类的项目有一个体会代码写得再漂亮数据库设计稀烂后期一定返工。超市收银系统尤其如此因为所有数据都有上下游关系表结构没想清楚写业务代码时会发现查一个数据要join五张表。2.1 商品档案与SKU同一种可乐两种包装如何建模超市里同一种商品经常有多个规格比如可乐有500ml瓶装和330ml罐装它们条码不同、进价不同、售价也不同。在表设计上我直接采用一张product表核心字段包括id、barcode(条码)、name、specification(规格)、category_id、purchase_price(进价)、sale_price(售价)、stock(当前库存)、status(上下架状态)。条码必须加唯一索引因为扫码查询是最高频的操作。有些同学会想到“商品SPU 规格SKU”的两层建模这在电商项目里是合理的但在超市收银这个场景属于过度设计。超市里的“规格”大多只是包装差异不需要像服装一样按颜色尺码组合所以单表加条码字段最直接查询时一条where barcode ?就出来性能和开发效率都更好。2.2 库存流水宁可多建一张表也不要直接改库存数量很多初学同学设计库表时会在product表里加一个stock字段然后每次销售入库都直接update product set stock stock - 1。这确实简单但问题很大库存对不上的时候你没有任何依据去查到底是哪笔操作导致的。正确的做法是建一张inventory_log库存流水表字段包括id、product_id、change_type(类型采购入库/销售出库/盘点调整/报损)、change_quantity(变动数量正负号表示加减)、before_stock、after_stock、source_order_no(来源单号)、create_time。每次库存变动都写一条流水product.stock负责展示当前数量流水表负责追溯历史。哪天账对不上了按时间查流水一眼就能找到问题。2.3 订单与订单明细父子表拆分带来的查询便利收银台一次结算会产生一个订单订单里可能包含十几种商品所以订单必须拆成父表和子表。sale_order主表存订单号、门店ID、收银员ID、会员ID、订单金额、实收金额、支付方式、订单状态、下单时间sale_order_item明细表存每个商品对应的商品ID、条码、商品名称、数量、单价、折扣金额、小计金额。为什么必须拆因为要算“客单价”订单金额/订单数就得用主表要统计“什么商品卖得好”就得用明细表要把两者关联起来用明细表的order_id外键或者逻辑关联就可以。如果不拆一张表塞下所有商品项行数和冗余字段会非常可怕。2.4 从商品到销售再到采购的闭环关系整个系统的表关系其实是一个闭环purchase_order采购单和purchase_order_item采购明细决定采购入库入库时写inventory_log更新product.stock销售时sale_order和sale_order_item生成订单同时再写inventory_log更新库存盘点时产生stock_take盘点单和stock_take_diff差异单审核后又一次调整库存和流水。我一般会在项目文档里画一张表格把这些核心表列举出来商品表、库存流水表、采购单/明细、销售单/明细、会员表、员工表、促销规则表。对毕设来说这些表已经能撑起整个业务闭环比盲目加一堆“系统管理”的杂表要有用得多。3. 核心技术点拆解库存扣减、并发控制与事务边界这个项目里有一个非常经典的问题也是答辩时大概率被追问的点库存扣减怎么防止超卖。很多同学在做的时候都会踩这个坑因为“查询库存→判断够不够→更新库存”这三步分开写在并发场景下会出大问题。3.1 为什么“查询库存再判断再扣减”会出事超卖场景分析假设现在商品A只剩最后1瓶两个收银台的顾客同时提交结账。如果没有控制两个线程都先查库存得到1都判断“够卖”然后都执行库存减1最后库存变成-1也就是超卖了。问题的根源在于“检查”和“扣减”不是原子操作中间插入了其他线程的操作。这种场景在超市里很常见尤其是促销活动期间收银台不止一个顾客也可能在小程序里下单所以数据库层面的并发控制是必须考虑的。3.2 用数据库行锁解决超卖Transactional select for update我采用的是悲观锁方案代码非常直接在事务方法里先执行select * from product where id ? for update把这一行数据锁住然后再判断库存、执行扣减、更新库存。因为加锁之后其他线程对同一行的操作会排队等待所以不会出现两个线程同时扣同一个库存的情况。关键代码如下Transactional(rollbackFor Exception.class) public void createOrder(CreateOrderRequest request) { Product product productMapper.selectByIdForUpdate(request.getProductId()); if (product.getStock() request.getQuantity()) { throw new BusinessException(库存不足); } // 扣减库存并写订单、写流水 productMapper.decreaseStock(product.getId(), request.getQuantity()); saleOrderMapper.insert(...); inventoryLogMapper.insert(...); }对应的Mapper语句要注意加锁select idselectByIdForUpdate resultType... select * from product where id #{id} for update /select为什么不用乐观锁乐观锁需要带版本号更新更新失败后要么重试要么报错。但在收银场景里顾客已经在等着付款了重试会因为用户再扫一遍商品而产生体验问题所以优先用行锁更稳妥。超市收银的并发量远够不上秒杀级别行锁带来的性能损耗可以忽略不计。3.3 一个容易忽略的事务陷阱先减库存还是先生成订单库存扣减和订单生成必须放在同一个事务里要么都成功要么都回滚。我在测试时故意模拟过“减库存成功但订单写入失败”的情况如果没有事务那库存就白白少了金额和实物对不上。顺序上我习惯先写订单明细再扣库存再写库存流水最后更新订单状态整个流程只要在同一个事务方法内就不怕中间出错。事务注解一定要写rollbackFor Exception.class因为Spring默认只在遇到RuntimeException时才回滚如果业务异常是自定义Exception不加这个参数会出现“库存回滚了但订单还在”的诡异问题。3.4 适合在答辩里补充的优化方向Redis缓存与消息队列如果你的项目时间充裕可以在期末阶段给收银台加一点“看似高级”的设计但一定要能讲清楚原理。比如用Redis预热商品库存收银台先扣Redis里的预扣库存再通过消息队列异步去扣数据库库存或者高并发下把日结报表改为定时任务异步生成。不过我个人建议除非你对这些中间件掌握得足够扎实否则在核心链路里引入Redis和MQ反而增加复杂度。答辩时你可以说出“系统现状是数据库行锁方案如果未来门店数量增长可以考虑Redis预扣库存”这种话比把半懂不懂的代码塞进项目里要安全得多。4. 前台收银功能落地扫码、折扣、挂单与小票打印的实现思路前台收银是整个系统中用户最直接接触的部分功能不难但每个细节都影响使用体验。这部分我按真实收银员的操作顺序来讲。4.1 扫码枪接入的两种方式模拟键盘输入与串口/USB-HID市面上大多数超市扫码枪其实就是一个“快速键盘”。它扫到条码后会把条码数字当作键盘输入发送给电脑通常后面还跟着一个回车。所以在Web前端最省事的接入方式是监听键盘事件把输入字符累积起来检测到回车就触发商品查询。代码逻辑大致是let barcode ; document.addEventListener(keydown, function (e) { if (e.key Enter) { if (barcode.length 0) { queryProduct(barcode); barcode ; } e.preventDefault(); } else if (e.key.length 1) { barcode e.key; } });这种方式的优点是零依赖浏览器页面里直接用演示效果也很好。还有一类扫码枪是通过串口或者网口传输数据需要在后端开Socket接收对毕设来说没有必要除非你后续要接真正的工业级硬件。4.2 折扣与满减营销规则的优先级怎么设计超市收银最常见的情况是“会员打95折同时满100减10”。这里最大的坑是如果多个规则同时生效到底按什么顺序算我在设计时建了一张promotion_rule促销规则表字段包括rule_type(满减/折扣/会员价)、priority(优先级)、threshold(门槛金额)、discount_value(折扣值或减免金额)、status。结算计算的顺序我固定为先商品级优惠比如单品特价再订单级满减再会员等级折扣最后整单抹零。每一步的计算结果都记录到订单的备注或优惠明细里这样对账时有据可查。如果没有规则引擎把所有营销逻辑堆在Service里会非常痛苦建议至少用一张规则表来驱动。4.3 挂单和取单前台操作体验的一个关键细节如果你去超市观察过会经常看到收银员对一个结了一半的顾客说“您先去拿个东西我等您”然后按一下挂单键下一位顾客继续结账。这个“挂单”功能很细小但特别能体现系统完整性。我的实现方案是购物车对象可以序列化后存到Redis或本地Map以挂单号为key并设置一个过期时间比如30分钟。取单时输入挂单号把购物车对象反序列化回来继续结算。用Redis的好处是方便设过期时间而且即使收银台页面刷新挂单数据也不会丢。如果你不想引入Redis用一个ConcurrentHashMapString, ShoppingCart挂着也能演示但重启服务会丢数据。4.4 小票打印模板渲染与售后凭据之间的平衡小票打印有两种方案。第一种是前端直接用window.print()打印一个排版好的HTML区域配合热敏纸的80mm宽度把页面CSS设好就能在演示时直接打印出小票第二种是后端通过ESC/POS指令生成打印内容发送给58mm热敏打印机这对普通学生来说难度偏高而且需要真实设备调试。毕设阶段我强烈推荐第一种。小票上至少要有门店名称、订单号、收银员、商品清单名称、单价、数量、小计、合计金额、实收金额、找零、支付方式、订单时间。这些信息在sale_order和sale_order_item里都有页面从接口拉数据渲染即可。另外小票上建议加一行“本单仅供售后凭证如需发票请咨询服务台”这让演示显得更真实也避免纸质小票和正式发票混淆。5. 库存与报表联动进销存的核心算法与统计口径进销存模块真正见功底的不是“增删改查库存”而是成本核算和统计口径。这块也是答辩老师容易深挖的地方。5.1 加权平均成本法毛利率算得准的关键商品每批次进货价格可能都不一样上周可乐进货价2.2元这周变成了2.5元。那么卖出100瓶可乐成本价到底按多少算我在系统里用的是移动加权平均法。算法是这样的每次采购入库后将“原有库存数量 新入库数量”和“原有库存总成本 新入库总成本”重新做除法得到一个新的平均成本价。销售出库时按当前的平均成本价结转成本毛利就等于销售收入减去销售数量乘以平均成本价。举例之前库存50瓶成本均价2.2元新入库50瓶进货价2.5元新均价就是(50×2.2 50×2.5)/1002.35元。后面卖出时的成本都按2.35元算直到下一次入库再重新调整。这个算法不复杂却能让毛利报表变得有意义。有些同学的进销存项目直接忽略成本核算只统计销售额那样“利润分析”就完全无从谈起。5.2 盘点流程盘盈盘亏为什么要走差异单盘点这件事看起来是“改一下库存数字”但正确做法必须留痕。我在系统里实现了两步先生成盘点单录入每个商品的账面库存快照然后开始实际盘点录入实盘数量系统自动计算差异生成一张“盘点差异单”里面区分盘盈实盘多和盘亏实盘少。审核差异单之后才真正调整product.stock并往inventory_log写入“盘点调整”类型。为什么要搞得这么麻烦因为如果直接改库存你不知道差异是盘错了、丢货了还是录入错了。有了差异单这张中间表随时可以反查每个门店每个商品的差异历史。超市门店每天开关店都要做日结盘点流程规范了财务对账才能说得清。5.3 销售日结与数据看板给店长看什么数据才有用报表不是数据越多越好而是要给店长看“今天到底赚没赚钱”。我的日结统计主要输出这几项销售额实际收款金额、订单数、客单价销售额/订单数、商品销售Top10、毛利销售额减去对应成本、打折让利金额。SQL实现也不复杂比如按日销售额select DATE_FORMAT(create_time, %Y-%m-%d) as day, sum(actual_amount) as total_sales, count(*) as order_count, sum(actual_amount) / count(*) as avg_order_amount from sale_order where store_id #{storeId} and create_time #{startTime} and create_time #{endTime} group by DATE_FORMAT(create_time, %Y-%m-%d) order by day desc;统计对象是sale_order主表不是明细表这个细节要注意。如果你从明细表汇总单笔订单多商品时会重复统计订单数。5.4 用Excel导入导出功能快速撑起“导出报表”的刚需答辩时老师很可能问“报表数据怎么导出来”。如果一个“导出”都没有系统会显得不完整。我推荐用EasyExcel而不是原生POI原因很简单POI的API写起来繁琐EasyExcel封装之后三五行就能搞定导出而且对内存的占用低很多。最简单的用法是引入com.alibaba:easyexcel依赖写一个和表字段对应的DTO类然后调EasyExcel.write(outputStream, DTO.class).sheet(销售明细).doWrite(dataList)即可。导入批量商品也是同理用EasyExcel.read(inputStream, DTO.class, listener)在listener里逐行校验并插入数据库。这个功能实现成本低但在演示和答辩中加分很明显。6. 做毕业设计最容易忽略的五个问题与我的解决方案在跑通主体功能之后我花了不少时间处理“边角问题”。这些问题单看都不难但组合起来非常影响系统质量也影响评委的印象分。6.1 密码存储别再用明文和MD5了答辩时如果老师打开数据库看员工表看到密码是明文印象分会大打折扣。正确做法是用BCrypt哈希存储最常见的实现方式是引入spring-security-crypto单独用它的BCryptPasswordEncoder不需要把完整的Spring Security接进来。BCryptPasswordEncoder的用法很简单注册时encoder.encode(password)登录时encoder.matches(rawPassword, encodedPassword)。BCrypt自带随机盐同一密码每次加密结果都不同比MD5安全得多而且这个类轻量不影响其他模块。6.2 全局异常处理让前端不再弹出白底黑字的报错页刚开始我的项目里每个Controller都是try-catch代码很啰嗦。后来改成用RestControllerAdvice做全局异常处理器统一返回一个包含状态码、消息、时间的JSON结构。业务异常比如“库存不足”“商品不存在”统一抛BusinessException然后在全局异常处理器里转成对应提示系统异常则记日志并返回统一兜底提示。这样做的好处是前端拿到的永远是结构化JSON不会因为某个异常出现一整页报错。而且Controller里的业务代码可以专心写流程不用到处catch。6.3 时间字段的时区问题系统跑着跑着发现差8小时测试时发现订单时间比本地时间正好少8小时这是MySQL连接串没配时区导致的。连接地址里加上serverTimezoneAsia/Shanghai同时实体类的时间字段用LocalDateTime基本就能避免这个坑。另外建议所有时间字段在数据库里用datetime类型而不是timestamp。timestamp的范围只到2038年而datetime的范围更大对毕设来说datetime更稳也不会受MySQL时区设置变化影响。6.4 参数校验别在Service里写几十行if判断我以前写CRUD喜欢在Service开头写一堆“if (name null || name.equals()) return false”这种代码。后来改成在DTO字段上加NotBlank、NotNull、Positive注解然后在Controller参数上加Validated入参校验交给框架完成Service里只处理业务逻辑。比如新增商品接口barcode不能为空salePrice必须大于0这些都可以通过注解声明。校验失败时全局异常处理器会接到MethodArgumentNotValidException我把它统一转成“参数校验失败: xxx字段不能为空”这种提示前端体验会好很多。6.5 分页查询性能和“假分页”问题如果一个列表页查出几万条数据一次性塞到前端页面会卡死。我用的是MyBatis Plus自带的Page对象代码里new Page(pageNum, pageSize)然后productMapper.selectPage(page, queryWrapper)即可。需要注意真分页一定要由数据库在SQL层面做limit千万别把全表数据查出来再在Java里截取那是“假分页”数据量一大就崩。如果你用的是手写SQL分页就写成limit #{offset}, #{pageSize}同时给排序和查询字段建好索引尤其是barcode、create_time这两个高频查询字段。7. 开发节奏与答辩准备如何真正做完并讲清楚做毕业设计从来不是“写完代码就行”而是要能在答辩现场把系统讲明白。我的项目从零到演示大概花了六周节奏可供参考。7.1 六周里程碑从环境搭建到功能闭环第一周最关键做需求梳理和数据库设计把核心表和字段定下来第二周搭Spring Boot项目骨架接入MyBatis Plus完成员工登录和权限拦截第三周做完商品管理和采购入库模块把库存流水链跑通第四周集中做前台收银包括扫码、结算、订单生成和库存扣减第五周做会员、促销和日结报表第六周整理测试数据、修复细节、写演示文档和录制视频。这套顺序的逻辑是先让“进销存”的库存数据能动起来再做收银台去消费这些数据最后用报表把整个闭环的结果展示出来。如果一开始就做收银台但库存没接上后面返工成本会很高。7.2 演示环境的准备把系统跑起来比讲PPT更有说服力答辩前一定要准备一个独立的演示环境和一套完美的演示数据。我当时的做法是在本地固定一个MySQL数据库导入预置好的商品数据覆盖食品、饮料、日用品几类库存设置为准确数字并提前生成几笔昨天的销售订单让日结报表有数据可看。演示账号也提前准备好不现场注册避免输入法和网络问题卡壳。如果条件允许我会把整个演示过程录成视频时长控制在五分钟登录后台、添加一条采购入库、打开收银台扫码结算、查看库存变化、打开日结报表。这套流程一气呵成基本把系统的核心功能全走了一遍即使现场网络出问题也能放视频兜底。7.3 答辩时如何回答“系统有哪些不足”被问到不足很正常关键是不要露怯也不要硬吹。你可以主动说系统目前的库存控制是数据库行锁方案适合单店几十个收银台的场景如果未来做多门店连锁和线上线下一体化可以引入Redis预扣库存和消息队列做异步处理这是技术上已知的改进方向。再比如权限模块目前只做了简单的角色区分后续可以引入Spring Security做细粒度控制。这样回答能让评委看到你有技术视野同时也不回避现状。说出来的每一个优化方向都必须是你真正理解、能解释清楚的否则一追问就容易暴露。做完这套系统我最大的收获其实不是那几个表或者接口而是学会了“以业务事件为中心”去组织数据和代码。超市收银看起来只是一个小场景但它把商品、库存、订单、会员、营销和报表全串起来了每个部分之间都有因果联系。你在开发时踩过的每个坑修复的每个线上问题最后都会变成答辩时讲得最顺的那段话。
网站建设高端定制企业官网
RELATED

相关资讯

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

较早相关资讯

最新相关资讯

用WeKnora搭建RAG知识库:解析、召回与编排全解 2026/10/1 5:20:20

用WeKnora搭建RAG知识库:解析、召回与编排全解

1.1 RAG应用的三座大山:解析、召回、编排这两年做AI应用你会发现一个现象:大模型本身越来越聪明,但真正到了企业内部落地,卡住的地方往往不是模型能力,而是数据怎么进去、怎么找出来、怎么和大模型配合干活。很多人一开…

阅读更多 →
CNN-KELM图像分类:卷积特征融合核极限学习机的原理与实践 2026/10/1 5:20:19

CNN-KELM图像分类:卷积特征融合核极限学习机的原理与实践

简介:该资源为基于CNN与核极限学习机(KELM)的图像分类预测项目,面向有一定Python与深度学习基础的研究者或开发者,适合需要对比卷积特征提取与ELM分类性能的实验场景。压缩包共43个文件,包含23个Python脚本…

阅读更多 →
OpenSSL版本演进与兼容性排查:从0.9.x到3.x的迁移指南 2026/10/1 5:20:12

OpenSSL版本演进与兼容性排查:从0.9.x到3.x的迁移指南

提到OpenSSL版本历史,很多人第一反应通常不是一连串版本号,而是升级后那行刺眼的报错:OpenSSL version mismatch. Built against 30000020, you have 30500060。我当年第一次见这个报错也愣了一下,同一个OpenSSL,怎么编…

阅读更多 →
Android启动流程详解:从Kernel到init的完整链路 2026/10/1 5:20:06

Android启动流程详解:从Kernel到init的完整链路

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

阅读更多 →
Gatling 3.0.0 升级迁移实战:API 重构、脚本改造与压测调优 2026/10/1 5:20:06

Gatling 3.0.0 升级迁移实战:API 重构、脚本改造与压测调优

简介:Gatling 3.0.0 是一款面向现代 Web 应用的性能测试工具,适合开发者与测试工程师用于高并发场景下的稳定性验证。它基于 Scala DSL 编写测试脚本,可模拟成千上万并发用户,测量响应时间、吞吐量与资源利用率,并支持…

阅读更多 →
ipad协议866源码拆解:IM长连接与二次开发实战指南 2026/10/1 5:20:06

ipad协议866源码拆解:IM长连接与二次开发实战指南

/* 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
📞 ✉