Browse Source

VOLTA EDI

wfg 5 months ago
parent
commit
79c0d8c7c1

+ 4 - 1
blade-common/pom.xml

@@ -90,7 +90,10 @@
             <version>2.0</version>
         </dependency>
 
-
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 52 - 0
blade-service-api/blade-los-api/src/main/java/org/springblade/los/business/sea/entity/Bills.java

@@ -2182,5 +2182,57 @@ public class Bills implements Serializable {
 	@TableField(exist = false)
 	private List<LosAuditPathsLevels> auditPathsLevels;
 
+	/**
+	 * BookingId MagicHand 用
+	 */
+	@TableField(exist = false)
+	private long bookingId;
+
+	/**
+	 * masterBookingNo MagicHand 用
+	 */
+	@TableField(exist = false)
+	private String masterBookingNo;
+
+	/**
+	 * ServiceCode MagicHand 用
+	 */
+	@TableField(exist = false)
+	private String serviceCode;
+
+	/**
+	 * fNLTransMode MagicHand 用
+	 */
+	@TableField(exist = false)
+	private String fNLTransMode;
+
+	/**
+	 * agentCode MagicHand 用
+	 */
+	@TableField(exist = false)
+	private String agentCode;
 
+	/**
+	 * IsFeederBooking MagicHand 用
+	 */
+	@TableField(exist = false)
+	private Boolean isFeederBooking;
+
+	/**
+	 * statusCode MagicHand 用
+	 */
+	@TableField(exist = false)
+	private String statusCode;
+
+	/**
+	 * DataTypeCode MagicHand 用
+	 */
+	@TableField(exist = false)
+	private String dataTypeCode;
+
+	/**
+	 * DataTypeCode MagicHand 用
+	 */
+	@TableField(exist = false)
+	private Boolean isAMS;
 }

+ 18 - 0
blade-service/blade-los/pom.xml

@@ -156,6 +156,24 @@
             <artifactId>commons-collections4</artifactId>
             <version>4.4</version>
         </dependency>
+        <!-- wfg MSC Import 解压 zip 文件 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-compress</artifactId>
+            <version>1.20</version> <!-- 请替换为实际的最新版本号 1.27.1 -->
+        </dependency>
+        <!-- wfg MSC XML 转 JSON -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.11.4</version>
+        </dependency>
+        <!-- wfg MSC XML 转 JSON -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+            <version>2.11.4</version>
+        </dependency>
         <dependency>
             <groupId>org.springblade</groupId>
             <artifactId>blade-user-api</artifactId>

+ 122 - 0
blade-service/blade-los/src/main/java/org/springblade/los/Util/GsonXml2Json.java

@@ -0,0 +1,122 @@
+package org.springblade.los.Util;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class GsonXml2Json {
+//	private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+	/**
+	 * 将 XML 字符串转换为 JSON 字符串
+	 * @param xml XML 字符串
+	 * @return 格式化后的 JSON 字符串
+	 * @throws Exception 解析异常
+	 */
+	public static String convert(String xml) throws Exception {
+		Map<String, Object> map = xmlToMap(xml);
+		// Gson gson = new GsonBuilder().setPrettyPrinting().create();
+		Gson gson = new Gson();
+		return gson.toJson(map);
+	}
+
+	public static JsonObject convertJsonObject(String xml) throws Exception {
+		Map<String, Object> map = xmlToMap(xml);
+		Gson gson = new Gson();
+		JsonElement je = gson.toJsonTree(map);
+		// 转换为 JsonObject
+		JsonObject jo = je.getAsJsonObject();
+		return jo;
+	}
+
+	/**
+	 * 将 XML 转换为 Map 结构
+	 */
+	private static Map<String, Object> xmlToMap(String xml) throws Exception {
+		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+		DocumentBuilder builder = factory.newDocumentBuilder();
+		Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
+
+		Element root = doc.getDocumentElement();
+		return parseNode(root);
+	}
+
+	/**
+	 * 递归解析 DOM 节点
+	 */
+	private static Map<String, Object> parseNode(Node node) {
+		Map<String, Object> map = new HashMap<>();
+
+		// 处理节点属性
+		if (node.getAttributes() != null && node.getAttributes().getLength() > 0) {
+			Map<String, String> attrs = new HashMap<>();
+			for (int i = 0; i < node.getAttributes().getLength(); i++) {
+				Node attr = node.getAttributes().item(i);
+				attrs.put(attr.getNodeName(), attr.getNodeValue());
+			}
+			map.put("@attributes", attrs);
+		}
+
+		// 处理子节点
+		NodeList nodeList = node.getChildNodes();
+		for (int i = 0; i < nodeList.getLength(); i++) {
+			Node childNode = nodeList.item(i);
+
+			// 跳过空白文本节点
+			if (childNode.getNodeType() == Node.TEXT_NODE &&
+				childNode.getTextContent().trim().isEmpty()) {
+				continue;
+			}
+
+			// 处理文本内容
+			if (childNode.getNodeType() == Node.TEXT_NODE) {
+				map.put("#text", childNode.getTextContent().trim());
+				continue;
+			}
+
+			String nodeName = childNode.getNodeName();
+			Object nodeValue = parseNode(childNode);
+
+			// 处理重复节点(转换为数组)
+			if (map.containsKey(nodeName)) {
+				Object existingValue = map.get(nodeName);
+				List<Object> values = new ArrayList<>();
+				if (existingValue instanceof List) {
+					values = (List<Object>) existingValue;
+				} else {
+					values.add(existingValue);
+				}
+				values.add(nodeValue);
+				map.put(nodeName, values);
+			} else {
+				map.put(nodeName, nodeValue);
+			}
+		}
+
+		// 如果是空节点且没有属性,则返回空字符串
+		if (map.isEmpty()) {
+			return new HashMap<>();
+		}
+
+		// 如果只有文本内容,直接返回文本
+		if (map.size() == 1 && map.containsKey("#text")) {
+			return new HashMap<String, Object>() {{
+				put("#text", map.get("#text"));
+			}};
+		}
+
+		return map;
+	}
+}

+ 39 - 0
blade-service/blade-los/src/main/java/org/springblade/los/business/sea/controller/MscZipImportController.java

@@ -0,0 +1,39 @@
+package org.springblade.los.business.sea.controller;
+
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import org.springblade.common.annotation.RepeatSubmit;
+import org.springblade.core.tool.api.R;
+import org.springblade.los.business.sea.entity.Bills;
+import org.springblade.los.business.sea.service.IMscZipImportService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+@RestController
+@AllArgsConstructor
+@RequestMapping("/magicHandImport")
+public class MscZipImportController {
+
+	private final IMscZipImportService mscZipImportService;
+
+	/**
+	 * MSC MagicHand 上传 zip 订舱压缩包
+	 */
+	@PostMapping("/importZip")
+	@RepeatSubmit
+	public R<List<Bills>> importZip(@RequestParam("file") MultipartFile file) throws Exception {
+		return mscZipImportService.importZip(file);
+	}
+
+	/**
+	 * MSC MagicHand 上传 zip 订舱压缩包解压后的 sea.Bills 数据
+	 */
+	@PostMapping("/submit")
+	@RepeatSubmit
+	public R submit(@RequestBody @Validated List<Bills> mhBills) throws Exception {
+		return mscZipImportService.submit(mhBills);
+	}
+}

+ 17 - 0
blade-service/blade-los/src/main/java/org/springblade/los/business/sea/service/IMscZipImportService.java

