修复部门设置负责人失败的问题

- 前端:允许选择所有用户作为部门负责人,不限于该部门成员
- 前端:修复leaderId和parentId的类型兼容性问题
- 后端:添加leaderId有效性验证,防止设置不存在的用户
This commit is contained in:
Your Name 2026-01-02 18:00:20 +08:00
parent ec6a0d1838
commit 6df38c4f89
2 changed files with 47 additions and 49 deletions

View File

@ -28,7 +28,6 @@ import java.util.stream.Collectors;
/**
* 部门管理Service
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DepartmentService {
@ -106,6 +105,14 @@ public class DepartmentService {
@Transactional(rollbackFor = Exception.class)
public DepartmentOverviewVO createDepartment(DepartmentCreateRequest request) {
validateDeptType(request.getDeptType());
// 验证负责人ID是否有效
if (request.getLeaderId() != null && request.getLeaderId() > 0) {
User leader = userMapper.selectById(request.getLeaderId());
if (leader == null) {
throw new BusinessException("指定的负责人不存在");
}
}
Department department = new Department();
department.setDeptName(request.getDeptName());
@ -179,6 +186,13 @@ public class DepartmentService {
// 但由于无法判断字段是否存在,我们采用:如果leaderId不为null,则更新
if (request.getLeaderId() != null) {
Long leaderId = request.getLeaderId();
// 验证负责人ID是否有效(0表示清除,大于0需要验证)
if (leaderId > 0) {
User leader = userMapper.selectById(leaderId);
if (leader == null) {
throw new BusinessException("指定的负责人不存在");
}
}
existDept.setLeaderId(leaderId == 0 ? null : leaderId);
} else if (request.getDeptName() != null) {
// 如果其他字段被更新(说明这是完整的更新请求),且leaderId为null,则清除负责人
@ -211,42 +225,20 @@ public class DepartmentService {
existDept.setOverLimitAction(request.getOverLimitAction());
}
try {
departmentMapper.updateById(existDept);
log.info("更新部门成功: id={}, deptName={}", id, existDept.getDeptName());
} catch (Exception e) {
log.error("更新部门失败: id={}, error={}", id, e.getMessage(), e);
throw new BusinessException("更新部门失败: " + e.getMessage());
}
departmentMapper.updateById(existDept);
// 同步积分池
try {
teamScorePoolService.upsertPool(existDept.getId(), existDept.getDeptName());
log.info("同步积分池成功: deptId={}, deptName={}", existDept.getId(), existDept.getDeptName());
} catch (Exception e) {
log.error("同步积分池失败: deptId={}, error={}", existDept.getId(), e.getMessage(), e);
throw new BusinessException("同步积分池失败: " + e.getMessage());
}
// 更新积分池限制配置(仅需求部门)
teamScorePoolService.upsertPool(existDept.getId(), existDept.getDeptName());
if ("demand".equals(existDept.getDeptType()) &&
(request.getLimitEnabled() != null || request.getLimitPeriod() != null ||
request.getPeriodLimit() != null || request.getAlertThreshold() != null)) {
try {
teamScorePoolService.updateLimitConfig(
existDept.getId(),
request.getLimitEnabled(),
request.getLimitPeriod(),
request.getPeriodLimit(),
request.getAlertThreshold()
);
log.info("更新积分池限制配置成功: deptId={}, limitEnabled={}, limitPeriod={}, periodLimit={}, alertThreshold={}",
existDept.getId(), request.getLimitEnabled(), request.getLimitPeriod(),
request.getPeriodLimit(), request.getAlertThreshold());
} catch (Exception e) {
log.error("更新积分池限制配置失败: deptId={}, error={}", existDept.getId(), e.getMessage(), e);
throw new BusinessException("更新积分池限制配置失败: " + e.getMessage());
}
teamScorePoolService.updateLimitConfig(
existDept.getId(),
request.getLimitEnabled(),
request.getLimitPeriod(),
request.getPeriodLimit(),
request.getAlertThreshold()
);
}
return true;

View File

@ -1118,7 +1118,7 @@
</a-form-item>
<a-form-item label="部门负责人">
<a-select
v-model:value="formData.department.leaderId"
v-model:value="departmentLeaderId"
placeholder="请选择部门负责人"
allow-clear
show-search
@ -1134,7 +1134,7 @@
</a-select>
</a-form-item>
<a-form-item label="上级部门">
<a-select v-model:value="formData.department.parentId" placeholder="请选择上级部门" allow-clear>
<a-select v-model:value="departmentParentId" placeholder="请选择上级部门" allow-clear>
<a-select-option :value="0">顶级部门</a-select-option>
<a-select-option
v-for="dept in departments.filter(d => !editingItem || d.id !== editingItem.id)"
@ -1367,7 +1367,7 @@ interface DepartmentForm {
deptCode?: string
deptType: DeptType
parentId?: number | null
leaderId?: number | null
leaderId?: number | null | undefined
description?: string
sortOrder?: number
status?: number
@ -1477,7 +1477,7 @@ const defaultDepartmentForm = (): DepartmentForm => ({
deptCode: '',
deptType: 'demand',
parentId: 0,
leaderId: null,
leaderId: undefined,
description: '',
sortOrder: 0,
status: 1,
@ -1521,18 +1521,13 @@ const unassignedUsers = computed(() => {
// 过滤出当前部门的用户(用于负责人选择)
const departmentUsers = computed(() => {
// 如果是编辑部门,只显示该部门的用户
if (editingItem.value?.id) {
const deptId = editingItem.value.id
const users = usersByDept.value[deptId] || []
return users.slice().sort((a, b) => {
const nameA = a.realName || a.username || ''
const nameB = b.realName || b.username || ''
return nameA.localeCompare(nameB)
})
}
// 如果是新建部门,显示未分配部门的用户
return unassignedUsers.value
// 部门负责人应该可以选择任何用户,不限制于该部门的成员
// 返回所有用户的排序列表
return allUsers.value.slice().sort((a, b) => {
const nameA = a.realName || a.username || ''
const nameB = b.realName || b.username || ''
return nameA.localeCompare(nameB)
})
})
const hasUserData = computed(() => {
@ -1949,7 +1944,7 @@ const showDepartmentModal = async (item?: DepartmentOverview) => {
deptCode: item.deptCode,
deptType: item.deptType,
parentId: item.parentId ?? 0,
leaderId: item.leaderId ?? null,
leaderId: item.leaderId ?? undefined,
description: item.description,
sortOrder: item.sortOrder ?? 0,
status: item.status ?? 1,
@ -2128,6 +2123,17 @@ const saveCategory = async () => {
const isDemandDept = computed(() => formData.department.deptType === 'demand')
const isReceivingDept = computed(() => formData.department.deptType === 'receiving')
// Computed属性用于处理leaderId和parentId的null到undefined的转换,以兼容a-select
const departmentLeaderId = computed({
get: () => formData.department.leaderId ?? undefined,
set: (val) => { formData.department.leaderId = val ?? undefined }
})
const departmentParentId = computed({
get: () => formData.department.parentId ?? undefined,
set: (val) => { formData.department.parentId = val ?? undefined }
})
const buildDepartmentPayload = (includeInitial: boolean): DepartmentPayload => {
const payload: DepartmentPayload = {
deptName: formData.department.deptName,