Kaynağa Gözat

新增活动管理模块相关接口;

codingliang 2 yıl önce
ebeveyn
işleme
d4ff29e02c
35 değiştirilmiş dosya ile 1645 ekleme ve 1 silme
  1. 24 0
      src/main/java/com/sqx/common/constant/MyConstant.java
  2. 2 1
      src/main/java/com/sqx/common/exception/SqxExceptionHandler.java
  3. 40 0
      src/main/java/com/sqx/modules/activity/bo/ShopActivityBO.java
  4. 103 0
      src/main/java/com/sqx/modules/activity/controller/ActivityController.java
  5. 35 0
      src/main/java/com/sqx/modules/activity/controller/ActivityGoodsController.java
  6. 62 0
      src/main/java/com/sqx/modules/activity/controller/ActivityShopController.java
  7. 34 0
      src/main/java/com/sqx/modules/activity/controller/app/AppActivityShopController.java
  8. 14 0
      src/main/java/com/sqx/modules/activity/dao/ActivityDao.java
  9. 15 0
      src/main/java/com/sqx/modules/activity/dao/ActivityGoodsDao.java
  10. 23 0
      src/main/java/com/sqx/modules/activity/dao/ActivityShopDao.java
  11. 76 0
      src/main/java/com/sqx/modules/activity/dto/ActivityDTO.java
  12. 27 0
      src/main/java/com/sqx/modules/activity/dto/ActivityGoodsDTO.java
  13. 29 0
      src/main/java/com/sqx/modules/activity/dto/ActivityOfFullReductionDTO.java
  14. 23 0
      src/main/java/com/sqx/modules/activity/dto/ActivityOfGlobalDiscountDTO.java
  15. 33 0
      src/main/java/com/sqx/modules/activity/dto/ActivityOfTimeIntervalDTO.java
  16. 39 0
      src/main/java/com/sqx/modules/activity/dto/ActivityQueryDTO.java
  17. 42 0
      src/main/java/com/sqx/modules/activity/dto/JoinActivityDTO.java
  18. 24 0
      src/main/java/com/sqx/modules/activity/dto/QuitActivityDTO.java
  19. 64 0
      src/main/java/com/sqx/modules/activity/entity/Activity.java
  20. 33 0
      src/main/java/com/sqx/modules/activity/entity/ActivityGoods.java
  21. 42 0
      src/main/java/com/sqx/modules/activity/entity/ActivityShop.java
  22. 22 0
      src/main/java/com/sqx/modules/activity/enums/ActivityTypeEnum.java
  23. 20 0
      src/main/java/com/sqx/modules/activity/enums/FullActivityTypeEnum.java
  24. 20 0
      src/main/java/com/sqx/modules/activity/enums/LimitTypeEnum.java
  25. 20 0
      src/main/java/com/sqx/modules/activity/enums/SuitTypeEnum.java
  26. 17 0
      src/main/java/com/sqx/modules/activity/service/ActivityGoodsService.java
  27. 32 0
      src/main/java/com/sqx/modules/activity/service/ActivityService.java
  28. 36 0
      src/main/java/com/sqx/modules/activity/service/ActivityShopService.java
  29. 45 0
      src/main/java/com/sqx/modules/activity/service/impl/ActivityGoodsServiceImpl.java
  30. 296 0
      src/main/java/com/sqx/modules/activity/service/impl/ActivityServiceImpl.java
  31. 171 0
      src/main/java/com/sqx/modules/activity/service/impl/ActivityShopServiceImpl.java
  32. 38 0
      src/main/java/com/sqx/modules/activity/vo/ActivityShopVO.java
  33. 61 0
      src/main/java/com/sqx/modules/activity/vo/ActivityVO.java
  34. 40 0
      src/main/java/com/sqx/modules/activity/vo/ShopActivityGroupVO.java
  35. 43 0
      src/main/resources/mapper/activity/ActivityShopDao.xml

+ 24 - 0
src/main/java/com/sqx/common/constant/MyConstant.java

@@ -0,0 +1,24 @@
+package com.sqx.common.constant;
+
+/**
+ * 常量类
+ * @author : codingliang
+ * @date : 2024-06-19 9:49
+ */
+public class MyConstant {
+
+    /**
+     * 是
+     */
+    public static final String YES = "1";
+
+    /**
+     * 否
+     */
+    public static final String NO = "0";
+
+    /**
+     * 每个店铺允许加入的活动数量
+     */
+    public static final Integer MAX_NUM_OF_SHOP_JOIN = 5;
+}

+ 2 - 1
src/main/java/com/sqx/common/exception/SqxExceptionHandler.java

@@ -58,7 +58,8 @@ public class SqxExceptionHandler {
 	}
 
 	@ExceptionHandler(HttpMessageNotReadableException.class)
