zoujiajian 3 жил өмнө
parent
commit
a18c0252ec
18 өөрчлөгдсөн 827 нэмэгдсэн , 10 устгасан
  1. 11 0
      netflix-common/pom.xml
  2. 12 0
      netflix-common/src/main/java/com/cyksj/common/annotation/NoSubmit.java
  3. 32 0
      netflix-common/src/main/java/com/cyksj/common/annotation/UrlCache.java
  4. 15 0
      netflix-common/src/main/java/com/cyksj/common/util/StringUtil.java
  5. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/exchange/ExchangeCodeMapper.java
  6. 43 0
      netflix-dao/src/main/java/com/cyksj/model/entity/ExchangeCode.java
  7. 24 1
      netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java
  8. 90 0
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelExchangeCodeData.java
  9. 29 0
      netflix-dao/src/main/java/com/cyksj/model/request/ExchangeCodeReq.java
  10. 19 0
      netflix-dao/src/main/java/com/cyksj/model/request/ExchangeCodeTicket.java
  11. 72 0
      netflix-dao/src/main/java/com/cyksj/model/views/ExchangeCodeView.java
  12. 28 0
      netflix-service/src/main/java/com/cyksj/service/exchange/ExchangeCodeService.java
  13. 297 0
      netflix-service/src/main/java/com/cyksj/service/exchange/impl/ExchangeCodeServiceImpl.java
  14. 8 0
      netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java
  15. 5 4
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  16. 48 0
      netflix-web/src/main/java/com/cyksj/web/aop/NoSubmitAop.java
  17. 68 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/exchange/ExchangeCodeController.java
  18. 13 5
      netflix-web/src/main/java/com/cyksj/web/controller/user/UserController.java

+ 11 - 0
netflix-common/pom.xml

@@ -68,6 +68,17 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>28.2-jre</version>
+        </dependency>
     </dependencies>
 
 

+ 12 - 0
netflix-common/src/main/java/com/cyksj/common/annotation/NoSubmit.java

@@ -0,0 +1,12 @@
+package com.cyksj.common.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD) // 作用到方法上
+@Retention(RetentionPolicy.RUNTIME) // 运行时有效
+public @interface NoSubmit {
+}

+ 32 - 0
netflix-common/src/main/java/com/cyksj/common/annotation/UrlCache.java

@@ -0,0 +1,32 @@
+package com.cyksj.common.annotation;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author zwhui
+ * @date 2020/7/16 上午11:07
+ */
+
+@Configuration
+public class UrlCache {
+    @Bean
+    public Cache<String, Integer> getCache() {
+        return CacheBuilder.newBuilder().expireAfterWrite(2L, TimeUnit.SECONDS).build();// 缓存有效期为2秒
+    }
+
+    @Bean("recordCache")
+    public Cache<String, String> recordCache() {
+        return CacheBuilder.newBuilder().expireAfterWrite(30L, TimeUnit.SECONDS).build();// 缓存有效期为30秒
+    }
+
+    @Bean("countCache")
+    public Cache<String, String> countCache() {
+        return CacheBuilder.newBuilder().expireAfterWrite(600L, TimeUnit.SECONDS).build();// 缓存有效期为600秒
+    }
+
+}

+ 15 - 0
netflix-common/src/main/java/com/cyksj/common/util/StringUtil.java

@@ -1,5 +1,7 @@
 package com.cyksj.common.util;
 
+import cn.hutool.core.util.RandomUtil;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -33,6 +35,8 @@ public final class StringUtil {
 
 	public static final String EMPTY = "";
 
+	public static final String randomStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#&/_-";
+
 	/**
 	 * provate constructor
 	 */
@@ -393,4 +397,15 @@ public final class StringUtil {
 			return "ERROR!";
 		}
 	}
