Commit 9f11a3de by zwb

新增根据html生成pdf文件

parent 18fa217a
...@@ -70,4 +70,19 @@ public interface CacheConstants { ...@@ -70,4 +70,19 @@ public interface CacheConstants {
* 下载使用简历对应模版缓存 * 下载使用简历对应模版缓存
*/ */
String RESUME_TEMPLATE_ID = "template:resume:download:%s"; String RESUME_TEMPLATE_ID = "template:resume:download:%s";
/**
* 简历下载数据缓存数据pdf
*/
String CREATE_RESUME_HTML_PDF_DATA = "create:resume:html:pdf:data:%s";
/**
* 简历下载数据缓存pdf
*/
String CREATE_RESUME_PDF_HTML = "create:resume:pdf:html:%s";
/**
* 下载使用简历对应模版缓存pdf
*/
String RESUME_TEMPLATE_PDF_ID = "template:resume:pdf:download:%s";
} }
...@@ -33,9 +33,9 @@ public class AiConfigProperties { ...@@ -33,9 +33,9 @@ public class AiConfigProperties {
// //
// public String avatarImages = "https://xzt-dev.jinsehuaqin.com/avatar_images/"; // public String avatarImages = "https://xzt-dev.jinsehuaqin.com/avatar_images/";
// //
// public String pdfDownZs = "https://xzt-dev.jinsehuaqin.com/pdf_down_zs/"; public String pdfDownZs;
//
// public String htmlDownZs = "https://xzt-dev.jinsehuaqin.com/html_down_zs/"; public String htmlDownZs;
// //
// public String imagesDownZs = "https://xzt-dev.jinsehuaqin.com/images_down_zs/"; // public String imagesDownZs = "https://xzt-dev.jinsehuaqin.com/images_down_zs/";
// //
......
...@@ -7,6 +7,7 @@ import com.bkty.system.domain.dto.ModuleOptimizationDto; ...@@ -7,6 +7,7 @@ import com.bkty.system.domain.dto.ModuleOptimizationDto;
import com.bkty.system.domain.dto.ResumeByPdfDto; import com.bkty.system.domain.dto.ResumeByPdfDto;
import com.bkty.system.domain.dto.ResumeListItemCache; import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.dto.ResumeMakeDto; import com.bkty.system.domain.dto.ResumeMakeDto;
import com.bkty.system.domain.vo.ResumeByPdfVo;
import com.bkty.system.domain.vo.ResumeModelVo; import com.bkty.system.domain.vo.ResumeModelVo;
import com.bkty.system.domain.vo.ResumeVo; import com.bkty.system.domain.vo.ResumeVo;
import com.bkty.system.service.resume.NewEditionResumeService; import com.bkty.system.service.resume.NewEditionResumeService;
...@@ -139,11 +140,26 @@ public class NewEditionResumeController { ...@@ -139,11 +140,26 @@ public class NewEditionResumeController {
*/ */
@PostMapping("/create-resume-by-html") @PostMapping("/create-resume-by-html")
public R createResumeHtml(@RequestBody ResumeByPdfDto dto){ public R createResumeHtml(@RequestBody ResumeByPdfDto dto){
if (dto.getResumeId()==null){
throw new WarnException("简历ID不能为空");
}
return new R<>(this.newEditionResumeService.createResumeHtml(dto.getResumeId(), dto.getTemplateName())); return new R<>(this.newEditionResumeService.createResumeHtml(dto.getResumeId(), dto.getTemplateName()));
} }
/** /**
* 根据html生成pdf文件
* @param dto
* @return
*/
@PostMapping("/create-resume-pdf")
public R<ResumeByPdfVo> createResumePdf(@RequestBody ResumeByPdfDto dto){
ResumeByPdfVo vo = this.newEditionResumeService.createResumePdf(dto);
return new R<>(vo);
}
/**
* 简历模块优化 * 简历模块优化
* @param dto * @param dto
* @return * @return
......
package com.bkty.system.service.resume; package com.bkty.system.service.resume;
import com.bkty.system.domain.dto.ModuleOptimizationDto; import com.bkty.system.domain.dto.ModuleOptimizationDto;
import com.bkty.system.domain.dto.ResumeByPdfDto;
import com.bkty.system.domain.dto.ResumeListItemCache; import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.dto.ResumeMakeDto; import com.bkty.system.domain.dto.ResumeMakeDto;
import com.bkty.system.domain.vo.ResumeByPdfVo;
import com.bkty.system.domain.vo.ResumeModelVo; import com.bkty.system.domain.vo.ResumeModelVo;
import com.bkty.system.domain.vo.ResumeVo; import com.bkty.system.domain.vo.ResumeVo;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
...@@ -90,4 +92,12 @@ public interface NewEditionResumeService { ...@@ -90,4 +92,12 @@ public interface NewEditionResumeService {
* @param dto * @param dto
*/ */
void resumeModelSort(ResumeMakeDto dto); void resumeModelSort(ResumeMakeDto dto);
/**
* 根据html生成pdf文件
* @param dto
* @return
*/
ResumeByPdfVo createResumePdf(ResumeByPdfDto dto);
} }
...@@ -13,6 +13,7 @@ import com.bkty.system.api.model.LoginUser; ...@@ -13,6 +13,7 @@ import com.bkty.system.api.model.LoginUser;
import com.bkty.system.config.AiConfigProperties; import com.bkty.system.config.AiConfigProperties;
import com.bkty.system.config.CommonAsyncThreadPool; import com.bkty.system.config.CommonAsyncThreadPool;
import com.bkty.system.domain.dto.ModuleOptimizationDto; import com.bkty.system.domain.dto.ModuleOptimizationDto;
import com.bkty.system.domain.dto.ResumeByPdfDto;
import com.bkty.system.domain.dto.ResumeListItemCache; import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.dto.ResumeMakeDto; import com.bkty.system.domain.dto.ResumeMakeDto;
import com.bkty.system.domain.entity.*; import com.bkty.system.domain.entity.*;
...@@ -29,12 +30,14 @@ import lombok.extern.slf4j.Slf4j; ...@@ -29,12 +30,14 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.CozeConstsnts; import org.dromara.common.core.constant.CozeConstsnts;
import org.dromara.common.core.constant.SecurityConstants;
import org.dromara.common.core.domain.UserLoginInfo; import org.dromara.common.core.domain.UserLoginInfo;
import org.dromara.common.core.enums.ResumeEnum; import org.dromara.common.core.enums.ResumeEnum;
import org.dromara.common.core.exception.WarnException; import org.dromara.common.core.exception.WarnException;
import org.dromara.common.core.utils.SecurityUtils; import org.dromara.common.core.utils.SecurityUtils;
import org.dromara.common.core.utils.SnowFlakeUtil; import org.dromara.common.core.utils.SnowFlakeUtil;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.WebUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
...@@ -43,6 +46,9 @@ import org.springframework.scheduling.annotation.Async; ...@@ -43,6 +46,9 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
...@@ -818,6 +824,126 @@ public class NewEditionResumeServiceImpl implements NewEditionResumeService { ...@@ -818,6 +824,126 @@ public class NewEditionResumeServiceImpl implements NewEditionResumeService {
} }
} }
@Override
public ResumeByPdfVo createResumePdf(ResumeByPdfDto dto) {
String templateName = dto.getTemplateName();
if (StringUtils.isBlank(templateName)){
templateName = "tpl21.ejs";
}
if (com.alibaba.excel.util.StringUtils.isBlank(dto.getResumeId())){
throw new WarnException("简历ID不能为空");
}
ResumeByPdfVo vo = new ResumeByPdfVo();
/**是否展示0.显示 1.不显示*/
Map<String, Object> objectMap = newEditionResumeUtil.queryAllModelData(dto.getResumeId(), "0");
Object baseInfo = objectMap.get("base");
if (baseInfo != null) {
JSONObject jsonObject = (JSONObject) JSON.toJSON(baseInfo);
String age = jsonObject.getString("birthday");
// 计算年龄
String calculatedAge = calculationAge(age);
// 放回 JSON 对象中
jsonObject.put("birthday", calculatedAge);
jsonObject.put("workTime", "");
// 更新 base_info
objectMap.put("base", jsonObject);
}
log.info("========导出简历数据1=========:{}", objectMap);
String jsonString = JSON.toJSONString(objectMap);
log.info("========导出简历数据2=========:{}", jsonString);
if(objectMap.get("base") != null){
Map<String, Object> map = JSON.parseObject(String.valueOf(objectMap.get("base")), Map.class);
if(map.get("resumeName") != null){
vo.setResumeName(map != null ? String.valueOf(map.get("resumeName")) : "");
}else {
FunctionResumeBase resumeBase = this.functionResumeBaseMapper.selectById(dto.getResumeId());
vo.setResumeName(resumeBase.getResumeName());
this.resumeCacheService.saveResumeModelDataCache(Long.valueOf(dto.getResumeId()), Long.valueOf(dto.getResumeId()), JSON.toJSONString(resumeBase));
}
vo.setUserName(map != null ? String.valueOf(map.get("username")) : "");
}
String data = redisTemplate.opsForValue().get(CacheConstants.CREATE_RESUME_HTML_PDF_DATA.formatted(dto.getResumeId()));
if (com.alibaba.excel.util.StringUtils.isNotBlank(data)){
if(jsonString.equals(data)){
String template = redisTemplate.opsForValue().get(CacheConstants.RESUME_TEMPLATE_PDF_ID.formatted(dto.getResumeId()));
if(com.alibaba.excel.util.StringUtils.isNotBlank(template) && template.equals(templateName)){
String json = redisTemplate.opsForValue().get(CacheConstants.CREATE_RESUME_PDF_HTML.formatted(dto.getResumeId()));
if(com.alibaba.excel.util.StringUtils.isNotBlank(json)){
ResumeByPdfVo resume = JSON.parseObject(json, ResumeByPdfVo.class);
return resume;
}
}
}
}
log.info("简历预览数据: {}", jsonString);
String htmlName = dto.getResumeId() + ".html";
FileProcessor.processFile(properties.getPath() + "template/" + templateName, properties.getPath() + "output/" + htmlName, "<%- data %>", jsonString);
// Node.js 命令及参数
String nodeExecutable = "node"; // 如果环境变量已配置 Node.js,可直接用 "node"
String scriptPath = properties.getPath() + "output";
String inputFile = "file:///"+properties.getPath()+"output/"+ dto.getResumeId() +".html";
String pdfName = sanitizeFileName(vo.getResumeName()) + ".pdf";
try {
// 构建 ProcessBuilder 并传递参数
ProcessBuilder processBuilder = new ProcessBuilder(
nodeExecutable, scriptPath, inputFile, properties.getPath() + "pdf/" + pdfName);
// 合并标准输出和错误输出
processBuilder.redirectErrorStream(true);
// 启动进程
Process process = processBuilder.start();
// 读取 Node.js 命令行输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
log.info("Node.js 输出pdf: {}", line);
}
// 等待命令执行完成
int exitCode = process.waitFor();
log.info("Node.js 命令执行完成,退出码: {}", exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
String header = WebUtils.getRequest().getHeader(SecurityConstants.QQXB_FROM);
if(com.alibaba.excel.util.StringUtils.isNotBlank(header) && !SecurityConstants.QQXB_OUT.equals(header) && !SecurityConstants.JYXZ_OUT.equals(header)){
vo.setPdfName(properties.getPdfDown() + pdfName);
vo.setHtmlName(properties.getHtmlDown() + htmlName);
}else {
vo.setPdfName(properties.getPdfDownZs() + pdfName);
vo.setHtmlName(properties.getHtmlDownZs() + htmlName);
}
redisTemplate.opsForValue().set(CacheConstants.CREATE_RESUME_HTML_PDF_DATA.formatted(dto.getResumeId()), jsonString);
redisTemplate.opsForValue().set(CacheConstants.CREATE_RESUME_PDF_HTML.formatted(dto.getResumeId()), JSON.toJSONString(vo));
redisTemplate.opsForValue().set(CacheConstants.RESUME_TEMPLATE_PDF_ID.formatted(dto.getResumeId()), templateName);
return vo;
}
public String sanitizeFileName(String fileName) {
if (fileName == null) return null;
// [^a-zA-Z0-9\u4e00-\u9fa5.-] 表示不是字母、数字、中文、点、短横线的字符都替换成 _
String sanitized = fileName.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5.-]", "_");
// 可选:去掉开头和结尾多余的 _
sanitized = sanitized.replaceAll("^_+", "").replaceAll("_+$", "");
// 可选:连续多个特殊字符替换成一个 _
sanitized = sanitized.replaceAll("_+", "_");
return sanitized;
}
private ResumeModelVo initModelVo(FunctionResumeModel m) { private ResumeModelVo initModelVo(FunctionResumeModel m) {
ResumeModelVo modelVo = new ResumeModelVo(); ResumeModelVo modelVo = new ResumeModelVo();
modelVo.setModelId(m.getId()); modelVo.setModelId(m.getId());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment