☰
Java+Vue幼儿园管理系统:从数据库设计到部署交付的完整实践
2026/10/20 16:17:42 网站建设 项目流程

做管理系统的这些年,我有个挺深的体会:看起来越“小”的业务场景,真做起来越琐碎。幼儿园管理系统就是这样,表面上是“幼儿信息增删改查”,可老师真正要用起来的晨检、考勤、请假、缴费、食谱、通知、接送人登记,每一块都在挑战最初的定义是否够细。很多团队一听到这个项目就当成普通 CRUD 来做,结果真去园里待半天,发现老师要的东西完全不止一张表。最近我刚把一个基于 Java + Vue 的幼儿园管理系统完整整理出来,源码、数据库脚本、配套说明文档都归档齐全,这篇就把从需求梳理、表结构设计到前后端联调、部署交付的完整过程重新捋一遍,给正在做课程设计、毕业设计,或者接了小型园所信息化项目的开发者一个可以直接参考的落地路径。

1. 先搞清楚幼儿园管理系统到底在管什么

1.1 一个幼儿园的日常管理,拆开看全是细活

我在动手前花了不少时间去观察园里的工作流,发现核心业务没有想象中复杂,但每一条都强调“实时”和“可追溯”。每天早晨,保健老师要在门口确认孩子精神状态、体温、是否有红眼病或手足口征兆;班级老师要记录谁到园、谁请假、谁家长代接;到了月底,财务要根据出勤和请假记录算出每个孩子应缴的费用;后勤要把一周食谱发出来,家长群每天都有人问“今天吃什么”。

这些事如果全部用微信群和 Excel 表格处理,不是不行,但会非常累。老师的手机里全是消息,统计出勤要手动数,缴费记录散在聊天记录里,月底对账时容易漏。园所规模再小,只要超过三个班,信息同步就会出现时间差。一个孩子今天请假了,消息只发在微信群里,保健老师不知道,厨房备餐也还是按全勤准备。幼儿园管理系统要解决的就是这类问题:让每个角色看到的都是同一个数据源,所有操作都有记录,月底报表不用靠人工翻聊天记录。

1.2 角色视角:园长、老师、家长关心的根本不是同一件事

在做需求分析时,我把使用者拆成了四个角色。园长看的是“大盘数据”:全园在读人数、各班出勤率、本月应收实收、教职工配置。班级老师关注的是“自己班上今天有没有异常”:谁没来、谁发烧了、谁需要家长来一趟。保健老师关注“晨检记录和异常跟踪”:体温超标的幼儿有没有复用记录,班上有没有传染性疾病迹象。家长只关心四个问题:孩子到没到园、今天吃什么、还需要交多少钱、怎么请假。

这决定了系统的页面不应该是一个大杂烩后台,而是每个角色登录后看到完全不同的工作台。园长打开系统要能直接看到本月出勤曲线和缴费进度;老师打开系统默认就是今天的考勤页;家长端则不需要看到全园数据,只盯着自己孩子的记录就行。这种角色差异不是“做得好不好看”的问题,而是功能菜单设计的最根本依据。

1.3 为什么选 Java + Vue 这套组合

技术选型上,我最终定了 Java 后端加 Vue 前端的经典搭配,数据库用 MySQL。理由很实际:Java 生态足够成熟,Spring Boot 对一个中小型管理系统来说有非常完整的脚手架,后续无论是加定时任务、对接小程序,还是做消息推送,都有大量现成方案可以参考;而 Vue 的上手成本低,配合组件库做后台页面效率很高,适合需要快速迭代的项目。

前后端分离带来的好处是接口职责清楚。后端只负责数据校验、业务逻辑和权限判断,前端只负责展示和交互。和传统的模板渲染相比,这种结构在角色差异明显的系统里更好维护。我知道有人会觉得 React 更灵活,Python 写起来更短,但对一个最终要交给别人维护、或者作为课程设计提交的系统来说,可读性和资料丰富度比“炫技”更重要。Java + Vue 恰好是资料最多、最容易找到人问的组合,这也是我推荐它的核心原因。

2. 数据库设计:先把业务拆成一张张能对上的表

