gongchunyi
7 小时以前 263b034b4058bb7a36c709278abdc88ca1ba26c1
feat: 生产成本导入数据入库
已添加13个文件
已修改2个文件
529 ■■■■■ 文件已修改
doc/宁夏-中盛建材.sql 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionSettlementDetailsController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/SettlementImportDto.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionSettlementBatchesMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionSettlementDetailsMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/IProductionSettlementDetailsService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionCostServiceImpl.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionSettlementDetailsServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionSettlementBatchesMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionSettlementDetailsMapper.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -468,4 +468,46 @@
    KEY `idx_order_id` (`order_id`) USING BTREE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_0900_ai_ci COMMENT ='生产订单绑定的工艺路线表';
  COLLATE = utf8mb4_0900_ai_ci COMMENT ='生产订单绑定的工艺路线表';
CREATE TABLE `production_settlement_batches`
(
    `id`          bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `period_time` date         DEFAULT NULL COMMENT '核算归属月份',
    `batch_name`  varchar(255) DEFAULT NULL COMMENT '批次名称',
    `status`      int          DEFAULT '0' COMMENT '状态:0-仅预算,1-结算计算中,2-已完成结算,3-已锁定',
    `create_user` varchar(255) DEFAULT NULL COMMENT '导入用户',
    `create_time` datetime     DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
    `tenant_id`   bigint       DEFAULT NULL COMMENT '租户ID',
    PRIMARY KEY (`id`),
    KEY `idx_period` (`period_time`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='生产成本核算批次主表';
CREATE TABLE `production_settlement_details`
(
    `id`           bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `batch_id`     bigint NOT NULL COMMENT '关联核算批次表ID',
    `product_id`   bigint          DEFAULT NULL COMMENT '产品ID',
    `product_type` varchar(100)    DEFAULT NULL COMMENT '产品类型名称',
    `category`     varchar(100)    DEFAULT NULL COMMENT '费用类别',
    `subject_name` varchar(100)    DEFAULT NULL COMMENT '科目名称',
    `budget_qty`   decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '预算耗量',
    `budget_price` decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '预算单价',
    `budget_total` decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '预算总成本',
    `actual_qty`   decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '实际耗量',
    `actual_price` decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '实际单价',
    `actual_total` decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '实际总成本',
    `diff_qty`     decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '耗量差异',
    `diff_price`   decimal(20, 15) DEFAULT '0.000000000000000' COMMENT '单价差异',
    `diff_total`   DECIMAL(20, 15) DEFAULT '0.000000000000000' COMMENT '总成本差异',
    `tenant_id`    bigint          DEFAULT NULL COMMENT '租户ID',
    PRIMARY KEY (`id`),
    KEY `idx_batch_id` (`batch_id`),
    KEY `idx_product_id` (`product_id`) COMMENT '方便按产品查询历史成本对比'
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='生产成本核算对比明细表';
src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.service.IProductionSettlementBatchesService;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—批次主表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
@RestController
@RequestMapping("/productionSettlementBatches")
@ApiModel(value = "ProductionSettlementBatchesControllerç±»", description = "生产成本核算批次主表 å‰ç«¯æŽ§åˆ¶å™¨")
public class ProductionSettlementBatchesController {
    @Autowired
    private IProductionSettlementBatchesService productionSettlementBatchesService;
    @PostMapping("/import")
    @ApiOperation("导入生产成本核算表")
    public AjaxResult importProductionSettlement(@RequestParam("file") MultipartFile file, @RequestParam(required = false) LocalDate periodTime, @RequestParam(required = false) String batchName) {
        productionSettlementBatchesService.importProductionSettlement(file, periodTime, batchName);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/production/controller/ProductionSettlementDetailsController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.production.controller;
import io.swagger.annotations.ApiModel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—对比明细表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
@RestController
@RequestMapping("/production-settlement-details")
@ApiModel(value = "ProductionSettlementDetailsControllerç±»", description = "生产成本核算对比明细表 å‰ç«¯æŽ§åˆ¶å™¨")
public class ProductionSettlementDetailsController {
}
src/main/java/com/ruoyi/production/dto/SettlementImportDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package com.ruoyi.production.dto;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
 * <br>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—对比文件导入Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/30
 */
@Data
@ApiModel(value = "SettlementImportDto", description = "生产成本核算对比文件导入Dto")
public class SettlementImportDto {
    @Excel(name = "产品类型")
    @ApiModelProperty("产品类型")
    private String productType;
    @Excel(name = "科目名称")
    @ApiModelProperty("科目名称")
    private String category;
    @Excel(name = "科目")
    @ApiModelProperty("科目")
    private String subjectName;
    @Excel(name = "成本预算")
    @ApiModelProperty("成本预算")
    private BigDecimal budgetTotal;
    @Excel(name = "耗量预算")
    @ApiModelProperty("耗量预算")
    private BigDecimal budgetQty;
    @Excel(name = "单价预算")
    @ApiModelProperty("单价预算")
    private BigDecimal budgetPrice;
}
src/main/java/com/ruoyi/production/mapper/ProductionSettlementBatchesMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.ruoyi.production.pojo.ProductionSettlementBatches;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—批次主表 Mapper æŽ¥å£
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
public interface ProductionSettlementBatchesMapper extends BaseMapper<ProductionSettlementBatches> {
}
src/main/java/com/ruoyi/production/mapper/ProductionSettlementDetailsMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.ruoyi.production.pojo.ProductionSettlementDetails;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—对比明细表 Mapper æŽ¥å£
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
public interface ProductionSettlementDetailsMapper extends BaseMapper<ProductionSettlementDetails> {
}
src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.production.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—批次主表
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_settlement_batches")
@ApiModel(value="ProductionSettlementBatches对象", description="生产成本核算批次主表")
public class ProductionSettlementBatches implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "核算归属月份")
    private LocalDate periodTime;
    @ApiModelProperty(value = "批次名称")
    private String batchName;
    @ApiModelProperty(value = "状态:0-仅预算,1-结算计算中,2-已完成结算,3-已锁定")
    private Integer status;
    @ApiModelProperty(value = "导入用户")
    private String createUser;
    @ApiModelProperty(value = "创建日期")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "租户ID")
    private Long tenantId;
}
src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package com.ruoyi.production.pojo;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—对比明细表
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_settlement_details")
@ApiModel(value="ProductionSettlementDetails对象", description="生产成本核算对比明细表")
public class ProductionSettlementDetails implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "关联核算批次表ID")
    private Long batchId;
    @ApiModelProperty(value = "产品ID")
    private Long productId;
    @ApiModelProperty(value = "产品类型名称")
    private String productType;
    @ApiModelProperty(value = "费用类别")
    private String category;
    @ApiModelProperty(value = "科目名称")
    private String subjectName;
    @ApiModelProperty(value = "预算耗量")
    private BigDecimal budgetQty;
    @ApiModelProperty(value = "预算单价")
    private BigDecimal budgetPrice;
    @ApiModelProperty(value = "预算总成本")
    private BigDecimal budgetTotal;
    @ApiModelProperty(value = "实际耗量")
    private BigDecimal actualQty;
    @ApiModelProperty(value = "实际单价")
    private BigDecimal actualPrice;
    @ApiModelProperty(value = "实际总成本")
    private BigDecimal actualTotal;
    @ApiModelProperty(value = "耗量差异")
    private BigDecimal diffQty;
    @ApiModelProperty(value = "单价差异")
    private BigDecimal diffPrice;
    @ApiModelProperty(value = "总成本差异")
    private BigDecimal diffTotal;
    @ApiModelProperty(value = "租户ID")
    private Long tenantId;
}
src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.production.service;
import com.ruoyi.production.pojo.ProductionSettlementBatches;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—批次主表 æœåŠ¡ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
public interface IProductionSettlementBatchesService extends IService<ProductionSettlementBatches> {
    void importProductionSettlement(MultipartFile file, LocalDate periodTime, String batchName);
}
src/main/java/com/ruoyi/production/service/IProductionSettlementDetailsService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.service;
import com.ruoyi.production.pojo.ProductionSettlementDetails;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—对比明细表 æœåŠ¡ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
public interface IProductionSettlementDetailsService extends IService<ProductionSettlementDetails> {
}
src/main/java/com/ruoyi/production/service/impl/ProductionCostServiceImpl.java
@@ -179,12 +179,24 @@
    private <T> IPage<T> getMemoryPage(Page<T> page, List<T> list) {
        int total = list.size();
        int size = (int) page.getSize();
        int current = (int) page.getCurrent();
        int fromIndex = (current - 1) * size;
        int toIndex = Math.min(fromIndex + size, total);
        long size = page.getSize();
        long current = page.getCurrent();
        List<T> subList = (fromIndex < total && fromIndex >= 0) ? list.subList(fromIndex, toIndex) : new ArrayList<>();
        if (size == -1 || current == -1) {
            page.setTotal(total);
            page.setRecords(list);
            return page;
        }
        int fromIndex = (int) ((current - 1) * size);
        int toIndex = Math.min(fromIndex + (int) size, total);
        List<T> subList;
        if (fromIndex >= 0 && fromIndex < total) {
            subList = list.subList(fromIndex, toIndex);
        } else {
            subList = new ArrayList<>();
        }
        page.setTotal(total);
        page.setRecords(subList);
src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package com.ruoyi.production.service.impl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.production.dto.SettlementImportDto;
import com.ruoyi.production.pojo.ProductionSettlementBatches;
import com.ruoyi.production.mapper.ProductionSettlementBatchesMapper;
import com.ruoyi.production.pojo.ProductionSettlementDetails;
import com.ruoyi.production.service.IProductionSettlementBatchesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.production.service.IProductionSettlementDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—批次主表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
@Service
public class ProductionSettlementBatchesServiceImpl extends ServiceImpl<ProductionSettlementBatchesMapper, ProductionSettlementBatches> implements IProductionSettlementBatchesService {
    @Autowired
    private IProductionSettlementDetailsService productionSettlementDetailsService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importProductionSettlement(MultipartFile file, LocalDate periodTime, String batchName) {
        if (file == null || file.isEmpty()) {
            throw new ServiceException("导入失败,文件不能为空");
        }
        List<SettlementImportDto> list;
        try {
            ExcelUtil<SettlementImportDto> util = new ExcelUtil<>(SettlementImportDto.class);
            list = util.importExcel(file.getInputStream());
        } catch (Exception e) {
            log.error("导入生产成本核算失败", e);
            throw new ServiceException("解析Excel文件失败:" + e.getMessage());
        }
        if (StringUtils.isEmpty(list)) {
            throw new ServiceException("导入数据不能为空!");
        }
        ProductionSettlementBatches batch = new ProductionSettlementBatches();
        batch.setBatchName(StringUtils.isNotEmpty(batchName) ? batchName : periodTime + "成本核算导入");
        batch.setPeriodTime(periodTime != null ? periodTime : LocalDate.now());
        batch.setStatus(0);
        batch.setCreateTime(LocalDateTime.now());
        batch.setCreateUser(SecurityUtils.getUsername());
        this.save(batch);
        List<ProductionSettlementDetails> detailList = list.stream().map(dto -> {
            ProductionSettlementDetails detail = new ProductionSettlementDetails();
            detail.setBatchId(batch.getId());
            detail.setProductType(dto.getProductType());
            detail.setCategory(dto.getCategory());
            detail.setSubjectName(dto.getSubjectName());
            detail.setBudgetQty(dto.getBudgetQty());
            detail.setBudgetPrice(dto.getBudgetPrice());
            detail.setBudgetTotal(dto.getBudgetTotal());
            detail.setActualQty(BigDecimal.ZERO);
            detail.setActualPrice(BigDecimal.ZERO);
            detail.setActualTotal(BigDecimal.ZERO);
            detail.setDiffQty(BigDecimal.ZERO);
            detail.setDiffPrice(BigDecimal.ZERO);
            detail.setDiffTotal(BigDecimal.ZERO);
            return detail;
        }).collect(Collectors.toList());
        productionSettlementDetailsService.saveBatch(detailList);
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionSettlementDetailsServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.service.impl;
import com.ruoyi.production.pojo.ProductionSettlementDetails;
import com.ruoyi.production.mapper.ProductionSettlementDetailsMapper;
import com.ruoyi.production.service.IProductionSettlementDetailsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 * ç”Ÿäº§æˆæœ¬æ ¸ç®—对比明细表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-30
 */
@Service
public class ProductionSettlementDetailsServiceImpl extends ServiceImpl<ProductionSettlementDetailsMapper, ProductionSettlementDetails> implements IProductionSettlementDetailsService {
}
src/main/resources/mapper/production/ProductionSettlementBatchesMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.production.mapper.ProductionSettlementBatchesMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.production.pojo.ProductionSettlementBatches">
        <id column="id" property="id" />
        <result column="period_time" property="periodTime" />
        <result column="batch_name" property="batchName" />
        <result column="status" property="status" />
        <result column="create_user" property="createUser" />
        <result column="create_time" property="createTime" />
        <result column="tenant_id" property="tenantId" />
    </resultMap>
</mapper>
src/main/resources/mapper/production/ProductionSettlementDetailsMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.production.mapper.ProductionSettlementDetailsMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.production.pojo.ProductionSettlementDetails">
        <id column="id" property="id" />
        <result column="batch_id" property="batchId" />
        <result column="product_id" property="productId" />
        <result column="product_type" property="productType" />
        <result column="category" property="category" />
        <result column="subject_name" property="subjectName" />
        <result column="budget_qty" property="budgetQty" />
        <result column="budget_price" property="budgetPrice" />
        <result column="budget_total" property="budgetTotal" />
        <result column="actual_qty" property="actualQty" />
        <result column="actual_price" property="actualPrice" />
        <result column="actual_total" property="actualTotal" />
        <result column="diff_qty" property="diffQty" />
        <result column="diff_price" property="diffPrice" />
        <result column="diff_total" property="diffTotal" />
        <result column="tenant_id" property="tenantId" />
    </resultMap>
</mapper>