From c9a4aa62bb18d2a9b7e45bbc5f6df18417e297fe Mon Sep 17 00:00:00 2001 From: libowen Date: Fri, 9 Jan 2026 15:44:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20'TML=E6=89=B9=E9=87=8F=E7=BB=B4=E6=8A=A4=E5=A4=9A=E8=82=BD?= =?UTF-8?q?=E5=B1=9E=E6=80=A7'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DSMaintenancePeptideAttrController.java | 162 ++++++++++ TML批量维护多肽属性/PartAttrDTO.java | 17 + TML批量维护多肽属性/PeptideUpdateRequest.java | 32 ++ .../TMLMaintainMaterialPeptideAttr_mxJPO.java | 299 ++++++++++++++++++ TML批量维护多肽属性/文档说明.docx | Bin 0 -> 15234 bytes 5 files changed, 510 insertions(+) create mode 100644 TML批量维护多肽属性/DSMaintenancePeptideAttrController.java create mode 100644 TML批量维护多肽属性/PartAttrDTO.java create mode 100644 TML批量维护多肽属性/PeptideUpdateRequest.java create mode 100644 TML批量维护多肽属性/TMLMaintainMaterialPeptideAttr_mxJPO.java create mode 100644 TML批量维护多肽属性/文档说明.docx diff --git a/TML批量维护多肽属性/DSMaintenancePeptideAttrController.java b/TML批量维护多肽属性/DSMaintenancePeptideAttrController.java new file mode 100644 index 0000000..0559c31 --- /dev/null +++ b/TML批量维护多肽属性/DSMaintenancePeptideAttrController.java @@ -0,0 +1,162 @@ +package com.tml.materialPeptide; + +import com.dassault_systemes.platform.restServices.RestService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import matrix.db.JPO; +import matrix.util.MatrixException; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.List; +import java.util.logging.Logger; + +@Path("/materialPeptide") +public class DSMaintenancePeptideAttrController extends RestService { + + private static final Logger LOGGER = Logger.getLogger(DSMaintenancePeptideAttrController.class.getName()); + + @GET + public Response getInfo(@Context HttpServletRequest request, + @QueryParam("partCode") String partCode) { + return this.getPartPeptideAttr(request, partCode); + } + + @PUT + @Path("/updatePartInfo") + @Consumes(MediaType.APPLICATION_JSON) + public Response updatePartInfo(@Context HttpServletRequest request, + PartAttrDTO partAttrDTO) { + return this.updatePartAttr(request,partAttrDTO); + } + + @PUT + @Path("/updateInfo") + @Consumes(MediaType.APPLICATION_JSON) + public Response updateInfo(@Context HttpServletRequest request, + PeptideUpdateRequest updateRequest) { + return this.updatePeptideAttrs(request, updateRequest); + } + + private Response updatePartAttr(HttpServletRequest request, PartAttrDTO partAttrDTO) { + if(partAttrDTO == null || partAttrDTO.getPartId() == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("Missing required properties").build(); + } + + matrix.db.Context context = this.getAuthenticatedContext(request, false); + String partId = partAttrDTO.getPartId(); + + StringBuilder updateInfoBuilder = new StringBuilder(); + updateInfoBuilder.append("modify bus ").append(partId); + + // 如果有规格型号属性,添加到更新字符串 + if (partAttrDTO.getSpecification() != null) { + updateInfoBuilder.append(" ").append("specification").append(" ").append(partAttrDTO.getSpecification()); + } + + // 如果有品牌属性,添加到更新字符串 + if (partAttrDTO.getBrand() != null) { + updateInfoBuilder.append(" ").append("brand").append(" ").append(partAttrDTO.getBrand()); + } + + String updateInfo = updateInfoBuilder.toString(); + String[] updateInfoArray = {updateInfo}; + LOGGER.info("调用JPO更新物料属性: " + updateInfo); + + try { + String resultJson = JPO.invoke(context, "TMLMaintainMaterialPeptideAttr", null, "updatePartAttr", updateInfoArray, String.class); + return Response.status(Response.Status.OK).entity(resultJson).build(); + } catch (MatrixException e) { + throw new RuntimeException(e); + } + } + + private Response updatePeptideAttrs(HttpServletRequest request, PeptideUpdateRequest updateRequest) { + if(updateRequest == null || updateRequest.getProperties() == null) { + return Response.status(Response.Status.BAD_REQUEST).entity("Missing required properties").build(); + } + + matrix.db.Context context = this.getAuthenticatedContext(request, false); + List properties = updateRequest.getProperties(); + String itemId = updateRequest.getPartId() + " "; + String itemInfo = itemId; + for(PropertyDTO property : properties) { + itemInfo += property.getName() + " "; + itemInfo += property.getValue() + " "; + } + String updateInfo = "modify bus " + itemInfo; + + String[] updateInfoArray = {updateInfo}; + try { + String resultJson = JPO.invoke(context, "TMLMaintainMaterialPeptideAttr", null, "updatePeptideAttr", updateInfoArray, String.class); + return Response.status(Response.Status.OK).entity(resultJson).build(); + } catch (MatrixException e) { + throw new RuntimeException(e); + } + } + + private Response getPartPeptideAttr(HttpServletRequest request, String partCode) { + if (partCode == null || partCode.trim().isEmpty()) { + return createErrorResponse(400, "缺少 partCode 参数"); + } + + try { + matrix.db.Context context = this.getAuthenticatedContext(request, false); + String[] partInfo; + if (partCode.contains("|")) { + partInfo = partCode.split("\\|"); + } else { + partInfo = new String[]{partCode}; + } + + return executeQuery(context, partInfo); + + } catch (Exception e) { + return createErrorResponse(500, "认证失败: " + e.getMessage()); + } + } + + private Response executeQuery(matrix.db.Context context, String[] partInfo) { + try { + // 调用 JPO 方法,返回包装的JSON + String resultJson = JPO.invoke(context, "TMLMaintainMaterialPeptideAttr", null, "getPeptideAttrMap", partInfo, String.class); + + // 直接返回JSON字符串 + return Response.ok() + .entity(resultJson) + .type(MediaType.APPLICATION_JSON) + .build(); + + } catch (MatrixException e) { + return createErrorResponse(500, "JPO调用失败: " + e.getMessage()); + } catch (Exception e) { + return createErrorResponse(500, "服务器错误: " + e.getMessage()); + } + } + + private Response createErrorResponse(int statusCode, String message) { + try { + JSONObject errorResponse = new JSONObject(); + errorResponse.put("success", false); + errorResponse.put("code", statusCode); + errorResponse.put("message", message); + errorResponse.put("data", new JSONArray()); + errorResponse.put("timestamp", System.currentTimeMillis()); + + return Response.status(statusCode) + .entity(errorResponse.toString()) + .type(MediaType.APPLICATION_JSON) + .build(); + + } catch (Exception e) { + // 如果JSON创建失败,返回简单文本 + return Response.status(500) + .entity("{\"error\":\"Internal server error\"}") + .type(MediaType.APPLICATION_JSON) + .build(); + } + } +} \ No newline at end of file diff --git a/TML批量维护多肽属性/PartAttrDTO.java b/TML批量维护多肽属性/PartAttrDTO.java new file mode 100644 index 0000000..a18ec2c --- /dev/null +++ b/TML批量维护多肽属性/PartAttrDTO.java @@ -0,0 +1,17 @@ +package com.tml.materialPeptide; + +public class PartAttrDTO { + private String partId; + private String specification; // 规格型号 + private String brand; // 品牌 + + // getters and setters + public String getPartId() { return partId; } + public void setPartId(String partId) { this.partId = partId; } + + public String getSpecification() { return specification; } + public void setSpecification(String specification) { this.specification = specification; } + + public String getBrand() { return brand; } + public void setBrand(String brand) { this.brand = brand; } +} diff --git a/TML批量维护多肽属性/PeptideUpdateRequest.java b/TML批量维护多肽属性/PeptideUpdateRequest.java new file mode 100644 index 0000000..290c636 --- /dev/null +++ b/TML批量维护多肽属性/PeptideUpdateRequest.java @@ -0,0 +1,32 @@ +package com.tml.materialPeptide; + +import java.util.List; + +public class PeptideUpdateRequest { + private String partId; + private List properties; + + public PeptideUpdateRequest(String partId, List properties) { + this.partId = partId; + this.properties = properties; + } + + public PeptideUpdateRequest() { + } + + public String getPartId() { + return partId; + } + + public List getProperties() { + return properties; + } + + public void setPartId(String partId) { + this.partId = partId; + } + + public void setProperties(List properties) { + this.properties = properties; + } +} diff --git a/TML批量维护多肽属性/TMLMaintainMaterialPeptideAttr_mxJPO.java b/TML批量维护多肽属性/TMLMaintainMaterialPeptideAttr_mxJPO.java new file mode 100644 index 0000000..a4e323f --- /dev/null +++ b/TML批量维护多肽属性/TMLMaintainMaterialPeptideAttr_mxJPO.java @@ -0,0 +1,299 @@ +import com.matrixone.apps.domain.util.FrameworkException; +import com.matrixone.apps.domain.util.MqlUtil; +import matrix.db.Context; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.*; +import java.util.logging.Logger; + +public class TMLMaintainMaterialPeptideAttr_mxJPO { + + private static final Logger LOGGER = Logger.getLogger(TMLMaintainMaterialPeptideAttr_mxJPO.class.getName()); + + public String updatePartAttr(Context context, String[] args) throws FrameworkException { + String updateInfo = args[0]; + LOGGER.info("updatePeptideAttr: " + updateInfo); + + MqlUtil.mqlCommand(context, updateInfo); + return "success"; + } + + public String updatePeptideAttr(Context context, String[] args) throws FrameworkException { + String updateInfo = args[0]; + LOGGER.info("updatePeptideAttr: " + updateInfo); + + MqlUtil.mqlCommand(context, updateInfo); + return "success"; + } + + public String getPeptideAttrMap(Context context, String[] args) throws FrameworkException { + JSONArray peptideAttrArray = new JSONArray(); + + if(args != null && args.length > 0) { + for (String itemValue : args) { + if (itemValue == null || itemValue.trim().isEmpty()) { + continue; + } + + JSONObject peptideAttrObj = new JSONObject(); + + try { + // 1. 获取item ID + String getItemInfo = "temp query bus VPMReference * * where \"attribute[PLMEntity.V_description].value == '" + + escapeMqlString(itemValue.trim()) + "'\" select id dump |"; + String itemInfo = MqlUtil.mqlCommand(context, getItemInfo); + + if (itemInfo == null || itemInfo.trim().isEmpty()) { + LOGGER.warning("未找到项目: " + itemValue); + peptideAttrObj.put("itemValue", itemValue); + peptideAttrObj.put("error", "未找到项目"); + peptideAttrArray.put(peptideAttrObj); + continue; + } + + String[] itemInfoParts = itemInfo.split("\\|"); + if (itemInfoParts.length <= 3) { + LOGGER.warning("项目信息格式不正确: " + itemInfo); + peptideAttrObj.put("itemValue", itemValue); + peptideAttrObj.put("error", "项目信息格式不正确"); + peptideAttrArray.put(peptideAttrObj); + continue; + } + + String itemId = itemInfoParts[3].trim(); + + // 2. 获取肽属性信息 + String getMaterialPeptideAttrInfo = "print bus " + itemId + + " select attribute[PLMEntity.V_Name].value " + + "attribute[TMLVPMReference.TMLSpecification].value attribute[TMLVPMReference.TMLBrand].value " + + "to[Classified Item].attribute to[Classified Item].attribute.value dump |"; + + String materialPeptideAttrInfo = MqlUtil.mqlCommand(context, getMaterialPeptideAttrInfo); + + // 添加基本信息 + peptideAttrObj.put("itemValue", itemValue); + peptideAttrObj.put("itemId", itemId); + + // 安全检查:确保返回结果不为空 + if (materialPeptideAttrInfo != null && !materialPeptideAttrInfo.trim().isEmpty()) { + try { + // 使用 -1 保留所有空字符串元素 + String[] materialPeptideAttrSplit = materialPeptideAttrInfo.split("\\|", -1); + + // 1. 先提取基本信息(前3个元素) + if (materialPeptideAttrSplit.length >= 1) { + peptideAttrObj.put("materialName", materialPeptideAttrSplit[0].trim()); + } + if (materialPeptideAttrSplit.length >= 2) { + peptideAttrObj.put("brand", materialPeptideAttrSplit[1].trim()); + } + if (materialPeptideAttrSplit.length >= 3) { + peptideAttrObj.put("specification", materialPeptideAttrSplit[2].trim()); + } + + List> peptideProperties = new ArrayList<>(); + // 2. 处理剩余的属性键值对(从第4个元素开始) + // 确保有足够的元素形成键值对 + int startIndex = 3; // 从第4个元素开始(索引3) + if (materialPeptideAttrSplit.length > startIndex) { + // 计算可用于形成键值对的元素数量 + int availableElements = materialPeptideAttrSplit.length - startIndex; + + // 如果剩余元素是奇数,添加一个空字符串使其成为偶数 + if (availableElements % 2 != 0) { + materialPeptideAttrSplit = Arrays.copyOf( + materialPeptideAttrSplit, + materialPeptideAttrSplit.length + 1 + ); + materialPeptideAttrSplit[materialPeptideAttrSplit.length - 1] = ""; + availableElements += 1; + } + + // 处理键值对 + int totalPairs = availableElements / 2; + for (int j = 0; j < totalPairs; j++) { + int keyIndex = startIndex + j; + int valueIndex = startIndex + totalPairs + j; + + String key = materialPeptideAttrSplit[keyIndex].trim(); + String value = materialPeptideAttrSplit[valueIndex].trim(); + + // 只有当键不为空时才添加到对象 + if (!key.isEmpty()) { + Map property = new HashMap<>(); + property.put("name", key); + property.put("value", value); + peptideProperties.add(property); + } + } + } + // 3. 将多肽属性列表添加到结果对象 + peptideAttrObj.put("peptideProperties", peptideProperties); + } catch (Exception e) { + // 异常处理 + System.err.println("Error processing material peptide attributes for itemId: " + itemId); + e.printStackTrace(); + + // 返回默认结构 + peptideAttrObj.put("PLMEntity.V_Name", ""); + peptideAttrObj.put("PLMEntity.V_Description", ""); + peptideAttrObj.put("PLMEntity.V_Classification", ""); + peptideAttrObj.put("TMLVPMReference.TMLBrand", ""); + peptideAttrObj.put("peptideProperties", new ArrayList<>()); + } + } else { + // MQL命令返回为空的情况 + peptideAttrObj.put("PLMEntity.V_Name", ""); + peptideAttrObj.put("PLMEntity.V_Description", ""); + peptideAttrObj.put("PLMEntity.V_Classification", ""); + peptideAttrObj.put("TMLVPMReference.TMLBrand", ""); + peptideAttrObj.put("peptideProperties", new ArrayList<>()); + } + + } catch (Exception e) { + LOGGER.warning("处理项目 " + itemValue + " 时出错: " + e.getMessage()); + try { + peptideAttrObj.put("itemValue", itemValue); + peptideAttrObj.put("error", e.getMessage()); + } catch (Exception ex) { + // 忽略 JSON 异常 + } + } + + peptideAttrArray.put(peptideAttrObj); + } + } + + // 直接返回 JSONArray 的字符串表示 + System.out.println(peptideAttrArray); + return peptideAttrArray.toString(); + } + + public String getPeptideAttrMapWithWrapper(Context context, String[] args) throws FrameworkException { + JSONObject result = new JSONObject(); + + try { + JSONArray data = new JSONArray(getPeptideAttrMap(context, args)); + + result.put("success", true); + result.put("code", 200); + result.put("message", "查询成功"); + result.put("data", data); + result.put("total", data.length()); + result.put("timestamp", System.currentTimeMillis()); + + } catch (Exception e) { + result.put("success", false); + result.put("code", 500); + result.put("message", "查询失败: " + e.getMessage()); + result.put("data", new JSONArray()); + result.put("timestamp", System.currentTimeMillis()); + } + + return result.toString(); + } + + /** + * 另一种实现:直接返回标准JSON格式 + */ + public String getPeptideAttrMapStandard(Context context, String[] args) throws FrameworkException { + JSONObject response = new JSONObject(); + JSONArray dataArray = new JSONArray(); + + try { + if(args != null && args.length > 0) { + for (String itemValue : args) { + JSONObject itemData = processSingleItem(context, itemValue); + dataArray.put(itemData); + } + } + + response.put("success", true); + response.put("code", 200); + response.put("message", "success"); + response.put("data", dataArray); + response.put("total", dataArray.length()); + response.put("timestamp", System.currentTimeMillis()); + + } catch (Exception e) { + response.put("success", false); + response.put("code", 500); + response.put("message", "Error: " + e.getMessage()); + response.put("data", new JSONArray()); + response.put("timestamp", System.currentTimeMillis()); + } + + return response.toString(); + } + + /** + * 处理单个项目 + */ + private JSONObject processSingleItem(Context context, String itemValue) throws Exception { + JSONObject itemObj = new JSONObject(); + + // 获取item ID + String getItemInfo = "temp query bus VPMReference * * where \"attribute[PLMEntity.V_description].value == '" + + escapeMqlString(itemValue.trim()) + "'\" select id dump |"; + String itemInfo = MqlUtil.mqlCommand(context, getItemInfo); + + if (itemInfo == null || itemInfo.trim().isEmpty()) { + itemObj.put("error", "Item not found"); + return itemObj; + } + + String[] itemInfoParts = itemInfo.split("\\|"); + if (itemInfoParts.length <= 3) { + itemObj.put("error", "Invalid item info format"); + return itemObj; + } + + String itemId = itemInfoParts[3].trim(); + itemObj.put("itemId", itemId); + itemObj.put("itemValue", itemValue); + + // 获取肽属性信息 + String getMaterialPeptideAttrInfo = "print bus " + itemId + + " select to[Classified Item].attribute to[Classified Item].attribute.value dump |"; + String materialPeptideAttrInfo = MqlUtil.mqlCommand(context, getMaterialPeptideAttrInfo); + + if (materialPeptideAttrInfo != null && !materialPeptideAttrInfo.trim().isEmpty()) { + JSONObject attributes = parseAttributes(materialPeptideAttrInfo); + itemObj.put("attributes", attributes); + } else { + itemObj.put("attributes", new JSONObject()); + } + + return itemObj; + } + + /** + * 解析属性字符串为 JSONObject + */ + private JSONObject parseAttributes(String attrInfo) throws Exception { + JSONObject attributes = new JSONObject(); + String[] parts = attrInfo.split("\\|", -1); + + if (parts.length % 2 != 0) { + parts = Arrays.copyOf(parts, parts.length + 1); + parts[parts.length - 1] = ""; + } + + int totalPairs = parts.length / 2; + for (int i = 0; i < totalPairs; i++) { + String key = parts[i].trim(); + String value = parts[totalPairs + i].trim(); + if (!key.isEmpty()) { + attributes.put(key, value); + } + } + + return attributes; + } + + private String escapeMqlString(String input) { + if (input == null) return ""; + return input.replace("'", "''"); + } +} \ No newline at end of file diff --git a/TML批量维护多肽属性/文档说明.docx b/TML批量维护多肽属性/文档说明.docx new file mode 100644 index 0000000000000000000000000000000000000000..2d8fc3cae9e5723bbf45f55a4fc3680e117e2eff GIT binary patch literal 15234 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 literal 0 HcmV?d00001