SpringBoot实战:构建C2C二手交易保障系统
1. 项目核心思路与系统设计做二手闲置交易系统我是有备而来的。几年前我自己在闲鱼上买卖过几台相机和镜头体验过卖家挂出去没人问、买家私聊半天不敢付款的尴尬也见过同学在QQ群里卖旧书被骗子坑了押金。所以当接到这个基于SpringBoot的闲置物品循环交易保障系统的项目需求时我第一反应就是这不是一个普通的CRUD练习真正难的是保障两个字。用户之间的信任怎么建立交易资金的流转怎么保证安全出现纠纷怎么仲裁这一整套逻辑才是系统的灵魂。这个项目本质上是一个C2C的二手交易平台核心用户是高校学生和社区内部人群买卖双方在同一个校园或社区里既有线下当面交易的便利又有线上撮合的需求。和闲鱼、转转这类大平台相比这个系统的定位更轻量但轻量不代表简陋反而因为用户规模可控、交易场景集中我们可以把保障机制做得更细。技术栈上选了SpringBoot作为后端骨架理由很直接SpringBoot的自动配置和约定优于配置特性能让开发重心从配置环境转移到业务逻辑上而且国内中小型项目里SpringBoot的生态最成熟招人容易、排错有社区经验、后续接小程序端或者管理后台都有现成方案。整个系统从需求上拆可以分成四个核心板块用户与信用体系注册登录、实名认证、信用积分。买家卖家的信用评价是二手交易信任的基础。商品与检索闲置物品发布、分类浏览、关键词搜索、状态管理在售、预约中、已售出、下架。交易保障引擎订单生成、资金冻结与解冻担保交易、取消订单、确认收货、售后与争议仲裁。这是整个系统最有价值的部分。通知与运营站内消息、订单状态变更通知、定时任务自动取消超时未支付订单、定期清理失效商品。这里有一个容易被新手忽略的设计点资金流和订单流要分离。订单表只记录交易状态资金冻结/解冻要通过虚拟钱包流水表去记录不能直接在订单表里加一个买家已付款的布尔字段完事。为什么因为一旦需要退款、部分退款、平台抽取手续费这些操作资金流水表就是唯一的账本依据没有流水表你根本说不清楚这笔钱经历了什么。这个设计是后来做代码讲解时反复强调的重点。场景上这个系统适合三类人参考第一类是正在做毕设的计算机专业学生这个题目比普通的图书管理系统有亮点得多交易保障机制拿出来讲就很加分第二类是准备接外包项目的开发者可以把它当作一个中小型电商项目的模板第三类是自己想搭一个校园二手平台的社团或创业团队直接用这套源码改改就能用。我后面会用一整篇文章的篇幅把代码结构、核心功能的实现思路、部署文档的完整操作步骤、还有我在实际跑这个项目时踩过的坑全部展开。源码、部署文档、代码讲解这三件套我会合在一起讲因为只给源码不给文档等于白给只讲文档不拆代码你也学不到东西。2. 代码讲解从工程骨架到核心业务链路2.1 工程结构与分层设计解读一个SpringBoot项目拿到手先别急着跑起来第一步是看懂目录结构。这个项目的工程结构是我刻意按主流企业级规范组织的包名是com.campus.secondhand核心结构如下secondhand-system/ ├── src/main/java/com/campus/secondhand/ │ ├── controller/ # 控制层HTTP接口 参数校验 │ ├── service/ # 业务层事务边界、业务规则 │ │ └── impl/ # 业务实现类 │ ├── mapper/ # 数据访问层MyBatis-Plus持久层 │ ├── entity/ # 数据库实体 │ ├── dto/ # 前端交互的数据传输对象 │ ├── vo/ # 视图返回对象聚合字段 │ ├── config/ # 配置类WebMvc、拦截器、Redis、定时任务 │ ├── exception/ # 统一异常体系 │ ├── common/ # 通用返回体、分页参数、常量枚举 │ └── util/ # 工具类JWT、日期、脱敏 ├── src/main/resources/ │ ├── mapper/ # MyBatis XML文件 │ ├── application.yml # 核心配置 │ ├── application-dev.yml │ └── sql/ # 数据库初始化脚本 └── pom.xml这个结构真正想解决的问题是职责边界。我见过太多毕设工程是controller里直接写SQL、service层空空的、所有代码堆在两个类里的。这种代码跑起来没问题但后续扩展和维护就是灾难。比如项目做到后面要加一个举报功能如果代码分层清楚你只需要新增一个ReportController、ReportService、ReportMapper原有代码一行不用动如果是大泥球结构加功能就可能要改动十几个地方改完还可能把原来的功能改崩了。分层设计还有一层更深的意思事务边界的控制。在Spring里事务是通过AOP代理实现的默认情况下Transactional加到service实现类的方法上才生效。如果把数据库操作直接写在controller里事务注解可能会失效到时候数据写到一半崩溃了你会发现库里留下一堆脏数据。这个项目里所有涉及到多个表的写操作比如创建订单要同时更新商品状态、生成流水、扣减库存这些必须在service层的一个事务方法中完成。2.2 认证与授权模块代码解读认证这块我用的是JWTJSON Web Token 拦截器的方案没有引入Spring Security那套全家桶。为什么因为Spring Security的过滤器链和配置体系对新手来说太劝退了对这类体量的项目来说有点杀鸡用牛刀。JWT的核心思路是用户登录成功后服务端生成一个包含用户ID和过期时间的签名Token前端存在本地每次请求放在Header里带上服务端通过拦截器验签解析出当前用户。JWT的签名验证过程值得展开讲一下。Token生成时服务端用HMAC-SHA256算法以密钥jwt.secret对用户ID 过期时间做签名。后续请求到达时拦截器先检查Header里的Authorization是不是以Bearer开头然后解析Token、比对签名、检查是否过期全部通过才放行。这里有一个非常关键的设置用户ID是从Token里解析出来的绝对不能信任前端传过来的userId参数。我在代码讲解里特意标红了这一点因为很多二手交易系统的漏洞就出在这——用户下单时把请求里的userId改成别人的ID就把别人的商品买走了或者把别人的余额扣了。正确做法是只从前端拿商品ID用户信息一律从request.getAttribute(currentUserId)里取这个值是拦截器解析Token后放进去的。密码存储用的不是MD5而是BCrypt。BCrypt和MD5的区别在于BCrypt是加盐的慢哈希同一个密码每次生成的密文都不一样而且计算速度天然很慢这让彩虹表攻击基本失效。在密码加密这块我建议你在项目里直接引入spring-security-crypto这个依赖只使用里面的BCryptPasswordEncoder不需要引入整个Spring Security。2.3 商品发布与检索功能拆解商品模块看起来是个标准的CRUD但这套系统里它有两个不标准的点图片处理和检索方案。图片处理方面项目里的方案是前端把图片上传后后端先把图片存储到本地的/uploads目录也可以配成阿里云OSS然后通过一个PictureController提供静态资源访问接口。这里要特别注意的点是跨域问题如果你把上传接口和静态资源访问放在同一个后端服务里前端Vue页面在访问图片时用的是http://ip:8080/uploads/xxx.jpg这种完整路径那么浏览器的跨域策略会拦截。解决方式有两种一是后端配置CORS允许跨域访问这些资源路径二是通过Nginx把/uploads路径反向代理到后端服务。我实际测试下来的结论是第二种更稳妥因为Nginx处理静态资源的性能比Tomcat好得多而且配置好之后前后端分离部署也不会再有跨域的坑。检索方案上我选择了MySQL的全文索引配合标题 分类 标签的组合条件查询。搜索逻辑不是简单地用LIKE %keyword%因为没有谁会想搜一本书的时候把数据库里几千条记录全部扫一遍。使用全文索引MATCH(title) AGAINST(keyword IN NATURAL LANGUAGE MODE)之后检索速度快了不少还能按相关度排序。但是这里也有个坑MySQL的全文索引对中文分词的支持很弱默认按空格分词中文句子没有空格全文索引基本等于没用。我在项目中实际的替代方案是对搜索关键词做简单的N-gram切分——按两个字符一组切词再全部拼成Like条件。比如用户搜机械键盘程序切出的词是机械和键盘查询条件就是title LIKE %机械% OR title LIKE %键盘%或者两个条件都有时用AND。这个土办法对中文搜索完全够用而且逻辑简单、没有任何外部依赖。商品状态流转是另一个容易写错的点。我在实体里用枚举GoodsStatus表示商品状态ON_SALE在售、RESERVED被预约、SOLD已售出、OFF_SHELF下架。状态机的转换规则通过service方法控制不是前端想改就能改的。比如买家下单付款后商品状态自动从ON_SALE变为RESERVED此时其他买家再想下单系统直接抛商品已被预约的异常。之前有个朋友参考这套代码做自己的项目他省略了这个状态结果出现了一个商品被三个人同时拍下的情况最后只能手动在数据库里改数据非常狼狈。2.4 交易保障核心担保支付与争议仲裁这是整个系统最有含金量的模块也是代码讲解里最长的部分。整个担保交易的流程我梳理成一张状态机买家下单 - 资金冻结买家账户余额锁定 - 卖家发货商品变为已售出 - 买家确认收货 - 资金解冻并转入卖家账户 - 交易完成这里面的每一步都有对应的数据库操作。拿买家确认收货这一步举例它涉及到同时更新三张表订单表状态改成COMPLETED钱包流水表新增一条入账记录给卖家加钱买家卖家的信用积分各自加上交易成功分。这三个操作必须在同一个事务里任何一个失败都要全部回滚。我在代码里通过Transactional(rollbackFor Exception.class)来控制在代码讲解时专门强调了一点rollbackFor一定要设置因为Spring默认只在抛出RuntimeException时才回滚如果抛的是IOException这类受检异常事务不会自动回滚。我自己就犯过这个错误买家付款后流水记录插入失败但订单状态还是显示已付款结果对账的时候怎么都对不上。争议仲裁机制是这个项目比普通电商项目多出来的亮点模块。交易中如果买家说收到的商品和描述不符可以发起售后订单状态进入DISPUTED争议中此时双方都可以上传证据文字和图片管理员或者平台方在后台进行仲裁仲裁结果分两种全额退款给买家或者把钱打给卖家。这个模块在设计上值得展开的是权力分离在争议处理期间买家和卖家都不能操作这个订单所有操作权限归仲裁角色。这能避免买家一边申请退款一边把钱确认给卖家这种矛盾的并发操作。仲裁结果记录在arbitration_records表里里面包含仲裁原因、证据描述、处理结果、操作人ID这些是审计要用的原始数据千万不能删。担保交易模式还有一个必须处理的场景超时未确认收货。买家一直不点确认收货钱一直冻着卖家的体验会非常差。我的方案是开启一个定时任务自动确认收货。具体规则是订单在已发货状态超过7天系统自动执行确认收货逻辑。这个定时任务是用SpringBoot自带的Scheduled注解实现的每10分钟扫描一次订单表把超时的订单批量处理。使用的时候要小心默认情况下Scheduled只有一个线程在跑如果你的任务是长任务下次触发时间到了前一个还没跑完会造成任务堆积。这个系统的定时任务逻辑很简单单线程完全够用但如果你要加复杂的定时任务建议上Async异步执行或者引入XXL-JOB这类分布式调度框架。2.5 消息通知与定时任务消息通知模块我最初是用站内信去做的后来加了一个交易状态变更之后通过邮件通知用户的增强。站内信实现很简单通知表里记录user_id、content、is_read、create_time用户登录后拉取最近未读消息即可。但这里有一个隐藏的坑如果用户在多个设备登录消息的已读状态是全局的——一台设备上已读了另一台设备的红点就没了。有的项目要按设备维度去处理已读但对于这个系统全局已读就够用了。定时任务这块除了上面说的自动确认收货还有一个定期下架超180天未成交的商品。数据库里商品越积越多很多商品发布之后就再也没人问过挂着也是占位不如自动下架。这个逻辑也是一个Scheduled(cron 0 0 3 * * ?)每天晚上三点跑一次把update_time超过半年的在售商品批量改成下架状态。这里我要额外提醒一个和SpringBoot版本有关的问题。有些新项目用的SpringBoot 2.6Scheduled的默认行为有一些变化比如spring.main.allow-bean-definition-overriding从默认false变成了true等。如果你的项目是跟着一些旧教程写的把版本升上来之后定时任务突然不跑了十有八九是配置或Bean扫描的坑。我们在部署文档里会专门列一张版本兼容对照表。3. 部署文档从零到云服务器上线3.1 环境准备与基础设施选型这个项目跑起来需要的环境其实不多我给一个经过验证的最低配置清单依赖组件版本要求用途说明JDK1.8 及以上运行SpringBoot应用Maven3.6构建打包MySQL5.7 / 8.0主数据库Redis5.0缓存与登录Token黑名单Node.js14仅前端Vue构建需要Nginx1.20反向代理和静态资源托管开发机是Windows/Mac都行测试环境的Linux服务器我用的是CentOS 7.9。这里我建议你在学习阶段不要一上来就上容器先把传统部署跑通了再考虑Docker。因为Docker虽然方便但里面任何一个环节出错端口映射、数据卷挂载、容器间网络排查的复杂度对新手不友好。等你在传统方式上把项目跑熟了再去看仓库里附带的docker-compose.yml那时候理解起来会快很多。版本选择方面有一个重要提醒JDK版本不要太新。JDK 17在某些SpringBoot 2.x版本上是兼容的但如果你跟我一样用的是JDK 8就完全不要升级。很多同学在部署时图新鲜装了JDK 21然后各种依赖报错最后拉低效率的还是自己。SpringBoot 2.7.x JDK 8 是我最终验证过的黄金组合。3.2 数据库初始化与配置文件解析数据库初始化脚本放在resources/sql/目录下文件名是secondhand.sql里面包含建库建表和基础数据脚本。执行方式我推荐用命令行直接在服务器上执行mysql -u root -p secondhand.sql如果你用Navicat或DataGrip直接选择运行SQL文件也可以。执行完以后库里会有12张表分别是用户表、商品表、订单表、钱包表、钱包流水表、商品图片表、收藏表、消息通知表、争议仲裁表、评价表、信用积分变更记录表、系统配置表。配置文件这一关是整个部署流程里最容易出错的地方。我把核心的application.yml拆开解释一下server: port: 8080 servlet: context-path: / spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/secondhand?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto jwt: secret: your-custom-secret-key-please-change expire-hours: 24配置里的三个细节值得特别强调serverTimezoneAsia/Shanghai这个不写会有时差问题。很多人查数据库时发现时间比正常时间早8个小时就是因为没配时区。MySQL 5.7之后默认时区是美国时区必须手动指定。map-underscore-to-camel-case: true这个配置让数据库的create_time字段自动映射到Java实体的createTime属性。不配的话你只能写一大串TableField(create_time)注解纯属浪费时间。JWT密钥一定要替换成自己的长随机字符串。用默认密钥上线的话别人只要知道你的密钥就能伪造管理员Token。除了application.yml这个项目还有application-dev.yml和application-prod.yml。我习惯把Redis地址、日志级别、上传文件路径这些按环境拆开。启动时通过--spring.profiles.activeprod指定用哪套配置这样做的好处是本地开发用dev配置连localhost的Redis线上用prod配置连云服务的Redis不用每次上线前改代码。3.3 打包与部署的两种主流方式SpringBoot项目的打包方式很简单使用Maven的package命令即可mvn clean package -DskipTests构建成功后在target/目录下会生成一个secondhand-system-0.0.1-SNAPSHOT.jar这个jar包就是完整的可运行产物。SpringBoot内置了Tomcat所以这其实是一个可以直接运行的java进程。启动命令java -jar secondhand-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod如果想让它在后台长期运行用nohup配合日志重定向nohup java -jar secondhand-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 这个命令要拆开解释一下nohup是让进程忽略终端挂断信号就算你关掉SSH窗口它也不会退出 app.log是把标准输出写进日志文件21是把错误输出也合并到同一个日志文件结尾的是让命令在后台执行。这四个部分少一个都不行有同学只用了java -jar然后直接关SSH再连上发现进程已经没了就是这个原因。另一种我看着现在很流行的方式是宝塔面板部署。宝塔上操作SpringBoot项目其实有两种一种是直接添加Java项目另一种是通过Docker容器运行。用宝塔的Java项目管理器部署时它会自动帮你设置进程守护进程崩了会自动拉起比手动写nohup省心一点。用Docker的话仓库里也配好了Dockerfile和docker-compose.yml。核心的Dockerfile是这个样子的FROM openjdk:8-jre WORKDIR /app COPY target/secondhand-system-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar, --spring.profiles.activeprod]整个构建过程是把jar包打进一个只有JRE运行环境的精简镜像里。打镜像的命令是docker build -t secondhand-system:latest . docker run -d --name secondhand-system -p 8080:8080 --restartalways secondhand-system:latest有一个需要特别提醒的点用Docker方式部署时MySQL和Redis不要也丢在Docker容器里除非你用docker-compose把三个容器一起编排了。否则容器之间的通信会变得很麻烦你需要在容器的数据库URL里改用宿主机IP不是localhost。3.4 部署后的验证清单很多同学把项目跑起来之后就以为部署成功了其实有一些隐藏的问题要逐一验证。我在部署文档里专门加了一个验证清单接口连通性访问http://服务器IP:8080/api/goods/list能返回商品列表JSON就说明Java进程正常。静态资源上传一张商品图片然后通过浏览器直接访问图片URL确认能显示。数据库连接在后台创建一个用户刷新数据库确认数据进去了这一步能发现字符集问题中文乱码就是一个典型。Redis连接登录接口调通后检查Redis里有没有生成Token记录没有就说明Redis配置有问题。定时任务改一下系统时间或者等一个周期确认自动上架任务执行了这里可以暂时将cron表达式改短来验证。我把这套验证清单放在部署文档的最后目的是让别人接手项目时不用猜一条一条照着试完就知道部署有没有成功。之前有个开发者来请教我说他的项目能打开登录页但登录之后所有接口都报401我让他看一眼Redis有没有Token一看就是Redis没配对。很多问题都是这样一开始看着像代码问题最后发现全是部署问题。4. 常见问题排查与避坑实录4.1 SpringBoot版本太高引发的连锁反应在写这个项目的源码讲解时我刻意把依赖版本都锁定在了SpringBoot 2.7.x。原因很简单SpringBoot 3.x把javax命名空间改成了jakarta很多老代码的import javax.servlet.*全部报错同时3.x最低要求JDK 17如果你还在用JDK 8根本跑不起来。所以遇到项目能编译但启动报ClassNotFound的问题第一反应先检查版本组合。另外有一个非常隐蔽的坑SpringBoot 2.6版本之后spring.mvc.pathmatch.matching-strategy默认值从AntPathMatcher改成了PathPatternParser。这个改动看起来很小但如果你写了类似/api/goods/**这种自定义拦截器路径在旧版本上运行没问题升级到2.6之后拦截器可能就失效了。解决方案是在配置文件里显式加上spring: mvc: pathmatch: matching-strategy: ant_path_matcher这个问题当时困扰了我一个下午最后是在本地把两个版本各跑一遍用mvn dependency:tree一行一行对比依赖才定位到的。吃一堑长一智后来我写项目时第一件事就是统一版本号用spring-boot-dependencies的BOM来管理依赖版本避免子依赖之间版本不一致。4.2 数据访问层的经典坑数据库字段与实体属性不一致这个项目用的持久层是MyBatis-Plus按理说自动映射已经很省事了但还有几个坑绕不过去第一Boolean和tinyint的映射问题。MySQL里推荐用tinyint(1)来存布尔值但如果你的字段名里有is_前缀比如is_deleted实体类里用Boolean isDeleted来接收MyBatis-Plus在某些版本里会产生歧义导致查询结果全是null。解决方案是加TableField(is_deleted)显式指定字段名或者在实体属性上避免使用is开头的命名。第二逻辑删除要配置全局。这个项目里用户、商品、订单都用了逻辑删除字段如果不配全局逻辑删除那每个查询的SQL里都要手写WHERE deleted 0漏掉一个就是查询结果不准确。我在配置里加了这两行效果是所有的selectById、selectList自动带上deleted 0条件mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 04.3 前后端联调最容易栽的跨域问题跨域问题我用一句话可以讲清楚浏览器安全策略规定一个域名下的网页不能直接发起指向另一个域名的Ajax请求。你在前端http://localhost:5173上跑Vue后端地址是http://localhost:8080端口不同属于跨域。调试时接口能通是因为后端配置了跨域过滤器但这个过滤器在上线后反而可能变成安全隐患所以我把跨域配置做了环境区分开发环境允许所有来源生产环境只允许自己的前端域名。实现时我是写了一个实现WebMvcConfigurer的配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .maxAge(3600); } }注意allowedOrigins(*)这个写法的局限性如果你开了allowCredentials(true)那么allowedOrigins就不能是通配符*必须写具体的域名否则浏览器会拦截。这也是一个我排查了很久才找到的坑。4.4 线上部署的其他隐蔽问题别人的项目部署后会出现数据库中文乱码这个问题的根源几乎都是数据库连接串没加characterEncodingutf8。我在部署文档里反复强调这个配置但还是有朋友没注意到结果商品标题变成了一堆问号。加在JDBC连接URL上注意不是配置文件的其他地方。另一个隐蔽问题是上传文件目录的权限。你在服务器上如果用root用户启动项目上传目录写文件没问题但如果用普通用户启动/uploads目录没有写权限就会报FileNotFoundException。解决方式也很简单给目录放开权限mkdir -p /opt/secondhand/uploads chmod 755 /opt/secondhand/uploads还有个容易被忽略的问题跟Linux文件系统有关。SpringBoot的jar包启动之后运行目录下的临时文件可能会被系统定时清理有些发行版自带systemd-tmpfiles-clean如果你的项目用到了/tmp来缓存上传文件磁盘空间会莫名其妙地下降。更稳妥的方案是把上传目录固定到非tmp路径比如/opt/secondhand/uploads。4.5 排查实战一次订单状态卡在已付款的复盘最后分享一个真实案例。测试人员在验收时发现一个订单的支付成功回调之后状态一直没有从待发货变成已发货数据库里查到的情况是订单表状态更新了但钱包流水表里同时出现了两条相同的扣款记录。我当时第一反应是重复请求导致的问题——支付回调接口被触发了两次。定位的方法很简单在支付回调的service方法入口加一段日志打出行参和当前线程号然后去调用方看日志果然发现回调通知组件因为没收到成功响应自动重试了一次。因为这两次请求是在同一时刻发出的所以都通过了幂等性校验结果重复扣款。解决方案是给支付回调加上分布式锁boolean locked redisTemplate.opsForValue().setIfAbsent(LOCK:PAY: orderNo, 1, Duration.ofSeconds(5)); if (!locked) { // log.info(重复回调直接返回成功); return; }加了锁之后第二次回调进来发现锁不存在第一次回调已经执行完并删除了锁但此时业务已经处理完成直接返回成功即可不会再重复处理。这个案例在代码讲解里讲得比较细因为它几乎囊括了分布式系统里最重要的三个思想幂等性、并发控制、日志追踪。5. 最后说点实在的项目做到这里源码、部署文档、代码讲解基本就闭环了。我在梳理这套东西时最大的感受是这个项目表面上是SpringBoot的一个综合练习实际上它真正训练的是交易系统中保障机制的设计能力。JWT做认证、MyBatis-Plus做持久化、Redis做并发控制这些技术栈本身都不难难的是你遇到卖家发货后买家跑路这种业务问题时能把技术手段恰到好处地用在正确的环节上。这套源码我建议你拿到手之后不要先急着在IDE里跑起来。按照部署文档把数据库初始化好然后用application-dev.yml启动把核心的一条链路——注册、登录、发布商品、下单、付款、发货、收货——完整走一遍。走完这一遍你对整个系统的认识会比单独读源码深得多。最后再分享一个我个人的小习惯如果是要交毕设或者交付给客户我会在项目里额外写一个docs/目录把所有为什么这么设计的决策记录放在里面比如为什么用JWT而不用Session为什么订单表和流水表要分开。这些决策记录在答辩或者验收时特别加分因为老师或客户最想听的往往不是你的功能做了什么而是你在做一个功能时有没有想过它背后的权衡。写文档这件事一开始可能觉得麻烦但真到了半年后你回来看自己写的代码那些当初随手记下来的决策就是你最宝贵的领路人。

