基于BS的绿植领养系统:Thymeleaf服务端渲染与并发控制实战

发布时间:2026/10/14 21:33:22

基于BS的绿植领养系统:Thymeleaf服务端渲染与并发控制实战 简介一套基于 BS 架构的绿植领养系统完整源码项目面向 Java 学习者与毕业设计、课程设计人群覆盖从用户注册登录、绿植信息展示到领养申请与后台管理的闭环流程。后端基于 Spring Boot 集成 MyBatis Plus 与 Shiro 权限框架前端使用 Thymeleaf 模板引擎配合 Bootstrap 构建响应式页面数据库选用 MySQL整体技术栈贴近主流企业开发。资源包共 40 个文件压缩后仅约 70KB其中 22 个 Java 文件承担控制层、服务层与实体逻辑13 个 HTML 模板用于页面渲染另含 SQL 脚本、YAML 配置文件、启动脚本及说明文档结构紧凑易于二次开发。已有 49 人学习下载适合想快速上手 BS 项目全流程、理解权限控制与模板引擎整合的读者。通过阅读源码可掌握用户管理、领养状态跟踪等业务实现思路也可直接作为课程设计或开源项目参照。1. 基于BS系统的绿植领养系统为什么这类项目该选Thymeleaf而不是Vue前后端分离很多人拿到「基于BS系统的绿植领养系统」这种题第一反应就是上 Vue Spring Boot 前后端分离接口一顿写。我的看法相反这种页面不多、表单密集、权限模型简单的管理型应用用 Spring Boot Thymeleaf 做服务端渲染开发效率和可维护性都明显更高。你不用维护两套工程、不用处理跨域、不用给每个页面单独写接口文档模板直接渲染数据库数据一个工程从登录到领养审核全跑通。这套思路适合高校课程设计、企业内部工具、社团活动报名这类用户量小但流程完整的场景。下面我把这套系统的数据模型、页面实现、并发控制和部署避坑完整拆一遍。2. 核心数据模型五张表撑起领养闭环与状态机流转领养系统表面看是个 CRUD实际难点在「植物状态」和「申请状态」的流转。如果把状态塞在业务代码里判断后面审核、退回、成长记录全部会乱。第一步要把数据模型和状态机定死。2.1 表结构设计植物、用户、申请、领养记录、成长记录我这个模拟项目X用的是 MySQL MyBatis库名green_adoption。核心是五张表其中adoption_record和growth_record最容易被初学者省掉后面会解释为什么必须拆出来。CREATE TABLE plant ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 绿植名称, variety VARCHAR(50) COMMENT 品种, intro TEXT COMMENT 介绍, light_need VARCHAR(20) COMMENT 光照需求喜阴/散光/喜阳, water_freq VARCHAR(20) COMMENT 浇水频率每日/每周/半月, cover_url VARCHAR(255) COMMENT 展示图, status VARCHAR(20) NOT NULL DEFAULT AVAILABLE, version INT NOT NULL DEFAULT 0, created_at DATETIME, updated_at DATETIME ); CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT BCrypt 加密后存储, role VARCHAR(20) NOT NULL DEFAULT USER, phone VARCHAR(20), address VARCHAR(120), created_at DATETIME ); CREATE TABLE adoption_application ( id BIGINT PRIMARY KEY AUTO_INCREMENT, plant_id BIGINT NOT NULL, user_id BIGINT NOT NULL, apply_reason VARCHAR(255) COMMENT 申请理由, care_plan VARCHAR(500) COMMENT 养护计划, status VARCHAR(20) NOT NULL DEFAULT PENDING, created_at DATETIME, handled_at DATETIME, handler_id BIGINT COMMENT 审核人ID ); CREATE TABLE adoption_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, application_id BIGINT NOT NULL, plant_id BIGINT NOT NULL, user_id BIGINT NOT NULL, adopted_at DATETIME, status VARCHAR(20) NOT NULL DEFAULT GROWING ); CREATE TABLE growth_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, adoption_record_id BIGINT NOT NULL, content TEXT, photo_url VARCHAR(255), created_at DATETIME );字段说明plant.status是植物当前生命周期状态取值AVAILABLE可领养、APPLYING有人申请待审核、ADOPTED已领养。adoption_application.status是申请单状态取值PENDING待审核、APPROVED通过、REJECTED驳回。version字段是乐观锁标记第三章讲并发控制时你会看到它的用处。2.2 为什么申请和领养记录要拆成两张表这是最容易想不明白的地方。很多人会问申请通过了直接把application的状态改成APPROVED不就完事了吗我一开始也这么干后来做成长记录功能时翻车了。申请单是「一次性事件」记录的是用户当时的申请理由和养护计划领养记录是「持续关系」之后用户上传植物照片、提交浇水记录都要挂在adoption_record上。如果两者混在一张表审核驳回后再申请历史申请理由会污染新记录而且一个领养人提交多次成长记录时你得反复过滤申请单状态。拆开后adoption_record的application_id可以回溯到最初的申请单职责清晰。状态流转这块我用一个简单规则约束用户提交申请 - plant.status APPLYING, application.status PENDING 管理员审核通过 - plant.status ADOPTED, application.status APPROVED, 插入 adoption_record 管理员审核驳回 - plant.status AVAILABLE, application.status REJECTED审核通过那一步必须是事务性的三个动作要么全成功要么全回滚。我在代码里加了Transactional并且保证先更新植物状态再插申请单顺序反了会出现下面的并发问题。2.3 索引设计与一个容易被忽略的边界adoption_application表要建一个联合索引(plant_id, status)因为查询「某棵植物是否有未处理的申请」是最高频操作。同时user_id也要单独建索引个人中心要按用户查申请记录。边界在哪plant_id statusPENDING在 MySQL 里没法做部分索引所以同一棵植物被多人同时申请时数据库层面拦不住。很多人以为加个UNIQUE(plant_id, status)就完事实际生成的是(plant_id, PENDING)组合唯一一旦一棵植物有两条 PENDING 记录唯一约束只在插入时生效而业务上你已经先查出「没有待审核申请」并发窗口就是这里漏进去的。真正的解法在第四章的 CAS 更新。3. 服务端渲染实战Thymeleaf 页面语法、表单绑定与 Controller 三层组织数据模型定完后接下来是页面和接口。Thymeleaf 写起来比 JSP 舒服的地方在于它走的是 HTML 属性命名空间前端给你套好的模板你往标签里加th:前缀就行不用改页面结构。3.1 模板目录与公共片段抽取我在src/main/resources/templates下按模块建目录公共导航抽成fragments/nav.html避免每个页面复制一遍菜单!-- templates/fragments/nav.html -- nav th:fragmentnav classnavbar a th:href{/plants} th:classappend${currentNav plants} ? active绿植列表/a a th:href{/my/adoptions} th:classappend${currentNav my} ? active我的领养/a a th:if${session.loginUser.role ADMIN} th:href{/admin/applications}审核管理/a a th:unless${session.loginUser} th:href{/login}登录/a a th:if${session.loginUser} th:href{/logout}退出/a /nav这段代码有三个要点。th:fragmentnav定义片段名其他页面用th:replace~{fragments/nav :: nav}引用。{/plants}会自动拼接上下文路径项目部署到带子路径的 Tomcat 下也不会 404这是新手最容易忽略的。th:if和th:unless配合 session 里的登录用户做菜单显隐比在每个 Controller 里传标记变量清爽得多。3.2 植物列表页与详情页th:each 遍历是主战场列表页是用户第一眼看到的东西直接查库循环渲染!-- templates/plant/list.html -- div classplant-grid th:eachp : ${page} div classcard img th:src${p.coverUrl} alt封面 h3 th:text${p.name}绿植名称/h3 p光照span th:text${p.lightNeed}喜阳/span/p p浇水span th:text${p.waterFreq}每周/span/p span th:if${p.status AVAILABLE} classtag可领养/span span th:if${p.status APPLYING} classtag审核中/span span th:if${p.status ADOPTED} classtag已领养/span a th:href{/plants/{id}(id${p.id})} classbtn查看详情/a /div /divth:eachp : ${page}遍历 Controller 传入的集合变量名随意但建议短。状态用三个th:if判断而不是在数据库里查出中文再输出好处是模板负责展示、枚举负责语义后面加「暂停领养」状态只改模板加一行。对应的 Controller 方法很短Controller RequestMapping(/plants) public class PlantController { private final PlantMapper plantMapper; public PlantController(PlantMapper plantMapper) { this.plantMapper plantMapper; } GetMapping public String list(Model model) { ListPlant plants plantMapper.selectAvailable(); model.addAttribute(page, plants); return plant/list; } GetMapping(/{id}) public String detail(PathVariable Long id, Model model) { Plant plant plantMapper.selectById(id); Application latest applicationMapper.selectPendingByPlantId(id); model.addAttribute(plant, plant); model.addAttribute(latestApp, latest); return plant/detail; } }Model是 Spring MVC 的数据通道所有model.addAttribute的键值会暴露给模板。返回字符串是模板文件名对应templates/plant/list.html。latestApp传到详情页后模板里用来判断「当前用户是否已经申请过这棵植物」避免重复申请。3.3 领养申请表单th:object th:field 绑定与后端校验表单绑定是 Thymeleaf 比手动拼接请求参数省心的地方。对照adoption_application表的字段写表单对象public class ApplyForm { NotBlank(message 申请理由不能为空) Size(max 255) private String applyReason; NotBlank(message 请填写你的养护计划) Size(max 500) private String carePlan; private Long plantId; // getter / setter 省略 }页面端绑定form th:action{/apply} th:object${applyForm} methodpost input typehidden th:field${applyForm.plantId} textarea th:field${applyForm.applyReason} placeholder为什么想领养这棵植物/textarea p th:if${#fields.hasErrors(applyReason)} th:errors${applyForm.applyReason}/p textarea th:field${applyForm.carePlan} placeholder你的浇水、光照安排/textarea p th:if${#fields.hasErrors(carePlan)} th:errors${applyForm.carePlan}/p button typesubmit提交申请/button /form这里th:field会自动把服务端回显的错误信息和用户上次输入的值填回对应控件。如果 Controller 里校验失败直接返回plant/detail用户刷新页面后填的内容还在不用重新敲一遍。这是很多人忽略的体验细节。提交接口要同时处理「校验」和「业务」PostMapping(/apply) public String apply(Valid ModelAttribute(applyForm) ApplyForm form, BindingResult bindingResult, HttpSession session, Model model) { if (bindingResult.hasErrors()) { return redirect:/plants/ form.getPlantId(); } User loginUser (User) session.getAttribute(loginUser); applicationService.createApply(form, loginUser.getId()); return redirect:/my/applications; }校验不通过时直接重定向简单但粗暴。更细的做法是把错误信息放进RedirectAttributes带到下一个请求我为了省事用了上面的方案。Valid加BindingResult是固定搭配顺序不能反否则 Spring 直接抛异常这个地方三天两头有人踩。3.4 审核页与状态回显管理员审核页展示待处理申请列表每一条带「通过」「驳回」两个按钮tr th:eachapp : ${pendingList} td th:text${app.plantName}/td td th:text${app.username}/td td th:text${app.applyReason}/td td th:text${#temporals.format(app.createdAt, yyyy-MM-dd HH:mm)}/td td a th:href{/admin/applications/{id}/approve(id${app.id})}通过/a a th:href{/admin/applications/{id}/reject(id${app.id})}驳回/a /td /tr#temporals.format是 Thymeleaf 3.0 之后内置的时间格式化工具老项目还在用fmt:formatDate那套思路换了模板引擎容易找不到对应能力。审核接口里调applicationService.approve(id)内部用事务把申请状态、植物状态、领养记录三处一起更新这就是第二章说的闭环。4. 登录识别与并发边界拦截器、校验回显与「一棵植物只归一人」的锁领养系统的权限模型很简单普通用户能浏览、申请、看自己的记录管理员能审核。难点不在权限多复杂而在「同一棵植物被两个用户同时申请」这种竞争条件的处理。这一章把登录拦截和并发控制一起讲完。4.1 登录会话与拦截器两行配置挡住未登录访问用户登录成功后把对象塞进 SessionPostMapping(/login) public String login(String username, String password, HttpSession session) { User user userService.login(username, password); if (user null) { return login; } session.setAttribute(loginUser, user); return redirect:/plants; }只塞 Session 不够还要防住「没登录直接敲 URL」的访问。用拦截器实现Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); if (session.getAttribute(loginUser) null) { response.sendRedirect(/login); return false; } return true; } }注册拦截器时最关键的是放行名单Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**, /plants, /plants/**, /error); } }/plants和/plants/**放行是因为植物列表和详情页应该允许游客围观但点「申请领养」时必须登录。放行静态资源这段很多人忘写结果登录页的 CSS 全部加载不出来页面呈现纯 HTML 裸奔状态。4.2 申请领养的并发锁CAS 更新是唯一可靠方案第三章埋了个伏笔同一棵植物并发申请时数据库唯一约束拦不全。真正可靠的是用乐观锁做 CAS 更新。plant表里的version字段就是干这个的。Transactional public boolean createApply(ApplyForm form, Long userId) { // 第 1 步抢占植物只有状态为 AVAILABLE 才允许更新 int updated plantMapper.casGrab(form.getPlantId()); if (updated ! 1) { throw new BizException(手慢了这棵植物正在被审核中); } // 第 2 步写入申请单 applicationMapper.insert(form.getPlantId(), userId, form.getApplyReason(), form.getCarePlan()); return true; }Mapper 里对应这条关键 SQLUPDATE plant SET status APPLYING, version version 1, updated_at NOW() WHERE id #{plantId} AND status AVAILABLE逻辑说明这条 UPDATE 是原子操作MySQL 行锁保证同一时刻只有一个事务能把它更新成功。updated返回 1 表示抢占成功返回 0 表示植物已经被别人先改成APPLYING了直接抛异常。version 1是顺便维护版本号后续做「审核驳回回退」时能感知到数据被改过。参数说明#{plantId}是 MyBatis 预编译参数防 SQL 注入status AVAILABLE是条件判断等价于 CASCompare And Swap里的 Compare。这种写法在秒杀系统里叫「乐观锁 条件更新」比SELECT ... FOR UPDATE的悲观锁省去锁等待时间对领养这种低频操作完全够用。很多课程设计会忽略这步直接selectByPlantId判断状态再insert并发测试一压就出双申请。我见过有人用「先锁用户再锁植物」的自定义顺序方案复杂且容易死锁。条件更新是最短路径。4.3 权限校验的另一种边界操作人与资源归属审核接口有个隐蔽漏洞如果只按applicationId操作不检查「当前登录人是不是管理员」普通用户也能访问/admin/applications/3/approve路径把审核给办了。拦截器只能挡登录挡不住角色越权。我在审核服务里加了角色断言public void approve(Long applicationId, Long handlerId, String handlerRole) { if (!ADMIN.equals(handlerRole)) { throw new BizException(无审核权限); } // 查申请单校验植物当前状态必须是 APPLYING Application app applicationMapper.selectById(applicationId); if (!APPLYING.equals(app.getPlantStatus())) { throw new BizException(该植物状态已变化请刷新后重试); } // 更新三张表 applicationMapper.updateStatus(applicationId, APPROVED, handlerId); plantMapper.updateStatus(app.getPlantId(), ADOPTED); adoptionRecordMapper.insert(app.getPlantId(), app.getUserId(), applicationId); }handlerRole是从 Session 的loginUser里取的不要信前端传的角色字段。这个「二次校验状态」的习惯治好了我不少毛病不只是权限任何状态流转操作都应该在事务内重新校验源头状态不能拿查询时的快照当真。5. 上线前避坑模板缓存、静态资源 404、中文乱码与并发抢单的实测教训这一章是我在这个系统上攒下的真实踩坑记录。每个问题都贴现象、原因、解决最后一个问题最难排查浪费了我一个下午。5.1 改了模板页面就是不生效浏览器看到的永远是旧页面现象改plant/list.html里的标题和样式刷新页面纹丝不动重启应用才生效。原因Thymeleaf 默认开启模板缓存生产环境这个是性能优化开发环境就是灾难。解决在application.properties里把开发环境缓存关掉。spring.thymeleaf.cachefalse spring.thymeleaf.prefixclasspath:/templates/ spring.thymeleaf.suffix.html还有一招IDEA 里改了模板文件按CtrlShiftF9重新编译资源或者用 devtools 做热重启。从那以后我每接到一个 Thymeleaf 项目第一件事就是先把cachefalse写进去。5.2 页面内容出来了但 CSS 和 JS 全部 404现象登录页能打开但页面没有任何样式浏览器控制台一堆GET /css/style.css 404。原因项目用了拦截器addPathPatterns(/**)把所有请求都拦了CSS 文件也包含在内又没放行静态资源目录。解决WebConfig 里必须加excludePathPatterns(/css/**, /js/**, /images/**, /favicon.ico)。另外检查静态资源是否放在src/main/resources/static下放错到templates目录一样 404。5.3 页面上的时间是 ISO 格式「2025-01-01T10:30:00」根本没法看现象申请时间列显示2025-01-01T10:30:00用户看不懂。原因Thymeleaf 默认输出LocalDateTime的 toString走了 ISO 格式。解决模板层用#temporals.format格式化或者在全局注册格式器Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatterRegistrar registrar new DateTimeFormatterRegistrar(); registrar.setUseIsoFormat(false); registrar.setDateFormatter(DateTimeFormatter.ofPattern(yyyy-MM-dd)); registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm)); registrar.registerFormatters(registry); }注册后所有模板、表单回显都自动用这个格式不用每个字段单独写。5.4 PC 上模拟两个浏览器同时申请同一棵植物出现了两条待审核申请现象开两个窗口同时点申请管理员后台出现两条PENDING记录植物状态卡在APPLYING。原因Controller 里先 select 再 insert两个请求同时查到AVAILABLE都认为可以申请于是插了两条。解决改用第四章的 CAS 更新方案UPDATE plant SET statusAPPLYING WHERE id? AND statusAVAILABLE判定updated 1才允许插申请单。再退一步给adoption_application加一个「申请中唯一」的辅助字段也能挡住一部分但最干净还是 CAS。5.5 提交申请后管理员看到的中文是「???」数据库里存的也是问号现象申请理由里的中文全部变成问号。原因三层编码没对齐。解决# application.properties server.servlet.encoding.charsetUTF-8 server.servlet.encoding.forcetrue spring.datasource.urljdbc:mysql://localhost:3306/green_adoption?characterEncodingutf8serverTimezoneAsia/ShanghaiMySQL 建库时也要确认字符集是utf8mb4不是utf8。曾见过数据库连接串里写了characterEncodingutf8但表结构是utf8mb4字段存特殊字符还是会出问题。顺手把表字段统一成ALTER TABLE adoption_application CONVERT TO CHARACTER SET utf8mb4;这个「CONVERT TO」一次改全表比逐个字段改省事血泪经验。5.6 审核通过后用户点「提交成长记录」报外键约束失败现象用户领养成功后提交第一条成长记录控制台报Cannot add or update a child row: a foreign key constraint fails。原因growth_record.adoption_record_id关联的adoption_record不是通过审核时插入的那条而是用户在页面手工指定的值。有人在页面写死了某个 record ID。解决后端只从 Session 取当前用户再查用户和植物维度的有效adoption_record前端传的任何 ID 都不信。AdoptionRecord record adoptionRecordMapper.selectByUserAndPlant(userId, plantId); if (record null) { throw new BizException(没有找到有效领养记录); } growthRecordMapper.insert(record.getId(), content, photoUrl);教训很明确涉及归属关系的 ID永远从服务端上下文拿不从请求参数取。6. 一键验收初始化数据脚本 领养全链路清单项目写完交付前我习惯准备一套「一键启动 数据初始化 链路验收」的东西不然演示时裂开后面全崩。这套系统我一般配一个data.sql自动播种演示数据-- 管理员账号密码均为 admin123 INSERT INTO user (username, password, role) VALUES (admin, $2a$10$abcdefg, ADMIN); -- 普通测试用户 INSERT INTO user (username, password, role) VALUES (tester, $2a$10$hijklmn, USER); -- 演示植物五种状态覆盖全部场景 INSERT INTO plant (name, variety, light_need, water_freq, status) VALUES (龟背竹, 天南星科, 喜散光, 每周, AVAILABLE), (绿萝, 麒麟叶属, 耐阴, 每周, AVAILABLE), (多肉拼盘, 景天科, 喜阳, 半月, AVAILABLE), (琴叶榕, 桑科榕属, 喜阳, 每周, AVAILABLE), (富贵竹, 龙舌兰科, 喜散光, 每周, AVAILABLE);spring.sql.init.modealways启动时自动执行每执行一次就重插。启动指令收进一个脚本mvn clean package -DskipTests java -jar target/green-adoption-0.0.1-SNAPSHOT.jar启动后我按下面这张清单走一遍全链路每步确认通过再放手步骤操作预期结果1打开/login用 tester 登录跳转到/plants导航显示用户名2进入一棵 AVAILABLE 植物的详情页显示「申请领养」按钮3提交领养申请植物状态变更为「审核中」4退出用 admin 登录导航出现「审核管理」入口5审核通过该申请植物状态变为「已领养」tester 的「我的领养」出现记录6tester 提交一条成长记录记录出现在领养详情页7用第二个浏览器再试申请同一棵植物被拦截提示「手慢了」还有一个我养成的习惯验证阶段开 SQL 日志看 MyBatis 到底发了什么语句logging.level.org.mybatisDEBUG logging.level.com.yourproject.mapperDEBUG出现问题先看 mapper 打印的带参 SQL九成问题一眼就能看出是条件写错还是参数传错。从那次 CAS 顺序翻车之后我每次交付前强制自己走一遍完整清单包括游客、用户、超管三个角色各一条路径确认状态流转没有死角。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/14 21:33:22

应急管理指挥系统源码拆解:Spring Boot与WebSocket调度闭环实战

简介:这是一套面向PHP后端与前端可视化开发者的应急管理综合指挥调度系统实战案例,以可运行源码形式提供,适合想学习大屏可视化与地图API集成的初中级开发者。项目以企业为中心,结合Echarts与百度地图,实现周边5公里资…

2026/10/14 21:33:22

ComfyUI模特姿势更换工作流:从OpenPose骨架到ControlNet参数调优实战

简介:这份源码包面向使用Stable Diffusion进行图像生成的开发者与设计爱好者,聚焦ComfyUI中一键更换模特姿势的工作流实现,适合希望借助节点式操作快速搭建图像处理流程的中初级用户。包内共3个文件,以inscode工程配置、html页面与…

2026/10/14 21:28:21

滑动窗口算法解析:LeetCode最小覆盖子串问题

1. 问题背景与核心挑战这道来自LeetCode的Hard题目要求我们在字符串S中找到包含字符串T所有字符的最短连续子串。举个例子,如果S"ADOBECODEBANC",T"ABC",那么正确结果应该是"BANC",因为它同时包含A…

2026/10/14 22:55:28

Scikit-learn入门实践:鸢尾花分类模型构建全流程

1. 项目概述与思路拆解1.1 为什么用Scikit-learn开启机器学习我不止一次在社区里看到有人提问:学机器学习到底应该从框架开始,还是从数学推导开始?我的答案一直很明确——先跑通一个完整的模型,再回头补理论。Scikit-learn就是最适…

2026/10/14 22:55:28

AI Agent系统学习与实践:从核心原理到生产级落地指南

最近半年后台老有人问我同一个问题:AI Agent这东西到底怎么系统学?市面上教程不少,要么半小时搭个聊天机器人,要么通篇概念PPT,看完依然不知道怎么落地。我最后选了一条最笨也最扎实的路:跟着某所老牌理工名…

2026/10/14 22:55:28

五子棋人机对战实战:C#数据模型、评估函数与Alpha-Beta剪枝

简介:一套基于Visual Studio与EasyX图形库实现的五子棋完整工程,面向学习C#/C窗体应用、游戏逻辑与基础AI的开发者,可作为课程设计或入门项目参考。工程含人机对战与人人对战双模式,棋盘绘制、落子判断、五子连珠检测均模块化实现…

2026/10/14 22:55:28

把大语言模型当人用:高效协作开发实战指南

1. 为什么要把大语言模型当“人”来用1.1 一个被大多数人忽略的协作视角我刚开始接触大语言模型辅助开发那会儿,和绝大多数人一样,把它当成一个高级搜索引擎来使。遇到不熟的API就丢个问题过去,看到报错就复制粘贴让它解释,偶尔让…

2026/10/14 22:55:28

2B小模型如何以30毫秒零文本输出抢大模型的判断活

1. 一个反直觉的判断:为什么“小模型”开始抢大模型的活1.1 从“什么都让大模型干”到“该谁干就让谁干”过去两年,很多团队做 AI 应用的默认思路是:只要涉及语义理解、内容判断、风险识别,就一股脑丢给大语言模型。结果就是——一…

2026/10/14 22:49:48

uni-app跨端开发完全指南:一套Vue代码搞定App、H5与小程序

韦奇这个项目代号,是我给自己攒的一个跨端应用练手项目,而uni-app是我最终选定的基础框架。一句话说清楚:uni-app是一套基于Vue语法的跨端开发框架,写一份代码,可以编译到App端、H5端以及各类小程序平台。对刚开始做前…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/14 0:00:33

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:33

macOS/iOS高精度计时:mach_absolute_time原理与实战

1. 项目概述:为什么mach_absolute_time是 macOS/iOS 时间测量的“黄金标准”在 macOS 和 iOS 开发中,我见过太多人用CFAbsoluteTimeGetCurrent()或NSDate.timeIntervalSinceReferenceDate做性能打点,结果在高精度场景下被系统时钟跳变、NTP 同…

2026/10/14 0:00:33

SCORM课件演示与测试全解析:从API通信到自动化验证

简介:这是一套遵循SCORM标准的交互式课件演示与测试资源,面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者,用于验证课件在不同学习管理系统中的识别、传递与跟踪能力。压缩包共28个文件,约12.07MB,以swf动…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