diff --git a/src/main/java/cn/lihongjie/coal/common/GroovyScriptUtils.java b/src/main/java/cn/lihongjie/coal/common/GroovyScriptUtils.java index 02cb20b3..d285e656 100644 --- a/src/main/java/cn/lihongjie/coal/common/GroovyScriptUtils.java +++ b/src/main/java/cn/lihongjie/coal/common/GroovyScriptUtils.java @@ -17,11 +17,36 @@ import org.codehaus.groovy.ast.expr.VariableExpression; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; @UtilityClass @Slf4j public class GroovyScriptUtils { + + public String replaceVariable( + String patternstr, String script, Function mapper) { + + Pattern pattern = Pattern.compile(patternstr); + + // 创建 Matcher 对象 + Matcher matcher = pattern.matcher(script); + + // 使用 Map 进行映射和替换 + StringBuffer resultString = new StringBuffer(); + while (matcher.find()) { + String replacement = mapper.apply(matcher.group(matcher.groupCount())); + matcher.appendReplacement( + resultString, + replacement != null ? Matcher.quoteReplacement(replacement) : matcher.group()); + } + matcher.appendTail(resultString); + + return resultString.toString(); + } + public static void validate(String formula) { if (StringUtils.isEmpty(formula)) { return; diff --git a/src/main/java/cn/lihongjie/coal/organization/service/OrganizationService.java b/src/main/java/cn/lihongjie/coal/organization/service/OrganizationService.java index 0e937f9b..db5c5dcb 100644 --- a/src/main/java/cn/lihongjie/coal/organization/service/OrganizationService.java +++ b/src/main/java/cn/lihongjie/coal/organization/service/OrganizationService.java @@ -10,6 +10,7 @@ import cn.lihongjie.coal.organization.dto.UpdateOrganizationDto; import cn.lihongjie.coal.organization.entity.OrganizationEntity; import cn.lihongjie.coal.organization.mapper.OrganizationMapper; import cn.lihongjie.coal.organization.repository.OrganizationRepository; +import cn.lihongjie.coal.salaryItem.service.SalaryItemService; import cn.lihongjie.coal.user.dto.CreateOrgAdminDto; import cn.lihongjie.coal.user.service.UserService; @@ -37,7 +38,7 @@ public class OrganizationService extends BaseService dependOn; + + @Comment("优先级") + private Integer priority; } diff --git a/src/main/java/cn/lihongjie/coal/salaryItem/repository/SalaryItemRepository.java b/src/main/java/cn/lihongjie/coal/salaryItem/repository/SalaryItemRepository.java index d664a900..8968f310 100644 --- a/src/main/java/cn/lihongjie/coal/salaryItem/repository/SalaryItemRepository.java +++ b/src/main/java/cn/lihongjie/coal/salaryItem/repository/SalaryItemRepository.java @@ -5,5 +5,9 @@ import cn.lihongjie.coal.salaryItem.entity.SalaryItemEntity; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository -public interface SalaryItemRepository extends BaseRepository {} +public interface SalaryItemRepository extends BaseRepository { + List findByOrganizationIdAndStatus(String organizationId, Integer status); +} diff --git a/src/main/java/cn/lihongjie/coal/salaryItem/service/SalaryItemService.java b/src/main/java/cn/lihongjie/coal/salaryItem/service/SalaryItemService.java index db1841de..de5c1420 100644 --- a/src/main/java/cn/lihongjie/coal/salaryItem/service/SalaryItemService.java +++ b/src/main/java/cn/lihongjie/coal/salaryItem/service/SalaryItemService.java @@ -2,7 +2,10 @@ package cn.lihongjie.coal.salaryItem.service; import cn.lihongjie.coal.base.dto.CommonQuery; import cn.lihongjie.coal.base.dto.IdRequest; +import cn.lihongjie.coal.base.entity.CommonEntity; import cn.lihongjie.coal.base.service.BaseService; +import cn.lihongjie.coal.common.GroovyScriptUtils; +import cn.lihongjie.coal.exception.BizException; import cn.lihongjie.coal.salaryItem.dto.CreateSalaryItemDto; import cn.lihongjie.coal.salaryItem.dto.SalaryItemDto; import cn.lihongjie.coal.salaryItem.dto.UpdateSalaryItemDto; @@ -12,6 +15,10 @@ import cn.lihongjie.coal.salaryItem.repository.SalaryItemRepository; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jgrapht.graph.DefaultDirectedGraph; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.traverse.TopologicalOrderIterator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.data.domain.Page; @@ -19,6 +26,13 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + @Service @Slf4j public class SalaryItemService extends BaseService { @@ -31,16 +45,122 @@ public class SalaryItemService extends BaseService allItems = + repository.findByOrganizationIdAndStatus(entity.getOrganizationId(), 1); + + Map nameMap = + allItems.stream() + .collect( + Collectors.toMap( + e -> String.format("【%s】", e.getFullName()), e -> e)); + + Set dependsOn = new HashSet<>(); + + Function mapper = + (String s) -> { + if (nameMap.containsKey(s)) { + String code = nameMap.get(s).getCode(); + dependsOn.add(code); + return code; + } + throw new BizException("公式中的依赖项 {} 不存在", s); + }; + // 生成公式 + + String formula = + GroovyScriptUtils.replaceVariable("【(.*?)】", entity.getFormulaShow(), mapper); + + entity.setDependOn(dependsOn.stream().toList()); + + entity.setFormula(formula); + + try { + + GroovyScriptUtils.variables(formula); + } catch (Exception e) { + throw new BizException("无效的公式"); + } + } + + private void syncFormula(SalaryItemEntity entity) { + + DefaultDirectedGraph graph = + new DefaultDirectedGraph<>(DefaultEdge.class); + List all = + repository.findByOrganizationIdAndStatus(entity.getOrganizationId(), 1); + + Set enabledCode = + all.stream().map(sie -> sie.getCode()).collect(Collectors.toSet()); + + for (SalaryItemEntity def : all) { + + if (StringUtils.equalsIgnoreCase(def.getInputType(), "1")) { + + Iterable dependents = def.getDependOn(); + + for (String d : dependents) { + if (!enabledCode.contains(d)) { + throw new BizException("公式中的依赖项 {} 不存在", d); + } + graph.addVertex(def.getCode()); + graph.addVertex(d); + + graph.addEdge(d, def.getCode()); + } + } + } + + Map codeMap = + all.stream().collect(Collectors.toMap(CommonEntity::getCode, e -> e)); + + Function mapper = + (String s) -> { + if (codeMap.containsKey(s)) { + return "【" + codeMap.get(s).getFullName() + "】"; + } + throw new BizException("公式中的依赖项 {} 不存在", s); + }; + // 生成公式 + + TopologicalOrderIterator iterator = + new TopologicalOrderIterator<>(graph); + + int order = 0; + while (iterator.hasNext()) { + Object next = iterator.next(); + + for (var item : all) { + + if (StringUtils.equalsIgnoreCase(item.getCode(), next + "")) { + item.setPriority(order++); + item.setFormulaShow( + GroovyScriptUtils.replaceVariable( + "item\\d+", entity.getFormula(), mapper)); + break; + } + } + } + this.repository.saveAll(all); + } + public SalaryItemDto update(UpdateSalaryItemDto request) { SalaryItemEntity entity = this.repository.get(request.getId()); + + this.mapper.updateEntity(entity, request); + updateField(entity); this.repository.save(entity); + syncFormula(entity); return getById(entity.getId()); } @@ -65,4 +185,20 @@ public class SalaryItemService extends BaseService "1")); + assertEquals( + "1 + 1", GroovyScriptUtils.replaceVariable("\\【(.+?)\\】", "【a】 + 【b】", (s) -> "1")); + assertEquals( + "1 + 1", GroovyScriptUtils.replaceVariable("\\【(.+?)\\】", "1 + 1", (s) -> "1")); + } +}