Browse Source

虚拟单位重组功能开发

dev-V1.0.1
庄锐波 6 months ago
parent
commit
dbf424e047
  1. 39
      script/sql/zfjd-refactoring-v1/011-重建虚拟单位树.sql
  2. 105
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/EfcodeVirtualUnitController.java
  3. 102
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/EfcodeVirtualUnit.java
  4. 105
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/EfcodeVirtualUnitBo.java
  5. 128
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EfcodeVirtualUnitVo.java
  6. 34
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/SrzDeptVo.java
  7. 15
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeVirtualUnitMapper.java
  8. 68
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeVirtualUnitService.java
  9. 144
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeVirtualUnitServiceImpl.java
  10. 6
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeVirtualUnitMapper.xml
  11. 5
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java
  12. 1
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml
  13. 344
      zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/service/test/InitVirtualUnitTest.java

39
script/sql/zfjd-refactoring-v1/011-重建虚拟单位树.sql

@ -1,22 +1,23 @@
create table efcode_virtual_unit
(
vir_id varchar(100) not null comment '虚拟单位ID' primary key,
vir_is_virtual varchar(20) null comment '是否是虚拟节点',
vir_tenant_id varchar(20) default '000000' null comment '租户编号',
vir_parent_id varchar(100) default '0' null comment '父部门id',
vir_zone tinyint null comment '类型',
vir_ancestors varchar(500) default '' null comment '虚拟单位祖级ID列表:包含自身',
vir_display_path varchar(1000) null comment '虚拟单位节点的单位路径:包含自身',
vir_include_type varchar(20) null comment '包含类型:查询/统计,查询,统计',
vir_domain varchar(40) null comment '域',
vir_status varchar(40) null comment '状态',
vir_remark varchar(500) null comment '备注',
sys_dept_id varchar(100) null comment '部门id',
srz_organization_id varchar(100) null comment '市认证的组织ID',
order_num int default 0 null comment '显示顺序',
create_dept varchar(100) null comment '创建部门',
create_by bigint null comment '创建者',
create_time datetime null comment '创建时间',
update_by bigint null comment '更新者',
update_time datetime null comment '更新时间'
vir_id varchar(100) not null comment '虚拟单位ID' primary key,
vir_is_virtual varchar(20) null comment '是否是虚拟节点',
vir_parent_id varchar(100) default '0' null comment '父部门id',
vir_name varchar(100) null comment '名称',
vir_zone tinyint null comment '类型',
vir_ancestors varchar(500) default '' null comment '虚拟单位祖级ID列表:包含自身',
vir_display_path varchar(1000) null comment '虚拟单位节点的单位路径:包含自身',
vir_include_type varchar(20) null comment '包含类型:查询/统计,查询,统计',
vir_domain varchar(40) null comment '域',
vir_status varchar(40) null comment '状态',
vir_show_at_business varchar(40) null comment '是否在业务中显示',
vir_remark varchar(500) null comment '备注',
sys_dept_id varchar(100) null comment '部门id',
srz_organization_id varchar(100) null comment '市认证的组织ID',
order_num int default 0 null comment '显示顺序',
create_dept varchar(100) null comment '创建部门',
create_by bigint null comment '创建者',
create_time datetime null comment '创建时间',
update_by bigint null comment '更新者',
update_time datetime null comment '更新时间'
) comment '虚拟单位树' row_format = DYNAMIC;

105
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/EfcodeVirtualUnitController.java

