From 1942fb9f1a4dd6a90daca24e3a093c138e0e48c3 Mon Sep 17 00:00:00 2001
From: zss <zss@example.com>
Date: 星期二, 12 五月 2026 16:40:23 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro

---
 src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java |  362 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 341 insertions(+), 21 deletions(-)

diff --git a/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java
index 152966a..37bf64b 100644
--- a/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/AccountSubjectServiceImpl.java
@@ -10,6 +10,7 @@
 import com.ruoyi.account.mapper.AccountSubjectMapper;
 import com.ruoyi.account.pojo.AccountSubject;
 import com.ruoyi.account.service.AccountSubjectService;
+import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import jakarta.servlet.http.HttpServletResponse;
@@ -18,7 +19,16 @@
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -37,30 +47,78 @@
 
     @Override
     public IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) {
-        LambdaQueryWrapper<AccountSubject> queryWrapper = new LambdaQueryWrapper<>();
-        if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectCode())) {
-            queryWrapper.like(AccountSubject::getSubjectCode, accountSubjectDto.getSubjectCode());
-        }
-        if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectName())) {
-            queryWrapper.like(AccountSubject::getSubjectName, accountSubjectDto.getSubjectName());
-        }
-        if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectType())) {
-            queryWrapper.eq(AccountSubject::getSubjectType, accountSubjectDto.getSubjectType());
-        }
-        queryWrapper.orderByDesc(AccountSubject::getId);
+        Page<AccountSubjectDto> requestPage = page == null ? new Page<>(1, 10) : page;
+        List<AccountSubject> allSubjects = list(loadBaseQueryWrapper(accountSubjectDto));
+        List<AccountSubject> filteredSubjects = applyTreeFilter(allSubjects, accountSubjectDto);
+        List<AccountSubjectVo> fullTree = buildTree(filteredSubjects);
 
-        Page<AccountSubject> entityPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
-        Page<AccountSubject> paramPage = page(entityPage, queryWrapper);
+        long current = requestPage.getCurrent() <= 0 ? 1 : requestPage.getCurrent();
+        long size = requestPage.getSize() <= 0 ? 10 : requestPage.getSize();
+        int fromIndex = (int) Math.min((current - 1) * size, fullTree.size());
+        int toIndex = (int) Math.min(fromIndex + size, fullTree.size());
+        List<AccountSubjectVo> pagedRoots = fromIndex >= toIndex
+                ? Collections.emptyList()
+                : fullTree.subList(fromIndex, toIndex);
 
-        Page<AccountSubjectVo> resultPage = new Page<>(paramPage.getCurrent(), paramPage.getSize(), paramPage.getTotal());
-        List<AccountSubjectVo> records = new ArrayList<>(paramPage.getRecords().size());
-        for (AccountSubject item : paramPage.getRecords()) {
-            AccountSubjectVo vo = new AccountSubjectVo();
-            BeanUtils.copyProperties(item, vo);
-            records.add(vo);
-        }
-        resultPage.setRecords(records);
+        Page<AccountSubjectVo> resultPage = new Page<>(current, size, fullTree.size());
+        resultPage.setRecords(pagedRoots);
         return resultPage;
