24282 2 роки тому
батько
коміт
c41b4c8819
24 змінених файлів з 953 додано та 0 видалено
  1. 68 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/controller/group/GroupRecordDetailsController.java
  2. 26 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GetCombinableQuantityDto.java
  3. 17 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordDetailsDto.java
  4. 25 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordDetailsSelectDto.java
  5. 26 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordDto.java
  6. 17 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordSelectDto.java
  7. 41 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/po/GroupRecord.java
  8. 56 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/po/GroupRecordDetails.java
  9. 23 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/vo/GetCombinableQuantityVo.java
  10. 47 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/vo/GroupRecordDetailsVo.java
  11. 17 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/vo/GroupRecordVo.java
  12. 26 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/mapper/group/GroupRecordDetailsMapper.java
  13. 26 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/mapper/group/GroupRecordMapper.java
  14. 45 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/GroupRecordDetailsService.java
  15. 21 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/GroupRecordService.java
  16. 232 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/impl/GroupRecordDetailsServiceImpl.java
  17. 21 0
      hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/impl/GroupRecordServiceImpl.java
  18. 17 0
      hx-victoriatourist/src/main/resources/mapper/group/GroupRecordDetailsMapper.xml
  19. 18 0
      hx-victoriatourist/src/main/resources/mapper/group/GroupRecordMapper.xml
  20. 22 0
      hx-wms/src/main/java/com/fjhx/wms/entity/stock/bo/InOutBo.java
  21. 15 0
      hx-wms/src/main/java/com/fjhx/wms/entity/stock/emums/InOutType.java
  22. 35 0
      hx-wms/src/main/java/com/fjhx/wms/entity/stock/emums/JournalType.java
  23. 16 0
      hx-wms/src/main/java/com/fjhx/wms/service/stock/StockService.java
  24. 96 0
      hx-wms/src/main/java/com/fjhx/wms/service/stock/impl/StockServiceImpl.java

+ 68 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/controller/group/GroupRecordDetailsController.java

@@ -0,0 +1,68 @@
+package com.fjhx.victoriatourist.controller.group;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.victoriatourist.entity.group.dto.GetCombinableQuantityDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDetailsSelectDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDto;
+import com.fjhx.victoriatourist.entity.group.vo.GetCombinableQuantityVo;
+import com.fjhx.victoriatourist.entity.group.vo.GroupRecordDetailsVo;
+import com.fjhx.victoriatourist.service.group.GroupRecordDetailsService;
+import com.ruoyi.common.core.domain.BaseSelectDto;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+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;
+
+
+/**
+ * <p>
+ * 组合记录明细 前端控制器
+ * </p>
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@RestController
+@RequestMapping("/groupRecordDetails")
+public class GroupRecordDetailsController {
+
+    @Autowired
+    private GroupRecordDetailsService groupRecordDetailsService;
+
+    /**
+     * 组合记录明细分页
+     */
+    @PostMapping("/page")
+    public Page<GroupRecordDetailsVo> page(@Validated @RequestBody GroupRecordDetailsSelectDto dto) {
+        return groupRecordDetailsService.getPage(dto);
+    }
+
+    /**
+     * 组合记录明细明细
+     */
+    @PostMapping("/detail")
+    public List<GroupRecordDetailsVo> detail(@RequestBody BaseSelectDto dto) {
+        return groupRecordDetailsService.detail(dto.getId());
+    }
+
+    /**
+     * 获取可组合数量
+     */
+    @PostMapping("/getCombinableQuantity")
+    public GetCombinableQuantityVo getCombinableQuantity(@Validated @RequestBody GetCombinableQuantityDto dto) {
+        return groupRecordDetailsService.getCombinableQuantity(dto);
+    }
+
+    /**
+     * 组合/拆分
+     */
+    @PostMapping("/add")
+    public void add(@Validated @RequestBody GroupRecordDto dto) {
+        groupRecordDetailsService.add(dto);
+    }
+
+}

+ 26 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GetCombinableQuantityDto.java

