diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/api/feign/RemoteMallProductService.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/api/feign/RemoteMallProductService.java new file mode 100644 index 000000000..020269ac3 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/api/feign/RemoteMallProductService.java @@ -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 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 checkStock(@RequestParam("productId") Long productId, + @RequestParam("productType") Integer productType, + @RequestParam("incrCount") Double incrCount); + + /** + * 查询商城商品分类树 + * + * @param merchantId 商户ID(可选) + * @return 分类树列表 + */ + @GetMapping("/mallCategory/tree") + R> 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(@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(@RequestParam(value = "merchantId", required = false) Long merchantId, + @RequestParam(value = "status", required = false) String status); + + /** + * 根据商品ID查询详情 + * + * @param id 商品ID + * @return 商品详情(包含SKU) + */ + @GetMapping("/mallProduct/detail") + R getDetailById(@RequestParam("id") Long id); + + /** + * 根据商品ID列表批量查询详情 + * + * @param ids 商品ID列表 + * @return 商品详情列表(包含SKU) + */ + @GetMapping("/mallProduct/detailByIds") + R> getDetailByIds(@RequestParam("ids") List ids); + + /** + * 根据商品ID列表批量查询基本信息 + * + * @param ids 商品ID列表 + * @return 商品基本信息列表 + */ + @GetMapping("/mallProduct/listByIds") + R> getListByIds(@RequestParam("ids") List ids); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductStockChangeDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductStockChangeDTO.java new file mode 100644 index 000000000..17f941714 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductStockChangeDTO.java @@ -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 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; + } +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductEntity.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductEntity.java similarity index 97% rename from pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductEntity.java rename to pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductEntity.java index 51cdbfff5..639535578 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductEntity.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductEntity.java @@ -99,8 +99,8 @@ public class MallProductEntity extends Model { /** * 计量单位 */ - @Schema(description="计量单位") - private String unit; + @Schema(description="商品性质") + private String productNature; /** * 状态(1上架 0下架) diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductSkuEntity.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductSkuEntity.java similarity index 100% rename from pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductSkuEntity.java rename to pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallProductSkuEntity.java diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductDetailAppVo.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductDetailAppVo.java new file mode 100644 index 000000000..60ea9f35c --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductDetailAppVo.java @@ -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 skuList; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductListAppVo.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductListAppVo.java new file mode 100644 index 000000000..dbb2045db --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductListAppVo.java @@ -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; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductSkuAppVo.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductSkuAppVo.java new file mode 100644 index 000000000..43c8b20e7 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/mall/MallProductSkuAppVo.java @@ -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; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallProductController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallProductController.java index 88e2a5c41..81de84093 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallProductController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallProductController.java @@ -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 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 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> 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 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 resultPage = mallProductService.page(page, wrapper); + + // 转换为 VO 格式 + Page voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal()); + List 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 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 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 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(@RequestParam(value = "merchantId", required = false) Long merchantId, + @RequestParam(value = "status", required = false) String status) { + LambdaQueryWrapper 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 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> getDetailByIds(@RequestParam("ids") List ids) { + try { + List products = mallProductService.listByIds(ids); + List 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> getListByIds(@RequestParam("ids") List ids) { + return R.ok(mallProductService.listByIds(ids)); + } + + // ==================== 私有辅助方法 ==================== + + /** + * 更新SPU总库存(从所有SKU汇总) + */ + private void updateSpuTotalStock(Long productId) { + List skuList = mallProductSkuService.list( + Wrappers.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 skuList = mallProductSkuService.list( + Wrappers.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 skuList = mallProductSkuService.list( + Wrappers.lambdaQuery() + .eq(MallProductSkuEntity::getProductId, product.getId()) + ); + + List 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; + } } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductCreateDTO.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductCreateDTO.java index 6908008e1..934a94a6e 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductCreateDTO.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/dto/MallProductCreateDTO.java @@ -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下架) diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/MallProductServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/MallProductServiceImpl.java index 10dbb1724..45f5db9ed 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/MallProductServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/MallProductServiceImpl.java @@ -50,7 +50,7 @@ public class MallProductServiceImpl extends ServiceImpl