diff --git a/1-sql/2026-01-13/simulation_run.sql b/1-sql/2026-01-13/simulation_run.sql new file mode 100644 index 00000000..a4f205db --- /dev/null +++ b/1-sql/2026-01-13/simulation_run.sql @@ -0,0 +1,2 @@ +ALTER TABLE spdm_baseline.simulation_run ADD reportTemplate varchar(120) NULL COMMENT '报告模板uuid'; +ALTER TABLE spdm_baseline.simulation_run ADD reportContent MEDIUMTEXT NULL COMMENT '报告模板填充内容'; diff --git a/capability/src/main/java/com/sdm/capability/controller/ReportTemplateController.java b/capability/src/main/java/com/sdm/capability/controller/ReportTemplateController.java index 9c3736ef..56301abb 100644 --- a/capability/src/main/java/com/sdm/capability/controller/ReportTemplateController.java +++ b/capability/src/main/java/com/sdm/capability/controller/ReportTemplateController.java @@ -1,22 +1,12 @@ package com.sdm.capability.controller; import com.sdm.capability.model.dto.ReportTemplateDto; -import com.sdm.capability.model.entity.SimulationFlowTemplate; -import com.sdm.capability.model.entity.SimulationReportTemplate; -import com.sdm.capability.model.req.flow.GetFlowTemplateReq; -import com.sdm.capability.model.req.flow.ReleaseFlowTemplateReq; -import com.sdm.capability.service.IFlowService; -import com.sdm.capability.service.ISimulationFlowNodeService; import com.sdm.capability.service.ISimulationReportTemplateService; import com.sdm.common.common.SdmResponse; -import com.sdm.common.entity.req.capability.FlowNodeDto; import com.sdm.common.entity.req.system.LaunchApproveReq; -import com.sdm.common.entity.resp.capability.FlowTemplateResp; -import com.sdm.common.feign.inter.capability.ISimulationFlowFeignClient; +import com.sdm.common.entity.resp.capability.ReportTemplateResp; import com.sdm.common.feign.inter.capability.ISimulationReportFeignClient; -import com.sdm.common.log.annotation.SysLog; import org.springframework.http.MediaType; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -106,7 +96,7 @@ public class ReportTemplateController implements ISimulationReportFeignClient { * @return */ @GetMapping("/queryReportTemplateInfo") - public SdmResponse queryReportTemplateInfo(@RequestParam("uuid") String uuid) { + public SdmResponse queryReportTemplateInfo(@RequestParam("uuid") String uuid) { return reportTemplateService.queryReportTemplateInfo(uuid); } diff --git a/capability/src/main/java/com/sdm/capability/service/ISimulationReportTemplateService.java b/capability/src/main/java/com/sdm/capability/service/ISimulationReportTemplateService.java index c9a82046..6019d2bb 100644 --- a/capability/src/main/java/com/sdm/capability/service/ISimulationReportTemplateService.java +++ b/capability/src/main/java/com/sdm/capability/service/ISimulationReportTemplateService.java @@ -5,6 +5,7 @@ import com.sdm.capability.model.dto.ReportTemplateDto; import com.sdm.capability.model.entity.SimulationReportTemplate; import com.sdm.common.common.SdmResponse; import com.sdm.common.entity.req.system.LaunchApproveReq; +import com.sdm.common.entity.resp.capability.ReportTemplateResp; public interface ISimulationReportTemplateService extends IService { @@ -24,7 +25,7 @@ public interface ISimulationReportTemplateService extends IService queryReportTemplateInfo(String uuid); SdmResponse handleApproveResult(LaunchApproveReq req); diff --git a/capability/src/main/java/com/sdm/capability/service/impl/SimulationReportTemplateServiceImpl.java b/capability/src/main/java/com/sdm/capability/service/impl/SimulationReportTemplateServiceImpl.java index acf7f9f1..e63bb156 100644 --- a/capability/src/main/java/com/sdm/capability/service/impl/SimulationReportTemplateServiceImpl.java +++ b/capability/src/main/java/com/sdm/capability/service/impl/SimulationReportTemplateServiceImpl.java @@ -21,6 +21,7 @@ import com.sdm.common.entity.req.data.GetFileBaseInfoReq; import com.sdm.common.entity.req.data.UpdateScriptAndReportReq; import com.sdm.common.entity.req.data.UploadFilesReq; import com.sdm.common.entity.req.system.LaunchApproveReq; +import com.sdm.common.entity.resp.capability.ReportTemplateResp; import com.sdm.common.entity.resp.data.FileMetadataInfoResp; import com.sdm.common.feign.impl.data.DataClientFeignClientImpl; import com.sdm.common.feign.impl.system.ApproveFeignClientImpl; @@ -269,9 +270,11 @@ public class SimulationReportTemplateServiceImpl extends ServiceImpl queryReportTemplateInfo(String uuid) { SimulationReportTemplate reportTemplate = this.lambdaQuery().eq(SimulationReportTemplate::getUuid, uuid).one(); - return SdmResponse.success(reportTemplate); + ReportTemplateResp resp = new ReportTemplateResp(); + resp.setTemplateName(reportTemplate.getTemplateName()); + return SdmResponse.success(resp); } @Override @@ -282,7 +285,7 @@ public class SimulationReportTemplateServiceImpl extends ServiceImpl queryReportTemplateInfo(String uuid) { + SdmResponse response; + try { + response = reportFeignClient.queryReportTemplateInfo(uuid); + if (!response.isSuccess()) { + return SdmResponse.failed("查询报告模板失败"); + } + return response; + } catch (Exception e) { + log.error("查询报告模板失败", e); + return SdmResponse.failed("查询报告模板失败"); + } + } + } diff --git a/common/src/main/java/com/sdm/common/feign/inter/capability/ISimulationReportFeignClient.java b/common/src/main/java/com/sdm/common/feign/inter/capability/ISimulationReportFeignClient.java index 8f289bf6..2f41e9b0 100644 --- a/common/src/main/java/com/sdm/common/feign/inter/capability/ISimulationReportFeignClient.java +++ b/common/src/main/java/com/sdm/common/feign/inter/capability/ISimulationReportFeignClient.java @@ -2,9 +2,12 @@ package com.sdm.common.feign.inter.capability; import com.sdm.common.common.SdmResponse; import com.sdm.common.entity.req.system.LaunchApproveReq; +import com.sdm.common.entity.resp.capability.ReportTemplateResp; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "capability",contextId = "reportTemplateFeignClient") @@ -13,4 +16,7 @@ public interface ISimulationReportFeignClient { @PostMapping("/report/approveHandleNotice") SdmResponse receiveApproveNotice(@RequestBody LaunchApproveReq req); + @GetMapping("/report/queryReportTemplateInfo") + SdmResponse queryReportTemplateInfo(@RequestParam("uuid") String uuid); + } diff --git a/project/modifyReport.py b/project/modifyReport.py new file mode 100644 index 00000000..e242debf --- /dev/null +++ b/project/modifyReport.py @@ -0,0 +1,393 @@ +"""根据前端传来的json数据,修改模板文件,生成新的word文档""" + +from docx.oxml.ns import qn +from docx.shared import Pt +from docx.enum.text import WD_ALIGN_PARAGRAPH +import os +import sys +import traceback +from datetime import datetime +import json +import shutil +from lxml import etree +import base64 +from docx import Document +from docx.shared import RGBColor +import re +from docx.enum.table import WD_ALIGN_VERTICAL + + +def getJsonData(jsonFilePath): + """获取JSON数据""" + if not os.path.exists(jsonFilePath): + print(f"Error:JSON文件路径 '{jsonFilePath}' 不存在,请确认") + return None + + with open(jsonFilePath, 'r', encoding='utf-8') as jsonFile: + data = json.load(jsonFile) + + return data + + +class Tee: + """自定义文件对象,同时写入文件和原控制台""" + + def __init__(self, *files): + self.files = files + + def write(self, obj): + for f in self.files: + f.write(obj) + f.flush() # 确保立即写入 + + def flush(self): + for f in self.files: + f.flush() + + +def replace_text_in_textbox(doc, data_dict): + """替换文本框中的文本""" + namespaces = { + 'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main', + 'wp': 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing', + 'a': 'http://schemas.openxmlformats.org/drawingml/2006/main', + 'wps': 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape' + } + + doc_modified = False # 整个文档是否被修改 + for part_id, related_part in doc.part.related_parts.items(): + if hasattr(related_part, 'blob'): + xml_content = related_part.blob + # 检查是否是 XML 内容(以 {value}") + part_modified = True + doc_modified = True + + # 关键步骤:将修改后的 XML 写回到部件中 + if part_modified: + # 同时更新 _element 和 _blob + if hasattr(related_part, '_element'): + related_part._element = root + updated_xml = etree.tostring( + root, encoding='UTF-8', xml_declaration=True) + related_part._blob = updated_xml + + except etree.XMLSyntaxError as e: + print(f"Warning:部件 {part_id} 不是有效的 XML: {e}") + continue + except Exception as e: + print(f"Warning:处理部件 {part_id} 时出错: {e}") + continue + + +def replace_text_in_paragraph(paragraph, data_dict, csvPath=None): + """新增段落中的文本""" + text = paragraph.text + # 居中显示 + # paragraph.alignment = 1 + ngFlag = False + if "$" in text and "<" not in text and ">" not in text: + keys_list = re.findall(r'\$([a-zA-Z0-9_]+)', text) + for keyName in keys_list: + value = data_dict.get(keyName, None) + if value is not None: + placeholder = f"${keyName}" + text = text.replace(placeholder, str(value)) + print(f"文本: {placeholder} -> {value}") + paragraph.text = text + + +def add_text_in_table(paragraph, data_dict): + """新增表格中的文本""" + text = paragraph.text + if "$" in text and "<" not in text and ">" not in text: + keys_list = re.findall(r'\$([a-zA-Z0-9_]+)', text) + for keyName in keys_list: + value = data_dict.get(keyName, None) + if value is not None: + placeholder = f"${keyName}" + text = text.replace(placeholder, str(value)) + print(f"文本: {placeholder} -> {value}") + paragraph.text = text + + +def get_reference_style(table): + """从表格第一行提取样式特征""" + style = { + 'font_name': '宋体', + 'font_size': Pt(10.5), + 'alignment': WD_ALIGN_PARAGRAPH.CENTER, + 'vertical': WD_ALIGN_VERTICAL.CENTER + } + try: + if len(table.rows) > 0: + # 尝试从第一行第一个单元格获取字体信息 + cell = table.rows[0].cells[0] + style['vertical'] = cell.vertical_alignment or WD_ALIGN_VERTICAL.CENTER + if cell.paragraphs and cell.paragraphs[0].runs: + run = cell.paragraphs[0].runs[0] + if run.font.name: + style['font_name'] = run.font.name + if run.font.size: + style['font_size'] = run.font.size + style['alignment'] = cell.paragraphs[0].alignment or WD_ALIGN_PARAGRAPH.CENTER + except Exception: + pass + return style + + +def apply_style_to_cell(cell, text, style): + """将提取或默认的样式应用到单元格""" + cell.text = str(text) + cell.vertical_alignment = style['vertical'] + for paragraph in cell.paragraphs: + paragraph.alignment = style['alignment'] + for run in paragraph.runs: + run.font.name = style['font_name'] + run.font.size = style['font_size'] + # 确保中文字体兼容性 + run._element.rPr.rFonts.set(qn('w:eastAsia'), style['font_name']) + + +def replace_text_in_table(table, data_dict, tableFlag=False, count_wrapper=[0]): + """ + 替换表格中的文本 + count_wrapper: 传入一个列表,例如 [2],表示剩余可执行次数 + """ + for row in table.rows: + headers = [cell.text for cell in row.cells] + if headers: + header_str = ",".join(headers) + if header_str in data_dict and not tableFlag and count_wrapper[0] > 0: + print(f"================表头匹配到数据: {header_str}") + table_data = data_dict[header_str] + + # 在表格末尾添加数据行 + # 提取原表格样式 + ref_style = get_reference_style(table) + for data_row in table_data: + new_row = table.add_row() + # 尝试继承原行高 + if len(table.rows) > 1: + new_row.height = table.rows[0].height + for idx, header in enumerate(headers): + # content = data_row.get(header, "") + # apply_style_to_cell( + # new_row.cells[idx], content, ref_style) + key_list = list(data_row.keys()) + new_row.cells[idx].text = str(data_row[key_list[idx]]) + # 根据内容长短,自适应水平居中显示 不换行 + # for paragraph in new_row.cells[idx].paragraphs: + # paragraph.vertical = WD_ALIGN_VERTICAL.CENTER + # paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER + + print( + f"表格新增行内容: {[str(data_row.get(header, '')) for header in headers]}") + tableFlag = True + count_wrapper[0] -= 1 + + if not tableFlag: + for cell in row.cells: + # 递归处理单元格中的嵌套表格 + if cell.tables: + for nested_table in cell.tables: + replace_text_in_table( + nested_table, data_dict, tableFlag=tableFlag, count_wrapper=count_wrapper) + + # 新增表格内容 + for paragraph in cell.paragraphs: + replace_text_in_paragraph(paragraph, data_dict) + + +def getDataDict(reportContent_list): + """获取数据字典 用于替换文本""" + data_dict = {} + table_num = 0 + for item_dict in reportContent_list: + if item_dict["type"] == "text": + data_dict[item_dict["key"]] = item_dict["value"] + elif item_dict["type"] == "table": + tableContent = item_dict.get("value", []) + # 获取表头 + headers = [] + if tableContent: + headers = list(tableContent[0].keys()) + if headers: + header_str = ",".join(headers) + # newKey = item_dict["key"] + "_" + header_str + newKey = header_str + data_dict[newKey] = tableContent + table_num += 1 + elif item_dict["type"] == "conclusion": + data_dict[item_dict["key"]] = item_dict["value"] + return data_dict, table_num + + +def base64ToImg(projectPath, reportContent_list): + """把base64字符串转成图片""" + img_num = 0 + for item_dict in reportContent_list: + if item_dict["type"] == "img": + img_base64_list = item_dict["value"] + img_base64_str = img_base64_list[0].get("src", "") + img_base64 = img_base64_str.split(",")[1] + img_data = base64.b64decode(img_base64) + img_path = f"{projectPath}/{item_dict['key']}.png" + img_num += 1 + with open(img_path, 'wb') as img_file: + img_file.write(img_data) + # 新增字典中的picPath为图片路径 + # item_dict["picPath"] = img_path + return img_num + + +def modiyReport(projectPath, reportContent_list, outputDocxPath): + """根据JSON数据修改报告文件""" + doc = Document(outputDocxPath) + + # 把base64转成图片 + img_num = base64ToImg(projectPath, reportContent_list) + print(f"Info:共转换{img_num}张图片") + # 图片与模版中图片名称的对应关系 模版从2开始 对应图片1.png 2.png ... + image_dict = { + f"image{i}.png": f"pic{i-1}.png" for i in range(2, img_num+2)} + # image_dict = {"image2.png": "pic1.png", + # "image3.png": "pic2.png", + # "image4.png": "pic3.png", + # "image5.png": "pic4.png", + # "image6.png": "pic5.png", + # } + + # 获取数据字典 用于替换文本 + data_dict, table_num = getDataDict(reportContent_list) + + # 处理页眉 + for section in doc.sections: + if section.header: + for paragraph in section.header.paragraphs: + replace_text_in_paragraph(paragraph, data_dict) + # 处理页眉中的表格 + for table in section.header.tables: + replace_text_in_table(table, data_dict) + + # 替换图片 + for rel in doc.part.rels.values(): + if "image" in rel.reltype: + # print(rel.target_ref, rel.reltype) + # media/image3.png + imageItem = rel.target_ref.split("/")[-1] + picName = image_dict.get(imageItem, None) + if picName is not None and picName != ".png": + replace_image_path = os.path.join(projectPath, picName) + if os.path.exists(replace_image_path): + print(f"图片: {imageItem} -> {picName}") + rel.target_part._blob = open( + replace_image_path, "rb").read() + + # 处理表格 + remaining_count = [table_num] + for table in doc.tables: + replace_text_in_table( + table, data_dict, tableFlag=False, count_wrapper=remaining_count) + + # 处理结论 + # replaceConclusion(doc, data_dict) + + # 处理文本框 + replace_text_in_textbox(doc, data_dict) + + # 保存文档 + doc.save(outputDocxPath) + print(f"Info:Word文档已创建: {outputDocxPath}") + + +if __name__ == "__main__": + + projectPath = sys.argv[1] + templatePath = sys.argv[2] + # pythonLogPath = "/opt/report" + + # currentPath = os.getcwd() + # projectPath = f"{currentPath}/project/modiyTemp" + # templatePath = f"{currentPath}/Test/report_tem.docx" + pythonLogPath = os.path.dirname(projectPath) + + # 输出log日志 + # 日志文件路径 + log_fileDir = f"{pythonLogPath}/pythonLog" + if not os.path.exists(log_fileDir): + os.makedirs(log_fileDir) + # log文件用日期命名 + log_file = f"{log_fileDir}/{datetime.now().strftime('%Y%m%d')}.log" + with open(log_file, 'a', encoding='utf-8') as log_file_obj: + # 重定向标准输出和标准错误 + original_stdout = sys.stdout + original_stderr = sys.stderr + + # 创建Tee对象,同时输出到文件和原控制台 + tee = Tee(log_file_obj, original_stdout) + sys.stdout = tee + sys.stderr = tee + + # 在日志开头添加时间戳 + print("=" * 60) + print(f"程序开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"日志文件: {log_file}") + print("=" * 60) + + try: + # 获取报告内容JSON数据 + reportContentPath = f"{projectPath}/reportContent.json" + reportContent_list = getJsonData(reportContentPath) + + # 拷贝模板文件到项目目录下 + outputDocxPath = f"{projectPath}/report_{datetime.now().strftime('%Y%m%d')}.docx" + shutil.copy(templatePath, outputDocxPath) + + # 修改报告文件 + modiyReport(projectPath, reportContent_list, outputDocxPath) + + print("\n" + "=" * 60) + print(f"程序结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print("程序执行成功") + print("=" * 60) + + except Exception as e: + print(f"\n程序执行出错: {str(e)}", file=sys.stderr) + traceback.print_exc() + print("=" * 60) + print(f"程序异常结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print("=" * 60) + raise + finally: + # 恢复标准输出 + sys.stdout = original_stdout + sys.stderr = original_stderr diff --git a/project/src/main/java/com/sdm/project/controller/SimulationRunController.java b/project/src/main/java/com/sdm/project/controller/SimulationRunController.java index 39407d97..ab4f6115 100644 --- a/project/src/main/java/com/sdm/project/controller/SimulationRunController.java +++ b/project/src/main/java/com/sdm/project/controller/SimulationRunController.java @@ -4,6 +4,7 @@ import com.sdm.common.common.SdmResponse; import com.sdm.common.entity.req.data.CreateDirReq; import com.sdm.common.entity.req.data.QueryDirReq; import com.sdm.common.entity.req.data.UploadFilesReq; +import com.sdm.common.entity.req.project.EditReportReq; import com.sdm.common.entity.req.project.SpdmReportReq; import com.sdm.common.entity.req.system.LaunchApproveReq; import com.sdm.common.entity.resp.PageDataResp; @@ -220,6 +221,17 @@ public class SimulationRunController implements ISimulationRunFeignClient { runService.generateReport(req,response); } + /** + * 编辑报告模板生成报告 + * + * @return + */ + @PostMapping("/editReport") + @Operation(summary = "编辑报告模板生成报告", description = "编辑报告模板生成报告") + public void editReport(@RequestBody EditReportReq req, HttpServletResponse response) { + runService.editReport(req,response); + } + /** * 内部调用生成报告 * diff --git a/project/src/main/java/com/sdm/project/model/entity/SimulationRun.java b/project/src/main/java/com/sdm/project/model/entity/SimulationRun.java index 17b8cf0b..bb8398f7 100644 --- a/project/src/main/java/com/sdm/project/model/entity/SimulationRun.java +++ b/project/src/main/java/com/sdm/project/model/entity/SimulationRun.java @@ -124,4 +124,12 @@ public class SimulationRun implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime finishTime; + @Schema(description= "报告模板uuid") + @TableField("reportTemplate") + private String reportTemplate; + + @Schema(description= "报告模板填充内容") + @TableField("reportContent") + private String reportContent; + } diff --git a/project/src/main/java/com/sdm/project/model/req/SpdmAddTaskRunReq.java b/project/src/main/java/com/sdm/project/model/req/SpdmAddTaskRunReq.java index 9f9bb632..928fd101 100644 --- a/project/src/main/java/com/sdm/project/model/req/SpdmAddTaskRunReq.java +++ b/project/src/main/java/com/sdm/project/model/req/SpdmAddTaskRunReq.java @@ -32,4 +32,10 @@ public class SpdmAddTaskRunReq { private String isPersonalTemplate; + @Schema(description= "报告模板uuid") + private String reportTemplate; + + @Schema(description= "报告模板填充内容") + private String reportContent; + } diff --git a/project/src/main/java/com/sdm/project/service/ISimulationRunService.java b/project/src/main/java/com/sdm/project/service/ISimulationRunService.java index 6e2f7233..a6ba66b6 100644 --- a/project/src/main/java/com/sdm/project/service/ISimulationRunService.java +++ b/project/src/main/java/com/sdm/project/service/ISimulationRunService.java @@ -4,6 +4,7 @@ import com.sdm.common.common.SdmResponse; import com.sdm.common.entity.req.data.CreateDirReq; import com.sdm.common.entity.req.data.QueryDirReq; import com.sdm.common.entity.req.data.UploadFilesReq; +import com.sdm.common.entity.req.project.EditReportReq; import com.sdm.common.entity.req.project.SpdmReportReq; import com.sdm.common.entity.req.system.LaunchApproveReq; import com.sdm.common.entity.resp.PageDataResp; @@ -67,6 +68,9 @@ public interface ISimulationRunService extends IService { void generateReport(SpdmReportReq req, HttpServletResponse response); + void editReport(EditReportReq req, HttpServletResponse response); + + /** * 内部调用生成报告 * @param req 报告请求参数 diff --git a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java index 99a48745..b9ed22e0 100644 --- a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java +++ b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java @@ -19,6 +19,7 @@ import com.sdm.common.entity.flowable.dto.FlowElementDTO; import com.sdm.common.entity.flowable.dto.ProcessDefinitionDTO; import com.sdm.common.entity.req.capability.FlowNodeDto; import com.sdm.common.entity.req.data.*; +import com.sdm.common.entity.req.project.EditReportReq; import com.sdm.common.entity.req.project.ProjecInfoReq; import com.sdm.common.entity.req.project.SimulationPerformance; import com.sdm.common.entity.req.project.SpdmReportReq; @@ -26,6 +27,7 @@ import com.sdm.common.entity.req.system.LaunchApproveReq; import com.sdm.common.entity.req.system.UserQueryReq; import com.sdm.common.entity.resp.PageDataResp; import com.sdm.common.entity.resp.capability.FlowTemplateResp; +import com.sdm.common.entity.resp.capability.ReportTemplateResp; import com.sdm.common.entity.resp.data.BatchAddFileInfoResp; import com.sdm.common.entity.resp.data.FileMetadataInfoResp; import com.sdm.common.entity.resp.data.SimulationTaskResultCurveResp; @@ -33,6 +35,7 @@ import com.sdm.common.entity.resp.flowable.ProcessInstanceDetailResponse; import com.sdm.common.entity.resp.flowable.ProcessInstanceResp; import com.sdm.common.entity.resp.system.CIDUserResp; import com.sdm.common.feign.impl.capability.SimulationFlowFeignClientImpl; +import com.sdm.common.feign.impl.capability.SimulationReportFeignClientImpl; import com.sdm.common.feign.impl.data.DataAnalysisFeignClientImpl; import com.sdm.common.feign.impl.data.DataClientFeignClientImpl; import com.sdm.common.feign.impl.flowable.FlowableClientFeignClientImpl; @@ -76,6 +79,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -140,9 +145,14 @@ public class SimulationRunServiceImpl extends ServiceImpl reportResponse = reportFeignClient.queryReportTemplateInfo(req.getReportTemplate()); + if (reportResponse.isSuccess()) { + Long reportTemplateFileId = reportResponse.getData().getFileId(); + // 获取报告模板名称 + GetFileBaseInfoReq getFileBaseInfoReq = new GetFileBaseInfoReq(); + getFileBaseInfoReq.setFileId(reportTemplateFileId); + SdmResponse fileBaseInfoResp = dataFeignClient.getFileBaseInfo(getFileBaseInfoReq); + String originalName = fileBaseInfoResp.getData().getOriginalName(); + // 下载到本地临时目录 + dataFeignClient.downloadFileToLocal(reportTemplateFileId, TEMP_REPORT_PATH + randomId); + + // 构建python命令 + List command = new ArrayList<>(); + command.add("python"); + command.add("/opt/script/modifyReport.py"); +// command.add(TEMP_REPORT_PATH + File.separator +"modifyReport.py"); + command.add(TEMP_REPORT_PATH + randomId); + command.add(TEMP_REPORT_PATH + randomId + File.separator + originalName); + String commands = String.join(" ", command); + + // 前端参数写入临时目录 + FileOutputStream projectInfoOutputStream = null; + try { + projectInfoOutputStream = new FileOutputStream(TEMP_REPORT_PATH + randomId + File.separator + "reportContent.json"); + projectInfoOutputStream.write(reportContent.getBytes(StandardCharsets.UTF_8)); + projectInfoOutputStream.flush(); + projectInfoOutputStream.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // 调用脚本 + log.info("执行 Python 命令: {}", commands); + int runningStatus = -1; + try { + log.info("开始同步执行脚本"); + Process process = Runtime.getRuntime().exec(commands); + log.info("开始获取脚本输出"); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + log.info("executePython:" + line); + } + log.info("脚本执行完成"); + runningStatus = process.waitFor(); + log.info("脚本运行状态:" + runningStatus); + } catch (IOException | InterruptedException e) { + log.error("执行脚本失败:" + e); + return; + } + if (runningStatus != 0) { + log.error("执行脚本失败"); + return; + } else { + log.info(commands + "执行脚本完成!"); + } + byte[] fileData = null; + if (response != null) { + try { + // 获取临时路径中脚本生成的报告 + String reportName = "report_" + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + + ".docx"; + FileInputStream fileInputStream = new FileInputStream(TEMP_REPORT_PATH + randomId + File.separator + reportName); + fileData = fileInputStream.readAllBytes(); + // 设置响应头 + response.reset(); + response.setContentType("application/octet-stream;charset=UTF-8"); + response.addHeader("Content-Length", String.valueOf(fileData.length)); + // 写入响应流 + OutputStream outputStream = response.getOutputStream(); + outputStream.write(fileData); + outputStream.flush(); + outputStream.close(); + fileInputStream.close(); + } catch (Exception ex) { + log.error("生成自动化报告失败:{}", ex.getMessage()); + throw new RuntimeException("生成自动化报告失败"); + } + } + // 删除临时路径 + log.info("删除临时路径:{},中。。。。。。", randomId); + deleteFolder(new File(TEMP_REPORT_PATH + randomId)); + } + } + } + @Override public SdmResponse generateReportInternal(SpdmReportReq req) { log.info("内部调用生成自动化报告参数为:{}", req);