@ -0,0 +1,105 @@
package com.zdxt.enforcementcode.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import com.zdxt.enforcementcode.domain.vo.EfcodeVirtualUnitVo;
import com.zdxt.enforcementcode.domain.bo.EfcodeVirtualUnitBo;
import com.zdxt.enforcementcode.service.IEfcodeVirtualUnitService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 虚拟单位树
*
* @author Lion Li
* @date 2026-05-07
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/enforcementcode/virtualUnit")
public class EfcodeVirtualUnitController extends BaseController {
private final IEfcodeVirtualUnitService efcodeVirtualUnitService;
/**
* 查询虚拟单位树列表
*/
@SaCheckPermission("enforcementcode:virtualUnit:list")
@GetMapping("/list")
public TableDataInfo<EfcodeVirtualUnitVo> list(EfcodeVirtualUnitBo bo, PageQuery pageQuery) {
return efcodeVirtualUnitService.queryPageList(bo, pageQuery);
}
/**
* 导出虚拟单位树列表
*/
@SaCheckPermission("enforcementcode:virtualUnit:export")
@Log(title = "虚拟单位树", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(EfcodeVirtualUnitBo bo, HttpServletResponse response) {
List<EfcodeVirtualUnitVo> list = efcodeVirtualUnitService.queryList(bo);
ExcelUtil.exportExcel(list, "虚拟单位树", EfcodeVirtualUnitVo.class, response);
}
/**
* 获取虚拟单位树详细信息
*
* @param virId 主键
*/
@SaCheckPermission("enforcementcode:virtualUnit:query")
@GetMapping("/{virId}")
public R<EfcodeVirtualUnitVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable String virId) {
return R.ok(efcodeVirtualUnitService.queryById(virId));
}
/**
* 新增虚拟单位树
*/
@SaCheckPermission("enforcementcode:virtualUnit:add")
@Log(title = "虚拟单位树", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody EfcodeVirtualUnitBo bo) {
return toAjax(efcodeVirtualUnitService.insertByBo(bo));
}
/**
* 修改虚拟单位树
*/
@SaCheckPermission("enforcementcode:virtualUnit:edit")
@Log(title = "虚拟单位树", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody EfcodeVirtualUnitBo bo) {
return toAjax(efcodeVirtualUnitService.updateByBo(bo));
}
/**
* 删除虚拟单位树
*
* @param virIds 主键串
*/
@SaCheckPermission("enforcementcode:virtualUnit:remove")
@Log(title = "虚拟单位树", businessType = BusinessType.DELETE)
@DeleteMapping("/{virIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable String[] virIds) {
return toAjax(efcodeVirtualUnitService.deleteWithValidByIds(List.of(virIds), true));
}
}

102
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/EfcodeVirtualUnit.java

@ -0,0 +1,102 @@
package com.zdxt.enforcementcode.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 虚拟单位树对象 efcode_virtual_unit
*
* @author Lion Li
* @date 2026-05-07
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("efcode_virtual_unit")
public class EfcodeVirtualUnit extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 虚拟单位ID
*/
@TableId(value = "vir_id")
private String virId;
/**
* 是否是虚拟节点
*/
private String virIsVirtual;
/**
* 父部门id
*/
private String virParentId;
/**
* 名称
*/
private String virName;
/**
* 类型
*/
private Long virZone;
/**
* 虚拟单位祖级ID列表:包含自身
*/
private String virAncestors;
/**
* 虚拟单位节点的单位路径:包含自身
*/
private String virDisplayPath;
/**
* 包含类型:查询/统计,查询,统计
*/
private String virIncludeType;
/**
* 域
*/
private String virDomain;
/**
* 状态
*/
private String virStatus;
/**
* 是否在业务中显示
*/
private String virShowAtBusiness;
/**
* 备注
*/
private String virRemark;
/**
* 部门id
*/
private String sysDeptId;
/**
* 市认证的组织ID
*/
private String srzOrganizationId;
/**
* 显示顺序
*/
private Long orderNum;
}

105
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/EfcodeVirtualUnitBo.java

@ -0,0 +1,105 @@
package com.zdxt.enforcementcode.domain.bo;
import com.zdxt.enforcementcode.domain.EfcodeVirtualUnit;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* 虚拟单位树业务对象 efcode_virtual_unit
*
* @author Lion Li
* @date 2026-05-07
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = EfcodeVirtualUnit.class, reverseConvertGenerate = false)
public class EfcodeVirtualUnitBo extends BaseEntity {
/**
* 虚拟单位ID
*/
@NotBlank(message = "虚拟单位ID不能为空", groups = { EditGroup.class })
private String virId;
/**
* 是否是虚拟节点
*/
private String virIsVirtual;
/**
* 租户编号
*/
private String virTenantId;
/**
* 父部门id
*/
private String virParentId;
/**
* 名称
*/
private String virName;
/**
* 类型
*/
private Long virZone;
/**
* 虚拟单位祖级ID列表:包含自身
*/
private String virAncestors;
/**
* 虚拟单位节点的单位路径:包含自身
*/
private String virDisplayPath;
/**
* 包含类型:查询/统计,查询,统计
*/
private String virIncludeType;
/**
* 域
*/
private String virDomain;
/**
* 状态
*/
private String virStatus;
/**
* 是否在业务中显示
*/
private String virShowAtBusiness;
/**
* 备注
*/
private String virRemark;
/**
* 部门id
*/
private String sysDeptId;
/**
* 市认证的组织ID
*/
private String srzOrganizationId;
/**
* 显示顺序
*/
private Long orderNum;
}