2.1 用户、班级、幼儿、家长的四层关系

数据库是整个系统的地基,我在建表前先把实体关系画了一遍。最核心的是四张基础表:用户表、班级表、幼儿档案表、家长信息表。用户表不区分老师还是园长,用一个角色字段标记,因为登录校验逻辑是同一套,只是权限不同;班级表记录年级和班名,比如“小班一班”“中班二班”,还要记录当前班主任;幼儿档案表是系统的中心表,保存孩子的姓名、性别、出生日期、入园日期、过敏史、备注等;家长信息表存家长的手机号、与孩子的关系、默认接送权限。

这里有一个容易被忽视的点:一个孩子通常绑定多个家长,而这个家长可能又不止一个孩子。比如有的家庭是两个孩子都在同一所园,妈妈把两个孩子都绑在同一账号下。所以“幼儿”和“家长”是多对多关系,要单独设计一张关联表,不能简单地在幼儿表里加一个 parent_id 字段了事。我见过很多半成品系统这里都建错了,导致登录后只能查看一个孩子,功能上线后家长反复投诉。

一个简化版的核心建表语句是这样:

CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), phone VARCHAR(20), role_type VARCHAR(20) DEFAULT 'teacher', status TINYINT DEFAULT 1, create_time DATETIME ); CREATE TABLE class_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL, grade_year VARCHAR(20), head_teacher_id BIGINT, remark VARCHAR(255) ); CREATE TABLE child_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, class_id BIGINT NOT NULL, name VARCHAR(50) NOT NULL, gender TINYINT COMMENT '1男 2女', birthday DATE, enrollment_date DATE, avatar_url VARCHAR(255), allergy_info VARCHAR(500), status TINYINT DEFAULT 1 COMMENT '1在读 2转出 0停用' );

字段命名我统一用下划线,Java 实体属性用驼峰命名法映射,MyBatis-Plus 可以直接靠配置完成转换,省掉很多手写映射。所有核心表必须有 create_time 和 update_time,方便后面排查问题和做数据回溯,这一步几乎每张表都会用到。

2.2 考勤、请假、缴费这类业务表,状态设计比字段数量更重要

业务表里最容易踩坑的是状态字段。考勤表我先设计为每天每孩一条记录,核心字段是考勤日期、到园状态、温度、入园时间、离园时间、接走人、备注。这里的“到园状态”我用 TINYINT 存储,1 代表正常到园,2 代表缺勤,3 代表请假,而不是用一个布尔字段表示“来没来”。因为出勤统计需要区分“缺勤”和“请假”,月底请假扣款的规则和缺勤完全不同,布尔值根本存不下这个信息。

考勤表上还加了一个唯一索引(child_id, attendance_date),这样同一孩子同一天只能有一条考勤记录。程序层面可以反复校验,但数据库索引才是防重复的最后一道闸门。考勤记录的体温字段我特意设计成可空,因为有的班级只记录体温异常的孩子,正常孩子不需要每天填体温,这样能减少老师的工作量。

缴费表的设计同样需要状态机。每个月给孩子生成一条缴费记录,保存应收总额、优惠金额、实收金额、缴费状态、支付方式和支付时间。金额必须用 DECIMAL,不能用 FLOAT 或 DOUBLE,否则涉及小数后会出现精度问题。状态字段不能只存“已收”和“未收”,至少要分成“待缴费”“部分缴费”“已完成”“已作废”,因为幼儿园缴费经常有预交、补交、减免等复杂场景,一张状态图可以在报表时省掉大量判断逻辑。

请假表反过来,状态要反映审批链。孩子请假不是家长发一句话就行,需要经过老师确认,否则月底核算时容易起纠纷。所以请假表里有申请时间、请假起始日期、结束日期、原因、状态字段,状态取值包括待审批、已通过、已驳回、已撤销。有了这四种状态,系统中后续的“待办”“通知家长审批结果”功能才有依据。

2.3 食谱、公告、亲子任务这些辅助表,直接用通用结构