@@ -0,0 +1,26 @@
+package com.fjhx.victoriatourist.entity.group.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Getter
+@Setter
+public class GetCombinableQuantityDto {
+
+    /**
+     * 仓库id
+     */
+    @NotNull(message = "仓库id不能为空")
+    private Long warehouseId;
+
+    /**
+     * 产品id列表
+     */
+    @NotEmpty(message = "组合id不能为空")
+    private List<Long> productIdList;
+
+}

+ 17 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordDetailsDto.java

@@ -0,0 +1,17 @@
+package com.fjhx.victoriatourist.entity.group.dto;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecordDetails;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 组合记录明细新增编辑入参实体
+ *
+ * @author 
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+public class GroupRecordDetailsDto extends GroupRecordDetails {
+
+}

+ 25 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordDetailsSelectDto.java

@@ -0,0 +1,25 @@
+package com.fjhx.victoriatourist.entity.group.dto;
+
+import com.ruoyi.common.core.domain.BaseSelectDto;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 组合记录明细列表查询入参实体
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+public class GroupRecordDetailsSelectDto extends BaseSelectDto {
+
+    /**
+     * 类型 1组合 2拆分
+     */
+    @NotNull(message = "组合类型不能为空")
+    private Integer type;
+
+}

+ 26 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordDto.java

@@ -0,0 +1,26 @@
+package com.fjhx.victoriatourist.entity.group.dto;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecord;
+import com.fjhx.victoriatourist.entity.group.po.GroupRecordDetails;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 组合记录新增编辑入参实体
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+public class GroupRecordDto extends GroupRecord {
+
+    @Valid
+    @NotEmpty(message = "组合明细不能为空")
+    private List<GroupRecordDetails> groupRecordDetailsList;
+
+}

+ 17 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/dto/GroupRecordSelectDto.java

@@ -0,0 +1,17 @@
+package com.fjhx.victoriatourist.entity.group.dto;
+
+import com.ruoyi.common.core.domain.BaseSelectDto;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 组合记录列表查询入参实体
+ *
+ * @author 
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+public class GroupRecordSelectDto extends BaseSelectDto {
+
+}

+ 41 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/po/GroupRecord.java

@@ -0,0 +1,41 @@
+package com.fjhx.victoriatourist.entity.group.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.common.core.domain.BasePo;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * <p>
+ * 组合记录
+ * </p>
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+@TableName("group_record")
+public class GroupRecord extends BasePo {
+
+    /**
+     * 类型 1组合 2拆分
+     */
+    @NotNull(message = "类型不能为空")
+    private Integer type;
+
+    /**
+     * 半成品所在仓库/拆分后放置仓库 id
+     */
+    @NotNull(message = "半成品所在仓库/拆分后放置仓库 id不能为空")
+    private Long productWarehouseId;
+
+    /**
+     * 组合后放置仓库/拆分前所在仓库 id
+     */
+    @NotNull(message = "组合后放置仓库/拆分前所在仓库 id不能为空")
+    private Long groupWarehouseId;
+
+}

+ 56 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/po/GroupRecordDetails.java

@@ -0,0 +1,56 @@
+package com.fjhx.victoriatourist.entity.group.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.common.core.domain.BasePo;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+/**
+ * <p>
+ * 组合记录明细
+ * </p>
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+@TableName("group_record_details")
+public class GroupRecordDetails extends BasePo {
+
+    /**
+     * 产品组合记录id
+     */
+    private Long groupRecordId;
+
+    /**
+     * 类型 1组合 2拆分
+     */
+    private Integer type;
+
+    /**
+     * 半成品所在仓库/拆分后放置仓库 id
+     */
+    private Long productWarehouseId;
+
+    /**
+     * 组合后放置仓库/拆分前所在仓库 id
+     */
+    private Long groupWarehouseId;
+
+    /**
+     * 产品id
+     */
+    @NotNull(message = "需要组合的产品id不能为空")
+    private Long productId;
+
+    /**
+     * 组合数量
+     */
+    @NotNull(message = "组合数量不能为空")
+    private BigDecimal groupNum;
+
+}

+ 23 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/vo/GetCombinableQuantityVo.java

