18 changed files with 476 additions and 16 deletions
@ -0,0 +1,91 @@ |
|||
package net.lab1024.sa.admin.module.word; |
|||
|
|||
import io.swagger.v3.oas.annotations.Operation; |
|||
import io.swagger.v3.oas.annotations.tags.Tag; |
|||
import net.lab1024.sa.admin.module.penalty.domain.entity.PenaltyApplyEntity; |
|||
import net.lab1024.sa.admin.module.penalty.service.PenaltyApplyService; |
|||
import net.lab1024.sa.admin.module.service.domain.form.*; |
|||
import net.lab1024.sa.admin.module.service.domain.vo.LawyerStatisticsVO; |
|||
import net.lab1024.sa.admin.module.service.domain.vo.ServiceApplicationsVO; |
|||
import net.lab1024.sa.admin.module.service.domain.vo.ServiceReportStatisticsVO; |
|||
import net.lab1024.sa.admin.module.service.service.ServiceApplicationsService; |
|||
import net.lab1024.sa.admin.module.system.department.domain.vo.DepartmentVO; |
|||
import net.lab1024.sa.admin.module.system.department.service.DepartmentService; |
|||
import net.lab1024.sa.admin.module.system.employee.domain.entity.EmployeeEntity; |
|||
import net.lab1024.sa.admin.module.system.employee.service.EmployeeService; |
|||
import net.lab1024.sa.base.common.domain.PageResult; |
|||
import net.lab1024.sa.base.common.domain.ResponseDTO; |
|||
import net.lab1024.sa.base.common.domain.ValidateList; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import javax.annotation.Resource; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import javax.validation.Valid; |
|||
import java.math.BigDecimal; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 无处罚证明开具 Controller |
|||
* |
|||
* @Author wzh |
|||
* @Date 2025-12-20 14:44:06 |
|||
* @Copyright 1.0 |
|||
*/ |
|||
|
|||
@RestController |
|||
@Tag(name = "无处罚证明开具") |
|||
public class WordApplicationsController { |
|||
@Resource |
|||
WordCertificateService wordCertificateService; |
|||
@Resource |
|||
private PenaltyApplyService penaltyApplyService; |
|||
@Resource |
|||
EmployeeService employeeService; |
|||
@Resource |
|||
DepartmentService departmentService; |
|||
@Operation(summary = "无处罚证明下载 @author wzh") |
|||
@GetMapping("/wordCertificate/export/{id}") |
|||
public void wordCertificateExport(HttpServletResponse response, @PathVariable Integer id, |
|||
@RequestParam(defaultValue = "word") String format) throws Exception { |
|||
//查询出当前的开具证明信息
|
|||
PenaltyApplyEntity penaltyApplyEntity = penaltyApplyService.selectOne(id); |
|||
//查询用户姓名执业证号
|
|||
EmployeeEntity byId = employeeService.getById(penaltyApplyEntity.getUserId()); |
|||
//机构信息
|
|||
DepartmentVO departmentVO = departmentService.getById(byId.getDepartmentId()); |
|||
WordCertificateService.CertificateData certificateData = new WordCertificateService.CertificateData( |
|||
departmentVO.getDepartmentName(), |
|||
departmentVO.getCreditCode(), |
|||
byId.getActualName(), |
|||
byId.getCertificateNumber(), |
|||
penaltyApplyEntity.getCreateTime() |
|||
); |
|||
|
|||
byte[] fileContent; |
|||
String fileName; |
|||
String contentType; |
|||
|
|||
if ("pdf".equalsIgnoreCase(format)) { |
|||
// 生成PDF文档
|
|||
fileContent = wordCertificateService.generateCertificateAsPdf(certificateData); |
|||
fileName = "certificate.pdf"; |
|||
contentType = "application/pdf"; |
|||
} else { |
|||
// 生成Word文档
|
|||
fileContent = wordCertificateService.generateCertificate(certificateData); |
|||
fileName = "certificate.docx"; |
|||
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; |
|||
} |
|||
|
|||
// 设置文档响应头
|
|||
response.setContentType(contentType); |
|||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName); |
|||
|
|||
response.setContentLength(fileContent.length); |
|||
|
|||
// 将文件内容写入响应输出流
|
|||
response.getOutputStream().write(fileContent); |
|||
response.getOutputStream().flush(); |
|||
} |
|||
} |
|||
@ -0,0 +1,269 @@ |
|||
package net.lab1024.sa.admin.module.word; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.poi.util.Units; |
|||
import org.apache.poi.xwpf.converter.pdf.PdfConverter; |
|||
import org.apache.poi.xwpf.converter.pdf.PdfOptions; |
|||
import org.apache.poi.xwpf.usermodel.ParagraphAlignment; |
|||
import org.apache.poi.xwpf.usermodel.XWPFDocument; |
|||
import org.apache.poi.xwpf.usermodel.XWPFParagraph; |
|||
import org.apache.poi.xwpf.usermodel.XWPFRun; |
|||
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.*; |
|||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; |
|||
import org.springframework.core.io.ClassPathResource; |
|||
import org.springframework.core.io.Resource; |
|||
import org.springframework.util.FileCopyUtils; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.ByteArrayOutputStream; |
|||
import java.io.InputStream; |
|||
import java.math.BigInteger; |
|||
import java.time.LocalDate; |
|||
import java.time.LocalDateTime; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @program: yun-parent |
|||
* @description: 模板生成 |
|||
* @author: Mr.Wang |
|||
* @create: 2026-01-13 16:38 |
|||
**/ |
|||
@Service |
|||
@Slf4j |
|||
public class WordCertificateService { |
|||
|
|||
private static final String TEMPLATE_PATH = "templates/certificate_template.docx"; |
|||
private static final String SEAL_PATH = "templates/official_seal.png"; |
|||
/** |
|||
* 生成Word格式的证书 |
|||
*/ |
|||
public byte[] generateCertificate(CertificateData data) throws Exception { |
|||
XWPFDocument document = prepareDocument(data); |
|||
|
|||
// 保存到字节数组
|
|||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
|||
document.write(outputStream); |
|||
document.close(); |
|||
|
|||
return outputStream.toByteArray(); |
|||
} |
|||
|
|||
/** |
|||
* 生成PDF格式的证书 |
|||
*/ |
|||
public byte[] generateCertificateAsPdf(CertificateData data) throws Exception { |
|||
XWPFDocument document = prepareDocument(data); |
|||
|
|||
// 转换为PDF
|
|||
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream(); |
|||
PdfConverter.getInstance().convert(document, pdfOutputStream, PdfOptions.create()); |
|||
document.close(); |
|||
|
|||
return pdfOutputStream.toByteArray(); |
|||
} |
|||
|
|||
/** |
|||
* 准备文档(用于Word或PDF生成) |
|||
*/ |
|||
private XWPFDocument prepareDocument(CertificateData data) throws Exception { |
|||
Resource templateResource = new ClassPathResource(TEMPLATE_PATH); |
|||
XWPFDocument document = new XWPFDocument(templateResource.getInputStream()); |
|||
|
|||
// 1. 替换文本占位符
|
|||
replaceTextPlaceholders(document, data); |
|||
|
|||
// 2. 插入电子章图片
|
|||
insertSealImage(document); |
|||
|
|||
return document; |
|||
} |
|||
|
|||
/** |
|||
* 替换文本占位符 |
|||
*/ |
|||
private void replaceTextPlaceholders(XWPFDocument document, CertificateData data) { |
|||
// 准备替换数据
|
|||
Map<String, String> replacements = new HashMap<>(); |
|||
//律所名称
|
|||
replacements.put("${certificateNo}", data.getCertificateNo()); |
|||
//信用社代码
|
|||
replacements.put("${purpose}", data.getPurpose()); |
|||
//律师名称
|
|||
replacements.put("${name}", data.getName()); |
|||
//身份证号码
|
|||
replacements.put("${idCard}", data.getIdCard()); |
|||
//时间
|
|||
replacements.put("${queryTime}", data.getLocalDateTime().format( |
|||
DateTimeFormatter.ofPattern("yyyy年MM月dd日"))); |
|||
|
|||
// 遍历所有段落进行替换
|
|||
for (XWPFParagraph paragraph : document.getParagraphs()) { |
|||
String text = paragraph.getText(); |
|||
if (text != null && text.contains("${")) { |
|||
for (Map.Entry<String, String> entry : replacements.entrySet()) { |
|||
text = text.replace(entry.getKey(), entry.getValue()); |
|||
} |
|||
|
|||
// 清空原有内容
|
|||
for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { |
|||
paragraph.removeRun(i); |
|||
} |
|||
|
|||
// 添加新内容
|
|||
XWPFRun run = paragraph.createRun(); |
|||
run.setText(text); |
|||
run.setFontFamily("宋体"); |
|||
run.setFontSize(12); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 插入电子章图片(正确的方法) |
|||
*/ |
|||
private void insertSealImage(XWPFDocument document) throws Exception { |
|||
try { |
|||
// 1. 读取电子章图片
|
|||
Resource sealResource = new ClassPathResource(SEAL_PATH); |
|||
if (!sealResource.exists()) { |
|||
log.warn("电子章图片不存在"); |
|||
insertTextSeal(document); |
|||
return; |
|||
} |
|||
|
|||
byte[] sealBytes = FileCopyUtils.copyToByteArray(sealResource.getInputStream()); |
|||
|
|||
// 2. 查找插入位置
|
|||
for (XWPFParagraph paragraph : document.getParagraphs()) { |
|||
String text = paragraph.getText(); |
|||
if (text != null && text.contains("${电子章}")) { |
|||
// 清除占位符文本
|
|||
for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { |
|||
paragraph.removeRun(i); |
|||
} |
|||
|
|||
// 创建居中的运行来放置图片
|
|||
XWPFRun run = paragraph.createRun(); |
|||
run.getParagraph().setAlignment(ParagraphAlignment.CENTER); |
|||
|
|||
// 添加图片数据并获取关系ID
|
|||
String blipId = document.addPictureData( |
|||
new ByteArrayInputStream(sealBytes), |
|||
XWPFDocument.PICTURE_TYPE_PNG |
|||
); |
|||
|
|||
// 设置图片尺寸(150×150像素)
|
|||
int width = 150; |
|||
int height = 150; |
|||
|
|||
// 使用addPicture方法插入图片
|
|||
run.addPicture( |
|||
new ByteArrayInputStream(sealBytes), |
|||
XWPFDocument.PICTURE_TYPE_PNG, |
|||
"official_seal.png", |
|||
Units.toEMU(width), |
|||
Units.toEMU(height) |
|||
); |
|||
|
|||
// 获取图片对象并设置为浮于文字上方
|
|||
CTDrawing drawing = run.getCTR().getDrawingArray(0); |
|||
if (drawing.sizeOfAnchorArray() > 0) { |
|||
CTInline inline = drawing.getInlineArray(0); |
|||
|
|||
// 创建锚定对象
|
|||
CTAnchor anchor = drawing.addNewAnchor(); |
|||
|
|||
// 设置位置
|
|||
anchor.addNewSimplePos().setX(0); |
|||
anchor.addNewSimplePos().setY(0); |
|||
|
|||
// 设置相对于页面
|
|||
CTPosH posH = anchor.addNewPositionH(); |
|||
posH.setRelativeFrom(STRelFromH.PAGE); |
|||
posH.setPosOffset(0); |
|||
|
|||
CTPosV posV = anchor.addNewPositionV(); |
|||
posV.setRelativeFrom(STRelFromV.PAGE); |
|||
posV.setPosOffset(0); |
|||
|
|||
// 设置环绕方式为无(浮于文字上方)
|
|||
anchor.addNewWrapNone(); |
|||
|
|||
// 设置在文字上方
|
|||
anchor.setBehindDoc(false); |
|||
// 删除原来的内联对象
|
|||
drawing.removeInline(0); |
|||
} |
|||
// 添加图片说明文字
|
|||
addSealCaption(paragraph); |
|||
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
log.error("插入电子章失败: {}", e.getMessage(), e); |
|||
insertTextSeal(document); |
|||
} |
|||
} |
|||
/** |
|||
* 添加图片说明文字到指定段落 |
|||
*/ |
|||
private void addSealCaption(XWPFParagraph paragraph) { |
|||
XWPFRun run = paragraph.createRun(); |
|||
run.setFontFamily("宋体"); |
|||
run.setFontSize(11); |
|||
run.setColor("000000"); |
|||
} |
|||
|
|||
/** |
|||
* 插入文字替代的电子章 |
|||
*/ |
|||
private void insertTextSeal(XWPFDocument document) { |
|||
for (XWPFParagraph paragraph : document.getParagraphs()) { |
|||
String text = paragraph.getText(); |
|||
if (text != null && text.contains("(此处插入电子章)")) { |
|||
for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { |
|||
paragraph.removeRun(i); |
|||
} |
|||
|
|||
XWPFRun run = paragraph.createRun(); |
|||
run.setText("(电子公章)"); |
|||
run.setFontFamily("宋体"); |
|||
run.setFontSize(12); |
|||
run.setColor("FF0000"); |
|||
run.setBold(true); |
|||
|
|||
break; |
|||
} |
|||
} |
|||
} |
|||
/** |
|||
* 格式化日期 |
|||
*/ |
|||
private String formatDate(LocalDate date) { |
|||
if (date == null) return ""; |
|||
return date.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")); |
|||
} |
|||
|
|||
/** |
|||
* 证书数据类 |
|||
*/ |
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public static class CertificateData { |
|||
private String certificateNo; |
|||
private String purpose; |
|||
private String name; |
|||
private String idCard; |
|||
private LocalDateTime localDateTime; // 修正字段名为驼峰命名
|
|||
} |
|||
|
|||
} |
|||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Loading…
Reference in new issue