套餐推广管理端接口

This commit is contained in:
gong
2025-12-18 15:17:05 +08:00
parent a1ad1088bb
commit 6256d72a13
24 changed files with 1331 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
package com.czg.controller.admin;
import com.alibaba.fastjson2.JSONObject;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.market.service.PpPackageService;
import com.czg.market.vo.PpPackagePageReqVo;
import com.czg.market.vo.PpPackageVO;
import com.czg.resp.CzgResult;
import com.czg.utils.AssertUtil;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 管理端/套餐推广
*
* @author yjjie
* @date 2025/12/18 11:18
*/
@RestController
@RequestMapping("/admin/package")
public class PpPackageController {
@Resource
private PpPackageService ppPackageService;
/**
* 添加套餐
*/
@PostMapping
@SaAdminCheckPermission(parentName = "套餐推广", value = "points:package:add", name = "添加套餐")
public CzgResult<Void> addPackage(@RequestBody @Validated PpPackageVO packageVO) {
ppPackageService.insertPackage(packageVO);
return CzgResult.success();
}
/**
* 修改套餐
*/
@PutMapping
@SaAdminCheckPermission(parentName = "套餐推广", value = "points:package:update", name = "修改套餐")
public CzgResult<Void> updatePackage(@RequestBody @Validated PpPackageVO packageVO) {
ppPackageService.updatePackage(packageVO);
return CzgResult.success();
}
/**
* 删除套餐
* 如果返回值大于0则删除失败存在进行中的订单请继续调用确认删除套餐接口
*/
@DeleteMapping("/{id}")
@SaAdminCheckPermission(parentName = "套餐推广", value = "points:package:delete", name = "删除套餐")
public CzgResult<Long> deletePackage(@PathVariable Long id) {
return CzgResult.success(ppPackageService.deletePackage(id));
}
/**
* 确认删除套餐
*/
@DeleteMapping("/sure/{id}")
@SaAdminCheckPermission(parentName = "套餐推广", value = "points:package:sureDelete", name = "确认删除套餐")
public CzgResult<Void> sureDeletePackage(@PathVariable Long id) {
ppPackageService.sureDeletePackage(id);
return CzgResult.success();
}
/**
* 获取套餐列表
*/
@GetMapping
public CzgResult<Page<PpPackageVO>> getPackageList(PpPackagePageReqVo reqVo) {
return CzgResult.success(ppPackageService.getPackagePage(reqVo, true));
}
/**
* 获取套餐推广开关
* 0: 关闭 1: 开启
*/
@GetMapping("/switch")
@SaAdminCheckPermission(parentName = "套餐推广", value = "points:package:getSwitch", name = "获取套餐推广开关")
public CzgResult<Integer> getPackagePromotionSwitch() {
return CzgResult.success(ppPackageService.getPackagePromotionSwitch());
}
/**
* 修改套餐推广开关
* 0: 关闭 1: 开启
* 参数 key = "status"
*/
@PutMapping("/switch")
@SaAdminCheckPermission(parentName = "套餐推广", value = "points:package:updateSwitch", name = "修改套餐推广开关")
public CzgResult<Boolean> updatePackagePromotionSwitch(@RequestBody JSONObject param) {
AssertUtil.isNull(param, "参数错误");
Integer status = param.getInteger("status");
AssertUtil.isNull(status, "参数错误");
return CzgResult.success(ppPackageService.updatePackagePromotionSwitch(status));
}
}

View File