除了核心业务表,系统里还要有食谱、公告、亲子任务之类的内容型数据。这类数据没有太复杂的关联关系,可以用比较通用的结构。食谱表保存日期、餐次类型(早餐/午餐/下午点心/水果)、菜品描述,一个星期的食谱就是多条日期加餐次的记录。公告表保存标题、正文、发布人、发布时间,再加一个 target_type 字段控制这条公告是发给全园还是只发给某几个班级。

亲子任务表也不特殊,核心字段是发布班级、任务标题、任务内容、截止时间、发布老师。因为这类数据的展示基本就是“按日期倒序、按班级过滤”,不需要在数据库上做花样。真正要注意的是索引:所有按日期查询的表,都要在日期字段上加索引;所有按班级和状态过滤的组合查询,可以建联合索引。小数据量没关系,但数据跑一年后,每天几百条考勤记录累积下来就是十万级,没有索引的页面会肉眼可见地变慢。

2.4 数据库脚本交付时怎么组织才不让人抓狂

源码交付时,数据库脚本要单独建一个 sql 目录,并且至少拆成两个文件。第一个 init.sql 负责建库建表和初始化基础数据,包括默认管理员账号、角色字段、基础配置项;第二个 data.sql 放演示数据,比如两个班级、二十个演示幼儿、近一个月的考勤和缴费记录。这两个文件分开的最大好处是:接手的人可以直接用 init.sql 搭一个干净的空库,也可以把 data.sql 一起导入,马上看到有数据的界面效果。

写脚本时有两个卫生习惯要养成。一是建表语句全部使用CREATE TABLE IF NOT EXISTS,避免重复执行报错;二是统一使用 InnoDB 引擎和 utf8mb4 字符集,否则中文可能乱码,外键和事务也用不了。演示数据里我强烈建议不要放真实姓名,用“幼儿A”“幼儿B”这类代号就行。一方面保护隐私,另一方面测试数据不需要足够“像真的”才能验证逻辑,反而真实姓名容易在演示时引发不必要的尴尬。

3. 后端实现:从登录鉴权到考勤缴费的完整闭环

3.1 项目分层与接口风格,先让代码结构自己会说话

后端我用了标准的 Spring Boot 分层结构:Controller 层只做参数接收和结果返回,Service 层处理业务规则,Mapper 层负责数据库访问,实体对象放在 entity,接口入参和返回视图对象分别用 dto 和 vo 区分。这种分层虽然前期多写几个类,但维护时非常舒服,尤其是外包项目经常需要半路换人接手,新同事一看包名就知道该改哪里。

统一返回结构是必须的。我的返回类很简单:

public class Result<T> { private Integer code; private String message; private T data; }

接口无论如何都不要直接返回裸的 Map 或者实体对象。统一结构的好处是前端 axios 拦截器可以统一处理错误码,登录过期、参数校验失败、业务异常都能用同一套逻辑弹提示。加了统一结构之后,后端每个接口的写法都差不多,代码量没有明显增加,但联调时少吵很多架。

持久层我用了 MyBatis-Plus,它对单表 CRUD 的支持几乎是零成本,省掉大量重复的 SQL。但复杂的统计查询,比如“某个月每班出勤率”,我还是手写 XML 里的自定义 SQL,不会强行用框架拼。MyBatis-Plus 自带分页插件,对考勤记录、缴费记录这类可能超过一页数据的列表非常实用,配置一个分页拦截器就能用。

3.2 登录认证:JWT + 拦截器的轻量方案

认证方案我选了 JWT 加拦截器,没有上完整的 Spring Security。原因很简单:项目规模不大,角色固定,用 Spring Security 反而要写大量配置,学习成本高,接手的人不一定熟悉。JWT 的核心逻辑是用户登录成功后,后端把用户 ID、角色、过期时间等信息签名生成一个 token 返回前端,前端每次请求都把它放在 Authorization 请求头里,后端拦截器解析 token 后把用户信息放入请求上下文。