@@ -0,0 +1,23 @@
+package com.fjhx.victoriatourist.entity.group.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+@Getter
+@Setter
+public class GetCombinableQuantityVo {
+
+    /**
+     * 组合需要数量
+     */
+    private Map<Long, Map<Long, BigDecimal>> combinableQuantity;
+
+    /**
+     * 产品库存map
+     */
+    private Map<Long, BigDecimal> stockMap;
+
+}

+ 47 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/vo/GroupRecordDetailsVo.java

@@ -0,0 +1,47 @@
+package com.fjhx.victoriatourist.entity.group.vo;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecordDetails;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 组合记录明细列表查询返回值实体
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+public class GroupRecordDetailsVo extends GroupRecordDetails {
+
+    /**
+     * 创建人名称
+     */
+    private String createUserName;
+
+    /**
+     * 组合产品仓库名称
+     */
+    private String groupWarehouseName;
+
+    /**
+     * 半成品仓库名称
+     */
+    private String productWarehouseName;
+
+    /**
+     * 组合编码
+     */
+    private String productCode;
+
+    /**
+     * 组合名称
+     */
+    private String productName;
+
+    /**
+     * 规格型号
+     */
+    private String productSpec;
+
+}

+ 17 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/entity/group/vo/GroupRecordVo.java

@@ -0,0 +1,17 @@
+package com.fjhx.victoriatourist.entity.group.vo;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecord;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 组合记录列表查询返回值实体
+ *
+ * @author 
+ * @since 2023-04-18
+ */
+@Getter
+@Setter
+public class GroupRecordVo extends GroupRecord {
+
+}

+ 26 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/mapper/group/GroupRecordDetailsMapper.java

@@ -0,0 +1,26 @@
+package com.fjhx.victoriatourist.mapper.group;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecordDetails;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.victoriatourist.entity.group.vo.GroupRecordDetailsVo;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * <p>
+ * 组合记录明细 Mapper 接口
+ * </p>
+ *
+ * @author 
+ * @since 2023-04-18
+ */
+public interface GroupRecordDetailsMapper extends BaseMapper<GroupRecordDetails> {
+
+    /**
+     * 组合记录明细分页
+     */
+    Page<GroupRecordDetailsVo> getPage(@Param("page") Page<Object> page, @Param("ew") IWrapper<GroupRecordDetails> wrapper);
+
+}

+ 26 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/mapper/group/GroupRecordMapper.java

@@ -0,0 +1,26 @@
+package com.fjhx.victoriatourist.mapper.group;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecord;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.victoriatourist.entity.group.vo.GroupRecordVo;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * <p>
+ * 组合记录 Mapper 接口
+ * </p>
+ *
+ * @author 
+ * @since 2023-04-18
+ */
+public interface GroupRecordMapper extends BaseMapper<GroupRecord> {
+
+    /**
+     * 组合记录分页
+     */
+    Page<GroupRecordVo> getPage(@Param("page") Page<Object> page, @Param("ew") IWrapper<GroupRecord> wrapper);
+
+}

+ 45 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/GroupRecordDetailsService.java

@@ -0,0 +1,45 @@
+package com.fjhx.victoriatourist.service.group;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.victoriatourist.entity.group.dto.GetCombinableQuantityDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDetailsSelectDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDto;
+import com.fjhx.victoriatourist.entity.group.po.GroupRecordDetails;
+import com.fjhx.victoriatourist.entity.group.vo.GetCombinableQuantityVo;
+import com.fjhx.victoriatourist.entity.group.vo.GroupRecordDetailsVo;
+import com.ruoyi.common.core.service.BaseService;
+
+import java.util.List;
+
+
+/**
+ * <p>
+ * 组合记录明细 服务类
+ * </p>
+ *
+ * @author
+ * @since 2023-04-18
+ */
+public interface GroupRecordDetailsService extends BaseService<GroupRecordDetails> {
+
+    /**
+     * 组合记录明细分页
+     */
+    Page<GroupRecordDetailsVo> getPage(GroupRecordDetailsSelectDto dto);
+
+    /**
+     * 组合记录明细明细
+     */
+    List<GroupRecordDetailsVo> detail(Long id);
+
+    /**
+     * 获取可组合数量
+     */
+    GetCombinableQuantityVo getCombinableQuantity(GetCombinableQuantityDto dto);
+
+    /**
+     * 组合/拆分
+     */
+    void add(GroupRecordDto dto);
+
+}

