From 25f0c98516757157448685ed2eca5729280711c2 Mon Sep 17 00:00:00 2001 From: dengjun Date: Thu, 29 Feb 2024 19:48:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=B9=E9=87=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../haitonggauto/rtosc/dto/ShipStatusVo.java | 23 + .../rtosc/excel/ExportInPlanExcel.java | 11 +- .../rtosc/query/DepartureQuery.java | 3 + .../rtosc/query/ExportInQuery.java | 6 + .../rtosc/query/FreeTradeQuery.java | 3 + .../haitonggauto/rtosc/api/NuzarOpenApi.java | 7 + .../haitonggauto/rtosc/api/NuzarPubApi.java | 4 +- .../haitonggauto/rtosc/api/NuzarShpApi.java | 3 + .../haitonggauto/rtosc/api/NuzarYardApi.java | 4 +- .../haitonggauto/rtosc/api/dto/FreightVo.java | 23 + .../haitonggauto/rtosc/api/dto/PortDTO.java | 1 + .../rtosc/handler/DictHandler.java | 16 + .../rtosc/handler/ExportInHandler.java | 397 ++++++++++++++---- .../rtosc/handler/ExportLoadHandler.java | 8 +- .../handler/excel/ExcelValidationUtils.java | 252 +++++++++++ .../service/impl/CustomerServiceImpl.java | 6 +- .../src/main/resources/templates/in_temp.xlsx | Bin 14004 -> 18902 bytes .../repository/entity/CustomerExportIn.java | 4 + .../mapper/CustomerExportLoadMapper.xml | 2 + 19 files changed, 670 insertions(+), 103 deletions(-) create mode 100644 nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/dto/ShipStatusVo.java create mode 100644 nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/FreightVo.java create mode 100644 nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/excel/ExcelValidationUtils.java diff --git a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/dto/ShipStatusVo.java b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/dto/ShipStatusVo.java new file mode 100644 index 0000000..f16be84 --- /dev/null +++ b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/dto/ShipStatusVo.java @@ -0,0 +1,23 @@ +package com.haitonggauto.rtosc.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +@ApiModel("船舶状态") +public class ShipStatusVo { + @ApiModelProperty("船ID") + @NotBlank(message = "船ID不能为空") + private String shipId; + + @ApiModelProperty("航次ID") + @NotBlank(message = "航次ID不能为空") + private String voyageId; + + @ApiModelProperty("0取消,1装船确认") + @NotBlank(message = "状态不能为空,0取消,1装船确认") + private String status; +} diff --git a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/excel/ExportInPlanExcel.java b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/excel/ExportInPlanExcel.java index c428ee1..a0eb724 100644 --- a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/excel/ExportInPlanExcel.java +++ b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/excel/ExportInPlanExcel.java @@ -94,6 +94,13 @@ public class ExportInPlanExcel { @NotBlank(message = "联系方式不能为空") private String contactPhone; + /** + * 港口 + */ + @ExcelProperty("*国家") + @NotBlank(message = "国家不能为空") + private String country; + /** * 港口 */ @@ -259,10 +266,10 @@ public class ExportInPlanExcel { * 源类型 */ @ExcelProperty("*能源类型") - @NotBlank(message = "*能源类型不能为空") +// @NotBlank(message = "*能源类型不能为空") private String energyTypeName; @ExcelProperty("*是否二手车") - @NotBlank(message = "是否是二手车不能为空") +// @NotBlank(message = "是否是二手车不能为空") private String secondHand; } \ No newline at end of file diff --git a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/DepartureQuery.java b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/DepartureQuery.java index b864494..fd12d82 100644 --- a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/DepartureQuery.java +++ b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/DepartureQuery.java @@ -8,6 +8,7 @@ import com.haitonggauto.rtosc.repository.enums.AuditEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; import java.util.List; @@ -42,11 +43,13 @@ public class DepartureQuery extends BaseQuery { private String batchNo; @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") @ApiModelProperty(value = "开始申请时间") @DbQuery(field = "applyTime", symbol = SqlSymbol.GTE) private Date beginApplyTime; @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") @ApiModelProperty(value = "结束申请时间") @DbQuery(field = "applyTime", symbol = SqlSymbol.LTE) private Date endApplyTime; diff --git a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/ExportInQuery.java b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/ExportInQuery.java index ecfdb2c..d8b4861 100644 --- a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/ExportInQuery.java +++ b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/ExportInQuery.java @@ -8,6 +8,7 @@ import com.haitonggauto.rtosc.repository.enums.AuditEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; import java.util.List; @@ -34,11 +35,13 @@ public class ExportInQuery extends BaseQuery { private String portAreaId; @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") @ApiModelProperty(value = "开始进场时间") @DbQuery(field = "applyTime", symbol = SqlSymbol.GTE) private Date beginEnterTime; @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") @ApiModelProperty(value = "结束进场时间") @DbQuery(field = "applyTime", symbol = SqlSymbol.LTE) private Date endEnterTime; @@ -76,6 +79,9 @@ public class ExportInQuery extends BaseQuery { @ApiModelProperty(value = "港口ID") private String portId; + @ApiModelProperty(value = "货代ID") + private String freightId; + @ApiModelProperty(hidden = true) @DbQuery(field = "vin", symbol = SqlSymbol.IN) private List vins; diff --git a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/FreeTradeQuery.java b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/FreeTradeQuery.java index 414c4b0..fb7885f 100644 --- a/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/FreeTradeQuery.java +++ b/nuzar-customer-client/src/main/java/com/haitonggauto/rtosc/query/FreeTradeQuery.java @@ -9,6 +9,7 @@ import com.haitonggauto.rtosc.repository.enums.AuditEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; import java.util.List; @@ -24,11 +25,13 @@ public class FreeTradeQuery extends BaseQuery { private String cargoCode; @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") @ApiModelProperty(value = "开始时间") @DbQuery(field = "createDate", symbol = SqlSymbol.GT) private Date beginCreateDate; @JsonFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") @ApiModelProperty(value = "结束时间") @DbQuery(field = "createDate", symbol = SqlSymbol.LTE) private Date endCreateDate; diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarOpenApi.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarOpenApi.java index 2f1e2fa..5a1f3cc 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarOpenApi.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarOpenApi.java @@ -18,6 +18,9 @@ import java.util.Map; @FeignClient(name = "rtos-openapi") public interface NuzarOpenApi { + @GetMapping("/miniapp/getDownCountry") + List getCountryList(); + // 根据港区ID获取国家 @GetMapping(value = "/miniapp/portAreaManage/getCountry") PortAreaCountryDTO getPortAreaCountry(@RequestParam("potId") String potId); @@ -133,4 +136,8 @@ public interface NuzarOpenApi { // 查询获物信息 @GetMapping("/miniapp/machineType/dict") List getMachineType(); + + // 获取绑定货代 + @GetMapping("/rtos/user/getFreightId") + List getUserBindFreight(); } diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarPubApi.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarPubApi.java index 002383b..e73d77c 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarPubApi.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarPubApi.java @@ -11,7 +11,6 @@ import java.util.List; @FeignClient(name = "basic-service") public interface NuzarPubApi { - //车辆操作模式 @GetMapping(value = "/typeRef/domain/OPPROC_MODE") List getOperateTypeList(); @@ -48,6 +47,9 @@ public interface NuzarPubApi { @GetMapping(value = "/city/queryOrigin") List getCountryList(@RequestParam("key") String key); + @GetMapping(value = "/city/queryOriginAll") + List getAllCountryList(@RequestParam("key") String key); + // 运输方式 @GetMapping(value = "/typeRef/domain/TRANSPORT_TYPE") List getTransportWayList(); diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarShpApi.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarShpApi.java index 0424981..19b7001 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarShpApi.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarShpApi.java @@ -29,4 +29,7 @@ public interface NuzarShpApi { @GetMapping("/vesselVoyages/queryByKey") List queryVoyageByKey(@RequestParam("ieType") String ieType, @RequestParam("key") String key, @RequestParam("pamId") String pamId, @RequestParam("spmId") String spmId , @RequestParam("tradeType") String tradeType); + + @PostMapping("/vesselVoyages/shipNameFilter") + List checkShipStatus(@RequestBody List req); } diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarYardApi.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarYardApi.java index 991f731..34dba83 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarYardApi.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/NuzarYardApi.java @@ -12,8 +12,8 @@ import org.springframework.web.bind.annotation.RequestParam; import java.util.List; -@FeignClient(name = "https://rtops4.haitongauto.com/tos/yard") -//@FeignClient(name = "yard-service") +//@FeignClient(name = "https://rtops4.haitongauto.com/tos/yard") +@FeignClient(name = "yard-service") public interface NuzarYardApi { @PostMapping("/yardCustomsRelease/queryCustomNoByvvyIdAndMnf") diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/FreightVo.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/FreightVo.java new file mode 100644 index 0000000..e677026 --- /dev/null +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/FreightVo.java @@ -0,0 +1,23 @@ +package com.haitonggauto.rtosc.api.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@ApiModel("货代信息") +@Data +public class FreightVo implements Serializable { + + @ApiModelProperty("客户简称") + private String cueAbbreviation; + + @ApiModelProperty("客户中文名称") + private String cueCnname; + + @ApiModelProperty("客户中文名称") + private String cueTypes; + + private String cueId; +} diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/PortDTO.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/PortDTO.java index cf0e4ec..cabf053 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/PortDTO.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/api/dto/PortDTO.java @@ -14,4 +14,5 @@ public class PortDTO implements Serializable { private String potEnname; + private String potCtycd; } diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/DictHandler.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/DictHandler.java index 6d825d3..0ad36d7 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/DictHandler.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/DictHandler.java @@ -145,6 +145,7 @@ public class DictHandler implements BaseHandler { dto.setId(item.getPotId()); dto.setText(item.getPotCnname()); dto.setExtra1(item.getPotEnname()); + dto.setExtra2(item.getPotCtycd()); // 港口的国家ID return dto; }).collect(Collectors.toList()); } @@ -239,6 +240,21 @@ public class DictHandler implements BaseHandler { return ResultUtil.success(list); } + @ApiOperation("国家城市全量") + @PostMapping("/city/queryAllOrigin") + public Result> getAllCountryList(@RequestParam(required = false) String q) { + + List list = nuzarPubApi.getAllCountryList(q); + + if (CollectionUtils.isEmpty(list)) { + return ResultUtil.success(Collections.emptyList()); + } else { + + } + + return ResultUtil.success(list); + } + // 运输方式 @ApiOperation("运输方式") @PostMapping("/transportWay") diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportInHandler.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportInHandler.java index c880343..cbccafa 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportInHandler.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportInHandler.java @@ -26,6 +26,7 @@ import com.haitonggauto.rtosc.common.handler.BaseHandler; import com.haitonggauto.rtosc.common.utils.*; import com.haitonggauto.rtosc.dto.*; import com.haitonggauto.rtosc.excel.*; +import com.haitonggauto.rtosc.handler.excel.ExcelValidationUtils; import com.haitonggauto.rtosc.query.CargoQuery; import com.haitonggauto.rtosc.query.ExportInCheckQuery; import com.haitonggauto.rtosc.query.ExportInQuery; @@ -47,6 +48,10 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.xssf.usermodel.*; import org.springframework.core.io.ClassPathResource; import org.springframework.transaction.annotation.Transactional; @@ -390,6 +395,17 @@ public class ExportInHandler implements BaseHandler { customerService.wrapperEntity(list); if (CollectionUtils.isNotEmpty(list)) { + // 港口和国家的英文明称 + Map portMap = new HashMap<>(); + Map countryMap = new HashMap<>(); + List countryIds = list.stream().map(item -> item.getCountryId()).distinct().collect(Collectors.toList()); + List portIds = list.stream().map(item -> item.getPortId()).distinct().collect(Collectors.toList()); + + List portByIds = openApi.getPortByIds(portIds); + List countryByIds = openApi.getCountryByIds(countryIds); + portMap.putAll(portByIds.stream().collect(Collectors.toMap(item -> item.getPotId(), item -> item.getPotEnname()))); + countryMap.putAll(countryByIds.stream().collect(Collectors.toMap(item -> item.getCtyId(), item -> item.getCtyEnname()))); + List cargos = customerExportInCargoService.lambdaQuery() .in(CustomerExportInCargo::getExportInId, list.stream().map(item -> item.getId()).collect(Collectors.toList())).list(); @@ -399,6 +415,33 @@ public class ExportInHandler implements BaseHandler { // 数据拼装 list.stream().forEach(item -> { List cs = collect.get(item.getId()); + + item.setPortEnName(portMap.get(item.getPortId())); + item.setCountryEnName(countryMap.get(item.getCountryId())); + + if (CollectionUtils.isNotEmpty(cs)) { + // 获取作业状态 + Map statCollect = null; + List vins = cs.stream().map(s -> s.getVin()).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(vins)) { + VinStatusRequest req = new VinStatusRequest(); + req.setBusinessType("IN_PORT"); + req.setImportExportType("E"); + req.setVinCodeList(vins); + if (!StringUtils.equalsAnyIgnoreCase(item.getVoyageId(), "HT6", "HTLG", "HTTC")) { + req.setVvyId(item.getVoyageId()); + } + List status = openApi.getVinStatus(req); + statCollect = status.stream().collect(Collectors.toMap(WorkStatusDTO::getVinCode, WorkStatusDTO::getWorkStatus)); + } + + if (MapUtils.isNotEmpty(statCollect)) { + for (CustomerExportInCargo cargo : cs) { + cargo.setWorkStatus(statCollect.get(cargo.getVin())); + } + } + } + item.setVins(cs); }); } @@ -734,7 +777,7 @@ public class ExportInHandler implements BaseHandler { nQuery.select("sum(quantity) as quantity, sum(volume) as volume, sum(weight) as weight") .eq("bill_num", exportIn.getBillNum()) .eq("ship_id", exportIn.getShipId()) - .eq("voyage_id", exportIn.getVoyageId()); + .eq("voyage_id", exportIn.getVoyageId()).ne("check_status", AuditEnum.AUDIT_REJECT); Map map = customerExportInService.getMap(nQuery); if (MapUtils.isEmpty(map)) { map = new HashMap<>(); @@ -746,10 +789,10 @@ public class ExportInHandler implements BaseHandler { return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "数量不得超过单票件数"); } if (exportIn.getEachWeight().divide(new BigDecimal(1000)).compareTo(((BigDecimal) map.get("weight")).add(exportIn.getWeight())) < 0) { - return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "重量不得超过单票重量"); +// return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "重量不得超过单票重量"); } if (exportIn.getEachVolume().compareTo(((BigDecimal) map.get("volume")).add(exportIn.getVolume()).multiply(new BigDecimal(exportIn.getQuantity()))) < 0) { - return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "体积不得超过单票体积"); +// return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "体积不得超过单票体积"); } // 数量必须≤单票件数,否则不能跳转下一步,并提示“数量不得超过单票件数” 通过自定义验证组件实现 @@ -793,6 +836,16 @@ public class ExportInHandler implements BaseHandler { public Result submitCheck(@RequestBody @NotNull(message = "请传入要审核的出口进场ID") @Size(min = 1, message = "ID列表不能为空") List ids) { + List list = customerExportInCargoService.query() + .select("count(id) as num, export_in_id") + .in("export_in_id", ids) + .groupBy("export_in_id") + .having("count(id) > 0") + .list(); + if (list.size() != ids.size()) { + return ResultUtil.failure(ErrorType.PARAMS_ERROR.id(), "请补充车架号等信息后,再提交审核"); + } + // 有没有明细的不允许提交审核 customerExportInService.lambdaUpdate().set(CustomerExportIn::getCheckStatus, AuditEnum.AUDIT) .set(CustomerExportIn::getCheckResult, null) .in(CustomerExportIn::getId, ids).update(); @@ -807,6 +860,7 @@ public class ExportInHandler implements BaseHandler { .eq(CustomerExportIn::getShipId, form.getShipId()) .eq(CustomerExportIn::getVoyageId, form.getVoyageId()) .eq(CustomerExportIn::getCheckStatus, AuditEnum.SUBMIT) + .exists("select id from customer_export_in_cargo where customer_export_in_cargo.export_in_id=customer_export_in.id") .eq(CustomerExportIn::getCreateBy, UserContext.getUser().getUserId()).update(); return ResultUtil.success("success"); } @@ -1064,7 +1118,7 @@ public class ExportInHandler implements BaseHandler { nQuery.select("sum(quantity) as quantity, sum(volume) as volume, sum(weight) as weight") .eq("ship_id", exportIn.getShipId()) .eq("voyage_id", exportIn.getVoyageId()) - .eq("bill_num", exportIn.getBillNum()).ne("id", exportIn.getId()); + .eq("bill_num", exportIn.getBillNum()).ne("check_status", AuditEnum.AUDIT_REJECT).ne("id", exportIn.getId()); Map vmap = customerExportInService.getMap(nQuery); if (MapUtils.isEmpty(vmap)) { vmap = new HashMap<>(); @@ -1076,10 +1130,10 @@ public class ExportInHandler implements BaseHandler { return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "数量不得超过单票件数"); } if (exportIn.getEachWeight().divide(new BigDecimal(1000)).compareTo(((BigDecimal) vmap.get("weight")).add(exportIn.getWeight())) < 0) { - return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "重量不得超过单票重量"); +// return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "重量不得超过单票重量"); } if (exportIn.getEachVolume().compareTo(((BigDecimal) vmap.get("volume")).add(exportIn.getVolume().multiply(new BigDecimal(exportIn.getQuantity())))) < 0) { - return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "体积不得超过单票体积"); +// return ResultUtil.failure(ErrorType.PROGRAM_ERROR.id(), "体积不得超过单票体积"); } //* 异常情况有,必填项车架号,车架号必须17位,数据重复,如果重复进行覆盖更新 @@ -1216,10 +1270,6 @@ public class ExportInHandler implements BaseHandler { if (row) { List list = customerExportInService.lambdaQuery().in(CustomerExportIn::getId, check.getIds()).list(); - if (check.getCheckStatus() == AuditEnum.AUDIT_PASS) { - customerService.syncAddExportInToOld(list); - } - // 记录日志 for (CustomerExportIn exportIn : list) { LogRecordDTO log = new LogRecordDTO(); @@ -1231,6 +1281,10 @@ public class ExportInHandler implements BaseHandler { EsLogApprovalUtil.writeLog(log); } + if (check.getCheckStatus() == AuditEnum.AUDIT_PASS) { + customerService.syncAddExportInToOld(list); + } + return ResultUtil.success("success"); } @@ -2023,7 +2077,7 @@ public class ExportInHandler implements BaseHandler { cargo.setExportInId(exportIn.getId()); cargo.setBrand(exportIn.getBrand()); cargo.setBrandId(exportIn.getBrandId()); - cargo.setCargoType(0); + cargo.setCargoType(1); cargo.setVin(p.getVin()); cargo.setTermcd(exportIn.getPortAreaId()); cargo.setVinStatus(1); @@ -2213,6 +2267,8 @@ public class ExportInHandler implements BaseHandler { // }); // } + // 同一个型号可能存在多个进港计划 + List exportInList = customerExportInService.list(cQuery); if (CollectionUtils.isEmpty(exportInList)) { // 船名,航次等不存在 @@ -2224,12 +2280,15 @@ public class ExportInHandler implements BaseHandler { return; } + // 计划总数量 + int planQuantity = exportInList.stream().mapToInt(p -> p.getQuantity()).sum(); + // 找到和本次数量一致的 - Optional first = exportInList.stream().filter(p -> p.getQuantity() == item.getValue().size()).findFirst(); +// Optional first = exportInList.stream().filter(p -> p.getQuantity() == item.getValue().size()).findFirst(); - CustomerExportIn exportIn = first.isPresent() ? first.get() : null; + CustomerExportIn exportIn = exportInList.get(0); - if (exportIn == null) { + if (planQuantity != item.getValue().size()) { errorDataList.addAll(item.getValue().stream().map(p -> { JSONObject o = JSONObject.from(p); o.put("status", "数量不一致不允许导入"); @@ -2238,7 +2297,8 @@ public class ExportInHandler implements BaseHandler { return; } - ids.add(exportIn.getId()); + ids.addAll(exportInList.stream().map(p -> p.getId()).collect(Collectors.toList())); +// ids.add(exportIn.getId()); // 已生成装船计划,不能导入 List havePlans = openApi.haveShipPlan(exportIn.getVoyageId()); @@ -2263,26 +2323,44 @@ public class ExportInHandler implements BaseHandler { // return; // } - if (exportIn.getEnterQuantity() != item.getValue().size()) { - errorDataList.addAll(item.getValue().stream().map(p -> { - JSONObject o = JSONObject.from(p); - o.put("status", "车架号数量和实际货物数量不符合,无法导入"); - return o; - }).collect(Collectors.toList())); - return; +// if (exportIn.getEnterQuantity() != item.getValue().size()) { +// errorDataList.addAll(item.getValue().stream().map(p -> { +// JSONObject o = JSONObject.from(p); +// o.put("status", "车架号数量和实际货物数量不符合,无法导入"); +// return o; +// }).collect(Collectors.toList())); +// return; +// } + // 按计划中的件数进行VIN的分配 + int j = 0; + List cargos = new ArrayList<>(); + for (CustomerExportIn e : exportInList) { + int n = e.getQuantity(); + for (int i= 0; i < n; i++) { + CustomerExportInCargo cargo = new CustomerExportInCargo(); + cargo.setBrandId(e.getBrandId()); + cargo.setBrand(e.getBrand()); + cargo.setExportInId(e.getId()); + cargo.setCargoType(0); + cargo.setVin(item.getValue().get(j++).getVin()); + cargo.setTermcd(e.getPortAreaId()); + cargo.setVinStatus(1); + + cargos.add(cargo); + } } - List cargos = item.getValue().stream().map(p -> { - CustomerExportInCargo cargo = new CustomerExportInCargo(); - cargo.setBrandId(exportIn.getBrandId()); - cargo.setBrand(exportIn.getBrand()); - cargo.setExportInId(exportIn.getId()); - cargo.setCargoType(0); - cargo.setVin(p.getVin()); - cargo.setTermcd(exportIn.getPortAreaId()); - cargo.setVinStatus(1); - return cargo; - }).collect(Collectors.toList()); +// List cargos = item.getValue().stream().map(p -> { +// CustomerExportInCargo cargo = new CustomerExportInCargo(); +// cargo.setBrandId(exportIn.getBrandId()); +// cargo.setBrand(exportIn.getBrand()); +// cargo.setExportInId(exportIn.getId()); +// cargo.setCargoType(0); +// cargo.setVin(p.getVin()); +// cargo.setTermcd(exportIn.getPortAreaId()); +// cargo.setVinStatus(1); +// return cargo; +// }).collect(Collectors.toList()); List vins = cargos.stream().map(p -> p.getVin()).collect(Collectors.toList()); @@ -2866,37 +2944,127 @@ public class ExportInHandler implements BaseHandler { XSSFWorkbook workbook = new XSSFWorkbook(inputStream); - XSSFSheet sheet = workbook.getSheetAt(1); + { + XSSFSheet sheet = workbook.getSheet("城市列表"); - // 车型 - XSSFRow row = sheet.getRow(0); - if (row == null) { - row = sheet.createRow(0); + // 城市基础数据 + List countryList = dictHandler.getAllCountryList(null).getData(); + List list = countryList.stream().map(item -> item.getCiyCnname()).collect(Collectors.toList()); + String[] str = list.toArray(new String[list.size()]); + + for (int i = 0; i < str.length; i++) { + XSSFRow row = sheet.getRow(i); + if (row == null) { + row = sheet.createRow(i); + } + XSSFCell cell = row.getCell(0); + if (cell == null) { + cell = row.createCell(0); + } + cell.setCellValue(str[i]); + } } - for (int i = 0; i < carTypeList.size(); i++) { - XSSFCell cell = row.getCell(i); - if (cell == null) { -// XSSFCellStyle cellStyle = row.getCell(i).getCellStyle(); - cell = row.createCell(i); -// cell.setCellStyle(cellStyle); - } - cell.setCellValue(carTypeList.get(i).getText()); - // 车型明细 - List data = dictHandler.getCartTypeDetailList(carTypeList.get(i).getId(), null).getData(); - for (int j = 0; j < data.size(); j++) { - XSSFRow r = sheet.getRow(j + 1); - if (r == null) { - r = sheet.createRow(j + 1); - } - XSSFCell c = r.getCell(i); - if (c == null) { -// XSSFCellStyle cellStyle = r.getCell(i).getCellStyle(); - c = r.createCell(i); -// c.setCellStyle(cellStyle); - } - c.setCellValue(data.get(j).getText()); + // 车型 及 车型明细 + { + XSSFSheet sheet = workbook.getSheet("车型管理"); + + XSSFRow row = sheet.getRow(0); + if (row == null) { + row = sheet.createRow(0); } + for (int i = 0; i < carTypeList.size(); i++) { + XSSFCell cell = row.getCell(i); + if (cell == null) { + cell = row.createCell(i); + } + cell.setCellValue(carTypeList.get(i).getText()); + + // 车型明细 + List data = dictHandler.getCartTypeDetailList(carTypeList.get(i).getId(), null).getData(); + for (int j = 0; j < data.size(); j++) { + XSSFRow r = sheet.getRow(j + 1); + if (r == null) { + r = sheet.createRow(j + 1); + } + XSSFCell c = r.getCell(i); + if (c == null) { + c = r.createCell(i); + } + c.setCellValue(data.get(j).getText()); + } + } + } + + // 车型及操作模式 + { + List operateTypeList = dictHandler.getOperateTypeList("车辆").getData(); // 车辆操作模式 + List spareTypeList = dictHandler.getOperateTypeList("备件").getData(); // 备件操作模式 + + XSSFSheet sheet = workbook.getSheet("车辆备件"); + + XSSFRow row = sheet.getRow(0); + if (row == null) { + row = sheet.createRow(0); + } + for (int i = 0; i < carTypeList.size(); i++) { + XSSFCell cell = row.getCell(i); + if (cell == null) { + cell = row.createCell(i); + } + cell.setCellValue(carTypeList.get(i).getText()); + + // 操作模式 + List data = StringUtils.equals("备件", carTypeList.get(i).getText()) ? spareTypeList : operateTypeList; + for (int j = 0; j < data.size(); j++) { + XSSFRow r = sheet.getRow(j + 1); + if (r == null) { + r = sheet.createRow(j + 1); + } + XSSFCell c = r.getCell(i); + if (c == null) { + c = r.createCell(i); + } + c.setCellValue(data.get(j).getText()); + } + } + } + + // 国家与港口 + { + XSSFSheet sheet = workbook.getSheet("国家港口"); + + List countryList = openApi.getCountryList(); + List list = pubApi.getPortList(); + + XSSFRow row = sheet.getRow(0); + if (row == null) { + row = sheet.createRow(0); + } + for (int i = 0; i < countryList.size(); i++) { + XSSFCell cell = row.getCell(i); + if (cell == null) { + cell = row.createCell(i); + } + cell.setCellValue(countryList.get(i).getCtyCnname()); + + String ctyId = countryList.get(i).getCtyId(); + // 港口 + List collect = list.stream().filter(item -> StringUtils.equals(item.getPotCtycd(), ctyId)).collect(Collectors.toList()); + for (int j = 0; j < collect.size(); j++) { + XSSFRow r = sheet.getRow(j + 1); + if (r == null) { + r = sheet.createRow(j + 1); + } + XSSFCell c = r.getCell(i); + if (c == null) { + c = r.createCell(i); + } + c.setCellValue(collect.get(j).getPotCnname()); + } + } + + } OutputStream out = response.getOutputStream(); @@ -2940,6 +3108,11 @@ public class ExportInHandler implements BaseHandler { // 验证通过的数据 List successDataList = new ArrayList<>(); + // 获取用户绑定的货代 + List userBindFreight = openApi.getUserBindFreight();; + + // 国家数据 + List countryList = openApi.getCountryList(); // 港口基础数据 List portList = dictHandler.getPortList(null).getData(); // 船名基础数据 @@ -2950,8 +3123,8 @@ public class ExportInHandler implements BaseHandler { List brandList = dictHandler.getBrandList(null).getData(); // 车型基础数据 List carTypeList = dictHandler.getCartTypeList(null).getData(); - // 国家基础数据 - List countryList = dictHandler.getCountryList(null).getData(); + // 产地基础数据 + List originPlaceList = dictHandler.getAllCountryList(null).getData(); // 运输模式 List transportWayList = dictHandler.getTransportWayList(null).getData(); // 操作模式 @@ -2961,7 +3134,7 @@ public class ExportInHandler implements BaseHandler { // 新能源类型 List energyTypeList = dictHandler.getEnergyTypeList().getData(); // 根据港口ID 获取国家 - Map portCountryList = new HashMap<>(); +// Map portCountryList = new HashMap<>(); // 货代列表 Map companyMap = new HashMap<>(); // 货物性质 @@ -3004,7 +3177,14 @@ public class ExportInHandler implements BaseHandler { // 港区、船名、港口、品牌、车型 validData.stream().forEach(item -> { - if (portList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getPortName())).count() == 0) { + if (countryList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCtyCnname(), item.getCountry())).count() == 0) { + JSONObject o = JSONObject.from(item); + o.put("status", "国家不存在"); + errorDataList.add(o); + return; + } + String tmpCountryId = countryList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCtyCnname(), item.getCountry())).findFirst().get().getCtyId(); + if (portList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getPortName()) && StringUtils.equals(tmpCountryId, p.getExtra2())).count() == 0) { JSONObject o = JSONObject.from(item); o.put("status", "港口不存在"); errorDataList.add(o); @@ -3034,7 +3214,7 @@ public class ExportInHandler implements BaseHandler { errorDataList.add(o); return; } - if (countryList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCiyCnname(), item.getOriginPlace())).count() == 0) { + if (originPlaceList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCiyCnname(), item.getOriginPlace())).count() == 0) { JSONObject o = JSONObject.from(item); o.put("status", "产地错误"); errorDataList.add(o); @@ -3048,6 +3228,14 @@ public class ExportInHandler implements BaseHandler { companyMap.put(item.getFreight(), StringUtils.trim(company.getCueId())); } } + if (StringUtils.equals(type, "0")) {// 验证货代不一致 + if (CollectionUtils.isEmpty(userBindFreight) || userBindFreight.stream().filter(s -> StringUtils.equalsAny(item.getFreight(), s.getCueCnname(), s.getCueAbbreviation())).count() == 0) { + JSONObject o = JSONObject.from(item); + o.put("status", "登录账号绑定货代不一致"); + errorDataList.add(o); + return; + } + } if (StringUtils.isEmpty(companyMap.get(item.getFreight()))) { JSONObject o = JSONObject.from(item); o.put("status", "货代不存在"); @@ -3092,11 +3280,25 @@ public class ExportInHandler implements BaseHandler { errorDataList.add(o); return; } - if (energyTypeList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getEnergyTypeName())).count() == 0) { - JSONObject o = JSONObject.from(item); - o.put("status", "能源类型不存在"); - errorDataList.add(o); - return; + if (!StringUtils.equals("备件", item.getCartType())) { + if (StringUtils.isEmpty(item.getEnergyTypeName())) { + JSONObject o = JSONObject.from(item); + o.put("status", "能源类型不能为空"); + errorDataList.add(o); + return; + } + if (StringUtils.isEmpty(item.getSecondHand())) { + JSONObject o = JSONObject.from(item); + o.put("status", "是否二手车不能为空"); + errorDataList.add(o); + return; + } + if (energyTypeList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getEnergyTypeName())).count() == 0) { + JSONObject o = JSONObject.from(item); + o.put("status", "能源类型不存在"); + errorDataList.add(o); + return; + } } if (goodsNature.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getPtrDesc(), item.getNatureFlagName())).count() == 0) { JSONObject o = JSONObject.from(item); @@ -3162,16 +3364,9 @@ public class ExportInHandler implements BaseHandler { String batchNo = customerService.getSequenceNo("export_in_batch_no", "出口进场", "EI"); in.setBatchNo(batchNo); in.setApplyTime(new Date()); - in.setPortId(portList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getPortName())).findFirst().get().getId()); // 处理国家 - if (!portCountryList.containsKey(in.getPortId())) { - DictDTO dto = dictHandler.getCountryByPortName(in.getPortId()).getData(); - portCountryList.put(in.getPortId(), dto); - } - if (portCountryList.containsKey(in.getPortId())) { - in.setCountryId(portCountryList.get(in.getPortId()).getId()); - in.setCountry(portCountryList.get(in.getPortId()).getText()); - } + in.setCountryId(countryList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCtyCnname(), item.getCountry())).findFirst().get().getCtyId()); + in.setPortId(portList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getPortName()) && StringUtils.equals(p.getExtra2(), in.getCountryId())).findFirst().get().getId()); if (StringUtils.isNotEmpty(item.getEnterTime())) { // 验证进场时间是否正确 @@ -3185,12 +3380,14 @@ public class ExportInHandler implements BaseHandler { in.setBrandId(brandList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getBrand())).findFirst().get().getId()); in.setCartTypeId(carTypeList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getCartType())).findFirst().get().getId()); in.setVoyageId(in.getVoyage()); - in.setOriginPlaceId(countryList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCiyCnname(), item.getOriginPlace())).findFirst().get().getCiyId()); + in.setOriginPlaceId(originPlaceList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getCiyCnname(), item.getOriginPlace())).findFirst().get().getCiyId()); in.setTransportWayId(transportWayList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getTransportWay())).findFirst().get().getId()); in.setCartTypeDetailId(carDetailTypeList.get(in.getCartType()).stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getCartTypeDetail())).findFirst().get().getId()); in.setOperateTypeId(operateTypeList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getOperateType())).findFirst().get().getId()); in.setSpecWorkId(specWorkList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getSpecWork())).findFirst().get().getId()); - in.setEnergyType(energyTypeList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getEnergyTypeName())).findFirst().get().getId()); + if (!StringUtils.equals("备件", item.getCartType())) { + in.setEnergyType(energyTypeList.stream().filter(p -> StringUtils.equalsIgnoreCase(p.getText(), item.getEnergyTypeName())).findFirst().get().getId()); + } in.setFreightId(companyMap.get(item.getFreight())); in.setApplicantId(UserContext.getUser().getUserId()); in.setTermcd(in.getPortAreaId()); @@ -3228,6 +3425,7 @@ public class ExportInHandler implements BaseHandler { cQuery.eq(CustomerExportIn::getShipId, shipId); cQuery.eq(CustomerExportIn::getVoyageId, voyageId); cQuery.eq(CustomerExportIn::getBillNum, billNo); + cQuery.ne(CustomerExportIn::getCheckStatus, AuditEnum.AUDIT_REJECT); List exportInList = customerExportInService.list(cQuery); @@ -3247,22 +3445,22 @@ public class ExportInHandler implements BaseHandler { // 重量 double totalWeight = exportInList.stream().map(p -> p.getWeight().doubleValue()).mapToDouble(p -> p).sum() + item.getValue().stream().map(p -> p.getWeight().doubleValue()).mapToDouble(p -> p).sum(); if (new BigDecimal(totalWeight).compareTo(lastE.getEachWeight().divide(new BigDecimal(1000))) > 0) { - errorDataList.addAll(item.getValue().stream().map(p -> { - JSONObject o = JSONObject.from(p); - o.put("status", "重量不得超过单票重量"); - return o; - }).collect(Collectors.toList())); - return; +// errorDataList.addAll(item.getValue().stream().map(p -> { +// JSONObject o = JSONObject.from(p); +// o.put("status", "重量不得超过单票重量"); +// return o; +// }).collect(Collectors.toList())); +// return; } // 体积 double totalVolume = exportInList.stream().map(p -> p.getVolume().doubleValue()).mapToDouble(p -> p).sum() + item.getValue().stream().map(p -> p.getVolume().doubleValue()).mapToDouble(p -> p).sum(); if (new BigDecimal(totalVolume).compareTo(lastE.getEachVolume()) > 0) { - errorDataList.addAll(item.getValue().stream().map(p -> { - JSONObject o = JSONObject.from(p); - o.put("status", "体积不得超过单票体积"); - return o; - }).collect(Collectors.toList())); - return; +// errorDataList.addAll(item.getValue().stream().map(p -> { +// JSONObject o = JSONObject.from(p); +// o.put("status", "体积不得超过单票体积"); +// return o; +// }).collect(Collectors.toList())); +// return; } successDataList.addAll(item.getValue().stream().map(p -> { @@ -3400,12 +3598,15 @@ public class ExportInHandler implements BaseHandler { @ApiOperation("出口进场计划船名航次") @PostMapping("/plan/ship") public Result> getExportInPlanShipList( + @RequestParam(required = false) @NotBlank(message = "港区ID不能为空") String portAreaId, @RequestParam(required = false, defaultValue = "1") Integer current, @RequestParam(required = false, defaultValue = "10") Integer size, @RequestParam(required = false) String q) { QueryWrapper query = new QueryWrapper<>(); query.select("distinct ship_id, ship_name, ship_en_name"); query.eq("check_status", AuditEnum.AUDIT_PASS); + query.eq("port_area_id", portAreaId); + query.eq("load_ship_flag", 0); if (StringUtils.isNotEmpty(q)) { query.and((wrapper) -> { wrapper.like("ship_name", q); @@ -3428,6 +3629,16 @@ public class ExportInHandler implements BaseHandler { return ResultUtil.success(rst, String.valueOf(page.getTotal())); } + @ApiOperation("装船确认状态通知") + @PostMapping("/ship/load/confirm") + public Result shipLoadConfirm(@RequestBody @Validated ShipStatusVo vo) { + customerExportInService.lambdaUpdate().eq(CustomerExportIn::getShipId, vo.getShipId()) + .eq(CustomerExportIn::getVoyageId, vo.getVoyageId()) + .set(CustomerExportIn::getLoadShipFlag, StringUtils.equals("0", vo.getStatus()) ? 0 : 1) + .update(); + return ResultUtil.success("success"); + } + @ApiOperation("出口进场计划航次") @PostMapping("/plan/voyage") public Result> getPlanVoyageList( diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportLoadHandler.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportLoadHandler.java index 9610da4..82d5cbd 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportLoadHandler.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/ExportLoadHandler.java @@ -1036,10 +1036,10 @@ public class ExportLoadHandler implements BaseHandler { List successDataList = new ArrayList<>(); // 要保存的表头 - Map heads = new LinkedHashMap<>(); + Map heads = new LinkedHashMap<>(); // 要保存的明细 - Map> details = new HashMap<>(); + Map> details = new HashMap<>(); // 港口基础数据 @@ -1221,8 +1221,8 @@ public class ExportLoadHandler implements BaseHandler { exportLoad.setApplyTime(new Date()); exportLoad.setApplicantId(UserContext.getUser().getUserId()); - heads.put(exportIn.getId(), exportLoad); - details.put(exportIn.getId(), saveCargos); + heads.put(item.getKey(), exportLoad); + details.put(item.getKey(), saveCargos); } diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/excel/ExcelValidationUtils.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/excel/ExcelValidationUtils.java new file mode 100644 index 0000000..706c212 --- /dev/null +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/handler/excel/ExcelValidationUtils.java @@ -0,0 +1,252 @@ +package com.haitonggauto.rtosc.handler.excel; + +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.xssf.usermodel.*; + +import java.io.FileOutputStream; +import java.util.*; + +/** + * excel验证工具类 + * + * @author chenchuancheng github.com/meethigher + * @since 2023/08/20 23:55 + */ +public class ExcelValidationUtils { + + + private static final int minRow = 1; + + private static final int maxRow = 100; + + private static final boolean debugHideSheet = false; + + + /** + * 创建一个xlsx + * + * @return {@link XSSFWorkbook} + */ + public static XSSFWorkbook createOneXLSX() { + return new XSSFWorkbook(); + } + + /** + * 为xlsx添加一个sheet + * + * @param wb xlsx + * @param sheetName sheet名 + * @param headers 首行标题头 + * @return sheet + */ + public static XSSFSheet addOneSheet(XSSFWorkbook wb, String sheetName, String[] headers) { + XSSFSheet st = wb.createSheet(sheetName); + //表头样式 + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式 + //字体样式 + Font fontStyle = wb.createFont(); + fontStyle.setFontName("微软雅黑"); + fontStyle.setFontHeightInPoints((short) 12); + style.setFont(fontStyle); + //单元格格式为文本 + XSSFDataFormat format = wb.createDataFormat(); + style.setDataFormat(format.getFormat("@")); + //写标题 + XSSFRow row = st.createRow(0); + st.createFreezePane(0, 1, 0, 1); + for (int i = 0; i < headers.length; i++) { + String value = headers[i]; + XSSFCell cell = row.createCell(i); + st.setColumnWidth(i, value.length() * 1000); + cell.setCellStyle(style); + st.setDefaultColumnStyle(i, style); + cell.setCellValue(value); + } + return st; + } + + + /** + * 添加两层级联数据 + * + * @param wb xlsx + * @param targetSheet 目标sheet + * @param linkageData 两层级联数据 + * @param parentCol 父列 + * @param childCol 孩子列 + * @param parentColIdentifier 父列标识符 + * @return {@link XSSFSheet} + */ + public static XSSFSheet addLinkageDataValidation(XSSFWorkbook wb, XSSFSheet targetSheet, Map> linkageData, + int parentCol, int childCol, String parentColIdentifier) { + XSSFSheet hideSt = wb.createSheet(); + wb.setSheetHidden(wb.getSheetIndex(hideSt), !debugHideSheet); + int rowId = 0; + Set keySet = linkageData.keySet(); + for (String parent : keySet) { + List sonList = linkageData.get(parent); + XSSFRow row = hideSt.createRow(rowId++); + row.createCell(0).setCellValue(parent); + for (int i = 0; i < sonList.size(); i++) { + XSSFCell cell = row.createCell(i + 1); + cell.setCellValue(sonList.get(i)); + } + // 添加名称管理器,1表示b列,从b列开始往后,都是子级 + String range = getRange(1, rowId, sonList.size()); + Name name = wb.createName(); + name.setNameName(parent); + String formula = hideSt.getSheetName() + "!" + range; + name.setRefersToFormula(formula); + } + //创建表达式校验 + XSSFDataValidationHelper helper = new XSSFDataValidationHelper(targetSheet); + +// //父级校验,如需生成更多,用户手动拖拽下拉即可。此操作会导致数组内容总长度超过255时报错 +// DataValidation parentValidation = helper.createValidation(helper.createExplicitListConstraint(keySet.toArray(new String[0])), +// new CellRangeAddressList(minRow, maxRow, parentCol, parentCol)); +// parentValidation.createErrorBox("错误", "请选择正确的父级类型"); +// parentValidation.setShowErrorBox(true); +// parentValidation.setSuppressDropDownArrow(true); +// targetSheet.addValidationData(parentValidation); + + //解决长度为255的问题 + Name name = wb.createName(); + name.setNameName(hideSt.getSheetName()); + name.setRefersToFormula(hideSt.getSheetName() + "!$A$1:$A$" + keySet.size()); + DataValidation parentValidation = helper.createValidation(helper.createFormulaListConstraint(hideSt.getSheetName()), new CellRangeAddressList(minRow, maxRow, parentCol, parentCol)); + parentValidation.createErrorBox("错误", "请选择正确的父级类型"); + parentValidation.setShowErrorBox(true); + targetSheet.addValidationData(parentValidation); + + //子级校验,如需生成更多,用户手动拖拽下拉即可 + for (int i = minRow; i < maxRow; i++) { + DataValidation childValidation = helper.createValidation(helper.createFormulaListConstraint("INDIRECT(" + parentColIdentifier + "" + (i + 1) + ")"), + new CellRangeAddressList(i, i, childCol, childCol)); + childValidation.createErrorBox("错误", "请选择正确的子级类型"); + childValidation.setShowErrorBox(true); + childValidation.setSuppressDropDownArrow(true); + targetSheet.addValidationData(childValidation); + } + + return hideSt; + } + + /** + * 添加简单下拉列表验证-下拉列表总内容不超过255字符 + * + * @param st sheet + * @param dropDownList 下拉列表数据 + * @param firstCol 开始列,从0开始 + * @param lastCol 结束列,从0开始 + */ + public static void addSimpleDropDownListValidation(XSSFSheet st, String[] dropDownList, int firstCol, int lastCol) { + XSSFDataValidationHelper helper = new XSSFDataValidationHelper(st); + XSSFDataValidationConstraint constraint = (XSSFDataValidationConstraint) helper.createExplicitListConstraint(dropDownList); + CellRangeAddressList addressList = new CellRangeAddressList(minRow, maxRow, firstCol, lastCol); + XSSFDataValidation validation = (XSSFDataValidation) helper.createValidation(constraint, addressList); + validation.setSuppressDropDownArrow(true); + validation.setShowErrorBox(true); + st.addValidationData(validation); + } + + + /** + * 添加复杂下拉列表验证-下拉列表总内容允许超过255字符 + * + * @param wb xlsx + * @param dropDownList 下拉列表数据 + * @param firstCol 开始列,从0开始 + * @param lastCol 结束列,从0开始 + */ + public static void addComplexDropDownListValidation(XSSFWorkbook wb, XSSFSheet st, String[] dropDownList, int firstCol, int lastCol) { + XSSFSheet hideSt = wb.createSheet(); + wb.setSheetHidden(wb.getSheetIndex(hideSt), !debugHideSheet); + XSSFDataValidationHelper helper = new XSSFDataValidationHelper(st); + for (int i = 0, length = dropDownList.length; i < length; i++) { + String value = dropDownList[i]; + XSSFRow row = hideSt.createRow(i); + XSSFCell cell = row.createCell(0); + cell.setCellValue(value); + } + //解决长度为255的问题 + Name name = wb.createName(); + name.setNameName(hideSt.getSheetName()); + name.setRefersToFormula(hideSt.getSheetName() + "!$A$1:$A$" + dropDownList.length); + DataValidation parentValidation = helper.createValidation(helper.createFormulaListConstraint(hideSt.getSheetName()), new CellRangeAddressList(minRow, maxRow, firstCol, lastCol)); + parentValidation.createErrorBox("错误", "请选择正确的类型"); + parentValidation.setShowErrorBox(true); + st.addValidationData(parentValidation); + } + + + /** + * 计算formula + * + * @param offset 偏移量,如果给0,表示从A列开始,1,就是从B列 + * @param rowId 第几行 + * @param colCount 一共多少列 + * @return 如果给入参 1,1,10. 表示从B1-K1。最终返回 $B$1:$K$1 + */ + private static String getRange(int offset, int rowId, int colCount) { + char start = (char) ('A' + offset); + if (colCount <= 25) { + char end = (char) (start + colCount - 1); + return "$" + start + "$" + rowId + ":$" + end + "$" + rowId; + } else { + char endPrefix = 'A', endSuffix; + if ((colCount - 25) / 26 == 0 || colCount == 51) {// 26-51之间,包括边界(仅两次字母表计算) + if ((colCount - 25) % 26 == 0) {// 边界值 + endSuffix = (char) ('A' + 25); + } else { + endSuffix = (char) ('A' + (colCount - 25) % 26 - 1); + } + } else {// 51以上 + if ((colCount - 25) % 26 == 0) { + endSuffix = (char) ('A' + 25); + endPrefix = (char) (endPrefix + (colCount - 25) / 26 - 1); + } else { + endSuffix = (char) ('A' + (colCount - 25) % 26 - 1); + endPrefix = (char) (endPrefix + (colCount - 25) / 26); + } + } + return "$" + start + "$" + rowId + ":$" + endPrefix + endSuffix + "$" + rowId; + } + } + + private final static String[] headers = new String[]{ + "性别", + "省", + "市", + "区", + }; + + + private static Map> 省级() { + Map> map = new HashMap<>(); + map.put("湖北省(可以吗)", Arrays.asList("武汉市", "襄阳市")); + map.put("吉林省", Arrays.asList("长春市", "吉林市")); + return map; + } + + private static Map> 市级() { + Map> map = new HashMap<>(); + map.put("武汉市", Arrays.asList("洪山区", "江夏区")); + map.put("长春市", Arrays.asList("宽城区", "南关区")); + return map; + } + + public static void main(String[] args) throws Exception { + + + XSSFWorkbook wb = createOneXLSX(); + XSSFSheet st = addOneSheet(wb, "data", headers); + addSimpleDropDownListValidation(st, new String[]{"男", "女"}, 0, 0); + addLinkageDataValidation(wb, st, 省级(), 1, 2, "B"); + addLinkageDataValidation(wb, st, 市级(), 2, 3, "C"); + + + wb.write(new FileOutputStream("d:\\aaa.xlsx")); + } +} diff --git a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/service/impl/CustomerServiceImpl.java b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/service/impl/CustomerServiceImpl.java index 0a1a5a3..e8a691e 100644 --- a/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/service/impl/CustomerServiceImpl.java +++ b/nuzar-customer-controller/src/main/java/com/haitonggauto/rtosc/service/impl/CustomerServiceImpl.java @@ -31,6 +31,7 @@ import java.util.*; import java.util.stream.Collectors; @Service +@Slf4j public class CustomerServiceImpl implements CustomerService { @Autowired @@ -787,6 +788,7 @@ public class CustomerServiceImpl implements CustomerService { @Override @Async public void syncAddExportInToOld(List list) { + log.info("进入同步方法"); if (!syncConfig.getSync() || CollectionUtils.isEmpty(list)) { // 没有打开同步或,列表为空真直接返回 return; } @@ -830,11 +832,13 @@ public class CustomerServiceImpl implements CustomerService { try { + log.info("同步老系统:" + JSONObject.toJSONString(req)); + String post = OkHttpUtils.post(syncConfig.getUrl() + "/execInPortPlanAdd", OkHttpUtils.buildJsonRequestBody(JSONObject.toJSONString(req)), null); JSONObject rst = JSONObject.parseObject(post); - System.err.println(rst.toJSONString()); + log.info("同步老系统结果:" + JSONObject.toJSONString(rst)); if (StringUtils.equals("0", rst.getString("success"))) { throw new RuntimeException("请求参数:" + JSONObject.toJSONString(req) + ", 错误信息:" + rst.getString("errmsg")); diff --git a/nuzar-customer-controller/src/main/resources/templates/in_temp.xlsx b/nuzar-customer-controller/src/main/resources/templates/in_temp.xlsx index b219172c9beea4d66f7998c6d4d680952adfc70e..5eb4381ae45384aa6d0babb9544a39311df7b3ad 100644 GIT binary patch delta 14003 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 delta 9361 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�jEX|uzNQA8 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