1. 项目概述
最近在开发一个基于Java的员工管理系统,这个项目让我想起了十年前第一次接触企业信息化系统时的场景。那时候的HR系统大多笨重难用,而现在我们可以用SpringBoot等现代框架轻松构建功能完善的管理系统。这个系统包含了员工信息管理、绩效考核、薪酬福利等核心模块,基本上覆盖了中小企业日常人事管理的全部需求。
系统采用经典的MVC架构,前端用JSP实现页面展示,后端基于SpringBoot框架,数据库选用MySQL。这种组合在中小型Web应用中非常常见,既能保证开发效率,又能满足性能需求。下面我会从设计思路到具体实现,详细分享这个项目的开发过程。
2. 系统架构设计
2.1 技术选型考量
选择SpringBoot作为基础框架有几个重要原因:
- 自动配置特性大大减少了XML配置的工作量
- 内嵌Tomcat服务器简化了部署流程
- 丰富的starter依赖可以快速集成各种常用组件
- 完善的文档和社区支持
数据库方面,MySQL是首选,因为:
- 人事管理系统的数据关系明确但不算特别复杂
- 事务需求主要是CRUD操作,不需要太高级的特性
- 开源免费,适合中小型企业预算
前端选择JSP而不是更现代的Vue/React,主要考虑到:
- 项目团队对JSP更熟悉,学习成本低
- 系统交互以表单提交为主,不需要复杂的前端状态管理
- 维护人员的技术栈匹配
2.2 系统模块划分
系统主要分为六大功能模块:
-
员工基础信息管理
- 员工档案CRUD
- 组织架构管理
- 权限分配
-
绩效评估模块
- 员工评语记录
- 过失记录管理
- 奖惩记录
-
薪酬福利模块
- 基本工资管理
- 奖金发放
- 社保公积金记录
-
公告通知模块
- 公告发布
- 历史查询
-
系统管理
- 用户登录验证
- 个人信息维护
- 系统配置
-
数据统计模块
- 员工数据报表
- 薪酬统计分析
这种模块化设计遵循了"高内聚低耦合"的原则,每个模块都有明确的职责边界,便于后期维护和功能扩展。
3. 数据库设计详解
3.1 E-R模型设计
数据库设计是系统的核心,我采用了实体-关系模型来规划数据结构。主要实体包括:
-
员工实体(yuangong)
- 存储员工基本信息
- 与评语、奖金等实体是一对多关系
-
评语实体(ygpy)
- 记录主管对员工的评价
- 包含评价内容、日期等信息
-
奖金实体(jiangjin)
- 记录奖金发放情况
- 关联员工和发放信息
-
社保实体(sbjl)
- 管理五险一金缴纳记录
- 包含各项保险明细
-
过失实体(gsjl)
- 记录员工工作失误
- 用于绩效考核参考
-
公告实体(gonggao)
- 存储系统公告
- 包含标题、内容、发布时间
实体间关系通过外键关联,如评语表中的yg字段关联员工表的ygid。这种设计保证了数据的完整性和一致性。
3.2 数据表结构
以下是核心表的结构设计:
员工信息表(yuangong)
sql复制CREATE TABLE `yuangong` (
`ygid` int(11) NOT NULL AUTO_INCREMENT,
`yhm` varchar(40) NOT NULL COMMENT '用户名',
`mm` varchar(40) NOT NULL COMMENT '密码',
`xm` varchar(40) NOT NULL COMMENT '姓名',
`gh` varchar(40) NOT NULL COMMENT '工号',
`qx` varchar(40) NOT NULL COMMENT '权限(admin/user)',
`xb` varchar(2) NOT NULL COMMENT '性别',
`dx` decimal(10,2) NOT NULL COMMENT '底薪',
`lxdh` varchar(20) NOT NULL COMMENT '联系电话',
`lxdz` varchar(100) NOT NULL COMMENT '联系地址',
PRIMARY KEY (`ygid`),
UNIQUE KEY `yhm_UNIQUE` (`yhm`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
奖金信息表(jiangjin)
sql复制CREATE TABLE `jiangjin` (
`jjid` int(11) NOT NULL AUTO_INCREMENT,
`yg` varchar(40) NOT NULL COMMENT '员工ID',
`rq` date NOT NULL COMMENT '发放日期',
`jj` decimal(10,2) NOT NULL COMMENT '奖金金额',
`ffkh` varchar(20) NOT NULL COMMENT '发放卡号',
`sm` varchar(200) DEFAULT NULL COMMENT '发放说明',
`ffr` varchar(40) NOT NULL COMMENT '发放人',
PRIMARY KEY (`jjid`),
KEY `yg_idx` (`yg`),
CONSTRAINT `jiangjin_ibfk_1` FOREIGN KEY (`yg`) REFERENCES `yuangong` (`ygid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
设计时特别注意了几点:
- 主键全部使用自增整数,提高查询效率
- 密码字段存储的是MD5加密后的值
- 金额字段使用DECIMAL类型,避免浮点精度问题
- 建立了适当的外键约束保证数据完整性
- 为常用查询字段添加了索引
4. 核心功能实现
4.1 登录认证模块
登录功能采用了经典的Session认证方式:
java复制@Controller
@RequestMapping("/auth")
public class AuthController {
@Autowired
private YuangongService yuangongService;
@PostMapping("/login")
public String login(String yhm, String mm, HttpSession session) {
Yuangong user = yuangongService.login(yhm, mm);
if(user != null) {
session.setAttribute("user", user);
return "redirect:/index";
}
return "redirect:/login?error=1";
}
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/login";
}
}
安全措施包括:
- 密码传输使用HTTPS
- 存储时使用MD5加盐哈希
- 登录失败不提示具体原因
- Session设置合理超时时间
- 关键操作需要重新认证
4.2 员工管理CRUD
员工信息的增删改查是系统的基础功能,采用经典的DAO-Service-Controller分层架构:
DAO层示例:
java复制@Repository
public class YuangongDaoImpl implements YuangongDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void insert(Yuangong yuangong) {
String sql = "INSERT INTO yuangong(yhm,mm,xm,gh,qx,xb,dx,lxdh,lxdz) VALUES(?,?,?,?,?,?,?,?,?)";
jdbcTemplate.update(sql,
yuangong.getYhm(),
DigestUtils.md5Hex(yuangong.getMm()),
yuangong.getXm(),
yuangong.getGh(),
yuangong.getQx(),
yuangong.getXb(),
yuangong.getDx(),
yuangong.getLxdh(),
yuangong.getLxdz());
}
@Override
public List<Yuangong> findAll() {
String sql = "SELECT * FROM yuangong";
return jdbcTemplate.query(sql, new YuangongRowMapper());
}
}
Service层处理业务逻辑:
java复制@Service
public class YuangongServiceImpl implements YuangongService {
@Autowired
private YuangongDao yuangongDao;
@Override
public void addYuangong(Yuangong yuangong) throws BusinessException {
// 检查用户名是否已存在
if(yuangongDao.countByYhm(yuangong.getYhm()) > 0) {
throw new BusinessException("用户名已存在");
}
yuangongDao.insert(yuangong);
}
@Override
public PageInfo<Yuangong> listYuangong(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Yuangong> list = yuangongDao.findAll();
return new PageInfo<>(list);
}
}
Controller处理HTTP请求:
java复制@Controller
@RequestMapping("/yuangong")
public class YuangongController {
@Autowired
private YuangongService yuangongService;
@GetMapping("/list")
public String list(@RequestParam(defaultValue="1") int pageNum,
@RequestParam(defaultValue="10") int pageSize,
Model model) {
PageInfo<Yuangong> pageInfo = yuangongService.listYuangong(pageNum, pageSize);
model.addAttribute("pageInfo", pageInfo);
return "yuangong/list";
}
@PostMapping("/add")
public String add(Yuangong yuangong, RedirectAttributes redirectAttributes) {
try {
yuangongService.addYuangong(yuangong);
redirectAttributes.addFlashAttribute("message", "添加成功");
} catch (BusinessException e) {
redirectAttributes.addFlashAttribute("error", e.getMessage());
}
return "redirect:/yuangong/list";
}
}
4.3 奖金管理实现
奖金管理需要处理金额计算和发放记录:
java复制@Service
public class JiangjinServiceImpl implements JiangjinService {
@Autowired
private JiangjinDao jiangjinDao;
@Autowired
private YuangongDao yuangongDao;
@Override
@Transactional
public void grantJiangjin(Jiangjin jiangjin) throws BusinessException {
// 验证员工是否存在
Yuangong yuangong = yuangongDao.findById(jiangjin.getYg());
if(yuangong == null) {
throw new BusinessException("员工不存在");
}
// 验证金额是否合法
if(jiangjin.getJj().compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("奖金金额必须大于0");
}
// 记录发放信息
jiangjin.setRq(new Date());
jiangjinDao.insert(jiangjin);
// 发送通知
sendJiangjinNotice(yuangong, jiangjin);
}
private void sendJiangjinNotice(Yuangong yuangong, Jiangjin jiangjin) {
// 实现邮件或短信通知
}
}
5. 系统部署与优化
5.1 部署方案
系统采用分层部署架构:
- Web层:Nginx + Tomcat集群
- 应用层:SpringBoot应用
- 数据层:MySQL主从复制
部署步骤:
- 打包应用:
mvn clean package - 配置Nginx负载均衡
- 初始化数据库
- 配置应用连接池
- 设置定时备份任务
5.2 性能优化措施
-
数据库优化
- 合理设计索引
- 查询避免SELECT *
- 大数据量表分区
-
应用层优化
- 使用连接池管理数据库连接
- 缓存常用数据
- 异步处理耗时操作
-
前端优化
- 静态资源CDN加速
- 启用Gzip压缩
- 合理使用浏览器缓存
6. 开发经验与问题解决
6.1 常见问题排查
-
中文乱码问题
- 确保数据库、连接、页面三处编码一致(UTF-8)
- 在JDBC连接字符串中添加
useUnicode=true&characterEncoding=UTF-8 - JSP页面添加
<%@ page contentType="text/html;charset=UTF-8" %>
-
事务不生效
- 检查方法是否为public
- 确认是否添加了@Transactional注解
- 异常类型是否正确配置(默认只回滚RuntimeException)
-
页面加载慢
- 使用开发者工具分析网络请求
- 检查N+1查询问题
- 添加合适的数据库索引
6.2 开发心得
-
代码规范很重要
- 统一命名风格
- 合理分包分层
- 编写清晰的注释
-
测试驱动开发
- 先写测试用例再实现功能
- 单元测试覆盖核心逻辑
- 集成测试验证模块交互
-
文档不可或缺
- 数据库设计文档
- API接口文档
- 部署运维手册
这个项目从设计到实现大约用了两个月时间,期间遇到了不少挑战,但也积累了很多宝贵的经验。特别是对于企业级应用开发,稳定性、安全性和可维护性往往比炫酷的功能更重要。