From dc4f084a89f735307b76a2e4222808d58e1a9043 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期六, 16 五月 2026 11:05:07 +0800
Subject: [PATCH] chore: 配置文件修改

---
 src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java |  231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 231 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java
new file mode 100644
index 0000000..cb7a476
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java
@@ -0,0 +1,231 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.mapper.financial.FinFixedAssetMapper;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import com.ruoyi.account.service.financial.FinFixedAssetService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 鍥哄畾璧勪骇鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinFixedAssetServiceImpl extends ServiceImpl<FinFixedAssetMapper, FinFixedAsset> implements FinFixedAssetService {
+
+    private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
+    private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+    private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    @Override
+    public IPage<FinFixedAsset> pageList(Page<FinFixedAsset> page, FinFixedAssetDto queryDto) {
+        LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetCode())) {
+            wrapper.like(FinFixedAsset::getAssetCode, queryDto.getAssetCode());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetName())) {
+            wrapper.like(FinFixedAsset::getAssetName, queryDto.getAssetName());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCategory())) {
+            wrapper.eq(FinFixedAsset::getCategory, queryDto.getCategory());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+            wrapper.eq(FinFixedAsset::getStatus, queryDto.getStatus());
+        }
+        wrapper.orderByDesc(FinFixedAsset::getId);
+        return page(page, wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(FinFixedAssetDto dto) {
+        validateForSave(dto, false);
+        if (StringUtils.isEmpty(dto.getAssetCode())) {
+            dto.setAssetCode(generateAssetCode());
+        }
+        BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+        dto.setResidualRate(residualRate);
+        BigDecimal accumulatedDepreciation = defaultMoney(dto.getAccumulatedDepreciation());
+        dto.setAccumulatedDepreciation(accumulatedDepreciation);
+        dto.setNetValue(calculateNetValue(dto.getOriginalValue(), accumulatedDepreciation));
+        if (StringUtils.isEmpty(dto.getStatus())) {
+            dto.setStatus("in_use");
+        }
+        return save(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(FinFixedAssetDto dto) {
+        if (dto == null || dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+        }
+        FinFixedAsset existed = getById(dto.getId());
+        if (existed == null) {
+            throw new ServiceException("淇敼澶辫触锛屽浐瀹氳祫浜т笉瀛樺湪");
+        }
+        if (StringUtils.isEmpty(dto.getAssetCode())) {
+            dto.setAssetCode(existed.getAssetCode());
+        }
+        if (StringUtils.isEmpty(dto.getStatus())) {
+            dto.setStatus(existed.getStatus());
+        }
+        validateForSave(dto, true);
+        BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+        dto.setResidualRate(residualRate);
+        if (dto.getAccumulatedDepreciation() == null) {
+            dto.setAccumulatedDepreciation(defaultMoney(existed.getAccumulatedDepreciation()));
+        }
+        dto.setNetValue(calculateNetValue(dto.getOriginalValue(), dto.getAccumulatedDepreciation()));
+        return updateById(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteByIds(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            throw new ServiceException("鍒犻櫎澶辫触锛岃閫夋嫨瑕佸垹闄ょ殑鏁版嵁");
+        }
+        return removeByIds(ids);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> depreciate(List<Long> ids) {
+        LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+        if (ids != null && !ids.isEmpty()) {
+            wrapper.in(FinFixedAsset::getId, ids);
+        } else {
+            wrapper.eq(FinFixedAsset::getStatus, "in_use");
+        }
+        List<FinFixedAsset> assets = list(wrapper);
+        BigDecimal totalMonthlyDepreciation = ZERO;
+        int processedCount = 0;
+        for (FinFixedAsset asset : assets) {
+            if (!"in_use".equals(asset.getStatus())) {
+                continue;
+            }
+            BigDecimal monthlyDepreciation = calculateMonthlyDepreciation(
+                    asset.getOriginalValue(),
+                    asset.getResidualRate(),
+                    asset.getUsefulLife()
+            );
+            BigDecimal accumulatedDepreciation = defaultMoney(asset.getAccumulatedDepreciation()).add(monthlyDepreciation);
+            if (accumulatedDepreciation.compareTo(defaultMoney(asset.getOriginalValue())) > 0) {
+                accumulatedDepreciation = defaultMoney(asset.getOriginalValue());
+            }
+            asset.setAccumulatedDepreciation(roundMoney(accumulatedDepreciation));
+            asset.setNetValue(calculateNetValue(asset.getOriginalValue(), asset.getAccumulatedDepreciation()));
+            updateById(asset);
+            processedCount++;
+            totalMonthlyDepreciation = totalMonthlyDepreciation.add(monthlyDepreciation);
+        }
+        Map<String, Object> result = new HashMap<>(4);
+        result.put("processedCount", processedCount);
+        result.put("totalMonthlyDepreciation", roundMoney(totalMonthlyDepreciation));
+        result.put("executionTime", LocalDateTime.now());
+        return result;
+    }
+
+    /**
+     * 鎸夋枃妗h鍒欐牎楠屽浐瀹氳祫浜ф暟鎹��
+     */
+    private void validateForSave(FinFixedAssetDto dto, boolean isUpdate) {
+        if (dto == null) {
+            throw new ServiceException("鍥哄畾璧勪骇鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (isUpdate && dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getAssetName())) {
+            throw new ServiceException("璧勪骇鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getCategory())) {
+            throw new ServiceException("璧勪骇绫诲埆涓嶈兘涓虹┖");
+        }
+        if (dto.getPurchaseDate() == null) {
+            throw new ServiceException("璐疆鏃ユ湡涓嶈兘涓虹┖");
+        }
+        if (dto.getOriginalValue() == null || dto.getOriginalValue().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("璧勪骇鍘熷�间笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+        }
+        if (dto.getUsefulLife() == null || dto.getUsefulLife() <= 0) {
+            throw new ServiceException("浣跨敤骞撮檺蹇呴』澶т簬0");
+        }
+        if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("娈嬪�肩巼涓嶈兘灏忎簬0");
+        }
+        if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(ONE_HUNDRED) > 0) {
+            throw new ServiceException("娈嬪�肩巼涓嶈兘澶т簬100%");
+        }
+        if (StringUtils.isNotEmpty(dto.getAssetCode())) {
+            LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(FinFixedAsset::getAssetCode, dto.getAssetCode());
+            if (isUpdate) {
+                wrapper.ne(FinFixedAsset::getId, dto.getId());
+            }
+            if (count(wrapper) > 0) {
+                throw new ServiceException("璧勪骇缂栧彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+            }
+        }
+    }
+
+    /**
+     * 鍥哄畾璧勪骇鎶樻棫鍏紡锛�
+     * monthlyDepreciation = originalValue * (1 - residualRate/100) / (usefulLife*12)
+     */
+    private BigDecimal calculateMonthlyDepreciation(BigDecimal originalValue, BigDecimal residualRate, Integer usefulLife) {
+        BigDecimal normalizedOriginalValue = defaultMoney(originalValue);
+        BigDecimal normalizedResidualRate = normalizeResidualRate(residualRate);
+        BigDecimal depreciableRatio = BigDecimal.ONE.subtract(normalizedResidualRate.divide(ONE_HUNDRED, 8, RoundingMode.HALF_UP));
+        BigDecimal months = BigDecimal.valueOf((long) usefulLife * 12L);
+        if (months.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("浣跨敤骞撮檺鏃犳晥锛屾棤娉曡鎻愭姌鏃�");
+        }
+        return roundMoney(normalizedOriginalValue.multiply(depreciableRatio).divide(months, 8, RoundingMode.HALF_UP));
+    }
+
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎶樻棫銆�
+     */
+    private BigDecimal calculateNetValue(BigDecimal originalValue, BigDecimal accumulatedDepreciation) {
+        BigDecimal value = defaultMoney(originalValue).subtract(defaultMoney(accumulatedDepreciation));
+        if (value.compareTo(BigDecimal.ZERO) < 0) {
+            value = BigDecimal.ZERO;
+        }
+        return roundMoney(value);
+    }
+
+    private BigDecimal normalizeResidualRate(BigDecimal residualRate) {
+        return residualRate == null ? BigDecimal.ZERO : residualRate;
+    }
+
+    private BigDecimal defaultMoney(BigDecimal value) {
+        return value == null ? ZERO : roundMoney(value);
+    }
+
+    private BigDecimal roundMoney(BigDecimal value) {
+        if (value == null) {
+            return ZERO;
+        }
+        return value.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private String generateAssetCode() {
+        return "GD" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
+    }
+}

--
Gitblit v1.9.3