diff --git a/promotion/pom.xml b/promotion/pom.xml
index 8d2f87d2f..870cfb1bd 100644
--- a/promotion/pom.xml
+++ b/promotion/pom.xml
@@ -17,8 +17,9 @@
promotion-biz-api
promotion-rpc-api
promotion-rpc
+ promotion-rest
-
+
diff --git a/promotion/promotion-biz-api/src/main/java/cn/iocoder/mall/promotion/biz/api/enums/PromotionErrorCodeEnum.java b/promotion/promotion-biz-api/src/main/java/cn/iocoder/mall/promotion/biz/api/enums/PromotionErrorCodeEnum.java
new file mode 100644
index 000000000..6f8d03cf3
--- /dev/null
+++ b/promotion/promotion-biz-api/src/main/java/cn/iocoder/mall/promotion/biz/api/enums/PromotionErrorCodeEnum.java
@@ -0,0 +1,53 @@
+package cn.iocoder.mall.promotion.biz.api.enums;
+
+/**
+ * 错误码枚举类
+ *
+ * 营销系统,使用 1-006-000-000 段
+ */
+public enum PromotionErrorCodeEnum {
+
+ // ========== Banner 模块 ==========
+ BANNER_NOT_EXISTS(1006000000, "账号不存在"),
+
+// // ========== PRODUCT RECOMMEND 模块 ==========
+// PRODUCT_RECOMMEND_NOT_EXISTS(1006001000, "商品推荐不存在"),
+// PRODUCT_RECOMMEND_PRODUCT_NOT_EXISTS(1006001001, "商品不存在"),
+// PRODUCT_RECOMMEND_EXISTS(1006001002, "该商品推荐已经存在"),
+//
+//
+// // ========== COUPON TEMPLATE 模块 ==========
+// COUPON_TEMPLATE_NOT_EXISTS(1006002000, "优惠劵模板(码)不存在"),
+// COUPON_TEMPLATE_NOT_CARD(1006002001, "不是优惠劵模板"),
+// COUPON_TEMPLATE_NOT_CODE(1006002002, "不是优惠码模板"),
+// COUPON_TEMPLATE_TOTAL_CAN_NOT_REDUCE(1006002003, "优惠劵(码)模板的发放数量不能减小"),
+// COUPON_TEMPLATE_STATUS_NOT_ENABLE(1006002004, "优惠劵模板(码)未开启"),
+// COUPON_TEMPLATE_TOTAL_NOT_ENOUGH(1006002005, "优惠劵(码)模板的发放量不足"),
+// COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA(1006002006, "优惠劵领取到达上限"),
+//
+// // ========== COUPON CARD 模块 ==========
+// COUPON_CARD_NOT_EXISTS(1006003000, "优惠劵不存在"),
+// COUPON_CARD_ERROR_USER(1006003001, "优惠劵不属于当前用户"),
+// COUPON_CARD_NOT_MATCH(1006003002, "优惠劵不匹配,无法使用"),
+// COUPON_CARD_STATUS_NOT_UNUSED(1006003003, "优惠劵不处于待使用状态"),
+// COUPON_CARD_STATUS_NOT_USED(1006003004, "优惠劵不处于已使用状态"),
+ ;
+
+
+ private final int code;
+ private final String message;
+
+ PromotionErrorCodeEnum(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/promotion/promotion-biz-api/src/main/java/cn/iocoder/mall/promotion/biz/api/package-info.java b/promotion/promotion-biz-api/src/main/java/cn/iocoder/mall/promotion/biz/api/package-info.java
new file mode 100644
index 000000000..ea366ee90
--- /dev/null
+++ b/promotion/promotion-biz-api/src/main/java/cn/iocoder/mall/promotion/biz/api/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * author: sin
+ * time: 2020/5/14 15:25
+ */
+package cn.iocoder.mall.promotion.biz.api;
\ No newline at end of file
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/banner/BannerListBO.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/banner/BannerListBO.java
new file mode 100644
index 000000000..7cf13983b
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/banner/BannerListBO.java
@@ -0,0 +1,50 @@
+package cn.iocoder.mall.promotion.biz.bo.banner;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * banner:list
+ *
+ * author: sin
+ * time: 2020/5/14 16:00
+ */
+@Data
+@Accessors(chain = true)
+public class BannerListBO implements Serializable {
+
+
+ @ApiModelProperty("编号")
+ private Integer id;
+
+ @ApiModelProperty("标题")
+ private String title;
+
+ @ApiModelProperty("跳转链接")
+ private String url;
+
+ @ApiModelProperty("图片链接")
+ private String picUrl;
+
+ @ApiModelProperty("排序")
+ private Integer sort;
+
+ @ApiModelProperty("状态")
+ private Integer status;
+
+ @ApiModelProperty("备注")
+ private String memo;
+
+ //
+ // 其他
+
+ @ApiModelProperty("更新时间")
+ private Date updatedTime;
+
+ @ApiModelProperty("创建时间")
+ private Date createdTime;
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/banner/BannerListOnReleaseBO.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/banner/BannerListOnReleaseBO.java
new file mode 100644
index 000000000..c460f4b3a
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/banner/BannerListOnReleaseBO.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.promotion.biz.bo.banner;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * banner - 已发布的banner
+ *
+ * author: sin
+ * time: 2020/5/14 16:56
+ */
+@Data
+@Accessors(chain = true)
+public class BannerListOnReleaseBO implements Serializable {
+
+ /**
+ * 编号
+ */
+ private Integer id;
+ /**
+ * 标题
+ */
+ private String title;
+ /**
+ * 跳转链接
+ */
+ private String url;
+ /**
+ * 图片链接
+ */
+ private String picUrl;
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/package-info.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/package-info.java
new file mode 100644
index 000000000..b2dce8565
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/bo/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * author: sin
+ * time: 2020/5/14 16:46
+ */
+package cn.iocoder.mall.promotion.biz.bo;
\ No newline at end of file
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/convert/BannerConvert.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/convert/BannerConvert.java
new file mode 100644
index 000000000..59fdcc20d
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/convert/BannerConvert.java
@@ -0,0 +1,26 @@
+package cn.iocoder.mall.promotion.biz.convert;
+
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListBO;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListOnReleaseBO;
+import cn.iocoder.mall.promotion.biz.dataobject.BannerDO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerAddDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerUpdateDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface BannerConvert {
+
+ BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);
+
+ List convert(List bannerDO);
+
+ List convertToBO(List bannerList);
+
+ BannerDO convert(BannerAddDTO bannerAddDTO);
+
+ BannerDO convert(BannerUpdateDTO bannerUpdateDTO);
+
+}
\ No newline at end of file
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dao/BannerMapper.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dao/BannerMapper.java
new file mode 100644
index 000000000..39cdfb5d0
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dao/BannerMapper.java
@@ -0,0 +1,44 @@
+package cn.iocoder.mall.promotion.biz.dao;
+
+import cn.iocoder.mall.promotion.biz.dataobject.BannerDO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerListDTO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+/**
+ * banner
+ *
+ * author: sin
+ * time: 2020/5/14 14:19
+ */
+@Repository
+@Mapper
+public interface BannerMapper extends BaseMapper {
+
+ /**
+ * 查询 - 列表
+ *
+ * @param dto
+ * @return
+ */
+ default IPage selectBannerList(BannerListDTO dto) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (StringUtils.isEmpty(dto.getStatus())) {
+ queryWrapper.eq(BannerDO::getStatus, dto.getStatus());
+ }
+
+ if (StringUtils.isEmpty(dto.getTitle())) {
+ queryWrapper.like(BannerDO::getTitle, dto.getTitle());
+ }
+
+ queryWrapper.orderByDesc(BannerDO::getId);
+ IPage result = selectPage(new Page<>(), queryWrapper);
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/BannerDO.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/BannerDO.java
new file mode 100644
index 000000000..49f4091c3
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dataobject/BannerDO.java
@@ -0,0 +1,47 @@
+package cn.iocoder.mall.promotion.biz.dataobject;
+
+import cn.iocoder.mall.mybatis.dataobject.DeletableDO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * Banner 广告页
+ */
+@Data
+@Accessors(chain = true)
+public class BannerDO extends DeletableDO {
+
+ /**
+ * 编号
+ */
+ private Integer id;
+ /**
+ * 标题
+ */
+ private String title;
+ /**
+ * 跳转链接
+ */
+ private String url;
+ /**
+ * 图片链接
+ */
+ private String picUrl;
+ /**
+ * 排序
+ */
+ private Integer sort;
+ /**
+ * 状态
+ *
+ * {@link cn.iocoder.common.framework.constant.CommonStatusEnum}
+ */
+ private Integer status;
+ /**
+ * 备注
+ */
+ private String memo;
+
+ // TODO 芋艿 点击次数。&& 其他数据相关
+
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerAddDTO.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerAddDTO.java
new file mode 100644
index 000000000..cfea28fff
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerAddDTO.java
@@ -0,0 +1,41 @@
+package cn.iocoder.mall.promotion.biz.dto.banner;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.URL;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * Banner 添加 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class BannerAddDTO implements Serializable {
+
+ @NotNull
+ private Integer adminId;
+
+ @NotEmpty(message = "标题不能为空")
+ @Length(min = 2, max = 32, message = "标题长度为 2-32 位")
+ private String title;
+
+ @NotEmpty(message = "跳转链接不能为空")
+ @URL(message = "跳转链接格式不正确")
+ @Length(max = 255, message = "跳转链接最大长度为 255 位")
+ private String url;
+
+ @NotEmpty(message = "图片链接不能为空")
+ @URL(message = "图片链接格式不正确")
+ @Length(max = 255, message = "图片链接最大长度为 255 位")
+ private String picUrl;
+
+ @NotNull(message = "排序不能为空")
+ private Integer sort;
+
+ @Length(max = 255, message = "备注最大长度为 255 位")
+ private String memo;
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerListDTO.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerListDTO.java
new file mode 100644
index 000000000..96e954d6f
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerListDTO.java
@@ -0,0 +1,26 @@
+package cn.iocoder.mall.promotion.biz.dto.banner;
+
+import cn.iocoder.common.framework.vo.PageParam;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * Banner 分页 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class BannerListDTO extends PageParam {
+
+ @ApiModelProperty("标题")
+ @NotNull(message = "页码不能为空")
+ private String title;
+
+ @ApiModelProperty("标题")
+ @NotNull(message = "页码不能为空")
+ private Integer status;
+
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerUpdateDTO.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerUpdateDTO.java
new file mode 100644
index 000000000..a522796fe
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/banner/BannerUpdateDTO.java
@@ -0,0 +1,45 @@
+package cn.iocoder.mall.promotion.biz.dto.banner;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.URL;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * Banner 更新 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class BannerUpdateDTO implements Serializable {
+
+ @NotNull
+ Integer adminId;
+
+ @NotNull(message = "编号不能为空")
+ private Integer id;
+
+ @NotEmpty(message = "标题不能为空")
+ @Length(min = 2, max = 32, message = "标题长度为 2-32 位")
+ private String title;
+
+ @NotEmpty(message = "跳转链接不能为空")
+ @URL(message = "跳转链接格式不正确")
+ @Length(max = 255, message = "跳转链接最大长度为 255 位")
+ private String url;
+
+ @NotEmpty(message = "图片链接不能为空")
+ @URL(message = "图片链接格式不正确")
+ @Length(max = 255, message = "图片链接最大长度为 255 位")
+ private String picUrl;
+
+ @NotNull(message = "排序不能为空")
+ private Integer sort;
+
+ @Length(max = 255, message = "备注最大长度为 255 位")
+ private String memo;
+
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/package-info.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/package-info.java
new file mode 100644
index 000000000..c7f03ef5b
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/dto/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * author: sin
+ * time: 2020/5/14 16:46
+ */
+package cn.iocoder.mall.promotion.biz.dto;
\ No newline at end of file
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/package-info.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/package-info.java
new file mode 100644
index 000000000..5435c0654
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/package-info.java
@@ -0,0 +1,6 @@
+
+/**
+ * author: sin
+ * time: 2020/5/14 14:19
+ */
+package cn.iocoder.mall.promotion.biz;
\ No newline at end of file
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/banner/BannerService.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/banner/BannerService.java
new file mode 100644
index 000000000..aea1db0d2
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/banner/BannerService.java
@@ -0,0 +1,68 @@
+package cn.iocoder.mall.promotion.biz.service.banner;
+
+import cn.iocoder.common.framework.constant.CommonStatusEnum;
+import cn.iocoder.common.framework.validator.InEnum;
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListBO;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListOnReleaseBO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerAddDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerListDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerUpdateDTO;
+
+import java.util.List;
+
+/**
+ * banner
+ *
+ * author: sin
+ * time: 2020/5/14 14:19
+ */
+public interface BannerService {
+
+ /**
+ * 列表 - 获取已发布的banner
+ *
+ * @return
+ */
+ List listBannerOnRelease();
+
+ /**
+ * 列表 - banner 列表
+ *
+ * @param bannerPageDTO
+ * @return
+ */
+ PageResult listBanner(BannerListDTO bannerPageDTO);
+
+ /**
+ * 添加 - 一个banner
+ *
+ * @param adminsBannerAddDTO
+ */
+ void addBanner(BannerAddDTO adminsBannerAddDTO);
+
+ /**
+ * 更新 - 根据id更新
+ *
+ * @param adminsBannerUpdateDTO
+ */
+ void updateBanner(BannerUpdateDTO adminsBannerUpdateDTO);
+
+ /**
+ * 更新 - banner 状态
+ *
+ * @param adminId
+ * @param bannerId
+ * @param status
+ * @return
+ */
+ void updateBannerStatus(Integer adminId, Integer bannerId, @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") Integer status);
+
+ /**
+ * 删除 - 根据id删除一个banner
+ *
+ * @param adminId
+ * @param bannerId
+ */
+ void deleteBanner(Integer adminId, Integer bannerId);
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/banner/BannerServiceImpl.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/banner/BannerServiceImpl.java
new file mode 100644
index 000000000..830a9fe7f
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/banner/BannerServiceImpl.java
@@ -0,0 +1,105 @@
+package cn.iocoder.mall.promotion.biz.service.banner;
+
+import cn.iocoder.common.framework.constant.CommonStatusEnum;
+import cn.iocoder.common.framework.util.ServiceExceptionUtil;
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.mybatis.enums.DeletedStatusEnum;
+import cn.iocoder.mall.promotion.biz.api.enums.PromotionErrorCodeEnum;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListBO;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListOnReleaseBO;
+import cn.iocoder.mall.promotion.biz.convert.BannerConvert;
+import cn.iocoder.mall.promotion.biz.dao.BannerMapper;
+import cn.iocoder.mall.promotion.biz.dataobject.BannerDO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerAddDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerListDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerUpdateDTO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * banner
+ *
+ * author: sin
+ * time: 2020/5/14 14:19
+ */
+@Service
+public class BannerServiceImpl implements BannerService {
+
+ @Autowired
+ private BannerMapper bannerMapper;
+
+ @Override
+ public List listBannerOnRelease() {
+ PageResult pageResult = this.listBanner(
+ (BannerListDTO) new BannerListDTO()
+ .setStatus(CommonStatusEnum.ENABLE.getValue())
+ .setTitle(null)
+ .setPageNo(1)
+ .setPageSize(10)
+ );
+ return BannerConvert.INSTANCE.convertToBO(pageResult.getList());
+ }
+
+ @Override
+ public PageResult listBanner(BannerListDTO dto) {
+ IPage page = bannerMapper.selectBannerList(dto);
+ List list = BannerConvert.INSTANCE.convert(page.getRecords());
+ return new PageResult().setList(list).setTotal(page.getTotal());
+ }
+
+ @Override
+ public void addBanner(BannerAddDTO adminsBannerAddDTO) {
+ // 转换DO
+ BannerDO banner = BannerConvert.INSTANCE.convert(adminsBannerAddDTO);
+ // 设置默认数据
+ banner.setStatus(CommonStatusEnum.ENABLE.getValue());
+ banner.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
+ banner.setCreateTime(new Date());
+ // 保存数据
+ bannerMapper.insert(banner);
+ }
+
+ @Override
+ public void updateBanner(BannerUpdateDTO adminsBannerUpdateDTO) {
+ // 校验 Banner 存在
+ if (bannerMapper.selectById(adminsBannerUpdateDTO.getId()) == null) {
+ throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.BANNER_NOT_EXISTS.getCode());
+ }
+ // 更新到数据库
+ BannerDO updateBanner = BannerConvert.INSTANCE.convert(adminsBannerUpdateDTO);
+ updateBanner.setUpdateTime(new Date());
+ bannerMapper.updateById(updateBanner);
+ }
+
+ @Override
+ public void updateBannerStatus(Integer adminId, Integer bannerId, Integer status) {
+ // 校验 Banner 存在
+ if (bannerMapper.selectById(bannerId) == null) {
+ throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.BANNER_NOT_EXISTS.getCode());
+ }
+ // 更新到数据库
+ BannerDO updateBanner = new BannerDO();
+ updateBanner.setId(bannerId);
+ updateBanner.setStatus(status);
+ updateBanner.setUpdateTime(new Date());
+ bannerMapper.updateById(updateBanner);
+ }
+
+ @Override
+ public void deleteBanner(Integer adminId, Integer bannerId) {
+ // 校验 Banner 存在
+ if (bannerMapper.selectById(bannerId) == null) {
+ throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.BANNER_NOT_EXISTS.getCode());
+ }
+ // 更新到数据库
+ BannerDO updateBanner = new BannerDO();
+ updateBanner.setId(bannerId);
+ updateBanner.setUpdateTime(new Date());
+ updateBanner.setDeleted(DeletedStatusEnum.DELETED_YES.getValue());
+ bannerMapper.updateById(updateBanner);
+ }
+}
diff --git a/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/package-info.java b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/package-info.java
new file mode 100644
index 000000000..bdfe46ced
--- /dev/null
+++ b/promotion/promotion-biz/src/main/java/cn/iocoder/mall/promotion/biz/service/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * author: sin
+ * time: 2020/5/14 16:47
+ */
+package cn.iocoder.mall.promotion.biz.service;
\ No newline at end of file
diff --git a/promotion/promotion-rest/pom.xml b/promotion/promotion-rest/pom.xml
new file mode 100644
index 000000000..d5451a0ab
--- /dev/null
+++ b/promotion/promotion-rest/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ promotion
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ promotion-rest
+
+
+
+
+ cn.iocoder.mall
+ promotion-biz
+ 1.0-SNAPSHOT
+
+
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-web
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-security
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-swagger
+ 1.0-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/banner/AdminsBannerController.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/banner/AdminsBannerController.java
new file mode 100644
index 000000000..d47689dc0
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/banner/AdminsBannerController.java
@@ -0,0 +1,84 @@
+package cn.iocoder.mall.promotion.rest.controller.banner;
+
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListBO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerAddDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerListDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerUpdateDTO;
+import cn.iocoder.mall.promotion.biz.service.banner.BannerService;
+import cn.iocoder.mall.promotion.rest.convert.BannerConvert;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerAddRequest;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerListRequest;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerUpdateRequest;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerUpdateStatusRequest;
+import cn.iocoder.mall.promotion.rest.response.banner.BannerListResponse;
+import cn.iocoder.mall.security.core.context.AdminSecurityContextHolder;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * Banner(管理员API)
+ *
+ * author: sin
+ * time: 2020/5/14 15:27
+ */
+@RestController
+@RequestMapping("/admins/banner")
+@Api(tags = "Banner(管理员API)")
+public class AdminsBannerController {
+
+ @Autowired
+ private BannerService bannerService;
+
+ @PostMapping("/list")
+ @ApiOperation(value = "列表-banner列表")
+ public CommonResult> page(@RequestBody @Valid BannerListRequest request) {
+ // 获取数据
+ BannerListDTO pageDTO = BannerConvert.INSTANCE.convert(request);
+ PageResult pageResult = bannerService.listBanner(pageDTO);
+ // 转换 response
+ List responseList = BannerConvert.INSTANCE.convert(pageResult.getList());
+ return CommonResult.success(new PageResult().setList(responseList).setTotal(pageResult.getTotal()));
+ }
+
+ @PostMapping("/add")
+ @ApiOperation(value = "添加-Banner")
+ public CommonResult add(@RequestBody @Valid BannerAddRequest request) {
+ BannerAddDTO bannerAddDTO = BannerConvert.INSTANCE.convert(request);
+ bannerAddDTO.setAdminId(AdminSecurityContextHolder.getContext().getAdminId());
+ bannerService.addBanner(bannerAddDTO);
+ return CommonResult.success(null);
+ }
+
+ @PutMapping("/update")
+ @ApiOperation(value = "更新-Banner信息")
+ public CommonResult update(@RequestBody @Valid BannerUpdateRequest request) {
+ BannerUpdateDTO bannerUpdateDTO = BannerConvert.INSTANCE.convert(request);
+ bannerUpdateDTO.setAdminId(AdminSecurityContextHolder.getContext().getAdminId());
+ bannerService.updateBanner(bannerUpdateDTO);
+ return CommonResult.success(null);
+ }
+
+ @PutMapping("/update-status")
+ @ApiOperation(value = "更新-banner状态")
+ public CommonResult updateStatus(@RequestBody @Valid BannerUpdateStatusRequest request) {
+ Integer adminId = AdminSecurityContextHolder.getContext().getAdminId();
+ bannerService.updateBannerStatus(adminId, request.getBannerId(), request.getStatus());
+ return CommonResult.success(null);
+ }
+
+ @DeleteMapping("/delete")
+ @ApiOperation(value = "删除-根据id删除")
+ @ApiImplicitParam(name = "id", value = "Banner 编号", required = true, example = "1")
+ public CommonResult delete(@RequestParam("id") Integer id) {
+ bannerService.deleteBanner(AdminSecurityContextHolder.getContext().getAdminId(), id);
+ return CommonResult.success(null);
+ }
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/banner/UsersBannerController.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/banner/UsersBannerController.java
new file mode 100644
index 000000000..4d69100d7
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/banner/UsersBannerController.java
@@ -0,0 +1,37 @@
+package cn.iocoder.mall.promotion.rest.controller.banner;
+
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListOnReleaseBO;
+import cn.iocoder.mall.promotion.biz.service.banner.BannerService;
+import cn.iocoder.mall.promotion.rest.convert.BannerConvert;
+import cn.iocoder.mall.promotion.rest.response.banner.BannerListOnReleaseResponse;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * Banner(用户API)
+ *
+ * author: sin
+ * time: 2020/5/14 15:27
+ */
+@RestController
+@RequestMapping("/users/banner")
+@Api(tags = "Banner(用户API)")
+public class UsersBannerController {
+
+ @Autowired
+ private BannerService bannerService;
+
+ @GetMapping("/listBannerOnRelease")
+ @ApiOperation("获取-已发布的banner")
+ public CommonResult> listBannerOnRelease() {
+ List releaseBOList = bannerService.listBannerOnRelease();
+ return CommonResult.success(BannerConvert.INSTANCE.convertReleaseResponse(releaseBOList));
+ }
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/package-info.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/package-info.java
new file mode 100644
index 000000000..636b0a891
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/controller/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * author: sin
+ * time: 2020/5/14 15:27
+ */
+package cn.iocoder.mall.promotion.rest.controller;
\ No newline at end of file
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/convert/BannerConvert.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/convert/BannerConvert.java
new file mode 100644
index 000000000..19a1f502c
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/convert/BannerConvert.java
@@ -0,0 +1,33 @@
+package cn.iocoder.mall.promotion.rest.convert;
+
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListBO;
+import cn.iocoder.mall.promotion.biz.bo.banner.BannerListOnReleaseBO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerAddDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerUpdateDTO;
+import cn.iocoder.mall.promotion.biz.dto.banner.BannerListDTO;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerAddRequest;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerListRequest;
+import cn.iocoder.mall.promotion.rest.request.banner.BannerUpdateRequest;
+import cn.iocoder.mall.promotion.rest.response.banner.BannerListResponse;
+import cn.iocoder.mall.promotion.rest.response.banner.BannerListOnReleaseResponse;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface BannerConvert {
+
+ BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);
+
+ BannerAddDTO convert(BannerAddRequest request);
+
+ BannerUpdateDTO convert(BannerUpdateRequest request);
+
+ BannerListDTO convert(BannerListRequest request);
+
+ List convert(List bannerListBO);
+
+ List convertReleaseResponse(List bannerListOnReleaseBOS);
+
+}
\ No newline at end of file
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerAddRequest.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerAddRequest.java
new file mode 100644
index 000000000..654f363a5
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerAddRequest.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.promotion.rest.request.banner;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * banner:更新 banner
+ *
+ * author: sin
+ * time: 2020/5/14 15:44
+ */
+@Data
+@Accessors(chain = true)
+public class BannerAddRequest implements Serializable {
+
+ @NotNull
+ @ApiModelProperty("跳转链接")
+ private Integer url;
+
+ @NotNull
+ @ApiModelProperty("图片链接")
+ private Integer picUrl;
+
+ @NotNull
+ @ApiModelProperty("排序")
+ private Integer sort;
+
+ @NotNull
+ @ApiModelProperty("备注")
+ private Integer memo;
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerListRequest.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerListRequest.java
new file mode 100644
index 000000000..ec08a13d8
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerListRequest.java
@@ -0,0 +1,27 @@
+package cn.iocoder.mall.promotion.rest.request.banner;
+
+import cn.iocoder.common.framework.vo.PageParam;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * banner:更新 banner
+ *
+ * author: sin
+ * time: 2020/5/14 15:44
+ */
+@Data
+@Accessors(chain = true)
+public class BannerListRequest extends PageParam {
+
+ @ApiModelProperty("标题")
+ private String title;
+
+ @ApiModelProperty("状态")
+ private Integer status;
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerUpdateRequest.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerUpdateRequest.java
new file mode 100644
index 000000000..38f4171b6
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerUpdateRequest.java
@@ -0,0 +1,39 @@
+package cn.iocoder.mall.promotion.rest.request.banner;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * banner:更新 banner
+ *
+ * author: sin
+ * time: 2020/5/14 15:44
+ */
+@Data
+@Accessors(chain = true)
+public class BannerUpdateRequest implements Serializable {
+
+ @NotNull
+ @ApiModelProperty("banner编号")
+ private Integer bannerId;
+
+ @NotNull
+ @ApiModelProperty("跳转链接")
+ private Integer url;
+
+ @NotNull
+ @ApiModelProperty("图片链接")
+ private Integer picUrl;
+
+ @NotNull
+ @ApiModelProperty("排序")
+ private Integer sort;
+
+ @NotNull
+ @ApiModelProperty("备注")
+ private Integer memo;
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerUpdateStatusRequest.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerUpdateStatusRequest.java
new file mode 100644
index 000000000..025261849
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/banner/BannerUpdateStatusRequest.java
@@ -0,0 +1,27 @@
+package cn.iocoder.mall.promotion.rest.request.banner;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * banner:更新 status
+ *
+ * author: sin
+ * time: 2020/5/14 15:44
+ */
+@Data
+@Accessors(chain = true)
+public class BannerUpdateStatusRequest implements Serializable {
+
+ @NotNull
+ @ApiModelProperty("banner编号")
+ private Integer bannerId;
+
+ @NotNull
+ @ApiModelProperty("status状态")
+ private Integer status;
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/package-info.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/package-info.java
new file mode 100644
index 000000000..d4f960f57
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/request/package-info.java
@@ -0,0 +1,6 @@
+
+/**
+ * author: sin
+ * time: 2020/5/14 17:00
+ */
+package cn.iocoder.mall.promotion.rest.request;
\ No newline at end of file
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/banner/BannerListOnReleaseResponse.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/banner/BannerListOnReleaseResponse.java
new file mode 100644
index 000000000..8a880b437
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/banner/BannerListOnReleaseResponse.java
@@ -0,0 +1,34 @@
+package cn.iocoder.mall.promotion.rest.response.banner;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * banner - 已发布的banner
+ *
+ * author: sin
+ * time: 2020/5/14 16:56
+ */
+@Data
+@Accessors(chain = true)
+public class BannerListOnReleaseResponse implements Serializable {
+
+ /**
+ * 编号
+ */
+ private Integer id;
+ /**
+ * 标题
+ */
+ private String title;
+ /**
+ * 跳转链接
+ */
+ private String url;
+ /**
+ * 图片链接
+ */
+ private String picUrl;
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/banner/BannerListResponse.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/banner/BannerListResponse.java
new file mode 100644
index 000000000..6357d5048
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/banner/BannerListResponse.java
@@ -0,0 +1,49 @@
+package cn.iocoder.mall.promotion.rest.response.banner;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * banner:list
+ *
+ * author: sin
+ * time: 2020/5/14 16:00
+ */
+@Data
+@Accessors(chain = true)
+public class BannerListResponse implements Serializable {
+
+ @ApiModelProperty("编号")
+ private Integer id;
+
+ @ApiModelProperty("标题")
+ private String title;
+
+ @ApiModelProperty("跳转链接")
+ private String url;
+
+ @ApiModelProperty("图片链接")
+ private String picUrl;
+
+ @ApiModelProperty("排序")
+ private Integer sort;
+
+ @ApiModelProperty("状态")
+ private Integer status;
+
+ @ApiModelProperty("备注")
+ private String memo;
+
+ //
+ // 其他
+
+ @ApiModelProperty("更新时间")
+ private Date updatedTime;
+
+ @ApiModelProperty("创建时间")
+ private Date createdTime;
+}
diff --git a/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/package-info.java b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/package-info.java
new file mode 100644
index 000000000..4fd8e1cce
--- /dev/null
+++ b/promotion/promotion-rest/src/main/java/cn/iocoder/mall/promotion/rest/response/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * author: sin
+ * time: 2020/5/14 15:26
+ */
+package cn.iocoder.mall.promotion.rest.response;
\ No newline at end of file