+ 21 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/GroupRecordService.java

@@ -0,0 +1,21 @@
+package com.fjhx.victoriatourist.service.group;
+
+import com.fjhx.victoriatourist.entity.group.po.GroupRecord;
+import com.ruoyi.common.core.service.BaseService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.victoriatourist.entity.group.vo.GroupRecordVo;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordSelectDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDto;
+
+
+/**
+ * <p>
+ * 组合记录 服务类
+ * </p>
+ *
+ * @author 
+ * @since 2023-04-18
+ */
+public interface GroupRecordService extends BaseService<GroupRecord> {
+
+}

+ 232 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/impl/GroupRecordDetailsServiceImpl.java

@@ -0,0 +1,232 @@
+package com.fjhx.victoriatourist.service.group.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.dynamic.datasource.annotation.DSTransactional;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fjhx.common.constant.SourceConstant;
+import com.fjhx.item.entity.product.po.ProductInfo;
+import com.fjhx.item.service.product.ProductInfoService;
+import com.fjhx.victoriatourist.entity.group.dto.GetCombinableQuantityDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDetailsSelectDto;
+import com.fjhx.victoriatourist.entity.group.dto.GroupRecordDto;
+import com.fjhx.victoriatourist.entity.group.po.GroupRecordDetails;
+import com.fjhx.victoriatourist.entity.group.vo.GetCombinableQuantityVo;
+import com.fjhx.victoriatourist.entity.group.vo.GroupRecordDetailsVo;
+import com.fjhx.victoriatourist.mapper.group.GroupRecordDetailsMapper;
+import com.fjhx.victoriatourist.service.group.GroupRecordDetailsService;
+import com.fjhx.victoriatourist.service.group.GroupRecordService;
+import com.fjhx.wms.entity.stock.bo.InOutBo;
+import com.fjhx.wms.entity.stock.emums.JournalType;
+import com.fjhx.wms.entity.stock.po.Stock;
+import com.fjhx.wms.service.stock.StockService;
+import com.fjhx.wms.service.warehouse.WarehouseService;
+import com.ruoyi.common.core.domain.BaseIdPo;
+import com.ruoyi.common.core.domain.BasePo;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import com.ruoyi.system.utils.UserUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * <p>
+ * 组合记录明细 服务实现类
+ * </p>
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Service
+public class GroupRecordDetailsServiceImpl extends ServiceImpl<GroupRecordDetailsMapper, GroupRecordDetails> implements GroupRecordDetailsService {
+
+    @Autowired
+    private WarehouseService warehouseService;
+
+    @Autowired
+    private ProductInfoService productInfoService;
+
+    @Autowired
+    private StockService stockService;
+
+    @Autowired
+    private GroupRecordService groupRecordService;
+
+    @Override
+    public Page<GroupRecordDetailsVo> getPage(GroupRecordDetailsSelectDto dto) {
+        IWrapper<GroupRecordDetails> wrapper = getWrapper();
+        wrapper.eq("grd", GroupRecordDetails::getType, dto.getType());
+        wrapper.orderByDesc("grd", GroupRecordDetails::getId);
+
+        Page<GroupRecordDetailsVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
+
+        List<GroupRecordDetailsVo> records = page.getRecords();
+        if (records.size() == 0) {
+            return page;
+        }
+
+        // 操作人
+        UserUtil.assignmentNickName(records, BasePo::getCreateUser, GroupRecordDetailsVo::setCreateUserName);
+
+        // 半成品、组合后放置仓库id
+        DynamicDataSourceContextHolder.push(SourceConstant.WMS);
+        warehouseService.attributeAssignBuilder(records)
+                .assignFun(GroupRecordDetails::getGroupWarehouseId, (item, warehouse) ->
+                        item.setGroupWarehouseName(warehouse.getName()))
+                .assignFun(GroupRecordDetails::getProductWarehouseId, (item, warehouse) ->
+                        item.setProductWarehouseName(warehouse.getName()))
+                .build();
+        DynamicDataSourceContextHolder.poll();
+
+        // 产品信息
+        productInfoService.attributeAssign(records, GroupRecordDetails::getProductId, (item, product) -> {
+            item.setProductCode(product.getCode());
+            item.setProductName(product.getName());
+            item.setProductSpec(product.getSpec());
+        });
+
+        return page;
+    }
+
+    @Override
+    public List<GroupRecordDetailsVo> detail(Long id) {
+        if (id == null) {
+            throw new ServiceException("产品组合记录id不能为空");
+        }
+        List<GroupRecordDetails> list = list(q -> q.eq(GroupRecordDetails::getGroupRecordId, id));
+        List<GroupRecordDetailsVo> groupRecordDetails = BeanUtil.copyToList(list, GroupRecordDetailsVo.class);
+        // 产品信息
+        productInfoService.attributeAssign(groupRecordDetails, GroupRecordDetails::getProductId, (item, product) -> {
+            item.setProductCode(product.getCode());
+            item.setProductName(product.getName());
+            item.setProductSpec(product.getSpec());
+        });
+        return groupRecordDetails;
+    }
+
+    @Override
+    public GetCombinableQuantityVo getCombinableQuantity(GetCombinableQuantityDto dto) {
+
+        List<Long> productIdList = dto.getProductIdList();
+        List<ProductInfo> productInfoList = productInfoService.listByIds(productIdList);
+        if (productInfoList.size() == 0) {
+            throw new ServiceException("产品列表为空");
+        }
+
+        Map<Long, Map<Long, BigDecimal>> combinableQuantity = new HashMap<>();
+        Set<Long> productIdSet = new HashSet<>();
+        for (ProductInfo productInfo : productInfoList) {
+            Map<Long, BigDecimal> groupProductNumMap = getGroupProductNumMap(productInfo);
+            combinableQuantity.put(productInfo.getId(), groupProductNumMap);
+            productIdSet.addAll(groupProductNumMap.keySet());
+        }
+
+        List<Stock> stockList = stockService.list(q -> q
+                .in(Stock::getProductId, productIdSet)
+                .eq(Stock::getWarehouseId, dto.getWarehouseId()));
+
+        Map<Long, BigDecimal> stockMap = stockList.stream().collect(Collectors.toMap(
+                Stock::getProductId,
+                Stock::getQuantity
+        ));
+
+        GetCombinableQuantityVo vo = new GetCombinableQuantityVo();
+        vo.setCombinableQuantity(combinableQuantity);
+        vo.setStockMap(stockMap);
+        return vo;
+    }
+
+    @DSTransactional
+    @Override
+    public void add(GroupRecordDto dto) {
+
+        // 添加组合主表
+        groupRecordService.save(dto);
+
+        List<GroupRecordDetails> groupRecordDetailsList = dto.getGroupRecordDetailsList();
+        groupRecordDetailsList.forEach(item -> {
+            item.setGroupRecordId(dto.getId());
+            item.setType(dto.getType());
+            item.setGroupWarehouseId(dto.getGroupWarehouseId());
+            item.setProductWarehouseId(dto.getProductWarehouseId());
+        });
+
+        // 添加组合明细表
+        saveBatch(groupRecordDetailsList);
+
+        // 组合产品id
+        List<Long> productIdList = groupRecordDetailsList.stream()
+                .map(GroupRecordDetails::getProductId).collect(Collectors.toList());
+        // 组合产品信息
+        Map<Long, ProductInfo> productInfoMap = productInfoService.mapKEntity(
+                BaseIdPo::getId, q -> q.in(BaseIdPo::getId, productIdList));
+
+        for (GroupRecordDetails groupRecordDetails : groupRecordDetailsList) {
+
+            // 获取组合产品有哪些半成品组合而成
+            ProductInfo productInfo = productInfoMap.get(groupRecordDetails.getProductId());
+            Map<Long, BigDecimal> groupProductNumMap = getGroupProductNumMap(productInfo);
+
+            InOutBo bo = new InOutBo();
+            bo.setProductId(groupRecordDetails.getProductId());
+            bo.setQuantity(groupRecordDetails.getGroupNum());
+
+            List<InOutBo> boList = new ArrayList<>();
+            groupProductNumMap.forEach((k, v) -> {
+                InOutBo outBo = new InOutBo();
+                outBo.setProductId(k);
+                outBo.setQuantity(v.multiply(groupRecordDetails.getGroupNum()));
+                boList.add(outBo);
+            });
+
+            // 组合产品入库或出库
+            stockService.inOut(Collections.singletonList(bo),
+                    dto.getGroupWarehouseId(),
+                    dto.getType() == 1 ? JournalType.COMBINATION_IN : JournalType.SPLIT_OUT,
+                    groupRecordDetails.getId());
+
+            // 半成品出库或入库
+            stockService.inOut(boList,
+                    dto.getGroupWarehouseId(),
+                    dto.getType() == 1 ? JournalType.COMBINATION_OUT : JournalType.SPLIT_IN,
+                    groupRecordDetails.getId());
+        }
+
+    }
+
+    /**
+     * 获取组合关联产品数量
+     */
+    private Map<Long, BigDecimal> getGroupProductNumMap(ProductInfo productInfo) {
+        String victoriatouristJson = productInfo.getVictoriatouristJson();
+        if (StrUtil.isBlank(victoriatouristJson)) {
+            throw new ServiceException("id为" + productInfo.getId() + "的产品victoriatouristJson字段为空");
+        }
+
+        JSONObject json = JSON.parseObject(victoriatouristJson);
+        JSONArray productCombinationList = json.getJSONArray("productCombinationList");
+        if (ObjectUtil.isEmpty(productCombinationList)) {
+            throw new ServiceException("id为" + productInfo.getId() + "的产品组合信息为空");
+        }
+
+        // 获取组合所需产品和数量
+        List<JSONObject> list = productCombinationList.toJavaList(JSONObject.class);
+        return list.stream().collect(Collectors.toMap(
+                item -> item.getLong("linkProductId"),
+                item -> item.getBigDecimal("linkQuantity"),
+                BigDecimal::add
+        ));
+    }
+
+}

