Bläddra i källkod

杰生 库存管理 今日统计

home 2 år sedan
förälder
incheckning
dcf2d7f2b5

+ 23 - 51
hx-common/client-util/src/main/java/com/fjhx/utils/FileClientUtil.java

@@ -7,6 +7,7 @@ import com.fjhx.params.FileInfoParam;
 import org.springblade.core.tool.api.R;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -17,25 +18,8 @@ public class FileClientUtil {
     private static final String APPLICATION_NAME = SpringUtil.getApplicationName();
     // fileClient
     private static final IFileClient fileClient = SpringUtil.getBean(IFileClient.class);
-
-    /**
-     * 文件绑定业务id
-     *
-     * @param businessId   业务id
-     * @param businessType 业务类型
-     * @param param        文件信息
-     */
-    public static void bindingFile(Long businessId, Integer businessType, FileInfoParam param) {
-        if (ObjectUtil.isEmpty(param)) return;
-        R result = fileClient.bindingFile(APPLICATION_NAME, businessId, businessType, Collections.singletonList(param));
-        Assert.eqTrue(result.isSuccess(), "保存文件信息失败");
-    }
-
-    public static void bindingFile(Long businessId, FileInfoParam param) {
-        if (ObjectUtil.isEmpty(param)) return;
-        R result = fileClient.bindingFile(APPLICATION_NAME, businessId, 0, Collections.singletonList(param));
-        Assert.eqTrue(result.isSuccess(), "保存文件信息失败");
-    }
+    // 默认文件业务类型
+    private static final Integer DEFAULT_TYPE = 1;
 
     /**
      * 文件绑定业务id
@@ -51,28 +35,17 @@ public class FileClientUtil {
     }
 
     public static void bindingFile(Long businessId, List<FileInfoParam> paramList) {
-        if (ObjectUtil.isEmpty(paramList)) return;
-        R result = fileClient.bindingFile(APPLICATION_NAME, businessId, 0, paramList);
-        Assert.eqTrue(result.isSuccess(), "保存文件信息失败");
+        bindingFile(businessId, DEFAULT_TYPE, paramList);
     }
 
-    /**
-     * 修改文件绑定信息
-     *
-     * @param businessId   业务id
-     * @param businessType 业务类型
-     * @param param        文件信息
-     */
-    public static void againBindingFile(Long businessId, Integer businessType, FileInfoParam param) {
+    public static void bindingFile(Long businessId, Integer businessType, FileInfoParam param) {
         if (ObjectUtil.isEmpty(param)) return;
-        R result = fileClient.againBindingFile(APPLICATION_NAME, businessId, businessType, Collections.singletonList(param));
+        R result = fileClient.bindingFile(APPLICATION_NAME, businessId, businessType, Collections.singletonList(param));
         Assert.eqTrue(result.isSuccess(), "保存文件信息失败");
     }
 
-    public static void againBindingFile(Long businessId, FileInfoParam param) {
-        if (ObjectUtil.isEmpty(param)) return;
-        R result = fileClient.againBindingFile(APPLICATION_NAME, businessId, 0, Collections.singletonList(param));
-        Assert.eqTrue(result.isSuccess(), "保存文件信息失败");
+    public static void bindingFile(Long businessId, FileInfoParam param) {
+        bindingFile(businessId, DEFAULT_TYPE, param);
     }
 
     /**
@@ -89,11 +62,20 @@ public class FileClientUtil {
     }
 
     public static void againBindingFile(Long businessId, List<FileInfoParam> paramList) {
-        if (ObjectUtil.isEmpty(paramList)) return;
-        R result = fileClient.againBindingFile(APPLICATION_NAME, businessId, 0, paramList);
+        againBindingFile(businessId, DEFAULT_TYPE, paramList);
+    }
+
+    public static void againBindingFile(Long businessId, Integer businessType, FileInfoParam param) {
+        if (ObjectUtil.isEmpty(param)) return;
+        R result = fileClient.againBindingFile(APPLICATION_NAME, businessId, businessType, Collections.singletonList(param));
         Assert.eqTrue(result.isSuccess(), "保存文件信息失败");
     }
 
+    public static void againBindingFile(Long businessId, FileInfoParam param) {
+        againBindingFile(businessId, DEFAULT_TYPE, param);
+    }
+
+
     /**
      * 删除文件绑定信息
      *
@@ -113,20 +95,16 @@ public class FileClientUtil {
         return data.size() == 0 ? null : data.get(data.size() - 1);
     }
 
-    /**
-     * 获取文件信息
-     */
     public static List<FileInfoParam> getFileInfoList(Long businessId) {
         R<List<FileInfoParam>> result = fileClient.getFileInfo(Collections.singletonList(businessId));
         return result.getData();
     }
 
-    /**
-     * 获取文件信息
-     */
     public static Map<Long, FileInfoParam> getFileInfoMap(List<Long> businessId) {
+        if (ObjectUtil.isEmpty(businessId)){
+            return new HashMap<>();
+        }
         R<List<FileInfoParam>> result = fileClient.getFileInfo(businessId);
-
         return result.getData().stream().collect(Collectors.toMap(
                 FileInfoParam::getBusinessId,
                 item -> item,
@@ -134,16 +112,10 @@ public class FileClientUtil {
         ));
     }
 
-    /**
-     * 获取文件信息
-     */
     public static Map<Long, List<FileInfoParam>> getFileInfoListMap(List<Long> businessId) {
         R<List<FileInfoParam>> result = fileClient.getFileInfo(businessId);
-
         return result.getData().stream().collect(Collectors.groupingBy(
-                FileInfoParam::getBusinessId
-        ));
+                FileInfoParam::getBusinessId));
     }
 
-
 }

+ 1 - 1
hx-common/code-generator/src/main/java/com/fjhx/generator/CodeGenerator.java

@@ -79,7 +79,7 @@ public class CodeGenerator {
 
         // 包配置
         PackageConfig pc = new PackageConfig();
-        pc.setParent(parent + ".serve");// 包路径
+        pc.setParent(parent);// 包路径
         pc.setModuleName(moduleName); //模块名
         pc.setService("service");
         pc.setServiceImpl("service.impl");

+ 18 - 0
hx-service-api/iot-management-api/src/main/java/com/fjhx/constant/IotManagementLockConstant.java

@@ -0,0 +1,18 @@
+package com.fjhx.constant;
+
+/**
+ * 锁常量
+ */
+public interface IotManagementLockConstant {
+
+    /**
+     * 添加前缀,防止其他模块出现共用一把锁的情况
+     */
+    String PREFIX = "iotManagement-";
+
+    /**
+     * 保证一个产品只有一个bom是启用状态
+     */
+    String BOM_EDITION_LOCK = PREFIX + "bomEditionLock-";
+
+}

+ 7 - 1
hx-service/iot-management/src/main/java/com/fjhx/bom/controller/BomController.java

@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -52,5 +53,10 @@ public class BomController {
         return R.success();
     }
 
-}
+    @PostMapping("/getBomRelationProductClassify")
+    public R getBomRelationProductClassify() {
+        List<Map<String, Object>> result = bomService.getBomRelationProductClassify();
+        return R.success(result);
+    }
 
+}

