|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
package org.dromara.common.word.util;
|
|
|
|
|
|
|
|
|
|
import com.deepoove.poi.XWPFTemplate;
|
|
|
|
|
import com.deepoove.poi.config.Configure;
|
|
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
|
import lombok.AccessLevel;
|
|
|
|
|
import lombok.NoArgsConstructor;
|
|
|
|
|
@ -105,6 +106,20 @@ public final class WordTemplateUtil {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建并渲染模板(支持自定义 Configure 配置)
|
|
|
|
|
*
|
|
|
|
|
* @param templatePath 模板路径(classpath)
|
|
|
|
|
* @param data 模板数据 Map
|
|
|
|
|
* @param config poi-tl 配置对象,可包含自定义 RenderPolicy
|
|
|
|
|
* @return 已完成占位符渲染的 XWPFTemplate 对象
|
|
|
|
|
*/
|
|
|
|
|
private static XWPFTemplate buildTemplate(String templatePath, Map<String, Object> data, Configure config) throws IOException {
|
|
|
|
|
try (InputStream templateStream = getTemplateStream(templatePath)) {
|
|
|
|
|
return XWPFTemplate.compile(templateStream, config).render(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 重置响应头,返回输出流
|
|
|
|
|
*
|
|
|
|
|
@ -122,6 +137,42 @@ public final class WordTemplateUtil {
|
|
|
|
|
return response.getOutputStream();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 渲染模板并输出到 HttpServletResponse(支持自定义 Configure 配置)
|
|
|
|
|
* <p>
|
|
|
|
|
* 适用于需要自定义渲染策略的场景,例如使用 DynamicTableRenderPolicy 动态生成表格。
|
|
|
|
|
*
|
|
|
|
|
* @param templatePath classpath 下的模板路径(含文件名),如 "templates/wms_shipping_bill.docx"
|
|
|
|
|
* @param fileName 下载文件名(不含后缀),如 "发货单_X123",方法内部自动补全 .docx 后缀
|
|
|
|
|
* @param data 模板数据 Map,业务自行约定占位符字段
|
|
|
|
|
* @param config poi-tl 配置对象,可包含自定义 RenderPolicy(如 DynamicTableRenderPolicy)
|
|
|
|
|
* @param response HttpServletResponse,由 Spring MVC 自动注入
|
|
|
|
|
*/
|
|
|
|
|
public static void renderToResponse(String templatePath, String fileName, Map<String, Object> data,
|
|
|
|
|
Configure config, HttpServletResponse response) {
|
|
|
|
|
Objects.requireNonNull(response, "HttpServletResponse must not be null");
|
|
|
|
|
Objects.requireNonNull(config, "Configure must not be null");
|
|
|
|
|
if (StringUtils.isBlank(templatePath)) {
|
|
|
|
|
throw new ServiceException("Word 模板路径不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String safeFileName = StringUtils.isBlank(fileName) ? "export" : fileName.trim();
|
|
|
|
|
Map<String, Object> safeData = (data == null ? Collections.emptyMap() : data);
|
|
|
|
|
|
|
|
|
|
log.info("Word模板导出开始(自定义Configure), templatePath={}, fileName={}, dataKeys={}",
|
|
|
|
|
templatePath, safeFileName, safeData.keySet());
|
|
|
|
|
|
|
|
|
|
try (XWPFTemplate template = buildTemplate(templatePath, safeData, config);
|
|
|
|
|
OutputStream os = resetResponse(safeFileName, response)) {
|
|
|
|
|
template.write(os);
|
|
|
|
|
os.flush();
|
|
|
|
|
log.info("Word模板导出成功, fileName={}", safeFileName);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
log.error("Word 模板导出失败, templatePath={}, fileName={}", templatePath, safeFileName, e);
|
|
|
|
|
throw new ServiceException("Word 模板导出失败,请联系管理员").setDetailMessage(e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 渲染模板并输出到指定 OutputStream(不负责关闭 out)
|
|
|
|
|
* 适用于非 HTTP 场景,例如:
|
|
|
|
|
|