-	public Result HttpMessageNotReadableException() {
+	public Result HttpMessageNotReadableException(Exception e) {
+		logger.error(e.getMessage(), e);
 		return Result.error("请求参数缺失");
 	}
 

+ 40 - 0
src/main/java/com/sqx/modules/activity/bo/ShopActivityBO.java

@@ -0,0 +1,40 @@
+package com.sqx.modules.activity.bo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 店铺活动bo
+ *
+ * @author : codingliang
+ * @date : 2024-06-22 10:07
+ */
+@Data
+public class ShopActivityBO {
+    @ApiModelProperty(name = "活动id")
+    private Long activityId;
+
+    @ApiModelProperty(name = "店铺id")
+    private Long shopId;
+
+    @ApiModelProperty(name = "适用类型;1无限制、2仅针对第一次下单")
+    private String suitType;
+
+    @ApiModelProperty(name = "限制类型;1无限制、2数量限制")
+    private String limitType;
+
+    @ApiModelProperty(name = "限制数量;limit为2时该值不能为空,每天限制使用的数量")
+    private Integer limitValue;
+
+    @ApiModelProperty(name = "活动标题")
+    private String title;
+
+    @ApiModelProperty(name = "活动描述")
+    private String content;
+
+    @ApiModelProperty(name = "活动类型;1普通活动、2时段优惠、3满额优惠、4全场优惠")
+    private String type;
+
+    @ApiModelProperty(name = "活动配置;json字符串")
+    private String config;
+}

+ 103 - 0
src/main/java/com/sqx/modules/activity/controller/ActivityController.java

@@ -0,0 +1,103 @@
+package com.sqx.modules.activity.controller;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.sqx.common.exception.SqxException;
+import com.sqx.common.utils.PageUtils;
+import com.sqx.common.utils.Result;
+import com.sqx.modules.activity.dto.ActivityDTO;
+import com.sqx.modules.activity.dto.ActivityQueryDTO;
+import com.sqx.modules.activity.service.ActivityService;
+import com.sqx.modules.activity.vo.ActivityVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Pattern;
+import java.util.Arrays;
+
+/**
+ * 活动
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Api(tags = "活动-活动管理")
+@RestController
+@RequestMapping("admin/activity")
+@RequiredArgsConstructor
+public class ActivityController{
+
+    private final ActivityService activityService;
+
+    @GetMapping("page")
+    @ApiOperation(value = "获取活动信息分页", response = PageUtils.class)
+    public Result page(@Valid ActivityQueryDTO queryDTO) {
+        PageUtils page = activityService.pageActivity(queryDTO);
+        return Result.success().put("data", page);
+    }
+
+    @GetMapping("{id}")
+    @ApiOperation(value = "根据id获取活动信息", response = ActivityVO.class)
+    public Result getById(@PathVariable Long id) {
+        ActivityVO ActivityVO = activityService.getActivityById(id);
+        return Result.success().put("data", ActivityVO);
+    }
+
+    @PostMapping
+    @ApiOperation("添加活动")
+    public Result add(@RequestBody @Valid ActivityDTO activityDTO) {
+        activityService.addActivity(activityDTO);
+        return Result.success();
+    }
+
+    @PutMapping
+    @ApiOperation("修改活动")
+    public Result update(@RequestBody @Valid ActivityDTO activityDTO) {
+        if (ObjectUtil.isNull(activityDTO.getId())) {
+            throw new SqxException("活动id不能为空");
+        }
+
+        activityService.updateActivity(activityDTO);
+        return Result.success();
+    }
+
+    @PutMapping("status/{id}/{flag}")
+    @ApiOperation("修改活动状态")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "活动id"),
+            @ApiImplicitParam(name = "flag", value = "启用标识,0关闭 1启用")
+    })
+    public Result updateStatus(@PathVariable Long id, @PathVariable @Pattern(regexp = "(0|1)", message = "是否启用只能为0或1") String flag) {
+        activityService.updateActivityStatus(id, flag);
+        return Result.success();
+    }
+
+    @PutMapping("join/{id}/{flag}")
+    @ApiOperation("修改是否允许商家主动加入")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "活动id"),
+            @ApiImplicitParam(name = "flag", value = "允许标识,0不允许 1允许")
+    })
+    public Result updateJoinMethod(@PathVariable Long id, @PathVariable @Pattern(regexp = "(0|1)", message = "是否启用只能为0或1") String flag) {
+        activityService.updateActivityJoinMethod(id, flag);
+        return Result.success();
+    }
+
+    @DeleteMapping
+    @ApiOperation("删除活动")
+    public Result deleteActivity(@RequestBody @ApiParam("id的集合") Long[] ids) {
+        activityService.deleteActivity(Arrays.asList(ids));
+        return Result.success();
+    }
+}

+ 35 - 0
src/main/java/com/sqx/modules/activity/controller/ActivityGoodsController.java

@@ -0,0 +1,35 @@
+package com.sqx.modules.activity.controller;
+
+import com.sqx.common.utils.Result;
+import com.sqx.modules.activity.dto.ActivityGoodsDTO;
+import com.sqx.modules.activity.service.ActivityGoodsService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+ /**
+ * 活动商品
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Api(tags = "活动-活动商品管理")
+@RestController
+@RequestMapping("admin/activity-goods")
+@RequiredArgsConstructor
+public class ActivityGoodsController{
+    
+    private final ActivityGoodsService activityGoodsService;
+    
+    @PostMapping
+    @ApiOperation("添加或者修改参与活动商品")
+    public Result updateActivityGoods(@RequestBody @Valid ActivityGoodsDTO activityGoodsDTO) {
+        activityGoodsService.updateActivityGoods(activityGoodsDTO);
+        return Result.success();
+    }
+}

+ 62 - 0
src/main/java/com/sqx/modules/activity/controller/ActivityShopController.java

@@ -0,0 +1,62 @@
+package com.sqx.modules.activity.controller;
+
+import com.sqx.common.query.PageQuery;
+import com.sqx.common.utils.PageUtils;
+import com.sqx.common.utils.Result;
+import com.sqx.modules.activity.dto.JoinActivityDTO;
+import com.sqx.modules.activity.dto.QuitActivityDTO;
+import com.sqx.modules.activity.service.ActivityShopService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * 活动商家
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Api(tags = "活动-活动商家管理")
+@RestController
+@RequestMapping("admin/activity-shop")
+@RequiredArgsConstructor
+public class ActivityShopController{
+
+    private final ActivityShopService activityShopService;
+
+    @ApiOperation("活动参与的商家列表")
+    @GetMapping("activity-shop-page/{activityId}")
+    public Result getActivityShopPage(@PathVariable Long activityId, PageQuery queryDTO) {
+        PageUtils page = activityShopService.getShopByActivityId(activityId, queryDTO);
+        return Result.success().put("data", page);
+    }
+
+     @ApiOperation("商家加入活动")
+     @PostMapping("join")
+     public Result joinActivity(@RequestBody @Valid JoinActivityDTO dto) {
+         activityShopService.joinActivity(dto);
+         return Result.success();
+     }
+
+     @ApiOperation("商家退出活动(根据活动id和店铺id退出)")
+     @PutMapping("quit")
+     public Result quitActivity(@Valid QuitActivityDTO dto) {
+         activityShopService.quitActivity(dto);
+         return Result.success();
+     }
+
+    @ApiOperation("商家退出活动(根据参与id退出)")
+    @PutMapping("quit-by-id/{activityShopId}")
+    public Result quitByActivityShopId(@PathVariable Long activityShopId) {
+        activityShopService.quitByActivityShopId(activityShopId);
+        return Result.success();
+    }
+}

+ 34 - 0
src/main/java/com/sqx/modules/activity/controller/app/AppActivityShopController.java

@@ -0,0 +1,34 @@
+package com.sqx.modules.activity.controller.app;
+
+import com.sqx.common.utils.Result;
+import com.sqx.modules.activity.service.ActivityShopService;
+import com.sqx.modules.activity.vo.ShopActivityGroupVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 活动店铺
+ *
+ * @author : codingliang
+ * @date : 2024-06-22 9:53
+ */
+@Api(tags = "活动-活动商家")
+@RestController
+@RequestMapping("app/activity-shop")
+@RequiredArgsConstructor
+public class AppActivityShopController {
+
+    private final ActivityShopService activityShopService;
+
+    @ApiOperation("获取店铺参与的活动分组")
+    @GetMapping("shop-activity-group/{shopId}")
+    public Result getShopActivity(@PathVariable Long shopId) {
+        ShopActivityGroupVO shopActivityVO = activityShopService.getShopActivityGroup(shopId);
+        return Result.success().put("data", shopActivityVO);
+    }
+}

+ 14 - 0
src/main/java/com/sqx/modules/activity/dao/ActivityDao.java

@@ -0,0 +1,14 @@
+package com.sqx.modules.activity.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.sqx.modules.activity.entity.Activity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 活动
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Mapper
+public interface ActivityDao extends BaseMapper<Activity> {
+}

+ 15 - 0
src/main/java/com/sqx/modules/activity/dao/ActivityGoodsDao.java

@@ -0,0 +1,15 @@
+package com.sqx.modules.activity.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.sqx.modules.activity.entity.ActivityGoods;
+import org.apache.ibatis.annotations.Mapper;
+
+ /**
+ * 活动商品
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Mapper
+public interface ActivityGoodsDao extends BaseMapper<ActivityGoods> {
+
+}

+ 23 - 0
src/main/java/com/sqx/modules/activity/dao/ActivityShopDao.java

@@ -0,0 +1,23 @@
+package com.sqx.modules.activity.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.sqx.modules.activity.entity.ActivityShop;
+import com.sqx.modules.activity.vo.ActivityShopVO;
+import com.sqx.modules.activity.vo.ShopActivityGroupVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 活动商家
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Mapper
+public interface ActivityShopDao extends BaseMapper<ActivityShop> {
+
+     IPage<ActivityShopVO> pageShopByActivityId(@Param("pages") Page<ActivityShop> pages, @Param("activityId") Long activityId);
+
+    ShopActivityGroupVO getShopActivityGroup(@Param("shopId") Long shopId);
+}

+ 76 - 0
src/main/java/com/sqx/modules/activity/dto/ActivityDTO.java

@@ -0,0 +1,76 @@
+package com.sqx.modules.activity.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 活动信息
+ *
+ * @author : codingliang
+ * @date : 2024-06-17 16:08
+ */
+@Data
+@ApiModel(value = "活动信息")
+public class ActivityDTO {
+
+    @ApiModelProperty(value = "活动id,修改时不能为空")
+    private Long id;
+
+    @ApiModelProperty(value = "活动标题", required = true)
+    @NotBlank(message = "活动标题不能为空")
+    private String title;
+
+    @ApiModelProperty(value = "活动描述", required = true)
+    @NotBlank(message = "活动描述不能为空")
+    private String content;
+
+    @ApiModelProperty(value = "活动图片", required = true)
+    @NotBlank(message = "活动图片不能为空")
+    private String image;
+
+    @ApiModelProperty(value = "是否启用;0否 1是", required = true)
+    @NotBlank(message = "是否启用不能为空")
+    @Pattern(regexp = "(0|1)", message = "是否启用只能为0或1")
+    private String enableFlag;
+
+    @ApiModelProperty(value = "是否允许商户主动加入;0否 1是", required = true)
+    @NotBlank(message = "是否允许商户主动加入不能为空")
+    @Pattern(regexp = "(0|1)", message = "是否允许商户主动加入只能为0或1")
+    private String allowShop;
+
+    @ApiModelProperty(value = "活动类型;1普通活动、2时段优惠、3满额优惠、4全场优惠", required = true)
+    @NotBlank(message = "活动类型不能为空")
+    @Pattern(regexp = "[1-4]", message = "活动类型只能为1-4")
+    private String type;
+
+    @ApiModelProperty(value = "活动开始时间 yyyy-MM-dd", required = true)
+    @NotNull(message = "活动开始时间不能为空")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date startTime;
+
+    @ApiModelProperty(value = "活动结束时间 yyy-MM-dd", required = true)
+    @NotNull(message = "活动结束时间不能为空")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date endTime;
+
+    @Valid
+    @ApiModelProperty(value = "优惠时段信息列表;type为2时该字段不能为空")
+    private List<ActivityOfTimeIntervalDTO> timeIntervalInfos;
+
+    @Valid
+    @ApiModelProperty(value = "满减字段优惠;type为3时该字段不能为空")
+    private ActivityOfFullReductionDTO fullReductionInfo;
+
+    @Valid
+    @ApiModelProperty(value = "全场优惠;type为4时该字段不能为空")
+    private ActivityOfGlobalDiscountDTO globalDiscountsInfo;
+}

+ 27 - 0
src/main/java/com/sqx/modules/activity/dto/ActivityGoodsDTO.java

@@ -0,0 +1,27 @@
+package com.sqx.modules.activity.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+/**
+ * 活动商品dto
+ *
+ * @author : codingliang
+ * @date : 2024-06-20 16:37
+ */
+@Data
+public class ActivityGoodsDTO {
+
+    @ApiModelProperty(value = "活动商家id", required = true)
+    @NotNull(message = "活动商家id不能为空")
+    private Long activityShopId;
+
+    @ApiModelProperty(value = "商品id集合", required = true)
+    @NotNull(message = "商品id集合不能为空")
+    @Size(message = "商品id集合不能为空")
+    private List<Long> goodsIds;
+}

+ 29 - 0
src/main/java/com/sqx/modules/activity/dto/ActivityOfFullReductionDTO.java

@@ -0,0 +1,29 @@
+package com.sqx.modules.activity.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import java.io.Serializable;
+
+ /**
+ * 满减优惠活动类型
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@ApiModel(value = "满减优惠信息")
+@Data
+public class ActivityOfFullReductionDTO implements Serializable {
+
+    @ApiModelProperty(value = "类型;1金额满减、2金额满折扣", required = true)
+    @Pattern(regexp = "(1|2)", message = "满减优惠类型只能为1或2")
+    @NotBlank(message = "不能为空")
+    private String type;
+    
+    @ApiModelProperty(value = "折扣内容;如果type为1该值标识为直减金额;如果type为2该值表示折扣比例,取值(0,10)", required = true)
+    @NotNull(message = "折扣内容不能为空")
+    private Double discountContent;
+}

+ 23 - 0
src/main/java/com/sqx/modules/activity/dto/ActivityOfGlobalDiscountDTO.java

@@ -0,0 +1,23 @@
+package com.sqx.modules.activity.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 全场优惠
+ *
+ * @author : codingliang
+ * @date : 2024-06-17 16:45
+ */
+@ApiModel(value = "全场优惠信息")
+@Data
+public class ActivityOfGlobalDiscountDTO implements Serializable {
+
+    @ApiModelProperty(value = "折扣比例,取值(0,10)", required = true)
+    @NotNull(message = "折扣比例不能为空")
+    private Double discountRate;
+}

+ 33 - 0
src/main/java/com/sqx/modules/activity/dto/ActivityOfTimeIntervalDTO.java

@@ -0,0 +1,33 @@
+package com.sqx.modules.activity.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 时段优惠活动类型
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@ApiModel(value = "时段优惠信息")
+@Data
+public class ActivityOfTimeIntervalDTO implements Serializable {
+
+    @ApiModelProperty(value = "开始时间;HH:mm", required = true)
+    @NotBlank(message = "时段优惠信息不能为空")
+    private String startTime;
+
+    @ApiModelProperty(value = "结束时间;HH:mm", required = true)
+    @NotBlank(message = "时段优惠信息不能为空")
+    private String endTime;
+
+    @ApiModelProperty(value = "折扣内容;取值范围(0-10),金额折扣", required = true)
+    @NotNull(message = "时段优惠信息折扣内容不能为空")
+    @Range(min = 0, max = 10, message = "时段优惠信息折扣内容取值范围(0-10)")
+    private Double discountContent;
+}

+ 39 - 0
src/main/java/com/sqx/modules/activity/dto/ActivityQueryDTO.java

@@ -0,0 +1,39 @@
+package com.sqx.modules.activity.dto;
+
+import com.sqx.common.query.PageQuery;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Pattern;
+import java.util.Date;
+
+/**
+ * 活动查询dto
+ *
+ * @author : codingliang
+ * @date : 2024-06-19 10:24
+ */
+@Data
+public class ActivityQueryDTO extends PageQuery {
+
+    @ApiModelProperty(value = "活动标题")
+    private String title;
+
+    @ApiModelProperty(value = "是否启用;0否 1是")
+    @Pattern(regexp = "(0|1)", message = "是否启用只能为0或1")
+    private String enableFlag;
+
+    @ApiModelProperty(value = "是否允许商户主动加入;0否 1是")
+    @Pattern(regexp = "(0|1)", message = "是否允许商户主动加入只能为0或1")
+    private String allowShop;
+
+    @ApiModelProperty(value = "活动类型;1普通活动、2时段优惠、3满额优惠、4全场优惠")
+    @Pattern(regexp = "[1-4]", message = "活动类型只能为1-4")
+    private String type;
+
+    @ApiModelProperty(value = "活动开始时间")
+    private Date startTime;
+
+    @ApiModelProperty(value = "活动结束时间")
+    private Date endTime;
+}

+ 42 - 0
src/main/java/com/sqx/modules/activity/dto/JoinActivityDTO.java

@@ -0,0 +1,42 @@
+package com.sqx.modules.activity.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+/**
+ * 加入活动dto
+ *
+ * @author : codingliang
+ * @date : 2024-06-19 11:05
+ */
+@Data
+public class JoinActivityDTO {
+
+    @ApiModelProperty(value = "活动id", required = true)
+    @NotNull(message = "活动id不能为空")
+    private Long activityId;
+
+    @ApiModelProperty(value = "店铺id集合", required = true)
+    @NotNull(message = "店铺id集合不能为空")
+    @Size(min = 1, message = "店铺id集合不能为空11")
+    private List<Long> shopIds;
+
+    @ApiModelProperty(value = "适用类型;1无限制、2仅针对第一次下单", required = true)
+    @NotBlank(message = "适用类型不能为空")
+    @Pattern(regexp = "(1|2)", message = "适用类型只能为1或2")
+    private String suitType;
+
+    @ApiModelProperty(value = "限制类型;1无限制、2数量限制", required = true)
+    @NotBlank(message = "限制类型不能为空")
+    @Pattern(regexp = "(1|2)", message = "限制类型只能为1或2")
+    private String limitType;
+
+    @ApiModelProperty(value = "限制数量;limit为2时该值不能为空,每天限制使用的数量")
+    private Integer limitValue;
+}

+ 24 - 0
src/main/java/com/sqx/modules/activity/dto/QuitActivityDTO.java

@@ -0,0 +1,24 @@
+package com.sqx.modules.activity.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 退出活动dto
+ *
+ * @author : codingliang
+ * @date : 2024-06-19 17:12
+ */
+@Data
+public class QuitActivityDTO {
+
+    @ApiModelProperty(value =  "店铺id", required = true)
+    @NotNull(message = "店铺id不能为空")
+    private Long shopId;
+
+    @ApiModelProperty(value =  "活动id", required = true)
+    @NotNull(message = "活动id不能为空")
+    private Long activityId;
+}

+ 64 - 0
src/main/java/com/sqx/modules/activity/entity/Activity.java

@@ -0,0 +1,64 @@
+package com.sqx.modules.activity.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 活动
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@ApiModel(value = "活动")
+@TableName("activity")
+@Data
+public class Activity implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(name = "活动id")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(name = "活动标题")
+    private String title;
+
+    @ApiModelProperty(name = "活动描述")
+    private String content;
+
+    @ApiModelProperty(name = "活动图片")
+    private String image;
+
+    @ApiModelProperty(name = "是否启用;0否 1是")
+    private String enableFlag;
+
+    @ApiModelProperty(name = "是否允许商户主动加入;0否 1是")
+    private String allowShop;
+
+    @ApiModelProperty(name = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(name = "更新时间")
+    private Date updateTime;
+
+    @ApiModelProperty(name = "删除标识;0未删除、1已删除")
+    @TableLogic(value = "0", delval = "1")
+    private String delFlag;
+
+    @ApiModelProperty(name = "活动类型;1普通活动、2时段优惠、3满额优惠、4全场优惠")
+    private String type;
+
+    @ApiModelProperty(name = "活动开始时间")
+    private Date startTime;
+
+    @ApiModelProperty(name = "活动结束时间")
+    private Date endTime;
+
+    @ApiModelProperty(name = "活动配置;json字符串")
+    private String config;
+}

+ 33 - 0
src/main/java/com/sqx/modules/activity/entity/ActivityGoods.java

@@ -0,0 +1,33 @@
+package com.sqx.modules.activity.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+ /**
+ * 活动sku
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@ApiModel(value = "活动sku")
+@TableName("activity_goods")
+@Data
+public class ActivityGoods implements Serializable {
+    private static final long serialVersionUID = 1L;
+    
+    @ApiModelProperty(name = "id")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    @ApiModelProperty(name = "活动商家id")
+    private Long activityShopId;
+    
+    @ApiModelProperty(name = "商品id集合;参与活动skuId集合,多个id之间使用,分割")
+    private String goodsIds;
+    
+}

+ 42 - 0
src/main/java/com/sqx/modules/activity/entity/ActivityShop.java

@@ -0,0 +1,42 @@
+package com.sqx.modules.activity.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+ /**
+ * 活动商家
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@ApiModel(value = "活动商家")
+@TableName("activity_shop")
+@Data
+public class ActivityShop implements Serializable {
+    private static final long serialVersionUID = 1L;
+    
+    @ApiModelProperty(name = "id")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    
+    @ApiModelProperty(name = "活动id")
+    private Long activityId;
+    
+    @ApiModelProperty(name = "店铺id")
+    private Long shopId;
+    
+    @ApiModelProperty(name = "适用类型;1无限制、2仅针对第一次下单")
+    private String suitType;
+    
+    @ApiModelProperty(name = "限制类型;1无限制、2数量限制")
+    private String limitType;
+    
+    @ApiModelProperty(name = "限制数量;limit为2时该值不能为空,每天限制使用的数量")
+    private Integer limitValue;
+    
+}

+ 22 - 0
src/main/java/com/sqx/modules/activity/enums/ActivityTypeEnum.java

@@ -0,0 +1,22 @@
+package com.sqx.modules.activity.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 活动类型枚举
+ *
+ * @author : codingliang
+ * @date : 2024-06-18 17:02
+ */
+@Getter
+@AllArgsConstructor
+public enum ActivityTypeEnum {
+    NORMAL("1", "普通活动"),
+    TIME("2", "时段优惠"),
+    FULL("3", "满减优惠"),
+    GLOBAL("4", "全场优惠");
+
+    private String typeCode;
+    private String typeName;
+}

+ 20 - 0
src/main/java/com/sqx/modules/activity/enums/FullActivityTypeEnum.java

@@ -0,0 +1,20 @@
+package com.sqx.modules.activity.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 满减活动优惠类型
+ *
+ * @author : codingliang
+ * @date : 2024-06-18 17:21
+ */
+@Getter
+@AllArgsConstructor
+public enum FullActivityTypeEnum {
+    AMOUNT("1", "金额满减"),
+    DISCOUNT("2", "折扣满减");
+
+    private String typeCode;
+    private String typeName;
+}

+ 20 - 0
src/main/java/com/sqx/modules/activity/enums/LimitTypeEnum.java

@@ -0,0 +1,20 @@
+package com.sqx.modules.activity.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 限制类型枚举
+ *
+ * @author : codingliang
+ * @date : 2024-06-19 16:51
+ */
+@Getter
+@AllArgsConstructor
+public enum LimitTypeEnum {
+    NO_LIMIT("1", "无限制"),
+    NUM_LIMIT("2", "数量限制");
+
+    private String typeCode;
+    private String typeName;
+}

+ 20 - 0
src/main/java/com/sqx/modules/activity/enums/SuitTypeEnum.java

@@ -0,0 +1,20 @@
+package com.sqx.modules.activity.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 适用类型枚举
+ *
+ * @author : codingliang
+ * @date : 2024-06-19 11:06
+ */
+@Getter
+@AllArgsConstructor
+public enum SuitTypeEnum {
+    BOTH("1", "皆适用"),
+    FIRST_ORDER("2", "第一次下单适用");
+
+    private String typeCode;
+    private String typeName;
+}

+ 17 - 0
src/main/java/com/sqx/modules/activity/service/ActivityGoodsService.java

@@ -0,0 +1,17 @@
+package com.sqx.modules.activity.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.sqx.modules.activity.dto.ActivityGoodsDTO;
+import com.sqx.modules.activity.entity.ActivityGoods;
+
+/**
+ * 活动商品
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+public interface ActivityGoodsService extends IService<ActivityGoods> {
+
+    void updateActivityGoods(ActivityGoodsDTO activityGoodsDTO);
+
+    void deleteByActivityShopId(Long activityId);
+}

+ 32 - 0
src/main/java/com/sqx/modules/activity/service/ActivityService.java

@@ -0,0 +1,32 @@
+package com.sqx.modules.activity.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.sqx.common.utils.PageUtils;
+import com.sqx.modules.activity.dto.ActivityDTO;
+import com.sqx.modules.activity.dto.ActivityQueryDTO;
+import com.sqx.modules.activity.entity.Activity;
+import com.sqx.modules.activity.vo.ActivityVO;
+
+import java.util.List;
+
+/**
+ * 活动
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+public interface ActivityService extends IService<Activity> {
+
+    ActivityVO getActivityById(Long id);
+
+    PageUtils pageActivity(ActivityQueryDTO queryDTO);
+
+    void addActivity(ActivityDTO activityDTO);
+
+    void updateActivity(ActivityDTO activityDTO);
+
+    void updateActivityStatus(Long id, String flag);
+
+    void updateActivityJoinMethod(Long id, String flag);
+
+    void deleteActivity(List<Long> ids);
+}

+ 36 - 0
src/main/java/com/sqx/modules/activity/service/ActivityShopService.java

@@ -0,0 +1,36 @@
+package com.sqx.modules.activity.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.sqx.common.query.PageQuery;
+import com.sqx.common.utils.PageUtils;
+import com.sqx.modules.activity.dto.JoinActivityDTO;
+import com.sqx.modules.activity.dto.QuitActivityDTO;
+import com.sqx.modules.activity.entity.ActivityShop;
+import com.sqx.modules.activity.vo.ShopActivityGroupVO;
+
+import java.util.List;
+
+/**
+ * 活动商家
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+public interface ActivityShopService extends IService<ActivityShop> {
+
+    void joinActivity(JoinActivityDTO dto);
+
+    void quitActivity(QuitActivityDTO dto);
+
+    void quitByActivityShopId(Long activityShopId);
+
+    List<ActivityShop> getByActivityIds(List<Long> activityIds);
+
+    PageUtils getShopByActivityId(Long activityId, PageQuery queryDTO);
+
+    /**
+     * 获取店铺活动分组
+     * @param shopId 店铺id
+     * @return 店铺活动分组
+     */
+    ShopActivityGroupVO getShopActivityGroup(Long shopId);
+}

