diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallCategoryEntity.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallCategoryEntity.java index 07bd86854..47fc6e962 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallCategoryEntity.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/MallCategoryEntity.java @@ -113,4 +113,10 @@ public class MallCategoryEntity extends Model { */ @Schema(description="租户编号") private Long tenantId; + + /** + * 商户ID + */ + @Schema(description="商户ID") + private Long merchantId; } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/constant/MallConstants.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/constant/MallConstants.java index 46d0ddb22..8220d8f7e 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/constant/MallConstants.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/constant/MallConstants.java @@ -95,3 +95,4 @@ public interface MallConstants { } + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java index c336912e4..556b28d0f 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java @@ -27,6 +27,7 @@ import com.bsd.pjcloud.supermarket.vo.mall.MallCategoryTreeVo; import com.bsd.pjcloud.supermarket.vo.app.MallProductListAppVo; import com.bsd.pjcloud.supermarket.vo.app.MallProductDetailAppVo; import com.bsd.pjcloud.supermarket.vo.app.MallProductSkuAppVo; +import com.bsd.pjcloud.supermarket.vo.app.MallMerchantListAppVo; import com.bsd.pjcloud.supermarket.dto.SkuStockChangeDTO; import com.bsd.pjcloud.supermarket.constant.MallConstants; import io.swagger.v3.oas.annotations.Operation; @@ -35,6 +36,7 @@ import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -67,9 +69,12 @@ public class MallAppController { */ @GetMapping("/category/tree") @Operation(summary = "获取分类树", description = "获取所有分类的树结构") - public R> getCategoryTree() { - List all = mallCategoryService.list(Wrappers.lambdaQuery() - .eq(MallCategoryEntity::getStatus, 1)); // 只查启用状态 + public R> getCategoryTree(@RequestParam(required = false) Long merchantId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(MallCategoryEntity::getStatus, 1); // 只查启用状态 + // 添加商户ID过滤条件 + wrapper.eq(merchantId != null, MallCategoryEntity::getMerchantId, merchantId); + List all = mallCategoryService.list(wrapper); List tree = buildCategoryTree(all, 0L); return R.ok(tree); } @@ -79,12 +84,13 @@ public class MallAppController { */ @GetMapping("/category/listByParent/{parentId}") @Operation(summary = "根据父级ID获取子分类", description = "根据父级ID获取子分类列表") - public R> getCategoryListByParent(@PathVariable("parentId") Long parentId) { - List list = mallCategoryService.list( - Wrappers.lambdaQuery() - .eq(MallCategoryEntity::getParentId, parentId) - .eq(MallCategoryEntity::getStatus, 1) - ); + public R> getCategoryListByParent(@PathVariable("parentId") Long parentId, @RequestParam(required = false) Long merchantId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(MallCategoryEntity::getParentId, parentId); + wrapper.eq(MallCategoryEntity::getStatus, 1); + // 添加商户ID过滤条件 + wrapper.eq(merchantId != null, MallCategoryEntity::getMerchantId, merchantId); + List list = mallCategoryService.list(wrapper); return R.ok(list); } @@ -94,14 +100,112 @@ public class MallAppController { * 获取商户列表 */ @GetMapping("/merchant/list") - @Operation(summary = "获取商户列表", description = "获取所有启用的商户列表") - public R> getMerchantList() { - List list = mallMerchantService.list( + @Operation(summary = "获取商户列表", description = "获取所有启用的商户列表,包含特色商品") + public R> getMerchantList(@RequestParam(defaultValue = "3") Integer productLimit) { + // 查询所有启用的商户 + List merchants = mallMerchantService.list( Wrappers.lambdaQuery() - .eq(MallMerchantEntity::getStatus, 1) + .eq(MallMerchantEntity::getStatus, MallConstants.Status.ENABLED) .orderByAsc(MallMerchantEntity::getName) ); - return R.ok(list); + + if (merchants.isEmpty()) { + return R.ok(List.of()); + } + + // 获取商户ID列表 + List merchantIds = merchants.stream() + .map(MallMerchantEntity::getId) + .collect(Collectors.toList()); + + // 查询每个商户的特色商品(按创建时间倒序,取前N个) + LocalDateTime now = LocalDateTime.now(); + List products = mallProductService.list( + Wrappers.lambdaQuery() + .in(MallProductEntity::getMerchantId, merchantIds) + .eq(MallProductEntity::getStatus, MallConstants.Status.ENABLED) // 只查上架商品 + .and(wrapper -> wrapper + .isNull(MallProductEntity::getSaleStartTime) + .or() + .le(MallProductEntity::getSaleStartTime, now) + ) + .and(wrapper -> wrapper + .isNull(MallProductEntity::getSaleEndTime) + .or() + .ge(MallProductEntity::getSaleEndTime, now) + ) + .orderByDesc(MallProductEntity::getCreateTime) + ); + + // 按商户ID分组商品 + Map> productMap = products.stream() + .collect(Collectors.groupingBy(MallProductEntity::getMerchantId)); + + // 获取商品ID列表,查询SKU信息 + List productIds = products.stream() + .map(MallProductEntity::getId) + .collect(Collectors.toList()); + + List skuList = productIds.isEmpty() ? List.of() : + mallProductSkuService.list( + Wrappers.lambdaQuery() + .in(MallProductSkuEntity::getProductId, productIds) + ); + + // 按商品ID分组SKU + Map> skuMap = skuList.stream() + .collect(Collectors.groupingBy(MallProductSkuEntity::getProductId)); + + // 转换为VO + List result = merchants.stream().map(merchant -> { + MallMerchantListAppVo vo = BeanUtils.toBean(merchant, MallMerchantListAppVo.class); + + // 获取该商户的商品列表 + List merchantProducts = productMap.getOrDefault(merchant.getId(), List.of()); + + // 设置商品总数 + vo.setProductCount(merchantProducts.size()); + + // 获取特色商品(限制数量) + List featuredProducts = merchantProducts.stream() + .limit(productLimit) + .map(product -> { + MallProductListAppVo productVo = BeanUtils.toBean(product, MallProductListAppVo.class); + List skus = skuMap.getOrDefault(product.getId(), List.of()); + + // 计算最低价格 + productVo.setMinPrice(skus.stream() + .map(MallProductSkuEntity::getPrice) + .filter(Objects::nonNull) + .min(java.math.BigDecimal::compareTo) + .orElse(java.math.BigDecimal.ZERO)); + + // 计算总库存 + productVo.setTotalStock(skus.stream() + .map(MallProductSkuEntity::getStock) + .filter(Objects::nonNull) + .mapToInt(Integer::intValue) + .sum()); + + + return productVo; + }) + .collect(Collectors.toList()); + + vo.setFeaturedProducts(featuredProducts); + + // 设置商户营业时间 + if (merchant.getBusinessHours() != null && !merchant.getBusinessHours().trim().isEmpty()) { + vo.setBusinessHours(merchant.getBusinessHours()); + } else { + // 默认商户营业时间 + vo.setBusinessHours("10:00-22:00"); + } + + return vo; + }).collect(Collectors.toList()); + + return R.ok(result); } /** @@ -427,6 +531,116 @@ public class MallAppController { } } + // ==================== 商户分类管理接口 ==================== + + /** + * 商户创建分类 + */ + @PostMapping("/merchant/category/create") + @Operation(summary = "商户创建分类", description = "移动端商户创建分类") + public R createCategory(@RequestBody MallCategoryEntity category) { + try { + Long merchantId = validateMerchant(); + // 自动设置商户ID + category.setMerchantId(merchantId); + category.setDeptId(SecurityUtils.getUser().getDeptId()); + + // 创建分类 + boolean success = mallCategoryService.save(category); + if (success) { + return R.ok("分类创建成功"); + } else { + return R.failed("分类创建失败"); + } + } catch (Exception e) { + return R.failed("分类创建失败:" + e.getMessage()); + } + } + + /** + * 商户更新分类 + */ + @PutMapping("/merchant/category/update") + @Operation(summary = "商户更新分类", description = "移动端商户更新分类") + public R updateCategory(@RequestBody MallCategoryEntity category) { + try { + Long merchantId = validateMerchant(); + // 验证分类权限 + validateCategoryPermission(category.getId(), merchantId); + + // 验证分类ID + if (category.getId() == null) { + return R.failed("分类ID不能为空"); + } + + // 更新分类 + boolean success = mallCategoryService.updateById(category); + if (success) { + return R.ok("分类更新成功"); + } else { + return R.failed("分类更新失败"); + } + } catch (Exception e) { + return R.failed("分类更新失败:" + e.getMessage()); + } + } + + /** + * 商户删除分类 + */ + @DeleteMapping("/merchant/category/{id}") + @Operation(summary = "商户删除分类", description = "移动端商户删除自己的分类") + public R deleteCategory(@PathVariable("id") Long id) { + try { + Long merchantId = validateMerchant(); + validateCategoryPermission(id, merchantId); + + // 删除分类(逻辑删除) + boolean success = mallCategoryService.removeById(id); + if (success) { + return R.ok("分类删除成功"); + } else { + return R.failed("分类删除失败"); + } + } catch (Exception e) { + return R.failed("分类删除失败:" + e.getMessage()); + } + } + + /** + * 商户获取自己的分类列表 + */ + @GetMapping("/merchant/category/list") + @Operation(summary = "商户分类列表", description = "移动端商户获取自己的分类列表") + public R> getMerchantCategoryList(@RequestParam(required = false) Long parentId) { + try { + Long merchantId = validateMerchant(); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(MallCategoryEntity::getMerchantId, merchantId); + wrapper.eq(parentId != null, MallCategoryEntity::getParentId, parentId); + wrapper.orderByAsc(MallCategoryEntity::getSort); + + List list = mallCategoryService.list(wrapper); + return R.ok(list); + } catch (Exception e) { + return R.failed("获取分类列表失败:" + e.getMessage()); + } + } + + /** + * 验证是否有处理该条分类数据的权限 + */ + private void validateCategoryPermission(Long categoryId, Long merchantId) { + MallCategoryEntity category = mallCategoryService.getById(categoryId); + if (category == null) { + throw new BusinessException("分类不存在"); + } + if (!merchantId.equals(category.getMerchantId())) { + throw new BusinessException("无权限处理此分类"); + } + } + // ==================== 库存相关接口 ==================== /** diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallCategoryController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallCategoryController.java index d161fd429..d3d1c6833 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallCategoryController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallCategoryController.java @@ -52,6 +52,8 @@ public class MallCategoryController { public R getMallCategoryPage(@ParameterObject Page page, @ParameterObject MallCategoryEntity MallCategory) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(StrUtil.isNotBlank(MallCategory.getName()),MallCategoryEntity::getName,MallCategory.getName()); + // 添加商户ID过滤条件 + wrapper.eq(MallCategory.getMerchantId() != null, MallCategoryEntity::getMerchantId, MallCategory.getMerchantId()); return R.ok(MallCategoryService.pageScope(page, wrapper)); } @@ -79,6 +81,13 @@ public class MallCategoryController { @PreAuthorize("@pms.hasPermission('supermarket_mallCategory_add')" ) public R save(@RequestBody MallCategoryEntity MallCategory) { MallCategory.setDeptId(SecurityUtils.getUser().getDeptId()); + // 设置商户ID + if (MallCategory.getMerchantId() == null) { + return R.failed("商户ID不能为空"); + } + // 如果前端没有传商户ID,可以从当前用户信息中获取 + // 这里需要根据实际业务逻辑来设置,可能需要从用户关联的商户信息中获取 + // MallCategory.setMerchantId(getCurrentUserMerchantId()); return R.ok(MallCategoryService.save(MallCategory)); } @@ -119,7 +128,11 @@ public class MallCategoryController { @GetMapping("/export") @PreAuthorize("@pms.hasPermission('supermarket_mallCategory_export')" ) public List export(MallCategoryEntity MallCategory,Long[] ids) { - return MallCategoryService.list(Wrappers.lambdaQuery(MallCategory).in(ArrayUtil.isNotEmpty(ids), MallCategoryEntity::getId, ids)); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(MallCategory); + wrapper.in(ArrayUtil.isNotEmpty(ids), MallCategoryEntity::getId, ids); + // 添加商户ID过滤条件 + wrapper.eq(MallCategory.getMerchantId() != null, MallCategoryEntity::getMerchantId, MallCategory.getMerchantId()); + return MallCategoryService.list(wrapper); } /** @@ -127,8 +140,11 @@ public class MallCategoryController { */ @GetMapping("/tree") @Operation(summary = "分类树", description = "获取所有分类的树结构") - public R> tree() { - List all = MallCategoryService.listScope(); + public R> tree(@RequestParam(required = false) Long merchantId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + // 添加商户ID过滤条件 + wrapper.eq(merchantId != null, MallCategoryEntity::getMerchantId, merchantId); + List all = MallCategoryService.list(wrapper); List tree = buildCategoryTree(all, 0L); return R.ok(tree); } @@ -150,8 +166,12 @@ public class MallCategoryController { */ @GetMapping("/listByParent/{parentId}") @Operation(summary = "根据父级ID查子分类", description = "根据父级ID查子分类") - public R> listByParent(@PathVariable("parentId") Long parentId) { - List list = MallCategoryService.list(Wrappers.lambdaQuery().eq(MallCategoryEntity::getParentId, parentId)); + public R> listByParent(@PathVariable("parentId") Long parentId, @RequestParam(required = false) Long merchantId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(MallCategoryEntity::getParentId, parentId); + // 添加商户ID过滤条件 + wrapper.eq(merchantId != null, MallCategoryEntity::getMerchantId, merchantId); + List list = MallCategoryService.list(wrapper); return R.ok(list); } diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallMerchantEntity.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallMerchantEntity.java index d4267aafd..5f129cc65 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallMerchantEntity.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/entity/MallMerchantEntity.java @@ -89,6 +89,12 @@ public class MallMerchantEntity extends Model { @Schema(description="备注") private String remark; + /** + * 营业时间(格式:HH:mm-HH:mm,如:10:30-22:00) + */ + @Schema(description="营业时间(格式:HH:mm-HH:mm)") + private String businessHours; + /** * 部门ID */ diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/MallMerchantUserVo.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/MallMerchantUserVo.java index c95fe7513..f90cc862e 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/MallMerchantUserVo.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/MallMerchantUserVo.java @@ -83,3 +83,4 @@ public class MallMerchantUserVo { } + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallMerchantListAppVo.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallMerchantListAppVo.java new file mode 100644 index 000000000..29b63942e --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallMerchantListAppVo.java @@ -0,0 +1,101 @@ +package com.bsd.pjcloud.supermarket.vo.app; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 移动端商户列表VO + * + * @author zjf + * @date 2025-01-27 + */ +@Data +@Schema(description = "移动端商户列表VO") +public class MallMerchantListAppVo { + + /** + * 商户ID + */ + @Schema(description = "商户ID") + private Long id; + + /** + * 商户名称 + */ + @Schema(description = "商户名称") + private String name; + + /** + * 商户简称 + */ + @Schema(description = "商户简称") + private String shortName; + + /** + * 联系人 + */ + @Schema(description = "联系人") + private String contactName; + + /** + * 联系电话 + */ + @Schema(description = "联系电话") + private String contactPhone; + + /** + * 邮箱 + */ + @Schema(description = "邮箱") + private String email; + + /** + * 状态(1启用 0停用) + */ + @Schema(description = "状态") + private String status; + + /** + * 营业执照号 + */ + @Schema(description = "营业执照号") + private String licenseNo; + + /** + * 详细地址 + */ + @Schema(description = "详细地址") + private String address; + + /** + * 商户图片URL(逗号分隔) + */ + @Schema(description = "商户图片URL") + private String images; + + /** + * 备注 + */ + @Schema(description = "备注") + private String remark; + + /** + * 特色商品列表(最多显示3个) + */ + @Schema(description = "特色商品列表") + private List featuredProducts; + + /** + * 商品总数 + */ + @Schema(description = "商品总数") + private Integer productCount; + + /** + * 商户营业时间(格式:HH:mm-HH:mm,如:10:30-22:00) + */ + @Schema(description = "商户营业时间") + private String businessHours; +} diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallProductListAppVo.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallProductListAppVo.java index 44b489230..86e0bea61 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallProductListAppVo.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/vo/app/MallProductListAppVo.java @@ -104,5 +104,6 @@ public class MallProductListAppVo { */ @Schema(description = "最低价格") private BigDecimal minPrice; + } diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/MallCategoryMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/MallCategoryMapper.xml index e89e9fa33..bd8c249b9 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/MallCategoryMapper.xml +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/MallCategoryMapper.xml @@ -18,5 +18,6 @@ + \ No newline at end of file