128
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EfcodeVirtualUnitVo.java

@ -0,0 +1,128 @@
package com.zdxt.enforcementcode.domain.vo;
import com.zdxt.enforcementcode.domain.EfcodeVirtualUnit;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 虚拟单位树视图对象 efcode_virtual_unit
*
* @author Lion Li
* @date 2026-05-07
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = EfcodeVirtualUnit.class)
public class EfcodeVirtualUnitVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 虚拟单位ID
*/
@ExcelProperty(value = "虚拟单位ID")
private String virId;
/**
* 是否是虚拟节点
*/
@ExcelProperty(value = "是否是虚拟节点")
private String virIsVirtual;
/**
* 租户编号
*/
@ExcelProperty(value = "租户编号")
private String virTenantId;
/**
* 父部门id
*/
@ExcelProperty(value = "父部门id")
private String virParentId;
/**
* 名称
*/
@ExcelProperty(value = "名称")
private String virName;
/**
* 类型
*/
@ExcelProperty(value = "类型")
private Long virZone;
/**
* 虚拟单位祖级ID列表:包含自身
*/
@ExcelProperty(value = "虚拟单位祖级ID列表:包含自身")
private String virAncestors;
/**
* 虚拟单位节点的单位路径:包含自身
*/
@ExcelProperty(value = "虚拟单位节点的单位路径:包含自身")
private String virDisplayPath;
/**
* 包含类型:查询/统计,查询,统计
*/
@ExcelProperty(value = "包含类型:查询/统计,查询,统计")
private String virIncludeType;
/**
* 域
*/
@ExcelProperty(value = "域")
private String virDomain;
/**
* 状态
*/
@ExcelProperty(value = "状态")
private String virStatus;
/**
* 是否在业务中显示
*/
@ExcelProperty(value = "是否在业务中显示")
private String virShowAtBusiness;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String virRemark;
/**
* 部门id
*/
@ExcelProperty(value = "部门id")
private String sysDeptId;
/**
* 市认证的组织ID
*/
@ExcelProperty(value = "市认证的组织ID")
private String srzOrganizationId;
/**
* 显示顺序
*/
@ExcelProperty(value = "显示顺序")
private Long orderNum;
}

34
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/SrzDeptVo.java

