当前位置: 首页 > news >正文

【SpringBoot Web实战】从零构建企业级人事管理系统:部门与员工模块的CRUD实践

1. 项目背景与技术选型

企业级人事管理系统是每个公司都离不开的核心应用,它直接关系到组织架构管理和员工信息维护的效率。传统的人事管理系统往往采用单体架构,存在扩展性差、维护成本高等问题。而基于SpringBoot的现代化解决方案,能够快速构建高可用的RESTful服务。

为什么选择SpringBoot?我在实际项目中验证过它的三大优势:

  • 内嵌Tomcat:无需单独部署Web服务器,一个jar包就能运行
  • 自动配置:90%的常用配置已经预设好,比如数据库连接池
  • 起步依赖:引入spring-boot-starter-web就包含MVC、JSON等全套组件

技术栈组合建议:

<dependencies> <!-- Web核心 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 数据库访问 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> </dependencies>

2. 数据库设计与实体建模

先来看部门表的DDL设计,这里有个坑我踩过 - 一定要设置parent_id实现树形结构:

CREATE TABLE `department` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '部门名称', `parent_id` bigint DEFAULT NULL COMMENT '父部门ID', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_parent` (`parent_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

员工表设计时特别注意:

  1. 使用逻辑外键关联部门
  2. 敏感字段如身份证号需要加密存储
  3. 添加状态字段方便软删除

对应的Java实体类要遵循JPA规范:

@Data public class Employee { private Long id; private String name; private Integer gender; private String mobile; @JsonFormat(pattern = "yyyy-MM-dd") private LocalDate entryDate; private Integer status; // 1在职 2离职 @TableField(exist = false) private Department department; }

3. RESTful接口规范实践

遵循Restful风格时,我推荐这样的URL设计:

  • 部门资源:/api/departments
  • 员工资源:/api/employees
  • 子资源:/api/departments/{id}/employees

统一响应体可以这样封装:

public class R<T> { private Integer code; private String msg; private T data; public static <T> R<T> ok(T data) { R<T> result = new R<>(); result.setCode(200); result.setData(data); return result; } }

在Controller层的典型应用:

@RestController @RequestMapping("/api/departments") public class DeptController { @GetMapping public R<List<Department>> listDepartments() { return R.ok(deptService.listAll()); } @DeleteMapping("/{id}") public R<Void> delete(@PathVariable Long id) { deptService.deleteById(id); return R.ok(null); } }

4. 核心业务逻辑实现

4.1 部门树形结构查询

这里推荐使用MyBatis的嵌套查询:

<resultMap id="treeResultMap" type="Department"> <collection property="children" column="id" select="findByParentId"/> </resultMap> <select id="findByParentId" resultMap="treeResultMap"> SELECT * FROM department WHERE parent_id = #{id} </select>

Service层处理树形结构:

public List<Department> getDepartmentTree() { List<Department> roots = mapper.selectByParentId(null); roots.forEach(root -> { root.setChildren(getChildren(root.getId())); }); return roots; }

4.2 员工分页查询

PageHelper的典型用法:

public PageInfo<Employee> listEmployees(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); List<Employee> list = mapper.selectAll(); return new PageInfo<>(list); }

带条件分页查询的Mapper示例:

<select id="selectByCondition" resultType="Employee"> SELECT * FROM employee <where> <if test="name != null"> AND name LIKE CONCAT('%',#{name},'%') </if> <if test="deptId != null"> AND department_id = #{deptId} </if> </where> </select>

4.3 事务管理

在批量操作时务必添加事务注解:

@Transactional(rollbackFor = Exception.class) public void batchImport(List<Employee> employees) { employees.forEach(emp -> { if(StringUtils.isEmpty(emp.getMobile())) { throw new RuntimeException("手机号不能为空"); } mapper.insert(emp); }); }

5. 前后端联调技巧

5.1 跨域问题解决

推荐全局配置方式:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .maxAge(3600); } }

5.2 接口文档生成

Swagger的配置示例:

@Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.example.hr")) .paths(PathSelectors.any()) .build(); }

5.3 参数校验实践

使用Validation注解:

@Data public class EmployeeDTO { @NotBlank(message = "姓名不能为空") private String name; @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误") private String mobile; }

Controller层校验:

@PostMapping public R addEmployee(@Valid @RequestBody EmployeeDTO dto) { return R.ok(employeeService.add(dto)); }

6. 性能优化方案

6.1 二级缓存配置

MyBatis开启二级缓存:

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

6.2 接口响应优化

使用Spring Cache注解:

@Cacheable(value = "dept", key = "#id") public Department getById(Long id) { return mapper.selectById(id); }

6.3 SQL性能监控

建议配置Druid监控:

# 开启监控统计 spring.datasource.druid.filter.stat.enabled=true # 开启慢SQL记录 spring.datasource.druid.filter.stat.log-slow-sql=true spring.datasource.druid.filter.stat.slow-sql-millis=2000

7. 异常处理机制

全局异常处理器示例:

@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) @ResponseBody public R handleBusinessException(BusinessException e) { return R.fail(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) @ResponseBody public R handleException(Exception e) { log.error("系统异常", e); return R.fail(500, "系统繁忙"); } }

