拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SSM员工考勤系统毕业设计实战:登录权限、请假流程与统计SQL解析

简介面向高校毕业设计/课程设计场景的SSMJSP公司员工考勤管理系统完整工程包包含可运行源码、SQL脚本与项目文档。系统围绕员工、部门经理、系统管理员三类角色设计员工与经理侧涵盖个人资料、上班公告、请假、出差、差费报销、考勤及日常出勤管理管理员在此基础上增加系统用户、部门、员工管理并提供员工统计与请假统计登录模块区分角色权限。压缩包约29.96MB主文件为Java源码、JSP页面、MyBatis映射及数据库SQL文件可用于本地部署与二次开发。目前已有1847人学习下载适合需要快速搭建考勤管理课题的本科生或程序员能够帮助理解SSM整合、角色权限划分及典型业务流实现尤其便于论文撰写与答辩演示。1. 把 SSM 员工考勤系统跑起来登录、请假的完整链路是毕业设计里最值钱的部分如果你正在找一套能直接改、能跑通的 SSM 课程设计源码这套员工考勤管理系统值得先看一眼。它覆盖了企业考勤里最常见的三个角色——一般员工、部门经理、系统管理员每个角色都有自己的功能边界而不是那种只有一个登录页面就算完事的空壳项目。最实用的是它把请假、出差、差费报销、日常出勤这几条主线串成了完整流程管理员还能对员工和请假做统计这些功能拿出来直接就是毕业设计论文里的核心章节素材。技术栈是 Spring SpringMVC MyBatis 配 MySQL 和 JSP典型的高校课程设计组合找工作面试时也可以顺手讲清楚权限控制和流程流转这两个点。我用这套源码完整复现过一遍环境搭建和数据初始化下面把登录鉴权、请假流程、统计报表 SQL 和部署步骤逐一拆开讲重点标出哪些地方容易翻车。这套系统的价值在「完整」而不在「复杂」。它没有引入 Shiro 或 Spring Security 那套重量级权限框架而是用 SSM 自带的拦截器或过滤器来做角色判断这恰好是很多课设项目的常见做法也更容易在答辩时讲清原理。数据层面用户表、部门表、请假表、出差表、报销表、考勤表之间通过外键或逻辑关联形成闭环统计功能也是直接用 SQL 聚合实现的没有套报表插件。也就是说你拿到的不只是一个能点着玩的页面而是一个每一层都能在论文里写出「为什么这么设计」的完整样本。接下来我按「登录与权限 → 请假出差流程 → 统计报表 SQL → 环境搭建 → 验证与避坑」的顺序展开每一部分都会给出可复用的代码片段和参数说明让你拿到手之后不是对着源码发呆而是能照着改、照着跑、照着答。2. 登录与角色权限从数据库用户表设计到拦截器实现2.1 用户表与角色字段的设计思路登录模块是整个系统的入口所有角色的身份都落在一张用户表上。看这套源码时先打开数据库脚本里的用户表结构你会发现它通常包含用户名、密码、角色类型、关联员工编号这几个核心字段。角色类型一般用整数或字符串标识比如 0 代表系统管理员、1 代表部门经理、2 代表一般员工这样在登录后写拦截器时只要比对 session 里存的角色值就能决定放行还是跳转。这种设计的优点是足够直观答辩时你可以解释为什么不用三张表分别存三类用户因为一般员工和部门经理都需要关联到员工基本信息表而管理员属于系统维护角色不参与考勤业务所以把账号信息和角色标识放在同一张表里用角色字段区分业务边界能减少联表查询的复杂度。密码字段在课设项目里通常直接存明文或简单的 MD5生产环境肯定要加盐处理但作为课程设计重点在于讲清楚鉴权流程本身。登录校验的逻辑也不复杂。前端 JSP 页面提交用户名和密码到 ControllerService 层调用 MyBatis 的 Mapper 按用户名查询用户记录再比对密码字段是否一致。比对通过后把用户 ID、用户名、角色类型写入 session同时根据角色类型跳转到不同的首页。这里值得注意的一个细节是很多课设源码会忽略「账号状态」这个字段导致被删除的用户还能登录。如果你要在这套源码上做改进优先给用户表加一个 status 字段登录时加一道状态判断这是答辩时很加分的优化点。2.2 菜单动态渲染与访问拦截的实现方式登录成功后的菜单不是写死在每个 JSP 页面里的而是根据 session 里的角色类型动态显示。常见做法是在 JSP 顶部用 JSTL 标签判断角色值再决定渲染哪些功能入口。比如管理员能看到「系统用户管理」「部门管理」「员工管理」而一般员工只看到「个人资料」「请假管理」「出差管理」「考勤管理」这些。这个逻辑听起来简单但实际写起来有一个容易漏的地方菜单隐藏不等于接口安全。如果只在页面上隐藏了菜单而 Controller 的 RequestMapping 没有做权限校验那员工直接输入管理员功能的 URL 一样能访问。所以这套源码里还需要一层拦截器或过滤器来兜底。SpringMVC 的拦截器是课设项目里最常用的方案配置一个 HandlerInterceptor在 preHandle 方法里取出 session 中的角色值判断当前请求路径是否在角色的允许访问列表里如果越权就重定向到登录页或错误页。下面给出一段常见的拦截器配置逻辑你可以对照源码里的实现来看public class AuthInterceptor implements HandlerInterceptor { // 管理员可访问的路径前缀实际项目中应根据菜单表动态维护 private static final ListString ADMIN_PATHS Arrays.asList( /admin/, /user/, /dept/, /statistics/ ); Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object role session.getAttribute(loginRole); String uri request.getRequestURI(); // 放行登录接口和静态资源 if (uri.endsWith(/login) || uri.contains(/static/)) { return true; } // 未登录统一跳转登录页 if (role null) { response.sendRedirect(request.getContextPath() /login.jsp); return false; } // 管理员角色放行所有管理路径 if (0.equals(role.toString())) { return true; } // 普通角色访问管理路径时拦截 for (String prefix : ADMIN_PATHS) { if (uri.startsWith(request.getContextPath() prefix)) { response.sendRedirect(request.getContextPath() /index.jsp); return false; } } return true; } }这段代码的逻辑关键点有三个第一登录接口必须放行否则会形成死循环跳转第二静态资源放行是为了避免 CSS、JS 文件被拦截而导致页面样式丢失第三角色判断的顺序是先判未登录、再判管理员、最后判越权顺序反了会出现管理员被拦在管理页面外的情况。SpringMVC 的配置里还需要把拦截器注册进去并且设置好拦截的路径模式。我一般会拦截所有请求然后在上面的代码里做白名单逻辑而不是只拦截部分路径这样新加的功能默认被保护不容易漏掉权限控制。2.3 三种角色的首页跳转与功能边界对比从源码的登录 Controller 里可以看到登录成功后并不是所有角色都跳同一个页面而是根据角色类型分别 forward 到不同的 JSP 模板。管理员首页通常包含系统用户管理、部门经理管理、员工管理、上班时间公告管理这几个入口这些功能都对应后台管理的 CRUD 操作。一般员工的首页则聚焦在个人资料、请假、出差、报销、考勤这几项自助操作上所有数据都与自己相关看不到其他员工的信息。部门经理的角色比较特殊它既能像一般员工那样提交请假和出差申请又能查看自己部门下属的考勤记录。这里就涉及数据权限的问题经理不应该看到其他部门的考勤数据。源码里的实现通常是先通过登录用户的员工编号查出所属部门 ID再在考勤查询 SQL 里加上部门过滤条件。数据权限往往比功能权限更容易被忽略如果你发现这套源码没有做到部门级隔离那正好可以作为你改造的切入点在答辩时讲「我优化了数据权限控制」比讲 CRUD 要有分量得多。3. 请假出差与报销流程表单设计、状态流转与防止重复提交3.1 请假单的数据表字段与状态机设计请假管理是这套系统里业务逻辑最完整的模块。请假单表通常包含申请员工编号、开始时间、结束时间、请假类型、请假事由、审批状态、审批意见、申请时间这些字段。审批状态一般用整数表示0 为待审批、1 为已通过、2 为已驳回这是流程类功能最常见的状态机设计。请假流程的链路一般是员工提交申请 → 数据写入请假表并置为待审批 → 部门经理或管理员在审批列表里看到该记录 → 审批通过或驳回 → 员工在请假记录列表里看到最终状态。报销和出差的流程结构与请假几乎一致只是业务字段不同。差费报销单会多出报销金额、发票号、费用明细等字段出差单会多出目的地、出差天数、出差事由等字段。因为这三类流程在结构和处理方式上是同构的源码里大概率采用了相近的 Controller 和 Service 设计甚至有可能共用一套审批逻辑。你在阅读源码时如果发现请假和出差的 Mapper 映射文件结构高度相似不要觉得是代码冗余反而是理解这类业务的好样本——把重复部分抽象成公共流程是改进方向但课设里分模块写清楚更容易向老师展示「我掌握了 CRUD 和状态流转」。3.2 日期校验与防止重复提交的代码实现请假模块最容易出 bug 的地方是日期校验。课设项目里常见的翻车场景是员工填写的请假结束时间早于开始时间或者选择的请假日期与已有请假记录重叠系统却直接保存成功。好的实现需要在 Service 层做两道校验第一道是基础校验保证结束时间晚于开始时间第二道是冲突校验查询当前员工已有的未结束请假记录看时间区间是否有交集。下面给出一段请假日期重叠校验的 Service 层代码示例你可以对照检查源码里是否实现了类似逻辑Override public boolean checkLeaveConflict(Integer empId, Date startTime, Date endTime) { // 查出当前员工所有状态为待审批或已通过的请假记录 LeaveQuery query new LeaveQuery(); query.setEmpId(empId); // 0 表示待审批1 表示已通过已驳回的记录不参与冲突判断 query.setStatusList(Arrays.asList(0, 1)); ListLeaveRecord records leaveMapper.selectByCondition(query); for (LeaveRecord record : records) { // 新请假时间区间与已有记录重叠的条件 // 新开始时间 旧结束时间 且 新结束时间 旧开始时间 if (!endTime.before(record.getStartTime()) !startTime.after(record.getEndTime())) { return false; // 存在冲突 } } return true; // 无冲突 }这段代码的核心是区间重叠判断公式两个时间区间 [A1, A2] 和 [B1, B2] 存在重叠的充要条件是 A1 B2 且 A2 B1。很多新手会写反写成 A1 B1 且 A2 B2这样只能判断包含关系无法覆盖部分重叠的情况。参数说明一下empId 是当前登录员工的编号startTime 和 endTime 是前端传入的请假起止时间查询时只查待审批和已通过状态的记录因为被驳回的记录不会与未来的请假产生实际冲突。防止重复提交是另一个值得补上的优化点。常见的实现方式有两种一种是在前端提交按钮上做 disabled 控制提交后立刻置灰另一种是在后端用 session 或 Redis 保存一个提交令牌第一次提交后删除令牌第二次提交时发现令牌不存在就拒绝。课设项目里用前端按钮控制就够了但如果你想让代码更有亮点可以在表单页面生成一个随机 token 存到 session 中提交时比对 token 是否匹配匹配则处理后删除。这个方案在答辩时能解释清楚「我考虑了重复提交问题」效果比只说前端禁用好很多。3.3 审批列表的权限过滤与分页查询审批功能涉及一个核心问题谁有权限看到哪些审批单。管理员的审批列表应该看到所有员工的申请记录部门经理的审批列表只应看到本部门员工的申请记录。这里的分页查询 SQL 就需要动态拼接部门过滤条件。MyBatis 的动态 SQL 功能在这个场景下非常实用使用if标签判断当前角色是否需要添加部门条件。下面是一个典型的 Mapper XML 片段select idselectApprovalList resultTypeLeaveRecord SELECT * FROM leave_record where if teststatus ! null AND status #{status} /if if testdeptId ! null AND emp_id IN (SELECT id FROM employee WHERE dept_id #{deptId}) /if if testempId ! null AND emp_id #{empId} /if /where ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} /select这里的关键参数是 deptId它不是在页面上由用户提交的而是后端根据登录用户的角色动态计算出来的。管理员调用这个查询时不传 deptId查全量数据部门经理登录后Service 层先查出他管理的部门 ID再作为参数传入。这样写的好处是 Mapper 可以复用不需要为不同角色单独写 SQL。分页参数 offset 和 pageSize 在 JSP 页面里通常对应页码和每页条数。前端传 pageNum 进来时后端要算 offset (pageNum - 1) * pageSize这是分页最容易算错的地方。很多翻车案例都是因为直接把 pageNum 当作 offset 用导致第二页开始数据错乱。你可以在源码里搜索 page 相关参数确认它的计算逻辑是否正确。4. 考勤统计与报表 SQL员工统计和请假统计的实现思路4.1 日常出勤的数据结构与打卡逻辑模拟考勤模块在这套系统里的形态通常是「日常出勤管理」它不像真正企业用的打卡系统那样对接硬件设备而是由管理员或员工手动维护出勤记录。出勤表一般包含员工编号、出勤日期、出勤状态、上班时间、下班时间、备注等字段。出勤状态可能是正常、迟到、早退、缺勤等枚举值管理端页面提供按日期和部门筛选的查询条件。如果你打算把考勤模块作为论文的亮点可以重点关注它的统计 SQL。日常出勤管理页面上通常需要展示「今日出勤人数」「迟到人数」「缺勤人数」这类汇总数据。这类数据可以用一条带 GROUP BY 的 SQL 实现也可以分别写几条 COUNT 查询。源码里大概率使用了几条独立查询的简单写法结构更清晰方便课设答辩时逐行讲解。下面给出一个按部门统计出勤情况的 SQL 示例SELECT d.dept_name AS 部门名称, COUNT(DISTINCT a.emp_id) AS 出勤人数, SUM(CASE WHEN a.status 迟到 THEN 1 ELSE 0 END) AS 迟到人数, SUM(CASE WHEN a.status 缺勤 THEN 1 ELSE 0 END) AS 缺勤人数 FROM attendance a LEFT JOIN employee e ON a.emp_id e.id LEFT JOIN dept d ON e.dept_id d.id WHERE a.attendance_date CURDATE() GROUP BY d.dept_name ORDER BY d.dept_name这条 SQL 有几个值得注意的点。LEFT JOIN 而不是 INNER JOIN 是为了把没有出勤记录的员工也统计进缺勤人数里因为缺勤在考勤表里可能根本没有对应记录而是通过「该上班的日子没有数据」推断出来的。CASE WHEN 配合 SUM 是统计布尔条件个数的标准写法比先查明细再在 Java 代码里数要高效得多。CURDATE() 是 MySQL 取当天日期的函数如果你需要按月份统计可以把 WHERE 条件改成DATE_FORMAT(a.attendance_date, %Y-%m) DATE_FORMAT(NOW(), %Y-%m)。4.2 员工统计与请假统计的聚合查询写法员工统计模块的目标一般是展示各部门人数分布、员工总人数、部门经理人数等汇总信息。最简单直接的实现是SELECT dept_id, COUNT(*) FROM employee GROUP BY dept_id再联表查出部门名称。更完整一点的做法是按入职时间或学历维度做分组统计这取决于员工表里有哪些字段。源码里如果没有这些扩展统计你可以自己加因为统计功能是最容易做出差异化效果的模块。请假统计则是按请假类型或状态维度聚合请假记录。常见需求有各部门请假总天数排行、请假类型占比、员工请假次数排名。其中请假天数的计算要注意一个坑直接用结束时间减去开始时间会多算一天。比如周一请假到周三实际请假 3 天但时间差只有 2 个整天所以需要加 1。下面给出一个按请假类型统计的 SQLSELECT leave_type AS 请假类型, COUNT(*) AS 申请次数, SUM(DATEDIFF(end_time, start_time) 1) AS 请假总天数 FROM leave_record WHERE status 1 -- 只统计已通过的请假 GROUP BY leave_type ORDER BY 请假总天数 DESCDATEDIFF 计算两个日期相差的天数加 1 才是实际的请假天数。这个逻辑在源码里如果没实现你的改进点就出现了。另外如果请假时间包含小时精度DATEDIFF 只看日期部分不会处理半天假的情况。课设项目里一般不考虑小时级精度但你可以把这个问题记下来在论文的创新点里写「当前系统支持按天计算后续可扩展小时级考勤精度」。统计结果的展示源码里通常是在 JSP 页面上用表格呈现必要时配一个简单的柱状图。如果项目里没有引入 ECharts你可以考虑加一个 ECharts 的 CDN 引用把查询出的 JSON 数据传给前端图表组件视觉效果会比纯表格好很多。这是低成本高回报的改进方向答辩演示时非常出效果。5. 从零复现环境JDK、MySQL、IDEA 与 Tomcat 的部署操作流程5.1 MySQL 初始化建库、导入 SQL 文件与账号密码配置拿到源码包后第一步不是急着在 IDEA 里导入项目而是先把数据库准备好。找一下压缩包里带的 .sql 文件通常有建库语句、建表语句和初始化数据。打开这个文件看一下开头部分它一般是CREATE DATABASE IF NOT EXISTS xxx; USE xxx;这样的结构可以直接用命令行工具导入。Windows 环境下的导入命令如下mysql -uroot -p # 输入密码后进入 MySQL 命令行然后执行 source D:/path/to/attendance.sql;导入完成后用SHOW TABLES;确认表是否创建成功。常见问题是 .sql 文件里有中文注释而命令行客户端的默认字符集不是 utf8导致导入后中文乱码或直接报错。遇到这种情况在导入前先执行SET NAMES utf8mb4;或者在连接命令里加上--default-character-setutf8mb4参数。数据库初始化完成后要重点确认两件事第一sql 文件里是否自带测试账号数据比如管理员账号 admin、普通员工账号 employee 之类的记录第二你连接的数据库账号密码是否与项目里 jdbc.properties 配置文件一致。5.2 jdbc 配置与 MyBatis 核心配置的匹配数据库连接配置在 SSM 项目里通常位于src/main/resources目录下的jdbc.properties文件。你需要检查jdbc.url、jdbc.username、jdbc.password三项是否与本地 MySQL 环境匹配。常见坑是项目里配置的数据库名与你导入的库名不一致或者密码包含特殊字符但没有转义。下面是一个标准的配置示例jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/attendance?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456注意useSSLfalse和serverTimezoneAsia/Shanghai这两项。MySQL 8.x 版本如果少了 serverTimezone 参数连接时会直接报 CST 时区异常不把 useSSL 关掉控制台会输出大量 SSL 警告日志虽然不影响运行但很碍眼。druid 或 c3p0 连接池的配置在 spring-dao.xml 里如果你发现数据库连接不稳定优先检查连接池的初始化大小和最大连接数是否够用。MyBatis 的配置文件需要注意 mapper 扫描路径是否正确。如果项目启动后报Invalid bound statement (not found)错误说明 Mapper 接口和 XML 文件的包路径没对上。检查 spring-dao.xml 里的mapperLocations配置是否指向了classpath*:mapper/*.xml同时确认 XML 文件里的 namespace 是否与接口全限定名一致。这类问题在导入别人项目时非常常见因为包名可能在上传前被改动过。5.3 IDEA 中导入项目并配置 Tomcat数据库准备好后打开 IDEA 导入源码。先别急着点运行按照「导入为 Maven 项目 → 等待依赖下载 → 检查 JDK 版本 → 配置 Tomcat」的顺序操作。如果源码里没有 Maven 的 pom.xml那可能是传统 Web 项目结构需要右键项目目录选择 Add Framework Support把 Web 模块加上。多数 SSM 课设源码都使用 Maven 管理依赖你重点关注 pom.xml 里的依赖版本即可比如 Spring 5.x、MyBatis 3.5.x、MyBatis-Spring 2.x 这些版本组合是否有兼容问题。配置 Tomcat 时在 Run/Debug Configurations 里新增一个 Tomcat Server Local 实例Deployment 选项卡里点击加号选择 Artifact把项目以 war exploded 方式部署。Application context 设置成/还是/项目名取决于项目的访问路径设计。如果你在浏览器里访问页面时发现样式全丢或请求 404通常就是这里的 context 路径与 JSP 里写的绝对路径不一致。更稳妥的做法是在 JSP 页面里用${pageContext.request.contextPath}拼接静态资源路径而不是写死/项目名/css/xxx.css。Tomcat 版本也要注意。源码如果基于 javax.servlet 写的配 Tomcat 9 及以下没问题如果项目里用的 Jakarta 命名空间那必须用 Tomcat 10。课设项目基本都停留在 javax 时代所以我建议直接用 Tomcat 8.5 或 Tomcat 9 避免踩坑。这三者用错的话运行时会报ClassNotFoundException: javax.servlet.http.HttpServlet这类错误。5.4 启动项目后应检查的五个环节项目启动后不要只盯着登录页看按以下顺序做基础巡检。第一看 IDEA 控制台有没有报错重点关注 Spring 容器初始化日志和 MyBatis 映射文件加载日志。第二打开浏览器访问登录页确认 CSS 和 JS 正常加载页面布局没有变形。第三用管理员账号登录逐一点击系统用户管理、部门管理、员工管理、考勤管理这几个入口观察有没有页面报 500。第四用普通员工账号登录测试提交一条请假记录再到管理员账号下查看这条记录是否出现在审批列表中。第五查看数据库里刚提交的数据是否完整写入时间字段是否有 8 小时的时区偏移。如果出现时区偏移问题比如提交后数据库里时间比实际时间少了 8 小时是因为 JDBC 连接串里的 serverTimezone 与 MySQL 全局时区不一致。两种处理方案改连接串为serverTimezoneGMT%2B8或者修改 MySQL 全局时区SET GLOBAL time_zone 8:00。推荐改连接串作用范围更小不会影响同一个 MySQL 实例上的其他数据库。6. 验证跑通之后数据造数技巧与三个最容易翻车的配置项系统能登录、能提交请假、能审批通过只能算基本跑通。要让它真正可用还差一步造一套像样的演示数据。很多课设项目默认的初始化数据只有几个测试账号和零星几条记录答辩演示时看着非常单薄。我的习惯是批量插入几千条考勤记录、几十条请假记录、覆盖多部门和多个员工这样统计页面才有看头。造数据的技巧是写存储过程或者用 Excel 拼接批量 INSERT 语句。比如生成某个员工一个月的工作日出勤记录先查一下该员工所属部门然后生成每个工作日的记录状态按 5% 概率迟到、3% 概率缺勤来随机赋值。请假记录方面为 3 到 5 个员工各生成 2 条已审批的请假记录日期分布在演示月份的前后两周类型覆盖事假、病假、年假。生成完用汇总 SQL 验证数据量是否合理避免出现请假总天数超过当月工作日的离谱数据答辩时被老师追问会很难收场。然后是三个最容易翻车的配置项我逐个说明踩坑现象和解决方式。第一字符集乱码问题。现象页面中文正常但提交到数据库后变成问号或导入 sql 文件后表里中文全是乱码。原因项目连接串 characterEncoding 设置不生效或 MySQL 表本身不是 utf8 字符集。解决统一三处字符集——连接串写characterEncodingutf8MySQL 5.7 可写 utf8MySQL 8 建议写 utf8mb4、sql 文件导入时先 SET NAMES utf8mb4、MySQL 建表语句统一DEFAULT CHARSETutf8mb4。如果你用 Navicat 导入数据导入前右键表检查一下字符集对不对不要假设可视化工具会自动处理编码转换。第二Tomcat 部署热重载导致的内存泄漏。现象反复修改 JSP 页面后重启 Tomcat偶尔报java.lang.OutOfMemoryError: PermGen space或直接卡死。原因Tomcat 8 及以下版本热部署时类加载器没有完全释放多次 reload 后内存被打满。解决开发阶段关闭热部署改成手动重启或在 Run Configuration 里加大 JVM 内存参数-Xms256m -Xmx512m -XX:MaxPermSize256m。做完修改后重启一次项目问题就消失了。第三访问路径 404 与静态资源拦截冲突。现象登录页能打开但 CSS 加载不出来点击功能菜单直接跳到 404。原因拦截器拦截了静态资源请求或项目 context path 与页面路径不一致。解决确认 SpringMVC 配置里mvc:resources location/static/ mapping/static/**/已配置同时把拦截器的 excludePathPatterns 加上/static/**。开发环境建议把 context path 设置为/这样页面里的根路径引用最不容易出错。从那以后我每次拿到一套 SSM 课设源码都要强制走一遍「导入数据库 → 核对 jdbc → 启动项目 → 三账号登录 → 提交一条审批流」这个流程。这套考勤系统我在部署验证时踩过不少坑尤其是 MySQL 时区和 JSP 路径这两块希望帮你少走这些弯路。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门