@@ -123,6 +123,10 @@ public class ShopConfig implements Serializable {
* 拼团开关 1-是 0-否
*/
private Integer isGroupBuy;
/**
* 套餐推广 开关
*/
private Integer isPackagePromotion;
private String dingAppKey;

View File

@@ -0,0 +1,49 @@
package com.czg.constants;
/**
* @author yjjie
* @date 2025/12/18 11:37
*/
public interface PpPackageConstants {
/**
* 订单状态
* ing进行中, wait_verify 待核销finish 已核销refunding 退款中refund 已退款cancel 已取消timeout 超时
*/
class OrderStatus {
/**
* 进行中
*/
public static final String PROCESSING = "ing";
/**
* 待核销
*/
public static final String WAIT_VERIFY = "wait_verify";
/**
* 已核销
*/
public static final String FINISH = "finish";
/**
* 退款中
*/
public static final String REFUNDING = "refunding";
/**
* 已退款
*/
public static final String REFUND = "refund";
/**
* 已取消
*/
public static final String CANCEL = "cancel";
/**
* 超时
*/
public static final String TIMEOUT = "timeout";
}
}

View File

@@ -0,0 +1,46 @@
package com.czg.constants;
/**
* 系统常量
* @author yjjie
* @date 2025/12/18 13:45
*/
public interface SystemConstants {
/**
* 状态 1 0
* 是否 1 0
*/
class OneZero {
/**
* 是否:是
* 状态:允许,开启
*/
public static final Integer ONE = 1;
/**
* 是否:否
* 状态:进制,关闭
*/
public static final Integer ZERO = 0;
}
/**
* 分店类型
*/
class SubShopType {
/**
* 全部
*/
public static final String ALL = "all";
/**
* 仅本店
*/
public static final String ONLY = "only";
/**
* 自定义
*/
public static final String CUSTOM = "custom";
}
}

View File

@@ -0,0 +1,51 @@
package com.czg.market.dto;
import java.io.Serializable;
import lombok.experimental.Accessors;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 套餐推广助力记录 实体类。
*
* @author gyj
* @since 2025-12-18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class PpHelpRecordDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 自增主键
*/
private Long id;
/**
* 订单 Id
*/
private Long orderId;
/**
* 用户 Id
*/
private Long userId;
/**
* 用户头像
*/
private String userName;
/**
* 用户头像
*/
private String userAvator;
}

View File

@@ -0,0 +1,114 @@
package com.czg.market.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.experimental.Accessors;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 套餐推广订单 实体类。
*
* @author gyj
* @since 2025-12-18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class PpPackageOrderDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 自增主键
*/
private Long id;
/**
* 店铺 Id
*/
private Long shopId;
/**
* 订单编号
*/
private String orderNo;
/**
* 套餐 Id
*/
private Long packageId;
/**
* 用户 Id
*/
private Long userId;
/**
* 分享人数
*/
private Integer shareNum;
/**
* 最终支付金额
*/
private BigDecimal finalPrice;
/**
* 订单状态ing进行中, wait_verify 待核销finish 已核销refunding 退款中refund 已退款cancel 已取消timeout 超时
*/
private String status;
/**
* 支付时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime payTime;
/**
* 支付订单 Id
*/
private Long payOrderId;
/**
* 核销码
*/
private String verifyCode;
/**
* 核销时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime verifyTime;
/**
* 取消时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime cancelTime;
/**
* 取消原因
*/
private String cancelReason;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,57 @@
package com.czg.market.entity;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 套餐推广助力记录 实体类。
*
* @author gyj
* @since 2025-12-18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("pp_help_record")
public class PpHelpRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 自增主键
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 订单 Id
*/
private Long orderId;
/**
* 用户 Id
*/
private Long userId;
/**
* 用户头像
*/
private String userName;
/**
* 用户头像
*/
private String userAvator;
}

View File

@@ -0,0 +1,138 @@
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 套餐推广套餐 实体类。
*
* @author gyj
* @since 2025-12-18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("pp_package")
public class PpPackage implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 自增主键
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 店铺ID
*/
private Long shopId;
/**
* only-仅本店 all全部 /custom 指定
* {@link com.czg.constants.SystemConstants.SubShopType}
*/
private String useShopType;
/**
* 可用门店指定门店时存储门店ID逗号分隔
*/
private String useShops;
/**
* 套餐名称
*/
private String packageName;
/**
* 套餐描述
*/
private String description;
/**
* 套餐图片
*/
private String images;
/**
* 套餐原价
*/
private BigDecimal originPrice;
/**
* 销售价格
*/
private BigDecimal price;
/**
* 套餐内容 json
*/
private String packageContent;
/**
* 可用时段08:00~21:30
*/
private String useTimes;
/**
* 可用周期: [周一,周二]
*/
private String useWeeks;
/**
* 其他使用描述
*/
private String otherDesc;
/**
* 阶梯优惠
*/
private String tieredDiscount;
/**
* 分享期限(小时)
*/
private Integer expireHours;
/**
* 上线状态
*/
private Integer onlineStatus;
/**
* 商品详情图片
*/
private String detailImages;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
/**
* 0否1是
*/
private Integer isDel;
}

View File