+ 21 - 0
hx-victoriatourist/src/main/java/com/fjhx/victoriatourist/service/group/impl/GroupRecordServiceImpl.java

@@ -0,0 +1,21 @@
+package com.fjhx.victoriatourist.service.group.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fjhx.victoriatourist.entity.group.po.GroupRecord;
+import com.fjhx.victoriatourist.mapper.group.GroupRecordMapper;
+import com.fjhx.victoriatourist.service.group.GroupRecordService;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * <p>
+ * 组合记录 服务实现类
+ * </p>
+ *
+ * @author
+ * @since 2023-04-18
+ */
+@Service
+public class GroupRecordServiceImpl extends ServiceImpl<GroupRecordMapper, GroupRecord> implements GroupRecordService {
+
+}

+ 17 - 0
hx-victoriatourist/src/main/resources/mapper/group/GroupRecordDetailsMapper.xml

@@ -0,0 +1,17 @@
+<?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.fjhx.victoriatourist.mapper.group.GroupRecordDetailsMapper">
+    <select id="getPage" resultType="com.fjhx.victoriatourist.entity.group.vo.GroupRecordDetailsVo">
+        select grd.group_record_id,
+               grd.type,
+               grd.product_warehouse_id,
+               grd.group_warehouse_id,
+               grd.product_id,
+               grd.group_num,
+               grd.create_user,
+               grd.create_time
+        from group_record_details grd
+            ${ew.customSqlSegment}
+    </select>
+
+</mapper>