+ 8 - 2
hx-service/iot-management/src/main/java/com/fjhx/bom/mapper/BomMapper.java

@@ -1,13 +1,16 @@
 package com.fjhx.bom.mapper;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fjhx.entity.bom.Bom;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fjhx.params.bom.BomEx;
-import com.fjhx.params.bom.BomVo;
+import org.apache.ibatis.annotations.MapKey;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * <p>
  * bom Mapper 接口
@@ -20,4 +23,7 @@ public interface BomMapper extends BaseMapper<Bom> {
 
     Page<BomEx> getPage(@Param("page") Page<Bom> page, @Param("ew") QueryWrapper<?> wrapper);
 
+    @MapKey("classifyId")
+    List<Map<String, Object>> getBomRelationProductClassify();
+
 }

+ 9 - 2
hx-service/iot-management/src/main/java/com/fjhx/bom/mapper/BomMapper.xml

@@ -11,11 +11,18 @@
                p.id   productId,
                p.name productName,
                p.code productCode,
-               c.name classifName
+               c.name classifyName
         from bom b
                  inner join product p on b.product_id = p.id
-                 left join classif c on p.classif_id = c.id
+                 left join classify c on p.classify_id = c.id
             ${ew.customSqlSegment}
     </select>
 
+    <select id="getBomRelationProductClassify" resultType="java.util.Map">
+        select distinct c.id classifyId, c.name classifyName
+        from bom b
+                 inner join product p on b.product_id = p.id
+                 inner join classify c on p.classify_id = c.id
+    </select>
+
 </mapper>

