maven
14 小时以前 6657d65777a1be6b97b745fe0c2be55a8b3ec11e
yys 财务报表,会计报表对接,设备增加字段
已添加4个文件
已修改4个文件
437 ■■■■■ 文件已修改
src/main/java/com/ruoyi/account/controller/AccountingController.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceLedger.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceLedgerMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/controller/AccountingController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package com.ruoyi.account.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.service.impl.AccountingServiceImpl;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author :yys
 * @date : 2026/1/17 10:40
 */
@Api(tags = "会计核算")
@RestController
@RequestMapping("/accounting")
public class AccountingController extends BaseController {
    @Autowired
    private AccountingServiceImpl accountingService;
    @ApiOperation("总计")
    @GetMapping("/total")
    public AjaxResult total(@RequestParam Integer year) {
        return accountingService.total(year);
    }
    @ApiOperation("设备类型分布")
    @GetMapping("/deviceTypeDistribution")
    public AjaxResult deviceTypeDistribution(@RequestParam Integer year) {
        return accountingService.deviceTypeDistribution(year);
    }
    @ApiOperation("设备分页查询计算折旧")
    @GetMapping("/calculateDepreciation")
    public AjaxResult calculateDepreciation(Page page, @RequestParam Integer year) {
        return accountingService.calculateDepreciation(page,year);
    }
}
src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.account.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
 * @author :yys
 * @date : 2026/1/17 13:42
 */
@Data
public class DeviceTypeDetail {
    private String type;       // è®¾å¤‡ç±»åž‹
    private BigDecimal count;  // æ•°é‡
    private BigDecimal amount; // ä»·æ ¼
}
src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.account.dto;
import com.mchange.v1.util.ListUtils;
import lombok.Data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
 * @author :yys
 * @date : 2026/1/17 13:19
 */
@Data
public class DeviceTypeDistributionVO {
    private Integer totalCount = 0; // è®¾å¤‡ç±»åž‹æ€»æ•°
    // é¥¼å›¾å’ŒæŠ˜çº¿å›¾çš„分类(设备类型)
    private List<String> categories = new ArrayList<>();
    // å„类型设备的数量
    private List<BigDecimal> countData = new ArrayList<>();
    // å„类型设备的总金额
    private List<BigDecimal> amountData = new ArrayList<>();
    // æ˜Žç»†åˆ—表,包含类型、数量、金额
    private List<DeviceTypeDetail> details = new ArrayList<>();
}
src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,292 @@
package com.ruoyi.account.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.dto.DeviceTypeDetail;
import com.ruoyi.account.dto.DeviceTypeDistributionVO;
import com.ruoyi.account.mapper.BorrowInfoMapper;
import com.ruoyi.account.pojo.BorrowInfo;
import com.ruoyi.device.mapper.DeviceLedgerMapper;
import com.ruoyi.device.pojo.DeviceLedger;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.procurementrecord.mapper.CustomStorageMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.CustomStorage;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.procurementrecord.service.impl.ProcurementRecordOutServiceImpl;
import com.ruoyi.procurementrecord.service.impl.ProcurementRecordServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author :yys
 * @date : 2026/1/17 10:41
 */
