新增同步公司、组织

This commit is contained in:
2026-09-18 20:25:33 +08:00
parent 8d13978e84
commit 3b26591f9d
4 changed files with 335 additions and 33 deletions
@@ -50,8 +50,10 @@ import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.enums.DictEnum;
import org.springblade.system.pojo.vo.DeptVO;
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
import org.springblade.system.pojo.vo.UserVO;
import org.springblade.system.service.IDeptService;
import org.springblade.system.service.IOASyncService;
import org.springblade.system.wrapper.DeptWrapper;
import org.springframework.web.bind.annotation.*;
@@ -73,6 +75,7 @@ import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
public class DeptController extends BladeController {
private final IDeptService deptService;
private final IOASyncService oaSyncService;
/**
* 详情
@@ -170,12 +173,38 @@ public class DeptController extends BladeController {
return R.data(deptService.syncIamOrganizations());
}
/**
* 从OA按页同步公司
*/
@IsAdmin
@PostMapping("/sync-oa-company")
@ApiOperationSupport(order = 8)
@Operation(summary = "同步OA公司")
public R<OaOrgSyncPageVO> syncOaCompany(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "50") Integer size) {
return R.data(oaSyncService.syncCompanyPage(current, size));
}
/**
* 从OA按页同步部门(需先完成公司同步)
*/
@IsAdmin
@PostMapping("/sync-oa-department")
@ApiOperationSupport(order = 9)
@Operation(summary = "同步OA部门")
public R<OaOrgSyncPageVO> syncOaDepartment(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "50") Integer size) {
return R.data(oaSyncService.syncDepartmentPage(current, size));
}
/**
* 删除
*/
@IsAdmin
@PostMapping("/remove")
@ApiOperationSupport(order = 8)
@ApiOperationSupport(order = 10)
@Operation(summary = "删除", description = "传入ids")
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
CacheUtil.clear(SYS_CACHE);
@@ -188,7 +217,7 @@ public class DeptController extends BladeController {
*/
@PreAuth(AuthConstant.PERMIT_ALL)
@GetMapping("/select")
@ApiOperationSupport(order = 9)
@ApiOperationSupport(order = 11)
@Operation(summary = "下拉数据源", description = "传入id集合")
public R<List<Dept>> select(Long userId, String deptId) {
if (Func.isNotEmpty(userId)) {
@@ -205,7 +234,7 @@ public class DeptController extends BladeController {
*/
@PreAuth(AuthConstant.PERMIT_ALL)
@GetMapping("/platform-company-select")
@ApiOperationSupport(order = 10)
@ApiOperationSupport(order = 12)
@Operation(summary = "平台公司下拉", description = "返回是否平台公司=是的部门列表")
public R<List<Dept>> platformCompanySelect() {
return R.data(deptService.listPlatformCompany());
@@ -216,7 +245,7 @@ public class DeptController extends BladeController {
*/
@IsAdmin
@GetMapping("/dept-leader-info")
@ApiOperationSupport(order = 11)
@ApiOperationSupport(order = 13)
@Operation(summary = "获取部门的主管信息", description = "传入deptId")
public R<List<UserVO>> deptLeaderInfo(@Parameter(description = "部门id", required = true) @RequestParam Long deptId) {
List<UserVO> list = deptService.deptLeaderInfo(deptId);
@@ -1,5 +1,6 @@
package org.springblade.system.service;
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
/**
@@ -36,4 +37,22 @@ public interface IOASyncService {
* @return 本页同步结果
*/
OaPersonSyncPageVO syncPersonFromUserList(int current, int size);
/**
* 按页从 OA 公司接口同步公司
*
* @param current 当前页,从 1 开始
* @param size 每页条数
* @return 本页同步结果
*/
OaOrgSyncPageVO syncCompanyPage(int current, int size);
/**
* 按页从 OA 部门接口同步部门;最后一页完成后更新祖级列表
*
* @param current 当前页,从 1 开始
* @param size 每页条数
* @return 本页同步结果
*/
OaOrgSyncPageVO syncDepartmentPage(int current, int size);
}
@@ -18,6 +18,7 @@ import org.springblade.system.log.ComposeLogUtil;
import org.springblade.system.pojo.entity.*;
import org.springblade.system.pojo.enums.DataSync;
import org.springblade.system.pojo.enums.DeptCategory;
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
import org.springblade.system.service.*;
import org.springblade.system.util.DataSyncRecordUtils;
@@ -124,6 +125,28 @@ public class OASyncServiceImpl implements IOASyncService {
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public OaOrgSyncPageVO syncCompanyPage(int current, int size) {
try {
ComposeLogUtil.addLog(log);
return this.syncCompanyFromOaPage(current, size);
} finally {
ComposeLogUtil.removeLastLog();
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public OaOrgSyncPageVO syncDepartmentPage(int current, int size) {
try {
ComposeLogUtil.addLog(log);
return this.syncDepartmentFromOaPage(current, size);
} finally {
ComposeLogUtil.removeLastLog();
}
}
/**
* 同步并记录
*
@@ -166,20 +189,15 @@ public class OASyncServiceImpl implements IOASyncService {
// 未处理的数据
List<Dept> notHandleList = new ArrayList<>();
// 1. 设置查询参数
OACompanySearch companySearch = new OACompanySearch();
companySearch.setCurPage(1);
if (startTime != null) {
// 开始时间不为空,设置修改时间参数
companySearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
OACompanySearch companySearch = buildCompanySearch(startTime);
// 2. 分页查询并处理数据
OAUtils.pageSyncHandler(companySearch, param -> oaClient.queryCompanyPage(new OASearch<>(param)), response -> {
ComposeLogUtil.getLastLog().error("调用OA接口查询公司信息失败 {}", JSON.toJSONString(response));
return new ServiceException("调用OA接口查询公司信息失败");
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
// 处理数据
List<Dept> deptList = handleCompany(list);
notHandleList.addAll(deptList);
OrgSyncCount syncCount = handleCompany(list);
notHandleList.addAll(syncCount.getNotHandledList());
});
// 3. 未处理的数据
if (CollectionUtil.isNotEmpty(notHandleList)) {
@@ -203,21 +221,15 @@ public class OASyncServiceImpl implements IOASyncService {
// 未处理的数据
List<Dept> notHandleList = new ArrayList<>();
// 1. 设置查询参数
OADepartmentSearch departmentSearch = new OADepartmentSearch();
departmentSearch.setCurPage(1);
departmentSearch.setSubcompanyid1(subCompanyIds);
if (startTime != null) {
// 开始时间不为空,设置修改时间参数
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
OADepartmentSearch departmentSearch = buildDepartmentSearch(startTime, subCompanyIds);
// 2. 分页查询并处理数据
OAUtils.pageSyncHandler(departmentSearch, param -> oaClient.queryDepartmentPage(new OASearch<>(param)), response -> {
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(response));
return new ServiceException("调用OA接口查询部门信息失败");
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
// 处理数据
List<Dept> deptList = handleDept(list);
notHandleList.addAll(deptList);
OrgSyncCount syncCount = handleDept(list);
notHandleList.addAll(syncCount.getNotHandledList());
});
// 3. 未处理的数据
if (CollectionUtil.isNotEmpty(notHandleList)) {
@@ -300,6 +312,139 @@ public class OASyncServiceImpl implements IOASyncService {
return pageVO;
}
/**
* 按页从 OA 公司接口同步公司
*
* @param current 当前页
* @param size 每页条数
* @return 本页同步结果
*/
private OaOrgSyncPageVO syncCompanyFromOaPage(int current, int size) {
int pageNo = current < 1 ? 1 : current;
int pageSize = size < 1 ? 50 : Math.min(size, 200);
OACompanySearch companySearch = buildCompanySearch(null);
companySearch.setCurPage(pageNo);
companySearch.setPageSize(pageSize);
OAResponse<OACompanyResponse> oaResponse = oaClient.queryCompanyPage(new OASearch<>(companySearch));
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
ComposeLogUtil.getLastLog().error("调用OA接口查询公司信息失败 {}", JSON.toJSONString(oaResponse));
throw new ServiceException("调用OA接口查询公司信息失败");
}
OAResponseData<OACompanyResponse> responseData = oaResponse.getData();
List<OACompanyResponse> oaCompanies = responseData.getDataList() == null
? Collections.emptyList() : responseData.getDataList();
long totalSize = responseData.getTotalSize() == null ? 0L : responseData.getTotalSize();
OrgSyncCount syncCount = handleCompany(oaCompanies);
if (CollectionUtil.isNotEmpty(syncCount.getNotHandledList())) {
ComposeLogUtil.getLastLog().warn("同步公司,本页未处理数据:{}", JSON.toJSONString(syncCount.getNotHandledList()));
}
CacheUtil.clear(SYS_CACHE);
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
OaOrgSyncPageVO pageVO = buildOrgSyncPageVO("company", pageNo, pageSize, totalSize, oaCompanies.size(), syncCount);
ComposeLogUtil.getLastLog().info("OA公司分页同步完成 {}/{},成功{},跳过{}",
pageNo, totalSize, syncCount.getSyncedCount(), syncCount.getSkippedCount());
return pageVO;
}
/**
* 按页从 OA 部门接口同步部门
*
* @param current 当前页
* @param size 每页条数
* @return 本页同步结果
*/
private OaOrgSyncPageVO syncDepartmentFromOaPage(int current, int size) {
int pageNo = current < 1 ? 1 : current;
int pageSize = size < 1 ? 50 : Math.min(size, 200);
String subCompanyIds = getSubCompanyIds();
if (StringUtils.isEmpty(subCompanyIds)) {
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数,跳过部门同步");
OaOrgSyncPageVO emptyPageVO = buildOrgSyncPageVO("department", pageNo, pageSize, 0L, 0, OrgSyncCount.empty());
emptyPageVO.setFinished(true);
return emptyPageVO;
}
OADepartmentSearch departmentSearch = buildDepartmentSearch(null, subCompanyIds);
departmentSearch.setCurPage(pageNo);
departmentSearch.setPageSize(pageSize);
OAResponse<OADepartmentResponse> oaResponse = oaClient.queryDepartmentPage(new OASearch<>(departmentSearch));
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(oaResponse));
throw new ServiceException("调用OA接口查询部门信息失败");
}
OAResponseData<OADepartmentResponse> responseData = oaResponse.getData();
List<OADepartmentResponse> oaDepartments = responseData.getDataList() == null
? Collections.emptyList() : responseData.getDataList();
long totalSize = responseData.getTotalSize() == null ? 0L : responseData.getTotalSize();
OrgSyncCount syncCount = handleDept(oaDepartments);
if (CollectionUtil.isNotEmpty(syncCount.getNotHandledList())) {
ComposeLogUtil.getLastLog().warn("同步部门,本页未处理数据:{}", JSON.toJSONString(syncCount.getNotHandledList()));
}
CacheUtil.clear(SYS_CACHE);
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
OaOrgSyncPageVO pageVO = buildOrgSyncPageVO("department", pageNo, pageSize, totalSize, oaDepartments.size(), syncCount);
if (Boolean.TRUE.equals(pageVO.getFinished())) {
// 部门同步完成后更新祖级列表
deptService.updateAncestors(null);
}
ComposeLogUtil.getLastLog().info("OA部门分页同步完成 {}/{},成功{},跳过{}",
pageNo, totalSize, syncCount.getSyncedCount(), syncCount.getSkippedCount());
return pageVO;
}
/**
* 组装组织分页同步结果
*/
private OaOrgSyncPageVO buildOrgSyncPageVO(String stage, int pageNo, int pageSize, long totalSize,
int fetchedCount, OrgSyncCount syncCount) {
OaOrgSyncPageVO pageVO = new OaOrgSyncPageVO();
pageVO.setStage(stage);
pageVO.setCurrent(pageNo);
pageVO.setSize(pageSize);
pageVO.setTotal(totalSize);
pageVO.setFetchedCount(fetchedCount);
pageVO.setSyncedCount(syncCount.getSyncedCount());
pageVO.setSkippedCount(syncCount.getSkippedCount());
boolean finished = fetchedCount == 0
|| fetchedCount < pageSize
|| (long) pageNo * pageSize >= totalSize;
pageVO.setFinished(finished);
return pageVO;
}
/**
* 组装 OA 公司分页查询参数
*
* @param startTime 增量查询开始时间
* @return 查询参数
*/
private OACompanySearch buildCompanySearch(Date startTime) {
OACompanySearch companySearch = new OACompanySearch();
companySearch.setCurPage(1);
companySearch.setPageSize(50);
if (startTime != null) {
companySearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
return companySearch;
}
/**
* 组装 OA 部门分页查询参数
*
* @param startTime 增量查询开始时间
* @param subCompanyIds 子公司 id 列表
* @return 查询参数
*/
private OADepartmentSearch buildDepartmentSearch(Date startTime, String subCompanyIds) {
OADepartmentSearch departmentSearch = new OADepartmentSearch();
departmentSearch.setCurPage(1);
departmentSearch.setPageSize(50);
departmentSearch.setSubcompanyid1(subCompanyIds);
if (startTime != null) {
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
return departmentSearch;
}
/**
* 组装 OA 人员分页查询参数
*
@@ -325,12 +470,11 @@ public class OASyncServiceImpl implements IOASyncService {
/**
* 处理oa公司
* @param oaCompanies
* @return 未处理的数据
* @return 同步统计
*/
private List<Dept> handleCompany(List<OACompanyResponse> oaCompanies) {
private OrgSyncCount handleCompany(List<OACompanyResponse> oaCompanies) {
if (CollectionUtil.isEmpty(oaCompanies)) {
// 数据为空,直接返回
return Collections.emptyList();
return OrgSyncCount.empty();
}
// 获取需要的公司名称
Set<String> companyNames = getCompanyNames();
@@ -340,30 +484,36 @@ public class OASyncServiceImpl implements IOASyncService {
.filter(company -> companyNames.contains(company.getSubcompanyname()))
.map(deptConvert::company2dept)
.toList();
int filteredSkipCount = oaCompanies.size() - allParam.size();
if (CollectionUtil.isEmpty(allParam)) {
return new OrgSyncCount(0, filteredSkipCount, Collections.emptyList());
}
// 查询数据库的部门,转换成map
Map<String, Long> deptMap = getAllCompanyDeptMap();
// 处理部门
return handleDept(allParam, deptMap, DeptCategory.COMPANY);
List<Dept> notHandledList = handleDept(allParam, deptMap, DeptCategory.COMPANY);
int syncedCount = allParam.size() - notHandledList.size();
int skippedCount = filteredSkipCount + notHandledList.size();
return new OrgSyncCount(syncedCount, skippedCount, notHandledList);
}
/**
* 处理oa部门
* @param oaDepts
* @return 未处理的数据
* @return 同步统计
*/
private List<Dept> handleDept(List<OADepartmentResponse> oaDepts) {
private OrgSyncCount handleDept(List<OADepartmentResponse> oaDepts) {
if (CollectionUtil.isEmpty(oaDepts)) {
// 数据为空,直接返回
return Collections.emptyList();
return OrgSyncCount.empty();
}
// 查询所有公司的编码和id的map
Map<String, Long> companyDeptMap = getAllCompanyDeptMap();
// 根公司id
String rootCompanyId = getRootCompanyId();
if (rootCompanyId == null) {
return Collections.emptyList();
return new OrgSyncCount(0, oaDepts.size(), Collections.emptyList());
}
// 根公司下要同步的部门名称
Set<String> rootCompanyDeptNames = getRootCompanyDeptNames();
@@ -373,8 +523,9 @@ public class OASyncServiceImpl implements IOASyncService {
.filter(oaDept -> !rootCompanyId.equals(oaDept.getSubcompanyid1()) || (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid()) && rootCompanyDeptNames.contains(oaDept.getDepartmentname())))
.map(dept -> deptConvert.dept2dept(dept, companyDeptMap))
.toList();
int filteredSkipCount = oaDepts.size() - allParam.size();
if (CollectionUtil.isEmpty(allParam)) {
return Collections.emptyList();
return new OrgSyncCount(0, filteredSkipCount, Collections.emptyList());
}
Set<String> deptCodes = allParam.stream()
.map(Dept::getDeptCode)
@@ -387,7 +538,10 @@ public class OASyncServiceImpl implements IOASyncService {
.filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode()))
.collect(Collectors.toMap(Dept::getDeptCode, Dept::getId, (a, b) -> b));
// 处理部门
return this.handleDept(allParam, deptMap, DeptCategory.DEPT);
List<Dept> notHandledList = this.handleDept(allParam, deptMap, DeptCategory.DEPT);
int syncedCount = allParam.size() - notHandledList.size();
int skippedCount = filteredSkipCount + notHandledList.size();
return new OrgSyncCount(syncedCount, skippedCount, notHandledList);
}
/**
@@ -441,6 +595,37 @@ public class OASyncServiceImpl implements IOASyncService {
.toList();
}
/**
* 组织同步统计
*/
private static class OrgSyncCount {
private final int syncedCount;
private final int skippedCount;
private final List<Dept> notHandledList;
private OrgSyncCount(int syncedCount, int skippedCount, List<Dept> notHandledList) {
this.syncedCount = syncedCount;
this.skippedCount = skippedCount;
this.notHandledList = notHandledList == null ? Collections.emptyList() : notHandledList;
}
private static OrgSyncCount empty() {
return new OrgSyncCount(0, 0, Collections.emptyList());
}
private int getSyncedCount() {
return syncedCount;
}
private int getSkippedCount() {
return skippedCount;
}
private List<Dept> getNotHandledList() {
return notHandledList;
}
}
/**
* 获取oa查询参数,子公司id参数
* @return