Bladeren bron

码库流水号加锁、生成码文件压缩包、导入码文件

bess-WeiganCai 4 jaren geleden
bovenliggende
commit
d1cf3e6e36
15 gewijzigde bestanden met toevoegingen van 603 en 163 verwijderingen
  1. 7 0
      abi-cloud-qr-platform-server/pom.xml
  2. 35 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/controller/console/QrDataController.java
  3. 8 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/controller/console/QrPackageController.java
  4. 24 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/dto/req/DownloadQrPackageReq.java
  5. 29 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/dto/req/ImportQrDataReq.java
  6. 0 23
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/dto/res/QrGenerateCode.java
  7. 22 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/config/RabbitmqConfig.java
  8. 59 55
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/mq/GenerateCodeConsumer.java
  9. 6 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/task/VerifyUniqueTask.java
  10. 0 82
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/util/ZipUtil.java
  11. 42 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/QrDataService.java
  12. 22 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/QrPackageService.java
  13. 255 0
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/impl/QrDataServiceImpl.java
  14. 92 2
      abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/impl/QrPackageServiceImpl.java
  15. 2 1
      abi-cloud-qr-platform-server/src/main/resources/dao/mapper/QrRepertoryMapper.xml

+ 7 - 0
abi-cloud-qr-platform-server/pom.xml

@@ -138,6 +138,13 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-mongodb</artifactId>
         </dependency>
+
+        <!-- redisson -->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson-spring-boot-starter</artifactId>
+            <version> 3.15.4</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 35 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/controller/console/QrDataController.java

@@ -0,0 +1,35 @@
+package com.abi.qms.platform.controller.console;
+
+import com.abi.qms.platform.dto.req.ImportQrDataReq;
+import com.abi.qms.platform.service.QrDataService;
+import com.abi.task.common.api.base.BaseResponse;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 码数据 Controller
+ *
+ * @author WeiganCai
+ * @date 2021-05-11
+ */
+@Slf4j
+@RestController
+@Api(tags = "码数据管理")
+@RequestMapping("/qrData")
+public class QrDataController {
+    @Autowired
+    private QrDataService qrDataService;
+
+    @ApiOperation("/导入码数据")
+    @PostMapping("/importQrData")
+    public BaseResponse importQrData(@Validated ImportQrDataReq importQrDataReq) {
+        qrDataService.importQrData(importQrDataReq);
+        return BaseResponse.create();
+    }
+
+}

+ 8 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/controller/console/QrPackageController.java

@@ -12,6 +12,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
+
 /**
  * 码包 Controller
  *
@@ -68,4 +70,10 @@ public class QrPackageController {
         return BaseResponse.create();
     }
 
+    @ApiOperation("/下载码包")
+    @GetMapping("/downloadQrPackage")
+    public void downloadQrPackage(@Validated DownloadQrPackageReq downloadQrPackageReq, HttpServletResponse response) {
+        qrPackageService.downloadQrPackage(downloadQrPackageReq, response);
+    }
+
 }

+ 24 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/dto/req/DownloadQrPackageReq.java

@@ -0,0 +1,24 @@
+package com.abi.qms.platform.dto.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 下载码包入参
+ *
+ * @author WeiganCai
+ * @date: 2021-05-11
+ */
+@Data
+@ApiModel
+public class DownloadQrPackageReq implements Serializable {
+
+	@NotNull(message = "id为空")
+	@ApiModelProperty(value = "id")
+	private Long id;
+
+}

+ 29 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/dto/req/ImportQrDataReq.java

@@ -0,0 +1,29 @@
+package com.abi.qms.platform.dto.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 导入码数据入参
+ *
+ * @author WeiganCai
+ * @date: 2021-05-11
+ */
+@Data
+@ApiModel
+public class ImportQrDataReq implements Serializable {
+
+	@NotNull(message = "码包id为空")
+	@ApiModelProperty(value = "码包id")
+	private Long qrPackageId;
+
+	@NotNull(message = "码数据文件为空")
+	@ApiModelProperty(value = "码数据文件")
+	private MultipartFile file;
+
+}

+ 0 - 23
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/dto/res/QrGenerateCode.java

@@ -1,23 +0,0 @@
-package com.abi.qms.platform.dto.res;
-
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * 生成码要用到的参数
- *
- * @author WeiganCai
- * @date: 2021-05-06
- */
-@Data
-@EqualsAndHashCode(callSuper = false)
-@Accessors(chain = true)
-public class QrGenerateCode {
-
-	/** 码库列id */
-	private Long qrRepertoryColumnId;
-
-	/** url前缀 */
-	private String urlPrefix;
-}

