diff --git a/detection/src/main/java/com/njcn/gather/detection/controller/LockController.java b/detection/src/main/java/com/njcn/gather/detection/controller/LockController.java new file mode 100644 index 00000000..26cddc15 --- /dev/null +++ b/detection/src/main/java/com/njcn/gather/detection/controller/LockController.java @@ -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 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); + } +} diff --git a/detection/src/main/java/com/njcn/gather/report/service/impl/PqReportServiceImpl.java b/detection/src/main/java/com/njcn/gather/report/service/impl/PqReportServiceImpl.java index d58532c3..e3e8eea4 100644 --- a/detection/src/main/java/com/njcn/gather/report/service/impl/PqReportServiceImpl.java +++ b/detection/src/main/java/com/njcn/gather/report/service/impl/PqReportServiceImpl.java @@ -1270,6 +1270,11 @@ public class PqReportServiceImpl extends ServiceImpl 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 i } + /** + * 判断书签段落之后是否还有有意义的内容(非空文本段落,或表格等非段落元素)。 + *

+ * 用于数模式 DATA_LINE 锚点位于 base 模板中间时,决定是否在插入的数据块末尾追加分页符, + * 使 base 剩余部分另起新页;书签在文档末尾(其后仅剩空段落)时返回 false,不追加分页符, + * 避免末尾凭空多出一页空白,存量模板(锚点在末尾)行为保持不变。 + *

+ * 注:仅以文本判断段落是否有意义,纯图片段落不会被识别为有效内容。 + * + * @param bookmarkInfo 书签信息 + * @return 书签之后存在有意义内容时返回 true + */ + private boolean hasContentAfterBookmark(BookmarkUtil.BookmarkInfo bookmarkInfo) { + List 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; + } + /** * 如何处理结果性数据进文档,各省级平台的结果表格不一致,如何做到一致