+ 18 - 0
hx-victoriatourist/src/main/resources/mapper/group/GroupRecordMapper.xml

@@ -0,0 +1,18 @@
+<?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.fjhx.victoriatourist.mapper.group.GroupRecordMapper">
+    <select id="getPage" resultType="com.fjhx.victoriatourist.entity.group.vo.GroupRecordVo">
+        select
+            gr.id,
+            gr.type,
+            gr.product_warehouse_id,
+            gr.group_warehouse_id,
+            gr.create_user,
+            gr.create_time,
+            gr.update_user,
+            gr.update_time
+        from group_record gr
+            ${ew.customSqlSegment}
+    </select>
+
+</mapper>

+ 22 - 0
hx-wms/src/main/java/com/fjhx/wms/entity/stock/bo/InOutBo.java

@@ -0,0 +1,22 @@
+package com.fjhx.wms.entity.stock.bo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+@Getter
+@Setter
+public class InOutBo {
+
+    /**
+     * 产品id
+     */
+    private Long productId;
+
+    /**
+     * 出入库数量
+     */
+    private BigDecimal quantity;
+
+}

+ 15 - 0
hx-wms/src/main/java/com/fjhx/wms/entity/stock/emums/InOutType.java

@@ -0,0 +1,15 @@
+package com.fjhx.wms.entity.stock.emums;
+
+public enum InOutType {
+
+    /**
+     * 入库
+     */
+    IN,
+
+    /**
+     * 出库
+     */
+    OUT
+
+}

