增加服务端执法人员管理相关类

This commit is contained in:
yuyan02 2025-02-28 17:12:54 +08:00
parent 58658e2199
commit b847648ea5
3 changed files with 629 additions and 0 deletions

View File

@ -0,0 +1,149 @@
package com.aisino.iles.lawenforcement.controller;
import com.aisino.iles.common.model.Fail;
import com.aisino.iles.common.model.Ok;
import com.aisino.iles.common.model.PageResult;
import com.aisino.iles.common.model.Result;
import com.aisino.iles.lawenforcement.model.Agency;
import com.aisino.iles.lawenforcement.model.query.AgencyQuery;
import com.aisino.iles.lawenforcement.service.AgencyService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 综合执法/执法机构管理
*/
@RestController
@RequestMapping("/api/lawenforcement/agencies")
public class AgencyController {
private final AgencyService agencyService;
public AgencyController(AgencyService agencyService) {
this.agencyService = agencyService;
}
/**
* 创建执法机构
*
* @param agency 执法机构信息
* @return 创建结果
*/
@PostMapping
public Result<Agency> createAgency(@RequestBody Agency agency) {
return Ok.of(agencyService.saveAgency(agency));
}
/**
* 根据ID查询执法机构
*
* @param agencyId 执法机构ID
* @return 执法机构信息
*/
@GetMapping("/{agencyId}")
public Result<Agency> getAgencyById(@PathVariable String agencyId) {
return agencyService.findAgencyById(agencyId)
.map(a->((Result<Agency>) Ok.of(a)))
.orElse(Fail.of("执法机构不存在"));
}
/**
* 根据执法机构代码查询执法机构
*
* @param agencyCode 执法机构代码
* @return 执法机构信息
*/
@GetMapping("/code/{agencyCode}")
public Result<Agency> getAgencyByCode(@PathVariable String agencyCode) {
return agencyService.findAgencyByAgencyCode(agencyCode)
.map(a->((Result<Agency>) Ok.of(a)))
.orElse(Fail.of("执法机构不存在"));
}
/**
* 分页查询执法机构
*
* @return 分页执法机构信息
*/
@GetMapping
public PageResult<Agency> getAgenciesPage(AgencyQuery agencyQuery) {
return PageResult.of(agencyService.findAgenciesPage(agencyQuery));
}
/**
* 更新执法机构
*
* @param agencyId 执法机构ID
* @param agency 执法机构信息
* @return 更新结果
*/
@PutMapping("/{agencyId}")
public Result<Agency> updateAgency(@PathVariable String agencyId, @RequestBody Agency agency) {
if (!agencyService.existsAgencyById(agencyId)) {
return Fail.of("执法机构不存在");
}
agency.setAgencyId(agencyId);
return Ok.of(agencyService.saveAgency(agency));
}
/**
* 根据ID删除执法机构
*
* @param agencyId 执法机构ID
* @return 删除结果
*/
@DeleteMapping("/{agencyId}")
public Result<Void> deleteAgencyById(@PathVariable String agencyId) {
if (!agencyService.existsAgencyById(agencyId)) {
return Fail.of("执法机构不存在");
}
agencyService.deleteAgencyById(agencyId);
return Ok.of();
}
/**
* 批量删除执法机构
*
* @param agencyIds 执法机构ID列表
* @return 删除结果
*/
@DeleteMapping
public Result<Void> deleteAgenciesByIds(@RequestBody List<String> agencyIds) {
agencyService.deleteAgenciesByIds(agencyIds);
return Ok.of();
}
/**
* 查询执法机构列表
*
* @return 执法机构列表
*/
@GetMapping("/list")
public Result<List<Agency>> getAgencies(AgencyQuery query) {
return Ok.of(agencyService.findAgencies(query));
}
/**
* 查询执法机构列表树形
* @param query 查询条件
* @return 执法机构列表树形
*/
@GetMapping("/tree")
public Result<List<Agency>> getAgenciesTree(AgencyQuery query) {
return Ok.of(agencyService.findAgenciesTree(query));
}
/**
* 获取指定父机构下的下一个排序号
* @param parentId 上级机构ID根节点传 0 或不传
*/
@GetMapping("/nextOrderNum")
public Result<Integer> getNextOrderNum(@RequestParam(required = false) String parentId) {
return Ok.of(agencyService.getNextOrderNum(parentId));
}
}

View File

@ -0,0 +1,55 @@
package com.aisino.iles.lawenforcement.repository;
import com.aisino.iles.core.repository.BaseRepo;
import com.aisino.iles.lawenforcement.model.Agency;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 执法机构仓库接口
*/
@Repository
public interface AgencyRepository extends BaseRepo<Agency, String> {
/**
* 根据执法机构代码查询执法机构
*
* @param agencyCode 执法机构代码
* @return 执法机构信息
*/
Optional<Agency> findByAgencyCode(String agencyCode);
/**
* 根据执法机构名称查询执法机构
*
* @param agencyName 执法机构名称
* @return 执法机构信息
*/
Optional<Agency> findByAgencyName(String agencyName);
List<Agency> findByAgencyId(String parentId);
List<Agency> findByParentIsNull();
List<Agency> findByParentAgencyId(String agencyId);
@Query("select a from Agency a where a.agencyCode like :agencyCode and a.agencyLevel = :agencyLevel")
List<Agency> findLikeAgencyCode(String agencyCode, Integer agencyLevel);
/**
* 查询指定父机构下的最大排序号
*/
@Query("select coalesce(max(a.orderNum),0) from Agency a where a.parent.agencyId = :parentId")
Integer findMaxOrderNumByParentId(@Param("parentId") String parentId);
/**
* 查询根节点父为空下的最大排序号
*/
@Query("select coalesce(max(a.orderNum),0) from Agency a where a.parent is null")
Integer findMaxOrderNumOfRoot();
}

View File