+    }
+
+    @Override
+    public Boolean saveAccountSubject(AccountSubjectDto accountSubjectDto) {
+        validateSubjectRequiredFields(accountSubjectDto);
+        validateSubjectCodeUnique(accountSubjectDto, false);
+        validateParent(accountSubjectDto.getParentId(), null);
+        if (accountSubjectDto.getStatus() == null) {
+            accountSubjectDto.setStatus(0);
+        }
+        return save(accountSubjectDto);
+    }
+
+    @Override
+    public Boolean updateAccountSubject(AccountSubjectDto accountSubjectDto) {
+        if (accountSubjectDto == null || accountSubjectDto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岀鐩甀D涓嶈兘涓虹┖");
+        }
+        if (getById(accountSubjectDto.getId()) == null) {
+            throw new ServiceException("淇敼澶辫触锛屾湭鎵惧埌瀵瑰簲绉戠洰");
+        }
+        validateParent(accountSubjectDto.getParentId(), accountSubjectDto.getId());
+        validateSubjectRequiredFields(accountSubjectDto);
+        validateSubjectCodeUnique(accountSubjectDto, true);
+        return updateById(accountSubjectDto);
+    }
+
+    @Override
+    public Boolean removeAccountSubjectByIds(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return true;
+        }
+        List<AccountSubject> allSubjects = list();
+        if (allSubjects == null || allSubjects.isEmpty()) {
+            return true;
+        }
+        Map<Long, List<Long>> childrenIdMap = buildChildrenIdMap(allSubjects);
+        Set<Long> removeIds = new LinkedHashSet<>();
+        for (Long id : ids) {
+            collectDescendantIds(id, childrenIdMap, removeIds);
+        }
+        if (removeIds.isEmpty()) {
+            return true;
+        }
+        List<String> subjectCodes = allSubjects.stream()
+                .filter(subject -> removeIds.contains(subject.getId()))
+                .map(AccountSubject::getSubjectCode)
+                .filter(StringUtils::isNotEmpty)
+                .collect(Collectors.toList());
+        if (!subjectCodes.isEmpty()) {
+            Long referencedCount = accountSubjectMapper.countReferencedBySubjectCodes(subjectCodes);
+            if (referencedCount != null && referencedCount > 0) {
+                throw new ServiceException("鍒犻櫎澶辫触锛岀鐩凡琚嚟璇佸垎褰曞紩鐢�");
+            }
+        }
+        return removeByIds(removeIds);
     }
 
     @Override
@@ -74,4 +132,266 @@
         ExcelUtil<AccountSubjectImportDto> util = new ExcelUtil<>(AccountSubjectImportDto.class);
         util.exportExcel(response, importDtos , "鎬昏处绉戠洰");
     }
