1
0
mirror of https://github.com/FatttSnake/Pinnacle-OA.git synced 2026-04-05 23:11:24 +08:00

Added StaffManagement

This commit is contained in:
2023-05-30 22:06:42 +08:00
parent 3aed237d5e
commit 804d056a89
17 changed files with 444 additions and 10 deletions

View File

@@ -1,7 +1,16 @@
package com.cfive.pinnacle.controller; package com.cfive.pinnacle.controller;
import org.springframework.web.bind.annotation.RequestMapping; import com.cfive.pinnacle.entity.common.ResponseCode;
import org.springframework.web.bind.annotation.RestController; import com.cfive.pinnacle.entity.common.ResponseResult;
import com.cfive.pinnacle.entity.permission.User;
import com.cfive.pinnacle.service.IStaffService;
import com.cfive.pinnacle.utils.WebUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/** /**
* <p> * <p>
@@ -11,8 +20,31 @@ import org.springframework.web.bind.annotation.RestController;
* @author FatttSnake * @author FatttSnake
* @since 2023-04-30 * @since 2023-04-30
*/ */
@Slf4j
@RestController @RestController
@RequestMapping("/staff") @RequestMapping("/staff")
public class StaffController { public class StaffController {
private IStaffService staffService;
@Autowired
public void setStaffService(IStaffService staffService) {
this.staffService = staffService;
}
@GetMapping
@PreAuthorize("hasAnyAuthority('staff:manege:get', 'staff:admin:get')")
public ResponseResult<List<User>> getAllStaff() {
return ResponseResult.databaseSelectSuccess(staffService.getAllStaff(WebUtil.hasAuthority("staff:admin:get") ? null : WebUtil.getLoginUser().getUser().getDepartmentId()));
}
@PutMapping
@PreAuthorize("hasAnyAuthority('staff:manege:modify', 'staff:admin:modify')")
public ResponseResult<?> modifyStaff(@RequestBody User user) {
log.info(user.toString());
if (staffService.modifyStaff(user)) {
return ResponseResult.databaseUpdateSuccess(null);
} else {
return ResponseResult.build(ResponseCode.DATABASE_SAVE_ERROR, "error", null);
}
}
} }

View File