这种方案的登录流程很清晰:前端把用户名密码提交给登录接口,后端校验密码成功后生成 token,同时返回用户的基本信息和角色。密码存储一定不能是明文,用 BCrypt 哈希加密。登录接口返回的 token 会设置过期时间,一般两小时到一天,过期后前端收到 401 状态码就跳转登录页重新登录。要注意的是,所有需要登录的接口都必须经过拦截器校验,不能在 Controller 里临时判断,否则很容易漏掉某个接口。

拦截器的核心代码并不复杂:

public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("/api/auth/login".equals(request.getRequestURI())) { return true; } String token = request.getHeader("Authorization"); // 解析 token,失败时返回 401 return true; } }

实际编码里我会把登录接口、静态资源访问排除在拦截器之外,其他一切 /api 下的接口默认都要校验。对权限要求更细的操作,比如“只有园长才能作废缴费单”,则在 Service 层里再做一次角色判断,这样安全性才够。

3.3 考勤批量接口和缴费接口,把业务规则沉到 Service 层

考勤接口是整个系统里最有业务含量的一块。老师不会一个一个孩子单独提交,而是打开页面后一口气把全班的到园状态点完,再一次性保存。所以接口设计成批量提交,入参是日期、班级 ID、考勤明细列表。Service 层拿到数据后,要逐条检查每个孩子是否属于这个班级、日期是否早于今天、某孩子当天是否已经存在考勤记录。如果已经存在就执行更新,否则插入。

这里不能只做新增或只做更新,因为老师可能早上先填了一遍,发现某个孩子体温异常又改一次。所以接口在 Service 层的逻辑是“有则更新,无则插入”。批量接口处理完以后,返回一个统计结果,比如“到园 20 人,请假 2 人,缺勤 1 人”,前端直接展示给老师确认,这个反馈非常有效,能防止老师以为保存成功但其实部分数据没通过校验。

缴费接口的复杂度在于生成收费单。每个月财务点一下“生成当月账单”,系统要遍历所有在读幼儿,为每个孩子生成一条缴费记录。这里要考虑部分孩子是新转进来的,不需要缴整月;部分孩子有优惠减免。所以生成账单的逻辑不能直接复制上个月的金额,而是在 Service 层读入班级、入学日期、历史缴费记录,计算本月的应收金额。生成的账单支持人工调整金额,但要记录操作日志,方便月底对账时追溯。

3.4 异常处理和文件上传,交付项目最容易跳过但必须做的事

全局异常处理是一个很容易被忽略但又很影响体验的功能。我用 @RestControllerAdvice 统一捕获业务异常和参数校验异常,返回统一的 Result 结构。比如老师传了一个空的考勤明细,后端返回 code 40001,message 是“考勤明细不能为空”,前端直接弹提示,不用写重复的 if else 逻辑。有了全局异常处理,Controller 层的代码干净很多,业务异常只负责抛出信息,具体封装交给框架。

文件上传在幼儿园系统里很常见,幼儿头像、体检报告、食谱图片都有可能上传。最简单的方案是把文件保存到本地目录,再在配置里把这个目录映射成可访问的静态资源路径,数据库里只存相对路径,比如/upload/avatar/20250601_xxx.jpg。上传接口要对文件类型和后缀做白名单校验,图片只允许 jpg、jpeg、png,大小限制在几 MB 以内。虽然这个项目用不上对象存储,但本地存储方案对交付部署最友好,不依赖第三方服务,也容易理解。

4. 前端页面:Vue 项目怎么把操作体验做顺

4.1 前端项目结构和状态管理

前端我用的 Vue 3 + Vite,组件库选的 Element Plus,状态管理用 Pinia。项目结构上分成 api、router、stores、views、components 几个目录。api 目录统一放请求接口定义,每个模块一个文件;stores 里管理用户信息和登录状态;views 按业务模块拆页面;公共组件如文件上传、班级选择器放在 components。

路由设计必须与角色权限对应。每个路由的 meta 里标记允许访问的角色,路由守卫在跳转前读取当前用户角色,没有权限就重定向到工作台。这种前端路由控制能让没有权限的人看不到菜单,但一定记住:它只是体验优化,不是安全措施。真正权限校验必须后端做,前端隐藏按钮并不代表接口不能被调用。