自定义业务异常类:

public class BusinessException extends RuntimeException { private Integer code; public BusinessException(Integer code, String message) { super(message); this.code = code; } }

8. 安全防护措施

8.1 SQL注入防护

永远不要拼接SQL:

// 错误示范 String sql = "SELECT * FROM user WHERE name = '" + name + "'"; // 正确做法 mapper.selectByName(name);

8.2 XSS过滤

添加全局过滤器:

@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { chain.doFilter(new XssHttpServletRequestWrapper(request), response); }

8.3 数据脱敏

在DTO层处理敏感字段:

public String getMobile() { return StringUtils.overlay(mobile, "****", 3, 7); }

9. 部署与监控

9.1 打包部署

推荐使用分层构建Docker镜像:

FROM adoptopenjdk:11-jre-hotspot COPY target/hr-system.jar /app.jar ENTRYPOINT ["java","-jar","/app.jar"]

9.2 健康检查

SpringBoot Actuator配置:

management.endpoints.web.exposure.include=health,info management.endpoint.health.show-details=always

9.3 日志收集

Logback的ELK配置示例:

<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <destination>logstash:5044</destination> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender>

10. 扩展功能展望

当基础功能稳定后,可以考虑:

  1. 集成工作流引擎处理审批流程
  2. 增加BI模块进行人力数据分析
  3. 开发移动端应用支持随时随地办公
  4. 接入AI能力实现智能排班

我在实际项目中遇到过部门树形结构查询的性能瓶颈,后来通过引入Redis缓存部门关系数据,查询性能提升了8倍。关键代码是这样的:

public List<Department> getCachedDepartmentTree() { String cacheKey = "dept:tree"; String json = redisTemplate.opsForValue().get(cacheKey); if(StringUtils.isNotEmpty(json)) { return JSON.parseArray(json, Department.class); } List<Department> tree = buildDepartmentTree(); redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(tree), 1, TimeUnit.HOURS); return tree; }
http://www.jsqmd.com/news/1192446/

相关文章:

  • AI聚合平台实战指南:多模型统一接入与智能调度原理
  • 8086汇编实战:8255A动态扫描数码管显示自定义字符串
  • Mythos与Glasswing:通用大模型驱动的AI安全范式革命
  • PyTorch工程实战:动态图、GPU内存与分布式训练深度解析
  • 合规解读:联邦法规CPSC 16 CFR § 1700.20儿童防护CR包装测试
  • 游戏加速新选择:3分钟掌握OpenSpeedy,让你的游戏帧率飞起来!
  • convoC2架构揭秘:深度分析利用Teams aria-label属性进行命令注入的技术原理
  • 百分比计算与提取:Money库的extractPercentage方法完全解析
  • 积家中国官方售后服务中心|服务电话及全部维修地址权威信息公告(2026年7月更新) - 积家官方售后服务中心
  • 模板驱动文档自动化:零代码生成PDF的工程实践
  • Kimi Code接入VS Code实战:自建LSP代理方案
  • C++高性能QUIC协议栈实现:从架构设计到性能优化实战
  • Jackson核心模块与实战应用解析
  • Godot游戏开发:SQLite数据库集成与数据持久化实战指南
  • 用CDF曲线做服务水位分析:从等待时间到SLA落地的工程实践
  • 基于STM32 DAC与XL6008的数控升压电源实现与精度优化
  • HsMod:让炉石传说变得更快、更智能、更有趣的55个魔法技巧
  • Happy Island Designer:3个步骤让你成为虚拟岛屿的建筑师
  • 忻州礼品定制杂粮推荐哪家? - 中媒介
  • EIGRP协议故障诊断与排错实战-Cisco Packet Tracer(思科模拟器)
  • 开源项目PicoGUS路线图:未来将支持哪些令人期待的新功能?
  • 深入解析yocto-embedded-tools交叉编译器构建:支持ARM64/ARM32/RISCV64/X86_64架构
  • convoC2快速入门:10分钟搭建基于Microsoft Teams的命令与控制系统
  • OC角色情感权重排序:从夯到拉构建深度叙事关系网
  • C++内存管理:从虚拟内存到内存池的底层原理与实战优化
  • y=cos(x)/x 在x=0处的垂直渐近线与无穷极限成因
  • TMS320C6746 McASP与McBSP串行接口配置详解与实战指南
  • GNFC智能流控机制:接收端驱动拥塞控制算法详解
  • 华为Sound X5与金标音质智能音箱实测:音质与智能如何兼得
  • 2026年7月最新东莞天梭官方售后客户服务电话及线下网点地址 - 天梭服务中心