+
+	/**
+	 * 随即获取length随机字符串
+	 */
+	public static void getRandomStr(StringBuilder sb, Integer length) {
+		Integer baseLength = randomStr.length();
+		for (int i = 0; i < length; i++) {
+			int number = RandomUtil.randomInt(baseLength);
+			sb.append(randomStr.charAt(number));
+		}
+	}
 }

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/exchange/ExchangeCodeMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.manage.exchange;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.ExchangeCode;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeMapper
+ *创建者: JavaZou
+ *创建时间:2022/11/7 16:33
+ */
+public interface ExchangeCodeMapper extends BaseMapper<ExchangeCode> {
+}

+ 43 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/ExchangeCode.java

@@ -0,0 +1,43 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCode
+ *创建者: JavaZou
+ *创建时间:2022/11/7 15:43
+ */
+@Getter
+@Setter
+public class ExchangeCode extends BaseEntity{
+	private String code;
+
+	/**
+	 * 平台id
+	 */
+	private Long goodsId;
+
+	/**
+	 * 规格id
+	 */
+	private Long skuId;
+
+	/**
+	 * 使用状态
+	 */
+	private Boolean useStatus;
+
+	/**
+	 * 使用时间
+	 */
+	private Date useTime;
+
+	/**
+	 * 使用人id
+	 */
+	private Long userId;
+}

+ 24 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java

@@ -1,9 +1,12 @@
 package com.cyksj.model.entity;
 
-import lombok.*;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.util.Arrays;
 import java.util.Date;
 
 /**
@@ -54,6 +57,11 @@ public class OrderDon extends BaseEntity implements Serializable {
     /** 支付状态 */
     private Status status ;
 
+    /**
+     * 支付类型
+     */
+    private Type type;
+
     /**
      * 支付时间
      */
@@ -81,4 +89,19 @@ public class OrderDon extends BaseEntity implements Serializable {
             return desc;
         }
     }
+
+    @Getter
+    @AllArgsConstructor
+    public enum Type{
+        WeChat("wechat","微信"),
+        ALI_PAY("zfb","支付宝"),
+        EX_CODE("exchange_code","兑换码")
+        ;
+        String type;
+        String desc;
+
+        public static Type getType(String type) {
+            return Arrays.stream(Type.values()).filter(tp -> tp.getType().equals(type)).findFirst().orElse(WeChat);
+        }
+    }
 }

+ 90 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelExchangeCodeData.java

@@ -0,0 +1,90 @@
+package com.cyksj.model.excel;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExcelExchangeCodeData
+ *创建者: JavaZou
+ *创建时间:2022/11/7 16:44
+ */
+@Getter
+@Setter
+@SearchBean(tables = "exchange_code ec left join user u on u.id = ec.user_id" +
+		" left join goods_don gd on ec.goods_id = gd.id" +
+		" left join goods_don_sku gdk on gdk.id = ec.sku_id")
+public class ExcelExchangeCodeData {
+
+	/**
+	 * 生成日期
+	 */
+	@ExcelProperty("生成日期")
+	@DbField("ec.created_time")
+	private Date createdTime;
+
+	@ExcelProperty("兑换码")
+	@DbField("ec.code")
+	private String code;
+
+	@ExcelIgnore
+	@DbField("ec.goods_id")
+	private Long goodsId;
+
+	/**
+	 * 平台名称
+	 */
+	@ExcelProperty("平台")
+	@DbField("gd.title")
+	private String title;
+
+	/**
+	 * 规格名称,金额
+	 */
+	@ExcelProperty("规格名称")
+	@DbField("concat(gdk.spec_val,'',cast(gdk.price/100 as decimal(10,2)))")
+	private String specVal;
+
+	/**
+	 * 使用状态str
+	 */
+	@ExcelProperty("使用状态")
+	@DbField("if(ec.use_status,'已使用','未使用')")
+	@TableField(exist = false)
+	private String useStatusStr;
+
+	/**
+	 * 使用状态
+	 */
+	@DbField("ec.use_status")
+	@ExcelIgnore
+	private Boolean useStatus;
+
+	/**
+	 * 使用时间
+	 */
+	@ExcelProperty("使用时间")
+	@DbField("ec.use_time")
+	private Date useTime;
+
+	/**
+	 * 使用用户
+	 */
+	@ExcelProperty("使用用户")
+	@DbField("u.nickname")
+	private String nickName;
+
+	/**
+	 * 用户id
+	 */
+	@ExcelProperty("用户id")
+	@DbField("u.id")
+	private Long userId;
+}