+ 45 - 0
src/main/java/com/sqx/modules/activity/service/impl/ActivityGoodsServiceImpl.java

@@ -0,0 +1,45 @@
+package com.sqx.modules.activity.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.sqx.modules.activity.dao.ActivityGoodsDao;
+import com.sqx.modules.activity.dto.ActivityGoodsDTO;
+import com.sqx.modules.activity.entity.ActivityGoods;
+import com.sqx.modules.activity.service.ActivityGoodsService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.stream.Collectors;
+
+/**
+ * 活动商品
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Service
+@RequiredArgsConstructor
+public class ActivityGoodsServiceImpl extends ServiceImpl<ActivityGoodsDao, ActivityGoods> implements ActivityGoodsService {
+
+    @Transactional
+    @Override
+    public void updateActivityGoods(ActivityGoodsDTO activityGoodsDTO) {
+        // 删除活动商家原有商品
+        deleteByActivityShopId(activityGoodsDTO.getActivityShopId());
+
+        ActivityGoods activityGoods = new ActivityGoods();
+        activityGoods.setActivityShopId(activityGoodsDTO.getActivityShopId());
+        activityGoods.setGoodsIds(activityGoodsDTO.getGoodsIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
+
+        this.save(activityGoods);
+    }
+
+    @Override
+    public void deleteByActivityShopId(Long activityShopId) {
+        LambdaQueryWrapper<ActivityGoods> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(ActivityGoods::getActivityShopId, activityShopId);
+        this.remove(queryWrapper);
+    }
+
+}

+ 296 - 0
src/main/java/com/sqx/modules/activity/service/impl/ActivityServiceImpl.java

@@ -0,0 +1,296 @@
+package com.sqx.modules.activity.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.sqx.common.constant.MyConstant;
+import com.sqx.common.exception.SqxException;
+import com.sqx.common.utils.PageUtils;
+import com.sqx.modules.activity.dao.ActivityDao;
+import com.sqx.modules.activity.dto.ActivityDTO;
+import com.sqx.modules.activity.dto.ActivityOfFullReductionDTO;
+import com.sqx.modules.activity.dto.ActivityOfTimeIntervalDTO;
+import com.sqx.modules.activity.dto.ActivityQueryDTO;
+import com.sqx.modules.activity.dto.ActivityOfGlobalDiscountDTO;
+import com.sqx.modules.activity.entity.Activity;
+import com.sqx.modules.activity.entity.ActivityShop;
+import com.sqx.modules.activity.enums.ActivityTypeEnum;
+import com.sqx.modules.activity.enums.FullActivityTypeEnum;
+import com.sqx.modules.activity.service.ActivityService;
+import com.sqx.modules.activity.service.ActivityShopService;
+import com.sqx.modules.activity.vo.ActivityVO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 活动
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Service
+@RequiredArgsConstructor
+public class ActivityServiceImpl extends ServiceImpl<ActivityDao, Activity> implements ActivityService {
+
+    private final ActivityShopService activityShopService;
+
+    /**
+     * 时间格式 HH:mm
+     */
+    private static final DateTimeFormatter TIME_DTF = DateTimeFormatter.ofPattern("HH:mm");
+
+    @Override
+    public ActivityVO getActivityById(Long id) {
+        Activity activity = this.findById(id);
+
+        return convertEntityToVO(activity);
+    }
+
+    @Override
+    public PageUtils pageActivity(ActivityQueryDTO queryDTO) {
+        Page<Activity> pages = new Page<>(queryDTO.getPage(), queryDTO.getLimit());
+
+        LambdaQueryWrapper<Activity> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.like(StrUtil.isNotBlank(queryDTO.getTitle()), Activity::getTitle, queryDTO.getTitle());
+        queryWrapper.eq(StrUtil.isNotBlank(queryDTO.getEnableFlag()), Activity::getEnableFlag, queryDTO.getEnableFlag());
+        queryWrapper.eq(StrUtil.isNotBlank(queryDTO.getAllowShop()), Activity::getAllowShop, queryDTO.getAllowShop());
+        queryWrapper.eq(StrUtil.isNotBlank(queryDTO.getType()), Activity::getType, queryDTO.getType());
+        queryWrapper.eq(ObjectUtil.isNotNull(queryDTO.getStartTime()), Activity::getStartTime, queryDTO.getStartTime());
+        queryWrapper.eq(ObjectUtil.isNotNull(queryDTO.getEndTime()), Activity::getEndTime, queryDTO.getEndTime());
+        queryWrapper.orderByDesc(Activity::getCreateTime);
+
+        IPage<Activity> page = this.page(pages, queryWrapper);
+        PageUtils pageUtils = new PageUtils(page);
+
+        List<ActivityVO> vos = pageUtils.getList().stream().map(e -> this.convertEntityToVO((Activity) e)).collect(Collectors.toList());
+        pageUtils.setList(vos);
+        return pageUtils;
+    }
+     
+    @Override
+    public void addActivity(ActivityDTO activityDTO) {
+        checkParam(activityDTO);
+
+        Activity activity = new Activity();
+        BeanUtils.copyProperties(activityDTO, activity);
+
+        String config = getActivityConfig(activityDTO);
+        activity.setConfig(config);
+        activity.setDelFlag(MyConstant.NO);
+
+        Date date = new Date();
+        activity.setCreateTime(date);
+        activity.setUpdateTime(date);
+
+        this.save(activity);
+    }
+
+    @Override
+    public void updateActivity(ActivityDTO activityDTO) {
+        Activity oldActivity = findById(activityDTO.getId());
+
+        if (!StrUtil.equals(oldActivity.getType(), activityDTO.getType())) {
+            throw new SqxException("活动类型不能修改");
+        }
+
+        checkParam(activityDTO);
+        Activity newActivity = new Activity();
+        BeanUtils.copyProperties(activityDTO, newActivity);
+
+        newActivity.setId(activityDTO.getId());
+        newActivity.setConfig(getActivityConfig(activityDTO));
+        newActivity.setDelFlag(MyConstant.NO);
+        newActivity.setCreateTime(oldActivity.getCreateTime());
+        newActivity.setUpdateTime(new Date());
+
+        this.updateById(newActivity);
+    }
+
+    @Override
+    public void updateActivityStatus(Long id, String flag) {
+        findById(id);
+
+        Activity newActivity = new Activity();
+        newActivity.setId(id);
+        newActivity.setEnableFlag(flag);
+
+        this.updateById(newActivity);
+    }
+
+    @Override
+    public void updateActivityJoinMethod(Long id, String flag) {
+        findById(id);
+
+        Activity newActivity = new Activity();
+        newActivity.setId(id);
+        newActivity.setAllowShop(flag);
+
+        this.updateById(newActivity);
+    }
+
+    @Override
+    public void deleteActivity(List<Long> activityIds) {
+        // 判断待删除活动是否还有商家未退出
+        List<ActivityShop> activityShops = activityShopService.getByActivityIds(activityIds);
+
+        List<Long> refuseDeleteIds = activityIds.stream().filter(e -> activityShops.contains(e)).collect(Collectors.toList());
+        if (refuseDeleteIds.size() > 0) {
+            String errorInfo = refuseDeleteIds.stream().map(e -> String.valueOf(activityIds.indexOf(e))).collect(Collectors.joining(","));
+
+            throw new SqxException("索引编号为【" + errorInfo + "】的活动下仍有商家,请退出后重试");
+        }
+
+        removeByIds(activityIds);
+    }
+
+    /**
+     * 活动实体类转成vo类
+     * @param activity 活动实体
+     * @return vo
+     */
+    private ActivityVO convertEntityToVO(Activity activity) {
+        ActivityVO vo = new ActivityVO();
+        BeanUtils.copyProperties(activity, vo);
+
+        String type = activity.getType();
+        String config = activity.getConfig();
+
+        if (ActivityTypeEnum.TIME.getTypeCode().equals(type)) {
+            vo.setTimeIntervalInfos(JSONUtil.toList(JSONUtil.parseArray(config), ActivityOfTimeIntervalDTO.class));
+        } else if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
+            vo.setFullReductionInfo(JSONUtil.toBean(config, ActivityOfFullReductionDTO.class));
+        } else if (ActivityTypeEnum.GLOBAL.getTypeCode().equals(type)) {
+            vo.setGlobalDiscountsInfo(JSONUtil.toBean(config, ActivityOfGlobalDiscountDTO.class));
+        }
+        return vo;
+    }
+
+    /**
+     * 根据id获取活动信息
+     *    如id对应记录不存在,则抛出数据不存在异常
+     * @param id 活动id
+     * @return activity
+     */
+    private Activity findById(Long id) {
+        Activity activity = this.getById(id);
+
+        if (ObjectUtil.isNull(activity)) {
+            throw new SqxException("无效的活动id");
+        }
+
+        return activity;
+    }
+
+    /**
+     * 获取活动配置
+     * @param activityDTO
+     * @return 活动config json串
+     */
+    private String getActivityConfig(ActivityDTO activityDTO) {
+        String config = null;
+        String type = activityDTO.getType();
+        if (ActivityTypeEnum.TIME.getTypeCode().equals(type)) {
+            config = JSONUtil.toJsonStr(activityDTO.getTimeIntervalInfos());
+        } else if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
+            config = JSONUtil.toJsonStr(activityDTO.getFullReductionInfo());
+        } else if (ActivityTypeEnum.GLOBAL.getTypeCode().equals(type)) {
+            config = JSONUtil.toJsonStr(activityDTO.getGlobalDiscountsInfo());
+        }
+        return config;
+    }
+
+    /**
+     * 校验活动参数
+     * @param activityDTO activityDTO
+     */
+    private void checkParam(ActivityDTO activityDTO) {
+        checkActivityDate(activityDTO.getStartTime(), activityDTO.getEndTime());
+
+        String type = activityDTO.getType();
+        if (ActivityTypeEnum.TIME.getTypeCode().equals(type)) {
+            checkTimeActivityParam(activityDTO.getTimeIntervalInfos());
+        } else if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
+            checkFullActivityParam(activityDTO.getFullReductionInfo());
+        } else if (ActivityTypeEnum.GLOBAL.getTypeCode().equals(type)) {
+            checkGlobalActivityParam(activityDTO.getGlobalDiscountsInfo());
+        }
+    }
+
+    /**
+     * 校验活动时间
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     */
+    private void checkActivityDate(Date startTime, Date endTime) {
+        // 结束时间必须在开始时间之后
+        if (endTime.compareTo(startTime) <= 0) {
+            throw new SqxException("活动结束时间必须在开始时间之后");
+        }
+    }
+
+    /**
+     * 校验全局优惠活动参数信息
+     * @param globalDiscountsInfo 全局优惠信息
+     */
+    private void checkGlobalActivityParam(ActivityOfGlobalDiscountDTO globalDiscountsInfo) {
+        if (ObjectUtil.isNull(globalDiscountsInfo)) {
+            throw new SqxException("全场优惠信息不能为空");
+        }
+
+        if (globalDiscountsInfo.getDiscountRate().doubleValue() >= 10 || globalDiscountsInfo.getDiscountRate().doubleValue() <= 0) {
+            throw new SqxException("全场优惠折扣只能为(0, 10)");
+        }
+    }
+
+    /**
+     * 校验满减优惠活动参数信息
+     * @param fullReductionInfo 满减优惠信息
+     */
+    private void checkFullActivityParam(ActivityOfFullReductionDTO fullReductionInfo) {
+        if (ObjectUtil.isNull(fullReductionInfo)) {
+            throw new SqxException("满减优惠信息不能为空");
+        }
+
+        String type = fullReductionInfo.getType();
+        if (FullActivityTypeEnum.DISCOUNT.getTypeCode().equals(type)) {
+            Double discountContent = fullReductionInfo.getDiscountContent();
+            if (discountContent.doubleValue() >= 10 || discountContent.doubleValue() <= 0) {
+                throw new SqxException("满减优惠折扣只能为(0, 10)");
+            }
+        }
+    }
+
+    /**
+     * 校验时段优惠活动参数信息
+     * @param timeIntervalInfos 时段优惠信息
+     */
+    private void checkTimeActivityParam(List<ActivityOfTimeIntervalDTO> timeIntervalInfos) {
+        if (CollUtil.isEmpty(timeIntervalInfos)) {
+            throw new SqxException("时段优惠信息不能为空");
+        }
+
+        for (ActivityOfTimeIntervalDTO timeIntervalInfo : timeIntervalInfos) {
+            if (timeIntervalInfo.getDiscountContent().doubleValue() == 0 || timeIntervalInfo.getDiscountContent().doubleValue() == 10) {
+                throw new SqxException("时段优惠信息折扣内容取值范围(0-10)");
+            }
+
+            try {
+                TIME_DTF.parse(timeIntervalInfo.getStartTime());
+                TIME_DTF.parse(timeIntervalInfo.getEndTime());
+            } catch (Exception e) {
+                throw new SqxException("时间格式有误,startTime和endTime格式应为HH:mm");
+            }
+        }
+    }
+}

