《苍穹外卖》Day3复盘:员工与分类模块的分页与动态SQL实践
《苍穹外卖》Day3终于推进到了业务开发的核心环节。这一天我的进度是员工管理模块和分类管理模块对应课程里的需求分析和代码实现两部分。如果你刚把这套Java外卖项目捡起来前两天的环境搭建和接口联调可能还有点懵那 Day3 会是一个明显的感觉分水岭——代码量开始上强度了业务的坑也开始变多了。这篇文章就完整复盘我当天的实操过程、绕过的弯路和可以复用的开发套路。1. 整体设计思路拆解为什么这两个模块适合放在一起做1.1 Day3 的任务范围与业务场景先明确一下背景《苍穹外卖》模拟的是一套真实的外卖管理后台。整个系统面向管理员、商家端和用户端Day3 的核心是管理端的基础数据维护——员工归属于商家后台的账号体系分类则是菜品和套餐的挂靠维度。这两个模块都有一个共性它们是后续所有业务开发的前提。举个例子没有员工管理你没法给店长、厨师、收银员分配登录权限和账号状态没有分类管理你后面做菜品管理、套餐管理时根本没有可选的分组。所以 Day3 代码虽然看起来是标准的 CRUD但在整个项目里承担的是 地基 的角色。地基打得稳不稳直接决定后面接需求时的开销。1.2 两个模块共用的三层架构套路做 Java 服务端开发Controller - Service - Mapper这套三层架构是绕不开的主线。Day3 两个功能模块我全部沿用了这种分层方式Controller只做参数接收、调用 Service、封装统一返回结果ResultT。这一层不出现任何业务逻辑业务逻辑一多就拆私有方法。Service承载核心业务校验和流程编排。比如新增员工时检查用户名是否重复、编辑分类时校验分类名下是否还挂着一线商品。这种约束规则统统放在 Service 层做放到 Controller 里会让代码发散。Mapper映射 SQL。Day3 接口基本都是单表操作复杂 SQL 不多但分页查询的动态条件拼接仍然考验 MyBatis 动态 SQL 的基本功。1.3 技术选型为什么用 PageHelper 而不是手写 Limit分页查询这块我直接就用了PageHelper插件而不是手动拼LIMIT。理由有三点第一PageHelper基于 MyBatis 拦截器实现对业务代码的侵入性极小。你只需要在查询前调用PageHelper.startPage(page, pageSize)紧接着的 Mapper 查询会被自动拼接LIMIT不用手动改 SQL代码干净。第二它返回的PageInfo封装好了total、pages、pageNum这些分页元数据你不用手动SELECT COUNT(*)再手动组装。对后端开发来说少写的代码就是少出的 bug。第三项目前期的员工查询、分类查询都是典型的分页列表用插件统一处理比一个接口一套手工逻辑要稳定得多。直接看表达式可能不够具体。实际操作中我的调用方式长这样PageHelper.startPage(page, pageSize); ListEmployee employees employeeMapper.pageQuery(name); PageEmployee pageData (PageEmployee) employees;注意一个细节startPage必须紧跟要分页的查询语句中间如果夹带了别的查询分页就会作用到错误的 SQL 上。这个坑我在第 4 节会专门讲。2. 员工管理模块实现分页查询与状态控制的完整细节2.1 分页查询员工参数设计、DTO 与动态 SQL员工分页查询的需求虽然直白但参数设计里的门道一点不少。前端要传的查询条件有两个page页码、pageSize每页条数搜索关键字是员工姓名name。我直接建了一个查询 DTO——EmployeePageQueryDTO把这三个字段封装进去。这样 Controller 层的方法签名不用堆一堆 RequestParam后续如果需要增加排序字段、时间范围筛选都在这个 DTO 里加字段同时兼顾向后兼容。Java 代码长这样Data public class EmployeePageQueryDTO implements Serializable { private static final long serialVersionUID 1L; private String name; private Integer page; private Integer pageSize; }序列化接口是我做项目时的一个习惯。只要项目里有分布式部署或者接口透传的可能DTO 实现Serializable就能避免麻烦虽然现在单体开发感知不到但规范意识要提前养成。Mapper 层是关键点。查询条件里name是可选项所以 SQL 必须支持 name 为空时返回全部数据。这里我使用了 MyBatis 的where标签结合if标签来动态拼接条件select idpageQuery resultTypecom.sky.entity.Employee select * from employee where if testname ! null and name.trim() ! and name like concat(%, #{name}, %) /if /where order by create_time desc /select为什么要用concat(%, #{name}, %)而不是直接写% #{name} %因为直接拼接字符串存在 SQL 注入风险而且 #{} 预编译更安全。用concat也是规避传参时百分号或下划线被当成通配符的隐患保证查询返回的是你真正想模糊匹配的内容。还有一个细节我踩过坑if testname ! null只判空是挡不住空字符串的。前端如果传了个空串过来SQL 就会变成and name like %%虽然结果也是查全表但这条垃圾条件白白参与执行。所以我加了name.trim() ! 的判断并且用trim()方法过滤了用户输入两端可能误带进来的空格。Controller 层测试接口路径是GET /admin/employee/page用ResultPageResult作为统一返回类型PageResult是我们项目自己封装的分页结果对象内部只含total和records两个字段。这样前端拿到结构固定不用关心底层到底用的什么分页插件。到这里员工分页查询的本体就完成了。整个过程难点不多但对 MyBatis 动态标签的熟练度是一次很好的检验。如果你做的时候发现PageHelper没生效可以先排查拦截器是否正确注册到了 MyBatis 配置里这个问题在注解式配置时特别容易遗漏。2.2 启用禁用员工update 语句、状态字段与前端联动启用禁用员工的核心是修改employee表里的status字段。状态码我用的 1 表示启用0 表示禁用这是业务系统里最常见的枚举定义。接口设计为POST /admin/employee/status/{status}路径参数直接接收要改成的目标状态。这里特意选了 POST 而不是 PUT是因为操作本质是修改单个字段值语义上用 POST 更接近 动作而且和前端请求方式容易对齐。Service 层实现的思路大概是根据前端传入的员工 id 和 status先判断要修改的员工是否存在不存在直接抛异常存在则更新状态和时间戳。public void startOrStop(Integer status, Long id) { Employee employee Employee.builder() .id(id) .status(status) .updateTime(LocalDateTime.now()) .build(); employeeMapper.update(employee); }推荐直接构造一个只有目标字段的实体传入 Mapper而不是把整个 Employee 对象从库里查出来再改回去。这样既能减少一次数据库查询也能让 SQL 只更新需要更新的字段降低并发更新冲突的概率。我 SQL 里用了set标签来动态生成 update 语句哪些字段有值就更新哪些字段灵活且安全。update idupdate parameterTypecom.sky.entity.Employee update employee set if testname ! nullname #{name},/if if teststatus ! nullstatus #{status},/if if testupdateTime ! nullupdate_time #{updateTime},/if /set where id #{id} /update启用禁用这个接口还有个重要旁支员工状态变化后前端的登录 Token 怎么办。由于我们的 JWT 登录校验发生在拦截器里而拦截器只是验证 Token 是否有效、用户是否存在不会每次都查数据库核对 status所以封锁用户的当次登录会话并不能立即失效。这个点其实是个安全隐患也是很多系统被白名单账号反复登录攻击的漏洞。后续如果要做增强可以在 JWT 里加入状态版本号或者在用户操作敏感接口时校验状态字段。2.3 统一结果封装与全局异常处理每个接口的返回值我都没有直接返回Employee对象或者裸的布尔值而是全部包了一层ResultT。这个封装类内部状态码设计为1表示成功0表示失败public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success() { ResultT result new Result(); result.code 1; result.msg success; result.data null; return result; } public static T ResultT error(String msg) { ResultT result new Result(); result.code 0; result.msg msg; result.data null; return result; } }前端开发拿到的响应格式统一、规整联调时不需要为每个接口做特判。而且全局异常处理器会捕获业务异常统一转成Result.error()返回而不是把堆栈信息甩给前端。这不仅仅是代码整洁问题也是防止内部信息泄露的底线做法。具体来说我在异常处理里自定义了AccountNotFoundException、PasswordErrorException、DeletionNotAllowedException等业务异常然后用RestControllerAdvice统一拦截。业务方法里只需要抛出自定义的异常外层就会自动包装成Result.error(msg)不用每个 Controller 都写 try-catch。3. 分类管理模块实现新增、分页、删除、修改的完整闭环3.1 分类与菜品套餐的关联关系设计分类管理在苍穹外卖里是一块容易被低估的模块。表面上分类就是给菜品和套餐分个组其实它承载着商品组织的层级结构。我的表结构设计中category表包含id、type、name、sort、status五个核心业务字段。其中type字段区分分类类型1是菜品分类2是套餐分类。设计这个字段的原因很直接——菜品和套餐虽然有各自的挂靠逻辑但在后台管理界面里它们共用一套分类管理页。想减少一套重复的 CRUD 代码就通过 type 区分业务归属。而sort字段是用来控制展示顺序的。很多新手会把排序交给前端做但前端排序只是局部的一旦换设备、换账号顺序就乱了。把 sort 存在后端配合查询的order by sort asc才能保证所有端展示的顺序完全一致。分类列表查询我用的同样是 PageHelper 分页只不过增加了type可选条件和name可选条件select idpageQuery resultTypecom.sky.entity.Category select * from category where if testname ! null and name.trim() ! and name like concat(%, #{name}, %) /if if testtype ! null and type #{type} /if /where order by sort asc, id desc /select排序字段加两个的用意是当 sort 相同时按 id 倒序保证先创建的分类排在前面避免分页结果顺序摇摆不定。分页偶发性乱序的问题在并发插入时会尤其明显提前在 SQL 层面把排序定死一劳永逸。3.2 新增分类与修改分类唯一性与状态校验新增分类时最容易犯的错就是不做重名校验。同一个分类名被插入两条商品管理页就会出现两个长得一模一样的分类给运营造成极大困惑。我在 Service 层新增分类的逻辑是这样的public void save(CategoryDTO categoryDTO) { // 分类名唯一性校验 int count categoryMapper.countByName(categoryDTO.getName()); if (count 0) { throw new CategoryNameDuplicateException(该分类名称已存在); } Category category new Category(); BeanUtils.copyProperties(categoryDTO, category); category.setStatus(1); // 新增分类默认启用 category.setCreateTime(LocalDateTime.now()); category.setUpdateTime(LocalDateTime.now()); categoryMapper.insert(category); }注意这里我默认把新分类的状态置为 1启用。如果业务上要求新增分类默认停用也就是后台管理员确认过内容后才上线那就把默认值改成 0。这个默认策略不同公司差异很大开发时直接问清楚业务方就可以。唯一性校验用countByName这个 Mapper 方法是在库里精确查找同名分类数量。有人会问为什么不查 list 再在代码里判断因为在数据库里直接计算数量显然更快也更省流量更重要的是为后续唯一索引做铺垫数据库层面加唯一索引兜底即使代码并发漏查数据库也能挡住重复数据。修改分类的逻辑和新增类似只是在update时要把分类名称唯一性校验排除自身。比如我把 id1 的分类名从 热菜 改成 凉菜如果 凉菜 是另一个分类的名字那就不允许修改但如果查重 SQL 不过滤自身 id连自己原来叫 热菜 都会被挡下来。修改分类接口我设计成PUT /admin/category请求体是CategoryDTO。因为修改场景往往需要全量更新 category 对象的多个字段所以我直接把整个 DTO 传进来Copy 属性后执行 update。public void update(CategoryDTO categoryDTO) { // 修改分类时需要排除自身查重 int count categoryMapper.countByNameAndId(categoryDTO.getName(), categoryDTO.getId()); if (count 0) { throw new CategoryNameDuplicateException(该分类名称已存在); } Category category new Category(); BeanUtils.copyProperties(categoryDTO, category); category.setUpdateTime(LocalDateTime.now()); categoryMapper.update(category); }注意BeanUtils.copyProperties这个工具类有个经典大坑如果两个对象的属性名不一致或者源对象中为 null 的字段把目标对象的已有数据覆盖了就会产生隐蔽 bug。所以用完之后务必核对目标对象的最终字段值。我在开发时会在 update 前打印一遍 category 对象的完整内容确认没有误传 null 才执行。3.3 删除分类为什么要判断关联数据删除分类接口是最考验业务敏感度的地方。如果分类下面已经挂了菜品或套餐这时直接把分类删除商品就变成 无家可归 的状态前端菜单里展示会出现脏数据。我在 Service 层删除之前做了这样的判断public void deleteById(Long id) { // 查询当前分类是否关联了菜品或套餐 Integer dishCount dishMapper.countByCategoryId(id); if (dishCount 0) { throw new DeletionNotAllowedException(当前分类下存在菜品无法删除); } Integer setmealCount setmealMapper.countByCategoryId(id); if (setmealCount 0) { throw new DeletionNotAllowedException(当前分类下存在套餐无法删除); } categoryMapper.deleteById(id); }这个逻辑步骤是先查dish表按 category_id 统计数量再查setmeal表做同样的统计。只要任一个统计结果大于 0就拦截删除并抛出详细异常。数据库开发里这个思路叫外键约束的软实现很常见——不依赖数据库的物理外键而是通过代码保证业务完整性。为什么故意不用数据库外键因为外卖系统的高并发场景下物理外键会让每次插入都去检查主表增加锁竞争和数据库开销而且后期分库分表时外键会变成牵绊。所以这种业务强一致靠 Service 层保证的做法是互联网企业的主流方案也是我在这里推荐的原因。如果删除分类时必须连带删除菜品那就不只是这里查一下数量那么简单了还需要事务控制把删除菜品和删除分类包在同一事务里。不过苍穹外卖 Day3 的需求尚不涉及级联删除这里保守处理就够了。3.4 启用禁用分类与菜品状态的联动思考分类状态接口和员工状态接口长得几乎一样同样是POST /admin/category/status/{status}。但分类状态背后有个值得深思的联动问题一个分类被禁用后它名下的所有菜品还能正常售卖吗答案是当前版本里分类禁用不影响菜品的 status。这意味着如果你禁用一个分类但菜品列表中这个分类下的菜品仍然显示 启售 状态这就出现业务矛盾了。我在实现时留了一个 TODO 思路如果后续产品要求 禁用分类连带下架其下所有菜品那在修改分类 status 后需要再执行一条更新语句update dish set status 0 where category_id #{categoryId}这个联动更新最好放在同一个事务里避免只改了一半数据的情况。如果你是自己练手想做得更完整这一步是很好的扩展点面试时讲出来会显得思考深度明显不同。4. 常见问题与排查技巧实录4.1 PageHelper 分页不生效的三种典型原因我在当天的开发过程中PageHelper 一共出过三次问题场景各不一样但最后都能归类到几个固定原因。第一次是分页失效查出来是全表数据。排查到根因是PageHelper.startPage()和 Mapper 查询之间隔了一条其他 SQL插件的拦截器无法判断哪条 SQL 才是真正要分页的那条于是就把分页作用到了我先执行的那条错误 SQL 上。解决办法很简单把 startPage 紧贴查询代码中间一行多余代码都不要有。第二次是内存分页问题数据量大时页面卡成幻灯片。原因是分页插件没有被正确配置成物理分页导致先查出全量数据再在内存里截断。搭项目时我用的完全是默认配置没注意到 MyBatis 配置里缺少拦截器注册。解决办法是在 MyBatis 配置类里显式加入Bean public PageInterceptor pageInterceptor() { PageInterceptor pageInterceptor new PageInterceptor(); Properties properties new Properties(); properties.setProperty(helperDialect, mysql); pageInterceptor.setProperties(properties); return pageInterceptor; }第三次是分页查询总数 total 一直不对。这个比较隐蔽是因为我项目里配置了pagehelper.returnPageInfo而我又对 Mapper 查询结果做了二次stream转换把 Page 对象变成了 List导致分页信息在转换中丢失。解决办法是不要直接返回 List而是把分页元数据单独组装成PageResult返回。4.2 MyBatis 动态 SQL 拼接中 where 条件的隐藏问题动态 SQL 里最经典的问题是三个查询条件用户什么都不填时SQL 末尾可能出现一个多余的where 11或者因为where标签使用不当直接语法错误。where标签的核心能力是如果内部有满足条件的if它会自动生成WHERE关键词如果内部if全部不满足它就不生成WHERE。它还会智能去除第一个条件的AND前缀。但我开头提到如果你传入了空字符串if判断的结果仍是 true就会生成一条类似AND name LIKE %%的条件虽然不至于报错但多余条件会误导索引。所以凡是字符串条件我统一用name ! null and name.trim() ! 双重判断。还有一个常见错误是把order by写进了where内部。注意where只处理条件筛选排序必须写在/where之外。我第一次写动态 SQL 时把排序放在了 where 内SQL 直接报错后来养成了写完动态 SQL 先 console 打印完整 SQL 的习惯再联调。4.3 本地联调时前端联调工具与 CORS 的配合Day3 的接口写完我直接用 Swagger 和 Postman 做了测试这是最快的单接口验证路径。但前端联调时遇到跨域报错浏览器拦截了来自localhost:8081的 AJAX 请求原因是后端地址是localhost:8080两边端口不同。解决办法是项目里实现了一个WebMvcConfigurer添加跨域映射Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); }注意allowCredentials(true)时allowedOriginPatterns不能写*否则浏览器会直接报错因为携带凭证的跨域请求不允许通配来源。这也是一个容易翻车的细节。做完这些配置我把前端启动后直接用登录好的账号访问员工列表和分类列表发现页面数据能正常渲染分页组件显示的总条数和数据库一致整个联调才算干净收尾。5. 一些实操心得和值得扩展的方向5.1 写代码前的表结构评审比写代码本身更值钱Day3 全部功能实现完回头看最有价值的环节反而不是 Controller 和 Mapper 的堆码而是动工前对表结构的评审。employee表有一个字段很容易忽略——create_user和update_user它们记录的是当前操作人的 id。我在新增员工时如果忘了从 Token 上下文里取出当前用户 id 回填就会导致员工创建记录里查不到是谁建的后续追责和审计都会扑空。这种字段如果不提前在设计阶段明确写完功能后靠记忆补是非常痛苦的。所以我的习惯是开始写 Mapper 之前先通读一遍表结构把所有带默认值、审计性质、逻辑删除标记的字段都列出来逐个确认哪些在 Service 层要显式赋值哪些数据库会自动维护。宁可花 10 分钟把字段理清也不要写一半再回头改 SQL。5.2 接口接收参数的校验不能只靠前端Day3 的入参像 page、pageSize、status 这些前端会自动传但接口要面对的是不完全受控的调用方。我在EmployeePageQueryDTO和CategoryDTO里都加了NotNull、Min这类校验注解并在 Controller 参数前加了Valid。如果 pageSize 传成 0 或者负数直接拦截返回参数错误比数据库报错后前端一脸懵要友好得多。Data public class CategoryDTO implements Serializable { NotNull(message 分类名称不能为空) private String name; NotNull(message 分类类型不能为空) private Integer type; private Integer sort; private Long id; }这里也有个细节修改分类时 id 不能为空新增分类时 id 必须为空。如果两个动作共用一个 DTO那就要在 Service 层根据动作类型做不同的字段校验Controller 层统一加太多限制反而会把两个场景互卡。5.3 这天的代码完成后建议你顺手补上的两个扩展点第一员工密码加密策略。Day3 里的员工密码我看了一下主要是通过DigestUtils.md5DigestAsHex(password.getBytes())做了一次 MD5 存储。MD5 在安全要求高的环境里显然不够至少应该换用 BCrypt 这类带盐的哈希算法。如果你是跟着课程练手课后可以自己动手把登录校验和密码存储改成 BCrypt这对理解密码学基础很有帮助。第二操作日志的埋点。员工管理、分类管理都属于后台敏感操作线上环境一旦误操作没日志基本无法追溯。DAY3 的代码里没有 AOP 日志切面但这恰恰是学习 Spring AOP 的绝佳场景。你可以自定义一个OperationLog注解在 Controller 方法上标注然后用 AOP 切面记录操作人、操作时间、请求参数、返回结果。这样做完不仅日志功能有了你对 AOP 的理解也会蹭蹭往上涨。这两件事做完Day3 的复习深度就超过了单纯照着敲一遍代码的层次。项目经历写进简历时你能讲清楚的问题也会多出很多。5.4 关于 Day3 的最后一个经验做这套项目时我最大的感受就是教程能帮你把骨架搭好但填肉的过程才是真正的学习。照抄代码时你永远不知道为什么这个方法要拆出来封装为什么查询条件判空要多一步 trim为什么删除前要查关联数据。只有当你自己把某个功能写坏一次、排查过一次、再修好一次这些点才会真正内化成肌肉记忆。Day3 虽然只是整个苍穹外卖项目的一小步但员工管理和分类管理这两个模块已经把 Java 后端开发最日常的开发范式完整演练了一遍DTO 设计、Service 校验、Mapper 动态 SQL、统一异常处理、分页插件、软删除判断。把这些练扎实后面 Day4 的菜品管理、文件上传你上手速度会翻倍。如果你也正在学这套项目欢迎在评论区和我交流你踩到的坑尤其是分页插件和动态 SQL 这两块不同版本、不同数据库方言的坑都各有花样聊起来才最有价值。

相关新闻

PHP 8新特性实战与迁移指南:从7.4升级到JIT优化

PHP 8新特性实战与迁移指南:从7.4升级到JIT优化

1. 为什么我把PHP 8又系统学了一遍说实话,做PHP开发这么多年,从PHP 5.2一路用到PHP 7.4,一开始听说PHP 8发布的时候,我内心是有点抗拒的。毕竟PHP 7.4已经很稳了,项目跑得好好的,何必折腾?但真正…

2026/10/10 13:11:04 阅读更多 →
Hydra v9.1 协议级爆破实战:Windows 编译、精准参数与无感扫描

Hydra v9.1 协议级爆破实战:Windows 编译、精准参数与无感扫描

简介:九头蛇安全测试工具(Hydra v9.1)Windows适配版,是一款面向网络安全初学者与渗透测试实践者的合法合规暴力破解辅助工具,适用于密码强度评估、服务认证机制验证等教学与研究场景。资源包为完整可执行环境&#xff…

2026/10/10 13:11:04 阅读更多 →
强化学习训练看板解读:MiMo-v2.6指标体系全解析

强化学习训练看板解读:MiMo-v2.6指标体系全解析

1. 这不是普通监控页面,而是一张“训练生命体征图谱”如果你刚接触强化学习项目,看到“MiMo-v2.6 RL 训练看板”这几个字,第一反应可能是:又一个带曲线图的网页界面?点开发现一堆缩写、跳动的数字、颜色不一的折线——…

2026/10/10 13:10:02 阅读更多 →

最新新闻

Android Studio AI 编程新阶段:Gemini Agent Mode 接入 TaoToken 的 MCP 配置与 API Key 验证

Android Studio AI 编程新阶段:Gemini Agent Mode 接入 TaoToken 的 MCP 配置与 API Key 验证

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

2026/10/10 19:51:18 阅读更多 →
蓝牙6.0与WiFi 7上车:车规级无线通信模块的设计与实践

蓝牙6.0与WiFi 7上车:车规级无线通信模块的设计与实践

1. 为什么智能汽车的无线通信突然不够用了?智能汽车跑到现在,座舱里堆满了大屏、雷达、摄像头、域控制器,算力早就够卷了,可通信链路反而是被忽视的死角。我这两年跟不少Tier 1和OEM的工程师打交道,大家普遍反映一个尴…

2026/10/10 19:51:18 阅读更多 →
棋牌透视111非外挂:内部调试工具的架构与安全实践

棋牌透视111非外挂:内部调试工具的架构与安全实践

作为一个在棋牌游戏开发运维圈子里混了十来年的老家伙,我见过太多把“调试辅助工具”和“作弊外挂”混为一谈的萌新,也见过不少正经项目组把内部测试工具的管理不当当回事,最后惹出乱子的案例。今天我想借“棋牌透视111”这个具体工程名&…

2026/10/10 19:51:18 阅读更多 →
JavaScript实战避坑指南:从类型判断到跨浏览器兼容

JavaScript实战避坑指南:从类型判断到跨浏览器兼容

写JavaScript快十年了,经常有朋友问我:“这语言到底该怎么系统学?”说实话,JavaScript入门门槛不高——写个弹窗、改个样式,安装一个编辑器就能上手。可一旦你在真实项目里踩到“数据明明判断对了却报错”、“事件绑定…

2026/10/10 19:51:18 阅读更多 →
后端面试六个回答逻辑:从知识储备到清晰表达的实战指南

后端面试六个回答逻辑:从知识储备到清晰表达的实战指南

后端面试,最让人头疼的往往不是题有多难,而是明明会的东西一说就乱。这些年我面过不少候选人,也帮很多朋友做过模拟面试,发现一个共性:大部分人不是输在知识储备上,而是输在"怎么开口"上。面试官…

2026/10/10 19:51:18 阅读更多 →
单点工具还是全家桶:supervision 与 SAHI、ByteTrack、OpenCV 的边界之争

单点工具还是全家桶:supervision 与 SAHI、ByteTrack、OpenCV 的边界之争

单点工具还是全家桶:supervision 与 SAHI、ByteTrack、OpenCV 的边界之争 【免费下载链接】supervision We write your reusable computer vision tools. 💜 项目地址: https://gitcode.com/GitHub_Trending/su/supervision 计算机视觉开发者长期…

2026/10/10 19:50:17 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/10 10:38:42 阅读更多 →