@ -0,0 +1,425 @@
package com.aisino.iles.lawenforcement.service;
import cn.hutool.core.util.StrUtil;
import com.aisino.iles.common.util.BeanUtils;
import com.aisino.iles.common.util.PageableHelper;
import com.aisino.iles.common.util.StringUtils;
import com.aisino.iles.lawenforcement.model.Agency;
import com.aisino.iles.lawenforcement.model.Agency_;
import com.aisino.iles.lawenforcement.model.query.AgencyQuery;
import com.aisino.iles.lawenforcement.repository.AgencyRepository;
import com.aisino.iles.lawenforcement.repository.CaseRepository;
import com.aisino.iles.lawenforcement.repository.OfficerRepository;
import jakarta.persistence.criteria.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* 执法机构服务类
*/
@Service
@Slf4j
@Transactional(readOnly = true)
public class AgencyService {
private final AgencyRepository agencyRepository;
private final OfficerRepository officerRepository;
private final CaseRepository caseRepository;
public AgencyService(AgencyRepository agencyRepository, OfficerRepository officerRepository, CaseRepository caseRepository) {
this.agencyRepository = agencyRepository;
this.officerRepository = officerRepository;
this.caseRepository = caseRepository;
}
/**
* 保存执法机构信息
*
* @param agency 执法机构信息
* @return 保存后的执法机构信息
*/
@Transactional
public Agency saveAgency(Agency agency) {
// 基础校验与去空格
String name = StrUtil.trimToNull(agency.getAgencyName());
String code = StrUtil.trimToNull(agency.getAgencyCode());
if (StrUtil.isBlank(name)) {
throw new IllegalArgumentException("机构名称不能为空");
}
if (StrUtil.isBlank(code)) {
throw new IllegalArgumentException("机构代码不能为空");
}
agency.setAgencyName(name);
agency.setAgencyCode(code);
// 生成机构简码去偶数0若前端未传
if (StrUtil.isBlank(agency.getAgencySimpleCode())) {
agency.setAgencySimpleCode(StringUtils.trimEven0(code));
}
boolean isCreate = StrUtil.isBlank(agency.getAgencyId());
// 唯一性校验名称 & 代码
agencyRepository.findByAgencyName(agency.getAgencyName())
.filter(existing -> isCreate || !existing.getAgencyId().equals(agency.getAgencyId()))
.ifPresent(e -> {
throw new IllegalStateException("机构名称已存在");
});
agencyRepository.findByAgencyCode(agency.getAgencyCode())
.filter(existing -> isCreate || !existing.getAgencyId().equals(agency.getAgencyId()))
.ifPresent(e -> {
throw new IllegalStateException("机构代码已存在");
});
// 解析上级机构支持仅携带 agencyId parent 对象或使用 transient parentAgencyId
Agency parent = null;
String parentAgencyId = agency.getParent() != null ? agency.getParent().getAgencyId() : null;
if (StrUtil.isNotBlank(parentAgencyId)) {
parent = agencyRepository.findById(parentAgencyId)
.orElseThrow(() -> new IllegalArgumentException("上级机构不存在"));
agency.setParent(parent);
} else {
agency.setParent(null);
}
if (isCreate) {
// 新增流程
agency.setLeaf(true);
agency.setAgencyLevel(parent == null ? 1 : (Optional.ofNullable(parent.getAgencyLevel()).orElse(0) + 1));
Agency saved = agencyRepository.save(agency);
// 计算并设置机构路径沿父链使用机构简码拼接
saved.setAgencyPath(computeAgencyPath(saved));
// 更新父节点 leaf 状态
if (parent != null && Boolean.TRUE.equals(parent.getLeaf())) {
parent.setLeaf(false);
agencyRepository.save(parent);
}
return agencyRepository.save(saved);
} else {
// 修改流程
Agency existing = agencyRepository.findById(agency.getAgencyId())
.orElseThrow(() -> new IllegalArgumentException("执法机构不存在"));
Agency oldParent = existing.getParent();
Agency newParent = parent; // 可能为 null
boolean parentChanged = (oldParent == null && newParent != null)
|| (oldParent != null && (newParent == null || !oldParent.getAgencyId().equals(newParent.getAgencyId())));
// 防止循环新父节点不能在当前节点的子树中
if (parentChanged && newParent != null && isDescendant(newParent, existing.getAgencyId())) {
throw new IllegalStateException("不允许将机构设置为自己的子机构");
}
// 应用基础字段更新
existing.setAgencyName(agency.getAgencyName());
existing.setAgencyCode(agency.getAgencyCode());
existing.setAgencySimpleCode(agency.getAgencySimpleCode());
existing.setOrderNum(agency.getOrderNum());
existing.setParent(newParent);
// 重新计算当前节点 level path
existing.setAgencyLevel(newParent == null ? 1 : (Optional.ofNullable(newParent.getAgencyLevel()).orElse(0) + 1));
existing.setAgencyPath(computeAgencyPath(existing));
Agency saved = agencyRepository.save(existing);
// 递归更新所有子节点 level path
updateChildrenLevelsAndPaths(saved);
// 维护父节点 leaf 状态
if (oldParent != null) {
boolean hasOtherChildren = !agencyRepository.findByParentAgencyId(oldParent.getAgencyId()).isEmpty();
if (!hasOtherChildren) {
oldParent.setLeaf(true);
agencyRepository.save(oldParent);
}
}
if (newParent != null) {
newParent.setLeaf(false);
agencyRepository.save(newParent);
}
// 维护当前节点 leaf 状态
boolean hasChildren = !agencyRepository.findByParentAgencyId(saved.getAgencyId()).isEmpty();
saved.setLeaf(!hasChildren);
return agencyRepository.save(saved);
}
}
/**
* 批量保存执法机构信息
*
* @param agencies 执法机构信息列表
* @return 保存后的执法机构信息列表
*/
@Transactional
public List<Agency> saveAgencies(List<Agency> agencies) {
return agencyRepository.saveAll(agencies);
}
/**
* 根据ID查询执法机构信息
*
* @param agencyId 执法机构ID
* @return 执法机构信息
*/
public Optional<Agency> findAgencyById(String agencyId) {
return agencyRepository.findById(agencyId).map(a -> {
Agency agency = new Agency();
BeanUtils.copyProperties(a, agency, Agency_.PARENT, Agency_.CHILDREN);
Agency parent = new Agency();
BeanUtils.copyProperties(a.getParent(), parent, Agency_.PARENT, Agency_.CHILDREN);
agency.setParent(parent);
return agency;
});
}
/**
* 根据执法机构代码查询执法机构信息
*
* @param agencyCode 执法机构代码
* @return 执法机构信息
*/
public Optional<Agency> findAgencyByAgencyCode(String agencyCode) {
return agencyRepository.findByAgencyCode(agencyCode);
}
/**
* 分页查询执法机构信息
*
* @param query 分页请求
* @return 分页执法机构信息
*/
public Page<Agency> findAgenciesPage(AgencyQuery query) {
return agencyRepository.findAll(buildSpec(query), PageableHelper.buildPageRequest(query.page(), query.pageSize(), query.sort(), query.dir()));
}
private Specification<Agency> buildSpec(AgencyQuery agencyQuery) {
return (root, query, builder) -> {
List<Predicate> predicates = Stream.of(
Optional.ofNullable(agencyQuery.getAgencyId())
.map(StrUtil::trimToNull)
.map(a -> builder.equal(root.get(Agency_.agencyId), a)),
Optional.ofNullable(agencyQuery.getAgencyCode())
.map(StrUtil::trimToNull)
.map(a -> builder.equal(root.get(Agency_.agencyCode), a)),
Optional.ofNullable(agencyQuery.getAgencyName())
.map(StrUtil::trimToNull)
.map(a -> builder.like(root.get(Agency_.agencyName), "%" + a + "%")),
Optional.ofNullable(agencyQuery.getParentId())
.map(StrUtil::trimToNull)
.map(p -> builder.equal(root.get(Agency_.parent).get(Agency_.agencyId), p))
).filter(Optional::isPresent)
.map(Optional::get)
.toList();
return builder.and(predicates.toArray(new Predicate[0]));
};
}
/**
* 条件查询执法机构信息
*
* @param agencyQuery@return 执法机构信息列表
*/
public List<Agency> findAgencies(AgencyQuery agencyQuery) {
return agencyRepository.findAll(buildSpec(agencyQuery));
}
/**
* 根据ID删除执法机构信息
*
* @param agencyId 执法机构ID
*/
@Transactional
public void deleteAgencyById(String agencyId) {
Agency agency = agencyRepository.findById(agencyId)
.orElseThrow(() -> new IllegalArgumentException("执法机构不存在"));
// 1. 有无子机构
if (!agencyRepository.findByParentAgencyId(agencyId).isEmpty()) {
throw new IllegalStateException("存在子机构,不允许删除");
}
// 2. 是否有关联执法人员
if (!officerRepository.findByAgencyId(agencyId).isEmpty()) {
throw new IllegalStateException("存在关联执法人员,不允许删除");
}
// 3. 是否有关联案件
if (caseRepository.existsByAgencyId(agencyId)) {
throw new IllegalStateException("存在关联案件,不允许删除");
}
// 删除
agencyRepository.deleteById(agencyId);
// 更新上级 leaf 状态
Agency parent = agency.getParent();
if (parent != null) {
boolean hasOtherChildren = !agencyRepository.findByParentAgencyId(parent.getAgencyId()).isEmpty();
if (!hasOtherChildren) {
parent.setLeaf(true);
agencyRepository.save(parent);
}
}
}
/**
* 批量删除执法机构信息
*
* @param agencyIds 执法机构ID列表
*/
@Transactional
public void deleteAgenciesByIds(List<String> agencyIds) {
for (String id : agencyIds) {
deleteAgencyById(id);
}
}
/**
* 检查执法机构是否存在
*
* @param agencyId 执法机构ID
* @return 是否存在
*/
public boolean existsAgencyById(String agencyId) {
return agencyRepository.existsById(agencyId);
}
/**
* 获取执法机构总数
*
* @return 执法机构总数
*/
public long countAgencies() {
return agencyRepository.count();
}
/**
* 获取指定父机构下的下一个排序号
* parentId 为空或等于 "0" 表示根节点
*/
public Integer getNextOrderNum(String parentId) {
String pid = StrUtil.trimToNull(parentId);
if (pid == null || "0".equals(pid)) {
Integer max = Optional.ofNullable(agencyRepository.findMaxOrderNumOfRoot()).orElse(0);
return max + 1;
}
// 校验父节点
if (!agencyRepository.existsById(pid)) {
throw new IllegalArgumentException("上级机构不存在");
}
Integer max = Optional.ofNullable(agencyRepository.findMaxOrderNumByParentId(pid)).orElse(0);
return max + 1;
}
public List<Agency> findAgenciesTree(AgencyQuery query) {
List<Agency> result = new ArrayList<>();
List<Agency> rootAgencies;
if (StrUtil.isNotBlank(query.getParentId())) {
rootAgencies = agencyRepository.findByParentAgencyId(query.getParentId());
} else if (StrUtil.isNotBlank(query.getAgencyId())) {
rootAgencies = new ArrayList<>();
agencyRepository.findById(query.getAgencyId()).ifPresent(rootAgencies::add);
} else {
// 顶级机构parent 为空
rootAgencies = agencyRepository.findByParentIsNull();
}
for (Agency root : rootAgencies) {
populateChildrenRecursively(root, 0, 50);
result.add(root);
}
return result;
}
/**
* 递归填充子机构
*/
private void populateChildrenRecursively(Agency parent, int depth, int maxDepth) {
if (parent == null || depth > maxDepth) return;
List<Agency> children = agencyRepository.findByParentAgencyId(parent.getAgencyId());
parent.setChildren(children);
if (children == null || children.isEmpty()) {
parent.setLeaf(true);
return;
}
parent.setLeaf(false);
for (Agency child : children) {
populateChildrenRecursively(child, depth + 1, maxDepth);
}
}
/**
* 递归更新所有子节点的 level path
*/
private void updateChildrenLevelsAndPaths(Agency parent) {
List<Agency> children = agencyRepository.findByParentAgencyId(parent.getAgencyId());
for (Agency child : children) {
child.setAgencyLevel(Optional.ofNullable(parent.getAgencyLevel()).orElse(0) + 1);
child.setAgencyPath(computeAgencyPath(child));
agencyRepository.save(child);
updateChildrenLevelsAndPaths(child);
}
}
/**
* 计算机构路径沿父链使用机构简码拼接避免依赖数据库中已保存的路径
*/
private String computeAgencyPath(Agency current) {
if (current == null) return null;
List<String> parts = new ArrayList<>();
Agency cursor = current;
int depth = 0;
final int maxDepth = 100;
while (cursor != null && depth < maxDepth) {
String part = StrUtil.trimToNull(cursor.getAgencySimpleCode());
if (part == null) {
part = StringUtils.trimEven0(StrUtil.emptyIfNull(cursor.getAgencyCode()));
cursor.setAgencySimpleCode(part);
}
parts.add(part);
cursor = cursor.getParent();
depth++;
}
Collections.reverse(parts);
return String.join(".", parts);
}
/**
* 判断 candidate 是否在 ancestorId 对应机构的子树中用于防止循环引用
*/
private boolean isDescendant(Agency candidate, String ancestorId) {
Agency cursor = candidate;
int depth = 0;
final int maxDepth = 100;
while (cursor != null && depth < maxDepth) {
if (ancestorId.equals(cursor.getAgencyId())) {
return true;
}
cursor = cursor.getParent();
depth++;
}
return false;
}
}