项目介绍
智能考勤管理系统是一个基于 Java 全栈技术开发的现代化企业考勤解决方案。该系统采用前后端分离架构,实现了员工考勤、请假管理、统计分析等核心功能,旨在帮助企业提高人力资源管理效率。
技术栈
后端技术
- Spring Boot 2.6.x
- Spring Security
- MyBatis-Plus
- MySQL 8.0
- Redis
- JWT
前端技术
- Vue 3
- Element Plus
- Axios
- Echarts
- Vite
核心功能
1. 考勤管理
- 上下班打卡
- 加班申请
- 外出登记
- 考勤规则设置
- 异常考勤处理
2. 请假管理
- 请假申请
- 请假审批
- 请假类型配置
- 假期余额查询
3. 统计分析
- 考勤统计报表
- 部门出勤率分析
- 加班统计
- 请假趋势分析
4. 系统管理
- 用户管理
- 角色权限
- 部门管理
- 系统配置
系统架构
├── smart-attendance-system
│ ├── attendance-admin # 后台管理服务
│ ├── attendance-api # 接口服务
│ ├── attendance-common # 公共模块
│ ├── attendance-model # 数据模型
│ └── attendance-web # 前端项目
数据库设计
核心表结构
-- 员工表
CREATE TABLE sys_employee (id BIGINT PRIMARY KEY,emp_no VARCHAR(32),name VARCHAR(50),department_id BIGINT,position VARCHAR(50),status TINYINT,create_time DATETIME
);-- 考勤记录表
CREATE TABLE attendance_record (id BIGINT PRIMARY KEY,emp_id BIGINT,check_in_time DATETIME,check_out_time DATETIME,status TINYINT,type TINYINT,remark VARCHAR(255)
);-- 请假记录表
CREATE TABLE leave_record (id BIGINT PRIMARY KEY,emp_id BIGINT,leave_type TINYINT,start_time DATETIME,end_time DATETIME,reason VARCHAR(255),status TINYINT
);
项目亮点
1. 人脸识别打卡
- 集成人脸识别SDK
- 活体检测
- 高精度识别算法
2. 智能定位打卡
- 基于地理围栏技术
- 支持移动端GPS定位
- 异地打卡预警
3. 灵活的考勤规则
- 多班次管理
- 弹性工时
- 节假日智能排班
- 加班规则配置
4. 数据可视化
- 直观的统计图表
- 多维度数据分析
- 自定义报表导出
性能优化
1. 缓存优化
java">@Cacheable(value = "attendance", key = "#empId")
public AttendanceDTO getAttendanceInfo(Long empId) {// 获取考勤信息逻辑
}
2. SQL优化
java">@Select("SELECT DATE_FORMAT(check_in_time,'%Y-%m-%d') as date, " +"COUNT(*) as count FROM attendance_record " +"WHERE emp_id = #{empId} " +"GROUP BY DATE_FORMAT(check_in_time,'%Y-%m-%d')")
List<StatisticsDTO> getAttendanceStatistics(Long empId);
3. 接口性能
- 接口响应时间控制在200ms以内
- 使用线程池处理异步任务
- 批量操作优化
安全性设计
1. 身份认证
java">@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/api/auth/**").permitAll().anyRequest().authenticated().and().addFilter(new JwtAuthenticationFilter(authenticationManager()));}
}
2. 数据安全
- 敏感数据加密
- SQL注入防护
- XSS防御
部署方案
1. 容器化部署
version: '3'
services:attendance-api:image: attendance-api:latestports:- "8080:8080"environment:- SPRING_PROFILES_ACTIVE=prod
2. 高可用方案
- 服务器集群
- 负载均衡
- 数据库主从复制
项目总结
智能考勤管理系统采用现代化的技术栈和架构设计,实现了企业考勤管理的智能化和自动化。系统具有良好的可扩展性和维护性,为企业提供了一个高效、可靠的考勤解决方案。
未来展望
- 引入机器学习算法,实现考勤预测
- 集成企业微信等第三方平台
- 开发移动端应用
- 支持多租户架构
智能考勤系统 - 考勤与请假管理模块详解
一、考勤管理模块
1. 上下班打卡
1.1 打卡记录表设计
CREATE TABLE attendance_record (id BIGINT PRIMARY KEY,emp_id BIGINT COMMENT '员工ID',check_type TINYINT COMMENT '打卡类型:1-上班,2-下班',check_time DATETIME COMMENT '打卡时间',check_location VARCHAR(255) COMMENT '打卡地点',device_info VARCHAR(100) COMMENT '设备信息',face_image VARCHAR(255) COMMENT '人脸照片URL',status TINYINT COMMENT '状态:1-正常,2-迟到,3-早退',remark VARCHAR(255) COMMENT '备注',create_time DATETIME,FOREIGN KEY (emp_id) REFERENCES sys_employee(id)
);
1.2 打卡服务实现
java">@Service
@Slf4j
public class CheckInService {@Autowiredprivate AttendanceRuleService ruleService;@Autowiredprivate FaceRecognitionService faceService;@Autowiredprivate LocationService locationService;@Transactionalpublic CheckInResult doCheckIn(CheckInDTO checkInDTO) {// 1. 人脸识别验证boolean faceValid = faceService.verify(checkInDTO.getEmpId(), checkInDTO.getFaceImage());if (!faceValid) {throw new BusinessException("人脸识别失败");}// 2. 位置验证boolean locationValid = locationService.verifyLocation(checkInDTO.getLocation(),checkInDTO.getEmpId());if (!locationValid) {throw new BusinessException("不在打卡范围内");}// 3. 判断打卡类型和状态AttendanceRule rule = ruleService.getEmployeeRule(checkInDTO.getEmpId());CheckInStatus status = calculateStatus(checkInDTO.getCheckTime(), rule);// 4. 保存打卡记录AttendanceRecord record = new AttendanceRecord();record.setEmpId(checkInDTO.getEmpId());record.setCheckType(checkInDTO.getCheckType());record.setCheckTime(checkInDTO.getCheckTime());record.setStatus(status.getCode());attendanceMapper.insert(record);// 5. 返回打卡结果return new CheckInResult(status, record.getId());}private CheckInStatus calculateStatus(LocalDateTime checkTime, AttendanceRule rule) {LocalTime time = checkTime.toLocalTime();if (isCheckIn) {if (time.isAfter(rule.getLateTime())) {return CheckInStatus.LATE;}} else {if (time.isBefore(rule.getEarlyLeaveTime())) {return CheckInStatus.EARLY_LEAVE;}}return CheckInStatus.NORMAL;}
}
1.3 打卡界面实现
<template><div class="check-in-container"><div class="camera-container"><video ref="video" class="camera-preview"></video><canvas ref="canvas" style="display: none;"></canvas></div><div class="check-in-info"><div class="time-display">{{ currentTime }}</div><el-button type="primary" size="large" @click="handleCheckIn":loading="checking">{{ checkType === 1 ? '上班打卡' : '下班打卡' }}</el-button><div class="location-info">当前位置:{{ location }}</div></div><!-- 打卡结果弹窗 --><el-dialogtitle="打卡结果":visible.sync="showResult"width="300px"center><div class="check-result"><i :class="resultIcon"></i><span>{{ resultMessage }}</span><div class="check-time">{{ checkTime }}</div></div></el-dialog></div>
</template><script>
export default {data() {return {checking: false,currentTime: '',location: '',checkType: this.getCheckType(),showResult: false,resultMessage: '',stream: null}},methods: {async initCamera() {try {this.stream = await navigator.mediaDevices.getUserMedia({video: true});this.$refs.video.srcObject = this.stream;} catch (error) {this.$message.error('摄像头启动失败');}},getCheckType() {const now = new Date();const hour = now.getHours();return hour < 12 ? 1 : 2; // 12点前为上班打卡},async handleCheckIn() {try {this.checking = true;// 1. 获取人脸图片const faceImage = this.captureFace();// 2. 获取位置信息const location = await this.getCurrentLocation();// 3. 提交打卡const result = await this.$api.attendance.checkIn({checkType: this.checkType,faceImage,location,checkTime: new Date()});// 4. 显示结果this.showCheckResult(result);} catch (error) {this.$message.error(error.message);} finally {this.checking = false;}},captureFace() {const canvas = this.$refs.canvas;const video = this.$refs.video;const context = canvas.getContext('2d');canvas.width = video.videoWidth;canvas.height = video.videoHeight;context.drawImage(video, 0, 0);return canvas.toDataURL('image/jpeg');}},mounted() {this.initCamera();// 更新当前时间setInterval(() => {this.currentTime = new Date().toLocaleTimeString();}, 1000);},beforeDestroy() {// 关闭摄像头if (this.stream) {this.stream.getTracks().forEach(track => track.stop());}}
}
</script>
2. 加班申请
2.1 加班申请表
CREATE TABLE overtime_application (id BIGINT PRIMARY KEY,emp_id BIGINT,start_time DATETIME,end_time DATETIME,overtime_type TINYINT COMMENT '加班类型:1-工作日,2-周末,3-节假日',reason VARCHAR(500),status TINYINT COMMENT '状态:0-待审批,1-已通过,2-已拒绝',approver_id BIGINT,approve_time DATETIME,approve_remark VARCHAR(255),create_time DATETIME
);
2.2 加班服务实现
java">@Service
public class OvertimeService {@Autowiredprivate WorkflowService workflowService;public void applyOvertime(OvertimeDTO overtimeDTO) {// 1. 验证加班时长validateOvertimeHours(overtimeDTO);// 2. 创建加班申请OvertimeApplication application = new OvertimeApplication();BeanUtils.copyProperties(overtimeDTO, application);application.setStatus(ApprovalStatus.PENDING.getCode());overtimeMapper.insert(application);// 3. 发起工作流workflowService.startProcess("overtime_process",application.getId(),overtimeDTO.getEmpId());}private void validateOvertimeHours(OvertimeDTO overtime) {// 计算加班时长long hours = ChronoUnit.HOURS.between(overtime.getStartTime(), overtime.getEndTime());// 获取员工当月已加班时长int monthlyHours = overtimeMapper.getMonthlyHours(overtime.getEmpId(),overtime.getStartTime());// 验证是否超过月度限制if (monthlyHours + hours > MAX_MONTHLY_HOURS) {throw new BusinessException("超过月度加班时长限制");}}
}
3. 外出登记
3.1 外出登记表
CREATE TABLE business_trip (id BIGINT PRIMARY KEY,emp_id BIGINT,start_time DATETIME,end_time DATETIME,destination VARCHAR(255),purpose VARCHAR(500),status TINYINT,contact_info VARCHAR(100),create_time DATETIME
);
3.2 外出服务实现
java">@Service
public class BusinessTripService {public void register(BusinessTripDTO tripDTO) {// 1. 检查是否有重叠的外出记录boolean hasOverlap = checkTimeOverlap(tripDTO.getEmpId(),tripDTO.getStartTime(),tripDTO.getEndTime());if (hasOverlap) {throw new BusinessException("当前时间段已有外出记录");}// 2. 保存外出记录BusinessTrip trip = new BusinessTrip();BeanUtils.copyProperties(tripDTO, trip);tripMapper.insert(trip);// 3. 更新考勤规则attendanceRuleService.addException(tripDTO.getEmpId(),tripDTO.getStartTime(),tripDTO.getEndTime(),AttendanceExceptionType.BUSINESS_TRIP);}
}
4. 考勤规则设置
4.1 考勤规则表
CREATE TABLE attendance_rule (id BIGINT PRIMARY KEY,rule_name VARCHAR(50),work_start_time TIME,work_end_time TIME,late_minutes INT COMMENT '迟到判定分钟数',early_leave_minutes INT COMMENT '早退判定分钟数',work_hours DECIMAL(4,1) COMMENT '每日工时',flexible_time INT COMMENT '弹性工作时间(分钟)',dept_id BIGINT COMMENT '适用部门',status TINYINT,create_time DATETIME
);
4.2 规则配置服务
java">@Service
public class AttendanceRuleService {@Cacheable(value = "attendance_rule", key = "#deptId")public AttendanceRule getDeptRule(Long deptId) {return ruleMapper.selectByDeptId(deptId);}@CacheEvict(value = "attendance_rule", key = "#rule.deptId")public void updateRule(AttendanceRule rule) {validateRule(rule);ruleMapper.updateById(rule);}private void validateRule(AttendanceRule rule) {// 验证工作时间设置if (rule.getWorkEndTime().isBefore(rule.getWorkStartTime())) {throw new BusinessException("下班时间不能早于上班时间");}// 验证弹性工作时间if (rule.getFlexibleTime() != null && rule.getFlexibleTime() > MAX_FLEXIBLE_TIME) {throw new BusinessException("弹性工作时间超出限制");}}
}
5. 异常考勤处理
5.1 异常考勤表
CREATE TABLE attendance_exception (id BIGINT PRIMARY KEY,emp_id BIGINT,exception_date DATE,exception_type TINYINT COMMENT '异常类型:1-漏打卡,2-迟到,3-早退',handle_status TINYINT COMMENT '处理状态',handle_result VARCHAR(255),handle_time DATETIME,handler_id BIGINT,create_time DATETIME
);
5.2 异常处理服务
java">@Service
public class ExceptionHandleService {@Asyncpublic void handleException(AttendanceException exception) {// 1. 判断异常类型switch (exception.getExceptionType()) {case MISSING_CHECK:handleMissingCheck(exception);break;case LATE:handleLate(exception);break;case EARLY_LEAVE:handleEarlyLeave(exception);break;}// 2. 发送通知notificationService.sendExceptionNotice(exception);// 3. 更新处理状态exceptionMapper.updateStatus(exception.getId(),ExceptionStatus.HANDLED);}private void handleMissingCheck(AttendanceException exception) {// 检查是否有相关的请假或外出记录List<LeaveRecord> leaveRecords = leaveMapper.findByEmpAndDate(exception.getEmpId(), exception.getExceptionDate());if (!leaveRecords.isEmpty()) {// 有请假记录,标记为已确认exception.setHandleResult("已确认请假");return;}// 发起补卡申请createSupplementaryApplication(exception);}
}
二、请假管理模块
1. 请假申请
1.1 请假记录表
CREATE TABLE leave_record (id BIGINT PRIMARY KEY,emp_id BIGINT,leave_type TINYINT COMMENT '请假类型',start_time DATETIME,end_time DATETIME,duration DECIMAL(5,1) COMMENT '请假时长(天)',reason VARCHAR(500),status TINYINT COMMENT '状态:0-待审批,1-已通过,2-已拒绝',approver_id BIGINT,approve_time DATETIME,approve_remark VARCHAR(255),attachment_url VARCHAR(255) COMMENT '附件URL',create_time DATETIME
);
1.2 请假服务实现
java">@Service
public class LeaveService {@Autowiredprivate LeaveBalanceService balanceService;@Autowiredprivate WorkflowService workflowService;@Transactionalpublic void applyLeave(LeaveApplicationDTO leaveDTO) {// 1. 校验请假时长validateLeaveDuration(leaveDTO);// 2. 检查假期余额checkLeaveBalance(leaveDTO.getEmpId(),leaveDTO.getLeaveType(),leaveDTO.getDuration());// 3. 创建请假记录LeaveRecord record = new LeaveRecord();BeanUtils.copyProperties(leaveDTO, record);record.setStatus(ApprovalStatus.PENDING.getCode());leaveMapper.insert(record);// 4. 发起工作流workflowService.startProcess("leave_process",record.getId(),leaveDTO.getEmpId());}private void checkLeaveBalance(Long empId, LeaveType leaveType, BigDecimal duration) {LeaveBalance balance = balanceService.getBalance(empId, leaveType);if (balance.getRemaining().compareTo(duration) < 0) {throw new BusinessException(leaveType.getName() + "假期余额不足");}}
}
2. 请假审批
2.1 审批流程配置
java">@Configuration
public class LeaveWorkflowConfig {@Autowiredprivate RuntimeService runtimeService;@Beanpublic ProcessEngineConfiguration leaveProcess() {return ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration().setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE).setJdbcUrl("jdbc:mysql://localhost:3306/workflow").setJdbcDriver("com.mysql.cj.jdbc.Driver").setJdbcUsername("root").setJdbcPassword("password").setActivityFontName("宋体").setLabelFontName("宋体").setAnnotationFontName("宋体");}
}
2.2 审批服务实现
java">@Service
public class LeaveApprovalService {@Autowiredprivate TaskService taskService;@Autowiredprivate LeaveBalanceService balanceService;@Transactionalpublic void approve(ApprovalDTO approvalDTO) {// 1. 获取请假记录LeaveRecord record = leaveMapper.selectById(approvalDTO.getRecordId());if (record == null) {throw new BusinessException("请假记录不存在");}// 2. 更新审批状态record.setStatus(approvalDTO.getApproved() ? ApprovalStatus.APPROVED.getCode(): ApprovalStatus.REJECTED.getCode());record.setApproveRemark(approvalDTO.getRemark());record.setApproveTime(LocalDateTime.now());record.setApproverId(approvalDTO.getApproverId());leaveMapper.updateById(record);// 3. 如果审批通过,扣减假期余额if (approvalDTO.getApproved()) {balanceService.deductBalance(record.getEmpId(),record.getLeaveType(),record.getDuration());}// 4. 完成工作流任务taskService.complete(approvalDTO.getTaskId());// 5. 发送通知notificationService.sendApprovalResult(record);}
}
3. 请假类型配置
3.1 请假类型表
CREATE TABLE leave_type (id BIGINT PRIMARY KEY,type_name VARCHAR(50),type_code VARCHAR(50),paid TINYINT COMMENT '是否带薪',max_duration INT COMMENT '最大请假天数',min_unit DECIMAL(2,1) COMMENT '最小请假单位(天)',need_attachment TINYINT COMMENT '是否需要附件',status TINYINT,create_time DATETIME
);
3.2 类型配置服务
java">@Service
public class LeaveTypeService {@Cacheable(value = "leave_type", key = "#typeId")public LeaveType getLeaveType(Long typeId) {return typeMapper.selectById(typeId);}@CacheEvict(value = "leave_type", allEntries = true)public void updateLeaveType(LeaveType leaveType) {validateLeaveType(leaveType);typeMapper.updateById(leaveType);}private void validateLeaveType(LeaveType type) {// 验证最大请假天数if (type.getMaxDuration() <= 0) {throw new BusinessException("最大请假天数必须大于0");}// 验证最小请假单位if (type.getMinUnit().compareTo(BigDecimal.ZERO) <= 0) {throw new BusinessException("最小请假单位必须大于0");}}
}
4. 假期余额查询
4.1 假期余额表
CREATE TABLE leave_balance (id BIGINT PRIMARY KEY,emp_id BIGINT,leave_type TINYINT,year INT,total_days DECIMAL(5,1),used_days DECIMAL(5,1),remaining_days DECIMAL(5,1),update_time DATETIME
);
4.2 余额查询服务
java">@Service
public class LeaveBalanceService {@Cacheable(value = "leave_balance", key = "#empId + ':' + #leaveType")public LeaveBalance getBalance(Long empId, LeaveType leaveType) {return balanceMapper.selectByEmpAndType(empId, leaveType);}@Transactional@CacheEvict(value = "leave_balance", key = "#empId + ':' + #leaveType")public void deductBalance(Long empId, LeaveType leaveType, BigDecimal days) {LeaveBalance balance = getBalance(empId, leaveType);if (balance == null) {throw new BusinessException("假期余额记录不存在");}// 检查余额是否足够if (balance.getRemainingDays().compareTo(days) < 0) {throw new BusinessException("假期余额不足");}// 更新已使用和剩余天数balance.setUsedDays(balance.getUsedDays().add(days));balance.setRemainingDays(balance.getRemainingDays().subtract(days));balanceMapper.updateById(balance);}// 年度假期初始化@Scheduled(cron = "0 0 0 1 1 ?") // 每年1月1日执行public void initializeYearlyBalance() {int year = LocalDate.now().getYear();// 获取所有在职员工List<Employee> employees = employeeMapper.selectAllActive();for (Employee emp : employees) {// 初始化各类假期余额initializeBalance(emp.getId(), year);}}
}
4.3 余额查询界面
<template><div class="leave-balance"><el-card class="balance-card"><div slot="header"><span>假期余额</span><el-button type="text" @click="refreshBalance"style="float: right;">刷新</el-button></div><el-table :data="balanceList" border><el-table-column prop="typeName" label="假期类型"/><el-table-column prop="totalDays" label="总天数"/><el-table-column prop="usedDays" label="已用天数"/><el-table-column prop="remainingDays" label="剩余天数"><template slot-scope="scope"><span :class="{'warning': scope.row.remainingDays < 5,'danger': scope.row.remainingDays <= 0}">{{ scope.row.remainingDays }}</span></template></el-table-column></el-table></el-card><!-- 假期使用记录 --><el-card class="usage-card"><div slot="header"><span>假期使用记录</span></div><el-table :data="usageRecords" border><el-table-column prop="leaveType" label="假期类型"/><el-table-column prop="startTime" label="开始时间"/><el-table-column prop="endTime" label="结束时间"/><el-table-column prop="duration" label="请假天数"/><el-table-column prop="status" label="状态"><template slot-scope="scope"><el-tag :type="getStatusType(scope.row.status)">{{ getStatusText(scope.row.status) }}</el-tag></template></el-table-column></el-table></el-card></div>
</template><script>
export default {data() {return {balanceList: [],usageRecords: []}},methods: {async refreshBalance() {try {const response = await this.$api.leave.getBalance(this.userId);this.balanceList = response.data;} catch (error) {this.$message.error('获取假期余额失败');}},getStatusType(status) {const typeMap = {0: 'info',1: 'success',2: 'danger'};return typeMap[status] || 'info';},getStatusText(status) {const textMap = {0: '待审批',1: '已通过',2: '已拒绝'};return textMap[status] || '未知';}},created() {this.refreshBalance();}
}
</script><style scoped>
.leave-balance {padding: 20px;
}.balance-card {margin-bottom: 20px;
}.warning {color: #E6A23C;
}.danger {color: #F56C6C;
}
</style>
这些模块的实现涵盖了:
- 数据库表设计
- 业务逻辑实现
- 工作流集成
- 缓存管理
- 定时任务
- 前端界面开发
- 权限控制
- 数据验证
通过这些功能的实现,可以为企业提供完整的考勤和请假管理解决方案。
智能考勤系统 - 统计分析与系统管理模块详解
一、统计分析模块
1. 考勤统计报表
1.1 数据维度
-- 考勤统计视图
CREATE VIEW v_attendance_statistics AS
SELECT e.department_id,e.emp_no,e.name,DATE_FORMAT(ar.check_in_time, '%Y-%m') as month,COUNT(DISTINCT DATE(ar.check_in_time)) as work_days,COUNT(CASE WHEN ar.status = 1 THEN 1 END) as normal_days,COUNT(CASE WHEN ar.status = 2 THEN 1 END) as late_days,COUNT(CASE WHEN ar.status = 3 THEN 1 END) as early_leave_days,COUNT(CASE WHEN ar.status = 4 THEN 1 END) as absent_days
FROM sys_employee e
LEFT JOIN attendance_record ar ON e.id = ar.emp_id
GROUP BY e.id, DATE_FORMAT(ar.check_in_time, '%Y-%m');
1.2 报表类型
- 月度考勤汇总表
- 个人考勤明细表
- 部门考勤对比表
- 异常考勤分析表
1.3 数据展示
java">@Service
public class AttendanceReportService {@Autowiredprivate AttendanceMapper attendanceMapper;public List<AttendanceReportDTO> generateMonthlyReport(String month) {return attendanceMapper.getMonthlyStatistics(month);}// 支持多种导出格式public void exportReport(String format, String month) {List<AttendanceReportDTO> data = generateMonthlyReport(month);switch(format.toLowerCase()) {case "excel":exportToExcel(data);break;case "pdf":exportToPDF(data);break;case "csv":exportToCSV(data);break;}}
}
2. 部门出勤率分析
2.1 核心指标
- 部门整体出勤率
- 迟到率
- 早退率
- 缺勤率
- 加班时长
2.2 可视化展示
<template><div class="department-analysis"><!-- 部门出勤率图表 --><div class="chart-container"><v-chart :option="chartOption" /></div><!-- 部门排名列表 --><el-table :data="departmentRanking"><el-table-column prop="deptName" label="部门" /><el-table-column prop="attendanceRate" label="出勤率"><template #default="scope"><el-progress :percentage="scope.row.attendanceRate" :color="getColorByRate(scope.row.attendanceRate)"/></template></el-table-column></el-table></div>
</template><script>
export default {data() {return {chartOption: {title: { text: '部门出勤率分析' },series: [{type: 'pie',data: [{ value: 95, name: '研发部' },{ value: 92, name: '市场部' },{ value: 88, name: '运营部' }]}]}}}
}
</script>
3. 加班统计
3.1 加班类型统计
java">public enum OvertimeType {WORKDAY(1, "工作日加班"),WEEKEND(2, "周末加班"),HOLIDAY(3, "节假日加班");private int code;private String desc;
}@Service
public class OvertimeStatisticsService {public Map<String, Object> calculateOvertimeHours(Long empId, String month) {Map<String, Object> statistics = new HashMap<>();// 计算不同类型加班时长statistics.put("workdayHours", calculateByType(empId, month, OvertimeType.WORKDAY));statistics.put("weekendHours", calculateByType(empId, month, OvertimeType.WEEKEND));statistics.put("holidayHours", calculateByType(empId, month, OvertimeType.HOLIDAY));// 计算加班费statistics.put("overtimePay", calculateOvertimePay(statistics));return statistics;}
}
4. 请假趋势分析
4.1 请假类型分布
SELECT leave_type,COUNT(*) as count,SUM(TIMESTAMPDIFF(HOUR, start_time, end_time)) as total_hours
FROM leave_record
WHERE DATE_FORMAT(start_time, '%Y-%m') = #{month}
GROUP BY leave_type;
4.2 趋势图表配置
javascript">const leaveAnalysisChart = {xAxis: {type: 'category',data: ['1月', '2月', '3月', '4月', '5月', '6月']},yAxis: {type: 'value'},series: [{name: '病假',type: 'line',data: [10, 8, 12, 6, 9, 7]},{name: '事假',type: 'line',data: [5, 7, 4, 8, 6, 9]},{name: '年假',type: 'line',data: [2, 4, 6, 8, 3, 5]}]
}
二、系统管理模块
1. 用户管理
1.1 用户表设计
CREATE TABLE sys_user (id BIGINT PRIMARY KEY,username VARCHAR(50) NOT NULL UNIQUE,password VARCHAR(100) NOT NULL,real_name VARCHAR(50),email VARCHAR(100),mobile VARCHAR(20),status TINYINT DEFAULT 1,create_time DATETIME,update_time DATETIME,last_login_time DATETIME,remark VARCHAR(255)
);
1.2 用户服务实现
java">@Service
public class UserService {@Autowiredprivate PasswordEncoder passwordEncoder;public void createUser(UserDTO userDTO) {// 密码加密String encodedPassword = passwordEncoder.encode(userDTO.getPassword());SysUser user = new SysUser();user.setUsername(userDTO.getUsername());user.setPassword(encodedPassword);// ... 设置其他属性userMapper.insert(user);}public void updateUserStatus(Long userId, Integer status) {userMapper.updateStatus(userId, status);}
}
2. 角色权限
2.1 RBAC权限模型
-- 角色表
CREATE TABLE sys_role (id BIGINT PRIMARY KEY,role_name VARCHAR(50),role_code VARCHAR(50),status TINYINT,create_time DATETIME
);-- 权限表
CREATE TABLE sys_permission (id BIGINT PRIMARY KEY,parent_id BIGINT,name VARCHAR(50),type TINYINT,permission_code VARCHAR(50),path VARCHAR(200),component VARCHAR(100),icon VARCHAR(50),sort INT
);-- 角色权限关联表
CREATE TABLE sys_role_permission (role_id BIGINT,permission_id BIGINT,PRIMARY KEY(role_id, permission_id)
);
2.2 权限控制实现
java">@PreAuthorize("hasRole('ADMIN') or hasPermission(#id, 'attendance:view')")
@GetMapping("/attendance/{id}")
public AttendanceDTO getAttendanceDetail(@PathVariable Long id) {return attendanceService.getById(id);
}@Service
public class CustomPermissionEvaluator implements PermissionEvaluator {@Overridepublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {// 权限判断逻辑return checkPermission(authentication, permission.toString());}
}
3. 部门管理
3.1 部门树形结构
java">@Data
public class DepartmentTree {private Long id;private String name;private Long parentId;private List<DepartmentTree> children;private String leader;private Integer employeeCount;
}@Service
public class DepartmentService {public List<DepartmentTree> buildDepartmentTree() {List<Department> allDepts = departmentMapper.selectAll();return buildTree(allDepts, 0L);}private List<DepartmentTree> buildTree(List<Department> depts, Long parentId) {return depts.stream().filter(dept -> dept.getParentId().equals(parentId)).map(dept -> {DepartmentTree node = new DepartmentTree();node.setId(dept.getId());node.setName(dept.getName());node.setChildren(buildTree(depts, dept.getId()));return node;}).collect(Collectors.toList());}
}
4. 系统配置
4.1 配置管理
java">@Configuration
@ConfigurationProperties(prefix = "system")
@Data
public class SystemConfig {private AttendanceConfig attendance;private SecurityConfig security;private NotificationConfig notification;@Datapublic static class AttendanceConfig {private String workStartTime;private String workEndTime;private Integer lateMinutes;private Integer earlyLeaveMinutes;}
}
4.2 动态配置实现
java">@Service
public class ConfigService {@Autowiredprivate StringRedisTemplate redisTemplate;public void updateConfig(String key, String value) {// 更新数据库configMapper.updateValue(key, value);// 更新缓存redisTemplate.opsForValue().set("system:config:" + key, value, 1, TimeUnit.DAYS);}public String getConfig(String key) {// 先从缓存获取String value = redisTemplate.opsForValue().get("system:config:" + key);if (value == null) {// 缓存未命中,从数据库获取value = configMapper.selectByKey(key);if (value != null) {redisTemplate.opsForValue().set("system:config:" + key, value, 1, TimeUnit.DAYS);}}return value;}
}
4.3 配置页面
<template><div class="system-config"><el-form :model="configForm" label-width="120px"><el-tabs v-model="activeTab"><!-- 考勤配置 --><el-tab-pane label="考勤配置" name="attendance"><el-form-item label="上班时间"><el-time-picker v-model="configForm.workStartTime" /></el-form-item><el-form-item label="下班时间"><el-time-picker v-model="configForm.workEndTime" /></el-form-item></el-tab-pane><!-- 系统配置 --><el-tab-pane label="系统配置" name="system"><el-form-item label="系统名称"><el-input v-model="configForm.systemName" /></el-form-item><el-form-item label="Logo"><el-uploadaction="/api/system/upload":show-file-list="false":on-success="handleLogoSuccess"><img v-if="configForm.logo" :src="configForm.logo" class="logo"><el-button v-else>上传Logo</el-button></el-upload></el-form-item></el-tab-pane></el-tabs><el-form-item><el-button type="primary" @click="saveConfig">保存配置</el-button></el-form-item></el-form></div>
</template><script>
export default {data() {return {activeTab: 'attendance',configForm: {workStartTime: '',workEndTime: '',systemName: '',logo: ''}}},methods: {async saveConfig() {try {await this.$api.system.updateConfig(this.configForm)this.$message.success('配置保存成功')} catch (error) {this.$message.error('配置保存失败')}}}
}
</script>
这些模块的实现涉及到了前后端的完整开发流程,包括:
- 数据库表设计
- 后端服务实现
- 权限控制
- 缓存优化
- 前端界面开发
- 数据可视化
通过这些功能的实现,可以为企业提供完整的考勤管理解决方案。