+ 7 - 2
hx-service/iot-management/src/main/java/com/fjhx/bom/service/BomService.java

@@ -1,12 +1,12 @@
 package com.fjhx.bom.service;
 
-import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.base.BaseService;
 import com.fjhx.entity.bom.Bom;
 import com.fjhx.params.bom.BomEx;
 import com.fjhx.params.bom.BomVo;
-import com.fjhx.base.BaseService;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -27,4 +27,9 @@ public interface BomService extends BaseService<Bom> {
 
     void delete(BomVo bomVo);
 
+    /**
+     * 获取bom已关联的产品分类
+     */
+    List<Map<String, Object>> getBomRelationProductClassify();
+
 }

+ 31 - 1
hx-service/iot-management/src/main/java/com/fjhx/bom/service/impl/BomServiceImpl.java

@@ -6,15 +6,20 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fjhx.base.BaseEntity;
 import com.fjhx.bom.mapper.BomMapper;
 import com.fjhx.bom.service.BomService;
+import com.fjhx.constant.ProductLibraryLockConstant;
+import com.fjhx.constants.ErrorMsgConstant;
 import com.fjhx.constants.StatusConstant;
 import com.fjhx.entity.bom.Bom;
 import com.fjhx.enums.bom.BomTypeEnum;
 import com.fjhx.params.FileInfoParam;
 import com.fjhx.params.bom.BomEx;
 import com.fjhx.params.bom.BomVo;
+import com.fjhx.utils.Assert;
 import com.fjhx.utils.FileClientUtil;
 import com.fjhx.utils.UserClientUtil;
 import com.fjhx.utils.WrapperUtil;
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.redis.lock.RedisLockClient;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -31,13 +36,16 @@ import java.util.stream.Collectors;
  * @since 2022-07-01
  */
 @Service
