Compare commits
5 Commits
eb7691b982
...
7ec4b60140
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ec4b60140 | ||
|
|
7eb65d9e9b | ||
|
|
ba7e23708f | ||
|
|
6420ed6ba7 | ||
|
|
cb077de58c |
@@ -59,7 +59,11 @@
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.17.1</version> <!-- 请使用最新版本 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>pc.exam.gg</groupId>
|
||||
<artifactId>exam-spring-boot-starter-redis</artifactId>
|
||||
|
||||
@@ -23,10 +23,15 @@ public class ErrorQuestionListVo {
|
||||
*/
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
private String taskName;
|
||||
|
||||
/**
|
||||
* 错误次数
|
||||
*/
|
||||
private String errorNumber;
|
||||
|
||||
/**
|
||||
* 章节名称
|
||||
*/
|
||||
@@ -36,40 +41,17 @@ public class ErrorQuestionListVo {
|
||||
*/
|
||||
private String subjectName;
|
||||
|
||||
/**
|
||||
* 题型难度(0:简单,1:一般,2:困难)
|
||||
*/
|
||||
private Integer quLevel;
|
||||
|
||||
/**
|
||||
* 试题内容(带样式:<p>下列表格123</p>\n<table style=\"border-collapse: collapse; width: 100%;\" border=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 31.4907%;\">1</td>\)
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 试题内容(纯文本)
|
||||
*/
|
||||
private String contentText;
|
||||
|
||||
/**
|
||||
* 解析(带样式)
|
||||
*/
|
||||
private String analysis;
|
||||
|
||||
/**
|
||||
* c语言参考答案
|
||||
*/
|
||||
private String answer;
|
||||
|
||||
|
||||
/**
|
||||
* 知识点
|
||||
*/
|
||||
private String pointNames;
|
||||
/**
|
||||
* 关键字
|
||||
*/
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 课程类别
|
||||
@@ -80,10 +62,7 @@ public class ErrorQuestionListVo {
|
||||
* 专业分类
|
||||
*/
|
||||
private String specialtyName;
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
private String tname;
|
||||
|
||||
|
||||
/**
|
||||
* 试题答案
|
||||
@@ -96,15 +75,12 @@ public class ErrorQuestionListVo {
|
||||
private List<ExamQuestionFile> fileUploads;
|
||||
|
||||
/**
|
||||
* 试题判分
|
||||
* 答案
|
||||
*/
|
||||
private ExamQuestionScore questionScores;
|
||||
|
||||
private String answer;
|
||||
/**
|
||||
* 试题关键字
|
||||
* 创建时间
|
||||
*/
|
||||
private List<ExamQuestionKeyword> questionKeywords;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private String creator;
|
||||
|
||||
@@ -37,89 +37,14 @@ public class ExamErrorQuestion {
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 章节名称
|
||||
* 创建时间
|
||||
*/
|
||||
private String chapteridDictText;
|
||||
/**
|
||||
* 题型名称
|
||||
*/
|
||||
private String subjectName;
|
||||
|
||||
/**
|
||||
* 题型难度(0:简单,1:一般,2:困难)
|
||||
*/
|
||||
private Integer quLevel;
|
||||
|
||||
/**
|
||||
* 试题内容(带样式:<p>下列表格123</p>\n<table style=\"border-collapse: collapse; width: 100%;\" border=\"1\">\n<tbody>\n<tr>\n<td style=\"width: 31.4907%;\">1</td>\)
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 试题内容(纯文本)
|
||||
*/
|
||||
private String contentText;
|
||||
|
||||
/**
|
||||
* 解析(带样式)
|
||||
*/
|
||||
private String analysis;
|
||||
|
||||
/**
|
||||
* c语言参考答案
|
||||
*/
|
||||
private String answer;
|
||||
|
||||
|
||||
/**
|
||||
* 知识点
|
||||
*/
|
||||
private String pointNames;
|
||||
/**
|
||||
* 关键字
|
||||
*/
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 课程类别
|
||||
*/
|
||||
private String courseName;
|
||||
|
||||
/**
|
||||
* 专业分类
|
||||
*/
|
||||
private String specialtyName;
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
private String tname;
|
||||
|
||||
/**
|
||||
* 试题答案
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<ExamQuestionAnswer> answerList;
|
||||
|
||||
/**
|
||||
* 试题文件
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<ExamQuestionFile> fileUploads;
|
||||
|
||||
/**
|
||||
* 试题判分
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private ExamQuestionScore questionScores;
|
||||
|
||||
/**
|
||||
* 试题关键字
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<ExamQuestionKeyword> questionKeywords;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
|
||||
private String creator;
|
||||
/**
|
||||
* 租户id
|
||||
|
||||
@@ -1,26 +1,63 @@
|
||||
package pc.exam.pp.module.exam.service.error;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.springframework.stereotype.Service;
|
||||
import pc.exam.pp.module.exam.controller.admin.error.vo.ErrorQuestionListVo;
|
||||
import pc.exam.pp.module.exam.dal.dataobject.ExamQuestion;
|
||||
import pc.exam.pp.module.exam.dal.mysql.error.ExamErrorQuestionInfoMapper;
|
||||
import pc.exam.pp.module.exam.dal.mysql.error.ExamErrorQuestionMapper;
|
||||
import pc.exam.pp.module.exam.service.question.IExamQuestionService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ExamErrorQuestionServiceImpl implements ExamErrorQuestionService{
|
||||
public class ExamErrorQuestionServiceImpl implements ExamErrorQuestionService {
|
||||
|
||||
@Resource
|
||||
ExamErrorQuestionMapper examErrorQuestionMapper;
|
||||
@Resource
|
||||
ExamErrorQuestionInfoMapper examErrorQuestionInfoMapper;
|
||||
@Resource
|
||||
IExamQuestionService examQuestionService;
|
||||
|
||||
@Override
|
||||
public List<ErrorQuestionListVo> selectByUserId(String userId) {
|
||||
List<ErrorQuestionListVo> errorQuestionListVos = examErrorQuestionMapper.selectByUserId(userId);
|
||||
errorQuestionListVos.forEach(item->{
|
||||
errorQuestionListVos.forEach(item -> {
|
||||
// 通过试题ID查询对应所有的内容
|
||||
ExamQuestion examQuestion = examQuestionService.selectExamQuestionByQuId(item.getQuId());
|
||||
item.setErrorNumber(examErrorQuestionInfoMapper.selectByQuId(item.getQuId()));
|
||||
item.setChapteridDictText(examQuestion.getChapteridDictTextVo());
|
||||
item.setSubjectName(examQuestion.getSubjectName());
|
||||
item.setPointNames(examQuestion.getPointNamesVo());
|
||||
item.setCourseName(examQuestion.getCourseName());
|
||||
item.setSpecialtyName(examQuestion.getSpecialtyName());
|
||||
item.setAnswerList(examQuestion.getAnswerList());
|
||||
item.setContent(examQuestion.getContent());
|
||||
item.setFileUploads(examQuestion.getFileUploads());
|
||||
item.setContentText(Jsoup.parse(item.getContent()).text());
|
||||
if (examQuestion.getFileUploads() != null) {
|
||||
// 说明是有文件的
|
||||
// 答案就是URL,文件路径,直接下载到本地查看
|
||||
examQuestion.getFileUploads().forEach(fileUpload -> {
|
||||
if (fileUpload.getFileType().equals("2")) {
|
||||
item.setAnswer(fileUpload.getUrl());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 说明没有文件
|
||||
// 选择题 选择题答案
|
||||
examQuestion.getAnswerList().forEach(answer -> {
|
||||
if (answer.getIsRight() != null) {
|
||||
if (answer.getIsRight().equals("0")) {
|
||||
item.setAnswer(answer.getContent());
|
||||
}
|
||||
} else {
|
||||
item.setAnswer(item.getAnswer() + answer.getContentIn() + "<br>");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return errorQuestionListVos;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.List;
|
||||
@RequestMapping("/auto/wps")
|
||||
@Tag(name = "测试判分2 - wps相关操作")
|
||||
@Validated
|
||||
public class AutoWpsController {
|
||||
public class AutoWpsController {
|
||||
|
||||
@Resource
|
||||
JudgementWpsWordService judgementWpsWordService;
|
||||
@@ -82,7 +82,7 @@ public class AutoWpsController {
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping(value = "/docxMaster", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public CommonResult<List<JudgementWordsVO>> docxMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file) throws Exception {
|
||||
public CommonResult<List<JudgementWordsVO>> docxMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file, @RequestParam(value = "name", required = false) String name) throws Exception {
|
||||
// 手动解析JSON数组
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<WpsDocxInfoVo> wpsDocxInfoVos = objectMapper.readValue(
|
||||
@@ -90,7 +90,7 @@ public class AutoWpsController {
|
||||
new TypeReference<List<WpsDocxInfoVo>>() {
|
||||
}
|
||||
);
|
||||
return CommonResult.success(judgementWpsWordService.docxMaster(wpsDocxInfoVos, file));
|
||||
return CommonResult.success(judgementWpsWordService.docxMaster(wpsDocxInfoVos, file,name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.List;
|
||||
*/
|
||||
public interface JudgementWpsWordService {
|
||||
|
||||
List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file) throws Exception;
|
||||
List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file,String name) throws Exception;
|
||||
|
||||
List<DocxDataInfoVO> docxDataInfo(MultipartFile file) throws Exception;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ public class JudgementWpsWordServiceImpl implements JudgementWpsWordService {
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@Override
|
||||
public List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file) throws Exception {
|
||||
return DocxMaster.docxMaster(wpsDocxInfoVos, file);
|
||||
public List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file,String name) throws Exception {
|
||||
return DocxMaster.docxMaster(wpsDocxInfoVos, file,name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -586,7 +586,7 @@ public class ShapePage {
|
||||
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
|
||||
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "dur");
|
||||
if (!value.isEmpty()) {
|
||||
return Integer.parseInt(value) / 1000 + "秒";
|
||||
return (double) Integer.parseInt(value) / 1000 + "秒";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -600,7 +600,7 @@ public class ShapePage {
|
||||
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
|
||||
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "delay");
|
||||
if (!value.isEmpty()) {
|
||||
return Integer.parseInt(value) / 1000 + "秒";
|
||||
return (double) Integer.parseInt(value) / 1000 + "秒";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -772,7 +772,7 @@ public class ShapePage {
|
||||
// 遍历段落中的所有文本运行
|
||||
Object obj = paragraph.getEGTextRun().get(0);
|
||||
if (obj instanceof CTRegularTextRun ctRegularTextRun) {
|
||||
return String.valueOf(ctRegularTextRun.getRPr().getSz());
|
||||
return String.valueOf(ctRegularTextRun.getRPr().getSz() / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1019,11 +1019,10 @@ public class ShapePage {
|
||||
// 遍历所有段落
|
||||
for (CTTextParagraph paragraph : textBody.getP()) {
|
||||
if (paragraph.getPPr().getLnSpc().getSpcPct() != null) {
|
||||
return "百分比行距"; // EMU 转 pt
|
||||
return (double) paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt
|
||||
} else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) {
|
||||
return "固定行距"; // EMU 转 pt
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@@ -1036,9 +1035,9 @@ public class ShapePage {
|
||||
// 遍历所有段落
|
||||
for (CTTextParagraph paragraph : textBody.getP()) {
|
||||
if (paragraph.getPPr().getLnSpc().getSpcPct() != null) {
|
||||
return paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt
|
||||
return (double) paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt
|
||||
} else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) {
|
||||
return paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 10 + "磅固定行距"; // EMU 转 pt
|
||||
return (double) paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 10 + "磅固定行距"; // EMU 转 pt
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1048,6 +1047,21 @@ public class ShapePage {
|
||||
|
||||
/// 段落 项目符号和编号→类型
|
||||
public static String getTextBoxParagraphSymbol(org.pptx4j.pml.Shape sp, CTSlideTiming timing) {
|
||||
CTTextBody ctTextBody = sp.getTxBody();
|
||||
// 只需要获取一组的数据即可
|
||||
CTTextCharBullet ctTextCharBullet = ctTextBody.getP().get(0).getPPr().getBuChar();
|
||||
if (ctTextCharBullet != null) {
|
||||
return "项目符号";
|
||||
}
|
||||
CTTextBlipBullet ctTextBlipBullet = ctTextBody.getP().get(0).getPPr().getBuBlip();
|
||||
if (ctTextBlipBullet != null) {
|
||||
return "项目符号";
|
||||
}
|
||||
// 编号
|
||||
CTTextAutonumberBullet numberingLevel = ctTextBody.getP().get(0).getPPr().getBuAutoNum();
|
||||
if (numberingLevel != null) {
|
||||
return "编号";
|
||||
}
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import org.docx4j.dml.*;
|
||||
import org.pptx4j.pml.*;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToCmConverter;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.Transition;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -16,7 +17,11 @@ public class ShapePic {
|
||||
// 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU)
|
||||
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
|
||||
double heightPt = emuToPt(ext.getCy());
|
||||
return String.valueOf(heightPt);
|
||||
// 转换成磅
|
||||
double pt = Math.round(heightPt * 100.0) / 100.0;
|
||||
// 转换成厘米
|
||||
double cm = PtToCmConverter.pointsToCm(pt);
|
||||
return pt + "磅(" + cm + "厘米)";
|
||||
}
|
||||
|
||||
// 大小 - 宽度
|
||||
@@ -25,29 +30,20 @@ public class ShapePic {
|
||||
// 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU)
|
||||
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
|
||||
double widthPt = emuToPt(ext.getCx());
|
||||
return String.valueOf(widthPt);
|
||||
// 转换成磅
|
||||
double pt = Math.round(widthPt * 100.0) / 100.0;
|
||||
// 转换成厘米
|
||||
double cm = PtToCmConverter.pointsToCm(pt);
|
||||
return pt + "磅(" + cm + "厘米)";
|
||||
}
|
||||
|
||||
// 大小 - 锁定纵横比
|
||||
public static String getShapeSizeLockAspectRatio(Pic sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
// 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU)
|
||||
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
|
||||
Boolean lockAspectRatio = null;
|
||||
if (sp.getSpPr().getPrstGeom() != null &&
|
||||
sp.getSpPr().getPrstGeom().getAvLst() != null) {
|
||||
// 这个部分一般不会直接提供锁定横纵比,要看 spPr.getExtLst()
|
||||
if (sp.getSpPr().getExtLst() != null) {
|
||||
// pptx4j 没有直接的 getter,要自己解析 XML
|
||||
String xml = sp.getSpPr().getExtLst().toString();
|
||||
if (xml.contains("lockAspectRatio=\"1\"") || xml.contains("lockAspectRatio=\"true\"")) {
|
||||
lockAspectRatio = true;
|
||||
} else if (xml.contains("lockAspectRatio=\"0\"") || xml.contains("lockAspectRatio=\"false\"")) {
|
||||
lockAspectRatio = false;
|
||||
}
|
||||
}
|
||||
CTPictureLocking ctPictureLocking = sp.getNvPicPr().getCNvPicPr().getPicLocks();
|
||||
if (ctPictureLocking != null) {
|
||||
return ctPictureLocking.isNoChangeAspect() ? "是" : "否";
|
||||
}
|
||||
return String.valueOf(lockAspectRatio);
|
||||
return "否";
|
||||
}
|
||||
|
||||
// EMU 转 pt
|
||||
@@ -562,7 +558,7 @@ public class ShapePic {
|
||||
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
|
||||
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "dur");
|
||||
if (!value.isEmpty()) {
|
||||
return Integer.parseInt(value) / 1000 + "秒";
|
||||
return (double) Integer.parseInt(value) / 1000 + "秒";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -576,7 +572,7 @@ public class ShapePic {
|
||||
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
|
||||
String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "delay");
|
||||
if (!value.isEmpty()) {
|
||||
return Integer.parseInt(value) / 1000 + "秒";
|
||||
return (double) Integer.parseInt(value) / 1000 + "秒";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,18 +1,91 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.openpackaging.exceptions.Docx4JException;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
|
||||
import org.pptx4j.pml.CTSlideTiming;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart;
|
||||
import org.pptx4j.pml.CTShowInfoKiosk;
|
||||
import org.pptx4j.pml.Presentation;
|
||||
import org.pptx4j.pml.PresentationPr;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PptxUtils;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToCmConverter;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToEmuConverter;
|
||||
|
||||
public class SlideAllSetting {
|
||||
// 设计 主题模板
|
||||
public static String getAllSlideTheme( SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getAllSlideTheme(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
// 获取第一个幻灯片母版(通常主主题在这里)
|
||||
return "WPS";
|
||||
}
|
||||
|
||||
// 幻灯片切换 切换声音
|
||||
// 幻灯片放映
|
||||
// 幻灯片放映-放映类型
|
||||
public static String getAllSlideShowType(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
|
||||
CTShowInfoKiosk ctShowInfoKiosk = presentationPr.getShowPr().getKiosk();
|
||||
if (ctShowInfoKiosk != null) {
|
||||
return "在展台浏览(全屏)";
|
||||
} else {
|
||||
return "演示者放映(全屏)";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 幻灯片放映-循环播放,按ESC键终止
|
||||
public static String getAllSlideShowLoop(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
return presentationPr.getShowPr().isLoop() ? "是" : "否";
|
||||
}
|
||||
|
||||
// 幻灯片放映-放映范围
|
||||
public static String getAllSlideShowRange(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
long st = presentationPr.getShowPr().getSldRg().getSt();
|
||||
long end = presentationPr.getShowPr().getSldRg().getEnd();
|
||||
return "第" + st + "页-第" + end + "页";
|
||||
}
|
||||
|
||||
// 幻灯片大小 幻灯片大小
|
||||
public static String getAllSlideSize(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
// 拿到 JAXB 模型对象
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
int cx = pres.getSldSz().getCx();
|
||||
int cy = pres.getSldSz().getCy();
|
||||
return PptxUtils.detectAspectName(cx, cy);
|
||||
}
|
||||
|
||||
//幻灯片大小 宽度
|
||||
public static String getAllSlideSizeCx(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
// 拿到 JAXB 模型对象
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
int cx = pres.getSldSz().getCx();
|
||||
return PtToEmuConverter.emuToPt(cx)+ "磅("+ PtToCmConverter.pointsToCm(PtToEmuConverter.emuToPt(cx))+"厘米)";
|
||||
}
|
||||
|
||||
//幻灯片大小 高度
|
||||
public static String getAllSlideSizeCy(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
// 拿到 JAXB 模型对象
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
int cy = pres.getSldSz().getCy();
|
||||
return PtToEmuConverter.emuToPt(cy)+ "磅("+ PtToCmConverter.pointsToCm(PtToEmuConverter.emuToPt(cy))+"厘米)";
|
||||
}
|
||||
|
||||
// 幻灯片大小 幻灯片编号起始值
|
||||
public static String getAllSlideSizeStartNum(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
// 拿到 JAXB 模型对象
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
int firstNum = pres.getFirstSlideNum();
|
||||
return String.valueOf(firstNum);
|
||||
}
|
||||
|
||||
// 幻灯片大小 方向
|
||||
public static String getAllSlideSizeOrientation(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
int cy = pres.getSldSz().getCy();
|
||||
int cx = pres.getSldSz().getCx();
|
||||
return cy > cx ? "纵向" : "横向";
|
||||
}
|
||||
|
||||
// 幻灯片大小 备注方向
|
||||
public static String getAllSlideSizeNoteOrientation(MainPresentationPart mpp, PresentationPr presentationPr) {
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
long cy = pres.getNotesSz().getCy();
|
||||
long cx = pres.getNotesSz().getCx();
|
||||
return cx > cy ? "横向" : "纵向";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,14 @@ public class SlideConversion {
|
||||
if (shapeObj instanceof Pic) {
|
||||
setSlideDataInfo(fourId, secondId, "形状"+slideIndexFoShape+"->"+((Pic) shapeObj).getNvPicPr().getCNvPr().getName(), "shape", String.valueOf(slideIndexFoFile) + "_" + String.valueOf(slideIndexFoShape), true, "第"+slideIndexFoFile+"页", dataInfoVOS);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
// 幻灯片设置
|
||||
String firstSetId = getStringRandom();
|
||||
String secondSetId = getStringRandom();
|
||||
setSlideDataInfo(firstSetId, "", "设置", "slideAllSetting", "", false, "", dataInfoVOS);
|
||||
setSlideDataInfo(secondSetId, firstSetId, "幻灯片设置", "slideAllSetting", "0_0", true, "设置", dataInfoVOS);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.CTBlipFillProperties;
|
||||
import org.docx4j.openpackaging.exceptions.Docx4JException;
|
||||
import org.docx4j.openpackaging.packages.OpcPackage;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.JaxbXmlPart;
|
||||
import org.docx4j.openpackaging.parts.Part;
|
||||
import org.docx4j.openpackaging.parts.Parts;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.NotesMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
|
||||
import org.pptx4j.pml.CTSlideTiming;
|
||||
import org.pptx4j.pml.GroupShape;
|
||||
import org.pptx4j.pml.Pic;
|
||||
import org.pptx4j.pml.Shape;
|
||||
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
|
||||
import org.docx4j.relationships.Relationship;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster;
|
||||
import org.pptx4j.pml.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsSlideInfoVo;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.vo.JudgementSlidesVO;
|
||||
@@ -17,9 +25,11 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SlideMaster {
|
||||
|
||||
|
||||
public static List<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, MultipartFile file) throws IOException, Docx4JException {
|
||||
// File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx");
|
||||
List<JudgementSlidesVO> judgementSlidesVOS = new ArrayList<>();
|
||||
@@ -29,6 +39,8 @@ public class SlideMaster {
|
||||
// 加载 .pptx 文件
|
||||
PresentationMLPackage ppt =
|
||||
(PresentationMLPackage) OpcPackage.load(inputStream);
|
||||
// 获取母版
|
||||
SlideMasterPart slideMasterPart = SlideMasterUtils.getSlideMaster(ppt);
|
||||
// 你可以在这里处理 ppt,比如读取文字、页数等
|
||||
for (WpsSlideInfoVo wpsSlideInfoVo : wpsSlideInfoVos) {
|
||||
// 幻灯片位置(第几张幻灯片)
|
||||
@@ -44,79 +56,96 @@ public class SlideMaster {
|
||||
String examCode = wpsSlideInfoVo.getExamCode();
|
||||
// 方式方法
|
||||
String method = wpsSlideInfoVo.getMethod();
|
||||
|
||||
String content = wpsSlideInfoVo.getSlideIndex() + "@" + firstName + "@" + function + "@" + examName + "@" + examCode + "@" + method;
|
||||
// 查询幻灯片图片
|
||||
List<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();
|
||||
int slideIndexFoFile = 0;
|
||||
for (SlidePart slidePart : slideParts) {
|
||||
slideIndexFoFile++;
|
||||
if (method.equals("shape")) {
|
||||
// 获取幻灯片内容
|
||||
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
|
||||
// 查询幻灯片
|
||||
// 遍历 shape tree 中的 sp(shape)元素
|
||||
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
|
||||
CTSlideTiming timing = slidePart.getJaxbElement().getTiming();
|
||||
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
|
||||
int shapeIndexFoFile = 0;
|
||||
for (Object shapeObj : shapes) {
|
||||
shapeIndexFoFile++;
|
||||
if (shapeObj instanceof Shape shape) {
|
||||
if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) {
|
||||
// 目标对象
|
||||
ShapePage shapeFunction = new ShapePage();
|
||||
// 获取参数中类型的定义
|
||||
Class<?>[] paramTypes = {Shape.class, CTSlideTiming.class};
|
||||
// 带参数的方法调用示例
|
||||
Method methodWithArgs = shapeFunction.getClass().getMethod(function, paramTypes);
|
||||
// 实际参数值
|
||||
Object[] arguments = {shape, timing};
|
||||
String value = (String) methodWithArgs.invoke(shapeFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
// 获取整个ppt文件的设置
|
||||
if (method.equals("slideAllSetting")) {
|
||||
MainPresentationPart mpp = ppt.getMainPresentationPart();
|
||||
PresentationPr presentationPr = SlideMasterUtils.getPresentationPr(ppt);
|
||||
SlideAllSetting slideAllSettingFunction = new SlideAllSetting();
|
||||
Class<?>[] paramTypes = {MainPresentationPart.class, PresentationPr.class};
|
||||
Method methodWithArgs = slideAllSettingFunction.getClass().getMethod(function, paramTypes);
|
||||
Object[] arguments = {mpp, presentationPr};
|
||||
String value = (String) methodWithArgs.invoke(slideAllSettingFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
}
|
||||
} else {
|
||||
// 查询幻灯片图片
|
||||
List<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();
|
||||
int slideIndexFoFile = 0;
|
||||
for (SlidePart slidePart : slideParts) {
|
||||
slideIndexFoFile++;
|
||||
if (method.equals("shape")) {
|
||||
// 获取幻灯片内容
|
||||
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
|
||||
// 查询幻灯片
|
||||
// 遍历 shape tree 中的 sp(shape)元素
|
||||
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
|
||||
CTSlideTiming timing = slidePart.getJaxbElement().getTiming();
|
||||
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
|
||||
int shapeIndexFoFile = 0;
|
||||
for (Object shapeObj : shapes) {
|
||||
shapeIndexFoFile++;
|
||||
if (shapeObj instanceof Shape shape) {
|
||||
if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) {
|
||||
// 目标对象
|
||||
ShapePage shapeFunction = new ShapePage();
|
||||
// 获取参数中类型的定义
|
||||
Class<?>[] paramTypes = {Shape.class, CTSlideTiming.class};
|
||||
// 带参数的方法调用示例
|
||||
Method methodWithArgs = shapeFunction.getClass().getMethod(function, paramTypes);
|
||||
// 实际参数值
|
||||
Object[] arguments = {shape, timing};
|
||||
String value = (String) methodWithArgs.invoke(shapeFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (shapeObj instanceof Pic pic) {
|
||||
if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) {
|
||||
// 目标对象
|
||||
ShapePic shapePicFunction = new ShapePic();
|
||||
// 获取参数中类型的定义
|
||||
Class<?>[] paramTypes = {Pic.class, CTSlideTiming.class};
|
||||
// 带参数的方法调用示例
|
||||
Method methodWithArgs = shapePicFunction.getClass().getMethod(function, paramTypes);
|
||||
// 实际参数值
|
||||
Object[] arguments = {pic, timing};
|
||||
String value = (String) methodWithArgs.invoke(shapePicFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
} else if (shapeObj instanceof Pic pic) {
|
||||
if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) {
|
||||
// 目标对象
|
||||
ShapePic shapePicFunction = new ShapePic();
|
||||
// 获取参数中类型的定义
|
||||
Class<?>[] paramTypes = {Pic.class, CTSlideTiming.class};
|
||||
// 带参数的方法调用示例
|
||||
Method methodWithArgs = shapePicFunction.getClass().getMethod(function, paramTypes);
|
||||
// 实际参数值
|
||||
Object[] arguments = {pic, timing};
|
||||
String value = (String) methodWithArgs.invoke(shapePicFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (method.equals("slideSetting")) {
|
||||
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
|
||||
SlideSetting slideSettingFunction = new SlideSetting();
|
||||
Class<?>[] paramTypes = {SlidePart.class, PresentationMLPackage.class};
|
||||
Method methodWithArgs = slideSettingFunction.getClass().getMethod(function, paramTypes);
|
||||
Object[] arguments = {slidePart, ppt};
|
||||
String value = (String) methodWithArgs.invoke(slideSettingFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
if (method.equals("slideSetting")) {
|
||||
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
|
||||
SlideSetting slideSettingFunction = new SlideSetting();
|
||||
Class<?>[] paramTypes = {SlidePart.class, PresentationMLPackage.class, SlideMasterPart.class};
|
||||
Method methodWithArgs = slideSettingFunction.getClass().getMethod(function, paramTypes);
|
||||
Object[] arguments = {slidePart, ppt, slideMasterPart};
|
||||
String value = (String) methodWithArgs.invoke(slideSettingFunction, arguments);
|
||||
if (!value.isEmpty()) {
|
||||
JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO();
|
||||
judgementSlidesVO.setContent(content + "@" + value);
|
||||
judgementSlidesVO.setContentIn(firstName + examName + value);
|
||||
judgementSlidesVO.setScoreRate(1);
|
||||
judgementSlidesVOS.add(judgementSlidesVO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.CTBlipFillProperties;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.JaxbXmlPart;
|
||||
import org.docx4j.openpackaging.parts.Part;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart;
|
||||
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
|
||||
import org.docx4j.relationships.Relationship;
|
||||
import org.pptx4j.jaxb.Context;
|
||||
import org.pptx4j.pml.CTBackground;
|
||||
import org.pptx4j.pml.Presentation;
|
||||
import org.pptx4j.pml.PresentationPr;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SlideMasterUtils {
|
||||
|
||||
public static SlideMasterPart getSlideMaster(PresentationMLPackage ppt) {
|
||||
// 2. 获取 Presentation 对象
|
||||
MainPresentationPart mpp = ppt.getMainPresentationPart();
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
RelationshipsPart rels = ppt.getMainPresentationPart().getRelationshipsPart();
|
||||
// 获取母版ID
|
||||
Presentation.SldMasterIdLst idList = pres.getSldMasterIdLst();
|
||||
for (Presentation.SldMasterIdLst.SldMasterId smId : idList.getSldMasterId()) {
|
||||
// 不同生成类版本,取 r:id 的方法名可能不同:getRid() 或 getId2()
|
||||
String rId = null;
|
||||
try {
|
||||
rId = smId.getRid(); // 新一点的模型常见
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
if (rId == null) {
|
||||
try {
|
||||
// 某些版本字段名为 id2(即 r:id)
|
||||
rId = (String) Presentation.SldMasterIdLst.SldMasterId.class.getMethod("getId2").invoke(smId);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
if (rId == null) {
|
||||
System.out.println("找不到该 sldMasterId 对应的 r:id。");
|
||||
continue;
|
||||
}
|
||||
Relationship rel = rels.getRelationshipByID(rId);
|
||||
if (rel == null) {
|
||||
System.out.println("关系未找到: " + rId);
|
||||
continue;
|
||||
}
|
||||
// 再通过关系拿到具体的 Part
|
||||
Part part = rels.getPart(rel);
|
||||
if (part instanceof SlideMasterPart) {
|
||||
SlideMasterPart master = (SlideMasterPart) part;
|
||||
return master;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// /ppt/presProps.xml 的关系类型
|
||||
private static final String REL_TYPE_PRES_PROPS =
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps";
|
||||
|
||||
// (可选兜底)/ppt/presProps.xml 的 Content-Type
|
||||
private static final String PRES_PROPS_CT =
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentationProperties+xml";
|
||||
|
||||
/**
|
||||
* 仿照你给的 getSlideMaster 写法:
|
||||
* 从 MainPresentationPart 的关系里定位 /ppt/presProps.xml,返回 PresentationPr。
|
||||
* 若不存在或无法解析,则返回 null。
|
||||
*/
|
||||
public static PresentationPr getPresentationPr(PresentationMLPackage ppt) {
|
||||
try {
|
||||
MainPresentationPart mpp = ppt.getMainPresentationPart();
|
||||
RelationshipsPart rels = mpp.getRelationshipsPart();
|
||||
|
||||
// 1) 先按关系类型精确查找
|
||||
for (Relationship rel : rels.getRelationships().getRelationship()) {
|
||||
if (REL_TYPE_PRES_PROPS.equals(rel.getType())) {
|
||||
Part part = rels.getPart(rel);
|
||||
PresentationPr pr = toPresentationPr(part);
|
||||
if (pr != null) return pr;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 兜底:在整包里按 Content-Type 扫描(有的文件没建关系时可用)
|
||||
for (Part p : ppt.getParts().getParts().values()) {
|
||||
if (PRES_PROPS_CT.equals(p.getContentType())) {
|
||||
PresentationPr pr = toPresentationPr(p);
|
||||
if (pr != null) return pr;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Part 转为 PresentationPr:
|
||||
* - 若 JAXB 根对象已是 PresentationPr,直接返回;
|
||||
* - 否则把 JAXB 对象序列化为 XML,再用 Context.jcPML 反序列化成 PresentationPr(兼容老版本根类型 CTPresentationProperties)。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static PresentationPr toPresentationPr(Part part) {
|
||||
try {
|
||||
if (part instanceof JaxbXmlPart) {
|
||||
Object jaxb = ((JaxbXmlPart<?>) part).getJaxbElement();
|
||||
if (jaxb instanceof PresentationPr) {
|
||||
return (PresentationPr) jaxb;
|
||||
}
|
||||
// 兼容老版本类型:用 XML 重新反解为 PresentationPr
|
||||
String xml = XmlUtils.marshaltoString(jaxb, true);
|
||||
Object obj = XmlUtils.unmarshalString(xml, Context.jcPML, PresentationPr.class);
|
||||
return (PresentationPr) obj;
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import java.util.List;
|
||||
// 单个幻灯片设置
|
||||
public class SlideSetting {
|
||||
// 幻灯片 版式
|
||||
public static String getSlideLayout(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideLayout(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
SlideLayoutPart layoutPart = slidePart.getSlideLayoutPart();
|
||||
if (layoutPart.getContents().getCSld() != null) {
|
||||
return layoutPart.getContents().getCSld().getName();
|
||||
@@ -28,19 +28,23 @@ public class SlideSetting {
|
||||
return "";
|
||||
}
|
||||
// 设计 主题模板
|
||||
public static String getSlideTheme(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideTheme(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
// 获取第一个幻灯片母版(通常主主题在这里)
|
||||
return "WPS";
|
||||
}
|
||||
|
||||
// 背景填充 填充方式
|
||||
public static String getSlideBackground(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideBackground(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slide = slidePart.getContents();
|
||||
CTBackground ctBackground = slidePart.getContents().getCSld().getBg();
|
||||
if (ctBackground != null) {
|
||||
CTBackgroundProperties ctBackgroundProperties = ctBackground.getBgPr();
|
||||
return getBackgroundFillType(ctBackgroundProperties);
|
||||
} else {
|
||||
// 需要检查母版的设置
|
||||
CTBackgroundProperties ctBackgroundProperties = slideMasterPart.getJaxbElement().getCSld().getBg().getBgPr();
|
||||
return getBackgroundFillType(ctBackgroundProperties);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private static String getBackgroundFillType(CTBackgroundProperties bgPr) {
|
||||
if (bgPr.getSolidFill() != null) return "纯色填充";
|
||||
@@ -52,7 +56,7 @@ public class SlideSetting {
|
||||
return "未知填充类型";
|
||||
}
|
||||
// 背景填充 纯色填充-颜色
|
||||
public static String getSlideBackgroundSolidColor(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideBackgroundSolidColor(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
CTBackground ctBackground = slidePart.getContents().getCSld().getBg();
|
||||
if (ctBackground != null) {
|
||||
CTBackgroundProperties ctBackgroundProperties = ctBackground.getBgPr();
|
||||
@@ -76,19 +80,19 @@ public class SlideSetting {
|
||||
}
|
||||
|
||||
// 背景填充 隐藏背景图形
|
||||
public static String getSlideBackgroundHideShape(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideBackgroundHideShape(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
// 幻灯片切换 切换效果
|
||||
public static String getSlideTransition(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideTransition(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slides = (Sld) slidePart.getJaxbElement();
|
||||
Sld slide = (Sld) slidePart.getContents();
|
||||
CTSlideTransition ctSlideTransition = slide.getTransition();
|
||||
return "";
|
||||
}
|
||||
// 幻灯片切换 切换声音
|
||||
public static String getSlideTransitionSound(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideTransitionSound(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slide = (Sld) slidePart.getContents();
|
||||
CTSlideTransition ctSlideTransition = slide.getTransition();
|
||||
CTTransitionSoundAction ctTransitionSoundAction = ctSlideTransition.getSndAc();
|
||||
@@ -100,18 +104,18 @@ public class SlideSetting {
|
||||
}
|
||||
|
||||
// 幻灯片切换 切换速度
|
||||
public static String getSlideTransitionSpeed(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideTransitionSpeed(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slide = (Sld) slidePart.getContents();
|
||||
CTSlideTransition ctSlideTransition = slide.getTransition();
|
||||
Long speed = ctSlideTransition.getAdvTm();
|
||||
if (speed != null) {
|
||||
return String.valueOf(speed) + "毫秒";
|
||||
return speed / 1000 + "秒";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 隐藏幻灯片
|
||||
public static String getSlideHide(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideHide(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld sld = slidePart.getJaxbElement();
|
||||
if (sld != null) {
|
||||
return sld.isShow() ? "显示幻灯片" : "隐藏幻灯片";
|
||||
@@ -120,7 +124,7 @@ public class SlideSetting {
|
||||
}
|
||||
|
||||
// 页眉页脚 包含→日期和时间
|
||||
public static String getSlideHeaderFooterDateTime(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideHeaderFooterDateTime(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
|
||||
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
|
||||
for (Object shapeObj : shapes) {
|
||||
@@ -134,7 +138,7 @@ public class SlideSetting {
|
||||
return "";
|
||||
}
|
||||
// 页眉页脚 包含→幻灯片编号
|
||||
public static String getSlideHeaderFooterSlideNumber(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideHeaderFooterSlideNumber(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
|
||||
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
|
||||
for (Object shapeObj : shapes) {
|
||||
@@ -148,7 +152,7 @@ public class SlideSetting {
|
||||
return "";
|
||||
}
|
||||
// 页眉页脚 包含→页脚
|
||||
public static String getSlideHeaderFooterFooter(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
|
||||
public static String getSlideHeaderFooterFooter(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
|
||||
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
|
||||
for (Object shapeObj : shapes) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils;
|
||||
|
||||
public class PptxUtils {
|
||||
public static String detectAspectName(long width, long height) {
|
||||
if (width <= 0 || height <= 0) return "自定";
|
||||
// 不区分横竖:3:4 也当 4:3
|
||||
long w = Math.max(width, height), h = Math.min(width, height);
|
||||
double tol = 0.01; // 允许1%误差
|
||||
|
||||
// 判断 4:3 -> w/h ≈ 4/3 <=> |w*3 - h*4| / max(w*3, h*4) <= tol
|
||||
long l43 = w * 3, r43 = h * 4;
|
||||
if (Math.abs(l43 - r43) <= Math.max(l43, r43) * tol) return "4:3";
|
||||
|
||||
// 判断 16:9 -> w/h ≈ 16/9 <=> |w*9 - h*16| / max(w*9, h*16) <= tol
|
||||
long l169 = w * 9, r169 = h * 16;
|
||||
if (Math.abs(l169 - r169) <= Math.max(l169, r169) * tol) return "16:9";
|
||||
|
||||
return "自定义";
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ public class PtToCmConverter {
|
||||
* @param points 点数
|
||||
* @return 厘米数
|
||||
*/
|
||||
public static double pointsToCm(double points) {
|
||||
return points * PT_TO_CM;
|
||||
public static int pointsToCm(double points) {
|
||||
return (int) Math.round(points * PT_TO_CM);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ public class Transition {
|
||||
case "drumroll.wav":
|
||||
return "擂鼓 / 滚奏";
|
||||
case "click.wav":
|
||||
return "点击";
|
||||
return "单机";
|
||||
case "coin.wav":
|
||||
return "硬币";
|
||||
case "bomb.wav":
|
||||
@@ -86,7 +86,7 @@ public class Transition {
|
||||
case "onclick":
|
||||
case "clickeffect": return "单击时";
|
||||
|
||||
case "witheffect":
|
||||
case "witheffect": return "之前";
|
||||
case "onbegin": return "与上一动画同时";
|
||||
|
||||
case "aftereffect":
|
||||
|
||||
@@ -23,62 +23,72 @@ public class DocxConversion {
|
||||
public static List<DocxDataInfoVO> DocxDataInfos(MultipartFile file) throws Exception {
|
||||
List<DocxDataInfoVO> dataInfoVOS = new ArrayList<>();
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
// 1. 直接使用 InputStream 构造 XWPFDocument(无需本地文件)
|
||||
XWPFDocument document = new XWPFDocument(inputStream);
|
||||
|
||||
// 2. 读取文档内容(示例:提取所有段落文本)
|
||||
StringBuilder content = new StringBuilder();
|
||||
document.getParagraphs().forEach(paragraph -> {
|
||||
content.append(paragraph.getText()).append("\n");
|
||||
});
|
||||
|
||||
|
||||
String xmlString = XmlUtil.getDocumentXml(document);
|
||||
String namespace = WpsWordNameSpaces.getNameSpace(xmlString);
|
||||
|
||||
XmlObject docXml = document.getDocument();
|
||||
// 1、获取文档段落W:P标签得数量,判断出一个有多少段
|
||||
|
||||
// 段落根节点
|
||||
XmlCursor wpCursor = docXml.newCursor();
|
||||
String wpPath = namespace + "/w:document/w:body/w:p";
|
||||
wpCursor.selectPath(wpPath);
|
||||
|
||||
int wpIndex = 0;
|
||||
// 段落
|
||||
String firstIdWp = getStringRandom();
|
||||
setWordDataInfo(firstIdWp, "", "段落", "w:p", "", false, dataInfoVOS);
|
||||
while (wpCursor.toNextSelection()) {
|
||||
wpIndex ++;
|
||||
// 段落属性
|
||||
if (!wpCursor.xmlText().contains("w:sectPr")) {
|
||||
// 获取文本
|
||||
XmlCursor wpTextXml = wpCursor.newCursor();
|
||||
wpTextXml.selectPath(namespace + ".//w:r/w:t");
|
||||
StringBuilder fullTextBuilder = new StringBuilder();
|
||||
while (wpTextXml.toNextSelection()) {
|
||||
String part = wpTextXml.getTextValue();
|
||||
if (part != null) {
|
||||
fullTextBuilder.append(part);
|
||||
}
|
||||
}
|
||||
String fullText = fullTextBuilder.toString().trim();
|
||||
if (!fullText.isEmpty()) {
|
||||
String secondIdWp = getStringRandom();
|
||||
String shortText = shorten(fullText); // 截断展示
|
||||
setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + shortText, "w:p", String.valueOf(wpIndex), true, dataInfoVOS);
|
||||
|
||||
while (wpCursor.toNextSelection()) {
|
||||
wpIndex++;
|
||||
if (!wpCursor.xmlText().contains("w:sectPr")) {
|
||||
// 1. 提取普通段落文本(排除文本框)
|
||||
String fullText = extractParagraphText(wpCursor, namespace);
|
||||
|
||||
// 2. 提取文本框文本
|
||||
List<String> textBoxSentences = processTextBoxParagraphs(wpCursor, namespace);
|
||||
|
||||
String secondIdWp = null;
|
||||
if (!fullText.isEmpty()) {
|
||||
// 过滤掉文本框句子
|
||||
StringBuilder filteredTextBuilder = new StringBuilder();
|
||||
String[] sentences = fullText.split("[。!?.!?]");
|
||||
for (String sentence : sentences) {
|
||||
sentence = sentence.trim();
|
||||
if (!sentence.isEmpty() && (textBoxSentences == null || !textBoxSentences.contains(sentence))) {
|
||||
if (filteredTextBuilder.length() > 0) {
|
||||
filteredTextBuilder.append("。"); // 可以用原文分隔符
|
||||
}
|
||||
filteredTextBuilder.append(sentence);
|
||||
}
|
||||
}
|
||||
String filteredFullText = filteredTextBuilder.toString();
|
||||
|
||||
// 生成段落节点,使用过滤后的文本
|
||||
secondIdWp = getStringRandom();
|
||||
setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + shorten(filteredFullText), "w:p", String.valueOf(wpIndex), true, dataInfoVOS);
|
||||
|
||||
// 拆分句子
|
||||
int sentenceIndex = 0;
|
||||
for (String sentence : sentences) {
|
||||
sentence = sentence.trim();
|
||||
if (!sentence.isEmpty()) {
|
||||
// 跳过文本框中已经存在的句子
|
||||
if (textBoxSentences != null && textBoxSentences.contains(sentence)) {
|
||||
continue;
|
||||
}
|
||||
sentenceIndex++;
|
||||
String thirdIdWp = getStringRandom();
|
||||
setWordDataInfo(thirdIdWp, secondIdWp, "句子" + sentenceIndex + ":" + shorten(sentence), "w:t", String.valueOf(wpIndex), true, dataInfoVOS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 2、页面
|
||||
XmlCursor wSectPrCursor = docXml.newCursor();
|
||||
String wSectPrPath = namespace + "//w:sectPr"; // 查找所有节
|
||||
@@ -243,7 +253,18 @@ public class DocxConversion {
|
||||
dataInfoVOS
|
||||
);
|
||||
}
|
||||
|
||||
String firstIdFTbl = getStringRandom();
|
||||
setWordDataInfo(firstIdFTbl, "", "杂项", "file", "1", false, dataInfoVOS);
|
||||
String secondIdTbl = getStringRandom();
|
||||
setWordDataInfo(
|
||||
secondIdTbl,
|
||||
firstIdFTbl,
|
||||
"文件" ,
|
||||
"file",
|
||||
"1",
|
||||
true,
|
||||
dataInfoVOS
|
||||
);
|
||||
|
||||
|
||||
// // 5、域
|
||||
@@ -319,5 +340,52 @@ public class DocxConversion {
|
||||
return text; // 如果没匹配到,就返回原字符串
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取段落文本,包括 w:t
|
||||
*/
|
||||
private static String extractParagraphText(XmlCursor wpCursor, String namespace) {
|
||||
XmlCursor wpTextXml = wpCursor.newCursor();
|
||||
wpTextXml.selectPath(namespace + ".//w:r/w:t");
|
||||
|
||||
StringBuilder fullTextBuilder = new StringBuilder();
|
||||
while (wpTextXml.toNextSelection()) {
|
||||
String part = wpTextXml.getTextValue();
|
||||
if (part != null) {
|
||||
fullTextBuilder.append(part);
|
||||
}
|
||||
}
|
||||
return fullTextBuilder.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归处理文本框段落
|
||||
*/
|
||||
|
||||
|
||||
|
||||
private static List<String> processTextBoxParagraphs(XmlCursor wpCursor, String namespace) throws Exception {
|
||||
List<String> textBoxSentences = new ArrayList<>();
|
||||
XmlCursor tbCursor = wpCursor.newCursor();
|
||||
// 注意命名空间声明方式,避免 XPath 异常
|
||||
tbCursor.selectPath(namespace + ".//w:txbxContent/w:p");
|
||||
while (tbCursor.toNextSelection()) {
|
||||
String tbText = extractParagraphText(tbCursor, namespace);
|
||||
if (!tbText.isEmpty()) {
|
||||
// 按句子拆分
|
||||
String[] sentences = tbText.split("[。!?.!?]");
|
||||
for (String sentence : sentences) {
|
||||
sentence = sentence.trim();
|
||||
if (!sentence.isEmpty()) {
|
||||
textBoxSentences.add(sentence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tbCursor.dispose();
|
||||
return textBoxSentences;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import org.w3c.dom.Node;
|
||||
import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsDocxInfoVo;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.endNote.EndNoteing;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.file.isFile;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.insert.InsertIng;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Paragraphs;
|
||||
@@ -72,7 +73,7 @@ public class DocxMaster {
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
public static List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file) throws Exception {
|
||||
public static List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file,String name) throws Exception {
|
||||
|
||||
// 一共分为 段落, 页面,图形
|
||||
String firstDuanLuoId = Convert.getStringRandom();
|
||||
@@ -626,6 +627,28 @@ public class DocxMaster {
|
||||
|
||||
|
||||
|
||||
if (firstName.contains("文件")) {
|
||||
try {
|
||||
|
||||
Class<?> clazz = isFile.class;
|
||||
Method method = clazz.getMethod(function);
|
||||
String value = (String) method.invoke(null );
|
||||
if (value != null) {
|
||||
judgementWordsVOS = setJudgementWord(
|
||||
judgementWordsVOS,
|
||||
docxFunction + "@" +name+"@"+ value,
|
||||
firstName + examName + name
|
||||
);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.err.println("方法不存在: " + function);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
break; // 找到指定表格就退出
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -90,19 +90,108 @@ public class Drawing {
|
||||
//布局 环绕方式
|
||||
public static String getLayoutHuanRao(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
// 是否衬于文字下方
|
||||
boolean behindDoc = anchor.isBehindDoc();
|
||||
if (behindDoc) return "衬于文字下方型";
|
||||
// 浮于文字上方型
|
||||
if (anchor.getWrapNone() != null) return "浮于文字上方型";
|
||||
if (anchor.getWrapSquare() != null) return "四周型";
|
||||
if (anchor.getWrapTight() != null) return "紧密型";
|
||||
if (anchor.getWrapThrough() != null) return "穿越型";
|
||||
if (anchor.getWrapTopAndBottom() != null) return "上下型";
|
||||
|
||||
return "无";
|
||||
String wrapType = null;
|
||||
|
||||
// 1. 优先判断环绕方式
|
||||
if (anchor.getWrapTight() != null) {
|
||||
wrapType = "紧密型";
|
||||
} else if (anchor.getWrapSquare() != null) {
|
||||
wrapType = "四周型";
|
||||
} else if (anchor.getWrapThrough() != null) {
|
||||
wrapType = "穿越型";
|
||||
} else if (anchor.getWrapTopAndBottom() != null) {
|
||||
wrapType = "上下型";
|
||||
} else if (anchor.getWrapNone() != null) {
|
||||
wrapType = "浮于文字上方型";
|
||||
}
|
||||
|
||||
// 2. 如果没有任何环绕方式,则根据 behindDoc 判断
|
||||
if (wrapType == null) {
|
||||
return anchor.isBehindDoc() ? "衬于文字下方型" : "无";
|
||||
}
|
||||
|
||||
// 3. 返回最终结果
|
||||
return wrapType;
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 对齐方式
|
||||
public static String getLayoutHorizontalAlign(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionH() == null) return "未知";
|
||||
if (anchor.getPositionH().getAlign() != null) {
|
||||
switch (anchor.getPositionH().getAlign()) {
|
||||
case LEFT:
|
||||
return "左对齐";
|
||||
case CENTER:
|
||||
return "居中";
|
||||
case RIGHT:
|
||||
return "右对齐";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 相对于
|
||||
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionH() == null) return "未知";
|
||||
if (anchor.getPositionH().getRelativeFrom() != null) {
|
||||
switch (anchor.getPositionH().getRelativeFrom()) {
|
||||
case COLUMN:
|
||||
return "列";
|
||||
case MARGIN:
|
||||
return "页边距";
|
||||
case PAGE:
|
||||
return "页面";
|
||||
case CHARACTER:
|
||||
return "字符";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 对齐方式
|
||||
public static String getLayoutVerticalAlign(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
||||
if (anchor.getPositionV().getAlign() != null) {
|
||||
switch (anchor.getPositionV().getAlign()) {
|
||||
case TOP:
|
||||
return "顶端对齐";
|
||||
case CENTER:
|
||||
return "居中";
|
||||
case BOTTOM:
|
||||
return "底端对齐";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 相对于
|
||||
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
||||
if (anchor.getPositionV().getRelativeFrom() != null) {
|
||||
switch (anchor.getPositionV().getRelativeFrom()) {
|
||||
case PAGE:
|
||||
return "页面";
|
||||
case MARGIN:
|
||||
return "页边距";
|
||||
case PARAGRAPH:
|
||||
return "段落";
|
||||
case LINE:
|
||||
return "行";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
|
||||
// 形状和样式
|
||||
public static List<JudgementWordsVO> getStyles(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
String name = "【第" + betoLong + "个图形】【图片】";
|
||||
@@ -1396,6 +1485,11 @@ public static String getSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
|
||||
return "无";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 获取倒影对象
|
||||
private static CTReflectionEffect getReflectionEffect(Anchor anchor) {
|
||||
if (anchor == null
|
||||
|
||||
@@ -4,6 +4,7 @@ import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.*;
|
||||
import org.docx4j.dml.chartDrawing.CTPicture;
|
||||
import org.docx4j.dml.picture.Pic;
|
||||
@@ -11,6 +12,8 @@ import org.docx4j.dml.wordprocessingDrawing.CTWrapSquare;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.DocxSetInfo;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.JudgementWordsVO;
|
||||
@@ -79,12 +82,206 @@ public class DrawingInline {
|
||||
}
|
||||
|
||||
//布局 环绕方式
|
||||
public static String getLayoutHuanRao(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
// Inline
|
||||
return null;
|
||||
public static String getLayoutHuanRao(List<JudgementWordsVO> judgementWordsVOS, Inline inline, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (inline == null) return "未知";
|
||||
|
||||
try {
|
||||
// 转为 W3C DOM
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(inline).getDocumentElement();
|
||||
|
||||
// 判断环绕方式
|
||||
NodeList wrapTight = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapTight");
|
||||
if (wrapTight.getLength() > 0) return "紧密型";
|
||||
|
||||
NodeList wrapSquare = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapSquare");
|
||||
if (wrapSquare.getLength() > 0) return "四周型";
|
||||
|
||||
NodeList wrapThrough = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapThrough");
|
||||
if (wrapThrough.getLength() > 0) return "穿越型";
|
||||
|
||||
NodeList wrapTopAndBottom = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapTopAndBottom");
|
||||
if (wrapTopAndBottom.getLength() > 0) return "上下型";
|
||||
|
||||
NodeList wrapNone = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapNone");
|
||||
if (wrapNone.getLength() > 0) return "浮于文字上方型";
|
||||
|
||||
// 判断 behindDoc(默认为 false)
|
||||
NodeList behindDocNodes = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "behindDoc");
|
||||
if (behindDocNodes.getLength() > 0) {
|
||||
String val = behindDocNodes.item(0).getTextContent();
|
||||
if ("1".equals(val) || "true".equalsIgnoreCase(val)) {
|
||||
return "衬于文字下方型";
|
||||
}
|
||||
}
|
||||
|
||||
return "无";
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 对齐方式
|
||||
public static String getLayoutHorizontalAlign(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
// 转为 W3C DOM
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
|
||||
// 获取 <wp:positionH> 节点
|
||||
NodeList posHList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionH"
|
||||
);
|
||||
|
||||
if (posHList.getLength() > 0) {
|
||||
Element posH = (Element) posHList.item(0);
|
||||
NodeList alignList = posH.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"align"
|
||||
);
|
||||
|
||||
if (alignList.getLength() > 0) {
|
||||
String align = alignList.item(0).getTextContent();
|
||||
switch (align.toLowerCase()) {
|
||||
case "left":
|
||||
return "左对齐";
|
||||
case "center":
|
||||
return "居中";
|
||||
case "right":
|
||||
return "右对齐";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "未知";
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 相对于
|
||||
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
// 转为 W3C DOM
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
|
||||
// 获取 <wp:positionH> 节点
|
||||
NodeList posHList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionH"
|
||||
);
|
||||
|
||||
if (posHList.getLength() > 0) {
|
||||
Element posH = (Element) posHList.item(0);
|
||||
NodeList relativeList = posH.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"relativeFrom"
|
||||
);
|
||||
|
||||
if (relativeList.getLength() > 0) {
|
||||
String relative = relativeList.item(0).getTextContent();
|
||||
switch (relative.toLowerCase()) {
|
||||
case "column":
|
||||
return "列";
|
||||
case "margin":
|
||||
return "页边距";
|
||||
case "page":
|
||||
return "页面";
|
||||
case "character":
|
||||
return "字符";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "未知";
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 对齐方式
|
||||
public static String getLayoutVerticalAlign(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
NodeList posVList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionV"
|
||||
);
|
||||
|
||||
if (posVList.getLength() > 0) {
|
||||
Element posV = (Element) posVList.item(0);
|
||||
NodeList alignList = posV.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"align"
|
||||
);
|
||||
|
||||
if (alignList.getLength() > 0) {
|
||||
String align = alignList.item(0).getTextContent();
|
||||
switch (align.toLowerCase()) {
|
||||
case "top": return "顶端对齐";
|
||||
case "center": return "居中";
|
||||
case "bottom": return "底端对齐";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 相对于
|
||||
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
NodeList posVList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionV"
|
||||
);
|
||||
|
||||
if (posVList.getLength() > 0) {
|
||||
Element posV = (Element) posVList.item(0);
|
||||
NodeList relativeList = posV.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"relativeFrom"
|
||||
);
|
||||
|
||||
if (relativeList.getLength() > 0) {
|
||||
String relative = relativeList.item(0).getTextContent();
|
||||
switch (relative.toLowerCase()) {
|
||||
case "page": return "页面";
|
||||
case "margin": return "页边距";
|
||||
case "paragraph": return "段落";
|
||||
case "line": return "行";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 形状和样式
|
||||
public static List<JudgementWordsVO> getStyles(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_word.docx4j.file;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class isFile {
|
||||
|
||||
public static String FileExists() {
|
||||
return "是";
|
||||
|
||||
}
|
||||
|
||||
public static String FileNotExists() {
|
||||
return "否";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_word.docx4j.section;
|
||||
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.Graphic;
|
||||
import org.docx4j.dml.GraphicData;
|
||||
@@ -15,14 +17,26 @@ import org.docx4j.openpackaging.exceptions.Docx4JException;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.docx4j.openpackaging.parts.Part;
|
||||
import org.docx4j.openpackaging.parts.WordprocessingML.*;
|
||||
import org.docx4j.vml.CTFill;
|
||||
import org.docx4j.wml.*;
|
||||
import org.docx4j.wml.CTBackground;
|
||||
import org.docx4j.wml.CTBorder;
|
||||
import org.docx4j.wml.CTColumns;
|
||||
import org.docx4j.wml.CTDocGrid;
|
||||
import org.docx4j.wml.CTShd;
|
||||
import org.docx4j.wml.STPageOrientation;
|
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -1324,25 +1338,6 @@ public class SectionPage {
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ----- 公共方法:获取单个属性(示例) -----
|
||||
//旋转角度
|
||||
public static String getRotationDegree(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
@@ -1372,18 +1367,103 @@ public class SectionPage {
|
||||
public static String getOpacityPercent(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections);
|
||||
if (vmlXml == null) return null;
|
||||
Pattern pattern = Pattern.compile("opacity=\"([0-9a-fA-F]+)f\"");
|
||||
Pattern opacityPattern = Pattern.compile("<v:fill[^>]*opacity=\"([0-9\\.]+)\"");
|
||||
Matcher matcher = opacityPattern.matcher(vmlXml);
|
||||
if (matcher.find()) {
|
||||
String opacityStr = matcher.group(1);
|
||||
float opacityPercent = Float.parseFloat(opacityStr) * 100;
|
||||
return String.format("%.2f%%", opacityPercent);
|
||||
|
||||
// 1. 小数形式,例如 opacity="0.4"
|
||||
Pattern decimalPattern = Pattern.compile("<v:fill[^>]*opacity=\"([0-9\\.]+)\"");
|
||||
Matcher decimalMatcher = decimalPattern.matcher(vmlXml);
|
||||
if (decimalMatcher.find()) {
|
||||
try {
|
||||
float opacity = Float.parseFloat(decimalMatcher.group(1)) * 100;
|
||||
// 反转:Word显示的透明度 = 100 - 实际值
|
||||
opacity = 100 - opacity;
|
||||
// 限制在 0~100%
|
||||
opacity = Math.min(Math.max(opacity, 0), 100);
|
||||
return String.format("%d%%", Math.round(opacity));
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略,尝试解析十六进制
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 匹配十六进制或整数值
|
||||
Pattern hexPattern = Pattern.compile("opacity\\s*=\\s*\"([0-9a-fA-F]+)\"");
|
||||
Matcher hexMatcher = hexPattern.matcher(vmlXml);
|
||||
if (hexMatcher.find()) {
|
||||
String hexStr = hexMatcher.group(1).replaceFirst("(?i)^0x", "");
|
||||
|
||||
// 如果最后一个字符是 'f' 或 'F',去掉
|
||||
if (hexStr.endsWith("f") || hexStr.endsWith("F")) {
|
||||
hexStr = hexStr.substring(0, hexStr.length() - 1);
|
||||
}
|
||||
|
||||
try {
|
||||
int value;
|
||||
|
||||
// 如果是纯数字字符串,直接十进制解析
|
||||
if (hexStr.matches("\\d+")) {
|
||||
value = Integer.parseInt(hexStr);
|
||||
} else {
|
||||
// 否则按16进制解析
|
||||
value = Integer.parseInt(hexStr, 16);
|
||||
}
|
||||
|
||||
// Word 的公式: 透明度 = (value / 65535.0) * 100
|
||||
float percent =100- (value / 65535.0f) * 100;
|
||||
|
||||
// 限制在 0-100
|
||||
percent = Math.min(Math.max(percent, 0), 100);
|
||||
|
||||
return String.format("%d%%", Math.round(percent));
|
||||
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
//水印文字
|
||||
|
||||
|
||||
|
||||
//页面背景→ 填充方式
|
||||
public static String getPageFillType(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
SectPr sectPr = wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().getSectPr();
|
||||
if (sectPr == null) return "无填充";
|
||||
|
||||
// 转为 XML
|
||||
String xml = org.docx4j.XmlUtils.marshaltoString(sectPr, true);
|
||||
|
||||
if (xml == null || xml.isEmpty()) return "无填充";
|
||||
|
||||
XPathFactory xPathfactory = XPathFactory.newInstance();
|
||||
XPath xpath = xPathfactory.newXPath();
|
||||
|
||||
// 检查是否有背景颜色或填充
|
||||
XPathExpression colorExpr = xpath.compile("//*[local-name()='bgColor' or local-name()='shd']");
|
||||
Node colorNode = (Node) colorExpr.evaluate(org.docx4j.XmlUtils.getNewDocumentBuilder().parse(
|
||||
new java.io.ByteArrayInputStream(xml.getBytes())), XPathConstants.NODE);
|
||||
if (colorNode != null) {
|
||||
return "纯色填充";
|
||||
}
|
||||
|
||||
// 检查是否有纹理或图案填充
|
||||
XPathExpression patternExpr = xpath.compile("//*[local-name()='fill' and @*]"); // 一般纹理/图案用 fill 属性
|
||||
Node patternNode = (Node) patternExpr.evaluate(org.docx4j.XmlUtils.getNewDocumentBuilder().parse(
|
||||
new java.io.ByteArrayInputStream(xml.getBytes())), XPathConstants.NODE);
|
||||
if (patternNode != null) {
|
||||
return "纹理/图案填充";
|
||||
}
|
||||
|
||||
// 检查是否有图片背景
|
||||
XPathExpression picExpr = xpath.compile("//*[local-name()='blip']"); // 图片一般是 <a:blip>
|
||||
Node picNode = (Node) picExpr.evaluate(org.docx4j.XmlUtils.getNewDocumentBuilder().parse(
|
||||
new java.io.ByteArrayInputStream(xml.getBytes())), XPathConstants.NODE);
|
||||
if (picNode != null) {
|
||||
return "图片填充";
|
||||
}
|
||||
|
||||
return "无填充";
|
||||
|
||||
}
|
||||
|
||||
//水印文字
|
||||
public static String getWatermarkText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections);
|
||||
if (vmlXml == null) return null;
|
||||
|
||||
@@ -71,9 +71,9 @@ spring:
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
redis:
|
||||
host: 47.101.60.131 # 地址
|
||||
host: 115.120.213.238 # 地址
|
||||
port: 6379 # 端口
|
||||
database: 2 # 数据库索引
|
||||
database: 0 # 数据库索引
|
||||
password: sadjklasnfasd # 密码,建议生产环境开启
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
@@ -116,10 +116,10 @@ rocketmq:
|
||||
spring:
|
||||
# RabbitMQ 配置项,对应 RabbitProperties 配置类 (手动连接)
|
||||
rabbitmq:
|
||||
host: 47.101.60.131 # RabbitMQ 服务的地址
|
||||
host: 115.120.213.238 # RabbitMQ 服务的地址
|
||||
port: 5672 # RabbitMQ 服务的端口
|
||||
username: admin # RabbitMQ 服务的账号
|
||||
password: fctBGJYD # RabbitMQ 服务的密码
|
||||
password: 19931f11 # RabbitMQ 服务的密码
|
||||
virtualHost: /exam_questions
|
||||
# Kafka 配置项,对应 KafkaProperties 配置类
|
||||
kafka:
|
||||
@@ -186,12 +186,12 @@ debug: false
|
||||
--- #################### 微信公众号、小程序相关配置 ####################
|
||||
wx:
|
||||
mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
|
||||
# app-id: wx041349c6f39b268b # 测试号(牛希尧提供的)
|
||||
# secret: 5abee519483bc9f8cb37ce280e814bd0
|
||||
# app-id: wx041349c6f39b268b # 测试号(牛希尧提供的)
|
||||
# secret: 5abee519483bc9f8cb37ce280e814bd0
|
||||
app-id: wx5b23ba7a5589ecbb # 测试号(自己的)
|
||||
secret: 2a7b3b20c537e52e74afd395eb85f61f
|
||||
# app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的)
|
||||
# secret: bd4f9fab889591b62aeac0d7b8d8b4a0
|
||||
# app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的)
|
||||
# secret: bd4f9fab889591b62aeac0d7b8d8b4a0
|
||||
# 存储配置,解决 AccessToken 的跨节点的共享
|
||||
config-storage:
|
||||
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
|
||||
@@ -200,10 +200,10 @@ wx:
|
||||
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
|
||||
# appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的)
|
||||
# secret: 333ae72f41552af1e998fe1f54e1584a
|
||||
# appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
|
||||
# secret: 6f270509224a7ae1296bbf1c8cb97aed
|
||||
# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
|
||||
# secret: 4a1a04e07f6a4a0751b39c3064a92c8b
|
||||
# appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
|
||||
# secret: 6f270509224a7ae1296bbf1c8cb97aed
|
||||
# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
|
||||
# secret: 4a1a04e07f6a4a0751b39c3064a92c8b
|
||||
appid: wx66186af0759f47c9 # 测试号(puhui 提供的)
|
||||
secret: 3218bcbd112cbc614c7264ceb20144ac
|
||||
config-storage:
|
||||
@@ -261,5 +261,5 @@ justauth:
|
||||
|
||||
--- #################### iot相关配置 ####################
|
||||
pf4j:
|
||||
# pluginsDir: /tmp/
|
||||
# pluginsDir: /tmp/
|
||||
pluginsDir: ../plugins
|
||||
Reference in New Issue
Block a user