@ -7,6 +7,7 @@ import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
@ -21,25 +22,14 @@ import java.util.Date;
* @date 2026-05-06
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ExcelIgnoreUnannotated
@AutoMapper(target = SrzDept.class)
public class SrzDeptVo implements Serializable {
public class SrzDeptVo extends SrzDeptMinVo {
@Serial
private static final long serialVersionUID = 1L;
/**
* 机构ID
*/
@ExcelProperty(value = "机构ID")
private String organizationid;
/**
* 机构名称
*/
@ExcelProperty(value = "机构名称")
private String name;
/**
* 机构代码
*/
@ -64,12 +54,6 @@ public class SrzDeptVo implements Serializable {
@ExcelProperty(value = "部门路径")
private String zzdisplaypath;
/**
* 是否叶子节点
*/
@ExcelProperty(value = "是否叶子节点")
private String zzisleaf;
/**
* 是否删除
*/
@ -88,18 +72,6 @@ public class SrzDeptVo implements Serializable {
@ExcelProperty(value = "系统ID")
private String systemid;
/**
* 机构层级
*/
@ExcelProperty(value = "机构层级")
private String zzlevel;
/**
* 机构父节点
*/
@ExcelProperty(value = "机构父节点")
private String parentid;
/**
* 排序
*/

15
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeVirtualUnitMapper.java

@ -0,0 +1,15 @@
package com.zdxt.enforcementcode.mapper;
import com.zdxt.enforcementcode.domain.EfcodeVirtualUnit;
import com.zdxt.enforcementcode.domain.vo.EfcodeVirtualUnitVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 虚拟单位树Mapper接口
*
* @author Lion Li
* @date 2026-05-07
*/
public interface EfcodeVirtualUnitMapper extends BaseMapperPlus<EfcodeVirtualUnit, EfcodeVirtualUnitVo> {
}

68
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeVirtualUnitService.java

@ -0,0 +1,68 @@
package com.zdxt.enforcementcode.service;
import com.zdxt.enforcementcode.domain.vo.EfcodeVirtualUnitVo;
import com.zdxt.enforcementcode.domain.bo.EfcodeVirtualUnitBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 虚拟单位树Service接口
*
* @author Lion Li
* @date 2026-05-07
*/
public interface IEfcodeVirtualUnitService {
/**
* 查询虚拟单位树
*
* @param virId 主键
* @return 虚拟单位树
*/
EfcodeVirtualUnitVo queryById(String virId);
/**
* 分页查询虚拟单位树列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 虚拟单位树分页列表
*/
TableDataInfo<EfcodeVirtualUnitVo> queryPageList(EfcodeVirtualUnitBo bo, PageQuery pageQuery);
/**
* 查询符合条件的虚拟单位树列表
*
* @param bo 查询条件
* @return 虚拟单位树列表
*/
List<EfcodeVirtualUnitVo> queryList(EfcodeVirtualUnitBo bo);
/**
* 新增虚拟单位树
*
* @param bo 虚拟单位树
* @return 是否新增成功
*/
Boolean insertByBo(EfcodeVirtualUnitBo bo);
/**
* 修改虚拟单位树
*
* @param bo 虚拟单位树
* @return 是否修改成功
*/
Boolean updateByBo(EfcodeVirtualUnitBo bo);
/**
* 校验并批量删除虚拟单位树信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid);
}

144
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeVirtualUnitServiceImpl.java

@ -0,0 +1,144 @@
package com.zdxt.enforcementcode.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.zdxt.enforcementcode.domain.bo.EfcodeVirtualUnitBo;
import com.zdxt.enforcementcode.domain.vo.EfcodeVirtualUnitVo;
import com.zdxt.enforcementcode.domain.EfcodeVirtualUnit;
import com.zdxt.enforcementcode.mapper.EfcodeVirtualUnitMapper;
import com.zdxt.enforcementcode.service.IEfcodeVirtualUnitService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 虚拟单位树Service业务层处理
*
* @author Lion Li
* @date 2026-05-07
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class EfcodeVirtualUnitServiceImpl implements IEfcodeVirtualUnitService {
private final EfcodeVirtualUnitMapper baseMapper;
/**
* 查询虚拟单位树
*
* @param virId 主键
* @return 虚拟单位树
*/
@Override
public EfcodeVirtualUnitVo queryById(String virId){
return baseMapper.selectVoById(virId);
}
/**
* 分页查询虚拟单位树列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 虚拟单位树分页列表
*/
@Override
public TableDataInfo<EfcodeVirtualUnitVo> queryPageList(EfcodeVirtualUnitBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<EfcodeVirtualUnit> lqw = buildQueryWrapper(bo);
Page<EfcodeVirtualUnitVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的虚拟单位树列表
*
* @param bo 查询条件
* @return 虚拟单位树列表
*/
@Override
public List<EfcodeVirtualUnitVo> queryList(EfcodeVirtualUnitBo bo) {
LambdaQueryWrapper<EfcodeVirtualUnit> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<EfcodeVirtualUnit> buildQueryWrapper(EfcodeVirtualUnitBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<EfcodeVirtualUnit> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(EfcodeVirtualUnit::getVirId);
lqw.eq(StringUtils.isNotBlank(bo.getVirIsVirtual()), EfcodeVirtualUnit::getVirIsVirtual, bo.getVirIsVirtual());
lqw.eq(StringUtils.isNotBlank(bo.getVirParentId()), EfcodeVirtualUnit::getVirParentId, bo.getVirParentId());
lqw.eq(bo.getVirZone() != null, EfcodeVirtualUnit::getVirZone, bo.getVirZone());
lqw.eq(StringUtils.isNotBlank(bo.getVirAncestors()), EfcodeVirtualUnit::getVirAncestors, bo.getVirAncestors());
lqw.eq(StringUtils.isNotBlank(bo.getVirDisplayPath()), EfcodeVirtualUnit::getVirDisplayPath, bo.getVirDisplayPath());
lqw.eq(StringUtils.isNotBlank(bo.getVirIncludeType()), EfcodeVirtualUnit::getVirIncludeType, bo.getVirIncludeType());
lqw.eq(StringUtils.isNotBlank(bo.getVirDomain()), EfcodeVirtualUnit::getVirDomain, bo.getVirDomain());
lqw.eq(StringUtils.isNotBlank(bo.getVirStatus()), EfcodeVirtualUnit::getVirStatus, bo.getVirStatus());
lqw.eq(StringUtils.isNotBlank(bo.getVirShowAtBusiness()), EfcodeVirtualUnit::getVirShowAtBusiness, bo.getVirShowAtBusiness());
lqw.eq(StringUtils.isNotBlank(bo.getVirRemark()), EfcodeVirtualUnit::getVirRemark, bo.getVirRemark());
lqw.eq(StringUtils.isNotBlank(bo.getSysDeptId()), EfcodeVirtualUnit::getSysDeptId, bo.getSysDeptId());
lqw.eq(StringUtils.isNotBlank(bo.getSrzOrganizationId()), EfcodeVirtualUnit::getSrzOrganizationId, bo.getSrzOrganizationId());
lqw.eq(bo.getOrderNum() != null, EfcodeVirtualUnit::getOrderNum, bo.getOrderNum());
return lqw;
}
/**
* 新增虚拟单位树
*
* @param bo 虚拟单位树
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(EfcodeVirtualUnitBo bo) {
EfcodeVirtualUnit add = MapstructUtils.convert(bo, EfcodeVirtualUnit.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setVirId(add.getVirId());
}
return flag;
}
/**
* 修改虚拟单位树
*
* @param bo 虚拟单位树
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(EfcodeVirtualUnitBo bo) {
EfcodeVirtualUnit update = MapstructUtils.convert(bo, EfcodeVirtualUnit.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(EfcodeVirtualUnit entity){
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除虚拟单位树信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

6
zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeVirtualUnitMapper.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zdxt.enforcementcode.mapper.EfcodeVirtualUnitMapper">
</mapper>

5
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java

@ -114,4 +114,9 @@ public class SysDeptVo implements Serializable {
private List<SysDept> children = new ArrayList<>();
private String zzDisplayPath;
/**
* 排序号
*/
private String sequence;
}

1
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml

@ -174,6 +174,7 @@ tenant:
- rule_list
- srz_dept
- srz_user
- efcode_virtual_unit
# MyBatisPlus配置
# https://baomidou.com/config/

344
zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/service/test/InitVirtualUnitTest.java

@ -0,0 +1,344 @@
package com.zdxt.service.test;
import cn.dev33.satoken.context.mock.SaTokenContextMockUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zdxt.enforcementcode.domain.EfcodeVirtualUnit;
import com.zdxt.enforcementcode.mapper.EfcodeVirtualUnitMapper;
import org.dromara.ZdxtAdminApplication;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.mapper.SysDeptMapper;
import org.dromara.system.service.ISysDictTypeService;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.*;
import java.util.stream.Collectors;
/**
* 初始化虚拟单位树
* <p>
* 将 selectDeptTreeListReBuildByZone 中的虚拟化节点转为实际节点,
* 连同真实部门节点一起存入 efcode_virtual_unit 表。
* </p>
*/
@SpringBootTest(classes = ZdxtAdminApplication.class)
@ActiveProfiles({"dev", "dev-cust-zrb"})
@AutoConfigureMockMvc
@DisplayName("初始化虚拟单位树")
public class InitVirtualUnitTest {
@Autowired
private SysDeptMapper sysDeptMapper;
@Autowired
private ISysDictTypeService dictTypeService;
@Autowired
private EfcodeVirtualUnitMapper efcodeVirtualUnitMapper;
private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e";
private static final String TENANT_ID = "000000";
@BeforeEach
public void setUp() {
SaTokenContextMockUtil.setMockContext();
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest));
LoginUser loginUser = new LoginUser();
loginUser.setUserId(1L);
loginUser.setUserType("sys_user");
loginUser.setTenantId(TENANT_ID);
loginUser.setUsername("test_admin");
loginUser.setNickname("测试管理员");
loginUser.setDeptId("103");
loginUser.setDeptName("测试部门");
loginUser.setRolePermission(new HashSet<>());
loginUser.setMenuPermission(new HashSet<>());
SaLoginParameter param = new SaLoginParameter();
param.setExtra(LoginHelper.CLIENT_KEY, TEST_CLIENT_ID);
LoginHelper.login(loginUser, param);
}
@AfterEach
public void tearDown() {
try {
StpUtil.logout();
} catch (Exception ignored) {
}
RequestContextHolder.resetRequestAttributes();
SaTokenContextMockUtil.clearContext();
}
@DisplayName("初始化虚拟单位树数据到 efcode_virtual_unit 表")
@Test
public void initVirtualUnitTree() {
// 与 SysUserController.deptTreeReBuildByZone 使用相同的 zone 列表
List<Integer> zones = Arrays.asList(1, 2, 3, 4, 5, 6, 16, 15);
// 1. 查询所有部门(按zone过滤)
LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
lqw.eq(SysDept::getDelFlag, SystemConstants.NORMAL);
lqw.orderByAsc(SysDept::getParentId);
lqw.orderByAsc(SysDept::getOrderNum);
lqw.orderByAsc(SysDept::getDeptId);
lqw.in(SysDept::getZone, zones);
List<SysDeptVo> depts = new ArrayList<>(sysDeptMapper.selectVoList(lqw));
if (CollUtil.isEmpty(depts)) {
System.out.println("未查询到任何部门数据,退出");
return;
}
System.out.println("查询到部门总数: " + depts.size());
// 构建 deptId -> organizationId 映射(用于设置 srz_organization_id)
Map<String, String> deptOrgIdMap = sysDeptMapper.selectList(Wrappers.<SysDept>lambdaQuery()
.select(SysDept::getDeptId, SysDept::getOrganizationId)
.in(SysDept::getZone, zones)
.eq(SysDept::getDelFlag, SystemConstants.NORMAL)
).stream().collect(Collectors.toMap(
SysDept::getDeptId,
d -> d.getOrganizationId() != null ? d.getOrganizationId() : "",
(a, b) -> a
));
// 用于收集所有待插入的虚拟单位节点
List<EfcodeVirtualUnit> allUnits = new ArrayList<>();
// id -> name 映射,用于构建 displayPath
Map<String, String> idNameMap = new LinkedHashMap<>();
// 2. 构建虚拟根节点 "深圳市"
EfcodeVirtualUnit rootUnit = buildVirtualUnit("_root_", "深圳市", null, 0L);
allUnits.add(rootUnit);
idNameMap.put(rootUnit.getVirId(), "深圳市");
// 3. 获取字典数据 role_tree_zone
List<SysDictDataVo> dictDataList = dictTypeService.selectDictDataByType("role_tree_zone");
if (CollUtil.isEmpty(dictDataList)) {
System.out.println("未获取到 role_tree_zone 字典数据,退出");
return;
}
// 3.1 添加 "市直"(dictValue=1) 和 "区"(dictValue=2) 虚拟节点
// dictValue -> 虚拟节点virId 的映射,用于后续绑定父节点
Map<String, String> zoneVirIdMap = new HashMap<>();
for (SysDictDataVo dict : dictDataList) {
if ("1".equals(dict.getDictValue()) || "2".equals(dict.getDictValue())) {
EfcodeVirtualUnit zoneUnit = buildVirtualUnit(
rootUnit.getVirId(), dict.getDictLabel(),
Long.parseLong(dict.getDictValue()), Long.parseLong(dict.getDictValue()));
allUnits.add(zoneUnit);
zoneVirIdMap.put(dict.getDictValue(), zoneUnit.getVirId());
idNameMap.put(zoneUnit.getVirId(), dict.getDictLabel());
}
}
// 4. 按 zone 重新绑定部门的父节点(同 selectDeptTreeListReBuildByZone 逻辑)
String shizhiVirId = zoneVirIdMap.get("1");
String quVirId = zoneVirIdMap.get("2");
depts.forEach(d -> {
if (Objects.equals(d.getZone(), 1)) {
d.setParentId(shizhiVirId);
}
if (Objects.equals(d.getZone(), 2)) {
d.setParentId(quVirId);
}
if (Objects.equals(d.getZone(), 15) || Objects.equals(d.getZone(), 16)) {
d.setParentId(shizhiVirId);
}
});
// 5. 收集区(zone=2)的 id->name 映射
Map<String, String> districtMap = depts.stream()
.filter(d -> Objects.equals(d.getZone(), 2))
.collect(Collectors.toMap(SysDeptVo::getDeptId, SysDeptVo::getDeptName));
// 收集 zone=3/5/6 的部门ID
List<String> checkDeptIds = depts.stream()
.filter(d -> Objects.equals(d.getZone(), 3) || Objects.equals(d.getZone(), 5) || Objects.equals(d.getZone(), 6))
.map(SysDeptVo::getDeptId)
.collect(Collectors.toList());
// 6. 为每个区创建 "区直" 虚拟节点,并将 zone=3/5/6 的部门挂到对应区直下
districtMap.forEach((pid, districtName) -> {
EfcodeVirtualUnit quzhiUnit = buildVirtualUnit(pid, "区直", null, 0L);
String quzhiId = quzhiUnit.getVirId();
allUnits.add(quzhiUnit);
idNameMap.put(quzhiId, "区直");
for (SysDeptVo dept : depts) {
if (Objects.equals(dept.getZone(), 3) || Objects.equals(dept.getZone(), 6) || Objects.equals(dept.getZone(), 5)) {
if (checkDeptIds.contains(dept.getParentId())) {
// 父部门也是区直类型(zone=3/5/6),保持原有父子关系
} else if (StringUtils.isNotEmpty(dept.getAncestors()) && dept.getAncestors().contains(pid)) {
dept.setParentId(quzhiId);
} else if (StringUtils.isEmpty(dept.getAncestors())
|| districtMap.values().stream().noneMatch(v -> dept.getAncestors().contains(v))) {
if (dept.getDeptName() != null && districtName.length() >= 2
&& dept.getDeptName().contains(districtName.substring(0, 2))) {
dept.setParentId(quzhiId);
}
}
}
}
});
// 7. 将实际部门节点转为 EfcodeVirtualUnit
// 7.1 先为每个部门生成 UUID,并建立 deptId -> UUID 映射
Map<String, String> deptIdToVirIdMap = new HashMap<>();
for (SysDeptVo dept : depts) {
String virId = UUID.randomUUID().toString().replace("-", "");
deptIdToVirIdMap.put(dept.getDeptId(), virId);
// idNameMap 同时存 deptId 和 virId 两个 key,确保 ancestors/displayPath 构建时能找到名称
idNameMap.put(dept.getDeptId(), dept.getDeptName());
idNameMap.put(virId, dept.getDeptName());
}
// 7.2 更新已有虚拟节点的 parentId(如"区直"节点的 parentId 还是原始 deptId,需替换为 UUID)
for (EfcodeVirtualUnit existingUnit : allUnits) {
String pid = existingUnit.getVirParentId();
if (deptIdToVirIdMap.containsKey(pid)) {
existingUnit.setVirParentId(deptIdToVirIdMap.get(pid));
}
}
// 7.3 构建实际部门节点,parentId 如果指向其他部门则替换为对应 UUID
// 收集所有已知的有效 virId(虚拟节点 + 部门映射)
Set<String> knownVirIds = allUnits.stream()
.map(EfcodeVirtualUnit::getVirId)
.collect(Collectors.toSet());
knownVirIds.addAll(deptIdToVirIdMap.values());
for (SysDeptVo dept : depts) {
EfcodeVirtualUnit unit = new EfcodeVirtualUnit();
unit.setVirId(deptIdToVirIdMap.get(dept.getDeptId()));
unit.setVirIsVirtual("否");
// parentId: 优先映射为对应部门的UUID,否则检查是否为已知虚拟节点virId
String parentId = dept.getParentId();
if (deptIdToVirIdMap.containsKey(parentId)) {
parentId = deptIdToVirIdMap.get(parentId);
} else if (!knownVirIds.contains(parentId)) {
// parentId 无法映射为有效virId,从原始 ancestors 中查找最近的有效祖级
String fallback = null;
if (StringUtils.isNotEmpty(dept.getAncestors())) {
String[] ancestorArr = dept.getAncestors().split(",");
for (int j = ancestorArr.length - 1; j >= 0; j--) {
String aid = ancestorArr[j].trim();
if (deptIdToVirIdMap.containsKey(aid)) {
fallback = deptIdToVirIdMap.get(aid);
break;
}
}
}
// 如果仍找不到,按 zone 降级到虚拟父节点
if (fallback == null) {
if (Objects.equals(dept.getZone(), 2)) {
fallback = quVirId;
} else if (Objects.equals(dept.getZone(), 1) || Objects.equals(dept.getZone(), 15) || Objects.equals(dept.getZone(), 16)) {
fallback = shizhiVirId;
} else {
fallback = rootUnit.getVirId();
}
}
parentId = fallback;
}
unit.setVirParentId(parentId);
unit.setVirName(dept.getDeptName());
unit.setVirZone(dept.getZone() != null ? Long.valueOf(dept.getZone()) : null);
unit.setVirStatus("启用");
unit.setVirIncludeType("全部");
unit.setVirShowAtBusiness("是");
unit.setSysDeptId(dept.getDeptId());
unit.setSrzOrganizationId(deptOrgIdMap.getOrDefault(dept.getDeptId(), ""));
unit.setOrderNum(StringUtils.isNotEmpty(dept.getSequence()) ? Long.valueOf(dept.getSequence()) : 0L);
allUnits.add(unit);
}
// 8. 构建 ancestors 和 displayPath
Map<String, EfcodeVirtualUnit> unitMap = allUnits.stream()
.collect(Collectors.toMap(EfcodeVirtualUnit::getVirId, u -> u, (a, b) -> a));
for (EfcodeVirtualUnit unit : allUnits) {
// 8.1 构建 vir_ancestors:向上追溯所有祖级virId + 自身 virId
List<String> ancestorIds = new ArrayList<>();
String parentId = unit.getVirParentId();
while (parentId != null && !"_root_".equals(parentId)) {
EfcodeVirtualUnit parentUnit = unitMap.get(parentId);
if (parentUnit != null) {
ancestorIds.add(0, parentId);
parentId = parentUnit.getVirParentId();
} else {
break;
}
}
ancestorIds.add(unit.getVirId());
unit.setVirAncestors(String.join(",", ancestorIds));
// 8.2 构建 vir_display_path:基于 ancestors 中每个 virId 查找名称
String displayPath = ancestorIds.stream()
.map(id -> idNameMap.getOrDefault(id, ""))
.collect(Collectors.joining("/"));
unit.setVirDisplayPath(displayPath);
}
// 9. 清空旧数据后批量插入
long existCount = efcodeVirtualUnitMapper.selectCount(Wrappers.lambdaQuery());
if (existCount > 0) {
System.out.println("清空旧数据,共 " + existCount + " 条");
efcodeVirtualUnitMapper.delete(Wrappers.lambdaQuery());
}
// 分批插入(每批500条)
int batchSize = 500;
int total = allUnits.size();
int inserted = 0;
for (int i = 0; i < total; i += batchSize) {
List<EfcodeVirtualUnit> batch = allUnits.subList(i, Math.min(i + batchSize, total));
efcodeVirtualUnitMapper.insertBatch(batch);
inserted += batch.size();
}
System.out.println("========== 初始化完成 ==========");
System.out.println("虚拟节点数: " + allUnits.stream().filter(u -> "1".equals(u.getVirIsVirtual())).count());
System.out.println("实际部门节点数: " + allUnits.stream().filter(u -> "0".equals(u.getVirIsVirtual())).count());
System.out.println("总插入数: " + inserted);
}
/**
* 构建虚拟节点
*/
private EfcodeVirtualUnit buildVirtualUnit(String parentId, String name, Long zone, Long orderNum) {
EfcodeVirtualUnit unit = new EfcodeVirtualUnit();
unit.setVirId(UUID.randomUUID().toString().replace("-", ""));
unit.setVirIsVirtual("是");
unit.setVirParentId(parentId);
unit.setVirName(name);
unit.setVirZone(zone);
unit.setVirStatus("启用");
unit.setVirIncludeType("全部");
unit.setVirShowAtBusiness("是");
unit.setSysDeptId(null);
unit.setSrzOrganizationId(null);
unit.setOrderNum(orderNum != null ? orderNum : 0L);
return unit;
}
}
Loading…
Cancel
Save