+ 29 - 0
netflix-dao/src/main/java/com/cyksj/model/request/ExchangeCodeReq.java

@@ -0,0 +1,29 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeReq
+ *创建者: JavaZou
+ *创建时间:2022/11/7 15:26
+ */
+@Getter
+@Setter
+public class ExchangeCodeReq {
+	/**
+	 * 平台id
+	 */
+	private Long goodsId;
+
+	/**
+	 * 规格id
+	 */
+	private Long skuId;
+
+	/**
+	 * 数量
+	 */
+	private Integer number;
+}

+ 19 - 0
netflix-dao/src/main/java/com/cyksj/model/request/ExchangeCodeTicket.java

@@ -0,0 +1,19 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeTicket
+ *创建者: JavaZou
+ *创建时间:2022/11/8 13:43
+ */
+@Getter
+@Setter
+public class ExchangeCodeTicket {
+	/**
+	 * 兑换码
+	 */
+	private String code;
+}

+ 72 - 0
netflix-dao/src/main/java/com/cyksj/model/views/ExchangeCodeView.java

@@ -0,0 +1,72 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeView
+ *创建者: JavaZou
+ *创建时间:2022/11/7 16:25
+ */
+@Getter
+@Setter
+@SearchBean(tables = "exchange_code ec left join user u on u.id = ec.user_id" +
+		" left join goods_don gd on ec.goods_id = gd.id" +
+		" left join goods_don_sku gdk on gdk.id = ec.sku_id")
+public class ExchangeCodeView {
+	@DbField("ec.id")
+	private Long id;
+
+	@DbField("ec.code")
+	private String code;
+
+	@DbField("ec.goods_id")
+	private Long goodsId;
+
+	/**
+	 * 平台名称
+	 */
+	@DbField("gd.title")
+	private String title;
+
+	/**
+	 * 规格名称,金额
+	 */
+	@DbField("concat(gdk.spec_val,'',cast(gdk.price/100 as decimal(10,2)))")
+	private String specVal;
+
+	/**
+	 * 使用状态
+	 */
+	@DbField("ec.use_status")
+	private Boolean useStatus;
+
+	/**
+	 * 使用时间
+	 */
+	@DbField("ec.use_time")
+	private Date useTime;
+
+	/**
+	 * 使用人id
+	 */
+	@DbField("ec.user_id")
+	private Long userId;
+
+	/**
+	 * 用户昵称
+	 */
+	@DbField("u.nickname")
+	private String nickName;
+
+	/**
+	 * 生成日期
+	 */
+	@DbField("ec.created_time")
+	private Date createdTime;
+}

+ 28 - 0
netflix-service/src/main/java/com/cyksj/service/exchange/ExchangeCodeService.java

@@ -0,0 +1,28 @@
+package com.cyksj.service.exchange;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.cyksj.dto.Result;
+import com.cyksj.model.entity.ExchangeCode;
+import com.cyksj.model.excel.ExcelExchangeCodeData;
+import com.cyksj.model.request.ExchangeCodeReq;
+import com.cyksj.model.request.ExchangeCodeTicket;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeService
+ *创建者: JavaZou
+ *创建时间:2022/11/7 15:41
+ */
+public interface ExchangeCodeService extends IService<ExchangeCode> {
+	/**
+	 * 生成兑换码
+	 */
+	List<ExcelExchangeCodeData> createExchangeCode(ExchangeCodeReq exchangeCodeReq);
+
+	/**
+	 * 兑换码兑换
+	 */
+	Result<String> getTicketByExchangeCode(ExchangeCodeTicket exchangeCodeTicket, Long userId);
+}

+ 297 - 0
netflix-service/src/main/java/com/cyksj/service/exchange/impl/ExchangeCodeServiceImpl.java

