1. 项目背景与核心价值
考勤系统作为企业管理的基础设施,其稳定性和易用性直接影响员工体验和管理效率。传统考勤系统往往采用单体架构,前后端耦合度高,导致维护困难、迭代缓慢。这套基于SpringBoot+Vue+MyBatis+MySQL的技术栈实现的考勤系统,采用了典型的前后端分离架构,具有以下核心优势:
- 解耦开发:前后端可并行开发,接口定义好后前端无需等待后端完成
- 性能优化:静态资源由Nginx独立部署,减轻应用服务器压力
- 技术栈优势:SpringBoot提供企业级后端支持,Vue实现响应式前端,MyBatis灵活操作MySQL
- 可扩展性:模块化设计便于功能扩展,如后续集成人脸识别等
提示:选择这套技术栈时,我们特别考虑了团队技术储备和社区生态。SpringBoot和Vue都有丰富的插件和解决方案,遇到问题容易找到参考。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术栈选型分析
后端技术栈:
- SpringBoot 2.7.x:简化配置,内置Tomcat,快速启动
- MyBatis 3.5.x:SQL与代码分离,动态SQL支持好
- MySQL 8.0:事务支持完善,适合考勤这类数据一致性要求高的场景
- Redis:缓存考勤规则等高频访问数据
前端技术栈:
- Vue 3.x:组合式API开发效率高,生态丰富
- Element Plus:提供现成的UI组件,快速搭建管理后台
- Axios:处理HTTP请求,支持拦截器配置
- Vue Router:实现前端路由,保持单页应用体验
2.2 数据库设计关键表
sql复制-- 员工表
CREATE TABLE `employee` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`department_id` int NOT NULL,
`position` varchar(50) DEFAULT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 考勤记录表
CREATE TABLE `attendance` (
`id` int NOT NULL AUTO_INCREMENT,
`employee_id` int NOT NULL,
`check_in` datetime DEFAULT NULL,
`check_out` datetime DEFAULT NULL,
`status` tinyint DEFAULT '0' COMMENT '0正常 1迟到 2早退 3旷工',
`remark` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_employee` (`employee_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意:实际项目中我们为考勤表添加了复合索引(employee_id, check_in),大幅提升查询效率。日期字段使用datetime而非timestamp,避免时区问题。
3. 核心功能实现
3.1 后端关键代码实现
考勤打卡接口:
java复制@RestController
@RequestMapping("/api/attendance")
public class AttendanceController {
@Autowired
private AttendanceService attendanceService;
@PostMapping("/check")
public Result checkInOut(@RequestBody CheckDTO dto) {
// 防止重复打卡
if (attendanceService.existsTodayRecord(dto.getEmployeeId())) {
return Result.error("今日已打卡");
}
return attendanceService.processCheck(dto);
}
}
动态SQL处理考勤查询:
xml复制<select id="selectByCondition" resultType="AttendanceVO">
SELECT a.*, e.name as employee_name
FROM attendance a
JOIN employee e ON a.employee_id = e.id
<where>
<if test="departmentId != null">
AND e.department_id = #{departmentId}
</if>
<if test="startDate != null and endDate != null">
AND a.check_in BETWEEN #{startDate} AND #{endDate}
</if>
</where>
ORDER BY a.check_in DESC
</select>
3.2 前端关键实现
考勤日历组件:
vue复制<template>
<el-calendar v-model="currentDate">
<template #date-cell="{ data }">
<div class="date-cell">
{{ data.day.split('-').slice(2).join('-') }}
<div v-for="item in getDateStatus(data)"
:key="item.type"
:class="['status-dot', item.type]"></div>
</div>
</template>
</el-calendar>
</template>
<script setup>
import { ref, computed } from 'vue'
const currentDate = ref(new Date())
const props = defineProps(['attendanceData'])
const getDateStatus = (date) => {
// 处理考勤状态显示逻辑
}
</script>
Axios请求封装:
javascript复制const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(config => {
if (store.getters.token) {
config.headers['Authorization'] = 'Bearer ' + getToken()
}
return config
}, error => {
return Promise.reject(error)
})
4. 部署实战指南
4.1 后端部署要点
- 打包配置:
xml复制<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
- 生产环境配置:
yaml复制spring:
datasource:
url: jdbc:mysql://mysql-prod:3306/attendance?useSSL=false&serverTimezone=Asia/Shanghai
username: prod_user
password: ${DB_PASSWORD}
redis:
host: redis-prod
port: 6379
4.2 前端部署优化
Nginx配置示例:
nginx复制server {
listen 80;
server_name attendance.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Vue生产环境构建:
bash复制# 安装依赖
npm install --registry=https://registry.npmmirror.com
# 构建生产环境
npm run build:prod
# 生成压缩包便于传输
tar -czvf dist.tar.gz dist
5. 常见问题解决方案
5.1 MyBatis缓存问题
现象:开启事务后查询结果不更新
原因:MyBatis一级缓存作用域为SqlSession
解决方案:
java复制@Transactional
public void updateAttendance(Attendance attendance) {
// 先更新操作
attendanceMapper.updateById(attendance);
// 手动清除缓存
SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession();
session.clearCache();
}
5.2 跨域问题处理
SpringBoot配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.maxAge(3600);
}
}
5.3 安全扫描报SQL注入
问题SQL:
xml复制<select id="findByName" resultType="Employee">
SELECT * FROM employee WHERE name = '${name}'
</select>
修复方案:
xml复制<select id="findByName" resultType="Employee">
SELECT * FROM employee WHERE name = #{name}
</select>
经验:MyBatis中能用#{}就不要用${},后者有SQL注入风险。动态表名等必须用${}的场景,要严格过滤参数。
6. 系统扩展思路
- 考勤异常自动通知:集成企业微信/钉钉API,自动发送迟到早退通知
- 生物识别集成:对接人脸识别设备,实现无感打卡
- 数据分析报表:使用ECharts生成部门考勤统计可视化报表
- 移动端适配:基于Vant UI开发移动端H5应用
- 多租户支持:通过Sa-Token实现多企业账号隔离
这套系统在实际部署时,我们特别建议添加Prometheus监控和SpringBoot Admin,便于掌握系统运行状态。数据库方面,可以考虑配置主从复制提升查询性能,考勤记录超过百万条后可考虑按年月分表
