提交 b5db0b85 authored 作者: hzh's avatar hzh

商城模块集成

上级 4dd9e647
package org.dromara.mall.controller.promotion.admin.article;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.article.vo.category.*;
import org.dromara.mall.convert.promotion.article.ArticleCategoryConvert;
import org.dromara.mall.domain.promotion.ArticleCategoryDO;
import org.dromara.mall.service.promotion.ArticleCategoryService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Comparator;
import java.util.List;
@Tag(name = "管理后台 - 文章分类")
@RestController
@RequestMapping("/admin/promotion/article-category")
@Validated
public class ArticleCategoryController {
@Resource
private ArticleCategoryService articleCategoryService;
@PostMapping("/create")
@Operation(summary = "创建文章分类")
@SaCheckPermission("promotion:article-category:create")
public R<Long> createArticleCategory(@Valid @RequestBody ArticleCategoryCreateReqVO createReqVO) {
return R.ok(articleCategoryService.createArticleCategory(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新文章分类")
@SaCheckPermission("promotion:article-category:update")
public R<Boolean> updateArticleCategory(@Valid @RequestBody ArticleCategoryUpdateReqVO updateReqVO) {
articleCategoryService.updateArticleCategory(updateReqVO);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除文章分类")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:article-category:delete")
public R<Boolean> deleteArticleCategory(@RequestParam("id") Long id) {
articleCategoryService.deleteArticleCategory(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得文章分类")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:article-category:query")
public R<ArticleCategoryRespVO> getArticleCategory(@RequestParam("id") Long id) {
ArticleCategoryDO category = articleCategoryService.getArticleCategory(id);
return R.ok(ArticleCategoryConvert.INSTANCE.convert(category));
}
@GetMapping("/list-all-simple")
@Operation(summary = "获取文章分类精简信息列表", description = "只包含被开启的文章分类,主要用于前端的下拉选项")
public R<List<ArticleCategorySimpleRespVO>> getSimpleDeptList() {
// 获得分类列表,只要开启状态的
List<ArticleCategoryDO> list = articleCategoryService.getArticleCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus());
// 降序排序后,返回给前端
list.sort(Comparator.comparing(ArticleCategoryDO::getSort).reversed());
return R.ok(ArticleCategoryConvert.INSTANCE.convertList03(list));
}
@GetMapping("/page")
@Operation(summary = "获得文章分类分页")
@SaCheckPermission("promotion:article-category:query")
public R<PageResult<ArticleCategoryRespVO>> getArticleCategoryPage(@Valid ArticleCategoryPageReqVO pageVO) {
PageResult<ArticleCategoryDO> pageResult = articleCategoryService.getArticleCategoryPage(pageVO);
return R.ok(ArticleCategoryConvert.INSTANCE.convertPage(pageResult));
}
}
package org.dromara.mall.controller.promotion.admin.article;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.article.vo.article.ArticleCreateReqVO;
import org.dromara.mall.controller.promotion.admin.article.vo.article.ArticlePageReqVO;
import org.dromara.mall.controller.promotion.admin.article.vo.article.ArticleRespVO;
import org.dromara.mall.controller.promotion.admin.article.vo.article.ArticleUpdateReqVO;
import org.dromara.mall.convert.promotion.article.ArticleConvert;
import org.dromara.mall.domain.promotion.ArticleDO;
import org.dromara.mall.service.promotion.ArticleService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Tag(name = "管理后台 - 文章管理")
@RestController
@RequestMapping("/admin/promotion/article")
@Validated
public class ArticleController {
@Resource
private ArticleService articleService;
@PostMapping("/create")
@Operation(summary = "创建文章管理")
@SaCheckPermission("promotion:article:create")
public R<Long> createArticle(@Valid @RequestBody ArticleCreateReqVO createReqVO) {
return R.ok(articleService.createArticle(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新文章管理")
@SaCheckPermission("promotion:article:update")
public R<Boolean> updateArticle(@Valid @RequestBody ArticleUpdateReqVO updateReqVO) {
articleService.updateArticle(updateReqVO);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除文章管理")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:article:delete")
public R<Boolean> deleteArticle(@RequestParam("id") Long id) {
articleService.deleteArticle(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得文章管理")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:article:query")
public R<ArticleRespVO> getArticle(@RequestParam("id") Long id) {
ArticleDO article = articleService.getArticle(id);
return R.ok(ArticleConvert.INSTANCE.convert(article));
}
@GetMapping("/page")
@Operation(summary = "获得文章管理分页")
@SaCheckPermission("promotion:article:query")
public R<PageResult<ArticleRespVO>> getArticlePage(@Valid ArticlePageReqVO pageVO) {
PageResult<ArticleDO> pageResult = articleService.getArticlePage(pageVO);
return R.ok(ArticleConvert.INSTANCE.convertPage(pageResult));
}
}
package org.dromara.mall.controller.promotion.admin.article.vo.article;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 文章管理 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class ArticleBaseVO {
@Schema(description = "文章分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15458")
@NotNull(message = "文章分类编号不能为空")
private Long categoryId;
@Schema(description = "关联商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22378")
@NotNull(message = "关联商品不能为空")
private Long spuId;
@Schema(description = "文章标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "这是一个标题")
@NotNull(message = "文章标题不能为空")
private String title;
@Schema(description = "文章作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
private String author;
@Schema(description = "文章封面图片地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
@NotNull(message = "文章封面图片地址不能为空")
private String picUrl;
@Schema(description = "文章简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "这是一个简介")
private String introduction;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "排序不能为空")
private Integer sort;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "是否热门(小程序)", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否热门(小程序)不能为空")
private Boolean recommendHot;
@Schema(description = "是否轮播图(小程序)", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否轮播图(小程序)不能为空")
private Boolean recommendBanner;
@Schema(description = "文章内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "这是文章内容")
@NotNull(message = "文章内容不能为空")
private String content;
}
package org.dromara.mall.controller.promotion.admin.article.vo.article;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 文章管理创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleCreateReqVO extends ArticleBaseVO {
}
package org.dromara.mall.controller.promotion.admin.article.vo.article;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 文章管理分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticlePageReqVO extends PageParam {
@Schema(description = "文章分类编号", example = "15458")
private Long categoryId;
@Schema(description = "关联商品编号", example = "22378")
private Long spuId;
@Schema(description = "文章标题")
private String title;
@Schema(description = "文章作者")
private String author;
@Schema(description = "状态", example = "2")
private Integer status;
@Schema(description = "是否热门(小程序)")
private Boolean recommendHot;
@Schema(description = "是否轮播图(小程序)")
private Boolean recommendBanner;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.article.vo.article;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文章管理 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleRespVO extends ArticleBaseVO {
@Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8606")
private Long id;
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "99999")
private Integer browseCount;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.article.vo.article;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 文章管理更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleUpdateReqVO extends ArticleBaseVO {
@Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8606")
@NotNull(message = "文章编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.article.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 文章分类 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class ArticleCategoryBaseVO {
@Schema(description = "文章分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "秒杀")
@NotNull(message = "文章分类名称不能为空")
private String name;
@Schema(description = "图标地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
private String picUrl;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "排序不能为空")
private Integer sort;
}
package org.dromara.mall.controller.promotion.admin.article.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 文章分类创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleCategoryCreateReqVO extends ArticleCategoryBaseVO {
}
package org.dromara.mall.controller.promotion.admin.article.vo.category;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 文章分类分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleCategoryPageReqVO extends PageParam {
@Schema(description = "文章分类名称", example = "秒杀")
private String name;
@Schema(description = "状态", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.article.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 文章分类 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleCategoryRespVO extends ArticleCategoryBaseVO {
@Schema(description = "文章分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19490")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.article.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 文章分类精简信息 Response VO")
@Data
public class ArticleCategorySimpleRespVO {
@Schema(description = "文章分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19490")
private Long id;
@Schema(description = "文章分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "秒杀")
private String name;
}
package org.dromara.mall.controller.promotion.admin.article.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 文章分类更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ArticleCategoryUpdateReqVO extends ArticleCategoryBaseVO {
@Schema(description = "文章分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19490")
@NotNull(message = "文章分类编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.banner;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.banner.vo.BannerCreateReqVO;
import org.dromara.mall.controller.promotion.admin.banner.vo.BannerPageReqVO;
import org.dromara.mall.controller.promotion.admin.banner.vo.BannerRespVO;
import org.dromara.mall.controller.promotion.admin.banner.vo.BannerUpdateReqVO;
import org.dromara.mall.convert.promotion.banner.BannerConvert;
import org.dromara.mall.domain.promotion.BannerDO;
import org.dromara.mall.service.promotion.BannerService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Tag(name = "管理后台 - Banner 管理")
@RestController
@RequestMapping("/admin/promotion/banner")
@Validated
public class BannerController {
@Resource
private BannerService bannerService;
@PostMapping("/create")
@Operation(summary = "创建 Banner")
@SaCheckPermission("promotion:banner:create")
public R<Long> createBanner(@Valid @RequestBody BannerCreateReqVO createReqVO) {
return R.ok(bannerService.createBanner(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新 Banner")
@SaCheckPermission("promotion:banner:update")
public R<Boolean> updateBanner(@Valid @RequestBody BannerUpdateReqVO updateReqVO) {
bannerService.updateBanner(updateReqVO);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除 Banner")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:banner:delete")
public R<Boolean> deleteBanner(@RequestParam("id") Long id) {
bannerService.deleteBanner(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得 Banner")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:banner:query")
public R<BannerRespVO> getBanner(@RequestParam("id") Long id) {
BannerDO banner = bannerService.getBanner(id);
return R.ok(BannerConvert.INSTANCE.convert(banner));
}
@GetMapping("/page")
@Operation(summary = "获得 Banner 分页")
@SaCheckPermission("promotion:banner:query")
public R<PageResult<BannerRespVO>> getBannerPage(@Valid BannerPageReqVO pageVO) {
PageResult<BannerDO> pageResult = bannerService.getBannerPage(pageVO);
return R.ok(BannerConvert.INSTANCE.convertPage(pageResult));
}
}
package org.dromara.mall.controller.promotion.admin.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.mall.enums.promotion.banner.BannerPositionEnum;
/**
* Banner Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
* @author xia
*/
@Data
public class BannerBaseVO {
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "标题不能为空")
private String title;
@Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "跳转链接不能为空")
private String url;
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "图片地址不能为空")
private String picUrl;
@Schema(description = "position", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "position 不能为空")
@InEnum(BannerPositionEnum.class)
private Integer position;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "排序不能为空")
private Integer sort;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "备注")
private String memo;
}
package org.dromara.mall.controller.promotion.admin.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author xia
*/
@Schema(description = "管理后台 - Banner 创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BannerCreateReqVO extends BannerBaseVO {
}
package org.dromara.mall.controller.promotion.admin.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - Banner 分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BannerPageReqVO extends PageParam {
@Schema(description = "标题", example = "这是一个标题")
private String title;
@Schema(description = "状态", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - Banner Response VO")
@Data
@ToString(callSuper = true)
public class BannerRespVO extends BannerBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-01 23:59:59")
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author xia
*/
@Schema(description = "管理后台 - Banner更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BannerUpdateReqVO extends BannerBaseVO {
@Schema(description = "banner 编号", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "banner 编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.bargain;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.admin.bargain.vo.activity.*;
import org.dromara.mall.convert.promotion.bargain.BargainActivityConvert;
import org.dromara.mall.domain.promotion.BargainActivityDO;
import org.dromara.mall.enums.promotion.bargain.BargainRecordStatusEnum;
import org.dromara.mall.service.promotion.BargainActivityService;
import org.dromara.mall.service.promotion.BargainHelpService;
import org.dromara.mall.service.promotion.BargainRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - 砍价活动")
@RestController
@RequestMapping("/admin/promotion/bargain-activity")
@Validated
public class BargainActivityController {
@Resource
private BargainActivityService bargainActivityService;
@Resource
private BargainRecordService bargainRecordService;
@Resource
private BargainHelpService bargainHelpService;
@Resource
private ProductSpuApi spuApi;
@PostMapping("/create")
@Operation(summary = "创建砍价活动")
@SaCheckPermission("promotion:bargain-activity:create")
public R<Long> createBargainActivity(@Valid @RequestBody BargainActivityCreateReqVO createReqVO) {
return R.ok(bargainActivityService.createBargainActivity(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新砍价活动")
@SaCheckPermission("promotion:bargain-activity:update")
public R<Boolean> updateBargainActivity(@Valid @RequestBody BargainActivityUpdateReqVO updateReqVO) {
bargainActivityService.updateBargainActivity(updateReqVO);
return R.ok(true);
}
@PutMapping("/close")
@Operation(summary = "关闭砍价活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:bargain-activity:close")
public R<Boolean> closeSeckillActivity(@RequestParam("id") Long id) {
bargainActivityService.closeBargainActivityById(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除砍价活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:bargain-activity:delete")
public R<Boolean> deleteBargainActivity(@RequestParam("id") Long id) {
bargainActivityService.deleteBargainActivity(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得砍价活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:bargain-activity:query")
public R<BargainActivityRespVO> getBargainActivity(@RequestParam("id") Long id) {
return R.ok(BargainActivityConvert.INSTANCE.convert(bargainActivityService.getBargainActivity(id)));
}
@GetMapping("/page")
@Operation(summary = "获得砍价活动分页")
@SaCheckPermission("promotion:bargain-activity:query")
public R<PageResult<BargainActivityPageItemRespVO>> getBargainActivityPage(
@Valid BargainActivityPageReqVO pageVO) {
// 查询砍价活动
PageResult<BargainActivityDO> pageResult = bargainActivityService.getBargainActivityPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), BargainActivityDO::getSpuId));
// 统计数据
Collection<Long> activityIds = convertList(pageResult.getList(), BargainActivityDO::getId);
Map<Long, Integer> recordUserCountMap = bargainRecordService.getBargainRecordUserCountMap(activityIds, null);
Map<Long, Integer> recordSuccessUserCountMap = bargainRecordService.getBargainRecordUserCountMap(activityIds,
BargainRecordStatusEnum.SUCCESS.getStatus());
Map<Long, Integer> helpUserCountMap = bargainHelpService.getBargainHelpUserCountMapByActivity(activityIds);
return R.ok(BargainActivityConvert.INSTANCE.convertPage(pageResult, spuList,
recordUserCountMap, recordSuccessUserCountMap, helpUserCountMap));
}
}
package org.dromara.mall.controller.promotion.admin.bargain;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.member.MemberUserRespDTO;
import org.dromara.mall.api.service.member.MemberUserApi;
import org.dromara.mall.controller.promotion.admin.bargain.vo.help.BargainHelpPageReqVO;
import org.dromara.mall.controller.promotion.admin.bargain.vo.help.BargainHelpRespVO;
import org.dromara.mall.convert.promotion.bargain.BargainHelpConvert;
import org.dromara.mall.domain.promotion.BargainHelpDO;
import org.dromara.mall.service.promotion.BargainHelpService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 砍价助力")
@RestController
@RequestMapping("/admin/promotion/bargain-help")
@Validated
public class BargainHelpController {
@Resource
private BargainHelpService bargainHelpService;
@Resource
private MemberUserApi memberUserApi;
@GetMapping("/page")
@Operation(summary = "获得砍价助力分页")
@SaCheckPermission("promotion:bargain-help:query")
public R<PageResult<BargainHelpRespVO>> getBargainHelpPage(@Valid BargainHelpPageReqVO pageVO) {
PageResult<BargainHelpDO> pageResult = bargainHelpService.getBargainHelpPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(
convertSet(pageResult.getList(), BargainHelpDO::getUserId));
return R.ok(BargainHelpConvert.INSTANCE.convertPage(pageResult, userMap));
}
}
package org.dromara.mall.controller.promotion.admin.bargain;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.member.MemberUserRespDTO;
import org.dromara.mall.api.service.member.MemberUserApi;
import org.dromara.mall.controller.promotion.admin.bargain.vo.recrod.BargainRecordPageItemRespVO;
import org.dromara.mall.controller.promotion.admin.bargain.vo.recrod.BargainRecordPageReqVO;
import org.dromara.mall.convert.promotion.bargain.BargainRecordConvert;
import org.dromara.mall.domain.promotion.BargainActivityDO;
import org.dromara.mall.domain.promotion.BargainRecordDO;
import org.dromara.mall.service.promotion.BargainActivityService;
import org.dromara.mall.service.promotion.BargainHelpService;
import org.dromara.mall.service.promotion.BargainRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 砍价记录")
@RestController
@RequestMapping("/admin/promotion/bargain-record")
@Validated
public class BargainRecordController {
@Resource
private BargainRecordService bargainRecordService;
@Resource
private BargainActivityService bargainActivityService;
@Resource
private BargainHelpService bargainHelpService;
@Resource
private MemberUserApi memberUserApi;
@GetMapping("/page")
@Operation(summary = "获得砍价记录分页")
@SaCheckPermission("promotion:bargain-record:query")
public R<PageResult<BargainRecordPageItemRespVO>> getBargainRecordPage(@Valid BargainRecordPageReqVO pageVO) {
PageResult<BargainRecordDO> pageResult = bargainRecordService.getBargainRecordPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(
convertSet(pageResult.getList(), BargainRecordDO::getUserId));
List<BargainActivityDO> activityList = bargainActivityService.getBargainActivityList(
convertSet(pageResult.getList(), BargainRecordDO::getActivityId));
Map<Long, Integer> helpCountMap = bargainHelpService.getBargainHelpUserCountMapByRecord(
convertSet(pageResult.getList(), BargainRecordDO::getId));
return R.ok(BargainRecordConvert.INSTANCE.convertPage(pageResult, helpCountMap, activityList, userMap));
}
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 砍价活动 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @author HUIHUI
*/
@Data
public class BargainActivityBaseVO {
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "砍得越多省得越多,是兄弟就来砍我")
@NotNull(message = "砍价名称不能为空")
private String name;
@Schema(description = "商品 SPU 编号", example = "1")
@NotNull(message = "砍价商品不能为空")
private Long spuId;
@Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
@NotNull(message = "商品 skuId 不能为空")
private Long skuId;
@Schema(description = "砍价起始价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
@NotNull(message = "砍价起始价格不能为空")
private Integer bargainFirstPrice;
@Schema(description = "砍价底价", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
@NotNull(message = "砍价底价不能为空")
private Integer bargainMinPrice;
@Schema(description = "活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
@NotNull(message = "活动库存不能为空")
private Integer stock;
@Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16218")
@NotNull(message = "总限购数量不能为空")
private Integer totalLimitCount;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
@NotNull(message = "活动开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
@NotNull(message = "活动结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "最大助力次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
@NotNull(message = "最大助力次数不能为空")
private Integer helpMaxCount;
@Schema(description = "最大帮砍次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
@NotNull(message = "最大帮砍次数不能为空")
private Integer bargainCount;
@Schema(description = "用户每次砍价的最小金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
@NotNull(message = "用户每次砍价的最小金额不能为空")
private Integer randomMinPrice;
@Schema(description = "用户每次砍价的最大金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
@NotNull(message = "用户每次砍价的最大金额不能为空")
private Integer randomMaxPrice;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 砍价活动创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainActivityCreateReqVO extends BargainActivityBaseVO {
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 砍价活动的分页项 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainActivityPageItemRespVO extends BargainActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "活动状态不能为空")
private Integer status;
@Schema(description = "活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
private Integer totalStock;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-01 23:59:59")
private LocalDateTime createTime;
// ========== 统计字段 ==========
@Schema(description = "总砍价的用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "999")
private Integer recordUserCount;
@Schema(description = "成功砍价的用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "500")
private Integer recordSuccessUserCount;
@Schema(description = "帮助砍价的用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
private Integer helpUserCount;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.activity;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 砍价活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainActivityPageReqVO extends PageParam {
@Schema(description = "砍价名称", example = "赵六")
private String name;
@Schema(description = "活动状态", example = "0")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 砍价活动 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainActivityRespVO extends BargainActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-01 23:59:59")
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 砍价活动更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainActivityUpdateReqVO extends BargainActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
@NotNull(message = "活动编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.help;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 砍价助力 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class BargainHelpBaseVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5402")
private Long userId;
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "16825")
private Long activityId;
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1800")
private Long recordId;
@Schema(description = "减少砍价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "32300")
private Integer reducePrice;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.help;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 砍价助力分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainHelpPageReqVO extends PageParam {
@Schema(description = "砍价记录编号", example = "1800")
private Long recordId;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.help;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 砍价助力 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainHelpRespVO extends BargainHelpBaseVO {
@Schema(description = "砍价助力编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25860")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
// ========== 用户相关 ==========
@Schema(description = "用户昵称", example = "老芋艿")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 砍价记录 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class BargainRecordBaseVO {
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "22690")
@NotNull(message = "砍价活动名称不能为空")
private Long activityId;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9430")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23622")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29950")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "砍价起始价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "31160")
@NotNull(message = "砍价起始价格,单位:分不能为空")
private Integer bargainFirstPrice;
@Schema(description = "当前砍价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "22743")
@NotNull(message = "当前砍价,单位:分不能为空")
private Integer bargainPrice;
@Schema(description = "砍价状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "砍价状态不能为空")
private Integer status;
@Schema(description = "订单编号", example = "27845")
private Long orderId;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.mall.controller.promotion.admin.bargain.vo.activity.BargainActivityRespVO;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 砍价记录的分页项 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainRecordPageItemRespVO extends BargainRecordBaseVO {
@Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022-07-01 23:59:59")
private LocalDateTime createTime;
@Schema(description = "帮砍次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Integer helpCount;
// ========== 用户相关 ==========
@Schema(description = "用户昵称", example = "老芋艿")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
// ========== 活动相关 ==========
private BargainActivityRespVO activity;
}
package org.dromara.mall.controller.promotion.admin.bargain.vo.recrod;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 砍价记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BargainRecordPageReqVO extends PageParam {
@Schema(description = "砍价状态", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.combination;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.admin.combination.vo.activity.*;
import org.dromara.mall.convert.promotion.combination.CombinationActivityConvert;
import org.dromara.mall.domain.promotion.CombinationActivityDO;
import org.dromara.mall.domain.promotion.CombinationProductDO;
import org.dromara.mall.domain.promotion.CombinationRecordDO;
import org.dromara.mall.enums.promotion.combination.CombinationRecordStatusEnum;
import org.dromara.mall.service.promotion.CombinationActivityService;
import org.dromara.mall.service.promotion.CombinationRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.hutool.core.collection.CollectionUtil.newArrayList;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 拼团活动")
@RestController
@RequestMapping("/admin/promotion/combination-activity")
@Validated
public class CombinationActivityController {
@Resource
private CombinationActivityService combinationActivityService;
@Resource
private CombinationRecordService combinationRecordService;
@Resource
private ProductSpuApi productSpuApi;
@PostMapping("/create")
@Operation(summary = "创建拼团活动")
@SaCheckPermission("promotion:combination-activity:create")
public R<Long> createCombinationActivity(@Valid @RequestBody CombinationActivityCreateReqVO createReqVO) {
return R.ok(combinationActivityService.createCombinationActivity(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新拼团活动")
@SaCheckPermission("promotion:combination-activity:update")
public R<Boolean> updateCombinationActivity(@Valid @RequestBody CombinationActivityUpdateReqVO updateReqVO) {
combinationActivityService.updateCombinationActivity(updateReqVO);
return R.ok(true);
}
@PutMapping("/close")
@Operation(summary = "关闭拼团活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:combination-activity:close")
public R<Boolean> closeCombinationActivity(@RequestParam("id") Long id) {
combinationActivityService.closeCombinationActivityById(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除拼团活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:combination-activity:delete")
public R<Boolean> deleteCombinationActivity(@RequestParam("id") Long id) {
combinationActivityService.deleteCombinationActivity(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得拼团活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:combination-activity:query")
public R<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) {
CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id);
List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(newArrayList(id));
return R.ok(CombinationActivityConvert.INSTANCE.convert(activity, products));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得拼团活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public R<List<CombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<CombinationActivityDO> activityList = combinationActivityService.getCombinationActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return R.ok(Collections.emptyList());
}
// 2. 拼接返回
List<CombinationProductDO> productList = combinationActivityService.getCombinationProductListByActivityIds(
convertList(activityList, CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
return R.ok(CombinationActivityConvert.INSTANCE.convertList(activityList, productList, spuList));
}
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
@SaCheckPermission("promotion:combination-activity:query")
public R<PageResult<CombinationActivityPageItemRespVO>> getCombinationActivityPage(
@Valid CombinationActivityPageReqVO pageVO) {
// 查询拼团活动
PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 统计数据
Set<Long> activityIds = convertSet(pageResult.getList(), CombinationActivityDO::getId);
Map<Long, Integer> groupCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
activityIds, null, CombinationRecordDO.HEAD_ID_GROUP);
Map<Long, Integer> groupSuccessCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
activityIds, CombinationRecordStatusEnum.SUCCESS.getStatus(), CombinationRecordDO.HEAD_ID_GROUP);
Map<Long, Integer> recordCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
activityIds, null, null);
// 拼接数据
List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(
convertSet(pageResult.getList(), CombinationActivityDO::getId));
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(
convertSet(pageResult.getList(), CombinationActivityDO::getSpuId));
return R.ok(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products,
groupCountMap, groupSuccessCountMap, recordCountMap, spus));
}
}
package org.dromara.mall.controller.promotion.admin.combination;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;
import org.dromara.mall.controller.promotion.admin.combination.vo.recrod.CombinationRecordReqPageVO;
import org.dromara.mall.controller.promotion.admin.combination.vo.recrod.CombinationRecordSummaryVO;
import org.dromara.mall.convert.promotion.combination.CombinationActivityConvert;
import org.dromara.mall.domain.promotion.CombinationActivityDO;
import org.dromara.mall.domain.promotion.CombinationProductDO;
import org.dromara.mall.domain.promotion.CombinationRecordDO;
import org.dromara.mall.enums.promotion.combination.CombinationRecordStatusEnum;
import org.dromara.mall.service.promotion.CombinationActivityService;
import org.dromara.mall.service.promotion.CombinationRecordService;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 拼团记录")
@RestController
@RequestMapping("/admin/promotion/combination-record")
@Validated
public class CombinationRecordController {
@Resource
private CombinationActivityService combinationActivityService;
@Resource
@Lazy
private CombinationRecordService combinationRecordService;
@GetMapping("/page")
@Operation(summary = "获得拼团记录分页")
@SaCheckPermission("promotion:combination-record:query")
public R<PageResult<CombinationRecordPageItemRespVO>> getCombinationRecordPage(
@Valid CombinationRecordReqPageVO pageVO) {
PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage(pageVO);
// 拼接数据
List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(
convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));
List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(
convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));
return R.ok(CombinationActivityConvert.INSTANCE.convert(recordPage, activities, products));
}
@GetMapping("/get-summary")
@Operation(summary = "获得拼团记录的概要信息", description = "用于拼团记录页面展示")
@SaCheckPermission("promotion:combination-record:query")
public R<CombinationRecordSummaryVO> getCombinationRecordSummary() {
CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO();
summaryVO.setUserCount(combinationRecordService.getCombinationUserCount()); // 获取拼团用户参与数量
summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录
CombinationRecordStatusEnum.SUCCESS.getStatus(), null, CombinationRecordDO.HEAD_ID_GROUP));
summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(// 获取虚拟成团记录
null, Boolean.TRUE, CombinationRecordDO.HEAD_ID_GROUP));
return R.ok(summaryVO);
}
}
package org.dromara.mall.controller.promotion.admin.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 拼团活动 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @author HUIHUI
*/
@Data
@Accessors(chain = true)
public class CombinationActivityBaseVO {
@Schema(description = "拼团名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "越拼越省钱")
@NotNull(message = "拼团名称不能为空")
private String name;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "拼团商品不能为空")
private Long spuId;
@Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16218")
@NotNull(message = "总限购数量不能为空")
private Integer totalLimitCount;
@Schema(description = "单次限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "28265")
@NotNull(message = "单次限购数量不能为空")
private Integer singleLimitCount;
@Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
@NotNull(message = "活动时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
@NotNull(message = "活动时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
@NotNull(message = "开团人数不能为空")
private Integer userSize;
@Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "虚拟成团不能为空")
private Boolean virtualGroup;
@Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "限制时长不能为空")
private Integer limitDuration;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.mall.controller.promotion.admin.combination.vo.product.CombinationProductBaseVO;
import java.util.List;
@Schema(description = "管理后台 - 拼团活动创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationActivityCreateReqVO extends CombinationActivityBaseVO {
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private List<CombinationProductBaseVO> products;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.mall.controller.promotion.admin.combination.vo.product.CombinationProductRespVO;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 拼团活动的分页项 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationActivityPageItemRespVO extends CombinationActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<CombinationProductRespVO> products;
// ========== 商品字段 ==========
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
example = "50")
private Integer marketPrice;
// ========== 统计字段 ==========
@Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33")
private Integer groupCount;
@Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer groupSuccessCount;
@Schema(description = "购买次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer recordCount;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.activity;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 拼团活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationActivityPageReqVO extends PageParam {
@Schema(description = "拼团名称", example = "赵六")
private String name;
@Schema(description = "活动状态", example = "0")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.mall.controller.promotion.admin.combination.vo.product.CombinationProductRespVO;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 拼团活动 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationActivityRespVO extends CombinationActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private Integer userSize;
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<CombinationProductRespVO> products;
@Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
private String spuName; // 从 SPU 的 name 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
private String picUrl; // 从 SPU 的 picUrl 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer marketPrice; // 从 SPU 的 marketPrice 读取
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer combinationPrice; // 从 products 获取最小 price 读取
}
package org.dromara.mall.controller.promotion.admin.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.mall.controller.promotion.admin.combination.vo.product.CombinationProductBaseVO;
import java.util.List;
@Schema(description = "管理后台 - 拼团活动更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
@NotNull(message = "活动编号不能为空")
private Long id;
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private List<CombinationProductBaseVO> products;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 拼团商品 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class CombinationProductBaseVO {
@Schema(description = "商品 spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
@NotNull(message = "商品 spuId 不能为空")
private Long spuId;
@Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
@NotNull(message = "商品 skuId 不能为空")
private Long skuId;
@Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682")
@NotNull(message = "拼团价格不能为空")
private Integer combinationPrice;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.product;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 拼团商品分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationProductPageReqVO extends PageParam {
@Schema(description = "拼团活动编号", example = "6829")
private Long activityId;
@Schema(description = "商品 SPU 编号", example = "18731")
private Long spuId;
@Schema(description = "商品 SKU 编号", example = "31675")
private Long skuId;
@Schema(description = "拼团商品状态", example = "2")
private Integer activityStatus;
@Schema(description = "活动开始时间点")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] activityStartTime;
@Schema(description = "活动结束时间点")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] activityEndTime;
@Schema(description = "拼团价格,单位分", example = "27682")
private Integer activePrice;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 拼团商品 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationProductRespVO extends CombinationProductBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28322")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 拼团记录 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @author HUIHUI
*/
@Data
public class CombinationRecordBaseVO {
@Schema(description = "拼团记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "拼团活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long activityId;
@Schema(description = "团长编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long headId;
// ========== 用户相关 ==========
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9430")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "用户昵称", example = "老芋艿")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
// ========== 商品相关 ==========
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23622")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29950")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "商品名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是大黄豆")
private String spuName;
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime expireTime;
@Schema(description = "可参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer userSize;
@Schema(description = "已参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Integer userCount;
@Schema(description = "拼团状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "是否虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean virtualGroup;
@Schema(description = "开始时间 (订单付款后开始的时间)", requiredMode = Schema.RequiredMode.REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "结束时间(成团时间/失败时间)", requiredMode = Schema.RequiredMode.REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.mall.controller.promotion.admin.combination.vo.activity.CombinationActivityRespVO;
@Schema(description = "管理后台 - 拼团记录的分页项 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationRecordPageItemRespVO extends CombinationRecordBaseVO {
// ========== 活动相关 ==========
private CombinationActivityRespVO activity;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.recrod;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 拼团记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationRecordReqPage2VO extends PageParam {
@Schema(description = "团长编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "团长编号不能为空")
private Long headId;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.mall.enums.promotion.combination.CombinationRecordStatusEnum;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 拼团记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CombinationRecordReqPageVO extends PageParam {
@Schema(description = "活动状态", example = "1")
@InEnum(CombinationRecordStatusEnum.class)
private Integer status;
@Schema(description = "团长编号", example = "1024")
private Long headId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.combination.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 拼团记录信息统计 Response VO")
@Data
public class CombinationRecordSummaryVO {
@Schema(description = "所有拼团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userCount;
@Schema(description = "成团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long successCount;
@Schema(description = "虚拟成团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long virtualGroupCount;
}
package org.dromara.mall.controller.promotion.admin.coupon;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.collection.MapUtils;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.member.MemberUserRespDTO;
import org.dromara.mall.api.service.member.MemberUserApi;
import org.dromara.mall.controller.promotion.admin.coupon.vo.coupon.CouponPageItemRespVO;
import org.dromara.mall.controller.promotion.admin.coupon.vo.coupon.CouponPageReqVO;
import org.dromara.mall.controller.promotion.admin.coupon.vo.coupon.CouponSendReqVO;
import org.dromara.mall.convert.promotion.coupon.CouponConvert;
import org.dromara.mall.domain.promotion.CouponDO;
import org.dromara.mall.service.promotion.CouponService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 优惠劵")
@RestController
@RequestMapping("/admin/promotion/coupon")
@Validated
public class CouponController {
@Resource
private CouponService couponService;
@Resource
private MemberUserApi memberUserApi;
@DeleteMapping("/delete")
@Operation(summary = "回收优惠劵")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:coupon:delete")
public R<Boolean> deleteCoupon(@RequestParam("id") Long id) {
couponService.deleteCoupon(id);
return R.ok(true);
}
@GetMapping("/page")
@Operation(summary = "获得优惠劵分页")
@SaCheckPermission("promotion:coupon:query")
public R<PageResult<CouponPageItemRespVO>> getCouponPage(@Valid CouponPageReqVO pageVO) {
PageResult<CouponDO> pageResult = couponService.getCouponPage(pageVO);
PageResult<CouponPageItemRespVO> pageResulVO = CouponConvert.INSTANCE.convertPage(pageResult);
if (CollUtil.isEmpty(pageResulVO.getList())) {
return R.ok(pageResulVO);
}
// 读取用户信息,进行拼接
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), CouponDO::getUserId));
pageResulVO.getList().forEach(itemRespVO -> MapUtils.findAndThen(userMap, itemRespVO.getUserId(),
userRespDTO -> itemRespVO.setNickname(userRespDTO.getNickname())));
return R.ok(pageResulVO);
}
@PostMapping("/send")
@Operation(summary = "发送优惠劵")
@SaCheckPermission("promotion:coupon:send")
public R<Boolean> sendCoupon(@Valid @RequestBody CouponSendReqVO reqVO) {
couponService.takeCouponByAdmin(reqVO.getTemplateId(), reqVO.getUserIds());
return R.ok(true);
}
}
package org.dromara.mall.controller.promotion.admin.coupon;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.coupon.vo.template.*;
import org.dromara.mall.convert.promotion.coupon.CouponTemplateConvert;
import org.dromara.mall.domain.promotion.CouponTemplateDO;
import org.dromara.mall.service.promotion.CouponTemplateService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
@Tag(name = "管理后台 - 优惠劵模板")
@RestController
@RequestMapping("/admin/promotion/coupon-template")
@Validated
public class CouponTemplateController {
@Resource
private CouponTemplateService couponTemplateService;
@PostMapping("/create")
@Operation(summary = "创建优惠劵模板")
@SaCheckPermission("promotion:coupon-template:create")
public R<Long> createCouponTemplate(@Valid @RequestBody CouponTemplateCreateReqVO createReqVO) {
return R.ok(couponTemplateService.createCouponTemplate(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新优惠劵模板")
@SaCheckPermission("promotion:coupon-template:update")
public R<Boolean> updateCouponTemplate(@Valid @RequestBody CouponTemplateUpdateReqVO updateReqVO) {
couponTemplateService.updateCouponTemplate(updateReqVO);
return R.ok(true);
}
@PutMapping("/update-status")
@Operation(summary = "更新优惠劵模板状态")
@SaCheckPermission("promotion:coupon-template:update")
public R<Boolean> updateCouponTemplateStatus(@Valid @RequestBody CouponTemplateUpdateStatusReqVO reqVO) {
couponTemplateService.updateCouponTemplateStatus(reqVO.getId(), reqVO.getStatus());
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除优惠劵模板")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:coupon-template:delete")
public R<Boolean> deleteCouponTemplate(@RequestParam("id") Long id) {
couponTemplateService.deleteCouponTemplate(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得优惠劵模板")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:coupon-template:query")
public R<CouponTemplateRespVO> getCouponTemplate(@RequestParam("id") Long id) {
CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(id);
return R.ok(CouponTemplateConvert.INSTANCE.convert(couponTemplate));
}
@GetMapping("/page")
@Operation(summary = "获得优惠劵模板分页")
@SaCheckPermission("promotion:coupon-template:query")
public R<PageResult<CouponTemplateRespVO>> getCouponTemplatePage(@Valid CouponTemplatePageReqVO pageVO) {
PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(pageVO);
return R.ok(CouponTemplateConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/list")
@Operation(summary = "获得优惠劵模板列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@SaCheckPermission("promotion:coupon-template:query")
public R<List<CouponTemplateRespVO>> getCouponTemplateList(@RequestParam("ids") Collection<Long> ids) {
List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(ids);
return R.ok(CouponTemplateConvert.INSTANCE.convertList(list));
}
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.coupon;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.mall.enums.promotion.common.PromotionDiscountTypeEnum;
import org.dromara.mall.enums.promotion.common.PromotionProductScopeEnum;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static org.dromara.common.mall.util.date.DateUtils.TIME_ZONE_DEFAULT;
/**
* 优惠劵 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class CouponBaseVO {
// ========== 基本信息 BEGIN ==========
@Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "优惠劵模板编号不能为空")
private Long templateId;
@Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
@NotNull(message = "优惠劵名不能为空")
private String name;
@Schema(description = "优惠码状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
// ========== 基本信息 END ==========
// ========== 领取情况 BEGIN ==========
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "领取方式不能为空")
private Integer takeType;
// ========== 领取情况 END ==========
// ========== 使用规则 BEGIN ==========
@Schema(description = "是否设置满多少金额可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 单位:分;0 - 不限制
@NotNull(message = "是否设置满多少金额可用不能为空")
private Integer usePrice;
@Schema(description = "固定日期 - 生效开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime validStartTime;
@Schema(description = "固定日期 - 生效结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime validEndTime;
@Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "商品范围不能为空")
@InEnum(PromotionProductScopeEnum.class)
private Integer productScope;
@Schema(description = "商品范围编号的数组", example = "1,3")
private List<Long> productScopeValues;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
@Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "优惠类型不能为空")
@InEnum(PromotionDiscountTypeEnum.class)
private Integer discountType;
@Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
private Integer discountPercent;
@Schema(description = "优惠金额", example = "10")
@Min(value = 0, message = "优惠金额需要大于等于 0")
private Integer discountPrice;
@Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
private Integer discountLimitPrice;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
@Schema(description = "使用订单号", example = "4096")
private Long useOrderId;
@Schema(description = "使用时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime useTime;
// ========== 使用情况 END ==========
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 优惠劵分页的每一项 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponPageItemRespVO extends CouponRespVO {
@Schema(description = "用户昵称", example = "老芋艿")
private String nickname;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.mall.enums.promotion.coupon.CouponStatusEnum;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.Collection;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 优惠劵分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponPageReqVO extends PageParam {
@Schema(description = "优惠劵模板编号", example = "2048")
private Long templateId;
@Schema(description = "优惠码状态", example = "1")
@InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "用户昵称", example = "芋艿")
private String nickname;
@Schema(description = "用户编号", example = "1")
private Collection<Long> userIds;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 优惠劵 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponRespVO extends CouponBaseVO {
@Schema(description = "优惠劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.ToString;
import java.util.Set;
@Schema(description = "管理后台 - 优惠劵发放 Request VO")
@Data
@ToString(callSuper = true)
public class CouponSendReqVO {
@Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "优惠劵模板编号不能为空")
private Long templateId;
@Schema(description = "用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
@NotEmpty(message = "用户编号列表不能为空")
private Set<Long> userIds;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.template;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.mall.enums.promotion.common.PromotionDiscountTypeEnum;
import org.dromara.mall.enums.promotion.common.PromotionProductScopeEnum;
import org.dromara.mall.enums.promotion.coupon.CouponTemplateValidityTypeEnum;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static org.dromara.common.mall.util.date.DateUtils.TIME_ZONE_DEFAULT;
/**
* 优惠劵模板 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class CouponTemplateBaseVO {
@Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
@NotNull(message = "优惠劵名不能为空")
private String name;
@Schema(description = "优惠券说明", example = "优惠券使用说明")
private String description;
@Schema(description = "发行总量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") // -1 - 则表示不限制发放数量
@NotNull(message = "发行总量不能为空")
private Integer totalCount;
@Schema(description = "每人限领个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") // -1 - 则表示不限制
@NotNull(message = "每人限领个数不能为空")
private Integer takeLimitCount;
@Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "领取方式不能为空")
private Integer takeType;
@Schema(description = "是否设置满多少金额可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 单位:分;0 - 不限制
@NotNull(message = "是否设置满多少金额可用不能为空")
private Integer usePrice;
@Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "商品范围不能为空")
@InEnum(PromotionProductScopeEnum.class)
private Integer productScope;
@Schema(description = "商品范围编号的数组", example = "[1, 3]")
private List<Long> productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "生效日期类型不能为空")
@InEnum(CouponTemplateValidityTypeEnum.class)
private Integer validityType;
@Schema(description = "固定日期 - 生效开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime validStartTime;
@Schema(description = "固定日期 - 生效结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime validEndTime;
@Schema(description = "领取日期 - 开始天数")
@Min(value = 0L, message = "开始天数必须大于 0")
private Integer fixedStartTerm;
@Schema(description = "领取日期 - 结束天数")
@Min(value = 1L, message = "开始天数必须大于 1")
private Integer fixedEndTerm;
@Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "优惠类型不能为空")
@InEnum(PromotionDiscountTypeEnum.class)
private Integer discountType;
@Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
private Integer discountPercent;
@Schema(description = "优惠金额", example = "10")
@Min(value = 0, message = "优惠金额需要大于等于 0")
private Integer discountPrice;
@Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
private Integer discountLimitPrice;
@AssertTrue(message = "商品范围编号的数组不能为空")
@JsonIgnore
public boolean isProductScopeValuesValid() {
return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时,可以为空
|| CollUtil.isNotEmpty(productScopeValues);
}
@AssertTrue(message = "生效开始时间不能为空")
@JsonIgnore
public boolean isValidStartTimeValid() {
return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.DATE.getType())
|| validStartTime != null;
}
@AssertTrue(message = "生效结束时间不能为空")
@JsonIgnore
public boolean isValidEndTimeValid() {
return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.DATE.getType())
|| validEndTime != null;
}
@AssertTrue(message = "开始天数不能为空")
@JsonIgnore
public boolean isFixedStartTermValid() {
return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.TERM.getType())
|| fixedStartTerm != null;
}
@AssertTrue(message = "结束天数不能为空")
@JsonIgnore
public boolean isFixedEndTermValid() {
return ObjectUtil.notEqual(validityType, CouponTemplateValidityTypeEnum.TERM.getType())
|| fixedEndTerm != null;
}
@AssertTrue(message = "折扣百分比需要大于等于 1,小于等于 99")
@JsonIgnore
public boolean isDiscountPercentValid() {
return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())
|| (discountPercent != null && discountPercent >= 1 && discountPercent<= 99);
}
@AssertTrue(message = "优惠金额不能为空")
@JsonIgnore
public boolean isDiscountPriceValid() {
return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType())
|| discountPrice != null;
}
@AssertTrue(message = "折扣上限不能为空")
@JsonIgnore
public boolean isDiscountLimitPriceValid() {
return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())
|| discountLimitPrice != null;
}
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 优惠劵模板创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponTemplateCreateReqVO extends CouponTemplateBaseVO {
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.mall.enums.promotion.common.PromotionProductScopeEnum;
import org.dromara.mall.enums.promotion.coupon.CouponTakeTypeEnum;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 优惠劵模板分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponTemplatePageReqVO extends PageParam {
@Schema(description = "优惠劵名", example = "你好")
private String name;
@Schema(description = "状态", example = "1")
private Integer status;
@Schema(description = "优惠类型", example = "1")
private Integer discountType;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "可以领取的类型", example = "[1, 2, 3]")
@InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}")
private List<Integer> canTakeTypes;
@Schema(description = "商品范围", example = "1")
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
private Integer productScope;
@Schema(description = "商品范围编号", example = "1")
private Long productScopeValue;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mall.validation.InEnum;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 优惠劵模板 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponTemplateRespVO extends CouponTemplateBaseVO {
@Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "领取优惠券的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer takeCount;
@Schema(description = "使用优惠券的次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Integer useCount;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 优惠劵模板更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CouponTemplateUpdateReqVO extends CouponTemplateBaseVO {
@Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "模板编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mall.validation.InEnum;
@Schema(description = "管理后台 - 优惠劵模板更新状态 Request VO")
@Data
public class CouponTemplateUpdateStatusReqVO {
@Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "优惠劵模板编号不能为空")
private Long id;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.discount;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.discount.vo.DiscountActivityCreateReqVO;
import org.dromara.mall.controller.promotion.admin.discount.vo.DiscountActivityPageReqVO;
import org.dromara.mall.controller.promotion.admin.discount.vo.DiscountActivityRespVO;
import org.dromara.mall.controller.promotion.admin.discount.vo.DiscountActivityUpdateReqVO;
import org.dromara.mall.convert.promotion.discount.DiscountActivityConvert;
import org.dromara.mall.domain.promotion.DiscountActivityDO;
import org.dromara.mall.domain.promotion.DiscountProductDO;
import org.dromara.mall.service.promotion.DiscountActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 限时折扣活动")
@RestController
@RequestMapping("/admin/promotion/discount-activity")
@Validated
public class DiscountActivityController {
@Resource
private DiscountActivityService discountActivityService;
@PostMapping("/create")
@Operation(summary = "创建限时折扣活动")
@SaCheckPermission("promotion:discount-activity:create")
public R<Long> createDiscountActivity(@Valid @RequestBody DiscountActivityCreateReqVO createReqVO) {
return R.ok(discountActivityService.createDiscountActivity(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新限时折扣活动")
@SaCheckPermission("promotion:discount-activity:update")
public R<Boolean> updateDiscountActivity(@Valid @RequestBody DiscountActivityUpdateReqVO updateReqVO) {
discountActivityService.updateDiscountActivity(updateReqVO);
return R.ok(true);
}
@PutMapping("/close")
@Operation(summary = "关闭限时折扣活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:discount-activity:close")
public R<Boolean> closeRewardActivity(@RequestParam("id") Long id) {
discountActivityService.closeDiscountActivity(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除限时折扣活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:discount-activity:delete")
public R<Boolean> deleteDiscountActivity(@RequestParam("id") Long id) {
discountActivityService.deleteDiscountActivity(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得限时折扣活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:discount-activity:query")
public R<DiscountActivityRespVO> getDiscountActivity(@RequestParam("id") Long id) {
DiscountActivityDO discountActivity = discountActivityService.getDiscountActivity(id);
if (discountActivity == null) {
return R.ok(null);
}
// 拼接结果
List<DiscountProductDO> discountProducts = discountActivityService.getDiscountProductsByActivityId(id);
return R.ok(DiscountActivityConvert.INSTANCE.convert(discountActivity, discountProducts));
}
@GetMapping("/page")
@Operation(summary = "获得限时折扣活动分页")
@SaCheckPermission("promotion:discount-activity:query")
public R<PageResult<DiscountActivityRespVO>> getDiscountActivityPage(@Valid DiscountActivityPageReqVO pageVO) {
PageResult<DiscountActivityDO> pageResult = discountActivityService.getDiscountActivityPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
List<DiscountProductDO> products = discountActivityService.getDiscountProductsByActivityId(
convertSet(pageResult.getList(), DiscountActivityDO::getId));
return R.ok(DiscountActivityConvert.INSTANCE.convertPage(pageResult, products));
}
}
package org.dromara.mall.controller.promotion.admin.discount.vo;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.mall.enums.promotion.common.PromotionDiscountTypeEnum;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 限时折扣活动 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class DiscountActivityBaseVO {
@Schema(description = "活动标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个标题")
@NotNull(message = "活动标题不能为空")
private String name;
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "商品")
@Data
public static class Product {
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "优惠类型不能为空")
@InEnum(PromotionDiscountTypeEnum.class)
private Integer discountType;
@Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
private Integer discountPercent;
@Schema(description = "优惠金额", example = "10")
@Min(value = 0, message = "优惠金额需要大于等于 0")
private Integer discountPrice;
@AssertTrue(message = "折扣百分比需要大于等于 0.01%,小于等于 99.99%")
@JsonIgnore
public boolean isDiscountPercentValid() {
return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())
|| (discountPercent != null && discountPercent >= 1 && discountPercent <= 9999);
}
@AssertTrue(message = "优惠金额不能为空")
@JsonIgnore
public boolean isDiscountPriceValid() {
return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType())
|| discountPrice != null;
}
}
}
package org.dromara.mall.controller.promotion.admin.discount.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@Schema(description = "管理后台 - 限时折扣活动创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiscountActivityCreateReqVO extends DiscountActivityBaseVO {
/**
* 商品列表
*/
@NotEmpty(message = "商品列表不能为空")
@Valid
private List<Product> products;
}
package org.dromara.mall.controller.promotion.admin.discount.vo;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 限时折扣活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiscountActivityPageReqVO extends PageParam {
@Schema(description = "活动标题", example = "一个标题")
private String name;
@Schema(description = "活动状态", example = "1")
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.discount.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 限时折扣活动 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiscountActivityRespVO extends DiscountActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "活动状态不能为空")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "限时折扣商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Product> products;
}
package org.dromara.mall.controller.promotion.admin.discount.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@Schema(description = "管理后台 - 限时折扣活动更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiscountActivityUpdateReqVO extends DiscountActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "活动编号不能为空")
private Long id;
/**
* 商品列表
*/
@NotEmpty(message = "商品列表不能为空")
@Valid
private List<Product> products;
}
package org.dromara.mall.controller.promotion.admin.diy;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.diy.vo.page.*;
import org.dromara.mall.convert.promotion.diy.DiyPageConvert;
import org.dromara.mall.domain.promotion.DiyPageDO;
import org.dromara.mall.service.promotion.DiyPageService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
@Tag(name = "管理后台 - 装修页面")
@RestController
@RequestMapping("/admin/promotion/diy-page")
@Validated
public class DiyPageController {
@Resource
private DiyPageService diyPageService;
@PostMapping("/create")
@Operation(summary = "创建装修页面")
@SaCheckPermission("promotion:diy-page:create")
public R<Long> createDiyPage(@Valid @RequestBody DiyPageCreateReqVO createReqVO) {
return R.ok(diyPageService.createDiyPage(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新装修页面")
@SaCheckPermission("promotion:diy-page:update")
public R<Boolean> updateDiyPage(@Valid @RequestBody DiyPageUpdateReqVO updateReqVO) {
diyPageService.updateDiyPage(updateReqVO);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除装修页面")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:diy-page:delete")
public R<Boolean> deleteDiyPage(@RequestParam("id") Long id) {
diyPageService.deleteDiyPage(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得装修页面")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:diy-page:query")
public R<DiyPageRespVO> getDiyPage(@RequestParam("id") Long id) {
DiyPageDO diyPage = diyPageService.getDiyPage(id);
return R.ok(DiyPageConvert.INSTANCE.convert(diyPage));
}
@GetMapping("/list")
@Operation(summary = "获得装修页面列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@SaCheckPermission("promotion:diy-page:query")
public R<List<DiyPageRespVO>> getDiyPageList(@RequestParam("ids") Collection<Long> ids) {
List<DiyPageDO> list = diyPageService.getDiyPageList(ids);
return R.ok(DiyPageConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得装修页面分页")
@SaCheckPermission("promotion:diy-page:query")
public R<PageResult<DiyPageRespVO>> getDiyPagePage(@Valid DiyPagePageReqVO pageVO) {
PageResult<DiyPageDO> pageResult = diyPageService.getDiyPagePage(pageVO);
return R.ok(DiyPageConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get-property")
@Operation(summary = "获得装修页面属性")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:diy-page:query")
public R<DiyPagePropertyRespVO> getDiyPageProperty(@RequestParam("id") Long id) {
DiyPageDO diyPage = diyPageService.getDiyPage(id);
return R.ok(DiyPageConvert.INSTANCE.convertPropertyVo(diyPage));
}
@PutMapping("/update-property")
@Operation(summary = "更新装修页面属性")
@SaCheckPermission("promotion:diy-page:update")
public R<Boolean> updateDiyPageProperty(@Valid @RequestBody DiyPagePropertyUpdateRequestVO updateReqVO) {
diyPageService.updateDiyPageProperty(updateReqVO);
return R.ok(true);
}
}
package org.dromara.mall.controller.promotion.admin.diy;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.diy.vo.template.*;
import org.dromara.mall.convert.promotion.diy.DiyTemplateConvert;
import org.dromara.mall.domain.promotion.DiyPageDO;
import org.dromara.mall.domain.promotion.DiyTemplateDO;
import org.dromara.mall.service.promotion.DiyPageService;
import org.dromara.mall.service.promotion.DiyTemplateService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "管理后台 - 装修模板")
@RestController
@RequestMapping("/admin/promotion/diy-template")
@Validated
public class DiyTemplateController {
@Resource
private DiyTemplateService diyTemplateService;
@Resource
private DiyPageService diyPageService;
@PostMapping("/create")
@Operation(summary = "创建装修模板")
@SaCheckPermission("promotion:diy-template:create")
public R<Long> createDiyTemplate(@Valid @RequestBody DiyTemplateCreateReqVO createReqVO) {
return R.ok(diyTemplateService.createDiyTemplate(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新装修模板")
@SaCheckPermission("promotion:diy-template:update")
public R<Boolean> updateDiyTemplate(@Valid @RequestBody DiyTemplateUpdateReqVO updateReqVO) {
diyTemplateService.updateDiyTemplate(updateReqVO);
return R.ok(true);
}
@PutMapping("/use")
@Operation(summary = "使用装修模板")
@SaCheckPermission("promotion:diy-template:use")
public R<Boolean> useDiyTemplate(@RequestParam("id") Long id) {
diyTemplateService.useDiyTemplate(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除装修模板")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:diy-template:delete")
public R<Boolean> deleteDiyTemplate(@RequestParam("id") Long id) {
diyTemplateService.deleteDiyTemplate(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得装修模板")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:diy-template:query")
public R<DiyTemplateRespVO> getDiyTemplate(@RequestParam("id") Long id) {
DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);
return R.ok(DiyTemplateConvert.INSTANCE.convert(diyTemplate));
}
@GetMapping("/page")
@Operation(summary = "获得装修模板分页")
@SaCheckPermission("promotion:diy-template:query")
public R<PageResult<DiyTemplateRespVO>> getDiyTemplatePage(@Valid DiyTemplatePageReqVO pageVO) {
PageResult<DiyTemplateDO> pageResult = diyTemplateService.getDiyTemplatePage(pageVO);
return R.ok(DiyTemplateConvert.INSTANCE.convertPage(pageResult));
}
// TODO @疯狂:这个要不和 getDiyTemplate 合并,然后 DiyTemplateRespVO 里面直接把 DiyPagePropertyRespVO 也加上。减少 VO 好了,管理后台 get 多返回点数据,也问题不大的。目的,还是想尽可能降低大家的理解成本哈;
@GetMapping("/get-property")
@Operation(summary = "获得装修模板属性")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:diy-template:query")
public R<DiyTemplatePropertyRespVO> getDiyTemplateProperty(@RequestParam("id") Long id) {
DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);
List<DiyPageDO> pages = diyPageService.getDiyPageByTemplateId(id);
return R.ok(DiyTemplateConvert.INSTANCE.convertPropertyVo(diyTemplate, pages));
}
// TODO @疯狂:这个接口,要不和 useDiyTemplate 合并成一个,然后 VO 改成我们新的 VO 规范。不改的字段,就不传递。
@PutMapping("/update-property")
@Operation(summary = "更新装修模板属性")
@SaCheckPermission("promotion:diy-template:update")
public R<Boolean> updateDiyTemplateProperty(@Valid @RequestBody DiyTemplatePropertyUpdateRequestVO updateReqVO) {
diyTemplateService.updateDiyTemplateProperty(updateReqVO);
return R.ok(true);
}
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
* 装修页面 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class DiyPageBaseVO {
@Schema(description = "装修模板编号", example = "26179")
private Long templateId;
@Schema(description = "页面名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@NotNull(message = "页面名称不能为空")
private String name;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "预览图")
private List<String> previewPicUrls;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 装修页面创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyPageCreateReqVO extends DiyPageBaseVO {
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 装修页面分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyPagePageReqVO extends PageParam {
@Schema(description = "页面名称", example = "王五")
private String name;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 装修页面属性 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyPagePropertyRespVO extends DiyPageBaseVO {
@Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
private Long id;
@Schema(description = "页面属性", example = "[]")
private String property;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.ToString;
@Schema(description = "管理后台 - 装修页面属性更新 Request VO")
@Data
@ToString(callSuper = true)
public class DiyPagePropertyUpdateRequestVO {
@Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
@NotNull(message = "装修页面编号不能为空")
private Long id;
@Schema(description = "页面属性,JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
@NotBlank(message = "页面属性不能为空")
private String property;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 装修页面 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyPageRespVO extends DiyPageBaseVO {
@Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12082")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.page;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 装修页面更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyPageUpdateReqVO extends DiyPageBaseVO {
@Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12082")
@NotNull(message = "装修页面编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
/**
* 装修模板 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class DiyTemplateBaseVO {
@Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "默认主题")
@NotEmpty(message = "模板名称不能为空")
private String name;
@Schema(description = "备注", example = "默认主题")
private String remark;
@Schema(description = "预览图", example = "[https://www.iocoder.cn/1.jpg]")
private List<String> previewPicUrls;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 装修模板创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyTemplateCreateReqVO extends DiyTemplateBaseVO {
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 装修模板分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyTemplatePageReqVO extends PageParam {
@Schema(description = "模板名称", example = "默认主题")
private String name;
@Schema(description = "是否使用", example = "true")
private Boolean used;
@Schema(description = "使用时间", example = "使用时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] usedTime;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.mall.controller.promotion.admin.diy.vo.page.DiyPagePropertyRespVO;
import java.util.List;
@Schema(description = "管理后台 - 装修模板属性 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyTemplatePropertyRespVO extends DiyTemplateBaseVO {
@Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
private Long id;
@Schema(description = "模板属性,JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
private String property;
@Schema(description = "模板页面", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]")
private List<DiyPagePropertyRespVO> pages;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.ToString;
@Schema(description = "管理后台 - 装修模板属性更新 Request VO")
@Data
@ToString(callSuper = true)
public class DiyTemplatePropertyUpdateRequestVO {
@Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
@NotNull(message = "装修模板编号不能为空")
private Long id;
@Schema(description = "模板属性,JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
@NotBlank(message = "模板属性不能为空")
private String property;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 装修模板 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyTemplateRespVO extends DiyTemplateBaseVO {
@Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "是否使用", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean used;
@Schema(description = "使用时间", example = "使用时间")
private LocalDateTime usedTime;
}
package org.dromara.mall.controller.promotion.admin.diy.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 装修模板更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DiyTemplateUpdateReqVO extends DiyTemplateBaseVO {
@Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
@NotNull(message = "装修模板编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.kefu;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.mall.api.dto.member.MemberUserRespDTO;
import org.dromara.mall.api.service.member.MemberUserApi;
import org.dromara.mall.controller.promotion.admin.kefu.vo.conversation.KeFuConversationRespVO;
import org.dromara.mall.controller.promotion.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
import org.dromara.mall.domain.promotion.KefuConversationDO;
import org.dromara.mall.service.promotion.KeFuConversationService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
import static org.dromara.common.mall.util.collection.MapUtils.findAndThen;
@Tag(name = "管理后台 - 客服会话")
@RestController
@RequestMapping("/admin/promotion/kefu-conversation")
@Validated
public class KeFuConversationController {
@Resource
private KeFuConversationService conversationService;
@Resource
private MemberUserApi memberUserApi;
@GetMapping("/get")
@Operation(summary = "获得客服会话")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:kefu-conversation:query")
public R<KeFuConversationRespVO> getConversation(@RequestParam("id") Long id) {
KefuConversationDO conversation = conversationService.getConversation(id);
if (conversation == null) {
return R.ok(null);
}
// 拼接数据
KeFuConversationRespVO result = BeanUtils.toBean(conversation, KeFuConversationRespVO.class);
MemberUserRespDTO memberUser = memberUserApi.getUser(conversation.getUserId());
if (memberUser != null) {
result.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname());
}
return R.ok(result);
}
@PutMapping("/update-conversation-pinned")
@Operation(summary = "置顶/取消置顶客服会话")
@SaCheckPermission("promotion:kefu-conversation:update")
public R<Boolean> updateConversationPinned(@Valid @RequestBody KeFuConversationUpdatePinnedReqVO updateReqVO) {
conversationService.updateConversationPinnedByAdmin(updateReqVO);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除客服会话")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:kefu-conversation:delete")
public R<Boolean> deleteConversation(@RequestParam("id") Long id) {
conversationService.deleteKefuConversation(id);
return R.ok(true);
}
@GetMapping("/list")
@Operation(summary = "获得客服会话列表")
@SaCheckPermission("promotion:kefu-conversation:query")
public R<List<KeFuConversationRespVO>> getConversationList() {
// 查询会话列表
List<KeFuConversationRespVO> respList = BeanUtils.toBean(conversationService.getKefuConversationList(),
KeFuConversationRespVO.class);
// 拼接数据
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(respList, KeFuConversationRespVO::getUserId));
respList.forEach(item-> findAndThen(userMap, item.getUserId(),
memberUser-> item.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname())));
return R.ok(respList);
}
}
package org.dromara.mall.controller.promotion.admin.kefu;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.UserTypeEnum;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.api.dto.system.AdminUserRespDTO;
import org.dromara.mall.api.service.system.AdminUserApi;
import org.dromara.mall.controller.promotion.admin.kefu.vo.message.KeFuMessageListReqVO;
import org.dromara.mall.controller.promotion.admin.kefu.vo.message.KeFuMessageRespVO;
import org.dromara.mall.controller.promotion.admin.kefu.vo.message.KeFuMessageSendReqVO;
import org.dromara.mall.domain.promotion.KefuMessageDO;
import org.dromara.mall.service.promotion.KeFuMessageService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
import static org.dromara.common.mall.util.collection.CollectionUtils.filterList;
import static org.dromara.common.mall.util.collection.MapUtils.findAndThen;
@Tag(name = "管理后台 - 客服消息")
@RestController
@RequestMapping("/admin/promotion/kefu-message")
@Validated
public class KeFuMessageController {
@Resource
private KeFuMessageService messageService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/send")
@Operation(summary = "发送客服消息")
@SaCheckPermission("promotion:kefu-message:send")
public R<Long> sendKeFuMessage(@Valid @RequestBody KeFuMessageSendReqVO sendReqVO) {
sendReqVO.setSenderId(LoginHelper.getUserId()).setSenderType(UserTypeEnum.ADMIN.getValue()); // 设置用户编号和类型
return R.ok(messageService.sendKefuMessage(sendReqVO));
}
@PutMapping("/update-read-status")
@Operation(summary = "更新客服消息已读状态")
@Parameter(name = "conversationId", description = "会话编号", required = true)
@SaCheckPermission("promotion:kefu-message:update")
public org.dromara.common.core.domain.R<Boolean> updateKeFuMessageReadStatus(@RequestParam("conversationId") Long conversationId) {
messageService.updateKeFuMessageReadStatus(conversationId, LoginHelper.getMemberId(), UserTypeEnum.ADMIN.getValue());
return R.ok(true);
}
@GetMapping("/list")
@Operation(summary = "获得客服消息列表")
@SaCheckPermission("promotion:kefu-message:query")
public R<List<KeFuMessageRespVO>> getKeFuMessageList(@Valid KeFuMessageListReqVO pageReqVO) {
// 获得数据
List<KefuMessageDO> list = messageService.getKeFuMessageList(pageReqVO);
// 拼接数据
List<KeFuMessageRespVO> result = BeanUtils.toBean(list, KeFuMessageRespVO.class);
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result,
item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId));
result.forEach(item -> findAndThen(userMap, item.getSenderId(), user -> item.setSenderAvatar(user.getAvatar())));
return R.ok(result);
}
}
package org.dromara.mall.controller.promotion.admin.kefu.vo.conversation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 客服会话 Response VO")
@Data
@Accessors(chain = true)
public class KeFuConversationRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24988")
private Long id;
@Schema(description = "会话所属用户", requiredMode = Schema.RequiredMode.REQUIRED, example = "8300")
private Long userId;
@Schema(description = "会话所属用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://yudao.com/images/avatar.jpg")
private String userAvatar;
@Schema(description = "会话所属用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String userNickname;
@Schema(description = "最后聊天时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime lastMessageTime;
@Schema(description = "最后聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "嗨,您好啊")
private String lastMessageContent;
@Schema(description = "最后发送的消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer lastMessageContentType;
@Schema(description = "管理端置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean adminPinned;
@Schema(description = "用户是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean userDeleted;
@Schema(description = "管理员是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean adminDeleted;
@Schema(description = "管理员未读消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
private Integer adminUnreadMessageCount;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.kefu.vo.conversation;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 客服会话置顶 Request VO")
@Data
public class KeFuConversationUpdatePinnedReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23202")
@NotNull(message = "会话编号不能为空")
private Long id;
@Schema(description = "管理端置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "管理端置顶不能为空")
private Boolean adminPinned;
}
package org.dromara.mall.controller.promotion.admin.kefu.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 客服消息列表 Request VO")
@Data
public class KeFuMessageListReqVO {
private static final Integer LIMIT = 10;
@Schema(description = "会话编号", example = "12580")
@NotNull(message = "会话编号不能为空")
private Long conversationId;
@Schema(description = "发送时间", example = "2024-03-27 12:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime createTime;
@Schema(description = "每次查询条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "每次查询条数不能为空")
@Min(value = 1, message = "每次查询条数最小值为 1")
@Max(value = 100, message = "每次查询最大值为 100")
private Integer limit = LIMIT;
}
package org.dromara.mall.controller.promotion.admin.kefu.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 客服消息 Response VO")
@Data
public class KeFuMessageRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23202")
private Long id;
@Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12580")
private Long conversationId;
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571")
private Long senderId;
@Schema(description = "发送人头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://yudao.com/images/avatar.jpg")
private String senderAvatar;
@Schema(description = "发送人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer senderType;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29124")
private Long receiverId;
@Schema(description = "接收人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer receiverType;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer contentType;
@Schema(description = "消息", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Boolean readStatus;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.kefu.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
@Schema(description = "管理后台 - 发送客服消息 Request VO")
@Data
@Accessors(chain = true)
public class KeFuMessageSendReqVO {
@Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12580")
@NotNull(message = "会话编号不能为空")
private Long conversationId;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "消息类型不能为空")
private Integer contentType;
@Schema(description = "消息", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "消息不能为空")
private String content;
// ========== 后端设置的参数,前端无需传递 ==========
@Schema(description = "发送人编号", example = "24571", hidden = true)
private Long senderId;
@Schema(description = "发送人类型", example = "1", hidden = true)
private Integer senderType;
}
package org.dromara.mall.controller.promotion.admin.point;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.admin.point.vo.activity.PointActivityPageReqVO;
import org.dromara.mall.controller.promotion.admin.point.vo.activity.PointActivityRespVO;
import org.dromara.mall.controller.promotion.admin.point.vo.activity.PointActivitySaveReqVO;
import org.dromara.mall.controller.promotion.admin.point.vo.product.PointProductRespVO;
import org.dromara.mall.domain.promotion.PointActivityDO;
import org.dromara.mall.domain.promotion.PointProductDO;
import org.dromara.mall.service.promotion.PointActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.*;
import static org.dromara.common.mall.util.collection.MapUtils.findAndThen;
@Tag(name = "管理后台 - 积分商城活动")
@RestController
@RequestMapping("/admin/promotion/point-activity")
@Validated
public class PointActivityController {
@Resource
private PointActivityService pointActivityService;
@Resource
private ProductSpuApi productSpuApi;
@PostMapping("/create")
@Operation(summary = "创建积分商城活动")
@SaCheckPermission("promotion:point-activity:create")
public R<Long> createPointActivity(@Valid @RequestBody PointActivitySaveReqVO createReqVO) {
return R.ok(pointActivityService.createPointActivity(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新积分商城活动")
@SaCheckPermission("promotion:point-activity:update")
public R<Boolean> updatePointActivity(@Valid @RequestBody PointActivitySaveReqVO updateReqVO) {
pointActivityService.updatePointActivity(updateReqVO);
return R.ok(true);
}
@PutMapping("/close")
@Operation(summary = "关闭积分商城活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:point-activity:close")
public R<Boolean> closeSeckillActivity(@RequestParam("id") Long id) {
pointActivityService.closePointActivity(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除积分商城活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:point-activity:delete")
public R<Boolean> deletePointActivity(@RequestParam("id") Long id) {
pointActivityService.deletePointActivity(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得积分商城活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:point-activity:query")
public R<PointActivityRespVO> getPointActivity(@RequestParam("id") Long id) {
PointActivityDO pointActivity = pointActivityService.getPointActivity(id);
if (pointActivity == null) {
return R.ok(null);
}
List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(Collections.singletonList(id));
PointActivityRespVO respVO = BeanUtils.toBean(pointActivity, PointActivityRespVO.class);
respVO.setProducts(BeanUtils.toBean(products, PointProductRespVO.class));
return R.ok(respVO);
}
@GetMapping("/page")
@Operation(summary = "获得积分商城活动分页")
@SaCheckPermission("promotion:point-activity:query")
public R<PageResult<PointActivityRespVO>> getPointActivityPage(@Valid PointActivityPageReqVO pageReqVO) {
PageResult<PointActivityDO> pageResult = pointActivityService.getPointActivityPage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
List<PointActivityRespVO> resultList = buildPointActivityRespVOList(pageResult.getList());
return R.ok(new PageResult<>(resultList, pageResult.getTotal()));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得积分商城活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public R<List<PointActivityRespVO>> getPointActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<PointActivityDO> activityList = pointActivityService.getPointActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return R.ok(Collections.emptyList());
}
// 2. 拼接返回
List<PointActivityRespVO> result = buildPointActivityRespVOList(activityList);
return R.ok(result);
}
private List<PointActivityRespVO> buildPointActivityRespVOList(List<PointActivityDO> activityList) {
List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(
convertSet(activityList, PointActivityDO::getId));
Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId);
Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpusMap(
convertSet(activityList, PointActivityDO::getSpuId));
List<PointActivityRespVO> result = BeanUtils.toBean(activityList, PointActivityRespVO.class);
result.forEach(activity -> {
// 设置 product 信息
PointProductDO minProduct = getMinObject(productsMap.get(activity.getId()), PointProductDO::getPoint);
assert minProduct != null;
activity.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice());
findAndThen(spuMap, activity.getSpuId(),
spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
});
return result;
}
}
package org.dromara.mall.controller.promotion.admin.point.vo.activity;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 积分商城活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PointActivityPageReqVO extends PageParam {
@Schema(description = "活动状态", example = "2")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.point.vo.activity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import org.dromara.mall.controller.promotion.admin.point.vo.product.PointProductRespVO;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 积分商城活动 Response VO")
@Data
@Accessors(chain = true)
@ExcelIgnoreUnannotated
public class PointActivityRespVO {
@Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373")
@ExcelProperty("积分商城活动编号")
private Long id;
@Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509")
@ExcelProperty("积分商城活动商品")
private Long spuId;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("活动状态")
private Integer status;
@Schema(description = "积分商城活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("积分商城活动库存")
private Integer stock; // 剩余库存积分兑换时扣减
@Schema(description = "积分商城活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("积分商城活动总库存")
private Integer totalStock;
@Schema(description = "备注", example = "你说的对")
@ExcelProperty("备注")
private String remark;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("排序")
private Integer sort;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "积分商城商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<PointProductRespVO> products;
// ========== 商品字段 ==========
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
example = "50")
private Integer marketPrice;
//======================= 显示所需兑换积分最少的 sku 信息 =======================
@Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer point;
@Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860")
private Integer price;
}
package org.dromara.mall.controller.promotion.admin.point.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.mall.controller.promotion.admin.point.vo.product.PointProductSaveReqVO;
import java.util.List;
@Schema(description = "管理后台 - 积分商城活动新增/修改 Request VO")
@Data
public class PointActivitySaveReqVO {
@Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373")
private Long id;
@Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509")
@NotNull(message = "积分商城活动商品不能为空")
private Long spuId;
@Schema(description = "备注", example = "你说的对")
private String remark;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "排序不能为空")
private Integer sort;
@Schema(description = "积分商城商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<PointProductSaveReqVO> products;
}
package org.dromara.mall.controller.promotion.admin.point.vo.product;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 积分商城商品 Response VO")
@Data
@ExcelIgnoreUnannotated
public class PointProductRespVO {
@Schema(description = "积分商城商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31718")
private Long id;
@Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29388")
private Long activityId;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8112")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2736")
private Long skuId;
@Schema(description = "可兑换数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3926")
private Integer count;
@Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer point;
@Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860")
private Integer price;
@Schema(description = "积分商城商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer stock;
@Schema(description = "积分商城商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer activityStatus;
}
package org.dromara.mall.controller.promotion.admin.point.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 积分商城商品新增/修改 Request VO")
@Data
public class PointProductSaveReqVO {
@Schema(description = "积分商城商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31718")
private Long id;
@Schema(description = "积分商城活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "29388")
@NotNull(message = "积分商城活动 id不能为空")
private Long activityId;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8112")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2736")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "可兑换数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3926")
@NotNull(message = "可兑换数量不能为空")
private Integer count;
@Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "兑换积分不能为空")
private Integer point;
@Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860")
@NotNull(message = "兑换金额,单位:分不能为空")
private Integer price;
@Schema(description = "积分商城商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@NotNull(message = "积分商城商品不能为空")
private Integer stock;
@Schema(description = "积分商城商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "积分商城商品状态不能为空")
private Integer activityStatus;
}
package org.dromara.mall.controller.promotion.admin.reward;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.reward.vo.RewardActivityCreateReqVO;
import org.dromara.mall.controller.promotion.admin.reward.vo.RewardActivityPageReqVO;
import org.dromara.mall.controller.promotion.admin.reward.vo.RewardActivityRespVO;
import org.dromara.mall.controller.promotion.admin.reward.vo.RewardActivityUpdateReqVO;
import org.dromara.mall.domain.promotion.RewardActivityDO;
import org.dromara.mall.service.promotion.RewardActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Tag(name = "管理后台 - 满减送活动")
@RestController
@RequestMapping("/admin/promotion/reward-activity")
@Validated
public class RewardActivityController {
@Resource
private RewardActivityService rewardActivityService;
@PostMapping("/create")
@Operation(summary = "创建满减送活动")
@SaCheckPermission("promotion:reward-activity:create")
public R<Long> createRewardActivity(@Valid @RequestBody RewardActivityCreateReqVO createReqVO) {
return R.ok(rewardActivityService.createRewardActivity(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新满减送活动")
@SaCheckPermission("promotion:reward-activity:update")
public R<Boolean> updateRewardActivity(@Valid @RequestBody RewardActivityUpdateReqVO updateReqVO) {
rewardActivityService.updateRewardActivity(updateReqVO);
return R.ok(true);
}
@PutMapping("/close")
@Operation(summary = "关闭满减送活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:reward-activity:close")
public R<Boolean> closeRewardActivity(@RequestParam("id") Long id) {
rewardActivityService.closeRewardActivity(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除满减送活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:reward-activity:delete")
public R<Boolean> deleteRewardActivity(@RequestParam("id") Long id) {
rewardActivityService.deleteRewardActivity(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得满减送活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:reward-activity:query")
public R<RewardActivityRespVO> getRewardActivity(@RequestParam("id") Long id) {
RewardActivityDO rewardActivity = rewardActivityService.getRewardActivity(id);
return R.ok(BeanUtils.toBean(rewardActivity, RewardActivityRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得满减送活动分页")
@SaCheckPermission("promotion:reward-activity:query")
public R<PageResult<RewardActivityRespVO>> getRewardActivityPage(@Valid RewardActivityPageReqVO pageVO) {
PageResult<RewardActivityDO> pageResult = rewardActivityService.getRewardActivityPage(pageVO);
return R.ok(org.dromara.common.mybatis.util.BeanUtils.toBean(pageResult, RewardActivityRespVO.class));
}
}
package org.dromara.mall.controller.promotion.admin.reward.vo;
import cn.hutool.core.collection.CollUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.mall.enums.promotion.common.PromotionConditionTypeEnum;
import org.dromara.mall.enums.promotion.common.PromotionProductScopeEnum;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 满减送活动 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*/
@Data
public class RewardActivityBaseVO {
@Schema(description = "活动标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "满啦满啦")
@NotNull(message = "活动标题不能为空")
private String name;
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开始时间不能为空")
private LocalDateTime startTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "结束时间不能为空")
@Future(message = "结束时间必须大于当前时间")
private LocalDateTime endTime;
@Schema(description = "备注", example = "biubiubiu")
private String remark;
@Schema(description = "条件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "条件类型不能为空")
@InEnum(value = PromotionConditionTypeEnum.class, message = "条件类型必须是 {value}")
private Integer conditionType;
@Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "商品范围不能为空")
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围必须是 {value}")
private Integer productScope;
@Schema(description = "商品范围编号的数组", example = "[1, 3]")
private List<Long> productScopeValues;
/**
* 优惠规则的数组
*/
@Valid // 校验下子对象
private List<Rule> rules;
@Schema(description = "优惠规则")
@Data
public static class Rule {
@Schema(description = "优惠门槛", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 1. 满 N 元,单位:分; 2. 满 N 件
@Min(value = 1L, message = "优惠门槛必须大于等于 1")
private Integer limit;
@Schema(description = "优惠价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@Min(value = 1L, message = "优惠价格必须大于等于 1")
private Integer discountPrice;
@Schema(description = "是否包邮", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "规则是否包邮不能为空")
private Boolean freeDelivery;
@Schema(description = "赠送的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer point;
@Schema(description = "赠送的优惠劵编号的数组")
private Map<Long, Integer> giveCouponTemplateCounts;
@AssertTrue(message = "赠送的积分不能小于 0")
@JsonIgnore
public boolean isPointValid() {
return point == null || point >= 0;
}
}
@AssertTrue(message = "商品范围编号的数组不能为空")
@JsonIgnore
public boolean isProductScopeValuesValid() {
return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时,可以为空
|| CollUtil.isNotEmpty(productScopeValues);
}
}
package org.dromara.mall.controller.promotion.admin.reward.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 满减送活动创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RewardActivityCreateReqVO extends RewardActivityBaseVO {
}
package org.dromara.mall.controller.promotion.admin.reward.vo;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 满减送活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RewardActivityPageReqVO extends PageParam {
@Schema(description = "活动标题", example = "满啦满啦")
private String name;
@Schema(description = "活动状态", example = "1")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.reward.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 满减送活动 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RewardActivityRespVO extends RewardActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer id;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.reward.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 满减送活动更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RewardActivityUpdateReqVO extends RewardActivityBaseVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "活动编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.seckill;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.admin.seckill.vo.activity.*;
import org.dromara.mall.convert.promotion.seckill.SeckillActivityConvert;
import org.dromara.mall.domain.promotion.SeckillActivityDO;
import org.dromara.mall.domain.promotion.SeckillProductDO;
import org.dromara.mall.service.promotion.SeckillActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 秒杀活动")
@RestController
@RequestMapping("/admin/promotion/seckill-activity")
@Validated
public class SeckillActivityController {
@Resource
private SeckillActivityService seckillActivityService;
@Resource
private ProductSpuApi productSpuApi;
@PostMapping("/create")
@Operation(summary = "创建秒杀活动")
@SaCheckPermission("promotion:seckill-activity:create")
public R<Long> createSeckillActivity(@Valid @RequestBody SeckillActivityCreateReqVO createReqVO) {
return R.ok(seckillActivityService.createSeckillActivity(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新秒杀活动")
@SaCheckPermission("promotion:seckill-activity:update")
public R<Boolean> updateSeckillActivity(@Valid @RequestBody SeckillActivityUpdateReqVO updateReqVO) {
seckillActivityService.updateSeckillActivity(updateReqVO);
return R.ok(true);
}
@PutMapping("/close")
@Operation(summary = "关闭秒杀活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:seckill-activity:close")
public R<Boolean> closeSeckillActivity(@RequestParam("id") Long id) {
seckillActivityService.closeSeckillActivity(id);
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除秒杀活动")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:seckill-activity:delete")
public R<Boolean> deleteSeckillActivity(@RequestParam("id") Long id) {
seckillActivityService.deleteSeckillActivity(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得秒杀活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:seckill-activity:query")
public R<SeckillActivityDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) {
SeckillActivityDO activity = seckillActivityService.getSeckillActivity(id);
List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityId(id);
return R.ok(SeckillActivityConvert.INSTANCE.convert(activity, products));
}
@GetMapping("/page")
@Operation(summary = "获得秒杀活动分页")
@SaCheckPermission("promotion:seckill-activity:query")
public R<PageResult<SeckillActivityRespVO>> getSeckillActivityPage(@Valid SeckillActivityPageReqVO pageVO) {
// 查询活动列表
PageResult<SeckillActivityDO> pageResult = seckillActivityService.getSeckillActivityPage(pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityIds(
convertSet(pageResult.getList(), SeckillActivityDO::getId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(
convertSet(pageResult.getList(), SeckillActivityDO::getSpuId));
return R.ok(SeckillActivityConvert.INSTANCE.convertPage(pageResult, products, spuList));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得秒杀活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public R<List<SeckillActivityRespVO>> getSeckillActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<SeckillActivityDO> activityList = seckillActivityService.getSeckillActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return R.ok(Collections.emptyList());
}
// 2. 拼接返回
List<SeckillProductDO> productList = seckillActivityService.getSeckillProductListByActivityIds(
convertList(activityList, SeckillActivityDO::getId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
return R.ok(SeckillActivityConvert.INSTANCE.convertList(activityList, productList, spuList));
}
}
package org.dromara.mall.controller.promotion.admin.seckill;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.admin.seckill.vo.config.*;
import org.dromara.mall.convert.promotion.seckill.SeckillConfigConvert;
import org.dromara.mall.domain.promotion.SeckillConfigDO;
import org.dromara.mall.service.promotion.SeckillConfigService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "管理后台 - 秒杀时段")
@RestController
@RequestMapping("/admin/promotion/seckill-config")
@Validated
public class SeckillConfigController {
@Resource
private SeckillConfigService seckillConfigService;
@PostMapping("/create")
@Operation(summary = "创建秒杀时段")
@SaCheckPermission("promotion:seckill-config:create")
public R<Long> createSeckillConfig(@Valid @RequestBody SeckillConfigCreateReqVO createReqVO) {
return R.ok(seckillConfigService.createSeckillConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新秒杀时段")
@SaCheckPermission("promotion:seckill-config:update")
public R<Boolean> updateSeckillConfig(@Valid @RequestBody SeckillConfigUpdateReqVO updateReqVO) {
seckillConfigService.updateSeckillConfig(updateReqVO);
return R.ok(true);
}
@PutMapping("/update-status")
@Operation(summary = "修改时段配置状态")
@SaCheckPermission("promotion:seckill-config:update")
public R<Boolean> updateSeckillConfigStatus(@Valid @RequestBody SeckillConfigUpdateStatusReqVo reqVO) {
seckillConfigService.updateSeckillConfigStatus(reqVO.getId(), reqVO.getStatus());
return R.ok(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除秒杀时段")
@Parameter(name = "id", description = "编号", required = true)
@SaCheckPermission("promotion:seckill-config:delete")
public R<Boolean> deleteSeckillConfig(@RequestParam("id") Long id) {
seckillConfigService.deleteSeckillConfig(id);
return R.ok(true);
}
@GetMapping("/get")
@Operation(summary = "获得秒杀时段")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@SaCheckPermission("promotion:seckill-config:query")
public R<SeckillConfigRespVO> getSeckillConfig(@RequestParam("id") Long id) {
SeckillConfigDO seckillConfig = seckillConfigService.getSeckillConfig(id);
return R.ok(SeckillConfigConvert.INSTANCE.convert(seckillConfig));
}
@GetMapping("/list")
@Operation(summary = "获得所有秒杀时段列表")
@SaCheckPermission("promotion:seckill-config:query")
public R<List<SeckillConfigRespVO>> getSeckillConfigList() {
List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigList();
return R.ok(SeckillConfigConvert.INSTANCE.convertList(list));
}
@GetMapping("/simple-list")
@Operation(summary = "获得所有开启状态的秒杀时段精简列表", description = "主要用于前端的下拉选项")
public R<List<SeckillConfigSimpleRespVO>> getSeckillConfigSimpleList() {
List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigListByStatus(
CommonStatusEnum.ENABLE.getStatus());
return R.ok(SeckillConfigConvert.INSTANCE.convertList1(list));
}
@GetMapping("/page")
@Operation(summary = "获得秒杀时间段分页")
@SaCheckPermission("promotion:seckill-config:query")
public R<PageResult<SeckillConfigRespVO>> getSeckillActivityPage(@Valid SeckillConfigPageReqVO pageVO) {
PageResult<SeckillConfigDO> pageResult = seckillConfigService.getSeckillConfigPage(pageVO);
return R.ok(SeckillConfigConvert.INSTANCE.convertPage(pageResult));
}
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 秒杀活动基地签证官
* 秒杀活动 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @author HUIHUI
*/
@Data
public class SeckillActivityBaseVO {
@Schema(description = "秒杀活动商品 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[121,1212]")
@NotNull(message = "秒杀活动商品不能为空")
private Long spuId;
@Schema(description = "秒杀活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促")
@NotNull(message = "秒杀活动名称不能为空")
private String name;
@Schema(description = "备注", example = "清仓大甩卖割韭菜")
private String remark;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "活动开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "活动结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "排序不能为空")
private Integer sort;
@Schema(description = "秒杀时段 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
@NotNull(message = "秒杀时段不能为空")
private List<Long> configIds;
@Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12877")
private Integer totalLimitCount;
@Schema(description = "单次限够数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "31683")
private Integer singleLimitCount;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.mall.controller.promotion.admin.seckill.vo.product.SeckillProductBaseVO;
import java.util.List;
@Schema(description = "管理后台 - 秒杀活动创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityCreateReqVO extends SeckillActivityBaseVO {
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<SeckillProductBaseVO> products;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.mall.controller.promotion.admin.seckill.vo.product.SeckillProductRespVO;
import java.util.List;
@Schema(description = "管理后台 - 秒杀活动的详细 Response VO")
@Data
@Accessors(chain = true)
@ToString(callSuper = true)
public class SeckillActivityDetailRespVO extends SeckillActivityBaseVO{
@Schema(description = "秒杀活动id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<SeckillProductRespVO> products;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.activity;
import org.dromara.common.mybatis.core.page.PageParam;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static org.dromara.common.mall.util.date.DateUtils.TIME_ZONE_DEFAULT;
@Schema(description = "管理后台 - 秒杀活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityPageReqVO extends PageParam {
@Schema(description = "秒杀活动名称", example = "晚九点限时秒杀")
private String name;
@Schema(description = "活动状态", example = "进行中")
private Integer status;
@Schema(description = "秒杀时段id", example = "1")
private Long configId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime[] createTime;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.mall.controller.promotion.admin.seckill.vo.product.SeckillProductRespVO;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 秒杀活动 Response VO")
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityRespVO extends SeckillActivityBaseVO {
@Schema(description = "秒杀活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<SeckillProductRespVO> products;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
@Schema(description = "订单实付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "22354")
private Integer totalPrice;
@Schema(description = "秒杀库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer stock;
@Schema(description = "秒杀总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer totalStock;
@Schema(description = "新增订单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer orderCount;
@Schema(description = "付款人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer userCount;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
// ========== 商品字段 ==========
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
example = "50")
private Integer marketPrice;
@Schema(description = "秒杀金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer seckillPrice; // 从 products 获取最小 price 读取
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.mall.controller.promotion.admin.seckill.vo.product.SeckillProductBaseVO;
import java.util.List;
@Schema(description = "管理后台 - 秒杀活动更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityUpdateReqVO extends SeckillActivityBaseVO {
@Schema(description = "秒杀活动id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<SeckillProductBaseVO> products;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalTime;
import java.util.List;
/**
* 秒杀时段 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @author HUIHUI
*/
@Data
public class SeckillConfigBaseVO {
@Schema(description = "秒杀时段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "早上场")
@NotNull(message = "秒杀时段名称不能为空")
private String name;
@Schema(description = "开始时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:00:00")
@NotNull(message = "开始时间点不能为空")
private String startTime;
@Schema(description = "结束时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "16:00:00")
@NotNull(message = "结束时间点不能为空")
private String endTime;
@Schema(description = "秒杀轮播图", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]")
@NotNull(message = "秒杀轮播图不能为空")
private List<String> sliderPicUrls;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "状态不能为空")
private Integer status;
@AssertTrue(message = "秒杀时段开始时间和结束时间不能相等")
@JsonIgnore
public boolean isValidStartTimeValid() {
return !LocalTime.parse(startTime).equals(LocalTime.parse(endTime));
}
@AssertTrue(message = "秒杀时段开始时间不能在结束时间之后")
@JsonIgnore
public boolean isValidEndTimeValid() {
return !LocalTime.parse(startTime).isAfter(LocalTime.parse(endTime));
}
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 秒杀时段创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillConfigCreateReqVO extends SeckillConfigBaseVO {
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 秒杀时段分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillConfigPageReqVO extends PageParam {
@Schema(description = "秒杀时段名称", example = "上午场")
private String name;
@Schema(description = "状态", example = "0")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 秒杀时段 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillConfigRespVO extends SeckillConfigBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "秒杀活动数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer seckillActivityCount;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 秒杀时段配置精简信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SeckillConfigSimpleRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "编号不能为空")
private Long id;
@Schema(description = "秒杀时段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "早上场")
@NotNull(message = "秒杀时段名称不能为空")
private String name;
@Schema(description = "开始时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:00:00")
private String startTime;
@Schema(description = "结束时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "16:00:00")
private String endTime;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 秒杀时段更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillConfigUpdateReqVO extends SeckillConfigBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "编号不能为空")
private Long id;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mall.validation.InEnum;
@Schema(description = "管理后台 - 修改时段配置状态 Request VO")
@Data
public class SeckillConfigUpdateStatusReqVo {
@Schema(description = "时段配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "时段配置编号不能为空")
private Long id;
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 秒杀参与商品 Base VO,提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
*
* @author HUIHUI
*/
@Data
public class SeckillProductBaseVO {
@Schema(description = "商品sku_id", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
@NotNull(message = "商品sku_id不能为空")
private Long skuId;
@Schema(description = "秒杀金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "6689")
@NotNull(message = "秒杀金额,单位:分不能为空")
private Integer seckillPrice;
@Schema(description = "秒杀库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@NotNull(message = "秒杀库存不能为空")
private Integer stock;
}
package org.dromara.mall.controller.promotion.admin.seckill.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 秒杀参与商品 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillProductRespVO extends SeckillProductBaseVO {
@Schema(description = "秒杀参与商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "256")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
### /promotion/activity/list-by-spu-ids 获得多个商品,近期参与的每个活动
GET {{appApi}}/promotion/activity/list-by-spu-ids?spuIds=222&spuIds=633
Authorization: Bearer {{appToken}}
Content-Type: application/json
tenant-id: {{appTenentId}}
package org.dromara.mall.controller.promotion.app.activity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.mall.controller.promotion.app.activity.vo.AppActivityRespVO;
import org.dromara.mall.domain.promotion.BargainActivityDO;
import org.dromara.mall.domain.promotion.CombinationActivityDO;
import org.dromara.mall.domain.promotion.SeckillActivityDO;
import org.dromara.mall.enums.promotion.common.PromotionTypeEnum;
import org.dromara.mall.service.promotion.BargainActivityService;
import org.dromara.mall.service.promotion.CombinationActivityService;
import org.dromara.mall.service.promotion.SeckillActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口
@RestController
@RequestMapping("/app/promotion/activity")
@Validated
public class AppActivityController {
@Resource
private CombinationActivityService combinationActivityService;
@Resource
private SeckillActivityService seckillActivityService;
@Resource
private BargainActivityService bargainActivityService;
@GetMapping("/list-by-spu-id")
@Operation(summary = "获得单个商品,进行中的拼团、秒杀、砍价活动信息", description = "每种活动,只返回一个")
@Parameter(name = "spuId", description = "商品编号", required = true)
@PermitAll
public R<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) {
List<AppActivityRespVO> activityVOList = new ArrayList<>();
// 1. 拼团活动
CombinationActivityDO combinationActivity = combinationActivityService.getMatchCombinationActivityBySpuId(spuId);
if (combinationActivity != null) {
activityVOList.add(new AppActivityRespVO(combinationActivity.getId(), PromotionTypeEnum.COMBINATION_ACTIVITY.getType(),
combinationActivity.getName(), combinationActivity.getSpuId(), combinationActivity.getStartTime(), combinationActivity.getEndTime()));
}
// 2. 秒杀活动
SeckillActivityDO seckillActivity = seckillActivityService.getMatchSeckillActivityBySpuId(spuId);
if (seckillActivity != null) {
activityVOList.add(new AppActivityRespVO(seckillActivity.getId(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(),
seckillActivity.getName(), seckillActivity.getSpuId(), seckillActivity.getStartTime(), seckillActivity.getEndTime()));
}
// 3. 砍价活动
BargainActivityDO bargainActivity = bargainActivityService.getMatchBargainActivityBySpuId(spuId);
if (bargainActivity != null) {
activityVOList.add(new AppActivityRespVO(bargainActivity.getId(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),
bargainActivity.getName(), bargainActivity.getSpuId(), bargainActivity.getStartTime(), bargainActivity.getEndTime()));
}
return R.ok(activityVOList);
}
}
package org.dromara.mall.controller.promotion.app.activity.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 营销活动 Response VO")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class AppActivityRespVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "活动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type; // 对应 PromotionTypeEnum 枚举
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促")
private String name;
@Schema(description = "spu 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "618")
private Long spuId;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
}
package org.dromara.mall.controller.promotion.app.article;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.mall.controller.promotion.app.article.vo.category.AppArticleCategoryRespVO;
import org.dromara.mall.convert.promotion.article.ArticleCategoryConvert;
import org.dromara.mall.domain.promotion.ArticleCategoryDO;
import org.dromara.mall.service.promotion.ArticleCategoryService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Comparator;
import java.util.List;
@Tag(name = "用户 APP - 文章分类")
@RestController
@RequestMapping("/app/promotion/article-category")
@Validated
public class AppArticleCategoryController {
@Resource
private ArticleCategoryService articleCategoryService;
@RequestMapping("/list")
@Operation(summary = "获得文章分类列表")
public R<List<AppArticleCategoryRespVO>> getArticleCategoryList() {
List<ArticleCategoryDO> categoryList = articleCategoryService.getArticleCategoryListByStatus(
CommonStatusEnum.ENABLE.getStatus());
categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort)); // 按 sort 降序排列
return R.ok(ArticleCategoryConvert.INSTANCE.convertList04(categoryList));
}
}
package org.dromara.mall.controller.promotion.app.article;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.controller.promotion.app.article.vo.article.AppArticlePageReqVO;
import org.dromara.mall.controller.promotion.app.article.vo.article.AppArticleRespVO;
import org.dromara.mall.convert.promotion.article.ArticleConvert;
import org.dromara.mall.domain.promotion.ArticleDO;
import org.dromara.mall.service.promotion.ArticleService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "用户 APP - 文章")
@RestController
@RequestMapping("/app/promotion/article")
@Validated
public class AppArticleController {
@Resource
private ArticleService articleService;
@RequestMapping("/list")
@Operation(summary = "获得文章详情列表")
@Parameters({
@Parameter(name = "recommendHot", description = "是否热门", example = "false"), // 场景一:查看指定的文章
@Parameter(name = "recommendBanner", description = "是否轮播图", example = "false") // 场景二:查看指定的文章
})
@PermitAll
public R<List<AppArticleRespVO>> getArticleList(
@RequestParam(value = "recommendHot", required = false) Boolean recommendHot,
@RequestParam(value = "recommendBanner", required = false) Boolean recommendBanner) {
return R.ok(ArticleConvert.INSTANCE.convertList03(
articleService.getArticleCategoryListByRecommend(recommendHot, recommendBanner)));
}
@RequestMapping("/page")
@Operation(summary = "获得文章详情分页")
@PermitAll
public R<PageResult<AppArticleRespVO>> getArticlePage(AppArticlePageReqVO pageReqVO) {
return R.ok(ArticleConvert.INSTANCE.convertPage02(articleService.getArticlePage(pageReqVO)));
}
@RequestMapping("/get")
@Operation(summary = "获得文章详情")
@Parameters({
@Parameter(name = "id", description = "文章编号", example = "1024"),
@Parameter(name = "title", description = "文章标题", example = "1024"),
})
@PermitAll
public R<AppArticleRespVO> getArticle(@RequestParam(value = "id", required = false) Long id,
@RequestParam(value = "title", required = false) String title) {
ArticleDO article = id != null ? articleService.getArticle(id)
: articleService.getLastArticleByTitle(title);
return R.ok(BeanUtils.toBean(article, AppArticleRespVO.class));
}
@PutMapping("/add-browse-count")
@Operation(summary = "增加文章浏览量")
@Parameter(name = "id", description = "文章编号", example = "1024")
@PermitAll
public R<Boolean> addBrowseCount(@RequestParam("id") Long id) {
articleService.addArticleBrowseCount(id);
return R.ok(true);
}
}
package org.dromara.mall.controller.promotion.app.article.vo.article;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "应用 App - 文章的分页 Request VO")
@Data
public class AppArticlePageReqVO extends PageParam {
@Schema(description = "分类编号", example = "2048")
private Long categoryId;
}
package org.dromara.mall.controller.promotion.app.article.vo.article;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "应用 App - 文章 Response VO")
@Data
public class AppArticleRespVO {
@Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "文章标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码 - 促销模块")
private String title;
@Schema(description = "文章作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String author;
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long categoryId;
@Schema(description = "图文封面", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "文章简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是简介")
private String introduction;
@Schema(description = "文章内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是详细")
private String content;
@Schema(description = "发布时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer browseCount;
@Schema(description = "关联的商品 SPU 编号", example = "1024")
private Long spuId;
}
package org.dromara.mall.controller.promotion.app.article.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "应用 App - 文章分类 Response VO")
@Data
public class AppArticleCategoryRespVO {
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术")
private String name;
@Schema(description = "分类图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
}
package org.dromara.mall.controller.promotion.app.banner;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.mall.controller.promotion.app.banner.vo.AppBannerRespVO;
import org.dromara.mall.convert.promotion.banner.BannerConvert;
import org.dromara.mall.domain.promotion.BannerDO;
import org.dromara.mall.service.promotion.BannerService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/app/promotion/banner")
@Tag(name = "用户 APP - 首页 Banner")
@Validated
public class AppBannerController {
@Resource
private BannerService bannerService;
@GetMapping("/list")
@Operation(summary = "获得 banner 列表")
@Parameter(name = "position", description = "Banner position", example = "1")
@PermitAll
public R<List<AppBannerRespVO>> getBannerList(@RequestParam("position") Integer position) {
List<BannerDO> bannerList = bannerService.getBannerListByPosition(position);
return R.ok(BannerConvert.INSTANCE.convertList01(bannerList));
}
@PutMapping("/add-browse-count")
@Operation(summary = "增加 Banner 点击量")
@Parameter(name = "id", description = "Banner 编号", example = "1024")
@PermitAll
public R<Boolean> addBrowseCount(@RequestParam("id") Long id) {
bannerService.addBannerBrowseCount(id);
return R.ok(true);
}
}
package org.dromara.mall.controller.promotion.app.banner.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "用户 App - Banner Response VO")
@Data
public class AppBannerRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "标题不能为空")
private String title;
@Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "跳转链接不能为空")
private String url;
@Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "图片地址不能为空")
private String picUrl;
}
package org.dromara.mall.controller.promotion.app.bargain;
import cn.hutool.core.collection.CollUtil;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.app.bargain.vo.activity.AppBargainActivityDetailRespVO;
import org.dromara.mall.controller.promotion.app.bargain.vo.activity.AppBargainActivityRespVO;
import org.dromara.mall.convert.promotion.bargain.BargainActivityConvert;
import org.dromara.mall.domain.promotion.BargainActivityDO;
import org.dromara.mall.enums.promotion.bargain.BargainRecordStatusEnum;
import org.dromara.mall.service.promotion.BargainActivityService;
import org.dromara.mall.service.promotion.BargainRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import static org.dromara.common.mall.util.cache.CacheUtils.buildAsyncReloadingCache;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
@Tag(name = "用户 App - 砍价活动")
@RestController
@RequestMapping("/app/promotion/bargain-activity")
@Validated
public class AppBargainActivityController {
/**
* {@link AppBargainActivityRespVO} 缓存,通过它异步刷新 {@link #getBargainActivityList0(Integer)} 所要的首页数据
*/
private final LoadingCache<Integer, List<AppBargainActivityRespVO>> bargainActivityListCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
new CacheLoader<Integer, List<AppBargainActivityRespVO>>() {
@Override
public List<AppBargainActivityRespVO> load(Integer count) {
return getBargainActivityList0(count);
}
});
@Resource
private BargainActivityService bargainActivityService;
@Resource
private BargainRecordService bargainRecordService;
@Resource
private ProductSpuApi spuApi;
@GetMapping("/list")
@Operation(summary = "获得砍价活动列表", description = "用于小程序首页")
@Parameter(name = "count", description = "需要展示的数量", example = "6")
@PermitAll
public R<List<AppBargainActivityRespVO>> getBargainActivityList(
@RequestParam(name = "count", defaultValue = "6") Integer count) {
return R.ok(bargainActivityListCache.getUnchecked(count));
}
private List<AppBargainActivityRespVO>getBargainActivityList0(Integer count) {
List<BargainActivityDO> list = bargainActivityService.getBargainActivityListByCount(count);
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
}
// 拼接数据
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(list, BargainActivityDO::getSpuId));
return BargainActivityConvert.INSTANCE.convertAppList(list, spuList);
}
@GetMapping("/page")
@Operation(summary = "获得砍价活动分页")
@PermitAll
public R<PageResult<AppBargainActivityRespVO>> getBargainActivityPage(PageParam pageReqVO) {
PageResult<BargainActivityDO> result = bargainActivityService.getBargainActivityPage(pageReqVO);
if (CollUtil.isEmpty(result.getList())) {
return R.ok(PageResult.empty(result.getTotal()));
}
// 拼接数据
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(result.getList(), BargainActivityDO::getSpuId));
return R.ok(BargainActivityConvert.INSTANCE.convertAppPage(result, spuList));
}
@GetMapping("/get-detail")
@Operation(summary = "获得砍价活动详情")
@Parameter(name = "id", description = "活动编号", example = "1")
@PermitAll
public R<AppBargainActivityDetailRespVO> getBargainActivityDetail(@RequestParam("id") Long id) {
BargainActivityDO activity = bargainActivityService.getBargainActivity(id);
if (activity == null) {
return R.ok(null);
}
// 拼接数据
Integer successUserCount = bargainRecordService.getBargainRecordUserCount(id, BargainRecordStatusEnum.SUCCESS.getStatus());
ProductSpuRespDTO spu = spuApi.getSpu(activity.getSpuId());
return R.ok(BargainActivityConvert.INSTANCE.convert(activity, successUserCount, spu));
}
}
### /promotion/bargain-record/create 创建砍价助力
POST {{appApi}}/promotion/bargain-help/create
Authorization: Bearer test248
Content-Type: application/json
tenant-id: {{appTenentId}}
{
"recordId": 26
}
package org.dromara.mall.controller.promotion.app.bargain;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.dromara.common.core.domain.R;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.api.dto.member.MemberUserRespDTO;
import org.dromara.mall.api.service.member.MemberUserApi;
import org.dromara.mall.controller.promotion.app.bargain.vo.help.AppBargainHelpCreateReqVO;
import org.dromara.mall.controller.promotion.app.bargain.vo.help.AppBargainHelpRespVO;
import org.dromara.mall.convert.promotion.bargain.BargainHelpConvert;
import org.dromara.mall.domain.promotion.BargainHelpDO;
import org.dromara.mall.service.promotion.BargainHelpService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "用户 App - 砍价助力")
@RestController
@RequestMapping("/app/promotion/bargain-help")
@Validated
public class AppBargainHelpController {
@Resource
private BargainHelpService bargainHelpService;
@Resource
private MemberUserApi memberUserApi;
@PostMapping("/create")
@Operation(summary = "创建砍价助力", description = "给拼团记录砍一刀") // 返回结果为砍价金额,单位:分
public R<Integer> createBargainHelp(@RequestBody AppBargainHelpCreateReqVO reqVO) {
BargainHelpDO help = bargainHelpService.createBargainHelp(LoginHelper.getMemberId(), reqVO);
return R.ok(help.getReducePrice());
}
@GetMapping("/list")
@Operation(summary = "获得砍价助力列表")
@Parameter(name = "recordId", description = "砍价记录编号", required = true, example = "111")
public R<List<AppBargainHelpRespVO>> getBargainHelpList(@RequestParam("recordId") Long recordId) {
List<BargainHelpDO> helps = bargainHelpService.getBargainHelpListByRecordId(recordId);
if (CollUtil.isEmpty(helps)) {
return R.ok(Collections.emptyList());
}
helps.sort((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime())); // 倒序展示
// 拼接数据
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(
convertSet(helps, BargainHelpDO::getUserId));
return R.ok(BargainHelpConvert.INSTANCE.convertList(helps, userMap));
}
}
### /promotion/bargain-record/create 创建砍价记录
POST {{appApi}}/promotion/bargain-record/create
Authorization: Bearer {{appToken}}
Content-Type: application/json
tenant-id: {{appTenentId}}
{
"activityId": 1
}
package org.dromara.mall.controller.promotion.app.bargain;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.api.dto.member.MemberUserRespDTO;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.dto.trade.TradeOrderRespDTO;
import org.dromara.mall.api.service.member.MemberUserApi;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.api.service.trade.TradeOrderApi;
import org.dromara.mall.controller.promotion.app.bargain.vo.record.AppBargainRecordCreateReqVO;
import org.dromara.mall.controller.promotion.app.bargain.vo.record.AppBargainRecordDetailRespVO;
import org.dromara.mall.controller.promotion.app.bargain.vo.record.AppBargainRecordRespVO;
import org.dromara.mall.controller.promotion.app.bargain.vo.record.AppBargainRecordSummaryRespVO;
import org.dromara.mall.convert.promotion.bargain.BargainRecordConvert;
import org.dromara.mall.domain.promotion.BargainActivityDO;
import org.dromara.mall.domain.promotion.BargainRecordDO;
import org.dromara.mall.enums.promotion.bargain.BargainRecordStatusEnum;
import org.dromara.mall.service.promotion.BargainActivityService;
import org.dromara.mall.service.promotion.BargainHelpService;
import org.dromara.mall.service.promotion.BargainRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
@Tag(name = "用户 App - 砍价记录")
@RestController
@RequestMapping("/app/promotion/bargain-record")
@Validated
public class AppBargainRecordController {
@Resource
private BargainHelpService bargainHelpService;
@Resource
private BargainRecordService bargainRecordService;
@Resource
private BargainActivityService bargainActivityService;
@Resource
private TradeOrderApi tradeOrderApi;
@Resource
private MemberUserApi memberUserApi;
@Resource
private ProductSpuApi productSpuApi;
@GetMapping("/get-summary")
@Operation(summary = "获得砍价记录的概要信息", description = "用于小程序首页")
@PermitAll
public R<AppBargainRecordSummaryRespVO> getBargainRecordSummary() {
// 砍价成功的用户数量
Integer successUserCount = bargainRecordService.getBargainRecordUserCount(
BargainRecordStatusEnum.SUCCESS.getStatus());
if (successUserCount == 0) {
return R.ok(new AppBargainRecordSummaryRespVO().setSuccessUserCount(0)
.setSuccessList(Collections.emptyList()));
}
// 砍价成功的用户列表
List<BargainRecordDO> successList = bargainRecordService.getBargainRecordList(
BargainRecordStatusEnum.SUCCESS.getStatus(), 7);
List<BargainActivityDO> activityList = bargainActivityService.getBargainActivityList(
convertSet(successList, BargainRecordDO::getActivityId));
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(
convertSet(successList, BargainRecordDO::getUserId));
// 拼接返回
return R.ok(BargainRecordConvert.INSTANCE.convert(successUserCount, successList, activityList, userMap));
}
@GetMapping("/get-detail")
@Operation(summary = "获得砍价记录的明细")
@Parameters({
@Parameter(name = "id", description = "砍价记录编号", example = "111"), // 场景一:查看指定的砍价记录
@Parameter(name = "activityId", description = "砍价活动编号", example = "222") // 场景二:查看指定的砍价活动
})
@PermitAll
public R<AppBargainRecordDetailRespVO> getBargainRecordDetail(
@RequestParam(value = "id", required = false) Long id,
@RequestParam(value = "activityId", required = false) Long activityId) {
// 1. 查询砍价记录 + 砍价活动
Assert.isTrue(id != null || activityId != null, "砍价记录编号和活动编号不能同时为空");
BargainRecordDO record = id != null ? bargainRecordService.getBargainRecord(id)
: bargainRecordService.getLastBargainRecord(LoginHelper.getMemberId(), activityId);
if (activityId == null || record != null) {
activityId = record.getActivityId();
}
// 2. 查询助力记录
Long userId = LoginHelper.getMemberId();
Integer helpAction = getHelpAction(userId, record, activityId);
// 3. 如果是自己的订单,则查询订单信息
TradeOrderRespDTO order = record != null && record.getOrderId() != null && record.getUserId().equals(LoginHelper.getMemberId())
? tradeOrderApi.getOrder(record.getOrderId()) : null;
// TODO 继续查询别的字段
// 拼接返回
return R.ok(BargainRecordConvert.INSTANCE.convert02(record, helpAction, order));
}
private Integer getHelpAction(Long userId, BargainRecordDO record, Long activityId) {
// 0.1 如果没有活动,无法帮砍
if (activityId == null) {
return null;
}
// 0.2 如果是自己的砍价记录,无法帮砍
if (record != null && record.getUserId().equals(userId)) {
return null;
}
// 1. 判断是否已经助力
if (record != null
&& bargainHelpService.getBargainHelp(record.getId(), userId) != null) {
return AppBargainRecordDetailRespVO.HELP_ACTION_SUCCESS;
}
// 2. 判断是否满助力
BargainActivityDO activity = bargainActivityService.getBargainActivity(activityId);
if (activity != null
&& bargainHelpService.getBargainHelpCountByActivity(activityId, userId) >= activity.getBargainCount()) {
return AppBargainRecordDetailRespVO.HELP_ACTION_FULL;
}
// 3. 允许助力
return AppBargainRecordDetailRespVO.HELP_ACTION_NONE;
}
@GetMapping("/page")
@Operation(summary = "获得砍价记录的分页")
public R<PageResult<AppBargainRecordRespVO>> getBargainRecordPage(PageParam pageParam) {
PageResult<BargainRecordDO> pageResult = bargainRecordService.getBargainRecordPage(LoginHelper.getMemberId(), pageParam);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接数据
List<BargainActivityDO> activityList = bargainActivityService.getBargainActivityList(
convertSet(pageResult.getList(), BargainRecordDO::getActivityId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(
convertSet(pageResult.getList(), BargainRecordDO::getSpuId));
List<TradeOrderRespDTO> orderList = tradeOrderApi.getOrderList(
convertSet(pageResult.getList(), BargainRecordDO::getOrderId));
return R.ok(BargainRecordConvert.INSTANCE.convertPage02(pageResult, activityList, spuList, orderList));
}
@PostMapping("/create")
@Operation(summary = "创建砍价记录", description = "参与砍价活动")
public R<Long> createBargainRecord(@RequestBody AppBargainRecordCreateReqVO reqVO) {
Long recordId = bargainRecordService.createBargainRecord(LoginHelper.getMemberId(), reqVO);
return R.ok(recordId);
}
}
package org.dromara.mall.controller.promotion.app.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 砍价活动的明细 Response VO")
@Data
@Accessors(chain = true)
public class AppBargainActivityDetailRespVO {
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
private String name;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long skuId;
@Schema(description = "商品价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer price;
@Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "我要吃西红柿")
private String description;
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer stock;
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // 从 SPU 的 picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // 从 SPU 的 marketPrice 读取
private Integer marketPrice;
@Schema(description = "砍价起始价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
private Integer bargainFirstPrice;
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer bargainMinPrice;
@Schema(description = "砍价成功数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer successUserCount;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 砍价活动 Response VO")
@Data
@Accessors(chain = true)
public class AppBargainActivityRespVO {
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
private String name;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long skuId;
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer stock;
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "4096")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
example = "50")
private Integer marketPrice;
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer bargainMinPrice;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.help;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "用户 App - 砍价助力的创建 Request VO")
@Data
public class AppBargainHelpCreateReqVO {
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "砍价记录编号不能为空")
private Long recordId;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.help;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 砍价助力 Response VO")
@Data
@Accessors(chain = true)
public class AppBargainHelpRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userId;
@Schema(description = "助力用户的昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String nickname;
@Schema(description = "助力用户的头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String avatar;
@Schema(description = "助力用户的砍价金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer reducePrice;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "用户 App - 砍价记录的创建 Request VO")
@Data
public class AppBargainRecordCreateReqVO {
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "砍价活动编号不能为空")
private Long activityId;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 砍价记录的明细 Response VO")
@Data
public class AppBargainRecordDetailRespVO {
public static final int HELP_ACTION_NONE = 1; // 帮砍动作 - 未帮砍,可以帮砍
public static final int HELP_ACTION_FULL = 2; // 帮砍动作 - 未帮砍,无法帮砍(可帮砍次数已满)
public static final int HELP_ACTION_SUCCESS = 3; // 帮砍动作 - 已帮砍
// ========== 砍价记录 ==========
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
private Long userId;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long skuId;
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
private Long activityId;
@Schema(description = "砍价起始价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
private Integer bargainFirstPrice;
@Schema(description = "当前砍价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
private Integer bargainPrice;
@Schema(description = "砍价记录状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
// ========== 订单相关 ========== 注意:只有是自己的砍价记录,才会返回,保证隐私性
@Schema(description = "订单编号", example = "1024")
private Long orderId;
@Schema(description = "支付状态", example = "true")
private Boolean payStatus;
@Schema(description = "支付订单编号", example = "1024")
private Long payOrderId;
// ========== 助力记录 ==========
private Integer helpAction;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 砍价记录的 Response VO")
@Data
@Accessors(chain = true)
public class AppBargainRecordRespVO {
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long skuId;
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long activityId;
@Schema(description = "砍价记录状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "当前价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "102")
private Integer bargainPrice;
// ========== 活动相关 ==========
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
private String activityName;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
// ========== 订单相关 ==========
@Schema(description = "订单编号", example = "1024")
private Long orderId;
@Schema(description = "支付状态", example = "true")
private Boolean payStatus;
@Schema(description = "支付订单编号", example = "1024")
private Long payOrderId;
}
package org.dromara.mall.controller.promotion.app.bargain.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Schema(description = "用户 App - 砍价记录的简要概括 Response VO")
@Data
@Accessors(chain = true)
public class AppBargainRecordSummaryRespVO {
@Schema(description = "砍价用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer successUserCount;
@Schema(description = "成功砍价的记录", requiredMode = Schema.RequiredMode.REQUIRED) // 只返回最近的 7 个
private List<Record> successList;
@Schema(description = "成功砍价记录")
@Data
public static class Record {
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王**")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "天蚕土豆")
private String activityName;
}
}
package org.dromara.mall.controller.promotion.app.combination;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.app.combination.vo.activity.AppCombinationActivityDetailRespVO;
import org.dromara.mall.controller.promotion.app.combination.vo.activity.AppCombinationActivityRespVO;
import org.dromara.mall.convert.promotion.combination.CombinationActivityConvert;
import org.dromara.mall.domain.promotion.CombinationActivityDO;
import org.dromara.mall.domain.promotion.CombinationProductDO;
import org.dromara.mall.service.promotion.CombinationActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
@Tag(name = "用户 APP - 拼团活动")
@RestController
@RequestMapping("/app/promotion/combination-activity")
@Validated
public class AppCombinationActivityController {
@Resource
private CombinationActivityService activityService;
@Resource
private ProductSpuApi spuApi;
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
@PermitAll
public R<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
PageResult<CombinationActivityDO> pageResult = activityService.getCombinationActivityPage(pageParam);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 拼接返回
List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
convertList(pageResult.getList(), CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), CombinationActivityDO::getSpuId));
return R.ok(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得拼团活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
@PermitAll
public R<List<AppCombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return R.ok(Collections.emptyList());
}
// 2. 拼接返回
List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
convertList(activityList, CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
return R.ok(CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));
}
@GetMapping("/get-detail")
@Operation(summary = "获得拼团活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024")
@PermitAll
public R<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) {
// 1. 获取活动
CombinationActivityDO activity = activityService.getCombinationActivity(id);
if (activity == null
|| ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
return R.ok(null);
}
// 2. 获取活动商品
List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(activity.getId());
return R.ok(CombinationActivityConvert.INSTANCE.convert3(activity, products));
}
}
package org.dromara.mall.controller.promotion.app.combination;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.controller.promotion.app.combination.vo.record.AppCombinationRecordDetailRespVO;
import org.dromara.mall.controller.promotion.app.combination.vo.record.AppCombinationRecordPageReqVO;
import org.dromara.mall.controller.promotion.app.combination.vo.record.AppCombinationRecordRespVO;
import org.dromara.mall.controller.promotion.app.combination.vo.record.AppCombinationRecordSummaryRespVO;
import org.dromara.mall.convert.promotion.combination.CombinationActivityConvert;
import org.dromara.mall.domain.promotion.CombinationRecordDO;
import org.dromara.mall.service.promotion.CombinationRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
@Tag(name = "用户 APP - 拼团活动")
@RestController
@RequestMapping("/app/promotion/combination-record")
@Validated
public class AppCombinationRecordController {
@Resource
private CombinationRecordService combinationRecordService;
@GetMapping("/get-summary")
@Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页")
@PermitAll
public R<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() {
AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
// 1. 获得拼团参与用户数量
Long userCount = combinationRecordService.getCombinationUserCount();
if (userCount == 0) {
summary.setAvatars(Collections.emptyList());
summary.setUserCount(userCount);
return R.ok(summary);
}
summary.setUserCount(userCount);
// 2. 获得拼团记录头像
List<CombinationRecordDO> records = combinationRecordService.getLatestCombinationRecordList(
AppCombinationRecordSummaryRespVO.AVATAR_COUNT);
summary.setAvatars(convertList(records, CombinationRecordDO::getAvatar));
return R.ok(summary);
}
@GetMapping("/get-head-list")
@Operation(summary = "获得最近 n 条拼团记录(团长发起的)")
@Parameters({
@Parameter(name = "activityId", description = "拼团活动编号"),
@Parameter(name = "status", description = "拼团状态"), // 对应 CombinationRecordStatusEnum 枚举
@Parameter(name = "count", description = "数量")
})
@PermitAll
public R<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList(
@RequestParam(value = "activityId", required = false) Long activityId,
@RequestParam("status") Integer status,
@RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) {
List<CombinationRecordDO> list = combinationRecordService.getHeadCombinationRecordList(activityId, status, count);
return R.ok(BeanUtils.toBean(list, AppCombinationRecordRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得我的拼团记录分页")
public R<PageResult<AppCombinationRecordRespVO>> getCombinationRecordPage(
@Valid AppCombinationRecordPageReqVO pageReqVO) {
PageResult<CombinationRecordDO> pageResult = combinationRecordService.getCombinationRecordPage(
LoginHelper.getMemberId(), pageReqVO);
return R.ok(org.dromara.common.mybatis.util.BeanUtils.toBean(pageResult, AppCombinationRecordRespVO.class));
}
@GetMapping("/get-detail")
@Operation(summary = "获得拼团记录明细")
@Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
@PermitAll
public R<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) {
// 1. 查找这条拼团记录
CombinationRecordDO record = combinationRecordService.getCombinationRecordById(id);
if (record == null) {
return R.ok(null);
}
// 2. 查找该拼团的参团记录
CombinationRecordDO headRecord;
List<CombinationRecordDO> memberRecords;
if (Objects.equals(record.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)) { // 情况一:团长
headRecord = record;
memberRecords = combinationRecordService.getCombinationRecordListByHeadId(record.getId());
} else { // 情况二:团员
headRecord = combinationRecordService.getCombinationRecordById(record.getHeadId());
memberRecords = combinationRecordService.getCombinationRecordListByHeadId(headRecord.getId());
}
// 3. 拼接数据
return R.ok(CombinationActivityConvert.INSTANCE.convert(LoginHelper.getMemberId(), headRecord, memberRecords));
}
}
package org.dromara.mall.controller.promotion.app.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 拼团活动明细 Response VO")
@Data
@Accessors(chain = true)
public class AppCombinationActivityDetailRespVO {
@Schema(description = "拼团活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "拼团活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大拼团")
private String name;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "拼团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
private Integer userSize;
@Schema(description = "成功的拼团数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer successCount;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "总共限购数量", example = "10")
private Integer totalLimitCount;
@Schema(description = "单次限购数量", example = "5")
private Integer singleLimitCount;
@Schema(description = "商品信息数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Product> products;
@Schema(description = "商品信息")
@Data
public static class Product {
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
private Long skuId;
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer combinationPrice;
}
}
package org.dromara.mall.controller.promotion.app.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@Schema(description = "用户 App - 拼团活动 Response VO")
@Data
@Accessors(chain = true)
public class AppCombinationActivityRespVO {
@Schema(description = "拼团活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "拼团活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大拼团")
private String name;
@Schema(description = "拼团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
private Integer userSize;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
private String spuName; // 从 SPU 的 name 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
private String picUrl; // 从 SPU 的 picUrl 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer marketPrice; // 从 SPU 的 marketPrice 读取
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer combinationPrice; // 从 products 获取最小 price 读取
}
package org.dromara.mall.controller.promotion.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Schema(description = "用户 App - 拼团记录详细 Response VO")
@Data
@Accessors(chain = true)
public class AppCombinationRecordDetailRespVO {
@Schema(description = "团长的拼团记录", requiredMode = Schema.RequiredMode.REQUIRED)
private AppCombinationRecordRespVO headRecord;
@Schema(description = "成员的拼团记录", requiredMode = Schema.RequiredMode.REQUIRED)
private List<AppCombinationRecordRespVO> memberRecords;
@Schema(description = "当前用户参团记录对应的订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long orderId;
}
package org.dromara.mall.controller.promotion.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.mall.enums.promotion.combination.CombinationRecordStatusEnum;
@Schema(description = "用户 App - 拼团记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppCombinationRecordPageReqVO extends PageParam {
@Schema(description = "拼团状态", example = "1")
@InEnum(value = CombinationRecordStatusEnum.class, message = "拼团状态必须是 {value}")
private Integer status;
}
package org.dromara.mall.controller.promotion.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 拼团记录 Response VO")
@Data
public class AppCombinationRecordRespVO {
@Schema(description = "拼团记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "拼团活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long activityId;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String nickname;
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String avatar;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expireTime;
@Schema(description = "可参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer userSize;
@Schema(description = "已参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Integer userCount;
@Schema(description = "拼团状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long orderId;
@Schema(description = "商品名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是大黄豆")
private String spuName;
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "购买的商品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer count;
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer combinationPrice;
}
package org.dromara.mall.controller.promotion.app.combination.vo.record;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用户 App - 拼团记录的简要概括 Response VO")
@Data
public class AppCombinationRecordSummaryRespVO {
/**
* 加载 {@link #avatars} 的数量
*/
public static final Integer AVATAR_COUNT = 7;
@Schema(description = "拼团用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userCount;
@Schema(description = "拼团用户头像列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> avatars;
}
package org.dromara.mall.controller.promotion.app.coupon;
import cn.hutool.core.collection.CollUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.common.mybatis.util.BeanUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.controller.promotion.app.coupon.vo.coupon.AppCouponPageReqVO;
import org.dromara.mall.controller.promotion.app.coupon.vo.coupon.AppCouponRespVO;
import org.dromara.mall.controller.promotion.app.coupon.vo.coupon.AppCouponTakeReqVO;
import org.dromara.mall.convert.promotion.coupon.CouponConvert;
import org.dromara.mall.domain.promotion.CouponDO;
import org.dromara.mall.domain.promotion.CouponTemplateDO;
import org.dromara.mall.enums.promotion.coupon.CouponTakeTypeEnum;
import org.dromara.mall.service.promotion.CouponService;
import org.dromara.mall.service.promotion.CouponTemplateService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
@Tag(name = "用户 App - 优惠劵")
@RestController
@RequestMapping("/app/promotion/coupon")
@Validated
public class AppCouponController {
@Resource
private CouponService couponService;
@Resource
private CouponTemplateService couponTemplateService;
@PostMapping("/take")
@Operation(summary = "领取优惠劵")
@Parameter(name = "templateId", description = "优惠券模板编号", required = true, example = "1024")
public R<Boolean> takeCoupon(@Valid @RequestBody AppCouponTakeReqVO reqVO) {
// 1. 领取优惠劵
Long userId = LoginHelper.getMemberId();
couponService.takeCoupon(reqVO.getTemplateId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
// 2. 检查是否可以继续领取
CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(reqVO.getTemplateId());
boolean canTakeAgain = true;
if (couponTemplate.getTakeLimitCount() != null && couponTemplate.getTakeLimitCount() > 0) {
Integer takeCount = couponService.getTakeCount(reqVO.getTemplateId(), userId);
canTakeAgain = takeCount < couponTemplate.getTakeLimitCount();
}
return R.ok(canTakeAgain);
}
@GetMapping("/page")
@Operation(summary = "我的优惠劵列表")
public R<PageResult<AppCouponRespVO>> getCouponPage(AppCouponPageReqVO pageReqVO) {
PageResult<CouponDO> pageResult = couponService.getCouponPage(
CouponConvert.INSTANCE.convert(pageReqVO, Collections.singleton(LoginHelper.getMemberId())));
return R.ok(BeanUtils.toBean(pageResult, AppCouponRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得优惠劵")
@Parameter(name = "id", description = "优惠劵编号", required = true, example = "1024")
public R<AppCouponRespVO> getCoupon(@RequestParam("id") Long id) {
CouponDO coupon = couponService.getCoupon(LoginHelper.getMemberId(), id);
return R.ok(BeanUtils.toBean(coupon, AppCouponRespVO.class));
}
@GetMapping(value = "/get-unused-count")
@Operation(summary = "获得未使用的优惠劵数量")
public R<Long> getUnusedCouponCount() {
return R.ok(couponService.getUnusedCouponCount(LoginHelper.getMemberId()));
}
}
package org.dromara.mall.controller.promotion.app.coupon;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.ObjectUtils;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.common.mybatis.util.BeanUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.app.coupon.vo.template.AppCouponTemplatePageReqVO;
import org.dromara.mall.controller.promotion.app.coupon.vo.template.AppCouponTemplateRespVO;
import org.dromara.mall.convert.promotion.coupon.CouponTemplateConvert;
import org.dromara.mall.domain.promotion.CouponTemplateDO;
import org.dromara.mall.enums.promotion.common.PromotionProductScopeEnum;
import org.dromara.mall.enums.promotion.coupon.CouponTakeTypeEnum;
import org.dromara.mall.service.promotion.CouponService;
import org.dromara.mall.service.promotion.CouponTemplateService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static java.util.Collections.singletonList;
@Tag(name = "用户 App - 优惠劵模板")
@RestController
@RequestMapping("/app/promotion/coupon-template")
@Validated
public class AppCouponTemplateController {
@Resource
private CouponTemplateService couponTemplateService;
@Resource
private CouponService couponService;
@Resource
private ProductSpuApi productSpuApi;
@GetMapping("/get")
@Operation(summary = "获得优惠劵模版")
@Parameter(name = "id", description = "优惠券模板编号", required = true, example = "1024")
@PermitAll
public R<AppCouponTemplateRespVO> getCouponTemplate(Long id) {
CouponTemplateDO template = couponTemplateService.getCouponTemplate(id);
if (template == null) {
return R.ok(null);
}
// 处理是否可领取
Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(LoginHelper.getMemberId(), singletonList(template));
return R.ok(BeanUtils.toBean(template, AppCouponTemplateRespVO.class)
.setCanTake(canCanTakeMap.get(template.getId())));
}
@GetMapping("/list")
@Operation(summary = "获得优惠劵模版列表")
@Parameters({
@Parameter(name = "spuId", description = "商品 SPU 编号"), // 目前主要给商品详情使用
@Parameter(name = "productScope", description = "使用类型"),
@Parameter(name = "count", description = "数量", required = true)
})
@PermitAll
public R<List<AppCouponTemplateRespVO>> getCouponTemplateList(
@RequestParam(value = "spuId", required = false) Long spuId,
@RequestParam(value = "productScope", required = false) Integer productScope,
@RequestParam(value = "count", required = false, defaultValue = "10") Integer count) {
// 1.1 处理查询条件:商品范围编号
Long productScopeValue = getProductScopeValue(productScope, spuId);
// 1.2 处理查询条件:领取方式 = 直接领取
List<Integer> canTakeTypes = singletonList(CouponTakeTypeEnum.USER.getType());
// 2. 查询
List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(canTakeTypes, productScope,
productScopeValue, count);
// 3.1 领取数量
Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(LoginHelper.getMemberId(), list);
// 3.2 拼接返回
return R.ok(CouponTemplateConvert.INSTANCE.convertAppList(list, canCanTakeMap));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得优惠劵模版列表")
@Parameter(name = "ids", description = "优惠券模板编号列表")
@PermitAll
public R<List<AppCouponTemplateRespVO>> getCouponTemplateList(
@RequestParam(value = "ids", required = false) Set<Long> ids) {
// 1. 查询
List<CouponTemplateDO> list = couponTemplateService.getCouponTemplateList(ids);
// 2.1 领取数量
Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(LoginHelper.getMemberId(), list);
// 2.2 拼接返回
return R.ok(CouponTemplateConvert.INSTANCE.convertAppList(list, canCanTakeMap));
}
@GetMapping("/page")
@Operation(summary = "获得优惠劵模版分页")
@PermitAll
public R<PageResult<AppCouponTemplateRespVO>> getCouponTemplatePage(AppCouponTemplatePageReqVO pageReqVO) {
// 1.1 处理查询条件:商品范围编号
Long productScopeValue = getProductScopeValue(pageReqVO.getProductScope(), pageReqVO.getSpuId());
// 1.2 处理查询条件:领取方式 = 直接领取
List<Integer> canTakeTypes = singletonList(CouponTakeTypeEnum.USER.getType());
// 2. 分页查询
PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(
CouponTemplateConvert.INSTANCE.convert(pageReqVO, canTakeTypes, pageReqVO.getProductScope(), productScopeValue));
// 3.1 领取数量
Map<Long, Boolean> canCanTakeMap = couponService.getUserCanCanTakeMap(LoginHelper.getMemberId(), pageResult.getList());
// 3.2 拼接返回
return R.ok(CouponTemplateConvert.INSTANCE.convertAppPage(pageResult, canCanTakeMap));
}
/**
* 获得商品的使用范围编号
*
* @param productScope 商品范围
* @param spuId 商品 SPU 编号
* @return 商品范围编号
*/
private Long getProductScopeValue(Integer productScope, Long spuId) {
// 通用券:没有商品范围
if (ObjectUtils.equalsAny(productScope, PromotionProductScopeEnum.ALL.getScope(), null)) {
return null;
}
// 品类券:查询商品的品类编号
if (Objects.equals(productScope, PromotionProductScopeEnum.CATEGORY.getScope()) && spuId != null) {
ProductSpuRespDTO spu = productSpuApi.getSpu(spuId);
return spu != null ? spu.getCategoryId() : null;
}
// 商品劵:直接返回
return spuId;
}
}
package org.dromara.mall.controller.promotion.app.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.mall.enums.promotion.coupon.CouponStatusEnum;
@Schema(description = "用户 App - 优惠劵分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppCouponPageReqVO extends PageParam {
@Schema(description = "优惠劵状态", example = "1")
@InEnum(value = CouponStatusEnum.class, message = "优惠劵状态,必须是 {value}")
private Integer status;
}
package org.dromara.mall.controller.promotion.app.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 优惠劵 Response VO")
@Data
public class AppCouponRespVO {
@Schema(description = "优惠劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
private String name;
@Schema(description = "优惠劵状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 参见 CouponStatusEnum 枚举
private Integer status;
@Schema(description = "是否设置满多少金额可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 单位:分;0 - 不限制
private Integer usePrice;
@Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer productScope;
@Schema(description = "商品范围编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private List<Long> productScopeValues;
@Schema(description = "固定日期 - 生效开始时间")
private LocalDateTime validStartTime;
@Schema(description = "固定日期 - 生效结束时间")
private LocalDateTime validEndTime;
@Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer discountType;
@Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
private Integer discountPercent;
@Schema(description = "优惠金额", example = "10")
private Integer discountPrice;
@Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
private Integer discountLimitPrice;
}
package org.dromara.mall.controller.promotion.app.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "用户 App - 优惠劵领取 Request VO")
@Data
public class AppCouponTakeReqVO {
@Schema(description = "优惠劵模板编号", example = "1")
@NotNull(message = "优惠劵模板编号不能为空")
private Long templateId;
}
package org.dromara.mall.controller.promotion.app.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.dromara.common.mall.validation.InEnum;
import org.dromara.common.mybatis.core.page.PageParam;
import org.dromara.mall.enums.promotion.common.PromotionProductScopeEnum;
@Schema(description = "用户 App - 优惠劵模板分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppCouponTemplatePageReqVO extends PageParam {
@Schema(description = "商品范围", example = "1")
@InEnum(value = PromotionProductScopeEnum.class, message = "商品范围,必须是 {value}")
private Integer productScope;
@Schema(description = "商品标号", example = "1")
private Long spuId;
}
package org.dromara.mall.controller.promotion.app.coupon.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 优惠劵模板 Response VO")
@Data
@Accessors(chain = true)
public class AppCouponTemplateRespVO {
@Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
private String name;
@Schema(description = "优惠券说明", example = "优惠券使用说明")
private String description;
@Schema(description = "每人限领个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") // -1 - 则表示不限制
private Integer takeLimitCount;
@Schema(description = "是否设置满多少金额可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 单位:分;0 - 不限制
private Integer usePrice;
@Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer productScope;
@Schema(description = "商品范围编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private List<Long> productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer validityType;
@Schema(description = "固定日期 - 生效开始时间")
private LocalDateTime validStartTime;
@Schema(description = "固定日期 - 生效结束时间")
private LocalDateTime validEndTime;
@Schema(description = "领取日期 - 开始天数")
@Min(value = 0L, message = "开始天数必须大于 0")
private Integer fixedStartTerm;
@Schema(description = "领取日期 - 结束天数")
@Min(value = 1L, message = "开始天数必须大于 1")
private Integer fixedEndTerm;
@Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer discountType;
@Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
private Integer discountPercent;
@Schema(description = "优惠金额", example = "10")
@Min(value = 0, message = "优惠金额需要大于等于 0")
private Integer discountPrice;
@Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
private Integer discountLimitPrice;
// ========== 用户相关字段 ==========
@Schema(description = "是否可以领取", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean canTake;
}
package org.dromara.mall.controller.promotion.app.diy;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.mall.controller.promotion.app.diy.vo.AppDiyPagePropertyRespVO;
import org.dromara.mall.domain.promotion.DiyPageDO;
import org.dromara.mall.service.promotion.DiyPageService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "用户 APP - 装修页面")
@RestController
@RequestMapping("/app/promotion/diy-page")
@Validated
public class AppDiyPageController {
@Resource
private DiyPageService diyPageService;
@GetMapping("/get")
@Operation(summary = "获得装修页面")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PermitAll
public R<AppDiyPagePropertyRespVO> getDiyPage(@RequestParam("id") Long id) {
DiyPageDO diyPage = diyPageService.getDiyPage(id);
return R.ok(BeanUtils.toBean(diyPage, AppDiyPagePropertyRespVO.class));
}
}
package org.dromara.mall.controller.promotion.app.diy;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.mall.controller.promotion.app.diy.vo.AppDiyTemplatePropertyRespVO;
import org.dromara.mall.convert.promotion.diy.DiyTemplateConvert;
import org.dromara.mall.domain.promotion.DiyPageDO;
import org.dromara.mall.domain.promotion.DiyTemplateDO;
import org.dromara.mall.enums.promotion.diy.DiyPageEnum;
import org.dromara.mall.service.promotion.DiyPageService;
import org.dromara.mall.service.promotion.DiyTemplateService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static org.dromara.common.mall.util.collection.CollectionUtils.findFirst;
@Tag(name = "用户 APP - 装修模板")
@RestController
@RequestMapping("/app/promotion/diy-template")
@Validated
public class AppDiyTemplateController {
@Resource
private DiyTemplateService diyTemplateService;
@Resource
private DiyPageService diyPageService;
// TODO @疯狂:要不要把 used 和 get 接口合并哈;不传递 id,直接拿默认;
@GetMapping("/used")
@Operation(summary = "使用中的装修模板")
@PermitAll
public R<AppDiyTemplatePropertyRespVO> getUsedDiyTemplate() {
DiyTemplateDO diyTemplate = diyTemplateService.getUsedDiyTemplate();
return R.ok(buildVo(diyTemplate));
}
@GetMapping("/get")
@Operation(summary = "获得装修模板")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PermitAll
public R<AppDiyTemplatePropertyRespVO> getDiyTemplate(@RequestParam("id") Long id) {
DiyTemplateDO diyTemplate = diyTemplateService.getDiyTemplate(id);
return R.ok(buildVo(diyTemplate));
}
private AppDiyTemplatePropertyRespVO buildVo(DiyTemplateDO diyTemplate) {
if (diyTemplate == null) {
return null;
}
// 查询模板下的页面
List<DiyPageDO> pages = diyPageService.getDiyPageByTemplateId(diyTemplate.getId());
String home = findFirst(pages, page -> DiyPageEnum.INDEX.getName().equals(page.getName()), DiyPageDO::getProperty);
String user = findFirst(pages, page -> DiyPageEnum.MY.getName().equals(page.getName()), DiyPageDO::getProperty);
// 拼接返回
return DiyTemplateConvert.INSTANCE.convertPropertyVo2(diyTemplate, home, user);
}
}
package org.dromara.mall.controller.promotion.app.diy.vo;
import com.fasterxml.jackson.annotation.JsonRawValue;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
@Schema(description = "用户 App - 装修页面属性 Response VO")
@Data
@ToString(callSuper = true)
public class AppDiyPagePropertyRespVO {
@Schema(description = "装修页面编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
private Long id;
@Schema(description = "页面名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
private String name;
@Schema(description = "页面属性", example = "[]")
@JsonRawValue
private String property;
}
package org.dromara.mall.controller.promotion.app.diy.vo;
import com.fasterxml.jackson.annotation.JsonRawValue;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
@Schema(description = "用户 App - 装修模板属性 Response VO")
@Data
@ToString(callSuper = true)
public class AppDiyTemplatePropertyRespVO {
@Schema(description = "装修模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31209")
private Long id;
@Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "默认主题")
private String name;
@Schema(description = "模板属性", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
@JsonRawValue
private String property;
@Schema(description = "首页", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
@JsonRawValue
private String home;
@Schema(description = "我的", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
@JsonRawValue
private String user;
}
package org.dromara.mall.controller.promotion.app.kefu;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.UserTypeEnum;
import org.dromara.common.mybatis.util.BeanUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.api.dto.system.AdminUserRespDTO;
import org.dromara.mall.api.service.system.AdminUserApi;
import org.dromara.mall.controller.promotion.admin.kefu.vo.message.KeFuMessageRespVO;
import org.dromara.mall.controller.promotion.app.kefu.vo.message.AppKeFuMessagePageReqVO;
import org.dromara.mall.controller.promotion.app.kefu.vo.message.AppKeFuMessageSendReqVO;
import org.dromara.mall.domain.promotion.KefuMessageDO;
import org.dromara.mall.service.promotion.KeFuMessageService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertSet;
import static org.dromara.common.mall.util.collection.CollectionUtils.filterList;
import static org.dromara.common.mall.util.collection.MapUtils.findAndThen;
@Tag(name = "用户 APP - 客服消息")
@RestController
@RequestMapping("/app/promotion/kefu-message")
@Validated
public class AppKeFuMessageController {
@Resource
private KeFuMessageService kefuMessageService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/send")
@Operation(summary = "发送客服消息")
public R<Long> sendKefuMessage(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) {
sendReqVO.setSenderId(LoginHelper.getMemberId()).setSenderType(UserTypeEnum.MEMBER.getValue()); // 设置用户编号和类型
return R.ok(kefuMessageService.sendKefuMessage(sendReqVO));
}
@PutMapping("/update-read-status")
@Operation(summary = "更新客服消息已读状态")
@Parameter(name = "conversationId", description = "会话编号", required = true)
public R<Boolean> updateKefuMessageReadStatus(@RequestParam("conversationId") Long conversationId) {
kefuMessageService.updateKeFuMessageReadStatus(conversationId, LoginHelper.getMemberId(), UserTypeEnum.MEMBER.getValue());
return R.ok(true);
}
@GetMapping("/list")
@Operation(summary = "获得客服消息列表")
public R<List<KeFuMessageRespVO>> getKefuMessageList(@Valid AppKeFuMessagePageReqVO pageReqVO) {
List<KefuMessageDO> list = kefuMessageService.getKeFuMessageList(pageReqVO, LoginHelper.getMemberId());
// 拼接数据
List<KeFuMessageRespVO> result = BeanUtils.toBean(list, KeFuMessageRespVO.class);
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result,
item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId));
result.forEach(item -> findAndThen(userMap, item.getSenderId(), user -> item.setSenderAvatar(user.getAvatar())));
return R.ok(result);
}
}
package org.dromara.mall.controller.promotion.app.kefu.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static org.dromara.common.mall.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "用户 App - 客服消息 Request VO")
@Data
public class AppKeFuMessagePageReqVO {
private static final Integer LIMIT = 10;
@Schema(description = "会话编号", example = "12580")
private Long conversationId;
@Schema(description = "发送时间", example = "2024-03-27 12:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime createTime;
@Schema(description = "每次查询条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "每次查询条数不能为空")
@Min(value = 1, message = "每次查询条数最小值为 1")
@Max(value = 100, message = "每次查询最大值为 100")
private Integer limit = LIMIT;
}
package org.dromara.mall.controller.promotion.app.kefu.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 App - 客服消息 Response VO")
@Data
public class AppKeFuMessageRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23202")
private Long id;
@Schema(description = "会话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12580")
private Long conversationId;
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571")
private Long senderId;
@Schema(description = "发送人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer senderType;
@Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29124")
private Long receiverId;
@Schema(description = "接收人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer receiverType;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer contentType;
@Schema(description = "消息", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Boolean readStatus;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}
package org.dromara.mall.controller.promotion.app.kefu.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
@Schema(description = "用户 App - 发送客服消息 Request VO")
@Data
@Accessors(chain = true)
public class AppKeFuMessageSendReqVO {
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "消息类型不能为空")
private Integer contentType;
@Schema(description = "消息", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "消息不能为空")
private String content;
// ========== 后端设置的参数,前端无需传递 ==========
@Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24571", hidden = true)
private Long senderId;
@Schema(description = "发送人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1", hidden = true)
private Integer senderType;
}
package org.dromara.mall.controller.promotion.app.point;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.common.mybatis.util.BeanUtils;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.admin.point.vo.activity.PointActivityPageReqVO;
import org.dromara.mall.controller.promotion.app.point.vo.AppPointActivityDetailRespVO;
import org.dromara.mall.controller.promotion.app.point.vo.AppPointActivityPageReqVO;
import org.dromara.mall.controller.promotion.app.point.vo.AppPointActivityRespVO;
import org.dromara.mall.domain.promotion.PointActivityDO;
import org.dromara.mall.domain.promotion.PointProductDO;
import org.dromara.mall.service.promotion.PointActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.dromara.common.mall.util.collection.CollectionUtils.*;
import static org.dromara.common.mall.util.collection.MapUtils.findAndThen;
@Tag(name = "用户 App - 积分商城活动")
@RestController
@RequestMapping("/app/promotion/point-activity")
@Validated
public class AppPointActivityController {
@Resource
private PointActivityService pointActivityService;
@Resource
private ProductSpuApi productSpuApi;
@GetMapping("/page")
@Operation(summary = "获得积分商城活动分页")
@PermitAll
public R<PageResult<AppPointActivityRespVO>> getPointActivityPage(AppPointActivityPageReqVO pageReqVO) {
// 1. 查询满足当前阶段的活动
PageResult<PointActivityDO> pageResult = pointActivityService.getPointActivityPage(
BeanUtils.toBean(pageReqVO, PointActivityPageReqVO.class));
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
// 2. 拼接数据
List<AppPointActivityRespVO> resultList = buildAppPointActivityRespVOList(pageResult.getList());
return R.ok(new PageResult<>(resultList, pageResult.getTotal()));
}
@GetMapping("/get-detail")
@Operation(summary = "获得积分商城活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024")
@PermitAll
public R<AppPointActivityDetailRespVO> getPointActivity(@RequestParam("id") Long id) {
// 1. 获取活动
PointActivityDO activity = pointActivityService.getPointActivity(id);
if (activity == null
|| ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
return R.ok(null);
}
// 2. 拼接数据
List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(Collections.singletonList(id));
PointProductDO minProduct = getMinObject(products, PointProductDO::getPoint);
assert minProduct != null;
AppPointActivityDetailRespVO respVO = BeanUtils.toBean(activity, AppPointActivityDetailRespVO.class)
.setProducts(BeanUtils.toBean(products, AppPointActivityDetailRespVO.Product.class))
.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice());
return R.ok(respVO);
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得积分商城活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
@PermitAll
public R<List<AppPointActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<PointActivityDO> activityList = pointActivityService.getPointActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return R.ok(Collections.emptyList());
}
// 2. 拼接返回
List<AppPointActivityRespVO> result = buildAppPointActivityRespVOList(activityList);
return R.ok(result);
}
private List<AppPointActivityRespVO> buildAppPointActivityRespVOList(List<PointActivityDO> activityList) {
List<PointProductDO> products = pointActivityService.getPointProductListByActivityIds(
convertSet(activityList, PointActivityDO::getId));
Map<Long, List<PointProductDO>> productsMap = convertMultiMap(products, PointProductDO::getActivityId);
Map<Long, ProductSpuRespDTO> spuMap = productSpuApi.getSpusMap(
convertSet(activityList, PointActivityDO::getSpuId));
List<AppPointActivityRespVO> result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class);
result.forEach(activity -> {
// 设置 product 信息
PointProductDO minProduct = getMinObject(productsMap.get(activity.getId()), PointProductDO::getPoint);
assert minProduct != null;
activity.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice());
findAndThen(spuMap, activity.getSpuId(),
spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
});
return result;
}
}
package org.dromara.mall.controller.promotion.app.point.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Schema(description = "用户 App - 积分商城活动的详细 Response VO")
@Data
@Accessors(chain = true)
public class AppPointActivityDetailRespVO {
@Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373")
private Long id;
@Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509")
private Long spuId;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer status;
@Schema(description = "积分商城活动库存(剩余库存积分兑换时扣减)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer stock;
@Schema(description = "积分商城活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer totalStock;
@Schema(description = "备注", example = "你说的对")
private String remark;
@Schema(description = "商品信息数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Product> products;
//======================= 显示所需兑换积分最少的 SKU 信息 =======================
@Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer point;
@Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860")
private Integer price;
@Schema(description = "商品信息")
@Data
public static class Product {
@Schema(description = "积分商城商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31718")
private Long id;
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2736")
private Long skuId;
@Schema(description = "可兑换数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3926")
private Integer count;
@Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer point;
@Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860")
private Integer price;
@Schema(description = "积分商城商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer stock;
}
}
package org.dromara.mall.controller.promotion.app.point.vo;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "用户 App - 积分商城活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppPointActivityPageReqVO extends PageParam {
}
package org.dromara.mall.controller.promotion.app.point.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@Schema(description = "用户 App - 积分商城活动 Response VO")
@Data
@Accessors(chain = true)
public class AppPointActivityRespVO {
@Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373")
@ExcelProperty("积分商城活动编号")
private Long id;
@Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509")
@ExcelProperty("积分商城活动商品")
private Long spuId;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("活动状态")
private Integer status;
@Schema(description = "积分商城活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("积分商城活动库存")
private Integer stock; // 剩余库存积分兑换时扣减
@Schema(description = "积分商城活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("积分商城活动总库存")
private Integer totalStock;
// ========== 商品字段 ==========
@Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
example = "618大促")
private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
example = "50")
private Integer marketPrice;
//======================= 显示所需兑换积分最少的 sku 信息 =======================
@Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer point;
@Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860")
private Integer price;
}
package org.dromara.mall.controller.promotion.app.reward;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.util.object.BeanUtils;
import org.dromara.mall.controller.promotion.app.reward.vo.AppRewardActivityRespVO;
import org.dromara.mall.domain.promotion.RewardActivityDO;
import org.dromara.mall.service.promotion.RewardActivityService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "用户 App - 满减送活动")
@RestController
@RequestMapping("/app/promotion/reward-activity")
@Validated
public class AppRewardActivityController {
@Resource
private RewardActivityService rewardActivityService;
@GetMapping("/get")
@Operation(summary = "获得满减送活动")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PermitAll
public R<AppRewardActivityRespVO> getRewardActivity(@RequestParam("id") Long id) {
RewardActivityDO activity = rewardActivityService.getRewardActivity(id);
if (activity == null) {
return R.ok(null);
}
// 拼接 Rule 描述
AppRewardActivityRespVO activityVO = BeanUtils.toBean(activity, AppRewardActivityRespVO.class);
for (int i = 0; i < activityVO.getRules().size(); i++) {
AppRewardActivityRespVO.Rule ruleVO = activityVO.getRules().get(i);
RewardActivityDO.Rule rule = activity.getRules().get(i);
ruleVO.setDescription(rewardActivityService.getRewardActivityRuleDescription(activity.getConditionType(), rule));
}
return R.ok(activityVO);
}
}
package org.dromara.mall.controller.promotion.app.reward.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.dromara.mall.controller.promotion.admin.reward.vo.RewardActivityBaseVO;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 满减送活动 Response VO")
@Data
public class AppRewardActivityRespVO {
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer id;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "活动标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "满啦满啦")
private String name;
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "条件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conditionType;
@Schema(description = "商品范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer productScope;
@Schema(description = "商品 SPU 编号的数组", example = "1,2,3")
private List<Long> productScopeValues;
@Schema(description = "优惠规则的数组")
private List<Rule> rules;
@Schema(description = "优惠规则")
@Data
public static class Rule extends RewardActivityBaseVO.Rule {
@Schema(description = "规则描述")
private String description; // 通过 {@link #limit}、{@link #discountPrice} 等字段进行拼接
}
}
package org.dromara.mall.controller.promotion.app.seckill;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.common.mybatis.core.page.PageResult;
import org.dromara.mall.api.dto.product.ProductSpuRespDTO;
import org.dromara.mall.api.service.product.ProductSpuApi;
import org.dromara.mall.controller.promotion.app.seckill.vo.activity.AppSeckillActivityDetailRespVO;
import org.dromara.mall.controller.promotion.app.seckill.vo.activity.AppSeckillActivityNowRespVO;
import org.dromara.mall.controller.promotion.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
import org.dromara.mall.controller.promotion.app.seckill.vo.activity.AppSeckillActivityRespVO;
import org.dromara.mall.convert.promotion.seckill.SeckillActivityConvert;
import org.dromara.mall.domain.promotion.SeckillActivityDO;
import org.dromara.mall.domain.promotion.SeckillConfigDO;
import org.dromara.mall.domain.promotion.SeckillProductDO;
import org.dromara.mall.service.promotion.SeckillActivityService;
import org.dromara.mall.service.promotion.SeckillConfigService;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collections;
import java.util.List;
import static org.dromara.common.core.utils.StreamUtils.findFirst;
import static org.dromara.common.mall.util.cache.CacheUtils.buildAsyncReloadingCache;
import static org.dromara.common.mall.util.collection.CollectionUtils.convertList;
import static org.dromara.common.mall.util.date.LocalDateTimeUtils.isBetween;
@Tag(name = "用户 App - 秒杀活动")
@RestController
@RequestMapping("/app/promotion/seckill-activity")
@Validated
public class AppSeckillActivityController {
/**
* {@link AppSeckillActivityNowRespVO} 缓存,通过它异步刷新 {@link #getNowSeckillActivity()} 所要的首页数据
*/
private final LoadingCache<String, AppSeckillActivityNowRespVO> nowSeckillActivityCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
new CacheLoader<String, AppSeckillActivityNowRespVO>() {
@Override
public AppSeckillActivityNowRespVO load(String key) {
return getNowSeckillActivity0();
}
});
@Resource
private SeckillActivityService activityService;
@Resource
@Lazy
private SeckillConfigService configService;
@Resource
private ProductSpuApi spuApi;
@GetMapping("/get-now")
@Operation(summary = "获得当前秒杀活动", description = "获取当前正在进行的活动,提供给首页使用")
@PermitAll
public R<AppSeckillActivityNowRespVO> getNowSeckillActivity() {
return R.ok(nowSeckillActivityCache.getUnchecked("")); // 缓存
}
private AppSeckillActivityNowRespVO getNowSeckillActivity0() {
// 1. 获取当前时间处在哪个秒杀阶段
SeckillConfigDO config = configService.getCurrentSeckillConfig();
if (config == null) { // 时段不存在直接返回 null
return new AppSeckillActivityNowRespVO();
}
// 2.1 查询满足当前阶段的活动
List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus());
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
convertList(activityList, SeckillActivityDO::getId));
// 2.2 获取 spu 信息
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
return SeckillActivityConvert.INSTANCE.convert(config, activityList, productList, spuList);
}
@GetMapping("/page")
@Operation(summary = "获得秒杀活动分页")
@PermitAll
public R<PageResult<AppSeckillActivityRespVO>> getSeckillActivityPage(AppSeckillActivityPageReqVO pageReqVO) {
// 1. 查询满足当前阶段的活动
PageResult<SeckillActivityDO> pageResult = activityService.getSeckillActivityAppPageByConfigId(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return R.ok(PageResult.empty(pageResult.getTotal()));
}
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
convertList(pageResult.getList(), SeckillActivityDO::getId));
// 2. 拼接数据
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), SeckillActivityDO::getSpuId));
return R.ok(SeckillActivityConvert.INSTANCE.convertPage02(pageResult, productList, spuList));
}
@GetMapping("/get-detail")
@Operation(summary = "获得秒杀活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024")
@PermitAll
public R<AppSeckillActivityDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) {
// 1. 获取活动
SeckillActivityDO activity = activityService.getSeckillActivity(id);
if (activity == null
|| ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
return R.ok(null);
}
// 2. 获取时间段
List<SeckillConfigDO> configs = configService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
configs.removeIf(config -> !CollUtil.contains(activity.getConfigIds(), config.getId()));
// 2.1 优先使用当前时间段
SeckillConfigDO config = findFirst(configs, config0 -> isBetween(config0.getStartTime(), config0.getEndTime()));
// 2.2 如果没有,则获取最后一个,因为倾向优先展示“未开始” > “已结束”
if (config == null) {
config = CollUtil.getLast(configs);
}
if (config == null) {
return null;
}
// 3. 计算开始时间、结束时间
LocalDate nowDate;
// 3.1 如果在活动日期范围内,则以今天为 nowDate
if (isBetween(activity.getStartTime(), activity.getEndTime())) {
nowDate = LocalDate.now();
} else {
// 3.2 如果不在活动时间范围内,则直接以活动的 endTime 作为 nowDate,因为还是倾向优先展示“未开始” > “已结束”
nowDate = activity.getEndTime().toLocalDate();
}
LocalDateTime startTime = LocalDateTime.of(nowDate, LocalTime.parse(config.getStartTime()));
LocalDateTime endTime = LocalDateTime.of(nowDate, LocalTime.parse(config.getEndTime()));
// 4. 拼接数据
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(activity.getId());
return R.ok(SeckillActivityConvert.INSTANCE.convert3(activity, productList, startTime, endTime));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得秒杀活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
@PermitAll
public R<List<AppSeckillActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return R.ok(Collections.emptyList());
}
// 2. 拼接返回
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
convertList(activityList, SeckillActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
return R.ok(SeckillActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));
}
}
package org.dromara.mall.controller.promotion.app.seckill;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.dromara.common.core.domain.R;
import org.dromara.common.mall.enums.CommonStatusEnum;
import org.dromara.mall.controller.promotion.app.seckill.vo.config.AppSeckillConfigRespVO;
import org.dromara.mall.convert.promotion.seckill.SeckillConfigConvert;
import org.dromara.mall.domain.promotion.SeckillConfigDO;
import org.dromara.mall.service.promotion.SeckillConfigService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "用户 App - 秒杀时间段")
@RestController
@RequestMapping("/app/promotion/seckill-config")
@Validated
public class AppSeckillConfigController {
@Resource
private SeckillConfigService configService;
@GetMapping("/list")
@Operation(summary = "获得秒杀时间段列表")
@PermitAll
public R<List<AppSeckillConfigRespVO>> getSeckillConfigList() {
List<SeckillConfigDO> list = configService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
return R.ok(SeckillConfigConvert.INSTANCE.convertList2(list));
}
}
package org.dromara.mall.controller.promotion.app.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 秒杀活动的详细 Response VO")
@Data
@Accessors(chain = true)
public class AppSeckillActivityDetailRespVO {
@Schema(description = "秒杀活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "秒杀活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "晚九点限时秒杀")
private String name;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "总共限购数量", example = "10")
private Integer totalLimitCount;
@Schema(description = "单次限购数量", example = "5")
private Integer singleLimitCount;
@Schema(description = "秒杀库存(剩余)", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer stock;
@Schema(description = "秒杀库存(总计)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer totalStock;
@Schema(description = "商品信息数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Product> products;
@Schema(description = "商品信息")
@Data
public static class Product {
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
private Long skuId;
@Schema(description = "秒杀金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer seckillPrice;
@Schema(description = "秒杀限量库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer stock;
}
}
package org.dromara.mall.controller.promotion.app.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.dromara.mall.controller.promotion.app.seckill.vo.config.AppSeckillConfigRespVO;
import java.util.List;
@Schema(description = "用户 App - 当前秒杀活动 Response VO")
@Data
public class AppSeckillActivityNowRespVO {
@Schema(description = "秒杀时间段", requiredMode = Schema.RequiredMode.REQUIRED)
private AppSeckillConfigRespVO config;
@Schema(description = "秒杀活动数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<AppSeckillActivityRespVO> activities;
}
package org.dromara.mall.controller.promotion.app.seckill.vo.activity;
import org.dromara.common.mybatis.core.page.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "用户 App - 商品评价分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppSeckillActivityPageReqVO extends PageParam {
@Schema(description = "秒杀配置编号", example = "1024")
private Long configId;
}
package org.dromara.mall.controller.promotion.app.seckill.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@Schema(description = "用户 App - 秒杀活动 Response VO")
@Data
@Accessors(chain = true)
public class AppSeckillActivityRespVO {
@Schema(description = "秒杀活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "秒杀活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "晚九点限时秒杀")
private String name;
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
private String spuName; // 从 SPU 的 name 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
example = "50")
private Integer marketPrice;
@Schema(description = "秒杀活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "秒杀库存(剩余)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer stock;
@Schema(description = "秒杀库存(总共)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
private Integer totalStock;
@Schema(description = "秒杀金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer seckillPrice;
}
package org.dromara.mall.controller.promotion.app.seckill.vo.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用户 App - 秒杀时间段 Response VO")
@Data
public class AppSeckillConfigRespVO {
@Schema(description = "秒杀时间段编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "开始时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:00")
private String startTime;
@Schema(description = "结束时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:59")
private String endTime;
@Schema(description = "轮播图", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> sliderPicUrls;
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论