axios 封装同样不能少。请求拦截器自动在 header 里带上 token,响应拦截器统一处理业务错误码,当返回 401 时清除本地登录状态并跳转登录页。这样一来,每个业务页面里只需要写业务逻辑,不需要反复处理“登录过期”这种基础设施问题。

4.2 几个核心页面:工作台、幼儿管理、缴费列表

工作台是我很看重的一个页面,因为它是每个角色打开系统的第一眼。园长的工作台展示全园统计:今日到园人数、出勤率、待处理请假、本月收费进度。老师的工作台直接显示自己的班级信息,把今天要做的考勤、待审批的请假放到显眼位置。这部分数据在后端单独写了一个仪表盘接口,前端用卡片和简单表格展示。

幼儿管理页面的核心是列表加详情。列表用表格展示班级、姓名、性别、生日、入园日期,支持按班级筛选和关键字搜索。点击详情弹出抽屉或对话框,展示这个孩子的完整档案,包括过敏史、家长联系方式、历史考勤记录。新增幼儿时,上传头像、填写基本信息、选择班级三个步骤要分开,避免老师在一个长表单里迷失方向。这里用 el-form 做校验,姓名为必填,手机号用正则校验,出生日期不能晚于入园日期。

缴费列表页是财务最常用的页面。默认展示当前月份的缴费记录,每一行显示孩子姓名、班级、应收金额、实收金额、缴费状态。财务可以勾选多条记录进行批量标记收款,也可以点击单条记录查看缴费详情。这个页面还需要一个简单的汇总统计,在表格下方显示本月应收总额、已收总额、未收总额,方便和实际对账结果快速比对。

4.3 考勤页:为“老师拿着平板在班级门口用”而设计

考勤页是整套系统里交互细节最丰富的页面。老师的使用场景是在早晨站在班级门口或活动室,手里拿着平板或手机,快速给每个孩子标记状态。所以这个页面不能是一张密密麻麻的数据表格,而要更像一张卡片墙:每个孩子一个卡片,上面是大头像和名字,下面是三个大按钮:到园、缺勤、请假。老师点一下按钮,卡片边框变色,按钮被选中,整体状态一目了然。

体温异常是需要单独处理的场景。当老师点选“到园”后,如果发现孩子体温超过正常范围,需要能在卡片上打开一个输入框填写体温值,也可以直接在卡片旁边显示一个小图标提醒保健老师重点关注。保存按钮固定在页面底部,老师确认所有状态后点一次提交。提交期间按钮要 loading 并禁用,防止双击导致重复提交。后端返回的统计结果要用醒目的方式提示,比如弹出“到园 18 人,缺勤 2 人,请假 1 人”。

离园接送信息是考勤的一部分,不能漏掉。每天离园时,老师要确认孩子被谁接走。我在考勤详情里专门加了接送人字段,支持文字填写和选择家长信息。虽然它不是很复杂的功能,但如果一开始没有设计,后面加需求会非常痛苦,因为底层表结构和接口都要动。

4.4 联调时一定要检查跨域、按钮级权限和空数据状态

前后端联调最容易卡在跨域。前端开发服务器默认跑在 5173 端口,后端跑在 8080,直接请求必然跨域。我的做法是后端配置 CORS 允许指定来源,同时前端的 Vite 配置代理,把/api转发到http://localhost:8080。两个方案都做,是为了兼顾生产环境用 Nginx 反代、本地开发用代理转发的不同情况。

按钮级权限也是一个要验证的点。页面上不是所有按钮每个老师都能看到,比如“作废缴费单”只有园长能操作。前端要控制按钮显示,后端对应接口也要做角色校验。联调时我会专门用一个非园长账号登录,把每个按钮逐个点一遍,确认没有权限的按钮不会出现在界面上,即使直接调用接口也返回 403。这个测试看似繁琐,但能在交付前把权限漏洞堵掉一大部分。

空数据状态很重要但经常被忽略。刚导入空库的时候,工作台的统计卡片可能是零,列表页是空白表格,很容易让老师觉得系统坏了。所以前端每个列表页都要做空状态提示,比如“暂无考勤记录,请先选择日期”;工作台的图表组件在数据为空时也展示友好提示。这一点做得好不好,会直接影响用户对系统的第一印象。

