package com.ruoyi.stock.word;
|
|
import com.ruoyi.basic.pojo.Product;
|
import com.ruoyi.basic.service.IProductService;
|
import com.ruoyi.stock.dto.StockInRecordDto;
|
import org.apache.poi.xwpf.usermodel.*;
|
import org.apache.poi.xwpf.usermodel.XWPFTable.XWPFBorderType;
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Component;
|
|
import java.io.File;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.math.BigDecimal;
|
import java.math.BigInteger;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
|
/**
|
* 磅单Word文档生成器
|
*/
|
@Component
|
public class WeighbridgeDocGenerator {
|
|
@Value("${file.upload-dir}")
|
private String uploadDir;
|
|
@Autowired
|
private IProductService productService;
|
|
/**
|
* 生成磅单Word文档
|
* @param dto 入库记录DTO
|
* @return 文件保存的绝对路径,生成失败返回null
|
*/
|
public String generateWeighbridgeDoc(StockInRecordDto dto) {
|
// 参数校验
|
if (dto == null) {
|
return null;
|
}
|
|
FileOutputStream out = null;
|
XWPFDocument document = null;
|
|
try {
|
// 创建新文档
|
document = new XWPFDocument();
|
|
// 构建文档内容
|
buildDocumentContent(document, dto);
|
|
// 构建文件保存路径(绝对路径)
|
String absolutePath = buildAbsoluteFilePath(dto);
|
File file = new File(absolutePath);
|
|
// 确保目录存在
|
file.getParentFile().mkdirs();
|
|
// 写入文件
|
out = new FileOutputStream(file);
|
document.write(out);
|
out.flush();
|
|
// 返回绝对路径
|
return absolutePath;
|
|
} catch (Exception e) {
|
e.printStackTrace();
|
return null;
|
} finally {
|
// 关闭资源
|
try {
|
if (out != null) {
|
out.close();
|
}
|
if (document != null) {
|
document.close();
|
}
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
}
|
|
/**
|
* 构建文档内容
|
* @param document Word文档对象
|
* @param dto 入库记录DTO
|
*/
|
private void buildDocumentContent(XWPFDocument document, StockInRecordDto dto) {
|
|
// 设置纸张大小为A5横向
|
CTSectPr sectPr = document.getDocument().getBody().isSetSectPr()
|
? document.getDocument().getBody().getSectPr()
|
: document.getDocument().getBody().addNewSectPr();
|
|
CTPageSz pageSize = sectPr.isSetPgSz() ? sectPr.getPgSz() : sectPr.addNewPgSz();
|
pageSize.setW(BigInteger.valueOf(11906)); // A5横向宽度 210mm
|
pageSize.setH(BigInteger.valueOf(8392)); // A5横向高度 148mm
|
pageSize.setOrient(STPageOrientation.LANDSCAPE);
|
|
// 标题 "磅码单"
|
XWPFParagraph titlePara = document.createParagraph();
|
titlePara.setAlignment(ParagraphAlignment.CENTER);
|
titlePara.setSpacingAfter(200);
|
|
XWPFRun titleRun = titlePara.createRun();
|
titleRun.setText("磅码单");
|
titleRun.setBold(true);
|
titleRun.setFontSize(28);
|
titleRun.setFontFamily("黑体");
|
|
// 定义统一的页面宽度值(所有表格使用相同的总宽度)
|
int pageWidth = 9000; // 统一的页面宽度值
|
|
// 创建一行两列表格来放置日期和计量单位
|
XWPFTable headerTable = document.createTable(1, 2);
|
headerTable.setWidth("100%");
|
|
// 设置表格无边框
|
headerTable.setInsideHBorder(XWPFBorderType.NONE, 0, 0, "000000");
|
headerTable.setInsideVBorder(XWPFBorderType.NONE, 0, 0, "000000");
|
headerTable.setTopBorder(XWPFBorderType.NONE, 0, 0, "000000");
|
headerTable.setBottomBorder(XWPFBorderType.NONE, 0, 0, "000000");
|
headerTable.setLeftBorder(XWPFBorderType.NONE, 0, 0, "000000");
|
headerTable.setRightBorder(XWPFBorderType.NONE, 0, 0, "000000");
|
|
// 设置表头表格列宽:日期列50%,计量单位列50%
|
CTTbl headerTbl = headerTable.getCTTbl();
|
CTTblGrid headerTblGrid = headerTbl.addNewTblGrid();
|
|
// 使用统一的页面宽度
|
int headerColWidth = pageWidth / 2; // 4500
|
|
CTTblGridCol col1 = headerTblGrid.addNewGridCol();
|
col1.setW(BigInteger.valueOf(headerColWidth));
|
|
CTTblGridCol col2 = headerTblGrid.addNewGridCol();
|
col2.setW(BigInteger.valueOf(headerColWidth));
|
|
// 设置行高
|
headerTable.getRow(0).setHeight(300);
|
|
// 为表头表格的每个单元格设置宽度
|
for (int colIndex = 0; colIndex < 2; colIndex++) {
|
XWPFTableCell cell = headerTable.getRow(0).getCell(colIndex);
|
CTTcPr tcPr = cell.getCTTc().addNewTcPr();
|
CTTblWidth cellWidth = tcPr.addNewTcW();
|
cellWidth.setType(STTblWidth.DXA);
|
cellWidth.setW(BigInteger.valueOf(headerColWidth));
|
}
|
|
// 左侧单元格:日期(左对齐)
|
XWPFTableCell dateCell = headerTable.getRow(0).getCell(0);
|
dateCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
|
|
// 清除单元格中可能存在的默认段落
|
for (int i = dateCell.getParagraphs().size() - 1; i > 0; i--) {
|
dateCell.removeParagraph(i);
|
}
|
|
XWPFParagraph datePara = dateCell.getParagraphs().get(0);
|
datePara.setAlignment(ParagraphAlignment.LEFT);
|
|
// 清除段落中可能存在的默认Run
|
for (int i = datePara.getRuns().size() - 1; i > 0; i--) {
|
datePara.removeRun(i);
|
}
|
|
XWPFRun dateRun = datePara.createRun();
|
|
// 格式化日期
|
String weighDate = "";
|
if (dto.getWeighingDate() != null) {
|
try {
|
weighDate = dto.getWeighingDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
} catch (Exception e) {
|
weighDate = dto.getWeighingDate().toString();
|
if (weighDate.length() > 10) {
|
weighDate = weighDate.substring(0, 10);
|
}
|
}
|
}
|
|
dateRun.setText("日 期:" + weighDate);
|
dateRun.setFontSize(12);
|
dateRun.setFontFamily("宋体");
|
|
// 计量单位(右对齐)
|
XWPFTableCell unitCell = headerTable.getRow(0).getCell(1);
|
unitCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
|
|
for (int i = unitCell.getParagraphs().size() - 1; i > 0; i--) {
|
unitCell.removeParagraph(i);
|
}
|
|
XWPFParagraph unitPara = unitCell.getParagraphs().get(0);
|
unitPara.setAlignment(ParagraphAlignment.RIGHT);
|
|
for (int i = unitPara.getRuns().size() - 1; i > 0; i--) {
|
unitPara.removeRun(i);
|
}
|
|
XWPFRun unitRun = unitPara.createRun();
|
unitRun.setText("计量单位:(吨)");
|
unitRun.setFontSize(12);
|
unitRun.setFontFamily("宋体");
|
|
// 创建主表格 - 3行6列
|
XWPFTable table = document.createTable(3, 6);
|
|
// 设置表格宽度
|
table.setWidth("100%");
|
|
// 设置表格边框加粗
|
table.setInsideHBorder(XWPFBorderType.SINGLE, 2, 0, "000000");
|
table.setInsideVBorder(XWPFBorderType.SINGLE, 2, 0, "000000");
|
table.setTopBorder(XWPFBorderType.SINGLE, 2, 0, "000000");
|
table.setBottomBorder(XWPFBorderType.SINGLE, 2, 0, "000000");
|
table.setLeftBorder(XWPFBorderType.SINGLE, 2, 0, "000000");
|
table.setRightBorder(XWPFBorderType.SINGLE, 2, 0, "000000");
|
|
// 设置行高
|
table.getRow(0).setHeight(400);
|
table.getRow(1).setHeight(400);
|
table.getRow(2).setHeight(400);
|
|
// 设置主表格列宽网格 - 使用相同的总宽度 pageWidth
|
CTTbl mainTbl = table.getCTTbl();
|
CTTblGrid mainTblGrid = mainTbl.addNewTblGrid();
|
|
// 列宽比例
|
int[] colWidths = {
|
(int)(pageWidth * 0.20),
|
(int)(pageWidth * 0.24),
|
(int)(pageWidth * 0.14),
|
(int)(pageWidth * 0.14),
|
(int)(pageWidth * 0.14),
|
(int)(pageWidth * 0.14)
|
};
|
|
// 确保总和等于pageWidth
|
int sum = 0;
|
for (int width : colWidths) {
|
sum += width;
|
}
|
// 如果有误差,调整最后一列
|
if (sum != pageWidth) {
|
colWidths[5] += (pageWidth - sum);
|
}
|
|
for (int width : colWidths) {
|
CTTblGridCol col = mainTblGrid.addNewGridCol();
|
col.setW(BigInteger.valueOf(width));
|
}
|
|
// 为每一行的每个单元格设置具体的宽度
|
for (int rowIndex = 0; rowIndex < 3; rowIndex++) {
|
XWPFTableRow row = table.getRow(rowIndex);
|
for (int colIndex = 0; colIndex < 6; colIndex++) {
|
XWPFTableCell cell = row.getCell(colIndex);
|
CTTcPr tcPr = cell.getCTTc().addNewTcPr();
|
CTTblWidth cellWidth = tcPr.addNewTcW();
|
cellWidth.setType(STTblWidth.DXA);
|
cellWidth.setW(BigInteger.valueOf(colWidths[colIndex]));
|
}
|
}
|
|
// 设置表头 - 使用黑体加粗
|
String[] headers = {"车号", "品名", "毛重", "皮重", "净重", "备注"};
|
XWPFTableRow headerRow = table.getRow(0);
|
for (int i = 0; i < headers.length; i++) {
|
XWPFTableCell cell = headerRow.getCell(i);
|
cell.setText(headers[i]);
|
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
|
|
// 清除可能存在的多余段落
|
while (cell.getParagraphs().size() > 1) {
|
cell.removeParagraph(1);
|
}
|
|
XWPFParagraph cellPara = cell.getParagraphs().get(0);
|
cellPara.setAlignment(ParagraphAlignment.CENTER);
|
|
while (cellPara.getRuns().size() > 1) {
|
cellPara.removeRun(1);
|
}
|
|
XWPFRun cellRun = cellPara.getRuns().get(0);
|
cellRun.setBold(true);
|
cellRun.setFontSize(12);
|
cellRun.setFontFamily("黑体");
|
}
|
|
// 设置数据行 - 使用宋体
|
XWPFTableRow dataRow = table.getRow(1);
|
String netWeight = dto.getNetWeight() != null ? dto.getNetWeight().toString() : "";
|
String grossWeight = dto.getGrossWeight() != null ? dto.getGrossWeight().toString() : "";
|
String tareWeight = dto.getTareWeight() != null ? dto.getTareWeight().toString() : "";
|
|
String[] data = {
|
dto.getLicensePlateNo() != null ? dto.getLicensePlateNo() : "",
|
getProductModelName(dto.getProductId()),
|
grossWeight + "t",
|
tareWeight + "t",
|
netWeight + "t",
|
dto.getRemark() != null ? dto.getRemark() : ""
|
};
|
|
for (int i = 0; i < data.length; i++) {
|
XWPFTableCell cell = dataRow.getCell(i);
|
cell.setText(data[i]);
|
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
|
|
while (cell.getParagraphs().size() > 1) {
|
cell.removeParagraph(1);
|
}
|
|
XWPFParagraph cellPara = cell.getParagraphs().get(0);
|
cellPara.setAlignment(ParagraphAlignment.CENTER);
|
|
while (cellPara.getRuns().size() > 1) {
|
cellPara.removeRun(1);
|
}
|
|
XWPFRun cellRun = cellPara.getRuns().get(0);
|
cellRun.setFontSize(12);
|
cellRun.setFontFamily("宋体");
|
}
|
|
// 合计行 - 第3行
|
XWPFTableRow totalRow = table.getRow(2);
|
|
// 第1列显示"合计(大写)"
|
XWPFTableCell totalLabelCell = totalRow.getCell(0);
|
totalLabelCell.setText("合计(大写)");
|
totalLabelCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
|
|
// 清除可能存在的多余段落
|
while (totalLabelCell.getParagraphs().size() > 1) {
|
totalLabelCell.removeParagraph(1);
|
}
|
|
XWPFParagraph totalLabelPara = totalLabelCell.getParagraphs().get(0);
|
totalLabelPara.setAlignment(ParagraphAlignment.CENTER);
|
|
// 清除可能存在的多余Run
|
while (totalLabelPara.getRuns().size() > 1) {
|
totalLabelPara.removeRun(1);
|
}
|
|
XWPFRun totalLabelRun = totalLabelPara.getRuns().get(0);
|
totalLabelRun.setBold(true);
|
totalLabelRun.setFontSize(12);
|
totalLabelRun.setFontFamily("黑体");
|
|
// 将净重转换为大写
|
String netWeightChinese = ChineseNumberUtil.numberToChinese(
|
dto.getNetWeight() != null ? dto.getNetWeight() : BigDecimal.ZERO
|
);
|
|
XWPFTableCell startCell = totalRow.getCell(1);
|
startCell.setText(netWeightChinese + "吨");
|
startCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
|
|
while (startCell.getParagraphs().size() > 1) {
|
startCell.removeParagraph(1);
|
}
|
|
XWPFParagraph startPara = startCell.getParagraphs().get(0);
|
startPara.setAlignment(ParagraphAlignment.CENTER);
|
|
while (startPara.getRuns().size() > 1) {
|
startPara.removeRun(1);
|
}
|
|
XWPFRun startRun = startPara.getRuns().get(0);
|
startRun.setFontSize(12);
|
startRun.setFontFamily("宋体");
|
|
// 标记第2列为合并起始
|
startCell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
|
|
// 第3-6列合并
|
for (int i = 2; i < 6; i++) {
|
XWPFTableCell cell = totalRow.getCell(i);
|
cell.setText("");
|
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
|
}
|
|
// 创建过磅员行(单独一行,左对齐)
|
XWPFParagraph operatorPara = document.createParagraph();
|
operatorPara.setSpacingAfter(100);
|
operatorPara.setAlignment(ParagraphAlignment.LEFT);
|
XWPFRun operatorRun = operatorPara.createRun();
|
String operator = dto.getWeighingOperator() != null ? dto.getWeighingOperator() : "";
|
operatorRun.setText("过磅员:" + operator);
|
operatorRun.setFontSize(12);
|
operatorRun.setFontFamily("宋体");
|
}
|
|
/**
|
* 构建文件保存的绝对路径
|
*/
|
private String buildAbsoluteFilePath(StockInRecordDto dto) {
|
LocalDateTime now = LocalDateTime.now();
|
String year = String.valueOf(now.getYear());
|
String month = String.format("%02d", now.getMonthValue());
|
String day = String.format("%02d", now.getDayOfMonth());
|
|
// 文件名格式:磅单_车牌号_年月日.docx
|
String fileName = String.format("磅单_%s_%s.docx",
|
dto.getLicensePlateNo() != null ? dto.getLicensePlateNo() : "未知车牌",
|
now.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
|
return uploadDir + File.separator + year + File.separator + month + File.separator + day + File.separator + fileName;
|
}
|
|
/**
|
* 获取产品型号名称
|
*/
|
private String getProductModelName(Long productModelId) {
|
if (productModelId == null) {
|
return "未知产品";
|
}
|
try {
|
Product product = productService.getById(productModelId);
|
return product != null ? product.getProductName() : "未知产品";
|
} catch (Exception e) {
|
e.printStackTrace();
|
return "未知产品";
|
}
|
}
|
}
|