From b77fbd88bd2c23a4fd14a5d63f27f9b7747bd442 Mon Sep 17 00:00:00 2001 From: libowen Date: Fri, 9 Jan 2026 15:45:59 +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=E7=BB=B4=E6=8A=A4Range=E5=80=BC'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TML维护Range值/AddRangeRequest.java | 17 + ...SGetProjectAttributeManagementController.java | 131 +++++ .../TMLProjectAttrManagement_mxJPO.java | 446 ++++++++++++++++++ TML维护Range值/Tools.java | 15 + TML维护Range值/业务说明.docx | Bin 0 -> 15339 bytes 5 files changed, 609 insertions(+) create mode 100644 TML维护Range值/AddRangeRequest.java create mode 100644 TML维护Range值/DSGetProjectAttributeManagementController.java create mode 100644 TML维护Range值/TMLProjectAttrManagement_mxJPO.java create mode 100644 TML维护Range值/Tools.java create mode 100644 TML维护Range值/业务说明.docx diff --git a/TML维护Range值/AddRangeRequest.java b/TML维护Range值/AddRangeRequest.java new file mode 100644 index 0000000..f79317f --- /dev/null +++ b/TML维护Range值/AddRangeRequest.java @@ -0,0 +1,17 @@ +package com.tml.projectAttrManagement.dto; + +public class AddRangeRequest { + private String attrValue; + private String attrRange; + private String attrRangeDes; + + // getters and setters + public String getAttrValue() { return attrValue; } + public void setAttrValue(String attrValue) { this.attrValue = attrValue; } + + public String getAttrRange() { return attrRange; } + public void setAttrRange(String attrRange) { this.attrRange = attrRange; } + + public String getAttrRangeDes() { return attrRangeDes; } + public void setAttrRangeDes(String attrRangeDes) { this.attrRangeDes = attrRangeDes; } +} diff --git a/TML维护Range值/DSGetProjectAttributeManagementController.java b/TML维护Range值/DSGetProjectAttributeManagementController.java new file mode 100644 index 0000000..31372c4 --- /dev/null +++ b/TML维护Range值/DSGetProjectAttributeManagementController.java @@ -0,0 +1,131 @@ +package com.tml.projectAttrManagement; + +import com.dassault_systemes.platform.restServices.RestService; +import com.tml.projectAttrManagement.dto.AddRangeRequest; +import com.tml.projectAttrManagement.dto.DeleteRangeRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import matrix.db.JPO; +import matrix.util.MatrixException; +import java.util.logging.Logger; + +@Path("/getProjectAttr") +public class DSGetProjectAttributeManagementController extends RestService { + + private static final Logger LOGGER = Logger.getLogger(DSGetProjectAttributeManagementController.class.getName()); + + @GET + @Path("/getAttrInfo") + public Response getInfo(@Context HttpServletRequest request, + @QueryParam("projectType") String projectAttr) { + return this.Info(request, projectAttr); + } + + // 2. 添加方法(POST)- 添加新数据 + @POST + @Path("/add") + @Consumes("application/json") + public Response addRangeValue(@Context HttpServletRequest request, + AddRangeRequest addRequest) { + if (addRequest == null || + addRequest.getAttrValue() == null || addRequest.getAttrValue().isEmpty() || + addRequest.getAttrRange() == null || addRequest.getAttrRange().isEmpty() || + addRequest.getAttrRangeDes() == null || addRequest.getAttrRangeDes().isEmpty()) { + + return Response.status(Response.Status.BAD_REQUEST) + .entity("{\"code\":400,\"message\":\"参数不完整\"}") + .type("application/json") + .build(); + } + matrix.db.Context context = this.getAuthenticatedContext(request, false); + + try { + String[] args = { + addRequest.getAttrValue(), + addRequest.getAttrRange(), + addRequest.getAttrRangeDes() + }; + + String result = JPO.invoke(context, "TMLProjectAttrManagement", null, + "addRangeValue", args, String.class); + + LOGGER.info("添加范围值成功:" + addRequest.getAttrRange() + " -> " + addRequest.getAttrRangeDes()); + + return Response.status(Response.Status.OK) + .entity("{\"code\":200,\"message\":\"" + result + "\",\"data\":{\"key\":\"" + + addRequest.getAttrRange() + "\",\"value\":\"" + addRequest.getAttrRangeDes() + "\"}}") + .type("application/json") + .build(); + + } catch (MatrixException e) { + LOGGER.severe("添加范围值失败:" + e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("{\"code\":500,\"message\":\"添加失败:" + e.getMessage() + "\"}") + .type("application/json") + .build(); + } + } + + // 3. 删除方法(DELETE)- 删除数据 + @DELETE + @Path("/delete") + @Consumes("application/json") + public Response deleteRangeValue(@Context HttpServletRequest request, + DeleteRangeRequest deleteRequest) { + + if (deleteRequest == null || + deleteRequest.getAttrValue() == null || deleteRequest.getAttrValue().isEmpty() || + deleteRequest.getAttrRange() == null || deleteRequest.getAttrRange().isEmpty()) { + + return Response.status(Response.Status.BAD_REQUEST) + .entity("{\"code\":400,\"message\":\"参数不完整\"}") + .type("application/json") + .build(); + } + + matrix.db.Context context = this.getAuthenticatedContext(request, false); + + try { + String[] args = { + deleteRequest.getAttrValue(), + deleteRequest.getAttrRange() + }; + + String result = JPO.invoke(context, "TMLProjectAttrManagement", null, + "delRangeValue", args, String.class); + + LOGGER.info("删除范围值成功:" + deleteRequest.getAttrRange()); + + return Response.status(Response.Status.OK) + .entity("{\"code\":200,\"message\":\"" + result + "\",\"data\":{\"deletedKey\":\"" + + deleteRequest.getAttrRange() + "\"}}") + .type("application/json") + .build(); + + } catch (MatrixException e) { + LOGGER.severe("删除范围值失败:" + e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("{\"code\":500,\"message\":\"删除失败:" + e.getMessage() + "\"}") + .type("application/json") + .build(); + } + } + + private Response Info(HttpServletRequest request, String projectAttr) { + if(projectAttr == null || projectAttr.isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST).entity("Missing project type").build(); + } + + matrix.db.Context context = this.getAuthenticatedContext(request, false); + + String[] itemValue = projectAttr.split("\\|"); + try { + String resultJson = JPO.invoke(context, "TMLProjectAttrManagement", null, "getTrialProductSeries", itemValue, String.class); + return Response.status(Response.Status.OK).entity(resultJson).build(); + } catch (MatrixException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/TML维护Range值/TMLProjectAttrManagement_mxJPO.java b/TML维护Range值/TMLProjectAttrManagement_mxJPO.java new file mode 100644 index 0000000..48fa768 --- /dev/null +++ b/TML维护Range值/TMLProjectAttrManagement_mxJPO.java @@ -0,0 +1,446 @@ +import com.dassault_systemes.platform.ven.jackson.databind.ObjectMapper; +import com.dassault_systemes.platform.ven.jackson.databind.node.ArrayNode; +import com.dassault_systemes.platform.ven.jackson.databind.node.ObjectNode; +import com.matrixone.apps.domain.util.MqlUtil; +import matrix.db.Context; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class TMLProjectAttrManagement_mxJPO { + + // 配置文件路径 - 使用你指定的路径 + private static final String CONFIG_FILE_PATH = "/jpo/File/liangqi.txt"; + + public String getTrialProductSeries(Context context, String[] args) throws Exception { + String itemValue = args[0]; + + List list = new ArrayList<>(); + String getProductSeriesRangeValue = "print attribute TrialProductSeries select range dump |"; + String productSeriesRangeValue = MqlUtil.mqlCommand(context, getProductSeriesRangeValue); + String[] arrValue = dealItemValue(context, productSeriesRangeValue); + + // 处理数据并生成JSON + String jsonResult = processDataAndGenerateJson(context, arrValue); + + // 将JSON结果添加到list + list.add(jsonResult); + + // 使用JSON库序列化list + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(list); + } + + public String delRangeValue(Context context, String[] args) throws Exception { + String attrValue = args[0]; + String attrRange = args[1]; + + String updateAttrRange = "modify attribute TrialProductSeries remove range = " + attrRange; + MqlUtil.mqlCommand(context, updateAttrRange); + + // 同时从配置文件删除对应的键值 + if (attrRange != null && !attrRange.trim().isEmpty()) { + boolean deleted = deleteFromConfigFile(attrRange.trim()); + System.out.println("从配置文件删除结果:" + (deleted ? "成功" : "失败")); + } + + return "删除完成"; + } + + public String addRangeValue(Context context, String[] args) throws Exception { + String attrValue = args[0]; + String attrRange = args[1]; + String attrRangeDes = args[2]; + + String addAttrRange = "add attribute TrialProductSeries add range = " + attrRange; + MqlUtil.mqlCommand(context, addAttrRange); + + + // 同时添加到配置文件 + if (attrRange != null && !attrRange.trim().isEmpty() && + attrRangeDes != null && !attrRangeDes.trim().isEmpty()) { + + boolean added = addToConfigFile(attrRange.trim(), attrRangeDes.trim()); + System.out.println("添加到配置文件结果:" + (added ? "成功" : "失败")); + } + + return "添加完成"; + } + + /** + * 向配置文件添加键值对 + * @param key 英文键名 + * @param value 中文值 + * @return 是否添加成功 + */ + public boolean addToConfigFile(String key, String value) { + try { + if (key == null || key.trim().isEmpty() || value == null || value.trim().isEmpty()) { + System.err.println("错误:键和值都不能为空"); + return false; + } + + File configFile = new File(CONFIG_FILE_PATH); + + // 如果文件不存在,创建文件 + if (!configFile.exists()) { + createDefaultConfigFile(); + } + + // 检查键是否已存在 + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(configFile), "UTF-8"))) { + + String line; + while ((line = reader.readLine()) != null) { + if (line.trim().startsWith(key + "=")) { + System.out.println("键已存在:" + key); + return false; + } + } + } + + // 追加到文件末尾 + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(configFile, true), "UTF-8"))) { + + writer.write(key + "=" + value); + writer.newLine(); + System.out.println("成功添加键值对:" + key + "=" + value); + return true; + + } catch (Exception e) { + System.err.println("写入文件失败:" + e.getMessage()); + return false; + } + + } catch (Exception e) { + System.err.println("添加文件内容时出错:" + e.getMessage()); + return false; + } + } + /** + * 删除配置文件中的指定键值 + * @param keyToDelete 要删除的英文键名 + * @return 是否删除成功 + */ + public boolean deleteFromConfigFile(String keyToDelete) { + try { + if (keyToDelete == null || keyToDelete.trim().isEmpty()) { + System.err.println("错误:要删除的键不能为空"); + return false; + } + + File configFile = new File(CONFIG_FILE_PATH); + + // 检查文件是否存在 + if (!configFile.exists()) { + System.err.println("配置文件不存在:" + CONFIG_FILE_PATH); + return false; + } + + // 读取文件内容 + List lines = new ArrayList<>(); + boolean keyFound = false; + + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new FileInputStream(configFile), "UTF-8"))) { + + String line; + while ((line = reader.readLine()) != null) { + if (!keyFound && line.trim().startsWith(keyToDelete + "=")) { + // 找到要删除的行,跳过不加入列表 + keyFound = true; + System.out.println("找到并删除键:" + keyToDelete); + continue; + } + lines.add(line); + } + } + + if (!keyFound) { + System.out.println("未找到要删除的键:" + keyToDelete); + return false; + } + + // 写回文件 + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(configFile), "UTF-8"))) { + + for (String line : lines) { + writer.write(line); + writer.newLine(); + } + } + + System.out.println("成功删除键:" + keyToDelete); + return true; + + } catch (Exception e) { + System.err.println("删除文件内容时出错:" + e.getMessage()); + return false; + } + } + + public String[] dealItemValue(Context context, String itemValue) { + if (itemValue == null || itemValue.trim().isEmpty()) { + return new String[0]; + } + + String[] values = itemValue.split("\\s*\\|=\\s*"); + + // 创建新数组,跳过第一个空元素 + String[] result = new String[values.length - 1]; + System.arraycopy(values, 1, result, 0, values.length - 1); + + // 打印结果(调试用) + System.out.println("解析到的原始值:"); + for (String value : result) { + System.out.println(" - " + value); + } + + return result; + } + + /** + * 主处理方法 - 处理 arrValue 并生成 JSON + */ + public String processDataAndGenerateJson(Context context, String[] arrValue) throws Exception { + System.out.println("=== 开始处理数据 ==="); + System.out.println("接收到 arrValue 数量: " + (arrValue != null ? arrValue.length : 0)); + + System.out.println("配置文件路径: " + CONFIG_FILE_PATH); + + // 1. 从文件读取中文映射(优先使用文件,如果文件不存在则使用默认数据) + Map chineseMapping = loadMappingFromFile(); + + // 2. 打印映射数据(用于调试) + System.out.println("使用的映射数据大小: " + chineseMapping.size()); + System.out.println("映射数据内容:"); + for (Map.Entry entry : chineseMapping.entrySet()) { + System.out.println(" " + entry.getKey() + " -> " + entry.getValue()); + } + + // 3. 处理 arrValue,构建数据列表 + List deviceItems = new ArrayList<>(); + + if (arrValue != null) { + for (int i = 0; i < arrValue.length; i++) { + String englishKey = arrValue[i].trim(); + String chineseName = chineseMapping.getOrDefault(englishKey, englishKey); + + System.out.println("处理项 " + (i + 1) + ": " + englishKey + " -> " + chineseName); + + DeviceItem item = new DeviceItem(); + item.setId(i + 1); + item.setName(englishKey); + item.setDescription(chineseName); + + deviceItems.add(item); + } + } + + // 4. 生成 JSON + String jsonResult = generateSimpleJson(deviceItems); + + // 5. 打印结果 + System.out.println("处理完成,生成 " + deviceItems.size() + " 条记录"); + + return jsonResult; + } + + /** + * 从文件加载映射数据 + */ + private Map loadMappingFromFile() { + Map mapping = new HashMap<>(); + File configFile = new File(CONFIG_FILE_PATH); + + System.out.println("尝试从文件加载映射数据: " + CONFIG_FILE_PATH); + System.out.println("文件绝对路径: " + configFile.getAbsolutePath()); + System.out.println("文件是否存在: " + configFile.exists()); + System.out.println("文件可读: " + configFile.canRead()); + + if (configFile.exists()) { + try { + // 使用 Properties 加载 + Properties props = new Properties(); + try (FileInputStream fis = new FileInputStream(configFile); + InputStreamReader isr = new InputStreamReader(fis, "UTF-8")) { + + props.load(isr); + + // 转换为Map + for (String key : props.stringPropertyNames()) { + String value = props.getProperty(key).trim(); + mapping.put(key.trim(), value); + System.out.println(" 加载: " + key.trim() + " = " + value); + } + + System.out.println("✓ 成功从文件加载 " + mapping.size() + " 条映射记录"); + + } catch (IOException e) { + System.err.println("✗ 读取文件失败: " + e.getMessage()); + mapping = getDefaultMapping(); + System.out.println("使用默认映射数据"); + } + + } catch (Exception e) { + System.err.println("✗ 加载配置文件异常: " + e.getMessage()); + e.printStackTrace(); + mapping = getDefaultMapping(); + } + } else { + System.out.println("✗ 配置文件不存在: " + CONFIG_FILE_PATH); + mapping = getDefaultMapping(); + System.out.println("使用默认映射数据"); + + // 创建默认配置文件 + createDefaultConfigFile(); + } + + return mapping; + } + + /** + * 创建默认配置文件 + */ + private void createDefaultConfigFile() { + try { + File configFile = new File(CONFIG_FILE_PATH); + File parentDir = configFile.getParentFile(); + + System.out.println("尝试创建配置文件: " + CONFIG_FILE_PATH); + System.out.println("父目录: " + (parentDir != null ? parentDir.getAbsolutePath() : "null")); + + // 确保目录存在 + if (parentDir != null && !parentDir.exists()) { + boolean created = parentDir.mkdirs(); + System.out.println("创建配置目录: " + (created ? "成功" : "失败")); + } + + // 创建配置文件 + Properties props = new Properties(); + Map defaultMap = getDefaultMapping(); + + for (Map.Entry entry : defaultMap.entrySet()) { + props.setProperty(entry.getKey(), entry.getValue()); + } + + try (FileOutputStream fos = new FileOutputStream(configFile); + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")) { + + props.store(osw, "设备类型映射表\n# 英文键名 = 中文名称"); + System.out.println("✓ 已创建默认配置文件: " + CONFIG_FILE_PATH); + System.out.println("文件大小: " + configFile.length() + " bytes"); + + } catch (IOException e) { + System.err.println("✗ 创建配置文件失败: " + e.getMessage()); + e.printStackTrace(); + } + + } catch (Exception e) { + System.err.println("✗ 创建配置文件异常: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * 创建配置文件工具方法(可单独调用) + */ + public void createConfigFile() { + createDefaultConfigFile(); + } + + /** + * 从指定路径加载映射文件 + */ + public Map loadMappingFromSpecificFile(String filePath) { + Map mapping = new HashMap<>(); + + try { + Properties props = new Properties(); + try (FileInputStream fis = new FileInputStream(filePath); + InputStreamReader isr = new InputStreamReader(fis, "UTF-8")) { + + props.load(isr); + + for (String key : props.stringPropertyNames()) { + mapping.put(key.trim(), props.getProperty(key).trim()); + } + + System.out.println("从指定文件加载了 " + mapping.size() + " 条映射记录"); + + } catch (IOException e) { + System.err.println("读取指定文件失败: " + e.getMessage()); + } + + } catch (Exception e) { + System.err.println("加载指定文件异常: " + e.getMessage()); + } + + return mapping; + } + + /** + * 默认映射数据(当文件不存在时使用) + */ + private Map getDefaultMapping() { + Map defaultMap = new HashMap<>(); + + defaultMap.put("SmallWalkInTestChamber", "小型步入式试验箱"); + defaultMap.put("LargeWalkInTestChamber", "大型步入式试验箱(大舱)"); + defaultMap.put("TemperatureAndHumidityChamber", "恒温恒湿箱"); + defaultMap.put("HighAndLowTemperatureChamber", "高低温箱"); + defaultMap.put("RackBox", "台架箱"); + defaultMap.put("RapidTemperatureChange", "快温变"); + defaultMap.put("TwoImpactBoxes", "两箱冲击箱"); + defaultMap.put("ThreeImpactBoxes", "三箱冲击箱"); + defaultMap.put("ThreeComprehensiveBoxes", "三箱综合"); + defaultMap.put("DirectCoolingMachine", "直冷机"); + defaultMap.put("Chiller", "冷水机"); + defaultMap.put("Other", "其它"); + + return defaultMap; + } + + private String generateSimpleJson(List items) { + try { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode array = mapper.createArrayNode(); + + for (DeviceItem item : items) { + ObjectNode node = mapper.createObjectNode(); + node.put("id", item.getId()); + node.put("name", item.getName()); + node.put("description", item.getDescription()); + array.add(node); + } + + return mapper.writeValueAsString(array); + } catch (Exception e) { + System.err.println("生成JSON失败: " + e.getMessage()); + return "[]"; + } + } + + class DeviceItem { + private int id; + private String name; + private String description; + + // getters and setters + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + } +} \ No newline at end of file diff --git a/TML维护Range值/Tools.java b/TML维护Range值/Tools.java new file mode 100644 index 0000000..0801695 --- /dev/null +++ b/TML维护Range值/Tools.java @@ -0,0 +1,15 @@ +package com.tml.projectAttrManagement; + + +import com.dassault_systemes.platform.restServices.ModelerBase; +import jakarta.ws.rs.ApplicationPath; + +@ApplicationPath("/TMLProjectAttr") +public class Tools extends ModelerBase { + @Override + public Class[] getServices() { + return new Class[]{ + DSGetProjectAttributeManagementController.class + }; + } +} diff --git a/TML维护Range值/业务说明.docx b/TML维护Range值/业务说明.docx new file mode 100644 index 0000000000000000000000000000000000000000..df4b7d9fcbf69420029b860e2d5eb30f9247754f GIT binary patch literal 15339 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 literal 0 HcmV?d00001