+@RequiredArgsConstructor
 public class BomServiceImpl extends ServiceImpl<BomMapper, Bom> implements BomService {
 
+    private final RedisLockClient redisLockClient;
+
     @Override
     public Page<BomEx> getPage(Map<String, String> condition) {
 
         QueryWrapper<?> wrapper = WrapperUtil.init(condition)
-                .eq("p.classif_id", "classifId") // 分类查询
+                .eq("p.classify_id", "classifyId") // 分类查询
                 .keyword("p.name", "p.code") // 关键字查询
                 .eqTenantId("b") // 租户id
                 .notDelete("b", "p") // 未删除
@@ -68,21 +76,43 @@ public class BomServiceImpl extends ServiceImpl<BomMapper, Bom> implements BomSe
     @Transactional
     @Override
     public void add(BomVo bomVo) {
+
+        Long productId = bomVo.getProductId();
+        Assert.notEmpty(productId, "产品id不能为空");
+        Assert.notEmpty(bomVo.getEdition(), "版本号不能为空");
+
         save(bomVo);
+        // 保证每个租户物料编码唯一
+//        Boolean flag = redisLockClient.lockFair(ProductLibraryLockConstant.MATERIAL_CODE_ONLY_LOCK + productId,
+//                () -> {
+//
+//                    return true;
+//                });
+
+//        Assert.eqTrue(flag, ErrorMsgConstant.SYSTEM_BUSY_ERROR);
+
         // 绑定文件
         FileClientUtil.bindingFile(bomVo.getId(), bomVo.getFileInfoParam());
     }
 
+    @Transactional
     @Override
     public void edit(BomVo bomVo) {
         updateById(bomVo);
+        // 重新绑定
         FileClientUtil.againBindingFile(bomVo.getId(), bomVo.getFileInfoParam());
     }
 
+    @Transactional
     @Override
     public void delete(BomVo bomVo) {
         removeById(bomVo.getId());
         FileClientUtil.relieveBindingFile(bomVo.getId());
     }
 
+    @Override
+    public List<Map<String, Object>> getBomRelationProductClassify() {
+        return baseMapper.getBomRelationProductClassify();
+    }
+
 }

+ 6 - 1
hx-service/service-file/src/main/java/com/fjhx/service/impl/FileInfoServiceImpl.java

@@ -151,11 +151,16 @@ public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> i
 
     @Override
     public void relieveBindingFile(Long businessId) {
-        remove(FileInfo::getBusinessId, businessId);
+        if (ObjectUtil.isNotEmpty(businessId)) {
+            remove(FileInfo::getBusinessId, businessId);
+        }
     }
 
     @Override
     public List<FileInfoParam> getFileInfo(List<Long> businessIdList) {
+        if (businessIdList.size() == 0) {
+            return new ArrayList<>();
+        }
         List<FileInfo> list = lambdaQuery().in(FileInfo::getBusinessId, businessIdList).list();
         return list.stream().map(item -> BeanUtil.toBean(item, FileInfoParam.class)).collect(Collectors.toList());
     }

+ 17 - 0
hx-service/storage/src/main/java/com/fjhx/stock/controller/StockWaterController.java

@@ -64,6 +64,23 @@ public class StockWaterController {
         return R.success(result);
     }
 
+    /**
+     * 今日剩余统计(原库存管理)
+     */
+    @PostMapping("remainingToday")
+    public R remainingToday(@RequestBody Map<String, String> condition) {
+        Map<String, Object> result = stockWaterService.remainingToday(condition);
+        return R.success(result);
+    }
+
+    /**
+     * 今日剩余分页列表(原库存管理)
+     */
+    @PostMapping("remainingTodayPage")
+    public R remainingTodayPage(@RequestBody Map<String, String> condition) {
+        Page<Map<String, Object>> result = stockWaterService.remainingTodayPage(condition);
+        return R.success(result);
+    }
 
 }
 

+ 9 - 1
hx-service/storage/src/main/java/com/fjhx/stock/mapper/StockWaterMapper.java

@@ -21,10 +21,18 @@ public interface StockWaterMapper extends BaseMapper<StockWater> {
 
     List<Map<String, Object>> getPickingStatistics(@Param("ew") QueryWrapper<?> wrapper);
 
-    Page<Map<String, Object>> getPickingPage(@Param("page") Page<StockWater> page,@Param("ew") QueryWrapper<?> wrapper);
+    Page<Map<String, Object>> getPickingPage(@Param("page") Page<StockWater> page, @Param("ew") QueryWrapper<?> wrapper);
 
     List<Map<String, Object>> contractPaymentStatistics(@Param("ew") QueryWrapper<?> wrapper);
 
     Page<Map<String, Object>> subscriptionAmountPage(@Param("page") Page<StockWater> page, @Param("ew") QueryWrapper<?> wrapper);
 
+    List<Map<String, Object>> remainingToday();
+
+    Page<Map<String, Object>> remainingTodayPage(@Param("page") Page<StockWater> page, @Param("ew") QueryWrapper<?> wrapper);
+
+    List<Map<String, Object>> selectMaterialHouse(@Param("ew") QueryWrapper<Object> wrapper);
+
+    List<String> selectMaterialIdByHouseId(@Param("houseId") String houseId);
+
 }

+ 83 - 12
hx-service/storage/src/main/java/com/fjhx/stock/mapper/StockWaterMapper.xml

@@ -16,18 +16,16 @@
     </select>
 
     <select id="getPickingPage" resultType="java.util.LinkedHashMap">