相关新闻

CTF杂项PDF题快速解题思路:从文件识别到对象流提取

CTF杂项PDF题快速解题思路:从文件识别到对象流提取

刚接触攻防世界的时候,我花了不少时间在Misc类题目上,结果印象最深的不是那些花里胡哨的流量包,反而是几道PDF题。你可能会觉得PDF能有什么搞头?打开看一眼不就行了。直到有一次,flag就藏在页面里一行几乎看不见的白色…

2026/10/9 8:18:55 阅读更多 →
基于PCAP的网络入侵检测系统:从抓包到告警的完整实现解析

基于PCAP的网络入侵检测系统:从抓包到告警的完整实现解析

简介:这是一套基于PCAP库开发的网络入侵检测系统源码包,面向计算机、网络安全、电子信息等专业的学生,尤其适合在课程设计或毕业设计中需要实现抓包分析与异常检测功能的开发者。压缩包共17个文件,整体约889KB,主体为6…

2026/10/9 8:18:55 阅读更多 →
YOLO训练管理平台实践:23个关键教训与架构避坑指南

YOLO训练管理平台实践:23个关键教训与架构避坑指南

开发 YOLO 训练管理平台的 23 个实践教训做视觉检测这两年,团队里训练任务越来越多,YOLO 的权重文件、数据集版本、超参数记录散落在各个工程师的笔记本和服务器里。今天跑通一个模型,下周就忘了当时的配置;换个人接手&#xff0c…