+ 22 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/config/RabbitmqConfig.java

@@ -0,0 +1,22 @@
+package com.abi.qms.platform.infrastructure.config;
+
+import com.abi.qms.platform.infrastructure.mq.GenerateCodeConsumer;
+import org.springframework.amqp.core.Queue;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * rabbitmq配置
+ *
+ * @author WeiganCai
+ * @date: 2021-05-11
+ */
+@Configuration
+public class RabbitmqConfig {
+
+	@Bean
+	public Queue declareQueue() {
+		return new Queue(GenerateCodeConsumer.GENERATE_CODE_BY_REPERTORY_QUEUE);
+	}
+
+}

+ 59 - 55
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/mq/GenerateCodeConsumer.java

@@ -1,5 +1,8 @@
 package com.abi.qms.platform.infrastructure.mq;
 
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileWriter;
+import cn.hutool.core.util.ZipUtil;
 import com.abi.qms.platform.dao.entity.QrData;
 import com.abi.qms.platform.dao.entity.QrInnerData;
 import com.abi.qms.platform.dao.entity.QrPackage;
@@ -15,21 +18,24 @@ import com.abi.qms.platform.dao.vo.result.QrRepertoryColumnVO;
 import com.abi.qms.platform.dao.vo.result.QrRepertoryVO;
 import com.abi.qms.platform.infrastructure.qr.build.parent.SerialBuildCode;
 import com.abi.qms.platform.infrastructure.util.BuildCodeUtil;
+import com.abi.qms.platform.service.QrDataService;
+import com.abi.qms.platform.service.QrPackageService;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.rabbitmq.client.Channel;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.springframework.amqp.rabbit.annotation.RabbitHandler;
 import org.springframework.amqp.rabbit.annotation.RabbitListener;
 import org.springframework.amqp.support.AmqpHeaders;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-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.messaging.Message;
 import org.springframework.stereotype.Component;
 
