记得上下班打卡 | git大法好,push需谨慎

Commit dedd5794 authored by wangyifan's avatar wangyifan

slime partner后台 拼团活动相关功能

parent f83b5de2
package com.liquidnet.service.kylin.constant;
/**
* 演出票拼团状态常量,覆盖活动、订单、成员三类状态机。
*/
public class GroupBuyStatusConst {
private GroupBuyStatusConst() {
}
/**
* 拼团活动状态:草稿 → 待启动 → 进行中 ⇄ 已关闭 → 已结束。
*/
public static class ActivityStatus {
/** 草稿(未提交) */
public static final Integer DRAFT = 0;
/** 待启动(手动/定时) */
public static final Integer PENDING = 1;
/** 进行中(用户可发起/加入拼团) */
public static final Integer ACTIVE = 2;
/** 已关闭(B端手动关停,可重新开启) */
public static final Integer CLOSED = 3;
/** 已结束(到结束时间,终态) */
public static final Integer ENDED = 4;
private ActivityStatus() {
}
}
/**
* 启动方式。
*/
public static class LaunchType {
/** 立即启动:保存后活动立即生效 */
public static final Integer IMMEDIATE = 1;
/** 手动启动:保存后状态=待启动,B端手动点启动 */
public static final Integer MANUAL = 2;
/** 定时启动:保存后状态=待启动,到定时时间自动激活 */
public static final Integer SCHEDULED = 3;
private LaunchType() {
}
}
/**
* 优惠类型。
*/
public static class DiscountType {
/** 固定价格(元) */
public static final Integer FIXED_PRICE = 1;
/** 折扣(1-100,如85表示85折) */
public static final Integer DISCOUNT = 2;
/** 满减(元) */
public static final Integer REDUCE = 3;
private DiscountType() {
}
}
/**
* 拼团订单状态:预占 → 拼单中 → 拼成 / 失败 / 已取消。
*/
public static class OrderStatus {
/** 待支付发起(团长已创建拼团但尚未下单) */
public static final Integer PREPARE = -1;
/** 拼单中(团长已支付,等待成员加入) */
public static final Integer PENDING = 0;
/** 拼成(达到目标人数) */
public static final Integer SUCCESS = 1;
/** 失败(24h超时未成团) */
public static final Integer FAILED = 2;
/** 已取消(团长手动取消) */
public static final Integer CANCELLED = 3;
private OrderStatus() {
}
}
/**
* 公开/私密标记。
*/
public static class PublicFlag {
/** 私密(不进公开轮播,仅通过 shareToken 加入) */
public static final Integer PRIVATE = 0;
/** 公开(进入轮播列表) */
public static final Integer PUBLIC = 1;
private PublicFlag() {
}
}
/**
* 拼团成员状态:下单后待支付 → 已加入 → 已退款。
*/
public static class MemberStatus {
/** 待支付(已下单但未支付/支付中) */
public static final Integer PENDING_PAY = -1;
/** 已加入(支付成功) */
public static final Integer JOINED = 0;
/** 已退款(拼单中退款/拼成后退款) */
public static final Integer REFUNDED = 1;
private MemberStatus() {
}
}
}
package com.liquidnet.service.kylin.dto.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class KylinGroupBuyActivityParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动ID,编辑时必传")
private String activityId;
@ApiModelProperty(value = "活动名称")
@NotNull(message = "活动名称不能为空")
private String name;
@ApiModelProperty(value = "关联演出ID")
@NotNull(message = "关联演出ID不能为空")
private String performanceId;
@ApiModelProperty(value = "成团人数")
@NotNull(message = "成团人数不能为空")
private Integer minGroupSize;
@ApiModelProperty(value = "每人限购")
@NotNull(message = "每人限购不能为空")
private Integer maxBuyPerUser;
@ApiModelProperty(value = "活动开始时间")
@NotNull(message = "活动开始时间不能为空")
private LocalDateTime startTime;
@ApiModelProperty(value = "活动结束时间")
@NotNull(message = "活动结束时间不能为空")
private LocalDateTime endTime;
@ApiModelProperty(value = "启动方式 1立即 2手动 3定时")
@NotNull(message = "启动方式不能为空")
private Integer launchType;
@ApiModelProperty(value = "定时启动时间")
private LocalDateTime scheduledTime;
@ApiModelProperty(value = "活动说明")
private String description;
@Valid
@NotEmpty(message = "票种优惠不能为空")
@ApiModelProperty(value = "票种优惠配置")
private List<DiscountParam> discounts;
@Data
public static class DiscountParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "场次ID")
@NotNull(message = "场次ID不能为空")
private String timeId;
@ApiModelProperty(value = "票种ID")
@NotNull(message = "票种ID不能为空")
private String ticketId;
@ApiModelProperty(value = "优惠类型 1固定价格 2折扣 3满减")
@NotNull(message = "优惠类型不能为空")
private Integer discountType;
@ApiModelProperty(value = "优惠值")
@NotNull(message = "优惠值不能为空")
private BigDecimal discountValue;
}
}
package com.liquidnet.service.kylin.dto.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class KylinGroupBuyCreateParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "拼团活动ID")
@NotNull(message = "拼团活动ID不能为空")
private String activityId;
@ApiModelProperty(value = "演出ID")
@NotNull(message = "演出ID不能为空")
private String performanceId;
@ApiModelProperty(value = "场次ID")
@NotNull(message = "场次ID不能为空")
private String timeId;
@ApiModelProperty(value = "票种ID")
@NotNull(message = "票种ID不能为空")
private String ticketId;
@ApiModelProperty(value = "购票张数")
@NotNull(message = "购票张数不能为空")
@Min(value = 1, message = "购票张数不能小于1")
private Integer ticketCount;
@ApiModelProperty(value = "是否公开 1公开 0私密,默认公开")
private Integer isPublic;
@ApiModelProperty(value = "前端展示拼团单价,仅用于服务端比对")
private BigDecimal groupBuyDisplayPrice;
}
package com.liquidnet.service.kylin.dto.vo.groupbuy;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class KylinGroupBuyActivityVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动ID")
private String activityId;
@ApiModelProperty(value = "活动名称")
private String name;
@ApiModelProperty(value = "演出ID")
private String performanceId;
@ApiModelProperty(value = "成团人数")
private Integer minGroupSize;
@ApiModelProperty(value = "每人限购")
private Integer maxBuyPerUser;
@ApiModelProperty(value = "活动开始时间")
private String startTime;
@ApiModelProperty(value = "活动结束时间")
private String endTime;
@ApiModelProperty(value = "启动方式 1立即 2手动 3定时")
private Integer launchType;
@ApiModelProperty(value = "定时启动时间")
private String scheduledTime;
@ApiModelProperty(value = "状态 0草稿 1待启动 2进行中 3已关闭 4已结束")
private Integer status;
@ApiModelProperty(value = "活动说明")
private String description;
@ApiModelProperty(value = "优惠配置")
private List<KylinGroupBuyDiscountVo> discounts;
@ApiModelProperty(value = "创建时间")
private String createdAt;
@ApiModelProperty(value = "更新时间")
private String updatedAt;
@ApiModelProperty(value = "关联演出名称(列表查询时填充)")
private String performanceName;
@ApiModelProperty(value = "拼单数量(列表查询时填充)")
private Long orderCount;
}
package com.liquidnet.service.kylin.dto.vo.groupbuy;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class KylinGroupBuyDiscountVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "优惠ID")
private String discountId;
@ApiModelProperty(value = "活动ID")
private String activityId;
@ApiModelProperty(value = "演出ID")
private String performanceId;
@ApiModelProperty(value = "场次ID")
private String timeId;
@ApiModelProperty(value = "票种ID")
private String ticketId;
@ApiModelProperty(value = "优惠类型 1固定价格 2折扣 3满减")
private Integer discountType;
@ApiModelProperty(value = "优惠值")
private BigDecimal discountValue;
@ApiModelProperty(value = "创建时间")
private String createdAt;
}
package com.liquidnet.service.kylin.dto.vo.groupbuy;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
public class KylinGroupBuyMemberVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "成员ID")
private String memberId;
@ApiModelProperty(value = "拼团订单ID")
private String groupOrderId;
@ApiModelProperty(value = "用户ID")
private String userId;
@ApiModelProperty(value = "订单ID")
private String orderTicketsId;
@ApiModelProperty(value = "购票张数")
private Integer ticketCount;
@ApiModelProperty(value = "状态 -1待支付 0已加入 1已退款")
private Integer status;
@ApiModelProperty(value = "加入时间")
private String joinTime;
}
...@@ -10,7 +10,8 @@ public class SlimeAuthorizationConst { ...@@ -10,7 +10,8 @@ public class SlimeAuthorizationConst {
SALES("4", "销售统计"), SALES("4", "销售统计"),
CHECK("5", "验票"), CHECK("5", "验票"),
GRANT("6", "授权"), GRANT("6", "授权"),
AUDIT("7", "审核"); AUDIT("7", "审核"),
GROUP_BUY("8", "拼团管理");
private final String id; private final String id;
private final String name; private final String name;
......
package com.liquidnet.service.kylin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
public class KylinGroupBuyActivity implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Integer mid;
private String activityId;
private String name;
private String performanceId;
private Integer minGroupSize;
private Integer maxBuyPerUser;
private LocalDateTime startTime;
private LocalDateTime endTime;
private Integer launchType;
private LocalDateTime scheduledTime;
private Integer status;
private String description;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private static final KylinGroupBuyActivity obj = new KylinGroupBuyActivity();
public static KylinGroupBuyActivity getNew() {
try {
return (KylinGroupBuyActivity) obj.clone();
} catch (CloneNotSupportedException e) {
return new KylinGroupBuyActivity();
}
}
public Object[] insertObject() {
return new Object[]{
activityId, name, performanceId, minGroupSize, maxBuyPerUser,
startTime, endTime, launchType, scheduledTime, status,
description, createdAt, updatedAt
};
}
public Object[] updateObject() {
return new Object[]{
name, minGroupSize, maxBuyPerUser, startTime, endTime,
launchType, scheduledTime, status, description, updatedAt,
activityId
};
}
}
package com.liquidnet.service.kylin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
public class KylinGroupBuyDiscount implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Integer mid;
private String discountId;
private String activityId;
private String performanceId;
private String timeId;
private String ticketId;
private Integer discountType;
private BigDecimal discountValue;
private LocalDateTime createdAt;
private static final KylinGroupBuyDiscount obj = new KylinGroupBuyDiscount();
public static KylinGroupBuyDiscount getNew() {
try {
return (KylinGroupBuyDiscount) obj.clone();
} catch (CloneNotSupportedException e) {
return new KylinGroupBuyDiscount();
}
}
public Object[] insertObject() {
return new Object[]{
discountId, activityId, performanceId, timeId, ticketId,
discountType, discountValue, createdAt
};
}
}
package com.liquidnet.service.kylin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
public class KylinGroupBuyMember implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Integer mid;
private String memberId;
private String groupOrderId;
private String userId;
private String orderTicketsId;
private Integer ticketCount;
private Integer status;
private LocalDateTime joinTime;
private LocalDateTime createdAt;
private static final KylinGroupBuyMember obj = new KylinGroupBuyMember();
public static KylinGroupBuyMember getNew() {
try {
return (KylinGroupBuyMember) obj.clone();
} catch (CloneNotSupportedException e) {
return new KylinGroupBuyMember();
}
}
public Object[] insertObject() {
return new Object[]{
memberId, groupOrderId, userId, orderTicketsId,
ticketCount, status, joinTime, createdAt
};
}
}
package com.liquidnet.service.kylin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
public class KylinGroupBuyOrder implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Integer mid;
private String groupOrderId;
private String activityId;
private String leaderUserId;
private String orderTicketsId;
private Integer targetCount;
private Integer currentCount;
private Integer status;
private Integer isPublic;
private String shareToken;
private LocalDateTime expiresAt;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private static final KylinGroupBuyOrder obj = new KylinGroupBuyOrder();
public static KylinGroupBuyOrder getNew() {
try {
return (KylinGroupBuyOrder) obj.clone();
} catch (CloneNotSupportedException e) {
return new KylinGroupBuyOrder();
}
}
public Object[] insertObject() {
return new Object[]{
groupOrderId, activityId, leaderUserId, orderTicketsId,
targetCount, currentCount, status, isPublic, shareToken,
expiresAt, createdAt, updatedAt
};
}
}
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for kylin_group_buy_activity
-- ----------------------------
CREATE TABLE IF NOT EXISTS `kylin_group_buy_activity` (
`mid` int NOT NULL AUTO_INCREMENT,
`activity_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拼团活动ID',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '活动名称',
`performance_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '演出ID',
`min_group_size` int NOT NULL COMMENT '成团人数',
`max_buy_per_user` int NOT NULL COMMENT '每人限购',
`start_time` datetime NOT NULL COMMENT '活动开始时间',
`end_time` datetime NOT NULL COMMENT '活动结束时间',
`launch_type` tinyint NOT NULL COMMENT '启动方式 1立即 2手动 3定时',
`scheduled_time` datetime DEFAULT NULL COMMENT '定时启动时间',
`status` tinyint NOT NULL COMMENT '状态 0草稿 1待启动 2进行中 3已关闭 4已结束',
`description` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '活动说明',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`) USING BTREE,
UNIQUE KEY `uk_activity_id` (`activity_id`) USING BTREE,
KEY `idx_performance_status` (`performance_id`, `status`) USING BTREE,
KEY `idx_status_schedule` (`status`, `launch_type`, `scheduled_time`) USING BTREE,
KEY `idx_status_end_time` (`status`, `end_time`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=Dynamic COMMENT='演出票拼团活动';
-- ----------------------------
-- Table structure for kylin_group_buy_discount
-- ----------------------------
CREATE TABLE IF NOT EXISTS `kylin_group_buy_discount` (
`mid` int NOT NULL AUTO_INCREMENT,
`discount_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '优惠ID',
`activity_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拼团活动ID',
`performance_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '演出ID',
`time_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '场次ID',
`ticket_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '票种ID',
`discount_type` tinyint NOT NULL COMMENT '优惠类型 1固定价格 2折扣 3满减',
`discount_value` decimal(10,2) NOT NULL COMMENT '优惠值',
`created_at` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`mid`) USING BTREE,
UNIQUE KEY `uk_discount_id` (`discount_id`) USING BTREE,
UNIQUE KEY `uk_activity_ticket` (`activity_id`, `time_id`, `ticket_id`) USING BTREE,
KEY `idx_performance_ticket` (`performance_id`, `time_id`, `ticket_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=Dynamic COMMENT='演出票拼团优惠配置';
-- ----------------------------
-- Table structure for kylin_group_buy_order
-- ----------------------------
CREATE TABLE IF NOT EXISTS `kylin_group_buy_order` (
`mid` int NOT NULL AUTO_INCREMENT,
`group_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拼团订单ID',
`activity_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拼团活动ID',
`leader_user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '团长用户ID',
`order_tickets_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '团长票务订单ID',
`target_count` int NOT NULL COMMENT '目标成团人数',
`current_count` int NOT NULL DEFAULT 0 COMMENT '当前参团人数',
`status` tinyint NOT NULL COMMENT '状态 -1待支付发起单 0拼单中 1拼成 2失败 3已取消',
`is_public` tinyint NOT NULL DEFAULT 1 COMMENT '是否公开 1公开 0私密',
`share_token` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分享token',
`expires_at` datetime DEFAULT NULL COMMENT '拼团过期时间',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`) USING BTREE,
UNIQUE KEY `uk_group_order_id` (`group_order_id`) USING BTREE,
UNIQUE KEY `uk_share_token` (`share_token`) USING BTREE,
KEY `idx_activity_status` (`activity_id`, `status`) USING BTREE,
KEY `idx_status_expires` (`status`, `expires_at`) USING BTREE,
KEY `idx_leader_status` (`leader_user_id`, `status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=Dynamic COMMENT='演出票拼团订单';
-- ----------------------------
-- Table structure for kylin_group_buy_member
-- ----------------------------
CREATE TABLE IF NOT EXISTS `kylin_group_buy_member` (
`mid` int NOT NULL AUTO_INCREMENT,
`member_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '成员ID',
`group_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '拼团订单ID',
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID',
`order_tickets_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '票务订单ID',
`ticket_count` int NOT NULL COMMENT '购票张数',
`status` tinyint NOT NULL DEFAULT -1 COMMENT '状态 -1待支付 0已加入 1已退款',
`join_time` datetime NOT NULL COMMENT '加入时间',
`created_at` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`mid`) USING BTREE,
UNIQUE KEY `uk_member_id` (`member_id`) USING BTREE,
UNIQUE KEY `uk_order_tickets_id` (`order_tickets_id`) USING BTREE,
KEY `idx_group_order_id` (`group_order_id`) USING BTREE,
KEY `idx_user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=Dynamic COMMENT='演出票拼团成员';
-- 条件状态迁移示例:
-- UPDATE kylin_group_buy_order SET status = 1, updated_at = NOW()
-- WHERE group_order_id = ? AND status = 0 AND current_count >= target_count;
-- UPDATE kylin_group_buy_order SET status = 2, updated_at = NOW()
-- WHERE group_order_id = ? AND status = 0 AND expires_at < NOW();
-- UPDATE kylin_group_buy_order SET status = 3, updated_at = NOW()
-- WHERE group_order_id = ? AND status = 0;
SET FOREIGN_KEY_CHECKS = 1;
package com.liquidnet.service.slime.controller;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.kylin.dto.param.KylinGroupBuyActivityParam;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyActivityVo;
import com.liquidnet.service.kylin.service.partner.IKylinGroupBuyPartnerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Map;
@Api(tags = "第三方-拼团", position = 1005)
@RestController
@RequestMapping("partner/groupBuy")
@Validated
public class KylinGroupBuyPartnerController {
@Autowired
private IKylinGroupBuyPartnerService groupBuyPartnerService;
@GetMapping("")
@ApiOperation(value = "拼团活动列表", position = 1)
@ApiResponse(response = KylinGroupBuyActivityVo.class, code = 200, message = "接口返回对象参数")
public ResponseDto<PageInfo<KylinGroupBuyActivityVo>> list(
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "status", required = false) Integer status,
@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
@RequestParam(value = "size", required = false, defaultValue = "20") Integer size) {
return groupBuyPartnerService.list(name, status, page, size);
}
@GetMapping("/stats")
@ApiOperation(value = "拼团活动统计", position = 8)
public ResponseDto<Map<String, Long>> stats() {
return groupBuyPartnerService.stats();
}
@GetMapping("/details")
@ApiOperation(value = "拼团活动详情", position = 2)
public ResponseDto<KylinGroupBuyActivityVo> detail(
@RequestParam("activityId") @NotNull(message = "活动ID不能为空") String activityId) {
return groupBuyPartnerService.detail(activityId);
}
@PostMapping("")
@ApiOperation(value = "创建拼团活动", position = 3)
public ResponseDto<KylinGroupBuyActivityVo> create(@RequestBody @Valid KylinGroupBuyActivityParam param) {
return groupBuyPartnerService.create(param);
}
@PutMapping("")
@ApiOperation(value = "编辑拼团活动", position = 4)
public ResponseDto<KylinGroupBuyActivityVo> update(@RequestBody @Valid KylinGroupBuyActivityParam param) {
return groupBuyPartnerService.update(param);
}
@DeleteMapping("")
@ApiOperation(value = "删除拼团活动", position = 5)
public ResponseDto<Boolean> delete(@RequestParam("activityId") @NotNull(message = "活动ID不能为空") String activityId) {
return groupBuyPartnerService.delete(activityId);
}
@PostMapping("/toggle")
@ApiOperation(value = "启停拼团活动", position = 6)
public ResponseDto<Boolean> toggle(@RequestParam("activityId") @NotNull(message = "活动ID不能为空") String activityId,
@RequestParam("targetStatus") @NotNull(message = "目标状态不能为空") Integer targetStatus) {
return groupBuyPartnerService.toggle(activityId, targetStatus);
}
//
// @GetMapping("/orders")
// @ApiOperation(value = "拼团订单列表", position = 7)
// public ResponseDto<List<KylinGroupBuyOrderVo>> orders(
// @RequestParam("activityId") @NotNull(message = "活动ID不能为空") String activityId,
// @RequestParam(value = "status", required = false) Integer status,
// @RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
// @RequestParam(value = "size", required = false, defaultValue = "20") Integer size) {
// return groupBuyPartnerService.orders(activityId, status, page, size);
// }
}
package com.liquidnet.service.slime.service.impl;
import com.github.pagehelper.PageInfo;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import com.liquidnet.service.kylin.constant.GroupBuyStatusConst;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.param.KylinGroupBuyActivityParam;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyActivityVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyDiscountVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyOrderVo;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketTimesVo;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinPerformanceVo;
import com.liquidnet.service.kylin.service.partner.IKylinGroupBuyPartnerService;
import com.liquidnet.service.slime.util.ObjectUtil;
import com.liquidnet.service.slime.util.QueueUtil;
import com.liquidnet.service.slime.util.RedisSlimeUtils;
import com.mongodb.BasicDBObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class KylinGroupBuyPartnerServiceImpl implements IKylinGroupBuyPartnerService {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private RedisSlimeUtils redisSlimeUtils;
@Autowired
private RedisUtil redisUtil;
@Autowired
private QueueUtil queueUtil;
@Override
public ResponseDto<PageInfo<KylinGroupBuyActivityVo>> list(String name, Integer status, Integer page, Integer size) {
Criteria criteria = new Criteria();
if (status != null) {
criteria.and("status").is(status);
}
if (name != null && !name.trim().isEmpty()) {
criteria.and("name").regex(".*" + name.trim() + ".*", "i");
}
Query countQuery = Query.query(criteria);
long total = mongoTemplate.count(countQuery, KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
Query dataQuery = Query.query(criteria).with(Sort.by(Sort.Direction.DESC, "createdAt"));
if (page != null && size != null && page > 0 && size > 0) {
dataQuery.skip((long) (page - 1) * size).limit(size);
}
List<KylinGroupBuyActivityVo> list = mongoTemplate.find(dataQuery,
KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
if (!list.isEmpty()) {
List<String> activityIds = list.stream().map(KylinGroupBuyActivityVo::getActivityId).collect(Collectors.toList());
Map<String, Long> orderCountMap = new HashMap<>();
for (String activityId : activityIds) {
long count = mongoTemplate.count(Query.query(Criteria.where("activityId").is(activityId)),
KylinGroupBuyOrderVo.class.getSimpleName());
orderCountMap.put(activityId, count);
}
for (KylinGroupBuyActivityVo vo : list) {
vo.setOrderCount(orderCountMap.getOrDefault(vo.getActivityId(), 0L));
KylinPerformanceVo performanceVo = redisSlimeUtils.getPerformanceVo(vo.getPerformanceId());
vo.setPerformanceName(performanceVo != null ? performanceVo.getTitle() : null);
}
}
PageInfo<KylinGroupBuyActivityVo> pageInfo = new PageInfo<>(list);
pageInfo.setTotal(total);
return ResponseDto.success(pageInfo);
}
@Override
public ResponseDto<Map<String, Long>> stats() {
Map<String, Long> result = new HashMap<>();
String collection = KylinGroupBuyActivityVo.class.getSimpleName();
result.put("totalActivities", mongoTemplate.count(Query.query(new Criteria()), collection));
result.put("activeCount", mongoTemplate.count(
Query.query(Criteria.where("status").is(GroupBuyStatusConst.ActivityStatus.ACTIVE)), collection));
result.put("endedCount", mongoTemplate.count(
Query.query(Criteria.where("status").is(GroupBuyStatusConst.ActivityStatus.ENDED)), collection));
result.put("orderCount", mongoTemplate.count(Query.query(new Criteria()),
KylinGroupBuyOrderVo.class.getSimpleName()));
return ResponseDto.success(result);
}
@Override
public ResponseDto<KylinGroupBuyActivityVo> detail(String activityId) {
KylinGroupBuyActivityVo vo = findActivity(activityId);
return vo == null ? ResponseDto.failure("拼团活动不存在") : ResponseDto.success(vo);
}
@Override
public ResponseDto<KylinGroupBuyActivityVo> create(KylinGroupBuyActivityParam param) {
ResponseDto<Boolean> validate = validateParam(param);
if (!validate.isSuccess()) {
return ResponseDto.failure(validate.getMessage());
}
String now = now();
KylinGroupBuyActivityVo vo = buildActivity(param, now);
vo.setActivityId(IDGenerator.nextSnowId());
vo.setCreatedAt(now);
vo.setUpdatedAt(now);
vo.setDiscounts(buildDiscounts(param, vo.getActivityId(), now));
mongoTemplate.insert(vo, KylinGroupBuyActivityVo.class.getSimpleName());
sendReplaceActivitySql(vo);
cacheActivity(vo);
return ResponseDto.success(vo);
}
@Override
public ResponseDto<KylinGroupBuyActivityVo> update(KylinGroupBuyActivityParam param) {
KylinGroupBuyActivityVo old = findActivity(param.getActivityId());
if (old == null) {
return ResponseDto.failure("拼团活动不存在");
}
ResponseDto<Boolean> validate = validateParam(param);
if (!validate.isSuccess()) {
return ResponseDto.failure(validate.getMessage());
}
String now = now();
KylinGroupBuyActivityVo vo = buildActivity(param, now);
vo.setActivityId(old.getActivityId());
vo.setStatus(old.getStatus());
vo.setCreatedAt(old.getCreatedAt());
vo.setUpdatedAt(now);
vo.setDiscounts(buildDiscounts(param, old.getActivityId(), now));
replaceActivity(vo);
sendReplaceActivitySql(vo);
return ResponseDto.success(vo);
}
@Override
public ResponseDto<Boolean> delete(String activityId) {
KylinGroupBuyActivityVo old = findActivity(activityId);
if (old == null) {
return ResponseDto.failure("拼团活动不存在");
}
if (GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(old.getStatus())) {
return ResponseDto.failure("进行中的拼团活动不可删除");
}
mongoTemplate.remove(Query.query(Criteria.where("activityId").is(activityId)),
KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
sendDeleteActivitySql(activityId);
clearActivityCache(old);
return ResponseDto.success(true);
}
@Override
public ResponseDto<Boolean> toggle(String activityId, Integer targetStatus) {
KylinGroupBuyActivityVo old = findActivity(activityId);
if (old == null) {
return ResponseDto.failure("拼团活动不存在");
}
if (!GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(targetStatus)
&& !GroupBuyStatusConst.ActivityStatus.CLOSED.equals(targetStatus)) {
return ResponseDto.failure("仅支持启用或关闭活动");
}
Integer fromStatus = old.getStatus();
if (GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(targetStatus)
&& !GroupBuyStatusConst.ActivityStatus.PENDING.equals(fromStatus)
&& !GroupBuyStatusConst.ActivityStatus.CLOSED.equals(fromStatus)) {
return ResponseDto.failure("仅待启动或已关闭的活动可启用");
}
if (GroupBuyStatusConst.ActivityStatus.CLOSED.equals(targetStatus)
&& !GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(fromStatus)) {
return ResponseDto.failure("仅进行中的活动可关闭");
}
old.setStatus(targetStatus);
old.setUpdatedAt(now());
replaceActivity(old);
sendUpdateActivityStatusSql(old.getActivityId(), fromStatus, targetStatus,
DateUtil.Formatter.yyyyMMddHHmmss.parse(old.getUpdatedAt()));
return ResponseDto.success(true);
}
@Override
public ResponseDto<List<KylinGroupBuyOrderVo>> orders(String activityId, Integer status, Integer page, Integer size) {
Criteria criteria = Criteria.where("activityId").is(activityId);
if (status != null) {
criteria.and("status").is(status);
}
Query query = Query.query(criteria).with(Sort.by(Sort.Direction.DESC, "createdAt"));
if (page != null && size != null && page > 0 && size > 0) {
query.skip((long) (page - 1) * size).limit(size);
}
return ResponseDto.success(mongoTemplate.find(query,
KylinGroupBuyOrderVo.class, KylinGroupBuyOrderVo.class.getSimpleName()));
}
private ResponseDto<Boolean> validateParam(KylinGroupBuyActivityParam param) {
if (param.getEndTime().isBefore(param.getStartTime()) || param.getEndTime().isEqual(param.getStartTime())) {
return ResponseDto.failure("活动结束时间必须晚于开始时间");
}
if (GroupBuyStatusConst.LaunchType.SCHEDULED.equals(param.getLaunchType()) && param.getScheduledTime() == null) {
return ResponseDto.failure("定时启动必须填写启动时间");
}
KylinPerformanceVo performanceVo = redisSlimeUtils.getPerformanceVo(param.getPerformanceId());
if (performanceVo == null || CollectionUtils.isEmpty(performanceVo.getTicketTimeList())) {
return ResponseDto.failure("演出不存在");
}
if (param.getMinGroupSize() == null || param.getMinGroupSize() < 2) {
return ResponseDto.failure("成团人数不能小于2");
}
if (param.getMaxBuyPerUser() == null || param.getMaxBuyPerUser() < 1) {
return ResponseDto.failure("限购数量不能小于1");
}
if (!CollectionUtils.isEmpty(param.getDiscounts())) {
for (KylinGroupBuyActivityParam.DiscountParam discount : param.getDiscounts()) {
if (!ticketExists(performanceVo, discount.getTimeId(), discount.getTicketId())) {
return ResponseDto.failure("优惠配置的场次或票档不存在");
}
if (discount.getDiscountValue() == null || discount.getDiscountValue().compareTo(BigDecimal.ZERO) <= 0) {
return ResponseDto.failure("优惠值必须大于0");
}
}
}
return ResponseDto.success(true);
}
private boolean ticketExists(KylinPerformanceVo performanceVo, String timeId, String ticketId) {
for (KylinTicketTimesVo timeVo : performanceVo.getTicketTimeList()) {
if (!timeId.equals(timeVo.getTimeId()) || CollectionUtils.isEmpty(timeVo.getTicketList())) {
continue;
}
for (KylinTicketVo ticketVo : timeVo.getTicketList()) {
if (ticketId.equals(ticketVo.getTicketsId())) {
return true;
}
}
}
return false;
}
private KylinGroupBuyActivityVo buildActivity(KylinGroupBuyActivityParam param, String now) {
KylinGroupBuyActivityVo vo = new KylinGroupBuyActivityVo();
vo.setName(param.getName());
vo.setPerformanceId(param.getPerformanceId());
vo.setMinGroupSize(param.getMinGroupSize());
vo.setMaxBuyPerUser(param.getMaxBuyPerUser());
vo.setStartTime(DateUtil.Formatter.yyyyMMddHHmmss.format(param.getStartTime()));
vo.setEndTime(DateUtil.Formatter.yyyyMMddHHmmss.format(param.getEndTime()));
vo.setLaunchType(param.getLaunchType());
if (param.getScheduledTime() != null) {
vo.setScheduledTime(DateUtil.Formatter.yyyyMMddHHmmss.format(param.getScheduledTime()));
}
vo.setDescription(param.getDescription());
if (GroupBuyStatusConst.LaunchType.IMMEDIATE.equals(param.getLaunchType())) {
vo.setStatus(GroupBuyStatusConst.ActivityStatus.ACTIVE);
} else {
vo.setStatus(GroupBuyStatusConst.ActivityStatus.PENDING);
}
vo.setUpdatedAt(now);
return vo;
}
private List<KylinGroupBuyDiscountVo> buildDiscounts(KylinGroupBuyActivityParam param, String activityId, String now) {
List<KylinGroupBuyDiscountVo> list = new ArrayList<>();
for (KylinGroupBuyActivityParam.DiscountParam discountParam : param.getDiscounts()) {
KylinGroupBuyDiscountVo vo = new KylinGroupBuyDiscountVo();
vo.setDiscountId(IDGenerator.nextSnowId());
vo.setActivityId(activityId);
vo.setPerformanceId(param.getPerformanceId());
vo.setTimeId(discountParam.getTimeId());
vo.setTicketId(discountParam.getTicketId());
vo.setDiscountType(discountParam.getDiscountType());
vo.setDiscountValue(discountParam.getDiscountValue());
vo.setCreatedAt(now);
list.add(vo);
}
return list;
}
private KylinGroupBuyActivityVo findActivity(String activityId) {
Object obj = redisUtil.get(KylinRedisConst.GROUP_BUY_ACTIVITY.concat(activityId));
if (obj != null) {
return (KylinGroupBuyActivityVo) obj;
}
KylinGroupBuyActivityVo activity = mongoTemplate.findOne(Query.query(Criteria.where("activityId").is(activityId)),
KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
cacheActivity(activity);
return activity;
}
private void replaceActivity(KylinGroupBuyActivityVo activity) {
clearActivityCache(activity);
Query query = Query.query(Criteria.where("activityId").is(activity.getActivityId()));
BasicDBObject updateObj = ObjectUtil.cloneBasicDBObject()
.append("$set", mongoTemplate.getConverter().convertToMongoType(activity));
mongoTemplate.getCollection(KylinGroupBuyActivityVo.class.getSimpleName())
.updateOne(query.getQueryObject(), updateObj);
cacheActivity(activity);
}
private void sendReplaceActivitySql(KylinGroupBuyActivityVo activity) {
LinkedList<String> sqls = CollectionUtil.linkedListString();
LinkedList<Object[]> activityDel = CollectionUtil.linkedListObjectArr();
LinkedList<Object[]> activityInsert = CollectionUtil.linkedListObjectArr();
LinkedList<Object[]> discountDel = CollectionUtil.linkedListObjectArr();
LinkedList<Object[]> discountInsert = CollectionUtil.linkedListObjectArr();
sqls.add(SqlMapping.get("kylin_group_buy_activity.delete"));
activityDel.add(new Object[]{activity.getActivityId()});
sqls.add(SqlMapping.get("kylin_group_buy_activity.insert"));
activityInsert.add(activityInsertObject(activity));
sqls.add(SqlMapping.get("kylin_group_buy_discount.delete_by_activity"));
discountDel.add(new Object[]{activity.getActivityId()});
if (!CollectionUtils.isEmpty(activity.getDiscounts())) {
sqls.add(SqlMapping.get("kylin_group_buy_discount.insert"));
for (KylinGroupBuyDiscountVo discount : activity.getDiscounts()) {
discountInsert.add(discountInsertObject(discount));
}
queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
SqlMapping.gets(sqls, activityDel, activityInsert, discountDel, discountInsert));
} else {
queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
SqlMapping.gets(sqls, activityDel, activityInsert, discountDel));
}
}
private void sendDeleteActivitySql(String activityId) {
LinkedList<String> sqls = CollectionUtil.linkedListString();
LinkedList<Object[]> activityDel = CollectionUtil.linkedListObjectArr();
LinkedList<Object[]> discountDel = CollectionUtil.linkedListObjectArr();
sqls.add(SqlMapping.get("kylin_group_buy_activity.delete"));
activityDel.add(new Object[]{activityId});
sqls.add(SqlMapping.get("kylin_group_buy_discount.delete_by_activity"));
discountDel.add(new Object[]{activityId});
queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
SqlMapping.gets(sqls, activityDel, discountDel));
}
private void sendUpdateActivityStatusSql(String activityId, Integer fromStatus, Integer toStatus, LocalDateTime updatedAt) {
queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
SqlMapping.get("kylin_group_buy_activity.update_status",
toStatus, updatedAt, activityId, fromStatus));
}
private Object[] activityInsertObject(KylinGroupBuyActivityVo vo) {
return new Object[]{
vo.getActivityId(), vo.getName(), vo.getPerformanceId(), vo.getMinGroupSize(), vo.getMaxBuyPerUser(),
DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getStartTime()),
DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getEndTime()),
vo.getLaunchType(),
vo.getScheduledTime() == null ? null : DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getScheduledTime()),
vo.getStatus(), vo.getDescription(),
DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getCreatedAt()),
DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getUpdatedAt())
};
}
private Object[] discountInsertObject(KylinGroupBuyDiscountVo vo) {
return new Object[]{
vo.getDiscountId(), vo.getActivityId(), vo.getPerformanceId(), vo.getTimeId(), vo.getTicketId(),
vo.getDiscountType(), vo.getDiscountValue(), DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getCreatedAt())
};
}
private void cacheActivity(KylinGroupBuyActivityVo activity) {
if (activity == null) {
return;
}
redisUtil.set(KylinRedisConst.GROUP_BUY_ACTIVITY.concat(activity.getActivityId()), activity, RedisKeyExpireConst.KYLIN_GROUP_BUY_EXPIRE);
}
private void clearActivityCache(KylinGroupBuyActivityVo activity) {
if (activity == null) {
return;
}
redisUtil.del(KylinRedisConst.GROUP_BUY_ACTIVITY.concat(activity.getActivityId()));
}
private String now() {
return DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now());
}
}
...@@ -73,3 +73,10 @@ kylin_ticket_relations.insert=INSERT INTO kylin_ticket_relations (ticket_relatio ...@@ -73,3 +73,10 @@ kylin_ticket_relations.insert=INSERT INTO kylin_ticket_relations (ticket_relatio
kylin_ticket_express_module.del=DELETE FROM kylin_ticket_express_module WHERE ticket_id = ? and created_at < ? kylin_ticket_express_module.del=DELETE FROM kylin_ticket_express_module WHERE ticket_id = ? and created_at < ?
kylin_ticket_express_module.insert=INSERT INTO kylin_ticket_express_module (ticket_id,produce_code,created_at,updated_at) VALUES (?,?,?,?) kylin_ticket_express_module.insert=INSERT INTO kylin_ticket_express_module (ticket_id,produce_code,created_at,updated_at) VALUES (?,?,?,?)
# ---- 演出票拼团 ----
kylin_group_buy_activity.delete=DELETE FROM kylin_group_buy_activity WHERE activity_id = ?
kylin_group_buy_activity.insert=INSERT INTO kylin_group_buy_activity (activity_id,name,performance_id,min_group_size,max_buy_per_user,start_time,end_time,launch_type,scheduled_time,status,description,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
kylin_group_buy_activity.update_status=UPDATE kylin_group_buy_activity SET status = ?, updated_at = ? WHERE activity_id = ? AND status = ?
kylin_group_buy_discount.delete_by_activity=DELETE FROM kylin_group_buy_discount WHERE activity_id = ?
kylin_group_buy_discount.insert=INSERT INTO kylin_group_buy_discount (discount_id,activity_id,performance_id,time_id,ticket_id,discount_type,discount_value,created_at) VALUES (?,?,?,?,?,?,?,?)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment