This commit is contained in:
dzc committed 2025-10-22 16:27:59 +08:00
commit e56d6da230
15 files changed
+1139 -78

No files matched your search

@@ -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);
}
@@ -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;
}
}
@@ -96,5 +96,6 @@ public class InventoryRecordEditItemDTO {
@Schema(description="包装单位id")
private Long unitId;
// 注意:packageNum 不需要前端传递,由后端根据 unitId 自动查询并设置
}
@@ -55,5 +55,6 @@ public class InventoryRecordSaveItemDTO {
@Schema(description="实际进价")
private Integer realPrice;
// 注意:packageNum 不需要前端传递,由后端根据 unitId 自动查询并设置
}
@@ -99,8 +99,8 @@ public class MallProductEntity extends Model<MallProductEntity> {
/**
* 计量单位
*/
@Schema(description="计量单位")
private String unit;
@Schema(description="商品性质")
private String productNature;
/**
* 状态(1上架 0下架)
@@ -166,4 +166,10 @@ public class SmErpInventoryRecordsItemEntity extends Model<SmErpInventoryRecords
@Schema(description="实际单价")
private Integer realPrice;
/**
* 内装数(保存时的内装数,用于编辑时准确计算库存)
*/
@Schema(description="内装数")
private Integer packageNum;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
}
@@ -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 {
@@ -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下架)
@@ -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());
@@ -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>()