package cn.iocoder.yudao.module.finance.service.subject; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectPageReqVO; import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectSaveReqVO; import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO; import cn.iocoder.yudao.module.finance.dal.mysql.subject.FinAccountSubjectMapper; import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherEntryMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.*; /** * 会计科目 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class FinAccountSubjectServiceImpl implements FinAccountSubjectService { @Resource private FinAccountSubjectMapper accountSubjectMapper; @Resource private FinVoucherEntryMapper voucherEntryMapper; @Override public Long createAccountSubject(FinAccountSubjectSaveReqVO createReqVO) { // 1. 校验科目编码唯一 validateSubjectCodeUnique(null, createReqVO.getSubjectCode()); // 2. 校验上级科目 validateParent(createReqVO.getParentId(), null); // 3. 插入 FinAccountSubjectDO subject = BeanUtils.toBean(createReqVO, FinAccountSubjectDO.class); accountSubjectMapper.insert(subject); return subject.getId(); } @Override public void updateAccountSubject(FinAccountSubjectSaveReqVO updateReqVO) { // 1. 校验存在 validateAccountSubjectExists(updateReqVO.getId()); // 2. 校验科目编码唯一 validateSubjectCodeUnique(updateReqVO.getId(), updateReqVO.getSubjectCode()); // 3. 校验上级科目(不能选自己,也不能选自己的下级) validateParent(updateReqVO.getParentId(), updateReqVO.getId()); // 4. 更新 accountSubjectMapper.updateById(BeanUtils.toBean(updateReqVO, FinAccountSubjectDO.class)); } @Override public void deleteAccountSubject(Long id) { FinAccountSubjectDO subject = validateAccountSubjectExists(id); // 1. 校验是否存在下级科目 List children = accountSubjectMapper.selectListByParentId(id); if (!children.isEmpty()) { throw exception(ACCOUNT_SUBJECT_HAS_CHILDREN, subject.getSubjectName(), children.size()); } // 2. 校验是否被凭证分录引用 Long referencedCount = voucherEntryMapper.selectCountBySubjectCodes(List.of(subject.getSubjectCode())); if (referencedCount != null && referencedCount > 0) { throw exception(ACCOUNT_SUBJECT_REFERENCED, subject.getSubjectName(), referencedCount); } // 3. 删除 accountSubjectMapper.deleteById(id); } @Override @Transactional(rollbackFor = Exception.class) public void deleteAccountSubjectList(List ids) { ids.forEach(this::deleteAccountSubject); } private FinAccountSubjectDO validateAccountSubjectExists(Long id) { FinAccountSubjectDO subject = accountSubjectMapper.selectById(id); if (subject == null) { throw exception(ACCOUNT_SUBJECT_NOT_EXISTS); } return subject; } private void validateSubjectCodeUnique(Long id, String subjectCode) { FinAccountSubjectDO subject = accountSubjectMapper.selectBySubjectCode(subjectCode); if (subject == null) { return; } if (id == null || !Objects.equals(subject.getId(), id)) { throw exception(ACCOUNT_SUBJECT_CODE_DUPLICATE, subjectCode); } } /** * 校验上级科目:非空时必须存在,且不能是自己或自己的下级(避免形成环) * * @param parentId 上级科目编号 * @param selfId 当前科目编号;新增时传 null */ private void validateParent(Long parentId, Long selfId) { if (parentId == null) { return; } if (Objects.equals(parentId, selfId)) { throw exception(ACCOUNT_SUBJECT_PARENT_SELF); } FinAccountSubjectDO parent = accountSubjectMapper.selectById(parentId); if (parent == null) { throw exception(ACCOUNT_SUBJECT_PARENT_NOT_EXISTS, parentId); } // 自下而上回溯,若遇到自己,说明选择的上级是自己的下级 Set visited = new HashSet<>(); Long cursor = parentId; while (cursor != null && visited.add(cursor)) { if (Objects.equals(cursor, selfId)) { throw exception(ACCOUNT_SUBJECT_PARENT_CYCLE, parent.getSubjectName()); } FinAccountSubjectDO node = accountSubjectMapper.selectById(cursor); cursor = node == null ? null : node.getParentId(); } } @Override public FinAccountSubjectDO getAccountSubject(Long id) { return accountSubjectMapper.selectById(id); } @Override public PageResult getAccountSubjectPage(FinAccountSubjectPageReqVO pageReqVO) { return accountSubjectMapper.selectPage(pageReqVO); } @Override public List getAccountSubjectList() { return accountSubjectMapper.selectListAll(); } @Override public FinAccountSubjectDO getAccountSubjectByCode(String subjectCode) { return accountSubjectMapper.selectBySubjectCode(subjectCode); } @Override public List getAccountSubjectListByCodes(Collection subjectCodes) { return accountSubjectMapper.selectListBySubjectCodes(subjectCodes); } }