+ 35 - 0
hx-wms/src/main/java/com/fjhx/wms/entity/stock/emums/JournalType.java

@@ -0,0 +1,35 @@
+package com.fjhx.wms.entity.stock.emums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum JournalType {
+
+    COMBINATION_IN(InOutType.IN, 6, "组合入库", "group_record_details"),
+    COMBINATION_OUT(InOutType.OUT, 7, "组合出库", "group_record_details"),
+    SPLIT_IN(InOutType.IN, 8, "组合拆分入库", "group_record_details"),
+    SPLIT_OUT(InOutType.OUT, 9, "组合拆分出库", "group_record_details");
+
+    /**
+     * 出入库类型
+     */
+    private final InOutType type;
+
+    /**
+     * 详细类型
+     */
+    private final Integer detailType;
+
+    /**
+     * 信息类型说明
+     */
+    private final String detailTypeRemark;
+
+    /**
+     * 关联业务表名
+     */
+    private final String linkBusinessTableName;
+
+}

+ 16 - 0
hx-wms/src/main/java/com/fjhx/wms/service/stock/StockService.java

@@ -1,8 +1,10 @@
 package com.fjhx.wms.service.stock;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.wms.entity.stock.bo.InOutBo;
 import com.fjhx.wms.entity.stock.dto.StockDto;
 import com.fjhx.wms.entity.stock.dto.StockSelectDto;
+import com.fjhx.wms.entity.stock.emums.JournalType;
 import com.fjhx.wms.entity.stock.po.Stock;
 import com.fjhx.wms.entity.stock.po.StockJournalDetails;
 import com.fjhx.wms.entity.stock.vo.StockVo;
@@ -30,6 +32,7 @@ public interface StockService extends BaseService<Stock> {
      * 维多利亚 按仓库分类分页
      */
     Page<StockVo> pageByWarehouse(StockSelectDto dto);
+
     /**
      * 维多利亚 按产品分类分页
      */
@@ -72,5 +75,18 @@ public interface StockService extends BaseService<Stock> {
      */
     void defectiveToQualified(Stock stock);
 
+    /**
+     * 通用出/入库方法
+     *
+     * <p>
+     * 调用时记得加上事务注解
+     * </P>
+     *
+     * @param list        产品改变数量
+     * @param warehouseId 仓库id
+     * @param journalType 出/入库类型
+     * @param businessId  业务id
+     */
+    void inOut(List<? extends InOutBo> list, Long warehouseId, JournalType journalType, Long businessId);
 
 }

+ 96 - 0
hx-wms/src/main/java/com/fjhx/wms/service/stock/impl/StockServiceImpl.java

@@ -1,13 +1,18 @@
 package com.fjhx.wms.service.stock.impl;
 
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fjhx.item.entity.product.po.ProductInfo;
 import com.fjhx.item.service.product.ProductInfoService;
+import com.fjhx.wms.entity.stock.bo.InOutBo;
 import com.fjhx.wms.entity.stock.dto.StockDto;
 import com.fjhx.wms.entity.stock.dto.StockJournalDetailsDto;
 import com.fjhx.wms.entity.stock.dto.StockSelectDto;
+import com.fjhx.wms.entity.stock.emums.InOutType;
+import com.fjhx.wms.entity.stock.emums.JournalType;
 import com.fjhx.wms.entity.stock.po.Stock;
 import com.fjhx.wms.entity.stock.po.StockJournal;
 import com.fjhx.wms.entity.stock.po.StockJournalDetails;
@@ -19,6 +24,7 @@ import com.fjhx.wms.service.stock.StockJournalService;
 import com.fjhx.wms.service.stock.StockService;
 import com.fjhx.wms.service.warehouse.WarehouseService;
 import com.obs.services.internal.ServiceException;
+import com.ruoyi.common.core.domain.BaseIdPo;
 import com.ruoyi.common.utils.wrapper.IWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -303,6 +309,20 @@ public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements
         updateById(stock1);
     }
 