2026/10/9 8:17:54 阅读更多 →

最新新闻

多模态无监督持续后训练:视觉依赖感知框架解析

多模态无监督持续后训练:视觉依赖感知框架解析

多模态模型的持续更新一直有个很现实的问题:新数据来了,直接继续训练容易忘掉旧能力;不做训练,新场景又用不上。如果数据还没有人工标注,问题会更麻烦。这次我们看的这个框架,名字叫A Visual Dependence-Aw…

2026/10/9 10:33:01 阅读更多 →
SpringBoot+SpringCloud电商源码实战:微服务启动顺序与避坑指南

SpringBoot+SpringCloud电商源码实战:微服务启动顺序与避坑指南

简介:这是一套面向计算机相关专业在校学生与教师的电商系统课程设计/毕业设计源码包,基于Spring Boot与Spring Cloud构建,采用Spring Security、MyBatis、Redis、Docker、Elasticsearch等技术栈,并运用分布式微服务架构&#xff0…

2026/10/9 10:33:01 阅读更多 →
惠普战66拔掉耳机后扬声器无声

惠普战66拔掉耳机后扬声器无声

机型 HP ZHAN 66 Pro A 14 G4 | Windows 10 | 声卡 Realtek ALC236帖主的问题最终还是借助 Cursor 得以修复,下附 Cursor 总结的具体的问题表现、排查过程及结论,供有需要的同仁参考。一、问题描述耳机插上以后,声音正常。耳机拔掉以后&a…