@@ -0,0 +1,297 @@
+package com.cyksj.service.exchange.impl;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.annotation.NoSubmit;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.snowflake.Sequence;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.dto.RedisKey;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.*;
+import com.cyksj.mapper.manage.exchange.ExchangeCodeMapper;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.excel.ExcelExchangeCodeData;
+import com.cyksj.model.request.ExchangeCodeReq;
+import com.cyksj.model.request.ExchangeCodeTicket;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.exchange.ExchangeCodeService;
+import com.cyksj.service.order.OrderDonService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeServiceImpl
+ *创建者: JavaZou
+ *创建时间:2022/11/7 15:41
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, ExchangeCode> implements ExchangeCodeService {
+	private final GoodsDonMapper goodsDonMapper;
+
+	private final GoodsDonSkuMapper goodsDonSkuMapper;
+
+	private final ExchangeCodeMapper exchangeCodeMapper;
+
+	private final GroupsRelationMapper groupsRelationMapper;
+
+	private final GroupsMapper groupsMapper;
+
+	private final RedisService redisService;
+
+	private static final Sequence SEQUENCE = new Sequence(0);
+
+	private final UserMapper userMapper;
+
+	private final OrderDonMapper orderDonMapper;
+
+	private final OrderDonService orderDonService;
+
+	private static final GlobalThreadPoolTaskExecutor THREAD_POOL_TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+	private final CustomerServiceMapper customerServiceMapper;
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public List<ExcelExchangeCodeData> createExchangeCode(ExchangeCodeReq exchangeCodeReq) {
+		GoodsDon goodsDon = goodsDonMapper.selectById(exchangeCodeReq.getGoodsId());
+		if (goodsDon == null) {
+			throw BusinessRuntimeException.getInstance("所选平台已被删除");
+		}
+		GoodsDonSku goodsDonSku = goodsDonSkuMapper.selectById(exchangeCodeReq.getSkuId());
+		if (goodsDonSku == null) {
+			throw BusinessRuntimeException.getInstance("所选规格已被删除");
+		}
+		List<ExcelExchangeCodeData> data = new ArrayList<>(exchangeCodeReq.getNumber());
+		for (int i = 0; i < exchangeCodeReq.getNumber(); i++) {
+			StringBuilder sb = new StringBuilder(32);
+			sb.append(goodsDon.getSecondType() == 1 ? "YQ" : "FY");
+			sb.append(getFillByGoodsId(exchangeCodeReq.getGoodsId()));
+			sb.append(getFillByMonth(exchangeCodeReq.getSkuId()));
+			//随即补充12位随即数,组成20位兑换码
+			StringUtil.getRandomStr(sb, 12);
+			ExchangeCode exchangeCode = new ExchangeCode();
+			exchangeCode.setGoodsId(exchangeCodeReq.getGoodsId());
+			exchangeCode.setSkuId(exchangeCodeReq.getSkuId());
+			exchangeCode.setCode(sb.toString());
+			exchangeCode.setUseStatus(false);
+			exchangeCodeMapper.insert(exchangeCode);
+			ExcelExchangeCodeData excelExchangeCodeData = new ExcelExchangeCodeData();
+			BeanUtils.copyProperties(exchangeCode, excelExchangeCodeData);
+			excelExchangeCodeData.setTitle(goodsDon.getTitle());
+			String specVal = goodsDonSku.getSpecVal();
+			BigDecimal price = goodsDonSku.getPrice();
+			excelExchangeCodeData.setSpecVal(String.format("%s%s", specVal, price.divide(BigDecimal.valueOf(100))));
+			excelExchangeCodeData.setUseStatusStr(exchangeCode.getUseStatus() ? "已使用" : "未使用");
+			data.add(excelExchangeCodeData);
+		}
+		return data;
+	}
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	@NoSubmit
+	public Result<String> getTicketByExchangeCode(ExchangeCodeTicket exchangeCodeTicket, Long userId) {
+		if (StrUtil.isEmpty(exchangeCodeTicket.getCode())) {
+			throw BusinessRuntimeException.getInstance("兑换码有误,请使用正确的兑换码");
+		}
+		ExchangeCode exchangeCode = this.getOne(Wrappers.lambdaQuery(ExchangeCode.class).eq(ExchangeCode::getCode, exchangeCodeTicket.getCode()).last("limit 1"));
+		if (exchangeCode == null) {
+			throw BusinessRuntimeException.getInstance("兑换码有误,请使用正确的兑换码");
+		}
+		if (exchangeCode.getUseStatus()) {
+			throw BusinessRuntimeException.getInstance("该兑换码已使用");
+		}
+		//兑换成功
+		exchangeCode.setUseStatus(true);
+		exchangeCode.setUserId(userId);
+		exchangeCode.setUseTime(DateTime.now());
+		int update = exchangeCodeMapper.update(exchangeCode, Wrappers.lambdaQuery(ExchangeCode.class).eq(ExchangeCode::getId, exchangeCode.getId()).eq(ExchangeCode::getUseStatus, false));
+		if (update != 1) {
+			throw BusinessRuntimeException.getInstance("该兑换码已使用");
+		}
+		//生成车票订单
+		getGroupTripsTicket(goodsDonSkuMapper.selectById(exchangeCode.getSkuId()), userId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("兑换成功");
+	}
+
+	/**
+	 * 根据平台id获取填充的字符串
+	 */
+	private String getFillByGoodsId(Long goodsId) {
+		String goodsIdStr = String.valueOf(goodsId);
+		if (goodsIdStr.length() == 1) {
+			return String.format("%s%s%s", 0, 0, goodsId);
+		}
+		if (goodsIdStr.length() == 2) {
+			return String.format("%s%s", 0, goodsId);
+		}
+		//最大不超过999
+		if (goodsIdStr.length() > 3) {
+			return goodsIdStr.substring(0, 3);
+		}
+		return goodsIdStr;
+	}
+
+	/**
+	 * 根据月份长度获取填充的字符串
+	 */
+	private String getFillByMonth(Long skuMonth) {
+		String goodsIdStr = String.valueOf(skuMonth);
+		if (goodsIdStr.length() == 1) {
+			return String.format("%s%s%s", 0, 0, skuMonth);
+		}
+		return String.format("%s%s", 0, skuMonth);
+	}
+
+	/**
+	 * 获取车位
+	 */
+	private void getGroupTripsTicket(GoodsDonSku sku, Long userId) {
+		if (sku == null) {
+			throw BusinessRuntimeException.getInstance("规格不存在,请联系客服");
+		}
+		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
+		if (goodsDon == null) {
+			throw BusinessRuntimeException.getInstance("平台不存在,请联系客服");
+		}
+		User user = userMapper.selectById(userId);
+		if (user == null) {
+			throw BusinessRuntimeException.getInstance("用户不存在");
+		}
+		GroupsTrips groups = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class).eq(GroupsTrips::getSkuId, sku.getId()).gt(GroupsTrips::getAvailableNum, 0).last("limit 1").orderByAsc(GroupsTrips::getAvailableNum));
+		GroupsRelation relation = null;
+		if (groups == null) {
+			//新增车位 并关联
+			createNewGroupTripsAndRelation(sku, groups, relation);
+		} else {
+			//寻找快满编车队进行占座
+			relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+					.eq(GroupsRelation::getGroupsId, groups.getId())
+					.eq(GroupsRelation::getStatus, "none")
+					.eq(GroupsRelation::getUserId, 0)
+					.orderByAsc(GroupsRelation::getNum)
+					.last("limit 1")
+			);
+			//如果占位失败 新增车位并关联
+			if (relation == null) {
+				createNewGroupTripsAndRelation(sku, groups, relation);
+			}
+		}
+
+		try {
+			//锁定座位
+			setRelation(relation.getId(), userId);
+		} catch (Exception e) {
+			//失败重新给他分配车位
+			getGroupTripsTicket(sku, userId);
+		}
+		relation.setUserId(userId);
+		relation.setStatus(GroupsRelation.Status.locking);
+		groupsRelationMapper.updateById(relation);
+		Long relationId = relation.getId();
+
+		/* 生成订单 */
+		String donNo = SEQUENCE.nextId().toString();
+		OrderDon orderDon = new OrderDon();
+		orderDon.setOrderNo(donNo);
+		orderDon.setOpenId(user.getOpenId());
+		orderDon.setMoney(BigDecimal.ZERO);
+		orderDon.setSkuId(sku.getId());
+		orderDon.setRelationId(relationId);
+		//默认数量为1
+		orderDon.setNum(1);
+		orderDon.setGoodsId(sku.getGoodsId());
+		orderDon.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
+		//兑换码兑换
+		orderDon.setType(OrderDon.Type.EX_CODE);
+		orderDon.setUserId(user.getId());
+		orderDonMapper.insert(orderDon);
+
+		if (StrUtil.isNotEmpty(user.getOpenId())) {
+			//发送模板消息
+			THREAD_POOL_TASK_EXECUTOR.execute(() -> {
+				try {
+					log.info("用户{}兑换车位成功,发送新订单模板消息", user.getId());
+					orderDonService.newOderSendMsgByType(orderDon, goodsDon, sku, user);
+				} catch (Exception e) {
+					log.info("用户{}兑换车位成功,发送消息失败:{}", user.getId(), e);
+				}
+			});
+		}
+
+		THREAD_POOL_TASK_EXECUTOR.execute(() -> {
+			try {
+				//发送至客服消息
+				List<CustomerService> customerServices = customerServiceMapper.selectList(null);
+				for (CustomerService customerService : customerServices) {
+					log.info("向客服{}发送用户兑换码兑换车票消息通知..", customerService.getNickName());
+					orderDonService.newOderSendMsgByType(orderDon, goodsDon, sku, user);
+				}
+			} catch (Exception e) {
+				log.error("向客服发送用户{}兑换码兑换车票成功,发送消息失败:{}", user.getId(), e);
+			}
+		});
+	}
+
+	private void setRelation(Long relationId, Long userId) {
+		if (redisService.get(RedisKey.GROUPS_RELATION_NUM_KEY + relationId) != null) {
+			throw new BusinessRuntimeException("客官手慢啦,该座位已经有人啦!");
+		} else {
+			redisService.set(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L);
+			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+			int count = groupsMapper.decrAvailableNum(relation.getGroupsId());
+			if (count == 0) {
+				log.error("车位异常 groupId:{}", relation.getGroupsId());
+				throw new BusinessRuntimeException("车位异常");
+			}
+		}
+	}
+
+	/**
+	 * 新增新车位并关联车位
+	 */
+	private void createNewGroupTripsAndRelation(GoodsDonSku sku, GroupsTrips groups, GroupsRelation relation) {
+		//通过兑换码兑换车票 新增车位
+		if (groups == null) {
+			groups = new GroupsTrips();
+		}
+		//补充占位
+		if (relation == null) {
+			relation = new GroupsRelation();
+		}
+		groups.setSkuId(sku.getId());
+		groups.setNum(sku.getNum());
+		groups.setAvailableNum(sku.getNum());
+		groups.setStatus(GroupsTrips.Status.waiting);
+		groups.setRemark("等待设置账号");
+		groupsMapper.insert(groups);
+		relation.setGroupsId(groups.getId());
+		relation.setNum(1);
+		relation.setStatus(GroupsRelation.Status.locking);
+		relation.setGroupsId(groups.getId());
+		groupsRelationMapper.insert(relation);
+		//补充其他占位
+		for (int i = 2; i <= groups.getNum(); i++) {
+			GroupsRelation groupsRelation = new GroupsRelation();
+			groupsRelation.setGroupsId(groups.getId());
+			groupsRelation.setNum(i);
+			groupsRelation.setStatus(GroupsRelation.Status.none);
+			groupsRelationMapper.insert(groupsRelation);
+		}
+	}
+}