5. 交付物整理:源码、数据库脚本和文档缺一不可

5.1 一个清晰的目录结构,比任何口头讲解都有用

项目交付时,我习惯把整个工程组织成一个让人不用问也能跑起来的目录结构。后端一个目录,前端一个目录,数据库脚本单独放 sql,文档放 docs。根目录下放一个 README,作为所有文档的入口。交接时不用带着别人一层层翻代码,只要打开 README,就能知道整套系统的技术栈、启动步骤、默认账号和常见问题。这个习惯在很多交付项目里帮我减少了大量的重复沟通。

一个可供参考的目录结构是这样的:

kindergarten-manage/ ├── backend/ │ ├── src/ │ └── pom.xml ├── frontend/ │ ├── src/ │ └── package.json ├── sql/ │ ├── init.sql │ └── data.sql └── docs/ ├── 需求说明.md ├── 数据库设计.md ├── 接口文档.md └── 部署文档.md

很多项目代码本身没问题,但交接给对方后完全跑不起来,就是因为少了“步骤说明”这个最后环节。README 里必须写明 JDK 版本、Node 版本、MySQL 版本、默认数据库账号密码,一个都不能省略。

5.2 数据库文档和接口文档,写到什么程度才算“够用”

数据库文档不是把所有建表语句复制一遍,而是用表格说明每张表的业务含义、关键字段、表与表之间的关系。比如 child_info 表,要写清楚“class_id 关联 class_info 表,表示当前所在班级;status 为 1 时表示在读,为 2 时表示转出”。接手的人看文档时不需要去翻 SQL 里的注释,就能快速建立全局认识。

接口文档我会按模块组织,每个接口说明请求方法、请求路径、参数列表和成功失败返回示例。登录接口、考勤批量保存接口、缴费单生成接口这三个最核心的接口要写得特别细,参数每个字段的含义都要注释到位。写接口文档最忌讳只写“参数名”不写“取值范围”,比如状态字段 1、2、3 分别表示什么,必须写清楚。一份好的接口文档能直接降低前后端联调阶段无效沟通的次数。

5.3 部署到服务器的完整流程

部署文档是交付物里最容易被认为“不值得写”但实际最救命的部分。后端部署很简单:用 Maven 打包成 jar 包,然后java -jar启动。前端打包成静态文件,放到 Nginx 的 html 目录,再把/api路径反向代理到后端的 8080 端口。这个流程对于平时在本地开发的人来说可能需要踩几次坑,写清楚后基本能照着一次通过。

部署前要注意几件事:数据库要先用 init.sql 和 data.sql 初始化,否则后端启动后查不到表;MySQL 连接配置里的 serverTimezone 要设置正确,否则日期时间会出现 8 小时偏差;前端打包后需要确认静态资源路径是相对路径还是绝对路径,否则刷新页面会 404。Nginx 的最小配置可以写成这样:

