first commit

This commit is contained in:
dev
2026-02-22 12:12:02 +08:00
commit b236048a24
1467 changed files with 160403 additions and 0 deletions
@@ -0,0 +1,204 @@
# 营销中心模块 (ruoyi-mall-promotion)
## 模块简介
`ruoyi-mall-promotion` 是商城系统的营销中心模块,提供多样化的营销活动功能,包括活动、拼团、秒杀、砍价等。
## 功能特性
### 1. 营销活动管理 ✅
- ✅ 活动CRUD操作
- ✅ 活动类型管理(普通活动、拼团、秒杀、砍价)
- ✅ 活动规则配置(JSON格式)
- ✅ 活动时间管理
- ✅ 活动状态管理(启用/禁用)
- ✅ 活动导出功能
### 2. 拼团功能 ✅
- ✅ 拼团活动创建
- ✅ 拼团活动查询(按SKU、可用列表)
- ✅ 拼团活动状态管理
- ⏳ 拼团订单管理(需在交易中心模块集成)
- ⏳ 拼团成团处理(需在交易中心模块集成)
- ⏳ 拼团过期处理(需定时任务支持)
### 3. 秒杀功能 ✅
- ✅ 秒杀活动创建
- ✅ 秒杀库存管理(扣减、恢复)
- ✅ 秒杀活动查询(按SKU、可用列表)
- ✅ 秒杀活动状态管理
- ✅ 秒杀活动状态自动更新(定时任务)
### 4. 砍价功能 ✅
- ✅ 砍价活动创建
- ✅ 砍价活动查询(按SKU、可用列表)
- ✅ 砍价活动状态管理
- ⏳ 砍价记录管理(需在交易中心模块集成)
- ⏳ 砍价助力记录(需在交易中心模块集成)
- ⏳ 砍价订单处理(需在交易中心模块集成)
## 数据库表
- `mall_promotion` - 营销活动表
- `mall_group_buy` - 拼团表
- `mall_group_buy_order` - 拼团订单表
- `mall_seckill` - 秒杀表
- `mall_bargain` - 砍价活动表
- `mall_bargain_record` - 砍价记录表
- `mall_bargain_help` - 砍价助力记录表
详细表结构请参考:`script/sql/ry_mall.sql`
## 模块结构
```
org.dromara.mall.promotion
├── controller
│ ├── admin // 管理端API
│ └── app // 用户端API
├── domain
│ ├── bo // 业务对象
│ └── vo // 视图对象
├── mapper // MyBatis Mapper
├── service // 业务逻辑层
│ └── impl
├── job // 定时任务
│ └── MallPromotionJob.java
├── enums // 枚举类
└── config // 配置类
```
## 依赖模块
- `ruoyi-mall-product` - 商品中心模块
- `ruoyi-mall-coupon` - 优惠券中心模块
- `ruoyi-common-*` - 通用工具模块
## API接口
### 管理端接口
#### 营销活动管理 (`/mall/promotion`)
- `GET /mall/promotion/list` - 查询营销活动列表(分页)
- `GET /mall/promotion/{id}` - 获取营销活动详情
- `POST /mall/promotion` - 新增营销活动
- `PUT /mall/promotion` - 修改营销活动
- `DELETE /mall/promotion/{ids}` - 删除营销活动
- `POST /mall/promotion/export` - 导出营销活动列表
- `PUT /mall/promotion/changeStatus/{id}` - 启用/禁用营销活动
### 移动端接口
#### 营销活动 (`/app/mall/promotion`)
- `GET /app/mall/promotion/list` - 查询营销活动列表(可按类型筛选)
- `GET /app/mall/promotion/{id}` - 获取营销活动详情
#### 秒杀活动管理 (`/mall/seckill`)
- `GET /mall/seckill/list` - 查询秒杀活动列表(分页)
- `GET /mall/seckill/{id}` - 获取秒杀活动详情
- `POST /mall/seckill` - 新增秒杀活动
- `PUT /mall/seckill` - 修改秒杀活动
- `DELETE /mall/seckill/{ids}` - 删除秒杀活动
- `POST /mall/seckill/export` - 导出秒杀活动列表
- `PUT /mall/seckill/changeStatus/{id}` - 启用/禁用秒杀活动
#### 秒杀活动 (`/app/mall/seckill`)
- `GET /app/mall/seckill/list` - 查询可用秒杀活动列表
- `GET /app/mall/seckill/{id}` - 获取秒杀活动详情
- `GET /app/mall/seckill/sku/{skuId}` - 根据SKU ID查询秒杀活动
#### 拼团活动管理 (`/mall/groupbuy`)
- `GET /mall/groupbuy/list` - 查询拼团活动列表(分页)
- `GET /mall/groupbuy/{id}` - 获取拼团活动详情
- `POST /mall/groupbuy` - 新增拼团活动
- `PUT /mall/groupbuy` - 修改拼团活动
- `DELETE /mall/groupbuy/{ids}` - 删除拼团活动
- `POST /mall/groupbuy/export` - 导出拼团活动列表
- `PUT /mall/groupbuy/changeStatus/{id}` - 启用/禁用拼团活动
#### 拼团活动 (`/app/mall/groupbuy`)
- `GET /app/mall/groupbuy/list` - 查询可用拼团活动列表
- `GET /app/mall/groupbuy/{id}` - 获取拼团活动详情
- `GET /app/mall/groupbuy/sku/{skuId}` - 根据SKU ID查询拼团活动
#### 砍价活动管理 (`/mall/bargain`)
- `GET /mall/bargain/list` - 查询砍价活动列表(分页)
- `GET /mall/bargain/{id}` - 获取砍价活动详情
- `POST /mall/bargain` - 新增砍价活动
- `PUT /mall/bargain` - 修改砍价活动
- `DELETE /mall/bargain/{ids}` - 删除砍价活动
- `POST /mall/bargain/export` - 导出砍价活动列表
- `PUT /mall/bargain/changeStatus/{id}` - 启用/禁用砍价活动
#### 砍价活动 (`/app/mall/bargain`)
- `GET /app/mall/bargain/list` - 查询可用砍价活动列表
- `GET /app/mall/bargain/{id}` - 获取砍价活动详情
- `GET /app/mall/bargain/sku/{skuId}` - 根据SKU ID查询砍价活动
## 待实现功能
- [x] 营销活动管理功能 ✅
- [x] 拼团功能 ✅(基础功能已完成,订单相关功能需在交易中心模块集成)
- [x] 秒杀功能 ✅
- [x] 砍价功能 ✅(基础功能已完成,记录和订单相关功能需在交易中心模块集成)
- [ ] 活动统计功能
- [ ] 拼团订单管理和成团处理(需在交易中心模块集成)
- [ ] 砍价记录和助力记录管理(需在交易中心模块集成)
## 使用说明
### 秒杀库存管理
秒杀库存使用独立的库存字段 `seckill_stock`,与商品SKU库存分离。在创建秒杀订单时,需要:
1. 调用 `reduceStock` 方法扣减秒杀库存
2. 如果订单取消,调用 `restoreStock` 方法恢复秒杀库存
3. 秒杀库存扣减失败表示库存不足,订单创建失败
## 使用说明
### 秒杀库存管理
秒杀库存使用独立的库存字段 `seckill_stock`,与商品SKU库存分离。在创建秒杀订单时,需要:
1. 调用 `reduceStock` 方法扣减秒杀库存
2. 如果订单取消,调用 `restoreStock` 方法恢复秒杀库存
3. 秒杀库存扣减失败表示库存不足,订单创建失败
### 拼团活动管理
拼团活动配置包括:
- `groupPrice`: 拼团价格(分)
- `groupSize`: 成团人数(至少2人)
- `durationHours`: 拼团有效期(小时,默认24小时)
注意:拼团功能只实现了活动管理,实际的拼团订单创建、成团处理等逻辑需要在交易中心模块(ruoyi-mall-trade)中集成。
### 砍价活动管理
砍价活动配置包括:
- `originalPrice`: 原价(分)
- `bargainPrice`: 砍后价(分)
- `minPrice`: 最低价(分)
- `helpCount`: 需要助力人数(可选,默认0
- `bargainStock`: 砍价库存
- `durationHours`: 砍价有效期(小时,默认24小时)
价格校验规则:
- 砍后价必须小于原价
- 砍后价不能小于最低价
- 最低价必须小于原价
注意:砍价功能只实现了活动管理,实际的砍价记录、助力记录、订单创建等逻辑需要在交易中心模块(ruoyi-mall-trade)中集成。
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-mall</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-mall-promotion</artifactId>
<description>
ruoyi-mall-promotion 营销中心模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-web</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-excel</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-satoken</artifactId>
</dependency>
<!-- 商品中心模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-mall-product</artifactId>
<version>${revision}</version>
</dependency>
<!-- 优惠券模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-mall-coupon</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,23 @@
package org.dromara.mall.promotion.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 营销中心自动配置类
* 主要用于扫描营销中心模块的组件
*
* @author pengles
*/
@Slf4j
@Configuration
@ComponentScan(basePackages = "org.dromara.mall.promotion")
@ConditionalOnProperty(prefix = "ruoyi.mall.promotion", name = "enabled", havingValue = "true", matchIfMissing = true)
public class MallPromotionAutoConfiguration {
public MallPromotionAutoConfiguration() {
log.info("营销中心模块已启用");
}
}
@@ -0,0 +1,126 @@
package org.dromara.mall.promotion.controller.admin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.bo.MallBargainBo;
import org.dromara.mall.promotion.domain.vo.MallBargainVo;
import org.dromara.mall.promotion.service.MallBargainService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
* 砍价活动管理
*
* @author pengles
*/
@Tag(name = "管理端-砍价活动管理", description = "砍价活动管理接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mall/bargain")
public class AdminMallBargainController extends BaseController {
private final MallBargainService mallBargainService;
/**
* 查询砍价活动列表
*/
@Operation(summary = "查询砍价活动列表", description = "分页查询砍价活动列表")
@SaCheckPermission("mall:bargain:list")
@GetMapping("/list")
public TableDataInfo<MallBargainVo> list(@Parameter(description = "砍价活动查询条件") MallBargainBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallBargainService.queryPageList(bo, pageQuery);
}
/**
* 导出砍价活动列表
*/
@Operation(summary = "导出砍价活动列表", description = "导出砍价活动到Excel")
@SaCheckPermission("mall:bargain:export")
@Log(title = "砍价活动", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "砍价活动查询条件") MallBargainBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallBargainVo> list = mallBargainService.queryList(bo);
ExcelUtil.exportExcel(list, "砍价活动", MallBargainVo.class, response);
}
/**
* 获取砍价活动详细信息
*/
@Operation(summary = "获取砍价活动详情", description = "根据砍价活动ID获取详细信息")
@SaCheckPermission("mall:bargain:query")
@GetMapping("/{id}")
public R<MallBargainVo> getInfo(@Parameter(description = "砍价活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallBargainService.queryById(id));
}
/**
* 新增砍价活动
*/
@Operation(summary = "新增砍价活动", description = "创建新的砍价活动")
@SaCheckPermission("mall:bargain:add")
@Log(title = "砍价活动", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "砍价活动信息") @Validated @RequestBody MallBargainBo bo) {
return toAjax(mallBargainService.insertByBo(bo));
}
/**
* 修改砍价活动
*/
@Operation(summary = "修改砍价活动", description = "更新砍价活动信息")
@SaCheckPermission("mall:bargain:edit")
@Log(title = "砍价活动", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "砍价活动信息") @Validated @RequestBody MallBargainBo bo) {
return toAjax(mallBargainService.updateByBo(bo));
}
/**
* 删除砍价活动
*/
@Operation(summary = "删除砍价活动", description = "删除砍价活动")
@SaCheckPermission("mall:bargain:remove")
@Log(title = "砍价活动", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "砍价活动ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallBargainService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 启用/禁用砍价活动
*/
@Operation(summary = "启用/禁用砍价活动", description = "更改砍价活动状态")
@SaCheckPermission("mall:bargain:edit")
@Log(title = "砍价活动", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus/{id}")
public R<Void> changeStatus(@Parameter(description = "砍价活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id,
@Parameter(description = "状态(0启用 1禁用)", required = true)
@RequestParam @NotNull(message = "状态不能为空") Integer status) {
return toAjax(mallBargainService.changeStatus(id, status));
}
}
@@ -0,0 +1,126 @@
package org.dromara.mall.promotion.controller.admin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.bo.MallGroupBuyBo;
import org.dromara.mall.promotion.domain.vo.MallGroupBuyVo;
import org.dromara.mall.promotion.service.MallGroupBuyService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
* 拼团活动管理
*
* @author pengles
*/
@Tag(name = "管理端-拼团活动管理", description = "拼团活动管理接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mall/groupbuy")
public class AdminMallGroupBuyController extends BaseController {
private final MallGroupBuyService mallGroupBuyService;
/**
* 查询拼团活动列表
*/
@Operation(summary = "查询拼团活动列表", description = "分页查询拼团活动列表")
@SaCheckPermission("mall:groupbuy:list")
@GetMapping("/list")
public TableDataInfo<MallGroupBuyVo> list(@Parameter(description = "拼团活动查询条件") MallGroupBuyBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallGroupBuyService.queryPageList(bo, pageQuery);
}
/**
* 导出拼团活动列表
*/
@Operation(summary = "导出拼团活动列表", description = "导出拼团活动到Excel")
@SaCheckPermission("mall:groupbuy:export")
@Log(title = "拼团活动", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "拼团活动查询条件") MallGroupBuyBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallGroupBuyVo> list = mallGroupBuyService.queryList(bo);
ExcelUtil.exportExcel(list, "拼团活动", MallGroupBuyVo.class, response);
}
/**
* 获取拼团活动详细信息
*/
@Operation(summary = "获取拼团活动详情", description = "根据拼团活动ID获取详细信息")
@SaCheckPermission("mall:groupbuy:query")
@GetMapping("/{id}")
public R<MallGroupBuyVo> getInfo(@Parameter(description = "拼团活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallGroupBuyService.queryById(id));
}
/**
* 新增拼团活动
*/
@Operation(summary = "新增拼团活动", description = "创建新的拼团活动")
@SaCheckPermission("mall:groupbuy:add")
@Log(title = "拼团活动", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "拼团活动信息") @Validated @RequestBody MallGroupBuyBo bo) {
return toAjax(mallGroupBuyService.insertByBo(bo));
}
/**
* 修改拼团活动
*/
@Operation(summary = "修改拼团活动", description = "更新拼团活动信息")
@SaCheckPermission("mall:groupbuy:edit")
@Log(title = "拼团活动", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "拼团活动信息") @Validated @RequestBody MallGroupBuyBo bo) {
return toAjax(mallGroupBuyService.updateByBo(bo));
}
/**
* 删除拼团活动
*/
@Operation(summary = "删除拼团活动", description = "删除拼团活动")
@SaCheckPermission("mall:groupbuy:remove")
@Log(title = "拼团活动", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "拼团活动ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallGroupBuyService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 启用/禁用拼团活动
*/
@Operation(summary = "启用/禁用拼团活动", description = "更改拼团活动状态")
@SaCheckPermission("mall:groupbuy:edit")
@Log(title = "拼团活动", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus/{id}")
public R<Void> changeStatus(@Parameter(description = "拼团活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id,
@Parameter(description = "状态(0启用 1禁用)", required = true)
@RequestParam @NotNull(message = "状态不能为空") Integer status) {
return toAjax(mallGroupBuyService.changeStatus(id, status));
}
}
@@ -0,0 +1,126 @@
package org.dromara.mall.promotion.controller.admin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.bo.MallPromotionBo;
import org.dromara.mall.promotion.domain.vo.MallPromotionVo;
import org.dromara.mall.promotion.service.MallPromotionService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
* 营销活动管理
*
* @author pengles
*/
@Tag(name = "管理端-营销活动管理", description = "营销活动管理接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mall/promotion")
public class AdminMallPromotionController extends BaseController {
private final MallPromotionService mallPromotionService;
/**
* 查询营销活动列表
*/
@Operation(summary = "查询营销活动列表", description = "分页查询营销活动列表")
@SaCheckPermission("mall:promotion:list")
@GetMapping("/list")
public TableDataInfo<MallPromotionVo> list(@Parameter(description = "营销活动查询条件") MallPromotionBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallPromotionService.queryPageList(bo, pageQuery);
}
/**
* 导出营销活动列表
*/
@Operation(summary = "导出营销活动列表", description = "导出营销活动到Excel")
@SaCheckPermission("mall:promotion:export")
@Log(title = "营销活动", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "营销活动查询条件") MallPromotionBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallPromotionVo> list = mallPromotionService.queryList(bo);
ExcelUtil.exportExcel(list, "营销活动", MallPromotionVo.class, response);
}
/**
* 获取营销活动详细信息
*/
@Operation(summary = "获取营销活动详情", description = "根据营销活动ID获取详细信息")
@SaCheckPermission("mall:promotion:query")
@GetMapping("/{id}")
public R<MallPromotionVo> getInfo(@Parameter(description = "营销活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallPromotionService.queryById(id));
}
/**
* 新增营销活动
*/
@Operation(summary = "新增营销活动", description = "创建新的营销活动")
@SaCheckPermission("mall:promotion:add")
@Log(title = "营销活动", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "营销活动信息") @Validated @RequestBody MallPromotionBo bo) {
return toAjax(mallPromotionService.insertByBo(bo));
}
/**
* 修改营销活动
*/
@Operation(summary = "修改营销活动", description = "更新营销活动信息")
@SaCheckPermission("mall:promotion:edit")
@Log(title = "营销活动", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "营销活动信息") @Validated @RequestBody MallPromotionBo bo) {
return toAjax(mallPromotionService.updateByBo(bo));
}
/**
* 删除营销活动
*/
@Operation(summary = "删除营销活动", description = "删除营销活动")
@SaCheckPermission("mall:promotion:remove")
@Log(title = "营销活动", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "营销活动ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallPromotionService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 启用/禁用营销活动
*/
@Operation(summary = "启用/禁用营销活动", description = "更改营销活动状态")
@SaCheckPermission("mall:promotion:edit")
@Log(title = "营销活动", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus/{id}")
public R<Void> changeStatus(@Parameter(description = "营销活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id,
@Parameter(description = "状态(0启用 1禁用)", required = true)
@RequestParam @NotNull(message = "状态不能为空") Integer status) {
return toAjax(mallPromotionService.changeStatus(id, status));
}
}
@@ -0,0 +1,126 @@
package org.dromara.mall.promotion.controller.admin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.bo.MallSeckillBo;
import org.dromara.mall.promotion.domain.vo.MallSeckillVo;
import org.dromara.mall.promotion.service.MallSeckillService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
* 秒杀活动管理
*
* @author pengles
*/
@Tag(name = "管理端-秒杀活动管理", description = "秒杀活动管理接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mall/seckill")
public class AdminMallSeckillController extends BaseController {
private final MallSeckillService mallSeckillService;
/**
* 查询秒杀活动列表
*/
@Operation(summary = "查询秒杀活动列表", description = "分页查询秒杀活动列表")
@SaCheckPermission("mall:seckill:list")
@GetMapping("/list")
public TableDataInfo<MallSeckillVo> list(@Parameter(description = "秒杀活动查询条件") MallSeckillBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallSeckillService.queryPageList(bo, pageQuery);
}
/**
* 导出秒杀活动列表
*/
@Operation(summary = "导出秒杀活动列表", description = "导出秒杀活动到Excel")
@SaCheckPermission("mall:seckill:export")
@Log(title = "秒杀活动", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "秒杀活动查询条件") MallSeckillBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallSeckillVo> list = mallSeckillService.queryList(bo);
ExcelUtil.exportExcel(list, "秒杀活动", MallSeckillVo.class, response);
}
/**
* 获取秒杀活动详细信息
*/
@Operation(summary = "获取秒杀活动详情", description = "根据秒杀活动ID获取详细信息")
@SaCheckPermission("mall:seckill:query")
@GetMapping("/{id}")
public R<MallSeckillVo> getInfo(@Parameter(description = "秒杀活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallSeckillService.queryById(id));
}
/**
* 新增秒杀活动
*/
@Operation(summary = "新增秒杀活动", description = "创建新的秒杀活动")
@SaCheckPermission("mall:seckill:add")
@Log(title = "秒杀活动", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "秒杀活动信息") @Validated @RequestBody MallSeckillBo bo) {
return toAjax(mallSeckillService.insertByBo(bo));
}
/**
* 修改秒杀活动
*/
@Operation(summary = "修改秒杀活动", description = "更新秒杀活动信息")
@SaCheckPermission("mall:seckill:edit")
@Log(title = "秒杀活动", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "秒杀活动信息") @Validated @RequestBody MallSeckillBo bo) {
return toAjax(mallSeckillService.updateByBo(bo));
}
/**
* 删除秒杀活动
*/
@Operation(summary = "删除秒杀活动", description = "删除秒杀活动")
@SaCheckPermission("mall:seckill:remove")
@Log(title = "秒杀活动", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "秒杀活动ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallSeckillService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 启用/禁用秒杀活动
*/
@Operation(summary = "启用/禁用秒杀活动", description = "更改秒杀活动状态")
@SaCheckPermission("mall:seckill:edit")
@Log(title = "秒杀活动", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus/{id}")
public R<Void> changeStatus(@Parameter(description = "秒杀活动ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id,
@Parameter(description = "状态(0启用 1禁用)", required = true)
@RequestParam @NotNull(message = "状态不能为空") Integer status) {
return toAjax(mallSeckillService.changeStatus(id, status));
}
}
@@ -0,0 +1,64 @@
package org.dromara.mall.promotion.controller.app;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.vo.MallBargainVo;
import org.dromara.mall.promotion.service.MallBargainService;
import org.springframework.validation.annotation.Validated;
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;
import java.util.List;
/**
* 砍价活动(移动端)
*
* @author pengles
*/
@Tag(name = "移动端-砍价活动", description = "砍价活动接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/bargain")
public class AppMallBargainController extends BaseController {
private final MallBargainService mallBargainService;
/**
* 查询可用砍价活动列表
*/
@Operation(summary = "查询可用砍价活动列表", description = "查询当前可用的砍价活动列表")
@GetMapping("/list")
public R<List<MallBargainVo>> list() {
List<MallBargainVo> list = mallBargainService.queryAvailableList();
return R.ok(list);
}
/**
* 获取砍价活动详情
*/
@Operation(summary = "获取砍价活动详情", description = "根据砍价活动ID获取详细信息")
@GetMapping("/{id}")
public R<MallBargainVo> getInfo(@Parameter(description = "砍价活动ID", required = true)
@PathVariable Long id) {
return R.ok(mallBargainService.queryById(id));
}
/**
* 根据SKU ID查询砍价活动
*/
@Operation(summary = "根据SKU ID查询砍价活动", description = "查询指定SKU的砍价活动")
@GetMapping("/sku/{skuId}")
public R<MallBargainVo> getBySkuId(@Parameter(description = "SKU ID", required = true)
@PathVariable Long skuId) {
MallBargainVo vo = mallBargainService.queryBySkuId(skuId);
return R.ok(vo);
}
}
@@ -0,0 +1,64 @@
package org.dromara.mall.promotion.controller.app;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.vo.MallGroupBuyVo;
import org.dromara.mall.promotion.service.MallGroupBuyService;
import org.springframework.validation.annotation.Validated;
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;
import java.util.List;
/**
* 拼团活动(移动端)
*
* @author pengles
*/
@Tag(name = "移动端-拼团活动", description = "拼团活动接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/groupbuy")
public class AppMallGroupBuyController extends BaseController {
private final MallGroupBuyService mallGroupBuyService;
/**
* 查询可用拼团活动列表
*/
@Operation(summary = "查询可用拼团活动列表", description = "查询当前可用的拼团活动列表")
@GetMapping("/list")
public R<List<MallGroupBuyVo>> list() {
List<MallGroupBuyVo> list = mallGroupBuyService.queryAvailableList();
return R.ok(list);
}
/**
* 获取拼团活动详情
*/
@Operation(summary = "获取拼团活动详情", description = "根据拼团活动ID获取详细信息")
@GetMapping("/{id}")
public R<MallGroupBuyVo> getInfo(@Parameter(description = "拼团活动ID", required = true)
@PathVariable Long id) {
return R.ok(mallGroupBuyService.queryById(id));
}
/**
* 根据SKU ID查询拼团活动
*/
@Operation(summary = "根据SKU ID查询拼团活动", description = "查询指定SKU的拼团活动")
@GetMapping("/sku/{skuId}")
public R<MallGroupBuyVo> getBySkuId(@Parameter(description = "SKU ID", required = true)
@PathVariable Long skuId) {
MallGroupBuyVo vo = mallGroupBuyService.queryBySkuId(skuId);
return R.ok(vo);
}
}
@@ -0,0 +1,57 @@
package org.dromara.mall.promotion.controller.app;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.bo.MallPromotionBo;
import org.dromara.mall.promotion.domain.vo.MallPromotionVo;
import org.dromara.mall.promotion.service.MallPromotionService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 营销活动(移动端)
*
* @author pengles
*/
@Tag(name = "移动端-营销活动", description = "营销活动接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/promotion")
public class AppMallPromotionController extends BaseController {
private final MallPromotionService mallPromotionService;
/**
* 查询营销活动列表(按类型)
*/
@Operation(summary = "查询营销活动列表", description = "根据活动类型查询启用的营销活动列表")
@GetMapping("/list")
public R<List<MallPromotionVo>> list(@Parameter(description = "活动类型(0普通活动 1拼团 2秒杀 3砍价)")
@RequestParam(required = false) Integer type) {
List<MallPromotionVo> list;
if (type != null) {
list = mallPromotionService.queryListByType(type);
} else {
list = mallPromotionService.queryList(new MallPromotionBo());
}
return R.ok(list);
}
/**
* 获取营销活动详情
*/
@Operation(summary = "获取营销活动详情", description = "根据营销活动ID获取详细信息")
@GetMapping("/{id}")
public R<MallPromotionVo> getInfo(@Parameter(description = "营销活动ID", required = true)
@PathVariable Long id) {
return R.ok(mallPromotionService.queryById(id));
}
}
@@ -0,0 +1,64 @@
package org.dromara.mall.promotion.controller.app;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.promotion.domain.vo.MallSeckillVo;
import org.dromara.mall.promotion.service.MallSeckillService;
import org.springframework.validation.annotation.Validated;
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;
import java.util.List;
/**
* 秒杀活动(移动端)
*
* @author pengles
*/
@Tag(name = "移动端-秒杀活动", description = "秒杀活动接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/seckill")
public class AppMallSeckillController extends BaseController {
private final MallSeckillService mallSeckillService;
/**
* 查询可用秒杀活动列表
*/
@Operation(summary = "查询可用秒杀活动列表", description = "查询当前可用的秒杀活动列表")
@GetMapping("/list")
public R<List<MallSeckillVo>> list() {
List<MallSeckillVo> list = mallSeckillService.queryAvailableList();
return R.ok(list);
}
/**
* 获取秒杀活动详情
*/
@Operation(summary = "获取秒杀活动详情", description = "根据秒杀活动ID获取详细信息")
@GetMapping("/{id}")
public R<MallSeckillVo> getInfo(@Parameter(description = "秒杀活动ID", required = true)
@PathVariable Long id) {
return R.ok(mallSeckillService.queryById(id));
}
/**
* 根据SKU ID查询秒杀活动
*/
@Operation(summary = "根据SKU ID查询秒杀活动", description = "查询指定SKU的秒杀活动")
@GetMapping("/sku/{skuId}")
public R<MallSeckillVo> getBySkuId(@Parameter(description = "SKU ID", required = true)
@PathVariable Long skuId) {
MallSeckillVo vo = mallSeckillService.queryBySkuId(skuId);
return R.ok(vo);
}
}
@@ -0,0 +1,102 @@
package org.dromara.mall.promotion.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 砍价活动对象 mall_bargain
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_bargain")
public class MallBargain implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 砍价ID
*/
@TableId
private Long id;
/**
* 活动ID
*/
private Long promotionId;
/**
* SKU ID
*/
private Long skuId;
/**
* 砍后价(分)
*/
private Integer bargainPrice;
/**
* 原价(分)
*/
private Integer originalPrice;
/**
* 砍价库存
*/
private Integer bargainStock;
/**
* 最低价(分)
*/
private Integer minPrice;
/**
* 需要助力人数
*/
private Integer helpCount;
/**
* 砍价有效期(小时)
*/
private Integer durationHours;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,87 @@
package org.dromara.mall.promotion.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 拼团活动对象 mall_group_buy
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_group_buy")
public class MallGroupBuy implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 拼团ID
*/
@TableId
private Long id;
/**
* 活动ID
*/
private Long promotionId;
/**
* SKU ID
*/
private Long skuId;
/**
* 拼团价(分)
*/
private Integer groupPrice;
/**
* 成团人数
*/
private Integer groupSize;
/**
* 拼团有效期(小时)
*/
private Integer durationHours;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,71 @@
package org.dromara.mall.promotion.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 拼团订单对象 mall_group_buy_order
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_group_buy_order")
public class MallGroupBuyOrder implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId
private Long id;
/**
* 拼团ID
*/
private Long groupBuyId;
/**
* 拼团编号
*/
private String groupNo;
/**
* 订单ID
*/
private Long orderId;
/**
* 用户ID
*/
private Long userId;
/**
* 状态(0拼团中 1已成团 2已失败)
*/
private Integer status;
/**
* 成团时间
*/
private Date successTime;
/**
* 过期时间
*/
private Date expireTime;
/**
* 创建时间
*/
private Date createTime;
}
@@ -0,0 +1,92 @@
package org.dromara.mall.promotion.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 营销活动对象 mall_promotion
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_promotion")
public class MallPromotion implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 活动ID
*/
@TableId
private Long id;
/**
* 活动名称
*/
private String name;
/**
* 活动类型(0普通活动 1拼团 2秒杀 3砍价)
*/
private Integer type;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 活动规则(JSON)
*/
private String rules;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 备注
*/
private String remark;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,92 @@
package org.dromara.mall.promotion.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 秒杀活动对象 mall_seckill
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_seckill")
public class MallSeckill implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 秒杀ID
*/
@TableId
private Long id;
/**
* 活动ID
*/
private Long promotionId;
/**
* SKU ID
*/
private Long skuId;
/**
* 秒杀价(分)
*/
private Integer seckillPrice;
/**
* 秒杀库存
*/
private Integer seckillStock;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,86 @@
package org.dromara.mall.promotion.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.promotion.domain.MallBargain;
/**
* 砍价活动业务对象 mall_bargain
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallBargain.class, reverseConvertGenerate = false)
public class MallBargainBo extends BaseEntity {
/**
* 砍价ID
*/
@NotNull(message = "砍价ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 活动ID
*/
@NotNull(message = "活动ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long promotionId;
/**
* SKU ID
*/
@NotNull(message = "SKU ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long skuId;
/**
* 砍后价(分)
*/
@NotNull(message = "砍后价不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "砍后价必须大于0")
private Integer bargainPrice;
/**
* 原价(分)
*/
@NotNull(message = "原价不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "原价必须大于0")
private Integer originalPrice;
/**
* 砍价库存
*/
@NotNull(message = "砍价库存不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "砍价库存必须大于0")
private Integer bargainStock;
/**
* 最低价(分)
*/
@NotNull(message = "最低价不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "最低价必须大于0")
private Integer minPrice;
/**
* 需要助力人数
*/
@Min(value = 0, message = "需要助力人数不能为负数")
private Integer helpCount;
/**
* 砍价有效期(小时)
*/
@Min(value = 1, message = "砍价有效期必须大于0")
private Integer durationHours;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
}
@@ -0,0 +1,66 @@
package org.dromara.mall.promotion.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.promotion.domain.MallGroupBuy;
/**
* 拼团活动业务对象 mall_group_buy
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallGroupBuy.class, reverseConvertGenerate = false)
public class MallGroupBuyBo extends BaseEntity {
/**
* 拼团ID
*/
@NotNull(message = "拼团ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 活动ID
*/
@NotNull(message = "活动ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long promotionId;
/**
* SKU ID
*/
@NotNull(message = "SKU ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long skuId;
/**
* 拼团价(分)
*/
@NotNull(message = "拼团价不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "拼团价必须大于0")
private Integer groupPrice;
/**
* 成团人数
*/
@NotNull(message = "成团人数不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 2, message = "成团人数至少为2人")
private Integer groupSize;
/**
* 拼团有效期(小时)
*/
@Min(value = 1, message = "拼团有效期必须大于0")
private Integer durationHours;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
}
@@ -0,0 +1,73 @@
package org.dromara.mall.promotion.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.promotion.domain.MallPromotion;
import java.util.Date;
/**
* 营销活动业务对象 mall_promotion
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallPromotion.class, reverseConvertGenerate = false)
public class MallPromotionBo extends BaseEntity {
/**
* 活动ID
*/
@NotNull(message = "活动ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 活动名称
*/
@NotBlank(message = "活动名称不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(max = 128, message = "活动名称长度不能超过128个字符")
private String name;
/**
* 活动类型(0普通活动 1拼团 2秒杀 3砍价)
*/
@NotNull(message = "活动类型不能为空", groups = {AddGroup.class, EditGroup.class})
private Integer type;
/**
* 开始时间
*/
@NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
private Date startTime;
/**
* 结束时间
*/
@NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
private Date endTime;
/**
* 活动规则(JSON)
*/
private String rules;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 备注
*/
@Size(max = 512, message = "备注长度不能超过512个字符")
private String remark;
}
@@ -0,0 +1,74 @@
package org.dromara.mall.promotion.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.promotion.domain.MallSeckill;
import java.util.Date;
/**
* 秒杀活动业务对象 mall_seckill
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallSeckill.class, reverseConvertGenerate = false)
public class MallSeckillBo extends BaseEntity {
/**
* 秒杀ID
*/
@NotNull(message = "秒杀ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 活动ID
*/
@NotNull(message = "活动ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long promotionId;
/**
* SKU ID
*/
@NotNull(message = "SKU ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long skuId;
/**
* 秒杀价(分)
*/
@NotNull(message = "秒杀价不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "秒杀价必须大于0")
private Integer seckillPrice;
/**
* 秒杀库存
*/
@NotNull(message = "秒杀库存不能为空", groups = {AddGroup.class, EditGroup.class})
@Min(value = 1, message = "秒杀库存必须大于0")
private Integer seckillStock;
/**
* 开始时间
*/
@NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
private Date startTime;
/**
* 结束时间
*/
@NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
private Date endTime;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
}
@@ -0,0 +1,98 @@
package org.dromara.mall.promotion.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.promotion.domain.MallBargain;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 砍价活动视图对象 mall_bargain
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MallBargain.class)
public class MallBargainVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 砍价ID
*/
@ExcelProperty(value = "砍价ID")
private Long id;
/**
* 活动ID
*/
@ExcelProperty(value = "活动ID")
private Long promotionId;
/**
* SKU ID
*/
@ExcelProperty(value = "SKU ID")
private Long skuId;
/**
* 砍后价(分)
*/
@ExcelProperty(value = "砍后价(分)")
private Integer bargainPrice;
/**
* 原价(分)
*/
@ExcelProperty(value = "原价(分)")
private Integer originalPrice;
/**
* 砍价库存
*/
@ExcelProperty(value = "砍价库存")
private Integer bargainStock;
/**
* 最低价(分)
*/
@ExcelProperty(value = "最低价(分)")
private Integer minPrice;
/**
* 需要助力人数
*/
@ExcelProperty(value = "需要助力人数")
private Integer helpCount;
/**
* 砍价有效期(小时)
*/
@ExcelProperty(value = "砍价有效期(小时)")
private Integer durationHours;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态")
private Integer status;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,80 @@
package org.dromara.mall.promotion.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.promotion.domain.MallGroupBuy;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 拼团活动视图对象 mall_group_buy
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MallGroupBuy.class)
public class MallGroupBuyVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 拼团ID
*/
@ExcelProperty(value = "拼团ID")
private Long id;
/**
* 活动ID
*/
@ExcelProperty(value = "活动ID")
private Long promotionId;
/**
* SKU ID
*/
@ExcelProperty(value = "SKU ID")
private Long skuId;
/**
* 拼团价(分)
*/
@ExcelProperty(value = "拼团价(分)")
private Integer groupPrice;
/**
* 成团人数
*/
@ExcelProperty(value = "成团人数")
private Integer groupSize;
/**
* 拼团有效期(小时)
*/
@ExcelProperty(value = "拼团有效期(小时)")
private Integer durationHours;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态")
private Integer status;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,86 @@
package org.dromara.mall.promotion.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.promotion.domain.MallPromotion;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 营销活动视图对象 mall_promotion
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MallPromotion.class)
public class MallPromotionVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 活动ID
*/
@ExcelProperty(value = "活动ID")
private Long id;
/**
* 活动名称
*/
@ExcelProperty(value = "活动名称")
private String name;
/**
* 活动类型(0普通活动 1拼团 2秒杀 3砍价)
*/
@ExcelProperty(value = "活动类型")
private Integer type;
/**
* 开始时间
*/
@ExcelProperty(value = "开始时间")
private Date startTime;
/**
* 结束时间
*/
@ExcelProperty(value = "结束时间")
private Date endTime;
/**
* 活动规则(JSON)
*/
@ExcelProperty(value = "活动规则")
private String rules;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态")
private Integer status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,86 @@
package org.dromara.mall.promotion.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.promotion.domain.MallSeckill;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 秒杀活动视图对象 mall_seckill
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = MallSeckill.class)
public class MallSeckillVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 秒杀ID
*/
@ExcelProperty(value = "秒杀ID")
private Long id;
/**
* 活动ID
*/
@ExcelProperty(value = "活动ID")
private Long promotionId;
/**
* SKU ID
*/
@ExcelProperty(value = "SKU ID")
private Long skuId;
/**
* 秒杀价(分)
*/
@ExcelProperty(value = "秒杀价(分)")
private Integer seckillPrice;
/**
* 秒杀库存
*/
@ExcelProperty(value = "秒杀库存")
private Integer seckillStock;
/**
* 开始时间
*/
@ExcelProperty(value = "开始时间")
private Date startTime;
/**
* 结束时间
*/
@ExcelProperty(value = "结束时间")
private Date endTime;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态")
private Integer status;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,57 @@
package org.dromara.mall.promotion.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 营销活动类型枚举
*
* @author pengles
*/
@Getter
@AllArgsConstructor
public enum PromotionType {
/**
* 普通活动
*/
NORMAL(0, "普通活动"),
/**
* 拼团活动
*/
GROUP_BUY(1, "拼团活动"),
/**
* 秒杀活动
*/
SECKILL(2, "秒杀活动"),
/**
* 砍价活动
*/
BARGAIN(3, "砍价活动");
/**
* 类型值
*/
private final Integer value;
/**
* 类型名称
*/
private final String name;
/**
* 根据值获取枚举
*/
public static PromotionType getByValue(Integer value) {
for (PromotionType type : values()) {
if (type.getValue().equals(value)) {
return type;
}
}
return null;
}
}
@@ -0,0 +1,78 @@
package org.dromara.mall.promotion.job;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mall.promotion.domain.MallSeckill;
import org.dromara.mall.promotion.mapper.MallSeckillMapper;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
/**
* 营销活动定时任务
*
* @author pengles
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class MallPromotionJob {
private final MallSeckillMapper seckillMapper;
/**
* 秒杀活动状态更新任务
* 每5分钟执行一次
* - 扫描即将开始的活动(start_time <= 当前时间 且 status=1禁用),自动启用(status=0
* - 扫描已结束的活动(end_time < 当前时间 且 status=0启用),自动禁用(status=1
*/
@Scheduled(cron = "0 0/5 * * * ?")
public void updateSeckillStatus() {
Date now = new Date();
int updatedCount = 0;
// 扫描即将开始的活动(start_time <= 当前时间 且 status=1禁用),自动启用
LambdaQueryWrapper<MallSeckill> startWrapper = new LambdaQueryWrapper<>();
startWrapper.le(MallSeckill::getStartTime, now);
startWrapper.gt(MallSeckill::getEndTime, now); // 还未结束
startWrapper.eq(MallSeckill::getStatus, 1); // 当前状态为禁用
List<MallSeckill> toStart = seckillMapper.selectList(startWrapper);
if (!toStart.isEmpty()) {
for (MallSeckill seckill : toStart) {
LambdaUpdateWrapper<MallSeckill> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(MallSeckill::getId, seckill.getId());
updateWrapper.set(MallSeckill::getStatus, 0); // 启用
seckillMapper.update(null, updateWrapper);
updatedCount++;
log.info("秒杀活动自动启用: id={}, promotionId={}", seckill.getId(), seckill.getPromotionId());
}
}
// 扫描已结束的活动(end_time < 当前时间 且 status=0启用),自动禁用
LambdaQueryWrapper<MallSeckill> endWrapper = new LambdaQueryWrapper<>();
endWrapper.lt(MallSeckill::getEndTime, now);
endWrapper.eq(MallSeckill::getStatus, 0); // 当前状态为启用
List<MallSeckill> toEnd = seckillMapper.selectList(endWrapper);
if (!toEnd.isEmpty()) {
for (MallSeckill seckill : toEnd) {
LambdaUpdateWrapper<MallSeckill> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(MallSeckill::getId, seckill.getId());
updateWrapper.set(MallSeckill::getStatus, 1); // 禁用
seckillMapper.update(null, updateWrapper);
updatedCount++;
log.info("秒杀活动自动禁用: id={}, promotionId={}", seckill.getId(), seckill.getPromotionId());
}
}
if (updatedCount > 0) {
log.info("秒杀活动状态更新完成,共更新{}个活动", updatedCount);
}
}
}
@@ -0,0 +1,16 @@
package org.dromara.mall.promotion.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.promotion.domain.MallBargain;
import org.dromara.mall.promotion.domain.vo.MallBargainVo;
/**
* 砍价活动Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallBargainMapper extends BaseMapperPlus<MallBargain, MallBargainVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.promotion.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.promotion.domain.MallGroupBuy;
import org.dromara.mall.promotion.domain.vo.MallGroupBuyVo;
/**
* 拼团活动Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallGroupBuyMapper extends BaseMapperPlus<MallGroupBuy, MallGroupBuyVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.promotion.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.promotion.domain.MallPromotion;
import org.dromara.mall.promotion.domain.vo.MallPromotionVo;
/**
* 营销活动Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallPromotionMapper extends BaseMapperPlus<MallPromotion, MallPromotionVo> {
}
@@ -0,0 +1,35 @@
package org.dromara.mall.promotion.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.promotion.domain.MallSeckill;
import org.dromara.mall.promotion.domain.vo.MallSeckillVo;
/**
* 秒杀活动Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallSeckillMapper extends BaseMapperPlus<MallSeckill, MallSeckillVo> {
/**
* 扣减秒杀库存
*
* @param id 秒杀ID
* @param quantity 扣减数量
* @return 影响行数
*/
int reduceStock(@Param("id") Long id, @Param("quantity") Integer quantity);
/**
* 恢复秒杀库存
*
* @param id 秒杀ID
* @param quantity 恢复数量
* @return 影响行数
*/
int restoreStock(@Param("id") Long id, @Param("quantity") Integer quantity);
}
@@ -0,0 +1,63 @@
package org.dromara.mall.promotion.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.bo.MallBargainBo;
import org.dromara.mall.promotion.domain.vo.MallBargainVo;
import java.util.Collection;
import java.util.List;
/**
* 砍价活动Service接口
*
* @author pengles
*/
public interface MallBargainService {
/**
* 查询砍价活动
*/
MallBargainVo queryById(Long id);
/**
* 查询砍价活动列表
*/
TableDataInfo<MallBargainVo> queryPageList(MallBargainBo bo, PageQuery pageQuery);
/**
* 查询砍价活动列表
*/
List<MallBargainVo> queryList(MallBargainBo bo);
/**
* 查询可用的砍价活动列表(移动端)
*/
List<MallBargainVo> queryAvailableList();
/**
* 根据SKU ID查询砍价活动
*/
MallBargainVo queryBySkuId(Long skuId);
/**
* 新增砍价活动
*/
Boolean insertByBo(MallBargainBo bo);
/**
* 修改砍价活动
*/
Boolean updateByBo(MallBargainBo bo);
/**
* 校验并批量删除砍价活动信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 启用/禁用砍价活动
*/
Boolean changeStatus(Long id, Integer status);
}
@@ -0,0 +1,63 @@
package org.dromara.mall.promotion.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.bo.MallGroupBuyBo;
import org.dromara.mall.promotion.domain.vo.MallGroupBuyVo;
import java.util.Collection;
import java.util.List;
/**
* 拼团活动Service接口
*
* @author pengles
*/
public interface MallGroupBuyService {
/**
* 查询拼团活动
*/
MallGroupBuyVo queryById(Long id);
/**
* 查询拼团活动列表
*/
TableDataInfo<MallGroupBuyVo> queryPageList(MallGroupBuyBo bo, PageQuery pageQuery);
/**
* 查询拼团活动列表
*/
List<MallGroupBuyVo> queryList(MallGroupBuyBo bo);
/**
* 查询可用的拼团活动列表(移动端)
*/
List<MallGroupBuyVo> queryAvailableList();
/**
* 根据SKU ID查询拼团活动
*/
MallGroupBuyVo queryBySkuId(Long skuId);
/**
* 新增拼团活动
*/
Boolean insertByBo(MallGroupBuyBo bo);
/**
* 修改拼团活动
*/
Boolean updateByBo(MallGroupBuyBo bo);
/**
* 校验并批量删除拼团活动信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 启用/禁用拼团活动
*/
Boolean changeStatus(Long id, Integer status);
}
@@ -0,0 +1,60 @@
package org.dromara.mall.promotion.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.MallPromotion;
import org.dromara.mall.promotion.domain.bo.MallPromotionBo;
import org.dromara.mall.promotion.domain.vo.MallPromotionVo;
import java.util.Collection;
import java.util.List;
/**
* 营销活动Service接口
*
* @author pengles
*/
public interface MallPromotionService extends IService<MallPromotion> {
/**
* 查询营销活动
*/
MallPromotionVo queryById(Long id);
/**
* 查询营销活动列表
*/
TableDataInfo<MallPromotionVo> queryPageList(MallPromotionBo bo, PageQuery pageQuery);
/**
* 查询营销活动列表
*/
List<MallPromotionVo> queryList(MallPromotionBo bo);
/**
* 新增营销活动
*/
Boolean insertByBo(MallPromotionBo bo);
/**
* 修改营销活动
*/
Boolean updateByBo(MallPromotionBo bo);
/**
* 校验并批量删除营销活动信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 启用/禁用营销活动
*/
Boolean changeStatus(Long id, Integer status);
/**
* 根据活动类型查询活动列表
*/
List<MallPromotionVo> queryListByType(Integer type);
}
@@ -0,0 +1,89 @@
package org.dromara.mall.promotion.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.bo.MallSeckillBo;
import org.dromara.mall.promotion.domain.vo.MallSeckillVo;
import java.util.Collection;
import java.util.List;
/**
* 秒杀活动Service接口
*
* @author pengles
*/
public interface MallSeckillService {
/**
* 查询秒杀活动
*/
MallSeckillVo queryById(Long id);
/**
* 查询秒杀活动列表
*/
TableDataInfo<MallSeckillVo> queryPageList(MallSeckillBo bo, PageQuery pageQuery);
/**
* 查询秒杀活动列表
*/
List<MallSeckillVo> queryList(MallSeckillBo bo);
/**
* 查询可用的秒杀活动列表(移动端)
*/
List<MallSeckillVo> queryAvailableList();
/**
* 根据SKU ID查询秒杀活动
*/
MallSeckillVo queryBySkuId(Long skuId);
/**
* 新增秒杀活动
*/
Boolean insertByBo(MallSeckillBo bo);
/**
* 修改秒杀活动
*/
Boolean updateByBo(MallSeckillBo bo);
/**
* 校验并批量删除秒杀活动信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 启用/禁用秒杀活动
*/
Boolean changeStatus(Long id, Integer status);
/**
* 扣减秒杀库存
*
* @param id 秒杀ID
* @param quantity 扣减数量
* @return 是否成功
*/
Boolean reduceStock(Long id, Integer quantity);
/**
* 恢复秒杀库存
*
* @param id 秒杀ID
* @param quantity 恢复数量
* @return 是否成功
*/
Boolean restoreStock(Long id, Integer quantity);
/**
* 检查秒杀活动是否可用
*
* @param id 秒杀ID
* @return 是否可用
*/
Boolean checkAvailable(Long id);
}
@@ -0,0 +1,210 @@
package org.dromara.mall.promotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.MallBargain;
import org.dromara.mall.promotion.domain.MallPromotion;
import org.dromara.mall.promotion.domain.bo.MallBargainBo;
import org.dromara.mall.promotion.domain.vo.MallBargainVo;
import org.dromara.mall.promotion.mapper.MallBargainMapper;
import org.dromara.mall.promotion.mapper.MallPromotionMapper;
import org.dromara.mall.promotion.service.MallBargainService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* 砍价活动Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallBargainServiceImpl extends ServiceImpl<MallBargainMapper, MallBargain> implements MallBargainService {
private final MallBargainMapper baseMapper;
private final MallPromotionMapper promotionMapper;
/**
* 查询砍价活动
*/
@Override
public MallBargainVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询砍价活动列表
*/
@Override
public TableDataInfo<MallBargainVo> queryPageList(MallBargainBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallBargain> lqw = buildQueryWrapper(bo);
Page<MallBargainVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询砍价活动列表
*/
@Override
public List<MallBargainVo> queryList(MallBargainBo bo) {
LambdaQueryWrapper<MallBargain> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallBargain> buildQueryWrapper(MallBargainBo bo) {
LambdaQueryWrapper<MallBargain> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getPromotionId() != null, MallBargain::getPromotionId, bo.getPromotionId());
lqw.eq(bo.getSkuId() != null, MallBargain::getSkuId, bo.getSkuId());
lqw.eq(bo.getStatus() != null, MallBargain::getStatus, bo.getStatus());
lqw.orderByDesc(MallBargain::getCreateTime);
return lqw;
}
/**
* 查询可用的砍价活动列表(移动端)
*/
@Override
public List<MallBargainVo> queryAvailableList() {
LambdaQueryWrapper<MallBargain> lqw = Wrappers.lambdaQuery();
lqw.eq(MallBargain::getStatus, 0); // 启用状态
lqw.gt(MallBargain::getBargainStock, 0); // 有库存
lqw.orderByDesc(MallBargain::getCreateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 根据SKU ID查询砍价活动
*/
@Override
public MallBargainVo queryBySkuId(Long skuId) {
LambdaQueryWrapper<MallBargain> lqw = Wrappers.lambdaQuery();
lqw.eq(MallBargain::getSkuId, skuId);
lqw.eq(MallBargain::getStatus, 0); // 启用状态
lqw.gt(MallBargain::getBargainStock, 0); // 有库存
lqw.orderByDesc(MallBargain::getCreateTime);
lqw.last("LIMIT 1");
return baseMapper.selectVoOne(lqw);
}
/**
* 新增砍价活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallBargainBo bo) {
MallBargain add = MapstructUtils.convert(bo, MallBargain.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getStatus() == null) {
add.setStatus(0); // 默认启用
}
if (add.getDurationHours() == null) {
add.setDurationHours(24); // 默认24小时
}
if (add.getHelpCount() == null) {
add.setHelpCount(0); // 默认0人
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改砍价活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallBargainBo bo) {
MallBargain update = MapstructUtils.convert(bo, MallBargain.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallBargain entity) {
// TODO: 可以添加业务校验逻辑,如检查活动是否存在、SKU是否存在等
if (entity.getOriginalPrice() != null && entity.getBargainPrice() != null) {
if (entity.getBargainPrice() >= entity.getOriginalPrice()) {
throw new ServiceException("砍后价必须小于原价");
}
}
if (entity.getMinPrice() != null && entity.getBargainPrice() != null) {
if (entity.getBargainPrice() < entity.getMinPrice()) {
throw new ServiceException("砍后价不能小于最低价");
}
}
if (entity.getMinPrice() != null && entity.getOriginalPrice() != null) {
if (entity.getMinPrice() >= entity.getOriginalPrice()) {
throw new ServiceException("最低价必须小于原价");
}
}
}
/**
* 批量删除砍价活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
Date now = new Date();
for (Long id : ids) {
MallBargain bargain = baseMapper.selectById(id);
if (bargain == null) {
continue;
}
// 检查关联的营销活动是否正在进行中
if (bargain.getPromotionId() != null) {
MallPromotion promotion = promotionMapper.selectById(bargain.getPromotionId());
if (promotion != null && promotion.getStartTime() != null && promotion.getEndTime() != null) {
// 判断当前时间是否在活动时间范围内(开始时间 <= 当前时间 <= 结束时间)
if (!promotion.getStartTime().after(now) && !promotion.getEndTime().before(now)) {
throw new ServiceException("砍价活动ID[" + id + "]关联的营销活动正在进行中,不能删除");
}
}
}
// 注意:如果有砍价记录表(mall_bargain_record),还可以检查是否有进行中的砍价记录(status=0)
// 但由于当前模块中可能还未实现MallBargainRecordMapper,此处暂不添加该检查
// 如需添加,可注入MallBargainRecordMapper,然后查询count(bargain_id=? AND status=0) > 0
}
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* 启用/禁用砍价活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean changeStatus(Long id, Integer status) {
MallBargain bargain = baseMapper.selectById(id);
if (bargain == null) {
throw new ServiceException("砍价活动不存在");
}
MallBargain update = new MallBargain();
update.setId(id);
update.setStatus(status);
return baseMapper.updateById(update) > 0;
}
}
@@ -0,0 +1,169 @@
package org.dromara.mall.promotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.MallGroupBuy;
import org.dromara.mall.promotion.domain.bo.MallGroupBuyBo;
import org.dromara.mall.promotion.domain.vo.MallGroupBuyVo;
import org.dromara.mall.promotion.mapper.MallGroupBuyMapper;
import org.dromara.mall.promotion.service.MallGroupBuyService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* 拼团活动Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallGroupBuyServiceImpl extends ServiceImpl<MallGroupBuyMapper, MallGroupBuy> implements MallGroupBuyService {
private final MallGroupBuyMapper baseMapper;
/**
* 查询拼团活动
*/
@Override
public MallGroupBuyVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询拼团活动列表
*/
@Override
public TableDataInfo<MallGroupBuyVo> queryPageList(MallGroupBuyBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallGroupBuy> lqw = buildQueryWrapper(bo);
Page<MallGroupBuyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询拼团活动列表
*/
@Override
public List<MallGroupBuyVo> queryList(MallGroupBuyBo bo) {
LambdaQueryWrapper<MallGroupBuy> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallGroupBuy> buildQueryWrapper(MallGroupBuyBo bo) {
LambdaQueryWrapper<MallGroupBuy> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getPromotionId() != null, MallGroupBuy::getPromotionId, bo.getPromotionId());
lqw.eq(bo.getSkuId() != null, MallGroupBuy::getSkuId, bo.getSkuId());
lqw.eq(bo.getStatus() != null, MallGroupBuy::getStatus, bo.getStatus());
lqw.orderByDesc(MallGroupBuy::getCreateTime);
return lqw;
}
/**
* 查询可用的拼团活动列表(移动端)
*/
@Override
public List<MallGroupBuyVo> queryAvailableList() {
LambdaQueryWrapper<MallGroupBuy> lqw = Wrappers.lambdaQuery();
lqw.eq(MallGroupBuy::getStatus, 0); // 启用状态
lqw.orderByDesc(MallGroupBuy::getCreateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 根据SKU ID查询拼团活动
*/
@Override
public MallGroupBuyVo queryBySkuId(Long skuId) {
LambdaQueryWrapper<MallGroupBuy> lqw = Wrappers.lambdaQuery();
lqw.eq(MallGroupBuy::getSkuId, skuId);
lqw.eq(MallGroupBuy::getStatus, 0); // 启用状态
lqw.orderByDesc(MallGroupBuy::getCreateTime);
lqw.last("LIMIT 1");
return baseMapper.selectVoOne(lqw);
}
/**
* 新增拼团活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallGroupBuyBo bo) {
MallGroupBuy add = MapstructUtils.convert(bo, MallGroupBuy.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getStatus() == null) {
add.setStatus(0); // 默认启用
}
if (add.getDurationHours() == null) {
add.setDurationHours(24); // 默认24小时
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改拼团活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallGroupBuyBo bo) {
MallGroupBuy update = MapstructUtils.convert(bo, MallGroupBuy.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallGroupBuy entity) {
// TODO: 可以添加业务校验逻辑,如检查活动是否存在、SKU是否存在等
if (entity.getGroupSize() != null && entity.getGroupSize() < 2) {
throw new ServiceException("成团人数至少为2人");
}
}
/**
* 批量删除拼团活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO: 可以添加业务校验逻辑,如检查是否有进行中的拼团活动等
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 启用/禁用拼团活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean changeStatus(Long id, Integer status) {
MallGroupBuy groupBuy = baseMapper.selectById(id);
if (groupBuy == null) {
throw new ServiceException("拼团活动不存在");
}
MallGroupBuy update = new MallGroupBuy();
update.setId(id);
update.setStatus(status);
return baseMapper.updateById(update) > 0;
}
}
@@ -0,0 +1,173 @@
package org.dromara.mall.promotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.MallPromotion;
import org.dromara.mall.promotion.domain.bo.MallPromotionBo;
import org.dromara.mall.promotion.domain.vo.MallPromotionVo;
import org.dromara.mall.promotion.mapper.MallPromotionMapper;
import org.dromara.mall.promotion.service.MallPromotionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* 营销活动Service业务层处理
*
* @author pengles
*/
@Service
public class MallPromotionServiceImpl extends ServiceImpl<MallPromotionMapper, MallPromotion> implements MallPromotionService {
/**
* 查询营销活动
*/
@Override
public MallPromotionVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询营销活动列表
*/
@Override
public TableDataInfo<MallPromotionVo> queryPageList(MallPromotionBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallPromotion> lqw = buildQueryWrapper(bo);
Page<MallPromotionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询营销活动列表
*/
@Override
public List<MallPromotionVo> queryList(MallPromotionBo bo) {
LambdaQueryWrapper<MallPromotion> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallPromotion> buildQueryWrapper(MallPromotionBo bo) {
LambdaQueryWrapper<MallPromotion> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallPromotion::getName, bo.getName());
lqw.eq(bo.getType() != null, MallPromotion::getType, bo.getType());
lqw.eq(bo.getStatus() != null, MallPromotion::getStatus, bo.getStatus());
lqw.ge(bo.getStartTime() != null, MallPromotion::getStartTime, bo.getStartTime());
lqw.le(bo.getEndTime() != null, MallPromotion::getEndTime, bo.getEndTime());
lqw.orderByDesc(MallPromotion::getCreateTime);
return lqw;
}
/**
* 新增营销活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallPromotionBo bo) {
MallPromotion add = MapstructUtils.convert(bo, MallPromotion.class);
validEntityBeforeSave(add);
// 设置默认状态
if (add.getStatus() == null) {
add.setStatus(0); // 默认启用
}
// 校验时间
validateTimeRange(add.getStartTime(), add.getEndTime());
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改营销活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallPromotionBo bo) {
MallPromotion update = MapstructUtils.convert(bo, MallPromotion.class);
validEntityBeforeSave(update);
// 校验时间
if (update.getStartTime() != null && update.getEndTime() != null) {
validateTimeRange(update.getStartTime(), update.getEndTime());
}
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallPromotion entity) {
// TODO: 可以添加业务校验逻辑,如活动类型校验等
}
/**
* 校验时间范围
*/
private void validateTimeRange(Date startTime, Date endTime) {
if (startTime != null && endTime != null) {
if (startTime.after(endTime)) {
throw new ServiceException("开始时间不能晚于结束时间");
}
}
}
/**
* 批量删除营销活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO: 可以添加业务校验逻辑,如检查是否有进行中的活动等
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 启用/禁用营销活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean changeStatus(Long id, Integer status) {
MallPromotion promotion = baseMapper.selectById(id);
if (promotion == null) {
throw new ServiceException("营销活动不存在");
}
MallPromotion update = new MallPromotion();
update.setId(id);
update.setStatus(status);
return baseMapper.updateById(update) > 0;
}
/**
* 根据活动类型查询活动列表
*/
@Override
public List<MallPromotionVo> queryListByType(Integer type) {
LambdaQueryWrapper<MallPromotion> lqw = Wrappers.lambdaQuery();
lqw.eq(MallPromotion::getType, type);
lqw.eq(MallPromotion::getStatus, 0); // 只查询启用的活动
Date now = new Date();
lqw.le(MallPromotion::getStartTime, now); // 开始时间已到
lqw.ge(MallPromotion::getEndTime, now); // 结束时间未到
lqw.orderByDesc(MallPromotion::getCreateTime);
return baseMapper.selectVoList(lqw);
}
}
@@ -0,0 +1,263 @@
package org.dromara.mall.promotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.promotion.domain.MallSeckill;
import org.dromara.mall.promotion.domain.bo.MallSeckillBo;
import org.dromara.mall.promotion.domain.vo.MallSeckillVo;
import org.dromara.mall.promotion.mapper.MallSeckillMapper;
import org.dromara.mall.promotion.service.MallSeckillService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* 秒杀活动Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallSeckillServiceImpl implements MallSeckillService {
private final MallSeckillMapper baseMapper;
/**
* 查询秒杀活动
*/
@Override
public MallSeckillVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询秒杀活动列表
*/
@Override
public TableDataInfo<MallSeckillVo> queryPageList(MallSeckillBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallSeckill> lqw = buildQueryWrapper(bo);
Page<MallSeckillVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询秒杀活动列表
*/
@Override
public List<MallSeckillVo> queryList(MallSeckillBo bo) {
LambdaQueryWrapper<MallSeckill> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallSeckill> buildQueryWrapper(MallSeckillBo bo) {
LambdaQueryWrapper<MallSeckill> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getPromotionId() != null, MallSeckill::getPromotionId, bo.getPromotionId());
lqw.eq(bo.getSkuId() != null, MallSeckill::getSkuId, bo.getSkuId());
lqw.eq(bo.getStatus() != null, MallSeckill::getStatus, bo.getStatus());
lqw.ge(bo.getStartTime() != null, MallSeckill::getStartTime, bo.getStartTime());
lqw.le(bo.getEndTime() != null, MallSeckill::getEndTime, bo.getEndTime());
lqw.orderByDesc(MallSeckill::getCreateTime);
return lqw;
}
/**
* 查询可用的秒杀活动列表(移动端)
*/
@Override
public List<MallSeckillVo> queryAvailableList() {
LambdaQueryWrapper<MallSeckill> lqw = Wrappers.lambdaQuery();
lqw.eq(MallSeckill::getStatus, 0); // 启用状态
Date now = new Date();
lqw.le(MallSeckill::getStartTime, now); // 已开始
lqw.ge(MallSeckill::getEndTime, now); // 未结束
lqw.gt(MallSeckill::getSeckillStock, 0); // 有库存
lqw.orderByDesc(MallSeckill::getCreateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 根据SKU ID查询秒杀活动
*/
@Override
public MallSeckillVo queryBySkuId(Long skuId) {
LambdaQueryWrapper<MallSeckill> lqw = Wrappers.lambdaQuery();
lqw.eq(MallSeckill::getSkuId, skuId);
lqw.eq(MallSeckill::getStatus, 0); // 启用状态
Date now = new Date();
lqw.le(MallSeckill::getStartTime, now); // 已开始
lqw.ge(MallSeckill::getEndTime, now); // 未结束
lqw.gt(MallSeckill::getSeckillStock, 0); // 有库存
lqw.orderByDesc(MallSeckill::getCreateTime);
lqw.last("LIMIT 1");
return baseMapper.selectVoOne(lqw);
}
/**
* 新增秒杀活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallSeckillBo bo) {
MallSeckill add = MapstructUtils.convert(bo, MallSeckill.class);
validEntityBeforeSave(add);
// 设置默认状态
if (add.getStatus() == null) {
add.setStatus(0); // 默认启用
}
// 校验时间
validateTimeRange(add.getStartTime(), add.getEndTime());
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改秒杀活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallSeckillBo bo) {
MallSeckill update = MapstructUtils.convert(bo, MallSeckill.class);
validEntityBeforeSave(update);
// 校验时间
if (update.getStartTime() != null && update.getEndTime() != null) {
validateTimeRange(update.getStartTime(), update.getEndTime());
}
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallSeckill entity) {
// TODO: 可以添加业务校验逻辑,如检查活动是否存在、SKU是否存在等
}
/**
* 校验时间范围
*/
private void validateTimeRange(Date startTime, Date endTime) {
if (startTime != null && endTime != null) {
if (startTime.after(endTime)) {
throw new ServiceException("开始时间不能晚于结束时间");
}
}
}
/**
* 批量删除秒杀活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO: 可以添加业务校验逻辑,如检查是否有进行中的秒杀活动等
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 启用/禁用秒杀活动
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean changeStatus(Long id, Integer status) {
MallSeckill seckill = baseMapper.selectById(id);
if (seckill == null) {
throw new ServiceException("秒杀活动不存在");
}
MallSeckill update = new MallSeckill();
update.setId(id);
update.setStatus(status);
return baseMapper.updateById(update) > 0;
}
/**
* 扣减秒杀库存
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean reduceStock(Long id, Integer quantity) {
if (quantity == null || quantity <= 0) {
throw new ServiceException("扣减数量必须大于0");
}
MallSeckill seckill = baseMapper.selectById(id);
if (seckill == null) {
throw new ServiceException("秒杀活动不存在");
}
if (seckill.getSeckillStock() < quantity) {
throw new ServiceException("秒杀库存不足");
}
int rows = baseMapper.reduceStock(id, quantity);
return rows > 0;
}
/**
* 恢复秒杀库存
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean restoreStock(Long id, Integer quantity) {
if (quantity == null || quantity <= 0) {
throw new ServiceException("恢复数量必须大于0");
}
MallSeckill seckill = baseMapper.selectById(id);
if (seckill == null) {
throw new ServiceException("秒杀活动不存在");
}
int rows = baseMapper.restoreStock(id, quantity);
return rows > 0;
}
/**
* 检查秒杀活动是否可用
*/
@Override
public Boolean checkAvailable(Long id) {
MallSeckill seckill = baseMapper.selectById(id);
if (seckill == null) {
return false;
}
// 检查状态
if (seckill.getStatus() != 0) {
return false;
}
// 检查时间
Date now = new Date();
if (seckill.getStartTime().after(now) || seckill.getEndTime().before(now)) {
return false;
}
// 检查库存
if (seckill.getSeckillStock() <= 0) {
return false;
}
return true;
}
}
@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.mall.promotion.config.MallPromotionAutoConfiguration
@@ -0,0 +1,50 @@
<?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="org.dromara.mall.promotion.mapper.MallSeckillMapper">
<resultMap type="org.dromara.mall.promotion.domain.MallSeckill" id="MallSeckillResult">
<id property="id" column="id"/>
<result property="promotionId" column="promotion_id"/>
<result property="skuId" column="sku_id"/>
<result property="seckillPrice" column="seckill_price"/>
<result property="seckillStock" column="seckill_stock"/>
<result property="startTime" column="start_time"/>
<result property="endTime" column="end_time"/>
<result property="status" column="status"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<resultMap type="org.dromara.mall.promotion.domain.vo.MallSeckillVo" id="MallSeckillVoResult">
<id property="id" column="id"/>
<result property="promotionId" column="promotion_id"/>
<result property="skuId" column="sku_id"/>
<result property="seckillPrice" column="seckill_price"/>
<result property="seckillStock" column="seckill_stock"/>
<result property="startTime" column="start_time"/>
<result property="endTime" column="end_time"/>
<result property="status" column="status"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 扣减秒杀库存 -->
<update id="reduceStock">
UPDATE mall_seckill
SET seckill_stock = seckill_stock - #{quantity}
WHERE id = #{id}
AND seckill_stock >= #{quantity}
</update>
<!-- 恢复秒杀库存 -->
<update id="restoreStock">
UPDATE mall_seckill
SET seckill_stock = seckill_stock + #{quantity}
WHERE id = #{id}
</update>
</mapper>