+import java.io.File;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -45,7 +51,7 @@ import java.util.stream.Collectors;
 @RabbitListener(queues = GenerateCodeConsumer.GENERATE_CODE_BY_REPERTORY_QUEUE)
 public class GenerateCodeConsumer {
 
-	public static final String GENERATE_CODE_BY_REPERTORY_QUEUE = "generate_code_queue_test";
+	public static final String GENERATE_CODE_BY_REPERTORY_QUEUE = "generate_code_queue";
 
 	@Autowired
 	private QrPackageMapper qrPackageMapper;
@@ -57,13 +63,19 @@ public class GenerateCodeConsumer {
 	private QrRepertorySerialNumberMapper qrRepertorySerialNumberMapper;
 
 	@Autowired
-	private MongoTemplate mongoTemplate;
+	private QrDataService qrDataService;
+
+	@Autowired
+	private QrPackageService qrPackageService;
+
+	@Autowired
+	private RedissonClient redissonClient;
 
 	/** 允许单个码生成重复的次数 */
 	private static final int REPEAT_BUILD_CODE_TIMES = 10;
 
 	/** 一次生成数量 */
-	private static final int ONCE_GENERATE_NUMBER = 5000;
+	public static final int ONCE_GENERATE_NUMBER = 5000;
 
 	/** 系统url */
 	@Value("${system.url}")
@@ -78,7 +90,7 @@ public class GenerateCodeConsumer {
 		} catch (Exception e) {
 			log.error("生成码失败", e);
 			// 回滚
-			rollback(qrPackage);
+			qrPackageService.failedRollback(qrPackage);
 		} finally {
 			// 手工ack
 			Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
@@ -93,6 +105,11 @@ public class GenerateCodeConsumer {
 		// 查询码包关联的幅面关联的码库的信息
 		QrPackageVO qrPackageVo = qrPackageMapper.selectQrPackageDetailById(qrPackage.getId());
 
+		// 码数据已生成,return
+		if (QrPackageGenerateStatusEnum.GENERATE_SUCCESS.is(qrPackageVo.getGenerateStatus())) {
+			return;
+		}
+
 		// 更改生成状态为生成中
 		QrPackage qrPackageUpdate = new QrPackage().setId(qrPackageVo.getId()).setGenerateStatus(QrPackageGenerateStatusEnum.GENERATING.getCode());
 		qrPackageMapper.updateById(qrPackageUpdate);
@@ -122,6 +139,10 @@ public class GenerateCodeConsumer {
 			} else if (QrFormatUrlEnum.DEPT_URL.is(urlType)) {
 				url = deptUrl;
 			}
+			// 如果url没有以/结尾,则加上/
+			if (StringUtils.isNotBlank(url) && !url.endsWith("/")) {
+				url += "/";
+			}
 			urlMap.put(qrRepertoryColumnId, url);
 
 			// 使用到流水号参数的列
@@ -133,15 +154,24 @@ public class GenerateCodeConsumer {
 					QueryWrapper<QrRepertorySerialNumber> qrRepertorySerialNumberQw = new QueryWrapper<>();
 					qrRepertorySerialNumberQw.eq("qr_repertory_column_id", qrRepertoryColumnId);
 					qrRepertorySerialNumberQw.eq("child_index", j);
-					QrRepertorySerialNumber qrRepertorySerialNumber = qrRepertorySerialNumberMapper.selectOne(qrRepertorySerialNumberQw);
-					if (qrRepertorySerialNumber == null) {
-						qrRepertorySerialNumber = new QrRepertorySerialNumber()
-								.setQrRepertoryId(qrRepertoryId)
-								.setQrRepertoryColumnId(qrRepertoryColumnId)
-								.setChildIndex(j)
-								.setSerialBuildClass(buildClass)
-								.setSerialNumber(0L);
-						qrRepertorySerialNumberMapper.insert(qrRepertorySerialNumber);
+
+					// 加锁查询插入流水号
+					RLock lock = redissonClient.getLock("serial_number_lock_" + qrRepertoryColumnId + "_" + j);
+					QrRepertorySerialNumber qrRepertorySerialNumber = null;
+					try {
+						lock.lock();
+						qrRepertorySerialNumber = qrRepertorySerialNumberMapper.selectOne(qrRepertorySerialNumberQw);
+						if (qrRepertorySerialNumber == null) {
+							qrRepertorySerialNumber = new QrRepertorySerialNumber()
+									.setQrRepertoryId(qrRepertoryId)
+									.setQrRepertoryColumnId(qrRepertoryColumnId)
+									.setChildIndex(j)
+									.setSerialBuildClass(buildClass)
+									.setSerialNumber(0L);
+							qrRepertorySerialNumberMapper.insert(qrRepertorySerialNumber);
+						}
+					} finally {
+						lock.unlock();
 					}
 
 					// key:qr_repertory_column_id + child_index
@@ -159,7 +189,7 @@ public class GenerateCodeConsumer {
 		// 生成码
 		while (true) {
 			// 查询已生成的数量
-			long alreadyGenerateNumber = selectCountByBatchNumber(batchNumber);
+			long alreadyGenerateNumber = qrDataService.selectCountByBatchNumber(batchNumber);
 
 			// 计算还需生成的数量
 			long needGenerateNumber = qrNumber - alreadyGenerateNumber;
@@ -221,7 +251,7 @@ public class GenerateCodeConsumer {
 			}
 
 			try {
-				mongoTemplate.insert(qrDataList, QrData.class);
+				qrDataService.batchInsert(qrDataList);
 			} catch (Exception e) {
 				// mongoDB唯一索引冲突导致插入失败,继续生成码
 				log.error("mongo insert error", e);
@@ -254,7 +284,8 @@ public class GenerateCodeConsumer {
 	 * 创建码文件
 	 */
 	private void createCodeFile(QrPackage qrPackage, QrRepertoryVO qrRepertory) {
-		List<QrData> qrDataList = selectCodeByBatch(qrPackage.getBatchNumber());
+		String batchNumber = qrPackage.getBatchNumber();
+		List<QrData> qrDataList = qrDataService.selectCodeByBatch(batchNumber);
 		List<QrRepertoryColumnVO> qrRepertoryColumnList = qrRepertory.getQrRepertoryColumnList();
 		StringBuilder content = new StringBuilder();
 		// 第一行为每列的名称,用“,”分隔
@@ -270,7 +301,15 @@ public class GenerateCodeConsumer {
 			content.append(innerData).append("\r\n");
 		}
 
-//		ByteArrayOutputStream zip = ZipUtil.zip(content.toString().getBytes(StandardCharsets.UTF_8), qrPackage.getBatchNumber() + ".txt");
+		// 生成txt和zip文件
+		FileWriter fileWriter = new FileWriter(batchNumber + ".txt");
+		fileWriter.write(content.toString());
+		File txtFile = fileWriter.getFile();
+		File zipFile = ZipUtil.zip(txtFile);
+
+		// 删除生成的临时文件,上传阿里云,并更新码包的下载路径
+		qrPackageService.delFileAndUpdatePath(txtFile, zipFile, qrPackage.getId());
+
 	}
 
 	/**
@@ -321,39 +360,4 @@ public class GenerateCodeConsumer {
 		}
 	}
 
-	/**
-	 * 回滚
-	 */
-	private void rollback(QrPackage qrPackage) {
-		// 删除本次生成的码
-		deleteCodeByBatchNumber(qrPackage.getBatchNumber());
-
-		// 更改生成状态为生成失败
-		QrPackage qrPackageUpdate = new QrPackage().setId(qrPackage.getId()).setGenerateStatus(QrPackageGenerateStatusEnum.GENERATE_FAIL.getCode());
-		qrPackageMapper.updateById(qrPackageUpdate);
-	}
-
-	/**
-	 * 根据批次号查询码数量
-	 */
-	private long selectCountByBatchNumber(String batchNumber) {
-		Query query = new Query();
-		query.addCriteria(Criteria.where("batchNumber").is(batchNumber));
-		return mongoTemplate.count(query, QrData.class);
-	}
-
-	/**
-	 * 根据批次号删除码
-	 */
-	private void deleteCodeByBatchNumber(String batchNumber) {
-		Query query = new Query();
-		query.addCriteria(Criteria.where("batchNumber").is(batchNumber));
-		mongoTemplate.remove(query, QrData.class);
-	}
-
-	private List<QrData> selectCodeByBatch(String batchNumber) {
-		Query query = new Query();
-		query.addCriteria(Criteria.where("batchNumber").is(batchNumber));
-		return mongoTemplate.find(query, QrData.class);
-	}
 }

+ 6 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/task/VerifyUniqueTask.java

@@ -2,6 +2,7 @@ package com.abi.qms.platform.infrastructure.task;
 
 import com.abi.qms.platform.dao.entity.QrFormat;
 import com.abi.qms.platform.dao.enums.QrFormatUniqueStatusEunm;
+import com.abi.qms.platform.dao.enums.QrFormatVerifyUniqueEnum;
 import com.abi.qms.platform.dao.mapper.QrFormatMapper;
 import com.abi.qms.platform.infrastructure.qr.build.parent.SerialBuildCode;
 import com.abi.qms.platform.infrastructure.util.BuildCodeUtil;
@@ -36,12 +37,17 @@ public class VerifyUniqueTask implements Runnable {
     @Override
     public void run() {
         log.info("正在对[id:{}, name:{}]码格式进行重复率验证", qrFormat.getId(), qrFormat.getName());
+
         // 验证开始时间
         long startTime = System.currentTimeMillis();
         // 码格式验证标识
         boolean flag = true;
 
         try {
+            // 验证中
+            QrFormat update = new QrFormat().setId(qrFormat.getId()).setUniqueStatus(QrFormatUniqueStatusEunm.VERIFYING.getCode());
+            qrFormatMapper.updateById(update);
+
             // 获取用于生成码的class类
             String[] buildClassArr = qrFormat.getCodeVariableBuildClass().split(",");
             // 用Set存放不重复的码

+ 0 - 82
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/util/ZipUtil.java

@@ -1,82 +0,0 @@
-package com.abi.qms.platform.infrastructure.util;
-
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-/**
- * 压缩文件工具类
- *
- * @author WeiganCai
- * @date 2021-04-19
- */
-@Slf4j
-public class ZipUtil {
-
-    /**
-     * 压缩文件
-     *
-     * @param bytes
-     * @return
-     */
-    public static ByteArrayOutputStream zip(byte[] bytes, String zipEntryName) {
-        ZipOutputStream zos = null;
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        try {
-            zos = new ZipOutputStream(baos);
-            zos.putNextEntry(new ZipEntry(zipEntryName));
-            zos.write(bytes);
-        } catch (Exception e) {
-            log.error("压缩ZIP失败", e);
-        } finally {
-            if (zos != null) {
-                try {
-                    zos.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
-        }
-        return baos;
-    }
-
-
-    /**
-     * 解压zip
-     * @param byteArrayInputStream
-     * @return
-     */
-    public static ByteArrayInputStream unzip(ByteArrayInputStream byteArrayInputStream){
-        ZipInputStream zis = null;
-        try{
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            zis = new ZipInputStream(byteArrayInputStream);
-            zis.getNextEntry();
-            byte[] buffer = new byte[10240];
-
-            int offset;
-            while((offset = zis.read(buffer)) != -1) {
-                out.write(buffer, 0, offset);
-            }
-
-            return new ByteArrayInputStream(out.toByteArray());
-        }catch (Exception e){
-            log.error("解压zip失败", e);
-        }finally {
-            if(zis != null) {
-                try {
-                    zis.close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
-        }
-        return null;
-    }
-
-}

+ 42 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/QrDataService.java

@@ -0,0 +1,42 @@
+package com.abi.qms.platform.service;
+
+import com.abi.qms.platform.dao.entity.QrData;
+import com.abi.qms.platform.dto.req.ImportQrDataReq;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+
+import java.util.List;
+
+/**
+ * 码数据 Service接口
+ *
+ * @author WeiganCai
+ * @date: 2021-05-11
+ */
+public interface QrDataService {
+	/**
+	 * 根据批次号查询码数量
+	 */
+	 long selectCountByBatchNumber(String batchNumber) ;
+
+	/**
+	 * 根据批次号删除码
+	 */
+	 void deleteCodeByBatchNumber(String batchNumber);
+
+	/**
+	 * 根据批次号查询码
+	 */
+	 List<QrData> selectCodeByBatch(String batchNumber);
+
+	/**
+	 * 批量插入
+	 */
+	void batchInsert(List<QrData> qrDataList);
+
+	/**
+	 * 导入码数据
+	 */
+	void importQrData(ImportQrDataReq importQrDataReq);
+
+}

+ 22 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/QrPackageService.java

@@ -1,10 +1,15 @@
 package com.abi.qms.platform.service;
 
 
+import com.abi.qms.platform.dao.entity.QrPackage;
 import com.abi.qms.platform.dto.req.*;
 import com.abi.qms.platform.dto.res.GetQrPackageDetailRes;
 import com.abi.qms.platform.dto.res.ListQrPackageRes;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+
 /**
  * 码包 Service接口
  *
@@ -42,4 +47,21 @@ public interface QrPackageService {
 	 * 审核拒绝码包
 	 */
 	void refuseQrPackage(RefuseQrPackageReq refuseQrPackageReq);
+
+	/**
+	 * 下载码包
+	 */
+	void downloadQrPackage(DownloadQrPackageReq downloadQrPackageReq, HttpServletResponse response);
+
+	/**
+	 * 删除生成的临时文件,上传阿里云,并更新码包的下载路径
+	 * @param txtFile txt文件
+	 * @param zipFile 压缩文件
+	 */
+	void delFileAndUpdatePath(File txtFile, File zipFile, Long qrPackageId);
+
+	/**
+	 * 码生成失败回滚
+	 */
+	void failedRollback(QrPackage qrPackage);
 }

+ 255 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/impl/QrDataServiceImpl.java

@@ -0,0 +1,255 @@
+package com.abi.qms.platform.service.impl;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ZipUtil;
+import com.abi.qms.platform.dao.entity.QrData;
+import com.abi.qms.platform.dao.entity.QrInnerData;
+import com.abi.qms.platform.dao.entity.QrPackage;
+import com.abi.qms.platform.dao.enums.QrPackageGenerateStatusEnum;
+import com.abi.qms.platform.dao.mapper.QrPackageMapper;
+import com.abi.qms.platform.dao.mapper.QrRepertoryMapper;
+import com.abi.qms.platform.dao.vo.result.QrPackageVO;
+import com.abi.qms.platform.dao.vo.result.QrRepertoryColumnVO;
+import com.abi.qms.platform.dao.vo.result.QrRepertoryVO;
+import com.abi.qms.platform.dto.req.ImportQrDataReq;
+import com.abi.qms.platform.infrastructure.mq.GenerateCodeConsumer;
+import com.abi.qms.platform.service.QrDataService;
+import com.abi.qms.platform.service.QrPackageService;
+import com.abi.task.common.api.exception.BusinessException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+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.web.multipart.MultipartFile;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 码数据 Service业务层处理
+ *
+ * @author WeiganCai
+ * @date: 2021-05-11
+ */
+@Service
+@Slf4j
+public class QrDataServiceImpl implements QrDataService {
+
+	@Autowired
+	private MongoTemplate mongoTemplate;
+
+	@Autowired
+	private QrPackageMapper qrPackageMapper;
+
+	@Autowired
+	private QrRepertoryMapper qrRepertoryMapper;
+
+	@Autowired
+	private QrPackageService qrPackageService;
+
+	/**
+	 * 根据批次号查询码数量
+	 */
+	public long selectCountByBatchNumber(String batchNumber) {
+		Query query = new Query();
+		query.addCriteria(Criteria.where("batchNumber").is(batchNumber));
+		return mongoTemplate.count(query, QrData.class);
+	}
+
+	/**
+	 * 根据批次号删除码
+	 */
+	public void deleteCodeByBatchNumber(String batchNumber) {
+		Query query = new Query();
+		query.addCriteria(Criteria.where("batchNumber").is(batchNumber));
+		mongoTemplate.remove(query, QrData.class);
+	}
+
+	/**
+	 * 根据批次号查询码
+	 */
+	public List<QrData> selectCodeByBatch(String batchNumber) {
+		Query query = new Query();
+		query.addCriteria(Criteria.where("batchNumber").is(batchNumber));
+		return mongoTemplate.find(query, QrData.class);
+	}
+
+	/**
+	 * 批量插入
+	 */
+	@Override
+	public void batchInsert(List<QrData> qrDataList) {
+		mongoTemplate.insert(qrDataList, QrData.class);
+	}
+
+	/**
+	 * 导入码数据
+	 */
+	@Override
+	public void importQrData(ImportQrDataReq req) {
+		// 校验txt文件
+		MultipartFile file = req.getFile();
+		checkTxtFile(file);
+
+		// 查询码包
+		Long qrPackageId = req.getQrPackageId();
+		QrPackageVO qrPackageVo = qrPackageMapper.selectQrPackageDetailById(qrPackageId);
+		List<QrData> qrDataList = new LinkedList<>();
+
+		// 从txt中提取码数据
+		txtToQrData(file, qrPackageVo, qrDataList);
+
+		try {
+			// 插入文本中的数据
+			insertImportData(qrPackageVo, qrDataList);
+
+			// 压缩文件
+			zipFile(file, qrPackageVo);
+
+			// 更改生成状态为已生成
+			QrPackage update = new QrPackage().setId(qrPackageVo.getId()).setGenerateStatus(QrPackageGenerateStatusEnum.GENERATE_SUCCESS.getCode());
+			qrPackageMapper.updateById(update);
+
+		} catch (Exception e) {
+			log.error("导入码失败", e);
+			qrPackageService.failedRollback(new QrPackage().setId(qrPackageVo.getId()).setBatchNumber(qrPackageVo.getBatchNumber()));
+		}
+	}
+
+	/**
+	 * 压缩文件
+	 */
+	private void zipFile(MultipartFile file, QrPackageVO qrPackageVo) {
+		try {
+			File txtFile = File.createTempFile(qrPackageVo.getBatchNumber(), "txt");
+			file.transferTo(txtFile);
+			// 按照批次号.txt的方式重命名
+			txtFile = FileUtil.rename(txtFile, qrPackageVo.getBatchNumber() + ".txt", true, true);
+
+			// 压缩文件
+			File zipFile = ZipUtil.zip(txtFile);
+
+			// 删除生成的临时文件,上传阿里云,并更新码包的下载路径
+			qrPackageService.delFileAndUpdatePath(txtFile, zipFile, qrPackageVo.getId());
+		} catch (IOException e) {
+			log.error("IOException", e);
+			throw new BusinessException("压缩文件错误");
+		}
+	}
+
+	/**
+	 * 从txt中提取码数据
+	 * @param file txt文件
+	 * @param qrPackageVo 码包
+	 * @param qrDataList 码数据
+	 */
+	private void txtToQrData(MultipartFile file, QrPackageVO qrPackageVo, List<QrData> qrDataList) {
+		// 码库 & 码库的列
+		QrRepertoryVO qrRepertory = qrRepertoryMapper.selectQrRepertoryDetailById(qrPackageVo.getQrRepertoryId());
+		List<QrRepertoryColumnVO> qrRepertoryColumnList = qrRepertory.getQrRepertoryColumnList();
+
+		BufferedReader br = null;
+		try {
+			br = new BufferedReader(new InputStreamReader(file.getInputStream()));
+			String line = null;
+			while ((line = br.readLine()) != null) {
+				if (StringUtils.isBlank(line)) {
+					continue;
+				}
+
+				line = line.replace(",", ",");
+				// 跳过特殊字符
+				if (!line.matches("[0-9a-zA-Z\\.\\,\\:\\-/]*")) {
+					continue;
+				}
+
+				String[] codeArr = line.split(",");
+				// 验证导入的码列数是否与定义的码库列数一致
+				if (codeArr.length != qrRepertoryColumnList.size()) {
+					throw new BusinessException("导入文件的码列数与码库定义的码列数不一致,导入失败");
+				}
+
+				QrData qrData = new QrData();
+				List<QrInnerData> qrInnerDataList = new LinkedList<>();
+				LocalDateTime localDateTime = LocalDateTime.now();
+
+				for (int i = 0; i < codeArr.length; i++) {
+					String urlCode = codeArr[i];
+					// 分离url与code
+					int urlIndex = urlCode.lastIndexOf("/") + 1;
+					QrInnerData qrInnerData = new QrInnerData();
+					qrInnerData.setUrl(urlCode.substring(0, urlIndex));
+					qrInnerData.setCode(urlCode.substring(urlIndex));
+					qrInnerData.setQrRepertoryColumnId(qrRepertoryColumnList.get(i).getId());
+					qrInnerData.setSortNumber(i);
+
+					qrInnerDataList.add(qrInnerData);
+				}
+
+				qrData.setQrRepertoryId(qrRepertory.getId());
+				qrData.setBatchNumber(qrPackageVo.getBatchNumber());
+				qrData.setQrRepertoryType(qrRepertory.getType());
+				qrData.setInnerDataList(qrInnerDataList);
+				qrData.setCreateTime(localDateTime);
+
+				qrDataList.add(qrData);
+			}
+		} catch (IOException e) {
+			log.error("读取码文件出错", e);
+			throw new BusinessException("读取码文件出错");
+		} finally {
+			IoUtil.close(br);
+		}
+	}
+
+	/**
+	 * 校验txt文件
+	 * @param file
+	 */
+	private void checkTxtFile(MultipartFile file) {
+		if (file != null && file.getSize() > 0) {
+			String fileName = file.getOriginalFilename();
+			String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
+			if (!"txt".equals(suffix)) {
+				throw new BusinessException("上传文件格式不正确");
+			}
+		} else {
+			throw new BusinessException("上传文件为空");
+		}
+	}
+
+	/**
+	 * 插入文本中的数据
+	 * @param qrPackageVo 码包
+	 * @param qrDataList 码数据List
+	 */
+	private void insertImportData(QrPackageVO qrPackageVo, List<QrData> qrDataList) {
+		try {
+			List<QrData> batchInsertList = new ArrayList<>();
+			for (int i = 0; i < qrDataList.size(); i++) {
+				batchInsertList.add(qrDataList.get(i));
+				// 每5000个插入一次
+				if (i > 0 && i % GenerateCodeConsumer.ONCE_GENERATE_NUMBER == 0) {
+					batchInsert(batchInsertList);
+					batchInsertList.clear();
+				}
+			}
+			if (!batchInsertList.isEmpty()) {
+				batchInsert(batchInsertList);
+			}
+		} catch (Exception e) {
+			log.error("导入的码插入mongodb失败", e);
+			throw new BusinessException("导入的码重复或与系统中的码重复,导入失败");
+		}
+	}
+}

+ 92 - 2
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/impl/QrPackageServiceImpl.java

@@ -1,25 +1,38 @@
 package com.abi.qms.platform.service.impl;
 
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
 import com.abi.qms.platform.dao.entity.QrPackage;
 import com.abi.qms.platform.dao.enums.QrPackageApplyStatusEnum;
+import com.abi.qms.platform.dao.enums.QrPackageGenerateStatusEnum;
+import com.abi.qms.platform.dao.enums.QrRepertoryTypeEnum;
 import com.abi.qms.platform.dao.mapper.QrPackageMapper;
+import com.abi.qms.platform.dao.mapper.QrRepertoryMapper;
 import com.abi.qms.platform.dao.vo.result.QrPackageVO;
 import com.abi.qms.platform.dto.req.*;
 import com.abi.qms.platform.dto.res.GetQrPackageDetailRes;
 import com.abi.qms.platform.dto.res.ListQrPackageRes;
+import com.abi.qms.platform.infrastructure.mq.GenerateCodeConsumer;
 import com.abi.qms.platform.infrastructure.util.AssertUtil;
 import com.abi.qms.platform.infrastructure.util.PageUtil;
 import com.abi.qms.platform.infrastructure.util.UserUtil;
+import com.abi.qms.platform.service.QrDataService;
 import com.abi.qms.platform.service.QrPackageService;
 import com.abi.task.common.api.exception.BusinessException;
 import com.abi.task.common.utils.PojoConverterUtils;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.AmqpTemplate;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
 import java.util.List;
 
 /**
@@ -29,11 +42,18 @@ import java.util.List;
  * @date 2021-04-28
  */
 @Service
+@Slf4j
 public class QrPackageServiceImpl implements QrPackageService {
 
 	@Autowired
 	private QrPackageMapper qrPackageMapper;
 
+	@Autowired
+	private AmqpTemplate amqpTemplate;
+
+	@Autowired
+	private QrDataService qrDataService;
+
 	/**
 	 * 保存码包
 	 */
@@ -138,7 +158,7 @@ public class QrPackageServiceImpl implements QrPackageService {
 	@Override
 	@Transactional(rollbackFor = Exception.class)
 	public void passQrPackage(PassQrPackageReq req) {
-		QrPackage qrPackage = qrPackageMapper.selectById(req.getId());
+		QrPackageVO qrPackage = qrPackageMapper.selectQrPackageDetailById(req.getId());
 		AssertUtil.isNull(qrPackage, "码包不存在");
 
 		// 只有待审核、审核拒绝状态下才可通过
@@ -150,7 +170,10 @@ public class QrPackageServiceImpl implements QrPackageService {
 		QrPackage update = new QrPackage().setId(req.getId()).setApplyStatus(QrPackageApplyStatusEnum.REVIEW_PASS.getCode());
 		qrPackageMapper.updateById(update);
 
-		// TODO 生成码
+		// 码库类型为系统生成时,放入rabbitmq,生成码
+		if (QrRepertoryTypeEnum.SYSTEM_GENERATE.is(qrPackage.getQrRepertoryType())) {
+			amqpTemplate.convertAndSend(GenerateCodeConsumer.GENERATE_CODE_BY_REPERTORY_QUEUE, qrPackage);
+		}
 	}
 
 	/**
@@ -171,4 +194,71 @@ public class QrPackageServiceImpl implements QrPackageService {
 		QrPackage update = new QrPackage().setId(req.getId()).setApplyStatus(QrPackageApplyStatusEnum.REVIEW_REFUSE.getCode());
 		qrPackageMapper.updateById(update);
 	}
+
+	@Override
+	public void downloadQrPackage(DownloadQrPackageReq req, HttpServletResponse response) {
+		QrPackage qrPackage = qrPackageMapper.selectById(req.getId());
+		AssertUtil.isNull(qrPackage, "码包不存在");
+
+		// 只有已生成状态下才可下载
+		Integer generateStatus = qrPackage.getGenerateStatus();
+		if (!QrPackageGenerateStatusEnum.GENERATE_SUCCESS.is(generateStatus)) {
+			throw new BusinessException("码包不可下载");
+		}
+
+		BufferedInputStream in = null;
+		BufferedOutputStream out = null;
+		try {
+			// TODO 从阿里云OSS读取文件,替换下面的测试代码
+			// TODO in = new BufferedInputStream(ossObject.getObjectContent());
+			in = new BufferedInputStream(new FileInputStream(new File("E:\\firefox_download\\202104161027287567357.zip")));
+			out = new BufferedOutputStream(response.getOutputStream());
+
+			String fileName = qrPackage.getBatchNumber() + ".zip";
+			response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName,"utf-8"));
+
+			byte [] b = new byte[1024];
+			int len = 0;
+			while(-1 != (len = in.read(b))) {
+				out.write(b, 0, len);
+			}
+			out.flush();
+		} catch (IOException e) {
+			log.error("IOException", e);
+		} finally {
+			IoUtil.close(in);
+			IoUtil.close(out);
+		}
+
+	}
+
+	/**
+	 * 删除生成的临时文件,并更新码包的下载路径
+	 * @param txtFile txt文件
+	 * @param zipFile 压缩文件
+	 */
+	@Override
+	public void delFileAndUpdatePath(File txtFile, File zipFile, Long qrPackageId) {
+		// TODO 上传zip包到阿里云OSS后,删除本次生成的txt和zip文件
+		FileUtil.del(txtFile);
+		FileUtil.del(zipFile);
+
+		// TODO 更新下载路径
+		String downloadPath = "";
+		QrPackage update = new QrPackage().setId(qrPackageId).setDownloadPath(downloadPath);
+		qrPackageMapper.updateById(update);
+	}
+
+	/**
+	 * 码生成失败回滚
+	 */
+	@Override
+	public void failedRollback(QrPackage qrPackage) {
+		// 删除本次生成的码
+		qrDataService.deleteCodeByBatchNumber(qrPackage.getBatchNumber());
+
+		// 更改生成状态为生成失败
+		QrPackage qrPackageUpdate = new QrPackage().setId(qrPackage.getId()).setGenerateStatus(QrPackageGenerateStatusEnum.GENERATE_FAIL.getCode());
+		qrPackageMapper.updateById(qrPackageUpdate);
+	}
 }

+ 2 - 1
abi-cloud-qr-platform-server/src/main/resources/dao/mapper/QrRepertoryMapper.xml

@@ -33,6 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <resultMap id="QrRepertoryQrRepertoryColumnResult" type="com.abi.qms.platform.dao.vo.result.QrRepertoryVO" extends="QrRepertoryResult">
         <result property="deptName" column="dept_name"/>
+        <result property="deptUrl" column="dept_url"/>
         <result property="updateByName" column="update_by_name"/>
         <collection property="qrRepertoryColumnList" notNullColumn="id" javaType="java.util.List" resultMap="QrRepertoryColumnResult" />
     </resultMap>
@@ -55,7 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             url_type,
             remark
         FROM qr_repertory_column
-        WHERE qr_repertory_id = #{id}
+        WHERE qr_repertory_id = #{id} AND is_delete = 0
     </select>
 
     <select id="listQrRepertory" resultMap="iPageResult">