@@ -1,10 +1,6 @@
package com.cfive.pinnacle.entity; package com.cfive.pinnacle.entity;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
@@ -63,7 +59,7 @@ public class Staff implements Serializable {
/** /**
* 生日 * 生日
*/ */
@TableField("birth") @TableField(value = "birth",updateStrategy = FieldStrategy.IGNORED)
private LocalDate birth; private LocalDate birth;
/** /**

View File

@@ -23,6 +23,7 @@ public class ResponseCode {
public static final int DATABASE_TIMEOUT_ERROR = 20035; public static final int DATABASE_TIMEOUT_ERROR = 20035;
public static final int DATABASE_CONNECT_ERROR = 20036; public static final int DATABASE_CONNECT_ERROR = 20036;
public static final int DATABASE_DATA_TO_LONG = 20037; public static final int DATABASE_DATA_TO_LONG = 20037;
public static final int DATABASE_DATA_VALIDATION_FAILED = 20038;
public static final int UNAUTHORIZED = 30010; public static final int UNAUTHORIZED = 30010;
public static final int ACCESS_DENIED = 30030; public static final int ACCESS_DENIED = 30030;

View File

@@ -9,6 +9,7 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import com.cfive.pinnacle.entity.Department;
import com.cfive.pinnacle.entity.Staff; import com.cfive.pinnacle.entity.Staff;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
@@ -54,6 +55,9 @@ public class User implements Serializable {
@JsonSerialize(using = ToStringSerializer.class) @JsonSerialize(using = ToStringSerializer.class)
private Long departmentId; private Long departmentId;
@TableField(exist = false)
private Department department;
/** /**
* 启用 * 启用
*/ */

View File

@@ -0,0 +1,23 @@
package com.cfive.pinnacle.exception;
public class DataValidationFailedException extends RuntimeException{
public DataValidationFailedException() {
super("Data validation failed");
}
public DataValidationFailedException(String message) {
super(message);
}
public DataValidationFailedException(String message, Throwable cause) {
super(message, cause);
}
public DataValidationFailedException(Throwable cause) {
super(cause);
}
public DataValidationFailedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -2,6 +2,7 @@ package com.cfive.pinnacle.handler;
import com.cfive.pinnacle.entity.common.ResponseCode; import com.cfive.pinnacle.entity.common.ResponseCode;
import com.cfive.pinnacle.entity.common.ResponseResult; import com.cfive.pinnacle.entity.common.ResponseResult;
import com.cfive.pinnacle.exception.DataValidationFailedException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
@@ -32,6 +33,9 @@ public class CustomExceptionHandler {
if (e instanceof DataIntegrityViolationException) { if (e instanceof DataIntegrityViolationException) {
return ResponseResult.build(ResponseCode.DATABASE_DATA_TO_LONG, e.getMessage(), null); return ResponseResult.build(ResponseCode.DATABASE_DATA_TO_LONG, e.getMessage(), null);
} }
if (e instanceof DataValidationFailedException) {
return ResponseResult.build(ResponseCode.DATABASE_DATA_VALIDATION_FAILED, e.getMessage(), null);
}
log.debug(e.getMessage(), e); log.debug(e.getMessage(), e);

View File

@@ -2,7 +2,11 @@ package com.cfive.pinnacle.mapper;
import com.cfive.pinnacle.entity.Staff; import com.cfive.pinnacle.entity.Staff;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cfive.pinnacle.entity.permission.User;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* <p> * <p>
@@ -14,5 +18,5 @@ import org.apache.ibatis.annotations.Mapper;
*/ */
@Mapper @Mapper
public interface StaffMapper extends BaseMapper<Staff> { public interface StaffMapper extends BaseMapper<Staff> {
List<User> getAllStaff(@Param("departmentId")Long departmentId);
} }

View File

@@ -2,6 +2,9 @@ package com.cfive.pinnacle.service;
import com.cfive.pinnacle.entity.Staff; import com.cfive.pinnacle.entity.Staff;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.cfive.pinnacle.entity.permission.User;
import java.util.List;
/** /**
* <p> * <p>
@@ -12,5 +15,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
* @since 2023-04-30 * @since 2023-04-30
*/ */
public interface IStaffService extends IService<Staff> { public interface IStaffService extends IService<Staff> {
List<User> getAllStaff(Long departmentId);
boolean modifyStaff(User user);
} }

View File

@@ -1,11 +1,19 @@
package com.cfive.pinnacle.service.impl; package com.cfive.pinnacle.service.impl;
import com.cfive.pinnacle.entity.Staff; import com.cfive.pinnacle.entity.Staff;
import com.cfive.pinnacle.entity.permission.User;
import com.cfive.pinnacle.exception.DataValidationFailedException;
import com.cfive.pinnacle.mapper.StaffMapper; import com.cfive.pinnacle.mapper.StaffMapper;
import com.cfive.pinnacle.mapper.permission.UserMapper;
import com.cfive.pinnacle.service.IStaffService; import com.cfive.pinnacle.service.IStaffService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cfive.pinnacle.utils.WebUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/** /**
* <p> * <p>
* 员工 服务实现类 * 员工 服务实现类
@@ -16,5 +24,41 @@ import org.springframework.stereotype.Service;
*/ */
@Service @Service
public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements IStaffService { public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements IStaffService {
private StaffMapper staffMapper;
private UserMapper userMapper;
@Autowired
public void setStaffMapper(StaffMapper staffMapper) {
this.staffMapper = staffMapper;
}
@Autowired
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public List<User> getAllStaff(Long departmentId) {
return staffMapper.getAllStaff(departmentId);
}
@Override
public boolean modifyStaff(User user) {
Staff newStaff = user.getStaff();
user = userMapper.getOneById(user.getId());
Staff oldStaff = user.getStaff();
if (!WebUtil.hasAuthority("staff:admin:modify")) {
if (!Objects.equals(user.getDepartmentId(), WebUtil.getLoginUser().getUser().getDepartmentId())) {
throw new DataValidationFailedException();
}
}
if (oldStaff == null) {
newStaff.setUserId(user.getId());
staffMapper.insert(newStaff);
} else {
newStaff.setId(oldStaff.getId());
staffMapper.updateById(newStaff);
}
return true;
}
} }

View File

@@ -2,4 +2,61 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cfive.pinnacle.mapper.StaffMapper"> <mapper namespace="com.cfive.pinnacle.mapper.StaffMapper">
<select id="getAllStaff" resultMap="staffMap">
select t_user.id as user_id,
t_user.username as user_username,
t_user.department_id as user_department_id,
t_user.deleted as user_deleted,
t_user.version as user_version,
ts.id as staff_id,
ts.user_id as staff_user_id,
ts.first_name as staff_first_name,
ts.last_name as staff_last_name,
ts.gender as staff_gender,
ts.birth as staff_birth,
ts.email as staff_email,
ts.tel as staff_tel,
ts.address as staff_address,
ts.deleted as staff_deleted,
ts.version as staff_version,
td.id as department_id,
td.name as department_name,
td.deleted as department_deleted,
td.version as department_version
from t_user
left join (select * from t_staff where deleted = 0) as ts on t_user.id = ts.user_id
left join (select * from t_department where deleted = 0) as td on td.id = t_user.department_id
where t_user.deleted = 0
<if test="departmentId != null">
and t_user.department_id = #{departmentId}
</if>
</select>
<resultMap id="staffMap" type="user">
<id property="id" column="user_id"/>
<result property="username" column="user_username"/>
<result property="departmentId" column="user_department_id"/>
<result property="deleted" column="user_deleted"/>
<result property="version" column="user_version"/>
<association property="staff" javaType="staff">
<id property="id" column="staff_id"/>
<result property="userId" column="staff_user_id"/>
<result property="firstName" column="staff_first_name"/>
<result property="lastName" column="staff_last_name"/>
<result property="gender" column="staff_gender"/>
<result property="birth" column="staff_birth"/>
<result property="email" column="staff_email"/>
<result property="tel" column="staff_tel"/>
<result property="address" column="staff_address"/>
<result property="deleted" column="staff_deleted"/>
<result property="version" column="staff_version"/>
</association>
<association property="department" javaType="department">
<id property="id" column="department_id"/>
<result property="name" column="department_name"/>
<result property="deleted" column="department_deleted"/>
<result property="version" column="department_version"/>
</association>
</resultMap>
</mapper> </mapper>

View File

@@ -188,7 +188,7 @@ create table `t_staff`
`birth` date null comment '生日', `birth` date null comment '生日',
`email` varchar(50) null comment '邮箱', `email` varchar(50) null comment '邮箱',
`tel` varchar(20) null comment '电话', `tel` varchar(20) null comment '电话',
`address` varchar(50) null comment '地址', `address` varchar(100) null comment '地址',
`deleted` bigint not null default 0, `deleted` bigint not null default 0,
`version` int not null default 0, `version` int not null default 0,
constraint t_staff_user_id_fk foreign key (user_id) references t_user (id) constraint t_staff_user_id_fk foreign key (user_id) references t_user (id)

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1685385425734" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8098"
width="200" height="200"><path d="M512 1024c282.771 0 512-229.23 512-512s-229.23-512-512-512-512 229.23-512 512 229.23 512 512 512zM432 256c0-44.183 35.817-80 80-80s80 35.817 80 80v31.999c0 44.183-35.817 80-80 80s-80-35.817-80-80v-31.999zM431.999 512c0-44.183 35.817-80 80-80s80 35.817 80 80v256c0 44.183-35.817 80-80 80s-80-35.817-80-80v-256z" p-id="8099"></path></svg>

After

Width:  |  Height:  |  Size: 632 B

View File

@@ -30,6 +30,7 @@ const DATABASE_DELETE_ERROR = 20034
const DATABASE_TIMEOUT_ERROR = 20035 const DATABASE_TIMEOUT_ERROR = 20035
const DATABASE_CONNECT_ERROR = 20036 const DATABASE_CONNECT_ERROR = 20036
const DATABASE_DATA_TO_LONG = 20037 const DATABASE_DATA_TO_LONG = 20037
const DATABASE_DATA_VALIDATION_FAILED = 20038
const UNAUTHORIZED = 30010 const UNAUTHORIZED = 30010
const ACCESS_DENIED = 30030 const ACCESS_DENIED = 30030
@@ -69,6 +70,7 @@ export {
DATABASE_TIMEOUT_ERROR, DATABASE_TIMEOUT_ERROR,
DATABASE_CONNECT_ERROR, DATABASE_CONNECT_ERROR,
DATABASE_DATA_TO_LONG, DATABASE_DATA_TO_LONG,
DATABASE_DATA_VALIDATION_FAILED,
UNAUTHORIZED, UNAUTHORIZED,
ACCESS_DENIED, ACCESS_DENIED,
USER_DISABLE, USER_DISABLE,

View File

@@ -0,0 +1,222 @@
<template>
<el-button bg style="background-color: white" :loading="tableLoading" @click="loadStaffTable">
<template #icon>
<el-icon>
<icon-pinnacle-refresh />
</el-icon>
</template>
</el-button>
<el-table
:data="staffTable"
v-loading="tableLoading"
element-loading-text="Loading..."
style="margin-top: 10px"
>
<el-table-column prop="username" label="用户名" width="120" align="center" />
<el-table-column label="姓名" align="center" width="120">
<template #default="scope">
{{
scope.row.staff !== null
? scope.row.staff.lastName + scope.row.staff.firstName
: ''
}}
</template>
</el-table-column>
<el-table-column label="性别" align="center" :formatter="genderFormatter" width="60" />
<el-table-column prop="staff.birth" label="生日" align="center" width="110" />
<el-table-column prop="staff.email" label="邮箱" align="center" width="140" />
<el-table-column prop="staff.tel" label="手机" align="center" width="140" />
<el-table-column prop="staff.address" label="地址" />
<el-table-column label="操作" width="80" align="center">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="编辑员工信息" :close-on-click-modal="false" draggable v-model="dialogVisible">
<template #default>
<el-form
label-width="100px"
:rules="rules"
ref="formRef"
:model="userForm"
v-loading="dialogLoading"
>
<el-form-item label="用户名" prop="inputUsername">
<el-input v-model="userForm.inputUsername" disabled />
</el-form-item>
<el-form-item label="姓氏" prop="inputLastName">
<el-input v-model="userForm.inputLastName" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="名字" prop="inputFirstName">
<el-input v-model="userForm.inputFirstName" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="性别" prop="selectedGender">
<el-select v-model="userForm.selectedGender">
<el-option label="未知" :value="0" />
<el-option label="男" :value="1" />
<el-option label="女" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="生日" prop="inputBirth">
<el-date-picker v-model="userForm.inputBirth" value-format="YYYY-MM-DD" />
</el-form-item>
<el-form-item label="邮箱" prop="inputEmail">
<el-input v-model="userForm.inputEmail" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="手机" prop="inputTel">
<el-input v-model="userForm.inputTel" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="地址" prop="inputAddress">
<el-input
v-model="userForm.inputAddress"
type="textarea"
resize="none"
maxlength="100"
show-word-limit
/>
</el-form-item>
</el-form>
</template>
<template #footer>
<el-button type="primary" @click="handleSubmit" :disabled="dialogLoading"
>提交
</el-button>
<el-button @click="handleCancel" :disabled="dialogLoading">取消</el-button>
</template>
</el-dialog>
</template>
<script lang="ts">
import request from '@/services'
import { DATABASE_SELECT_OK, DATABASE_UPDATE_OK } from '@/constants/Common.constants'
import { ElMessage } from 'element-plus'
export default {
name: 'StaffManagement',
data() {
return {
dialogVisible: false,
tableLoading: true,
dialogLoading: true,
staffTable: [],
editUserId: '',
rules: {
inputFirstName: [
{
required: true,
message: '名称为必填项'
}
],
inputLastName: [
{
required: true,
message: '姓氏为必填项'
}
]
},
userForm: {
inputUsername: '',
inputFirstName: '',
inputLastName: '',
selectedGender: 0,
inputBirth: '',
inputEmail: '',
inputTel: '',
inputAddress: ''
}
}
},
methods: {
loadStaffTable() {
this.tableLoading = true
request.get('/staff').then((res) => {
const response = res.data
if (response.code === DATABASE_SELECT_OK) {
this.staffTable = response.data
this.tableLoading = false
} else {
ElMessage.error({
dangerouslyUseHTMLString: true,
message: '<strong>查询出错</strong>: ' + response.msg
})
}
})
},
genderFormatter(row) {
if (row.staff === null) {
return ''
}
switch (row.staff.gender) {
case 0:
return '未知'
case 1:
return '男'
case 2:
return '女'
default:
return ''
}
},
handleEdit(row) {
this.editUserId = row.id
this.userForm.inputUsername = row.username
this.userForm.inputFirstName = row.staff?.firstName
this.userForm.inputLastName = row.staff?.lastName
this.userForm.selectedGender = row.staff?.gender || 0
this.userForm.inputBirth = row.staff?.birth
this.userForm.inputEmail = row.staff?.email
this.userForm.inputTel = row.staff?.tel
this.userForm.inputAddress = row.staff?.address
this.dialogLoading = false
this.dialogVisible = true
},
async handleSubmit() {
await this.$refs.formRef.validate((valid) => {
if (valid) {
this.dialogLoading = true
const userObject = {
id: this.editUserId,
staff: {
firstName: this.userForm.inputFirstName,
lastName: this.userForm.inputLastName,
gender: this.userForm.selectedGender,
birth: this.userForm.inputBirth,
email: this.userForm.inputEmail,
tel: this.userForm.inputTel,
address: this.userForm.inputAddress
}
}
request.put('/staff', userObject).then((res) => {
const response = res.data
if (response.code === DATABASE_UPDATE_OK) {
ElMessage.success({
dangerouslyUseHTMLString: true,
message: '<strong>修改成功</strong>'
})
this.dialogVisible = false
this.loadStaffTable()
} else {
ElMessage.error({
dangerouslyUseHTMLString: true,
message: '<strong>修改失败</strong>: ' + response.msg
})
this.dialogLoading = false
}
})
}
})
},
handleCancel() {
this.dialogVisible = false
}
},
mounted() {
this.loadStaffTable()
}
}
</script>
<style scoped></style>

View File

@@ -7,6 +7,7 @@ import affairRouter from '@/router/affair'
import noticeRouter from '@/router/notice' import noticeRouter from '@/router/notice'
import powerRouter from '@/router/power' import powerRouter from '@/router/power'
import _ from 'lodash' import _ from 'lodash'
import infoRouter from '@/router/info'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@@ -36,6 +37,7 @@ const router = createRouter({
noticeRouter, noticeRouter,
attendanceRouter, attendanceRouter,
affairRouter, affairRouter,
infoRouter,
powerRouter powerRouter
] ]
}, },

28
ui/src/router/info.ts Normal file
View File

@@ -0,0 +1,28 @@
const infoRouter = {
path: '/info',
name: 'infoManagement',
redirect: '/info/staff',
children: [
{
path: 'staff',
name: 'staffManagement',
component: async () => await import('@/pages/info/StaffManagement.vue'),
meta: {
title: '员工信息管理',
requiresMenu: true,
requiresScrollbar: false,
requiresPadding: true
}
}
],
meta: {
title: '信息管理',
icon: shallowRef(IconPinnacleInfo),
requiresMenu: true,
requiresScrollbar: false,
requiresPadding: true,
requiresAuth: true
}
}
export default infoRouter

View File

@@ -4,6 +4,7 @@ import router from '@/router'
import { import {
ACCESS_DENIED, ACCESS_DENIED,
DATABASE_DATA_TO_LONG, DATABASE_DATA_TO_LONG,
DATABASE_DATA_VALIDATION_FAILED,
TOKEN_HAS_EXPIRED, TOKEN_HAS_EXPIRED,
TOKEN_IS_ILLEGAL, TOKEN_IS_ILLEGAL,
UNAUTHORIZED UNAUTHORIZED
@@ -56,6 +57,12 @@ service.interceptors.response.use(
message: '<strong>数据过长</strong>' message: '<strong>数据过长</strong>'
}) })
return await Promise.reject(response?.data) return await Promise.reject(response?.data)
case DATABASE_DATA_VALIDATION_FAILED:
ElMessage.error({
dangerouslyUseHTMLString: true,
message: '<strong>数据验证失败</strong>'
})
return await Promise.reject(response?.data)
} }
return response return response
}, },