@@ -0,0 +1,120 @@
package com.czg.market.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 套餐推广订单 实体类。
*
* @author gyj
* @since 2025-12-18
*/
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Table("pp_package_order")
public class PpPackageOrder implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 自增主键
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 店铺 Id
*/
private Long shopId;
/**
* 订单编号
*/
private String orderNo;
/**
* 套餐 Id
*/
private Long packageId;
/**
* 用户 Id
*/
private Long userId;
/**
* 分享人数
*/
private Integer shareNum;
/**
* 最终支付金额
*/
private BigDecimal finalPrice;
/**
* 订单状态ing进行中, wait_verify 待核销finish 已核销refunding 退款中refund 已退款cancel 已取消timeout 超时
* {@link com.czg.constants.PpPackageConstants.OrderStatus}
*/
private String status;
/**
* 支付时间
*/
private LocalDateTime payTime;
/**
* 支付订单 Id
*/
private Long payOrderId;
/**
* 核销码
*/
private String verifyCode;
/**
* 核销时间
*/
private LocalDateTime verifyTime;
/**
* 取消时间
*/
private LocalDateTime cancelTime;
/**
* 取消原因
*/
private String cancelReason;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,14 @@
package com.czg.market.service;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.PpHelpRecord;
/**
* 套餐推广助力记录 服务层。
*
* @author gyj
* @since 2025-12-18
*/
public interface PpHelpRecordService extends IService<PpHelpRecord> {
}

View File

@@ -0,0 +1,28 @@
package com.czg.market.service;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.PpPackageOrder;
/**
* 套餐推广订单 服务层。
*
* @author gyj
* @since 2025-12-18
*/
public interface PpPackageOrderService extends IService<PpPackageOrder> {
/**
* 创建推广订单
*/
Long createPackageOrder(Long packageId);
/**
* 获取进行中的套餐推广订单数量
*/
long getProgressingPackageOrderCount(Long packageId);
/**
* 将所有正在进行的推广订单设置为取消
*/
void cancelProgressingPackageOrder(Long shopId, Long packageId);
}

View File

@@ -0,0 +1,51 @@
package com.czg.market.service;
import com.czg.market.vo.PpPackagePageReqVo;
import com.czg.market.vo.PpPackageVO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.market.entity.PpPackage;
/**
* 套餐推广套餐 服务层。
*
* @author gyj
* @since 2025-12-18
*/
public interface PpPackageService extends IService<PpPackage> {
/**
* 获取套餐推广开关
*/
Integer getPackagePromotionSwitch();
/**
* 更新套餐开关
*/
boolean updatePackagePromotionSwitch(Integer status);
/**
* 插入套餐
*/
void insertPackage(PpPackageVO packageVO);
/**
* 更新套餐
*/
void updatePackage(PpPackageVO packageVO);
/**
* 删除套餐
*/
long deletePackage(Long id);
/**
* 确认删除套餐
*/
void sureDeletePackage(Long id);
/**
* 获取套餐分页
*/
Page<PpPackageVO> getPackagePage(PpPackagePageReqVo reqVo, boolean isAdmin);
}

View File

@@ -0,0 +1,23 @@
package com.czg.market.vo;
import com.czg.BaseQueryParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author yjjie
* @date 2025/12/18 11:25
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class PpPackagePageReqVo extends BaseQueryParam {
/**
* 上架状态
*/
private Integer onlineStatus;
/**
* 套餐名称
*/
private String packageName;
}

View File