2026/10/9 10:33:01 阅读更多 →
从临时Subagent到持久化AI团队:状态恢复与审计追踪设计

从临时Subagent到持久化AI团队:状态恢复与审计追踪设计

这次我们来看一个很有意思的项目:Show HN: Turn ad-hoc subagents into durable, accountable AI teams。从标题就能看出,它解决的不是“再做一个 Agent”,而是更现实的问题:平时随手创建的临时 Subagent 一到任务结束就丢了&…

2026/10/9 10:33:01 阅读更多 →
RIGOL DS1000系列LabVIEW驱动实战:从RS232/GPIB通信到自动化测试集成

RIGOL DS1000系列LabVIEW驱动实战:从RS232/GPIB通信到自动化测试集成

简介:这份资源面向使用普源DS1000系列示波器、希望借助LabVIEW实现远程控制与数据采集的工程师与测试人员,重点解决RS232串行通信和GPIB总线两种接口下的驱动调用问题。压缩包共54个文件,约570KB,以42个vi虚拟仪器文件为核心&…

2026/10/9 10:33:01 阅读更多 →
图书馆预约系统小程序源码拆解:Java+微信小程序+MySQL三层架构

图书馆预约系统小程序源码拆解:Java+微信小程序+MySQL三层架构

简介:这是一套基于微信小程序的图书馆预约系统毕业设计项目,面向计算机相关专业学生,适用于毕业设计或课程设计场景。系统采用微信小程序开发工具、MySQL数据库与Java的B/S架构实现,完整覆盖管理员、用户、员工三类角色&#xff1…

2026/10/9 10:32:00 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

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/8 15:26:32 阅读更多 →
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/8 15:26:40 阅读更多 →
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/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练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/9 6:17:20 阅读更多 →