项目Gitee
地址
博客地址
项目需求
人力资源管理系统定位
- 实现的人力资源管理系统面向股东,员工以及实习生服务。
- 通过实现各模块功能,通过系统的方法进行公司的人员的加入,内部调动及离职人员变动,并为这些人员流动提供辅助管理。
- 以确保在适当的职位上又各种适当的人才能够既有效率又有效能地完成各项任务。
用户权限与认证
- 本模块实现的功能是用户的注册以及权限的分配,连接数据库和
shiro
实现用户安全,结合thymeleaf-shiro
实现用户权限分组。 JS
设计登录的密码长度判断,用户名格式是否正确。- 登陆成功跳转到主页,或者选择注册,注册成功后,跳转到登录。
- 不同权限用户可查看的页面不同,操作权限也不同。
部门管理
- 前端方面,需要画树状图,在点击结点的时候,显示其对应部门下的所有员工。
- 在这里,树状图的绘制通过模板的
fuel ux
库完成。 - 在前端中得到从后端传来的
json
字符串作为画树状图的数据源,在js中用其parse
函数转换为js
对象,并定义渲染时的回调函数。 - 在点击结点的时候,调用一个
jumptoDepartment
函数,根据点击的节点自动跳转到相应的部门人员显示页面。 - 后端方面,建立一颗树用来表示部门之间的从属关系,每个结点中用一个
List
储存子部门的各个信息,同时给前端提供其相应的函数。
薪资管理
- 员工信息统计:
包括基础工资,所属部门,每月加班时长累计,每月考勤请假情况等;并且实现查询功能,员工信息添加功能,删除功能,分页功能,模糊查询功能,分页查询功能。 - 薪资明细表:
算出员工的奖金,处罚金,纳税金额,最后结算工资;并且实现查询功能,删除功能,分页功能,模糊查询功能,分页查询功能。
考勤管理
- 本模块实现的功能是查看员工的考勤状况
- 将员工的考勤状态分为:1:出勤,2:请假,3:出差,4:加班,5:补休,6:调班,7:停工。
- 页面在显示状态的同时也显示出员工的ID,姓名,状态的开始和结束时间,并添加备注。
- 在每个员工表格行的末尾添加修改功能,点击修改跳转到修改页面,可以修改该员工考勤状态、时间和备注。
- 另外增加了筛选功能,可以通过选择考勤状况查看处于该状态的员工。
人事处理
- 此模块实现调动管理管理模块,包括职员入职、转正、调岗、调薪、离职、复职等业务。
发布部署到服务器
项目设计
登录注册

部门管理

薪资管理

考勤管理

人事处理