-        select sw.CreatedTime                                         createdTime,
-               m.TechnologyType                                       materialType,
-               m.code                                                 materialCode,
-               m.name                                                 materialName,
-               m.spec                                                 materialSpec,
-               m.StockUnitID                                          materialUnit,
-               sw.ChangeNum                                           changeNum,
-               sw.Price                                               materialPrice,
-               format(if(m.TechnologyType = 3,
-                         sw.Price * sw.ChangeNum,
-                         sw.Price * sw.ChangeNum * m.Width / 100), 2) money,
-               uu.RealName                                            realName
+        select sw.CreatedTime                     createdTime,
+               m.TechnologyType                   materialType,
+               m.code                             materialCode,
+               m.name                             materialName,
+               m.spec                             materialSpec,
+               m.StockUnitID                      materialUnit,
+               sw.ChangeNum                       changeNum,
+               sw.Price                           materialPrice,
+               format(sw.Price * sw.ChangeNum, 2) money,
+               uu.RealName                        realName
         from stock_waterdetial sw
                  inner join material m on sw.MaterialCode = m.code
                  inner join u_user uu on uu.ID = sw.OperUserId
@@ -61,4 +59,77 @@
             ${ew.customSqlSegment}
     </select>
 
+    <select id="remainingToday" resultType="java.util.Map">
+        select ifnull(m.Purpose, '其他')     materialPurpose,
+               ifnull(m.TechnologyType, 4) materialType,
+               m.Width                     materialWidth,
+               sd.Quantity                 quantity,
+               sd.Price                    price
+        from stock_detail sd
+                 inner join material m on sd.MaterialCode = m.code
+        where sd.IsDelete = 0
+          and m.TechnologyType is not null
+          and m.TechnologyType != 4
+    </select>
+
+    <select id="remainingTodayPage" resultType="java.util.LinkedHashMap">
+        SELECT m.ID                                                                               materialId,
+               mc.Name                                                                            categoryName,
+               m.Name                                                                             materialName,
+               m.Code                                                                             materialCode,
+               m.Width                                                                            materialWidth,
+               sum(sd.Quantity)                                                                   sum,
+               count(sd.Quantity)                                                                 count,
+               sum(IF(datediff(now(), sd.CreatedTime) > m.DelayPeriod, sd.Quantity, 0))           retentionQuantity,
+               m.SafetyStock                                                                      materialSafetyStock,
+               IF((a.CheckTagCount = a.TagQuantity) and (a.CheckTagCount = a.HandTagCount), 1, 0) inventoryResults,
+               a.CheckTagCount - a.TagQuantity                                                    correctionQuantity,
+               a.CheckTime                                                                        checkTime,
+               uu.RealName                                                                        OperUserName
+        FROM material m
+                 INNER JOIN stock_detail sd ON sd.MaterialCode = m.Code
+                 LEFT JOIN (
+            SELECT s.HandTagCount,
+                   s.TagQuantity,
+                   s.CheckTagCount,
+                   s.MaterialCode,
+                   s.CheckTime,
+                   s.RecordId
+            FROM (
+                     SELECT scd.HandTagCount,
+                            scd.TagQuantity,
+                            scd.CheckTagCount,
+                            scd.MaterialCode,
+                            scd.CheckTime,
+                            scd.RecordId
+                     FROM stock_checkrecorddetail scd
+                     WHERE scd.CheckTime IS NOT NULL
+                     ORDER BY scd.CheckTime DESC
+                 ) s
+            GROUP BY s.MaterialCode
+        ) a ON m.CODE = a.MaterialCode
+                 LEFT JOIN stock_checkrecord sc ON a.RecordId = sc.ID
+                 LEFT JOIN u_user uu ON uu.id = sc.OperUserId
+                 LEFT JOIN material_category mc ON mc.Code = m.CategoryCode
+            ${ew.customSqlSegment}
+    </select>
+
+    <select id="selectMaterialHouse" resultType="java.util.Map">
+        select m.id               materialId,
+               sh.Name            houseName,
+               sum(sd.Quantity)   sum,
+               count(sd.Quantity) count
+        from material m
+                 inner join stock_detail sd on sd.MaterialCode = m.code
+                 inner join stock_house sh on sh.id = sd.StockHouseId
+            ${ew.customSqlSegment}
+    </select>
+
+    <select id="selectMaterialIdByHouseId" resultType="java.lang.String">
+        select distinct sd.MaterialCode
+        from stock_detail sd
+        where sd.IsDelete = 0
+          and sd.StockHouseId = #{houseId}
+    </select>
+
 </mapper>

