🐛 修复MK公开页审批回写,并支持OA部门按公司oaId轮询同步
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+15
@@ -72,4 +72,19 @@ public class OaOrgSyncPageVO implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "全量同步结束后原始数据层级 JSON 文件路径")
|
@Schema(description = "全量同步结束后原始数据层级 JSON 文件路径")
|
||||||
private String dumpFilePath;
|
private String dumpFilePath;
|
||||||
|
|
||||||
|
@Schema(description = "当前同步的公司 OA 主键(部门阶段按公司轮询时使用)")
|
||||||
|
private String companyOaId;
|
||||||
|
|
||||||
|
@Schema(description = "当前公司序号,从 1 开始")
|
||||||
|
private Integer companyIndex;
|
||||||
|
|
||||||
|
@Schema(description = "待同步公司总数")
|
||||||
|
private Integer companyTotal;
|
||||||
|
|
||||||
|
@Schema(description = "当前公司的部门是否已同步完")
|
||||||
|
private Boolean companyFinished;
|
||||||
|
|
||||||
|
@Schema(description = "下一公司 OA 主键;当前公司完成后回传,前端据此切换并重置页码")
|
||||||
|
private String nextCompanyOaId;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -204,7 +204,7 @@ public class DeptController extends BladeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从OA按页同步部门(需先完成公司同步)
|
* 从OA按页同步部门(需先完成公司同步;按公司 oaId 轮询查询)
|
||||||
*/
|
*/
|
||||||
@IsAdmin
|
@IsAdmin
|
||||||
@PostMapping("/sync-oa-department")
|
@PostMapping("/sync-oa-department")
|
||||||
@@ -214,8 +214,9 @@ public class DeptController extends BladeController {
|
|||||||
@RequestParam(defaultValue = "1") Integer current,
|
@RequestParam(defaultValue = "1") Integer current,
|
||||||
@RequestParam(defaultValue = "20") Integer size,
|
@RequestParam(defaultValue = "20") Integer size,
|
||||||
@RequestParam(defaultValue = "false") Boolean incremental,
|
@RequestParam(defaultValue = "false") Boolean incremental,
|
||||||
@RequestParam(required = false) String modifiedSince) {
|
@RequestParam(required = false) String modifiedSince,
|
||||||
return R.data(oaSyncService.syncDepartmentPage(current, size, Boolean.TRUE.equals(incremental), modifiedSince));
|
@RequestParam(required = false) String companyOaId) {
|
||||||
|
return R.data(oaSyncService.syncDepartmentPage(current, size, Boolean.TRUE.equals(incremental), modifiedSince, companyOaId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+4
-3
@@ -52,13 +52,14 @@ public interface IOASyncService {
|
|||||||
OaOrgSyncPageVO syncCompanyPage(int current, int size, boolean incremental, String modifiedSince);
|
OaOrgSyncPageVO syncCompanyPage(int current, int size, boolean incremental, String modifiedSince);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按页从 OA 部门接口同步部门;最后一页完成后更新祖级列表
|
* 按页从 OA 部门接口同步部门;须先完成公司同步,再按公司 oaId 轮询查询部门
|
||||||
*
|
*
|
||||||
* @param current 当前页,从 1 开始
|
* @param current 当前页,从 1 开始(相对当前公司)
|
||||||
* @param size 每页条数
|
* @param size 每页条数
|
||||||
* @param incremental 是否增量同步(按本地最大 oaModified 过滤 OA)
|
* @param incremental 是否增量同步(按本地最大 oaModified 过滤 OA)
|
||||||
* @param modifiedSince 增量起始时间;首屏可空,后续分页回传首屏返回值
|
* @param modifiedSince 增量起始时间;首屏可空,后续分页回传首屏返回值
|
||||||
|
* @param companyOaId 当前公司 OA 主键;首屏可空(自动取第一家),后续回传
|
||||||
* @return 本页同步结果
|
* @return 本页同步结果
|
||||||
*/
|
*/
|
||||||
OaOrgSyncPageVO syncDepartmentPage(int current, int size, boolean incremental, String modifiedSince);
|
OaOrgSyncPageVO syncDepartmentPage(int current, int size, boolean incremental, String modifiedSince, String companyOaId);
|
||||||
}
|
}
|
||||||
|
|||||||
+74
-38
@@ -153,10 +153,10 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@Override
|
@Override
|
||||||
public OaOrgSyncPageVO syncDepartmentPage(int current, int size, boolean incremental, String modifiedSince) {
|
public OaOrgSyncPageVO syncDepartmentPage(int current, int size, boolean incremental, String modifiedSince, String companyOaId) {
|
||||||
try {
|
try {
|
||||||
ComposeLogUtil.addLog(log);
|
ComposeLogUtil.addLog(log);
|
||||||
return this.syncDepartmentFromOaPage(current, size, incremental, modifiedSince);
|
return this.syncDepartmentFromOaPage(current, size, incremental, modifiedSince, companyOaId);
|
||||||
} finally {
|
} finally {
|
||||||
ComposeLogUtil.removeLastLog();
|
ComposeLogUtil.removeLastLog();
|
||||||
}
|
}
|
||||||
@@ -224,29 +224,28 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步部门
|
* 同步部门:先整理已同步公司,再按公司 oaId 逐个轮询查询部门
|
||||||
* @param startTime 查询开始时间
|
* @param startTime 查询开始时间
|
||||||
*/
|
*/
|
||||||
private void syncDept(Date startTime) {
|
private void syncDept(Date startTime) {
|
||||||
String subCompanyIds = getOaSubCompanyIds(false);
|
List<String> companyOaIds = listAllowedCompanyOaIds(false);
|
||||||
if (StringUtils.isEmpty(subCompanyIds)) {
|
if (CollectionUtil.isEmpty(companyOaIds)) {
|
||||||
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数");
|
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 未处理的数据
|
// 未处理的数据
|
||||||
List<Dept> notHandleList = new ArrayList<>();
|
List<Dept> notHandleList = new ArrayList<>();
|
||||||
// 1. 设置查询参数
|
for (String companyOaId : companyOaIds) {
|
||||||
OADepartmentSearch departmentSearch = buildDepartmentSearch(startTime, subCompanyIds);
|
ComposeLogUtil.getLastLog().info("按公司轮询同步部门 companyOaId={}", companyOaId);
|
||||||
// 2. 分页查询并处理数据
|
OADepartmentSearch departmentSearch = buildDepartmentSearch(startTime, companyOaId);
|
||||||
OAUtils.pageSyncHandler(departmentSearch, param -> oaClient.queryDepartmentPage(new OASearch<>(param)), response -> {
|
OAUtils.pageSyncHandler(departmentSearch, param -> oaClient.queryDepartmentPage(new OASearch<>(param)), response -> {
|
||||||
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(response));
|
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 companyOaId={} {}", companyOaId, JSON.toJSONString(response));
|
||||||
return new ServiceException("调用OA接口查询部门信息失败");
|
return new ServiceException("调用OA接口查询部门信息失败");
|
||||||
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
|
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
|
||||||
// 处理数据
|
|
||||||
OrgSyncCount syncCount = handleDept(list, false);
|
OrgSyncCount syncCount = handleDept(list, false);
|
||||||
notHandleList.addAll(syncCount.getNotHandledList());
|
notHandleList.addAll(syncCount.getNotHandledList());
|
||||||
});
|
});
|
||||||
// 3. 未处理的数据
|
}
|
||||||
if (CollectionUtil.isNotEmpty(notHandleList)) {
|
if (CollectionUtil.isNotEmpty(notHandleList)) {
|
||||||
ComposeLogUtil.getLastLog().warn("同步部门,还有未处理的数据:{}", JSON.toJSONString(notHandleList));
|
ComposeLogUtil.getLastLog().warn("同步部门,还有未处理的数据:{}", JSON.toJSONString(notHandleList));
|
||||||
}
|
}
|
||||||
@@ -398,34 +397,55 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按页从 OA 部门接口同步部门
|
* 按页从 OA 部门接口同步部门:公司数据整理完成后,按公司 oaId 逐个轮询查询
|
||||||
*
|
*
|
||||||
* @param current 当前页
|
* @param current 当前页(相对当前公司)
|
||||||
* @param size 每页条数
|
* @param size 每页条数
|
||||||
* @param incremental 是否增量同步
|
* @param incremental 是否增量同步
|
||||||
* @param modifiedSince 增量起始时间(后续分页回传)
|
* @param modifiedSince 增量起始时间(后续分页回传)
|
||||||
|
* @param companyOaId 当前公司 OA 主键;空则从第一家开始
|
||||||
* @return 本页同步结果
|
* @return 本页同步结果
|
||||||
*/
|
*/
|
||||||
private OaOrgSyncPageVO syncDepartmentFromOaPage(int current, int size, boolean incremental, String modifiedSince) {
|
private OaOrgSyncPageVO syncDepartmentFromOaPage(int current, int size, boolean incremental, String modifiedSince,
|
||||||
|
String companyOaId) {
|
||||||
int pageNo = current < 1 ? 1 : current;
|
int pageNo = current < 1 ? 1 : current;
|
||||||
int pageSize = size < 1 ? 20 : Math.min(size, 200);
|
int pageSize = size < 1 ? 20 : Math.min(size, 200);
|
||||||
String subCompanyIds = getOaSubCompanyIds(true);
|
// 先整理已同步企业列表,再按企业轮询
|
||||||
if (StringUtils.isEmpty(subCompanyIds)) {
|
List<String> companyOaIds = listAllowedCompanyOaIds(true);
|
||||||
|
if (CollectionUtil.isEmpty(companyOaIds)) {
|
||||||
ComposeLogUtil.getLastLog().warn("未查询到已同步的OA公司,跳过部门同步");
|
ComposeLogUtil.getLastLog().warn("未查询到已同步的OA公司,跳过部门同步");
|
||||||
OaOrgSyncPageVO emptyPageVO = buildOrgSyncPageVO("department", pageNo, pageSize, 0L, 0, OrgSyncCount.empty());
|
OaOrgSyncPageVO emptyPageVO = buildOrgSyncPageVO("department", pageNo, pageSize, 0L, 0, OrgSyncCount.empty());
|
||||||
emptyPageVO.setFinished(true);
|
emptyPageVO.setFinished(true);
|
||||||
|
emptyPageVO.setCompanyFinished(true);
|
||||||
|
emptyPageVO.setCompanyIndex(0);
|
||||||
|
emptyPageVO.setCompanyTotal(0);
|
||||||
if (!incremental) {
|
if (!incremental) {
|
||||||
emptyPageVO.setDumpFilePath(oaSyncRawDumpHelper.finishOrgFullDump());
|
emptyPageVO.setDumpFilePath(oaSyncRawDumpHelper.finishOrgFullDump());
|
||||||
}
|
}
|
||||||
return emptyPageVO;
|
return emptyPageVO;
|
||||||
}
|
}
|
||||||
|
int companyIndex = 0;
|
||||||
|
String currentCompanyOaId = companyOaIds.get(0);
|
||||||
|
if (StringUtils.isNotBlank(companyOaId)) {
|
||||||
|
int foundIndex = companyOaIds.indexOf(companyOaId.trim());
|
||||||
|
if (foundIndex >= 0) {
|
||||||
|
companyIndex = foundIndex;
|
||||||
|
currentCompanyOaId = companyOaIds.get(foundIndex);
|
||||||
|
} else {
|
||||||
|
ComposeLogUtil.getLastLog().warn("回传公司oaId不在待同步列表中,回退第一家 companyOaId={}", companyOaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
IncrementalStart incrementalStart = resolveIncrementalStart(incremental, modifiedSince, DeptCategory.DEPT);
|
IncrementalStart incrementalStart = resolveIncrementalStart(incremental, modifiedSince, DeptCategory.DEPT);
|
||||||
OADepartmentSearch departmentSearch = buildDepartmentSearch(incrementalStart.getStartTime(), subCompanyIds);
|
// 查询部门时只传当前公司的 oa_id
|
||||||
|
OADepartmentSearch departmentSearch = buildDepartmentSearch(incrementalStart.getStartTime(), currentCompanyOaId);
|
||||||
departmentSearch.setCurPage(pageNo);
|
departmentSearch.setCurPage(pageNo);
|
||||||
departmentSearch.setPageSize(pageSize);
|
departmentSearch.setPageSize(pageSize);
|
||||||
|
ComposeLogUtil.getLastLog().info("按公司查询OA部门 companyOaId={} companyIndex={}/{} page={}/{}",
|
||||||
|
currentCompanyOaId, companyIndex + 1, companyOaIds.size(), pageNo, pageSize);
|
||||||
OAResponse<OADepartmentResponse> oaResponse = oaClient.queryDepartmentPage(new OASearch<>(departmentSearch));
|
OAResponse<OADepartmentResponse> oaResponse = oaClient.queryDepartmentPage(new OASearch<>(departmentSearch));
|
||||||
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
|
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
|
||||||
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(oaResponse));
|
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 companyOaId={} {}",
|
||||||
|
currentCompanyOaId, JSON.toJSONString(oaResponse));
|
||||||
throw new ServiceException("调用OA接口查询部门信息失败");
|
throw new ServiceException("调用OA接口查询部门信息失败");
|
||||||
}
|
}
|
||||||
OAResponseData<OADepartmentResponse> responseData = oaResponse.getData();
|
OAResponseData<OADepartmentResponse> responseData = oaResponse.getData();
|
||||||
@@ -437,12 +457,26 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
}
|
}
|
||||||
OrgSyncCount syncCount = handleDept(oaDepartments, true);
|
OrgSyncCount syncCount = handleDept(oaDepartments, true);
|
||||||
if (CollectionUtil.isNotEmpty(syncCount.getNotHandledList())) {
|
if (CollectionUtil.isNotEmpty(syncCount.getNotHandledList())) {
|
||||||
ComposeLogUtil.getLastLog().warn("同步部门,本页未处理数据:{}", JSON.toJSONString(syncCount.getNotHandledList()));
|
ComposeLogUtil.getLastLog().warn("同步部门,本页未处理数据 companyOaId={}:{}",
|
||||||
|
currentCompanyOaId, JSON.toJSONString(syncCount.getNotHandledList()));
|
||||||
}
|
}
|
||||||
OaOrgSyncPageVO pageVO = buildOrgSyncPageVO("department", pageNo, pageSize, totalSize, oaDepartments.size(), syncCount);
|
OaOrgSyncPageVO pageVO = buildOrgSyncPageVO("department", pageNo, pageSize, totalSize, oaDepartments.size(), syncCount);
|
||||||
pageVO.setModifiedSince(incrementalStart.getModifiedSince());
|
pageVO.setModifiedSince(incrementalStart.getModifiedSince());
|
||||||
if (Boolean.TRUE.equals(pageVO.getFinished())) {
|
pageVO.setCompanyOaId(currentCompanyOaId);
|
||||||
// 部门分批同步完成后,清理白名单外组织、重建层级、清理无法挂载数据并刷新祖级列表
|
pageVO.setCompanyIndex(companyIndex + 1);
|
||||||
|
pageVO.setCompanyTotal(companyOaIds.size());
|
||||||
|
boolean companyPageFinished = Boolean.TRUE.equals(pageVO.getFinished());
|
||||||
|
if (companyPageFinished) {
|
||||||
|
if (companyIndex + 1 < companyOaIds.size()) {
|
||||||
|
// 当前公司部门已拉完,进入下一公司(整体未结束,不重建层级)
|
||||||
|
pageVO.setCompanyFinished(true);
|
||||||
|
pageVO.setNextCompanyOaId(companyOaIds.get(companyIndex + 1));
|
||||||
|
pageVO.setFinished(false);
|
||||||
|
} else {
|
||||||
|
// 全部公司轮询完成:清理白名单外组织、重建层级、清理无法挂载数据并刷新祖级列表
|
||||||
|
pageVO.setCompanyFinished(true);
|
||||||
|
pageVO.setNextCompanyOaId(null);
|
||||||
|
pageVO.setFinished(true);
|
||||||
this.removeOutsideWhitelistOrgs();
|
this.removeOutsideWhitelistOrgs();
|
||||||
deptService.rebuildOaHierarchy();
|
deptService.rebuildOaHierarchy();
|
||||||
deptService.removeOaOrphansWithoutParent();
|
deptService.removeOaOrphansWithoutParent();
|
||||||
@@ -451,11 +485,16 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
pageVO.setDumpFilePath(oaSyncRawDumpHelper.finishOrgFullDump());
|
pageVO.setDumpFilePath(oaSyncRawDumpHelper.finishOrgFullDump());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
pageVO.setCompanyFinished(false);
|
||||||
|
pageVO.setNextCompanyOaId(currentCompanyOaId);
|
||||||
|
pageVO.setFinished(false);
|
||||||
|
}
|
||||||
CacheUtil.clear(SYS_CACHE);
|
CacheUtil.clear(SYS_CACHE);
|
||||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||||
ComposeLogUtil.getLastLog().info("OA部门分页同步完成 mode={} {}/{},成功{},跳过{}",
|
ComposeLogUtil.getLastLog().info("OA部门分页同步完成 mode={} companyOaId={} company={}/{} page={}/{},成功{},跳过{}",
|
||||||
incremental ? "incremental" : "full", pageNo, totalSize,
|
incremental ? "incremental" : "full", currentCompanyOaId, companyIndex + 1, companyOaIds.size(),
|
||||||
syncCount.getSyncedCount(), syncCount.getSkippedCount());
|
pageNo, totalSize, syncCount.getSyncedCount(), syncCount.getSkippedCount());
|
||||||
return pageVO;
|
return pageVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,14 +644,14 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
* 组装 OA 部门分页查询参数
|
* 组装 OA 部门分页查询参数
|
||||||
*
|
*
|
||||||
* @param startTime 增量查询开始时间
|
* @param startTime 增量查询开始时间
|
||||||
* @param subCompanyIds 子公司 id 列表
|
* @param companyOaId 当前公司 OA 主键(单次只传一家公司)
|
||||||
* @return 查询参数
|
* @return 查询参数
|
||||||
*/
|
*/
|
||||||
private OADepartmentSearch buildDepartmentSearch(Date startTime, String subCompanyIds) {
|
private OADepartmentSearch buildDepartmentSearch(Date startTime, String companyOaId) {
|
||||||
OADepartmentSearch departmentSearch = new OADepartmentSearch();
|
OADepartmentSearch departmentSearch = new OADepartmentSearch();
|
||||||
departmentSearch.setCurPage(1);
|
departmentSearch.setCurPage(1);
|
||||||
departmentSearch.setPageSize(20);
|
departmentSearch.setPageSize(20);
|
||||||
departmentSearch.setSubcompanyid1(subCompanyIds);
|
departmentSearch.setSubcompanyid1(companyOaId);
|
||||||
if (startTime != null) {
|
if (startTime != null) {
|
||||||
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||||
}
|
}
|
||||||
@@ -1145,12 +1184,12 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 OA 部门查询使用的子公司 id
|
* 整理部门同步用的公司 OA 主键列表(公司阶段完成后调用)
|
||||||
*
|
*
|
||||||
* @param syncAll true=取已同步的全部 OA 公司;false=仅取字典配置的公司
|
* @param syncAll true=取已同步的全部 OA 公司;false=仅取字典配置的公司
|
||||||
* @return 逗号分隔的 OA 公司 id
|
* @return 白名单内公司 oaId 列表(去重、保序)
|
||||||
*/
|
*/
|
||||||
private String getOaSubCompanyIds(boolean syncAll) {
|
private List<String> listAllowedCompanyOaIds(boolean syncAll) {
|
||||||
List<Dept> companyList;
|
List<Dept> companyList;
|
||||||
if (syncAll) {
|
if (syncAll) {
|
||||||
companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||||
@@ -1161,7 +1200,7 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
} else {
|
} else {
|
||||||
Set<String> companyNames = this.getCompanyNames();
|
Set<String> companyNames = this.getCompanyNames();
|
||||||
if (companyNames.isEmpty()) {
|
if (companyNames.isEmpty()) {
|
||||||
return null;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
companyList = deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||||
.in(Dept::getDeptName, companyNames)
|
.in(Dept::getDeptName, companyNames)
|
||||||
@@ -1170,20 +1209,17 @@ public class OASyncServiceImpl implements IOASyncService {
|
|||||||
.ne(Dept::getOaId, "")
|
.ne(Dept::getOaId, "")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Map<String, Dept> companyByOaId = new HashMap<>();
|
Map<String, Dept> companyByOaId = new LinkedHashMap<>();
|
||||||
for (Dept company : companyList) {
|
for (Dept company : companyList) {
|
||||||
String oaId = this.resolveOaCompanyId(company);
|
String oaId = this.resolveOaCompanyId(company);
|
||||||
if (StringUtils.isNotBlank(oaId)) {
|
if (StringUtils.isNotBlank(oaId)) {
|
||||||
companyByOaId.put(oaId, company);
|
companyByOaId.putIfAbsent(oaId, company);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<String> allowedCompanyOaIds = resolveAllowedCompanyOaIds(companyByOaId);
|
Set<String> allowedCompanyOaIds = resolveAllowedCompanyOaIds(companyByOaId);
|
||||||
return companyList.stream()
|
return companyByOaId.keySet().stream()
|
||||||
.map(this::resolveOaCompanyId)
|
|
||||||
.filter(StringUtils::isNotBlank)
|
|
||||||
.filter(allowedCompanyOaIds::contains)
|
.filter(allowedCompanyOaIds::contains)
|
||||||
.distinct()
|
.collect(Collectors.toList());
|
||||||
.collect(Collectors.joining(","));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+20
-4
@@ -1,6 +1,7 @@
|
|||||||
package org.springblade.transport.mk;
|
package org.springblade.transport.mk;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.transport.pojo.dto.BillPaymentStatusRequest;
|
import org.springblade.transport.pojo.dto.BillPaymentStatusRequest;
|
||||||
@@ -270,6 +271,7 @@ public final class MkProcessBizHandlers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@TenantIgnore
|
@TenantIgnore
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -290,6 +292,12 @@ public final class MkProcessBizHandlers {
|
|||||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||||
FormalSettlement entity = service.getById(id);
|
FormalSettlement entity = service.getById(id);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
|
log.warn("MK同步当前节点失败,正式结算单不存在 id={}", id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 已终态单据禁止再写回审批中,避免流程结束后的节点刷新把状态打回 reviewing
|
||||||
|
if ("approved".equals(entity.getApprovalStatus()) || "returned".equals(entity.getApprovalStatus())
|
||||||
|
|| "voided".equals(entity.getApprovalStatus())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||||
@@ -310,8 +318,12 @@ public final class MkProcessBizHandlers {
|
|||||||
@Override
|
@Override
|
||||||
public boolean approveFromProcess(Long id, String processorName) {
|
public boolean approveFromProcess(Long id, String processorName) {
|
||||||
FormalSettlement entity = service.getById(id);
|
FormalSettlement entity = service.getById(id);
|
||||||
if (entity == null || "approved".equals(entity.getApprovalStatus())) {
|
if (entity == null) {
|
||||||
return entity != null;
|
log.error("MK审批通过同步失败,正式结算单不存在 id={}", id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ("approved".equals(entity.getApprovalStatus())) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// 统一走 service.approve:与手动审批同一入口(补变更记录),审批事务提交后自动触发金蝶推送事件;
|
// 统一走 service.approve:与手动审批同一入口(补变更记录),审批事务提交后自动触发金蝶推送事件;
|
||||||
// 仅审批中单据允许通过,重复回调被状态校验拒绝,不重复推送
|
// 仅审批中单据允许通过,重复回调被状态校验拒绝,不重复推送
|
||||||
@@ -330,8 +342,12 @@ public final class MkProcessBizHandlers {
|
|||||||
@Override
|
@Override
|
||||||
public boolean rejectFromProcess(Long id, String processorName) {
|
public boolean rejectFromProcess(Long id, String processorName) {
|
||||||
FormalSettlement entity = service.getById(id);
|
FormalSettlement entity = service.getById(id);
|
||||||
if (entity == null || "returned".equals(entity.getApprovalStatus())) {
|
if (entity == null) {
|
||||||
return entity != null;
|
log.error("MK审批驳回同步失败,正式结算单不存在 id={}", id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ("returned".equals(entity.getApprovalStatus())) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
entity.setApprovalStatus("returned");
|
entity.setApprovalStatus("returned");
|
||||||
entity.setCurrentNode("审核不通过");
|
entity.setCurrentNode("审核不通过");
|
||||||
|
|||||||
+75
-28
@@ -4,10 +4,12 @@ import com.alibaba.fastjson2.JSON;
|
|||||||
import jakarta.annotation.PreDestroy;
|
import jakarta.annotation.PreDestroy;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springblade.core.tenant.TenantUtil;
|
||||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||||
import org.springblade.core.tool.api.FR;
|
import org.springblade.core.tool.api.FR;
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.process.feign.IBusinessProcessClient;
|
import org.springblade.process.feign.IBusinessProcessClient;
|
||||||
|
import org.springblade.process.pojo.vo.MkFormBindVO;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -69,9 +71,9 @@ public class MkProcessMessageService {
|
|||||||
log.warn("公开页流程消息未找到 processId,跳过查询当前节点 bizType={}", bizType);
|
log.warn("公开页流程消息未找到 processId,跳过查询当前节点 bizType={}", bizType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String bizIdText = firstText(asMap(body == null ? null : body.get("formData")), "id", "formInstanceId");
|
Long bizId = resolveBizId(asMap(body == null ? null : body.get("formData")));
|
||||||
queryCurrentNodes(bizType, processId, loginName);
|
queryCurrentNodes(bizType, processId, loginName);
|
||||||
scheduleProcessInfo(handler, bizType, processId, loginName, bizIdText, formValues);
|
scheduleProcessInfo(handler, bizType, processId, loginName, bizId, formValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queryCurrentNodes(String bizType, String processId, String loginName) {
|
private void queryCurrentNodes(String bizType, String processId, String loginName) {
|
||||||
@@ -86,36 +88,35 @@ public class MkProcessMessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleProcessInfo(IMkProcessBizHandler handler, String bizType, String processId,
|
private void scheduleProcessInfo(IMkProcessBizHandler handler, String bizType, String processId,
|
||||||
String loginName, String bizIdText, Map<String, Object> formValues) {
|
String loginName, Long bizId, Map<String, Object> formValues) {
|
||||||
Map<String, Object> formValuesCopy = new HashMap<>(formValues == null ? Map.of() : formValues);
|
Map<String, Object> formValuesCopy = new HashMap<>(formValues == null ? Map.of() : formValues);
|
||||||
log.info("公开页将在{}秒后查询流程实例详情 bizType={} processId={}", PROCESS_INFO_DELAY_SECONDS, bizType, processId);
|
log.info("公开页将在{}秒后查询流程实例详情 bizType={} processId={} bizId={}",
|
||||||
|
PROCESS_INFO_DELAY_SECONDS, bizType, processId, bizId);
|
||||||
|
// 延迟任务跑在独立线程,请求态 @TenantIgnore 已丢失,必须显式 ignore,否则 getById 可能因租户条件查空
|
||||||
scheduler.schedule(
|
scheduler.schedule(
|
||||||
() -> queryProcessInfo(handler, bizType, processId, loginName, bizIdText, formValuesCopy),
|
() -> TenantUtil.ignore(() -> queryProcessInfo(handler, bizType, processId, loginName, bizId, formValuesCopy)),
|
||||||
PROCESS_INFO_DELAY_SECONDS,
|
PROCESS_INFO_DELAY_SECONDS,
|
||||||
TimeUnit.SECONDS
|
TimeUnit.SECONDS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queryProcessInfo(IMkProcessBizHandler handler, String bizType, String processId,
|
private void queryProcessInfo(IMkProcessBizHandler handler, String bizType, String processId,
|
||||||
String loginName, String bizIdText, Map<String, Object> formValues) {
|
String loginName, Long bizId, Map<String, Object> formValues) {
|
||||||
try {
|
try {
|
||||||
FR<Object> processInfoResult = businessProcessClient.getProcessInfo(processId, loginName);
|
FR<Object> processInfoResult = businessProcessClient.getProcessInfo(processId, loginName);
|
||||||
Object processInfo = processInfoResult == null ? null : processInfoResult.getData();
|
Object processInfo = processInfoResult == null ? null : processInfoResult.getData();
|
||||||
log.info("公开页流程消息流程实例详情 bizType={} processId={} loginName={} result={}",
|
log.info("公开页流程消息流程实例详情 bizType={} processId={} loginName={} bizId={} result={}",
|
||||||
bizType, processId, loginName, JSON.toJSONString(processInfo));
|
bizType, processId, loginName, bizId, JSON.toJSONString(processInfo));
|
||||||
if (isProcessFinished(processInfo)) {
|
if (isProcessFinished(processInfo)) {
|
||||||
applyProcessResult(handler, bizType, bizIdText, processId, formValues, true);
|
applyProcessResult(handler, bizType, bizId, processId, formValues, true);
|
||||||
} else if (MkProcessNodeHelper.isProcessRejected(processInfo)) {
|
} else if (MkProcessNodeHelper.isProcessRejected(processInfo)) {
|
||||||
applyProcessResult(handler, bizType, bizIdText, processId, formValues, false);
|
applyProcessResult(handler, bizType, bizId, processId, formValues, false);
|
||||||
} else if (StringUtil.isNotBlank(bizIdText)) {
|
} else if (bizId != null) {
|
||||||
try {
|
handler.syncFromProcessInfo(bizId, processInfo);
|
||||||
handler.syncFromProcessInfo(Long.valueOf(bizIdText), processInfo);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
log.warn("公开页流程消息业务id格式不正确 bizType={} id={}", bizType, bizIdText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("公开页查询流程实例详情失败 bizType={} processId={} loginName={}", bizType, processId, loginName, e);
|
log.error("公开页查询流程实例详情失败 bizType={} processId={} loginName={} bizId={}",
|
||||||
|
bizType, processId, loginName, bizId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,21 +124,24 @@ public class MkProcessMessageService {
|
|||||||
return "30".equals(firstText(asMap(processInfo), "fdProcessStatus"));
|
return "30".equals(firstText(asMap(processInfo), "fdProcessStatus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyProcessResult(IMkProcessBizHandler handler, String bizType, String bizIdText,
|
private void applyProcessResult(IMkProcessBizHandler handler, String bizType, Long bizId,
|
||||||
String processId, Map<String, Object> formValues, boolean approved) {
|
String processId, Map<String, Object> formValues, boolean approved) {
|
||||||
String action = approved ? "审核通过" : "审核驳回";
|
String action = approved ? "审核通过" : "审核驳回";
|
||||||
if (StringUtil.isBlank(bizIdText)) {
|
if (bizId == null) {
|
||||||
log.warn("流程{}但未找到业务id,跳过同步 bizType={} processId={}", action, bizType, processId);
|
log.warn("流程{}但未解析到业务主键,跳过同步 bizType={} processId={}", action, bizType, processId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String processorName = firstText(formValues, "mkUserName", "mkLoginName");
|
String processorName = firstText(formValues, "mkUserName", "mkLoginName");
|
||||||
Long bizId = Long.valueOf(bizIdText);
|
boolean synced = approved
|
||||||
if (approved) {
|
? handler.approveFromProcess(bizId, processorName)
|
||||||
handler.approveFromProcess(bizId, processorName);
|
: handler.rejectFromProcess(bizId, processorName);
|
||||||
} else {
|
if (!synced) {
|
||||||
handler.rejectFromProcess(bizId, processorName);
|
log.error("流程{}同步业务状态失败(业务单据不存在或未更新) bizType={} bizId={} processId={}",
|
||||||
|
action, bizType, bizId, processId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
log.info("流程{}同步业务状态成功 bizType={} bizId={} processId={}", action, bizType, bizId, processId);
|
||||||
try {
|
try {
|
||||||
FR<Boolean> markResult = businessProcessClient.markProcessFinished(processId, approved);
|
FR<Boolean> markResult = businessProcessClient.markProcessFinished(processId, approved);
|
||||||
log.info("公开页标记业务流程完结 bizType={} processId={} approved={} result={}",
|
log.info("公开页标记业务流程完结 bizType={} processId={} approved={} result={}",
|
||||||
@@ -146,13 +150,56 @@ public class MkProcessMessageService {
|
|||||||
log.error("公开页标记业务流程完结失败 bizType={} processId={} approved={}",
|
log.error("公开页标记业务流程完结失败 bizType={} processId={} approved={}",
|
||||||
bizType, processId, approved, markException);
|
bizType, processId, approved, markException);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
log.warn("流程{}但业务id格式不正确 bizType={} id={}", action, bizType, bizIdText);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("流程{}同步业务状态失败 bizType={} id={} processId={}", action, bizType, bizIdText, processId, e);
|
log.error("流程{}同步业务状态失败 bizType={} bizId={} processId={}", action, bizType, bizId, processId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公开页 formData 里的 id / formInstanceId 可能是表单实例 id(与业务主键不同),
|
||||||
|
* 须先按 MkFormBind 反查业务主键,与 detail 接口保持一致。
|
||||||
|
*/
|
||||||
|
private Long resolveBizId(Map<String, Object> formData) {
|
||||||
|
String formInstanceIdText = firstText(formData, "formInstanceId");
|
||||||
|
String idText = firstText(formData, "id");
|
||||||
|
Long resolved = resolveByFormInstanceId(formInstanceIdText);
|
||||||
|
if (resolved != null) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
resolved = resolveByFormInstanceId(idText);
|
||||||
|
if (resolved != null) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
if (StringUtil.isNotBlank(idText)) {
|
||||||
|
try {
|
||||||
|
return Long.valueOf(idText);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("公开页流程消息业务id格式不正确 id={}", idText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolveByFormInstanceId(String formInstanceIdText) {
|
||||||
|
if (StringUtil.isBlank(formInstanceIdText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Long formInstanceId = Long.valueOf(formInstanceIdText);
|
||||||
|
FR<MkFormBindVO> result = businessProcessClient.resolveByFormInstanceId(formInstanceId);
|
||||||
|
if (result != null && result.isSuccess() && result.getData() != null && result.getData().getBizId() != null) {
|
||||||
|
Long bizId = result.getData().getBizId();
|
||||||
|
log.info("公开页按表单实例id解析业务主键 formInstanceId={} bizId={}", formInstanceId, bizId);
|
||||||
|
return bizId;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("公开页表单实例id格式不正确 formInstanceId={}", formInstanceIdText);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("公开页按表单实例id解析业务主键失败 formInstanceId={}", formInstanceIdText, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<String, Object> asMap(Object value) {
|
private static Map<String, Object> asMap(Object value) {
|
||||||
if (!(value instanceof Map<?, ?> map)) {
|
if (!(value instanceof Map<?, ?> map)) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
|
|||||||
Reference in New Issue
Block a user