新增提货券

This commit is contained in:
zhangji committed 2025-07-25 15:37:10 +08:00
1 parent b0d6e56c42
commit 87486b9ea0
44 files changed
+2583 -4

No files matched your search

@@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.promotion.api.giftvoucher;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherRespDTO;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherUseReqDTO;
/**
* 优惠劵 API 接口
*
* @author 芋道源码
*/
public interface GiftVoucherApi {
GiftVoucherRespDTO getGiftVoucherById(Long giftVoucherId);
void useGiftVoucher(GiftVoucherUseReqDTO giftVoucherUseReqDTO);
}
@@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.promotion.api.giftvoucher.dto;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 提货劵 Response DTO
*
* @author 茂县源码
*/
@Data
public class GiftVoucherRespDTO{
/**
* 提货劵编号
*/
private Long id;
/**
* 提货劵模板编号
*/
private Long templateId;
/**
* 提货劵名
*/
private String name;
/**
* 提货劵状态
*
* 1-未使用
* 2-已使用
* 3-已失效
*/
private Integer status;
/**
* 商品范围编号的数组
*/
private List<Long> productScopeValues;
// private String productScopeValues;
/**
* 可购买商品数量
*/
private Integer productCount;
/**
* 用户编号
*/
private Long userId;
/**
* 领取类型
*
* 1 - 用户主动领取
* 2 - 后台自动发放
*/
private Integer takeType;
/**
* 生效开始时间
*/
private LocalDateTime validStartTime;
/**
* 生效结束时间
*/
private LocalDateTime validEndTime;
/**
* 使用订单号
*/
private Long useOrderId;
/**
* 使用时间
*/
private LocalDateTime useTime;
}
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.promotion.api.giftvoucher.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 提货劵使用 Request DTO
*
* @author 芋道源码
*/
@Data
public class GiftVoucherUseReqDTO {
/**
* 优惠劵编号
*/
@NotNull(message = "提货劵编号不能为空")
private Long id;
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 订单编号
*/
@NotNull(message = "订单编号不能为空")
private Long orderId;
}
@@ -138,4 +138,15 @@ public interface ErrorCodeConstants {
// ========== 客服消息 1-013-020-000 ==========
ErrorCode KEFU_MESSAGE_NOT_EXISTS = new ErrorCode(1_013_020_000, "客服消息不存在");
// ========== 提货券模板 1-013-021-000 ==========
ErrorCode GIFT_VOUCHER_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_021_000, "提货券模板不存在");
ErrorCode GIFT_VOUCHER_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1_013_021_001, "用户已领取过此提货券");
ErrorCode GIFT_VOUCHER_STATUS_NOT_UNUSED = new ErrorCode(1_013_021_002, "提货劵不处于待使用状态");
// ========== 提货劵 1-013-022-000 ==========
ErrorCode GIFT_VOUCHER_NOT_EXISTS = new ErrorCode(1_013_022_000, "提货劵不存在");
ErrorCode GIFT_VOUCHER_TEMPLATE_CANNOT_TAKE = new ErrorCode(1_013_022_001, "领取方式不正确");
ErrorCode GIFT_VOUCHER_TEMPLATE_EXPIRED = new ErrorCode(1_013_022_002, "提货券模板已过期");
ErrorCode GIFT_VOUCHER_TEMPLATE_NOT_ENOUGH = new ErrorCode(1_013_022_003, "当前剩余数量不够领取");
}
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.promotion.enums.giftvoucher;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import cn.iocoder.yudao.module.promotion.enums.diy.DiyPageEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 提货劵状态枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum GiftVoucherStatusEnum implements ArrayValuable<Integer> {
UNUSED(1, "未使用"),
USED(2, "已使用"),
EXPIRE(3, "已过期");
private static final Integer[] ARRAYS = Arrays.stream(values()).map(GiftVoucherStatusEnum::getStatus).toArray(Integer[]::new);
/**
* 值
*/
private final Integer status;
/**
* 名字
*/
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
}
@@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.promotion.enums.giftvoucher;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 优惠劵模板的有限期类型的枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum GiftVoucherTemplateValidityTypeEnum implements ArrayValuable<Integer> {
DATE(1, "固定日期"),
TERM(2, "领取之后"),
;
private static final Integer[] ARRAYS = Arrays.stream(values()).map(GiftVoucherTemplateValidityTypeEnum::getType).toArray(Integer[]::new);
/**
* 值
*/
private final Integer type;
/**
* 名字
*/
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
}
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.promotion.enums.giftvoucher;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
/**
* 优惠劵领取方式
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum GiftVoucherTypeEnum implements ArrayValuable<Integer> {
USER(1, "扫码领取"), // 用户可扫码直接领取
ADMIN(2, "指定发放"), // 后台指定会员赠送优惠劵
;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(GiftVoucherTypeEnum::getType).toArray(Integer[]::new);
/**
* 值
*/
private final Integer type;
/**
* 名字
*/
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
public static boolean isUser(Integer type) {
return Objects.equals(USER.getType(), type);
}
}
@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.promotion.api.giftvoucher;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherRespDTO;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherUseReqDTO;
import cn.iocoder.yudao.module.promotion.service.giftvoucher.GiftVoucherService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 优惠劵 API 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class GiftVoucherApiImpl implements GiftVoucherApi {
@Resource
private GiftVoucherService giftVoucherService;
@Override
public GiftVoucherRespDTO getGiftVoucherById(Long giftVoucherId) {
return BeanUtils.toBean(giftVoucherService.getGiftVoucher(giftVoucherId), GiftVoucherRespDTO.class);
}
@Override
public void useGiftVoucher(GiftVoucherUseReqDTO giftVoucherUseReqDTO) {
giftVoucherService.useGiftVoucher(giftVoucherUseReqDTO);
}
}
@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherSaveReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherSendReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherDO;
import cn.iocoder.yudao.module.promotion.service.giftvoucher.GiftVoucherService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 提货劵")
@RestController
@RequestMapping("/promotion/gift-voucher")
@Validated
public class GiftVoucherController {
@Resource
private GiftVoucherService giftVoucherService;
@PostMapping("/create")
@Operation(summary = "创建提货劵")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher:create')")
public CommonResult<Long> createGiftVoucher(@Valid @RequestBody GiftVoucherSaveReqVO createReqVO) {
return success(giftVoucherService.createGiftVoucher(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新提货劵")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher:update')")
public CommonResult<Boolean> updateGiftVoucher(@Valid @RequestBody GiftVoucherSaveReqVO updateReqVO) {
giftVoucherService.updateGiftVoucher(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除提货劵")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher:delete')")
public CommonResult<Boolean> deleteGiftVoucher(@RequestParam("id") Long id) {
giftVoucherService.deleteGiftVoucher(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得提货劵")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher:query')")
public CommonResult<GiftVoucherRespVO> getGiftVoucher(@RequestParam("id") Long id) {
GiftVoucherDO giftVoucher = giftVoucherService.getGiftVoucher(id);
return success(BeanUtils.toBean(giftVoucher, GiftVoucherRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得提货劵分页")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher:query')")
public CommonResult<PageResult<GiftVoucherRespVO>> getGiftVoucherPage(@Valid GiftVoucherPageReqVO pageReqVO) {
PageResult<GiftVoucherDO> pageResult = giftVoucherService.getGiftVoucherPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, GiftVoucherRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出提货劵 Excel")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportGiftVoucherExcel(@Valid GiftVoucherPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<GiftVoucherDO> list = giftVoucherService.getGiftVoucherPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "提货劵.xls", "数据", GiftVoucherRespVO.class,
BeanUtils.toBean(list, GiftVoucherRespVO.class));
}
@PostMapping("/send")
@Operation(summary = "发送提货券")
@PreAuthorize("@ss.hasPermission('promotion:coupon:send')")
public CommonResult<Boolean> sendGiftVoucher(@Valid @RequestBody GiftVoucherSendReqVO reqVO) {
giftVoucherService.takeGiftVoucherByAdmin(reqVO.getTemplateId(), reqVO.getUserIds());
return success(true);
}
}
@@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplateRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplateSaveReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import cn.iocoder.yudao.module.promotion.service.giftvoucher.GiftVoucherTemplateService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
@Tag(name = "管理后台 - 提货券模板")
@RestController
@RequestMapping("/promotion/gift-voucher-template")
@Validated
public class GiftVoucherTemplateController {
@Resource
private GiftVoucherTemplateService giftVoucherTemplateService;
@PostMapping("/create")
@Operation(summary = "创建提货券模板")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher-template:create')")
public CommonResult<Long> createGiftVoucherTemplate(@Valid @RequestBody GiftVoucherTemplateSaveReqVO createReqVO) {
return success(giftVoucherTemplateService.createGiftVoucherTemplate(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新提货券模板")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher-template:update')")
public CommonResult<Boolean> updateGiftVoucherTemplate(@Valid @RequestBody GiftVoucherTemplateSaveReqVO updateReqVO) {
giftVoucherTemplateService.updateGiftVoucherTemplate(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除提货券模板")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher-template:delete')")
public CommonResult<Boolean> deleteGiftVoucherTemplate(@RequestParam("id") Long id) {
giftVoucherTemplateService.deleteGiftVoucherTemplate(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得提货券模板")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher-template:query')")
public CommonResult<GiftVoucherTemplateRespVO> getGiftVoucherTemplate(@RequestParam("id") Long id) {
GiftVoucherTemplateDO giftVoucherTemplate = giftVoucherTemplateService.getGiftVoucherTemplate(id);
return success(BeanUtils.toBean(giftVoucherTemplate, GiftVoucherTemplateRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得提货券模板分页")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher-template:query')")
public CommonResult<PageResult<GiftVoucherTemplateRespVO>> getGiftVoucherTemplatePage(@Valid GiftVoucherTemplatePageReqVO pageReqVO) {
PageResult<GiftVoucherTemplateDO> pageResult = giftVoucherTemplateService.getGiftVoucherTemplatePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, GiftVoucherTemplateRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出提货券模板 Excel")
@PreAuthorize("@ss.hasPermission('promotion:gift-voucher-template:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportGiftVoucherTemplateExcel(@Valid GiftVoucherTemplatePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<GiftVoucherTemplateDO> list = giftVoucherTemplateService.getGiftVoucherTemplatePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "提货券模板.xls", "数据", GiftVoucherTemplateRespVO.class,
BeanUtils.toBean(list, GiftVoucherTemplateRespVO.class));
}
}
@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 提货劵分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class GiftVoucherPageReqVO extends PageParam {
@Schema(description = "提货劵模板编号", example = "7305")
private Long templateId;
@Schema(description = "提货劵名", example = "王五")
private String name;
@Schema(description = "提货劵状态", example = "2")
private Integer status;
@Schema(description = "商品范围编号的数组")
private String productScopeValues;
@Schema(description = "用户编号", example = "17286")
private Long userId;
@Schema(description = "领取类型", example = "1")
private Integer takeType;
@Schema(description = "生效开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] validStartTime;
@Schema(description = "生效结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] validEndTime;
@Schema(description = "使用订单号", example = "23301")
private Long useOrderId;
@Schema(description = "使用时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] useTime;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "可购买商品数量", example = "18952")
private Integer productCount;
}
@@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 提货劵 Response VO")
@Data
@ExcelIgnoreUnannotated
public class GiftVoucherRespVO {
@Schema(description = "提货劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17673")
@ExcelProperty("提货劵编号")
private Long id;
@Schema(description = "提货劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7305")
@ExcelProperty("提货劵模板编号")
private Long templateId;
@Schema(description = "提货劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@ExcelProperty("提货劵名")
private String name;
@Schema(description = "提货劵状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("提货劵状态")
private Integer status;
@Schema(description = "商品范围编号的数组")
@ExcelProperty("商品范围编号的数组")
private String productScopeValues;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17286")
@ExcelProperty("用户编号")
private Long userId;
@Schema(description = "领取类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("领取类型")
private Integer takeType;
@Schema(description = "生效开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("生效开始时间")
private LocalDateTime validStartTime;
@Schema(description = "生效结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("生效结束时间")
private LocalDateTime validEndTime;
@Schema(description = "使用订单号", example = "23301")
@ExcelProperty("使用订单号")
private Long useOrderId;
@Schema(description = "使用时间")
@ExcelProperty("使用时间")
private LocalDateTime useTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "可购买商品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "18952")
@ExcelProperty("可购买商品数量")
private Integer productCount;
}
@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 提货劵新增/修改 Request VO")
@Data
public class GiftVoucherSaveReqVO {
@Schema(description = "提货劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17673")
private Long id;
@Schema(description = "提货劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7305")
@NotNull(message = "提货劵模板编号不能为空")
private Long templateId;
@Schema(description = "提货劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@NotEmpty(message = "提货劵名不能为空")
private String name;
@Schema(description = "提货劵状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "提货劵状态")
private Integer status;
@Schema(description = "商品范围编号的数组")
private String productScopeValues;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17286")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "领取类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "领取类型")
private Integer takeType;
@Schema(description = "生效开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "生效开始时间不能为空")
private LocalDateTime validStartTime;
@Schema(description = "生效结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "生效结束时间不能为空")
private LocalDateTime validEndTime;
@Schema(description = "使用订单号", example = "23301")
private Long useOrderId;
@Schema(description = "使用时间")
private LocalDateTime useTime;
@Schema(description = "可购买商品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "18952")
@NotNull(message = "可购买商品数量不能为空")
private Integer productCount;
}
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.ToString;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Set;
@Schema(description = "管理后台 - 提货劵发放 Request VO")
@Data
@ToString(callSuper = true)
public class GiftVoucherSendReqVO {
@Schema(description = "提货劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "提货劵模板编号不能为空")
private Long templateId;
@Schema(description = "用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
@NotEmpty(message = "用户编号列表不能为空")
private Set<Long> userIds;
}
@@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template;
import lombok.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 提货券模板分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class GiftVoucherTemplatePageReqVO extends PageParam {
@Schema(description = "提货券名", example = "赵六")
private String name;
@Schema(description = "提货券描述", example = "你猜")
private String description;
@Schema(description = "状态", example = "2")
private Integer status;
@Schema(description = "发放数量, -1 - 则表示不限制", example = "23927")
private Integer totalCount;
@Schema(description = "每人限领个数, -1 - 则表示不限制", example = "31760")
private Integer takeLimitCount;
@Schema(description = "领取方式", example = "1")
private Integer takeType;
@Schema(description = "商品范围编号的数组")
private String productScopeValues;
@Schema(description = "生效日期类型", example = "1")
private Integer validityType;
@Schema(description = "固定日期-生效开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] validStartTime;
@Schema(description = "固定日期-生效结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] validEndTime;
@Schema(description = "领取日期-开始天数")
private Integer fixedStartTerm;
@Schema(description = "领取日期-结束天数")
private Integer fixedEndTerm;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "领取提货券的数量", example = "1818")
private Integer takeCount;
@Schema(description = "使用提货券的次数", example = "25128")
private Integer useCount;
@Schema(description = "可购买商品数量", example = "22134")
private Integer productCount;
}
@@ -0,0 +1,82 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 提货券模板 Response VO")
@Data
@ExcelIgnoreUnannotated
public class GiftVoucherTemplateRespVO {
@Schema(description = "模板编号,自增唯一。", requiredMode = Schema.RequiredMode.REQUIRED, example = "13004")
@ExcelProperty("模板编号,自增唯一。")
private Long id;
@Schema(description = "提货券名", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@ExcelProperty("提货券名")
private String name;
@Schema(description = "提货券描述", example = "你猜")
@ExcelProperty("提货券描述")
private String description;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("状态")
private Integer status;
@Schema(description = "发放数量, -1 - 则表示不限制", requiredMode = Schema.RequiredMode.REQUIRED, example = "23927")
@ExcelProperty("发放数量, -1 - 则表示不限制")
private Integer totalCount;
@Schema(description = "每人限领个数, -1 - 则表示不限制", requiredMode = Schema.RequiredMode.REQUIRED, example = "31760")
@ExcelProperty("每人限领个数, -1 - 则表示不限制")
private Integer takeLimitCount;
@Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("领取方式")
private Integer takeType;
@Schema(description = "商品范围编号的数组")
@ExcelProperty("商品范围编号的数组")
private String productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("生效日期类型")
private Integer validityType;
@Schema(description = "固定日期-生效开始时间")
@ExcelProperty("固定日期-生效开始时间")
private LocalDateTime validStartTime;
@Schema(description = "固定日期-生效结束时间")
@ExcelProperty("固定日期-生效结束时间")
private LocalDateTime validEndTime;
@Schema(description = "领取日期-开始天数")
@ExcelProperty("领取日期-开始天数")
private Integer fixedStartTerm;
@Schema(description = "领取日期-结束天数")
@ExcelProperty("领取日期-结束天数")
private Integer fixedEndTerm;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "领取提货券的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1818")
@ExcelProperty("领取提货券的数量")
private Integer takeCount;
@Schema(description = "使用提货券的次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25128")
@ExcelProperty("使用提货券的次数")
private Integer useCount;
@Schema(description = "可购买商品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "22134")
@ExcelProperty("可购买商品数量")
private Integer productCount;
}
@@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.*;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 提货券模板新增/修改 Request VO")
@Data
public class GiftVoucherTemplateSaveReqVO {
@Schema(description = "模板编号,自增唯一。", requiredMode = Schema.RequiredMode.REQUIRED, example = "13004")
private Long id;
@Schema(description = "提货券名", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@NotEmpty(message = "提货券名不能为空")
private String name;
@Schema(description = "提货券描述", example = "你猜")
private String description;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "状态不能为空")
private Integer status;
@Schema(description = "发放数量, -1 - 则表示不限制", requiredMode = Schema.RequiredMode.REQUIRED, example = "23927")
@NotNull(message = "发放数量, -1 - 则表示不限制不能为空")
private Integer totalCount;
@Schema(description = "每人限领个数, -1 - 则表示不限制", requiredMode = Schema.RequiredMode.REQUIRED, example = "31760")
@NotNull(message = "每人限领个数, -1 - 则表示不限制不能为空")
private Integer takeLimitCount;
@Schema(description = "领取方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "领取方式不能为空")
private Integer takeType;
@Schema(description = "商品范围编号的数组")
private String productScopeValues;
@Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "生效日期类型不能为空")
private Integer validityType;
@Schema(description = "固定日期-生效开始时间")
private LocalDateTime validStartTime;
@Schema(description = "固定日期-生效结束时间")
private LocalDateTime validEndTime;
@Schema(description = "领取日期-开始天数")
private Integer fixedStartTerm;
@Schema(description = "领取日期-结束天数")
private Integer fixedEndTerm;
@Schema(description = "领取提货券的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1818")
@NotNull(message = "领取提货券的数量不能为空")
private Integer takeCount;
@Schema(description = "使用提货券的次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25128")
@NotNull(message = "使用提货券的次数不能为空")
private Integer useCount;
@Schema(description = "可购买商品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "22134")
@NotNull(message = "可购买商品数量不能为空")
private Integer productCount;
}
@@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.promotion.controller.app.giftvoucher;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo.AppGiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo.AppGiftVoucherRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo.AppGiftVoucherTakeReqVO;
import cn.iocoder.yudao.module.promotion.convert.giftvoucher.GiftVoucherConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherTypeEnum;
import cn.iocoder.yudao.module.promotion.service.giftvoucher.GiftVoucherService;
import cn.iocoder.yudao.module.promotion.service.giftvoucher.GiftVoucherTemplateService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "用户 App - 我的提货劵列表")
@RestController
@RequestMapping("/promotion/gift-voucher")
@Validated
public class AppGiftVoucherController {
@Resource
private GiftVoucherService giftVoucherService;
@Resource
private GiftVoucherTemplateService giftVoucherTemplateService;
@PostMapping("/take")
@Operation(summary = "领取提货劵")
@Parameter(name = "templateId", description = "提货劵模板编号", required = true, example = "1024")
public CommonResult<Boolean> takeGiftVoucher(@Valid @RequestBody AppGiftVoucherTakeReqVO reqVO) {
// 1. 领取提货劵
Long userId = getLoginUserId();
giftVoucherService.takeGiftVoucher(reqVO.getTemplateId(), CollUtil.newHashSet(userId), GiftVoucherTypeEnum.USER);
// 2. 检查是否可以继续领取
GiftVoucherTemplateDO giftVoucherTemplate = giftVoucherTemplateService.getGiftVoucherTemplate(reqVO.getTemplateId());
boolean canTakeAgain = true;
if (giftVoucherTemplate.getTakeLimitCount() != null && giftVoucherTemplate.getTakeLimitCount() > 0) {
Integer takeCount = giftVoucherService.getTakeCount(reqVO.getTemplateId(), userId);
canTakeAgain = takeCount < giftVoucherTemplate.getTakeLimitCount();
}
return success(canTakeAgain);
}
@GetMapping("/page")
@Operation(summary = "我的提货劵列表")
public CommonResult<PageResult<AppGiftVoucherRespVO>> getGiftVoucherPage(AppGiftVoucherPageReqVO pageReqVO) {
PageResult<GiftVoucherDO> pageResult = giftVoucherService.getGiftVoucherPage(
GiftVoucherConvert.INSTANCE.convert(pageReqVO, Collections.singleton(getLoginUserId())));
return success(BeanUtils.toBean(pageResult, AppGiftVoucherRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得提货劵")
@Parameter(name = "id", description = "优惠劵编号", required = true, example = "1024")
public CommonResult<AppGiftVoucherRespVO> getGiftVoucher(@RequestParam("id") Long id) {
GiftVoucherDO giftVoucherDO = giftVoucherService.getGiftVoucherByUserId(getLoginUserId(), id);
return success(BeanUtils.toBean(giftVoucherDO, AppGiftVoucherRespVO.class));
}
@GetMapping(value = "/get-unused-count")
@Operation(summary = "获得未使用的提货劵数量")
public CommonResult<Long> getUnusedCouponCount() {
return success(giftVoucherService.getUnusedCouponCount(getLoginUserId()));
}
}
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "用户 App - 提货劵分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AppGiftVoucherPageReqVO extends PageParam {
@Schema(description = "提货劵状态", example = "1")
@InEnum(value = CouponStatusEnum.class, message = "提货劵状态,必须是 {value}")
private Integer status;
}
@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 提货劵 Response VO")
@Data
@ExcelIgnoreUnannotated
public class AppGiftVoucherRespVO {
@Schema(description = "提货劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "16947")
@ExcelProperty("提货劵编号")
private Long id;
@Schema(description = "提货劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@ExcelProperty("提货劵名")
private String name;
@Schema(description = "提货劵状态 * * 1-未使用 * 2-已使用 * 3-已失效", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("提货劵状态 * * 1-未使用 * 2-已使用 * 3-已失效")
private Integer status;
@Schema(description = "商品 SPU 编号", example = "9673")
@ExcelProperty("商品 SPU 编号")
private Long spuId;
@Schema(description = "商品 SKU 编号", example = "28153")
@ExcelProperty("商品 SKU 编号")
private Long skuId;
@Schema(description = "可购买商品数量", example = "2")
@ExcelProperty("可购买商品数量")
private Integer productCount;
@Schema(description = "提货券流水号", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("提货券流水号")
private String no;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2975")
@ExcelProperty("用户编号")
private Long userId;
@Schema(description = "领取类型 * * 1 - 用户主动领取 * 2 - 后台自动发放", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("领取类型 * * 1 - 用户主动领取 * 2 - 后台自动发放")
private Integer takeType;
@Schema(description = "生效开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("生效开始时间")
private LocalDateTime validStartTime;
@Schema(description = "生效结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("生效结束时间")
private LocalDateTime validEndTime;
@Schema(description = "使用订单号", example = "27627")
@ExcelProperty("使用订单号")
private Long useOrderId;
@Schema(description = "使用时间")
@ExcelProperty("使用时间")
private LocalDateTime useTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "用户 App - 提货劵领取 Request VO")
@Data
public class AppGiftVoucherTakeReqVO {
@Schema(description = "提货劵模板编号", example = "1")
@NotNull(message = "提货劵模板编号不能为空")
private Long templateId;
}
@@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.promotion.convert.giftvoucher;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.giftvoucher.vo.AppGiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherStatusEnum;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherTemplateValidityTypeEnum;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.time.LocalDateTime;
import java.util.Collection;
/**
* 提货劵 Convert
*
* @author 芋道源码
*/
@Mapper
public interface GiftVoucherConvert {
GiftVoucherConvert INSTANCE = Mappers.getMapper(GiftVoucherConvert.class);
default GiftVoucherDO convert(GiftVoucherTemplateDO template, Long userId) {
GiftVoucherDO giftVoucherDO = new GiftVoucherDO()
.setTemplateId(template.getId())
.setName(template.getName())
.setTakeType(template.getTakeType())
.setStatus(GiftVoucherStatusEnum.UNUSED.getStatus())
.setProductScopeValues(template.getProductScopeValues())
.setProductCount(template.getProductCount())
.setUserId(userId);
if (GiftVoucherTemplateValidityTypeEnum.DATE.getType().equals(template.getValidityType())) {
giftVoucherDO.setValidStartTime(template.getValidStartTime());
giftVoucherDO.setValidEndTime(template.getValidEndTime());
} else if (GiftVoucherTemplateValidityTypeEnum.TERM.getType().equals(template.getValidityType())) {
giftVoucherDO.setValidStartTime(LocalDateTime.now().plusDays(template.getFixedStartTerm()));
giftVoucherDO.setValidEndTime(LocalDateTime.now().plusDays(template.getFixedEndTerm()));
}
return giftVoucherDO;
}
GiftVoucherPageReqVO convert(AppGiftVoucherPageReqVO pageReqVO, Collection<Long> userIds);
}
@@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* 提货劵 DO
*
* @author 小如之家
*/
@TableName("promotion_gift_voucher")
@KeySequence("promotion_gift_voucher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GiftVoucherDO extends BaseDO {
/**
* 提货劵编号
*/
@TableId
private Long id;
/**
* 提货劵模板编号
*/
private Long templateId;
/**
* 提货劵名
*/
private String name;
/**
* 提货劵状态
* <p>
* 1-未使用
* 2-已使用
* 3-已失效
*/
private Integer status;
/**
* 商品范围编号的数组
*
* 冗余 {@link GiftVoucherTemplateDO#getProductScopeValues()}
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> productScopeValues;
/**
* 可购买商品数量
*/
private Integer productCount;
/**
* 用户编号
*/
private Long userId;
/**
* 领取类型
* <p>
* 1 - 用户主动领取
* 2 - 后台自动发放
*/
private Integer takeType;
/**
* 生效开始时间
*/
private LocalDateTime validStartTime;
/**
* 生效结束时间
*/
private LocalDateTime validEndTime;
/**
* 使用订单号
*/
private Long useOrderId;
/**
* 使用时间
*/
private LocalDateTime useTime;
}
@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 提货券模板 DO
*
* @author 小如之家
*/
@TableName("promotion_gift_voucher_template")
@KeySequence("promotion_gift_voucher_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GiftVoucherTemplateDO extends BaseDO {
/**
* 模板编号,自增唯一。
*/
@TableId
private Long id;
/**
* 提货券名
*/
private String name;
/**
* 提货券描述
*/
private String description;
/**
* 状态
*/
private Integer status;
/**
* 发放数量, -1 - 则表示不限制
*/
private Integer totalCount;
/**
* 每人限领个数, -1 - 则表示不限制
*/
private Integer takeLimitCount;
/**
* 领取方式
*/
private Integer takeType;
/**
* 商品范围编号的数组
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> productScopeValues;
/**
* 生效日期类型
*/
private Integer validityType;
/**
* 固定日期-生效开始时间
*/
private LocalDateTime validStartTime;
/**
* 固定日期-生效结束时间
*/
private LocalDateTime validEndTime;
/**
* 领取日期-开始天数
*/
private Integer fixedStartTerm;
/**
* 领取日期-结束天数
*/
private Integer fixedEndTerm;
/**
* 领取提货券的数量
*/
private Integer takeCount;
/**
* 使用提货券的次数
*/
private Integer useCount;
/**
* 可购买商品数量
*/
private Integer productCount;
}
@@ -0,0 +1,93 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.giftvoucher;
import java.time.LocalDateTime;
import java.util.*;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.yulichang.toolkit.MPJWrappers;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* 提货劵 Mapper
*
* @author 小如之家
*/
@Mapper
public interface GiftVoucherMapper extends BaseMapperX<GiftVoucherDO> {
default PageResult<GiftVoucherDO> selectPage(GiftVoucherPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<GiftVoucherDO>()
.eqIfPresent(GiftVoucherDO::getTemplateId, reqVO.getTemplateId())
.likeIfPresent(GiftVoucherDO::getName, reqVO.getName())
.eqIfPresent(GiftVoucherDO::getStatus, reqVO.getStatus())
.eqIfPresent(GiftVoucherDO::getProductScopeValues, reqVO.getProductScopeValues())
.eqIfPresent(GiftVoucherDO::getUserId, reqVO.getUserId())
.eqIfPresent(GiftVoucherDO::getTakeType, reqVO.getTakeType())
.betweenIfPresent(GiftVoucherDO::getValidStartTime, reqVO.getValidStartTime())
.betweenIfPresent(GiftVoucherDO::getValidEndTime, reqVO.getValidEndTime())
.eqIfPresent(GiftVoucherDO::getUseOrderId, reqVO.getUseOrderId())
.betweenIfPresent(GiftVoucherDO::getUseTime, reqVO.getUseTime())
.betweenIfPresent(GiftVoucherDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(GiftVoucherDO::getProductCount, reqVO.getProductCount())
.orderByDesc(GiftVoucherDO::getId));
}
default List<GiftVoucherDO> selectListByTemplateIdAndUserIds(Long templateId, Set<Long> userIds){
return selectList(new LambdaQueryWrapperX<GiftVoucherDO>()
.eq(GiftVoucherDO::getTemplateId, templateId)
.in(GiftVoucherDO::getUserId, userIds)
);
}
default GiftVoucherDO getGiftVoucherByUserId(Long userId, Long id){
return selectOne(new LambdaQueryWrapperX<GiftVoucherDO>()
.eq(GiftVoucherDO::getUserId, userId)
.eq(GiftVoucherDO::getId, id));
}
default Long selectCountByUserIdAndStatus(Long userId, Integer status){
return selectCount(new LambdaQueryWrapperX<GiftVoucherDO>()
.eq(GiftVoucherDO::getUserId, userId)
.eq(GiftVoucherDO::getStatus, status));
}
default Map<Long, Integer> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
String templateIdAlias = "templateId";
String countAlias = "count";
List<Map<String, Object>> list = selectMaps(MPJWrappers.lambdaJoin(GiftVoucherDO.class)
.selectAs(GiftVoucherDO::getTemplateId, templateIdAlias)
.selectCount(GiftVoucherDO::getId, countAlias)
.eq(GiftVoucherDO::getUserId, userId)
.in(GiftVoucherDO::getTemplateId, templateIds)
.groupBy(GiftVoucherDO::getTemplateId));
return convertMap(list, map -> MapUtil.getLong(map, templateIdAlias), map -> MapUtil.getInt(map, countAlias));
}
default GiftVoucherDO selectByTemplateIdAndUserId(Long id, Long userId){
return selectOne(new LambdaQueryWrapperX<GiftVoucherDO>()
.eq(GiftVoucherDO::getId, id)
.eq(GiftVoucherDO::getUserId, userId));
}
default int updateByIdAndStatus(Long id, Integer status, GiftVoucherDO giftVoucherDO){
return update(giftVoucherDO, new LambdaUpdateWrapper<GiftVoucherDO>()
.eq(GiftVoucherDO::getId, id).eq(GiftVoucherDO::getStatus, status));
}
default List<GiftVoucherDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime){
return selectList(new LambdaQueryWrapperX<GiftVoucherDO>()
.eq(GiftVoucherDO::getStatus, status)
.le(GiftVoucherDO::getValidEndTime, validEndTime)
);
}
}
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.giftvoucher;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 提货券模板 Mapper
*
* @author 小如之家
*/
@Mapper
public interface GiftVoucherTemplateMapper extends BaseMapperX<GiftVoucherTemplateDO> {
default PageResult<GiftVoucherTemplateDO> selectPage(GiftVoucherTemplatePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<GiftVoucherTemplateDO>()
.likeIfPresent(GiftVoucherTemplateDO::getName, reqVO.getName())
.eqIfPresent(GiftVoucherTemplateDO::getDescription, reqVO.getDescription())
.eqIfPresent(GiftVoucherTemplateDO::getStatus, reqVO.getStatus())
.eqIfPresent(GiftVoucherTemplateDO::getTotalCount, reqVO.getTotalCount())
.eqIfPresent(GiftVoucherTemplateDO::getTakeLimitCount, reqVO.getTakeLimitCount())
.eqIfPresent(GiftVoucherTemplateDO::getTakeType, reqVO.getTakeType())
.eqIfPresent(GiftVoucherTemplateDO::getProductScopeValues, reqVO.getProductScopeValues())
.eqIfPresent(GiftVoucherTemplateDO::getValidityType, reqVO.getValidityType())
.betweenIfPresent(GiftVoucherTemplateDO::getValidStartTime, reqVO.getValidStartTime())
.betweenIfPresent(GiftVoucherTemplateDO::getValidEndTime, reqVO.getValidEndTime())
.eqIfPresent(GiftVoucherTemplateDO::getFixedStartTerm, reqVO.getFixedStartTerm())
.eqIfPresent(GiftVoucherTemplateDO::getFixedEndTerm, reqVO.getFixedEndTerm())
.betweenIfPresent(GiftVoucherTemplateDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(GiftVoucherTemplateDO::getTakeCount, reqVO.getTakeCount())
.eqIfPresent(GiftVoucherTemplateDO::getUseCount, reqVO.getUseCount())
.eqIfPresent(GiftVoucherTemplateDO::getProductCount, reqVO.getProductCount())
.orderByDesc(GiftVoucherTemplateDO::getId));
}
default void updateTakeCount(Long id, int incrCount){
update(null, new LambdaUpdateWrapper<GiftVoucherTemplateDO>()
.eq(GiftVoucherTemplateDO::getId, id)
.setSql("take_count = take_count + " + incrCount));
}
}
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.promotion.job.giftvoucher;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.promotion.service.giftvoucher.GiftVoucherService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 提货券过期 Job
*
* @author owen
*/
@Component
public class GiftVoucherExpireJob implements JobHandler {
@Resource
private GiftVoucherService giftVoucherService;
@Override
@TenantJob
public String execute(String param) {
int count = giftVoucherService.expireGiftVoucher();
return StrUtil.format("过期提货券 {} 个", count);
}
}
@@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.promotion.service.giftvoucher;
import java.util.*;
import javax.validation.*;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherUseReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.*;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherSaveReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherTypeEnum;
/**
* 提货劵 Service 接口
*
* @author 小如之家
*/
public interface GiftVoucherService {
/**
* 创建提货劵
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createGiftVoucher(@Valid GiftVoucherSaveReqVO createReqVO);
/**
* 更新提货劵
*
* @param updateReqVO 更新信息
*/
void updateGiftVoucher(@Valid GiftVoucherSaveReqVO updateReqVO);
/**
* 删除提货劵
*
* @param id 编号
*/
void deleteGiftVoucher(Long id);
/**
* 获得提货劵
*
* @param id 编号
* @return 提货劵
*/
GiftVoucherDO getGiftVoucher(Long id);
/**
* 获得提货劵分页
*
* @param pageReqVO 分页查询
* @return 提货劵分页
*/
PageResult<GiftVoucherDO> getGiftVoucherPage(GiftVoucherPageReqVO pageReqVO);
/**
* 领取优惠券
*
* @param templateId 提货券模板编号
* @param userIds 用户编号列表
* @param takeType 领取方式
* @return key: userId, value: 优惠券编号列表
*/
Map<Long, List<Long>> takeGiftVoucher(Long templateId, Set<Long> userIds, GiftVoucherTypeEnum takeType);
default Map<Long, List<Long>> takeGiftVoucherByAdmin(Long templateId, Set<Long> userIds){
return takeGiftVoucher(templateId, userIds, GiftVoucherTypeEnum.ADMIN);
}
GiftVoucherDO getGiftVoucherByUserId(Long userId, Long id);
Long getUnusedCouponCount(Long userId);
/**
* 获取会员领取指定提货券的数量
*
* @param templateId 提货券模板编号
* @param userId 用户编号
* @return 领取提货券的数量
*/
default Integer getTakeCount(Long templateId, Long userId) {
Map<Long, Integer> map = getTakeCountMapByTemplateIds(Collections.singleton(templateId), userId);
return MapUtil.getInt(map, templateId, 0);
}
Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId);
void useGiftVoucher(GiftVoucherUseReqDTO giftVoucherUseReqDTO);
int expireGiftVoucher();
}
@@ -0,0 +1,245 @@
package cn.iocoder.yudao.module.promotion.service.giftvoucher;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherUseReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.giftvoucher.GiftVoucherSaveReqVO;
import cn.iocoder.yudao.module.promotion.convert.giftvoucher.GiftVoucherConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherStatusEnum;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherTemplateValidityTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.giftvoucher.GiftVoucherTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.*;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.dal.mysql.giftvoucher.GiftVoucherMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
/**
* 提货劵 Service 实现类
*
* @author 小如之家
*/
@Service
@Validated
@Slf4j
public class GiftVoucherServiceImpl implements GiftVoucherService {
@Resource
private GiftVoucherMapper giftVoucherMapper;
@Resource
private GiftVoucherTemplateService giftVoucherTemplateService;
@Override
public Long createGiftVoucher(GiftVoucherSaveReqVO createReqVO) {
// 插入
GiftVoucherDO giftVoucher = BeanUtils.toBean(createReqVO, GiftVoucherDO.class);
giftVoucherMapper.insert(giftVoucher);
// 返回
return giftVoucher.getId();
}
@Override
public void updateGiftVoucher(GiftVoucherSaveReqVO updateReqVO) {
// 校验存在
validateGiftVoucherExists(updateReqVO.getId());
// 更新
GiftVoucherDO updateObj = BeanUtils.toBean(updateReqVO, GiftVoucherDO.class);
giftVoucherMapper.updateById(updateObj);
}
@Override
public void deleteGiftVoucher(Long id) {
// 校验存在
validateGiftVoucherExists(id);
// 删除
giftVoucherMapper.deleteById(id);
}
private void validateGiftVoucherExists(Long id) {
if (giftVoucherMapper.selectById(id) == null) {
throw exception(GIFT_VOUCHER_NOT_EXISTS);
}
}
@Override
public GiftVoucherDO getGiftVoucher(Long id) {
return giftVoucherMapper.selectById(id);
}
@Override
public PageResult<GiftVoucherDO> getGiftVoucherPage(GiftVoucherPageReqVO pageReqVO) {
return giftVoucherMapper.selectPage(pageReqVO);
}
@Override
public Map<Long, List<Long>> takeGiftVoucher(Long templateId, Set<Long> userIds, GiftVoucherTypeEnum takeType) {
GiftVoucherTemplateDO template = giftVoucherTemplateService.getGiftVoucherTemplate(templateId);
// 1. 过滤掉达到领取限制的用户
removeTakeLimitUser(userIds, template);
// 2. 校验提货劵是否可以领取
validateGiftVoucherTemplateCanTake(template, userIds, takeType);
// 3. 批量保存优惠劵
List<GiftVoucherDO> giftVoucherList = convertList(userIds, userId -> GiftVoucherConvert.INSTANCE.convert(template, userId));
giftVoucherMapper.insertBatch(giftVoucherList);
// 4. 增加优惠劵模板的领取数量
giftVoucherTemplateService.updateGiftVoucherTemplateTakeCount(templateId, userIds.size());
return convertMultiMap(giftVoucherList, GiftVoucherDO::getUserId, GiftVoucherDO::getId);
}
@Override
public GiftVoucherDO getGiftVoucherByUserId(Long userId, Long id) {
return giftVoucherMapper.getGiftVoucherByUserId(userId, id);
}
@Override
public Long getUnusedCouponCount(Long userId) {
return giftVoucherMapper.selectCountByUserIdAndStatus(userId, GiftVoucherStatusEnum.UNUSED.getStatus());
}
@Override
public Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
if (CollUtil.isEmpty(templateIds)) {
return Collections.emptyMap();
}
return giftVoucherMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
}
@Override
public void useGiftVoucher(GiftVoucherUseReqDTO giftVoucherUseReqDTO) {
// 校验状态
GiftVoucherDO giftVoucher = giftVoucherMapper.selectByTemplateIdAndUserId(giftVoucherUseReqDTO.getId(), giftVoucherUseReqDTO.getUserId());
if (ObjectUtil.notEqual(giftVoucher.getStatus(), GiftVoucherStatusEnum.UNUSED.getStatus())) {
throw exception(GIFT_VOUCHER_STATUS_NOT_UNUSED);
}
// 校验有效期;为避免定时器没跑,实际优惠劵已经过期
if (!LocalDateTimeUtils.isBetween(giftVoucher.getValidStartTime(), giftVoucher.getValidEndTime())) {
throw exception(COUPON_VALID_TIME_NOT_NOW);
}
// 更新状态
int updateCount = giftVoucherMapper.updateByIdAndStatus(giftVoucherUseReqDTO.getId(), GiftVoucherStatusEnum.UNUSED.getStatus(),
new GiftVoucherDO().setStatus(GiftVoucherStatusEnum.USED.getStatus())
.setUseOrderId(giftVoucherUseReqDTO.getOrderId()).setUseTime(LocalDateTime.now()));
if (updateCount == 0) {
throw exception(GIFT_VOUCHER_STATUS_NOT_UNUSED);
}
}
@Override
public int expireGiftVoucher() {
// 1. 查询待过期的优惠券
List<GiftVoucherDO> list = giftVoucherMapper.selectListByStatusAndValidEndTimeLe(
GiftVoucherStatusEnum.UNUSED.getStatus(), LocalDateTime.now());
if (CollUtil.isEmpty(list)) {
return 0;
}
// 2. 遍历执行
int count = 0;
for (GiftVoucherDO giftVoucher : list) {
try {
boolean success = expireGiftVoucherOne(giftVoucher);
if (success) {
count++;
}
} catch (Exception e) {
log.error("[expireCoupon][giftVoucher({}) 更新为已过期失败]", giftVoucher.getId(), e);
}
}
return count;
}
/**
* 过期单个提货劵
*
* @param giftVoucher 提货劵
* @return 是否过期成功
*/
private boolean expireGiftVoucherOne(GiftVoucherDO giftVoucher) {
// 更新记录状态
int updateRows = giftVoucherMapper.updateByIdAndStatus(giftVoucher.getId(), GiftVoucherStatusEnum.UNUSED.getStatus(),
new GiftVoucherDO().setStatus(GiftVoucherStatusEnum.EXPIRE.getStatus()));
if (updateRows == 0) {
log.error("[expireCoupon][coupon({}) 更新为已过期失败]", giftVoucher.getId());
return false;
}
log.info("[expireCoupon][coupon({}) 更新为已过期成功]", giftVoucher.getId());
return true;
}
/**
* 过滤掉达到领取限制的用户
*
* @param userIds
* @param template
*/
private void removeTakeLimitUser(Set<Long> userIds, GiftVoucherTemplateDO template) {
if (template.getTakeLimitCount() <= 0) {
return;
}
// 查询已领过券的用户
List<GiftVoucherDO> alreadyTakeCoupons = giftVoucherMapper.selectListByTemplateIdAndUserIds(template.getId(), userIds);
if (CollUtil.isEmpty(alreadyTakeCoupons)) {
return;
}
// 移除达到领取限制的用户
Map<Long, Integer> userTakeCountMap = CollStreamUtil.groupBy(alreadyTakeCoupons, GiftVoucherDO::getUserId, Collectors.summingInt(c -> 1));
userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= template.getTakeLimitCount());
}
private void validateGiftVoucherTemplateCanTake(GiftVoucherTemplateDO template, Set<Long> userIds, GiftVoucherTypeEnum takeType) {
// 如果所有用户都领取过,则抛出异常
if (CollUtil.isEmpty(userIds)) {
throw exception(GIFT_VOUCHER_TEMPLATE_USER_ALREADY_TAKE);
}
// 校验模板
if (template == null) {
throw exception(GIFT_VOUCHER_TEMPLATE_NOT_EXISTS);
}
// 校验剩余数量
if (template.getTotalCount()>=0) {
if (template.getTakeCount() + userIds.size() > template.getTotalCount()) {
throw exception(GIFT_VOUCHER_TEMPLATE_NOT_ENOUGH);
}
}
// 校验"固定日期"的有效期类型是否过期
if (GiftVoucherTemplateValidityTypeEnum.DATE.getType().equals(template.getValidityType())) {
if (LocalDateTimeUtils.beforeNow(template.getValidEndTime())) {
throw exception(GIFT_VOUCHER_TEMPLATE_EXPIRED);
}
}
// 校验领取方式
if (ObjectUtil.notEqual(template.getTakeType(), takeType.getType())) {
throw exception(GIFT_VOUCHER_TEMPLATE_CANNOT_TAKE);
}
}
}
@@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.promotion.service.giftvoucher;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplateSaveReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import javax.validation.Valid;
/**
* 提货券模板 Service 接口
*
* @author 小如之家
*/
public interface GiftVoucherTemplateService {
/**
* 创建提货券模板
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createGiftVoucherTemplate(@Valid GiftVoucherTemplateSaveReqVO createReqVO);
/**
* 更新提货券模板
*
* @param updateReqVO 更新信息
*/
void updateGiftVoucherTemplate(@Valid GiftVoucherTemplateSaveReqVO updateReqVO);
/**
* 删除提货券模板
*
* @param id 编号
*/
void deleteGiftVoucherTemplate(Long id);
/**
* 获得提货券模板
*
* @param id 编号
* @return 提货券模板
*/
GiftVoucherTemplateDO getGiftVoucherTemplate(Long id);
/**
* 获得提货券模板分页
*
* @param pageReqVO 分页查询
* @return 提货券模板分页
*/
PageResult<GiftVoucherTemplateDO> getGiftVoucherTemplatePage(GiftVoucherTemplatePageReqVO pageReqVO);
void updateGiftVoucherTemplateTakeCount(Long id, int incrCount);
}
@@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.promotion.service.giftvoucher;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplatePageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.giftvoucher.vo.template.GiftVoucherTemplateSaveReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.giftvoucher.GiftVoucherTemplateDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.giftvoucher.GiftVoucherTemplateMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.GIFT_VOUCHER_TEMPLATE_NOT_EXISTS;
/**
* 提货券模板 Service 实现类
*
* @author 小如之家
*/
@Service
@Validated
public class GiftVoucherTemplateServiceImpl implements GiftVoucherTemplateService {
@Resource
private GiftVoucherTemplateMapper giftVoucherTemplateMapper;
@Override
public Long createGiftVoucherTemplate(GiftVoucherTemplateSaveReqVO createReqVO) {
// 插入
GiftVoucherTemplateDO giftVoucherTemplate = BeanUtils.toBean(createReqVO, GiftVoucherTemplateDO.class);
giftVoucherTemplateMapper.insert(giftVoucherTemplate);
// 返回
return giftVoucherTemplate.getId();
}
@Override
public void updateGiftVoucherTemplate(GiftVoucherTemplateSaveReqVO updateReqVO) {
// 校验存在
validateGiftVoucherTemplateExists(updateReqVO.getId());
// 更新
GiftVoucherTemplateDO updateObj = BeanUtils.toBean(updateReqVO, GiftVoucherTemplateDO.class);
giftVoucherTemplateMapper.updateById(updateObj);
}
@Override
public void deleteGiftVoucherTemplate(Long id) {
// 校验存在
validateGiftVoucherTemplateExists(id);
// 删除
giftVoucherTemplateMapper.deleteById(id);
}
private void validateGiftVoucherTemplateExists(Long id) {
if (giftVoucherTemplateMapper.selectById(id) == null) {
throw exception(GIFT_VOUCHER_TEMPLATE_NOT_EXISTS);
}
}
@Override
public GiftVoucherTemplateDO getGiftVoucherTemplate(Long id) {
return giftVoucherTemplateMapper.selectById(id);
}
@Override
public PageResult<GiftVoucherTemplateDO> getGiftVoucherTemplatePage(GiftVoucherTemplatePageReqVO pageReqVO) {
return giftVoucherTemplateMapper.selectPage(pageReqVO);
}
@Override
public void updateGiftVoucherTemplateTakeCount(Long id, int incrCount) {
giftVoucherTemplateMapper.updateTakeCount(id, incrCount);
}
}
@@ -104,4 +104,11 @@ public interface ErrorCodeConstants {
ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, "提现金额不能低于 {} 元");
ErrorCode BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH = new ErrorCode(1_011_008_003, "您当前最多可提现 {} 元");
// ========== 提货券 模块 1-011-009-000 ==========
ErrorCode BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE = new ErrorCode(1_011_009_000, "该提货劵无法使用,原因:{}」");
ErrorCode BUILD_ORDER_GIFT_VOUCHER_SKU_NOT_EXISTS = new ErrorCode(1_011_009_001, "商品 SKU 不存在");
ErrorCode BUILD_ORDER_GIFT_VOUCHER_STOCK_NOT_ENOUGH = new ErrorCode(1_011_009_002, "商品 SKU 库存不足");
ErrorCode BUILD_ORDER_GIFT_VOUCHER_SPU_NOT_EXISTS = new ErrorCode(1_011_009_003, "商品 SPU 不存在");
ErrorCode BUILD_ORDER_GIFT_VOUCHER_SPU_NOT_ENABLE = new ErrorCode(1_011_009_004, "商品 SPU【{}】不处于上架状态");
}
@@ -21,6 +21,7 @@ public enum TradeOrderTypeEnum implements ArrayValuable<Integer> {
BARGAIN(2, "砍价订单"),
COMBINATION(3, "拼团订单"),
POINT(4, "积分商城"),
GIFTVOUCHER(5, "提货券订单"),
;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderTypeEnum::getType).toArray(Integer[]::new);
@@ -87,6 +87,13 @@ public class AppTradeOrderController {
return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));
}
@PostMapping("/create-giftvoucher")
@Operation(summary = "创建提货码订单")
public CommonResult<Long> createGiftVoucherOrder(@Valid @RequestBody AppGiftVoucherOrderSettlementReqVO createReqVO) {
TradeOrderDO order = tradeOrderUpdateService.createGiftVoucherOrder(getLoginUserId(), createReqVO);
return success(order.getId());
}
@PostMapping("/update-paid")
@Operation(summary = "更新订单为已支付") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
@PermitAll
@@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "用户 App - 交易订单结算 Request VO")
@Data
@Valid
public class AppGiftVoucherOrderSettlementReqVO {
@Schema(description = "商品项数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "商品不能为空")
private List<Item> items;
@Schema(description = "提货劵编号", example = "1024")
private Long giftVoucherId;
// ========== 配送相关相关字段 ==========
@Schema(description = "配送方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确")
private Integer deliveryType;
@Schema(description = "收件地址编号", example = "1")
private Long addressId;
@Schema(description = "自提门店编号", example = "1088")
private Long pickUpStoreId;
@Schema(description = "收件人名称", example = "芋艿") // 选择门店自提时,该字段为联系人名
private String receiverName;
@Schema(description = "收件人手机", example = "15601691300") // 选择门店自提时,该字段为联系人手机
@Mobile(message = "收件人手机格式不正确")
private String receiverMobile;
@Schema(description = "备注", example = "这个是我的订单哟")
private String remark;
@AssertTrue(message = "配送方式不能为空")
@JsonIgnore
public boolean isDeliveryTypeNotNull() {
return deliveryType != null;
}
@AssertTrue(message = "提货商品每次只能购买一种规格")
@JsonIgnore
public boolean isValidActivityItems() {
// 校验订单项是否超出
return items.size() == 1;
}
@Data
@Schema(description = "用户 App - 商品项")
@Valid
public static class Item {
@Schema(description = "商品 SKU 编号", example = "2048")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@Schema(description = "购买数量", example = "1")
@Min(value = 1, message = "购买数量最小值为 {value}")
private Integer count;
@AssertTrue(message = "商品不正确")
@JsonIgnore
public boolean isValid() {
// 组合一:skuId + count 使用商品 SKU
return skuId != null && count != null;
}
}
}
@@ -34,6 +34,7 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEn
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.GiftVoucherCalculateRespBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.mapstruct.Mapper;
@@ -286,4 +287,18 @@ public interface TradeOrderConvert {
})
CombinationRecordCreateReqDTO convert(TradeOrderDO order, TradeOrderItemDO item);
default List<TradeOrderItemDO> convertGiftVoucherOrderList(TradeOrderDO order, GiftVoucherCalculateRespBO calculateRespBO){
return CollectionUtils.convertList(calculateRespBO.getItems(), item -> {
TradeOrderItemDO orderItem = convert(item);
orderItem.setUserId(order.getUserId());
orderItem.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
orderItem.setCommentStatus(false);
return orderItem;
});
}
TradeOrderItemDO convert(GiftVoucherCalculateRespBO.OrderItem item);
TradeOrderDO convert(Long userId, GiftVoucherCalculateRespBO calculateRespBO);
}
@@ -360,4 +360,10 @@ public class TradeOrderDO extends BaseDO {
*/
private Long pointActivityId;
/**
* 提货劵编号
*/
private Long giftVoucherId;
}
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliver
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppGiftVoucherOrderSettlementReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
@@ -220,4 +221,5 @@ public interface TradeOrderUpdateService {
*/
void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds);
TradeOrderDO createGiftVoucherOrder(Long userId, AppGiftVoucherOrderSettlementReqVO createReqVO);
}
@@ -12,6 +12,7 @@ import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -27,13 +28,18 @@ import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.sku.ProductSkuPurchaseBizTypeEnum;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.GiftVoucherApi;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherRespDTO;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppGiftVoucherOrderSettlementReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
@@ -57,6 +63,7 @@ import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;
import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
import cn.iocoder.yudao.module.trade.service.order.bo.GiftVoucherCalculateRespBO;
import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
@@ -84,6 +91,7 @@ import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.min
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BUILD_ORDER_GIFT_VOUCHER_SPU_NOT_ENABLE;
import static cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants.WXA_ORDER_DELIVERY;
/**
@@ -129,10 +137,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
public PayRefundApi payRefundApi;
@Resource
public ProductSkuApi productSkuApi;
@Resource
public ProductSpuApi productSpuApi;
@Resource
private TradeOrderProperties tradeOrderProperties;
@Resource
private GiftVoucherApi giftVoucherApi;
// =================== Order ===================
public List<TradeOrderItemDO> findUserSuccessOrderItem(Long payUserId, List<Long> skuIdList, ProductSkuPurchaseBizTypeEnum status) {
@@ -987,6 +1000,130 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
tradeOrderMapper.updateById(new TradeOrderDO().setId(orderId).setGiveCouponIds(giveCouponIds));
}
@Override
public TradeOrderDO createGiftVoucherOrder(Long userId, AppGiftVoucherOrderSettlementReqVO createReqVO) {
// 1 构建订单
GiftVoucherCalculateRespBO giftVoucherCalculateRespBO=giftVoucherCalculate(userId, createReqVO);
TradeOrderDO order = buildGiftVoucherOrder(userId, giftVoucherCalculateRespBO);
List<TradeOrderItemDO> orderItems = buildGiftVoucherOrderItems(order,giftVoucherCalculateRespBO);
// 2. 订单创建前的逻辑
tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));
// 3. 保存订单
tradeOrderMapper.insert(order);
orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId()));
tradeOrderItemMapper.insertBatch(orderItems);
// 4. 订单创建后的逻辑
tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));
// 5. 插入订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
return order;
}
private TradeOrderDO buildGiftVoucherOrder(Long userId, GiftVoucherCalculateRespBO calculateRespBO) {
return TradeOrderConvert.INSTANCE.convert(userId,calculateRespBO);
}
private List<TradeOrderItemDO> buildGiftVoucherOrderItems(TradeOrderDO order, GiftVoucherCalculateRespBO calculateRespBO) {
return TradeOrderConvert.INSTANCE.convertGiftVoucherOrderList(order,calculateRespBO);
}
private GiftVoucherCalculateRespBO giftVoucherCalculate(Long userId, AppGiftVoucherOrderSettlementReqVO createReqVO) {
//看提货券是否可用
GiftVoucherRespDTO giftVoucher=giftVoucherApi.getGiftVoucherById(createReqVO.getGiftVoucherId());
if (null == giftVoucher) {
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE, "提货劵不存在");
}
if (giftVoucher.getStatus()!=1){
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE, "提货劵已使用或已失效");
}
if (LocalDateTimeUtils.beforeNow(giftVoucher.getValidEndTime())) {
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE,"提货券已过期");
}
if (LocalDateTimeUtils.afterNow(giftVoucher.getValidStartTime())) {
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE,"提货券未生效");
}
if (!Objects.equals(createReqVO.getItems().get(0).getCount(), giftVoucher.getProductCount())){
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE,"购买商品数量与提货券可购买数量不一致");
}
if (!Objects.equals(userId, giftVoucher.getUserId())){
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE,"当前用户与提货券用户不一致");
}
if (createReqVO.getItems().size()>1){
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE,"提货券只允许购买一种商品");
}
//看商品是否可用
ProductSkuRespDTO sku = productSkuApi.getSku(createReqVO.getItems().get(0).getSkuId());
if (null == sku) {
throw exception(BUILD_ORDER_GIFT_VOUCHER_SKU_NOT_EXISTS);
}
if(sku.getStock()<createReqVO.getItems().get(0).getCount()){
throw exception(BUILD_ORDER_GIFT_VOUCHER_STOCK_NOT_ENOUGH);
}
ProductSpuRespDTO spu = productSpuApi.getSpu(sku.getSpuId());
if (null == spu) {
throw exception(BUILD_ORDER_GIFT_VOUCHER_SPU_NOT_EXISTS);
}
//提货券的商品范围是否包含购买的商品
if (!giftVoucher.getProductScopeValues().contains(createReqVO.getItems().get(0).getSkuId())){
throw exception(BUILD_ORDER_GIFT_VOUCHER_CAN_NOT_USE,"该提货券无法购买此商品");
}
if (spu.getStatus()!=1){
throw exception(BUILD_ORDER_GIFT_VOUCHER_SPU_NOT_ENABLE,spu.getName());
}
GiftVoucherCalculateRespBO order = new GiftVoucherCalculateRespBO();
order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
order.setType(TradeOrderTypeEnum.GIFTVOUCHER.getType());
order.setUserIp(getClientIP()).setTerminal(getTerminal()).setUserId(userId);
order.setUserRemark(createReqVO.getRemark());
//待发货
order.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
order.setProductCount(createReqVO.getItems().get(0).getCount());
order.setPayStatus(true).setPayTime(LocalDateTime.now());
order.setTotalPrice(sku.getPrice()*createReqVO.getItems().get(0).getCount());
order.setDeliveryType(createReqVO.getDeliveryType());
if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId);
Assert.notNull(address, "地址({}) 不能为空", createReqVO.getAddressId()); // 价格计算时,已经计算
order.setReceiverName(address.getName()).setReceiverMobile(address.getMobile())
.setReceiverAreaId(address.getAreaId()).setReceiverDetailAddress(address.getDetailAddress());
} else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.PICK_UP.getType())) {
order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile());
order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码,长度为 8 位
}
order.setPickUpStoreId(createReqVO.getPickUpStoreId());
order.setGiftVoucherId(createReqVO.getGiftVoucherId());
//子项
GiftVoucherCalculateRespBO.OrderItem orderItem = new GiftVoucherCalculateRespBO.OrderItem();
orderItem.setSpuId(sku.getSpuId()).setSkuId(sku.getId())
.setCount(createReqVO.getItems().get(0).getCount());
// sku 价格
orderItem.setPrice(sku.getPrice()).setPayPrice(0)
.setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0).setVipPrice(0);
// sku 信息
orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties())
.setWeight(sku.getWeight()).setVolume(sku.getVolume());
// spu 信息
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId())
.setDeliveryTypes(spu.getDeliveryTypes()).setDeliveryTemplateId(spu.getDeliveryTemplateId())
.setGivePoint(spu.getGiveIntegral()).setUsePoint(0);
if (StrUtil.isBlank(orderItem.getPicUrl())) {
orderItem.setPicUrl(spu.getPicUrl());
}
if(order.getItems()==null){
order.setItems(new ArrayList<>()); // 初始化 items
}
order.getItems().add(orderItem);
return order;
}
/**
* 创建单个订单的评论
*
@@ -0,0 +1,156 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import lombok.Data;
import java.util.List;
/**
* 提货券构建订单 Response BO
*
* @author 芋道源码
*/
@Data
public class GiftVoucherCalculateRespBO extends TradeOrderDO {
/**
* 订单项数组
*/
private List<OrderItem> items;
/**
* 订单商品 SKU
*/
@Data
public static class OrderItem {
/**
* SPU 编号
*/
private Long spuId;
/**
* SKU 编号
*/
private Long skuId;
/**
* 购买数量
*/
private Integer count;
/**
* 购物车项的编号
*/
private Long cartId;
/**
* 是否选中
*/
private Boolean selected;
/**
* 商品原价(单),单位:分
*
* 对应 ProductSkuDO 的 price 字段
* 对应 taobao 的 order.price 字段
*/
private Integer price;
/**
* 优惠金额(总),单位:分
*
* 对应 taobao 的 order.discount_fee 字段
*/
private Integer discountPrice;
/**
* 运费金额(总),单位:分
*/
private Integer deliveryPrice;
/**
* 优惠劵减免金额,单位:分
*
* 对应 taobao 的 trade.coupon_fee 字段
*/
private Integer couponPrice;
/**
* 积分抵扣的金额,单位:分
*
* 对应 taobao 的 trade.point_fee 字段
*/
private Integer pointPrice;
/**
* 使用的积分
*/
private Integer usePoint;
/**
* VIP 减免金额,单位:分
*/
private Integer vipPrice;
/**
* 应付金额(总),单位:分
*
* = {@link #price} * {@link #count}
* - {@link #couponPrice}
* - {@link #pointPrice}
* - {@link #discountPrice}
* + {@link #deliveryPrice}
* - {@link #vipPrice}
*/
private Integer payPrice;
// ========== 商品 SPU 信息 ==========
/**
* 商品名
*/
private String spuName;
/**
* 商品图片
*
* 优先级:SKU.picUrl > SPU.picUrl
*/
private String picUrl;
/**
* 分类编号
*/
private Long categoryId;
// ========== 物流相关字段 =========
/**
* 配送方式数组
*
* 对应 DeliveryTypeEnum 枚举
*/
private List<Integer> deliveryTypes;
/**
* 物流配置模板编号
*
* 对应 TradeDeliveryExpressTemplateDO 的 id 编号
*/
private Long deliveryTemplateId;
// ========== 商品 SKU 信息 ==========
/**
* 商品重量,单位:kg 千克
*/
private Double weight;
/**
* 商品体积,单位:m^3 平米
*/
private Double volume;
/**
* 商品属性数组
*/
private List<ProductPropertyValueDetailRespDTO> properties;
/**
* 赠送的积分
*/
private Integer givePoint;
}
}
@@ -32,7 +32,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_RECEIVE_FAIL_STATUS_NOT_OVERPAY;
/**
* 优惠劵的 {@link TradeOrderHandler} 实现类
* 贩卖机出货的 {@link TradeOrderHandler} 实现类
*
* @author 芋道源码
*/
@@ -182,6 +182,7 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler {
}
}else {
System.out.println("提货柜订单spu列表为空");
log.error("订单:{},提货柜订单spu列表为空",order.getNo());
}
}
@@ -0,0 +1,190 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuTypesEnum;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.GiftVoucherApi;
import cn.iocoder.yudao.module.promotion.api.giftvoucher.dto.GiftVoucherUseReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseRequestBo;
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseResponseBo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_RECEIVE_FAIL_STATUS_NOT_OVERPAY;
/**
* 提货劵的 {@link TradeOrderHandler} 实现类
*
* @author 芋道源码
*/
@Component
@Slf4j
public class TradeGiftVoucherOrderHandler implements TradeOrderHandler {
@Resource
private GiftVoucherApi giftVoucherApi;
@Resource
private ProductSpuApi productSpuApi;
@Resource
private ProductSkuApi productSkuApi;
@Resource
private AdminUserApi adminUserApi;
@Resource
private TradeOrderMapper tradeOrderMapper;
// TOKEN
@Value("${xr.cabinet.token}")
private String token;
//API_URL
@Value("${xr.cabinet.api_url}")
private String api_url;
@Override
public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (order.getGiftVoucherId() == null || order.getGiftVoucherId() <= 0) {
return;
}
//提货券商品的贩卖机出货
//商品SPU ID列表
List<Long> spuIds = orderItems.stream()
.map(TradeOrderItemDO::getSpuId)
.distinct()
.collect(Collectors.toList());
// if(null==spuIds && spuIds.size()<=0){
// System.out.println("商品SPU ID列表 为空");
// return;
// }
//查询spu表
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
//判断商品类型是否有不是提货柜商品的
if (CollUtil.isNotEmpty(spuList)) {
boolean hasNonDevice = spuList.stream()
.anyMatch(spu -> !ProductSpuTypesEnum.ENABLE.getTypes().equals(spu.getTypes()));
if (hasNonDevice) {
//订单中包含非提货柜商品
log.info("订单中包含非提货柜商品");
}else {
//判断是不是有不同机器的订单(一个创建人是一台机器)
boolean isSingleCreator = spuList.stream()
.map(ProductSpuRespDTO::getCreator) //获取每个商品的Creator
.distinct() //去重Creator
.count() == 1; // 判断是否只有一个唯一的创建人
if (isSingleCreator) {
// 只有一个Creator的逻辑处理
System.out.println("订单明细中只有一个商品创建人");
//查询商品创建人的设备IMEI
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(spuList.get(0).getCreator()));
if (user == null|| user.getDeviceId() == null){
return;
}
//获取商品通道
List<Long> skuIds = orderItems.stream()
.map(TradeOrderItemDO::getSkuId)
.distinct()
.collect(Collectors.toList());
if(CollUtil.isEmpty(skuIds)){
return;
}
List<ProductSkuRespDTO> skuList = productSkuApi.getSkuList(skuIds);
//判断是不是有不同通道类型的订单
boolean isSingleCabinetType = skuList.stream()
.map(ProductSkuRespDTO::getCabinetType) //获取每个商品的Creator
.distinct() //去重Creator
.count() == 1; // 判断是否只有一个唯一的创建人
if (isSingleCabinetType) {
DispenseRequestBo request = new DispenseRequestBo();
request.setDeviceId(user.getDeviceId());
StringBuilder message= new StringBuilder(skuList.get(0).getCabinetType() + ",");
//遍历订单明细
for (int i = 0; i < orderItems.size(); i++) {
TradeOrderItemDO orderItem = orderItems.get(i);
ProductSkuRespDTO sku = productSkuApi.getSku(orderItem.getSkuId());
message.append(sku.getCabinetChannel()).append("&").append(orderItem.getCount());
if (i != orderItems.size() - 1) {
message.append(",");
}
}
request.setMessage(message.toString());
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", token);
// 创建请求实体
HttpEntity<DispenseRequestBo> requestEntity = new HttpEntity<>(request, headers);
// 创建RestTemplate
RestTemplate restTemplate = new RestTemplate();
try {
// 发送请求并解析响应
ResponseEntity<DispenseResponseBo> response = restTemplate.exchange(
api_url,
HttpMethod.POST,
requestEntity,
DispenseResponseBo.class
);
DispenseResponseBo body = response.getBody();
if (body != null) {
System.out.println("响应结果: " + body);
if (body.getCode() == 0) {
System.out.println("出货成功!详情: " + body.getData());
} else {
System.out.println("操作失败!原因: " + body.getMsg());
log.error("订单--{}:[出货失败]:原因: {}",order.getNo(), body.getMsg());
}
}
// 3. 更新 TradeOrderDO 状态为已完成
TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(order.getId());
int updateCount = tradeOrderMapper.updateByIdAndStatus(tradeOrderDO.getId(), tradeOrderDO.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now()));
if (updateCount == 0) {
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_OVERPAY);
}
} catch (Exception e) {
System.err.println("请求异常: " + e.getMessage());
log.error("订单--{}:[出货异常]:原因: {}",order.getNo(), e.getMessage());
}
}else {
System.out.println("订单明细中包含多个通道类型");
log.error("订单:{},明细中包含多个通道类型",order.getNo());
}
} else {
System.out.println("订单明细中包含多个商品创建人");
log.error("订单:{},明细中包含多个商品创建人",order.getNo());
}
}
}else {
System.out.println("提货柜订单spu列表为空");
log.error("订单:{},提货柜订单spu列表为空",order.getNo());
}
// 提货券更改为已使用
giftVoucherApi.useGiftVoucher(new GiftVoucherUseReqDTO().setId(order.getGiftVoucherId()).setUserId(order.getUserId())
.setOrderId(order.getId()));
}
}
@@ -80,7 +80,7 @@ spring:
host: 127.0.0.1 # 地址
port: 6379 # 端口
database: 10 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
password: 123456 # 密码,建议生产环境开启
--- #################### 定时任务相关配置 ####################
@@ -3,8 +3,8 @@ spring:
name: yudao-server
profiles:
# active: prod
active: local
active: prod
# active: local
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。