+ 8 - 0
netflix-service/src/main/java/com/cyksj/service/order/OrderDonService.java

@@ -2,7 +2,10 @@ package com.cyksj.service.order;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.cyksj.model.dto.H5JsPayParams;
+import com.cyksj.model.entity.GoodsDon;
+import com.cyksj.model.entity.GoodsDonSku;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.entity.User;
 import com.cyksj.model.manage.views.OrderDonView;
 import com.cyksj.model.request.OrderPayRequest;
 
@@ -38,4 +41,9 @@ public interface OrderDonService extends IService<OrderDon> {
     OrderDonView getDetail(Long orderId, long userId);
 
 	List<OrderDon> getAppleOneOrderDons(String appleOne, List<Long> skuIds, List<String> noOrderStatus, Date yesZeroDate, Date thisZeroDate);
+
+    /**
+     * 发送新订单模板消息
+     */
+	void newOderSendMsgByType(OrderDon order, GoodsDon goodsDon, GoodsDonSku sku, User user) throws Exception;
 }

+ 5 - 4
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -301,7 +301,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
         THREAD_POOL_TASK_EXECUTOR.execute(() -> {
             try {
                 //发送至用户消息
-                newOderSendMsgByType(order, goodsDon, sku, user, user.getOpenId());
+                this.newOderSendMsgByType(order, goodsDon, sku, user);
             } catch (Exception e) {
                 log.error("向用户发送新订单{}通知消息失败:{}", order.getOrderNo(), e);
             }
@@ -312,7 +312,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
                 List<CustomerService> customerServices = customerServiceMapper.selectList(null);
                 for (CustomerService customerService : customerServices) {
                     log.info("向客服{}发送新订单消息通知..", customerService.getNickName());
-                    newOderSendMsgByType(order, goodsDon, sku, user, customerService.getOpenId());
+                    this.newOderSendMsgByType(order, goodsDon, sku, user);
                 }
             } catch (Exception e) {
                 log.error("向客服发送新订单{}通知消息失败:{}", order.getOrderNo(), e);
@@ -320,7 +320,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
         });
     }
 