+ 6 - 1
hx-service/storage/src/main/java/com/fjhx/stock/service/StockWaterService.java

@@ -1,9 +1,10 @@
 package com.fjhx.stock.service;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.fjhx.entity.stock.StockWater;
 import com.fjhx.base.BaseService;
+import com.fjhx.entity.stock.StockWater;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -24,4 +25,8 @@ public interface StockWaterService extends BaseService<StockWater> {
 
     Page<Map<String, Object>> subscriptionAmountPage(Map<String, String> condition);
 
+    Map<String, Object> remainingToday(Map<String, String> condition);
+
+    Page<Map<String, Object>> remainingTodayPage(Map<String, String> condition);
+
 }

+ 148 - 0
hx-service/storage/src/main/java/com/fjhx/stock/service/impl/StockWaterServiceImpl.java

@@ -3,6 +3,7 @@ package com.fjhx.stock.service.impl;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fjhx.entity.stock.StockWater;
@@ -352,4 +353,151 @@ public class StockWaterServiceImpl extends ServiceImpl<StockWaterMapper, StockWa
         return result;
     }
 
+    @Override
+    public Map<String, Object> remainingToday(Map<String, String> condition) {
+
+        List<Map<String, Object>> list = baseMapper.remainingToday();
+
+        Map<String, Map<String, Object>> Statistics = list.stream()
+//                .filter(item -> !item.get("materialType").toString().equals("4")) // 过滤出其他不统计
+                .collect(Collectors.toMap(
+                        item -> item.get("materialType").toString().equals("3") ? "ink" : "fabric",
+                        this::remainingTodayGetMap,
+                        this::remainingTodayMerge
+                ));
+
+        HashMap<String, Object> absentInk = new HashMap<>();
+        absentInk.put("count", 0);
+        absentInk.put("sum", BigDecimal.ZERO);
+        absentInk.put("measureArea", BigDecimal.ZERO);
+        absentInk.put("money", BigDecimal.ZERO);
+        Statistics.putIfAbsent("ink", absentInk);
+
+        HashMap<String, Object> absentFabric = new HashMap<>();
+        absentInk.put("count", 0);
+        absentInk.put("sum", BigDecimal.ZERO);
+        absentInk.put("money", BigDecimal.ZERO);
+        Statistics.putIfAbsent("fabric", absentFabric);
+
+        HashMap<String, Object> result = new HashMap<>(Statistics);
+
+        Map<String, Map<String, Object>> typeStatistics = list.stream()
+                .filter(item -> !item.get("materialType").toString().equals("3")) // 过滤出墨水不统计
+                .collect(Collectors.toMap(
+                        item -> item.get("materialType").toString(),
+                        this::remainingTodayGetMap,
+                        this::remainingTodayMerge
+                ));
+
+        result.put("typeStatistics", typeStatistics);
+
+
+        Map<String, Map<String, Object>> purposeStatistics = list.stream()
+                .filter(item -> !item.get("materialType").toString().equals("3")) // 过滤出墨水不统计
+                .collect(Collectors.toMap(
+                        item -> item.get("materialPurpose").toString(),
+                        item -> {
+                            Map<String, Object> map = remainingTodayGetMap(item);
+                            map.put("materialPurpose", item.get("materialPurpose"));
+                            return map;
+                        },
+                        this::remainingTodayMerge
+                ));
+
+        result.put("purposeStatistics", new ArrayList<>(purposeStatistics.values()));
+
+        return result;
+    }
+
+    @Override
+    public Page<Map<String, Object>> remainingTodayPage(Map<String, String> condition) {
+        QueryWrapper<?> wrapper = WrapperUtil.init(condition)
+                .keyword("m.Code", "m.Name") // 关键字查询
+                .eq("m.CategoryCode", "categoryCode") // 物料分类
+                .eq("IF( a.CheckTagCount = a.TagQuantity AND a.CheckTagCount = a.HandTagCount, 1, 0)", "inventoryResults")
+                .getWrapper();
+
+        // 物料分组
+        wrapper.groupBy("m.id");
+        // 在库
+        wrapper.eq("sd.IsDelete", 0);
+
+        // 仓库id
+        String houseId = condition.get("houseId");
+        if (ObjectUtil.isNotEmpty(houseId)) {
+            List<String> materialCodeList = baseMapper.selectMaterialIdByHouseId(houseId);
+            wrapper.in("m.code", materialCodeList);
+        }
+
+        // 工艺类型
+        String technologyType = condition.get("technologyType");
+        if (ObjectUtil.isNotEmpty(technologyType)) {
+            if (technologyType.equals("4")) {
+                wrapper.in("m.TechnologyType", 0, 1, 2);
+            } else {
+                wrapper.eq("m.TechnologyType", technologyType);
+            }
+        }
+
+        // 用途
+        String purpose = condition.get("purpose");
+        if (ObjectUtil.isNotEmpty(purpose)) {
+            if (purpose.equals("其他")) {
+                wrapper.ne("m.TechnologyType", 3);
+                wrapper.eq("ifNull(m.Purpose,'其他')", purpose);
+            } else {
+                wrapper.eq("m.Purpose", purpose);
+            }
+        }
+
+        // 查询基本信息
+        Page<Map<String, Object>> page = baseMapper.remainingTodayPage(createPage(condition), wrapper);
+
+        List<Map<String, Object>> records = page.getRecords();
+        if (records.size() == 0) {
+            return page;
+        }
+
+        List<String> materialIdList = records.stream().map(item -> item.get("materialId").toString()).collect(Collectors.toList());
+
+        // 查库存所在信息
+        List<Map<String, Object>> materialHouseList = baseMapper.selectMaterialHouse(
+                Wrappers.query().in("m.id", materialIdList).eq("sd.IsDelete", 0).groupBy("m.id,sh.id"));
+
+        // 按物料id分组
+        Map<String, List<Map<String, Object>>> materialIdGroup =
+                materialHouseList.stream().collect(Collectors.groupingBy(item -> item.get("materialId").toString()));
+
+        records.forEach(item -> {
+            String materialId = item.get("materialId").toString();
+            // 赋值物料所在库存信息
+            item.put("houseInfo", materialIdGroup.get(materialId));
+        });
+
+        return page;
+    }
+
+    private Map<String, Object> remainingTodayGetMap(Map<String, Object> item) {
+        BigDecimal quantity = (BigDecimal) item.get("quantity");
+        Map<String, Object> map = new HashMap<>();
+        map.put("count", 1);
+        map.put("sum", quantity.setScale(2, RoundingMode.HALF_UP));
+        if (!item.get("materialType").toString().equals("3")) {
+            map.put("measureArea", quantity.multiply(BigDecimal.valueOf((float) item.get("materialWidth"))).setScale(2, RoundingMode.HALF_UP));
+        }
+        map.put("money", quantity.multiply((BigDecimal) item.get("price")).setScale(2, RoundingMode.HALF_UP));
+        return map;
+    }
+
+    private Map<String, Object> remainingTodayMerge(Map<String, Object> v1, Map<String, Object> v2) {
+        v1.put("count", (Integer) v1.get("count") + 1);
+        v1.put("sum", ((BigDecimal) v1.get("sum")).add((BigDecimal) v2.get("sum")));
+        Object measureArea = v1.get("measureArea");
+        if (measureArea != null) {
+            v1.put("measureArea", ((BigDecimal) measureArea).add((BigDecimal) v2.get("measureArea")));
+        }
+        v1.put("money", ((BigDecimal) v1.get("money")).add((BigDecimal) v2.get("money")));
+        return v1;
+    }
+
 }