package com.sinata.appointment.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.sinata.appointment.constants.BizErrorCode; import com.sinata.appointment.constants.LocalUnitApproverType; import com.sinata.appointment.constants.MiniUserType; import com.sinata.appointment.dto.LocalUnitCreateRequest; import com.sinata.appointment.dto.LocalUnitDetailVo; import com.sinata.appointment.dto.LocalUnitListVo; import com.sinata.appointment.dto.LocalUnitMemberItemVo; import com.sinata.appointment.dto.LocalUnitUpdateRequest; import com.sinata.appointment.entity.MpPassLocalUnit; import com.sinata.appointment.entity.MpPassLocalUnitMember; import com.sinata.appointment.entity.MpStaffGroup; import com.sinata.appointment.entity.MpUser; import com.sinata.appointment.entity.SysDept; import com.sinata.appointment.exception.BizException; import com.sinata.appointment.mapper.MpPassLocalUnitMapper; import com.sinata.appointment.mapper.MpPassLocalUnitMemberMapper; import com.sinata.appointment.mapper.MpStaffGroupMapper; import com.sinata.appointment.mapper.MpUserMapper; import com.sinata.appointment.mapper.SysDeptMapper; import com.sinata.appointment.util.UuidAssert; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; @Service public class LocalUnitManageApplicationService { private final MpPassLocalUnitMapper mpPassLocalUnitMapper; private final MpPassLocalUnitMemberMapper mpPassLocalUnitMemberMapper; private final ObjectMapper objectMapper; private final SysDeptMapper sysDeptMapper; private final MpStaffGroupMapper mpStaffGroupMapper; private final MpUserMapper mpUserMapper; public LocalUnitManageApplicationService( MpPassLocalUnitMapper mpPassLocalUnitMapper, MpPassLocalUnitMemberMapper mpPassLocalUnitMemberMapper, ObjectMapper objectMapper, SysDeptMapper sysDeptMapper, MpStaffGroupMapper mpStaffGroupMapper, MpUserMapper mpUserMapper) { this.mpPassLocalUnitMapper = mpPassLocalUnitMapper; this.mpPassLocalUnitMemberMapper = mpPassLocalUnitMemberMapper; this.objectMapper = objectMapper; this.sysDeptMapper = sysDeptMapper; this.mpStaffGroupMapper = mpStaffGroupMapper; this.mpUserMapper = mpUserMapper; } public IPage page(long page, long size, String unitName) { LambdaQueryWrapper q = new LambdaQueryWrapper() .eq(MpPassLocalUnit::getDelFlag, 0) .like(StringUtils.hasText(unitName), MpPassLocalUnit::getUnitName, trim(unitName)) .orderByDesc(MpPassLocalUnit::getCreateTime); Page pg = mpPassLocalUnitMapper.selectPage(new Page<>(page, size), q); return pg.convert(this::toListVo); } public LocalUnitDetailVo detail(Long id, String uuid) { MpPassLocalUnit e = loadActive(id, uuid); LocalUnitDetailVo vo = new LocalUnitDetailVo(); vo.setId(e.getId()); vo.setUuid(e.getUuid()); vo.setUnitName(e.getUnitName()); vo.setRemark(e.getRemark()); vo.setApproverType(e.getApproverType()); vo.setApproverGroupId(e.getApproverGroupId()); vo.setApproverDeptIds(readDeptIds(e.getApproverDeptIds())); if (e.getApproverType() != null && e.getApproverType() == LocalUnitApproverType.STAFF) { vo.setMembers(loadMemberItems(e.getId())); } return vo; } @Transactional(rollbackFor = Exception.class) public Map create(LocalUnitCreateRequest req) { validateApprover(req.getApproverType(), req.getApproverDeptIds(), req.getApproverGroupId(), req.getMemberIds()); MpPassLocalUnit e = new MpPassLocalUnit(); e.setUnitName(req.getUnitName().trim()); e.setRemark(trimToNull(req.getRemark())); e.setApproverType(req.getApproverType()); applyApproverColumns(e, req.getApproverType(), req.getApproverDeptIds(), req.getApproverGroupId()); mpPassLocalUnitMapper.insert(e); MpPassLocalUnit fresh = mpPassLocalUnitMapper.selectById(e.getId()); if (fresh == null || !StringUtils.hasText(fresh.getUuid())) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "创建后读取失败"); } if (req.getApproverType() == LocalUnitApproverType.STAFF) { saveMembers(fresh.getId(), req.getMemberIds()); } return Map.of("id", fresh.getId(), "uuid", fresh.getUuid()); } @Transactional(rollbackFor = Exception.class) public void update(Long id, LocalUnitUpdateRequest req) { MpPassLocalUnit e = loadActive(id, req.getUuid()); validateApprover(req.getApproverType(), req.getApproverDeptIds(), req.getApproverGroupId(), req.getMemberIds()); mpPassLocalUnitMemberMapper.deletePhysicalByUnitId(e.getId()); e.setUnitName(req.getUnitName().trim()); e.setRemark(trimToNull(req.getRemark())); e.setApproverType(req.getApproverType()); applyApproverColumns(e, req.getApproverType(), req.getApproverDeptIds(), req.getApproverGroupId()); mpPassLocalUnitMapper.updateById(e); if (req.getApproverType() == LocalUnitApproverType.STAFF) { saveMembers(e.getId(), req.getMemberIds()); } } @Transactional(rollbackFor = Exception.class) public void delete(Long id, String uuid) { MpPassLocalUnit e = loadActive(id, uuid); mpPassLocalUnitMemberMapper.deletePhysicalByUnitId(e.getId()); int affected = mpPassLocalUnitMapper.deleteById(e.getId()); if (affected <= 0) { throw new BizException(BizErrorCode.RESOURCE_VERIFY_FAILED, "删除失败,请刷新后重试"); } } private void applyApproverColumns( MpPassLocalUnit e, int type, List deptIds, Long groupId) { e.setApproverDeptIds(null); e.setApproverGroupId(null); if (type == LocalUnitApproverType.DEPT) { e.setApproverDeptIds(writeDeptIds(deptIds)); } else if (type == LocalUnitApproverType.STAFF_GROUP) { e.setApproverGroupId(groupId); } } private void validateApprover(int type, List deptIds, Long groupId, List memberIds) { if (type == LocalUnitApproverType.DEPT) { if (deptIds == null || deptIds.isEmpty()) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "请选择审批部门"); } for (Long id : new LinkedHashSet<>(deptIds)) { if (id == null || id <= 0) { continue; } validateDeptExists(id); } } else if (type == LocalUnitApproverType.STAFF_GROUP) { if (groupId == null || groupId <= 0) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "请选择人员分组"); } MpStaffGroup g = mpStaffGroupMapper.selectById(groupId); if (g == null || (g.getDelFlag() != null && g.getDelFlag() == 1)) { throw new BizException(BizErrorCode.RESOURCE_VERIFY_FAILED, "人员分组不存在"); } } else if (type == LocalUnitApproverType.STAFF) { if (memberIds == null || memberIds.isEmpty()) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "请选择审批人员"); } validateEmployees(new ArrayList<>(new LinkedHashSet<>(memberIds))); } else { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "审批人员类型无效"); } } private void validateDeptExists(Long deptId) { SysDept d = sysDeptMapper.selectById(deptId); if (d == null || (d.getDelFlag() != null && d.getDelFlag() == 1)) { throw new BizException(BizErrorCode.DEPT_PARENT_INVALID, "部门不存在: " + deptId); } } private void validateEmployees(List mpUserIds) { for (Long id : mpUserIds) { if (id == null || id <= 0) { continue; } MpUser u = mpUserMapper.selectById(id); if (u == null || (u.getDelFlag() != null && u.getDelFlag() == 1)) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "人员不存在: " + id); } if (!MiniUserType.EMPLOYEE.equals(u.getUserType())) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "仅可选择员工: " + id); } if (u.getStatus() != null && u.getStatus() == 0) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "员工已禁用: " + id); } } } private void saveMembers(Long unitId, List memberIds) { if (memberIds == null) { return; } for (Long mid : new LinkedHashSet<>(memberIds)) { if (mid == null || mid <= 0) { continue; } MpPassLocalUnitMember m = new MpPassLocalUnitMember(); m.setUnitId(unitId); m.setMpUserId(mid); mpPassLocalUnitMemberMapper.insert(m); } } private List loadMemberItems(Long unitId) { List rels = mpPassLocalUnitMemberMapper.selectList( new LambdaQueryWrapper() .eq(MpPassLocalUnitMember::getUnitId, unitId) .eq(MpPassLocalUnitMember::getDelFlag, 0)); if (rels.isEmpty()) { return List.of(); } Set userIds = new HashSet<>(); for (MpPassLocalUnitMember r : rels) { userIds.add(r.getMpUserId()); } Map userMap = new HashMap<>(); if (!userIds.isEmpty()) { List users = mpUserMapper.selectList( new LambdaQueryWrapper() .eq(MpUser::getDelFlag, 0) .in(MpUser::getId, userIds)); for (MpUser u : users) { userMap.put(u.getId(), u); } } Set deptIds = new HashSet<>(); for (MpUser u : userMap.values()) { if (u.getDeptId() != null) { deptIds.add(u.getDeptId()); } } Map deptNames = new HashMap<>(); if (!deptIds.isEmpty()) { List depts = sysDeptMapper.selectList( new LambdaQueryWrapper() .eq(SysDept::getDelFlag, 0) .in(SysDept::getId, deptIds)); for (SysDept d : depts) { deptNames.put(d.getId(), d.getDeptName()); } } List out = new ArrayList<>(); for (MpPassLocalUnitMember r : rels) { MpUser u = userMap.get(r.getMpUserId()); if (u == null) { continue; } LocalUnitMemberItemVo it = new LocalUnitMemberItemVo(); it.setMpUserId(u.getId()); it.setUuid(u.getUuid()); it.setRealName(u.getNickName()); it.setDeptName( u.getDeptId() == null ? "—" : deptNames.getOrDefault(u.getDeptId(), "—")); out.add(it); } return out; } private LocalUnitListVo toListVo(MpPassLocalUnit e) { LocalUnitListVo vo = new LocalUnitListVo(); vo.setId(e.getId()); vo.setUuid(e.getUuid()); vo.setUnitName(e.getUnitName()); vo.setRemark(e.getRemark()); vo.setApproverSummary(buildApproverSummary(e)); vo.setCreateTime(e.getCreateTime()); return vo; } private String buildApproverSummary(MpPassLocalUnit e) { Integer t = e.getApproverType(); if (t == null) { return "—"; } if (t == LocalUnitApproverType.DEPT) { return buildDeptApproverDisplay(readDeptIds(e.getApproverDeptIds())); } if (t == LocalUnitApproverType.STAFF_GROUP && e.getApproverGroupId() != null) { MpStaffGroup g = mpStaffGroupMapper.selectById(e.getApproverGroupId()); String n = (g != null && StringUtils.hasText(g.getGroupName())) ? g.getGroupName() : "—"; return "人员分组:" + n; } if (t == LocalUnitApproverType.STAFF) { return buildStaffApproverDisplay(e.getId()); } return "—"; } /** 列表审批摘要:部门类型展示部门名称(按配置顺序,顿号分隔) */ private String buildDeptApproverDisplay(List rawIds) { List ids = new ArrayList<>(new LinkedHashSet<>(rawIds)); ids.removeIf(x -> x == null || x <= 0); if (ids.isEmpty()) { return "—"; } List depts = sysDeptMapper.selectList( new LambdaQueryWrapper() .eq(SysDept::getDelFlag, 0) .in(SysDept::getId, ids)); Map nameById = new HashMap<>(); for (SysDept d : depts) { if (d.getId() != null && StringUtils.hasText(d.getDeptName())) { nameById.put(d.getId(), d.getDeptName()); } } List parts = new ArrayList<>(); for (Long id : ids) { parts.add(nameById.getOrDefault(id, "未知部门")); } return String.join("、", parts); } /** 列表审批摘要:人员类型展示员工姓名(关联表顺序,顿号分隔) */ private String buildStaffApproverDisplay(Long unitId) { List rels = mpPassLocalUnitMemberMapper.selectList( new LambdaQueryWrapper() .eq(MpPassLocalUnitMember::getUnitId, unitId) .eq(MpPassLocalUnitMember::getDelFlag, 0)); if (rels.isEmpty()) { return "—"; } LinkedHashSet userIds = new LinkedHashSet<>(); for (MpPassLocalUnitMember r : rels) { if (r.getMpUserId() != null && r.getMpUserId() > 0) { userIds.add(r.getMpUserId()); } } if (userIds.isEmpty()) { return "—"; } List users = mpUserMapper.selectList( new LambdaQueryWrapper() .eq(MpUser::getDelFlag, 0) .in(MpUser::getId, userIds)); Map nameById = new HashMap<>(); for (MpUser u : users) { String label; if (StringUtils.hasText(u.getNickName())) { label = u.getNickName().trim(); } else if (StringUtils.hasText(u.getStaffNo())) { label = u.getStaffNo().trim(); } else { label = "—"; } nameById.put(u.getId(), label); } List parts = new ArrayList<>(); for (Long uid : userIds) { parts.add(nameById.getOrDefault(uid, "未知人员")); } return String.join("、", parts); } private List readDeptIds(String json) { if (!StringUtils.hasText(json)) { return List.of(); } try { List list = objectMapper.readValue(json, new TypeReference>() {}); return list == null ? List.of() : list; } catch (Exception ignored) { return List.of(); } } private String writeDeptIds(List ids) { if (ids == null || ids.isEmpty()) { return null; } List distinct = new ArrayList<>(new LinkedHashSet<>(ids)); distinct.removeIf(x -> x == null || x <= 0); if (distinct.isEmpty()) { return null; } try { return objectMapper.writeValueAsString(distinct); } catch (JsonProcessingException ex) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "部门数据序列化失败"); } } private MpPassLocalUnit loadActive(Long id, String uuid) { MpPassLocalUnit e = mpPassLocalUnitMapper.selectById(id); if (e == null || (e.getDelFlag() != null && e.getDelFlag() == 1)) { throw new BizException(BizErrorCode.RESOURCE_VERIFY_FAILED, "资源不存在或无权访问"); } UuidAssert.match(e, uuid); return e; } private static String trim(String s) { return s == null ? null : s.trim(); } private static String trimToNull(String s) { if (!StringUtils.hasText(s)) { return null; } String t = s.trim(); return t.isEmpty() ? null : t; } }