这个基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0技术栈的中小企业人事管理系统,是我去年为本地一家80人规模的制造企业实施的内部管理平台。系统上线后,他们的HR部门工作效率提升了40%以上,特别是考勤统计和薪资计算这两个最耗时的环节,从原来需要3天的手工处理缩短到2小时内自动完成。
这类系统在50-300人规模的企业中需求最为集中。大企业通常有成熟的ERP系统,而小微企业可能用Excel就能应付。但处于快速发展期的中小企业,正需要这样"够用不浪费"的解决方案——既能满足核心人事管理需求,又不会带来过重的实施和维护负担。
SpringBoot 2.7.x版本是经过长期验证的稳定选择。相比最新的3.x版本,它对Java8的兼容性更好,企业现有服务器环境基本都能直接运行。我在pom.xml中特别配置了:
xml复制<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.7.12</spring-boot.version>
</properties>
MyBatis-Plus 3.5.x提供了强大的单表CRUD操作能力。对于人事系统这种业务表结构固定的场景,它的BaseMapper和Wrapper能减少约60%的重复SQL编写。比如员工分页查询原本需要写10行SQL,现在只需要:
java复制Page<Employee> page = new Page<>(current, size);
LambdaQueryWrapper<Employee> wrapper = Wrappers.lambdaQuery();
wrapper.like(StringUtils.isNotBlank(name), Employee::getName, name);
return employeeMapper.selectPage(page, wrapper);
Vue3的组合式API让复杂业务组件更容易维护。以员工信息编辑表单为例,原本Options API下各种数据和方法分散在不同选项中,现在可以按功能组织代码:
javascript复制// 表单提交逻辑
const handleSubmit = async () => {
if (await formRef.value.validate()) {
loading.value = true
try {
await updateEmployee(formModel.value)
message.success('更新成功')
} finally {
loading.value = false
}
}
}
// 部门选择器逻辑
const departmentOptions = ref([])
onMounted(async () => {
departmentOptions.value = await fetchDepartments()
})
MySQL8.0的窗口函数在统计报表中非常实用。比如计算部门薪资排名:
sql复制SELECT
name, salary,
RANK() OVER(PARTITION BY dept_id ORDER BY salary DESC) as dept_rank
FROM employee
人事系统的核心表包括:
采用RBAC模型,通过后端返回的路由配置实现动态菜单。关键代码在Spring Security的配置中:
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().access("@rbacService.hasPermission(request,authentication)");
}
前端路由守卫处理:
javascript复制router.beforeEach(async (to) => {
const userStore = useUserStore()
if (!userStore.token && !whiteList.includes(to.path)) {
return '/login'
}
if (!userStore.roles.length) {
await userStore.getUserInfo()
// 动态添加路由
const routes = await generateRoutes(userStore.roles)
routes.forEach(route => router.addRoute(route))
return to.fullPath
}
})
处理跨夜班次是个经典难题。我的解决方案是将打卡时间转换为当天的分钟数:
java复制// 处理晚上23:00-次日7:00的夜班
LocalDateTime checkTime = LocalDateTime.parse("2023-05-20T23:30:00");
int minutes = checkTime.getHour() * 60 + checkTime.getMinute();
if (minutes >= 1380) { // 23:00
// 计入前一天的考勤
checkTime = checkTime.minusDays(1);
}
采用策略模式处理不同的薪资组成项:
java复制public interface SalaryCalculator {
BigDecimal calculate(Employee employee, LocalDate payPeriod);
}
@Component
public class BasicSalaryCalculator implements SalaryCalculator {
@Override
public BigDecimal calculate(Employee employee, LocalDate payPeriod) {
return employee.getBaseSalary();
}
}
@Component
public class OvertimeCalculator implements SalaryCalculator {
@Override
public BigDecimal calculate(Employee employee, LocalDate payPeriod) {
// 查询该月的加班记录计算
}
}
使用MyBatis-Plus的批量插入优化:
java复制List<Attendance> records = parseExcel(file);
int batchSize = 1000;
for (int i = 0; i < records.size(); i += batchSize) {
List<Attendance> batch = records.subList(i, Math.min(i + batchSize, records.size()));
attendanceService.saveBatch(batch);
}
Spring Cache + Redis缓存计算结果:
java复制@Cacheable(value = "salaryReport", key = "#yearMonth")
public SalaryReportVO generateReport(String yearMonth) {
// 复杂计算逻辑...
}
使用Spring Boot的profile机制:
yaml复制# application-dev.yml
server:
port: 8080
datasource:
url: jdbc:mysql://localhost:3306/hr_dev
# application-prod.yml
server:
port: 80
datasource:
url: jdbc:mysql://cluster-mysql:3306/hr_prod
Vite生产环境构建配置:
javascript复制export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'
}
}
}
}
}
})
开发环境常见解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowCredentials(true);
}
}
生产环境建议通过Nginx配置:
nginx复制location /api {
proxy_pass http://backend;
add_header 'Access-Control-Allow-Origin' '$http_origin';
add_header 'Access-Control-Allow-Methods' 'GET,POST,PUT,DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization';
}
MySQL8.0的EXPLAIN ANALYZE非常实用:
sql复制EXPLAIN ANALYZE
SELECT e.name, d.name
FROM employee e
JOIN department d ON e.dept_id = d.id
WHERE e.status = 1
对于分页查询,推荐使用延迟关联:
sql复制SELECT e.* FROM employee e
JOIN (SELECT id FROM employee WHERE status = 1 LIMIT 10000, 10) tmp
ON e.id = tmp.id
这个项目最让我有成就感的是看到客户从最初的手工Excel表格,到最终实现全流程数字化管理。技术层面上,Vue3的Composition API与SpringBoot的模块化设计形成了很好的前后端呼应,让后期新增一个功能模块的平均开发时间控制在2人日以内。