两个月前我们班班长老赵往群里丢了一个在线文档标题写着毕业五年聚会报名请大家尽快填写。我点开的时候已经过去一天三十多个人填得五花八门有人把带家属写在备注里有人报了名后来又撤销管理员手工删掉一行但统计表里的总人数却不会自动变。第二天老赵对着那份文档算了半个小时最后在群里吼了一句到底谁报了谁没报我眼睛都花了。然后我就动手写了个东西——SSM212班级同学录聚会报名网站。先解释一下标题里的两个SSM。SSM212是我们班的班级编号不是SSM框架的某个版本纯属撞名。而这个网站后端用的框架恰好也叫SSMSpring SpringMVC MyBatis。一边是班号一边是技术栈开发的时候我总觉得有种奇怪的仪式感。这个项目能做的事其实很朴素把全班的同学录信息整理起来让管理员可以发布聚会活动同学们在线报名、查看名单、带着家属一起参加最后所有报名数据能一键导出。它解决的就是那种报名靠接龙、统计靠眼力的原始状态。如果你是学Java Web的学生或者正在找课设题目又或者只是一个想给班级搞点什么东西的临时组织者这篇文章应该能给你一些可以直接抄作业的思路。1. 从一次崩溃的聚餐统计开始的需求到底长什么样老赵那份在线文档崩溃之后我拉着他聊了十分钟问清楚班级聚会报名的完整流程。听完之后我得出结论这不是一个能靠在线文档解决的问题因为本质上有三个数据关系需要管理而文档只能线性排列。第一个是同学录。毕业五年大家换手机号、换城市、有的还改了名字虽然很少谁现在在哪儿、能不能联系上完全是混乱的。第二个是活动本身聚会时间、地点、费用、截止日期、人数上限这些信息需要一个固定的发布入口。第三个是报名记录谁报了、谁取消了、带了几个家属、有没有忌口留言每一届聚会都要重新来一遍。把这些需求翻译成系统功能其实就五件事。普通同学登录后能看到同学录可以修改自己的联系方式其他同学的信息只展示姓名和城市手机号这些敏感字段在登录状态下可见避免被爬虫抓走。管理员可以发布聚会活动设置地点、时间、截止报名日期、费用和人数上限。所有同学可以报名或取消报名报名的时候可以填写携带家属人数和留言。活动页面实时展示当前已报名人数、剩余名额和已报名名单。管理员后台可以按活动查看完整报名记录导出成Excel。这套需求听起来简单但真正动手之前我还主动砍掉了三块内容不做在线支付因为班费收缴本来就是线下转账硬做成支付反而增加信任成本不做站内聊天和动态班级群已经够吵了网站不是用来社交的不做复杂的角色权限体系只有普通同学和管理员两种角色班委那几个人给管理员权限就行。需求边界划清楚之后整个项目的难度就降下来了。它本质上就是一个带角色区分的CRUD系统但CRUD和CRUD之间也是有讲究的后面实现的时候你会发现最坑的几个点恰恰都藏在看似简单的报名逻辑里。2. 五张表和三件套为什么SSM在这个场景依然能打你可能要问2025年了新项目还有不加Spring Boot直接用SSM的吗有。而且理由很实在。第一我们班这个项目里班里几个学弟正在上《Java Web应用开发》课程课程内容就是SpringMVC MyBatis我用SSM写他们能直接拿去看源码、跑代码、做课设参考。第二这个系统没有复杂的微服务诉求没有分布式事务没有高并发一个SSM单体应用加一台低配服务器完全够用启动占用内存还小。第三手写一遍SSM的XML配置和Spring注解配置你对Spring容器、Bean生命周期、事务传播机制的理解比直接在Spring Boot里敲两个注解深刻得多。技术选型定下来之后我先列了一张技术栈清单方便后面统一环境JDK 1.8Maven 3.6IDEA开发Tomcat 8.5部署Spring 5.2.5SpringMVC 5.2.5MyBatis 3.5.5MySQL 5.7数据库连接池用Druid视图层用JSP JSTL前端框架用Bootstrap 3 jQuery 3.5JSON序列化用Jackson文件上传用commons-fileupload数据库设计是这次开发里我认为最值得讲的部分。整个项目只用了五张表但每一张都是围绕班级聚会报名这个场景专门设计的。t_user表存同学信息核心字段是stu_no学号、name姓名、phone、email、city、password、role。学号是唯一键也是登录账号格式做了校验只允许SSM212开头的学号注册这样能挡住校外无关人员。t_activity表存聚会活动字段包括title、location、start_time、deadline、max_count、cost、description、status。这里最需要注意的字段是status我设计了四个状态0草稿、1报名中、2已截止、3已完成。因为活动从创建到结束是有生命周期变化的状态字段能让列表页SQL写起来特别舒服。t_enrollment表存报名记录字段是activity_id、user_id、companion_count、message、status。这里我做了两个关键决定。第一个是companion_count只存额外人数也就是除了本人之外还带几个人统计总人头数的时候用1companion_count一目了然。第二个是取消报名不删记录而是把status改成1已取消这样后台可以清楚区分没报名和取消报名两类人不会遗漏任何一个曾经想参加的人。这两张表之间靠t_enrollment关联但还有一个被很多人忽略的表t_notification。本来不想发的后来觉得聚会前一天要提醒大家于是加了一张站内消息表管理员发通知后同学们登录就能看到。这个功能代码量不大但班里的反馈意外地好。建表SQL里有一行是我认为全项目最重要的CREATE TABLE t_enrollment ( id INT PRIMARY KEY AUTO_INCREMENT, activity_id INT NOT NULL, user_id INT NOT NULL, companion_count INT DEFAULT 0, message VARCHAR(255), status TINYINT DEFAULT 0 COMMENT 0正常 1已取消, create_time DATETIME, cancel_time DATETIME, UNIQUE KEY uk_activity_user (activity_id, user_id) );UNIQUE KEY uk_activity_user这一行后面在第4节的并发踩坑里还会反复提到。现在先说结论这行索引是防止同一个人对同一个活动重复报名的最后一道防线必须在数据库层做不能只靠Java代码判断。3. 报名这条链路我是怎么一步步串起来的功能实现阶段我按用户操作路径来推进先登录注册再看同学录再进活动列表最后走报名流程。每一步踩到的问题其实都不会写在教科书里。登录注册用的是学号加密码。密码存储用了MD5加盐虽然现在更推荐BCrypt但考虑到项目是部署在一台低配服务器上的纯内测系统班级同学也都是熟人MD5加盐的强度足够。这个点在博客里我一般建议后续升级BCrypt不差那两行代码。校验逻辑里有一个细节注册表单提交前先在前端用正则校验学号格式后端再校验一次防止有人绕过页面直接POST。前端校验是为了体验后端校验才是安全两者定位不同。角色控制我用了一个自定义拦截器在SpringMVC配置里注册拦截所有以/admin开头的路径判断session里的role字段是不是1不是就跳回首页。这里有个经验初学者很容易把权限判断散落在每个Controller方法里但那样子别人加一个新接口就可能忘记校验集中成一个拦截器才是真正可维护的方案。活动管理这一块管理员新建活动时填写的截止时间必须早于活动开始时间这个校验放在Service层做。有个场景我需要补充一个懒更新策略活动状态什么时候从报名中变到已截止如果只用定时任务扫描部署起来多一个环节。我的做法很简单在每次查询活动列表、每次尝试报名的时候顺手查一遍有没有过了截止日期但状态还没更新的活动直接把status改成2。对班级聚会这种低频场景懒更新完全够用还能少维护一个定时器。最后是核心的报名流程。Service层的代码结构长这样Transactional public MapString, Object enroll(Integer activityId, Integer userId, int companionCount, String message) { Activity activity activityMapper.selectById(activityId); if (activity null || activity.getStatus() ! 1) { return error(活动不存在或不在报名期内); } if (activity.getDeadline().before(new Date())) { return error(报名已截止); } Enrollment enrollment new Enrollment(); enrollment.setActivityId(activityId); enrollment.setUserId(userId); enrollment.setCompanionCount(companionCount); enrollment.setMessage(message); enrollment.setStatus(0); try { enrollmentMapper.insert(enrollment); } catch (DuplicateKeyException e) { return error(你已经报过名了不要重复操作); } return success(报名成功当前人数 enrollmentMapper.countByActivityId(activityId)); }你会发现这里面并没有先查是否已报名再决定是否插入而是直接插入捕获重复键异常来兜底。原因是并发情况下两个请求同时进来都先查了一遍发现没有记录然后先后插入了两条先查后插的逻辑在并发下就是摆设。直接insert然后捕获DuplicateKeyException隐患只在唯一索引上而唯一索引是数据库层面保证的绝对可靠。人数上限的判断我放在了前端展示和后端查询统计里并没有在插入前加一个如果countmax就拒绝的严格限制。说实话这是有意为之——班级聚会多报一两个人问题不大而且我做了唯一索引重复报名不可能。如果未来要做收费活动那这个判断必须移到事务里加上锁。整个流程串联完之后你会发现其实没有任何一个功能是高难度的真正的难点全在边界条件的处理上重复报名怎么防、状态怎么流转、取消之后能不能再报我设计的规则是取消后可以重新报名只要没截止、截止时间过了怎么锁死。这些细节决定了一个网站是能用还是好用。4. 上线前夜并发重复报名、中文乱码与EL表达式三个事故现场项目上线前我拉着几个同学做了两天联调结果出事的三次每一次都值得写下来。第一个事故就是重复报名。那个同学很执着一边在电脑上点“报名”一边用手机又开了一个页面同时提交。结果管理员后台出现了两条一模一样的报名记录。排查这个问题的链路是这样的我先检查Service层代码发现有判断是否已报名逻辑看起来没问题。再看数据库没有唯一索引。原因一下就清楚了——两个请求同时到达都通过了Service层的if查询各自执行了insert在数据库层面没人拦住。修复方案就是加唯一索引uk_activity_user同时把Service层的先查再插改成直接插捕获DuplicateKeyException。这个案例以后我会直接放进课设讲解里让学弟学妹理解为什么代码里的同步锁拦不住并发数据库的唯一索引才是兜底。第二个事故是中文乱码。测试同学在留言里写了帮我留个辣一点的位子存进数据库变成了一串问号。排查链路非常经典我按顺序检查了四个环节。第一SpringMVC的CharacterEncodingFilter是否配置是否在DispatcherServlet之前——这是最常见的坑很多人把过滤器写在DispatcherServlet之后请求参数已经经过DispatcherServlet解析了编码设置就晚了。第二Tomcat的URIEncoding是否设为UTF-8这个影响GET请求的URL参数。第三MySQL连接串有没有加characterEncodingutf8不加的话驱动会按数据库默认编码处理。第四MyBatis的XML文件头里的编码声明。最后查出来是SpringMVC配置里过滤器位置没有问题但MySQL连接串漏了characterEncoding参数加上之后重启乱码消失。贴出来常用的完整连接串jdbc.urljdbc:mysql://localhost:3306/ssm212_class?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai第三个事故可能最反直觉。我用JSP渲染活动列表页面上出现了字面量${activity.title}而不是活动标题。这个问题的根源在于web.xml的版本声明太低了。我一开始照搬网上老项目用了web-app 2.5的声明Servlet 2.5规范里EL表达式默认在JSP页面中是关闭的需要手动isELIgnoredfalse或者直接用Servlet 3.1的web.xml声明。我的修复是把web.xml升级到3.1页面里全部用${}正常渲染。这个坑对用惯了模板引擎Thymeleaf、Freemarker的人来说几乎遇不到但只要还在用JSP写老项目迟早会被它绊一次。事故处理完我还顺手修了一个小问题Jackson把Date类型序列化成时间戳了前端显示的日期是一串数字。配置一下ObjectMapper的日期格式就好几行代码的事。这些事故教会我的事情其实比功能开发本身多。每修一个问题我都会把排查链路记在项目的README里因为这类问题的规律特别强下次换个项目大概率还会遇到而且修复成本只需要几分钟。5. 上线一周后的复盘哪些设计立了功哪些还要改聚会的实际报名是在一个周五晚上开始的。老赵在班级群发了一个链接说大家自己上去报名记得带上家属数量。那天晚上我盯着后台看报名人数从0涨到29用了大概三个小时第二天早上到了32。最终截至时间是周六晚上一共报名35个人其中5个人带了家属。对比之前那份在线文档的三天混乱这次半天就统计完了老赵的工作量从算半天降到了导出一次Excel。上线后我做了个小范围的复盘分成了这次做对了和下次要改两堆。做对的部分有三个。第一唯一索引设计它让重复报名这个隐患从根本上消失而不是靠代码打补丁。第二状态字段的懒更新策略省掉了一个定时任务组件部署的时候少一个要担心的东西。第三报名取消不物理删除而是逻辑标记后台能清楚列出来已经取消了的人员名单对物料准备和排桌特别有用。要改的部分也有三个。首先是密码加密方案MD5加盐应付现在的场景够用但如果这个网站要公开给别人部署建议换成BCrypt。其次是头像上传目前是存在项目本地目录的服务器带宽只有几兆图片稍微大一点就会卡以后要想办法换成对象存储或者限制图片大小。最后是消息通知目前站内信的通知效率一般很多人根本不登录网站根本看不到通知最后老赵还是要去群里喊一句。如果以后做二期应该接入邮件或者企业微信通知。这里放一下当时的部署清单给想复现的人做个参考。我用的是一台2核4G的CentOS 7服务器装好JDK 1.8、Tomcat 8.5、MySQL 5.7然后用Maven的package命令打成war包传到Tomcat的webapps目录配好数据库连接串启动服务就能访问。前端没有单独部署JSP页面和静态资源都打在war包里全由Tomcat处理。流量就几十个人完全没必要上Nginx。整个部署过程大概半小时就完成了其中大半时间花在等MySQL初始化上。按我个人的实际体会这种小系统最大的价值不是技术含量而是它真的会被一群人使用。一份在线文档解决不了的数据混乱问题一个几十KB的小网站就能处理得干干净净。班级聚会结束之后这个系统我还留着明年的聚会改个时间地点就能继续用同学录也成了大家互相联系的工具。对学Java的学弟学妹来说如果需要找一个麻雀虽小五脏俱全的SSM课设项目这套代码和文档也够他们折腾好几个星期了。最后再分享一个我印象很深的小细节。活动结束后老赵把报名名单导出成了Excel对着名单跟饭店核对了三张桌子的辣度要求。他跟我说靠谱。就这两个字我觉得比什么架构评价都有分量。
企业数字化 ERP 产品动态
相关推荐
多Agent协作系统架构设计与任务调度实战指南 1. 多Agent协作到底在解决什么问题单Agent跑任务,跑到一定复杂度就会撞墙。这不是模型能力不够,而是架构层面的天花板。我拿一个真实场景来说明:让一个Agent去完成“调研某个技术方向、输出一份带数据支撑的分析报告”这件事,它需… · 2026/9/26 7:26:46
五个正在颠覆Python开发体验的新库:环境、数据、AI全覆盖 前两天帮一个做数据分析的朋友配环境,他还在用conda创建虚拟环境,等命令跑完的工夫已经泡了杯茶。我说你手上这批操作,其实这两年新出来的工具早就把体验提升了一个档次,他还不信。后来我给他装完uv和marimo,他回头跟我… · 2026/9/26 7:26:46
大模型记忆系统实战:架构、落地方案与避坑指南 大模型的“失忆”问题,我这两年几乎每做一个应用都会撞上一次。用户上午跟助手聊清楚的文件归档规则,下午再问就被忘得一干二净;智能体处理到第三轮任务时,连自己第一步的结论都能搞错。这让我越来越确定一件事:当大家… · 2026/9/26 7:26:40
windows下git使用教程1(安装与使用) git版本:2.53.0.2
1.什么是git
Git 是一款开源的分布式版本控制系统,由 Linus Torvalds 于 2005 年开发,核心作用是追踪文件(尤其是代码)的修改历史、管理多人协作开发流程,确保代码版本可追溯、可回滚&a… · 2026/9/26 7:58:07
金融科技落地实践:支付系统、反欺诈与监管合规架构设计 三年前我第一次进金融项目现场的时候,甲方问我的第一句话是:“你的方案能不能保证每一分钱都对得上?”我当时觉得这是个简单问题,后来才知道,这是金融服务行业所有技术决策的起点。这些年我一直在做金融服务相关系统的… · 2026/9/26 7:58:07
Ince-Gaussian光束生成涡旋阵列:VirtualLab Fusion仿真全解析 之前一直在VirtualLab Fusion里折腾结构光束仿真,总想着用现成的拉盖尔-高斯或厄米-高斯模式拼出涡旋阵列,结果不是对称性不理想,就是阵列排布太“正”,调参调到怀疑人生。后来换到Ince-Gaussian这一类解系,才意识到自… · 2026/9/26 7:58:01
Jev模型API接入与SDK集成实战:类型安全结构化输出测评 1. 这个模型到底是个什么东西Jev 模型最近在技术社区里刷屏刷得厉害,我身边好几个做 AI 应用的朋友都在群里问“这玩意儿到底怎么接”“跟其他模型比强在哪”。我花了大概三天时间,从官网文档到实际 API 调用,再到 SDK 集成,完整跑… · 2026/9/26 7:58:01
基于UniApp与Spring Boot的微信小程序问卷系统设计与实践 1. 项目背景与技术选型1.1 为什么会做一套小程序问卷系统去年接了一个企业内部的满意度调研需求,原本对方想用现成的第三方问卷平台,但聊下来发现几个问题:一是内部数据不能走外部服务,二是问卷题型比较特殊,需要嵌套逻… · 2026/9/26 7:58:01
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46