@@ -0,0 +1,176 @@
package com.czg.market.vo;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.validator.group.InsertGroup;
import com.czg.validator.group.UpdateGroup;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author yjjie
* @date 2025/12/18 10:22
*/
@Data
public class PpPackageVO {
@Data
public static class TieredDiscount {
/**
* 人数
*/
private Integer peopleNum;
/**
* 价格
*/
private BigDecimal price;
}
@Data
public static class PackageContent {
/**
* 名称
*/
private String name;
/**
* 选择数量
*/
private Integer num;
/**
* 商品内容
*/
private List<PackageProduct> packageProducts;
}
@Data
public static class PackageProduct {
/**
* 名称
*/
private String name;
/**
* 价格
*/
private BigDecimal price;
/**
* 数量
*/
private Integer num;
}
/**
* 自增主键
*/
@NotNull(message = "id不能为空", groups = {UpdateGroup.class})
private Long id;
/**
* 店铺ID
*/
private Long shopId;
/**
* 可用门店only-仅本店 all全部 /custom 指定
* {@link com.czg.constants.SystemConstants.SubShopType}
*/
@NotBlank(message = "可用门店不能为空", groups = {UpdateGroup.class, InsertGroup.class})
private String useShopType;
/**
* 可用门店指定门店时存储门店ID逗号分隔
*/
private String useShops;
/**
* 套餐名称
*/
@NotBlank(message = "套餐名称不能为空", groups = {UpdateGroup.class, InsertGroup.class})
private String packageName;
/**
* 套餐描述
*/
private String description;
/**
* 套餐图片
*/
@NotNull(message = "套餐图片不能为空", groups = {UpdateGroup.class, InsertGroup.class})
private List<String> images;
/**
* 套餐原价
*/
@NotNull(message = "套餐原价不能为空", groups = {UpdateGroup.class, InsertGroup.class})
private BigDecimal originPrice;
/**
* 销售价格
*/
@NotNull(message = "销售价格不能为空", groups = {UpdateGroup.class, InsertGroup.class})
private BigDecimal price;
/**
* 套餐内容 json
*/
@NotNull(message = "套餐内容不能为空", groups = {UpdateGroup.class, InsertGroup.class})
private List<PackageContent> packageContent;
/**
* 可用时段08:00~21:30
*/
private String useTimes;
/**
* 可用周期: [周一,周二]
*/
private List<String> useWeeks;
/**
* 其他使用描述
*/
private String otherDesc;
/**
* 阶梯优惠
*/
private List<TieredDiscount> tieredDiscount;
/**
* 分享期限(小时)
*/
private Integer expireHours;
/**
* 上线状态
*/
private Integer onlineStatus = 1;
/**
* 商品详情图片
*/
private String detailImages;
/**
* 创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}

View File

@@ -21,6 +21,7 @@ public enum OrderNoPrefixEnum {
GB("GB", "拼团-团单号"),
GBO("GBO", "拼团-订单号"),
REG("REG", "拼团-退单"),
PPO("PPO", "套餐推广订单号"),
;
private final String value;

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.PpHelpRecord;
/**
* 套餐推广助力记录 映射层。
*
* @author gyj
* @since 2025-12-18
*/
public interface PpHelpRecordMapper extends BaseMapper<PpHelpRecord> {
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.PpPackage;
/**
* 套餐推广套餐 映射层。
*
* @author gyj
* @since 2025-12-18
*/
public interface PpPackageMapper extends BaseMapper<PpPackage> {
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.market.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.market.entity.PpPackageOrder;
/**
* 套餐推广订单 映射层。
*
* @author gyj
* @since 2025-12-18
*/
public interface PpPackageOrderMapper extends BaseMapper<PpPackageOrder> {
}

View File

@@ -0,0 +1,18 @@
package com.czg.service.market.service.impl;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.market.entity.PpHelpRecord;
import com.czg.market.service.PpHelpRecordService;
import com.czg.service.market.mapper.PpHelpRecordMapper;
import org.springframework.stereotype.Service;
/**
* 套餐推广助力记录 服务层实现。
*
* @author gyj
* @since 2025-12-18
*/
@Service
public class PpHelpRecordServiceImpl extends ServiceImpl<PpHelpRecordMapper, PpHelpRecord> implements PpHelpRecordService{
}

View File

@@ -0,0 +1,78 @@
package com.czg.service.market.service.impl;
import com.czg.constants.PpPackageConstants;
import com.czg.constants.SystemConstants;
import com.czg.enums.OrderNoPrefixEnum;
import com.czg.exception.CzgException;
import com.czg.market.entity.PpPackage;
import com.czg.market.entity.PpPackageOrder;
import com.czg.market.service.PpPackageOrderService;
import com.czg.sa.StpKit;
import com.czg.service.market.mapper.PpPackageMapper;
import com.czg.service.market.mapper.PpPackageOrderMapper;
import com.czg.utils.CzgRandomUtils;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* 套餐推广订单 服务层实现。
*
* @author gyj
* @since 2025-12-18
*/
@Service
public class PpPackageOrderServiceImpl extends ServiceImpl<PpPackageOrderMapper, PpPackageOrder> implements PpPackageOrderService {
@Resource
private PpPackageMapper ppPackageMapper;
@Override
public Long createPackageOrder(Long packageId) {
PpPackage aPackage = ppPackageMapper.selectOneByQuery(QueryWrapper.create().eq(PpPackage::getId, packageId)
.eq(PpPackage::getIsDel, SystemConstants.OneZero.ZERO));
if (aPackage == null) {
throw new CzgException("套餐不存在");
}
if (!aPackage.getOnlineStatus().equals(SystemConstants.OneZero.ONE)) {
throw new CzgException("套餐已下架");
}
PpPackageOrder order = new PpPackageOrder()
.setShopId(aPackage.getShopId())
.setPackageId(packageId)
.setUserId(StpKit.USER.getLoginIdAsLong())
.setShareNum(0)
.setOrderNo(CzgRandomUtils.randomNumber(OrderNoPrefixEnum.PPO, 12, false))
.setStatus(PpPackageConstants.OrderStatus.PROCESSING);
save(order);
return order.getId();
}
@Override
public long getProgressingPackageOrderCount(Long packageId) {
QueryWrapper wrapper = QueryWrapper.create().eq(PpPackageOrder::getPackageId, packageId)
.eq(PpPackageOrder::getStatus, PpPackageConstants.OrderStatus.PROCESSING);
return count(wrapper);
}
@Override
public void cancelProgressingPackageOrder(Long shopId, Long packageId) {
UpdateChain<PpPackageOrder> update = UpdateChain.of(PpPackageOrder.class)
.set(PpPackageOrder::getStatus, PpPackageConstants.OrderStatus.CANCEL)
.set(PpPackageOrder::getCancelTime, LocalDateTime.now())
.set(PpPackageOrder::getCancelReason, "商家下架套餐");
if (packageId != null && packageId > 0L) {
update.eq(PpPackageOrder::getPackageId, packageId);
}
update.eq(PpPackageOrder::getShopId, shopId)
.eq(PpPackageOrder::getStatus, PpPackageConstants.OrderStatus.PROCESSING)
.update();
}
}

View File

@@ -0,0 +1,201 @@
package com.czg.service.market.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.czg.account.entity.ShopConfig;
import com.czg.account.service.ShopConfigService;
import com.czg.account.service.ShopInfoService;
import com.czg.constants.SystemConstants;
import com.czg.exception.CzgException;
import com.czg.market.entity.PpPackage;
import com.czg.market.service.PpPackageOrderService;
import com.czg.market.service.PpPackageService;
import com.czg.market.vo.PpPackagePageReqVo;
import com.czg.market.vo.PpPackageVO;
import com.czg.sa.StpKit;
import com.czg.service.market.mapper.PpPackageMapper;
import com.czg.utils.AssertUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* 套餐推广套餐 服务层实现。
*
* @author gyj
* @since 2025-12-18
*/
@Service
public class PpPackageServiceImpl extends ServiceImpl<PpPackageMapper, PpPackage> implements PpPackageService {
@Resource
private PpPackageOrderService ppPackageOrderService;
@DubboReference
private ShopInfoService shopInfoService;
@DubboReference
private ShopConfigService shopConfigService;
@Override
public Integer getPackagePromotionSwitch() {
return shopConfigService.getById(StpKit.USER.getShopId()).getIsPackagePromotion();
}
@Override
@Transactional
public boolean updatePackagePromotionSwitch(Integer status) {
Long shopId = StpKit.USER.getShopId();
ShopConfig shopConfig = new ShopConfig();
shopConfig.setIsPackagePromotion(status);
boolean update = shopConfigService.update(shopConfig, query().eq(ShopConfig::getId, shopId));
if (update && status == 0) {
// 下架所有进行中的套餐订单
UpdateChain.of(PpPackage.class)
.set(PpPackage::getOnlineStatus, SystemConstants.OneZero.ZERO)
.eq(PpPackage::getShopId, shopId)
.update();
ppPackageOrderService.cancelProgressingPackageOrder(StpKit.USER.getShopId(), 0L);
}
return update;
}
@Override
public void insertPackage(PpPackageVO packageVO) {
if (SystemConstants.SubShopType.CUSTOM.equals(packageVO.getUseShopType())) {
AssertUtil.isBlank(packageVO.getUseShops(), "请配置可用门店");
}
Long shopId = StpKit.USER.getShopId();
// 查询名称是否存在
PpPackage ppPackage = getOne(QueryWrapper.create().eq(PpPackage::getPackageName, packageVO.getPackageName())
.eq(PpPackage::getShopId, shopId).eq(PpPackage::getIsDel, SystemConstants.OneZero.ZERO));
if (ppPackage != null) {
throw new CzgException("名称已存在");
}
ppPackage = BeanUtil.copyProperties(packageVO, PpPackage.class);
ppPackage.setShopId(shopId);
ppPackage.setPackageContent(JSONObject.toJSONString(packageVO.getPackageContent()));
ppPackage.setUseWeeks(JSONObject.toJSONString(packageVO.getUseWeeks()));
ppPackage.setTieredDiscount(JSONObject.toJSONString(packageVO.getTieredDiscount()));
save(ppPackage);
}
@Override
public void updatePackage(PpPackageVO packageVO) {
if (SystemConstants.SubShopType.CUSTOM.equals(packageVO.getUseShopType())) {
AssertUtil.isBlank(packageVO.getUseShops(), "请配置可用门店");
}
Long shopId = StpKit.USER.getShopId();
PpPackage ppPackage = getPackageById(packageVO.getId());
if (!ppPackage.getShopId().equals(shopId)) {
throw new CzgException("无权限");
}
ppPackage = BeanUtil.copyProperties(packageVO, PpPackage.class);
ppPackage.setPackageContent(JSONObject.toJSONString(packageVO.getPackageContent()));
ppPackage.setUseWeeks(JSONObject.toJSONString(packageVO.getUseWeeks()));
ppPackage.setTieredDiscount(JSONObject.toJSONString(packageVO.getTieredDiscount()));
updateById(ppPackage);
}
@Override
@Transactional
public long deletePackage(Long id) {
Long shopId = StpKit.USER.getShopId();
PpPackage ppPackage = getPackageById(id);
if (!ppPackage.getShopId().equals(shopId)) {
throw new RuntimeException("无权限");
}
long orderCount = ppPackageOrderService.getProgressingPackageOrderCount(id);
if (orderCount > 0) {
return orderCount;
}
ppPackage.setIsDel(1);
updateById(ppPackage);
return 0L;
}
@Override
@Transactional
public void sureDeletePackage(Long id) {
PpPackage ppPackage = getPackageById(id);
ppPackage.setIsDel(1);
updateById(ppPackage);
ppPackageOrderService.cancelProgressingPackageOrder(StpKit.USER.getShopId(), id);
}
@Override
public Page<PpPackageVO> getPackagePage(PpPackagePageReqVo reqVo, boolean isAdmin) {
Long shopId = StpKit.USER.getShopId();
// 如果没有开启操作,直接返回空数据
ShopConfig shopConfig = shopConfigService.getById(shopId);
if (SystemConstants.OneZero.ZERO.equals(shopConfig.getIsPackagePromotion())) {
return new Page<>();
}
Long mainShopId = shopInfoService.getMainIdByShopId(shopId);
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq(PpPackage::getIsDel, SystemConstants.OneZero.ZERO)
.eq(PpPackage::getOnlineStatus, reqVo.getOnlineStatus())
.like(PpPackage::getPackageName, reqVo.getPackageName())
.orderBy(PpPackage::getCreateTime).desc();
queryWrapper.and(q -> {
q.eq(PpPackage::getShopId, shopId).or(q1 -> {
q1.eq(PpPackage::getUseShopType, SystemConstants.SubShopType.ALL).eq(PpPackage::getShopId, mainShopId);
}).or(q2 -> {
q2.eq(PpPackage::getUseShopType, SystemConstants.SubShopType.CUSTOM).and("FIND_IN_SET( " + shopId + ", use_shops ) > 0");
});
});
Page<PpPackage> page = page(Page.of(reqVo.getPage(), reqVo.getSize()), queryWrapper);
List<PpPackageVO> voList = new ArrayList<>();
page.getRecords().forEach(item -> {
PpPackageVO packageVO = BeanUtil.copyProperties(item, PpPackageVO.class);
packageVO.setUseWeeks(JSONArray.parseArray(item.getUseWeeks(), String.class));
packageVO.setPackageContent(JSONArray.parseArray(item.getPackageContent(), PpPackageVO.PackageContent.class));
packageVO.setTieredDiscount(JSONArray.parseArray(item.getTieredDiscount(), PpPackageVO.TieredDiscount.class));
voList.add(packageVO);
});
Page<PpPackageVO> voPage = new Page<>();
BeanUtil.copyProperties(page, voPage, "records");
voPage.setRecords(voList);
return voPage;
}
private PpPackage getPackageById(Long id) {
PpPackage ppPackage = getOne(QueryWrapper.create().eq(PpPackage::getId, id)
.eq(PpPackage::getIsDel, SystemConstants.OneZero.ZERO));
if (ppPackage == null) {
throw new RuntimeException("套餐不存在");
}
return ppPackage;
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.market.mapper.PpHelpRecordMapper">
</mapper>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.market.mapper.PpPackageMapper">
</mapper>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.market.mapper.PpPackageOrderMapper">
</mapper>