共富添加接口
This commit is contained in:
1 parent
a3d8df9ad8
commit
8647444e10
10 files changed
+844
-15
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;
|
||||
}
|
||||
}
|
||||
|
||||
+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.
+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
@@ -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());
|
||||
|
||||
Reference in new issue
Block a user