server { listen 80; server_name _; location / { root /usr/share/nginx/html; index index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

如果是课程设计演示,不需要真正去云服务器,直接在本地跑也行。但部署文档仍然要写,因为评审或答辩时经常会被问“将来上线怎么部署”。能答出 Nginx 反代、jar 包启动、初始化 SQL 这三步,已经足够体现工程意识。

5.4 默认账号和演示数据里的隐私处理

系统交付必须有默认账号,不然对方拿到源码后连登录界面都进不去。我会在 README 和 data.sql 里各放一次默认账号信息:管理员账号 admin,密码是初始化脚本里用 BCrypt 生成好的哈希值,同时附带明文密码说明,方便测试登录。演示数据里的幼儿和家长信息一律使用“幼儿A”“家长甲”这种代号,不使用真实姓名和手机号。

这里还想多说一句,写 demo 数据其实比写真实数据更花心思。演示数据要有一定规模,至少两个班、二十个孩子、一个月的考勤记录,这样页面展示出来才有效果。但数据量也不要太大,导入后影响页面性能,而且很难手动定位某条记录。我一般控制在 20 到 50 条核心记录之间,既能演示列表分页,又不会让人觉得假。

6. 测试过程中踩过的坑与后续扩展思路

6.1 前端隐藏按钮不等于后端安全,接口权限必须闭环

第一次做好系统时,我以为只要前端把没有权限的按钮藏起来,普通老师就不会看到园长的功能。测试时发现,只要我用浏览器开发者工具直接调用后端接口,就能绕过前端拿到不该有的数据。这个坑让我意识到,权限控制的唯一真正防线在后端。

后来我把所有敏感接口都加了角色校验,在 Service 层根据当前登录用户角色判断是否允许执行。比如“作废缴费单”这个操作,接口入参里不带角色,而是从 token 里解析出当前用户信息,再判断角色是否为园长。前端隐藏按钮只是提升体验,后端校验才是安全底线。这个测试经验对任何管理系统都适用。

6.2 日期时间处理不当,出勤统计会差八小时

考勤统计里涉及日期,一开始我用的是带时间的 LocalDateTime,统计“今天”的出勤记录时直接用当前时间比较,结果晚上十点以后生成的记录会被算到第二天,导致月底出勤数据张冠李戴。后来统一改成 LocalDate 作为业务日期,考勤表里保存的是 date 类型,和数据库里的日期字段直接对应,不再依赖当前时刻计算。

MySQL 连接串也要注意 serverTimezone 参数。不同版本的 MySQL 驱动对时区处理不一样,如果不设置,有可能出现存入数据库的时间比本地时间早八个小时的情况。这类问题在本地开发时不容易发现,因为本机和数据库可能在同一时区,一旦部署到云服务器,问题立刻暴露。最好在开发阶段就把连接串写清楚,避免上线后补坑。

6.3 考勤重复提交和缴费单重复生成,要在数据库层面兜底

考勤记录的重复提交问题,我在前端做了按钮 loading 处理,理论上不会被双击,但网络重试、接口超时后客户端自动重试等场景还是会制造重复数据。只靠前端不可靠,所以我在考勤表加了唯一索引,数据库层保证同一孩子同一天只有一条记录。即使后端代码有 bug,也不会产生脏数据。

缴费单重复生成也有类似风险。财务如果手快连点了两次“生成账单”,同一个孩子可能生成两条当月账单。这个我通过“先查当月是否已有账单再决定是否重新生成”来处理,同时给(child_id, payment_month, bill_type)加了逻辑校验。测试时专门连续点击按钮模拟重复请求,确认不会出双账单后才放心。

6.4 下一步扩展:消息提醒、接送人脸、数据报表

如果要继续完善这个系统,优先级最高的扩展是消息通知。家长端如果做成小程序,孩子请假需要老师审批、缴费单生成、明日食谱发布这些事件都要主动推送到家长手机上,而小程序通知比站内信有效得多。我目前的系统里只做了站内消息,实际使用中家长不一定每天登录,所以消息触达是最大的缺口。

第二个值得做的是接送场景的人脸识别或 NFC 打卡。晨检考勤现在靠老师手动点选,数据准确没问题,但离园高峰期一个老师同时看几十个孩子,手动登记容易忙中出错。对接一个简单的门禁设备自动记录离园时间,会大幅降低老师的工作压力。这个功能需要硬件配合,不是纯软件能完成,但接口层面可以预留。

第三个方向是数据报表。现在的工作台统计偏简单,如果做一个月度班级出勤趋势图、缴费业绩对比图,园长会更直观地掌握园所运营状况。报表在技术上不复杂,难点在于口径统一,比如“出勤率”按天算还是按人次算,需要和园方明确规则后写进统计逻辑。

做这类中小型管理系统,我的体会是:技术难点往往不在某个框架的高阶用法上,而在对业务细节的理解和对交付物完整性的把控里。一个踏实跑起来的系统,比一个用了一堆技术但跑不起来的系统有价值得多。这也是我把源码、数据库和文档三样东西看得同等重要的原因。如果后面有人能在考勤交互和通知推送这两个方向继续深入,这个项目还有很大的提升空间,我自己也在考虑把家长小程序端补上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询