微服务改造
This commit is contained in:
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "分页响应结果")
|
||||
public class PageResponse<T> extends Response<List<T>> {
|
||||
public class PageResponse<T> {
|
||||
@Schema(description = "页码")
|
||||
private long page;
|
||||
@Schema(description = "每页数量")
|
||||
@@ -26,21 +26,16 @@ public class PageResponse<T> extends Response<List<T>> {
|
||||
@Schema(description = "结果集")
|
||||
private List<T> content;
|
||||
|
||||
public PageResponse(Response<List<T>> response, long page, long size, long total, long pages) {
|
||||
if (response != null) {
|
||||
this.setData(response.getData());
|
||||
this.setErrCode(response.getErrCode());
|
||||
this.setErrMsg(response.getErrMsg());
|
||||
}
|
||||
|
||||
public PageResponse(List<T> list, long page, long size, long total, long pages) {
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
this.total = total;
|
||||
this.pages = pages;
|
||||
this.content = response.getData();
|
||||
this.content = list;
|
||||
}
|
||||
|
||||
public static <T> PageResponse<T> success(IPage<T> page) {
|
||||
Response<List<T>> response = success(page.getRecords());
|
||||
return new PageResponse<>(response, page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
|
||||
return new PageResponse<>(page.getRecords(), page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.aida.seller.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Configuration
|
||||
public class MyBatisPlusConfig {
|
||||
|
||||
@@ -15,4 +19,20 @@ public class MyBatisPlusConfig {
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MetaObjectHandler metaObjectHandler() {
|
||||
return new MetaObjectHandler() {
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
|
||||
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.aida.seller.module.designer.controller;
|
||||
|
||||
import com.aida.seller.common.context.UserContext;
|
||||
import com.aida.seller.common.result.PageResponse;
|
||||
import com.aida.seller.common.result.Response;
|
||||
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.service.DesignerService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -22,8 +24,8 @@ public class DesignerController {
|
||||
|
||||
@Operation(summary = "查询设计师是否有售卖资格")
|
||||
@GetMapping("/check")
|
||||
public Response<Boolean> check(
|
||||
@Parameter(description = "用户ID") @RequestParam Long userId) {
|
||||
public Response<Boolean> check() {
|
||||
Long userId = UserContext.getUserId();
|
||||
boolean hasQualification = designerService.checkQualification(userId);
|
||||
return Response.success(hasQualification);
|
||||
}
|
||||
@@ -61,4 +63,28 @@ public class DesignerController {
|
||||
designerService.audit(dto);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取设计师申请状态", description = "根据当前登录用户ID获取设计师申请状态0-待审核, 1-审核通过, 2-审核拒绝,null-未申请过")
|
||||
@GetMapping("/apply/status")
|
||||
public Response<Integer> getApplyStatus() {
|
||||
Long userId = UserContext.getUserId();
|
||||
Integer applyStatus = designerService.getApplyStatus(userId);
|
||||
return Response.success(applyStatus);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新设计师信息", description = "当前登录设计师更新自身信息")
|
||||
@PutMapping("/update")
|
||||
public Response<Void> update(
|
||||
@Parameter(description = "更新表单") @RequestBody DesignerDTO dto) {
|
||||
designerService.update(dto);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取设计师详细信息", description = "根据当前登录用户ID获取设计师详细信息")
|
||||
@GetMapping("/info")
|
||||
public Response<DesignerDTO> getInfo() {
|
||||
Long userId = UserContext.getUserId();
|
||||
DesignerDTO designerInfo = designerService.getDesignerInfo(userId);
|
||||
return Response.success(designerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,11 @@ public class DesignerApplyDTO implements Serializable {
|
||||
|
||||
@Schema(description = "手机号")
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "作品集/社交媒体链接(JSON数组)")
|
||||
private String socialLinks;
|
||||
|
||||
@Schema(description = "设计师简介")
|
||||
private String description;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.aida.seller.module.designer.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设计师信息DTO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "设计师信息表单")
|
||||
public class DesignerDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "店铺名称")
|
||||
private String shopName;
|
||||
|
||||
@Schema(description = "店铺头像URL")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "品牌Banner URL")
|
||||
private String brandBanner;
|
||||
|
||||
@Schema(description = "所有者全名")
|
||||
private String ownerName;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "作品集/社交媒体链接(JSON数组)")
|
||||
private String socialLinks;
|
||||
|
||||
@Schema(description = "设计师简介")
|
||||
private String description;
|
||||
}
|
||||
@@ -9,13 +9,13 @@ import java.time.LocalDateTime;
|
||||
* 设计师表实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("designer")
|
||||
@TableName("seller_designer")
|
||||
public class DesignerEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 设计师ID */
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 用户ID(关联用户表) */
|
||||
@@ -41,6 +41,9 @@ public class DesignerEntity implements Serializable {
|
||||
|
||||
/** 作品集/社交媒体链接(JSON数组) */
|
||||
private String socialLinks;
|
||||
|
||||
/** 设计师简介 */
|
||||
private String description;
|
||||
|
||||
/** 申请状态: 0-待审核, 1-审核通过, 2-审核拒绝 */
|
||||
private Integer applyStatus;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.aida.seller.module.designer.service;
|
||||
|
||||
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.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
@@ -33,4 +34,19 @@ public interface DesignerService extends IService<DesignerEntity> {
|
||||
* 审核设计师入驻申请
|
||||
*/
|
||||
void audit(DesignerAuditDTO dto);
|
||||
|
||||
/**
|
||||
* 获取设计师申请状态
|
||||
*/
|
||||
Integer getApplyStatus(Long userId);
|
||||
|
||||
/**
|
||||
* 更新设计师信息
|
||||
*/
|
||||
void update(DesignerDTO dto);
|
||||
|
||||
/**
|
||||
* 获取设计师详细信息
|
||||
*/
|
||||
DesignerDTO getDesignerInfo(Long userId);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.aida.seller.module.designer.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.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.enums.DesignerApplyStatusEnum;
|
||||
import com.aida.seller.module.designer.mapper.DesignerMapper;
|
||||
import com.aida.seller.util.MinioUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -20,6 +24,7 @@ import java.time.LocalDateTime;
|
||||
@RequiredArgsConstructor
|
||||
public class DesignerServiceImpl extends ServiceImpl<DesignerMapper, DesignerEntity> implements DesignerService {
|
||||
|
||||
private final MinioUtil minioUtil;
|
||||
@Override
|
||||
public Boolean checkQualification(Long userId) {
|
||||
DesignerEntity entity = this.getOne(
|
||||
@@ -50,14 +55,15 @@ public class DesignerServiceImpl extends ServiceImpl<DesignerMapper, DesignerEnt
|
||||
}
|
||||
|
||||
DesignerEntity entity = new DesignerEntity();
|
||||
entity.setUserId(dto.getUserId());
|
||||
entity.setUserId(UserContext.getUserId());
|
||||
entity.setShopName(dto.getShopName());
|
||||
entity.setAvatar(dto.getAvatar());
|
||||
entity.setBrandBanner(dto.getBrandBanner());
|
||||
entity.setAvatar(minioUtil.convertToLogicalPath(dto.getAvatar()));
|
||||
entity.setBrandBanner(minioUtil.convertToLogicalPath(dto.getBrandBanner()));
|
||||
entity.setOwnerName(dto.getOwnerName());
|
||||
entity.setEmail(dto.getEmail());
|
||||
entity.setMobile(dto.getMobile());
|
||||
entity.setSocialLinks(dto.getSocialLinks());
|
||||
entity.setDescription(dto.getDescription());
|
||||
entity.setApplyStatus(DesignerApplyStatusEnum.PENDING.getCode());
|
||||
entity.setStatus(0);
|
||||
|
||||
@@ -130,4 +136,78 @@ public class DesignerServiceImpl extends ServiceImpl<DesignerMapper, DesignerEnt
|
||||
|
||||
this.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getApplyStatus(Long userId) {
|
||||
DesignerEntity entity = this.getOne(
|
||||
new LambdaQueryWrapper<DesignerEntity>()
|
||||
.eq(DesignerEntity::getUserId, userId)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
return entity != null ? entity.getApplyStatus() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DesignerDTO dto) {
|
||||
Long userId = UserContext.getUserId();
|
||||
DesignerEntity entity = this.getOne(
|
||||
new LambdaQueryWrapper<DesignerEntity>()
|
||||
.eq(DesignerEntity::getUserId, userId)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (entity == null) {
|
||||
throw new BusinessException("设计师记录不存在");
|
||||
}
|
||||
|
||||
if (dto.getShopName() != null) {
|
||||
entity.setShopName(dto.getShopName());
|
||||
}
|
||||
if (dto.getAvatar() != null) {
|
||||
entity.setAvatar(dto.getAvatar());
|
||||
}
|
||||
if (dto.getBrandBanner() != null) {
|
||||
entity.setBrandBanner(dto.getBrandBanner());
|
||||
}
|
||||
if (dto.getOwnerName() != null) {
|
||||
entity.setOwnerName(dto.getOwnerName());
|
||||
}
|
||||
if (dto.getEmail() != null) {
|
||||
entity.setEmail(dto.getEmail());
|
||||
}
|
||||
if (dto.getMobile() != null) {
|
||||
entity.setMobile(dto.getMobile());
|
||||
}
|
||||
if (dto.getSocialLinks() != null) {
|
||||
entity.setSocialLinks(dto.getSocialLinks());
|
||||
}
|
||||
if (dto.getDescription() != null) {
|
||||
entity.setDescription(dto.getDescription());
|
||||
}
|
||||
|
||||
this.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesignerDTO getDesignerInfo(Long userId) {
|
||||
DesignerEntity entity = this.getOne(
|
||||
new LambdaQueryWrapper<DesignerEntity>()
|
||||
.eq(DesignerEntity::getUserId, userId)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (entity == null) {
|
||||
throw new BusinessException("设计师记录不存在");
|
||||
}
|
||||
|
||||
DesignerDTO dto = new DesignerDTO();
|
||||
dto.setShopName(entity.getShopName());
|
||||
dto.setAvatar(minioUtil.processMinioResource(entity.getAvatar(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
dto.setBrandBanner(minioUtil.processMinioResource(entity.getBrandBanner(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
dto.setOwnerName(entity.getOwnerName());
|
||||
dto.setEmail(entity.getEmail());
|
||||
dto.setMobile(entity.getMobile());
|
||||
dto.setSocialLinks(entity.getSocialLinks());
|
||||
dto.setDescription(entity.getDescription());
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.aida.seller.module.file;
|
||||
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
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.common.result.Response;
|
||||
import com.aida.seller.util.MinioUtil;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 文件上传控制器
|
||||
*
|
||||
* @author Fida Team
|
||||
* @date 2026-02-03
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
@Tag(name = "文件管理", description = "文件上传、下载等功能接口")
|
||||
@RequiredArgsConstructor
|
||||
public class FileUploadController {
|
||||
|
||||
private final MinioUtil minioUtil;
|
||||
|
||||
@Value("${multipart.max-file-size}")
|
||||
private Long maxFileSize;
|
||||
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "文件上传", description = "上传文件到Minio服务器")
|
||||
public Response<String> uploadFile(
|
||||
@Parameter(description = "文件", required = true) @RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "允许的文件类型") @RequestParam(value = "allowedTypes", required = false) String[] allowedTypes
|
||||
) {
|
||||
Long userId = UserContext.getUserId();
|
||||
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("文件不能为空");
|
||||
}
|
||||
// 验证文件类型
|
||||
String contentType = file.getContentType();
|
||||
if (allowedTypes != null && allowedTypes.length > 0) {
|
||||
boolean validType = false;
|
||||
for (String allowedType : allowedTypes) {
|
||||
if (contentType != null && contentType.startsWith(allowedType)) {
|
||||
validType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!validType) {
|
||||
throw new BusinessException("不支持的文件类型: " + contentType);
|
||||
}
|
||||
}
|
||||
// 验证文件大小
|
||||
if (file.getSize() > maxFileSize * 1024 * 1024) {
|
||||
throw new BusinessException("文件大小超出限制: " + maxFileSize + " MB");
|
||||
}
|
||||
try {
|
||||
// 计算文件MD5(可选,用于文件完整性校验)
|
||||
String md5 = DigestUtil.md5Hex(file.getInputStream());
|
||||
log.info("文件MD5: {}", md5);
|
||||
|
||||
// 调用MinioUtil上传文件(使用默认桶名,按userId划分文件夹)
|
||||
String filePath = minioUtil.uploadImage(file, String.valueOf(userId));
|
||||
|
||||
log.info("文件上传成功: {}, 文件路径: {}", file.getOriginalFilename(), filePath);
|
||||
|
||||
return Response.success(minioUtil.processMinioResource(filePath, CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
} catch (IOException e) {
|
||||
log.error("文件上传失败: {}", e.getMessage(), e);
|
||||
throw new BusinessException("文件上传失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.aida.seller.module.listing.controller;
|
||||
|
||||
import com.aida.seller.common.context.UserContext;
|
||||
import com.aida.seller.common.result.PageResponse;
|
||||
import com.aida.seller.common.result.Response;
|
||||
import com.aida.seller.module.listing.dto.*;
|
||||
import com.aida.seller.module.listing.service.ListingService;
|
||||
import com.aida.seller.module.listing.vo.ListingPageVO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 商品管理 Controller
|
||||
*/
|
||||
@Tag(name = "Listing - 商品管理")
|
||||
@RestController
|
||||
@RequestMapping("/listing")
|
||||
@RequiredArgsConstructor
|
||||
public class ListingController {
|
||||
|
||||
private final ListingService listingService;
|
||||
|
||||
@Operation(summary = "批量保存/更新商品", description = "根据 id 是否存在判断新增或更新,同时保存图片")
|
||||
@PostMapping("/batch")
|
||||
public Response<Void> saveOrUpdate(
|
||||
@Parameter(description = "商品保存/更新表单") @RequestBody ListingSaveDTO dto) {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
listingService.saveOrUpdate(dto, sellerId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取商品详情", description = "返回商品信息及所有图片")
|
||||
@GetMapping("/detail")
|
||||
public Response<ListingSaveDTO> getById(
|
||||
@Parameter(description = "商品ID") @RequestParam Long id) {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
ListingSaveDTO result = listingService.getById(id, sellerId);
|
||||
return Response.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询商品列表", description = "按 status 过滤,返回 ListingPageVO 不含图片详情")
|
||||
@GetMapping("/page")
|
||||
public Response<PageResponse<ListingPageVO>> getPage(
|
||||
@Parameter(description = "商品状态,可选0-草稿, 1-已发布, 2-已删除") @RequestParam(required = false) Integer status,
|
||||
@Parameter(description = "页码,默认1") @RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@Parameter(description = "每页数量,默认10") @RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
ListingQueryDTO dto = new ListingQueryDTO();
|
||||
dto.setStatus(status);
|
||||
dto.setPageNum(pageNum);
|
||||
dto.setPageSize(pageSize);
|
||||
IPage<ListingPageVO> page = listingService.getPage(dto, sellerId);
|
||||
return Response.success(PageResponse.success(page));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新商品状态", description = "支持设为已删除或恢复为草稿")
|
||||
@PutMapping("/status")
|
||||
public Response<Void> updateStatus(
|
||||
@Parameter(description = "状态更新表单") @RequestBody ListingStatusUpdateDTO dto) {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
listingService.updateStatus(dto.getId(), dto.getStatus(), sellerId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "批量设置库存", description = "入参为商品ID和库存值列表")
|
||||
@PutMapping("/stock/batch")
|
||||
public Response<Void> batchUpdateStock(
|
||||
@Parameter(description = "库存更新列表") @RequestBody List<ListingStockDTO> list) {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
listingService.batchUpdateStock(list, sellerId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "设置弹窗提醒标志", description = "在Redis中设置7天过期的弹窗提醒标志")
|
||||
@PostMapping("/popup/set")
|
||||
public Response<Void> setPopupReminder() {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
listingService.setPopupReminder(sellerId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "检查是否需要弹窗提醒", description = "检查Redis中是否存在未过期的弹窗提醒标志")
|
||||
@GetMapping("/popup/check")
|
||||
public Response<Integer> checkPopupReminder() {
|
||||
Long sellerId = UserContext.getUserId();
|
||||
boolean needPopup = listingService.checkPopupReminder(sellerId);
|
||||
return Response.success(needPopup ? 1 : 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.aida.seller.module.listing.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 商品图片 DTO(入参/出参复用)
|
||||
*/
|
||||
@Data
|
||||
public class ListingImageDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 图片ID,有值则更新,无则新增 */
|
||||
@Schema(description = "图片ID,有值则更新,无则新增")
|
||||
private Long id;
|
||||
|
||||
/** 图片类别: cover/main_product/product/sketch/apparel */
|
||||
@Schema(description = "图片类别: cover/main_product/product/sketch/apparel")
|
||||
private String category;
|
||||
|
||||
/** 图片URL */
|
||||
@Schema(description = "图片URL")
|
||||
private String imageUrl;
|
||||
|
||||
/** 排序 */
|
||||
@Schema(description = "排序")
|
||||
private Integer sortOrder;
|
||||
|
||||
/** 是否选中: 0-未选中, 1-选中(仅 product 有效) */
|
||||
@Schema(description = "是否选中: 0-未选中, 1-选中(仅 product 有效)")
|
||||
private Boolean isSelected;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.aida.seller.module.listing.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 商品分页查询 DTO
|
||||
*/
|
||||
@Data
|
||||
public class ListingQueryDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 按状态过滤(选填,不传则查所有非删除) */
|
||||
@Schema(description = "按状态过滤(选填,不传则查所有非删除)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "页码,默认1")
|
||||
private Integer pageNum = 1;
|
||||
|
||||
@Schema(description = "每页数量,默认10")
|
||||
private Integer pageSize = 10;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.aida.seller.module.listing.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品保存/更新 DTO(入参/出参复用)
|
||||
*/
|
||||
@Data
|
||||
public class ListingSaveDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 商品ID,无则新建,有则更新 */
|
||||
@Schema(description = "商品ID,无则新建,有则更新")
|
||||
private Long id;
|
||||
|
||||
/** 商品标题 */
|
||||
@Schema(description = "商品标题")
|
||||
private String title;
|
||||
|
||||
/** 商品描述 */
|
||||
@Schema(description = "商品描述")
|
||||
private String description;
|
||||
|
||||
/** 价格 */
|
||||
@Schema(description = "价格")
|
||||
private BigDecimal price;
|
||||
|
||||
/** 库存 */
|
||||
@Schema(description = "库存")
|
||||
private Integer stock;
|
||||
|
||||
/** 浏览量(更新时传入) */
|
||||
@Schema(description = "浏览量(更新时传入)")
|
||||
private Integer viewCount;
|
||||
|
||||
/** 状态: 0-草稿, 1-已发布, 2-已删除 */
|
||||
@Schema(description = "状态: 0-草稿, 1-已发布, 2-已删除")
|
||||
private Integer status;
|
||||
|
||||
/** 图片列表(更新时全量覆盖) */
|
||||
@Schema(description = "图片列表(更新时全量覆盖)")
|
||||
private List<ListingImageDTO> images;
|
||||
|
||||
/** 适用性别: male/female */
|
||||
@Schema(description = "适用性别: male/female")
|
||||
private String designFor;
|
||||
|
||||
/** 商品分类列表: outwear/trousers/blouse/dress/skirt/accessories */
|
||||
@Schema(description = "商品分类列表: outwear/trousers/blouse/dress/skirt/accessories")
|
||||
private List<String> productCategory;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.aida.seller.module.listing.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 商品状态更新 DTO
|
||||
*/
|
||||
@Data
|
||||
public class ListingStatusUpdateDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 商品ID */
|
||||
@Schema(description = "商品ID")
|
||||
private Long id;
|
||||
|
||||
/** 目标状态: 0-草稿, 1-已发布, 2-已删除 */
|
||||
@Schema(description = "目标状态: 0-草稿, 1-已发布, 2-已删除")
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.aida.seller.module.listing.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 批量库存更新 DTO
|
||||
*/
|
||||
@Data
|
||||
public class ListingStockDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 商品ID */
|
||||
@Schema(description = "商品ID")
|
||||
private Long id;
|
||||
|
||||
/** 库存值 */
|
||||
@Schema(description = "库存值")
|
||||
private Integer stock;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.aida.seller.module.listing.entity;
|
||||
|
||||
import com.aida.seller.module.listing.enums.ProductCategoryEnum;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("seller_listing")
|
||||
public class ListingEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 商品ID */
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 卖家ID */
|
||||
private Long sellerId;
|
||||
|
||||
/** 商品标题 */
|
||||
private String title;
|
||||
|
||||
/** 商品描述 */
|
||||
private String description;
|
||||
|
||||
/** 价格 */
|
||||
private BigDecimal price;
|
||||
|
||||
/** 库存数量 */
|
||||
private Integer stock;
|
||||
|
||||
/** 封面图URL(列表页展示用) */
|
||||
private String cover;
|
||||
|
||||
/** 浏览量 */
|
||||
private Integer viewCount;
|
||||
|
||||
/** 状态: 0-草稿, 1-已发布, 2-已删除 */
|
||||
private Integer status;
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 是否删除:0-否,1-是 */
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
/** 适用性别: male/female */
|
||||
private String designFor;
|
||||
|
||||
/** 商品分类列表 */
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<ProductCategoryEnum> productCategory;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.aida.seller.module.listing.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 商品图片实体
|
||||
*/
|
||||
@Data
|
||||
@TableName("seller_listing_image")
|
||||
public class ListingImageEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 图片ID */
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 商品ID */
|
||||
private Long listingId;
|
||||
|
||||
/** 图片类别: cover/main_product/product/sketch/apparel */
|
||||
private String category;
|
||||
|
||||
/** 图片URL */
|
||||
private String imageUrl;
|
||||
|
||||
/** 排序 */
|
||||
private Integer sortOrder;
|
||||
|
||||
/** 是否选中: 0-未选中, 1-选中(仅 product 有效) */
|
||||
private Integer isSelected;
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.aida.seller.module.listing.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 适用性别枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DesignForEnum {
|
||||
|
||||
MALE("male", "男性"),
|
||||
FEMALE("female", "女性");
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
public static DesignForEnum of(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
for (DesignForEnum value : values()) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.aida.seller.module.listing.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 商品图片类别枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ImageCategoryEnum {
|
||||
|
||||
COVER("cover", "封面图", false),
|
||||
MAIN_PRODUCT("main_product", "主产品图", false),
|
||||
PRODUCT("product", "产品图", true),
|
||||
SKETCH("sketch", "草图", false),
|
||||
APPAREL("apparel", "成衣图", false);
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
private final boolean hasSelection;
|
||||
|
||||
public static ImageCategoryEnum of(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
for (ImageCategoryEnum value : values()) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.aida.seller.module.listing.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 商品状态枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ListingStatusEnum {
|
||||
|
||||
DRAFT(0, "草稿"),
|
||||
PUBLISHED(1, "已发布"),
|
||||
DELETED(2, "已删除");
|
||||
|
||||
private final Integer code;
|
||||
private final String desc;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.aida.seller.module.listing.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 商品分类枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProductCategoryEnum {
|
||||
|
||||
OUTWEAR("outwear", "外套"),
|
||||
TROUSERS("trousers", "裤装"),
|
||||
BLOUSE("blouse", "衬衫"),
|
||||
DRESS("dress", "连衣裙"),
|
||||
SKIRT("skirt", "半身裙"),
|
||||
ACCESSORIES("accessories", "配饰");
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
|
||||
public static ProductCategoryEnum of(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
for (ProductCategoryEnum value : values()) {
|
||||
if (value.code.equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.aida.seller.module.listing.mapper;
|
||||
|
||||
import com.aida.seller.module.listing.entity.ListingImageEntity;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 商品图片 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface ListingImageMapper extends BaseMapper<ListingImageEntity> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.aida.seller.module.listing.mapper;
|
||||
|
||||
import com.aida.seller.module.listing.entity.ListingEntity;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 商品 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface ListingMapper extends BaseMapper<ListingEntity> {
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.aida.seller.module.listing.service;
|
||||
|
||||
import com.aida.seller.module.listing.dto.ListingQueryDTO;
|
||||
import com.aida.seller.module.listing.dto.ListingSaveDTO;
|
||||
import com.aida.seller.module.listing.dto.ListingStockDTO;
|
||||
import com.aida.seller.module.listing.entity.ListingEntity;
|
||||
import com.aida.seller.module.listing.vo.ListingPageVO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* 商品 Service 接口
|
||||
*/
|
||||
public interface ListingService extends IService<ListingEntity> {
|
||||
|
||||
/**
|
||||
* 保存或更新商品(含图片)
|
||||
*
|
||||
* @param dto 商品信息
|
||||
* @param sellerId 卖家ID
|
||||
*/
|
||||
void saveOrUpdate(ListingSaveDTO dto, Long sellerId);
|
||||
|
||||
/**
|
||||
* 获取商品详情(含所有图片)
|
||||
*
|
||||
* @param id 商品ID
|
||||
* @param sellerId 卖家ID
|
||||
* @return 商品详情
|
||||
*/
|
||||
ListingSaveDTO getById(Long id, Long sellerId);
|
||||
|
||||
/**
|
||||
* 分页查询商品列表
|
||||
*
|
||||
* @param dto 查询条件
|
||||
* @param sellerId 卖家ID
|
||||
* @return 分页结果
|
||||
*/
|
||||
IPage<ListingPageVO> getPage(ListingQueryDTO dto, Long sellerId);
|
||||
|
||||
/**
|
||||
* 更新商品状态
|
||||
*
|
||||
* @param id 商品ID
|
||||
* @param status 目标状态
|
||||
* @param sellerId 卖家ID
|
||||
*/
|
||||
void updateStatus(Long id, Integer status, Long sellerId);
|
||||
|
||||
/**
|
||||
* 批量更新库存
|
||||
*
|
||||
* @param list 库存列表
|
||||
* @param sellerId 卖家ID
|
||||
*/
|
||||
void batchUpdateStock(java.util.List<ListingStockDTO> list, Long sellerId);
|
||||
|
||||
/**
|
||||
* 设置弹窗提醒标志(7天过期)
|
||||
*
|
||||
* @param sellerId 卖家ID
|
||||
*/
|
||||
void setPopupReminder(Long sellerId);
|
||||
|
||||
/**
|
||||
* 检查是否需要弹窗提醒
|
||||
*
|
||||
* @param sellerId 卖家ID
|
||||
* @return true需要弹窗,false不需要
|
||||
*/
|
||||
boolean checkPopupReminder(Long sellerId);
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.aida.seller.module.listing.service;
|
||||
|
||||
import com.aida.seller.common.constants.CommonConstants;
|
||||
import com.aida.seller.common.exception.BusinessException;
|
||||
import com.aida.seller.module.listing.dto.*;
|
||||
import com.aida.seller.module.listing.entity.ListingEntity;
|
||||
import com.aida.seller.module.listing.entity.ListingImageEntity;
|
||||
import com.aida.seller.module.listing.enums.ImageCategoryEnum;
|
||||
import com.aida.seller.module.listing.enums.ListingStatusEnum;
|
||||
import com.aida.seller.module.listing.enums.DesignForEnum;
|
||||
import com.aida.seller.module.listing.enums.ProductCategoryEnum;
|
||||
import com.aida.seller.module.listing.mapper.ListingImageMapper;
|
||||
import com.aida.seller.module.listing.mapper.ListingMapper;
|
||||
import com.aida.seller.module.listing.vo.ListingPageVO;
|
||||
import com.aida.seller.util.MinioUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 商品 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ListingServiceImpl extends ServiceImpl<ListingMapper, ListingEntity> implements ListingService {
|
||||
|
||||
private final ListingImageMapper listingImageMapper;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final MinioUtil minioUtil;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveOrUpdate(ListingSaveDTO dto, Long sellerId) {
|
||||
ListingEntity entity = new ListingEntity();
|
||||
BeanUtils.copyProperties(dto, entity);
|
||||
entity.setSellerId(sellerId);
|
||||
|
||||
if (!CollectionUtils.isEmpty(dto.getProductCategory())) {
|
||||
List<ProductCategoryEnum> categories = dto.getProductCategory().stream()
|
||||
.map(ProductCategoryEnum::of)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
entity.setProductCategory(categories.isEmpty() ? null : categories);
|
||||
}
|
||||
|
||||
if (dto.getDesignFor() != null && DesignForEnum.of(dto.getDesignFor()) == null) {
|
||||
throw new BusinessException("designFor 只能为 male/female");
|
||||
}
|
||||
|
||||
if (entity.getViewCount() == null) {
|
||||
entity.setViewCount(0);
|
||||
}
|
||||
|
||||
Long listingId;
|
||||
if (dto.getId() == null) {
|
||||
entity.setStatus(ListingStatusEnum.DRAFT.getCode());
|
||||
this.save(entity);
|
||||
listingId = entity.getId();
|
||||
} else {
|
||||
ListingEntity existing = this.getOne(
|
||||
new LambdaQueryWrapper<ListingEntity>()
|
||||
.eq(ListingEntity::getId, dto.getId())
|
||||
.eq(ListingEntity::getSellerId, sellerId));
|
||||
if (existing == null) {
|
||||
throw new BusinessException("商品不存在");
|
||||
}
|
||||
entity.setCreateTime(existing.getCreateTime());
|
||||
this.updateById(entity);
|
||||
listingImageMapper.delete(new LambdaQueryWrapper<ListingImageEntity>()
|
||||
.eq(ListingImageEntity::getListingId, dto.getId()));
|
||||
listingId = dto.getId();
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(dto.getImages())) {
|
||||
handleImages(listingId, dto.getImages());
|
||||
String cover = extractCover(dto.getImages());
|
||||
if (StringUtils.hasText(cover)) {
|
||||
ListingEntity update = new ListingEntity();
|
||||
update.setId(listingId);
|
||||
update.setCover(minioUtil.convertToLogicalPath(cover));
|
||||
this.updateById(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListingSaveDTO getById(Long id, Long sellerId) {
|
||||
ListingEntity entity = this.getOne(
|
||||
new LambdaQueryWrapper<ListingEntity>()
|
||||
.eq(ListingEntity::getId, id)
|
||||
.eq(ListingEntity::getSellerId, sellerId));
|
||||
if (entity == null) {
|
||||
throw new BusinessException("商品不存在");
|
||||
}
|
||||
|
||||
ListingSaveDTO dto = new ListingSaveDTO();
|
||||
BeanUtils.copyProperties(entity, dto);
|
||||
|
||||
if (!CollectionUtils.isEmpty(entity.getProductCategory())) {
|
||||
dto.setProductCategory(
|
||||
entity.getProductCategory().stream()
|
||||
.map(ProductCategoryEnum::getCode)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
List<ListingImageEntity> images = listingImageMapper.selectList(
|
||||
new LambdaQueryWrapper<ListingImageEntity>()
|
||||
.eq(ListingImageEntity::getListingId, id)
|
||||
.orderByAsc(ListingImageEntity::getSortOrder));
|
||||
if (!CollectionUtils.isEmpty(images)) {
|
||||
List<ListingImageDTO> imageDTOs = images.stream().map(img -> {
|
||||
ListingImageDTO imgDto = new ListingImageDTO();
|
||||
BeanUtils.copyProperties(img, imgDto);
|
||||
imgDto.setImageUrl(minioUtil.processMinioResource(imgDto.getImageUrl(),CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
imgDto.setIsSelected(img.getIsSelected() != null && img.getIsSelected() == 1);
|
||||
return imgDto;
|
||||
}).collect(Collectors.toList());
|
||||
dto.setImages(imageDTOs);
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<ListingPageVO> getPage(ListingQueryDTO dto, Long sellerId) {
|
||||
Page<ListingEntity> pageParam = new Page<>(dto.getPageNum(), dto.getPageSize());
|
||||
LambdaQueryWrapper<ListingEntity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ListingEntity::getSellerId, sellerId);
|
||||
if (dto.getStatus() != null) {
|
||||
queryWrapper.eq(ListingEntity::getStatus, dto.getStatus());
|
||||
} else {
|
||||
queryWrapper.ne(ListingEntity::getStatus, ListingStatusEnum.DELETED.getCode());
|
||||
}
|
||||
queryWrapper.orderByDesc(ListingEntity::getCreateTime);
|
||||
IPage<ListingEntity> page = this.page(pageParam, queryWrapper);
|
||||
|
||||
Page<ListingPageVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
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));
|
||||
return vo;
|
||||
}).collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStatus(Long id, Integer status, Long sellerId) {
|
||||
ListingEntity existing = this.getOne(
|
||||
new LambdaQueryWrapper<ListingEntity>()
|
||||
.eq(ListingEntity::getId, id)
|
||||
.eq(ListingEntity::getSellerId, sellerId));
|
||||
if (existing == null) {
|
||||
throw new BusinessException("商品不存在");
|
||||
}
|
||||
ListingEntity update = new ListingEntity();
|
||||
update.setId(id);
|
||||
update.setStatus(status);
|
||||
this.updateById(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void batchUpdateStock(List<ListingStockDTO> list, Long sellerId) {
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
List<Long> ids = list.stream().map(ListingStockDTO::getId).collect(Collectors.toList());
|
||||
List<ListingEntity> existing = this.list(
|
||||
new LambdaQueryWrapper<ListingEntity>()
|
||||
.eq(ListingEntity::getSellerId, sellerId)
|
||||
.in(ListingEntity::getId, ids));
|
||||
if (existing.size() != ids.size()) {
|
||||
throw new BusinessException("部分商品不存在或无权操作");
|
||||
}
|
||||
Map<Long, Integer> stockMap = list.stream()
|
||||
.collect(Collectors.toMap(ListingStockDTO::getId, ListingStockDTO::getStock));
|
||||
existing.forEach(e -> e.setStock(stockMap.get(e.getId())));
|
||||
this.updateBatchById(existing);
|
||||
}
|
||||
|
||||
private void handleImages(Long listingId, List<ListingImageDTO> images) {
|
||||
Map<String, List<ListingImageDTO>> byCategory = images.stream()
|
||||
.collect(Collectors.groupingBy(img -> img.getCategory() == null ? "" : img.getCategory()));
|
||||
|
||||
for (Map.Entry<String, List<ListingImageDTO>> entry : byCategory.entrySet()) {
|
||||
String category = entry.getKey();
|
||||
List<ListingImageDTO> categoryImages = entry.getValue();
|
||||
|
||||
if (!StringUtils.hasText(category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ImageCategoryEnum categoryEnum = ImageCategoryEnum.of(category);
|
||||
boolean supportsSelection = categoryEnum != null && categoryEnum.isHasSelection();
|
||||
|
||||
for (int i = 0; i < categoryImages.size(); i++) {
|
||||
ListingImageDTO imgDto = categoryImages.get(i);
|
||||
ListingImageEntity imgEntity = new ListingImageEntity();
|
||||
imgEntity.setListingId(listingId);
|
||||
imgEntity.setCategory(category);
|
||||
imgEntity.setImageUrl(minioUtil.convertToLogicalPath(imgDto.getImageUrl()));
|
||||
imgEntity.setSortOrder(imgDto.getSortOrder() != null ? imgDto.getSortOrder() : i);
|
||||
|
||||
if (supportsSelection) {
|
||||
imgEntity.setIsSelected(Boolean.TRUE.equals(imgDto.getIsSelected()) ? 1 : 0);
|
||||
} else {
|
||||
imgEntity.setIsSelected(0);
|
||||
}
|
||||
listingImageMapper.insert(imgEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String extractCover(List<ListingImageDTO> images) {
|
||||
if (CollectionUtils.isEmpty(images)) {
|
||||
return null;
|
||||
}
|
||||
for (ListingImageDTO img : images) {
|
||||
if (ImageCategoryEnum.COVER.getCode().equals(img.getCategory())) {
|
||||
return img.getImageUrl();
|
||||
}
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(images)) {
|
||||
return images.get(0).getImageUrl();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPopupReminder(Long sellerId) {
|
||||
String key = "popup:reminder:" + sellerId;
|
||||
redisTemplate.opsForValue().set(key, true, 7, java.util.concurrent.TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPopupReminder(Long sellerId) {
|
||||
String key = "popup:reminder:" + sellerId;
|
||||
Object value = redisTemplate.opsForValue().get(key);
|
||||
return value == null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.aida.seller.module.listing.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 商品分页列表 VO(不含图片详情)
|
||||
*/
|
||||
@Data
|
||||
public class ListingPageVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 商品ID */
|
||||
private Long id;
|
||||
|
||||
/** 封面图URL(列表页直返,无须关联图片表) */
|
||||
private String cover;
|
||||
|
||||
/** 商品标题 */
|
||||
private String title;
|
||||
|
||||
/** 价格 */
|
||||
private BigDecimal price;
|
||||
|
||||
/** 库存 */
|
||||
private Integer stock;
|
||||
|
||||
/** 浏览量 */
|
||||
private Integer viewCount;
|
||||
|
||||
/** 状态 */
|
||||
private Integer status;
|
||||
|
||||
/** 创建时间(用于排序) */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -11,13 +11,15 @@ import java.time.LocalDateTime;
|
||||
* 订单主表
|
||||
*/
|
||||
@Data
|
||||
@TableName("order_info")
|
||||
@TableName("seller_orders")
|
||||
public class OrderInfoEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 订单唯一标识(如 SP897772698) */
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String orderId;
|
||||
|
||||
/** 卖家ID */
|
||||
|
||||
@@ -11,13 +11,13 @@ import java.time.LocalDateTime;
|
||||
* 订单商品明细表
|
||||
*/
|
||||
@Data
|
||||
@TableName("order_item")
|
||||
@TableName("seller_order_item")
|
||||
public class OrderItemEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 订单ID(关联 order_info) */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.aida.seller.module.order.service;
|
||||
|
||||
import com.aida.seller.common.constants.CommonConstants;
|
||||
import com.aida.seller.module.order.dto.OrderListDTO;
|
||||
import com.aida.seller.module.order.entity.OrderInfoEntity;
|
||||
import com.aida.seller.module.order.entity.OrderItemEntity;
|
||||
@@ -7,6 +8,7 @@ import com.aida.seller.module.order.mapper.OrderInfoMapper;
|
||||
import com.aida.seller.module.order.mapper.OrderItemMapper;
|
||||
import com.aida.seller.module.order.vo.OrderSummaryVO;
|
||||
import com.aida.seller.module.order.vo.OrderVO;
|
||||
import com.aida.seller.util.MinioUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -30,6 +32,7 @@ import java.util.stream.Collectors;
|
||||
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfoEntity> implements OrderService {
|
||||
|
||||
private final OrderItemMapper orderItemMapper;
|
||||
private final MinioUtil minioUtil;
|
||||
|
||||
/**
|
||||
* 查询指定卖家的订单汇总数据
|
||||
@@ -107,7 +110,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
||||
OrderVO.ItemVO itemVO = new OrderVO.ItemVO();
|
||||
itemVO.setProductId(item.getProductId());
|
||||
itemVO.setProductName(item.getProductName());
|
||||
itemVO.setThumbnailUrl(item.getThumbnailUrl());
|
||||
itemVO.setThumbnailUrl(minioUtil.processMinioResource(item.getThumbnailUrl(), CommonConstants.MINIO_PATH_TIMEOUT));
|
||||
return itemVO;
|
||||
}).collect(Collectors.toList());
|
||||
vo.setItems(itemVOs);
|
||||
|
||||
@@ -1,87 +1,91 @@
|
||||
-- 商品表
|
||||
CREATE TABLE seller_listing (
|
||||
id BIGINT PRIMARY KEY COMMENT '商品ID',
|
||||
seller_id BIGINT NOT NULL COMMENT '卖家ID',
|
||||
title VARCHAR(255) NOT NULL COMMENT '商品标题',
|
||||
description TEXT COMMENT '商品描述',
|
||||
price DECIMAL(10,2) COMMENT '价格',
|
||||
stock INT COMMENT '库存数量',
|
||||
cover VARCHAR(200) COMMENT '封面图URL',
|
||||
view_count INT DEFAULT 0 COMMENT '浏览量',
|
||||
status INT(1) DEFAULT 0 COMMENT '状态: 0-草稿, 1-已发布, 2-已删除',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
deleted INT(1) DEFAULT 0 COMMENT '是否删除:0-否,1-是',
|
||||
design_for VARCHAR(50) COMMENT '适用性别: male/female',
|
||||
product_category JSON COMMENT '商品分类列表',
|
||||
INDEX idx_seller_id (seller_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_deleted (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
|
||||
|
||||
-- 商品图片表
|
||||
CREATE TABLE seller_listing_image (
|
||||
id BIGINT PRIMARY KEY COMMENT '图片ID',
|
||||
listing_id BIGINT NOT NULL COMMENT '商品ID',
|
||||
category VARCHAR(50) NOT NULL COMMENT '图片类别: cover/main_product/product/sketch/apparel',
|
||||
image_url VARCHAR(500) NOT NULL COMMENT '图片URL',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序',
|
||||
is_selected INT(1) DEFAULT 0 COMMENT '是否选中: 0-未选中, 1-选中(仅product有效)',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
INDEX idx_listing_id (listing_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品图片表';
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS aida_seller DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
USE aida_seller;
|
||||
|
||||
-- ==================== 1. 设计师表 ====================
|
||||
DROP TABLE IF EXISTS `designer`;
|
||||
CREATE TABLE `designer` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '设计师ID',
|
||||
`user_id` BIGINT DEFAULT NULL COMMENT '用户ID(关联用户表)',
|
||||
`shop_name` VARCHAR(100) NOT NULL COMMENT '店铺名称',
|
||||
`owner_name` VARCHAR(50) NOT NULL COMMENT '所有者全名',
|
||||
`email` VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
|
||||
`mobile` VARCHAR(20) NOT NULL COMMENT '手机号',
|
||||
`social_links` TEXT DEFAULT NULL COMMENT '作品集/社交媒体链接(JSON数组)',
|
||||
`apply_status` TINYINT NOT NULL DEFAULT 0 COMMENT '申请状态: 0-待审核, 1-审核通过, 2-审核拒绝',
|
||||
`audit_remark` VARCHAR(500) DEFAULT NULL COMMENT '审核备注',
|
||||
`audit_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0-禁用, 1-启用',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除: 0-否, 1-是',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_mobile` (`mobile`),
|
||||
KEY `idx_email` (`email`),
|
||||
KEY `idx_apply_status` (`apply_status`),
|
||||
KEY `idx_status` (`status`)
|
||||
-- 设计师表
|
||||
CREATE TABLE seller_designer (
|
||||
id BIGINT PRIMARY KEY COMMENT '设计师ID',
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
shop_name VARCHAR(100) NOT NULL COMMENT '店铺名称',
|
||||
avatar VARCHAR(200) COMMENT '店铺头像URL',
|
||||
brand_banner VARCHAR(200) COMMENT '品牌Banner URL',
|
||||
owner_name VARCHAR(100) COMMENT '所有者全名',
|
||||
email VARCHAR(100) COMMENT '邮箱',
|
||||
mobile VARCHAR(30) COMMENT '手机号',
|
||||
social_links JSON COMMENT '作品集/社交媒体链接',
|
||||
description TEXT COMMENT '设计师简介',
|
||||
apply_status INT(1) DEFAULT 0 COMMENT '申请状态: 0-待审核, 1-审核通过, 2-审核拒绝',
|
||||
audit_remark VARCHAR(500) COMMENT '审核备注',
|
||||
audit_time DATETIME COMMENT '审核时间',
|
||||
status INT(1) DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
deleted INT(1) DEFAULT 0 COMMENT '是否删除: 0-否, 1-是',
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_apply_status (apply_status),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_deleted (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设计师表';
|
||||
|
||||
-- ==================== 2. 订单表 ====================
|
||||
-- 注意: 代码中 OrderInfoEntity 使用 @TableName("order_info"),
|
||||
-- 若生产库表名为 "orders" 请改为 "order_info",列名 "order_no" 建议改为 "order_id"
|
||||
DROP TABLE IF EXISTS `orders`;
|
||||
CREATE TABLE `orders` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`order_id` VARCHAR(32) NOT NULL COMMENT '订单号',
|
||||
`seller_id` BIGINT NOT NULL COMMENT '商家ID',
|
||||
`buyer_id` BIGINT NOT NULL COMMENT '买家ID',
|
||||
`buyer_name` VARCHAR(100) DEFAULT NULL COMMENT '买家名称',
|
||||
`total_price` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '商品总金额',
|
||||
`order_status` TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态: 0-待支付, 1-已支付, 2-已发货, 3-已完成, 4-已取消, 5-退款中',
|
||||
`total_views` BIGINT NOT NULL DEFAULT 0 COMMENT '商品浏览量(订单关联商品的总浏览数)',
|
||||
`shipping_address` TEXT DEFAULT NULL COMMENT '收货地址',
|
||||
`receiver_name` VARCHAR(50) DEFAULT NULL COMMENT '收货人',
|
||||
`receiver_phone` VARCHAR(20) DEFAULT NULL COMMENT '联系电话',
|
||||
`receiver_address` VARCHAR(500) DEFAULT NULL COMMENT '详细地址',
|
||||
`tracking_number` VARCHAR(100) DEFAULT NULL COMMENT '快递单号',
|
||||
`tracking_company` VARCHAR(100) DEFAULT NULL COMMENT '快递公司',
|
||||
`pay_time` DATETIME DEFAULT NULL COMMENT '支付时间',
|
||||
`ship_time` DATETIME DEFAULT NULL COMMENT '发货时间',
|
||||
`receive_time` DATETIME DEFAULT NULL COMMENT '收货时间',
|
||||
`cancel_time` DATETIME DEFAULT NULL COMMENT '取消时间',
|
||||
`cancel_reason` VARCHAR(500) DEFAULT NULL COMMENT '取消原因',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除: 0-否, 1-是',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_order_id` (`order_id`),
|
||||
KEY `idx_seller_id` (`seller_id`),
|
||||
KEY `idx_buyer_id` (`buyer_id`),
|
||||
KEY `idx_order_status` (`order_status`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
|
||||
-- 订单主表
|
||||
CREATE TABLE seller_orders (
|
||||
id BIGINT PRIMARY KEY COMMENT '主键ID',
|
||||
order_id VARCHAR(50) NOT NULL COMMENT '订单唯一标识(如 SP897772698)',
|
||||
seller_id BIGINT NOT NULL COMMENT '卖家ID',
|
||||
total_price DECIMAL(10,2) COMMENT '订单总金额(HK$)',
|
||||
buyer_username VARCHAR(100) COMMENT '买家账号',
|
||||
total_items INT COMMENT '商品总数量',
|
||||
total_views BIGINT DEFAULT 0 COMMENT '总浏览量',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
deleted INT(1) DEFAULT 0 COMMENT '是否删除:0-否,1-是',
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_seller_id (seller_id),
|
||||
INDEX idx_deleted (deleted),
|
||||
INDEX idx_create_time (create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';
|
||||
|
||||
-- ==================== 3. 订单项表 ====================
|
||||
DROP TABLE IF EXISTS `order_item`;
|
||||
CREATE TABLE `order_item` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '订单项ID',
|
||||
`order_id` BIGINT NOT NULL COMMENT '订单ID(关联orders.id)',
|
||||
`order_no` VARCHAR(32) NOT NULL COMMENT '订单号',
|
||||
`product_id` BIGINT NOT NULL COMMENT '商品ID',
|
||||
`sku_id` BIGINT DEFAULT NULL COMMENT 'SKU ID',
|
||||
`product_name` VARCHAR(200) NOT NULL COMMENT '商品名称',
|
||||
`sku_name` VARCHAR(200) DEFAULT NULL COMMENT 'SKU名称',
|
||||
`product_image` VARCHAR(500) DEFAULT NULL COMMENT '商品图片',
|
||||
`price` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '商品单价',
|
||||
`quantity` INT NOT NULL DEFAULT 1 COMMENT '购买数量',
|
||||
`total_amount` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '小计金额',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除: 0-否, 1-是',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_order_id` (`order_id`),
|
||||
KEY `idx_order_no` (`order_no`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_sku_id` (`sku_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表';
|
||||
-- 订单商品明细表
|
||||
CREATE TABLE seller_order_item (
|
||||
id BIGINT PRIMARY KEY COMMENT '主键ID',
|
||||
order_id VARCHAR(50) NOT NULL COMMENT '订单ID',
|
||||
product_id BIGINT NOT NULL COMMENT '商品ID',
|
||||
product_name VARCHAR(255) COMMENT '商品名称',
|
||||
thumbnail_url VARCHAR(200) COMMENT '商品缩略图URL',
|
||||
price DECIMAL(10,2) COMMENT '成交单价(HK$)',
|
||||
quantity INT NOT NULL COMMENT '购买数量',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
deleted INT(1) DEFAULT 0 COMMENT '是否删除:0-否,1-是',
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_product_id (product_id),
|
||||
INDEX idx_deleted (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单商品明细表';
|
||||
|
||||
Reference in New Issue
Block a user