diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java index 6f3143a4..e133b6c5 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OASyncServiceImpl.java @@ -263,7 +263,7 @@ public class OASyncServiceImpl implements IOASyncService { } /** - * 从 OA 人员列表同步组织与人员 + * 从 OA 人员列表同步人员并绑定已有组织(不维护组织树,须先同步组织架构) * * @param startTime 增量查询开始时间,为空则全量 * @return 处理的人员数量 @@ -284,7 +284,7 @@ public class OASyncServiceImpl implements IOASyncService { } /** - * 按页从 OA 人员列表同步组织与人员 + * 按页从 OA 人员列表同步人员并绑定已有组织(不维护组织树,须先同步组织架构) * * @param current 当前页 * @param size 每页条数 diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OaUserListSyncHelper.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OaUserListSyncHelper.java index 77ba09d0..87e3cd50 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OaUserListSyncHelper.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/OaUserListSyncHelper.java @@ -30,7 +30,6 @@ import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; -import org.springblade.common.constant.DataStatusEnum; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.utils.DigestUtil; @@ -53,7 +52,6 @@ import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -66,10 +64,9 @@ import java.util.stream.Collectors; import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_PASSWORD; import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_ROLE; import static org.springblade.common.constant.CommonConstant.DEFAULT_ROLE; -import static org.springblade.common.constant.CommonConstant.YES; /** - * 从 OA 人员列表提取组织并同步人员 + * OA 人员同步:绑定已有组织机构,不维护组织树 * * @author Chill */ @@ -86,104 +83,312 @@ public class OaUserListSyncHelper { private final IRoleService roleService; /** - * 从人员数据提取二级公司、三级部门,并挂到集团顶级(id=1/GWWL) 下 + * 加载已有组织索引,并将人员旁路产生的 OAC/OAD 存量归并到正式组织。 + *

人员同步不再新建/更新组织树,须先执行组织架构同步。

* * @param oaPersons OA人员 - * @return 组织索引 + * @return 组织索引(仅含已有组织) */ public OaOrgIndex syncOrgsFromPersons(List oaPersons) { OaOrgIndex orgIndex = new OaOrgIndex(); if (CollectionUtil.isEmpty(oaPersons)) { return orgIndex; } - Date orgSyncStart = new Date(); - Dept rootCompany = this.getOrCreateRootCompany(); - String tenantId = resolveTenantId(); - Map companyPersonMap = new LinkedHashMap<>(); - Map departmentPersonMap = new LinkedHashMap<>(); - for (OAPersonResponse oaPerson : oaPersons) { - String companyKey = resolveCompanyKey(oaPerson); - if (StringUtils.isNotBlank(companyKey) && StringUtils.isNotBlank(oaPerson.getSubcompanyname())) { - companyPersonMap.putIfAbsent(companyKey, oaPerson); - } - String departmentKey = resolveDepartmentKey(oaPerson); - if (StringUtils.isNotBlank(departmentKey) && StringUtils.isNotBlank(oaPerson.getDepartmentname()) - && StringUtils.isNotBlank(oaPerson.getSubcompanyname())) { - departmentPersonMap.putIfAbsent(departmentKey, oaPerson); - } - } - - Map existingCompanyByCode = new HashMap<>(); - Map existingCompanyByName = new HashMap<>(); - deptService.list(Wrappers.lambdaQuery() - .eq(Dept::getParentId, rootCompany.getId()) - .eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode()) - ).forEach(dept -> { - if (StringUtils.isNotBlank(dept.getDeptCode())) { - existingCompanyByCode.put(dept.getDeptCode(), dept); - } - if (StringUtils.isNotBlank(dept.getDeptName())) { - existingCompanyByName.put(dept.getDeptName(), dept); - } - }); - List addCompanies = new ArrayList<>(); - List updateCompanies = new ArrayList<>(); - companyPersonMap.forEach((companyKey, oaPerson) -> { - String oaCode = buildCompanyCode(oaPerson, companyKey); - Dept existing = existingCompanyByCode.get(oaCode); - if (existing == null) { - existing = existingCompanyByName.get(oaPerson.getSubcompanyname()); - } - Dept company = this.upsertOrg(oaPerson.getSubcompanyname(), oaCode, existing, rootCompany, tenantId, - DeptCategory.COMPANY, addCompanies, updateCompanies); - orgIndex.companyByOaId.put(companyKey, company); - }); - this.saveOrgs(addCompanies, updateCompanies, DeptCategory.COMPANY); - - Map existingDeptByCode = new HashMap<>(); - Map existingDeptByParentAndName = new HashMap<>(); - List companyIds = orgIndex.companyByOaId.values().stream() - .map(Dept::getId) - .filter(Objects::nonNull) - .distinct() - .toList(); - if (CollectionUtil.isNotEmpty(companyIds)) { - deptService.list(Wrappers.lambdaQuery() - .in(Dept::getParentId, companyIds) - .eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode()) - ).forEach(dept -> { - if (StringUtils.isNotBlank(dept.getDeptCode())) { - existingDeptByCode.put(dept.getDeptCode(), dept); - } - if (dept.getParentId() != null && StringUtils.isNotBlank(dept.getDeptName())) { - existingDeptByParentAndName.put(dept.getParentId() + "#" + dept.getDeptName(), dept); - } - }); - } - List addDepartments = new ArrayList<>(); - List updateDepartments = new ArrayList<>(); - departmentPersonMap.forEach((departmentKey, oaPerson) -> { - Dept parentCompany = orgIndex.findCompany(oaPerson); - if (parentCompany == null || parentCompany.getId() == null) { - return; - } - String oaCode = buildDepartmentCode(oaPerson, departmentKey); - Dept existing = existingDeptByCode.get(oaCode); - if (existing == null) { - existing = existingDeptByParentAndName.get(parentCompany.getId() + "#" + oaPerson.getDepartmentname()); - } - Dept department = this.upsertOrg(oaPerson.getDepartmentname(), oaCode, existing, parentCompany, tenantId, - DeptCategory.DEPT, addDepartments, updateDepartments); - orgIndex.deptByOaId.put(departmentKey, department); - orgIndex.deptByCompanyAndName.put(resolveCompanyKey(oaPerson) + "#" + oaPerson.getDepartmentname(), department); - }); - this.saveOrgs(addDepartments, updateDepartments, DeptCategory.DEPT); - deptService.updateAncestors(orgSyncStart); - ComposeLogUtil.getLastLog().info("同步人员提取组织完成,二级公司{}个,三级部门{}个", + this.repairPersonBypassOrgs(); + this.loadExistingOrgIndex(orgIndex); + ComposeLogUtil.getLastLog().info("人员同步绑定已有组织,二级公司{}个,三级部门{}个", orgIndex.companyByOaId.size(), orgIndex.deptByOaId.size()); return orgIndex; } + /** + * 将 OAC/OAD 旁路节点上的人员关系迁移到同 oa_id 的正式组织,并逻辑删除旁路节点 + */ + private void repairPersonBypassOrgs() { + List companyList = deptService.list(Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())); + List departmentList = deptService.list(Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode())); + Map officialCompanyByOaId = new HashMap<>(); + Map officialDeptByOaId = new HashMap<>(); + Map companyById = new HashMap<>(); + for (Dept company : companyList) { + if (company.getId() != null) { + companyById.put(company.getId(), company); + } + if (!isPersonBypassCode(company.getDeptCode(), DeptCategory.COMPANY)) { + this.putPreferredByOaId(officialCompanyByOaId, company, DeptCategory.COMPANY); + } + } + for (Dept department : departmentList) { + if (!isPersonBypassCode(department.getDeptCode(), DeptCategory.DEPT)) { + this.putPreferredByOaId(officialDeptByOaId, department, DeptCategory.DEPT); + } + } + + Set softDeleteIds = new HashSet<>(); + Set affectedUserIds = new HashSet<>(); + int migratedDeptBindingCount = 0; + int migratedCompanyBindingCount = 0; + + for (Dept bypass : departmentList) { + if (!isPersonBypassCode(bypass.getDeptCode(), DeptCategory.DEPT) || bypass.getId() == null) { + continue; + } + String oaId = StringUtils.trimToNull(bypass.getOaId()); + if (oaId == null) { + oaId = extractOaIdFromBypassCode(bypass.getDeptCode(), DeptCategory.DEPT); + } + if (oaId == null) { + continue; + } + Dept official = officialDeptByOaId.get(oaId); + if (official == null || official.getId() == null || Objects.equals(official.getId(), bypass.getId()) + || isPersonBypassCode(official.getDeptCode(), DeptCategory.DEPT)) { + continue; + } + Dept officialCompany = resolveBelongCompany(official, companyById); + migratedDeptBindingCount += this.migrateUserDeptBindings(bypass, official, officialCompany, affectedUserIds); + softDeleteIds.add(bypass.getId()); + } + + for (Dept bypass : companyList) { + if (!isPersonBypassCode(bypass.getDeptCode(), DeptCategory.COMPANY) || bypass.getId() == null) { + continue; + } + String oaId = StringUtils.trimToNull(bypass.getOaId()); + if (oaId == null) { + oaId = extractOaIdFromBypassCode(bypass.getDeptCode(), DeptCategory.COMPANY); + } + if (oaId == null) { + continue; + } + Dept official = officialCompanyByOaId.get(oaId); + if (official == null || official.getId() == null || Objects.equals(official.getId(), bypass.getId()) + || isPersonBypassCode(official.getDeptCode(), DeptCategory.COMPANY)) { + continue; + } + migratedCompanyBindingCount += this.migrateUserCompanyBindings(bypass, official, affectedUserIds); + long childCount = deptService.count(Wrappers.lambdaQuery().eq(Dept::getParentId, bypass.getId())); + if (childCount == 0) { + softDeleteIds.add(bypass.getId()); + } else { + ComposeLogUtil.getLastLog().warn("旁路公司仍有子节点,暂不删除 companyId={} deptCode={} childCount={}", + bypass.getId(), bypass.getDeptCode(), childCount); + } + } + + if (CollectionUtil.isNotEmpty(softDeleteIds)) { + deptService.removeByIds(softDeleteIds); + ComposeLogUtil.getLastLog().info("人员同步归并旁路组织完成,迁移部门关系{}条,迁移公司关系{}条,逻辑删除旁路节点{}个", + migratedDeptBindingCount, migratedCompanyBindingCount, softDeleteIds.size()); + } else if (migratedDeptBindingCount > 0 || migratedCompanyBindingCount > 0) { + ComposeLogUtil.getLastLog().info("人员同步归并旁路组织完成,迁移部门关系{}条,迁移公司关系{}条", + migratedDeptBindingCount, migratedCompanyBindingCount); + } + this.refreshUserDeptFields(affectedUserIds); + } + + /** + * 仅加载已有组织,按 oa_id / 名称建立索引供人员绑定 + */ + private void loadExistingOrgIndex(OaOrgIndex orgIndex) { + Map companyByOaId = new HashMap<>(); + Map companyByName = new HashMap<>(); + Map companyById = new HashMap<>(); + deptService.list(Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode()) + ).forEach(dept -> { + if (dept.getId() != null) { + companyById.put(dept.getId(), dept); + } + this.putPreferredByOaKey(companyByOaId, resolveOrgOaKey(dept, DeptCategory.COMPANY), dept, DeptCategory.COMPANY); + if (StringUtils.isNotBlank(dept.getDeptName())) { + Dept previous = companyByName.get(dept.getDeptName()); + if (previous == null + || (isPersonBypassCode(previous.getDeptCode(), DeptCategory.COMPANY) + && !isPersonBypassCode(dept.getDeptCode(), DeptCategory.COMPANY))) { + companyByName.put(dept.getDeptName(), dept); + } + } + }); + companyByOaId.forEach((oaId, dept) -> orgIndex.companyByOaId.put(oaId, dept)); + companyByName.forEach((name, dept) -> orgIndex.companyByOaId.putIfAbsent("NAME:" + name, dept)); + + Map deptByOaId = new HashMap<>(); + deptService.list(Wrappers.lambdaQuery() + .eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode()) + ).forEach(dept -> { + this.putPreferredByOaKey(deptByOaId, resolveOrgOaKey(dept, DeptCategory.DEPT), dept, DeptCategory.DEPT); + Dept parentCompany = resolveBelongCompany(dept, companyById); + if (parentCompany == null || StringUtils.isBlank(dept.getDeptName())) { + return; + } + String companyKey = resolveOrgOaKey(parentCompany, DeptCategory.COMPANY); + if (companyKey == null && StringUtils.isNotBlank(parentCompany.getDeptName())) { + companyKey = "NAME:" + parentCompany.getDeptName(); + } + if (companyKey == null) { + return; + } + String nameKey = companyKey + "#" + dept.getDeptName(); + Dept previous = orgIndex.deptByCompanyAndName.get(nameKey); + if (previous == null + || (isPersonBypassCode(previous.getDeptCode(), DeptCategory.DEPT) + && !isPersonBypassCode(dept.getDeptCode(), DeptCategory.DEPT))) { + orgIndex.deptByCompanyAndName.put(nameKey, dept); + } + }); + orgIndex.deptByOaId.putAll(deptByOaId); + } + + /** 优先取 oa_id;旁路空 oa_id 时从 OAC/OAD 编码后缀解析 */ + private String resolveOrgOaKey(Dept dept, DeptCategory deptCategory) { + if (dept == null) { + return null; + } + String oaId = StringUtils.trimToNull(dept.getOaId()); + if (oaId != null) { + return oaId; + } + return extractOaIdFromBypassCode(dept.getDeptCode(), deptCategory); + } + + private int migrateUserDeptBindings(Dept bypass, Dept official, Dept officialCompany, Set affectedUserIds) { + List bindingList = userDeptService.list(Wrappers.lambdaQuery() + .and(wrapper -> wrapper.eq(UserDept::getDeptId, bypass.getId()) + .or().eq(UserDept::getDeptCode, bypass.getDeptCode()))); + if (CollectionUtil.isEmpty(bindingList)) { + return 0; + } + Set userIds = bindingList.stream() + .map(UserDept::getUserId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Map existingByKey = userDeptService.list(Wrappers.lambdaQuery() + .in(UserDept::getUserId, userIds) + ).stream() + .collect(Collectors.toMap(this::getUserDeptKey, userDept -> userDept, (first, second) -> first)); + List updateList = new ArrayList<>(); + List removeIds = new ArrayList<>(); + for (UserDept binding : bindingList) { + if (binding.getUserId() == null) { + continue; + } + affectedUserIds.add(binding.getUserId()); + String companyCode = officialCompany != null ? officialCompany.getDeptCode() : binding.getCompanyCode(); + String companyName = officialCompany != null ? officialCompany.getDeptName() : binding.getCompanyName(); + String newKey = binding.getUserId() + StringUtils.defaultString(companyCode) + + StringUtils.defaultString(official.getDeptCode()); + UserDept alreadyOfficial = existingByKey.get(newKey); + if (alreadyOfficial != null && !Objects.equals(alreadyOfficial.getId(), binding.getId())) { + removeIds.add(binding.getId()); + continue; + } + binding.setDeptId(official.getId()); + binding.setDeptCode(official.getDeptCode()); + binding.setDeptName(official.getDeptName()); + if (officialCompany != null) { + binding.setCompanyCode(companyCode); + binding.setCompanyName(companyName); + } + updateList.add(binding); + existingByKey.put(newKey, binding); + } + if (CollectionUtil.isNotEmpty(updateList)) { + userDeptService.updateBatchById(updateList); + } + if (CollectionUtil.isNotEmpty(removeIds)) { + userDeptService.removeByIds(removeIds); + } + return updateList.size() + removeIds.size(); + } + + private int migrateUserCompanyBindings(Dept bypass, Dept official, Set affectedUserIds) { + if (StringUtils.isBlank(bypass.getDeptCode())) { + return 0; + } + List bindingList = userDeptService.list(Wrappers.lambdaQuery() + .eq(UserDept::getCompanyCode, bypass.getDeptCode())); + if (CollectionUtil.isEmpty(bindingList)) { + return 0; + } + List updateList = new ArrayList<>(); + for (UserDept binding : bindingList) { + if (binding.getUserId() != null) { + affectedUserIds.add(binding.getUserId()); + } + binding.setCompanyCode(official.getDeptCode()); + binding.setCompanyName(official.getDeptName()); + updateList.add(binding); + } + userDeptService.updateBatchById(updateList); + return updateList.size(); + } + + private void refreshUserDeptFields(Set userIds) { + if (CollectionUtil.isEmpty(userIds)) { + return; + } + List userDeptIdsList = userDeptService.queryUserDeptIds(userIds); + List updateUserParams = userDeptIdsList.stream() + .filter(userDeptIds -> StringUtils.isNotBlank(userDeptIds.getDeptIds())) + .filter(userDeptIds -> { + int deptIdLength = userDeptIds.getDeptIds().length(); + int deptCodesLength = StringUtils.length(userDeptIds.getDeptCodes()); + return deptIdLength <= MAX_DEPT_ID_LENGTH && deptCodesLength <= MAX_DEPT_ID_LENGTH; + }) + .map(userDeptIds -> { + User user = new User(); + user.setId(userDeptIds.getUserId()); + user.setDeptId(userDeptIds.getDeptIds()); + user.setDeptCodes(userDeptIds.getDeptCodes()); + return user; + }).toList(); + if (CollectionUtil.isNotEmpty(updateUserParams)) { + userService.updateBatchById(updateUserParams); + } + } + + private Dept resolveBelongCompany(Dept department, Map companyById) { + if (department == null) { + return null; + } + if (department.getParentId() != null) { + Dept parent = companyById.get(department.getParentId()); + if (parent != null) { + return parent; + } + } + if (StringUtils.isNotBlank(department.getBelongCompanyCode())) { + return companyById.values().stream() + .filter(company -> department.getBelongCompanyCode().equals(company.getDeptCode())) + .findFirst() + .orElse(null); + } + return null; + } + + /** + * 从 OAC37 / OAD37 旁路编码解析 OA 主键;哈希旁路(OACNxxx)返回 null + */ + private String extractOaIdFromBypassCode(String deptCode, DeptCategory deptCategory) { + if (StringUtils.isBlank(deptCode) || !isPersonBypassCode(deptCode, deptCategory)) { + return null; + } + String prefix = DeptCategory.COMPANY.equals(deptCategory) + ? OAConvertConstant.COMPANY_OA_PREFIX + : OAConvertConstant.DEPARTMENT_OA_PREFIX; + if (deptCode.length() <= prefix.length()) { + return null; + } + String suffix = deptCode.substring(prefix.length()); + if (suffix.isEmpty() || suffix.startsWith("N")) { + return null; + } + return suffix; + } + /** * 同步人员并绑定到三级部门 * @@ -395,73 +600,47 @@ public class OaUserListSyncHelper { return localModified.equals(remoteModified); } - private Dept getOrCreateRootCompany() { - return deptService.ensureOaSyncRootDept(); + private boolean isPersonBypassCode(String deptCode, DeptCategory deptCategory) { + if (StringUtils.isBlank(deptCode)) { + return true; + } + if (DeptCategory.COMPANY.equals(deptCategory)) { + return deptCode.startsWith(OAConvertConstant.COMPANY_OA_PREFIX); + } + return deptCode.startsWith(OAConvertConstant.DEPARTMENT_OA_PREFIX); } - private Dept upsertOrg(String name, String oaCode, Dept existing, Dept parent, String tenantId, - DeptCategory deptCategory, List addList, List updateList) { - if (existing != null) { - Dept updateParam = new Dept(); - updateParam.setId(existing.getId()); - updateParam.setDeptName(name); - updateParam.setFullName(name); - updateParam.setShortName(name); - updateParam.setParentId(parent.getId()); - updateParam.setParentCode(parent.getDeptCode()); - updateParam.setAncestors(buildAncestors(parent)); - updateParam.setIsOa(YES); - updateParam.setSyncTime(new Date()); - updateList.add(updateParam); - existing.setDeptName(name); - existing.setFullName(name); - existing.setShortName(name); - existing.setParentId(parent.getId()); - existing.setParentCode(parent.getDeptCode()); - existing.setAncestors(updateParam.getAncestors()); - return existing; + /** 同一 oa_id 多条时优先正式编码,避免人员挂到 OAC/OAD 旁路重复节点。 */ + private void putPreferredByOaId(Map byOaId, Dept dept, DeptCategory deptCategory) { + if (dept == null || StringUtils.isBlank(dept.getOaId())) { + return; } - Dept dept = this.buildOrgDept(name, oaCode, parent, tenantId, deptCategory); - dept.setId(DefaultIdentifierGenerator.getInstance().nextId(null)); - addList.add(dept); - return dept; + this.putPreferredByOaKey(byOaId, dept.getOaId().trim(), dept, deptCategory); } - private void saveOrgs(List addList, List updateList, DeptCategory deptCategory) { - if (CollectionUtil.isNotEmpty(addList)) { - ComposeLogUtil.getLastLog().info("批量新增{}:{}", deptCategory.getName(), addList.size()); - deptService.saveBatch(addList); + private void putPreferredByOaKey(Map byOaId, String oaKey, Dept dept, DeptCategory deptCategory) { + if (dept == null || StringUtils.isBlank(oaKey)) { + return; } - if (CollectionUtil.isNotEmpty(updateList)) { - ComposeLogUtil.getLastLog().info("批量修改{}:{}", deptCategory.getName(), updateList.size()); - deptService.updateBatchById(updateList); + Dept previous = byOaId.get(oaKey); + if (previous == null) { + byOaId.put(oaKey, dept); + return; + } + boolean previousBypass = isPersonBypassCode(previous.getDeptCode(), deptCategory); + boolean currentBypass = isPersonBypassCode(dept.getDeptCode(), deptCategory); + if (previousBypass && !currentBypass) { + byOaId.put(oaKey, dept); } - } - - private Dept buildOrgDept(String name, String deptCode, Dept parent, String tenantId, DeptCategory deptCategory) { - Dept dept = new Dept(); - dept.setTenantId(tenantId); - dept.setParentId(parent.getId()); - dept.setParentCode(parent.getDeptCode()); - dept.setAncestors(this.buildAncestors(parent)); - dept.setDeptName(name); - dept.setFullName(name); - dept.setShortName(name); - dept.setDeptCode(deptCode); - dept.setBelongCompanyCode(DeptCategory.COMPANY.equals(deptCategory) ? deptCode : parent.getBelongCompanyCode()); - dept.setDeptCategory(deptCategory.getCode()); - dept.setSort(0); - dept.setStatus(DataStatusEnum.ENABLE.getCode()); - dept.setIsDeleted(BladeConstant.DB_NOT_DELETED); - dept.setIsOa(YES); - dept.setIsPlatformCompany(0); - dept.setSyncTime(new Date()); - return dept; } private UserDept buildUserDept(OAPersonResponse oaPerson, Map userMap, OaOrgIndex orgIndex) { Dept department = orgIndex.findDept(oaPerson); if (department == null || department.getId() == null) { + ComposeLogUtil.getLastLog().warn( + "人员未匹配到已有部门,请先同步组织架构 account={} departmentid={} departmentname={} subcompanyid1={}", + userConvert.resolveAccount(oaPerson), oaPerson.getDepartmentid(), oaPerson.getDepartmentname(), + oaPerson.getSubcompanyid1()); return null; } UserDept userDept = userConvert.person2userDept(oaPerson, userMap); @@ -493,55 +672,6 @@ public class OaUserListSyncHelper { return roleList.get(0).getId().toString(); } - private String resolveCompanyKey(OAPersonResponse oaPerson) { - if (oaPerson == null) { - return null; - } - if (StringUtils.isNotBlank(oaPerson.getSubcompanyid1())) { - return oaPerson.getSubcompanyid1().trim(); - } - if (StringUtils.isNotBlank(oaPerson.getSubcompanyname())) { - return "NAME:" + oaPerson.getSubcompanyname().trim(); - } - return null; - } - - private String resolveDepartmentKey(OAPersonResponse oaPerson) { - if (oaPerson == null) { - return null; - } - if (StringUtils.isNotBlank(oaPerson.getDepartmentid())) { - return oaPerson.getDepartmentid().trim(); - } - String companyKey = resolveCompanyKey(oaPerson); - if (StringUtils.isNotBlank(companyKey) && StringUtils.isNotBlank(oaPerson.getDepartmentname())) { - return companyKey + ":" + oaPerson.getDepartmentname().trim(); - } - return null; - } - - private String buildCompanyCode(OAPersonResponse oaPerson, String companyKey) { - if (StringUtils.isNotBlank(oaPerson.getSubcompanyid1())) { - return OAConvertConstant.COMPANY_OA_PREFIX + oaPerson.getSubcompanyid1().trim(); - } - return OAConvertConstant.COMPANY_OA_PREFIX + "N" + Math.abs(companyKey.hashCode()); - } - - private String buildDepartmentCode(OAPersonResponse oaPerson, String departmentKey) { - if (StringUtils.isNotBlank(oaPerson.getDepartmentid())) { - return OAConvertConstant.DEPARTMENT_OA_PREFIX + oaPerson.getDepartmentid().trim(); - } - return OAConvertConstant.DEPARTMENT_OA_PREFIX + "N" + Math.abs(departmentKey.hashCode()); - } - - private String buildAncestors(Dept parent) { - String ancestors = parent.getAncestors(); - if (StringUtils.isBlank(ancestors)) { - ancestors = String.valueOf(BladeConstant.TOP_PARENT_ID); - } - return ancestors + "," + parent.getId(); - } - private String resolveTenantId() { String tenantId = AuthUtil.getTenantId(); if (StringUtils.isBlank(tenantId)) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/BillPaymentMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/BillPaymentMapper.java index 2b2db978..ca20187c 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/BillPaymentMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/BillPaymentMapper.java @@ -27,9 +27,20 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.BillPayment; /** 汇票付款 Mapper。 @author Chill */ @Mapper public interface BillPaymentMapper extends BaseMapper { + + /** + * 查询指定租户、指定日期前缀下已使用的最大汇票付款流水号。 + * 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(payment_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_bill_payment WHERE tenant_id = #{tenantId} " + + "AND payment_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementMapper.java index 36f22485..2c01c226 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementMapper.java @@ -29,4 +29,13 @@ public interface FormalSettlementMapper extends BaseMapper { /** 恢复逻辑删除的正式结算单主记录。 */ @Update("UPDATE blade_formal_settlement SET is_deleted = 0 WHERE tenant_id = #{tenantId} AND id = #{id} AND is_deleted = 1") int restoreByIdIncludingDeleted(@Param("tenantId") String tenantId, @Param("id") Long id); + + /** + * 查询指定租户、指定日期前缀下已使用的最大正式结算单流水号。 + * 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(formal_settlement_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_formal_settlement WHERE tenant_id = #{tenantId} " + + "AND formal_settlement_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementPaymentMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementPaymentMapper.java index fe41e31b..2e0e61a1 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementPaymentMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/FormalSettlementPaymentMapper.java @@ -10,9 +10,20 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.FormalSettlementPayment; /** 正式结算付款申请 Mapper。 @author Chill */ @Mapper public interface FormalSettlementPaymentMapper extends BaseMapper { + + /** + * 查询指定租户、指定日期前缀下已使用的最大付款申请流水号。 + * 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(payment_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_formal_settlement_payment WHERE tenant_id = #{tenantId} " + + "AND payment_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/InvoiceApplicationMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/InvoiceApplicationMapper.java index 9e9db78c..4bcca990 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/InvoiceApplicationMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/InvoiceApplicationMapper.java @@ -27,6 +27,8 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.InvoiceApplication; /** @@ -36,4 +38,13 @@ import org.springblade.transport.pojo.entity.InvoiceApplication; */ @Mapper public interface InvoiceApplicationMapper extends BaseMapper { + + /** + * 查询指定租户、指定日期前缀下已使用的最大开票申请流水号。 + * 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(application_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_invoice_application WHERE tenant_id = #{tenantId} " + + "AND application_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationMapper.java index 770dc202..f1507f68 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PaymentApplicationMapper.java @@ -21,9 +21,20 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.PaymentApplication; /** 付款申请 Mapper。 @author Chill */ @Mapper public interface PaymentApplicationMapper extends BaseMapper { + + /** + * 查询指定租户、指定日期前缀下已使用的最大付款申请流水号。 + * 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(payment_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_payment_application WHERE tenant_id = #{tenantId} " + + "AND payment_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementAdvanceMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementAdvanceMapper.java index c8bb1558..734364da 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementAdvanceMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementAdvanceMapper.java @@ -10,6 +10,8 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.PreSettlementAdvance; /** @@ -19,4 +21,12 @@ import org.springblade.transport.pojo.entity.PreSettlementAdvance; */ @Mapper public interface PreSettlementAdvanceMapper extends BaseMapper { + + /** + * 查询预付单号最大流水(含逻辑删除)。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(advance_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_pre_settlement_advance WHERE tenant_id = #{tenantId} " + + "AND advance_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementMapper.java index c2b86484..2fbe0db4 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/PreSettlementMapper.java @@ -10,6 +10,8 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.PreSettlement; /** @@ -19,4 +21,20 @@ import org.springblade.transport.pojo.entity.PreSettlement; */ @Mapper public interface PreSettlementMapper extends BaseMapper { + + /** + * 查询预结算单号最大流水(含逻辑删除)。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(pre_settlement_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_pre_settlement WHERE tenant_id = #{tenantId} " + + "AND pre_settlement_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxPreSettlementDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); + + /** + * 查询预结算转正式结算单号最大流水(含逻辑删除)。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(formal_settlement_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_pre_settlement WHERE tenant_id = #{tenantId} " + + "AND formal_settlement_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxFormalSettlementDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/SettlementAdjustmentMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/SettlementAdjustmentMapper.java index 1a2b5629..9a7b7e64 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/SettlementAdjustmentMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/SettlementAdjustmentMapper.java @@ -2,7 +2,19 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.springblade.transport.pojo.entity.SettlementAdjustment; @Mapper -public interface SettlementAdjustmentMapper extends BaseMapper {} +public interface SettlementAdjustmentMapper extends BaseMapper { + + /** + * 查询指定租户、指定日期前缀下已使用的最大结算调整单流水号。 + * 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。 + */ + @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(adjustment_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) " + + "FROM blade_settlement_adjustment WHERE tenant_id = #{tenantId} " + + "AND adjustment_no LIKE CONCAT(#{prefix}, '%')") + Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix); +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportReconciliationMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportReconciliationMapper.java index e808e682..db46b800 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportReconciliationMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/TransportReconciliationMapper.java @@ -22,4 +22,13 @@ public interface TransportReconciliationMapper extends BaseMapperlambdaQuery() - .likeRight(BillPayment::getPaymentNo, prefix)) + 1); + Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%05d", (maxSequence == null ? 0L : maxSequence) + 1); } private BigDecimal positive(BigDecimal value, String name) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index cbfdb8e0..83462702 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -286,21 +286,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl throw new ServiceException("结算类型不正确"); }; String prefix = code + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); - List numbers = list(Wrappers.lambdaQuery() - .select(FormalSettlement::getFormalSettlementNo) - .likeRight(FormalSettlement::getFormalSettlementNo, prefix)) - .stream().map(FormalSettlement::getFormalSettlementNo).toList(); - int sequence = 1; - if (!numbers.isEmpty()) { - String latest = numbers.stream().max(String::compareTo).orElse(prefix); - try { - sequence = Integer.parseInt(latest.substring(prefix.length())) + 1; - } catch (RuntimeException ignored) { - sequence = Math.toIntExact(count(Wrappers.lambdaQuery() - .likeRight(FormalSettlement::getFormalSettlementNo, prefix))) + 1; - } - } - return prefix + String.format("%05d", sequence); + Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%05d", (maxSequence == null ? 0L : maxSequence) + 1); } @Override @@ -2190,11 +2177,11 @@ public class FormalSettlementServiceImpl extends BaseServiceImpllambdaQuery() - .likeRight(FormalSettlementPayment::getPaymentNo, prefix)); - return prefix + String.format("%04d", count + 1); + Long maxSequence = paymentMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%04d", (maxSequence == null ? 0L : maxSequence) + 1); } private BigDecimal money(BigDecimal value) { return (value == null ? BigDecimal.ZERO : value).setScale(2, RoundingMode.HALF_UP); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java index 10b197a8..6ca7eaef 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java @@ -1679,10 +1679,11 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpllambdaQuery() - .likeRight(InvoiceApplication::getApplicationNo, prefix)) + 1); + Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%05d", (maxSequence == null ? 0L : maxSequence) + 1); } private String normalizeDepartmentEmails(String value, List invoiceInfos) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java index 6b827dd5..9a3af520 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java @@ -2455,7 +2455,14 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpllambdaQuery().likeRight(PaymentApplication::getPaymentNo, prefix)) + 1); } + /** + * 生成当日付款申请单号;按最大流水号 +1,且计入逻辑删除数据,避免撞 uk_payment_application_no + */ + private synchronized String nextNo() { + String prefix = "FKD" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); + Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%05d", (maxSequence == null ? 0L : maxSequence) + 1); + } private BigDecimal nonNegative(BigDecimal value, String name) { if (value == null || value.compareTo(BigDecimal.ZERO) < 0) throw new ServiceException(name + "不能小于0"); return value; } private BigDecimal money(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } private String limit(String value, int length) { if (value != null && value.length() > length) throw new ServiceException("备注不能超过" + length + "个字符"); return value; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java index 354ec65f..ff913107 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PreSettlementServiceImpl.java @@ -85,7 +85,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -1780,44 +1779,25 @@ public class PreSettlementServiceImpl extends BaseServiceImpllambdaQuery() - .likeRight(PreSettlementAdvance::getAdvanceNo, prefix) - .orderByDesc(PreSettlementAdvance::getAdvanceNo) - .last("limit 1")); - return prefix + String.format("%05d", nextSequence(latest == null ? null : latest.getAdvanceNo(), prefix)); - } - - private String nextDailyCode(String code, Function getter) { - String prefix = code + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); - PreSettlement latest = list(Wrappers.lambdaQuery() - .and(query -> query.likeRight(PreSettlement::getPreSettlementNo, prefix) - .or().likeRight(PreSettlement::getFormalSettlementNo, prefix)) - .orderByDesc(PreSettlement::getCreateTime) - .last("limit 1")).stream().max(Comparator.comparing(item -> { - String value = getter.apply(item); - return value == null ? "" : value; - })).orElse(null); - String latestCode = latest == null ? null : getter.apply(latest); - return prefix + String.format("%05d", nextSequence(latestCode, prefix)); - } - - private int nextSequence(String latestCode, String prefix) { - if (latestCode == null || !latestCode.startsWith(prefix)) return 1; - try { - return Integer.parseInt(latestCode.substring(prefix.length())) + 1; - } catch (NumberFormatException exception) { - return 1; - } + Long maxSequence = advanceMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%05d", (maxSequence == null ? 0L : maxSequence) + 1); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/SettlementAdjustmentServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/SettlementAdjustmentServiceImpl.java index 8dda1c1c..d6dd4a95 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/SettlementAdjustmentServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/SettlementAdjustmentServiceImpl.java @@ -392,7 +392,12 @@ public class SettlementAdjustmentServiceImpl extends BaseServiceImpllambdaQuery().likeRight(SettlementAdjustment::getAdjustmentNo, prefix)); return prefix + String.format("%04d", count + 1); } + /** 生成当日结算调整单号;按最大流水号 +1,且计入逻辑删除数据 */ + private synchronized String nextNo() { + String prefix = "TZ" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); + Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + return prefix + String.format("%04d", (maxSequence == null ? 0L : maxSequence) + 1); + } private BigDecimal money(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } private String requiredText(String value, String field, int max) { if (Func.isEmpty(value)) throw new ServiceException(field + "不能为空"); return limit(value.trim(), max); } private String limit(String value, int max) { if (value != null && value.length() > max) throw new ServiceException("内容不能超过" + max + "个字"); return value; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java index dff1f39b..065ec3b9 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/TransportReconciliationServiceImpl.java @@ -1512,20 +1512,14 @@ public class TransportReconciliationServiceImpl private BigDecimal sumExternalQuantity(List rows) { return rows.stream().map(TransportReconciliationExternal::getTransportQuantity).map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add); } private BigDecimal sumInternalAmount(List rows) { return rows.stream().map(TransportReconciliationInternal::getSettlementAmount).map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add); } private BigDecimal sumExternalAmount(List rows) { return rows.stream().map(TransportReconciliationExternal::getSettlementAmount).map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add); } + /** 生成当日运输对账单号;按最大流水号 +1,且计入逻辑删除数据 */ private synchronized String nextNo() { String prefix = "DZD" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); - int sequence = list(Wrappers.lambdaQuery() - .select(TransportReconciliation::getReconciliationNo) - .likeRight(TransportReconciliation::getReconciliationNo, prefix)) - .stream() - .map(TransportReconciliation::getReconciliationNo) - .filter(number -> number != null && number.length() == prefix.length() + 5) - .map(number -> number.substring(prefix.length())) - .filter(suffix -> suffix.chars().allMatch(Character::isDigit)) - .mapToInt(Integer::parseInt) - .max() - .orElse(0) + 1; - if (sequence > 99999) throw new ServiceException("当日运输对账单号流水已用完"); + Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix); + long sequence = (maxSequence == null ? 0L : maxSequence) + 1; + if (sequence > 99999) { + throw new ServiceException("当日运输对账单号流水已用完"); + } return prefix + String.format("%05d", sequence); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java index 0f92b9b4..2d52773c 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillImportBatchServiceImpl.java @@ -25,6 +25,7 @@ import org.springblade.system.cache.UserCache; import org.springblade.system.feign.ISysClient; import org.springblade.system.pojo.entity.DictBiz; import org.springblade.system.pojo.entity.CargoType; +import org.springblade.system.pojo.entity.MeasurementUnit; import org.springblade.transport.excel.WaybillImportBatchExcel; import org.springblade.transport.mapper.WaybillImportBatchMapper; import org.springblade.transport.pojo.dto.WaybillImportBatchRequest; @@ -919,13 +920,14 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl row, List quantityUnitOptions, List errors) { - String quantityUnit = stringValue(row, "quantityUnit", "数量单位"); + String quantityUnit = stringValue(row, "quantityUnit", "数量单位", "*数量单位"); if (Func.isEmpty(quantityUnit)) { return; } - - if (!quantityUnitOptions.contains(quantityUnit)) { - errors.add("数量单位必须为系统枚举值之一"); + String normalized = quantityUnit.trim(); + boolean matched = quantityUnitOptions.stream().anyMatch(option -> option.equalsIgnoreCase(normalized)); + if (!matched) { + errors.add("数量单位「" + normalized + "」未在计量单位主数据中匹配"); } } @@ -1186,9 +1188,28 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl loadQuantityUnitOptions() { - // 常见数量单位 - return List.of("吨", "千克", "立方米", "件", "箱", "台", "个", "升", "米", "平方米"); + R> response = sysClient.getMeasurementUnits(); + if (response == null || !response.isSuccess() || response.getData() == null) { + throw new ServiceException("计量单位基础数据读取失败,请稍后重试"); + } + Set options = new HashSet<>(); + for (MeasurementUnit unit : response.getData()) { + if (unit == null || (unit.getStatus() != null && !Objects.equals(unit.getStatus(), 1))) { + continue; + } + if (Func.isNotEmpty(unit.getUnitName())) { + options.add(unit.getUnitName().trim()); + } + if (Func.isNotEmpty(unit.getUnitCode())) { + options.add(unit.getUnitCode().trim()); + } + } + if (options.isEmpty()) { + throw new ServiceException("计量单位主数据为空,请先在基础数据中配置计量单位"); + } + return new ArrayList<>(options); } /**