数据库设计
Userall
(用户)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
user | varchar | 255 | 0 | 1 | key |
password | varchar | 255 | 0 | 1 | |
salt | varchar | 255 | 0 | 0 | |
UserPer
(用户权限)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
user | varchar | 255 | 0 | 1 | key |
perms | varchar | 255 | 0 | 0 | |
department
(部门)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
department | varchar | 255 | 0 | 1 | |
d_id | int | 255 | 0 | 1 | key |
parid | int | 255 | 0 | 0 | |
employees
(职员【所有】)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
id | int | 11 | 0 | 1 | key |
name | varchar | 255 | 0 | 1 | |
age | int | 11 | 0 | 1 | |
sex | varchar | 2 | 0 | 1 | |
phone | varchar | 255 | 0 | 0 | |
email | varchar | 255 | 0 | 0 | |
dName | varchar | 255 | 0 | 1 | |
pName | varchar | 255 | 0 | 1 | |
gName | varchar | 255 | 0 | 1 | |
flag | bit | 1 | 0 | 1 | |
gangwei
(岗位)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
name | varchar | 255 | 0 | 0 | |
g_id | int | 255 | 0 | 1 | key |
kaoqin
(考勤)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
id | int | 11 | 0 | 1 | key |
name | varchar | 255 | 0 | 0 | |
type | int | 11 | 0 | 1 | |
start | date | 0 | 0 | 0 | |
end | date | 0 | 0 | 1 | |
remark | varchar | 255 | 0 | 0 | |
position
(职位)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
position | varchar | 255 | 0 | 1 | |
p_id | int | 255 | 0 | 1 | key |
level | int | 255 | 0 | 0 | |
salary
(薪资)
名 | 类型 | 长度 | 小数点 | 不是null | |
---|
time | varchar | 255 | 0 | 0 | |
gid | int | 11 | 0 | 1 | key |
pid | int | 11 | 0 | 1 | |
username | varchar | 255 | 0 | 0 | |
basicSalary | int | 11 | 0 | 1 | |
dname | varchar | 255 | 0 | 0 | |
workovertime | int | 11 | 0 | 1 | |
checkaccout | int | 11 | 0 | 1 | |
项目核心代码
详细设计
登录与注册
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String indexPage(String username, String password, Map<String, Object> map, HttpSession session, HttpServletRequest request, Model model) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
model.addAttribute("username", username);
session.setAttribute("username", username);
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名不存在!");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "用户名或密码错误!");
return "login";
}
}
@RequestMapping(value = "/login")
public String regPageToLogin(String username, String password, Map<String, Object> map, HttpSession session, Model model) {
Userall userall = new Userall(username, password);
if (username == null || password == null) {
return "reg";
}
if (password.length() < 4 || password.length() > 10) {
return "reg";
}
Userall userall1 = userCheckServiceImpl.selectByKeyName(username);
if (userall1 != null) {
map.put("error", "用户名重复");
return "reg";
}
try {
Integer id = Integer.parseInt(username);
boolean flag = userCheckServiceImpl.insert(username, password);
if (flag) {
String role = employServiceImpl.getRole(Integer.parseInt(username));
userCheckServiceImpl.insertUserPer(username, role);
return "login";
} else {
Userall usercheck = userCheckServiceImpl.selectByNameAndPassword(username, password);
if (usercheck == null) {
return "reg";
} else {
return "login";
}
}
} catch (Exception e) {
map.put("error", "用户名不符合数字格式");
return "reg";
}
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证:Authentiction");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Userall user = userCheckService.selectByKeyName(token.getUsername());
String DBPassword = user.getPassword();
String Salt = user.getSalt();
System.out.println(Salt);
return new SimpleAuthenticationInfo(user,DBPassword, ByteSource.Util.bytes(Salt),"");
}
@Override
public boolean insert(String username, String password) {
String Salt = new SecureRandomNumberGenerator().nextBytes().toHex();
SimpleHash simpleHash = new SimpleHash("md5",password,Salt,1);
String NewPassword = simpleHash.toString();
userallMapper.insert(username,NewPassword,Salt);
return true;
}
<div class="container">
<form class="form-signin" action="/index" id = "signForm">
<div class="form-signin-heading text-center">
<h1 class="sign-title">登录</h1>
<img src="../static/images/login-top.png" alt=""/>
</div>
<div class="login-wrap">
<input type="text" class="form-control" placeholder="User ID" autofocus name="username" id = "username" required="required">
<input type="password" class="form-control" placeholder="Password" name="password" id = "password" required="required"><p class="message">请输入4~10位密码</p>
<script>
var password = document.querySelector('.form-control');
var message = document.querySelector('.message');
password.onblur = function() {
if (this.value.length < 4 || this.value.length > 10) {
message.innerHTML = '密码长度错误,应为4~10位';
message.className = 'message wrong';
} else {
message.innerHTML = '密码长度正确';
message.className = 'message right';
}
}
</script>
<button class="btn btn-lg btn-login btn-block" type="submit" onclick="mycheck()" >
<i class="fa fa-check"></i>
</button>
<div class="registration">
<a class="" href="/reg">
注册
</a>
</div>
</div>
</form>
</div>
<div class="login-wrap">
<input type="text" autofocus="" placeholder="输入编号,实习生请输入0" class="form-control" name="username" required="required" id="username">
<input type="password" autofocus="" placeholder="password" class="form-control" name="password" required="required" id="password"><p class="message">请输入4~10位密码</p>
<p th:text="${error}" color="red"></p>
<script>
var password = document.querySelector('.form-control');
var message = document.querySelector('.message');
password.onblur = function() {
if (this.value.length < 4 || this.value.length > 10) {
message.innerHTML = '密码长度错误,应为4~10位';
message.className = 'message wrong';
} else {
message.innerHTML = '密码长度正确';
message.className = 'message right';
}
}
</script>
<button type="submit" class="btn btn-lg btn-login btn-block" onclick="mycheck()">
<i class="fa fa-check"></i>
</button>
<div class="registration">
Already Registered.
<a href="login.html" class="">
Login
</a>
</div>
</div>
部门管理
Controller层
Service层
@Service
public class departmentTree {
private Department department;
private List<departmentTree> children;
departmentTree() {
}
departmentTree(DepartmentMapper departmentMapper, int id) {
department = departmentMapper.getById(id);
department.setdId(id);
children = new ArrayList<departmentTree>();
for (int childrenId : departmentMapper.geIdtByParId(id)) {
children.add(new departmentTree(departmentMapper, childrenId));
}
}
ArrayList<Employees> getAllEmployee(EmployeesMapper employeesMapper) {
ArrayList<Employees> ans = employeesMapper.getBydName(department.getDepartment());
for (departmentTree tmp : children) {
ans.addAll(tmp.getAllEmployee(employeesMapper));
}
return ans;
}
Department getDepartment() {
return department;
}
public List<departmentTree> getChildren() {
return children;
}
}
@Service
public class departmentTool implements departmantToolService {
@Autowired
DepartmentMapper departmentMapper;
@Autowired
EmployeesMapper employeesMapper;
private String getJSONByNode(departmentTree node) {
JSONObject JSON = new JSONObject();
String ch = null;
for (departmentTree son : node.getChildren()) {
if (ch == null)
ch = getJSONByNode(son);
else
ch = ch + "," + getJSONByNode(son);
}
return "{" + "\"name\": \"" + node.getDepartment().getDepartment()
+ "\" ,\"type\": \"" + (node.getChildren().size() != 0 ? "folder" : "item")
+ "\" ,\"id\": " + node.getDepartment().getdId()
+ " ,\"children\": " + "[" + (ch == null ? "" : ch) + "]"
+ "}";
}
@Override
public departmentTree getRoot() {
return new departmentTree(departmentMapper, 0);
}
@Override
public String getJSON() {
return "[" + getJSONByNode(new departmentTree(departmentMapper, 0)) + "]";
}
@Override
public ArrayList<Employees> getEmployeeById(int id) {
return (new departmentTree(departmentMapper, id)).getAllEmployee(employeesMapper);
}
}
Mapper层
@Mapper
public interface DepartmentMapper {
int deleteByPrimaryKey(Integer dId);
int insert(Department record);
int insertSelective(Department record);
Department selectByPrimaryKey(Integer dId);
int updateByPrimaryKeySelective(Department record);
int updateByPrimaryKey(Department record);
@Select("select * from department where d_id = #{id} ")
Department getById(int id);
@Select("select d_id from department where parid = #{id} ")
List<Integer> geIdtByParId(int id);
@Select("select * from department")
List<Department> getAll();
}
@Mapper
public interface EmployeesMapper {
int deleteByPrimaryKey(Integer id);
int insert(Employees record);
int insertSelective(Employees record);
Employees selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Employees record);
int updateByPrimaryKey(Employees record);
@Select("select * from employees")
List<Employees> getAll();
@Select("select * from employees where dName = #{dName} ")
ArrayList<Employees> getBydName(String dName);
}
前端HTML&JS
var DMPTree = function () {
return {
init: function () {
var DataSourceTree = function (options) {
this._data = options.data;
this._delay = options.delay;
};
DataSourceTree.prototype = {
data: function (options, callback) {
var nodes;
if($.isEmptyObject(options)) {
nodes = this._data;
}
else {
if(options.children == null || options.children == undefined) {
nodes = {};
}
else {
nodes = options.children;
}
}
setTimeout(function () {
callback({ data: nodes });
}, this._delay)
}
};
var treeDataSourceDynamicStr = $("#dataTreeSrc").val();
var treeDataSourceDynamic = JSON.parse(treeDataSourceDynamicStr);
var deparmentTreeSrc = new DataSourceTree({
data: treeDataSourceDynamic,
delay: 400
});
$('#DepartmentTree').tree({
dataSource: deparmentTreeSrc,
loadingHTML: '<img src="../static/images/input-spinner.gif"/>'
});
}
};
}();
前端管理跳转函数:
<script>
function jumpToDepartment() {
var dmpid = ($('#DepartmentTree').tree('selectedItems')[0]).id;
window.location.href = "/index/department/" + dmpid;
}
</script>
HTML接收后端传入json字符串:
<input type="hidden" id="dataTreeSrc" th:value="${treeDataSrc}">
薪资管理
Controller层
@RequestMapping("/index/salary_search")
public String salary_select(Model model,String username,
@RequestParam(required = false,defaultValue="1",value="pageNum")Integer pageNum,
@RequestParam(defaultValue="5",value="pageSize")Integer pageSize){
if(pageNum==null || pageNum<=0){
pageNum = 1;
}
if(pageSize == null){
pageSize = 1;
}
PageHelper.startPage(pageNum,pageSize);
try {
List<Salary> users=salaryServiceImpl.findUserByName(username);
PageInfo<Salary> pageInfo = new PageInfo<Salary>(users,pageSize);
model.addAttribute("pageInfo",pageInfo);
model.addAttribute("salary",users);
model.addAttribute("username",username);
}finally {
}
return "salary_search";
@GetMapping("/index/salary_delete/{id}")
public String salary_delete(@PathVariable Integer id){
salaryServiceImpl.salary_delete(id);
return "redirect:/index/salary";
}
@RequestMapping("/index/salary_insert")
public String insertPage(){
return "salary_insert";
}
@RequestMapping("/index/salaryBack")
public String insert(Salary salary) {
salaryServiceImpl.add(salary);
return "redirect:/index/salary_control";
}
Service层
List<Salary> getSalary();
int salary_delete(Integer id);
List<Salary> findUserByName(String username);
void add(Salary salary);
int update(Salary salary);
Mapper层
@Select("select * from salary")
List<Salary> getSalary();
@Select("select * from kaoqin")
List<Salary> getKaqqinInformation();
@Delete("delete from salary\n" +
" where gid = #{gid,jdbcType=INTEGER}")
int salary_delete(Integer gid);
@Select("select * from salary where username like CONCAT('%',#{name},'%')")
List<Salary> findUserByName(String username);
@Insert("insert into salary (`time`, pid, username, \n" +
" basicSalary, dname, workovertime, \n" +
" checkaccount)\n" +
" values (#{time,jdbcType=VARCHAR}, #{pid,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, \n" +
" #{basicsalary,jdbcType=INTEGER}, #{dname,jdbcType=VARCHAR}, #{workovertime,jdbcType=INTEGER}, \n" +
" #{checkaccount,jdbcType=INTEGER})")
void add(Salary user);
考勤管理
Controller层
@RequestMapping("/index/employee_table2")
public String KaoqinList(Model model,
@RequestParam(required = false,defaultValue="1",value="pageNum")Integer pageNum,
@RequestParam(defaultValue="10",value="pageSize")Integer pageSize){
if(pageNum==null || pageNum<=0){
pageNum = 1;
}
if(pageSize == null){
pageSize = 1;
}
PageHelper.startPage(pageNum,pageSize);
List<Kaoqin> kaoqins = kaoqinServiceImpl.kaoqinList();
PageInfo<Kaoqin> pageInfo = new PageInfo<Kaoqin>(kaoqins,pageSize);
model.addAttribute("pageInfo",pageInfo);
model.addAttribute("kaoqins",kaoqins);
return "kaoqin_table";
}
@RequestMapping("/find")
public String find(Integer type, Model model){
List<Kaoqin> kaoqins = kaoqinServiceImpl.findKaoqin(type);
model.addAttribute("kaoqins",kaoqins);
System.out.println(type);
return "kaoqin_table";
}
@GetMapping("/update/{id}")
public String update(Model model, @PathVariable int id){
Kaoqin kaoqin = kaoqinServiceImpl.findKaoqinById(id);
model.addAttribute("kaoqin",kaoqin);
return "updateKaoqin";
}
@PostMapping("/update")
public String updateKaoqin(Kaoqin kaoqin){
kaoqinServiceImpl.updateKaoqin(kaoqin);
return "redirect:/index/employee_table2";
}
Service层
@Autowired
KaoqinMapper kaoqinMapper;
@Override
public List<Kaoqin> kaoqinList(){
return kaoqinMapper.kaoqinList();
}
@Override
public List<Kaoqin> findKaoqin(Integer type){
return kaoqinMapper.findKaoqin(type);
}
@Override
public Kaoqin findKaoqinById(int id){
return kaoqinMapper.findKaoqinById(id);
}
@Override
public int updateKaoqin(Kaoqin kaoqin){
return kaoqinMapper.updateKaoqin(kaoqin);
}
List<Kaoqin> kaoqinList();
List<Kaoqin> findKaoqin(Integer type);
Kaoqin findKaoqinById(int id);
int updateKaoqin(Kaoqin kaoqin);
Mapper层
@Select("select * from kaoqin")
List<Kaoqin> kaoqinList();
@Select("select * from kaoqin where type = #{type}")
List<Kaoqin> findKaoqin(Integer type);
@Select("select * from kaoqin where id = #{id}")
Kaoqin findKaoqinById(int id);
@Update("update kaoqin set type = #{type}, start = #{start}, end = #{end}, remark = #{remark} where id = #{id}")
int updateKaoqin(Kaoqin kaoqin);
人事处理
Controller层
public String employeeListPage(Model model, @RequestParam(defaultValue = "1") Integer pageNum) {
PageHelper.startPage(pageNum, 5);
List<Employees> list = employServiceImpl.getAll();
PageInfo<Employees> pageInfo = new PageInfo<Employees>(list);
model.addAttribute("pageInfo", pageInfo);
model.addAttribute("employee", list);
return "employee_table";
}
@RequestMapping(value = "/index/addEmployee")
public String addStus(String name,int age,String sex,
String dname,String pname,String gname,boolean flag,int id,String email,String phone,
@RequestParam(defaultValue = "1") Integer pageNum,
Model model) {
Employees employees = new Employees(name,age,sex,dname,pname,gname,email,phone,id,flag);
try {
employServiceImpl.addEmployees(employees);
}catch (Exception e){
return "addStu";
}
return "index";
}
@RequestMapping(value = "/index/delEmployee")
public String delEmployee(String name,int age,String sex,
String dname,String pname,String gname,boolean flag,int id,String email,String phone) {
Employees employees = new Employees(name,age,sex,dname,pname,gname,email,phone,id,flag);
employServiceImpl.delEmployees(employees);
return "delSuccess";
}
Sevice层
EmployeesMapper employeesMapper;
@Override
public void addEmployees(Employees employees) {
employeesMapper.save(employees);
}
@Override
public void delEmployees(Employees employees) {
employeesMapper.delete(employees);
}
@Override
public List<Employees> getAll() {
return employeesMapper.getAll();
}
Mapper层
@Select("select * from employees")
List<Employees> getAll();
@Delete("DELETE from employees where name = #{name} and age = #{age} and sex = #{sex} and id = #{id} ")
void delete(Employees employee);
@Insert("INSERT INTO employees VALUES(#{id},#{name},#{age},#{sex},#{phone},#{email},#{dname},#{pname},#{gname},#{flag})")
void save(Employees employee);
用户权限分组
shiroConfiguration
@Configuration
public class ShiroConfiguration {
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
return hashedCredentialsMatcher;
}
@Bean(name = "shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroRealm shiroRealm() {
ShiroRealm realm = new ShiroRealm();
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
return securityManager;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login");
shiroFilterFactoryBean.setFilters(filters);
Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
filterChainDefinitionManager.put("/login","anon");
filterChainDefinitionManager.put("/logout", "anon");
filterChainDefinitionManager.put("/index","anon");
filterChainDefinitionManager.put("/index/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);
shiroFilterFactoryBean.setLoginUrl("/");
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
return shiroFilterFactoryBean;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
aASA.setSecurityManager(securityManager());
return aASA;
}
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
shiroRealm
public class ShiroRealm extends AuthorizingRealm {
@Autowired
userCheckServiceImpl userCheckService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权:Authorization");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();
Userall userall = (Userall)subject.getPrincipal();
String role = userCheckService.getUserPer(userall.getUser());
System.out.println(role);
info.addStringPermission(role);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证:Authentiction");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Userall user = userCheckService.selectByKeyName(token.getUsername());
String DBPassword = user.getPassword();
String Salt = user.getSalt();
System.out.println(Salt);
return new SimpleAuthenticationInfo(user,DBPassword, ByteSource.Util.bytes(Salt),"");
}
}
thymeleaf+shiro
<li class="menu-list nav-active" shiro:hasAnyPermissions="boss,deputy,fudeputy,staff">
<a href="#"><i class="fa fa-th-list"></i> <span>人事归档</span></a>
<ul class="sub-menu-list">
<li class="active"><a href="/index/employee_table">人事归档</a></li>
<li class="active" shiro:hasAnyPermissions="boss,deputy,fudeputy">
<a href="/index/addStu">员工入职</a>
</li>
<li class="active" shiro:hasAnyPermissions="boss,deputy,fudeputy">
<a href="/index/delStu">员工离职</a>
</li>
</ul>
</li>
项目部署
部署网址