13 changed files with 697 additions and 62 deletions
@ -0,0 +1,84 @@
|
||||
package com.mh.web.controller.ai; |
||||
|
||||
import com.mh.common.config.MHConfig; |
||||
import com.mh.common.core.controller.BaseController; |
||||
import org.springframework.core.io.FileSystemResource; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import java.io.File; |
||||
import java.net.URLEncoder; |
||||
import java.nio.charset.StandardCharsets; |
||||
|
||||
/** |
||||
* AI文件下载控制器 |
||||
* 处理AI生成的报表文件下载请求 |
||||
* |
||||
* @author mh |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/ai/files") |
||||
public class AiFileController extends BaseController { |
||||
|
||||
/** |
||||
* 下载AI生成的文件 |
||||
* |
||||
* @param filename 文件名 |
||||
* @return 文件响应 |
||||
*/ |
||||
@GetMapping("/{filename:.+}") |
||||
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) { |
||||
return getResourceResponseEntity(filename); |
||||
} |
||||
|
||||
private ResponseEntity<Resource> getResourceResponseEntity(@PathVariable String filename) { |
||||
try { |
||||
// 构建文件路径
|
||||
String storageDir = MHConfig.getProfile(); |
||||
File file = new File(storageDir, filename); |
||||
|
||||
// 检查文件是否存在
|
||||
if (!file.exists()) { |
||||
return ResponseEntity.notFound().build(); |
||||
} |
||||
|
||||
// 创建资源
|
||||
Resource resource = new FileSystemResource(file); |
||||
|
||||
// 使用RFC 5987标准格式,支持UTF-8编码
|
||||
String encodedFileName = URLEncoder.encode(filename, StandardCharsets.UTF_8); |
||||
String disposition = String.format("attachment; filename*=UTF-8''%s", encodedFileName); |
||||
|
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION, disposition); |
||||
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION); |
||||
|
||||
return ResponseEntity.ok() |
||||
.headers(headers) |
||||
.contentLength(file.length()) |
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM) |
||||
.body(resource); |
||||
|
||||
} catch (Exception e) { |
||||
return ResponseEntity.internalServerError().build(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 设备分析导出 |
||||
* |
||||
* @param filename 文件名 |
||||
* @return 文件响应 |
||||
*/ |
||||
@PostMapping("/{filename:.+}") |
||||
public ResponseEntity<Resource> deviceAnalyzeExport(@PathVariable String filename) { |
||||
return getResourceResponseEntity(filename); |
||||
} |
||||
} |
||||
@ -1,39 +0,0 @@
|
||||
package com.mh.web.controller.ai; |
||||
|
||||
import org.springframework.beans.factory.annotation.Value; |
||||
import org.springframework.core.io.Resource; |
||||
import org.springframework.core.io.UrlResource; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
||||
import java.nio.file.Path; |
||||
import java.nio.file.Paths; |
||||
|
||||
/** |
||||
* @author LJF |
||||
* @version 1.0 |
||||
* @project EEMCS |
||||
* @description 文件下载链接 |
||||
* @date 2026-02-27 11:40:07 |
||||
*/ |
||||
@RequestMapping("/api/files") |
||||
public class FileDownloadController { |
||||
@Value("${mh.profile:/tmp/energy-reports}") |
||||
private String storageDir; |
||||
|
||||
@GetMapping("/{filename}") |
||||
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) throws Exception { |
||||
Path filePath = Paths.get(storageDir).resolve(filename); |
||||
Resource resource = new UrlResource(filePath.toUri()); |
||||
if (resource.exists()) { |
||||
return ResponseEntity.ok() |
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"") |
||||
.body(resource); |
||||
} |
||||
return ResponseEntity.notFound().build(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,22 @@
|
||||
package com.mh.common.core.domain.dto; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
/** |
||||
* @author LJF |
||||
* @version 1.0 |
||||
* @project EEMCS |
||||
* @description 异步任务事件dto |
||||
* @date 2026-02-28 |
||||
*/ |
||||
@Data |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class AsyncTaskEvent { |
||||
|
||||
private String event; // 事件类型:reasoning, COMPLETED, FAILED
|
||||
private Object data; // 事件数据
|
||||
|
||||
} |
||||
@ -0,0 +1,471 @@
|
||||
package com.mh.system.service.ai.impl; |
||||
|
||||
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; |
||||
import org.apache.poi.xwpf.usermodel.*; |
||||
import com.mh.common.core.domain.dto.EnergyReportDTO; |
||||
import com.mh.common.core.domain.dto.ReportRequestDTO; |
||||
import com.mh.system.service.ai.IFileGenerationService; |
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.math.BigInteger; |
||||
|
||||
/** |
||||
* @author MH |
||||
* @version 2.0 |
||||
* @project EEMCS |
||||
* @description 高级Word文档生成服务 - 支持表格和水印 |
||||
* @date 2026-02-28 |
||||
*/ |
||||
@Service |
||||
public class AdvancedWordGenerationServiceImpl implements IFileGenerationService { |
||||
|
||||
@Override |
||||
public byte[] generateReport(EnergyReportDTO report, ReportRequestDTO request) throws IOException { |
||||
try (XWPFDocument document = new XWPFDocument()) { |
||||
// 添加专业水印
|
||||
if (request.getWatermark()) { |
||||
addProfessionalWatermark(document, request.getWatermarkText()); |
||||
} |
||||
|
||||
// 创建专业的文档结构
|
||||
createProfessionalDocument(document, report); |
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
||||
document.write(baos); |
||||
return baos.toByteArray(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 创建专业的文档结构 |
||||
*/ |
||||
private void createProfessionalDocument(XWPFDocument document, EnergyReportDTO report) { |
||||
// 添加封面页
|
||||
addCoverPage(document, report); |
||||
|
||||
// 添加目录(可选)
|
||||
addTableOfContents(document); |
||||
|
||||
// 添加正文内容
|
||||
addMainContent(document, report); |
||||
} |
||||
|
||||
/** |
||||
* 添加封面页 |
||||
*/ |
||||
private void addCoverPage(XWPFDocument document, EnergyReportDTO report) { |
||||
// 标题
|
||||
XWPFParagraph titlePara = document.createParagraph(); |
||||
titlePara.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun titleRun = titlePara.createRun(); |
||||
titleRun.setText("中央空调能效分析报告"); |
||||
titleRun.setBold(true); |
||||
titleRun.setFontSize(28); |
||||
titleRun.addBreak(); |
||||
|
||||
// 副标题
|
||||
XWPFParagraph subtitlePara = document.createParagraph(); |
||||
subtitlePara.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun subtitleRun = subtitlePara.createRun(); |
||||
subtitleRun.setText("Central Air Conditioning Energy Efficiency Analysis Report"); |
||||
subtitleRun.setFontSize(16); |
||||
subtitleRun.setColor("666666"); |
||||
subtitleRun.addBreak(); |
||||
subtitleRun.addBreak(); |
||||
|
||||
// 报告日期
|
||||
XWPFParagraph datePara = document.createParagraph(); |
||||
datePara.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun dateRun = datePara.createRun(); |
||||
dateRun.setText("报告日期: " + report.getReportDate()); |
||||
dateRun.setFontSize(14); |
||||
dateRun.addBreak(); |
||||
dateRun.addBreak(); |
||||
dateRun.addBreak(); |
||||
|
||||
// 添加分页
|
||||
document.createParagraph().createRun().addBreak(BreakType.PAGE); |
||||
} |
||||
|
||||
/** |
||||
* 添加目录 |
||||
*/ |
||||
private void addTableOfContents(XWPFDocument document) { |
||||
XWPFParagraph tocTitle = document.createParagraph(); |
||||
tocTitle.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun tocRun = tocTitle.createRun(); |
||||
tocRun.setText("目 录"); |
||||
tocRun.setBold(true); |
||||
tocRun.setFontSize(18); |
||||
tocRun.addBreak(); |
||||
|
||||
// 目录项
|
||||
addTocItem(document, "1. 基本信息", 1); |
||||
addTocItem(document, "2. 设备能效明细", 1); |
||||
addTocItem(document, "3. 异常情况分析", 1); |
||||
addTocItem(document, "4. 优化建议", 1); |
||||
|
||||
document.createParagraph().createRun().addBreak(BreakType.PAGE); |
||||
} |
||||
|
||||
private void addTocItem(XWPFDocument document, String text, int level) { |
||||
XWPFParagraph para = document.createParagraph(); |
||||
if (level > 1) { |
||||
para.setIndentationLeft(400); |
||||
} |
||||
XWPFRun run = para.createRun(); |
||||
run.setText(text); |
||||
run.setFontSize(12); |
||||
} |
||||
|
||||
/** |
||||
* 添加主要内容 |
||||
*/ |
||||
private void addMainContent(XWPFDocument document, EnergyReportDTO report) { |
||||
// 第一章:基本信息
|
||||
addChapter(document, "第一章 基本信息", report); |
||||
|
||||
// 第二章:设备能效明细(包含表格)
|
||||
addDeviceDetailsChapter(document, report); |
||||
|
||||
// 第三章:异常情况分析
|
||||
addAnomaliesChapter(document, report); |
||||
|
||||
// 第四章:优化建议
|
||||
addSuggestionsChapter(document, report); |
||||
} |
||||
|
||||
/** |
||||
* 添加章节标题 |
||||
*/ |
||||
private void addChapter(XWPFDocument document, String title, EnergyReportDTO report) { |
||||
XWPFParagraph chapterPara = document.createParagraph(); |
||||
chapterPara.setStyle("Heading1"); |
||||
XWPFRun chapterRun = chapterPara.createRun(); |
||||
chapterRun.setText(title); |
||||
chapterRun.setBold(true); |
||||
chapterRun.setFontSize(16); |
||||
chapterRun.addBreak(); |
||||
|
||||
// 添加基本信息内容
|
||||
addBasicInfoContent(document, report); |
||||
document.createParagraph().createRun().addBreak(); |
||||
} |
||||
|
||||
/** |
||||
* 添加基本信息内容 |
||||
*/ |
||||
private void addBasicInfoContent(XWPFDocument document, EnergyReportDTO report) { |
||||
String[] infoItems = { |
||||
"报表日期: " + report.getReportDate(), |
||||
"整体能效评分: " + report.getOverallScore(), |
||||
"系统综合能效比(EER): " + report.getSystemEER(), |
||||
"总能耗: " + formatDouble(report.getTotalEnergyConsumption()) + " kWh" |
||||
}; |
||||
|
||||
for (String item : infoItems) { |
||||
XWPFParagraph para = document.createParagraph(); |
||||
para.setIndentationLeft(400); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText("• " + item); |
||||
run.setFontSize(12); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 添加设备明细章节(包含专业表格) |
||||
*/ |
||||
private void addDeviceDetailsChapter(XWPFDocument document, EnergyReportDTO report) { |
||||
XWPFParagraph chapterPara = document.createParagraph(); |
||||
chapterPara.setStyle("Heading1"); |
||||
XWPFRun chapterRun = chapterPara.createRun(); |
||||
chapterRun.setText("第二章 设备能效明细"); |
||||
chapterRun.setBold(true); |
||||
chapterRun.setFontSize(16); |
||||
chapterRun.addBreak(); |
||||
|
||||
EnergyReportDTO.DeviceEnergy[] devices = report.getDeviceDetails().toArray(new EnergyReportDTO.DeviceEnergy[0]); |
||||
if (devices != null && devices.length > 0) { |
||||
createDeviceDetailsTable(document, devices); |
||||
} else { |
||||
XWPFParagraph para = document.createParagraph(); |
||||
para.setIndentationLeft(400); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText("暂无设备数据"); |
||||
run.setFontSize(12); |
||||
} |
||||
document.createParagraph().createRun().addBreak(); |
||||
} |
||||
|
||||
/** |
||||
* 创建专业的设备明细表格 |
||||
*/ |
||||
private void createDeviceDetailsTable(XWPFDocument document, EnergyReportDTO.DeviceEnergy[] devices) { |
||||
// 创建表格 (行数 = 表头1行 + 数据行数)
|
||||
int rows = devices.length + 1; |
||||
int cols = 5; |
||||
XWPFTable table = document.createTable(rows, cols); |
||||
|
||||
// 设置表格样式
|
||||
setTableStyle(table); |
||||
|
||||
// 设置表头
|
||||
String[] headers = {"设备名称", "设备类型", "能耗(kWh)", "效率(%)", "偏差率(%)"}; |
||||
XWPFTableRow headerRow = table.getRow(0); |
||||
for (int i = 0; i < headers.length; i++) { |
||||
XWPFTableCell cell = headerRow.getCell(i); |
||||
cell.setColor("3366FF"); |
||||
XWPFParagraph para = cell.getParagraphs().get(0); |
||||
para.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText(headers[i]); |
||||
run.setBold(true); |
||||
run.setColor("FFFFFF"); |
||||
run.setFontSize(12); |
||||
} |
||||
|
||||
// 填充数据
|
||||
for (int i = 0; i < devices.length; i++) { |
||||
EnergyReportDTO.DeviceEnergy device = devices[i]; |
||||
XWPFTableRow row = table.getRow(i + 1); |
||||
|
||||
String[] values = { |
||||
device.getDeviceName(), |
||||
device.getDeviceType(), |
||||
formatDouble(device.getEnergyConsumption()), |
||||
formatDouble(device.getEfficiency()), |
||||
formatDouble(device.getDeviationRate()) |
||||
}; |
||||
|
||||
for (int j = 0; j < values.length; j++) { |
||||
XWPFTableCell cell = row.getCell(j); |
||||
XWPFParagraph para = cell.getParagraphs().get(0); |
||||
para.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText(values[j]); |
||||
run.setFontSize(11); |
||||
|
||||
// 根据偏差率设置背景色
|
||||
if (j == 4) { // 偏差率列
|
||||
double deviation = device.getDeviationRate() != null ? device.getDeviationRate() : 0; |
||||
if (deviation > 10) { |
||||
cell.setColor("FFCCCC"); // 红色背景表示高偏差
|
||||
} else if (deviation > 5) { |
||||
cell.setColor("FFE5CC"); // 橙色背景表示中等偏差
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 添加异常情况分析章节 |
||||
*/ |
||||
private void addAnomaliesChapter(XWPFDocument document, EnergyReportDTO report) { |
||||
XWPFParagraph chapterPara = document.createParagraph(); |
||||
chapterPara.setStyle("Heading1"); |
||||
XWPFRun chapterRun = chapterPara.createRun(); |
||||
chapterRun.setText("第三章 异常情况分析"); |
||||
chapterRun.setBold(true); |
||||
chapterRun.setFontSize(16); |
||||
chapterRun.addBreak(); |
||||
|
||||
EnergyReportDTO.Anomaly[] anomalies = report.getAnomalies().toArray(new EnergyReportDTO.Anomaly[0]); |
||||
if (anomalies != null && anomalies.length > 0) { |
||||
createAnomaliesTable(document, anomalies); |
||||
} else { |
||||
XWPFParagraph para = document.createParagraph(); |
||||
para.setIndentationLeft(400); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText("系统运行正常,无异常情况"); |
||||
run.setFontSize(12); |
||||
} |
||||
document.createParagraph().createRun().addBreak(); |
||||
} |
||||
|
||||
/** |
||||
* 创建异常情况表格 |
||||
*/ |
||||
private void createAnomaliesTable(XWPFDocument document, EnergyReportDTO.Anomaly[] anomalies) { |
||||
int rows = anomalies.length + 1; |
||||
int cols = 5; |
||||
XWPFTable table = document.createTable(rows, cols); |
||||
setTableStyle(table); |
||||
|
||||
// 表头
|
||||
String[] headers = {"设备名称", "异常类型", "异常描述", "严重程度", "预估损失"}; |
||||
XWPFTableRow headerRow = table.getRow(0); |
||||
for (int i = 0; i < headers.length; i++) { |
||||
XWPFTableCell cell = headerRow.getCell(i); |
||||
cell.setColor("FF6666"); |
||||
XWPFParagraph para = cell.getParagraphs().get(0); |
||||
para.setAlignment(ParagraphAlignment.CENTER); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText(headers[i]); |
||||
run.setBold(true); |
||||
run.setColor("FFFFFF"); |
||||
run.setFontSize(12); |
||||
} |
||||
|
||||
// 数据行
|
||||
for (int i = 0; i < anomalies.length; i++) { |
||||
EnergyReportDTO.Anomaly anomaly = anomalies[i]; |
||||
XWPFTableRow row = table.getRow(i + 1); |
||||
|
||||
String[] values = { |
||||
anomaly.getDeviceName(), |
||||
anomaly.getAnomalyType(), |
||||
anomaly.getDescription(), |
||||
anomaly.getSeverity(), |
||||
formatDouble(anomaly.getEstimatedLoss()) |
||||
}; |
||||
|
||||
for (int j = 0; j < values.length; j++) { |
||||
XWPFTableCell cell = row.getCell(j); |
||||
XWPFParagraph para = cell.getParagraphs().get(0); |
||||
para.setAlignment(ParagraphAlignment.LEFT); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText(values[j]); |
||||
run.setFontSize(11); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 添加优化建议章节 |
||||
*/ |
||||
private void addSuggestionsChapter(XWPFDocument document, EnergyReportDTO report) { |
||||
XWPFParagraph chapterPara = document.createParagraph(); |
||||
chapterPara.setStyle("Heading1"); |
||||
XWPFRun chapterRun = chapterPara.createRun(); |
||||
chapterRun.setText("第四章 优化建议"); |
||||
chapterRun.setBold(true); |
||||
chapterRun.setFontSize(16); |
||||
chapterRun.addBreak(); |
||||
|
||||
EnergyReportDTO.Suggestion[] suggestions = report.getSuggestions().toArray(new EnergyReportDTO.Suggestion[0]); |
||||
if (suggestions != null && suggestions.length > 0) { |
||||
for (int i = 0; i < suggestions.length; i++) { |
||||
addSuggestionItem(document, suggestions[i], i + 1); |
||||
} |
||||
} else { |
||||
XWPFParagraph para = document.createParagraph(); |
||||
para.setIndentationLeft(400); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText("暂无优化建议"); |
||||
run.setFontSize(12); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 添加建议项 |
||||
*/ |
||||
private void addSuggestionItem(XWPFDocument document, EnergyReportDTO.Suggestion suggestion, int index) { |
||||
// 建议标题
|
||||
XWPFParagraph titlePara = document.createParagraph(); |
||||
titlePara.setIndentationLeft(400); |
||||
XWPFRun titleRun = titlePara.createRun(); |
||||
titleRun.setText(index + ". " + suggestion.getTitle()); |
||||
titleRun.setBold(true); |
||||
titleRun.setFontSize(13); |
||||
titleRun.addBreak(); |
||||
|
||||
// 建议详情
|
||||
String[] details = { |
||||
"优先级: " + suggestion.getPriority(), |
||||
"预期节能: " + formatDouble(suggestion.getExpectedSaving()) + "%", |
||||
"建议操作: " + suggestion.getAction(), |
||||
"详细说明: " + suggestion.getDescription() |
||||
}; |
||||
|
||||
for (String detail : details) { |
||||
XWPFParagraph para = document.createParagraph(); |
||||
para.setIndentationLeft(800); |
||||
XWPFRun run = para.createRun(); |
||||
run.setText("• " + detail); |
||||
run.setFontSize(11); |
||||
} |
||||
|
||||
document.createParagraph().createRun().addBreak(); |
||||
} |
||||
|
||||
/** |
||||
* 添加专业水印 |
||||
*/ |
||||
private void addProfessionalWatermark(XWPFDocument document, String watermarkText) { |
||||
try { |
||||
// 使用POI的水印API(适用于较新版本)
|
||||
// 注意:POI 4.1.2对水印支持有限,这里提供兼容性实现
|
||||
|
||||
// 方法1:在页眉中添加水印文本
|
||||
addWatermarkToHeader(document, watermarkText); |
||||
|
||||
} catch (Exception e) { |
||||
System.err.println("添加水印时出错: " + e.getMessage()); |
||||
// 水印失败不影响文档生成
|
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 在页眉中添加水印文本 |
||||
*/ |
||||
private void addWatermarkToHeader(XWPFDocument document, String watermarkText) { |
||||
try { |
||||
// 获取或创建页眉
|
||||
XWPFHeaderFooterPolicy policy = document.getHeaderFooterPolicy(); |
||||
if (policy == null) { |
||||
policy = document.createHeaderFooterPolicy(); |
||||
} |
||||
|
||||
// 创建页眉
|
||||
XWPFHeader header = policy.getDefaultHeader(); |
||||
if (header == null) { |
||||
header = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT); |
||||
} |
||||
|
||||
// 添加水印段落
|
||||
XWPFParagraph watermarkPara = header.createParagraph(); |
||||
watermarkPara.setAlignment(ParagraphAlignment.CENTER); |
||||
|
||||
XWPFRun watermarkRun = watermarkPara.createRun(); |
||||
watermarkRun.setText(watermarkText); |
||||
watermarkRun.setFontSize(48); |
||||
watermarkRun.setColor("D3D3D3"); |
||||
watermarkRun.setBold(true); |
||||
|
||||
} catch (Exception e) { |
||||
System.err.println("添加页眉水印失败: " + e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 设置表格样式 |
||||
*/ |
||||
private void setTableStyle(XWPFTable table) { |
||||
// 设置表格边框
|
||||
table.setTableAlignment(TableRowAlign.CENTER); |
||||
|
||||
// 设置表格宽度
|
||||
table.setWidth("80%"); |
||||
|
||||
// 设置行高
|
||||
for (XWPFTableRow row : table.getRows()) { |
||||
row.setHeight(300); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 格式化双精度数字 |
||||
*/ |
||||
private String formatDouble(Double value) { |
||||
return value != null ? String.format("%.2f", value) : "-"; |
||||
} |
||||
|
||||
@Override |
||||
public String getFileExtension() { |
||||
return ".docx"; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue