【修改】文字考点,
学生进入考试 不单独从缓存中获取 ,推送试题中更新试题
This commit is contained in:
@@ -143,7 +143,11 @@ public class MonitorServiceImpl implements MonitorService {
|
|||||||
String key = "userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId();
|
String key = "userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId();
|
||||||
MonitorDO info = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), MonitorDO.class);
|
MonitorDO info = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), MonitorDO.class);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return 0L;
|
info=monitorMapper.selectOne(
|
||||||
|
new QueryWrapper<MonitorDO>()
|
||||||
|
.eq("task_id", stuMonitorPaperVo.getTaskId())
|
||||||
|
.eq("stu_id", stuMonitorPaperVo.getStuId())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
//获取属性 判断 专业/课程点位
|
//获取属性 判断 专业/课程点位
|
||||||
Long loginTenantId = SecurityFrameworkUtils.getLoginTenantId();
|
Long loginTenantId = SecurityFrameworkUtils.getLoginTenantId();
|
||||||
@@ -266,7 +270,7 @@ public class MonitorServiceImpl implements MonitorService {
|
|||||||
info.setTemporaryId(stuMonitorPaperVo.getTemporaryId());
|
info.setTemporaryId(stuMonitorPaperVo.getTemporaryId());
|
||||||
info.setIp(stuMonitorPaperVo.getIp());
|
info.setIp(stuMonitorPaperVo.getIp());
|
||||||
if (info.getRemainingTime() == null) {
|
if (info.getRemainingTime() == null) {
|
||||||
stringRedisTemplate.opsForValue().set("userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId(), JsonUtils.toJsonString(info));
|
stringRedisTemplate.opsForValue().set(key, JsonUtils.toJsonString(info));
|
||||||
monitorMapper.updateById(info);
|
monitorMapper.updateById(info);
|
||||||
return 24 * 60 * 60L; // (1天)
|
return 24 * 60 * 60L; // (1天)
|
||||||
} else {
|
} else {
|
||||||
@@ -320,6 +324,7 @@ public class MonitorServiceImpl implements MonitorService {
|
|||||||
info.setEndTime(end.format(fmt));
|
info.setEndTime(end.format(fmt));
|
||||||
// 判分后更新记录
|
// 判分后更新记录
|
||||||
monitorMapper.updateById(info);
|
monitorMapper.updateById(info);
|
||||||
|
stringRedisTemplate.opsForValue().set(key, JsonUtils.toJsonString(info));
|
||||||
return finalRemaining;
|
return finalRemaining;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -334,7 +339,7 @@ public class MonitorServiceImpl implements MonitorService {
|
|||||||
LocalDateTime target = now.plusSeconds((long) examTime.toLocalTime().toSecondOfDay());
|
LocalDateTime target = now.plusSeconds((long) examTime.toLocalTime().toSecondOfDay());
|
||||||
// 没有考场更新结束时间
|
// 没有考场更新结束时间
|
||||||
info.setEndTime(target.format(fmt));
|
info.setEndTime(target.format(fmt));
|
||||||
stringRedisTemplate.opsForValue().set("userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId(), JsonUtils.toJsonString(info));
|
stringRedisTemplate.opsForValue().set(key, JsonUtils.toJsonString(info));
|
||||||
monitorMapper.updateById(info);
|
monitorMapper.updateById(info);
|
||||||
return (long) examTime.toLocalTime().toSecondOfDay();
|
return (long) examTime.toLocalTime().toSecondOfDay();
|
||||||
}
|
}
|
||||||
|
@@ -810,7 +810,7 @@ public class ExamQuestionServiceImpl implements IExamQuestionService {
|
|||||||
// 批量插入
|
// 批量插入
|
||||||
examQuestionMapper.insertExamPsKeywordList(psKeywords);
|
examQuestionMapper.insertExamPsKeywordList(psKeywords);
|
||||||
}
|
}
|
||||||
examQuestionMapper.updateBatch(examQuestion);
|
examQuestionMapper.updateById(examQuestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -139,7 +139,7 @@
|
|||||||
<select id="getSchoolNameTotal" resultType="java.lang.Long">
|
<select id="getSchoolNameTotal" resultType="java.lang.Long">
|
||||||
SELECT count(*)
|
SELECT count(*)
|
||||||
FROM system_tenant
|
FROM system_tenant
|
||||||
WHERE id != 1 AND deleted = 0
|
WHERE deleted = 0
|
||||||
<if test="name != null and name != ''">
|
<if test="name != null and name != ''">
|
||||||
AND name LIKE CONCAT('%', #{name}, '%')
|
AND name LIKE CONCAT('%', #{name}, '%')
|
||||||
</if>
|
</if>
|
||||||
|
@@ -121,6 +121,8 @@
|
|||||||
<select id="selectExamSpecialtyBySpId" parameterType="Long" resultMap="ExamSpecialtyResult">
|
<select id="selectExamSpecialtyBySpId" parameterType="Long" resultMap="ExamSpecialtyResult">
|
||||||
<include refid="selectExamSpecialtyVo"/>
|
<include refid="selectExamSpecialtyVo"/>
|
||||||
where sp_id = #{spId}
|
where sp_id = #{spId}
|
||||||
|
and status = 0
|
||||||
|
and deleted = 0
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectExamSpecialtyBySpNameOne" parameterType="String" resultType="ExamSpecialty">
|
<select id="selectExamSpecialtyBySpNameOne" parameterType="String" resultType="ExamSpecialty">
|
||||||
@@ -147,6 +149,8 @@
|
|||||||
SELECT sp_id AS id, sp_name AS name, ancestors, parent_id, status, create_time
|
SELECT sp_id AS id, sp_name AS name, ancestors, parent_id, status, create_time
|
||||||
from exam_specialty
|
from exam_specialty
|
||||||
where parent_id = #{parentId}
|
where parent_id = #{parentId}
|
||||||
|
and status = 0
|
||||||
|
and deleted = 0
|
||||||
</select>
|
</select>
|
||||||
<select id="getRoleById" resultType="java.lang.String">
|
<select id="getRoleById" resultType="java.lang.String">
|
||||||
select roles
|
select roles
|
||||||
|
@@ -141,12 +141,20 @@ public class DocxMaster {
|
|||||||
Object[] arguments = {paragraph, stylePart, wordMLPackage, ndp};
|
Object[] arguments = {paragraph, stylePart, wordMLPackage, ndp};
|
||||||
String value = (String) methodWithArgs.invoke(obj, arguments);
|
String value = (String) methodWithArgs.invoke(obj, arguments);
|
||||||
if (value!=null){
|
if (value!=null){
|
||||||
// 写入结果
|
if ("getParagraphListLvlText".equals(function)||("getParagraphListAbstractNumId").equals(function)||("getParagraphListLvlFuHao").equals(function)){
|
||||||
judgementWordsVOS = setJudgementWord(
|
judgementWordsVOS = setJudgementWord(
|
||||||
judgementWordsVOS,
|
judgementWordsVOS,
|
||||||
docxFunction + "@" + value,
|
docxFunction + "@" + value,
|
||||||
firstName + examName + value
|
firstName + examName + "【是否正确】"
|
||||||
);
|
);
|
||||||
|
}else {
|
||||||
|
// 写入结果
|
||||||
|
judgementWordsVOS = setJudgementWord(
|
||||||
|
judgementWordsVOS,
|
||||||
|
docxFunction + "@" + value,
|
||||||
|
firstName + examName + value
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -78,7 +78,7 @@ public class Drawing {
|
|||||||
String width = String.format("%.2fcm", widthCm);
|
String width = String.format("%.2fcm", widthCm);
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
//布局 锁定横纵比
|
//布局 锁定横纵比
|
||||||
public static String getLayoutLock(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
public static String getLayoutLock(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect();
|
boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect();
|
||||||
@@ -134,6 +134,15 @@ public class Drawing {
|
|||||||
}
|
}
|
||||||
return "未知";
|
return "未知";
|
||||||
}
|
}
|
||||||
|
// 位置 → 水平 → 绝对位置
|
||||||
|
public static String getLayoutHorizontalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
|
if (anchor == null || anchor.getPositionH() == null) return "未知";
|
||||||
|
|
||||||
|
long offsetEmu = anchor.getPositionH().getPosOffset(); // EMU单位
|
||||||
|
double offsetCm = offsetEmu / 360000.0;
|
||||||
|
|
||||||
|
return String.format("相对于右侧%.2fcm", offsetCm);
|
||||||
|
}
|
||||||
|
|
||||||
// 位置 → 水平 → 相对于
|
// 位置 → 水平 → 相对于
|
||||||
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
@@ -172,7 +181,15 @@ public class Drawing {
|
|||||||
}
|
}
|
||||||
return "未知";
|
return "未知";
|
||||||
}
|
}
|
||||||
|
// 位置 → 垂直 → 绝对位置
|
||||||
|
public static String getLayoutVerticalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
|
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
||||||
|
|
||||||
|
long offsetEmu = anchor.getPositionV().getPosOffset(); // EMU单位
|
||||||
|
double offsetCm = offsetEmu / 360000.0;
|
||||||
|
|
||||||
|
return String.format("相对于下侧%.2fcm", offsetCm);
|
||||||
|
}
|
||||||
// 位置 → 垂直 → 相对于
|
// 位置 → 垂直 → 相对于
|
||||||
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
||||||
|
@@ -166,7 +166,14 @@ public class DrawingInline {
|
|||||||
return "未知";
|
return "未知";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 位置 → 水平 → 绝对位置
|
||||||
|
public static String getLayoutHorizontalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
|
return "无";
|
||||||
|
}
|
||||||
|
// 位置 → 垂直 → 绝对位置
|
||||||
|
public static String getLayoutVerticalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
|
return "无";
|
||||||
|
}
|
||||||
// 位置 → 水平 → 相对于
|
// 位置 → 水平 → 相对于
|
||||||
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||||
if (anchor == null) return "未知";
|
if (anchor == null) return "未知";
|
||||||
|
@@ -102,45 +102,71 @@ public class Paragraphs {
|
|||||||
}
|
}
|
||||||
/// 段落格式(缩进) 首行缩进 磅
|
/// 段落格式(缩进) 首行缩进 磅
|
||||||
public static String getParagraphIndFirstLine(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
public static String getParagraphIndFirstLine(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||||
|
if (paragraph == null) return "无";
|
||||||
|
|
||||||
PPr pPr = paragraph.getPPr();
|
PPr pPr = paragraph.getPPr();
|
||||||
PPrBase.Ind ind = pPr.getInd();
|
Double firstLine = null;
|
||||||
if (ind != null) {
|
|
||||||
if (ind.getFirstLine() != null) {
|
if (pPr != null && pPr.getInd() != null && pPr.getInd().getFirstLine() != null) {
|
||||||
return ind.getFirstLine().intValue() + " 磅";
|
firstLine = pPr.getInd().getFirstLine().doubleValue();
|
||||||
}
|
} else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
||||||
}
|
|
||||||
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
|
||||||
String styleId = pPr.getPStyle().getVal();
|
String styleId = pPr.getPStyle().getVal();
|
||||||
Style style = stylePart.getStyleById(styleId);
|
Style style = stylePart.getStyleById(styleId);
|
||||||
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null) {
|
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null &&
|
||||||
PPrBase.Ind indStyle = style.getPPr().getInd();
|
style.getPPr().getInd().getFirstLine() != null) {
|
||||||
if (indStyle.getFirstLine() != null) {
|
firstLine = style.getPPr().getInd().getFirstLine().doubleValue();
|
||||||
return indStyle.getFirstLine().intValue() + " 磅";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
if (firstLine == null) return "无";
|
||||||
|
|
||||||
|
// 1 字符 ≈ 200 twip(即 10 磅)
|
||||||
|
double chars = firstLine / 240.0;
|
||||||
|
|
||||||
|
// 四舍五入到 0.5
|
||||||
|
double rounded = Math.round(chars * 2) / 2.0;
|
||||||
|
|
||||||
|
// 如果是整数,去掉小数点
|
||||||
|
if (rounded == (int) rounded) {
|
||||||
|
return (int) rounded + " 字符";
|
||||||
|
} else {
|
||||||
|
return rounded + " 字符";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// 段落格式(缩进) 悬挂缩进 磅
|
/// 段落格式(缩进) 悬挂缩进 磅
|
||||||
public static String getParagraphIndHanging(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
public static String getParagraphIndHanging(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||||
|
if (paragraph == null) return "无";
|
||||||
|
|
||||||
PPr pPr = paragraph.getPPr();
|
PPr pPr = paragraph.getPPr();
|
||||||
PPrBase.Ind ind = pPr.getInd();
|
Double hanging = null;
|
||||||
if (ind != null) {
|
|
||||||
if (ind.getHanging() != null) {
|
// 1️⃣ 直接在段落中取
|
||||||
return ind.getHanging().intValue() + " 磅";
|
if (pPr != null && pPr.getInd() != null && pPr.getInd().getHanging() != null) {
|
||||||
}
|
hanging = pPr.getInd().getHanging().doubleValue();
|
||||||
}
|
}
|
||||||
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
// 2️⃣ 样式中取
|
||||||
|
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
||||||
String styleId = pPr.getPStyle().getVal();
|
String styleId = pPr.getPStyle().getVal();
|
||||||
Style style = stylePart.getStyleById(styleId);
|
Style style = stylePart.getStyleById(styleId);
|
||||||
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null) {
|
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null &&
|
||||||
PPrBase.Ind indStyle = style.getPPr().getInd();
|
style.getPPr().getInd().getHanging() != null) {
|
||||||
if (indStyle.getHanging() != null) {
|
hanging = style.getPPr().getInd().getHanging().doubleValue();
|
||||||
return indStyle.getHanging().intValue() + " 磅";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
|
if (hanging == null) return "无";
|
||||||
|
|
||||||
|
// 1 字符 ≈ 200 twip(10 磅)
|
||||||
|
double chars = hanging / 240.0;
|
||||||
|
|
||||||
|
// 四舍五入到 0.5
|
||||||
|
double rounded = Math.round(chars * 2) / 2.0;
|
||||||
|
|
||||||
|
// 输出为整数或 .5 字符
|
||||||
|
if (rounded == (int) rounded) {
|
||||||
|
return (int) rounded + " 字符";
|
||||||
|
} else {
|
||||||
|
return rounded + " 字符";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 段落格式(间距) 段前行
|
/// 段落格式(间距) 段前行
|
||||||
@@ -165,39 +191,75 @@ public class Paragraphs {
|
|||||||
}
|
}
|
||||||
/// 段落格式(间距) 段前磅
|
/// 段落格式(间距) 段前磅
|
||||||
public static String getParagraphSpacingBefore(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
public static String getParagraphSpacingBefore(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||||
|
if (paragraph == null) return "无";
|
||||||
|
|
||||||
PPr pPr = paragraph.getPPr();
|
PPr pPr = paragraph.getPPr();
|
||||||
if (pPr != null && pPr.getSpacing() != null) {
|
Double before = null;
|
||||||
if(pPr.getSpacing().getBefore()!=null){
|
|
||||||
return String.valueOf(pPr.getSpacing().getBefore().intValue() / 20)+"磅";
|
// 1️⃣ 段落自身定义
|
||||||
}
|
if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getBefore() != null) {
|
||||||
|
before = pPr.getSpacing().getBefore().doubleValue();
|
||||||
}
|
}
|
||||||
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
// 2️⃣ 从样式定义中继承
|
||||||
|
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
||||||
String styleId = pPr.getPStyle().getVal();
|
String styleId = pPr.getPStyle().getVal();
|
||||||
Style style = stylePart.getStyleById(styleId);
|
Style style = stylePart.getStyleById(styleId);
|
||||||
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null) {
|
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null &&
|
||||||
PPrBase.Spacing spacing = style.getPPr().getSpacing();
|
style.getPPr().getSpacing().getBefore() != null) {
|
||||||
return String.valueOf(style.getPPr().getSpacing().getBefore().intValue() / 20)+"磅";
|
before = style.getPPr().getSpacing().getBefore().doubleValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
|
if (before == null) return "";
|
||||||
|
|
||||||
|
// 转换为磅(1磅 = 20twip)
|
||||||
|
double point = before / 20.0;
|
||||||
|
|
||||||
|
// 四舍五入到 0.5
|
||||||
|
double rounded = Math.round(point * 2) / 2.0;
|
||||||
|
|
||||||
|
// 输出格式
|
||||||
|
if (rounded == (int) rounded) {
|
||||||
|
return (int) rounded + " 磅";
|
||||||
|
} else {
|
||||||
|
return rounded + " 磅";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// 段落格式(间距) 段后磅
|
/// 段落格式(间距) 段后磅
|
||||||
public static String getParagraphSpacingAfter(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
public static String getParagraphSpacingAfter(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||||
|
if (paragraph == null) return "无";
|
||||||
|
|
||||||
PPr pPr = paragraph.getPPr();
|
PPr pPr = paragraph.getPPr();
|
||||||
if (pPr != null && pPr.getSpacing() != null) {
|
Double after = null;
|
||||||
if(pPr.getSpacing().getAfter()!=null){
|
|
||||||
return String.valueOf(pPr.getSpacing().getAfter().intValue() / 20)+"磅";
|
// 1️⃣ 段落自身定义
|
||||||
}
|
if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getAfter() != null) {
|
||||||
|
after = pPr.getSpacing().getAfter().doubleValue();
|
||||||
}
|
}
|
||||||
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
// 2️⃣ 样式定义
|
||||||
|
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
|
||||||
String styleId = pPr.getPStyle().getVal();
|
String styleId = pPr.getPStyle().getVal();
|
||||||
Style style = stylePart.getStyleById(styleId);
|
Style style = stylePart.getStyleById(styleId);
|
||||||
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null) {
|
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null &&
|
||||||
PPrBase.Spacing spacing = style.getPPr().getSpacing();
|
style.getPPr().getSpacing().getAfter() != null) {
|
||||||
return String.valueOf(style.getPPr().getSpacing().getAfter().intValue() / 20)+"磅";
|
after = style.getPPr().getSpacing().getAfter().doubleValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
|
if (after == null) return "";
|
||||||
|
|
||||||
|
// 转换为磅(1 磅 = 20 twip)
|
||||||
|
double point = after / 20.0;
|
||||||
|
|
||||||
|
// 四舍五入到 0.5 磅
|
||||||
|
double rounded = Math.round(point * 2) / 2.0;
|
||||||
|
|
||||||
|
// 输出格式:整数不带小数,半磅保留 .5
|
||||||
|
if (rounded == (int) rounded) {
|
||||||
|
return (int) rounded + " 磅";
|
||||||
|
} else {
|
||||||
|
return rounded + " 磅";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 段落格式(间距) 段后行
|
/// 段落格式(间距) 段后行
|
||||||
@@ -510,6 +572,57 @@ public class Paragraphs {
|
|||||||
}
|
}
|
||||||
return "无";
|
return "无";
|
||||||
}
|
}
|
||||||
|
/// 段落 编号列表 编号序号
|
||||||
|
public static String getParagraphListLvlNumber(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||||
|
PPr pPr = paragraph.getPPr();
|
||||||
|
if (pPr == null || pPr.getNumPr() == null) return "无";
|
||||||
|
|
||||||
|
BigInteger numId = pPr.getNumPr().getNumId() != null ? pPr.getNumPr().getNumId().getVal() : null;
|
||||||
|
BigInteger ilvl = pPr.getNumPr().getIlvl() != null ? pPr.getNumPr().getIlvl().getVal() : null;
|
||||||
|
if (numId == null || ilvl == null || ndp == null) return "无";
|
||||||
|
|
||||||
|
// 获取文档所有段落
|
||||||
|
List<Object> paragraphs = wordMLPackage.getMainDocumentPart().getContent();
|
||||||
|
int count = 0;
|
||||||
|
for (Object obj : paragraphs) {
|
||||||
|
if (!(obj instanceof P)) continue;
|
||||||
|
P p = (P) obj;
|
||||||
|
if (p == paragraph) break; // 到当前段落停止计数
|
||||||
|
PPr pp = p.getPPr();
|
||||||
|
if (pp != null && pp.getNumPr() != null) {
|
||||||
|
BigInteger nid = pp.getNumPr().getNumId() != null ? pp.getNumPr().getNumId().getVal() : null;
|
||||||
|
BigInteger lvl = pp.getNumPr().getIlvl() != null ? pp.getNumPr().getIlvl().getVal() : null;
|
||||||
|
if (numId.equals(nid) && ilvl.equals(lvl)) count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 Lvl 对象
|
||||||
|
Numbering.Num num = ndp.getJaxbElement().getNum().stream()
|
||||||
|
.filter(n -> n.getNumId().equals(numId))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
if (num == null) return "无";
|
||||||
|
|
||||||
|
BigInteger abstractNumId = num.getAbstractNumId().getVal();
|
||||||
|
Numbering.AbstractNum absNum = Convert.getAbstractNumById(ndp, abstractNumId);
|
||||||
|
if (absNum == null) return "无";
|
||||||
|
|
||||||
|
Lvl lvl = absNum.getLvl().stream()
|
||||||
|
.filter(l -> l.getIlvl().equals(ilvl))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
if (lvl == null) return "无";
|
||||||
|
|
||||||
|
// 获取编号模板和起始值
|
||||||
|
String lvlText = lvl.getLvlText() != null ? lvl.getLvlText().getVal() : "%1.";
|
||||||
|
BigInteger start = lvl.getStart() != null ? lvl.getStart().getVal() : BigInteger.ONE;
|
||||||
|
|
||||||
|
// 实际编号
|
||||||
|
int actualNumber = start.intValue() + count;
|
||||||
|
|
||||||
|
// 替换模板中的 %1、%2… 注意 ilvl 从 0 开始
|
||||||
|
String result = lvlText.replace("%" + (ilvl.intValue() + 1), String.valueOf(actualNumber));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// 段落边框 样式(带汉化)
|
// 段落边框 样式(带汉化)
|
||||||
public static String getParagraphBorderStyle(P paragraph, StyleDefinitionsPart stylePart,
|
public static String getParagraphBorderStyle(P paragraph, StyleDefinitionsPart stylePart,
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
package pc.exam.pp.module.judgement.utils.wps_word.docx4j.section;
|
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.JAXBElement;
|
||||||
import jakarta.xml.bind.Marshaller;
|
import jakarta.xml.bind.JAXBException;
|
||||||
|
import org.docx4j.TextUtils;
|
||||||
import org.docx4j.XmlUtils;
|
import org.docx4j.XmlUtils;
|
||||||
import org.docx4j.dml.Graphic;
|
import org.docx4j.dml.Graphic;
|
||||||
import org.docx4j.dml.GraphicData;
|
import org.docx4j.dml.GraphicData;
|
||||||
import org.docx4j.dml.wordprocessingDrawing.Anchor;
|
import org.docx4j.dml.wordprocessingDrawing.Anchor;
|
||||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||||
import org.docx4j.mce.AlternateContent;
|
import org.docx4j.mce.AlternateContent;
|
||||||
import org.docx4j.model.structure.DocumentModel;
|
import org.docx4j.model.fields.FieldUpdater;
|
||||||
import org.docx4j.model.structure.HeaderFooterPolicy;
|
import org.docx4j.model.structure.HeaderFooterPolicy;
|
||||||
import org.docx4j.model.structure.PageDimensions;
|
import org.docx4j.model.structure.PageDimensions;
|
||||||
import org.docx4j.model.structure.SectionWrapper;
|
import org.docx4j.model.structure.SectionWrapper;
|
||||||
@@ -17,29 +17,27 @@ import org.docx4j.openpackaging.exceptions.Docx4JException;
|
|||||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||||
import org.docx4j.openpackaging.parts.Part;
|
import org.docx4j.openpackaging.parts.Part;
|
||||||
import org.docx4j.openpackaging.parts.WordprocessingML.*;
|
import org.docx4j.openpackaging.parts.WordprocessingML.*;
|
||||||
import org.docx4j.vml.CTFill;
|
import org.docx4j.vml.CTTextbox;
|
||||||
import org.docx4j.wml.*;
|
import org.docx4j.wml.*;
|
||||||
import org.docx4j.wml.CTBackground;
|
|
||||||
import org.docx4j.wml.CTBorder;
|
import org.docx4j.wml.CTBorder;
|
||||||
import org.docx4j.wml.CTColumn;
|
import org.docx4j.wml.CTColumn;
|
||||||
import org.docx4j.wml.CTColumns;
|
import org.docx4j.wml.CTColumns;
|
||||||
import org.docx4j.wml.CTDocGrid;
|
import org.docx4j.wml.CTDocGrid;
|
||||||
import org.docx4j.wml.CTShd;
|
|
||||||
import org.docx4j.wml.STPageOrientation;
|
import org.docx4j.wml.STPageOrientation;
|
||||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
|
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
|
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
|
||||||
import javax.xml.xpath.XPath;
|
import javax.xml.xpath.XPath;
|
||||||
import javax.xml.xpath.XPathConstants;
|
import javax.xml.xpath.XPathConstants;
|
||||||
import javax.xml.xpath.XPathExpression;
|
import javax.xml.xpath.XPathExpression;
|
||||||
import javax.xml.xpath.XPathFactory;
|
import javax.xml.xpath.XPathFactory;
|
||||||
import java.io.StringWriter;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -78,7 +76,9 @@ public class SectionPage {
|
|||||||
long topTwips = pgMar.getTop().longValue(); // twips
|
long topTwips = pgMar.getTop().longValue(); // twips
|
||||||
double topPt = topTwips / 20.0; // 转换为磅
|
double topPt = topTwips / 20.0; // 转换为磅
|
||||||
double topCm = topPt * 0.0352778; // 转换为厘米
|
double topCm = topPt * 0.0352778; // 转换为厘米
|
||||||
return String.format("%.1f磅(%.2f厘米)", topPt, topCm);
|
// return String.format("%.1f磅(%.2f厘米)", topPt, topCm);
|
||||||
|
return String.format("%.2f厘米", topCm);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -95,7 +95,8 @@ public class SectionPage {
|
|||||||
long bottomTwips = pgMar.getBottom().longValue(); // twips
|
long bottomTwips = pgMar.getBottom().longValue(); // twips
|
||||||
double bottomPt = bottomTwips / 20.0; // 磅
|
double bottomPt = bottomTwips / 20.0; // 磅
|
||||||
double bottomCm = bottomPt * 0.0352778; // 厘米
|
double bottomCm = bottomPt * 0.0352778; // 厘米
|
||||||
return String.format("%.1f磅(%.2f厘米)", bottomPt, bottomCm);
|
// return String.format("%.1f磅(%.2f厘米)", bottomPt, bottomCm);
|
||||||
|
return String.format("%.2f厘米", bottomCm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -112,7 +113,8 @@ public class SectionPage {
|
|||||||
long leftTwips = pgMar.getLeft().longValue(); // twips
|
long leftTwips = pgMar.getLeft().longValue(); // twips
|
||||||
double leftPt = leftTwips / 20.0; // 转换为磅
|
double leftPt = leftTwips / 20.0; // 转换为磅
|
||||||
double leftCm = leftPt * 0.0352778; // 转换为厘米
|
double leftCm = leftPt * 0.0352778; // 转换为厘米
|
||||||
return String.format("%.1f磅(%.2f厘米)", leftPt, leftCm);
|
// return String.format("%.1f磅(%.2f厘米)", leftPt, leftCm);
|
||||||
|
return String.format("%.2f厘米", leftCm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -129,7 +131,8 @@ public class SectionPage {
|
|||||||
long rightTwips = pgMar.getRight().longValue(); // twips
|
long rightTwips = pgMar.getRight().longValue(); // twips
|
||||||
double rightPt = rightTwips / 20.0; // 转换为磅
|
double rightPt = rightTwips / 20.0; // 转换为磅
|
||||||
double rightCm = rightPt * 0.0352778; // 转换为厘米
|
double rightCm = rightPt * 0.0352778; // 转换为厘米
|
||||||
return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm);
|
return String.format("%.2f厘米", rightCm);
|
||||||
|
// return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -146,7 +149,8 @@ public class SectionPage {
|
|||||||
long gutterTwips = pgMar.getGutter().longValue(); // twips
|
long gutterTwips = pgMar.getGutter().longValue(); // twips
|
||||||
double gutterPt = gutterTwips / 20.0; // 转换为磅
|
double gutterPt = gutterTwips / 20.0; // 转换为磅
|
||||||
double gutterCm = gutterPt * 0.0352778; // 转换为厘米
|
double gutterCm = gutterPt * 0.0352778; // 转换为厘米
|
||||||
return String.format("%.1f磅(%.2f厘米)", gutterPt, gutterCm);
|
// return String.format("%.1f磅(%.2f厘米)", gutterPt, gutterCm);
|
||||||
|
return String.format("%.2f厘米", gutterCm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -1170,33 +1174,130 @@ public class SectionPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 页脚(元素) 包含页码 返回 是/否
|
// 页脚(元素) 页码
|
||||||
public static String getHeaderContainsPageNumber(
|
public static String getHeaderContainsPageNumber(
|
||||||
P paragraph,
|
P paragraph,
|
||||||
HeaderFooterPolicy hfp,
|
HeaderFooterPolicy hfp,
|
||||||
WordprocessingMLPackage wordMLPackage,
|
WordprocessingMLPackage wordMLPackage,
|
||||||
List<SectionWrapper> sections) {
|
List<SectionWrapper> sections) throws IOException, SAXException, Docx4JException, JAXBException {
|
||||||
|
if (hfp == null) return "无页码";
|
||||||
|
|
||||||
if (hfp == null) return "否";
|
// 更新字段,保证获取显示值
|
||||||
|
FieldUpdater updater = new FieldUpdater(wordMLPackage);
|
||||||
// 获取页脚对象列表
|
updater.update(true);
|
||||||
FooterPart[] footerParts = new FooterPart[]{
|
FooterPart[] footerParts = new FooterPart[]{
|
||||||
hfp.getFirstFooter(),
|
hfp.getFirstFooter(),
|
||||||
hfp.getEvenFooter(),
|
hfp.getEvenFooter(),
|
||||||
hfp.getDefaultFooter()
|
hfp.getDefaultFooter()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Set<String> chineseNumbers = Set.of("一","二","三","四","五","六","七","八","九","十");
|
||||||
|
Set<String> capitalChineseNumbers = Set.of("壹","贰","叁","肆","伍","陆","柒","捌","玖","拾");
|
||||||
|
|
||||||
for (FooterPart fp : footerParts) {
|
for (FooterPart fp : footerParts) {
|
||||||
if (fp == null) continue;
|
if (fp == null) continue;
|
||||||
|
|
||||||
String xml = XmlUtils.marshaltoString(fp.getJaxbElement(), true, true);
|
String xml = XmlUtils.marshaltoString(fp.getJaxbElement(), true, true);
|
||||||
System.out.println("Footer XML:\n" + xml);
|
org.w3c.dom.Document doc = XmlUtils.getNewDocumentBuilder()
|
||||||
// 判断是否包含页码域 (PAGE 或 NUMPAGES)
|
.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
|
||||||
if (xml.contains("PAGE") || xml.contains("NUMPAGES")) {
|
|
||||||
return "是";
|
NodeList pNodes = doc.getElementsByTagNameNS(
|
||||||
|
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "p");
|
||||||
|
|
||||||
|
for (int i = 0; i < pNodes.getLength(); i++) {
|
||||||
|
org.w3c.dom.Element pElem = (org.w3c.dom.Element) pNodes.item(i);
|
||||||
|
|
||||||
|
// 检查 PAGE / NUMPAGES
|
||||||
|
NodeList instrNodes = pElem.getElementsByTagNameNS(
|
||||||
|
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "instrText");
|
||||||
|
boolean hasPAGE = false, hasNUMPAGES = false;
|
||||||
|
for (int j = 0; j < instrNodes.getLength(); j++) {
|
||||||
|
String instr = instrNodes.item(j).getTextContent();
|
||||||
|
if (instr.contains("PAGE")) hasPAGE = true;
|
||||||
|
if (instr.contains("NUMPAGES")) hasNUMPAGES = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拼接 run 内文本 + 字段文本
|
||||||
|
NodeList rNodes = pElem.getElementsByTagNameNS(
|
||||||
|
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "r");
|
||||||
|
|
||||||
|
StringBuilder textBuilder = new StringBuilder();
|
||||||
|
for (int j = 0; j < rNodes.getLength(); j++) {
|
||||||
|
Element rElem = (Element) rNodes.item(j);
|
||||||
|
|
||||||
|
NodeList tNodes = rElem.getElementsByTagNameNS(
|
||||||
|
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "t");
|
||||||
|
for (int k = 0; k < tNodes.getLength(); k++) {
|
||||||
|
textBuilder.append(tNodes.item(k).getTextContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeList instrNodesR = rElem.getElementsByTagNameNS(
|
||||||
|
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "instrText");
|
||||||
|
for (int k = 0; k < instrNodesR.getLength(); k++) {
|
||||||
|
textBuilder.append(instrNodesR.item(k).getTextContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = textBuilder.toString().trim();
|
||||||
|
System.out.println("页脚文本内容: " + text);
|
||||||
|
if (text.isEmpty()) continue;
|
||||||
|
|
||||||
|
boolean containsChineseNum = chineseNumbers.stream().anyMatch(text::contains);
|
||||||
|
boolean containsDigit = text.matches(".*\\d+.*");
|
||||||
|
// -------------------- 核心逻辑 --------------------
|
||||||
|
// 数字斜杠序列
|
||||||
|
if (text.matches(".*\\d+\\s*/\\s*(?:\\d+|PAGE|NUMPAGES).*")) {
|
||||||
|
return "1 / x";
|
||||||
|
}
|
||||||
|
if (containsChineseNum && hasPAGE && hasNUMPAGES) {
|
||||||
|
return "第一页 共x页"; // WPS 中文 + PAGE + NUMPAGES
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containsDigit && hasPAGE && hasNUMPAGES) {
|
||||||
|
return "第1页 共x页"; // 普通数字 + PAGE + NUMPAGES
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containsChineseNum && hasPAGE && !hasNUMPAGES) {
|
||||||
|
return "第一页"; // WPS 中文单页
|
||||||
|
}
|
||||||
|
// ✅ 关键部分:支持短横线页码形式
|
||||||
|
if (text.matches(".*[-–]\\s*\\d+\\s*[-–].*")) {
|
||||||
|
return "-1-, -2-, -3- ..."; // 短横线包裹数字
|
||||||
|
}
|
||||||
|
if (text.matches(".*[—]\\s*\\d+\\s*[—].*")) {
|
||||||
|
return "— 1 —, — 2 —, — 3 — ..."; // 数字横杠序列
|
||||||
|
}
|
||||||
|
// 半角数字序列
|
||||||
|
if (text.matches("\\d+\\s*PAGE.*\\d+\\s*PAGE.*")) {
|
||||||
|
return "1, 2, 3 ..."; // 数字序列
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全角数字序列
|
||||||
|
if (text.matches(".*[0-9]+\\s*PAGE.*[0-9]+\\s*PAGE.*")) {
|
||||||
|
return "1,2,3 ...";
|
||||||
|
}
|
||||||
|
if (containsDigit && hasPAGE && !hasNUMPAGES) {
|
||||||
|
return "第1页"; // 普通数字单页
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.matches("[IVXLCDM]+.*")) {
|
||||||
|
return "I, II, III ..."; // 罗马数字
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.matches("[A-Z]+\\d*.*")) {
|
||||||
|
return "A, B, C ..."; // 大写字母
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.matches("[a-z]+\\d*.*")) {
|
||||||
|
return "a, b, c ..."; // 小写字母
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "否";
|
return "无页码";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1204,9 +1305,6 @@ public class SectionPage {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// public static void readTextWatermark(
|
// public static void readTextWatermark(
|
||||||
// P paragraph,
|
// P paragraph,
|
||||||
// HeaderFooterPolicy hfp,
|
// HeaderFooterPolicy hfp,
|
||||||
|
Reference in New Issue
Block a user