买家端需要的获取商家主页和模糊搜索接口
This commit is contained in:
@@ -8,12 +8,16 @@ import com.aida.seller.module.designer.dto.DesignerAuditDTO;
|
||||
import com.aida.seller.module.designer.dto.DesignerDTO;
|
||||
import com.aida.seller.module.designer.entity.DesignerEntity;
|
||||
import com.aida.seller.module.designer.service.DesignerService;
|
||||
import com.aida.seller.module.designer.vo.DesignerSearchVO;
|
||||
import com.aida.seller.module.designer.vo.DesignerShopVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "设计师入驻管理")
|
||||
@RestController
|
||||
@RequestMapping("/designer")
|
||||
@@ -95,4 +99,20 @@ public class DesignerController {
|
||||
designerService.deleteByUserId(userId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "搜索设计师", description = "根据关键词不区分大小写同时匹配店铺名称和所有者姓名,返回设计师信息、最近5张商品封面图(按updateTime倒序)、商品总数")
|
||||
@GetMapping("/search")
|
||||
public Response<List<DesignerSearchVO>> search(
|
||||
@Parameter(description = "关键词(同时匹配店铺名称和所有者姓名,不区分大小写)") @RequestParam String keyword) {
|
||||
List<DesignerSearchVO> result = designerService.searchDesigners(keyword);
|
||||
return Response.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取设计师店铺详情", description = "根据 sellerId(即 卖家userId)获取店铺公开信息,供买家端店铺主页调用")
|
||||
@GetMapping("/shop/{sellerId}")
|
||||
public Response<DesignerShopVO> getShopDetail(
|
||||
@Parameter(description = "设计师用户ID") @PathVariable Long sellerId) {
|
||||
DesignerShopVO vo = designerService.getShopDetailBySellerId(sellerId);
|
||||
return Response.success(vo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@ import com.aida.seller.module.designer.dto.DesignerApplyDTO;
|
||||
import com.aida.seller.module.designer.dto.DesignerAuditDTO;
|
||||
import com.aida.seller.module.designer.dto.DesignerDTO;
|
||||
import com.aida.seller.module.designer.entity.DesignerEntity;
|
||||
import com.aida.seller.module.designer.vo.DesignerSearchVO;
|
||||
import com.aida.seller.module.designer.vo.DesignerShopVO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DesignerService extends IService<DesignerEntity> {
|
||||
|
||||
/**
|
||||
@@ -58,4 +62,20 @@ public interface DesignerService extends IService<DesignerEntity> {
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
void deleteByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 模糊搜索设计师(不区分大小写),返回设计师信息及关联商品封面列表
|
||||
*
|
||||
* @param keyword 关键词(同时匹配店铺名称和所有者姓名,不区分大小写)
|
||||
* @return 搜索结果列表,每条包含设计师基础信息、最近5张商品封面图(按updateTime倒序)、商品总数
|
||||
*/
|
||||
List<DesignerSearchVO> searchDesigners(String keyword);
|
||||
|
||||
/**
|
||||
* 根据 sellerId(即 userId)获取设计师店铺详情,供买家端店铺主页调用
|
||||
*
|
||||
* @param sellerId 设计师用户ID
|
||||
* @return 店铺详情
|
||||
*/
|
||||
DesignerShopVO getShopDetailBySellerId(Long sellerId);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.aida.seller.module.designer.dto.DesignerDTO;
|
||||
import com.aida.seller.module.designer.entity.DesignerEntity;
|
||||
import com.aida.seller.module.designer.enums.DesignerApplyStatusEnum;
|
||||
import com.aida.seller.module.designer.mapper.DesignerMapper;
|
||||
import com.aida.seller.module.designer.vo.DesignerSearchVO;
|
||||
import com.aida.seller.module.designer.vo.DesignerShopVO;
|
||||
import com.aida.seller.module.listing.entity.ListingEntity;
|
||||
import com.aida.seller.module.listing.entity.ListingImageEntity;
|
||||
import com.aida.seller.module.listing.mapper.ListingImageMapper;
|
||||
@@ -25,8 +27,10 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -296,4 +300,95 @@ public class DesignerServiceImpl extends ServiceImpl<DesignerMapper, DesignerEnt
|
||||
// 3. 逻辑删除设计师本人
|
||||
this.removeById(sellerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DesignerSearchVO> searchDesigners(String keyword) {
|
||||
// Step 1: 构造设计师模糊查询条件,同时匹配店铺名称和所有者姓名,不区分大小写
|
||||
LambdaQueryWrapper<DesignerEntity> designerQuery = new LambdaQueryWrapper<>();
|
||||
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
designerQuery.and(wrapper -> wrapper
|
||||
.apply("LOWER(shop_name) LIKE LOWER({0})", "%" + keyword + "%")
|
||||
.or()
|
||||
.apply("LOWER(owner_name) LIKE LOWER({0})", "%" + keyword + "%")
|
||||
);
|
||||
}
|
||||
|
||||
List<DesignerEntity> designers = this.list(designerQuery);
|
||||
if (designers.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// Step 2: 提取设计师的 userId 集合,用于后续按 userId 查询其关联商品
|
||||
List<Long> userIds = designers.stream()
|
||||
.map(DesignerEntity::getUserId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Step 3: 查询所有匹配设计师关联的商品,按 updateTime 倒序
|
||||
LambdaQueryWrapper<ListingEntity> listingQuery = new LambdaQueryWrapper<ListingEntity>()
|
||||
.in(ListingEntity::getSellerId, userIds)
|
||||
.orderByDesc(ListingEntity::getUpdateTime);
|
||||
List<ListingEntity> listings = listingMapper.selectList(listingQuery);
|
||||
|
||||
if (listings.isEmpty()) {
|
||||
return designers.stream().map(d -> buildSearchVO(d, List.of(), 0L))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// Step 4: 按 sellerId 分组,统计每个设计师的商品总数
|
||||
Map<Long, Long> listingCountMap = listings.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
ListingEntity::getSellerId,
|
||||
Collectors.counting()
|
||||
));
|
||||
|
||||
// Step 5: 按 sellerId 分组,便于后续取每个设计师的商品列表
|
||||
Map<Long, List<ListingEntity>> listingsByDesigner = listings.stream()
|
||||
.collect(Collectors.groupingBy(ListingEntity::getSellerId));
|
||||
|
||||
// Step 6: 组装每个设计师的搜索结果,最多取 5 个商品封面图
|
||||
return designers.stream().map(d -> {
|
||||
List<String> covers = listingsByDesigner
|
||||
.getOrDefault(d.getId(), List.of())
|
||||
.stream()
|
||||
.filter(l -> l.getCover() != null && !l.getCover().isBlank())
|
||||
.limit(5)
|
||||
.map(l -> minioUtil.processMinioResource(l.getCover(), CommonConstants.MINIO_PATH_TIMEOUT))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
long listingTotal = listingCountMap.getOrDefault(d.getId(), 0L);
|
||||
return buildSearchVO(d, covers, listingTotal);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private DesignerSearchVO buildSearchVO(DesignerEntity entity, List<String> covers, Long listingTotal) {
|
||||
DesignerSearchVO vo = new DesignerSearchVO();
|
||||
vo.setSellerId(entity.getUserId());
|
||||
vo.setShopName(entity.getShopName());
|
||||
vo.setOwnerName(entity.getOwnerName());
|
||||
vo.setAvatar(minioUtil.processMinioResource(entity.getAvatar(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
vo.setCovers(covers);
|
||||
vo.setListingTotal(listingTotal);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesignerShopVO getShopDetailBySellerId(Long sellerId) {
|
||||
DesignerEntity entity = this.getOne(
|
||||
new LambdaQueryWrapper<DesignerEntity>()
|
||||
.eq(DesignerEntity::getUserId, sellerId)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (entity == null) {
|
||||
throw new BusinessException("设计师不存在");
|
||||
}
|
||||
DesignerShopVO vo = new DesignerShopVO();
|
||||
vo.setShopName(entity.getShopName());
|
||||
vo.setAvatar(minioUtil.processMinioResource(entity.getAvatar(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
vo.setBrandBanner(minioUtil.processMinioResource(entity.getBrandBanner(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
vo.setOwnerName(entity.getOwnerName());
|
||||
vo.setDescription(entity.getDescription());
|
||||
vo.setSocialLinks(entity.getSocialLinks());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.aida.seller.module.designer.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 设计师搜索结果VO
|
||||
*/
|
||||
@Data
|
||||
public class DesignerSearchVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 用户ID */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long sellerId;
|
||||
|
||||
/** 店铺名称 */
|
||||
private String shopName;
|
||||
|
||||
/** 所有者全名 */
|
||||
private String ownerName;
|
||||
|
||||
/** 店铺头像URL */
|
||||
private String avatar;
|
||||
|
||||
/** 商品封面图列表(最多5张,按更新时间倒序) */
|
||||
private List<String> covers;
|
||||
|
||||
/** 该设计师的商品总数 */
|
||||
private Long listingTotal;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.aida.seller.module.designer.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设计师店铺详情VO,供买家端店铺主页调用
|
||||
*/
|
||||
@Data
|
||||
public class DesignerShopVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 店铺名称 */
|
||||
private String shopName;
|
||||
|
||||
/** 店铺头像URL */
|
||||
private String avatar;
|
||||
|
||||
/** 品牌 Banner URL */
|
||||
private String brandBanner;
|
||||
|
||||
/** 所有者全名 */
|
||||
private String ownerName;
|
||||
|
||||
/** 店铺简介 */
|
||||
private String description;
|
||||
|
||||
/** 社交媒体链接(JSON 字符串) */
|
||||
private String socialLinks;
|
||||
}
|
||||
@@ -83,4 +83,15 @@ public class ListingController {
|
||||
boolean needPopup = listingService.checkPopupReminder(sellerId);
|
||||
return Response.success(needPopup ? 1 : 0);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取店铺商品列表", description = "按 status=1、deleted=0、designFor 筛选,返回店铺已发布商品分页列表")
|
||||
@GetMapping("/shop")
|
||||
public Response<PageResponse<ListingPageVO>> getShopListings(
|
||||
@Parameter(description = "设计师用户ID") @RequestParam Long sellerId,
|
||||
@Parameter(description = "适用性别 female/male/all") @RequestParam String designFor,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int pageNum,
|
||||
@Parameter(description = "每页数量") @RequestParam(defaultValue = "10") int pageSize) {
|
||||
IPage<ListingPageVO> page = listingService.getShopListings(sellerId, designFor, pageNum, pageSize);
|
||||
return Response.success(PageResponse.success(page));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import lombok.Getter;
|
||||
public enum DesignForEnum {
|
||||
|
||||
MALE("male", "男性"),
|
||||
FEMALE("female", "女性");
|
||||
FEMALE("female", "女性"),
|
||||
ALL("all", "全部");
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
@@ -71,4 +71,16 @@ public interface ListingService extends IService<ListingEntity> {
|
||||
* @return true需要弹窗,false不需要
|
||||
*/
|
||||
boolean checkPopupReminder(Long sellerId);
|
||||
|
||||
/**
|
||||
* 获取店铺已发布商品列表,供买家端店铺主页调用
|
||||
* <p>按 status=1、deleted=0、sellerId、designFor 筛选,按 updateTime 倒序</p>
|
||||
*
|
||||
* @param sellerId 设计师用户ID
|
||||
* @param designFor 适用性别 female/male,all 表示不限制性别
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 分页商品列表
|
||||
*/
|
||||
IPage<ListingPageVO> getShopListings(Long sellerId, String designFor, int pageNum, int pageSize);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.aida.seller.module.listing.service;
|
||||
|
||||
import com.aida.seller.common.constants.CommonConstants;
|
||||
import com.aida.seller.common.context.UserContext;
|
||||
import com.aida.seller.common.exception.BusinessException;
|
||||
import com.aida.seller.module.listing.dto.*;
|
||||
import com.aida.seller.module.listing.entity.ListingEntity;
|
||||
@@ -298,4 +299,35 @@ public class ListingServiceImpl extends ServiceImpl<ListingMapper, ListingEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<ListingPageVO> getShopListings(Long sellerId, String designFor, int pageNum, int pageSize) {
|
||||
Page<ListingEntity> pageParam = new Page<>(pageNum, pageSize);
|
||||
LambdaQueryWrapper<ListingEntity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ListingEntity::getSellerId, sellerId);
|
||||
DesignForEnum designForEnum = DesignForEnum.of(designFor);
|
||||
if (designForEnum == null) {
|
||||
throw new BusinessException("designFor 只能为 female/male/all");
|
||||
}
|
||||
if (designForEnum != DesignForEnum.ALL) {
|
||||
queryWrapper.eq(ListingEntity::getDesignFor, designForEnum.getCode());
|
||||
}
|
||||
queryWrapper.eq(ListingEntity::getStatus, 1);
|
||||
queryWrapper.eq(ListingEntity::getDeleted, 0);
|
||||
queryWrapper.orderByDesc(ListingEntity::getUpdateTime);
|
||||
IPage<ListingEntity> page = this.page(pageParam, queryWrapper);
|
||||
|
||||
Page<ListingPageVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
boolean loggedIn = UserContext.getUserId() != null;
|
||||
result.setRecords(page.getRecords().stream().map(entity -> {
|
||||
ListingPageVO vo = new ListingPageVO();
|
||||
BeanUtils.copyProperties(entity, vo);
|
||||
vo.setCover(minioUtil.processMinioResource(vo.getCover(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
if (!loggedIn) {
|
||||
vo.setPrice(null);
|
||||
}
|
||||
return vo;
|
||||
}).collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,15 +29,6 @@ public class ListingPageVO implements Serializable {
|
||||
/** 价格 */
|
||||
private BigDecimal price;
|
||||
|
||||
/** 销量 */
|
||||
private Integer salesVolume;
|
||||
|
||||
/** 浏览量 */
|
||||
private Integer viewCount;
|
||||
|
||||
/** 状态 */
|
||||
private Integer status;
|
||||
|
||||
/** 创建时间(用于排序) */
|
||||
private LocalDateTime createTime;
|
||||
/** 修改时间 */
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ mybatis-plus:
|
||||
type-aliases-package: com.aida.seller.module.*.entity
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
id-type: assign_id
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
|
||||
Reference in New Issue
Block a user