feat(report): 优化文档模板中数据行插入的分页处理

- 在数据行锚点后有内容时自动添加分页符,确保剩余内容另起新页
- 新增 hasContentAfterBookmark 方法判断锚点后是否存在有意义内容
- 避免锚点在文档末尾时产生多余空白页,保持存量模板兼容性
- 实现智能分页逻辑,仅对非空文本段落和表格等有效内容进行分页处理
This commit is contained in:
2026-06-11 18:12:46 +08:00
parent 29ddc8c2de
commit e2c7e745c8
2 changed files with 93 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
package com.njcn.gather.detection.controller;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.gather.detection.lock.DetectionLock;
import com.njcn.gather.detection.lock.DetectionLockManager;
import com.njcn.gather.detection.pojo.vo.DetectionLockHolderVO;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import com.njcn.web.utils.RequestUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 检测互斥锁管理接口。
* - GET /detection/lock/current 查询当前持锁状态;空闲返回 data=null
* - POST /detection/lock/forceRelease 管理员强制释放
*
* 鉴权:默认由 AuthGlobalFilter 做 JWT 校验,登录用户均可访问;
* 强释操作通过 @OperateInfo 落审计日志,谁操作谁担责。
*/
@Slf4j
@Api(tags = "检测互斥锁")
@RestController
@RequestMapping("/detection/lock")
@RequiredArgsConstructor
public class LockController extends BaseController {
@GetMapping("/current")
@ApiOperation("查询当前持锁状态;空闲返回 data=null")
public HttpResult<DetectionLockHolderVO> current() {
String methodDescribe = getMethodDescribe("current");
DetectionLock cur = DetectionLockManager.getInstance().getCurrent();
DetectionLockHolderVO data = cur == null ? null : DetectionLockManager.toHolderVO(cur);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, data, methodDescribe);
}
@PostMapping("/forceRelease")
@OperateInfo
@ApiOperation("管理员强制释放检测锁")
public HttpResult<?> forceRelease() {
String methodDescribe = getMethodDescribe("forceRelease");
String operator = RequestUtil.getUserId();
DetectionLockManager.getInstance().forceRelease(operator, "ADMIN_FORCE");
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
}

View File

@@ -1270,6 +1270,11 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
bookmarkInfo = BookmarkUtil.getBookmarkInfo(BookmarkEnum.DATA_LINE.getKey(), bookmarks);
todoInsertList = dealDataLine(detailDocumentPart, devReportParam, pqDevVO, resultMap);
if (Objects.nonNull(bookmarkInfo) && CollectionUtil.isNotEmpty(todoInsertList)) {
// 锚点位于 base 模板中间(其后仍有有效内容)时,在数据块末尾补一个分页符,
// 让 base 剩余部分另起新页;锚点在文档末尾时不加,存量模板行为不变
if (hasContentAfterBookmark(bookmarkInfo)) {
todoInsertList.add(Docx4jUtil.getPageBreak());
}
BookmarkUtil.insertElement(bookmarkInfo, todoInsertList);
BookmarkUtil.removeBookmark(bookmarkInfo);
}
@@ -1322,6 +1327,40 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
}
/**
* 判断书签段落之后是否还有有意义的内容(非空文本段落,或表格等非段落元素)。
* <p>
* 用于数模式 DATA_LINE 锚点位于 base 模板中间时,决定是否在插入的数据块末尾追加分页符,
* 使 base 剩余部分另起新页;书签在文档末尾(其后仅剩空段落)时返回 false不追加分页符
* 避免末尾凭空多出一页空白,存量模板(锚点在末尾)行为保持不变。
* <p>
* 注:仅以文本判断段落是否有意义,纯图片段落不会被识别为有效内容。
*
* @param bookmarkInfo 书签信息
* @return 书签之后存在有意义内容时返回 true
*/
private boolean hasContentAfterBookmark(BookmarkUtil.BookmarkInfo bookmarkInfo) {
List<Object> parentContent = bookmarkInfo.parentContainer.getContent();
int idx = parentContent.indexOf(bookmarkInfo.parentParagraph);
if (idx < 0) {
return false;
}
for (int i = idx + 1; i < parentContent.size(); i++) {
Object obj = parentContent.get(i);
Object realObj = (obj instanceof JAXBElement) ? ((JAXBElement<?>) obj).getValue() : obj;
if (realObj instanceof P) {
// 非空文本段落才算有意义,纯空段落不触发分页
if (StrUtil.isNotBlank(Docx4jUtil.getTextFromP((P) realObj))) {
return true;
}
} else {
// 表格等非段落元素一律视为有意义内容
return true;
}
}
return false;
}
/**
* 如何处理结果性数据进文档,各省级平台的结果表格不一致,如何做到一致