@@ -0,0 +1,17 @@
+package org.springblade.los.business.sea.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springblade.core.tool.api.R;
+import org.springblade.los.business.sea.entity.Bills;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+public interface IMscZipImportService extends IService<Bills> {
+
+	R<List<Bills>> importZip(MultipartFile file) throws Exception;
+
+	R submit(List<Bills> mhBills) throws Exception;
+}

+ 1253 - 0
blade-service/blade-los/src/main/java/org/springblade/los/business/sea/service/impl/MscZipImportImpl.java

@@ -0,0 +1,1253 @@
+package org.springblade.los.business.sea.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.compress.utils.IOUtils;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.los.basic.business.entity.BusinessType;
+import org.springblade.los.basic.business.service.IBusinessTypeService;
+import org.springblade.los.basic.corps.entity.BCorps;
+import org.springblade.los.basic.corps.service.IBCorpsService;
+import org.springblade.los.basic.vessels.entity.BVessels;
+import org.springblade.los.basic.vessels.service.IBVesselsService;
+import org.springblade.los.basic.ports.entity.BPorts;
+import org.springblade.los.basic.ports.service.IBPortsService;
+import org.springblade.los.basic.lines.entity.BLines;
+import org.springblade.los.basic.lines.service.IBLinesService;
+import org.springblade.los.basic.packages.service.IBPackagesService;
+import org.springblade.los.basic.packages.entity.BPackages;
+import org.springblade.los.basic.cntr.service.IBCntrTypesService;
+import org.springblade.los.basic.cntr.entity.BCntrTypes;
+import org.springblade.los.billno.entity.BusinessBillNo;
+import org.springblade.los.billno.service.IBusinessBillNoService;
+import org.springblade.los.business.sea.entity.Bills;
+import org.springblade.los.business.sea.mapper.BillsMapper;
+import org.springblade.los.business.sea.service.IBillsService;
+import org.springblade.los.business.sea.entity.SeaBillsDetail;
+import org.springblade.los.business.sea.service.ISeaBillsDetailService;
+import org.springblade.los.business.sea.service.IMscZipImportService;
+import org.springblade.los.business.sea.entity.PreContainers;
+import org.springblade.los.business.sea.service.IPreContainersService;
+import org.springblade.los.business.sea.entity.Containers;
+import org.springblade.los.business.sea.service.IContainersService;
+import org.springblade.los.business.sea.entity.ContainersCommodity;
+import org.springblade.los.business.sea.service.IContainersCommodityService;
+import org.springblade.system.entity.Dept;
+import org.springblade.system.feign.ISysClient;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.*;
+import java.nio.charset.StandardCharsets;
+
+import org.springblade.los.Util.*;
+
+@Service
+@AllArgsConstructor
+public class MscZipImportImpl extends ServiceImpl<BillsMapper, Bills> implements IMscZipImportService {
+
+	private final IBillsService bBillsService;
+	private final ISeaBillsDetailService bSeaBillsDetailService;
+	private final IPreContainersService bPreContainersService;
+	private final IContainersService bContainersService;
+	private final IContainersCommodityService bContainersCommodityService;
+
+	private final IBCorpsService bCorpsService;
+
+	private final IBVesselsService bVesselsService;
+	private final IBPortsService bPortsService;
+	private final IBLinesService bLinesService;
+	private final IBPackagesService bPackagesService;
+	private final IBCntrTypesService bCntrTypesService;
+
+	private final ISysClient sysClient;
+	private final IUserClient userClient;
+	private final IDeptUtils deptUtils;
+
+	private final IBusinessTypeService bBusinessTypeService;
+	private final IBusinessBillNoService businessBillNoService;
+
+	public static String[] extractZip(File zipFile, File destDir) throws IOException {
+		List<String> destFiles = new ArrayList<>();
+
+		// 确保目标目录存在
+		if (!destDir.exists()) {
+			destDir.mkdirs();
+		}
+
+		try (ZipFile zip = new ZipFile(zipFile)) {
+			Enumeration<ZipArchiveEntry> entries = zip.getEntries();
+
+			while (entries.hasMoreElements()) {
+				ZipArchiveEntry entry = entries.nextElement();
+				Path entryPath = Paths.get(destDir.getPath(), entry.getName());
+
+				// 确保父目录存在
+				File parent = entryPath.getParent().toFile();
+				if (!parent.exists()) {
+					parent.mkdirs();
+				}
+
+				if (entry.isDirectory()) {
+					// 如果是目录,创建目录
+					entryPath.toFile().mkdirs();
+				} else {
+					// 如果是文件,解压文件
+					try (InputStream in = zip.getInputStream(entry);
+					     OutputStream out = Files.newOutputStream(entryPath)) {
+						IOUtils.copy(in, out);
+						try{
+							destFiles.add(entryPath.toRealPath().toString());
+						}catch(Exception e){
+							// NoSuchFileException
+						}
+					}
+				}
+			}
+		}
+
+		return destFiles.stream().toArray(String[]::new);
+	}
+
+	public static String[] extractLargeZip(File zipFile, File destDir) throws IOException {
+		List<String> destFiles = new ArrayList<>();
+
+		if (!destDir.exists()) {
+			destDir.mkdirs();
+		}
+
+		try (InputStream fis = new FileInputStream(zipFile);
+		     ZipArchiveInputStream zis = new ZipArchiveInputStream(fis)) {
+
+			ZipArchiveEntry entry;
+			while ((entry = zis.getNextZipEntry()) != null) {
+				File entryFile = new File(destDir, entry.getName());
+
+				if (entry.isDirectory()) {
+					entryFile.mkdirs();
+				} else {
+					try (OutputStream out = new FileOutputStream(entryFile)) {
+						byte[] buffer = new byte[8192];
+						int len;
+						while ((len = zis.read(buffer)) > 0) {
+							out.write(buffer, 0, len);
+						}
+					}
+					try{
+						destFiles.add(entryFile.toString());
+					}catch(Exception e){
+						// NoSuchFileException
+					}
+				}
+			}
+		}
+
+		return destFiles.stream().toArray(String[]::new);
+	}
+
+	// Jackson 无法转数组
+	/*
+	public static String XmlToJsonWithJackson(Path xmlFile) throws Exception {
+		// String xml = "";
+		// try {
+		// 	xml = new String(Files.readAllBytes(xmlFile), StandardCharsets.UTF_8);
+		// 	System.out.println(xml);
+		// } catch (IOException e) {
+		// 	xml = "";
+		// }
+		//
+		// if(xml.isEmpty()) return "";
+
+		byte[] content = null;
+		try {
+			content = Files.readAllBytes(xmlFile);
+		} catch (Exception e) {
+			content = null;
+		}
+
+		if (ObjectUtils.isNull(content)) {
+			return "";
+		}
+
+		// 创建XmlMapper
+		XmlMapper xmlMapper = new XmlMapper();
+
+		// 将XML读取为JsonNode
+		// JsonNode node = xmlMapper.readTree(content);
+		JsonNode node = xmlMapper.readValue(content, JsonNode.class);
+
+		// 创建ObjectMapper用于输出JSON
+		ObjectMapper jsonMapper = new ObjectMapper();
+
+		// 转换为JSON字符串
+		// String json = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
+		String json = jsonMapper.writeValueAsString(node);
+		System.out.println(json);
+
+		return json;
+	}
+
+	 */
+
+	public static String XmlToJsonWithGson(Path xmlFile) throws Exception {
+		String xml = "";
+		try {
+			xml = new String(Files.readAllBytes(xmlFile), StandardCharsets.UTF_8);
+			System.out.println(xml);
+		} catch (IOException e) {
+			xml = "";
+		}
+
+		if(xml.isEmpty()) return "";
+
+		String json = GsonXml2Json.convert(xml);
+		return json;
+	}
+
+	public static JsonObject XmlToJsonObjectWithGson(Path xmlFile) throws Exception {
+		String xml = "";
+		try {
+			xml = new String(Files.readAllBytes(xmlFile), StandardCharsets.UTF_8);
+			System.out.println(xml);
+		} catch (IOException e) {
+			xml = "";
+		}
+
+		if(xml.isEmpty()) return null;
+
+		JsonObject jo = GsonXml2Json.convertJsonObject(xml);
+		return jo;
+	}
+
+	public static String getJsonValueAsString(JsonElement jo, String name){
+		JsonElement je = ObjectUtils.isNotNull(jo) && jo.isJsonObject() ? ((JsonObject) jo).get(name) : null;
+		if(ObjectUtils.isNotNull(je)){
+			return je.getAsString().trim();
+		}else{
+			return "";
+		}
+	}
+
+	public static long getJsonValueAsLong(JsonElement jo, String name){
+		JsonElement je = ObjectUtils.isNotNull(jo) && jo.isJsonObject() ? ((JsonObject) jo).get(name) : null;
+		if(ObjectUtils.isNotNull(je)){
+			String s = je.getAsString().trim();
+			long l = 0;
+			try {
+				l = Long.parseLong(s);
+			}catch (Exception e){
+				l = 0;
+			}
+			return l;
+		}else{
+			return 0;
+		}
+	}
+
+	public static BigDecimal getJsonValueAsBigDecimal(JsonElement jo, String name) {
+		BigDecimal ret;
+
+		JsonElement je = ObjectUtils.isNotNull(jo) && jo.isJsonObject() ? ((JsonObject) jo).get(name) : null;
+		String s = ObjectUtils.isNotNull(je) ? je.getAsString().trim() : "";
+		if(ObjectUtils.isNotNull(s)){
+			try{
+				ret = new BigDecimal(s);
+			}catch (Exception e){
+				ret = new BigDecimal("0.00");
+			}
+		}else{
+			ret = new BigDecimal("0.00");
+		}
+		ret.setScale(3, RoundingMode.HALF_UP);
+		return ret;
+	}
+
+	public static boolean getJsonValueAsBool(JsonElement jo, String name){
+		JsonElement je = ObjectUtils.isNotNull(jo) && jo.isJsonObject() ? ((JsonObject) jo).get(name) : null;
+		if(ObjectUtils.isNotNull(je)){
+			return "true".compareTo(je.getAsString().trim().toLowerCase())==0;
+		}else{
+			return false;
+		}
+	}
+
+	public BCorps getCorpByCode(List<BCorps> corps, String code){
+		BCorps corp = null;
+		if (ObjectUtils.isNotNull(code)) {
+			if(ObjectUtils.isNotNull(corps)){
+				Optional<BCorps> corpsOptional = corps.stream()
+					.filter(v -> code.compareToIgnoreCase(v.getCode())==0)
+					.findFirst();
+				corp = corpsOptional.isPresent() ? corpsOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(corp)) {
+				corp = bCorpsService.getOne(new LambdaQueryWrapper<BCorps>()
+					.eq(BCorps::getTenantId, AuthUtil.getTenantId())
+					.eq(BCorps::getIsDeleted, 0)
+					.eq(BCorps::getCode, code)
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(corp)){
+					corps.add(corp);
+				}
+			}
+		}
+
+		return corp;
+	}
+
+	public BVessels getVesselByEnName(List<BVessels> vessels, Bills bill, String enName){
+		BVessels vessel = null;
+		if (ObjectUtils.isNotNull(enName)) {
+			if(ObjectUtils.isNotNull(vessels)){
+				Optional<BVessels> vesselsOptional = vessels.stream()
+					.filter(v -> enName.compareToIgnoreCase(v.getEnName())==0)
+					.findFirst();
+				vessel = vesselsOptional.isPresent() ? vesselsOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(vessel)) {
+				vessel = bVesselsService.getOne(new LambdaQueryWrapper<BVessels>()
+					.eq(BVessels::getTenantId, AuthUtil.getTenantId())
+					.eq(BVessels::getIsDeleted, 0)
+					.like(BVessels::getEnName, enName)
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(vessel)){
+					vessels.add(vessel);
+				}
+			}
+
+			if (ObjectUtils.isNotNull(vessel) && ObjectUtils.isNotNull(bill)) {
+				bill.setVesselEnName(vessel.getEnName());
+				bill.setVesselCnName(vessel.getCnName());
+				bill.setVesselId(vessel.getId());
+			}
+		}
+
+		return vessel;
+	}
+
+	public BPorts getPortByCode(List<BPorts> ports, String name){
+		BPorts port = null;
+		if (ObjectUtils.isNotNull(name)) {
+			if(ObjectUtils.isNotNull(ports)){
+				Optional<BPorts> portsOptional = ports.stream()
+					.filter(v -> name.compareToIgnoreCase(v.getCode())==0)
+					.findFirst();
+				port = portsOptional.isPresent() ? portsOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(port)) {
+				port = bPortsService.getOne(new LambdaQueryWrapper<BPorts>()
+					.eq(BPorts::getTenantId, AuthUtil.getTenantId())
+					.eq(BPorts::getIsDeleted, 0)
+					.eq(BPorts::getCode, name)
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(port)){
+					port.setUnCode("");
+					ports.add(port);
+				}
+			}
+		}
+
+		return port;
+	}
+
+	public BPorts getPortByEdiCode(List<BPorts> ports, String name){
+		BPorts port = null;
+		if (ObjectUtils.isNotNull(name)) {
+			if(ObjectUtils.isNotNull(ports)){
+				Optional<BPorts> portsOptional = ports.stream()
+					.filter(v -> name.compareToIgnoreCase(v.getUnCode())==0)
+					.findFirst();
+				port = portsOptional.isPresent() ? portsOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(port)) {
+				port = bPortsService.getOne(new LambdaQueryWrapper<BPorts>()
+					.eq(BPorts::getTenantId, AuthUtil.getTenantId())
+					.eq(BPorts::getIsDeleted, 0)
+					.like(BPorts::getExtendedData, "\"name\": \"MSC\", \"value\": \"" + name + "\"")
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(port)){
+					port.setUnCode(name);
+					ports.add(port);
+				}
+			}
+		}
+
+		return port;
+	}
+
+	public BLines getLineByEdiCode(List<BLines> lines, String name){
+		BLines line = null;
+		if (ObjectUtils.isNotNull(name)) {
+			if(ObjectUtils.isNotNull(lines)){
+				Optional<BLines> linesOptional = lines.stream()
+					.filter(v -> name.compareToIgnoreCase(v.getCode())==0)
+					.findFirst();
+				line = linesOptional.isPresent() ? linesOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(line)) {
+				line = bLinesService.getOne(new LambdaQueryWrapper<BLines>()
+					.eq(BLines::getTenantId, AuthUtil.getTenantId())
+					.eq(BLines::getIsDeleted, 0)
+					.eq(BLines::getStatus, 0)
+					.like(BLines::getExtendedData, "{\"name\": \"MSC\", \"value\": \"" + name + "\"}")
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(line)){
+					line.setCode(name);
+					lines.add(line);
+				}
+			}
+		}
+
+		return line;
+	}
+
+	public BCntrTypes getCntrTypeByEdiCode(List<BCntrTypes> cts, String name){
+		if(name=="40HF"){
+			System.out.println("getCntrTypeByEdiCode " + name);
+		}
+		BCntrTypes cntrType = null;
+		if (ObjectUtils.isNotNull(name)) {
+			if(ObjectUtils.isNotNull(cts)){
+				Optional<BCntrTypes> linesOptional = cts.stream()
+					.filter(v -> name.compareToIgnoreCase(v.getCode())==0)
+					.findFirst();
+				cntrType = linesOptional.isPresent() ? linesOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(cntrType)) {
+				cntrType = bCntrTypesService.getOne(new LambdaQueryWrapper<BCntrTypes>()
+					.eq(BCntrTypes::getTenantId, AuthUtil.getTenantId())
+					.eq(BCntrTypes::getIsDeleted, 0)
+					.like(BCntrTypes::getExtendedData, "{\"name\": \"MSC\", \"value\": \"" + name + "\"}")
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(cntrType)){
+					cntrType.setCode(name);
+					cts.add(cntrType);
+				}
+			}
+		}
+
+		return cntrType;
+	}
+
+	public BPackages getPackageByEdiCode(List<BPackages> pkgs, String name){
+		BPackages pkg = null;
+		if (ObjectUtils.isNotNull(name)) {
+			if(ObjectUtils.isNotNull(pkgs)){
+				Optional<BPackages> linesOptional = pkgs.stream()
+					.filter(v -> name.compareToIgnoreCase(v.getCode())==0)
+					.findFirst();
+				pkg = linesOptional.isPresent() ? linesOptional.get() : null;
+			}
+
+			if(ObjectUtils.isNull(pkg)) {
+				pkg = bPackagesService.getOne(new LambdaQueryWrapper<BPackages>()
+					.eq(BPackages::getTenantId, AuthUtil.getTenantId())
+					.eq(BPackages::getIsDeleted, 0)
+					.like(BPackages::getExtendedData, "{\"name\": \"MSC\", \"value\": \"" + name + "\"}")
+					.last("limit 1"));
+				if(ObjectUtils.isNotNull(pkg)){
+					pkg.setCode(name);
+					pkgs.add(pkg);
+				}
+			}
+		}
+
+		return pkg;
+	}
+
+	public static int getCompanyTypeId(String t){
+		// TYPEID	COMPANYCODE	COMPANYTYPE
+		// 1	    16	        SHIPPER
+		// 2	    17	        CONSIGN
+		// 3	    18	        NOTIFY1
+		// 4	    19	        NOTIFY2
+		// 5	    V1	        VIP
+		// 6	    NA	        NVOCC
+		// 7	    26	        ELSEINVOICE
+		// 8	    30	        DGCONSIGN
+		// 9	    34	        BKGINVOICE
+		// 10	    22	        CONTRACTHOLDER
+		// 11	    24	        NAMEACCOUNT
+		// 12	    25	        FFSHIPPER
+		return 0;
+	}
+
+	@Override
+	public R<List<Bills>> importZip(MultipartFile file) throws Exception {
+		List<Bills> mhBills = new ArrayList<>();
+
+		// 创建临时文件
+		Path tempFile = Files.createTempFile("magicHand-", ".mh");
+		System.out.println(tempFile.toString());
+
+		// 将MultipartFile的内容复制到临时文件
+		Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
+
+		// 创建FileInputStream来读取临时文件
+		FileInputStream fis = new FileInputStream(tempFile.toFile());
+
+		String[] files = extractZip(tempFile.toFile(), tempFile.getParent().toFile());
+		List<JsonObject> jsonObjs = new ArrayList<>();
+		for (String s : files) {
+			System.out.println(s);
+			// xml 文件
+			if(s.endsWith(".xml")) {
+				Path xmlFile = Paths.get(s);
+				try {
+					// String json = XmlToJsonWithJackson(xmlFile);
+					JsonObject jo = XmlToJsonObjectWithGson(xmlFile);
+					jsonObjs.add(jo);
+				}catch (Exception e){
+
+				}
+			}
+		}
+
+		List<BCorps> corps = new ArrayList<>();
+
+		// 市场部
+		BCorps marketCorp = getCorpByCode(corps, "QDJMSMARKET");
+		if(ObjectUtils.isNull(marketCorp)){
+			throw new RuntimeException("编码为 QDJMSMARKET 的市场部往来单位不存在!");
+		}
+
+		List<BVessels> vessels = new ArrayList<>();
+		List<BPorts> ports = new ArrayList<>();
+		List<BLines> lines = new ArrayList<>();
+		List<BCntrTypes> cntrTypes = new ArrayList<>();
+		List<BPackages> packages = new ArrayList<>();
+
+		String deptId;
+		String deptName;
+		Long branchId = deptUtils.getDeptPid();
+		R<String> bdr = sysClient.getDeptName(branchId);
+		String branchName = bdr.isSuccess() ? bdr.getData() : "";
+		//获取部门ids对应中文名
+		if (ObjectUtils.isNotNull(deptUtils.getDeptPid() + "")) {
+			deptId = deptUtils.getDeptPid() + "";
+			R<List<String>> res = sysClient.getDeptNames(deptUtils.getDeptPid() + "");
+			if (res.isSuccess() && ObjectUtils.isNotNull(res.getData())) {
+				deptName = String.join(",", res.getData());
+			} else {
+				deptName = "";
+			}
+		} else {
+			deptName = "";
+			deptId = "";
+		}
+
+		jsonObjs.forEach(jo->{
+			JsonArray jaBookings = jo.getAsJsonArray("Booking");
+			JsonArray jaBookingTransports = jo.getAsJsonArray("BookingTransport");
+			JsonArray jaCompanys = jo.getAsJsonArray("Company");
+			JsonArray jaContainers = jo.getAsJsonArray("Container");
+			JsonArray jaContainerSealNumbers = jo.getAsJsonArray("ContainerSealNumber");
+			JsonArray jaCargos = jo.getAsJsonArray("Cargo");
+			// 化学品信息
+			// "CargoChemical": [
+			// 	{
+			// 		"@attributes": {
+			// 			"DCPackageTypeCode": "I",
+			// 			"CargoId": "480361",
+			// 			"EmergencyProvider": "TIBA CHINA (SHANGHAI) CO., LTD.",
+			// 			"DCPackingCode": "31HA1     ",
+			// 			"DCWeight": "21600",
+			// 			"PSName": "SODIUM DIMETHYL DITHIOCARBAMATE (SDDC) 40% MIN.",
+			// 			"EmergencyPhone": "86-13563629797",
+			// 			"OuterQTY": "18",
+			// 			"PSNCode": "UN3082*3    ",
+			// 			"InnerQTY": "0",
+			// 			"MPCode": "P",
+			// 			"ContactName": "MR.PAUL",
+			// 			"IsReportableQTY": "true"
+			// 		}
+			// 	}
+			// ]
+			JsonArray jaCargoChemicals = jo.getAsJsonArray("CargoChemical");
+
+			if(ObjectUtils.isNotNull(jaBookings) && !jaBookings.isEmpty()){
+				jaBookings.forEach(jaBooking->{
+					JsonObject job = (JsonObject) jaBooking;
+					JsonObject joa = job.getAsJsonObject("@attributes");
+
+					long bookingId = getJsonValueAsLong(joa, "BookingId");
+
+					// 根据订舱号查询是否存在 getJsonValueAsString(joa, "BookingNumber")
+					boolean isNewBill = true;
+					Bills bill = bBillsService.getOne(new LambdaQueryWrapper<Bills>()
+						.eq(Bills::getTenantId, AuthUtil.getTenantId())
+						.eq(Bills::getIsDeleted, 0)
+						.eq(Bills::getTeamId, AuthUtil.getPostId())
+						.eq(Bills::getBookingNo, getJsonValueAsString(joa, "BookingNumber"))
+						.last("limit 1"));
+
+					SeaBillsDetail seaBillsDetail;
+					if(ObjectUtils.isNotNull(bill)){
+						isNewBill = false;
+
+						seaBillsDetail = bSeaBillsDetailService.getOne(new LambdaQueryWrapper<SeaBillsDetail>()
+							.eq(SeaBillsDetail::getTenantId, AuthUtil.getTenantId())
+							.eq(SeaBillsDetail::getBranchId, branchId + "")
+							.eq(SeaBillsDetail::getPid, bill.getId()));
+					}else {
+						bill = new Bills();
+						seaBillsDetail = new SeaBillsDetail();
+					}
+					bill.setDetail(seaBillsDetail);
+
+					List<PreContainers> preContainers = new ArrayList<>();
+					bill.setPreContainersList(preContainers);
+
+					List<Containers> containers = new ArrayList<>();
+					bill.setContainersList(containers);
+
+					String commodityShName = "";
+					String commodityEnName = "";
+					String hsCode = "";
+					String goodsDescr = "";
+					String markDescr = "";
+					BigDecimal billQty = new BigDecimal("0.00").setScale(3, RoundingMode.HALF_UP);
+					BigDecimal billGw = new BigDecimal("0.00").setScale(3, RoundingMode.HALF_UP);
+					BigDecimal billVol = new BigDecimal("0.00").setScale(3, RoundingMode.HALF_UP);
+
+					BPackages billPkg = null;
+
+					mhBills.add(bill);
+
+					if(isNewBill) {
+						// 赋默认值
+						// 基础信息
+						// billDate: dateFormat(new Date(), "yyyy-MM-dd") + ' 00:00', // 单据日期 默认 当天
+						// 	operatorName: JSON.parse(localStorage.getItem('saber-userInfo')).content.user_name, // OP 默认登录人
+						// 	operatorId: JSON.parse(localStorage.getItem('saber-userInfo')).content.user_id, // OP 默认登录人
+						// 	businessType: 'SE', // 业务类型 默认 海运出口
+						// 	businessTypes: '海运',
+						// 	billType: 'DD', // 单据类型 默认 直单
+						// 	accDeptName: JSON.parse(localStorage.getItem('sysitemData')).deptName,
+						// 	accDept: JSON.parse(localStorage.getItem('sysitemData')).deptId,
+						// 	operatorDeptName: JSON.parse(localStorage.getItem('sysitemData')).deptName,
+						// 	operatorDept: JSON.parse(localStorage.getItem('sysitemData')).deptId,
+						// 	issueType: "ORI", // 签单方式 默认 正本提单
+						// 	mpaymode: 'PP', // 主单付费方式 默认PP
+						// 	hpaymode: 'PP', // 分单付费方式 默认 PP
+						// 	loadType: JSON.parse(localStorage.getItem('saber-tenantId')).content == '409341' ? 'FCL' : '整箱', // 装箱方式默认整箱
+						// 	srcType: 'OWN', // 业务来源默认公司   来源 内容 默认登录人所属公司
+						// 	numberOfObl: 'THREE', // 正本份数 默认 THREE
+						// 	numberOfCopy: 'THREE', // 副本份数 默认 THREE
+						// 	seaType: 'E', // 进出口 默认出口 E=出口 I=进口"
+						// 	cargoType: 'dry', // 货物类型默认普货
+						// 	marks: 'N/M',
+						// 	dgPackingLevel: '0', // 危险品包装等级
+						// 	serviceTerms: 'CY-CY', // 服务方式
+						// 	chargeableWeight: '方',
+
+						bill.setCreateTime(new Date());
+						bill.setCreateUser(AuthUtil.getUserId());
+						bill.setCreateUserName(AuthUtil.getUserName());
+						bill.setTenantId(AuthUtil.getTenantId());
+						bill.setBranchId(branchId + "");
+						bill.setBillDate(new Date());
+						bill.setOperatorId(AuthUtil.getUserId());
+						bill.setOperatorName(AuthUtil.getUserName());
+						Dept dept = deptUtils.getDept(AuthUtil.getDeptId());
+						if (dept != null) {
+							bill.setOperatorDept(dept.getId() + "");
+							bill.setOperatorDeptName(dept.getDeptName());
+							bill.setAccDept(dept.getId() + "");
+							bill.setAccDeptName(dept.getDeptName());
+
+							seaBillsDetail.setCreateDept(dept.getId() + "");
+							seaBillsDetail.setCreateDeptName(dept.getDeptName());
+						}
+						bill.setBusinessType("SE");
+						bill.setBusinessTypes("海运");
+						bill.setBillType("DD");
+						bill.setIssueType("ORI");
+						bill.setMpaymode("PP");
+						bill.setHpaymode("PP");
+						bill.setLoadType("FCL");
+						bill.setSrcType("OWN");
+						bill.setSrcId(branchId);
+						bill.setSrcCnName(branchName);
+						bill.setSrcEnName(branchName);
+						bill.setNumberOfObl("THREE");
+						bill.setNumberOfCopy("THREE");
+						bill.setSeaType("E");
+						bill.setCargoType("dry");
+						bill.setMarks("N/M");
+						bill.setDgPackingLevel("0");
+						bill.setServiceTerms("CY-CY");
+						bill.setChargeableWeight("方");
+
+						// 船公司 MSC
+						bill.setCarrierId(8L);
+						bill.setCarrierShortName("地中海航运");
+						bill.setCarrierCnName("地中海航运(香港)有限公司");
+						bill.setCarrierEnName("MEDITERRANEAN SHIPPING COMPANY (HONGKONG)LIMITED");
+
+						// 客户 市场部 QDJMSMARKET
+						bill.setCorpId(marketCorp.getId());
+						bill.setCorpCnName(marketCorp.getCnName());
+						bill.setCorpEnName(marketCorp.getEnName());
+
+						seaBillsDetail.setId(null);
+						seaBillsDetail.setPid(null);
+						seaBillsDetail.setTenantId(AuthUtil.getTenantId());
+						seaBillsDetail.setBranchId(branchId + "");
+						seaBillsDetail.setCreateUser(AuthUtil.getUserId());
+						seaBillsDetail.setCreateUserName(AuthUtil.getUserName());
+						seaBillsDetail.setCreateTime(new Date());
+					}else{
+						// 保存时删除
+						// SQL.Add('Delete from BscntrQty ');
+						// SQL.Add('WHERE SYSID=:SYSID AND ENTITYID=:ENTITYID ');
+						// ParamByName('Sysid').AsInteger:=FSysID;
+						// ParamByName('Entityid').AsInteger:=AEntityID;
+						// ExecSQL(false);
+						//
+						// SQL.Add('Delete from SeabillsCntrs');
+						// SQL.Add('WHERE SYSID=:SYSID AND ENTITYID=:ENTITYID ');
+						// ParamByName('Sysid').AsInteger:=FSysID;
+						// ParamByName('Entityid').AsInteger:=AEntityID;
+						// ExecSQL(false);
+						//
+						// SQL.Add('Delete from SeabillsCntrsCargo');
+						// SQL.Add('WHERE SYSID=:SYSID AND ENTITYID=:ENTITYID ');
+						// ParamByName('Sysid').AsInteger:=FSysID;
+						// ParamByName('Entityid').AsInteger:=AEntityID;
+						// ExecSQL(false);
+					}
+
+					bill.setBusinessTypeCode("HYCK");
+					bill.setBillNoFormat("HYCK");
+
+					bill.setBookingId(bookingId);
+					bill.setBookingNo(getJsonValueAsString(joa, "BookingNumber"));
+					bill.setMblno("");
+					bill.setHblno("");
+					bill.setMasterBookingNo(getJsonValueAsString(joa, "MasterBookingNumber"));
+					bill.setAgentCode(getJsonValueAsString(joa, "AgentCode"));
+					bill.setStatusCode(getJsonValueAsString(joa, "StatusCode"));
+					// 船名
+					getVesselByEnName(vessels, bill, getJsonValueAsString(joa, "Vessel"));
+					bill.setVoyageNo(getJsonValueAsString(joa, "Voyage"));
+					bill.setServiceCode(getJsonValueAsString(joa, "ServiceCode"));
+					bill.setIsFeederBooking(getJsonValueAsBool(joa, "IsFeederBooking"));
+					bill.setCorpArgreementNo(getJsonValueAsString(joa, "ServiceContract"));
+					String s = getJsonValueAsString(joa, "PaymentTypeCode");
+					bill.setMpaymode("P".equals(s) ? "PP" : ("C".equals(s) ? "CC" : "FPA"));
+					if(isNewBill){
+						bill.setHpaymode("PP");
+					}
+
+					// Ports
+					BPorts port = getPortByCode(ports, "CNTAO");
+					if(ObjectUtils.isNotNull(port)) {
+						bill.setPlaceReceiptId(port.getId());
+						bill.setPlaceReceiptCode(port.getCode());
+						bill.setPlaceReceiptName(port.getEnName());
+						bill.setPlaceReceiptNamePrint(port.getEnName());
+					}
+
+					// 不能为空
+					s = getJsonValueAsString(joa, "POLCode");
+					if(ObjectUtils.isNotNull(s)){
+						port = getPortByEdiCode(ports, s);
+						if(ObjectUtils.isNotNull(port)) {
+							bill.setPolId(port.getId());
+							bill.setPolCode(port.getCode());
+							bill.setPolCnName(port.getCnName());
+							bill.setPolEnName(port.getEnName());
+							bill.setPolNamePrint(port.getEnName());
+						}
+					}
+
+					// 不能为空
+					s = getJsonValueAsString(joa, "PODCode");
+					if(ObjectUtils.isNotNull(s)){
+						port = getPortByEdiCode(ports, s);
+						if(ObjectUtils.isNotNull(port)) {
+							bill.setPodId(port.getId());
+							bill.setPodCode(port.getCode());
+							bill.setPodCnName(port.getCnName());
+							bill.setPodEnName(port.getEnName());
+							bill.setPodNamePrint(port.getEnName());
+						}
+					}
+
+					s = getJsonValueAsString(joa, "FinalDestCode");
+					if(ObjectUtils.isNotNull(s)){
+						port = getPortByEdiCode(ports, s);
+						if(ObjectUtils.isNotNull(port)) {
+							bill.setFinalDestinationId(port.getId());
+							bill.setFinalDestinationCode(port.getCode());
+							bill.setFinalDestinationName(port.getEnName());
+							s = getJsonValueAsString(joa, "FinalDest");
+							bill.setFinalDestinationNamePrint(ObjectUtils.isNotNull(s) ? s : port.getEnName());
+						}
+					}
+
+					// 航线 不能为空
+					bill.setLineId(0L);
+					bill.setLineCnName("");
+					bill.setLineEnName("");
+					s = getJsonValueAsString(joa, "ServiceCode");
+					if(ObjectUtils.isNotNull(s)) {
+						BLines line = getLineByEdiCode(lines, s);
+						if(ObjectUtils.isNotNull(line)) {
+							bill.setLineId(line.getId());
+							bill.setLineCnName(line.getCnName());
+							bill.setLineEnName(line.getEnName());
+						}
+					}
+
+					s = "";
+					if(ObjectUtils.isNotNull(jaBookingTransports)){
+						for (JsonElement ja : jaBookingTransports) {
+							JsonObject jb = (JsonObject) ja;
+							JsonObject jc = jb.getAsJsonObject("@attributes");
+							if(getJsonValueAsLong(jc, "BookingId") == bookingId){
+								s = getJsonValueAsString(jc, "TransportModeCode");
+								break;
+							}
+						}
+					}
+
+					String ftm = "";
+					switch (s){
+						case "BRG":
+						case "TRK":
+						case "RAL":
+						case "RTK":
+						case "FEE":
+							ftm = s;
+							break;
+						case "BRGTRK":
+							ftm = "BTK";
+							break;
+						case "BRGRAL":
+							ftm = "BRA";
+							break;
+						case "RALTRK":
+							ftm = "RLT";
+							break;
+					}
+					bill.setFNLTransMode(ftm);
+
+					// 不能为空
+					s = getJsonValueAsString(joa, "TermsCode");
+					switch (s){
+						case "CYRA":
+							ftm = "CY-RAMP";
+							break;
+						case "CYCY":
+							ftm = "CY-CY";
+							break;
+						case "CYDR":
+							ftm = "CY-DOOR";
+							break;
+						case "CYFO":
+							ftm = "CY-FO";
+							break;
+						default:
+							ftm = "";
+					}
+					bill.setServiceTerms(ftm);
+
+					bill.setBookingRemarks(getJsonValueAsString(joa, "Comment"));
+
+					bill.setDataTypeCode(getJsonValueAsString(joa, "DataTypeCode"));
+					bill.setIsAMS(getJsonValueAsBool(joa, "IsAMS"));
+
+					// 收发通
+					// TYPEID	COMPANYCODE	COMPANYTYPE
+					// 1	    16	        SHIPPER
+					// 2	    17	        CONSIGN
+					// 3	    18	        NOTIFY1
+					// 4	    19	        NOTIFY2
+					// 5	    V1	        VIP
+					// 6	    NA	        NVOCC
+					// 7	    26	        ELSEINVOICE
+					// 8	    30	        DGCONSIGN
+					// 9	    34	        BKGINVOICE
+					// 10	    22	        CONTRACTHOLDER
+					// 11	    24	        NAMEACCOUNT
+					// 12	    25	        FFSHIPPER
+					if(ObjectUtils.isNotNull(jaCompanys) && !jaCompanys.isEmpty()){
+						List<JsonElement> removeEles = new ArrayList<>();
+						jaCompanys.forEach(joCompany->{
+							JsonObject joc = (JsonObject) joCompany;
+							JsonObject jod = joc.getAsJsonObject("@attributes");
+							if(getJsonValueAsLong(jod, "BookingId") == bookingId){
+								String ds = getJsonValueAsString(jod, "Company") + "\n"
+									      + getJsonValueAsString(jod, "CompanyAddress") + "\n"
+  									      + "TEL:" + getJsonValueAsString(jod, "PhoneNumber") + "\n"
+									      + "FAX:" + getJsonValueAsString(jod, "FaxNumber");
+								if("SHIPPER".equals(getJsonValueAsString(jod, "CompanyTypeCode"))){
+									seaBillsDetail.setHshipperDetails(ds);
+								}
+								if("CONSIGN".equals(getJsonValueAsString(jod, "CompanyTypeCode"))){
+									seaBillsDetail.setHconsigneeDetails(ds);
+								}
+								if("NOTIFY1".equals(getJsonValueAsString(jod, "CompanyTypeCode"))){
+									seaBillsDetail.setHnotifyDetails(ds);
+								}
+								if("NOTIFY2".equals(getJsonValueAsString(jod, "CompanyTypeCode"))){
+									seaBillsDetail.setHnotify2Details(ds);
+								}
+								removeEles.add(joCompany);
+							}
+						});
+
+						for (JsonElement removeEle : removeEles) {
+							jaCompanys.remove(removeEle);
+						}
+					}
+
+					// 预配箱 及 已装箱
+					Long lineNo = 0L;
+					if(ObjectUtils.isNotNull(jaContainers) && jaContainers.size()>0) {
+						for (JsonElement jaContainer : jaContainers) {
+							JsonObject joe = (JsonObject) jaContainer;
+							JsonObject jof = joe.getAsJsonObject("@attributes");
+
+							if(getJsonValueAsLong(jof, "BookingId") == bookingId){
+								BCntrTypes cntrType = getCntrTypeByEdiCode(cntrTypes, getJsonValueAsString(jof, "ContainerTypeCode"));
+								if(ObjectUtils.isNotNull(cntrType)){
+									// 预配箱
+									Optional<PreContainers> preCntrsOptional = preContainers.stream()
+										.filter(v -> v.getCntrTypeCodeId().equals(cntrType.getId()))
+										.findFirst();
+									if(preCntrsOptional.isPresent()){
+										preCntrsOptional.get().setQuantity(preCntrsOptional.get().getQuantity() + 1);
+									}else{
+										PreContainers preContainer = new PreContainers();
+										// preContainer.setId(0L);
+										preContainer.setPid(0L);
+										preContainer.setTenantId(AuthUtil.getTenantId());
+										preContainer.setBranchId(branchId + "");
+										preContainer.setCreateUser(AuthUtil.getUserId());
+										preContainer.setCreateUserName(AuthUtil.getUserName());
+										preContainer.setCreateTime(new Date());
+										preContainer.setCreateDept(deptId);
+										preContainer.setCreateDeptName(deptName);
+										preContainer.setCntrTypeCodeId(cntrType.getId().toString());
+										// CnName or Code ?
+										preContainer.setCntrTypeCode(cntrType.getCnName());
+										preContainer.setTeu(cntrType.getTeu());
+										preContainer.setQuantity(1);
+										preContainer.setCurCode("USD");
+										preContainers.add(preContainer);
+									}
+
+									// 已装箱
+									Containers cntr = new Containers();
+									// cntr.setId(0L);
+									cntr.setPid(0L);
+									cntr.setLineNo(++lineNo);
+									cntr.setTenantId(AuthUtil.getTenantId());
+									cntr.setBranchId(branchId + "");
+									cntr.setCreateUser(AuthUtil.getUserId());
+									cntr.setCreateUserName(AuthUtil.getUserName());
+									cntr.setCreateTime(new Date());
+									cntr.setCreateDept(deptId);
+									cntr.setCreateDeptName(deptName);
+									cntr.setCntrTypeCodeId(cntrType.getId().toString());
+									// CnName or Code ?
+									cntr.setCntrTypeCode(cntrType.getCnName());
+									cntr.setTeu(cntrType.getTeu());
+									cntr.setCntrNo(getJsonValueAsString(jof, "ContainerNumber"));
+									// SealNo
+									String sealNo = "";
+									Long containerId = getJsonValueAsLong(jof, "ContainerId");
+									if(ObjectUtils.isNotNull(jaContainerSealNumbers) && !jaContainerSealNumbers.isEmpty()) {
+										for (JsonElement jaContainerSealNumber : jaContainerSealNumbers) {
+											JsonObject jog = (JsonObject) jaContainerSealNumber;
+											JsonObject joh = jog.getAsJsonObject("@attributes");
+											if(containerId == getJsonValueAsLong(joh, "ContainerId")) {
+												sealNo = getJsonValueAsString(joh, "SealNumber");
+												break;
+											}
+										}
+									}
+									cntr.setSealNo(sealNo);
+									cntr.setStatus(0);
+									containers.add(cntr);
+
+									BigDecimal cntrQty = new BigDecimal("0.000").setScale(3, RoundingMode.HALF_UP);
+									BigDecimal cntrGw = new BigDecimal("0.000").setScale(3, RoundingMode.HALF_UP);
+									BigDecimal cntrVol = new BigDecimal("0.000").setScale(3, RoundingMode.HALF_UP);
+
+									List<ContainersCommodity> cntrCommoditys = new ArrayList<>();
+									// 箱内货物
+									if(ObjectUtils.isNotNull(jaCargos) && !jaCargos.isEmpty()) {
+										Long cargoLineNo = 0L;
+										for (JsonElement jaCargo : jaCargos) {
+											JsonObject joi = (JsonObject) jaCargo;
+											JsonObject joj = joi.getAsJsonObject("@attributes");
+											if(containerId == getJsonValueAsLong(joj, "ContainerId")) {
+												ContainersCommodity cargo = new ContainersCommodity();
+												// cargo.setId(0L);
+												cargo.setPpId(0L);
+												cargo.setPid(0L);
+												cargo.setLineNo(++cargoLineNo);
+												cargo.setTenantId(AuthUtil.getTenantId());
+												cargo.setBranchId(branchId + "");
+												cargo.setCreateUser(AuthUtil.getUserId());
+												cargo.setCreateUserName(AuthUtil.getUserName());
+												cargo.setCreateTime(new Date());
+												cargo.setCreateDept(deptId);
+												cargo.setCreateDeptName(deptName);
+												cargo.setHscode(getJsonValueAsString(joj, "HarmonizedCode"));
+												cargo.setQuantity(getJsonValueAsBigDecimal(joj, "Quantity"));
+												cargo.setGrossWeight(getJsonValueAsBigDecimal(joj, "Weight"));
+												cargo.setMeasurement(getJsonValueAsBigDecimal(joj, "Measurement"));
+												cargo.setCommodityShName(getJsonValueAsString(joj, "ShortDescription"));
+												cargo.setCommodityCnName(getJsonValueAsString(joj, "Commodity"));
+												cargo.setCommodityEnName(getJsonValueAsString(joj, "Commodity"));
+												cargo.setCommodityDescr(getJsonValueAsString(joj, "ShipperDescription"));
+												cargo.setMarks(getJsonValueAsString(joj, "MarksNumbers"));
+
+												BPackages pkg = getPackageByEdiCode(packages, getJsonValueAsString(joj, "PackageTypeCode"));
+												cargo.setPackingUnitId(ObjectUtils.isNotNull(pkg) ? pkg.getId() : 0L);
+												cargo.setPackingUnitCode(ObjectUtils.isNotNull(pkg) ? pkg.getCode() : "");
+												cargo.setPackingUnit(ObjectUtils.isNotNull(pkg) ? pkg.getEnName() : "");
+												if(ObjectUtils.isNotNull(pkg) && ObjectUtils.isNull(billPkg)){
+													billPkg = pkg;
+												}
+
+												cntrQty = cntrQty.add(cargo.getQuantity());
+												cntrGw = cntrGw.add(cargo.getGrossWeight());
+												cntrVol = cntrVol.add(cargo.getMeasurement());
+
+												billQty = billQty.add(cargo.getQuantity());
+												billGw = billGw.add(cargo.getGrossWeight());
+												billVol = billVol.add(cargo.getMeasurement());
+
+												if(commodityShName.isEmpty()){
+													commodityShName = getJsonValueAsString(joj, "ShortDescription");
+												}
+
+												if(commodityEnName.isEmpty()){
+													commodityEnName = getJsonValueAsString(joj, "Commodity");
+												}
+
+												if(hsCode.isEmpty()){
+													hsCode = getJsonValueAsString(joj, "HarmonizedCode");
+												}
+												if(goodsDescr.isEmpty()){
+													goodsDescr = getJsonValueAsString(joj, "ShipperDescription");
+												}
+												if(markDescr.isEmpty()){
+													markDescr = getJsonValueAsString(joj, "MarksNumbers");
+												}
+
+												cntrCommoditys.add(cargo);
+											}
+										}
+									}
+									cntr.setQuantity(cntrQty);
+									cntr.setGrossWeight(cntrGw);
+									cntr.setMeasurement(cntrVol);
+									cntr.setContainersCommodityList(cntrCommoditys);
+								}else{
+									throw new RuntimeException("订舱号 " + bill.getBookingNo() + " 箱号 " + getJsonValueAsString(jof, "ContainerNumber") + " 箱型代码不能识别 " + getJsonValueAsString(jof, "ContainerTypeCode") + " !");
+								}
+							}
+						}
+					}
+
+					bill.setHscode(hsCode);
+					bill.setCommodityShName(commodityShName);
+					bill.setCommodityCnName(commodityEnName);
+					bill.setCommodityEnName(commodityEnName);
+					bill.setCommodityDescr(goodsDescr);
+					bill.setMarks(markDescr);
+
+					bill.setQuantity(billQty);
+					bill.setGrossWeight(billGw);
+					bill.setMeasurement(billVol);
+
+					bill.setPackingUnitId(ObjectUtils.isNotNull(billPkg) ? billPkg.getId() : 0L);
+					bill.setPackingUnit(ObjectUtils.isNotNull(billPkg) ? billPkg.getEnName() : "");
+
+					bill.setQuantityPackingDescr("SAY " + BigDecimalUtils.convertToEnglish(bill.getQuantity()) + " " + bill.getPackingUnit() + " ONLY.");
+
+					String quantityCntrTypesDescr = "";
+					for (PreContainers preContainer : preContainers) {
+						quantityCntrTypesDescr += (quantityCntrTypesDescr.isEmpty() ? "" : " AND ") + BigDecimalUtils.convertToEnglish(new BigDecimal(preContainer.getQuantity() + ""));
+					}
+					if(!quantityCntrTypesDescr.isEmpty()){
+						quantityCntrTypesDescr = "SAY " + quantityCntrTypesDescr + "ONLY";
+					}
+					bill.setQuantityCntrTypesDescr(quantityCntrTypesDescr);
+					/*
+		bills.setQuantityV20(V20);
+		bills.setQuantityV40(V40);
+		bills.setQuantityV40hc(V40HC);
+		bills.setQuantityV45(V45);
+		bills.setQuantityV48(V48);
+		bills.setQuantityOther(other);
+		bills.setTeu(teu.intValue());
+					 */
+				});
+			}
+		});
+
+		return R.data(mhBills);
+	}
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public R submit(@RequestBody @Validated List<Bills> mhBills) throws Exception {
+		if(ObjectUtils.isNotNull(mhBills)){
+			BusinessType businessType = bBusinessTypeService.getOne(new LambdaQueryWrapper<BusinessType>()
+				.select(BusinessType::getId)
+				.eq(BusinessType::getTenantId, AuthUtil.getTenantId())
+				.eq(BusinessType::getIsDeleted, 0)
+				.eq(BusinessType::getStatus, 0)
+				.eq(BusinessType::getCode, "HYCK"));
+			if (businessType == null) {
+				throw new RuntimeException("未找到可用业务类型");
+			}
+
+			// 测试用,只保存一票
+			Random rand = new Random();
+			int billIdx = rand.nextInt(mhBills.size()) + 1;
+			int currIdx = 0;
+
+			for (Bills mhBill : mhBills) {
+				// 测试用,只保存一票
+				currIdx++;
+				if(currIdx!=billIdx){
+					continue;
+				}
+
+				Object object = StringTools.handle(mhBill, "Bills");
+				Bills bills = JSONObject.parseObject(JSONObject.toJSONString(object), Bills.class);
+
+
+				if (ObjectUtils.isNull(bills.getBillNo())) {
+					BusinessBillNo businessBillNo = new BusinessBillNo();
+					businessBillNo.setBusinessTypeId(businessType.getId());
+					businessBillNo.setCode(bills.getBillNoFormat());
+					R clientBillNo = businessBillNoService.getBillNoLos(businessBillNo);
+					if (!clientBillNo.isSuccess()) {
+						TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+						return R.fail(500, "生成订单编号失败");
+					}
+					bills.setBillNo((String) clientBillNo.getData());
+				}
+
+				if (ObjectUtils.isNull(bills.getId())) {
+					bills.setCreateTime(new Date());
+					bills.setCreateUser(AuthUtil.getUserId());
+					bills.setCreateUserName(AuthUtil.getUserName());
+					bills.setTenantId(AuthUtil.getTenantId());
+					bills.setVersion("1");
+
+					this.save(bills);
+				} else {
+					Bills dataSourceBill = baseMapper.selectOne(new LambdaQueryWrapper<Bills>().select(Bills::getId, Bills::getVersion).eq(Bills::getId, bills.getId()));
+					if (!Objects.equals(dataSourceBill.getVersion(), bills.getVersion())) {
+						return R.fail(601, "数据已被其他用户更新,请等待刷新后重试");
+					}
+					// 每更新一次往上累加一次版本
+					// 旧数据处理
+					int version = StringUtil.isBlank(dataSourceBill.getVersion()) ? 1 : Integer.parseInt(dataSourceBill.getVersion());
+					bills.setVersion(String.valueOf(version + 1));
+
+					bills.setUpdateUser(AuthUtil.getUserId());
+					bills.setUpdateTime(new Date());
+					bills.setUpdateUserName(AuthUtil.getUserName());
+
+					this.updateById(bills);
+
+					bPreContainersService.remove(new LambdaQueryWrapper<PreContainers>()
+						.eq(PreContainers::getTenantId, AuthUtil.getTenantId())
+						.eq(PreContainers::getPid, bills.getId())
+					);
+
+					bContainersService.remove(new LambdaQueryWrapper<Containers>()
+						.eq(Containers::getTenantId, AuthUtil.getTenantId())
+						.eq(Containers::getPid, bills.getId())
+					);
+
+					bContainersCommodityService.remove(new LambdaQueryWrapper<ContainersCommodity>()
+						.eq(ContainersCommodity::getTenantId, AuthUtil.getTenantId())
+						.eq(ContainersCommodity::getPpId, bills.getId())
+					);
+				}
+
+				SeaBillsDetail seaBillsDetail = bills.getDetail();
+				if(ObjectUtils.isNotNull(seaBillsDetail)) {
+					seaBillsDetail.setPid(bills.getId());
+					bSeaBillsDetailService.saveOrUpdate(seaBillsDetail);
+				}
+
+				List<PreContainers> preContainers = bills.getPreContainersList();
+				if(ObjectUtils.isNotNull(preContainers)) {
+					for (PreContainers preContainer : preContainers) {
+						preContainer.setPid(bills.getId());
+					}
+				}
+				bPreContainersService.saveOrUpdateBatch(preContainers);
+
+				List<Containers> containers = bills.getContainersList();
+				if(ObjectUtils.isNotNull(containers)) {
+					for (Containers container : containers) {
+						container.setPid(bills.getId());
+					}
+
+					bContainersService.saveOrUpdateBatch(containers);
+
+					for (Containers container : containers) {
+						List<ContainersCommodity> cntrCommoditys = container.getContainersCommodityList();
+						if(ObjectUtils.isNotNull(cntrCommoditys)) {
+							for (ContainersCommodity cntrCommodity : cntrCommoditys) {
+								cntrCommodity.setPpId(bills.getId());
+								cntrCommodity.setPid(container.getId());
+							}
+							bContainersCommodityService.saveOrUpdateBatch(cntrCommoditys);
+						}
+					}
+				}
+			}
+		}
+
+		return R.success("success");
+	}
+}