@Service
@Slf4j
public class AccountingServiceImpl {
    @Autowired
    private DeviceLedgerMapper deviceLedgerMapper;
    @Autowired
    private BorrowInfoMapper borrowInfoMapper;
    @Autowired
    private CustomStorageMapper customStorageMapper;
    @Autowired
    private ProcurementRecordMapper procurementRecordMapper;
    @Autowired
    private ProcurementRecordOutMapper procurementRecordOutMapper;
    public AjaxResult total(Integer year) {
        Map<String,Object> map = new HashMap<>();
        map.put("deprAmount",0); // æŠ˜æ—§é‡‘额
        map.put("deviceTotal",0); // è®¾å¤‡æ€»æ•°
        map.put("deviceAmount",0); // è®¾å¤‡èµ„产原值
        map.put("netValue",0); // å‡€å€¼
        map.put("debt",0); // è´Ÿå€º
        map.put("inventoryValue",0); // åº“存资产
        LambdaQueryWrapper<DeviceLedger> deviceLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceLedgerLambdaQueryWrapper.like(DeviceLedger::getCreateTime,year);
        List<DeviceLedger> deviceLedgers = deviceLedgerMapper.selectList(deviceLedgerLambdaQueryWrapper);
        if(CollectionUtils.isNotEmpty(deviceLedgers)){
            map.put("deviceTotal",deviceLedgers.size());
            BigDecimal reduce = deviceLedgers.stream()
                    .map(DeviceLedger::getTaxIncludingPriceTotal)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            map.put("deviceAmount",reduce);
            List<DeviceLedger> collect = deviceLedgers.stream().filter(deviceLedger -> deviceLedger.getIsDepr() != null && deviceLedger.getIsDepr() == 1).collect(Collectors.toList());
            // æ ¹æ®å½“前年份和设备录入时间年份比较 * æ¯å¹´æŠ˜æ—§é‡‘额 = è®¾å¤‡æŠ˜æ—§é‡‘额
            BigDecimal total = new BigDecimal(0);
            if(CollectionUtils.isNotEmpty(collect)){
                for (DeviceLedger deviceLedger : collect) {
                    BigDecimal totalDepreciation = calculatePreciseDepreciation(deviceLedger);
                    total = total.add(totalDepreciation);
                }
                map.put("deprAmount",total);
            }
            // å‡€å€¼ = è®¾å¤‡èµ„产原值 - è®¾å¤‡ç´¯è®¡æŠ˜æ—§é‡‘额
            map.put("netValue",reduce.subtract(total));
        }
        // è´Ÿå€º
        LambdaQueryWrapper<BorrowInfo> borrowInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        borrowInfoLambdaQueryWrapper.like(BorrowInfo::getCreateTime,year)
                .eq(BorrowInfo::getStatus,1);
        List<BorrowInfo> borrowInfos = borrowInfoMapper.selectList(borrowInfoLambdaQueryWrapper);
        if(CollectionUtils.isNotEmpty(borrowInfos)){
            BigDecimal reduce = borrowInfos.stream()
                    .map(BorrowInfo::getBorrowAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            map.put("debt",reduce);
        }
        // åº“存资产
        LambdaQueryWrapper<ProcurementRecordStorage> procurementRecordStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        procurementRecordStorageLambdaQueryWrapper.like(ProcurementRecordStorage::getCreateTime,year);
        List<ProcurementRecordStorage> procurementRecordStorages = procurementRecordMapper.selectList(procurementRecordStorageLambdaQueryWrapper);
        BigDecimal procurementRecordTotal = new BigDecimal(0);
        if(CollectionUtils.isNotEmpty(procurementRecordStorages)){
            // èŽ·å–å‡ºåº“æ•°æ®
            LambdaQueryWrapper<ProcurementRecordOut> procurementRecordOutLambdaQueryWrapper = new LambdaQueryWrapper<>();
            procurementRecordOutLambdaQueryWrapper.like(ProcurementRecordOut::getCreateTime,year)
                    .in(ProcurementRecordOut::getProcurementRecordStorageId, procurementRecordStorages
                            .stream()
                            .map(ProcurementRecordStorage::getId)
                            .collect(Collectors.toList()))
                    .in(ProcurementRecordOut::getType,Arrays.asList(1,2));
            List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(procurementRecordOutLambdaQueryWrapper);
            for (ProcurementRecordStorage procurementRecordStorage : procurementRecordStorages) {
                // é‡‡è´­ï¼Œç”Ÿäº§å…¥åº“总价值
                procurementRecordTotal.add(procurementRecordStorage.getUnitPrice().multiply(procurementRecordStorage.getInboundNum()));
                // é€šè¿‡å…¥åº“id,类型获取出库数据
                List<ProcurementRecordOut> procurementRecordOutsByStorageId = procurementRecordOuts.stream()
                        .filter(procurementRecordOut -> procurementRecordOut.getProcurementRecordStorageId().equals(procurementRecordStorage.getId())
                        && procurementRecordOut.getType().equals(procurementRecordStorage.getType()))
                        .collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(procurementRecordOutsByStorageId)){
                    for (ProcurementRecordOut procurementRecordOut : procurementRecordOutsByStorageId) {
                        // é‡‡è´­ï¼Œç”Ÿäº§å‡ºåº“总价值
                        procurementRecordTotal.subtract(procurementRecordStorage.getUnitPrice().multiply(procurementRecordOut.getInboundNum()));
                    }
                }
            }
        }
        LambdaQueryWrapper<CustomStorage> customStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        customStorageLambdaQueryWrapper.like(CustomStorage::getInboundDate,year);
        List<CustomStorage> customStorages = customStorageMapper.selectList(customStorageLambdaQueryWrapper);
        BigDecimal customStorageTotal = new BigDecimal(0);
        if(CollectionUtils.isNotEmpty(customStorages)){
            // èŽ·å–å‡ºåº“æ•°æ®
            LambdaQueryWrapper<ProcurementRecordOut> procurementRecordOutLambdaQueryWrapper = new LambdaQueryWrapper<>();
            procurementRecordOutLambdaQueryWrapper.like(ProcurementRecordOut::getCreateTime,year)
                    .in(ProcurementRecordOut::getProcurementRecordStorageId, customStorages
                            .stream()
                            .map(CustomStorage::getId)
                            .collect(Collectors.toList()))
                    .eq(ProcurementRecordOut::getType,3);
            List<ProcurementRecordOut> procurementRecordOuts = procurementRecordOutMapper.selectList(procurementRecordOutLambdaQueryWrapper);
            customStorages.forEach(customStorage -> {
                // è‡ªå®šä¹‰å…¥åº“总价值
                customStorageTotal.add(customStorage.getTaxInclusiveUnitPrice().multiply(customStorage.getInboundNum()));
                // é€šè¿‡å…¥åº“id,类型获取出库数据
                List<ProcurementRecordOut> procurementRecordOutsByStorageId = procurementRecordOuts.stream()
                        .filter(procurementRecordOut -> procurementRecordOut.getProcurementRecordStorageId().equals(customStorage.getId()))
                        .collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(procurementRecordOutsByStorageId)){
                    for (ProcurementRecordOut procurementRecordOut : procurementRecordOutsByStorageId) {
                        // è‡ªå®šä¹‰å‡ºåº“总价值
                        customStorageTotal.subtract(procurementRecordOut.getInboundNum().multiply(customStorage.getTaxInclusiveUnitPrice()));
                    }
                }
            });
        }
        map.put("inventoryValue",procurementRecordTotal.add(customStorageTotal));
        return AjaxResult.success( map);
    }
    /**
     * è®¡ç®—设备累计折旧金额
     * @param deviceLedger è®¾å¤‡å°è´¦å®žä½“
     * @return ç´¯è®¡æŠ˜æ—§é‡‘额(BigDecimal,保留2位小数)
     */
    public static BigDecimal calculateTotalDepreciation(DeviceLedger deviceLedger) {
        // 1. ç©ºå€¼æ ¡éªŒ
        if (deviceLedger == null) {
            return BigDecimal.ZERO;
        }
        // 2. åˆ¤æ–­æ˜¯å¦å¼€å¯æŠ˜æ—§ï¼Œæœªå¼€å¯åˆ™æŠ˜æ—§é‡‘额为0
        Integer isDepr = deviceLedger.getIsDepr();
        if (isDepr == null || isDepr != 1) { // 1-是 2-否
            return BigDecimal.ZERO;
        }
        // 3. èŽ·å–æ¯å¹´æŠ˜æ—§é‡‘é¢ï¼Œä¸ºç©ºåˆ™è¿”å›ž0
        BigDecimal annualDepreciation = deviceLedger.getAnnualDepreciationAmount();
        if (annualDepreciation == null || annualDepreciation.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        // 4. èŽ·å–è®¾å¤‡å½•å…¥æ—¶é—´ï¼Œä¸ºç©ºåˆ™è¿”å›ž0
        LocalDateTime createTime = deviceLedger.getCreateTime();
        if (createTime == null) {
            return BigDecimal.ZERO;
        }
        // 5. è®¡ç®—年份差值
        int currentYear = Year.now().getValue(); // å½“前年份
        int createYear = createTime.getYear();    // è®¾å¤‡å½•入年份
        int yearDiff = currentYear - createYear;
        // 6. å¤„理年份差值为负数的情况(录入时间在未来)
        if (yearDiff < 0) {
            return BigDecimal.ZERO;
        }
        // 7. è®¡ç®—总折旧金额 = å¹´ä»½å·®å€¼ * æ¯å¹´æŠ˜æ—§é‡‘额
        BigDecimal totalDepreciation = annualDepreciation.multiply(BigDecimal.valueOf(yearDiff));
        // 8. è¿”回保留2位小数的结果(金额规范)
        return totalDepreciation.setScale(2, BigDecimal.ROUND_HALF_UP);
    }
    /**
     * ã€è¿›é˜¶ç‰ˆã€‘按实际天数计算折旧(更精准)
     * @param deviceLedger è®¾å¤‡å°è´¦å®žä½“
     * @return ç´¯è®¡æŠ˜æ—§é‡‘额
     */
    public static BigDecimal calculatePreciseDepreciation(DeviceLedger deviceLedger) {
        if (deviceLedger == null) {
            return BigDecimal.ZERO;
        }
        // åˆ¤æ–­æ˜¯å¦å¼€å¯æŠ˜æ—§
        Integer isDepr = deviceLedger.getIsDepr();
        if (isDepr == null || isDepr != 1) {
            return BigDecimal.ZERO;
        }
        // èŽ·å–æ¯å¹´æŠ˜æ—§é‡‘é¢
        BigDecimal annualDepreciation = deviceLedger.getAnnualDepreciationAmount();
        if (annualDepreciation == null || annualDepreciation.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        // èŽ·å–è®¾å¤‡å½•å…¥æ—¶é—´
        LocalDateTime createTime = deviceLedger.getCreateTime();
        if (createTime == null) {
            return BigDecimal.ZERO;
        }
        // å½“前时间
        LocalDateTime now = LocalDateTime.now();
        // å¦‚果录入时间在未来,返回0
        if (createTime.isAfter(now)) {
            return BigDecimal.ZERO;
        }
        // è®¡ç®—总天数
        long days = ChronoUnit.DAYS.between(createTime, now);
        // æŒ‰æ¯å¹´365天计算折旧金额
        BigDecimal dailyDepreciation = annualDepreciation.divide(BigDecimal.valueOf(365), 6, BigDecimal.ROUND_HALF_UP);
        BigDecimal totalDepreciation = dailyDepreciation.multiply(BigDecimal.valueOf(days));
        return totalDepreciation.setScale(2, BigDecimal.ROUND_HALF_UP);
    }
    public AjaxResult deviceTypeDistribution(Integer year) {
        // 2. ç»„装返回VO
       DeviceTypeDistributionVO vo = new DeviceTypeDistributionVO();
       List<DeviceTypeDetail> details = deviceLedgerMapper.getDeviceTypeDistributionByYear( year);
        vo.setDetails(details);
       if(CollectionUtils.isNotEmpty(details)){
           // 3. æå–图表所需的分类和数据
           vo.setCategories(details.stream()
                   .map(DeviceTypeDetail::getType)
                   .collect(Collectors.toList()));
           vo.setCountData(details.stream()
                   .map(DeviceTypeDetail::getCount)
                   .collect(Collectors.toList()));
           vo.setAmountData(details.stream()
                   .map(DeviceTypeDetail::getAmount)
                   .collect(Collectors.toList()));
           vo.setTotalCount(vo.getCategories().size());
       }
        return AjaxResult.success(vo);
    }
    public AjaxResult calculateDepreciation(Page page, Integer year) {
        LambdaQueryWrapper<DeviceLedger> deviceLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceLedgerLambdaQueryWrapper.like(DeviceLedger::getCreateTime,year)
                .eq(DeviceLedger::getIsDepr,1);
        IPage<DeviceLedger> deviceLedgerIPage = deviceLedgerMapper.selectPage(page, deviceLedgerLambdaQueryWrapper);
        for (DeviceLedger record : deviceLedgerIPage.getRecords()) {
            record.setDeprAmount(calculatePreciseDepreciation(record));
            record.setNetValue(record.getTaxIncludingPriceTotal().subtract(record.getDeprAmount()));
        }
        return AjaxResult.success(deviceLedgerIPage);
    }
}
src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java
@@ -135,4 +135,14 @@
    @ApiModelProperty("运行时长")
    private String runtimeDuration;
    @ApiModelProperty("是否折旧 1-是 2-否")
    private Integer isDepr;
    @ApiModelProperty("每年折旧金额")
    private BigDecimal annualDepreciationAmount;
    @ApiModelProperty("设备类型")
    private String type;
}
src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.dto.DeviceTypeDetail;
import com.ruoyi.account.dto.DeviceTypeDistributionVO;
import com.ruoyi.device.dto.DeviceLedgerDto;
import com.ruoyi.device.execl.DeviceLedgerExeclDto;
import com.ruoyi.device.pojo.DeviceLedger;
@@ -21,4 +23,11 @@
    @InterceptorIgnore(tenantLine = "true")
    DeviceLedger selectById1(Long id);
    /**
     * æŒ‰å¹´ä»½ç»Ÿè®¡è®¾å¤‡ç±»åž‹åˆ†å¸ƒ
     * @param year ä¼ å…¥çš„年份(如2026)
     * @return å„类型的数量和金额
     */
    List<DeviceTypeDetail> getDeviceTypeDistributionByYear(@Param("year") Integer year);
}
src/main/java/com/ruoyi/device/pojo/DeviceLedger.java
@@ -142,4 +142,21 @@
    @ApiModelProperty("运行时长")
    private String runtimeDuration;
    @ApiModelProperty("是否折旧 1-是 2-否")
    private Integer isDepr;
    @ApiModelProperty("每年折旧金额")
    private BigDecimal annualDepreciationAmount;
    @TableField(exist = false)
    @ApiModelProperty("累计折旧")
    private BigDecimal deprAmount;
    @TableField(exist = false)
    @ApiModelProperty("净值")
    private BigDecimal netValue;
    @ApiModelProperty("设备类型")
    private String type;
}
src/main/resources/mapper/device/DeviceLedgerMapper.xml
@@ -28,7 +28,10 @@
        dl.update_time ,
        su.nick_name AS createUser,
        dl.update_user,
        dl.tenant_id
        dl.tenant_id,
        dl.is_depr,
        dl.annual_depreciation_amount,
        dl.type
        FROM device_ledger dl
        left join sys_user su on dl.create_user = su.user_id
        <where>
@@ -84,5 +87,18 @@
        from device_ledger
        where id = #{id}
    </select>
    <select id="getDeviceTypeDistributionByYear"
            resultType="com.ruoyi.account.dto.DeviceTypeDetail"
            parameterType="java.lang.Integer">
        SELECT
            `type`,
            SUM(`number`) AS `count`,
            SUM(tax_including_price_unit) AS amount
        FROM device_ledger
        WHERE YEAR(create_time) = #{year}
          AND type IS NOT NULL
        GROUP BY type
    </select>
</mapper>