+ 171 - 0
src/main/java/com/sqx/modules/activity/service/impl/ActivityShopServiceImpl.java

@@ -0,0 +1,171 @@
+package com.sqx.modules.activity.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.sqx.common.constant.MyConstant;
+import com.sqx.common.exception.SqxException;
+import com.sqx.common.query.PageQuery;
+import com.sqx.common.utils.PageUtils;
+import com.sqx.modules.activity.dao.ActivityShopDao;
+import com.sqx.modules.activity.dto.JoinActivityDTO;
+import com.sqx.modules.activity.dto.QuitActivityDTO;
+import com.sqx.modules.activity.entity.ActivityShop;
+import com.sqx.modules.activity.enums.LimitTypeEnum;
+import com.sqx.modules.activity.service.ActivityGoodsService;
+import com.sqx.modules.activity.service.ActivityShopService;
+import com.sqx.modules.activity.vo.ActivityShopVO;
+import com.sqx.modules.activity.vo.ShopActivityGroupVO;
+import com.sqx.modules.datacentre.entity.SysUserShop;
+import com.sqx.modules.sys.entity.SysUserEntity;
+import com.sqx.modules.sys.service.SysUserShopService;
+import lombok.RequiredArgsConstructor;
+import org.apache.shiro.SecurityUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 活动商家
+ * @author : codingliang
+ * @date : 2024-6-17
+ */
+@Service
+@RequiredArgsConstructor
+public class ActivityShopServiceImpl extends ServiceImpl<ActivityShopDao, ActivityShop> implements ActivityShopService {
+
+    private final ActivityGoodsService activityGoodsService;
+    private final SysUserShopService sysUserShopService;
+     
+    @Override
+    public void joinActivity(JoinActivityDTO dto) {
+        // 权限校验
+        checkAuth(dto.getShopIds());
+
+        // 校验参数
+        checkParam(dto);
+
+        List<ActivityShop> activityShops = new ArrayList<>();
+        for (Long shopId : dto.getShopIds()) {
+            // 判断当前店铺已经加入的活动数量
+            LambdaQueryWrapper<ActivityShop> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(ActivityShop::getShopId, shopId);
+            List<ActivityShop> oldActivityShops = list(queryWrapper);
+            if (oldActivityShops.size() > MyConstant.MAX_NUM_OF_SHOP_JOIN) {
+                throw new SqxException("店铺【" + shopId + "】参与活动数量达到上限,请退出其他活动后再次尝试加入活动");
+            }
+
+            // 判断当前店铺是否已参加当前活动
+            if (oldActivityShops.stream().map(ActivityShop::getActivityId).filter(e -> e.equals(dto.getActivityId())).findFirst().isPresent()) {
+                throw new SqxException("店铺【" + shopId + "】已经参与当前活动,不能重复参与");
+            }
+
+            // 保存数据
+            ActivityShop activityShop = new ActivityShop();
+            BeanUtils.copyProperties(dto, activityShop);
+            activityShop.setShopId(shopId);
+
+            activityShops.add(activityShop);
+        }
+
+        saveBatch(activityShops);
+    }
+
+    @Transactional
+    @Override
+    public void quitActivity(QuitActivityDTO dto) {
+        checkAuth(Arrays.asList(dto.getShopId()));
+
+        LambdaQueryWrapper<ActivityShop> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.eq(ActivityShop::getShopId, dto.getShopId());
+        queryWrapper.eq(ActivityShop::getActivityId, dto.getActivityId());
+
+        ActivityShop activityShop = this.getOne(queryWrapper);
+
+        if (ObjectUtil.isNotNull(activityShop)) {
+            removeById(activityShop);
+
+            // 同时删除商品
+            activityGoodsService.deleteByActivityShopId(activityShop.getId());
+        }
+
+    }
+
+    @Override
+    public void quitByActivityShopId(Long activityShopId) {
+        ActivityShop activityShop = this.getById(activityShopId);
+
+        if (ObjectUtil.isNotNull(activityShop)) {
+            checkAuth(Arrays.asList(activityShop.getShopId()));
+
+            this.removeById(activityShopId);
+
+            // 同时删除商品
+            activityGoodsService.deleteByActivityShopId(activityShop.getId());
+        }
+    }
+
+    @Override
+    public List<ActivityShop> getByActivityIds(List<Long> activityIds) {
+        LambdaQueryWrapper<ActivityShop> queryWrapper = Wrappers.lambdaQuery();
+        queryWrapper.in(ActivityShop::getActivityId, activityIds);
+        return list(queryWrapper);
+    }
+
+    @Override
+    public PageUtils getShopByActivityId(Long activityId, PageQuery queryDTO) {
+        Page<ActivityShop> pages = new Page<>(queryDTO.getPage(), queryDTO.getLimit());
+        IPage<ActivityShopVO> page = baseMapper.pageShopByActivityId(pages, activityId);
+
+        PageUtils pageUtils = new PageUtils(page);
+        return pageUtils;
+    }
+
+    @Override
+    public ShopActivityGroupVO getShopActivityGroup(Long shopId) {
+        return baseMapper.getShopActivityGroup(shopId);
+    }
+
+    /**
+     * 参数校验
+     * @param dto 加入活动参数
+     */
+    private void checkParam(JoinActivityDTO dto) {
+        String limitType = dto.getLimitType();
+        if (StrUtil.equals(limitType, LimitTypeEnum.NUM_LIMIT.getTypeCode())) {
+            if (ObjectUtil.isNull(dto.getLimitValue())) {
+                throw new SqxException("限制数量不能为空");
+            }
+        }
+    }
+
+    /**
+     * 检查权限
+     *  判断当前用户对相关的店铺有没有操作权限
+     * @param shopIds 店铺列表
+     */
+    private void checkAuth(List<Long> shopIds) {
+        SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
+        // 1平台管理员、2商户管理员
+        Integer userType = user.getUserType();
+        if (userType.intValue() == 2) {
+            if (shopIds.size() > 1) {
+                throw new SqxException("权限不足:商户管理员只能添加自己的店铺到活动中");
+            }
+
+            Long curShopId = shopIds.get(0);
+            SysUserShop userShop = sysUserShopService.getByUserId(user.getUserId());
+            if (ObjectUtil.notEqual(curShopId, userShop.getShopId())) {
+                throw new SqxException("权限不足:商户管理员只能添加自己的店铺到活动中");
+            }
+        }
+    }
+}

