mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 12:52:07 +08:00
1、新增组件订单数据拉取接口;2、新增上传url产生文件数据/根据文件id获取文件地址接口;
This commit is contained in:
+9
@@ -25,4 +25,13 @@ public class ErrorCodeConstants {
|
||||
public static final ErrorCode WEBCAD_ORDER_IMPORT_ORDER_UPDATE_CONCURRENCY_ERROR = new ErrorCode(1_005_000_015, "webcad.order.import.order.update.concurrent");
|
||||
public static final ErrorCode WEBCAD_ORDER_IMPORT_GOODS_UPDATE_CONCURRENCY_ERROR = new ErrorCode(1_005_000_016, "webcad.order.import.goods.update.concurrent");
|
||||
public static final ErrorCode WEBCAD_ORDER_IMPORT_CAD_VIEW_FILE_ID_EMPTY_ERROR = new ErrorCode(1_005_000_017, "webcad.order.import.cad.view.file.id.empty.error");
|
||||
public static final ErrorCode PRODUCING_ORDER_PULL_FAILED = new ErrorCode(1_005_000_018, "producing.order.pull.failed");
|
||||
public static final ErrorCode PRODUCING_ORDER_RESPONSE_INVALID = new ErrorCode(1_005_000_019, "producing.order.response.invalid");
|
||||
public static final ErrorCode PRODUCING_ORDER_BLOCK_ITEMS_EMPTY = new ErrorCode(1_005_000_020, "producing.order.block_items.empty");
|
||||
public static final ErrorCode PRODUCING_ORDER_REFERENCE_INVALID = new ErrorCode(1_005_000_021, "producing.order.reference.invalid");
|
||||
public static final ErrorCode PRODUCING_ORDER_MATERIAL_MAPPING_INVALID = new ErrorCode(1_005_000_022, "producing.order.material_mapping.invalid");
|
||||
public static final ErrorCode PRODUCING_ORDER_CAD_VIEW_PATH_EMPTY = new ErrorCode(1_005_000_023, "producing.order.cad_view_path.empty");
|
||||
public static final ErrorCode PRODUCING_ORDER_CAD_OBJECT_INVALID = new ErrorCode(1_005_000_024, "producing.order.cad_object.invalid");
|
||||
public static final ErrorCode PRODUCING_ORDER_ENUM_INVALID = new ErrorCode(1_005_000_025, "producing.order.enum.invalid");
|
||||
public static final ErrorCode PRODUCING_ORDER_IMPORT_FAILED = new ErrorCode(1_005_000_026, "producing.order.import.failed");
|
||||
}
|
||||
|
||||
+20
@@ -6,12 +6,16 @@ import com.cf.imes.framework.common.pojo.CommonResult;
|
||||
import com.cf.imes.framework.operatelog.core.annotations.OperateLog;
|
||||
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
|
||||
import com.cf.imes.module.plan.service.orderImport.WebCadOrderImportService;
|
||||
import com.cf.imes.module.plan.service.orderImport.ProducingOrderImportService;
|
||||
import com.cf.imes.module.plan.controller.admin.orderimport.producing.vo.ProducingOrderImportReqVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -29,6 +33,8 @@ import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPO
|
||||
public class OrderImportController {
|
||||
@Resource
|
||||
private WebCadOrderImportService webCadOrderImportService;
|
||||
@Resource
|
||||
private ProducingOrderImportService producingOrderImportService;
|
||||
|
||||
@PostMapping("/webcad/import")
|
||||
@Operation(summary = "webcad拆单导入订单")
|
||||
@@ -42,4 +48,18 @@ public class OrderImportController {
|
||||
webCadOrderImportService.webCadOrderImport(file);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步拉取一条组件生产单,并导入到指定的 iMES 订单。
|
||||
*
|
||||
* @param request 来源订单、目标订单及板材映射
|
||||
* @return 是否导入成功
|
||||
*/
|
||||
@PostMapping("/producing/import")
|
||||
@Operation(summary = "拉取组件生产单并同步导入订单")
|
||||
@OperateLog(type = IMPORT)
|
||||
public CommonResult<Boolean> producingOrderImport(
|
||||
@Valid @RequestBody ProducingOrderImportReqVO request) {
|
||||
return CommonResult.success(producingOrderImportService.importOrder(request));
|
||||
}
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.cf.imes.module.plan.controller.admin.orderimport.producing.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单同步导入请求。
|
||||
*
|
||||
* <p>一次请求仅处理一条来源订单和一条目标订单;板材映射由前端完成后提交。</p>
|
||||
*/
|
||||
@Schema(description = "管理后台 - 组件生产单同步导入 Request VO")
|
||||
@Data
|
||||
public class ProducingOrderImportReqVO {
|
||||
|
||||
/**
|
||||
* 组件生产系统订单 ID。
|
||||
*/
|
||||
@Schema(description = "组件系统订单 ID", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "26072801000449")
|
||||
@NotBlank(message = "{producing.order.import.sourceOrderId.notBlank}")
|
||||
private String sourceOrderId;
|
||||
|
||||
/**
|
||||
* 本次数据要拆入的 iMES 目标订单 ID。
|
||||
*/
|
||||
@Schema(description = "iMES 目标订单 ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{producing.order.import.targetOrderId.notNull}")
|
||||
private Long targetOrderId;
|
||||
|
||||
/**
|
||||
* 对方板材 ID 与 iMES 板材库 plate.id 的一对一映射。
|
||||
*/
|
||||
@Schema(description = "组件系统板材与 iMES 板材映射", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
@NotEmpty(message = "{producing.order.import.materialMappings.notEmpty}")
|
||||
private List<MaterialMapping> materialMappings;
|
||||
|
||||
@Data
|
||||
public static class MaterialMapping {
|
||||
|
||||
/**
|
||||
* 对方 materials 中 type=1 的板材 ID。
|
||||
*/
|
||||
@Schema(description = "组件系统 materials[type=1].id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{producing.order.import.sourceMaterialId.notBlank}")
|
||||
private String sourceMaterialId;
|
||||
|
||||
/**
|
||||
* 映射后的 iMES 材质库 plate.id。
|
||||
*/
|
||||
@Schema(description = "iMES 材质库 plate.id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "{producing.order.import.plateId.notNull}")
|
||||
private Long plateId;
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -75,6 +75,12 @@ public class OrderModelDO {
|
||||
@Schema(description = "偏移量Y")
|
||||
private BigDecimal offsetY;
|
||||
|
||||
@Schema(description = "封边数据 X 偏移")
|
||||
private BigDecimal sealOffsetX;
|
||||
|
||||
@Schema(description = "封边数据 Y 偏移")
|
||||
private BigDecimal sealOffsetY;
|
||||
|
||||
@Schema(description = "是否有二维刀路")
|
||||
private Boolean has2DModel;
|
||||
@Schema(description = "是否有三维刀路")
|
||||
@@ -121,4 +127,4 @@ public class OrderModelDO {
|
||||
private String remarkJson; // 普通备注(板材数据中)
|
||||
@Schema(description = "特殊备注(板材数据中)")
|
||||
private String specialRemark; // 特殊备注(板材数据中)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -268,4 +268,9 @@ public class PlateDO extends BaseDO {
|
||||
* 造型数据
|
||||
*/
|
||||
private String modelData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件生产系统的物理板件 ID,对应来源报文中的 {@code blockItems[].id}。
|
||||
*/
|
||||
private String sourceBlockItemId;
|
||||
}
|
||||
|
||||
+6
@@ -36,6 +36,12 @@ public class PointDetail {
|
||||
@Schema(description = "点的封边尺寸,封边尺寸,封边的厚度,长同边长")
|
||||
private BigDecimal sealSize;
|
||||
|
||||
@Schema(description = "封边颜色")
|
||||
private String sealColor;
|
||||
|
||||
@Schema(description = "开料预留边")
|
||||
private BigDecimal reservedEdge;
|
||||
|
||||
@Schema(description = "加工工艺")
|
||||
private String process;
|
||||
}
|
||||
|
||||
+7
-2
@@ -2,6 +2,7 @@ package com.cf.imes.module.plan.framework.rpc.config;
|
||||
|
||||
import com.cf.imes.module.infra.api.file.FileApi;
|
||||
import com.cf.imes.module.system.api.assemblyconfig.AssemblyConfigApi;
|
||||
import com.cf.imes.module.system.api.auth.AuthApi;
|
||||
import com.cf.imes.module.system.api.customplateno.CustomPlateNoSeqApi;
|
||||
import com.cf.imes.module.system.api.dict.DictDataApi;
|
||||
import com.cf.imes.module.system.api.systemconfig.SystemConfigApi;
|
||||
@@ -10,9 +11,13 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author there
|
||||
* 生产计划模块 RPC 客户端配置。
|
||||
*
|
||||
* <p>组件生产单导入通过 {@link FileApi} 登记外部图纸地址,并通过 {@link AuthApi}
|
||||
* 获取调用组件生产系统所需的内部 JWT。</p>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = {AdminUserApi.class, DictDataApi.class, SystemConfigApi.class, CustomPlateNoSeqApi.class, FileApi.class, AssemblyConfigApi.class})
|
||||
@EnableFeignClients(clients = {AdminUserApi.class, DictDataApi.class, SystemConfigApi.class, CustomPlateNoSeqApi.class,
|
||||
FileApi.class, AssemblyConfigApi.class, AuthApi.class})
|
||||
public class RpcConfiguration {
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.cf.imes.module.plan.service.orderImport;
|
||||
|
||||
import com.cf.imes.module.plan.controller.admin.orderimport.producing.vo.ProducingOrderImportReqVO;
|
||||
|
||||
/**
|
||||
* 组件生产系统订单的同步导入入口。
|
||||
*/
|
||||
public interface ProducingOrderImportService {
|
||||
|
||||
/**
|
||||
* 同步拉取、校验并导入一条组件生产单。
|
||||
*
|
||||
* @param request 来源订单、目标订单及板材映射
|
||||
* @return 是否导入成功
|
||||
*/
|
||||
boolean importOrder(ProducingOrderImportReqVO request);
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package com.cf.imes.module.plan.service.orderImport;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
|
||||
import com.cf.imes.framework.redis.config.ChenfengCacheProperties;
|
||||
import com.cf.imes.framework.redis.constants.RedisKeyConstants;
|
||||
import com.cf.imes.framework.redis.util.RedisLockUtil;
|
||||
import com.cf.imes.module.infra.api.file.FileApi;
|
||||
import com.cf.imes.module.infra.api.file.dto.ExternalFileCreateReqDTO;
|
||||
import com.cf.imes.module.plan.controller.admin.orderimport.producing.vo.ProducingOrderImportReqVO;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.ProducingOrderClient;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.ProducingOrderConverter;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingOrderResponse;
|
||||
import com.cf.imes.module.system.api.auth.AuthApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORGAN_LOCK_ERROR;
|
||||
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.PRODUCING_ORDER_IMPORT_FAILED;
|
||||
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORGANID_EMPTY_ERROR;
|
||||
|
||||
/**
|
||||
* 编排组件生产单导入的事务外步骤:生成内部调用令牌、拉取并校验报文、登记图纸外链、
|
||||
* 获取组织级导入锁,再进入数据库事务。
|
||||
*
|
||||
* <p>图纸文件记录必须先创建;后续失败时仅做尽力补偿,补偿失败不覆盖原始导入异常。</p>
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ProducingOrderImportServiceImpl implements ProducingOrderImportService {
|
||||
|
||||
private static final String IMPORT_LOCK_VALUE = "webcad";
|
||||
|
||||
@Resource
|
||||
private AuthApi authApi;
|
||||
@Resource
|
||||
private FileApi fileApi;
|
||||
@Resource
|
||||
private ProducingOrderClient producingOrderClient;
|
||||
@Resource
|
||||
private ProducingOrderConverter producingOrderConverter;
|
||||
@Resource
|
||||
private ProducingOrderImportTransactionalService transactionalService;
|
||||
@Resource
|
||||
private RedisLockUtil redisLockUtil;
|
||||
@Resource
|
||||
private ChenfengCacheProperties chenfengCacheProperties;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>数据库事务开始前完成远程报文拉取、完整校验和图纸地址登记,
|
||||
* 避免把网络等待纳入订单写库事务。</p>
|
||||
*/
|
||||
@Override
|
||||
public boolean importOrder(ProducingOrderImportReqVO request) {
|
||||
long totalStartedAt = System.nanoTime();
|
||||
Long organId = OrganContextHolder.getOrganId();
|
||||
if (ObjectUtil.isNull(organId)) {
|
||||
throw new ServiceException(WEBCAD_ORDER_IMPORT_ORGANID_EMPTY_ERROR);
|
||||
}
|
||||
|
||||
long stageStartedAt = System.nanoTime();
|
||||
String token = authApi.getMesJwtToken().getCheckedData();
|
||||
logStage(request, "token", stageStartedAt);
|
||||
|
||||
stageStartedAt = System.nanoTime();
|
||||
ProducingOrderResponse response =
|
||||
producingOrderClient.getOrder(request.getSourceOrderId(), token);
|
||||
logStage(request, "pull", stageStartedAt);
|
||||
|
||||
stageStartedAt = System.nanoTime();
|
||||
producingOrderConverter.validate(response, request);
|
||||
logStage(request, "validate", stageStartedAt);
|
||||
|
||||
ExternalFileCreateReqDTO fileRequest = new ExternalFileCreateReqDTO();
|
||||
fileRequest.setName("cad-view-" + request.getSourceOrderId() + ".json");
|
||||
fileRequest.setUrl(response.getData().getCadViewPath());
|
||||
stageStartedAt = System.nanoTime();
|
||||
Long fileId = fileApi.createExternalFile(fileRequest).getCheckedData();
|
||||
logStage(request, "file-register", stageStartedAt);
|
||||
if (fileId == null) {
|
||||
throw new ServiceException(PRODUCING_ORDER_IMPORT_FAILED);
|
||||
}
|
||||
|
||||
String lockKey = String.format(RedisKeyConstants.ORDER_IMPORT_LOCK_KEY, organId);
|
||||
boolean locked = false;
|
||||
try {
|
||||
stageStartedAt = System.nanoTime();
|
||||
locked = redisLockUtil.acquireLock(lockKey, IMPORT_LOCK_VALUE,
|
||||
chenfengCacheProperties.getLockTimeout());
|
||||
logStage(request, "lock", stageStartedAt);
|
||||
if (!locked) {
|
||||
throw new ServiceException(ORDER_IMPORT_ORGAN_LOCK_ERROR);
|
||||
}
|
||||
|
||||
stageStartedAt = System.nanoTime();
|
||||
transactionalService.importOrder(response, request, organId, fileId);
|
||||
logStage(request, "transaction", stageStartedAt);
|
||||
log.info("[producing-import] sourceOrderId={} targetOrderId={} stage=total elapsedMs={}",
|
||||
request.getSourceOrderId(), request.getTargetOrderId(),
|
||||
elapsedMillis(totalStartedAt));
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
compensateFileRecord(fileId);
|
||||
if (ex instanceof ServiceException serviceException) {
|
||||
throw serviceException;
|
||||
}
|
||||
log.error("Import producing order {} failed", request.getSourceOrderId(), ex);
|
||||
throw new ServiceException(PRODUCING_ORDER_IMPORT_FAILED);
|
||||
} finally {
|
||||
if (locked) {
|
||||
redisLockUtil.releaseLock(lockKey, IMPORT_LOCK_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入失败后尽力删除本次生成的文件登记记录。
|
||||
*
|
||||
* <p>补偿失败只记录日志,不覆盖原始导入异常,也不会尝试删除 OSS 文件。</p>
|
||||
*
|
||||
* @param fileId 本次导入预先生成的 iMES 文件 ID
|
||||
*/
|
||||
private void compensateFileRecord(Long fileId) {
|
||||
try {
|
||||
fileApi.deleteExternalFileRecord(fileId).getCheckedData();
|
||||
} catch (Exception compensationError) {
|
||||
log.warn("Failed to compensate external file record {}", fileId, compensationError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出事务外导入阶段耗时。
|
||||
*/
|
||||
private void logStage(ProducingOrderImportReqVO request, String stage, long startedAt) {
|
||||
log.info("[producing-import] sourceOrderId={} targetOrderId={} stage={} elapsedMs={}",
|
||||
request.getSourceOrderId(), request.getTargetOrderId(), stage,
|
||||
elapsedMillis(startedAt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将纳秒计时起点换算为已耗费毫秒数。
|
||||
*/
|
||||
private long elapsedMillis(long startedAt) {
|
||||
return (System.nanoTime() - startedAt) / 1_000_000;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package com.cf.imes.module.plan.service.orderImport;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.cf.imes.framework.common.enums.OrderPackageStatusEnum;
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.framework.id.core.util.SnowflakeIdWorker3rd;
|
||||
import com.cf.imes.framework.mybatis.core.generator.SnowFlakeGenerator;
|
||||
import com.cf.imes.module.executor.enums.DataTypeEnum;
|
||||
import com.cf.imes.module.executor.enums.OrderStatusEnum;
|
||||
import com.cf.imes.module.plan.controller.admin.orderimport.producing.vo.ProducingOrderImportReqVO;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingImportData;
|
||||
import com.cf.imes.module.plan.dal.dataobject.order.OrderDO;
|
||||
import com.cf.imes.module.plan.dal.dataobject.plate.PlateDO;
|
||||
import com.cf.imes.module.plan.dal.mysql.goods.GoodsMapper;
|
||||
import com.cf.imes.module.plan.dal.mysql.order.OrderMapper;
|
||||
import com.cf.imes.module.plan.dal.mysql.orderBody.OrderBodyMapper;
|
||||
import com.cf.imes.module.plan.dal.mysql.plate.PlateMapper;
|
||||
import com.cf.imes.module.plan.service.customplateno.CustomPlateNoGenerateService;
|
||||
import com.cf.imes.module.plan.service.orderImport.factory.ProducingOrderImportFactory;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.ProducingOrderConverter;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingOrderResponse;
|
||||
import com.cf.imes.module.plan.service.orderImport.property.ProducingOrderImportProperties;
|
||||
import com.cf.imes.module.system.api.assemblyconfig.AssemblyConfigApi;
|
||||
import com.cf.imes.module.system.api.systemconfig.SystemConfigApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_IMPORT_ORDER_PLATENUM_REACH_THRESHOLD_ERROR;
|
||||
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.WEBCAD_ORDER_NOT_EXISTS_ERROR;
|
||||
|
||||
/**
|
||||
* 组件生产单的写库事务边界。
|
||||
*
|
||||
* <p>目标订单校验、报文转换、订单状态更新以及板件/配件/加工组/部件组入库任一步失败,
|
||||
* 都会回滚本次数据库变更。</p>
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ProducingOrderImportTransactionalService {
|
||||
|
||||
@Resource
|
||||
private OrderMapper orderMapper;
|
||||
@Resource
|
||||
private OrderBodyMapper orderBodyMapper;
|
||||
@Resource
|
||||
private PlateMapper plateMapper;
|
||||
@Resource
|
||||
private GoodsMapper goodsMapper;
|
||||
@Resource
|
||||
private SnowFlakeGenerator snowFlakeGenerator;
|
||||
@Resource
|
||||
private SnowflakeIdWorker3rd idWorker;
|
||||
@Resource
|
||||
private CustomPlateNoGenerateService customPlateNoGenerateService;
|
||||
@Resource
|
||||
private SystemConfigApi systemConfigApi;
|
||||
@Resource
|
||||
private AssemblyConfigApi assemblyConfigApi;
|
||||
@Resource
|
||||
private ProducingOrderImportProperties producingOrderImportProperties;
|
||||
@Resource
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
@Resource
|
||||
private ProducingOrderConverter producingOrderConverter;
|
||||
|
||||
/**
|
||||
* 在一个事务内完成目标订单状态更新及全部拆单业务数据写入。
|
||||
*
|
||||
* @param response 对方组件生产单完整报文
|
||||
* @param request 本次导入请求
|
||||
* @param organId 当前组织 ID
|
||||
* @param cadViewFileId 事务外预先生成的图纸文件 ID
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void importOrder(ProducingOrderResponse response,
|
||||
ProducingOrderImportReqVO request,
|
||||
Long organId,
|
||||
Long cadViewFileId) {
|
||||
long totalStartedAt = System.nanoTime();
|
||||
long stageStartedAt = System.nanoTime();
|
||||
OrderDO order = getTargetOrder(request.getTargetOrderId(), organId);
|
||||
logStage(request, "target-order", stageStartedAt);
|
||||
|
||||
stageStartedAt = System.nanoTime();
|
||||
validatePlateThreshold(order.getId(), organId,
|
||||
response.getData().getData().getBlockItems().size());
|
||||
logStage(request, "plate-threshold", stageStartedAt);
|
||||
|
||||
stageStartedAt = System.nanoTime();
|
||||
ProducingImportData producingImportData = producingOrderConverter.convert(
|
||||
response, request, organId, cadViewFileId);
|
||||
logStage(request, "convert", stageStartedAt);
|
||||
|
||||
stageStartedAt = System.nanoTime();
|
||||
updateOrderForImport(order);
|
||||
logStage(request, "update-order-state", stageStartedAt);
|
||||
|
||||
ProducingOrderImportFactory factory = new ProducingOrderImportFactory(
|
||||
organId, order, snowFlakeGenerator, orderMapper, orderBodyMapper, plateMapper,
|
||||
idWorker, goodsMapper, customPlateNoGenerateService, systemConfigApi,
|
||||
assemblyConfigApi, producingOrderImportProperties.getPlateNumThreshold(),
|
||||
jdbcTemplate);
|
||||
stageStartedAt = System.nanoTime();
|
||||
factory.analyzeTempData(producingImportData);
|
||||
logStage(request, "factory", stageStartedAt);
|
||||
log.info("[producing-import-transaction] sourceOrderId={} targetOrderId={} stage=total elapsedMs={}",
|
||||
request.getSourceOrderId(), request.getTargetOrderId(),
|
||||
elapsedMillis(totalStartedAt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出事务内各处理阶段耗时。
|
||||
*/
|
||||
private void logStage(ProducingOrderImportReqVO request, String stage, long startedAt) {
|
||||
log.info("[producing-import-transaction] sourceOrderId={} targetOrderId={} stage={} elapsedMs={}",
|
||||
request.getSourceOrderId(), request.getTargetOrderId(), stage,
|
||||
elapsedMillis(startedAt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将纳秒计时起点换算为已耗费毫秒数。
|
||||
*/
|
||||
private long elapsedMillis(long startedAt) {
|
||||
return (System.nanoTime() - startedAt) / 1_000_000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在当前组织内查询未删除的目标订单。
|
||||
*/
|
||||
private OrderDO getTargetOrder(Long orderId, Long organId) {
|
||||
OrderDO order = orderMapper.selectOne(new LambdaQueryWrapper<OrderDO>()
|
||||
.eq(OrderDO::getId, orderId)
|
||||
.eq(OrderDO::getOrganId, organId)
|
||||
.eq(OrderDO::getDeleted, false));
|
||||
if (ObjectUtil.isNull(order)) {
|
||||
throw new ServiceException(WEBCAD_ORDER_NOT_EXISTS_ERROR, orderId);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在写库前校验“已有板件数 + 本次板件数”没有超过订单导入上限。
|
||||
*/
|
||||
private void validatePlateThreshold(Long orderId, Long organId, int importCount) {
|
||||
Long existingCount = plateMapper.selectCount(new LambdaQueryWrapper<PlateDO>()
|
||||
.eq(PlateDO::getOrderId, orderId)
|
||||
.eq(PlateDO::getOrganId, organId)
|
||||
.eq(PlateDO::getDeleted, false));
|
||||
int threshold = producingOrderImportProperties.getPlateNumThreshold();
|
||||
if (existingCount + importCount > threshold) {
|
||||
throw new ServiceException(
|
||||
WEBCAD_ORDER_IMPORT_ORDER_PLATENUM_REACH_THRESHOLD_ERROR,
|
||||
orderId, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 沿用原拆单规则调整订单数据来源、生产状态和打包状态。
|
||||
*/
|
||||
private void updateOrderForImport(OrderDO order) {
|
||||
Integer status = order.getStatus();
|
||||
LambdaUpdateWrapper<OrderDO> update = new LambdaUpdateWrapper<OrderDO>()
|
||||
.eq(OrderDO::getId, order.getId())
|
||||
.eq(OrderDO::getOrganId, order.getOrganId())
|
||||
.eq(OrderDO::getDeleted, false)
|
||||
.set(OrderDO::getDataType, DataTypeEnum.API_IMPORT.getStatus());
|
||||
if (OrderStatusEnum.EMPTY.getStatus().equals(status)) {
|
||||
update.set(OrderDO::getStatus, OrderStatusEnum.NEW_ORDER.getStatus());
|
||||
}
|
||||
if (OrderPackageStatusEnum.PACKAGED.getStatus().equals(order.getPackaged())) {
|
||||
update.set(OrderDO::getPackaged, OrderPackageStatusEnum.UNPACKED.getStatus());
|
||||
}
|
||||
if (OrderStatusEnum.FINISH_PRODUCTION.getStatus().equals(status)) {
|
||||
update.set(OrderDO::getStatus, OrderStatusEnum.IN_PRODUCTION.getStatus());
|
||||
}
|
||||
orderMapper.update(update);
|
||||
}
|
||||
}
|
||||
+2814
File diff suppressed because it is too large
Load Diff
+102
@@ -0,0 +1,102 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing;
|
||||
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingOrderResponse;
|
||||
import com.cf.imes.module.plan.service.orderImport.property.ProducingOrderImportProperties;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.PRODUCING_ORDER_PULL_FAILED;
|
||||
import static com.cf.imes.module.plan.enums.ErrorCodeConstants.PRODUCING_ORDER_RESPONSE_INVALID;
|
||||
|
||||
/**
|
||||
* 组件生产系统订单查询客户端。
|
||||
*
|
||||
* <p>仅网络 I/O 异常按配置重试;HTTP 错误、业务错误和无法解析的报文直接失败,
|
||||
* 防止无意义的重复请求拉长同步导入事务外耗时。</p>
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ProducingOrderClient {
|
||||
|
||||
private final ProducingOrderImportProperties properties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
/**
|
||||
* 创建组件生产系统客户端,并从配置初始化超时参数。
|
||||
*
|
||||
* @param properties 组件生产单接口配置
|
||||
* @param objectMapper 系统 Jackson 配置
|
||||
*/
|
||||
public ProducingOrderClient(ProducingOrderImportProperties properties, ObjectMapper objectMapper) {
|
||||
this.properties = properties;
|
||||
this.objectMapper = objectMapper.copy()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(properties.getConnectTimeout())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用后端生成的 MES JWT 同步拉取一条组件生产单。
|
||||
*
|
||||
* @param orderId 组件生产系统订单 ID
|
||||
* @param token MES JWT,不包含 {@code Bearer } 前缀
|
||||
* @return 已完成基础业务码校验的生产单报文
|
||||
*/
|
||||
public ProducingOrderResponse getOrder(String orderId, String token) {
|
||||
String encodedOrderId = URLEncoder.encode(orderId, StandardCharsets.UTF_8);
|
||||
URI uri = URI.create(properties.getOrderUrl().replace("{id}", encodedOrderId));
|
||||
int maxAttempts = Math.max(1, properties.getNetworkRetryTimes() + 1);
|
||||
IOException lastNetworkError = null;
|
||||
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
HttpRequest request = HttpRequest.newBuilder(uri)
|
||||
.timeout(properties.getReadTimeout())
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.header("Accept", "application/json")
|
||||
.GET()
|
||||
.build();
|
||||
try {
|
||||
HttpResponse<String> response = httpClient.send(
|
||||
request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||
throw new ServiceException(PRODUCING_ORDER_PULL_FAILED,
|
||||
"HTTP " + response.statusCode());
|
||||
}
|
||||
ProducingOrderResponse result = objectMapper.readValue(
|
||||
response.body(), ProducingOrderResponse.class);
|
||||
if (result == null || result.getCode() == null || result.getCode() != 0) {
|
||||
String message = result == null ? "empty response" : result.getMessage();
|
||||
throw new ServiceException(PRODUCING_ORDER_RESPONSE_INVALID, message);
|
||||
}
|
||||
return result;
|
||||
} catch (JsonProcessingException ex) {
|
||||
throw new ServiceException(PRODUCING_ORDER_RESPONSE_INVALID,
|
||||
"invalid JSON response");
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ServiceException(PRODUCING_ORDER_PULL_FAILED, "request interrupted");
|
||||
} catch (IOException ex) {
|
||||
lastNetworkError = ex;
|
||||
if (attempt < maxAttempts) {
|
||||
log.warn("Pull producing order {} failed on attempt {}, retrying",
|
||||
orderId, attempt, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
String message = lastNetworkError == null ? "network error" : lastNetworkError.getMessage();
|
||||
throw new ServiceException(PRODUCING_ORDER_PULL_FAILED, message);
|
||||
}
|
||||
}
|
||||
+1346
File diff suppressed because it is too large
Load Diff
+442
@@ -0,0 +1,442 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单板件中间模型。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "组件生产单板件中间模型")
|
||||
@Data
|
||||
public class ProducingImportBlock {
|
||||
/**
|
||||
* 对方组件生产系统的板件 ID。
|
||||
*/
|
||||
private String sourceBlockItemId;
|
||||
private Integer orginId;
|
||||
private Integer objectId;
|
||||
private String name;
|
||||
private Integer type;
|
||||
private Integer custBlockNo;
|
||||
@JsonProperty(value = "ID")
|
||||
private Integer ID;
|
||||
|
||||
@Max(value = 99999, message = "板件宽度不能超过99999")
|
||||
@Min(value = 0, message = "板件宽度最小值为0")
|
||||
private Double width;
|
||||
|
||||
@Max(value = 99999, message = "板件高度不能超过99999")
|
||||
@Min(value = 0, message = "板件高度最小值为0")
|
||||
private Double height;
|
||||
private String material;
|
||||
private String materialName;
|
||||
|
||||
@Max(value = 99999, message = "板件厚度不能超过99999")
|
||||
@Min(value = 0, message = "板件厚度最小值为0")
|
||||
private Double thickness;
|
||||
private Integer boardType;
|
||||
private Double area;
|
||||
private Integer texture;
|
||||
private Integer holeFace;
|
||||
private Integer holeArrange;
|
||||
@JsonProperty(value = "unregular_point_count")
|
||||
private Integer unregularPointCount;
|
||||
private List<FrontBackHolesDTO> frontBackHoles;
|
||||
private List<SideHolesDTO> sideHoles;
|
||||
private Integer frontHoleCount;
|
||||
private Integer backHoleCount;
|
||||
private Integer sideHoleCount;
|
||||
private Integer frontModelCount;
|
||||
private Integer backModelCount;
|
||||
private Boolean isDoor;
|
||||
private Integer openDoorType;
|
||||
private Double offsetX;
|
||||
private Double offsetY;
|
||||
private Double sealOffsetX;
|
||||
private Double sealOffsetY;
|
||||
private Boolean isArcBase;
|
||||
private boolean isSpecialShape;
|
||||
private String remark;
|
||||
private Double kaiLiaoHeight;
|
||||
private Double kaiLiaoWidth;
|
||||
|
||||
@Valid
|
||||
// private InfoDTO info;
|
||||
private String cabinetName;
|
||||
private String roomName;
|
||||
private Double cabinetDepth;
|
||||
private Double cabinetWidth;
|
||||
private Double cabinetHeight;
|
||||
private PointInfoDTO pointInfo;
|
||||
|
||||
private Double sealLeft;
|
||||
private Double sealRight;
|
||||
private Double sealUp;
|
||||
private Double sealDown;
|
||||
private Double reservedEdgeUp;
|
||||
private Double reservedEdgeDown;
|
||||
private Double reservedEdgeLeft;
|
||||
private Double reservedEdgeRight;
|
||||
private Boolean isRect;
|
||||
private String spliteHeight;
|
||||
private String spliteThickness;
|
||||
private String spliteWidth;
|
||||
private boolean has2DModel;
|
||||
private boolean has3DModel;
|
||||
private String boardTypeName;
|
||||
private boolean isModel;
|
||||
private boolean isSideModel;
|
||||
private boolean hasSide2DModel;
|
||||
|
||||
private List<List<String>> allRemarkList;
|
||||
|
||||
// private Long plateGoodsId;
|
||||
private String goodsCode;
|
||||
|
||||
private List<Integer> groupIds = new ArrayList<>();
|
||||
|
||||
// 组件id
|
||||
private Integer componentId;
|
||||
|
||||
/**
|
||||
* 设置是否为异形板。
|
||||
*
|
||||
* @param specialShape 是否为异形板
|
||||
*/
|
||||
public void setIsSpecialShape(boolean specialShape) {
|
||||
isSpecialShape = specialShape;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否包含侧面造型。
|
||||
*
|
||||
* @param sideModel 是否包含侧面造型
|
||||
*/
|
||||
public void setIsSideModel(boolean sideModel) {
|
||||
isSideModel = sideModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否包含正反面造型。
|
||||
*
|
||||
* @param model 是否包含正反面造型
|
||||
*/
|
||||
public void setIsModel(boolean model) {
|
||||
isModel = model;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class FrontBackHolesDTO {
|
||||
private Integer type;
|
||||
private PositionDTO position;
|
||||
private Double radius;
|
||||
private Double depth;
|
||||
private Integer face;
|
||||
private Double angle;
|
||||
private String name;
|
||||
private String goodsId;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PositionDTO {
|
||||
private Double x;
|
||||
private Double y;
|
||||
private Double z;
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class SideHolesDTO {
|
||||
private Integer type;
|
||||
private PositionDTO position;
|
||||
private EndPtDTO endPt;
|
||||
private Integer radius;
|
||||
private Double depth;
|
||||
private Integer face;
|
||||
private String name;
|
||||
private String goodsId;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PositionDTO {
|
||||
private Double x;
|
||||
private Double y;
|
||||
private Double z;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class EndPtDTO {
|
||||
private Double x;
|
||||
private Double y;
|
||||
private Integer z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PointInfoDTO {
|
||||
@JsonProperty("PointDetail")
|
||||
private List<PointDetailDTO> pointDetail;
|
||||
@JsonProperty("OrgPointDetail")
|
||||
private List<OrgPointDetailDTO> orgPointDetail;
|
||||
@JsonProperty("HoleDetail")
|
||||
private List<HoleDetailDTO> holeDetail;
|
||||
@JsonProperty("SideHoleDetail")
|
||||
private List<SideHoleDetailDTO> sideHoleDetail;
|
||||
@JsonProperty("ModelDetail")
|
||||
private List<ModelDetailDTO> modelDetail;
|
||||
@JsonProperty("SideModelDetail")
|
||||
private List<SideModelDetailDTO> sideModelDetail;
|
||||
@JsonProperty("Side2DModelDetail")
|
||||
private List<SideModelDetailDTO> side2DModelDetail;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PointDetailDTO {
|
||||
@JsonProperty("PointID")
|
||||
private Integer pointID;
|
||||
@JsonProperty("PointX")
|
||||
private Double pointX;
|
||||
@JsonProperty("PointY")
|
||||
private Double pointY;
|
||||
@JsonProperty("Curve")
|
||||
private Double curve;
|
||||
@JsonProperty("SealSize")
|
||||
private Double sealSize;
|
||||
@JsonProperty("SealColor")
|
||||
private String sealColor;
|
||||
@JsonProperty("ReservedEdge")
|
||||
private Double reservedEdge;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class OrgPointDetailDTO {
|
||||
@JsonProperty("PointID")
|
||||
private Integer pointID;
|
||||
@JsonProperty("PointX")
|
||||
private Double pointX;
|
||||
@JsonProperty("PointY")
|
||||
private Double pointY;
|
||||
@JsonProperty("Curve")
|
||||
private Double curve;
|
||||
@JsonProperty("SealSize")
|
||||
private Double sealSize;
|
||||
@JsonProperty("SealColor")
|
||||
private String sealColor;
|
||||
@JsonProperty("ReservedEdge")
|
||||
private Double reservedEdge;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class HoleDetailDTO {
|
||||
@JsonProperty("HoleID")
|
||||
private Integer holeID;
|
||||
@JsonProperty("HoleType")
|
||||
private Integer holeType;
|
||||
@JsonProperty("Face")
|
||||
private Integer face;
|
||||
@JsonProperty("PointX")
|
||||
private Double pointX;
|
||||
@JsonProperty("PointY")
|
||||
private Double pointY;
|
||||
@JsonProperty("PointZ")
|
||||
private Integer pointZ;
|
||||
@JsonProperty("Radius")
|
||||
private Double radius;
|
||||
@JsonProperty("Depth")
|
||||
private Double depth;
|
||||
@JsonProperty("EndPoint")
|
||||
private String endPoint;
|
||||
@JsonProperty("Angle")
|
||||
private Integer angle;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class SideHoleDetailDTO {
|
||||
@JsonProperty("HoleID")
|
||||
private Integer holeID;
|
||||
@JsonProperty("HoleType")
|
||||
private Integer holeType;
|
||||
@JsonProperty("Face")
|
||||
private Integer face;
|
||||
@JsonProperty("PointX")
|
||||
private Double pointX;
|
||||
@JsonProperty("PointY")
|
||||
private Double pointY;
|
||||
@JsonProperty("PointZ")
|
||||
private Double pointZ;
|
||||
@JsonProperty("Radius")
|
||||
private Double radius;
|
||||
@JsonProperty("Depth")
|
||||
private Double depth;
|
||||
@JsonProperty("EndPoint")
|
||||
private String endPoint;
|
||||
@JsonProperty("PointX2")
|
||||
private Double pointX2;
|
||||
@JsonProperty("PointY2")
|
||||
private Double pointY2;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class ModelDetailDTO {
|
||||
@JsonProperty("ModelID")
|
||||
private Integer modelID;
|
||||
@JsonProperty("LineID")
|
||||
private Integer lineID;
|
||||
@JsonProperty("Face")
|
||||
private Integer face;
|
||||
@JsonProperty("KnifeName")
|
||||
private String knifeName;
|
||||
@JsonProperty("KnifeRadius")
|
||||
private Double knifeRadius;
|
||||
@JsonProperty("Depth")
|
||||
private Double depth;
|
||||
@JsonProperty("PointList")
|
||||
private List<PointListDTO> pointList;
|
||||
@JsonProperty("OriginModeling")
|
||||
private OriginModelingDTO originModeling;
|
||||
@JsonProperty("ModelType")
|
||||
private Integer modelType;
|
||||
@JsonProperty("OffSetList")
|
||||
private List<OffSetListDTO> offSetList;
|
||||
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class OriginModelingDTO {
|
||||
@JsonProperty("outline")
|
||||
private OutlineDTO outline;
|
||||
@JsonProperty("addLen")
|
||||
private Double addLen;
|
||||
@JsonProperty("addWidth")
|
||||
private Double addWidth;
|
||||
@JsonProperty("addDepth")
|
||||
private Double addDepth;
|
||||
@JsonProperty("thickness")
|
||||
private Double thickness;
|
||||
@JsonProperty("dir")
|
||||
private Integer dir;
|
||||
@JsonProperty("knifeRadius")
|
||||
private Double knifeRadius;
|
||||
@JsonProperty("holes")
|
||||
private List<HolesDTO> holes;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class OutlineDTO {
|
||||
@JsonProperty("pts")
|
||||
private List<PtsDTO> pts;
|
||||
@JsonProperty("buls")
|
||||
private List<Double> buls;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PtsDTO {
|
||||
@JsonProperty("x")
|
||||
private Double x;
|
||||
@JsonProperty("y")
|
||||
private Double y;
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class HolesDTO {
|
||||
@JsonProperty("pts")
|
||||
private List<PtsDTO> pts;
|
||||
@JsonProperty("buls")
|
||||
private List<Double> buls;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PtsDTO {
|
||||
@JsonProperty("x")
|
||||
private Double x;
|
||||
@JsonProperty("y")
|
||||
private Double y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class PointListDTO {
|
||||
@JsonProperty("LineID")
|
||||
private Integer lineID;
|
||||
@JsonProperty("PointID")
|
||||
private Integer pointID;
|
||||
@JsonProperty("PointX")
|
||||
private Double pointX;
|
||||
@JsonProperty("PointY")
|
||||
private Double pointY;
|
||||
@JsonProperty("Depth")
|
||||
private Double depth;
|
||||
@JsonProperty("Curve")
|
||||
private Double curve;
|
||||
@JsonProperty("Radius")
|
||||
private Double radius;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class OffSetListDTO {
|
||||
@JsonProperty("ID")
|
||||
private String id;
|
||||
@JsonProperty("Value")
|
||||
private Double value;
|
||||
@JsonProperty("Deep")
|
||||
private Double deep;
|
||||
@JsonProperty("Angle")
|
||||
private Double angle;
|
||||
@JsonProperty("Name")
|
||||
private String name;
|
||||
@JsonProperty("Radius")
|
||||
private Double radius;
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class SideModelDetailDTO {
|
||||
@JsonProperty("ModelID")
|
||||
private Integer modelID;
|
||||
@JsonProperty("LineID")
|
||||
private Integer lineID;
|
||||
@JsonProperty("Face")
|
||||
private Integer face;
|
||||
@JsonProperty("KnifeName")
|
||||
private String knifeName;
|
||||
@JsonProperty("ChaiDanName")
|
||||
private String chaiDanName;
|
||||
@JsonProperty("KnifeRadius")
|
||||
private Double knifeRadius;
|
||||
@JsonProperty("Depth")
|
||||
private Double depth;
|
||||
@JsonProperty("ModelType")
|
||||
private Integer modelType;
|
||||
@JsonProperty("OriginModeling")
|
||||
private ModelDetailDTO.OriginModelingDTO originModeling;
|
||||
@JsonProperty("PointList")
|
||||
private List<ModelDetailDTO.PointListDTO> pointList;
|
||||
@JsonProperty("OffSetList")
|
||||
private List<ModelDetailDTO.OffSetListDTO> offSetList;
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单板件备注中间模型。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ProducingImportBlockRemark {
|
||||
|
||||
private List<String> remarks;
|
||||
|
||||
private JSONObject extraObject;
|
||||
|
||||
private JSONArray drillsObj;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单部件组中间模型。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ProducingImportComponentGroup {
|
||||
|
||||
@JsonProperty("groupID")
|
||||
private Integer groupId;
|
||||
|
||||
private String name;
|
||||
|
||||
private BoxSize boxSize = new BoxSize();
|
||||
|
||||
private List<ProducingImportComponentGroup> children = new ArrayList<>();
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class BoxSize {
|
||||
|
||||
private Double width;
|
||||
|
||||
private Double height;
|
||||
|
||||
private Double depth;
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单导入中间模型。
|
||||
*/
|
||||
@Schema(description = "组件生产单导入中间模型")
|
||||
@Data
|
||||
public class ProducingImportData {
|
||||
|
||||
@JsonProperty(value = "OrderNo")
|
||||
private String orderNo;
|
||||
|
||||
@JsonProperty(value = "CadViewFileId")
|
||||
private Long cadViewFileId;
|
||||
|
||||
/**
|
||||
* 是否使用 type=4 的实际加工组名称。
|
||||
*/
|
||||
private boolean useConcreteProcessGroupName;
|
||||
|
||||
@JsonProperty(value = "Materials")
|
||||
private List<ProducingImportMaterial> Materials;
|
||||
|
||||
@JsonProperty(value = "Parts")
|
||||
private List<ProducingImportRoomTree> Parts;
|
||||
|
||||
@JsonProperty(value = "ProcessGroup")
|
||||
@Valid
|
||||
private List<ProducingImportProcessGroup> ProcessGroup;
|
||||
|
||||
@JsonProperty(value = "ComponentGroupTree")
|
||||
@Valid
|
||||
private List<ProducingImportComponentGroup> ComponentGroup;
|
||||
|
||||
@Valid
|
||||
private List<ProducingImportRoomTree> douleRoomTree;
|
||||
|
||||
@JsonProperty(value = "Blocks")
|
||||
private List<ProducingImportRoomTree> blocks;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 组件生产单加工组解析结果。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ProducingImportGroupInfo {
|
||||
|
||||
private String groupTypeName;
|
||||
|
||||
private Long groupTypeId;
|
||||
|
||||
private String groupName;
|
||||
|
||||
private Double width;
|
||||
|
||||
private Double height;
|
||||
|
||||
private Double depth;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单材质中间模型。
|
||||
*/
|
||||
@Data
|
||||
public class ProducingImportMaterial {
|
||||
|
||||
private Double height;
|
||||
|
||||
private Double width;
|
||||
|
||||
private Double thickness;
|
||||
|
||||
private String material;
|
||||
|
||||
private String color;
|
||||
|
||||
private String brand;
|
||||
|
||||
private String goodsId;
|
||||
|
||||
private List<Integer> orginIds;
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean texture;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 获取板材纹理标识。
|
||||
*
|
||||
* @return {@code true} 表示有纹理
|
||||
*/
|
||||
public boolean getTexture() {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单配件中间模型。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ProducingImportPart {
|
||||
|
||||
private String roomName;
|
||||
|
||||
private String cabinetName;
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
private String model;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String width;
|
||||
|
||||
private String height;
|
||||
|
||||
private String thickness;
|
||||
|
||||
private String material;
|
||||
|
||||
private String brand;
|
||||
|
||||
private String factory;
|
||||
|
||||
private String unit;
|
||||
|
||||
private Double num;
|
||||
|
||||
private Boolean isComposite;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Integer objectId;
|
||||
|
||||
private Long imesPartsId;
|
||||
|
||||
private List<Integer> groupIds = new ArrayList<>();
|
||||
|
||||
private GroupData groupData;
|
||||
|
||||
private List<Integer> componentIds = new ArrayList<>();
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class GroupData {
|
||||
|
||||
@JsonProperty("size")
|
||||
private Integer size;
|
||||
|
||||
@JsonProperty("material")
|
||||
private String material;
|
||||
|
||||
@JsonProperty("boardName")
|
||||
private String boardName;
|
||||
|
||||
@JsonProperty("thickness")
|
||||
private Integer thickness;
|
||||
|
||||
@JsonProperty("color")
|
||||
private String color;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单加工组中间模型。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ProducingImportProcessGroup {
|
||||
|
||||
private String type;
|
||||
|
||||
private List<Project> projects;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class Project {
|
||||
|
||||
private String projName;
|
||||
|
||||
private List<Integer> brIds;
|
||||
|
||||
private List<Integer> objIds;
|
||||
|
||||
private BoxSize boxSize;
|
||||
|
||||
private Integer groupID;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class BoxSize {
|
||||
|
||||
private Double width;
|
||||
|
||||
private Double height;
|
||||
|
||||
private Double depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件生产单房间、柜体及其内容的中间模型。
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ProducingImportRoomTree {
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private Integer count;
|
||||
|
||||
@Valid
|
||||
private List<ChildrenDTO> children;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class ChildrenDTO {
|
||||
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 外部模块的稳定标识,避免仅凭房间名和柜体名定位。
|
||||
*/
|
||||
private String identityKey;
|
||||
|
||||
private Double width;
|
||||
|
||||
private Double height;
|
||||
|
||||
private Double depth;
|
||||
|
||||
@NotNull(message = "拆单倍数不能为空")
|
||||
@Max(value = 5000, message = "拆单倍数不能超过5000")
|
||||
@Min(value = 1, message = "拆单倍数最小值为1")
|
||||
private Integer count;
|
||||
|
||||
private List<ProducingImportBlock> blockList;
|
||||
|
||||
private List<ProducingImportPart> objList;
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 组件生产系统订单响应。
|
||||
*
|
||||
* <p>对方实体 ID 全部保留为字符串,只有写入原拆单结构时才按目标字段要求转换,
|
||||
* 避免反序列化阶段发生精度丢失。</p>
|
||||
*/
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ProducingOrderResponse {
|
||||
|
||||
private Integer code;
|
||||
private String message;
|
||||
private Payload data;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Payload {
|
||||
private Map<String, String> cadObjectIdMap = Collections.emptyMap();
|
||||
private ProductionOrder data;
|
||||
private String cadViewPath;
|
||||
private String cadFileId;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ProductionOrder {
|
||||
private String id;
|
||||
private List<ProductOrder> orderList = Collections.emptyList();
|
||||
private List<Material> materials = Collections.emptyList();
|
||||
private List<Module> modules = Collections.emptyList();
|
||||
private List<Block> blockList = Collections.emptyList();
|
||||
private List<Item> blockItems = Collections.emptyList();
|
||||
private List<Part> partsList = Collections.emptyList();
|
||||
private List<Item> partsItems = Collections.emptyList();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ProductOrder {
|
||||
private String id;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Material {
|
||||
private String id;
|
||||
private Integer type;
|
||||
private String name;
|
||||
private String factory;
|
||||
private String brand;
|
||||
private String spec;
|
||||
private String material;
|
||||
private String color;
|
||||
private Double width;
|
||||
private Double length;
|
||||
private Double thickness;
|
||||
private Boolean hasWave;
|
||||
private String model;
|
||||
private Double size;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Module {
|
||||
private String id;
|
||||
private String orderId;
|
||||
private String name;
|
||||
private Integer type;
|
||||
private String parentId;
|
||||
private ModuleExtra extra;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ModuleExtra {
|
||||
private Size3D size;
|
||||
private Boolean isComposite;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Size3D {
|
||||
private Double width;
|
||||
private Double height;
|
||||
private Double depth;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class BoardSize {
|
||||
private Double width;
|
||||
private Double length;
|
||||
private Double thickness;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Block {
|
||||
private String id;
|
||||
private String orderId;
|
||||
private String name;
|
||||
private String materialId;
|
||||
private String type;
|
||||
private BoardSize size;
|
||||
private BoardSize realSize;
|
||||
private List<String> sealIds = Collections.emptyList();
|
||||
private List<String> edgeRemarks = Collections.emptyList();
|
||||
private List<Double> reservedEdges = Collections.emptyList();
|
||||
private Point sealOffset;
|
||||
private Integer waveType;
|
||||
private Integer doorType;
|
||||
private Integer composingType;
|
||||
private BorderLengths borderLengths;
|
||||
private Boolean isArcBlock;
|
||||
private Path points;
|
||||
private Path orgPoints;
|
||||
private List<Drill> drills = Collections.emptyList();
|
||||
private List<SideDrill> sideDrills = Collections.emptyList();
|
||||
private List<Shape> shapes = Collections.emptyList();
|
||||
private List<Shape> sideShapes = Collections.emptyList();
|
||||
/**
|
||||
* 新报文可能直接返回刀路标记;旧报文则放在 remarks 中,转换层同时兼容两种结构。
|
||||
*/
|
||||
private Boolean has2DModel;
|
||||
private Boolean has3DModel;
|
||||
/**
|
||||
* 板件定义级扩展信息,仅用于读取兼容字段,不作为导入后的板件备注。
|
||||
*/
|
||||
private Map<String, Object> remarks = Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Item {
|
||||
private String orderId;
|
||||
private String id;
|
||||
private String relationId;
|
||||
private String boxId;
|
||||
private List<String> processGroupIds = Collections.emptyList();
|
||||
private String componentGroupId;
|
||||
/**
|
||||
* 物理板件/配件实例的业务备注,导入备注只取本字段。
|
||||
*/
|
||||
private Map<String, Object> remarks = Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Part {
|
||||
private String id;
|
||||
private String orderId;
|
||||
private String name;
|
||||
private String materialId;
|
||||
private Integer type;
|
||||
private String group;
|
||||
private Double quantity;
|
||||
/**
|
||||
* 对方配件定义的长、宽、高;转换层适配到原拆单结构的宽、长、厚字段。
|
||||
*/
|
||||
private Double length;
|
||||
private Double width;
|
||||
private Double height;
|
||||
private Map<String, Object> remarks = Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class BorderLengths {
|
||||
private Double heavy;
|
||||
private Double light;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Path {
|
||||
private List<Point> pts = Collections.emptyList();
|
||||
private List<Double> buls = Collections.emptyList();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Point {
|
||||
private Double x;
|
||||
private Double y;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Drill {
|
||||
private Integer id;
|
||||
private Double depth;
|
||||
private Point offset;
|
||||
private Integer position;
|
||||
private Double radius;
|
||||
private Double rotation;
|
||||
private Integer holeType;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class SideDrill {
|
||||
private Integer id;
|
||||
private Double depth;
|
||||
private Point startOffset;
|
||||
private Point endOffset;
|
||||
private Double pointZ;
|
||||
private Object holeType;
|
||||
private Integer position;
|
||||
private Double radius;
|
||||
private Integer face;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Shape {
|
||||
private Integer id;
|
||||
private String type;
|
||||
private Integer toolPathId;
|
||||
private List<ShapePath> profiles = Collections.emptyList();
|
||||
private Integer position;
|
||||
private Double depth;
|
||||
private Double radius;
|
||||
private String knifeName;
|
||||
private Integer face;
|
||||
private String processName;
|
||||
private Path contour;
|
||||
private List<Path> innerProfile = Collections.emptyList();
|
||||
private Double addWidth;
|
||||
private Double addLen;
|
||||
private Double addDepth;
|
||||
private List<ShapeOffset> offsetList = Collections.emptyList();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ShapePath extends Path {
|
||||
private Integer id;
|
||||
private List<Double> depths = Collections.emptyList();
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class ShapeOffset {
|
||||
private Double angle;
|
||||
private Double depth;
|
||||
@JsonAlias("KnifeName")
|
||||
private String knifeName;
|
||||
private Double radius;
|
||||
private Double value;
|
||||
private String toolPath;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.property;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 组件生产单拉取及导入配置。
|
||||
*
|
||||
* <p>统一管理对方订单接口地址、HTTP 超时和重试策略,以及单订单板件数量上限。</p>
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "chenfeng.plan.producing-order")
|
||||
public class ProducingOrderImportProperties {
|
||||
|
||||
/**
|
||||
* 组件生产单查询地址,其中 {@code {id}} 会替换为来源订单 ID。
|
||||
*/
|
||||
private String orderUrl =
|
||||
"http://proc.mes.cf/api-branch=opt_split_order/api/v1/producing/order/{id}";
|
||||
|
||||
/**
|
||||
* 建立 HTTP 连接的超时时间。
|
||||
*/
|
||||
private Duration connectTimeout = Duration.ofSeconds(5);
|
||||
|
||||
/**
|
||||
* 单次 HTTP 请求的读取超时时间。
|
||||
*/
|
||||
private Duration readTimeout = Duration.ofSeconds(60);
|
||||
|
||||
/**
|
||||
* 网络 I/O 失败后的重试次数;HTTP 或业务错误不重试。
|
||||
*/
|
||||
private int networkRetryTimes = 1;
|
||||
|
||||
/**
|
||||
* 单个订单允许累计导入的板件数量上限。
|
||||
*/
|
||||
private int plateNumThreshold = 20000;
|
||||
}
|
||||
@@ -164,6 +164,12 @@ chenfeng:
|
||||
enable: false
|
||||
publicKey: cfimes
|
||||
plan:
|
||||
producing-order:
|
||||
order-url: http://proc.mes.cf/api-branch=opt_split_order/api/v1/producing/order/{id}
|
||||
connect-timeout: 5s
|
||||
read-timeout: 60s
|
||||
network-retry-times: 1
|
||||
plate-num-threshold: 20000
|
||||
webcad:
|
||||
import:
|
||||
node: 1
|
||||
@@ -173,4 +179,4 @@ chenfeng:
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志
|
||||
|
||||
+7
-1
@@ -192,6 +192,12 @@ chenfeng:
|
||||
enable: false
|
||||
publicKey: cfimes
|
||||
plan:
|
||||
producing-order:
|
||||
order-url: http://proc.mes.cf/api-branch=opt_split_order/api/v1/producing/order/{id}
|
||||
connect-timeout: 5s
|
||||
read-timeout: 60s
|
||||
network-retry-times: 1
|
||||
plate-num-threshold: 20000
|
||||
webcad:
|
||||
import:
|
||||
node: 1
|
||||
@@ -201,4 +207,4 @@ chenfeng:
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
producing.order.import.sourceOrderId.notBlank=組件系統訂單 ID 不能為空
|
||||
producing.order.import.targetOrderId.notNull=目標訂單 ID 不能為空
|
||||
producing.order.import.materialMappings.notEmpty=板材映射不能為空
|
||||
producing.order.import.sourceMaterialId.notBlank=組件系統板材 ID 不能為空
|
||||
producing.order.import.plateId.notNull=iMES 板材 ID 不能為空
|
||||
@@ -0,0 +1,5 @@
|
||||
producing.order.import.sourceOrderId.notBlank=Component system order ID cannot be empty
|
||||
producing.order.import.targetOrderId.notNull=Target order ID cannot be empty
|
||||
producing.order.import.materialMappings.notEmpty=Material mappings cannot be empty
|
||||
producing.order.import.sourceMaterialId.notBlank=Component system material ID cannot be empty
|
||||
producing.order.import.plateId.notNull=iMES material ID cannot be empty
|
||||
+10
-1
@@ -30,4 +30,13 @@ order.import.plate.goods.no_match=匯入失敗,板材庫中無板材匹配,
|
||||
webcad.order.import.peek.not_exist=匯入失敗:資料來源不存在,請檢查訊息可靠性
|
||||
webcad.order.import.order.update.concurrent=匯入失敗:訂單號{0}板件數量已發生變更,請重新匯入
|
||||
webcad.order.import.goods.update.concurrent=匯入失敗:材質{0}板件數量已發生變更,請重新匯入
|
||||
webcad.order.import.cad.view.file.id.empty.error=匯入失敗:請檢查CAD圖紙數據是否上傳成功
|
||||
webcad.order.import.cad.view.file.id.empty.error=匯入失敗:請檢查CAD圖紙數據是否上傳成功
|
||||
producing.order.pull.failed=拉取組件生產單失敗:{0}
|
||||
producing.order.response.invalid=組件生產單報文無效:{0}
|
||||
producing.order.block_items.empty=組件生產單沒有板件,禁止匯入
|
||||
producing.order.reference.invalid=組件生產單關聯資料無效:{0}
|
||||
producing.order.material_mapping.invalid=板材映射無效:{0}
|
||||
producing.order.cad_view_path.empty=組件生產單缺少 CAD 圖紙地址
|
||||
producing.order.cad_object.invalid=組件生產單 CAD 物件映射無效:{0}
|
||||
producing.order.enum.invalid=組件生產單枚舉值無效:{0}
|
||||
producing.order.import.failed=組件生產單匯入失敗
|
||||
|
||||
+10
-1
@@ -30,4 +30,13 @@ order.import.plate.goods.no_match=Import failed: no matching plate found in plat
|
||||
webcad.order.import.peek.not_exist=Import failed: data source does not exist, please check message reliability
|
||||
webcad.order.import.order.update.concurrent=Import failed: plate quantity under production order {0} has changed, please re-import
|
||||
webcad.order.import.goods.update.concurrent=Import failed: plate quantity for material {0} has changed, please re-import
|
||||
webcad.order.import.cad.view.file.id.empty.error=Import failed: Please check if the CAD drawing data was uploaded successfully
|
||||
webcad.order.import.cad.view.file.id.empty.error=Import failed: Please check if the CAD drawing data was uploaded successfully
|
||||
producing.order.pull.failed=Failed to pull producing order: {0}
|
||||
producing.order.response.invalid=Invalid producing order response: {0}
|
||||
producing.order.block_items.empty=The producing order contains no board items
|
||||
producing.order.reference.invalid=Invalid producing order reference: {0}
|
||||
producing.order.material_mapping.invalid=Invalid material mapping: {0}
|
||||
producing.order.cad_view_path.empty=The producing order has no CAD view path
|
||||
producing.order.cad_object.invalid=Invalid CAD object mapping: {0}
|
||||
producing.order.enum.invalid=Invalid producing order enum value: {0}
|
||||
producing.order.import.failed=Failed to import producing order
|
||||
|
||||
+10
-1
@@ -30,4 +30,13 @@ order.import.plate.goods.no_match=导入失败,板材库中无板材匹配,
|
||||
webcad.order.import.peek.not_exist=导入失败:数据源不存在,请检查消息可靠性
|
||||
webcad.order.import.order.update.concurrent=导入失败:订单号{0}板件数量已发生改变,请重新导入
|
||||
webcad.order.import.goods.update.concurrent=导入失败:材质{0}板件数量已发生改变,请重新导入
|
||||
webcad.order.import.cad.view.file.id.empty.error=导入失败:请检查CAD图纸数据是否上传成功
|
||||
webcad.order.import.cad.view.file.id.empty.error=导入失败:请检查CAD图纸数据是否上传成功
|
||||
producing.order.pull.failed=拉取组件生产单失败:{0}
|
||||
producing.order.response.invalid=组件生产单报文无效:{0}
|
||||
producing.order.block_items.empty=组件生产单没有板件,禁止导入
|
||||
producing.order.reference.invalid=组件生产单关联数据无效:{0}
|
||||
producing.order.material_mapping.invalid=板材映射无效:{0}
|
||||
producing.order.cad_view_path.empty=组件生产单缺少 CAD 图纸地址
|
||||
producing.order.cad_object.invalid=组件生产单 CAD 对象映射无效:{0}
|
||||
producing.order.enum.invalid=组件生产单枚举值无效:{0}
|
||||
producing.order.import.failed=组件生产单导入失败
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
producing.order.import.sourceOrderId.notBlank=组件系统订单 ID 不能为空
|
||||
producing.order.import.targetOrderId.notNull=目标订单 ID 不能为空
|
||||
producing.order.import.materialMappings.notEmpty=板材映射不能为空
|
||||
producing.order.import.sourceMaterialId.notBlank=组件系统板材 ID 不能为空
|
||||
producing.order.import.plateId.notNull=iMES 板材 ID 不能为空
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
package com.cf.imes.module.plan.service.orderImport.producing;
|
||||
|
||||
import com.cf.imes.framework.common.exception.ServiceException;
|
||||
import com.cf.imes.module.plan.controller.admin.orderimport.producing.vo.ProducingOrderImportReqVO;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingImportBlock;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingImportData;
|
||||
import com.cf.imes.module.plan.dal.dataobject.plate.PlateGoodDO;
|
||||
import com.cf.imes.module.plan.dal.mysql.plate.PlateGoodMapper;
|
||||
import com.cf.imes.module.plan.service.orderImport.producing.dto.ProducingOrderResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
class ProducingOrderConverterTest {
|
||||
|
||||
private ProducingOrderConverter converter;
|
||||
private PlateGoodMapper plateGoodMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
converter = new ProducingOrderConverter();
|
||||
plateGoodMapper = Mockito.mock(PlateGoodMapper.class);
|
||||
ReflectionTestUtils.setField(converter, "plateGoodMapper", plateGoodMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConvertPhysicalBoardAndKeepExternalIdentity() {
|
||||
ProducingOrderResponse response = validResponse();
|
||||
ProducingOrderImportReqVO request = validRequest();
|
||||
PlateGoodDO localPlate = localPlate();
|
||||
Mockito.when(plateGoodMapper.selectList(any())).thenReturn(List.of(localPlate));
|
||||
|
||||
converter.validate(response, request);
|
||||
ProducingImportData converted = converter.convert(response, request, 1L, 123L);
|
||||
|
||||
assertThat(converted.getCadViewFileId()).isEqualTo(123L);
|
||||
assertThat(converted.isUseConcreteProcessGroupName()).isTrue();
|
||||
assertThat(converted.getMaterials()).singleElement()
|
||||
.extracting("goodsId").isEqualTo("LOCAL-GOODS");
|
||||
ProducingImportBlock block = converted.getBlocks().get(0)
|
||||
.getChildren().get(0).getBlockList().get(0);
|
||||
assertThat(block.getSourceBlockItemId()).isEqualTo("physical-1");
|
||||
assertThat(block.getID()).isEqualTo(9001);
|
||||
assertThat(block.getTexture()).isEqualTo(2);
|
||||
assertThat(block.getSealDown()).isEqualTo(1D);
|
||||
assertThat(block.getSealRight()).isZero();
|
||||
assertThat(block.getSealUp()).isEqualTo(1D);
|
||||
assertThat(block.getSealLeft()).isZero();
|
||||
assertThat(block.getComponentId()).isNull();
|
||||
assertThat(block.getAllRemarkList())
|
||||
.containsExactly(List.of("Remark-0", "item remark"));
|
||||
assertThat(converted.getBlocks().get(0).getChildren().get(0).getIdentityKey())
|
||||
.isEqualTo("producing:body-1");
|
||||
assertThat(converted.getBlocks().get(0).getChildren().get(0).getWidth())
|
||||
.isEqualTo(600D);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectOrderWithoutPhysicalBoards() {
|
||||
ProducingOrderResponse response = validResponse();
|
||||
response.getData().getData().setBlockItems(List.of());
|
||||
|
||||
assertThatThrownBy(() -> converter.validate(response, validRequest()))
|
||||
.isInstanceOf(ServiceException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConvertReferencedPartWithDefaultValues() {
|
||||
ProducingOrderResponse response = validResponse();
|
||||
ProducingOrderResponse.Material material = new ProducingOrderResponse.Material();
|
||||
material.setId("part-material-1");
|
||||
material.setType(2);
|
||||
material.setSpec("规格|材质|颜色");
|
||||
|
||||
ProducingOrderResponse.Part part = new ProducingOrderResponse.Part();
|
||||
part.setId("part-1");
|
||||
part.setOrderId("order-1");
|
||||
part.setName("铰链");
|
||||
part.setMaterialId(material.getId());
|
||||
part.setType(0);
|
||||
part.setLength(100D);
|
||||
part.setWidth(20D);
|
||||
part.setHeight(5D);
|
||||
part.setRemarks(Map.of("definition", "definition remark"));
|
||||
part.setGroup("五金");
|
||||
|
||||
ProducingOrderResponse.Item item = new ProducingOrderResponse.Item();
|
||||
item.setId("part-item-1");
|
||||
item.setOrderId("order-1");
|
||||
item.setRelationId(part.getId());
|
||||
item.setBoxId("body-1");
|
||||
item.setProcessGroupIds(List.of("401"));
|
||||
item.setComponentGroupId("0");
|
||||
item.setRemarks(Map.of("Remark-0", "item remark"));
|
||||
|
||||
var materials = new java.util.ArrayList<>(
|
||||
response.getData().getData().getMaterials());
|
||||
materials.add(material);
|
||||
response.getData().getData().setMaterials(materials);
|
||||
response.getData().getData().setPartsList(List.of(part));
|
||||
response.getData().getData().setPartsItems(List.of(item));
|
||||
response.getData().setCadObjectIdMap(Map.of(
|
||||
"block-1", "9001",
|
||||
"part-1", "9002"));
|
||||
Mockito.when(plateGoodMapper.selectList(any()))
|
||||
.thenReturn(List.of(localPlate()));
|
||||
|
||||
converter.validate(response, validRequest());
|
||||
ProducingImportData converted = converter.convert(
|
||||
response, validRequest(), 1L, 123L);
|
||||
|
||||
var convertedPart = converted.getParts().get(0)
|
||||
.getChildren().get(0).getObjList().get(0);
|
||||
assertThat(convertedPart.getObjectId()).isEqualTo(9002);
|
||||
assertThat(convertedPart.getNum()).isEqualTo(1D);
|
||||
assertThat(convertedPart.getWidth()).isEqualTo("20.0");
|
||||
assertThat(convertedPart.getHeight()).isEqualTo("100.0");
|
||||
assertThat(convertedPart.getThickness()).isEqualTo("5.0");
|
||||
assertThat(convertedPart.getType()).isEqualTo("五金");
|
||||
assertThat(convertedPart.getIsComposite()).isFalse();
|
||||
assertThat(convertedPart.getRemark()).isEqualTo("item remark");
|
||||
}
|
||||
|
||||
private PlateGoodDO localPlate() {
|
||||
PlateGoodDO localPlate = PlateGoodDO.builder()
|
||||
.id(99L)
|
||||
.goodsId("LOCAL-GOODS")
|
||||
.goodsName("本地板材")
|
||||
.material("生态板")
|
||||
.color("檀木")
|
||||
.texture(true)
|
||||
.width(1220D)
|
||||
.height(2440D)
|
||||
.thickness(18D)
|
||||
.spec("1220*2440*18")
|
||||
.brand("CF")
|
||||
.organId(1L)
|
||||
.build();
|
||||
localPlate.setDeleted(false);
|
||||
return localPlate;
|
||||
}
|
||||
|
||||
private ProducingOrderImportReqVO validRequest() {
|
||||
ProducingOrderImportReqVO.MaterialMapping mapping =
|
||||
new ProducingOrderImportReqVO.MaterialMapping();
|
||||
mapping.setSourceMaterialId("material-1");
|
||||
mapping.setPlateId(99L);
|
||||
ProducingOrderImportReqVO request = new ProducingOrderImportReqVO();
|
||||
request.setSourceOrderId("order-1");
|
||||
request.setTargetOrderId(100L);
|
||||
request.setMaterialMappings(List.of(mapping));
|
||||
return request;
|
||||
}
|
||||
|
||||
private ProducingOrderResponse validResponse() {
|
||||
ProducingOrderResponse.Material board = new ProducingOrderResponse.Material();
|
||||
board.setId("material-1");
|
||||
board.setType(1);
|
||||
ProducingOrderResponse.Material seal = new ProducingOrderResponse.Material();
|
||||
seal.setId("seal-1");
|
||||
seal.setType(3);
|
||||
seal.setSize(1D);
|
||||
ProducingOrderResponse.Material noSeal = new ProducingOrderResponse.Material();
|
||||
noSeal.setId("seal-0");
|
||||
noSeal.setType(3);
|
||||
noSeal.setSize(0D);
|
||||
|
||||
ProducingOrderResponse.Module room = module("room-1", 1, "0", "房间");
|
||||
ProducingOrderResponse.Module body = module("body-1", 2, "room-1", "柜体");
|
||||
ProducingOrderResponse.ModuleExtra extra = new ProducingOrderResponse.ModuleExtra();
|
||||
ProducingOrderResponse.Size3D bodySize = new ProducingOrderResponse.Size3D();
|
||||
bodySize.setWidth(600D);
|
||||
bodySize.setHeight(2000D);
|
||||
bodySize.setDepth(500D);
|
||||
extra.setSize(bodySize);
|
||||
body.setExtra(extra);
|
||||
ProducingOrderResponse.Module groupType = module("301", 3, "0", "加工类型");
|
||||
ProducingOrderResponse.Module group = module("401", 4, "301", "加工组");
|
||||
|
||||
ProducingOrderResponse.Block block = new ProducingOrderResponse.Block();
|
||||
block.setId("block-1");
|
||||
block.setOrderId("order-1");
|
||||
block.setName("背板");
|
||||
block.setMaterialId("material-1");
|
||||
block.setType("2");
|
||||
ProducingOrderResponse.BoardSize size = new ProducingOrderResponse.BoardSize();
|
||||
size.setWidth(500D);
|
||||
size.setLength(1000D);
|
||||
size.setThickness(18D);
|
||||
block.setSize(size);
|
||||
block.setSealIds(List.of("seal-1", "seal-0", "seal-1", "seal-0"));
|
||||
block.setReservedEdges(List.of(0D, 0D, 0D, 0D));
|
||||
block.setWaveType(1);
|
||||
block.setDoorType(0);
|
||||
block.setComposingType(2);
|
||||
ProducingOrderResponse.Path emptyPath = new ProducingOrderResponse.Path();
|
||||
emptyPath.setPts(List.of());
|
||||
emptyPath.setBuls(List.of());
|
||||
block.setPoints(emptyPath);
|
||||
block.setOrgPoints(emptyPath);
|
||||
block.setRemarks(Map.of("definition", "definition remark"));
|
||||
|
||||
ProducingOrderResponse.Item item = new ProducingOrderResponse.Item();
|
||||
item.setId("physical-1");
|
||||
item.setOrderId("order-1");
|
||||
item.setRelationId("block-1");
|
||||
item.setBoxId("body-1");
|
||||
item.setProcessGroupIds(List.of("401"));
|
||||
item.setComponentGroupId("0");
|
||||
item.setRemarks(Map.of("Remark-0", "item remark"));
|
||||
|
||||
ProducingOrderResponse.ProductionOrder order =
|
||||
new ProducingOrderResponse.ProductionOrder();
|
||||
order.setMaterials(List.of(board, seal, noSeal));
|
||||
order.setModules(List.of(room, body, groupType, group));
|
||||
order.setBlockList(List.of(block));
|
||||
order.setBlockItems(List.of(item));
|
||||
order.setPartsList(List.of());
|
||||
order.setPartsItems(List.of());
|
||||
|
||||
ProducingOrderResponse.Payload payload = new ProducingOrderResponse.Payload();
|
||||
payload.setCadViewPath("https://oss.example/cad-view");
|
||||
payload.setCadObjectIdMap(Map.of("block-1", "9001"));
|
||||
payload.setData(order);
|
||||
|
||||
ProducingOrderResponse response = new ProducingOrderResponse();
|
||||
response.setCode(0);
|
||||
response.setData(payload);
|
||||
return response;
|
||||
}
|
||||
|
||||
private ProducingOrderResponse.Module module(
|
||||
String id, int type, String parentId, String name) {
|
||||
ProducingOrderResponse.Module module = new ProducingOrderResponse.Module();
|
||||
module.setId(id);
|
||||
module.setOrderId("order-1");
|
||||
module.setName(name);
|
||||
module.setType(type);
|
||||
module.setParentId(parentId);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user