Merge branch 'master' of https://gitea.zianxin.com/zax/biz-api-cloud
This commit is contained in:
commit
e56d6da230
15 files changed
+1139
-78
No files matched your search
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, pjcloud All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: pjcloud
|
||||
*
|
||||
*/
|
||||
|
||||
package com.bsd.pjcloud.supermarket.api.feign;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.supermarket.dto.MallProductStockChangeDTO;
|
||||
import com.bsd.pjcloud.supermarket.entity.MallProductEntity;
|
||||
import com.bsd.pjcloud.supermarket.vo.mall.MallCategoryTreeVo;
|
||||
import com.bsd.pjcloud.supermarket.vo.mall.MallProductDetailAppVo;
|
||||
import com.bsd.pjcloud.supermarket.vo.mall.MallProductListAppVo;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 共富商城商品远程调用服务
|
||||
*
|
||||
* @author zjf
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@FeignClient(contextId = "remoteMallProductService", value = ServiceNameConstants.SUPERMARKET_SERVICE)
|
||||
public interface RemoteMallProductService {
|
||||
|
||||
/**
|
||||
* 批量扣减/增加库存(正数增加,负数扣减)
|
||||
*
|
||||
* @param changes 库存变更DTO
|
||||
* @return 是否成功
|
||||
*/
|
||||
@PostMapping("/mallProduct/deductStock")
|
||||
R<Boolean> deductStock(@RequestBody MallProductStockChangeDTO changes);
|
||||
|
||||
/**
|
||||
* 购物车校验库存
|
||||
*
|
||||
* @param productId 商品ID(多SKU模式传SKU ID,单SPU模式传SPU ID)
|
||||
* @param productType 商品类型(1=SKU, 2=SPU)
|
||||
* @param incrCount 扣减数量(正数或负数)
|
||||
* @return 库存是否充足
|
||||
*/
|
||||
@GetMapping("/mallProduct/checkStock")
|
||||
R<Boolean> checkStock(@RequestParam("productId") Long productId,
|
||||
@RequestParam("productType") Integer productType,
|
||||
@RequestParam("incrCount") Double incrCount);
|
||||
|
||||
/**
|
||||
* 查询商城商品分类树
|
||||
*
|
||||
* @param merchantId 商户ID(可选)
|
||||
* @return 分类树列表
|
||||
*/
|
||||
@GetMapping("/mallCategory/tree")
|
||||
R<List<MallCategoryTreeVo>> getCategoryTree(@RequestParam(value = "merchantId", required = false) Long merchantId);
|
||||
|
||||
/**
|
||||
* 分页查询商品列表
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param categoryId 分类ID(可选)
|
||||
* @param keyword 关键词(可选)
|
||||
* @param merchantId 商户ID(可选)
|
||||
* @param status 状态(可选)
|
||||
* @return 分页结果
|
||||
*/
|
||||
@GetMapping("/mallProduct/page/vo")
|
||||
R<Page<MallProductListAppVo>> page(@RequestParam Page page,
|
||||
@RequestParam(value = "categoryId", required = false) Long categoryId,
|
||||
@RequestParam(value = "keyword", required = false) String keyword,
|
||||
@RequestParam(value = "merchantId", required = false) Long merchantId,
|
||||
@RequestParam(value = "status", required = false) String status);
|
||||
|
||||
/**
|
||||
* 查询所有商品(不分页)
|
||||
*
|
||||
* @param merchantId 商户ID(可选)
|
||||
* @param status 状态(可选)
|
||||
* @return 商品列表
|
||||
*/
|
||||
@GetMapping("/mallProduct/list")
|
||||
R<List<MallProductEntity>> list(@RequestParam(value = "merchantId", required = false) Long merchantId,
|
||||
@RequestParam(value = "status", required = false) String status);
|
||||
|
||||
/**
|
||||
* 根据商品ID查询详情
|
||||
*
|
||||
* @param id 商品ID
|
||||
* @return 商品详情(包含SKU)
|
||||
*/
|
||||
@GetMapping("/mallProduct/detail")
|
||||
R<MallProductDetailAppVo> getDetailById(@RequestParam("id") Long id);
|
||||
|
||||
/**
|
||||
* 根据商品ID列表批量查询详情
|
||||
*
|
||||
* @param ids 商品ID列表
|
||||
* @return 商品详情列表(包含SKU)
|
||||
*/
|
||||
@GetMapping("/mallProduct/detailByIds")
|
||||
R<List<MallProductDetailAppVo>> getDetailByIds(@RequestParam("ids") List<Long> ids);
|
||||
|
||||
/**
|
||||
* 根据商品ID列表批量查询基本信息
|
||||
*
|
||||
* @param ids 商品ID列表
|
||||
* @return 商品基本信息列表
|
||||
*/
|
||||
@GetMapping("/mallProduct/listByIds")
|
||||
R<List<MallProductEntity>> getListByIds(@RequestParam("ids") List<Long> ids);
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.bsd.pjcloud.supermarket.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 共富商城商品库存变更DTO
|
||||
*
|
||||
* @author zjf
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "共富商城商品库存变更DTO")
|
||||
public class MallProductStockChangeDTO {
|
||||
|
||||
@NotNull(message = "商品列表不能为空")
|
||||
@Schema(description = "商品列表")
|
||||
private List<Item> items;
|
||||
|
||||
@Schema(description = "下单设备类型,0默认安卓机,1移动端")
|
||||
private Integer deviceType;
|
||||
|
||||
@Data
|
||||
@Schema(description = "库存变更明细")
|
||||
public static class Item {
|
||||
|
||||
@NotNull(message = "商品ID不能为空(SPU或SKU ID)")
|
||||
@Schema(description = "商品ID(多SKU模式传SKU ID,单SPU模式传SPU ID)")
|
||||
private Long id;
|
||||
|
||||
@NotNull(message = "库存变化数量不能为空")
|
||||
@Schema(description = "库存变化数量(正数增加,负数扣减)")
|
||||
private Double incrCount;
|
||||
|
||||
@Schema(description = "商品类型(1=SKU, 2=SPU)")
|
||||
private Integer productType;
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -96,5 +96,6 @@ public class InventoryRecordEditItemDTO {
|
||||
@Schema(description="包装单位id")
|
||||
private Long unitId;
|
||||
|
||||
// 注意:packageNum 不需要前端传递,由后端根据 unitId 自动查询并设置
|
||||
|
||||
}
|
||||
+1
@@ -55,5 +55,6 @@ public class InventoryRecordSaveItemDTO {
|
||||
@Schema(description="实际进价")
|
||||
private Integer realPrice;
|
||||
|
||||
// 注意:packageNum 不需要前端传递,由后端根据 unitId 自动查询并设置
|
||||
|
||||
}
|
||||
+2
-2
@@ -99,8 +99,8 @@ public class MallProductEntity extends Model<MallProductEntity> {
|
||||
/**
|
||||
* 计量单位
|
||||
*/
|
||||
@Schema(description="计量单位")
|
||||
private String unit;
|
||||
@Schema(description="商品性质")
|
||||
private String productNature;
|
||||
|
||||
/**
|
||||
* 状态(1上架 0下架)
|
||||
File renamed without changes.
+6
@@ -166,4 +166,10 @@ public class SmErpInventoryRecordsItemEntity extends Model<SmErpInventoryRecords
|
||||
|
||||
@Schema(description="实际单价")
|
||||
private Integer realPrice;
|
||||
|
||||
/**
|
||||
* 内装数(保存时的内装数,用于编辑时准确计算库存)
|
||||
*/
|
||||
@Schema(description="内装数")
|
||||
private Integer packageNum;
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package com.bsd.pjcloud.supermarket.vo.mall;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 共富商城移动端商品详情VO
|
||||
*
|
||||
* @author zjf
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "共富商城移动端商品详情VO")
|
||||
public class MallProductDetailAppVo {
|
||||
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
@Schema(description = "商品ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 商户ID
|
||||
*/
|
||||
@Schema(description = "商户ID")
|
||||
private Long merchantId;
|
||||
|
||||
/**
|
||||
* 类目ID
|
||||
*/
|
||||
@Schema(description = "类目ID")
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 商品标题
|
||||
*/
|
||||
@Schema(description = "商品标题")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 副标题
|
||||
*/
|
||||
@Schema(description = "副标题")
|
||||
private String subTitle;
|
||||
|
||||
/**
|
||||
* 品牌
|
||||
*/
|
||||
@Schema(description = "品牌")
|
||||
private String brand;
|
||||
|
||||
/**
|
||||
* 主图
|
||||
*/
|
||||
@Schema(description = "主图")
|
||||
private String coverImage;
|
||||
|
||||
/**
|
||||
* 轮播图
|
||||
*/
|
||||
@Schema(description = "轮播图")
|
||||
private String carouselImages;
|
||||
|
||||
/**
|
||||
* 定价方式(1多SKU 2单价SPU)
|
||||
*/
|
||||
@Schema(description = "定价方式")
|
||||
private Integer priceType;
|
||||
|
||||
/**
|
||||
* SPU售价(单位:分)
|
||||
*/
|
||||
@Schema(description = "SPU售价(单位:分)")
|
||||
private Integer spuPrice;
|
||||
|
||||
/**
|
||||
* 划线价(单位:分)
|
||||
*/
|
||||
@Schema(description = "划线价(单位:分)")
|
||||
private Integer originalPrice;
|
||||
|
||||
/**
|
||||
* 总库存
|
||||
*/
|
||||
@Schema(description = "总库存")
|
||||
private Double totalStock;
|
||||
|
||||
/**
|
||||
* 商品性质
|
||||
*/
|
||||
@Schema(description = "商品性质")
|
||||
private String productNature;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 开售时间
|
||||
*/
|
||||
@Schema(description = "开售时间")
|
||||
private LocalDateTime saleStartTime;
|
||||
|
||||
/**
|
||||
* 停售时间
|
||||
*/
|
||||
@Schema(description = "停售时间")
|
||||
private LocalDateTime saleEndTime;
|
||||
|
||||
/**
|
||||
* 详情描述
|
||||
*/
|
||||
@Schema(description = "详情描述")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* SKU列表
|
||||
*/
|
||||
@Schema(description = "SKU列表")
|
||||
private List<MallProductSkuAppVo> skuList;
|
||||
}
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.bsd.pjcloud.supermarket.vo.mall;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 共富商城移动端商品列表VO
|
||||
*
|
||||
* @author zjf
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "共富商城移动端商品列表VO")
|
||||
public class MallProductListAppVo {
|
||||
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
@Schema(description = "商品ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 商户ID
|
||||
*/
|
||||
@Schema(description = "商户ID")
|
||||
private Long merchantId;
|
||||
|
||||
/**
|
||||
* 类目ID
|
||||
*/
|
||||
@Schema(description = "类目ID")
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 商品标题
|
||||
*/
|
||||
@Schema(description = "商品标题")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 副标题
|
||||
*/
|
||||
@Schema(description = "副标题")
|
||||
private String subTitle;
|
||||
|
||||
/**
|
||||
* 品牌
|
||||
*/
|
||||
@Schema(description = "品牌")
|
||||
private String brand;
|
||||
|
||||
/**
|
||||
* 主图
|
||||
*/
|
||||
@Schema(description = "主图")
|
||||
private String coverImage;
|
||||
|
||||
/**
|
||||
* 轮播图
|
||||
*/
|
||||
@Schema(description = "轮播图")
|
||||
private String carouselImages;
|
||||
|
||||
/**
|
||||
* 定价方式(1多SKU 2单价SPU)
|
||||
*/
|
||||
@Schema(description = "定价方式")
|
||||
private Integer priceType;
|
||||
|
||||
/**
|
||||
* SPU售价(单位:分)
|
||||
*/
|
||||
@Schema(description = "SPU售价(单位:分)")
|
||||
private Integer spuPrice;
|
||||
|
||||
/**
|
||||
* 划线价(单位:分)
|
||||
*/
|
||||
@Schema(description = "划线价(单位:分)")
|
||||
private Integer originalPrice;
|
||||
|
||||
/**
|
||||
* 总库存
|
||||
*/
|
||||
@Schema(description = "总库存")
|
||||
private Double totalStock;
|
||||
|
||||
/**
|
||||
* 商品性质
|
||||
*/
|
||||
@Schema(description = "商品性质")
|
||||
private String productNature;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 最低价格(从SKU中计算,单位:分)
|
||||
*/
|
||||
@Schema(description = "最低价格(单位:分)")
|
||||
private Integer minPrice;
|
||||
}
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.bsd.pjcloud.supermarket.vo.mall;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 共富商城移动端商品SKU VO
|
||||
*
|
||||
* @author zjf
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "共富商城移动端商品SKU VO")
|
||||
public class MallProductSkuAppVo {
|
||||
|
||||
/**
|
||||
* SKU ID
|
||||
*/
|
||||
@Schema(description = "SKU ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* SPU ID
|
||||
*/
|
||||
@Schema(description = "SPU ID")
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* SKU编码
|
||||
*/
|
||||
@Schema(description = "SKU编码")
|
||||
private String skuCode;
|
||||
|
||||
/**
|
||||
* SKU名称(规格组合)
|
||||
*/
|
||||
@Schema(description = "SKU名称(规格组合)")
|
||||
private String skuName;
|
||||
|
||||
/**
|
||||
* 售价(单位:分)
|
||||
*/
|
||||
@Schema(description = "售价(单位:分)")
|
||||
private Integer price;
|
||||
|
||||
|
||||
/**
|
||||
* 库存
|
||||
*/
|
||||
@Schema(description = "库存")
|
||||
private Double stock;
|
||||
|
||||
/**
|
||||
* 条码
|
||||
*/
|
||||
@Schema(description = "条码")
|
||||
private String barcode;
|
||||
|
||||
/**
|
||||
* 重量(克)
|
||||
*/
|
||||
@Schema(description = "重量(克)")
|
||||
private Integer weightG;
|
||||
|
||||
|
||||
/**
|
||||
* 状态(1启用 0停用)
|
||||
*/
|
||||
@Schema(description = "状态(1启用 0停用)")
|
||||
private String status;
|
||||
}
|
||||
|
||||
+332
-3
@@ -9,8 +9,14 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.log.annotation.SysLog;
|
||||
import com.bsd.pjcloud.supermarket.entity.MallProductEntity;
|
||||
import com.bsd.pjcloud.supermarket.entity.MallProductSkuEntity;
|
||||
import com.bsd.pjcloud.supermarket.service.MallProductService;
|
||||
import com.bsd.pjcloud.supermarket.service.MallProductSkuService;
|
||||
import com.bsd.pjcloud.supermarket.dto.MallProductCreateDTO;
|
||||
import com.bsd.pjcloud.supermarket.dto.MallProductStockChangeDTO;
|
||||
import com.bsd.pjcloud.supermarket.vo.mall.MallProductDetailAppVo;
|
||||
import com.bsd.pjcloud.supermarket.vo.mall.MallProductListAppVo;
|
||||
import com.bsd.pjcloud.supermarket.vo.mall.MallProductSkuAppVo;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
|
||||
@@ -39,21 +45,75 @@ import java.util.Objects;
|
||||
public class MallProductController {
|
||||
|
||||
private final MallProductService mallProductService;
|
||||
private final MallProductSkuService mallProductSkuService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* 分页查询(管理端)
|
||||
* @param page 分页对象
|
||||
* @param mallProduct 共富商城商品信息
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "分页查询" , description = "分页查询" )
|
||||
@Operation(summary = "分页查询(管理端)" , description = "分页查询" )
|
||||
@GetMapping("/page" )
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_mallProduct_view')" )
|
||||
public R getMallProductPage(@ParameterObject Page page, @ParameterObject MallProductEntity mallProduct) {
|
||||
LambdaQueryWrapper<MallProductEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(Objects.nonNull(mallProduct.getCategoryId()),MallProductEntity::getCategoryId,mallProduct.getCategoryId());
|
||||
wrapper.like(StrUtil.isNotBlank(mallProduct.getTitle()),MallProductEntity::getTitle,mallProduct.getTitle());
|
||||
return R.ok(mallProductService.page(page, wrapper));
|
||||
wrapper.eq(Objects.nonNull(mallProduct.getMerchantId()),MallProductEntity::getMerchantId,mallProduct.getMerchantId());
|
||||
wrapper.eq(StrUtil.isNotBlank(mallProduct.getStatus()),MallProductEntity::getStatus,mallProduct.getStatus());
|
||||
|
||||
// 如果是 Feign 调用,返回 MallProductListAppVo 格式
|
||||
Page<MallProductEntity> resultPage = mallProductService.page(page, wrapper);
|
||||
return R.ok(resultPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询商品列表(Feign专用 - 返回VO格式)
|
||||
* @param page 分页对象
|
||||
* @param categoryId 分类ID
|
||||
* @param keyword 关键词
|
||||
* @param merchantId 商户ID
|
||||
* @param status 状态
|
||||
* @return
|
||||
*/
|
||||
@Operation(summary = "分页查询商品列表(Feign)" , description = "分页查询商品列表,返回VO格式" )
|
||||
@GetMapping("/page/vo" )
|
||||
public R<Page<MallProductListAppVo>> getProductPageVo(
|
||||
@ParameterObject Page page,
|
||||
@RequestParam(value = "categoryId", required = false) Long categoryId,
|
||||
@RequestParam(value = "keyword", required = false) String keyword,
|
||||
@RequestParam(value = "merchantId", required = false) Long merchantId,
|
||||
@RequestParam(value = "status", required = false) String status) {
|
||||
try {
|
||||
LambdaQueryWrapper<MallProductEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(categoryId != null, MallProductEntity::getCategoryId, categoryId);
|
||||
wrapper.eq(merchantId != null, MallProductEntity::getMerchantId, merchantId);
|
||||
wrapper.eq(StrUtil.isNotBlank(status), MallProductEntity::getStatus, status);
|
||||
|
||||
// 关键词搜索:标题或副标题
|
||||
if (StrUtil.isNotBlank(keyword)) {
|
||||
wrapper.and(w -> w.like(MallProductEntity::getTitle, keyword)
|
||||
.or()
|
||||
.like(MallProductEntity::getSubTitle, keyword));
|
||||
}
|
||||
|
||||
// 排序:按创建时间倒序
|
||||
wrapper.orderByDesc(MallProductEntity::getCreateTime);
|
||||
|
||||
Page<MallProductEntity> resultPage = mallProductService.page(page, wrapper);
|
||||
|
||||
// 转换为 VO 格式
|
||||
Page<MallProductListAppVo> voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal());
|
||||
List<MallProductListAppVo> voList = resultPage.getRecords().stream()
|
||||
.map(this::convertToListVo)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
voPage.setRecords(voList);
|
||||
|
||||
return R.ok(voPage);
|
||||
} catch (Exception e) {
|
||||
return R.failed("分页查询商品失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,4 +209,273 @@ public class MallProductController {
|
||||
public List<MallProductEntity> export(MallProductEntity mallProduct,Long[] ids) {
|
||||
return mallProductService.list(Wrappers.lambdaQuery(mallProduct).in(ArrayUtil.isNotEmpty(ids), MallProductEntity::getId, ids));
|
||||
}
|
||||
|
||||
// ==================== Feign 接口方法 ====================
|
||||
|
||||
/**
|
||||
* 批量扣减/增加库存(正数增加,负数扣减)
|
||||
*/
|
||||
@Operation(summary = "批量扣减/增加库存", description = "批量扣减/增加库存")
|
||||
@PostMapping("/deductStock")
|
||||
public R<Boolean> deductStock(@Valid @RequestBody MallProductStockChangeDTO changes) {
|
||||
try {
|
||||
if (CollUtil.isEmpty(changes.getItems())) {
|
||||
return R.failed("库存变更项不能为空");
|
||||
}
|
||||
|
||||
for (MallProductStockChangeDTO.Item item : changes.getItems()) {
|
||||
if (item.getProductType() == null) {
|
||||
return R.failed("商品类型不能为空");
|
||||
}
|
||||
|
||||
if (item.getProductType() == 1) {
|
||||
// SKU 模式:更新 SKU 库存
|
||||
MallProductSkuEntity sku = mallProductSkuService.getById(item.getId());
|
||||
if (sku == null) {
|
||||
return R.failed("SKU不存在:" + item.getId());
|
||||
}
|
||||
|
||||
Double newStock = (sku.getStock() != null ? sku.getStock() : 0D) + item.getIncrCount();
|
||||
if (newStock < 0) {
|
||||
return R.failed("SKU库存不足:" + sku.getSkuName());
|
||||
}
|
||||
|
||||
sku.setStock(newStock);
|
||||
mallProductSkuService.updateById(sku);
|
||||
|
||||
// 更新 SPU 总库存
|
||||
updateSpuTotalStock(sku.getProductId());
|
||||
|
||||
} else if (item.getProductType() == 2) {
|
||||
// SPU 模式:直接更新 SPU 总库存
|
||||
MallProductEntity product = mallProductService.getById(item.getId());
|
||||
if (product == null) {
|
||||
return R.failed("商品不存在:" + item.getId());
|
||||
}
|
||||
|
||||
Double newStock = (product.getTotalStock() != null ? product.getTotalStock() : 0D) + item.getIncrCount();
|
||||
if (newStock < 0) {
|
||||
return R.failed("商品库存不足:" + product.getTitle());
|
||||
}
|
||||
|
||||
product.setTotalStock(newStock);
|
||||
mallProductService.updateById(product);
|
||||
}
|
||||
}
|
||||
|
||||
return R.ok(true);
|
||||
} catch (Exception e) {
|
||||
return R.failed("库存变更失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 购物车校验库存
|
||||
*/
|
||||
@Operation(summary = "购物车校验库存", description = "购物车校验库存")
|
||||
@GetMapping("/checkStock")
|
||||
public R<Boolean> checkStock(@RequestParam("productId") Long productId,
|
||||
@RequestParam("productType") Integer productType,
|
||||
@RequestParam("incrCount") Double incrCount) {
|
||||
try {
|
||||
if (productType == 1) {
|
||||
// SKU 模式
|
||||
MallProductSkuEntity sku = mallProductSkuService.getById(productId);
|
||||
if (sku == null) {
|
||||
return R.ok(false);
|
||||
}
|
||||
|
||||
Double currentStock = sku.getStock() != null ? sku.getStock() : 0D;
|
||||
return R.ok(currentStock + incrCount >= 0);
|
||||
|
||||
} else if (productType == 2) {
|
||||
// SPU 模式
|
||||
MallProductEntity product = mallProductService.getById(productId);
|
||||
if (product == null) {
|
||||
return R.ok(false);
|
||||
}
|
||||
|
||||
Double currentStock = product.getTotalStock() != null ? product.getTotalStock() : 0D;
|
||||
return R.ok(currentStock + incrCount >= 0);
|
||||
}
|
||||
|
||||
return R.failed("无效的商品类型");
|
||||
} catch (Exception e) {
|
||||
return R.failed("校验库存失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询商品列表(不分页)
|
||||
*/
|
||||
@Operation(summary = "查询商品列表", description = "查询商品列表(不分页)")
|
||||
@GetMapping("/list")
|
||||
public R<List<MallProductEntity>> list(@RequestParam(value = "merchantId", required = false) Long merchantId,
|
||||
@RequestParam(value = "status", required = false) String status) {
|
||||
LambdaQueryWrapper<MallProductEntity> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.eq(merchantId != null, MallProductEntity::getMerchantId, merchantId);
|
||||
wrapper.eq(StrUtil.isNotBlank(status), MallProductEntity::getStatus, status);
|
||||
return R.ok(mallProductService.list(wrapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品ID查询详情
|
||||
*/
|
||||
@Operation(summary = "根据商品ID查询详情", description = "根据商品ID查询详情(包含SKU)")
|
||||
@GetMapping("/detail")
|
||||
public R<MallProductDetailAppVo> getDetailById(@RequestParam("id") Long id) {
|
||||
try {
|
||||
MallProductEntity product = mallProductService.getById(id);
|
||||
if (product == null) {
|
||||
return R.failed("商品不存在");
|
||||
}
|
||||
|
||||
MallProductDetailAppVo vo = convertToDetailVo(product);
|
||||
return R.ok(vo);
|
||||
} catch (Exception e) {
|
||||
return R.failed("查询商品详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品ID列表批量查询详情
|
||||
*/
|
||||
@Operation(summary = "根据商品ID列表批量查询详情", description = "根据商品ID列表批量查询详情(包含SKU)")
|
||||
@GetMapping("/detailByIds")
|
||||
public R<List<MallProductDetailAppVo>> getDetailByIds(@RequestParam("ids") List<Long> ids) {
|
||||
try {
|
||||
List<MallProductEntity> products = mallProductService.listByIds(ids);
|
||||
List<MallProductDetailAppVo> voList = products.stream()
|
||||
.map(this::convertToDetailVo)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
return R.ok(voList);
|
||||
} catch (Exception e) {
|
||||
return R.failed("批量查询商品详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品ID列表批量查询基本信息
|
||||
*/
|
||||
@Operation(summary = "根据商品ID列表批量查询基本信息", description = "根据商品ID列表批量查询基本信息")
|
||||
@GetMapping("/listByIds")
|
||||
public R<List<MallProductEntity>> getListByIds(@RequestParam("ids") List<Long> ids) {
|
||||
return R.ok(mallProductService.listByIds(ids));
|
||||
}
|
||||
|
||||
// ==================== 私有辅助方法 ====================
|
||||
|
||||
/**
|
||||
* 更新SPU总库存(从所有SKU汇总)
|
||||
*/
|
||||
private void updateSpuTotalStock(Long productId) {
|
||||
List<MallProductSkuEntity> skuList = mallProductSkuService.list(
|
||||
Wrappers.<MallProductSkuEntity>lambdaQuery()
|
||||
.eq(MallProductSkuEntity::getProductId, productId)
|
||||
);
|
||||
|
||||
Double totalStock = skuList.stream()
|
||||
.mapToDouble(sku -> sku.getStock() != null ? sku.getStock() : 0D)
|
||||
.sum();
|
||||
|
||||
MallProductEntity product = new MallProductEntity();
|
||||
product.setId(productId);
|
||||
product.setTotalStock(totalStock);
|
||||
mallProductService.updateById(product);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为列表VO
|
||||
*/
|
||||
private MallProductListAppVo convertToListVo(MallProductEntity product) {
|
||||
MallProductListAppVo vo = new MallProductListAppVo();
|
||||
vo.setId(product.getId());
|
||||
vo.setMerchantId(product.getMerchantId());
|
||||
vo.setCategoryId(product.getCategoryId());
|
||||
vo.setTitle(product.getTitle());
|
||||
vo.setSubTitle(product.getSubTitle());
|
||||
vo.setBrand(product.getBrand());
|
||||
vo.setCoverImage(product.getCoverImage());
|
||||
vo.setCarouselImages(product.getCarouselImages());
|
||||
vo.setPriceType(product.getPriceType());
|
||||
vo.setSpuPrice(product.getSpuPrice());
|
||||
vo.setOriginalPrice(product.getOriginalPrice());
|
||||
vo.setTotalStock(product.getTotalStock());
|
||||
vo.setProductNature(product.getProductNature());
|
||||
vo.setStatus(product.getStatus());
|
||||
|
||||
// 如果是多SKU模式,计算最低价格
|
||||
if (product.getPriceType() != null && product.getPriceType() == 1) {
|
||||
List<MallProductSkuEntity> skuList = mallProductSkuService.list(
|
||||
Wrappers.<MallProductSkuEntity>lambdaQuery()
|
||||
.eq(MallProductSkuEntity::getProductId, product.getId())
|
||||
.orderByAsc(MallProductSkuEntity::getPrice)
|
||||
);
|
||||
|
||||
if (CollUtil.isNotEmpty(skuList)) {
|
||||
vo.setMinPrice(skuList.get(0).getPrice());
|
||||
}
|
||||
} else {
|
||||
// 单SPU模式,最低价格就是SPU价格
|
||||
vo.setMinPrice(product.getSpuPrice());
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为详情VO
|
||||
*/
|
||||
private MallProductDetailAppVo convertToDetailVo(MallProductEntity product) {
|
||||
MallProductDetailAppVo vo = new MallProductDetailAppVo();
|
||||
vo.setId(product.getId());
|
||||
vo.setMerchantId(product.getMerchantId());
|
||||
vo.setCategoryId(product.getCategoryId());
|
||||
vo.setTitle(product.getTitle());
|
||||
vo.setSubTitle(product.getSubTitle());
|
||||
vo.setBrand(product.getBrand());
|
||||
vo.setCoverImage(product.getCoverImage());
|
||||
vo.setCarouselImages(product.getCarouselImages());
|
||||
vo.setPriceType(product.getPriceType());
|
||||
vo.setSpuPrice(product.getSpuPrice());
|
||||
vo.setOriginalPrice(product.getOriginalPrice());
|
||||
vo.setTotalStock(product.getTotalStock());
|
||||
vo.setProductNature(product.getProductNature());
|
||||
vo.setStatus(product.getStatus());
|
||||
vo.setSaleStartTime(product.getSaleStartTime());
|
||||
vo.setSaleEndTime(product.getSaleEndTime());
|
||||
vo.setDescription(product.getDescription());
|
||||
|
||||
// 如果是多SKU模式,查询SKU列表
|
||||
if (product.getPriceType() != null && product.getPriceType() == 1) {
|
||||
List<MallProductSkuEntity> skuList = mallProductSkuService.list(
|
||||
Wrappers.<MallProductSkuEntity>lambdaQuery()
|
||||
.eq(MallProductSkuEntity::getProductId, product.getId())
|
||||
);
|
||||
|
||||
List<MallProductSkuAppVo> skuVoList = skuList.stream()
|
||||
.map(this::convertToSkuVo)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
vo.setSkuList(skuVoList);
|
||||
}
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为SKU VO
|
||||
*/
|
||||
private MallProductSkuAppVo convertToSkuVo(MallProductSkuEntity sku) {
|
||||
MallProductSkuAppVo vo = new MallProductSkuAppVo();
|
||||
vo.setId(sku.getId());
|
||||
vo.setProductId(sku.getProductId());
|
||||
vo.setSkuCode(sku.getSkuCode());
|
||||
vo.setSkuName(sku.getSkuName());
|
||||
vo.setPrice(sku.getPrice());
|
||||
vo.setStock(sku.getStock());
|
||||
vo.setBarcode(sku.getBarcode());
|
||||
vo.setWeightG(sku.getWeightG());
|
||||
vo.setStatus(sku.getStatus());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -155,10 +155,16 @@ public class SmErpInventoryRecordsController {
|
||||
updateStockDTO.setId(cgPlanItemEntity.getErpSpuId());
|
||||
Double quantity = cgPlanItemEntity.getQuantity();
|
||||
String orderType = one.getOrderType();
|
||||
// 使用保存时的内装数,如果没有则查询当前的
|
||||
Integer packageNum = cgPlanItemEntity.getPackageNum();
|
||||
if (packageNum == null) {
|
||||
SmErpUnitEntity unit = smErpUnitService.getById(cgPlanItemEntity.getUnitId());
|
||||
packageNum = unit != null ? unit.getPackageNum() : 1;
|
||||
}
|
||||
if (orderType.equals(OrderTypeEnum.RK.getType())){
|
||||
updateStockDTO.setIncrCount(quantity);
|
||||
updateStockDTO.setIncrCount(packageNum * quantity);
|
||||
}else if (orderType.equals(OrderTypeEnum.CK.getType())){
|
||||
updateStockDTO.setIncrCount(-quantity);
|
||||
updateStockDTO.setIncrCount(-packageNum * quantity);
|
||||
}else if (orderType.equals(OrderTypeEnum.No.getType())){
|
||||
//无需更新库存
|
||||
}else {
|
||||
|
||||
+8
-2
@@ -91,11 +91,17 @@ public class MallProductCreateDTO {
|
||||
@Schema(description = "划线价")
|
||||
private Integer originalPrice;
|
||||
|
||||
/**
|
||||
* 总库存(冗余)
|
||||
*/
|
||||
@Schema(description = "总库存(冗余)")
|
||||
private Double totalStock;
|
||||
|
||||
/**
|
||||
* 计量单位
|
||||
*/
|
||||
@Schema(description = "计量单位")
|
||||
private String unit;
|
||||
@Schema(description = "商品性质")
|
||||
private String productNature;
|
||||
|
||||
/**
|
||||
* 状态(1上架 0下架)
|
||||
|
||||
+22
-8
@@ -50,7 +50,7 @@ public class MallProductServiceImpl extends ServiceImpl<MallProductMapper, MallP
|
||||
product.setPriceType(productCreateDTO.getPriceType());
|
||||
product.setSpuPrice(productCreateDTO.getSpuPrice());
|
||||
product.setOriginalPrice(productCreateDTO.getOriginalPrice());
|
||||
product.setUnit(productCreateDTO.getUnit());
|
||||
product.setProductNature(productCreateDTO.getProductNature());
|
||||
product.setStatus(productCreateDTO.getStatus());
|
||||
product.setSaleStartTime(productCreateDTO.getSaleStartTime());
|
||||
product.setSaleEndTime(productCreateDTO.getSaleEndTime());
|
||||
@@ -94,12 +94,20 @@ public class MallProductServiceImpl extends ServiceImpl<MallProductMapper, MallP
|
||||
product.setTotalStock(totalStock);
|
||||
this.updateById(product);
|
||||
} else if (productCreateDTO.getPriceType() == 2) {
|
||||
// 单价SPU模式,设置总库存为0(或根据业务需求设置)
|
||||
product.setTotalStock(0D);
|
||||
// 单价SPU模式,使用前端传入的totalStock
|
||||
Double totalStock = productCreateDTO.getTotalStock();
|
||||
if (totalStock == null) {
|
||||
totalStock = 0D; // 如果没有传入,默认为0
|
||||
}
|
||||
product.setTotalStock(totalStock);
|
||||
this.updateById(product);
|
||||
} else {
|
||||
// 没有SKU信息,设置总库存为0
|
||||
product.setTotalStock(0D);
|
||||
// 没有SKU信息,使用前端传入的totalStock或默认为0
|
||||
Double totalStock = productCreateDTO.getTotalStock();
|
||||
if (totalStock == null) {
|
||||
totalStock = 0D;
|
||||
}
|
||||
product.setTotalStock(totalStock);
|
||||
this.updateById(product);
|
||||
}
|
||||
|
||||
@@ -129,7 +137,7 @@ public class MallProductServiceImpl extends ServiceImpl<MallProductMapper, MallP
|
||||
product.setPriceType(productCreateDTO.getPriceType());
|
||||
product.setSpuPrice(productCreateDTO.getSpuPrice());
|
||||
product.setOriginalPrice(productCreateDTO.getOriginalPrice());
|
||||
product.setUnit(productCreateDTO.getUnit());
|
||||
product.setProductNature(productCreateDTO.getProductNature());
|
||||
product.setStatus(productCreateDTO.getStatus());
|
||||
product.setSaleStartTime(productCreateDTO.getSaleStartTime());
|
||||
product.setSaleEndTime(productCreateDTO.getSaleEndTime());
|
||||
@@ -162,7 +170,12 @@ public class MallProductServiceImpl extends ServiceImpl<MallProductMapper, MallP
|
||||
deleteWrapper.eq(MallProductSkuEntity::getProductId, product.getId());
|
||||
mallProductSkuService.remove(deleteWrapper);
|
||||
|
||||
product.setTotalStock(0D);
|
||||
// 使用前端传入的totalStock
|
||||
Double totalStock = productCreateDTO.getTotalStock();
|
||||
if (totalStock == null) {
|
||||
totalStock = 0D; // 如果没有传入,默认为0
|
||||
}
|
||||
product.setTotalStock(totalStock);
|
||||
this.updateById(product);
|
||||
}
|
||||
|
||||
@@ -283,7 +296,8 @@ public class MallProductServiceImpl extends ServiceImpl<MallProductMapper, MallP
|
||||
dto.setPriceType(product.getPriceType());
|
||||
dto.setSpuPrice(product.getSpuPrice());
|
||||
dto.setOriginalPrice(product.getOriginalPrice());
|
||||
dto.setUnit(product.getUnit());
|
||||
dto.setTotalStock(product.getTotalStock());
|
||||
dto.setProductNature(product.getProductNature());
|
||||
dto.setStatus(product.getStatus());
|
||||
dto.setSaleStartTime(product.getSaleStartTime());
|
||||
dto.setSaleEndTime(product.getSaleEndTime());
|
||||
|
||||
+279
-61
@@ -68,7 +68,6 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
|
||||
private final SmErpSupplierMapper smErpSupplierMapper;
|
||||
|
||||
private final RemoteFlowApiFlowService remoteFlowApiFlowService;
|
||||
@Override
|
||||
@Transactional
|
||||
public Object saveInventoryRecord(InventoryRecordSaveDTO inventoryRecordSaveDTO) {
|
||||
@@ -120,6 +119,8 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
String orderType = smErpCgDdEntity.getOrderType();
|
||||
cgPlanItemEntity.setOrderType(orderType);
|
||||
Integer packageNum = unitMap.get(cgPlanItemEntity.getUnitId()).getPackageNum();
|
||||
// 保存内装数到item中,用于编辑时准确计算库存
|
||||
cgPlanItemEntity.setPackageNum(packageNum);
|
||||
if (orderType.equals(OrderTypeEnum.RK.getType())){
|
||||
updateStockDTO.setIncrCount(packageNum*quantity);
|
||||
}else if (orderType.equals(OrderTypeEnum.CK.getType())){
|
||||
@@ -147,43 +148,116 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
smErpCgDdMapper.update(updateSmErpCgDdEntity,queryWrapper);
|
||||
|
||||
|
||||
//计算库存商品的成本价= 采购金额*采购数量/采购数量
|
||||
//计算库存商品的成本价 = (剩余商品进货价*剩余数量+新进商品进货价*新进数量)/(剩余数量+新进数量)
|
||||
if (inventoryRecordSaveDTO.getInventoryType().equals(InventoryTypeEnum.CKRK.getType())){
|
||||
// 获取当前采购入库的商品ID列表
|
||||
List<Long> currentSpuIds = cgPlanItemEntities.stream()
|
||||
.map(SmErpInventoryRecordsItemEntity::getErpSpuId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<SmErpCostPriceVo> itemEntityList = smErpInventoryRecordsItemMapper.getRecordsItemListBySpuIdsAndInventoryType(
|
||||
currentSpuIds
|
||||
// 获取当前库存信息和商品信息
|
||||
List<SmErpStockEntity> stockList = smErpStockService.list(
|
||||
new LambdaQueryWrapper<SmErpStockEntity>().in(SmErpStockEntity::getErpSpuId, currentSpuIds)
|
||||
);
|
||||
Map<Long, List<SmErpCostPriceVo>> costPriceMap = itemEntityList.stream()
|
||||
.collect(Collectors.groupingBy(SmErpCostPriceVo::getErpSpuId));
|
||||
Map<Long, SmErpStockEntity> stockMap = stockList.stream()
|
||||
.collect(Collectors.toMap(SmErpStockEntity::getErpSpuId, stock -> stock));
|
||||
|
||||
// 计算并更新成本价
|
||||
cgPlanItemEntities.forEach(item -> {
|
||||
Long erpSpuId = item.getErpSpuId();
|
||||
List<SmErpCostPriceVo> smErpInventoryRecordsItemEntityList = costPriceMap.get(erpSpuId);
|
||||
Double totalNum = smErpInventoryRecordsItemEntityList.stream().filter(Objects::nonNull).mapToDouble(itemEntity -> itemEntity.getPackageNum() * itemEntity.getQuantity()).sum();
|
||||
List<SmErpSpuEntity> spuList = smErpSpuMapper.selectList(
|
||||
new LambdaQueryWrapper<SmErpSpuEntity>().in(SmErpSpuEntity::getId, currentSpuIds)
|
||||
);
|
||||
Map<Long, SmErpSpuEntity> spuMap = spuList.stream()
|
||||
.collect(Collectors.toMap(SmErpSpuEntity::getId, spu -> spu));
|
||||
|
||||
int totalRealRrice = smErpInventoryRecordsItemEntityList.stream().filter(Objects::nonNull).mapToInt(SmErpCostPriceVo::getTotalAmount).sum();
|
||||
double costPrice = totalRealRrice / totalNum;
|
||||
// 按商品分组,计算每个商品的加权平均成本价
|
||||
Map<Long, List<SmErpInventoryRecordsItemEntity>> itemsBySpuId = cgPlanItemEntities.stream()
|
||||
.collect(Collectors.groupingBy(SmErpInventoryRecordsItemEntity::getErpSpuId));
|
||||
|
||||
SmErpSpuEntity smErpSpuEntity = new SmErpSpuEntity();
|
||||
smErpSpuEntity.setCostPrice(costPrice);
|
||||
smErpSpuEntity.setId(erpSpuId);
|
||||
// 更新库存成本价
|
||||
smErpSpuMapper.updateById(smErpSpuEntity);
|
||||
itemsBySpuId.forEach((erpSpuId, spuItems) -> {
|
||||
// 获取剩余库存数量(入库前的库存)
|
||||
SmErpStockEntity stock = stockMap.get(erpSpuId);
|
||||
double remainingQuantity = 0;
|
||||
double remainingPurchasePrice = 0;
|
||||
|
||||
if (stock != null) {
|
||||
// 获取本次入库数量
|
||||
double newInQuantity = spuItems.stream()
|
||||
.mapToDouble(item -> item.getPackageNum() * item.getQuantity())
|
||||
.sum();
|
||||
// 剩余数量 = 当前库存(已更新) - 本次入库数量 = 入库前的库存
|
||||
remainingQuantity = stock.getCurrentStock() - newInQuantity;
|
||||
|
||||
// 如果剩余数量小于0,说明是新商品或库存异常,设为0
|
||||
if (remainingQuantity < 0) {
|
||||
remainingQuantity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取剩余商品的进货价(从基础单位的采购价获取,单位:分)
|
||||
LambdaQueryWrapper<SmErpUnitEntity> baseUnitWrapper = new LambdaQueryWrapper<>();
|
||||
baseUnitWrapper.eq(SmErpUnitEntity::getSpuId, erpSpuId);
|
||||
baseUnitWrapper.eq(SmErpUnitEntity::getIsBase, true);
|
||||
SmErpUnitEntity baseUnit = smErpUnitMapper.selectOne(baseUnitWrapper);
|
||||
if (baseUnit != null && baseUnit.getPurchasePrice() != null) {
|
||||
remainingPurchasePrice = baseUnit.getPurchasePrice();
|
||||
}
|
||||
|
||||
//更新实际进价到商品包装单位里
|
||||
SmErpUnitEntity smErpUnitEntity = new SmErpUnitEntity();
|
||||
smErpUnitEntity.setPurchasePrice(item.getRealPrice());
|
||||
smErpUnitEntity.setId(item.getUnitId());
|
||||
smErpUnitMapper.updateById(smErpUnitEntity);
|
||||
// 计算新进商品的总金额和总数量
|
||||
double newInTotalAmount = 0;
|
||||
double newInTotalQuantity = 0;
|
||||
|
||||
for (SmErpInventoryRecordsItemEntity item : spuItems) {
|
||||
// 新进数量 = 包装数量(箱数) * 内装数(个/箱)
|
||||
double quantity = item.getPackageNum() * item.getQuantity();
|
||||
newInTotalQuantity += quantity;
|
||||
|
||||
// 新进商品总金额 = 实际进价(分/个) * 数量(个)
|
||||
if (item.getRealPrice() != null) {
|
||||
newInTotalAmount += item.getRealPrice() * quantity;
|
||||
}
|
||||
|
||||
//更新实际进价到商品包装单位里
|
||||
SmErpUnitEntity smErpUnitEntity = new SmErpUnitEntity();
|
||||
smErpUnitEntity.setPurchasePrice(item.getRealPrice());
|
||||
smErpUnitEntity.setId(item.getUnitId());
|
||||
smErpUnitMapper.updateById(smErpUnitEntity);
|
||||
}
|
||||
|
||||
// 计算加权平均成本价 = (剩余商品进货价*剩余数量 + 新进商品进货价*新进数量) / (剩余数量 + 新进数量)
|
||||
double newCostPrice = 0;
|
||||
double totalQuantity = remainingQuantity + newInTotalQuantity;
|
||||
|
||||
if (totalQuantity > 0) {
|
||||
double totalAmount = remainingPurchasePrice * remainingQuantity + newInTotalAmount;
|
||||
newCostPrice = totalAmount / totalQuantity;
|
||||
|
||||
// 调试日志:打印成本价计算过程
|
||||
// System.out.println("========== 成本价计算详情 ==========");
|
||||
// System.out.println("商品ID: " + erpSpuId);
|
||||
// System.out.println("剩余数量: " + remainingQuantity);
|
||||
// System.out.println("剩余商品进货价(基础单位采购价): " + remainingPurchasePrice + " 分/个");
|
||||
// System.out.println("剩余总金额: " + (remainingPurchasePrice * remainingQuantity) + " 分");
|
||||
// System.out.println("新进数量: " + newInTotalQuantity);
|
||||
// System.out.println("新进商品进货价(realPrice): " + (newInTotalQuantity > 0 ? (newInTotalAmount / newInTotalQuantity) : 0) + " 分/个");
|
||||
// System.out.println("新进总金额: " + newInTotalAmount + " 分");
|
||||
// System.out.println("总数量: " + totalQuantity);
|
||||
// System.out.println("总金额: " + totalAmount + " 分");
|
||||
// System.out.println("新成本价: " + newCostPrice + " 分/个");
|
||||
// System.out.println("新成本价(保留2位): " + String.format("%.2f", newCostPrice) + " 分/个");
|
||||
// System.out.println("====================================");
|
||||
} else {
|
||||
// 如果总数量为0,使用新进商品的平均价格
|
||||
if (newInTotalQuantity > 0) {
|
||||
newCostPrice = newInTotalAmount / newInTotalQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新商品成本价(double类型会自动保留精度)
|
||||
SmErpSpuEntity updateSpu = new SmErpSpuEntity();
|
||||
updateSpu.setId(erpSpuId);
|
||||
updateSpu.setCostPrice(newCostPrice);
|
||||
smErpSpuMapper.updateById(updateSpu);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
return insert;
|
||||
}
|
||||
@@ -216,13 +290,19 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
//收集userids
|
||||
Set<Long> userIds = records.stream()
|
||||
.map(SmErpInventoryRecordsEntity::getHandlerUserId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
//收集supplierIds
|
||||
Set<Long> supplierIds = records.stream()
|
||||
.map(SmErpInventoryRecordsEntity::getSupplierId)
|
||||
.collect(Collectors.toSet());
|
||||
//请求得到所有的userIds
|
||||
Map<Long, SysUser> userMap = remoteUserService.getUserMap(userIds).getData();
|
||||
Map<Long, SysUser> userMap;
|
||||
if (!CollectionUtils.isEmpty(userIds)){
|
||||
userMap = remoteUserService.getUserMap(userIds).getData();
|
||||
} else {
|
||||
userMap = new HashMap<>();
|
||||
}
|
||||
// List<String> processInstanceIds= records.stream().map(SmErpInventoryRecordsEntity::getProcessInstanceId).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
|
||||
LambdaQueryWrapper<SmErpInventoryRecordsItemEntity> smErpCgPlanItemEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
@@ -248,7 +328,6 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
// Integer totalAmount = inventoryTotalAmountMap.getOrDefault(record.getId(), 0);
|
||||
List<SmErpInventoryRecordsItemEntity> smErpInventoryRecordsItemEntityList = smerpInventoryRecordsItemEntityMap.get(record.getId());
|
||||
// record.setTotalAmount(totalAmount);
|
||||
//todo 这里有一个问题就是出入库编辑的时候,没有更新主表的totalAmount,所以一直用的子表的totalAmount,子表的totalAmount又存在为空的情况,涉及到多个业务
|
||||
//存在脏数据,判断下,防止空数据查询报错
|
||||
if (!CollectionUtils.isEmpty(smErpInventoryRecordsItemEntityList)) {
|
||||
record.setTotalAmount(smErpInventoryRecordsItemEntityList.stream().mapToInt(item -> item.getTotalAmount()==null?0:item.getTotalAmount()).sum());
|
||||
@@ -279,19 +358,6 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
Collectors.toList()
|
||||
)
|
||||
));
|
||||
//查询代办任务的task
|
||||
// TaskQueryParamDto taskQueryParamDto = new TaskQueryParamDto();
|
||||
// taskQueryParamDto.setProcessInstanceIds(processInstanceIds);
|
||||
// taskQueryParamDto.setCurrent(1);
|
||||
// taskQueryParamDto.setSize(processInstanceIds.size());
|
||||
// R<Page<TaskDto>> pageR = remoteFlowApiFlowService.queryMineTask(taskQueryParamDto);
|
||||
// Page<TaskDto> data = pageR.getData();
|
||||
//
|
||||
// final Map<String, TaskDto> processInstanceIdMap = (data != null && !ObjectUtil.isEmpty(data.getRecords()))
|
||||
// ? data.getRecords().stream()
|
||||
// .collect(Collectors.toMap(TaskDto::getProcessInstanceId, taskDto -> taskDto))
|
||||
// : Collections.emptyMap();
|
||||
|
||||
// 设置商品名称到主表实体
|
||||
records.forEach(plan -> {
|
||||
//移动端自动扣款入库,无需经办人
|
||||
@@ -304,15 +370,6 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
String supplierName = supplierMap.getOrDefault(plan.getSupplierId(), new SmErpSupplierEntity()).getSupplierName();
|
||||
plan.setSupplierName(supplierName);
|
||||
|
||||
// if (!processInstanceIdMap.isEmpty()) {
|
||||
// TaskDto taskDto = processInstanceIdMap.get(plan.getProcessInstanceId());
|
||||
// if (taskDto != null) {
|
||||
// plan.setTaskId(taskDto.getTaskId());
|
||||
// plan.setFlowId(taskDto.getFlowId());
|
||||
// plan.setParamMap(taskDto.getParamMap());
|
||||
// plan.setNodeId(taskDto.getNodeId());
|
||||
// }
|
||||
// }
|
||||
});
|
||||
return pageData;
|
||||
}
|
||||
@@ -506,6 +563,8 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
String orderType = item.getOrderType();
|
||||
smErpInventoryRecordsItemEntity.setOrderType(orderType);
|
||||
Integer packageNum = unitMap.get(item.getUnitId()).getPackageNum();
|
||||
// 保存内装数到item中,用于下次编辑时准确计算库存
|
||||
smErpInventoryRecordsItemEntity.setPackageNum(packageNum);
|
||||
if (orderType.equals(OrderTypeEnum.RK.getType())){
|
||||
updateStockDTO.setIncrCount(packageNum*quantity);
|
||||
}else if (orderType.equals(OrderTypeEnum.CK.getType())){
|
||||
@@ -535,14 +594,15 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
List<SmErpInventoryRecordsItemEntity> dataBaseSmErpInventoryRecordsItemEntityList = smErpInventoryRecordsItemMapper
|
||||
.selectList(new LambdaQueryWrapper<SmErpInventoryRecordsItemEntity>().eq(SmErpInventoryRecordsItemEntity::getInventoryId, inventoryRecordEditDTO.getId()));
|
||||
|
||||
|
||||
Set<Long> unitsRemoved = dataBaseSmErpInventoryRecordsItemEntityList.stream().map(s -> s.getUnitId()).collect(Collectors.toSet());
|
||||
List<SmErpUnitEntity> smErpUnitEntitiesRemoved = smErpUnitMapper.selectList(new LambdaQueryWrapper<SmErpUnitEntity>().in(SmErpUnitEntity::getId, unitsRemoved));
|
||||
Map<Long, SmErpUnitEntity> unitMapRemoved = smErpUnitEntitiesRemoved.stream().collect(Collectors.toMap(SmErpUnitEntity::getId, unit -> unit));
|
||||
|
||||
//这部分是数据库原本添加的库存数据,需要全部减掉,重新添加更新的库存数据进去
|
||||
dataBaseSmErpInventoryRecordsItemEntityList.forEach(item -> {
|
||||
Integer packageNum = unitMapRemoved.get(item.getUnitId()).getPackageNum();
|
||||
// 使用保存时的内装数,而不是查询最新的内装数,这样即使内装数改变了也能正确计算
|
||||
Integer packageNum = item.getPackageNum();
|
||||
// 如果之前的数据没有保存内装数(旧数据),则查询当前的内装数作为兜底
|
||||
if (packageNum == null) {
|
||||
SmErpUnitEntity unit = smErpUnitMapper.selectById(item.getUnitId());
|
||||
packageNum = unit != null ? unit.getPackageNum() : 1;
|
||||
}
|
||||
UpdateStockDTO updateStockDTO = new UpdateStockDTO();
|
||||
updateStockDTO.setId(item.getErpSpuId());
|
||||
if (item.getOrderType().equals(OrderTypeEnum.RK.getType())){
|
||||
@@ -555,8 +615,14 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
});
|
||||
|
||||
|
||||
smErpInventoryRecordsItemMapper.updateById(updateItems);
|
||||
smErpInventoryRecordsItemMapper.insert(insertItems);
|
||||
// 批量更新已存在的明细(包括 packageNum 字段)
|
||||
if (!CollectionUtils.isEmpty(updateItems)) {
|
||||
updateItems.forEach(item -> smErpInventoryRecordsItemMapper.updateById(item));
|
||||
}
|
||||
// 批量插入新增的明细
|
||||
if (!CollectionUtils.isEmpty(insertItems)) {
|
||||
smErpInventoryRecordsItemMapper.insert(insertItems);
|
||||
}
|
||||
|
||||
List<Long> existingCodeIds = smErpInventoryRecordsItemMapper.selectList(
|
||||
new LambdaQueryWrapper<SmErpInventoryRecordsItemEntity>()
|
||||
@@ -600,6 +666,146 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
//更新库存
|
||||
smErpStockService.updateStock(updateStockDTOS);
|
||||
|
||||
//计算库存商品的成本价 = (剩余商品进货价*剩余数量+新进商品进货价*新进数量)/(剩余数量+新进数量)
|
||||
//只有采购入库类型才需要更新成本价
|
||||
if (smErpInventoryRecordsEntity.getInventoryType() != null &&
|
||||
smErpInventoryRecordsEntity.getInventoryType().equals(InventoryTypeEnum.CKRK.getType())){
|
||||
|
||||
// 获取最终保存的商品列表
|
||||
List<SmErpInventoryRecordsItemEntity> savedItems = smErpInventoryRecordsItemMapper.selectList(
|
||||
new LambdaQueryWrapper<SmErpInventoryRecordsItemEntity>()
|
||||
.eq(SmErpInventoryRecordsItemEntity::getInventoryId, inventoryRecordEditDTO.getId())
|
||||
);
|
||||
|
||||
// 获取涉及的商品ID
|
||||
List<Long> currentSpuIds = savedItems.stream()
|
||||
.map(SmErpInventoryRecordsItemEntity::getErpSpuId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!CollectionUtils.isEmpty(currentSpuIds)) {
|
||||
// 获取当前库存信息和商品信息
|
||||
List<SmErpStockEntity> stockList = smErpStockService.list(
|
||||
new LambdaQueryWrapper<SmErpStockEntity>().in(SmErpStockEntity::getErpSpuId, currentSpuIds)
|
||||
);
|
||||
Map<Long, SmErpStockEntity> stockMap = stockList.stream()
|
||||
.collect(Collectors.toMap(SmErpStockEntity::getErpSpuId, stock -> stock));
|
||||
|
||||
List<SmErpSpuEntity> spuList = smErpSpuMapper.selectList(
|
||||
new LambdaQueryWrapper<SmErpSpuEntity>().in(SmErpSpuEntity::getId, currentSpuIds)
|
||||
);
|
||||
Map<Long, SmErpSpuEntity> spuMap = spuList.stream()
|
||||
.collect(Collectors.toMap(SmErpSpuEntity::getId, spu -> spu));
|
||||
|
||||
// 按商品分组,计算每个商品的加权平均成本价
|
||||
Map<Long, List<SmErpInventoryRecordsItemEntity>> itemsBySpuId = savedItems.stream()
|
||||
.collect(Collectors.groupingBy(SmErpInventoryRecordsItemEntity::getErpSpuId));
|
||||
|
||||
itemsBySpuId.forEach((erpSpuId, spuItems) -> {
|
||||
// 获取当前库存数量(已经更新后的库存)
|
||||
SmErpStockEntity stock = stockMap.get(erpSpuId);
|
||||
double currentStockQuantity = 0;
|
||||
|
||||
if (stock != null) {
|
||||
currentStockQuantity = stock.getCurrentStock();
|
||||
}
|
||||
|
||||
// 计算本次编辑的入库数量
|
||||
double editInQuantity = spuItems.stream()
|
||||
.mapToDouble(item -> {
|
||||
Integer pkgNum = item.getPackageNum();
|
||||
if (pkgNum == null) {
|
||||
SmErpUnitEntity unit = smErpUnitMapper.selectById(item.getUnitId());
|
||||
pkgNum = unit != null ? unit.getPackageNum() : 1;
|
||||
}
|
||||
return pkgNum * item.getQuantity();
|
||||
})
|
||||
.sum();
|
||||
|
||||
// 剩余数量 = 当前库存 - 本次入库数量
|
||||
double remainingQuantity = currentStockQuantity - editInQuantity;
|
||||
if (remainingQuantity < 0) {
|
||||
remainingQuantity = 0;
|
||||
}
|
||||
|
||||
// 获取剩余商品的进货价(从基础单位的采购价获取,单位:分)
|
||||
double remainingPurchasePrice = 0;
|
||||
LambdaQueryWrapper<SmErpUnitEntity> baseUnitWrapper = new LambdaQueryWrapper<>();
|
||||
baseUnitWrapper.eq(SmErpUnitEntity::getSpuId, erpSpuId);
|
||||
baseUnitWrapper.eq(SmErpUnitEntity::getIsBase, true);
|
||||
SmErpUnitEntity baseUnit = smErpUnitMapper.selectOne(baseUnitWrapper);
|
||||
if (baseUnit != null && baseUnit.getPurchasePrice() != null) {
|
||||
remainingPurchasePrice = baseUnit.getPurchasePrice();
|
||||
}
|
||||
|
||||
// 计算新进(编辑后)商品的总金额和总数量
|
||||
double newInTotalAmount = 0;
|
||||
double newInTotalQuantity = 0;
|
||||
|
||||
for (SmErpInventoryRecordsItemEntity item : spuItems) {
|
||||
// 获取内装数
|
||||
Integer pkgNum = item.getPackageNum();
|
||||
if (pkgNum == null) {
|
||||
SmErpUnitEntity unit = smErpUnitMapper.selectById(item.getUnitId());
|
||||
pkgNum = unit != null ? unit.getPackageNum() : 1;
|
||||
}
|
||||
|
||||
// 新进数量 = 包装数量 * 内装数
|
||||
double quantity = pkgNum * item.getQuantity();
|
||||
newInTotalQuantity += quantity;
|
||||
|
||||
// 新进商品总金额 = 实际进价 * 数量
|
||||
if (item.getRealPrice() != null) {
|
||||
newInTotalAmount += item.getRealPrice() * quantity;
|
||||
}
|
||||
|
||||
//更新实际进价到商品包装单位里
|
||||
if (item.getRealPrice() != null) {
|
||||
SmErpUnitEntity smErpUnitEntity = new SmErpUnitEntity();
|
||||
smErpUnitEntity.setPurchasePrice(item.getRealPrice());
|
||||
smErpUnitEntity.setId(item.getUnitId());
|
||||
smErpUnitMapper.updateById(smErpUnitEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算加权平均成本价 = (剩余商品进货价*剩余数量 + 新进商品进货价*新进数量) / (剩余数量 + 新进数量)
|
||||
double newCostPrice = 0;
|
||||
double totalQuantity = remainingQuantity + newInTotalQuantity;
|
||||
|
||||
if (totalQuantity > 0) {
|
||||
double totalAmount = remainingPurchasePrice * remainingQuantity + newInTotalAmount;
|
||||
newCostPrice = totalAmount / totalQuantity;
|
||||
|
||||
// 调试日志:打印成本价计算过程(编辑)
|
||||
System.out.println("========== 成本价计算详情(编辑) ==========");
|
||||
System.out.println("商品ID: " + erpSpuId);
|
||||
System.out.println("剩余数量: " + remainingQuantity);
|
||||
System.out.println("剩余商品进货价(基础单位采购价): " + remainingPurchasePrice + " 分/个");
|
||||
System.out.println("剩余总金额: " + (remainingPurchasePrice * remainingQuantity) + " 分");
|
||||
System.out.println("新进数量: " + newInTotalQuantity);
|
||||
System.out.println("新进商品进货价(realPrice): " + (newInTotalQuantity > 0 ? (newInTotalAmount / newInTotalQuantity) : 0) + " 分/个");
|
||||
System.out.println("新进总金额: " + newInTotalAmount + " 分");
|
||||
System.out.println("总数量: " + totalQuantity);
|
||||
System.out.println("总金额: " + totalAmount + " 分");
|
||||
System.out.println("新成本价: " + newCostPrice + " 分/个");
|
||||
System.out.println("新成本价(保留2位): " + String.format("%.2f", newCostPrice) + " 分/个");
|
||||
System.out.println("==========================================");
|
||||
} else {
|
||||
// 如果总数量为0,使用新进商品的平均价格
|
||||
if (newInTotalQuantity > 0) {
|
||||
newCostPrice = newInTotalAmount / newInTotalQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新商品成本价
|
||||
SmErpSpuEntity updateSpu = new SmErpSpuEntity();
|
||||
updateSpu.setId(erpSpuId);
|
||||
updateSpu.setCostPrice(newCostPrice);
|
||||
smErpSpuMapper.updateById(updateSpu);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -631,11 +837,17 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
updateStockDTO.setId(item.getErpSpuId());
|
||||
Double quantity = item.getQuantity();
|
||||
String orderType = item.getOrderType();
|
||||
// 使用保存时的内装数,如果没有则查询当前的
|
||||
Integer packageNum = item.getPackageNum();
|
||||
if (packageNum == null) {
|
||||
SmErpUnitEntity unit = smErpUnitMapper.selectById(item.getUnitId());
|
||||
packageNum = unit != null ? unit.getPackageNum() : 1;
|
||||
}
|
||||
//入库的删除就是减去入的库存,所以是负数
|
||||
if (orderType.equals(OrderTypeEnum.RK.getType())){
|
||||
updateStockDTO.setIncrCount(-quantity);
|
||||
updateStockDTO.setIncrCount(-packageNum * quantity);
|
||||
}else if (orderType.equals(OrderTypeEnum.CK.getType())){
|
||||
updateStockDTO.setIncrCount(quantity);
|
||||
updateStockDTO.setIncrCount(packageNum * quantity);
|
||||
}else {
|
||||
throw new RuntimeException("订单类型错误");
|
||||
}
|
||||
@@ -788,8 +1000,14 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
|
||||
updateStockDTOS.add(updateStockDTO);
|
||||
});
|
||||
|
||||
smErpInventoryRecordsItemMapper.updateById(updateItems);
|
||||
smErpInventoryRecordsItemMapper.insert(insertItems);
|
||||
// 批量更新已存在的明细
|
||||
if (!CollectionUtils.isEmpty(updateItems)) {
|
||||
updateItems.forEach(item -> smErpInventoryRecordsItemMapper.updateById(item));
|
||||
}
|
||||
// 批量插入新增的明细
|
||||
if (!CollectionUtils.isEmpty(insertItems)) {
|
||||
smErpInventoryRecordsItemMapper.insert(insertItems);
|
||||
}
|
||||
|
||||
List<Long> existingCodeIds = smErpInventoryRecordsItemMapper.selectList(
|
||||
new LambdaQueryWrapper<SmErpInventoryRecordsItemEntity>()
|
||||
|
||||
Reference in new issue
Block a user