+ 38 - 0
src/main/java/com/sqx/modules/activity/vo/ActivityShopVO.java

@@ -0,0 +1,38 @@
+package com.sqx.modules.activity.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 活动商家vo
+ *
+ * @author : codingliang
+ * @date : 2024-06-21 23:29
+ */
+@Data
+public class ActivityShopVO {
+
+    @ApiModelProperty("参与活动id")
+    private Long activityShopId;
+
+    @ApiModelProperty("店铺id")
+    private Long shopId;
+
+    @ApiModelProperty("活动id")
+    private Long activityId;
+
+    @ApiModelProperty("店铺名称")
+    private String shopName;
+
+    @ApiModelProperty("店铺手机号")
+    private String shopPhone;
+
+    @ApiModelProperty("店铺类型")
+    private String shopTypeName;
+
+    @ApiModelProperty("店铺封面图")
+    private String shopImg;
+
+    @ApiModelProperty("店铺轮播图")
+    private String shopSwiperImg;
+}

+ 61 - 0
src/main/java/com/sqx/modules/activity/vo/ActivityVO.java

@@ -0,0 +1,61 @@
+package com.sqx.modules.activity.vo;
+
+import com.sqx.modules.activity.dto.ActivityOfFullReductionDTO;
+import com.sqx.modules.activity.dto.ActivityOfTimeIntervalDTO;
+import com.sqx.modules.activity.dto.ActivityOfGlobalDiscountDTO;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 活动vo
+ *
+ * @author : codingliang
+ * @date : 2024-06-19 10:07
+ */
+@Data
+public class ActivityVO {
+    @ApiModelProperty("活动id")
+    private Long id;
+
+    @ApiModelProperty("活动标题")
+    private String title;
+
+    @ApiModelProperty("活动描述")
+    private String content;
+
+    @ApiModelProperty("活动图片")
+    private String image;
+
+    @ApiModelProperty("是否启用;0否 1是")
+    private String enableFlag;
+
+    @ApiModelProperty("是否允许商户主动加入;0否 1是")
+    private String allowShop;
+
+    @ApiModelProperty("创建时间")
+    private Date createTime;
+
+    @ApiModelProperty("更新时间")
+    private Date updateTime;
+
+    @ApiModelProperty("活动类型;1普通活动、2时段优惠、3满额优惠、4全场优惠")
+    private String type;
+
+    @ApiModelProperty("活动开始时间")
+    private Date startTime;
+
+    @ApiModelProperty("活动结束时间")
+    private Date endTime;
+
+    @ApiModelProperty("优惠时段信息列表;type为2时该字段不为空")
+    private List<ActivityOfTimeIntervalDTO> timeIntervalInfos;
+
+    @ApiModelProperty("满减字段优惠;type为3时该字段不为空")
+    private ActivityOfFullReductionDTO fullReductionInfo;
+
+    @ApiModelProperty("全场优惠;type为4时该字段不为空")
+    private ActivityOfGlobalDiscountDTO globalDiscountsInfo;
+}