+    @Override
+    public void inOut(List<? extends InOutBo> list, Long warehouseId, JournalType journalType, Long businessId) {
+
+        // 改变库存数量
+        changeStock(list, warehouseId, journalType);
+
+        // 增加出入库记录
+        Long journalId = addJournal(warehouseId, journalType, businessId);
+
+        // 添加出入库明细
+        addJournalDetails(list, journalId);
+
+    }
+
     /**
      * 良品转次品
      */
@@ -323,5 +343,81 @@ public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements
         updateById(stock1);
     }
 
+    /**
+     * 改变库存数量
+     */
+    private void changeStock(List<? extends InOutBo> list, Long warehouseId, JournalType journalType) {
+
+        Map<Long, BigDecimal> map = list.stream().collect(Collectors.toMap(
+                InOutBo::getProductId,
+                InOutBo::getQuantity,
+                BigDecimal::add
+        ));
+
+        if (journalType.getType().equals(InOutType.IN)) {
+            map.forEach((productId, quantity) -> {
+                Stock stock = getOne(q -> q.eq(Stock::getProductId, productId).eq(Stock::getWarehouseId, warehouseId));
+
+                if (stock == null) {
+                    stock = new Stock();
+                    stock.setWarehouseId(warehouseId);
+                    stock.setProductId(productId);
+                    stock.setQuantity(quantity);
+                    save(stock);
+                } else {
+                    Long stockId = stock.getId();
+                    update(q -> q
+                            .eq(BaseIdPo::getId, stockId)
+                            .setSql("quantity = quantity + " + quantity)
+                    );
+                }
+            });
+        } else {
+            map.forEach((productId, quantity) -> {
+                boolean update = update(q -> q
+                        .setSql("quantity = quantity - '" + quantity + "'")
+                        .eq(Stock::getProductId, productId)
+                        .eq(Stock::getWarehouseId, warehouseId)
+                        .apply("quantity - {0} >= 0", quantity)
+                );
+
+                if (!update) {
+                    ProductInfo productInfo = productInfoService.getById(productId);
+                    if (productInfo == null) {
+                        throw new ServiceException("产品id" + productId + "不存在");
+                    }
+
+                    String err = "产品 {} 库存不足,出库失败";
+                    throw new ServiceException(StrUtil.format(err, productInfo.getName()));
+                }
+            });
+        }
+    }
+
+    /**
+     * 添加出入库记录
+     */
+    private Long addJournal(Long warehouseId, JournalType journalType, Long businessId) {
+        StockJournal stockJournal = new StockJournal();
+        stockJournal.setType(journalType.getDetailType());
+        stockJournal.setBusinessId(businessId);
+        stockJournal.setWarehouseId(warehouseId);
+        stockJournalService.save(stockJournal);
+        return stockJournal.getId();
+    }
+
+    /**
+     * 添加出入库明细
+     */
+    private void addJournalDetails(List<? extends InOutBo> list, Long journalId) {
+        List<StockJournalDetails> journalDetailsList = list.stream().map(item -> {
+            StockJournalDetails stockJournalDetails = new StockJournalDetails();
+            stockJournalDetails.setStockJournalId(journalId);
+            stockJournalDetails.setProductId(item.getProductId());
+            stockJournalDetails.setQuantity(item.getQuantity());
+            return stockJournalDetails;
+        }).collect(Collectors.toList());
+        stockJournalDetailsService.saveBatch(journalDetailsList);
+    }
 
 }