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
File diff suppressed because it is too large Load Diff
+264
View File
@@ -0,0 +1,264 @@
# 电商模块 (ruoyi-mall)
## 模块简介
`ruoyi-mall` 是 PoJie 项目的电商核心业务模块,提供完整的电商功能,包括商品管理、购物车、订单管理、支付集成等。
## 模块结构
根据模块结构规划,`ruoyi-mall` 采用子模块拆分的方式,包含以下子模块:
- `ruoyi-mall-product` - 商品中心 (SPU, SKU, 分类, 品牌, 属性)
- `ruoyi-mall-trade` - 交易中心 (购物车, 订单, 售后)
- `ruoyi-mall-promotion` - 营销中心 (活动, 拼团, 秒杀, 砍价)
- `ruoyi-mall-coupon` - 优惠券中心 (独立拆分,便于复用)
- `ruoyi-mall-statistics` - 统计中心 (交易分析, 会员分析)
各子模块详细说明请参考对应的 README.md 文件。
## 功能特性
### 1. 商品管理
- ✅ 商品CRUD操作
- ✅ 商品上架/下架
- ✅ 商品浏览量和销量统计
- ✅ 商品详情管理
### 2. SKU管理
- ✅ SKU CRUD操作
- ✅ 库存管理
- ✅ 库存扣减和恢复
- ✅ 规格属性管理
### 3. 购物车管理
- ✅ 添加/删除购物车
- ✅ 购物车数量修改
- ✅ 购物车选中状态管理
- ✅ 购物车详情查询(包含商品和SKU信息)
### 4. 订单管理
- ✅ 订单创建
- ✅ 订单查询(列表、详情)
- ✅ 订单取消
- ✅ 订单状态管理
- ✅ 订单支付集成
### 5. 支付集成
- ✅ 与支付中心(ruoyi-pay)集成
- ✅ 支付回调处理
- ✅ 支付状态同步
## 数据库表
### 商品中心 (ruoyi-mall-product)
- `mall_product` - 商品表
- `mall_sku` - SKU表
- `mall_category` - 商品分类表
- `mall_brand` - 品牌表
### 交易中心 (ruoyi-mall-trade)
- `mall_cart` - 购物车表
- `mall_order` - 订单表
- `mall_order_item` - 订单明细表
- `mall_after_sale` - 售后申请表
- `mall_order_evaluate` - 订单评价表
- `mall_order_coupon` - 订单优惠券使用记录表
详细表结构请参考:`script/sql/ry_mall.sql`
## 配置说明
`application.yml` 中添加以下配置:
```yaml
ruoyi:
mall:
pay-app-id: 1 # 支付应用ID
pay-notify-url: https://your-domain.com/app/mall/pay/notify # 支付回调地址
```
## API接口
### 移动端接口
#### 商品接口
- `GET /app/mall/product/list` - 查询商品列表
- `GET /app/mall/product/{id}` - 查询商品详情
- `GET /app/mall/product/{productId}/skus` - 查询商品SKU列表
#### 商品分类接口
- `GET /app/mall/product/category/tree` - 查询商品分类树形列表(仅启用分类)
#### 品牌接口
- `GET /app/mall/product/brand/list` - 查询品牌列表(仅启用品牌)
#### 购物车接口
- `GET /app/mall/cart/list` - 查询购物车列表
- `GET /app/mall/cart/detail/list` - 查询购物车详情列表(包含商品信息)
- `POST /app/mall/cart` - 添加购物车
- `PUT /app/mall/cart` - 修改购物车
- `DELETE /app/mall/cart/{ids}` - 删除购物车
- `DELETE /app/mall/cart/clear` - 清空购物车
- `PUT /app/mall/cart/selected/{id}` - 更新选中状态
- `PUT /app/mall/cart/selected/batch` - 批量更新选中状态
#### 订单接口
- `POST /app/mall/order/submit` - 提交订单并发起支付
- `GET /app/mall/order/list` - 查询订单列表
- `GET /app/mall/order/{id}` - 查询订单详情
- `PUT /app/mall/order/cancel/{id}` - 取消订单
- `POST /app/mall/order/pay/notify` - 支付回调(由支付中心调用)
#### 优惠券接口
- `GET /app/mall/coupon/available/list` - 查询可领取的优惠券列表
- `GET /app/mall/coupon/{id}` - 获取优惠券详情
- `GET /app/mall/coupon/user/list` - 查询用户优惠券列表
- `GET /app/mall/coupon/user/available/list` - 查询用户可用优惠券列表
- `GET /app/mall/coupon/user/{id}` - 获取用户优惠券详情
- `POST /app/mall/coupon/user/receive/{couponId}` - 领取优惠券
### 管理端接口
#### 商品管理
- `GET /mall/product/list` - 查询商品列表
- `GET /mall/product/{id}` - 查询商品详情
- `POST /mall/product` - 新增商品
- `PUT /mall/product` - 修改商品
- `DELETE /mall/product/{ids}` - 删除商品
- `PUT /mall/product/status/{id}` - 上架/下架商品
#### 商品分类管理
- `GET /mall/product/category/list` - 查询商品分类列表(分页)
- `GET /mall/product/category/tree` - 查询商品分类树形列表
- `GET /mall/product/category/{id}` - 获取商品分类详情
- `POST /mall/product/category` - 新增商品分类
- `PUT /mall/product/category` - 修改商品分类
- `DELETE /mall/product/category/{ids}` - 删除商品分类
#### 优惠券管理
- `GET /mall/coupon/list` - 查询优惠券列表(分页)
- `GET /mall/coupon/{id}` - 获取优惠券详情
- `POST /mall/coupon` - 新增优惠券
- `PUT /mall/coupon` - 修改优惠券
- `DELETE /mall/coupon/{ids}` - 删除优惠券
- `POST /mall/coupon/export` - 导出优惠券列表
- `PUT /mall/coupon/changeStatus/{id}` - 启用/禁用优惠券
#### 用户优惠券管理
- `GET /mall/coupon/user/list` - 查询用户优惠券列表(分页)
- `GET /mall/coupon/user/{id}` - 获取用户优惠券详情
- `DELETE /mall/coupon/user/{ids}` - 删除用户优惠券
- `POST /mall/coupon/user/export` - 导出用户优惠券列表
- `POST /mall/product/category/export` - 导出商品分类列表
#### 品牌管理
- `GET /mall/product/brand/list` - 查询品牌列表(分页)
- `GET /mall/product/brand/{id}` - 获取品牌详情
- `POST /mall/product/brand` - 新增品牌
- `PUT /mall/product/brand` - 修改品牌
- `DELETE /mall/product/brand/{ids}` - 删除品牌
- `POST /mall/product/brand/export` - 导出品牌列表
#### SKU管理
- `GET /mall/sku/list` - 查询SKU列表
- `GET /mall/sku/{id}` - 查询SKU详情
- `GET /mall/sku/product/{productId}` - 根据商品ID查询SKU列表
- `POST /mall/sku` - 新增SKU
- `PUT /mall/sku` - 修改SKU
- `DELETE /mall/sku/{ids}` - 删除SKU
#### 订单管理
- `GET /mall/order/list` - 查询订单列表
- `GET /mall/order/{id}` - 查询订单详情
- `PUT /mall/order` - 修改订单
- `DELETE /mall/order/{ids}` - 删除订单
- `PUT /mall/order/delivery/{id}` - 订单发货
## 业务流程
### 订单提交流程
1. 用户选择商品(从购物车或直接购买)
2. 调用 `/app/mall/order/submit` 提交订单
3. 系统创建订单和订单明细
4. 调用支付中心创建支付单
5. 返回支付参数给前端
6. 前端调起支付
7. 支付成功后,支付中心回调 `/app/mall/order/pay/notify`
8. 系统更新订单状态并扣减库存
### 购物车流程
1. 用户浏览商品,选择SKU
2. 调用 `/app/mall/cart` 添加到购物车
3. 如果购物车中已存在该SKU,则更新数量
4. 用户可以在购物车中修改数量、选中状态
5. 提交订单时,从购物车获取商品信息
6. 订单创建成功后,删除购物车中的商品
## 依赖模块
- `ruoyi-pay` - 支付中心模块
- `ruoyi-common-*` - 通用工具模块
## 注意事项
1. **库存管理**:订单支付成功后才扣减库存,避免超卖
2. **幂等性**:支付回调需要保证幂等处理
3. **地址管理**:当前版本地址信息简化处理,后续可扩展地址表
4. **订单号生成**:使用时间戳+随机字符串生成唯一订单号
## 待完善功能
### 商品中心
- [ ] 商品属性管理
- [ ] 商品规格模板
- [ ] 商品搜索功能
### 交易中心
- ✅ 订单评价功能
- ✅ 售后管理功能(退款、退货、换货)
- [ ] 地址管理模块(可考虑集成到会员模块)
- [ ] 订单优惠券使用记录
### 营销中心
- [ ] 营销活动管理
- [ ] 拼团功能
- [ ] 秒杀功能
- [ ] 砍价功能
### 优惠券中心
- [ ] 优惠券管理
- [ ] 用户优惠券功能
- [ ] 优惠券统计
### 统计中心
- [ ] 交易分析
- [ ] 会员分析
- [ ] 商品分析
- [ ] 营销分析
+27
View File
@@ -0,0 +1,27 @@
<?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-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-mall</artifactId>
<packaging>pom</packaging>
<description>
ruoyi-mall 商城系统聚合模块
</description>
<modules>
<module>ruoyi-mall-product</module>
<module>ruoyi-mall-trade</module>
<module>ruoyi-mall-promotion</module>
<module>ruoyi-mall-coupon</module>
<module>ruoyi-mall-statistics</module>
</modules>
</project>
@@ -0,0 +1,133 @@
# 优惠券中心模块 (ruoyi-mall-coupon)
## 模块简介
`ruoyi-mall-coupon` 是商城系统的优惠券中心模块,提供独立的优惠券服务,支持发券、核销、过期处理等功能。
## 功能特性
### 1. 优惠券管理 ✅
- ✅ 优惠券CRUD操作
- ✅ 优惠券类型管理(满减、折扣、现金)
- ✅ 优惠券发放规则配置
- ✅ 优惠券有效期管理
- ✅ 优惠券状态管理(启用/禁用)
- ✅ 优惠券导出功能
### 2. 用户优惠券 ✅
- ✅ 用户领券
- ✅ 用户优惠券列表查询
- ✅ 优惠券使用(订单结算时调用)
- ✅ 优惠券退还(订单取消时调用)
- ✅ 优惠券过期处理(定时任务)✅
### 3. 优惠券统计
- [ ] 发放统计(可在管理端通过列表查询实现)
- [ ] 使用统计(可在管理端通过列表查询实现)
- [ ] 核销统计(可在管理端通过列表查询实现)
## 数据库表
- `mall_coupon` - 优惠券表
- `mall_coupon_user` - 用户优惠券表
详细表结构请参考:`script/sql/ry_mall.sql`
## 模块结构
```
org.dromara.mall.coupon
├── controller
│ ├── admin // 管理端API
│ │ ├── AdminMallCouponController.java
│ │ └── AdminMallCouponUserController.java
│ └── app // 用户端API
│ ├── AppMallCouponController.java
│ └── AppMallCouponUserController.java
├── domain
│ ├── MallCoupon.java
│ ├── MallCouponUser.java
│ ├── bo // 业务对象
│ │ ├── MallCouponBo.java
│ │ └── MallCouponUserBo.java
│ └── vo // 视图对象
│ ├── MallCouponVo.java
│ └── MallCouponUserVo.java
├── mapper
│ ├── MallCouponMapper.java
│ └── MallCouponUserMapper.java
├── service
│ ├── MallCouponService.java
│ ├── MallCouponUserService.java
│ └── impl
│ ├── MallCouponServiceImpl.java
│ └── MallCouponUserServiceImpl.java
├── job // 定时任务
│ └── MallCouponJob.java
└── config
└── MallCouponAutoConfiguration.java
```
## API接口
### 管理端接口
#### 优惠券管理 (`/mall/coupon`)
- `GET /mall/coupon/list` - 查询优惠券列表(分页)
- `GET /mall/coupon/{id}` - 获取优惠券详情
- `POST /mall/coupon` - 新增优惠券
- `PUT /mall/coupon` - 修改优惠券
- `DELETE /mall/coupon/{ids}` - 删除优惠券
- `POST /mall/coupon/export` - 导出优惠券列表
- `PUT /mall/coupon/changeStatus/{id}` - 启用/禁用优惠券
#### 用户优惠券管理 (`/mall/coupon/user`)
- `GET /mall/coupon/user/list` - 查询用户优惠券列表(分页)
- `GET /mall/coupon/user/{id}` - 获取用户优惠券详情
- `DELETE /mall/coupon/user/{ids}` - 删除用户优惠券
- `POST /mall/coupon/user/export` - 导出用户优惠券列表
### 移动端接口
#### 优惠券 (`/app/mall/coupon`)
- `GET /app/mall/coupon/available/list` - 查询可领取的优惠券列表
- `GET /app/mall/coupon/{id}` - 获取优惠券详情
#### 用户优惠券 (`/app/mall/coupon/user`)
- `GET /app/mall/coupon/user/list` - 查询用户优惠券列表
- `GET /app/mall/coupon/user/available/list` - 查询用户可用优惠券列表(未使用且未过期)
- `GET /app/mall/coupon/user/{id}` - 获取用户优惠券详情
- `POST /app/mall/coupon/user/receive/{couponId}` - 领取优惠券
## 优惠券类型说明
- **0 - 满减券**: 需要设置优惠金额和使用门槛
- **1 - 折扣券**: 需要设置折扣率(百分比)和使用门槛
- **2 - 现金券**: 需要设置优惠金额,无使用门槛
## 依赖模块
- `ruoyi-common-*` - 通用工具模块
- `ruoyi-common-excel` - Excel导出功能
- `ruoyi-common-satoken` - 用户身份认证
## 待实现功能
- [ ] 优惠券统计功能(可在管理端通过列表查询和筛选实现)
## 使用说明
### 订单结算时使用优惠券
在订单提交时,可以调用 `MallCouponUserService.useCoupon(couponUserId, orderId)` 来使用优惠券。
### 订单取消时退还优惠券
在订单取消时,可以调用 `MallCouponUserService.returnCoupon(couponUserId)` 来退还优惠券。
@@ -0,0 +1,67 @@
<?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-coupon</artifactId>
<description>
ruoyi-mall-coupon 优惠券中心模块
</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>
</dependencies>
</project>
@@ -0,0 +1,23 @@
package org.dromara.mall.coupon.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.coupon")
@ConditionalOnProperty(prefix = "ruoyi.mall.coupon", name = "enabled", havingValue = "true", matchIfMissing = true)
public class MallCouponAutoConfiguration {
public MallCouponAutoConfiguration() {
log.info("优惠券中心模块已启用");
}
}
@@ -0,0 +1,140 @@
package org.dromara.mall.coupon.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.coupon.domain.bo.MallCouponBo;
import org.dromara.mall.coupon.domain.vo.CouponStatisticsVo;
import org.dromara.mall.coupon.domain.vo.MallCouponVo;
import org.dromara.mall.coupon.service.MallCouponService;
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/coupon")
public class AdminMallCouponController extends BaseController {
private final MallCouponService mallCouponService;
/**
* 查询优惠券列表
*/
@Operation(summary = "查询优惠券列表", description = "分页查询优惠券列表")
@SaCheckPermission("mall:coupon:list")
@GetMapping("/list")
public TableDataInfo<MallCouponVo> list(@Parameter(description = "优惠券查询条件") MallCouponBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallCouponService.queryPageList(bo, pageQuery);
}
/**
* 导出售后申请列表
*/
@Operation(summary = "导出优惠券列表", description = "导出优惠券到Excel")
@SaCheckPermission("mall:coupon:export")
@Log(title = "优惠券", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "优惠券查询条件") MallCouponBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallCouponVo> list = mallCouponService.queryList(bo);
ExcelUtil.exportExcel(list, "优惠券", MallCouponVo.class, response);
}
/**
* 获取优惠券详细信息
*/
@Operation(summary = "获取优惠券详情", description = "根据优惠券ID获取详细信息")
@SaCheckPermission("mall:coupon:query")
@GetMapping("/{id}")
public R<MallCouponVo> getInfo(@Parameter(description = "优惠券ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallCouponService.queryById(id));
}
/**
* 新增优惠券
*/
@Operation(summary = "新增优惠券", description = "创建新的优惠券")
@SaCheckPermission("mall:coupon:add")
@Log(title = "优惠券", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "优惠券信息") @Validated @RequestBody MallCouponBo bo) {
return toAjax(mallCouponService.insertByBo(bo));
}
/**
* 修改优惠券
*/
@Operation(summary = "修改优惠券", description = "更新优惠券信息")
@SaCheckPermission("mall:coupon:edit")
@Log(title = "优惠券", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "优惠券信息") @Validated @RequestBody MallCouponBo bo) {
return toAjax(mallCouponService.updateByBo(bo));
}
/**
* 删除优惠券
*/
@Operation(summary = "删除优惠券", description = "删除优惠券")
@SaCheckPermission("mall:coupon:remove")
@Log(title = "优惠券", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "优惠券ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallCouponService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 启用/禁用优惠券
*/
@Operation(summary = "启用/禁用优惠券", description = "更改优惠券状态")
@SaCheckPermission("mall:coupon: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 = "状态", required = true)
@NotNull(message = "状态不能为空")
@RequestParam Integer status) {
return toAjax(mallCouponService.changeStatus(id, status));
}
/**
* 获取优惠券统计信息
*/
@Operation(summary = "获取优惠券统计信息", description = "获取优惠券的发放、使用、核销等统计数据")
@SaCheckPermission("mall:coupon:query")
@GetMapping("/statistics")
public R<CouponStatisticsVo> getStatistics(
@Parameter(description = "优惠券ID,为空则统计所有优惠券")
@RequestParam(required = false) Long couponId) {
return R.ok(mallCouponService.getStatistics(couponId));
}
}
@@ -0,0 +1,89 @@
package org.dromara.mall.coupon.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.coupon.domain.bo.MallCouponUserBo;
import org.dromara.mall.coupon.domain.vo.MallCouponUserVo;
import org.dromara.mall.coupon.service.MallCouponUserService;
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/coupon/user")
public class AdminMallCouponUserController extends BaseController {
private final MallCouponUserService mallCouponUserService;
/**
* 查询用户优惠券列表
*/
@Operation(summary = "查询用户优惠券列表", description = "分页查询用户优惠券列表")
@SaCheckPermission("mall:coupon:user:list")
@GetMapping("/list")
public TableDataInfo<MallCouponUserVo> list(@Parameter(description = "用户优惠券查询条件") MallCouponUserBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallCouponUserService.queryPageList(bo, pageQuery);
}
/**
* 导出用户优惠券列表
*/
@Operation(summary = "导出用户优惠券列表", description = "导出用户优惠券到Excel")
@SaCheckPermission("mall:coupon:user:export")
@Log(title = "用户优惠券", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "用户优惠券查询条件") MallCouponUserBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallCouponUserVo> list = mallCouponUserService.queryList(bo);
ExcelUtil.exportExcel(list, "用户优惠券", MallCouponUserVo.class, response);
}
/**
* 获取用户优惠券详细信息
*/
@Operation(summary = "获取用户优惠券详情", description = "根据用户优惠券ID获取详细信息")
@SaCheckPermission("mall:coupon:user:query")
@GetMapping("/{id}")
public R<MallCouponUserVo> getInfo(@Parameter(description = "用户优惠券ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallCouponUserService.queryById(id));
}
/**
* 删除用户优惠券
*/
@Operation(summary = "删除用户优惠券", description = "删除用户优惠券")
@SaCheckPermission("mall:coupon:user:remove")
@Log(title = "用户优惠券", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "用户优惠券ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallCouponUserService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,50 @@
package org.dromara.mall.coupon.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.mall.coupon.domain.vo.MallCouponVo;
import org.dromara.mall.coupon.service.MallCouponService;
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 = "优惠券接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/coupon")
public class AppMallCouponController {
private final MallCouponService mallCouponService;
/**
* 查询可领取的优惠券列表
*/
@Operation(summary = "查询可领取的优惠券列表", description = "查询用户可以领取的优惠券列表")
@GetMapping("/available/list")
public R<List<MallCouponVo>> getAvailableList() {
List<MallCouponVo> list = mallCouponService.queryAvailableList();
return R.ok(list);
}
/**
* 获取优惠券详情
*/
@Operation(summary = "获取优惠券详情", description = "根据优惠券ID获取详细信息")
@GetMapping("/{id}")
public R<MallCouponVo> getInfo(@Parameter(description = "优惠券ID", required = true)
@PathVariable Long id) {
return R.ok(mallCouponService.queryById(id));
}
}
@@ -0,0 +1,78 @@
package org.dromara.mall.coupon.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 jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.coupon.domain.bo.MallCouponUserBo;
import org.dromara.mall.coupon.domain.vo.MallCouponUserVo;
import org.dromara.mall.coupon.service.MallCouponUserService;
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/coupon/user")
public class AppMallCouponUserController {
private final MallCouponUserService mallCouponUserService;
/**
* 查询用户优惠券列表
*/
@Operation(summary = "查询用户优惠券列表", description = "查询当前用户的优惠券列表")
@GetMapping("/list")
public R<TableDataInfo<MallCouponUserVo>> list(@Parameter(description = "用户优惠券查询条件") MallCouponUserBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
// 移动端只能查询当前用户的优惠券
TableDataInfo<MallCouponUserVo> result = mallCouponUserService.queryPageList(bo, pageQuery);
return R.ok(result);
}
/**
* 查询用户可用优惠券列表
*/
@Operation(summary = "查询用户可用优惠券列表", description = "查询当前用户可用的优惠券列表(未使用且未过期)")
@GetMapping("/available/list")
public R<List<MallCouponUserVo>> getAvailableList() {
Long userId = org.dromara.common.satoken.utils.LoginHelper.getUserId();
List<MallCouponUserVo> list = mallCouponUserService.queryAvailableList(userId);
return R.ok(list);
}
/**
* 获取用户优惠券详情
*/
@Operation(summary = "获取用户优惠券详情", description = "根据用户优惠券ID获取详细信息")
@GetMapping("/{id}")
public R<MallCouponUserVo> getInfo(@Parameter(description = "用户优惠券ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallCouponUserService.queryById(id));
}
/**
* 领取优惠券
*/
@Operation(summary = "领取优惠券", description = "用户领取优惠券")
@PostMapping("/receive/{couponId}")
public R<Void> receiveCoupon(@Parameter(description = "优惠券ID", required = true)
@NotNull(message = "优惠券ID不能为空")
@PathVariable Long couponId) {
return R.ok(mallCouponUserService.receiveCoupon(couponId));
}
}
@@ -0,0 +1,118 @@
package org.dromara.mall.coupon.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.math.BigDecimal;
import java.util.Date;
/**
* 优惠券对象 mall_coupon
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_coupon")
public class MallCoupon implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 优惠券ID
*/
@TableId
private Long id;
/**
* 优惠券名称
*/
private String name;
/**
* 优惠券类型(0满减 1折扣 2现金)
*/
private Integer type;
/**
* 优惠金额(分)
*/
private Integer discountAmount;
/**
* 折扣率(%)
*/
private BigDecimal discountRate;
/**
* 使用门槛(分)
*/
private Integer minAmount;
/**
* 发放总数(null表示不限)
*/
private Integer totalCount;
/**
* 已使用数量
*/
private Integer usedCount;
/**
* 每人限领数量
*/
private Integer perLimit;
/**
* 有效开始时间
*/
private Date validStartTime;
/**
* 有效结束时间
*/
private Date validEndTime;
/**
* 状态(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,54 @@
package org.dromara.mall.coupon.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
/**
* 用户优惠券对象 mall_coupon_user
*
* @author pengles
*/
@Data
@TableName("mall_coupon_user")
public class MallCouponUser {
/**
* ID
*/
@TableId
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 优惠券ID
*/
private Long couponId;
/**
* 状态(0未使用 1已使用 2已过期)
*/
private Integer status;
/**
* 使用时间
*/
private Date usedTime;
/**
* 使用的订单ID
*/
private Long orderId;
/**
* 领取时间
*/
private Date createTime;
}
@@ -0,0 +1,99 @@
package org.dromara.mall.coupon.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.coupon.domain.MallCoupon;
import java.math.BigDecimal;
import java.util.Date;
/**
* 优惠券业务对象 mall_coupon
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallCoupon.class, reverseConvertGenerate = false)
public class MallCouponBo extends BaseEntity {
/**
* 优惠券ID
*/
private Long id;
/**
* 优惠券名称
*/
@NotBlank(message = "优惠券名称不能为空")
private String name;
/**
* 优惠券类型(0满减 1折扣 2现金)
*/
@NotNull(message = "优惠券类型不能为空")
private Integer type;
/**
* 优惠金额(分)
*/
@Min(value = 0, message = "优惠金额不能为负数")
private Integer discountAmount;
/**
* 折扣率(%)
*/
@Min(value = 0, message = "折扣率不能为负数")
private BigDecimal discountRate;
/**
* 使用门槛(分)
*/
@Min(value = 0, message = "使用门槛不能为负数")
private Integer minAmount;
/**
* 发放总数(null表示不限)
*/
@Min(value = 1, message = "发放总数必须大于0")
private Integer totalCount;
/**
* 已使用数量
*/
private Integer usedCount;
/**
* 每人限领数量
*/
@Min(value = 1, message = "每人限领数量必须大于0")
private Integer perLimit;
/**
* 有效开始时间
*/
@NotNull(message = "有效开始时间不能为空")
private Date validStartTime;
/**
* 有效结束时间
*/
@NotNull(message = "有效结束时间不能为空")
private Date validEndTime;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,51 @@
package org.dromara.mall.coupon.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.mall.coupon.domain.MallCouponUser;
import java.util.Date;
/**
* 用户优惠券业务对象 mall_coupon_user
*
* @author pengles
*/
@Data
@AutoMapper(target = MallCouponUser.class, reverseConvertGenerate = false)
public class MallCouponUserBo {
/**
* ID
*/
private Long id;
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空")
private Long userId;
/**
* 优惠券ID
*/
@NotNull(message = "优惠券ID不能为空")
private Long couponId;
/**
* 状态(0未使用 1已使用 2已过期)
*/
private Integer status;
/**
* 使用时间
*/
private Date usedTime;
/**
* 使用的订单ID
*/
private Long orderId;
}
@@ -0,0 +1,49 @@
package org.dromara.mall.coupon.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 优惠券统计数据视图对象
*
* @author pengles
*/
@Data
public class CouponStatisticsVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 优惠券ID
*/
private Long couponId;
/**
* 优惠券名称
*/
private String couponName;
/**
* 发放总数(已领取数量)
*/
private Long issuedCount;
/**
* 使用数量(已使用数量)
*/
private Long usedCount;
/**
* 使用率(使用数量/发放数量,百分比)
*/
private Double usageRate;
/**
* 核销金额(分,基于订单优惠券使用记录表)
*/
private Long redeemedAmount;
}
@@ -0,0 +1,63 @@
package org.dromara.mall.coupon.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.coupon.domain.MallCouponUser;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 用户优惠券视图对象 mall_coupon_user
*
* @author pengles
*/
@Data
@AutoMapper(target = MallCouponUser.class)
public class MallCouponUserVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 优惠券ID
*/
private Long couponId;
/**
* 状态(0未使用 1已使用 2已过期)
*/
private Integer status;
/**
* 使用时间
*/
private Date usedTime;
/**
* 使用的订单ID
*/
private Long orderId;
/**
* 领取时间
*/
private Date createTime;
/**
* 优惠券信息(关联查询)
*/
private MallCouponVo couponInfo;
}
@@ -0,0 +1,99 @@
package org.dromara.mall.coupon.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.coupon.domain.MallCoupon;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 优惠券视图对象 mall_coupon
*
* @author pengles
*/
@Data
@AutoMapper(target = MallCoupon.class)
public class MallCouponVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 优惠券ID
*/
private Long id;
/**
* 优惠券名称
*/
private String name;
/**
* 优惠券类型(0满减 1折扣 2现金)
*/
private Integer type;
/**
* 优惠金额(分)
*/
private Integer discountAmount;
/**
* 折扣率(%)
*/
private BigDecimal discountRate;
/**
* 使用门槛(分)
*/
private Integer minAmount;
/**
* 发放总数(null表示不限)
*/
private Integer totalCount;
/**
* 已使用数量
*/
private Integer usedCount;
/**
* 每人限领数量
*/
private Integer perLimit;
/**
* 有效开始时间
*/
private Date validStartTime;
/**
* 有效结束时间
*/
private Date validEndTime;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 备注
*/
private String remark;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,37 @@
package org.dromara.mall.coupon.job;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mall.coupon.service.MallCouponUserService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 优惠券定时任务
*
* @author pengles
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class MallCouponJob {
private final MallCouponUserService couponUserService;
/**
* 优惠券过期处理任务
* 每天凌晨2点执行一次
* 处理所有已过期的优惠券,将状态更新为已过期(status=2)
*/
@Scheduled(cron = "0 0 2 * * ?")
public void processExpiredCoupons() {
log.info("开始执行优惠券过期处理任务");
try {
couponUserService.processExpiredCoupons();
log.info("优惠券过期处理任务执行完成");
} catch (Exception e) {
log.error("优惠券过期处理任务执行失败", e);
}
}
}
@@ -0,0 +1,16 @@
package org.dromara.mall.coupon.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.coupon.domain.MallCoupon;
import org.dromara.mall.coupon.domain.vo.MallCouponVo;
/**
* 优惠券Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallCouponMapper extends BaseMapperPlus<MallCoupon, MallCouponVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.coupon.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.coupon.domain.MallCouponUser;
import org.dromara.mall.coupon.domain.vo.MallCouponUserVo;
/**
* 用户优惠券Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallCouponUserMapper extends BaseMapperPlus<MallCouponUser, MallCouponUserVo> {
}
@@ -0,0 +1,67 @@
package org.dromara.mall.coupon.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.coupon.domain.bo.MallCouponBo;
import org.dromara.mall.coupon.domain.vo.CouponStatisticsVo;
import org.dromara.mall.coupon.domain.vo.MallCouponVo;
import java.util.Collection;
import java.util.List;
/**
* 优惠券Service接口
*
* @author pengles
*/
public interface MallCouponService {
/**
* 查询优惠券
*/
MallCouponVo queryById(Long id);
/**
* 查询优惠券列表
*/
TableDataInfo<MallCouponVo> queryPageList(MallCouponBo bo, PageQuery pageQuery);
/**
* 查询优惠券列表
*/
List<MallCouponVo> queryList(MallCouponBo bo);
/**
* 查询可领取的优惠券列表(移动端)
*/
List<MallCouponVo> queryAvailableList();
/**
* 新增优惠券
*/
Boolean insertByBo(MallCouponBo bo);
/**
* 修改优惠券
*/
Boolean updateByBo(MallCouponBo bo);
/**
* 校验并批量删除优惠券信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 启用/禁用优惠券
*/
Boolean changeStatus(Long id, Integer status);
/**
* 获取优惠券统计信息
*
* @param couponId 优惠券ID,如果为空则统计所有优惠券
* @return 优惠券统计数据
*/
CouponStatisticsVo getStatistics(Long couponId);
}
@@ -0,0 +1,63 @@
package org.dromara.mall.coupon.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.coupon.domain.bo.MallCouponUserBo;
import org.dromara.mall.coupon.domain.vo.MallCouponUserVo;
import java.util.Collection;
import java.util.List;
/**
* 用户优惠券Service接口
*
* @author pengles
*/
public interface MallCouponUserService {
/**
* 查询用户优惠券
*/
MallCouponUserVo queryById(Long id);
/**
* 查询用户优惠券列表
*/
TableDataInfo<MallCouponUserVo> queryPageList(MallCouponUserBo bo, PageQuery pageQuery);
/**
* 查询用户优惠券列表
*/
List<MallCouponUserVo> queryList(MallCouponUserBo bo);
/**
* 查询用户可用优惠券列表
*/
List<MallCouponUserVo> queryAvailableList(Long userId);
/**
* 用户领取优惠券
*/
Boolean receiveCoupon(Long couponId);
/**
* 使用优惠券(订单结算时调用)
*/
Boolean useCoupon(Long couponUserId, Long orderId);
/**
* 退还优惠券(订单取消时调用)
*/
Boolean returnCoupon(Long couponUserId);
/**
* 批量删除用户优惠券信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 处理过期优惠券(定时任务调用)
*/
void processExpiredCoupons();
}
@@ -0,0 +1,264 @@
package org.dromara.mall.coupon.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.coupon.domain.MallCoupon;
import org.dromara.mall.coupon.domain.MallCouponUser;
import org.dromara.mall.coupon.domain.bo.MallCouponBo;
import org.dromara.mall.coupon.domain.vo.CouponStatisticsVo;
import org.dromara.mall.coupon.domain.vo.MallCouponVo;
import org.dromara.mall.coupon.mapper.MallCouponMapper;
import org.dromara.mall.coupon.mapper.MallCouponUserMapper;
import org.dromara.mall.coupon.service.MallCouponService;
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 MallCouponServiceImpl implements MallCouponService {
private final MallCouponMapper baseMapper;
private final MallCouponUserMapper couponUserMapper;
/**
* 查询优惠券
*/
@Override
public MallCouponVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询优惠券列表
*/
@Override
public TableDataInfo<MallCouponVo> queryPageList(MallCouponBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallCoupon> lqw = buildQueryWrapper(bo);
Page<MallCouponVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询优惠券列表
*/
@Override
public List<MallCouponVo> queryList(MallCouponBo bo) {
LambdaQueryWrapper<MallCoupon> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 查询可领取的优惠券列表(移动端)
*/
@Override
public List<MallCouponVo> queryAvailableList() {
LambdaQueryWrapper<MallCoupon> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallCoupon::getStatus, 0); // 启用状态
Date now = new Date();
lqw.le(MallCoupon::getValidStartTime, now); // 已开始
lqw.ge(MallCoupon::getValidEndTime, now); // 未结束
// 发放总数未限制或已发放数量小于总数
lqw.and(wrapper -> wrapper
.isNull(MallCoupon::getTotalCount)
.or(wrapper2 -> wrapper2.apply("used_count < total_count"))
);
lqw.orderByDesc(MallCoupon::getCreateTime);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallCoupon> buildQueryWrapper(MallCouponBo bo) {
LambdaQueryWrapper<MallCoupon> lqw = new LambdaQueryWrapper<>();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallCoupon::getName, bo.getName());
lqw.eq(bo.getType() != null, MallCoupon::getType, bo.getType());
lqw.eq(bo.getStatus() != null, MallCoupon::getStatus, bo.getStatus());
lqw.ge(bo.getValidStartTime() != null, MallCoupon::getValidStartTime, bo.getValidStartTime());
lqw.le(bo.getValidEndTime() != null, MallCoupon::getValidEndTime, bo.getValidEndTime());
lqw.orderByDesc(MallCoupon::getCreateTime);
return lqw;
}
/**
* 新增优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallCouponBo bo) {
MallCoupon add = MapstructUtils.convert(bo, MallCoupon.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getMinAmount() == null) {
add.setMinAmount(0);
}
if (add.getUsedCount() == null) {
add.setUsedCount(0);
}
if (add.getPerLimit() == null) {
add.setPerLimit(1);
}
if (add.getStatus() == null) {
add.setStatus(0); // 默认启用
}
// 校验优惠券类型和对应字段
validateCouponType(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallCouponBo bo) {
MallCoupon update = MapstructUtils.convert(bo, MallCoupon.class);
validEntityBeforeSave(update);
// 校验优惠券类型和对应字段
validateCouponType(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallCoupon entity) {
// 校验有效期
if (entity.getValidStartTime() != null && entity.getValidEndTime() != null) {
if (entity.getValidEndTime().before(entity.getValidStartTime())) {
throw new ServiceException("有效结束时间不能早于开始时间");
}
}
}
/**
* 校验优惠券类型和对应字段
*/
private void validateCouponType(MallCoupon entity) {
if (entity.getType() == null) {
return;
}
// 0-满减,1-折扣,2-现金
if (entity.getType() == 0) {
// 满减类型必须设置优惠金额
if (entity.getDiscountAmount() == null || entity.getDiscountAmount() <= 0) {
throw new ServiceException("满减类型优惠券必须设置优惠金额");
}
} else if (entity.getType() == 1) {
// 折扣类型必须设置折扣率
if (entity.getDiscountRate() == null || entity.getDiscountRate().compareTo(java.math.BigDecimal.ZERO) <= 0) {
throw new ServiceException("折扣类型优惠券必须设置折扣率");
}
if (entity.getDiscountRate().compareTo(new java.math.BigDecimal("100")) > 0) {
throw new ServiceException("折扣率不能大于100%");
}
} else if (entity.getType() == 2) {
// 现金类型必须设置优惠金额
if (entity.getDiscountAmount() == null || entity.getDiscountAmount() <= 0) {
throw new ServiceException("现金类型优惠券必须设置优惠金额");
}
}
}
/**
* 批量删除优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 可以添加业务校验逻辑,如:已使用数量大于0的优惠券不能删除
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 启用/禁用优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean changeStatus(Long id, Integer status) {
MallCoupon coupon = baseMapper.selectById(id);
if (coupon == null) {
throw new ServiceException("优惠券不存在");
}
if (status != 0 && status != 1) {
throw new ServiceException("状态值不正确");
}
coupon.setStatus(status);
return baseMapper.updateById(coupon) > 0;
}
/**
* 获取优惠券统计信息
*/
@Override
public CouponStatisticsVo getStatistics(Long couponId) {
CouponStatisticsVo vo = new CouponStatisticsVo();
vo.setCouponId(couponId);
// 如果指定了优惠券ID,获取优惠券信息
if (couponId != null) {
MallCouponVo couponVo = baseMapper.selectVoById(couponId);
if (couponVo != null) {
vo.setCouponName(couponVo.getName());
}
}
// 统计发放数量(已领取数量,即mall_coupon_user表中的总记录数)
LambdaQueryWrapper<MallCouponUser> issuedWrapper = new LambdaQueryWrapper<>();
if (couponId != null) {
issuedWrapper.eq(MallCouponUser::getCouponId, couponId);
}
Long issuedCount = couponUserMapper.selectCount(issuedWrapper);
vo.setIssuedCount(issuedCount != null ? issuedCount : 0L);
// 统计使用数量(status=1的记录数)
LambdaQueryWrapper<MallCouponUser> usedWrapper = new LambdaQueryWrapper<>();
if (couponId != null) {
usedWrapper.eq(MallCouponUser::getCouponId, couponId);
}
usedWrapper.eq(MallCouponUser::getStatus, 1); // 已使用
Long usedCount = couponUserMapper.selectCount(usedWrapper);
vo.setUsedCount(usedCount != null ? usedCount : 0L);
// 计算使用率
if (vo.getIssuedCount() > 0) {
double usageRate = (double) vo.getUsedCount() / vo.getIssuedCount() * 100;
vo.setUsageRate(Math.round(usageRate * 100.0) / 100.0);
} else {
vo.setUsageRate(0.0);
}
// 核销金额统计(基于mall_order_coupon表,需要在交易中心模块查询)
// 由于跨模块依赖问题,这里暂时设为0
// 如果需要在优惠券模块直接查询,可以考虑通过Feign调用交易中心模块的接口,或者将统计功能放在交易中心模块
// 当前实现:如果交易中心模块已实现订单优惠券记录,可以通过依赖注入MallOrderCouponService来查询
// TODO: 考虑通过服务间调用或事件机制来获取核销金额统计
vo.setRedeemedAmount(0L);
return vo;
}
}
@@ -0,0 +1,296 @@
package org.dromara.mall.coupon.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.mall.coupon.domain.MallCoupon;
import org.dromara.mall.coupon.domain.MallCouponUser;
import org.dromara.mall.coupon.domain.bo.MallCouponUserBo;
import org.dromara.mall.coupon.domain.vo.MallCouponUserVo;
import org.dromara.mall.coupon.domain.vo.MallCouponVo;
import org.dromara.mall.coupon.mapper.MallCouponMapper;
import org.dromara.mall.coupon.mapper.MallCouponUserMapper;
import org.dromara.mall.coupon.service.MallCouponUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户优惠券Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallCouponUserServiceImpl implements MallCouponUserService {
private final MallCouponUserMapper baseMapper;
private final MallCouponMapper couponMapper;
/**
* 查询用户优惠券
*/
@Override
public MallCouponUserVo queryById(Long id) {
MallCouponUserVo vo = baseMapper.selectVoById(id);
if (vo != null && vo.getCouponId() != null) {
// 关联查询优惠券信息
MallCouponVo couponVo = couponMapper.selectVoById(vo.getCouponId());
vo.setCouponInfo(couponVo);
}
return vo;
}
/**
* 查询用户优惠券列表
*/
@Override
public TableDataInfo<MallCouponUserVo> queryPageList(MallCouponUserBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallCouponUser> lqw = buildQueryWrapper(bo);
Page<MallCouponUserVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
// 关联查询优惠券信息
fillCouponInfo(result.getRecords());
return TableDataInfo.build(result);
}
/**
* 查询用户优惠券列表
*/
@Override
public List<MallCouponUserVo> queryList(MallCouponUserBo bo) {
LambdaQueryWrapper<MallCouponUser> lqw = buildQueryWrapper(bo);
List<MallCouponUserVo> list = baseMapper.selectVoList(lqw);
// 关联查询优惠券信息
fillCouponInfo(list);
return list;
}
/**
* 查询用户可用优惠券列表
*/
@Override
public List<MallCouponUserVo> queryAvailableList(Long userId) {
LambdaQueryWrapper<MallCouponUser> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallCouponUser::getUserId, userId);
lqw.eq(MallCouponUser::getStatus, 0); // 未使用
lqw.orderByDesc(MallCouponUser::getCreateTime);
List<MallCouponUserVo> list = baseMapper.selectVoList(lqw);
// 过滤已过期的优惠券
Date now = new Date();
list = list.stream().filter(vo -> {
if (vo.getCouponId() == null) {
return false;
}
MallCouponVo couponVo = couponMapper.selectVoById(vo.getCouponId());
if (couponVo == null || couponVo.getStatus() != 0) {
return false;
}
return couponVo.getValidEndTime() != null && couponVo.getValidEndTime().after(now);
}).collect(Collectors.toList());
// 关联查询优惠券信息
fillCouponInfo(list);
return list;
}
/**
* 填充优惠券信息
*/
private void fillCouponInfo(List<MallCouponUserVo> list) {
if (list == null || list.isEmpty()) {
return;
}
for (MallCouponUserVo vo : list) {
if (vo.getCouponId() != null) {
MallCouponVo couponVo = couponMapper.selectVoById(vo.getCouponId());
vo.setCouponInfo(couponVo);
}
}
}
private LambdaQueryWrapper<MallCouponUser> buildQueryWrapper(MallCouponUserBo bo) {
LambdaQueryWrapper<MallCouponUser> lqw = new LambdaQueryWrapper<>();
lqw.eq(bo.getUserId() != null, MallCouponUser::getUserId, bo.getUserId());
lqw.eq(bo.getCouponId() != null, MallCouponUser::getCouponId, bo.getCouponId());
lqw.eq(bo.getStatus() != null, MallCouponUser::getStatus, bo.getStatus());
lqw.orderByDesc(MallCouponUser::getCreateTime);
return lqw;
}
/**
* 用户领取优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean receiveCoupon(Long couponId) {
Long userId = LoginHelper.getUserId();
// 查询优惠券信息
MallCoupon coupon = couponMapper.selectById(couponId);
if (coupon == null) {
throw new ServiceException("优惠券不存在");
}
if (coupon.getStatus() != 0) {
throw new ServiceException("优惠券已禁用");
}
// 校验有效期
Date now = new Date();
if (coupon.getValidStartTime().after(now) || coupon.getValidEndTime().before(now)) {
throw new ServiceException("优惠券不在有效期内");
}
// 校验发放数量
if (coupon.getTotalCount() != null) {
if (coupon.getUsedCount() >= coupon.getTotalCount()) {
throw new ServiceException("优惠券已发放完毕");
}
}
// 校验每人限领数量
LambdaQueryWrapper<MallCouponUser> countWrapper = new LambdaQueryWrapper<>();
countWrapper.eq(MallCouponUser::getUserId, userId);
countWrapper.eq(MallCouponUser::getCouponId, couponId);
Long receivedCount = baseMapper.selectCount(countWrapper);
if (receivedCount >= coupon.getPerLimit()) {
throw new ServiceException("已达到每人限领数量");
}
// 创建用户优惠券记录
MallCouponUser couponUser = new MallCouponUser();
couponUser.setUserId(userId);
couponUser.setCouponId(couponId);
couponUser.setStatus(0); // 未使用
couponUser.setCreateTime(now);
boolean flag = baseMapper.insert(couponUser) > 0;
if (flag) {
// 更新优惠券已发放数量(注意:这里used_count实际存储的是已发放数量,不是已使用数量)
// 如果业务需要区分,可以考虑增加字段
LambdaUpdateWrapper<MallCoupon> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(MallCoupon::getId, couponId);
updateWrapper.setSql("used_count = used_count + 1");
couponMapper.update(null, updateWrapper);
}
return flag;
}
/**
* 使用优惠券(订单结算时调用)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean useCoupon(Long couponUserId, Long orderId) {
MallCouponUser couponUser = baseMapper.selectById(couponUserId);
if (couponUser == null) {
throw new ServiceException("用户优惠券不存在");
}
if (couponUser.getStatus() != 0) {
throw new ServiceException("优惠券已使用或已过期");
}
// 校验优惠券是否过期
MallCoupon coupon = couponMapper.selectById(couponUser.getCouponId());
if (coupon == null) {
throw new ServiceException("优惠券不存在");
}
Date now = new Date();
if (coupon.getValidEndTime().before(now)) {
throw new ServiceException("优惠券已过期");
}
// 更新用户优惠券状态
couponUser.setStatus(1); // 已使用
couponUser.setUsedTime(now);
couponUser.setOrderId(orderId);
return baseMapper.updateById(couponUser) > 0;
}
/**
* 退还优惠券(订单取消时调用)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean returnCoupon(Long couponUserId) {
MallCouponUser couponUser = baseMapper.selectById(couponUserId);
if (couponUser == null) {
throw new ServiceException("用户优惠券不存在");
}
if (couponUser.getStatus() != 1) {
throw new ServiceException("优惠券状态不正确,无法退还");
}
// 校验优惠券是否过期
MallCoupon coupon = couponMapper.selectById(couponUser.getCouponId());
if (coupon == null) {
throw new ServiceException("优惠券不存在");
}
Date now = new Date();
if (coupon.getValidEndTime().before(now)) {
// 已过期,状态改为已过期
couponUser.setStatus(2);
} else {
// 未过期,状态改为未使用
couponUser.setStatus(0);
}
couponUser.setUsedTime(null);
couponUser.setOrderId(null);
return baseMapper.updateById(couponUser) > 0;
}
/**
* 批量删除用户优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 可以添加业务校验逻辑
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 处理过期优惠券(定时任务调用)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void processExpiredCoupons() {
Date now = new Date();
// 查询所有未使用的用户优惠券
LambdaQueryWrapper<MallCouponUser> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallCouponUser::getStatus, 0); // 未使用
List<MallCouponUser> list = baseMapper.selectList(lqw);
for (MallCouponUser couponUser : list) {
MallCoupon coupon = couponMapper.selectById(couponUser.getCouponId());
if (coupon != null && coupon.getValidEndTime().before(now)) {
// 优惠券已过期,更新状态
couponUser.setStatus(2); // 已过期
baseMapper.updateById(couponUser);
}
}
}
}
@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.mall.coupon.config.MallCouponAutoConfiguration
@@ -0,0 +1,223 @@
# 商品中心模块 (ruoyi-mall-product)
## 模块简介
`ruoyi-mall-product` 是商城系统的商品中心模块,负责商品全生命周期管理,包括商品(SPU)、SKU、分类、品牌等核心功能。
## 功能特性
### 1. 商品管理 (SPU)
- ✅ 商品CRUD操作
- ✅ 商品上架/下架
- ✅ 商品浏览量和销量统计
- ✅ 商品详情管理
### 2. SKU管理
- ✅ SKU CRUD操作
- ✅ 库存管理
- ✅ 库存扣减和恢复
- ✅ 规格属性管理
### 3. 商品分类管理 ✅
- ✅ 分类树形结构查询(支持三级分类)
- ✅ 分类CRUD操作(列表、详情、新增、修改、删除)
- ✅ 分类排序
- ✅ 分类导出功能
- ⏳ 分类图标管理(字段已支持,需前端集成文件上传功能)
### 4. 品牌管理 ✅
- ✅ 品牌CRUD操作(列表、详情、新增、修改、删除)
- ✅ 品牌描述管理
- ✅ 品牌排序
- ✅ 品牌状态管理(启用/禁用)
- ✅ 品牌导出功能
- ⏳ 品牌Logo上传(字段已支持,需前端集成文件上传功能)
## 数据库表
- `mall_product` - 商品表
- `mall_sku` - SKU表
- `mall_category` - 商品分类表
- `mall_brand` - 品牌表
- `mall_attribute_template` - 属性模板表
- `mall_attribute` - 属性表
- `mall_attribute_value` - 属性值表
- `mall_product_attribute` - 商品属性关联表
详细表结构请参考:`script/sql/ry_mall.sql`
## 模块结构
```
org.dromara.mall.product
├── controller
│ ├── admin // 管理端API
│ │ ├── AdminMallCategoryController.java
│ │ ├── AdminMallBrandController.java
│ │ ├── AdminMallAttributeTemplateController.java
│ │ ├── AdminMallAttributeController.java
│ │ ├── AdminMallAttributeValueController.java
│ │ └── AdminMallProductAttributeController.java
│ └── app // 用户端API
│ ├── AppMallCategoryController.java
│ └── AppMallBrandController.java
├── domain
│ ├── MallCategory.java
│ ├── MallBrand.java
│ ├── MallAttributeTemplate.java
│ ├── MallAttribute.java
│ ├── MallAttributeValue.java
│ ├── MallProductAttribute.java
│ ├── bo // 业务对象
│ │ ├── MallCategoryBo.java
│ │ ├── MallBrandBo.java
│ │ ├── MallAttributeTemplateBo.java
│ │ ├── MallAttributeBo.java
│ │ ├── MallAttributeValueBo.java
│ │ └── MallProductAttributeBo.java
│ └── vo // 视图对象
│ ├── MallCategoryVo.java
│ ├── MallBrandVo.java
│ ├── MallAttributeTemplateVo.java
│ ├── MallAttributeVo.java
│ ├── MallAttributeValueVo.java
│ └── MallProductAttributeVo.java
├── mapper
│ ├── MallCategoryMapper.java
│ ├── MallBrandMapper.java
│ ├── MallAttributeTemplateMapper.java
│ ├── MallAttributeMapper.java
│ ├── MallAttributeValueMapper.java
│ └── MallProductAttributeMapper.java
├── service
│ ├── MallCategoryService.java
│ ├── MallBrandService.java
│ ├── MallAttributeTemplateService.java
│ ├── MallAttributeService.java
│ ├── MallAttributeValueService.java
│ ├── MallProductAttributeService.java
│ └── impl
│ ├── MallCategoryServiceImpl.java
│ ├── MallBrandServiceImpl.java
│ ├── MallAttributeTemplateServiceImpl.java
│ ├── MallAttributeServiceImpl.java
│ ├── MallAttributeValueServiceImpl.java
│ └── MallProductAttributeServiceImpl.java
├── enums // 枚举类
│ └── AttributeType.java
└── config
└── MallProductAutoConfiguration.java
```
## 依赖模块
- `ruoyi-common-*` - 通用工具模块
## API接口
### 管理端接口
#### 商品分类管理 (`/mall/product/category`)
- `GET /mall/product/category/list` - 查询商品分类列表(分页)
- `GET /mall/product/category/tree` - 查询商品分类树形列表
- `GET /mall/product/category/{id}` - 获取商品分类详情
- `POST /mall/product/category` - 新增商品分类
- `PUT /mall/product/category` - 修改商品分类
- `DELETE /mall/product/category/{ids}` - 删除商品分类
- `POST /mall/product/category/export` - 导出商品分类列表
#### 品牌管理 (`/mall/product/brand`)
- `GET /mall/product/brand/list` - 查询品牌列表(分页)
- `GET /mall/product/brand/{id}` - 获取品牌详情
- `POST /mall/product/brand` - 新增品牌
- `PUT /mall/product/brand` - 修改品牌
- `DELETE /mall/product/brand/{ids}` - 删除品牌
- `POST /mall/product/brand/export` - 导出品牌列表
### 移动端接口
#### 商品分类 (`/app/mall/product/category`)
- `GET /app/mall/product/category/tree` - 查询商品分类树形列表(仅启用分类)
#### 品牌 (`/app/mall/product/brand`)
- `GET /app/mall/product/brand/list` - 查询品牌列表(仅启用品牌)
#### 属性模板管理 (`/mall/product/attribute/template`)
- `GET /mall/product/attribute/template/list` - 查询属性模板列表(分页)
- `GET /mall/product/attribute/template/{id}` - 获取属性模板详情
- `POST /mall/product/attribute/template` - 新增属性模板
- `PUT /mall/product/attribute/template` - 修改属性模板
- `DELETE /mall/product/attribute/template/{ids}` - 删除属性模板
- `POST /mall/product/attribute/template/export` - 导出属性模板列表
#### 属性管理 (`/mall/product/attribute`)
- `GET /mall/product/attribute/list` - 查询属性列表(分页)
- `GET /mall/product/attribute/{id}` - 获取属性详情
- `GET /mall/product/attribute/template/{templateId}` - 根据模板ID查询属性列表
- `POST /mall/product/attribute` - 新增属性
- `PUT /mall/product/attribute` - 修改属性
- `DELETE /mall/product/attribute/{ids}` - 删除属性
- `POST /mall/product/attribute/export` - 导出属性列表
#### 属性值管理 (`/mall/product/attribute/value`)
- `GET /mall/product/attribute/value/list` - 查询属性值列表(分页)
- `GET /mall/product/attribute/value/{id}` - 获取属性值详情
- `GET /mall/product/attribute/value/attribute/{attributeId}` - 根据属性ID查询属性值列表
- `POST /mall/product/attribute/value` - 新增属性值
- `PUT /mall/product/attribute/value` - 修改属性值
- `DELETE /mall/product/attribute/value/{ids}` - 删除属性值
- `POST /mall/product/attribute/value/export` - 导出属性值列表
#### 商品属性关联管理 (`/mall/product/attribute/product`)
- `GET /mall/product/attribute/product/{id}` - 获取商品属性关联详情
- `GET /mall/product/attribute/product/product/{productId}` - 根据商品ID查询商品属性关联列表
- `POST /mall/product/attribute/product` - 新增商品属性关联
- `POST /mall/product/attribute/product/batch` - 批量新增商品属性关联
- `POST /mall/product/attribute/product/product/{productId}` - 保存商品属性关联(先删除旧的,再插入新的)
- `PUT /mall/product/attribute/product` - 修改商品属性关联
- `DELETE /mall/product/attribute/product/{ids}` - 删除商品属性关联
- `DELETE /mall/product/attribute/product/product/{productId}` - 根据商品ID删除商品属性关联
### 5. 商品属性管理 ✅
- ✅ 属性模板管理
- ✅ 属性模板CRUD操作
- ✅ 属性模板绑定分类(可选)
- ✅ 属性模板导出功能
- ✅ 属性管理
- ✅ 属性CRUD操作
- ✅ 属性类型管理(文本、数字、日期、枚举)
- ✅ 属性绑定模板(可选)
- ✅ 属性必填设置
- ✅ 属性导出功能
- ✅ 属性值管理
- ✅ 属性值CRUD操作
- ✅ 属性值绑定属性
- ✅ 属性值排序
- ✅ 属性值导出功能
- ✅ 商品属性关联
- ✅ 商品关联属性值
- ✅ 批量保存商品属性
- ✅ 根据商品ID查询属性
- ✅ 支持枚举类型属性值选择
- ✅ 支持文本/数字/日期类型自定义值
## 待实现功能
- [ ] 规格模板定义
- [ ] 规格值管理
- [ ] 规格模板与商品关联
- [ ] 商品搜索功能
- [ ] 全文搜索(集成Elasticsearch或类似方案)
- [ ] 商品筛选(按分类、品牌、价格等)
@@ -0,0 +1,61 @@
<?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-product</artifactId>
<description>
ruoyi-mall-product 商品中心模块
</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>
</dependencies>
</project>
@@ -0,0 +1,23 @@
package org.dromara.mall.product.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.product")
@ConditionalOnProperty(prefix = "ruoyi.mall.product", name = "enabled", havingValue = "true", matchIfMissing = true)
public class MallProductAutoConfiguration {
public MallProductAutoConfiguration() {
log.info("商品中心模块已启用");
}
}
@@ -0,0 +1,122 @@
package org.dromara.mall.product.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.product.domain.bo.MallAttributeBo;
import org.dromara.mall.product.domain.vo.MallAttributeVo;
import org.dromara.mall.product.service.MallAttributeService;
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/product/attribute")
public class AdminMallAttributeController extends BaseController {
private final MallAttributeService mallAttributeService;
/**
* 查询属性列表
*/
@Operation(summary = "查询属性列表", description = "分页查询属性列表")
@SaCheckPermission("mall:product:attribute:list")
@GetMapping("/list")
public TableDataInfo<MallAttributeVo> list(@Parameter(description = "属性查询条件") MallAttributeBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallAttributeService.queryPageList(bo, pageQuery);
}
/**
* 导出属性列表
*/
@Operation(summary = "导出属性列表", description = "导出属性到Excel")
@SaCheckPermission("mall:product:attribute:export")
@Log(title = "属性", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "属性查询条件") MallAttributeBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallAttributeVo> list = mallAttributeService.queryList(bo);
ExcelUtil.exportExcel(list, "属性", MallAttributeVo.class, response);
}
/**
* 获取属性详细信息
*/
@Operation(summary = "获取属性详情", description = "根据属性ID获取详细信息")
@SaCheckPermission("mall:product:attribute:query")
@GetMapping("/{id}")
public R<MallAttributeVo> getInfo(@Parameter(description = "属性ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallAttributeService.queryById(id));
}
/**
* 根据模板ID查询属性列表
*/
@Operation(summary = "根据模板ID查询属性列表", description = "根据属性模板ID查询属性列表")
@SaCheckPermission("mall:product:attribute:list")
@GetMapping("/template/{templateId}")
public R<List<MallAttributeVo>> listByTemplateId(@Parameter(description = "模板ID", required = true)
@NotNull(message = "模板ID不能为空")
@PathVariable Long templateId) {
return R.ok(mallAttributeService.queryListByTemplateId(templateId));
}
/**
* 新增属性
*/
@Operation(summary = "新增属性", description = "新增属性")
@SaCheckPermission("mall:product:attribute:add")
@Log(title = "属性", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "属性信息") @Validated @RequestBody MallAttributeBo bo) {
return toAjax(mallAttributeService.insertByBo(bo));
}
/**
* 修改属性
*/
@Operation(summary = "修改属性", description = "修改属性")
@SaCheckPermission("mall:product:attribute:edit")
@Log(title = "属性", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "属性信息") @Validated @RequestBody MallAttributeBo bo) {
return toAjax(mallAttributeService.updateByBo(bo));
}
/**
* 删除属性
*/
@Operation(summary = "删除属性", description = "批量删除属性")
@SaCheckPermission("mall:product:attribute:remove")
@Log(title = "属性", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "属性ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallAttributeService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,110 @@
package org.dromara.mall.product.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.product.domain.bo.MallAttributeTemplateBo;
import org.dromara.mall.product.domain.vo.MallAttributeTemplateVo;
import org.dromara.mall.product.service.MallAttributeTemplateService;
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/product/attribute/template")
public class AdminMallAttributeTemplateController extends BaseController {
private final MallAttributeTemplateService mallAttributeTemplateService;
/**
* 查询属性模板列表
*/
@Operation(summary = "查询属性模板列表", description = "分页查询属性模板列表")
@SaCheckPermission("mall:product:attribute:template:list")
@GetMapping("/list")
public TableDataInfo<MallAttributeTemplateVo> list(@Parameter(description = "属性模板查询条件") MallAttributeTemplateBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallAttributeTemplateService.queryPageList(bo, pageQuery);
}
/**
* 导出属性模板列表
*/
@Operation(summary = "导出属性模板列表", description = "导出属性模板到Excel")
@SaCheckPermission("mall:product:attribute:template:export")
@Log(title = "属性模板", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "属性模板查询条件") MallAttributeTemplateBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallAttributeTemplateVo> list = mallAttributeTemplateService.queryList(bo);
ExcelUtil.exportExcel(list, "属性模板", MallAttributeTemplateVo.class, response);
}
/**
* 获取属性模板详细信息
*/
@Operation(summary = "获取属性模板详情", description = "根据属性模板ID获取详细信息")
@SaCheckPermission("mall:product:attribute:template:query")
@GetMapping("/{id}")
public R<MallAttributeTemplateVo> getInfo(@Parameter(description = "属性模板ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallAttributeTemplateService.queryById(id));
}
/**
* 新增属性模板
*/
@Operation(summary = "新增属性模板", description = "新增属性模板")
@SaCheckPermission("mall:product:attribute:template:add")
@Log(title = "属性模板", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "属性模板信息") @Validated @RequestBody MallAttributeTemplateBo bo) {
return toAjax(mallAttributeTemplateService.insertByBo(bo));
}
/**
* 修改属性模板
*/
@Operation(summary = "修改属性模板", description = "修改属性模板")
@SaCheckPermission("mall:product:attribute:template:edit")
@Log(title = "属性模板", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "属性模板信息") @Validated @RequestBody MallAttributeTemplateBo bo) {
return toAjax(mallAttributeTemplateService.updateByBo(bo));
}
/**
* 删除属性模板
*/
@Operation(summary = "删除属性模板", description = "批量删除属性模板")
@SaCheckPermission("mall:product:attribute:template:remove")
@Log(title = "属性模板", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "属性模板ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallAttributeTemplateService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,122 @@
package org.dromara.mall.product.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.product.domain.bo.MallAttributeValueBo;
import org.dromara.mall.product.domain.vo.MallAttributeValueVo;
import org.dromara.mall.product.service.MallAttributeValueService;
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/product/attribute/value")
public class AdminMallAttributeValueController extends BaseController {
private final MallAttributeValueService mallAttributeValueService;
/**
* 查询属性值列表
*/
@Operation(summary = "查询属性值列表", description = "分页查询属性值列表")
@SaCheckPermission("mall:product:attribute:value:list")
@GetMapping("/list")
public TableDataInfo<MallAttributeValueVo> list(@Parameter(description = "属性值查询条件") MallAttributeValueBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallAttributeValueService.queryPageList(bo, pageQuery);
}
/**
* 导出属性值列表
*/
@Operation(summary = "导出属性值列表", description = "导出属性值到Excel")
@SaCheckPermission("mall:product:attribute:value:export")
@Log(title = "属性值", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "属性值查询条件") MallAttributeValueBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallAttributeValueVo> list = mallAttributeValueService.queryList(bo);
ExcelUtil.exportExcel(list, "属性值", MallAttributeValueVo.class, response);
}
/**
* 获取属性值详细信息
*/
@Operation(summary = "获取属性值详情", description = "根据属性值ID获取详细信息")
@SaCheckPermission("mall:product:attribute:value:query")
@GetMapping("/{id}")
public R<MallAttributeValueVo> getInfo(@Parameter(description = "属性值ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallAttributeValueService.queryById(id));
}
/**
* 根据属性ID查询属性值列表
*/
@Operation(summary = "根据属性ID查询属性值列表", description = "根据属性ID查询属性值列表")
@SaCheckPermission("mall:product:attribute:value:list")
@GetMapping("/attribute/{attributeId}")
public R<List<MallAttributeValueVo>> listByAttributeId(@Parameter(description = "属性ID", required = true)
@NotNull(message = "属性ID不能为空")
@PathVariable Long attributeId) {
return R.ok(mallAttributeValueService.queryListByAttributeId(attributeId));
}
/**
* 新增属性值
*/
@Operation(summary = "新增属性值", description = "新增属性值")
@SaCheckPermission("mall:product:attribute:value:add")
@Log(title = "属性值", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "属性值信息") @Validated @RequestBody MallAttributeValueBo bo) {
return toAjax(mallAttributeValueService.insertByBo(bo));
}
/**
* 修改属性值
*/
@Operation(summary = "修改属性值", description = "修改属性值")
@SaCheckPermission("mall:product:attribute:value:edit")
@Log(title = "属性值", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "属性值信息") @Validated @RequestBody MallAttributeValueBo bo) {
return toAjax(mallAttributeValueService.updateByBo(bo));
}
/**
* 删除属性值
*/
@Operation(summary = "删除属性值", description = "批量删除属性值")
@SaCheckPermission("mall:product:attribute:value:remove")
@Log(title = "属性值", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "属性值ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallAttributeValueService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,110 @@
package org.dromara.mall.product.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.product.domain.bo.MallBrandBo;
import org.dromara.mall.product.domain.vo.MallBrandVo;
import org.dromara.mall.product.service.MallBrandService;
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/product/brand")
public class AdminMallBrandController extends BaseController {
private final MallBrandService mallBrandService;
/**
* 查询品牌列表
*/
@Operation(summary = "查询品牌列表", description = "分页查询品牌列表")
@SaCheckPermission("mall:product:brand:list")
@GetMapping("/list")
public TableDataInfo<MallBrandVo> list(@Parameter(description = "品牌查询条件") MallBrandBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallBrandService.queryPageList(bo, pageQuery);
}
/**
* 导出品牌列表
*/
@Operation(summary = "导出品牌列表", description = "导出品牌到Excel")
@SaCheckPermission("mall:product:brand:export")
@Log(title = "品牌", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "品牌查询条件") MallBrandBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallBrandVo> list = mallBrandService.queryList(bo);
ExcelUtil.exportExcel(list, "品牌", MallBrandVo.class, response);
}
/**
* 获取品牌详细信息
*/
@Operation(summary = "获取品牌详情", description = "根据品牌ID获取详细信息")
@SaCheckPermission("mall:product:brand:query")
@GetMapping("/{id}")
public R<MallBrandVo> getInfo(@Parameter(description = "品牌ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallBrandService.queryById(id));
}
/**
* 新增品牌
*/
@Operation(summary = "新增品牌", description = "新增品牌")
@SaCheckPermission("mall:product:brand:add")
@Log(title = "品牌", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "品牌信息") @Validated @RequestBody MallBrandBo bo) {
return toAjax(mallBrandService.insertByBo(bo));
}
/**
* 修改品牌
*/
@Operation(summary = "修改品牌", description = "修改品牌")
@SaCheckPermission("mall:product:brand:edit")
@Log(title = "品牌", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "品牌信息") @Validated @RequestBody MallBrandBo bo) {
return toAjax(mallBrandService.updateByBo(bo));
}
/**
* 删除品牌
*/
@Operation(summary = "删除品牌", description = "删除品牌")
@SaCheckPermission("mall:product:brand:remove")
@Log(title = "品牌", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "品牌ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallBrandService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,121 @@
package org.dromara.mall.product.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.product.domain.bo.MallCategoryBo;
import org.dromara.mall.product.domain.vo.MallCategoryVo;
import org.dromara.mall.product.service.MallCategoryService;
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/product/category")
public class AdminMallCategoryController extends BaseController {
private final MallCategoryService mallCategoryService;
/**
* 查询商品分类列表
*/
@Operation(summary = "查询商品分类列表", description = "分页查询商品分类列表")
@SaCheckPermission("mall:product:category:list")
@GetMapping("/list")
public TableDataInfo<MallCategoryVo> list(@Parameter(description = "分类查询条件") MallCategoryBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallCategoryService.queryPageList(bo, pageQuery);
}
/**
* 查询商品分类树形列表
*/
@Operation(summary = "查询商品分类树形列表", description = "查询商品分类树形结构")
@SaCheckPermission("mall:product:category:list")
@GetMapping("/tree")
public R<List<MallCategoryVo>> tree() {
List<MallCategoryVo> list = mallCategoryService.queryTreeList();
return R.ok(list);
}
/**
* 导出商品分类列表
*/
@Operation(summary = "导出商品分类列表", description = "导出商品分类到Excel")
@SaCheckPermission("mall:product:category:export")
@Log(title = "商品分类", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Parameter(description = "分类查询条件") MallCategoryBo bo,
@Parameter(description = "响应对象") HttpServletResponse response) {
List<MallCategoryVo> list = mallCategoryService.queryList(bo);
ExcelUtil.exportExcel(list, "商品分类", MallCategoryVo.class, response);
}
/**
* 获取商品分类详细信息
*/
@Operation(summary = "获取商品分类详情", description = "根据分类ID获取详细信息")
@SaCheckPermission("mall:product:category:query")
@GetMapping("/{id}")
public R<MallCategoryVo> getInfo(@Parameter(description = "分类ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallCategoryService.queryById(id));
}
/**
* 新增商品分类
*/
@Operation(summary = "新增商品分类", description = "新增商品分类")
@SaCheckPermission("mall:product:category:add")
@Log(title = "商品分类", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "分类信息") @Validated @RequestBody MallCategoryBo bo) {
return toAjax(mallCategoryService.insertByBo(bo));
}
/**
* 修改商品分类
*/
@Operation(summary = "修改商品分类", description = "修改商品分类")
@SaCheckPermission("mall:product:category:edit")
@Log(title = "商品分类", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "分类信息") @Validated @RequestBody MallCategoryBo bo) {
return toAjax(mallCategoryService.updateByBo(bo));
}
/**
* 删除商品分类
*/
@Operation(summary = "删除商品分类", description = "删除商品分类")
@SaCheckPermission("mall:product:category:remove")
@Log(title = "商品分类", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "分类ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallCategoryService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,132 @@
package org.dromara.mall.product.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.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.mall.product.domain.bo.MallProductAttributeBo;
import org.dromara.mall.product.domain.vo.MallProductAttributeVo;
import org.dromara.mall.product.service.MallProductAttributeService;
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/product/attribute/product")
public class AdminMallProductAttributeController extends BaseController {
private final MallProductAttributeService mallProductAttributeService;
/**
* 获取商品属性关联详细信息
*/
@Operation(summary = "获取商品属性关联详情", description = "根据商品属性关联ID获取详细信息")
@SaCheckPermission("mall:product:attribute:product:query")
@GetMapping("/{id}")
public R<MallProductAttributeVo> getInfo(@Parameter(description = "商品属性关联ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallProductAttributeService.queryById(id));
}
/**
* 根据商品ID查询商品属性关联列表
*/
@Operation(summary = "根据商品ID查询商品属性关联列表", description = "根据商品ID查询商品属性关联列表")
@SaCheckPermission("mall:product:attribute:product:list")
@GetMapping("/product/{productId}")
public R<List<MallProductAttributeVo>> listByProductId(@Parameter(description = "商品ID", required = true)
@NotNull(message = "商品ID不能为空")
@PathVariable Long productId) {
return R.ok(mallProductAttributeService.queryListByProductId(productId));
}
/**
* 新增商品属性关联
*/
@Operation(summary = "新增商品属性关联", description = "新增商品属性关联")
@SaCheckPermission("mall:product:attribute:product:add")
@Log(title = "商品属性关联", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "商品属性关联信息") @Validated @RequestBody MallProductAttributeBo bo) {
return toAjax(mallProductAttributeService.insertByBo(bo));
}
/**
* 批量新增商品属性关联
*/
@Operation(summary = "批量新增商品属性关联", description = "批量新增商品属性关联")
@SaCheckPermission("mall:product:attribute:product:add")
@Log(title = "商品属性关联", businessType = BusinessType.INSERT)
@PostMapping("/batch")
public R<Void> addBatch(@Parameter(description = "商品属性关联信息列表") @Validated @RequestBody List<MallProductAttributeBo> list) {
return toAjax(mallProductAttributeService.insertBatch(list));
}
/**
* 保存商品属性关联(先删除旧的,再插入新的)
*/
@Operation(summary = "保存商品属性关联", description = "保存商品属性关联,先删除旧的,再插入新的")
@SaCheckPermission("mall:product:attribute:product:edit")
@Log(title = "商品属性关联", businessType = BusinessType.UPDATE)
@PostMapping("/product/{productId}")
public R<Void> saveProductAttributes(@Parameter(description = "商品ID", required = true)
@NotNull(message = "商品ID不能为空")
@PathVariable Long productId,
@Parameter(description = "商品属性关联信息列表") @RequestBody List<MallProductAttributeBo> list) {
return toAjax(mallProductAttributeService.saveProductAttributes(productId, list));
}
/**
* 修改商品属性关联
*/
@Operation(summary = "修改商品属性关联", description = "修改商品属性关联")
@SaCheckPermission("mall:product:attribute:product:edit")
@Log(title = "商品属性关联", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "商品属性关联信息") @Validated @RequestBody MallProductAttributeBo bo) {
return toAjax(mallProductAttributeService.updateByBo(bo));
}
/**
* 删除商品属性关联
*/
@Operation(summary = "删除商品属性关联", description = "批量删除商品属性关联")
@SaCheckPermission("mall:product:attribute:product:remove")
@Log(title = "商品属性关联", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "商品属性关联ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallProductAttributeService.deleteWithValidByIds(Arrays.asList(ids), true));
}
/**
* 根据商品ID删除商品属性关联
*/
@Operation(summary = "根据商品ID删除商品属性关联", description = "根据商品ID删除商品属性关联")
@SaCheckPermission("mall:product:attribute:product:remove")
@Log(title = "商品属性关联", businessType = BusinessType.DELETE)
@DeleteMapping("/product/{productId}")
public R<Void> removeByProductId(@Parameter(description = "商品ID", required = true)
@NotNull(message = "商品ID不能为空")
@PathVariable Long productId) {
return toAjax(mallProductAttributeService.deleteByProductId(productId));
}
}
@@ -0,0 +1,94 @@
package org.dromara.mall.product.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.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
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.product.domain.bo.MallProductBo;
import org.dromara.mall.product.domain.vo.MallProductVo;
import org.dromara.mall.product.service.MallProductService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
/**
* 商品管理Controller
*
* @author pengles
*/
@Tag(name = "管理端-商品管理", description = "商品管理接口")
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mall/product")
public class AdminMallProductController extends BaseController {
private final MallProductService mallProductService;
/**
* 查询商品列表
*/
@Operation(summary = "查询商品列表", description = "分页查询商品列表")
@SaCheckPermission("mall:product:list")
@GetMapping("/list")
public TableDataInfo<MallProductVo> list(@Parameter(description = "商品查询条件") MallProductBo bo,
@Parameter(description = "分页参数") PageQuery pageQuery) {
return mallProductService.queryPageList(bo, pageQuery);
}
/**
* 获取商品详细信息
*/
@Operation(summary = "获取商品详细信息", description = "根据商品ID获取商品详细信息")
@SaCheckPermission("mall:product:query")
@GetMapping("/{id}")
public R<MallProductVo> getInfo(@Parameter(description = "商品ID", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(mallProductService.queryById(id));
}
/**
* 新增商品
*/
@Operation(summary = "新增商品", description = "新增商品")
@SaCheckPermission("mall:product:add")
@Log(title = "商品管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Parameter(description = "商品信息") @Validated @RequestBody MallProductBo bo) {
return toAjax(mallProductService.insertByBo(bo));
}
/**
* 修改商品
*/
@Operation(summary = "修改商品", description = "修改商品")
@SaCheckPermission("mall:product:edit")
@Log(title = "商品管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Parameter(description = "商品信息") @Validated @RequestBody MallProductBo bo) {
return toAjax(mallProductService.updateByBo(bo));
}
/**
* 删除商品
*/
@Operation(summary = "删除商品", description = "批量删除商品")
@SaCheckPermission("mall:product:remove")
@Log(title = "商品管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@Parameter(description = "商品ID数组", required = true)
@NotNull(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(mallProductService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
@@ -0,0 +1,40 @@
package org.dromara.mall.product.controller.app;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.mall.product.domain.bo.MallBrandBo;
import org.dromara.mall.product.domain.vo.MallBrandVo;
import org.dromara.mall.product.service.MallBrandService;
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;
/**
* 品牌(移动端)
*
* @author pengles
*/
@Tag(name = "移动端-品牌", description = "品牌查询接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/product/brand")
public class AppMallBrandController {
private final MallBrandService mallBrandService;
/**
* 查询品牌列表(移动端)
*/
@Operation(summary = "查询品牌列表", description = "查询启用的品牌列表")
@GetMapping("/list")
public R<List<MallBrandVo>> list() {
MallBrandBo bo = new MallBrandBo();
bo.setStatus(0); // 只查询启用的品牌
List<MallBrandVo> list = mallBrandService.queryList(bo);
return R.ok(list);
}
}
@@ -0,0 +1,37 @@
package org.dromara.mall.product.controller.app;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.mall.product.domain.vo.MallCategoryVo;
import org.dromara.mall.product.service.MallCategoryService;
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;
/**
* 商品分类(移动端)
*
* @author pengles
*/
@Tag(name = "移动端-商品分类", description = "商品分类查询接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/product/category")
public class AppMallCategoryController {
private final MallCategoryService mallCategoryService;
/**
* 查询商品分类树形列表(移动端)
*/
@Operation(summary = "查询商品分类树形列表", description = "查询启用的商品分类树形结构")
@GetMapping("/tree")
public R<List<MallCategoryVo>> tree() {
List<MallCategoryVo> list = mallCategoryService.queryTreeList();
return R.ok(list);
}
}
@@ -0,0 +1,82 @@
package org.dromara.mall.product.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.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallProductBo;
import org.dromara.mall.product.domain.vo.MallProductVo;
import org.dromara.mall.product.domain.vo.MallSkuVo;
import org.dromara.mall.product.service.MallProductService;
import org.dromara.mall.product.service.MallSkuService;
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 = "商品查询接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/mall/product")
public class AppMallProductController {
private final MallProductService mallProductService;
private final MallSkuService mallSkuService;
/**
* 查询商品列表(移动端)
*/
@Operation(summary = "查询商品列表", description = "查询上架的商品列表")
@GetMapping("/list")
public R<TableDataInfo<MallProductVo>> list(
@Parameter(description = "查询条件") MallProductBo bo,
@Parameter(description = "分页参数") @Validated PageQuery pageQuery) {
// 移动端只查询上架的商品
if (bo == null) {
bo = new MallProductBo();
}
bo.setStatus(0); // 0表示上架
TableDataInfo<MallProductVo> result = mallProductService.queryPageList(bo, pageQuery);
return R.ok(result);
}
/**
* 查询商品详情(移动端)
*/
@Operation(summary = "查询商品详情", description = "根据商品ID查询商品详情")
@GetMapping("/{id}")
public R<MallProductVo> getInfo(
@Parameter(description = "商品ID", required = true) @PathVariable Long id) {
MallProductVo product = mallProductService.queryById(id);
if (product == null) {
return R.fail("商品不存在");
}
// 只有上架的商品才能查看
if (product.getStatus() != null && product.getStatus() != 0) {
return R.fail("商品已下架");
}
return R.ok(product);
}
/**
* 查询商品SKU列表(移动端)
*/
@Operation(summary = "查询商品SKU列表", description = "根据商品ID查询SKU列表")
@GetMapping("/{productId}/skus")
public R<List<MallSkuVo>> getSkus(
@Parameter(description = "商品ID", required = true) @PathVariable Long productId) {
List<MallSkuVo> skus = mallSkuService.queryListByProductId(productId);
return R.ok(skus);
}
}
@@ -0,0 +1,92 @@
package org.dromara.mall.product.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_attribute
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_attribute")
public class MallAttribute implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 属性ID
*/
@TableId
private Long id;
/**
* 模板ID(可选,绑定到模板)
*/
private Long templateId;
/**
* 属性名称
*/
private String name;
/**
* 属性类型(0文本 1数字 2日期 3枚举)
*/
private Integer type;
/**
* 是否必填(0否 1是)
*/
private Integer required;
/**
* 排序
*/
private Integer sort;
/**
* 状态(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,82 @@
package org.dromara.mall.product.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_attribute_template
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_attribute_template")
public class MallAttributeTemplate implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 模板ID
*/
@TableId
private Long id;
/**
* 模板名称
*/
private String name;
/**
* 分类ID(可选,绑定到分类)
*/
private Long categoryId;
/**
* 排序
*/
private Integer sort;
/**
* 状态(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,77 @@
package org.dromara.mall.product.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_attribute_value
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_attribute_value")
public class MallAttributeValue implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 属性值ID
*/
@TableId
private Long id;
/**
* 属性ID
*/
private Long attributeId;
/**
* 属性值
*/
private String value;
/**
* 排序
*/
private Integer sort;
/**
* 状态(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,82 @@
package org.dromara.mall.product.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_brand
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_brand")
public class MallBrand implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 品牌ID
*/
@TableId
private Long id;
/**
* 品牌名称
*/
private String name;
/**
* 品牌Logo
*/
private String logo;
/**
* 品牌描述
*/
private String description;
/**
* 排序
*/
private Integer sort;
/**
* 状态(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.product.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_category
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_category")
public class MallCategory implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分类ID
*/
@TableId
private Long id;
/**
* 分类名称
*/
private String name;
/**
* 父分类ID(0表示顶级)
*/
private Long parentId;
/**
* 分类级别(1一级 2二级 3三级)
*/
private Integer level;
/**
* 分类图标
*/
private String icon;
/**
* 排序
*/
private Integer sort;
/**
* 状态(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,107 @@
package org.dromara.mall.product.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.util.Date;
/**
* 商品对象 mall_product
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_product")
public class MallProduct {
/**
* 商品ID
*/
@TableId
private Long id;
/**
* 商品名称
*/
private String name;
/**
* 分类ID
*/
private Long categoryId;
/**
* 品牌ID
*/
private Long brandId;
/**
* 封面图片
*/
private String coverUrl;
/**
* 商品图片(JSON数组)
*/
private String images;
/**
* 商品描述
*/
private String description;
/**
* 商品详情
*/
private String detail;
/**
* 状态(0上架 1下架)
*/
private Integer status;
/**
* 排序
*/
private Integer sort;
/**
* 销量
*/
private Integer salesCount;
/**
* 浏览量
*/
private Integer viewCount;
/**
* 创建者
*/
@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,77 @@
package org.dromara.mall.product.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_product_attribute
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_product_attribute")
public class MallProductAttribute implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId
private Long id;
/**
* 商品ID
*/
private Long productId;
/**
* 属性ID
*/
private Long attributeId;
/**
* 属性值ID(当属性类型为枚举时使用)
*/
private Long attributeValueId;
/**
* 属性值(当属性类型为文本/数字/日期时使用,或自定义值)
*/
private String value;
/**
* 创建者
*/
@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,97 @@
package org.dromara.mall.product.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.util.Date;
/**
* SKU对象 mall_sku
*
* @author pengles
*/
@Data
@EqualsAndHashCode
@TableName("mall_sku")
public class MallSku {
/**
* SKU ID
*/
@TableId
private Long id;
/**
* 商品ID
*/
private Long productId;
/**
* SKU编码
*/
private String skuCode;
/**
* SKU名称
*/
private String name;
/**
* 价格(分)
*/
private Integer price;
/**
* 原价(分)
*/
private Integer originalPrice;
/**
* 库存
*/
private Integer stock;
/**
* SKU图片
*/
private String image;
/**
* 规格属性(JSON)
*/
private String specs;
/**
* 状态(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,64 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.product.domain.MallAttribute;
/**
* 属性业务对象 mall_attribute
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallAttribute.class, reverseConvertGenerate = false)
public class MallAttributeBo extends BaseEntity {
/**
* 属性ID
*/
private Long id;
/**
* 模板ID(可选,绑定到模板)
*/
private Long templateId;
/**
* 属性名称
*/
@NotBlank(message = "属性名称不能为空")
private String name;
/**
* 属性类型(0文本 1数字 2日期 3枚举)
*/
@NotNull(message = "属性类型不能为空")
private Integer type;
/**
* 是否必填(0否 1是)
*/
private Integer required;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@NotNull(message = "状态不能为空")
private Integer status;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,53 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.product.domain.MallAttributeTemplate;
/**
* 属性模板业务对象 mall_attribute_template
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallAttributeTemplate.class, reverseConvertGenerate = false)
public class MallAttributeTemplateBo extends BaseEntity {
/**
* 模板ID
*/
private Long id;
/**
* 模板名称
*/
@NotBlank(message = "模板名称不能为空")
private String name;
/**
* 分类ID(可选,绑定到分类)
*/
private Long categoryId;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@NotNull(message = "状态不能为空")
private Integer status;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,49 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.product.domain.MallAttributeValue;
/**
* 属性值业务对象 mall_attribute_value
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallAttributeValue.class, reverseConvertGenerate = false)
public class MallAttributeValueBo extends BaseEntity {
/**
* 属性值ID
*/
private Long id;
/**
* 属性ID
*/
@NotNull(message = "属性ID不能为空")
private Long attributeId;
/**
* 属性值
*/
@NotBlank(message = "属性值不能为空")
private String value;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@NotNull(message = "状态不能为空")
private Integer status;
}
@@ -0,0 +1,53 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.product.domain.MallBrand;
/**
* 品牌业务对象 mall_brand
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallBrand.class, reverseConvertGenerate = false)
public class MallBrandBo extends BaseEntity {
/**
* 品牌ID
*/
private Long id;
/**
* 品牌名称
*/
@NotBlank(message = "品牌名称不能为空")
private String name;
/**
* 品牌Logo
*/
private String logo;
/**
* 品牌描述
*/
private String description;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@NotNull(message = "状态不能为空")
private Integer status;
}
@@ -0,0 +1,58 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.product.domain.MallCategory;
/**
* 商品分类业务对象 mall_category
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallCategory.class, reverseConvertGenerate = false)
public class MallCategoryBo extends BaseEntity {
/**
* 分类ID
*/
private Long id;
/**
* 分类名称
*/
@NotBlank(message = "分类名称不能为空")
private String name;
/**
* 父分类ID(0表示顶级)
*/
private Long parentId;
/**
* 分类级别(1一级 2二级 3三级)
*/
private Integer level;
/**
* 分类图标
*/
private String icon;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@NotNull(message = "状态不能为空")
private Integer status;
}
@@ -0,0 +1,47 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.mall.product.domain.MallProductAttribute;
/**
* 商品属性关联业务对象 mall_product_attribute
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = MallProductAttribute.class, reverseConvertGenerate = false)
public class MallProductAttributeBo extends BaseEntity {
/**
* ID
*/
private Long id;
/**
* 商品ID
*/
@NotNull(message = "商品ID不能为空")
private Long productId;
/**
* 属性ID
*/
@NotNull(message = "属性ID不能为空")
private Long attributeId;
/**
* 属性值ID(当属性类型为枚举时使用)
*/
private Long attributeValueId;
/**
* 属性值(当属性类型为文本/数字/日期时使用,或自定义值)
*/
private String value;
}
@@ -0,0 +1,82 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.product.domain.MallProduct;
import java.io.Serial;
import java.io.Serializable;
/**
* 商品业务对象 mall_product
*
* @author pengles
*/
@Data
@AutoMapper(target = MallProduct.class, reverseConvertGenerate = false)
public class MallProductBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 商品ID
*/
private Long id;
/**
* 商品名称
*/
private String name;
/**
* 分类ID
*/
private Long categoryId;
/**
* 品牌ID
*/
private Long brandId;
/**
* 封面图片
*/
private String coverUrl;
/**
* 商品图片(JSON数组)
*/
private String images;
/**
* 商品描述
*/
private String description;
/**
* 商品详情
*/
private String detail;
/**
* 状态(0上架 1下架)
*/
private Integer status;
/**
* 排序
*/
private Integer sort;
/**
* 销量
*/
private Integer salesCount;
/**
* 浏览量
*/
private Integer viewCount;
}
@@ -0,0 +1,72 @@
package org.dromara.mall.product.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.product.domain.MallSku;
import java.io.Serial;
import java.io.Serializable;
/**
* SKU业务对象 mall_sku
*
* @author pengles
*/
@Data
@AutoMapper(target = MallSku.class, reverseConvertGenerate = false)
public class MallSkuBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* SKU ID
*/
private Long id;
/**
* 商品ID
*/
private Long productId;
/**
* SKU编码
*/
private String skuCode;
/**
* SKU名称
*/
private String name;
/**
* 价格(分)
*/
private Integer price;
/**
* 原价(分)
*/
private Integer originalPrice;
/**
* 库存
*/
private Integer stock;
/**
* SKU图片
*/
private String image;
/**
* 规格属性(JSON)
*/
private String specs;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
}
@@ -0,0 +1,74 @@
package org.dromara.mall.product.domain.vo;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.mall.product.domain.MallAttributeTemplate;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 属性模板视图对象 mall_attribute_template
*
* @author pengles
*/
@Data
@AutoMapper(target = MallAttributeTemplate.class)
public class MallAttributeTemplateVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 模板ID
*/
@ExcelProperty(value = "模板ID")
private Long id;
/**
* 模板名称
*/
@ExcelProperty(value = "模板名称")
private String name;
/**
* 分类ID(可选,绑定到分类)
*/
@ExcelProperty(value = "分类ID")
private Long categoryId;
/**
* 排序
*/
@ExcelProperty(value = "排序")
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=启用,1=禁用")
private Integer status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,68 @@
package org.dromara.mall.product.domain.vo;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.mall.product.domain.MallAttributeValue;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 属性值视图对象 mall_attribute_value
*
* @author pengles
*/
@Data
@AutoMapper(target = MallAttributeValue.class)
public class MallAttributeValueVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 属性值ID
*/
@ExcelProperty(value = "属性值ID")
private Long id;
/**
* 属性ID
*/
@ExcelProperty(value = "属性ID")
private Long attributeId;
/**
* 属性值
*/
@ExcelProperty(value = "属性值")
private String value;
/**
* 排序
*/
@ExcelProperty(value = "排序")
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=启用,1=禁用")
private Integer status;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,88 @@
package org.dromara.mall.product.domain.vo;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.mall.product.domain.MallAttribute;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 属性视图对象 mall_attribute
*
* @author pengles
*/
@Data
@AutoMapper(target = MallAttribute.class)
public class MallAttributeVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 属性ID
*/
@ExcelProperty(value = "属性ID")
private Long id;
/**
* 模板ID(可选,绑定到模板)
*/
@ExcelProperty(value = "模板ID")
private Long templateId;
/**
* 属性名称
*/
@ExcelProperty(value = "属性名称")
private String name;
/**
* 属性类型(0文本 1数字 2日期 3枚举)
*/
@ExcelProperty(value = "属性类型", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=文本,1=数字,2=日期,3=枚举")
private Integer type;
/**
* 是否必填(0否 1是)
*/
@ExcelProperty(value = "是否必填", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=否,1=是")
private Integer required;
/**
* 排序
*/
@ExcelProperty(value = "排序")
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=启用,1=禁用")
private Integer status;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,63 @@
package org.dromara.mall.product.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.product.domain.MallBrand;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 品牌视图对象 mall_brand
*
* @author pengles
*/
@Data
@AutoMapper(target = MallBrand.class)
public class MallBrandVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 品牌ID
*/
private Long id;
/**
* 品牌名称
*/
private String name;
/**
* 品牌Logo
*/
private String logo;
/**
* 品牌描述
*/
private String description;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,74 @@
package org.dromara.mall.product.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.product.domain.MallCategory;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 商品分类视图对象 mall_category
*
* @author pengles
*/
@Data
@AutoMapper(target = MallCategory.class)
public class MallCategoryVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 分类ID
*/
private Long id;
/**
* 分类名称
*/
private String name;
/**
* 父分类ID(0表示顶级)
*/
private Long parentId;
/**
* 分类级别(1一级 2二级 3三级)
*/
private Integer level;
/**
* 分类图标
*/
private String icon;
/**
* 排序
*/
private Integer sort;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 子分类列表(用于树形结构)
*/
private List<MallCategoryVo> children;
}
@@ -0,0 +1,47 @@
package org.dromara.mall.product.domain.vo;
import lombok.Data;
import org.dromara.mall.product.domain.MallProductAttribute;
import io.github.linpeilie.annotations.AutoMapper;
import java.io.Serial;
import java.io.Serializable;
/**
* 商品属性关联视图对象 mall_product_attribute
*
* @author pengles
*/
@Data
@AutoMapper(target = MallProductAttribute.class)
public class MallProductAttributeVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 商品ID
*/
private Long productId;
/**
* 属性ID
*/
private Long attributeId;
/**
* 属性值ID(当属性类型为枚举时使用)
*/
private Long attributeValueId;
/**
* 属性值(当属性类型为文本/数字/日期时使用,或自定义值)
*/
private String value;
}
@@ -0,0 +1,93 @@
package org.dromara.mall.product.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.product.domain.MallProduct;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 商品视图对象 mall_product
*
* @author pengles
*/
@Data
@AutoMapper(target = MallProduct.class)
public class MallProductVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 商品ID
*/
private Long id;
/**
* 商品名称
*/
private String name;
/**
* 分类ID
*/
private Long categoryId;
/**
* 品牌ID
*/
private Long brandId;
/**
* 封面图片
*/
private String coverUrl;
/**
* 商品图片(JSON数组)
*/
private String images;
/**
* 商品描述
*/
private String description;
/**
* 商品详情
*/
private String detail;
/**
* 状态(0上架 1下架)
*/
private Integer status;
/**
* 排序
*/
private Integer sort;
/**
* 销量
*/
private Integer salesCount;
/**
* 浏览量
*/
private Integer viewCount;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,83 @@
package org.dromara.mall.product.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.mall.product.domain.MallSku;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* SKU视图对象 mall_sku
*
* @author pengles
*/
@Data
@AutoMapper(target = MallSku.class)
public class MallSkuVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* SKU ID
*/
private Long id;
/**
* 商品ID
*/
private Long productId;
/**
* SKU编码
*/
private String skuCode;
/**
* SKU名称
*/
private String name;
/**
* 价格(分)
*/
private Integer price;
/**
* 原价(分)
*/
private Integer originalPrice;
/**
* 库存
*/
private Integer stock;
/**
* SKU图片
*/
private String image;
/**
* 规格属性(JSON)
*/
private String specs;
/**
* 状态(0启用 1禁用)
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,57 @@
package org.dromara.mall.product.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 属性类型枚举
*
* @author pengles
*/
@Getter
@AllArgsConstructor
public enum AttributeType {
/**
* 文本
*/
TEXT(0, "文本"),
/**
* 数字
*/
NUMBER(1, "数字"),
/**
* 日期
*/
DATE(2, "日期"),
/**
* 枚举
*/
ENUM(3, "枚举");
/**
* 类型值
*/
private final Integer value;
/**
* 类型名称
*/
private final String name;
/**
* 根据值获取枚举
*/
public static AttributeType getByValue(Integer value) {
for (AttributeType type : values()) {
if (type.getValue().equals(value)) {
return type;
}
}
return null;
}
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallAttribute;
import org.dromara.mall.product.domain.vo.MallAttributeVo;
/**
* 属性Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallAttributeMapper extends BaseMapperPlus<MallAttribute, MallAttributeVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallAttributeTemplate;
import org.dromara.mall.product.domain.vo.MallAttributeTemplateVo;
/**
* 属性模板Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallAttributeTemplateMapper extends BaseMapperPlus<MallAttributeTemplate, MallAttributeTemplateVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallAttributeValue;
import org.dromara.mall.product.domain.vo.MallAttributeValueVo;
/**
* 属性值Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallAttributeValueMapper extends BaseMapperPlus<MallAttributeValue, MallAttributeValueVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallBrand;
import org.dromara.mall.product.domain.vo.MallBrandVo;
/**
* 品牌Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallBrandMapper extends BaseMapperPlus<MallBrand, MallBrandVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallCategory;
import org.dromara.mall.product.domain.vo.MallCategoryVo;
/**
* 商品分类Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallCategoryMapper extends BaseMapperPlus<MallCategory, MallCategoryVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallProductAttribute;
import org.dromara.mall.product.domain.vo.MallProductAttributeVo;
/**
* 商品属性关联Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallProductAttributeMapper extends BaseMapperPlus<MallProductAttribute, MallProductAttributeVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallProduct;
import org.dromara.mall.product.domain.vo.MallProductVo;
/**
* 商品Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallProductMapper extends BaseMapperPlus<MallProduct, MallProductVo> {
}
@@ -0,0 +1,16 @@
package org.dromara.mall.product.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.mall.product.domain.MallSku;
import org.dromara.mall.product.domain.vo.MallSkuVo;
/**
* SKU Mapper接口
*
* @author pengles
*/
@Mapper
public interface MallSkuMapper extends BaseMapperPlus<MallSku, MallSkuVo> {
}
@@ -0,0 +1,53 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallAttributeBo;
import org.dromara.mall.product.domain.vo.MallAttributeVo;
import java.util.Collection;
import java.util.List;
/**
* 属性Service接口
*
* @author pengles
*/
public interface MallAttributeService {
/**
* 查询属性
*/
MallAttributeVo queryById(Long id);
/**
* 查询属性列表
*/
TableDataInfo<MallAttributeVo> queryPageList(MallAttributeBo bo, PageQuery pageQuery);
/**
* 查询属性列表
*/
List<MallAttributeVo> queryList(MallAttributeBo bo);
/**
* 根据模板ID查询属性列表
*/
List<MallAttributeVo> queryListByTemplateId(Long templateId);
/**
* 新增属性
*/
Boolean insertByBo(MallAttributeBo bo);
/**
* 修改属性
*/
Boolean updateByBo(MallAttributeBo bo);
/**
* 校验并批量删除属性信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,48 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallAttributeTemplateBo;
import org.dromara.mall.product.domain.vo.MallAttributeTemplateVo;
import java.util.Collection;
import java.util.List;
/**
* 属性模板Service接口
*
* @author pengles
*/
public interface MallAttributeTemplateService {
/**
* 查询属性模板
*/
MallAttributeTemplateVo queryById(Long id);
/**
* 查询属性模板列表
*/
TableDataInfo<MallAttributeTemplateVo> queryPageList(MallAttributeTemplateBo bo, PageQuery pageQuery);
/**
* 查询属性模板列表
*/
List<MallAttributeTemplateVo> queryList(MallAttributeTemplateBo bo);
/**
* 新增属性模板
*/
Boolean insertByBo(MallAttributeTemplateBo bo);
/**
* 修改属性模板
*/
Boolean updateByBo(MallAttributeTemplateBo bo);
/**
* 校验并批量删除属性模板信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,53 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallAttributeValueBo;
import org.dromara.mall.product.domain.vo.MallAttributeValueVo;
import java.util.Collection;
import java.util.List;
/**
* 属性值Service接口
*
* @author pengles
*/
public interface MallAttributeValueService {
/**
* 查询属性值
*/
MallAttributeValueVo queryById(Long id);
/**
* 查询属性值列表
*/
TableDataInfo<MallAttributeValueVo> queryPageList(MallAttributeValueBo bo, PageQuery pageQuery);
/**
* 查询属性值列表
*/
List<MallAttributeValueVo> queryList(MallAttributeValueBo bo);
/**
* 根据属性ID查询属性值列表
*/
List<MallAttributeValueVo> queryListByAttributeId(Long attributeId);
/**
* 新增属性值
*/
Boolean insertByBo(MallAttributeValueBo bo);
/**
* 修改属性值
*/
Boolean updateByBo(MallAttributeValueBo bo);
/**
* 校验并批量删除属性值信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,48 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallBrandBo;
import org.dromara.mall.product.domain.vo.MallBrandVo;
import java.util.Collection;
import java.util.List;
/**
* 品牌Service接口
*
* @author pengles
*/
public interface MallBrandService {
/**
* 查询品牌
*/
MallBrandVo queryById(Long id);
/**
* 查询品牌列表
*/
TableDataInfo<MallBrandVo> queryPageList(MallBrandBo bo, PageQuery pageQuery);
/**
* 查询品牌列表
*/
List<MallBrandVo> queryList(MallBrandBo bo);
/**
* 新增品牌
*/
Boolean insertByBo(MallBrandBo bo);
/**
* 修改品牌
*/
Boolean updateByBo(MallBrandBo bo);
/**
* 校验并批量删除品牌信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,53 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallCategoryBo;
import org.dromara.mall.product.domain.vo.MallCategoryVo;
import java.util.Collection;
import java.util.List;
/**
* 商品分类Service接口
*
* @author pengles
*/
public interface MallCategoryService {
/**
* 查询商品分类
*/
MallCategoryVo queryById(Long id);
/**
* 查询商品分类列表
*/
TableDataInfo<MallCategoryVo> queryPageList(MallCategoryBo bo, PageQuery pageQuery);
/**
* 查询商品分类列表
*/
List<MallCategoryVo> queryList(MallCategoryBo bo);
/**
* 查询商品分类树形列表
*/
List<MallCategoryVo> queryTreeList();
/**
* 新增商品分类
*/
Boolean insertByBo(MallCategoryBo bo);
/**
* 修改商品分类
*/
Boolean updateByBo(MallCategoryBo bo);
/**
* 校验并批量删除商品分类信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,56 @@
package org.dromara.mall.product.service;
import org.dromara.mall.product.domain.bo.MallProductAttributeBo;
import org.dromara.mall.product.domain.vo.MallProductAttributeVo;
import java.util.Collection;
import java.util.List;
/**
* 商品属性关联Service接口
*
* @author pengles
*/
public interface MallProductAttributeService {
/**
* 查询商品属性关联
*/
MallProductAttributeVo queryById(Long id);
/**
* 根据商品ID查询商品属性关联列表
*/
List<MallProductAttributeVo> queryListByProductId(Long productId);
/**
* 新增商品属性关联
*/
Boolean insertByBo(MallProductAttributeBo bo);
/**
* 批量新增商品属性关联
*/
Boolean insertBatch(List<MallProductAttributeBo> list);
/**
* 修改商品属性关联
*/
Boolean updateByBo(MallProductAttributeBo bo);
/**
* 批量删除商品属性关联信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据商品ID删除商品属性关联
*/
Boolean deleteByProductId(Long productId);
/**
* 保存商品属性关联(先删除旧的,再插入新的)
*/
Boolean saveProductAttributes(Long productId, List<MallProductAttributeBo> list);
}
@@ -0,0 +1,48 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallProductBo;
import org.dromara.mall.product.domain.vo.MallProductVo;
import java.util.Collection;
import java.util.List;
/**
* 商品Service接口
*
* @author pengles
*/
public interface MallProductService {
/**
* 查询商品
*/
MallProductVo queryById(Long id);
/**
* 查询商品列表
*/
TableDataInfo<MallProductVo> queryPageList(MallProductBo bo, PageQuery pageQuery);
/**
* 查询商品列表
*/
List<MallProductVo> queryList(MallProductBo bo);
/**
* 新增商品
*/
Boolean insertByBo(MallProductBo bo);
/**
* 修改商品
*/
Boolean updateByBo(MallProductBo bo);
/**
* 校验并批量删除商品信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,53 @@
package org.dromara.mall.product.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.bo.MallSkuBo;
import org.dromara.mall.product.domain.vo.MallSkuVo;
import java.util.Collection;
import java.util.List;
/**
* SKU Service接口
*
* @author pengles
*/
public interface MallSkuService {
/**
* 查询SKU
*/
MallSkuVo queryById(Long id);
/**
* 查询SKU列表
*/
TableDataInfo<MallSkuVo> queryPageList(MallSkuBo bo, PageQuery pageQuery);
/**
* 查询SKU列表
*/
List<MallSkuVo> queryList(MallSkuBo bo);
/**
* 根据商品ID查询SKU列表
*/
List<MallSkuVo> queryListByProductId(Long productId);
/**
* 新增SKU
*/
Boolean insertByBo(MallSkuBo bo);
/**
* 修改SKU
*/
Boolean updateByBo(MallSkuBo bo);
/**
* 校验并批量删除SKU信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,167 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallAttribute;
import org.dromara.mall.product.domain.bo.MallAttributeBo;
import org.dromara.mall.product.domain.vo.MallAttributeVo;
import org.dromara.mall.product.mapper.MallAttributeMapper;
import org.dromara.mall.product.service.MallAttributeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 属性Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallAttributeServiceImpl implements MallAttributeService {
private final MallAttributeMapper baseMapper;
/**
* 查询属性
*/
@Override
public MallAttributeVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询属性列表
*/
@Override
public TableDataInfo<MallAttributeVo> queryPageList(MallAttributeBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallAttribute> lqw = buildQueryWrapper(bo);
Page<MallAttributeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询属性列表
*/
@Override
public List<MallAttributeVo> queryList(MallAttributeBo bo) {
LambdaQueryWrapper<MallAttribute> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 根据模板ID查询属性列表
*/
@Override
public List<MallAttributeVo> queryListByTemplateId(Long templateId) {
LambdaQueryWrapper<MallAttribute> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallAttribute::getTemplateId, templateId);
lqw.eq(MallAttribute::getStatus, 0);
lqw.orderByAsc(MallAttribute::getSort);
lqw.orderByAsc(MallAttribute::getId);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallAttribute> buildQueryWrapper(MallAttributeBo bo) {
LambdaQueryWrapper<MallAttribute> lqw = new LambdaQueryWrapper<>();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallAttribute::getName, bo.getName());
lqw.eq(bo.getTemplateId() != null, MallAttribute::getTemplateId, bo.getTemplateId());
lqw.eq(bo.getType() != null, MallAttribute::getType, bo.getType());
lqw.eq(bo.getStatus() != null, MallAttribute::getStatus, bo.getStatus());
lqw.orderByAsc(MallAttribute::getSort);
lqw.orderByAsc(MallAttribute::getId);
return lqw;
}
/**
* 新增属性
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallAttributeBo bo) {
MallAttribute add = MapstructUtils.convert(bo, MallAttribute.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getRequired() == null) {
add.setRequired(0);
}
if (add.getSort() == null) {
add.setSort(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改属性
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallAttributeBo bo) {
MallAttribute update = MapstructUtils.convert(bo, MallAttribute.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallAttribute entity) {
if (StringUtils.isBlank(entity.getName())) {
throw new ServiceException("属性名称不能为空");
}
if (entity.getType() == null) {
throw new ServiceException("属性类型不能为空");
}
if (entity.getType() < 0 || entity.getType() > 3) {
throw new ServiceException("属性类型不合法");
}
// 检查属性名称是否重复(在同一模板下)
LambdaQueryWrapper<MallAttribute> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallAttribute::getName, entity.getName());
if (entity.getTemplateId() != null) {
lqw.eq(MallAttribute::getTemplateId, entity.getTemplateId());
}
if (entity.getId() != null) {
lqw.ne(MallAttribute::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("属性名称已存在");
}
}
/**
* 批量删除属性
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
for (Long id : ids) {
MallAttribute attribute = baseMapper.selectById(id);
if (attribute == null) {
throw new ServiceException("属性不存在");
}
// 可以后续添加属性值表关联查询,检查是否有关联的属性值
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,141 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallAttributeTemplate;
import org.dromara.mall.product.domain.bo.MallAttributeTemplateBo;
import org.dromara.mall.product.domain.vo.MallAttributeTemplateVo;
import org.dromara.mall.product.mapper.MallAttributeTemplateMapper;
import org.dromara.mall.product.service.MallAttributeTemplateService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 属性模板Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallAttributeTemplateServiceImpl implements MallAttributeTemplateService {
private final MallAttributeTemplateMapper baseMapper;
/**
* 查询属性模板
*/
@Override
public MallAttributeTemplateVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询属性模板列表
*/
@Override
public TableDataInfo<MallAttributeTemplateVo> queryPageList(MallAttributeTemplateBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallAttributeTemplate> lqw = buildQueryWrapper(bo);
Page<MallAttributeTemplateVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询属性模板列表
*/
@Override
public List<MallAttributeTemplateVo> queryList(MallAttributeTemplateBo bo) {
LambdaQueryWrapper<MallAttributeTemplate> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallAttributeTemplate> buildQueryWrapper(MallAttributeTemplateBo bo) {
LambdaQueryWrapper<MallAttributeTemplate> lqw = new LambdaQueryWrapper<>();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallAttributeTemplate::getName, bo.getName());
lqw.eq(bo.getCategoryId() != null, MallAttributeTemplate::getCategoryId, bo.getCategoryId());
lqw.eq(bo.getStatus() != null, MallAttributeTemplate::getStatus, bo.getStatus());
lqw.orderByAsc(MallAttributeTemplate::getSort);
lqw.orderByAsc(MallAttributeTemplate::getId);
return lqw;
}
/**
* 新增属性模板
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallAttributeTemplateBo bo) {
MallAttributeTemplate add = MapstructUtils.convert(bo, MallAttributeTemplate.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getSort() == null) {
add.setSort(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改属性模板
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallAttributeTemplateBo bo) {
MallAttributeTemplate update = MapstructUtils.convert(bo, MallAttributeTemplate.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallAttributeTemplate entity) {
if (StringUtils.isBlank(entity.getName())) {
throw new ServiceException("模板名称不能为空");
}
// 检查模板名称是否重复
LambdaQueryWrapper<MallAttributeTemplate> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallAttributeTemplate::getName, entity.getName());
if (entity.getId() != null) {
lqw.ne(MallAttributeTemplate::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("模板名称已存在");
}
}
/**
* 批量删除属性模板
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
for (Long id : ids) {
MallAttributeTemplate template = baseMapper.selectById(id);
if (template == null) {
throw new ServiceException("属性模板不存在");
}
// 可以后续添加属性表关联查询,检查是否有关联的属性
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,158 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallAttributeValue;
import org.dromara.mall.product.domain.bo.MallAttributeValueBo;
import org.dromara.mall.product.domain.vo.MallAttributeValueVo;
import org.dromara.mall.product.mapper.MallAttributeValueMapper;
import org.dromara.mall.product.service.MallAttributeValueService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 属性值Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallAttributeValueServiceImpl implements MallAttributeValueService {
private final MallAttributeValueMapper baseMapper;
/**
* 查询属性值
*/
@Override
public MallAttributeValueVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询属性值列表
*/
@Override
public TableDataInfo<MallAttributeValueVo> queryPageList(MallAttributeValueBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallAttributeValue> lqw = buildQueryWrapper(bo);
Page<MallAttributeValueVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询属性值列表
*/
@Override
public List<MallAttributeValueVo> queryList(MallAttributeValueBo bo) {
LambdaQueryWrapper<MallAttributeValue> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 根据属性ID查询属性值列表
*/
@Override
public List<MallAttributeValueVo> queryListByAttributeId(Long attributeId) {
LambdaQueryWrapper<MallAttributeValue> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallAttributeValue::getAttributeId, attributeId);
lqw.eq(MallAttributeValue::getStatus, 0);
lqw.orderByAsc(MallAttributeValue::getSort);
lqw.orderByAsc(MallAttributeValue::getId);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallAttributeValue> buildQueryWrapper(MallAttributeValueBo bo) {
LambdaQueryWrapper<MallAttributeValue> lqw = new LambdaQueryWrapper<>();
lqw.eq(bo.getAttributeId() != null, MallAttributeValue::getAttributeId, bo.getAttributeId());
lqw.like(StringUtils.isNotBlank(bo.getValue()), MallAttributeValue::getValue, bo.getValue());
lqw.eq(bo.getStatus() != null, MallAttributeValue::getStatus, bo.getStatus());
lqw.orderByAsc(MallAttributeValue::getSort);
lqw.orderByAsc(MallAttributeValue::getId);
return lqw;
}
/**
* 新增属性值
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallAttributeValueBo bo) {
MallAttributeValue add = MapstructUtils.convert(bo, MallAttributeValue.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getSort() == null) {
add.setSort(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改属性值
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallAttributeValueBo bo) {
MallAttributeValue update = MapstructUtils.convert(bo, MallAttributeValue.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallAttributeValue entity) {
if (entity.getAttributeId() == null) {
throw new ServiceException("属性ID不能为空");
}
if (StringUtils.isBlank(entity.getValue())) {
throw new ServiceException("属性值不能为空");
}
// 检查属性值是否重复(在同一属性下)
LambdaQueryWrapper<MallAttributeValue> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallAttributeValue::getAttributeId, entity.getAttributeId());
lqw.eq(MallAttributeValue::getValue, entity.getValue());
if (entity.getId() != null) {
lqw.ne(MallAttributeValue::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("属性值已存在");
}
}
/**
* 批量删除属性值
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
for (Long id : ids) {
MallAttributeValue value = baseMapper.selectById(id);
if (value == null) {
throw new ServiceException("属性值不存在");
}
// 可以后续添加商品属性关联表查询,检查是否有关联的商品属性
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,141 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallBrand;
import org.dromara.mall.product.domain.bo.MallBrandBo;
import org.dromara.mall.product.domain.vo.MallBrandVo;
import org.dromara.mall.product.mapper.MallBrandMapper;
import org.dromara.mall.product.service.MallBrandService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 品牌Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallBrandServiceImpl implements MallBrandService {
private final MallBrandMapper baseMapper;
/**
* 查询品牌
*/
@Override
public MallBrandVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询品牌列表
*/
@Override
public TableDataInfo<MallBrandVo> queryPageList(MallBrandBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallBrand> lqw = buildQueryWrapper(bo);
Page<MallBrandVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询品牌列表
*/
@Override
public List<MallBrandVo> queryList(MallBrandBo bo) {
LambdaQueryWrapper<MallBrand> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallBrand> buildQueryWrapper(MallBrandBo bo) {
LambdaQueryWrapper<MallBrand> lqw = new LambdaQueryWrapper<>();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallBrand::getName, bo.getName());
lqw.eq(bo.getStatus() != null, MallBrand::getStatus, bo.getStatus());
lqw.orderByAsc(MallBrand::getSort);
lqw.orderByAsc(MallBrand::getId);
return lqw;
}
/**
* 新增品牌
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallBrandBo bo) {
MallBrand add = MapstructUtils.convert(bo, MallBrand.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getSort() == null) {
add.setSort(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改品牌
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallBrandBo bo) {
MallBrand update = MapstructUtils.convert(bo, MallBrand.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallBrand entity) {
if (StringUtils.isBlank(entity.getName())) {
throw new ServiceException("品牌名称不能为空");
}
// 检查品牌名称是否重复
LambdaQueryWrapper<MallBrand> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallBrand::getName, entity.getName());
if (entity.getId() != null) {
lqw.ne(MallBrand::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("品牌名称已存在");
}
}
/**
* 批量删除品牌
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 检查是否有关联的商品(这里需要关联商品表查询,暂时跳过)
// 可以后续添加商品表关联查询
for (Long id : ids) {
MallBrand brand = baseMapper.selectById(id);
if (brand == null) {
throw new ServiceException("品牌不存在");
}
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,250 @@
package org.dromara.mall.product.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallCategory;
import org.dromara.mall.product.domain.bo.MallCategoryBo;
import org.dromara.mall.product.domain.vo.MallCategoryVo;
import org.dromara.mall.product.mapper.MallCategoryMapper;
import org.dromara.mall.product.service.MallCategoryService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 商品分类Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallCategoryServiceImpl implements MallCategoryService {
private final MallCategoryMapper baseMapper;
/**
* 查询商品分类
*/
@Override
public MallCategoryVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询商品分类列表
*/
@Override
public TableDataInfo<MallCategoryVo> queryPageList(MallCategoryBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallCategory> lqw = buildQueryWrapper(bo);
Page<MallCategoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询商品分类列表
*/
@Override
public List<MallCategoryVo> queryList(MallCategoryBo bo) {
LambdaQueryWrapper<MallCategory> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 查询商品分类树形列表
*/
@Override
public List<MallCategoryVo> queryTreeList() {
MallCategoryBo bo = new MallCategoryBo();
bo.setStatus(0); // 只查询启用的分类
List<MallCategoryVo> list = queryList(bo);
return buildTree(list);
}
private LambdaQueryWrapper<MallCategory> buildQueryWrapper(MallCategoryBo bo) {
LambdaQueryWrapper<MallCategory> lqw = new LambdaQueryWrapper<>();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallCategory::getName, bo.getName());
lqw.eq(bo.getParentId() != null, MallCategory::getParentId, bo.getParentId());
lqw.eq(bo.getLevel() != null, MallCategory::getLevel, bo.getLevel());
lqw.eq(bo.getStatus() != null, MallCategory::getStatus, bo.getStatus());
lqw.orderByAsc(MallCategory::getSort);
lqw.orderByAsc(MallCategory::getId);
return lqw;
}
/**
* 构建树形结构
*/
private List<MallCategoryVo> buildTree(List<MallCategoryVo> list) {
if (CollUtil.isEmpty(list)) {
return new ArrayList<>();
}
// 按父ID分组
Map<Long, List<MallCategoryVo>> childrenMap = list.stream()
.filter(item -> item.getParentId() != null && item.getParentId() != 0)
.collect(Collectors.groupingBy(MallCategoryVo::getParentId));
// 设置子节点
list.forEach(item -> {
List<MallCategoryVo> children = childrenMap.get(item.getId());
item.setChildren(children != null ? children : new ArrayList<>());
});
// 返回顶级节点
return list.stream()
.filter(item -> item.getParentId() == null || item.getParentId() == 0)
.sorted(Comparator.comparing(MallCategoryVo::getSort, Comparator.nullsLast(Comparator.naturalOrder())))
.collect(Collectors.toList());
}
/**
* 新增商品分类
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallCategoryBo bo) {
MallCategory add = MapstructUtils.convert(bo, MallCategory.class);
validEntityBeforeSave(add);
// 设置父级ID和级别
if (add.getParentId() == null || add.getParentId() == 0) {
add.setParentId(0L);
add.setLevel(1);
} else {
// 查询父分类
MallCategory parent = baseMapper.selectById(add.getParentId());
if (parent == null) {
throw new ServiceException("父分类不存在");
}
if (parent.getStatus() != 0) {
throw new ServiceException("父分类已禁用,不能添加子分类");
}
if (parent.getLevel() >= 3) {
throw new ServiceException("分类级别不能超过3级");
}
add.setLevel(parent.getLevel() + 1);
}
// 设置默认值
if (add.getSort() == null) {
add.setSort(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改商品分类
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallCategoryBo bo) {
MallCategory update = MapstructUtils.convert(bo, MallCategory.class);
validEntityBeforeSave(update);
// 不能将父分类设置为自己的子分类
if (update.getParentId() != null && update.getParentId() != 0) {
if (update.getId().equals(update.getParentId())) {
throw new ServiceException("不能将分类设置为自己的父分类");
}
// 检查是否会形成循环
if (hasCircularReference(update.getId(), update.getParentId())) {
throw new ServiceException("不能将分类设置为自己的子分类的子分类");
}
// 查询父分类
MallCategory parent = baseMapper.selectById(update.getParentId());
if (parent == null) {
throw new ServiceException("父分类不存在");
}
if (parent.getLevel() >= 3) {
throw new ServiceException("分类级别不能超过3级");
}
update.setLevel(parent.getLevel() + 1);
} else {
update.setParentId(0L);
update.setLevel(1);
}
return baseMapper.updateById(update) > 0;
}
/**
* 检查是否存在循环引用
*/
private boolean hasCircularReference(Long id, Long parentId) {
Long currentParentId = parentId;
Set<Long> visited = new HashSet<>();
visited.add(id);
while (currentParentId != null && currentParentId != 0) {
if (visited.contains(currentParentId)) {
return true;
}
visited.add(currentParentId);
MallCategory parent = baseMapper.selectById(currentParentId);
if (parent == null) {
break;
}
currentParentId = parent.getParentId();
}
return false;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallCategory entity) {
if (StringUtils.isBlank(entity.getName())) {
throw new ServiceException("分类名称不能为空");
}
// 检查同级分类名称是否重复
LambdaQueryWrapper<MallCategory> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallCategory::getName, entity.getName());
lqw.eq(entity.getParentId() != null, MallCategory::getParentId, entity.getParentId());
if (entity.getId() != null) {
lqw.ne(MallCategory::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("同级分类名称不能重复");
}
}
/**
* 批量删除商品分类
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 检查是否存在子分类
for (Long id : ids) {
LambdaQueryWrapper<MallCategory> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallCategory::getParentId, id);
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("存在子分类,不能删除");
}
// 检查是否有关联的商品(这里需要关联商品表查询,暂时跳过)
// 可以后续添加商品表关联查询
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,163 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.mall.product.domain.MallProductAttribute;
import org.dromara.mall.product.domain.bo.MallProductAttributeBo;
import org.dromara.mall.product.domain.vo.MallProductAttributeVo;
import org.dromara.mall.product.mapper.MallProductAttributeMapper;
import org.dromara.mall.product.service.MallProductAttributeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 商品属性关联Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallProductAttributeServiceImpl implements MallProductAttributeService {
private final MallProductAttributeMapper baseMapper;
/**
* 查询商品属性关联
*/
@Override
public MallProductAttributeVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 根据商品ID查询商品属性关联列表
*/
@Override
public List<MallProductAttributeVo> queryListByProductId(Long productId) {
LambdaQueryWrapper<MallProductAttribute> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallProductAttribute::getProductId, productId);
lqw.orderByAsc(MallProductAttribute::getId);
return baseMapper.selectVoList(lqw);
}
/**
* 新增商品属性关联
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallProductAttributeBo bo) {
MallProductAttribute add = MapstructUtils.convert(bo, MallProductAttribute.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 批量新增商品属性关联
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertBatch(List<MallProductAttributeBo> list) {
if (list == null || list.isEmpty()) {
return true;
}
List<MallProductAttribute> entities = MapstructUtils.convert(list, MallProductAttribute.class);
for (MallProductAttribute entity : entities) {
validEntityBeforeSave(entity);
}
// 批量插入
for (MallProductAttribute entity : entities) {
baseMapper.insert(entity);
}
return true;
}
/**
* 修改商品属性关联
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallProductAttributeBo bo) {
MallProductAttribute update = MapstructUtils.convert(bo, MallProductAttribute.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallProductAttribute entity) {
if (entity.getProductId() == null) {
throw new ServiceException("商品ID不能为空");
}
if (entity.getAttributeId() == null) {
throw new ServiceException("属性ID不能为空");
}
// 检查同一商品下是否已有相同的属性
LambdaQueryWrapper<MallProductAttribute> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallProductAttribute::getProductId, entity.getProductId());
lqw.eq(MallProductAttribute::getAttributeId, entity.getAttributeId());
if (entity.getId() != null) {
lqw.ne(MallProductAttribute::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("商品已存在该属性");
}
}
/**
* 批量删除商品属性关联
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
for (Long id : ids) {
MallProductAttribute attribute = baseMapper.selectById(id);
if (attribute == null) {
throw new ServiceException("商品属性关联不存在");
}
}
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* 根据商品ID删除商品属性关联
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteByProductId(Long productId) {
LambdaQueryWrapper<MallProductAttribute> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallProductAttribute::getProductId, productId);
return baseMapper.delete(lqw) >= 0;
}
/**
* 保存商品属性关联(先删除旧的,再插入新的)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveProductAttributes(Long productId, List<MallProductAttributeBo> list) {
// 删除旧的关联
deleteByProductId(productId);
// 插入新的关联
if (list != null && !list.isEmpty()) {
for (MallProductAttributeBo bo : list) {
bo.setProductId(productId);
}
return insertBatch(list);
}
return true;
}
}
@@ -0,0 +1,137 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallProduct;
import org.dromara.mall.product.domain.bo.MallProductBo;
import org.dromara.mall.product.domain.vo.MallProductVo;
import org.dromara.mall.product.mapper.MallProductMapper;
import org.dromara.mall.product.service.MallProductService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 商品Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallProductServiceImpl implements MallProductService {
private final MallProductMapper baseMapper;
/**
* 查询商品
*/
@Override
public MallProductVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询商品列表
*/
@Override
public TableDataInfo<MallProductVo> queryPageList(MallProductBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallProduct> lqw = buildQueryWrapper(bo);
Page<MallProductVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询商品列表
*/
@Override
public List<MallProductVo> queryList(MallProductBo bo) {
LambdaQueryWrapper<MallProduct> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallProduct> buildQueryWrapper(MallProductBo bo) {
LambdaQueryWrapper<MallProduct> lqw = new LambdaQueryWrapper<>();
lqw.like(StringUtils.isNotBlank(bo.getName()), MallProduct::getName, bo.getName());
lqw.eq(bo.getCategoryId() != null, MallProduct::getCategoryId, bo.getCategoryId());
lqw.eq(bo.getBrandId() != null, MallProduct::getBrandId, bo.getBrandId());
lqw.eq(bo.getStatus() != null, MallProduct::getStatus, bo.getStatus());
lqw.orderByDesc(MallProduct::getSort);
lqw.orderByDesc(MallProduct::getId);
return lqw;
}
/**
* 新增商品
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallProductBo bo) {
MallProduct add = MapstructUtils.convert(bo, MallProduct.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getSort() == null) {
add.setSort(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
if (add.getSalesCount() == null) {
add.setSalesCount(0);
}
if (add.getViewCount() == null) {
add.setViewCount(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改商品
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallProductBo bo) {
MallProduct update = MapstructUtils.convert(bo, MallProduct.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallProduct entity) {
if (StringUtils.isBlank(entity.getName())) {
throw new ServiceException("商品名称不能为空");
}
}
/**
* 批量删除商品
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
for (Long id : ids) {
MallProduct product = baseMapper.selectById(id);
if (product == null) {
throw new ServiceException("商品不存在");
}
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,158 @@
package org.dromara.mall.product.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.mall.product.domain.MallSku;
import org.dromara.mall.product.domain.bo.MallSkuBo;
import org.dromara.mall.product.domain.vo.MallSkuVo;
import org.dromara.mall.product.mapper.MallSkuMapper;
import org.dromara.mall.product.service.MallSkuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* SKU Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class MallSkuServiceImpl implements MallSkuService {
private final MallSkuMapper baseMapper;
/**
* 查询SKU
*/
@Override
public MallSkuVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询SKU列表
*/
@Override
public TableDataInfo<MallSkuVo> queryPageList(MallSkuBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<MallSku> lqw = buildQueryWrapper(bo);
Page<MallSkuVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询SKU列表
*/
@Override
public List<MallSkuVo> queryList(MallSkuBo bo) {
LambdaQueryWrapper<MallSku> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 根据商品ID查询SKU列表
*/
@Override
public List<MallSkuVo> queryListByProductId(Long productId) {
LambdaQueryWrapper<MallSku> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallSku::getProductId, productId);
lqw.eq(MallSku::getStatus, 0); // 只查询启用的SKU
lqw.orderByAsc(MallSku::getId);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MallSku> buildQueryWrapper(MallSkuBo bo) {
LambdaQueryWrapper<MallSku> lqw = new LambdaQueryWrapper<>();
lqw.eq(bo.getProductId() != null, MallSku::getProductId, bo.getProductId());
lqw.eq(StringUtils.isNotBlank(bo.getSkuCode()), MallSku::getSkuCode, bo.getSkuCode());
lqw.like(StringUtils.isNotBlank(bo.getName()), MallSku::getName, bo.getName());
lqw.eq(bo.getStatus() != null, MallSku::getStatus, bo.getStatus());
lqw.orderByAsc(MallSku::getId);
return lqw;
}
/**
* 新增SKU
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(MallSkuBo bo) {
MallSku add = MapstructUtils.convert(bo, MallSku.class);
validEntityBeforeSave(add);
// 设置默认值
if (add.getStock() == null) {
add.setStock(0);
}
if (add.getStatus() == null) {
add.setStatus(0);
}
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改SKU
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(MallSkuBo bo) {
MallSku update = MapstructUtils.convert(bo, MallSku.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(MallSku entity) {
if (entity.getProductId() == null) {
throw new ServiceException("商品ID不能为空");
}
if (StringUtils.isBlank(entity.getSkuCode())) {
throw new ServiceException("SKU编码不能为空");
}
if (entity.getPrice() == null) {
throw new ServiceException("价格不能为空");
}
// 检查SKU编码是否重复
LambdaQueryWrapper<MallSku> lqw = new LambdaQueryWrapper<>();
lqw.eq(MallSku::getSkuCode, entity.getSkuCode());
if (entity.getId() != null) {
lqw.ne(MallSku::getId, entity.getId());
}
Long count = baseMapper.selectCount(lqw);
if (count > 0) {
throw new ServiceException("SKU编码已存在");
}
}
/**
* 批量删除SKU
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
for (Long id : ids) {
MallSku sku = baseMapper.selectById(id);
if (sku == null) {
throw new ServiceException("SKU不存在");
}
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.mall.product.config.MallProductAutoConfiguration
@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More