-    private void newOderSendMsgByType(OrderDon order, GoodsDon goodsDon, GoodsDonSku sku, User user, String toPeople) throws Exception {
+    @Override
+    public void newOderSendMsgByType(OrderDon order, GoodsDon goodsDon, GoodsDonSku sku, User user) throws Exception {
         TemplateEnum templateEnum = null;
         if (EnvCommonService.active.equals(envCommonService.getEnv())) {
             templateEnum = TemplateEnum.NEW_ORDER_ENV_TEMPLATE;
@@ -328,7 +329,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
             templateEnum = TemplateEnum.NEW_ORDER_PRD_TEMPLATE;
         }
         WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
-                .setToUser(toPeople)
+                .setToUser(user.getOpenId())
                 .setTemplateId(templateEnum.getTemplateId())
                 //TODO
                 .setUrl(" ");

+ 48 - 0
netflix-web/src/main/java/com/cyksj/web/aop/NoSubmitAop.java

@@ -0,0 +1,48 @@
+package com.cyksj.web.aop;
+
+import cn.hutool.extra.servlet.ServletUtil;
+import com.cyksj.common.annotation.NoSubmit;
+import com.cyksj.enums.GatewayResponse;
+import com.google.common.cache.Cache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author zwhui
+ * @date 2020/7/16 上午10:55
+ */
+@Aspect
+@Component
+public class NoSubmitAop {
+	private Log logger = LogFactory.getLog(getClass());
+
+	@Autowired
+	private Cache<String, Integer> cache;
+
+	@Around("execution(* com.cyksj..*.*(..)) && @annotation(nrs)")
+	public synchronized Object arround(ProceedingJoinPoint pjp, NoSubmit nrs) throws Throwable{
+
+		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+		HttpServletRequest request = attributes.getRequest();
+		String ipAddr = ServletUtil.getClientIP(request);
+		String key = ipAddr + "-" + request.getServletPath();
+		if (cache.getIfPresent(key) == null) {
+			// 如果缓存中有这个url视为重复提交
+			cache.put(key, 0);
+			return pjp.proceed();
+		} else {
+			logger.error("重复提交");
+			return GatewayResponse.FAIL.newBuilder().toResult("请勿重复提交");
+		}
+	}
+
+}

+ 68 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/exchange/ExchangeCodeController.java

@@ -0,0 +1,68 @@
+package com.cyksj.web.controller.manage.exchange;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.excel.ExcelExchangeCodeData;
+import com.cyksj.model.request.ExchangeCodeReq;
+import com.cyksj.model.views.ExchangeCodeView;
+import com.cyksj.service.exchange.ExchangeCodeService;
+import com.cyksj.web.util.EasyExcelUtils;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: ExchangeCodeController
+ *创建者: JavaZou
+ *创建时间:2022/11/7 14:37
+ */
+@RestController
+@RequestMapping("/manage/exchange/code")
+@RequiredArgsConstructor
+public class ExchangeCodeController {
+
+	private final HttpServletRequest request;
+
+	private final BeanSearcher beanSearcher;
+
+	private final ExchangeCodeService exchangeCodeService;
+
+	/**
+	 * 生成兑换码 并导出
+	 */
+	@PostMapping("/createAndExport")
+	private void createAndExportCode(@RequestBody ExchangeCodeReq exchangeCodeReq, HttpServletResponse response) {
+		if (ObjectUtil.hasEmpty(exchangeCodeReq.getGoodsId(), exchangeCodeReq.getSkuId(), exchangeCodeReq.getNumber())) {
+			throw BusinessRuntimeException.getInstance("必填项为空");
+		}
+		List<ExcelExchangeCodeData> data =  exchangeCodeService.createExchangeCode(exchangeCodeReq);
+		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelExchangeCodeData.class, data, "银河兑换码","银河兑换码", ExcelTypeEnum.XLSX, null);
+	}
+
+	/**
+	 * 兑换码列表
+	 */
+	@GetMapping("/get")
+	public Result<SearchResult<ExchangeCodeView>> getExchangeCodeView() {
+		return GatewayResponse.SUCCESS.newBuilder().toResult(beanSearcher.search(ExchangeCodeView.class, MapUtils.flat(request.getParameterMap())));
+	}
+
+	/**
+	 * 导出兑换码
+	 */
+	@GetMapping("/export")
+	public void exportExchangeCode(HttpServletResponse response) {
+		List<ExcelExchangeCodeData> data = beanSearcher.searchAll(ExcelExchangeCodeData.class, MapUtils.flat(request.getParameterMap()));
+		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelExchangeCodeData.class, data, "银河兑换码","银河兑换码", ExcelTypeEnum.XLSX, null);
+	}
+}

+ 13 - 5
netflix-web/src/main/java/com/cyksj/web/controller/user/UserController.java

@@ -3,13 +3,12 @@ package com.cyksj.web.controller.user;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.model.dto.UserWhoami;
-import com.cyksj.service.order.OrderDonService;
+import com.cyksj.model.request.ExchangeCodeTicket;
+import com.cyksj.service.exchange.ExchangeCodeService;
 import com.cyksj.service.user.UserService;
 import com.cyksj.web.util.StpUserUtil;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 /**
  * @author chan
@@ -22,7 +21,7 @@ public class UserController {
 
     private final UserService userService;
 
-    private final OrderDonService orderDonService;
+    private final ExchangeCodeService exchangeCodeService;
 
 
 
@@ -35,4 +34,13 @@ public class UserController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(userWhoami);
     }
 
+    /**
+     * 兑换码兑换车票
+     */
+    @PostMapping("/get/ticket")
+    public Result<String> getTicketByExchangeCode(@RequestBody ExchangeCodeTicket exchangeCodeTicket) {
+        Long userId = StpUserUtil.getLoginIdAsLong();
+        return exchangeCodeService.getTicketByExchangeCode(exchangeCodeTicket, userId);
+    }
+
 }