+
+    /**
+     * 鏍¢獙绉戠洰蹇呭~瀛楁锛岄伩鍏嶈剰鏁版嵁鍐欏叆銆�
+     */
+    private void validateSubjectRequiredFields(AccountSubjectDto accountSubjectDto) {
+        if (accountSubjectDto == null) {
+            throw new ServiceException("鎬昏处绉戠洰鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(accountSubjectDto.getSubjectCode())) {
+            throw new ServiceException("绉戠洰缂栫爜涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(accountSubjectDto.getSubjectName())) {
+            throw new ServiceException("绉戠洰鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(accountSubjectDto.getSubjectType())) {
+            throw new ServiceException("绉戠洰绫诲瀷涓嶈兘涓虹┖");
+        }
+    }
+
+    /**
+     * 鏍¢獙绉戠洰缂栫爜鍞竴锛屾柊澧炲拰淇敼閮借鎵ц銆�
+     */
+    private void validateSubjectCodeUnique(AccountSubjectDto accountSubjectDto, boolean isUpdate) {
+        LambdaQueryWrapper<AccountSubject> codeQueryWrapper = new LambdaQueryWrapper<>();
+        codeQueryWrapper.eq(AccountSubject::getSubjectCode, accountSubjectDto.getSubjectCode());
+        if (isUpdate) {
+            codeQueryWrapper.ne(AccountSubject::getId, accountSubjectDto.getId());
+        }
+        AccountSubject exists = getOne(codeQueryWrapper, false);
+        if (Objects.nonNull(exists)) {
+            throw new ServiceException("绉戠洰缂栫爜宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+        }
+    }
+
+    /**
+     * 浠呮寜閫氱敤杩囨护鏉′欢鏌ヨ鍩虹鏁版嵁锛堟爲褰㈣繃婊ゅ悗缁啀鍋氾級銆�
+     */
+    private LambdaQueryWrapper<AccountSubject> loadBaseQueryWrapper(AccountSubjectDto accountSubjectDto) {
+        LambdaQueryWrapper<AccountSubject> queryWrapper = new LambdaQueryWrapper<>();
+        if (accountSubjectDto != null && accountSubjectDto.getStatus() != null) {
+            queryWrapper.eq(AccountSubject::getStatus, accountSubjectDto.getStatus());
+        }
+        queryWrapper.orderByAsc(AccountSubject::getSubjectCode).orderByAsc(AccountSubject::getId);
+        return queryWrapper;
+    }
+
+    /**
+     * 鏍戝舰杩囨护锛氬懡涓妭鐐瑰悗淇濈暀鍏剁埗閾句笌瀛愭爲锛屼繚璇侀�掑綊缁撴瀯瀹屾暣銆�
+     */
+    private List<AccountSubject> applyTreeFilter(List<AccountSubject> allSubjects, AccountSubjectDto queryDto) {
+        if (allSubjects == null || allSubjects.isEmpty()) {
+            return Collections.emptyList();
+        }
+        boolean hasFilter = queryDto != null && (
+                StringUtils.isNotEmpty(queryDto.getSubjectCode())
+                        || StringUtils.isNotEmpty(queryDto.getSubjectName())
+                        || StringUtils.isNotEmpty(queryDto.getSubjectType())
+        );
+        if (!hasFilter) {
+            return allSubjects;
+        }
+
+        Map<Long, AccountSubject> subjectMap = allSubjects.stream()
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(AccountSubject::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
+        Map<Long, List<AccountSubject>> childrenMap = buildChildrenMap(allSubjects);
+
+        Set<Long> matchedIds = new LinkedHashSet<>();
+        for (AccountSubject subject : allSubjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            if (matchesFilter(subject, queryDto)) {
+                matchedIds.add(subject.getId());
+            }
+        }
+        if (matchedIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        Set<Long> resultIds = new LinkedHashSet<>(matchedIds);
+        for (Long matchedId : matchedIds) {
+            addAncestors(matchedId, subjectMap, resultIds);
+            addDescendants(matchedId, childrenMap, resultIds);
+        }
+
+        return allSubjects.stream()
+                .filter(item -> item.getId() != null && resultIds.contains(item.getId()))
+                .collect(Collectors.toList());
+    }
+
+    private boolean matchesFilter(AccountSubject subject, AccountSubjectDto queryDto) {
+        if (queryDto == null) {
+            return true;
+        }
+        if (StringUtils.isNotEmpty(queryDto.getSubjectCode())
+                && (subject.getSubjectCode() == null || !subject.getSubjectCode().contains(queryDto.getSubjectCode()))) {
+            return false;
+        }
+        if (StringUtils.isNotEmpty(queryDto.getSubjectName())
+                && (subject.getSubjectName() == null || !subject.getSubjectName().contains(queryDto.getSubjectName()))) {
+            return false;
+        }
+        if (StringUtils.isNotEmpty(queryDto.getSubjectType())
+                && !queryDto.getSubjectType().equals(subject.getSubjectType())) {
+            return false;
+        }
+        return true;
+    }
+
+    private void addAncestors(Long subjectId, Map<Long, AccountSubject> subjectMap, Set<Long> resultIds) {
+        AccountSubject current = subjectMap.get(subjectId);
+        if (current == null) {
+            return;
+        }
+        Long parentId = current.getParentId();
+        while (parentId != null && parentId > 0) {
+            AccountSubject parent = subjectMap.get(parentId);
+            if (parent == null) {
+                break;
+            }
+            if (!resultIds.add(parent.getId())) {
+                break;
+            }
+            parentId = parent.getParentId();
+        }
+    }
+
+    private void addDescendants(Long subjectId, Map<Long, List<AccountSubject>> childrenMap, Set<Long> resultIds) {
+        List<AccountSubject> children = childrenMap.getOrDefault(subjectId, Collections.emptyList());
+        for (AccountSubject child : children) {
+            if (child.getId() == null) {
+                continue;
+            }
+            if (resultIds.add(child.getId())) {
+                addDescendants(child.getId(), childrenMap, resultIds);
+            }
+        }
+    }
+
+    private Map<Long, List<AccountSubject>> buildChildrenMap(List<AccountSubject> subjects) {
+        Map<Long, List<AccountSubject>> childrenMap = new HashMap<>();
+        for (AccountSubject subject : subjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            Long parentId = subject.getParentId();
+            if (parentId == null || parentId <= 0) {
+                continue;
+            }
+            childrenMap.computeIfAbsent(parentId, key -> new ArrayList<>()).add(subject);
+        }
+        return childrenMap;
+    }
+
+    /**
+     * 鍩轰簬 parentId 閫掑綊鏋勫缓绉戠洰鏍戙��
+     */
+    private List<AccountSubjectVo> buildTree(List<AccountSubject> subjects) {
+        if (subjects == null || subjects.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<AccountSubject> sortedSubjects = new ArrayList<>(subjects);
+        sortedSubjects.sort(Comparator
+                .comparing(AccountSubject::getSubjectCode, Comparator.nullsLast(String::compareTo))
+                .thenComparing(AccountSubject::getId, Comparator.nullsLast(Long::compareTo)));
+
+        Map<Long, AccountSubjectVo> subjectVoMap = new LinkedHashMap<>();
+        for (AccountSubject subject : sortedSubjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            AccountSubjectVo vo = new AccountSubjectVo();
+            BeanUtils.copyProperties(subject, vo);
+            subjectVoMap.put(subject.getId(), vo);
+        }
+
+        List<AccountSubjectVo> roots = new ArrayList<>();
+        for (AccountSubject subject : sortedSubjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            AccountSubjectVo current = subjectVoMap.get(subject.getId());
+            Long parentId = subject.getParentId();
+            if (parentId != null && parentId > 0 && subjectVoMap.containsKey(parentId)) {
+                subjectVoMap.get(parentId).getChildren().add(current);
+            } else {
+                roots.add(current);
+            }
+        }
+
+        markLeafRecursively(roots);
+        return roots;
+    }
+
+    private void markLeafRecursively(List<AccountSubjectVo> nodes) {
+        for (AccountSubjectVo node : nodes) {
+            List<AccountSubjectVo> children = node.getChildren();
+            node.setLeaf(children == null || children.isEmpty());
+            if (children != null && !children.isEmpty()) {
+                markLeafRecursively(children);
+            }
+        }
+    }
+
+    /**
+     * 鏍¢獙鐖跺瓙鍏崇郴锛氱埗鑺傜偣蹇呴』瀛樺湪锛屼笖涓嶈兘褰㈡垚寰幆寮曠敤銆�
+     */
+    private void validateParent(Long parentId, Long currentId) {
+        if (parentId == null || parentId <= 0) {
+            return;
+        }
+        if (currentId != null && parentId.equals(currentId)) {
+            throw new ServiceException("鐖剁鐩笉鑳介�夋嫨鑷韩");
+        }
+        AccountSubject parent = getById(parentId);
+        if (parent == null) {
+            throw new ServiceException("鐖剁鐩笉瀛樺湪锛岃閲嶆柊閫夋嫨");
+        }
+        // 闃叉褰㈡垚鐜細鏇存柊鏃讹紝鐖惰妭鐐逛笉鑳芥槸褰撳墠鑺傜偣鐨勪换鎰忓瓙瀛欒妭鐐广��
+        if (currentId != null) {
+            Set<Long> visited = new HashSet<>();
+            Long traceParentId = parentId;
+            while (traceParentId != null && traceParentId > 0) {
+                if (!visited.add(traceParentId)) {
+                    throw new ServiceException("绉戠洰灞傜骇瀛樺湪寰幆寮曠敤锛岃妫�鏌ョ埗绉戠洰璁剧疆");
+                }
+                if (traceParentId.equals(currentId)) {
+                    throw new ServiceException("鐖剁鐩笉鑳芥槸褰撳墠绉戠洰鎴栧叾瀛愮鐩�");
+                }
+                AccountSubject traceNode = getById(traceParentId);
+                if (traceNode == null) {
+                    break;
+                }
+                traceParentId = traceNode.getParentId();
+            }
+        }
+    }
+
+    private Map<Long, List<Long>> buildChildrenIdMap(List<AccountSubject> subjects) {
+        Map<Long, List<Long>> map = new HashMap<>();
+        for (AccountSubject subject : subjects) {
+            if (subject.getId() == null || subject.getParentId() == null || subject.getParentId() <= 0) {
+                continue;
+            }
+            map.computeIfAbsent(subject.getParentId(), key -> new ArrayList<>()).add(subject.getId());
+        }
+        return map;
+    }
+
+    /**
+     * 鏀堕泦寰呭垹闄よ妭鐐瑰強鍏舵墍鏈夊瓙瀛欒妭鐐广��
+     */
+    private void collectDescendantIds(Long id, Map<Long, List<Long>> childrenIdMap, Set<Long> result) {
+        if (id == null || !result.add(id)) {
+            return;
+        }
+        List<Long> children = childrenIdMap.getOrDefault(id, Collections.emptyList());
+        for (Long childId : children) {
+            collectDescendantIds(childId, childrenIdMap, result);
+        }
+    }
 }

--
Gitblit v1.9.3