+ 40 - 0
src/main/java/com/sqx/modules/activity/vo/ShopActivityGroupVO.java

@@ -0,0 +1,40 @@
+package com.sqx.modules.activity.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 店铺活动vo
+ *
+ * @author : codingliang
+ * @date : 2024-06-22 9:57
+ */
+@Data
+@ApiModel("店铺参与活动分组")
+public class ShopActivityGroupVO {
+
+    @ApiModelProperty("店铺id")
+    private Long shopId;
+
+    private List<ActivityTypeGroup> activityTypeGroups;
+
+    @Data
+    static class ActivityTypeGroup {
+        @ApiModelProperty("活动类型,1普通活动、2时段优惠、3满额优惠、4全场优惠")
+        private String activityType;
+
+        private List<ActivityInGroup> activityInGroups;
+    }
+
+    @Data
+    static class ActivityInGroup {
+        @ApiModelProperty("活动名称")
+        private String activityTitle;
+
+        @ApiModelProperty("活动限制")
+        private String suitType;
+    }
+}

+ 43 - 0
src/main/resources/mapper/activity/ActivityShopDao.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sqx.modules.activity.dao.ActivityShopDao">
+
+
+    <select id="pageShopByActivityId" resultType="com.sqx.modules.activity.vo.ActivityShopVO">
+        select
+            ach.id as activityShopId,
+            ach.shop_id,
+            ach.activity_id,
+            gs.shop_name,
+            gs.phone as shopPhone,
+            st.shop_type_name,
+            gs.shop_cover as shopImg,
+            gs.shop_banner as shopSwiperImg
+        from activity_shop ach
+        left join goods_shop gs on gs.shop_id = ach.shop_id
+        left join shop_type st on st.id = gs.shop_type_id
+        where ach.activity_id = ${activityId}
+    </select>
+
+    <resultMap id="shopActivityGroupVO" type="com.sqx.modules.activity.vo.ShopActivityGroupVO">
+        <result column="shop_id" property="shopId"/>
+        <collection property="activityTypeGroups" ofType="com.sqx.modules.activity.vo.ShopActivityGroupVO$ActivityTypeGroup">
+            <result column="type" property="activityType"/>
+            <collection property="activityInGroups" ofType="com.sqx.modules.activity.vo.ShopActivityGroupVO$ActivityInGroup">
+                <result column="title" property="activityTitle"/>
+                <result column="suit_type" property="suitType"/>
+            </collection>
+        </collection>
+    </resultMap>
+
+    <select id="getShopActivityGroup" resultMap="shopActivityGroupVO">
+        select
+            ach.shop_id,
+            a.type,
+            a.title,
+            ach.suit_type
+        from activity_shop ach
+        left join activity a on a.id = ach.activity_id
+        where ach.shop_id = #{shopId}
+    </select>
+</mapper>