| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- package com.dms.mapper;
- import com.dms.model.DocumentEntity;
- import java.time.LocalDateTime;
- import java.util.List;
- import java.util.Map;
- import org.apache.ibatis.annotations.Insert;
- import org.apache.ibatis.annotations.Options;
- import org.apache.ibatis.annotations.Param;
- import org.apache.ibatis.annotations.Select;
- import org.apache.ibatis.annotations.Update;
- /** doc_document 表访问(按阶段逐步扩展)。 */
- public interface DocumentMapper {
- /** 路径级联:同步挂载文档的 organization_name 快照(row_version+1)。 */
- @Update("UPDATE doc_document SET organization_name = #{orgName}, "
- + "updated_by = #{actorId}, updated_at = #{now}, "
- + "row_version = row_version + 1 "
- + "WHERE organization_id = #{organizationId} AND is_deleted = 0")
- int syncOrganizationName(@Param("organizationId") long organizationId,
- @Param("orgName") String orgName,
- @Param("actorId") Long actorId,
- @Param("now") LocalDateTime now);
- // ------------------------------------------------------------ 基础读取(阶段5.2/5.3)
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND is_deleted = 0")
- DocumentEntity findByIdNotDeleted(@Param("id") long id);
- /** 子方案权限源:root 主案(有效 MAIN)。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND document_type = 'MAIN' "
- + "AND is_deleted = 0")
- DocumentEntity findActiveMainById(@Param("id") long id);
- /** 分类父节点检查:直挂有效主案数(_valid_parent / update_category)。 */
- @Select("SELECT COUNT(*) FROM doc_document WHERE category_id = #{categoryId} "
- + "AND document_type = 'MAIN' AND is_deleted = 0")
- long countActiveMainByCategory(@Param("categoryId") long categoryId);
- /** 分类删除占用:直挂方案(MAIN+SUB_PLAN)数。 */
- @Select("SELECT COUNT(*) FROM doc_document WHERE category_id = #{categoryId} "
- + "AND is_deleted = 0 AND document_type IN ('MAIN', 'SUB_PLAN')")
- long countActivePlansByCategory(@Param("categoryId") long categoryId);
- /** 分类路径级联:同步直挂方案快照(rv+1),返回受影响行数。 */
- @Update("UPDATE doc_document SET category_name = #{categoryName}, "
- + "category_path = #{categoryPath}, updated_by = #{actorId}, "
- + "updated_at = #{now}, row_version = row_version + 1 "
- + "WHERE category_id = #{categoryId} AND is_deleted = 0 "
- + "AND document_type IN ('MAIN', 'SUB_PLAN')")
- int syncCategorySnapshot(@Param("categoryId") long categoryId,
- @Param("categoryName") String categoryName,
- @Param("categoryPath") String categoryPath,
- @Param("actorId") Long actorId,
- @Param("now") LocalDateTime now);
- // ------------------------------------------------------------ 密码锁(阶段5.3)
- /** 密码锁维护目标:锁定有效主案行。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND document_type = 'MAIN' "
- + "AND is_deleted = 0 FOR UPDATE")
- DocumentEntity findActiveMainForUpdate(@Param("id") long id);
- /** 密码锁整体写入(哈希 + 7 勾选位 + rv+1)。 */
- @Update("UPDATE doc_document SET password_hash = #{passwordHash}, "
- + "protect_view = #{protectView}, protect_download = #{protectDownload}, "
- + "protect_edit = #{protectEdit}, protect_delete = #{protectDelete}, "
- + "protect_config_permission = #{protectConfigPermission}, "
- + "protect_bind_attachment = #{protectBindAttachment}, "
- + "protect_unbind_attachment = #{protectUnbindAttachment}, "
- + "updated_by = #{updatedBy}, updated_at = #{updatedAt}, "
- + "row_version = row_version + 1 WHERE id = #{id}")
- int updatePasswordLock(DocumentEntity document);
- // ------------------------------------------------------------ 只读查询(阶段5.4)
- /**
- * 主案/子方案通用动态查询,对齐 document_query_service 的组合过滤:
- * types 必填;categoryIds/parentDocumentId/parentIds/titlePattern/contentPattern/
- * securityLevels/orgPattern/tags(配tagsMatch)/securityLevel/status/updated范围可选;
- * orderBy 为服务层白名单校验后的排序子串,缺省不排序(对齐 Python 无 order_by 场景)。
- */
- @Select("<script>SELECT * FROM doc_document WHERE is_deleted = 0 AND document_type IN "
- + "<foreach collection='types' item='t' open='(' separator=',' close=')'>#{t}</foreach>"
- + "<if test='ids != null'>AND id IN "
- + "<foreach collection='ids' item='i' open='(' separator=',' close=')'>"
- + "#{i}</foreach></if>"
- + "<if test='categoryIds != null'>AND category_id IN "
- + "<foreach collection='categoryIds' item='c' open='(' separator=',' close=')'>"
- + "#{c}</foreach></if>"
- + "<if test='parentDocumentId != null'>AND parent_document_id = #{parentDocumentId}</if>"
- + "<if test='parentIds != null'>AND parent_document_id IN "
- + "<foreach collection='parentIds' item='p' open='(' separator=',' close=')'>"
- + "#{p}</foreach></if>"
- + "<if test='titlePattern != null'>AND document_name LIKE #{titlePattern} ESCAPE '\\\\'</if>"
- + "<if test='contentPattern != null'>AND (content_text LIKE #{contentPattern} ESCAPE '\\\\' "
- + "OR search_text LIKE #{contentPattern} ESCAPE '\\\\')</if>"
- + "<if test='securityLevels != null'>AND security_level IN "
- + "<foreach collection='securityLevels' item='l' open='(' separator=',' close=')'>"
- + "#{l}</foreach></if>"
- + "<if test='orgPattern != null'>AND organization_name LIKE #{orgPattern} ESCAPE '\\\\'</if>"
- + "<if test='attachmentType != null'>AND attachment_type = #{attachmentType}</if>"
- + "<if test='fileExtension != null'>AND file_extension = #{fileExtension}</if>"
- + "<if test='tags != null'>AND (<choose>"
- + "<when test='tagsMatch == \"all\"'>"
- + "<foreach collection='tags' item='tg' separator=' AND '>JSON_CONTAINS(tags, #{tg})"
- + "</foreach></when><otherwise>"
- + "<foreach collection='tags' item='tg' separator=' OR '>JSON_CONTAINS(tags, #{tg})"
- + "</foreach></otherwise></choose>)</if>"
- + "<if test='securityLevel != null'>AND security_level = #{securityLevel}</if>"
- + "<if test='status != null'>AND document_status = #{status}</if>"
- + "<if test='updatedFrom != null'>AND updated_at >= #{updatedFrom}</if>"
- + "<if test='updatedTo != null'>AND updated_at <= #{updatedTo}</if>"
- + "<if test='orderBy != null'>ORDER BY ${orderBy}, id ASC</if>"
- + "</script>")
- List<DocumentEntity> listByFilters(Map<String, Object> filters);
- /** 对齐 _active_plan:详情目标(MAIN/SUB_PLAN)。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND is_deleted = 0 "
- + "AND document_type IN ('MAIN', 'SUB_PLAN')")
- DocumentEntity findActivePlanById(@Param("id") long id);
- /** _record_view 锁定行。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND is_deleted = 0 FOR UPDATE")
- DocumentEntity findNotDeletedForUpdate(@Param("id") long id);
- /** _record_view 查看计数自增。 */
- @Update("UPDATE doc_document SET view_count = view_count + 1 WHERE id = #{id}")
- int incrementViewCount(@Param("id") long id);
- // ------------------------------------------------------------ 写操作(阶段5.5)
- /** 新建方案/附件(密码锁/计数等由库默认承担,对齐 Python session.add)。 */
- @Insert("INSERT INTO doc_document (document_name, summary, document_type, "
- + "document_status, security_level, visibility_type, attachment_type, "
- + "category_id, category_name, category_path, organization_id, "
- + "organization_name, parent_document_id, root_document_id, tags, "
- + "original_file_name, file_relative_path, file_extension, mime_type, "
- + "file_size, file_hash, sort_no, content_text, content_extract_status, "
- + "content_extract_error, content_extracted_at, search_text, created_by, updated_by, "
- + "created_by_name, updated_by_name) VALUES (#{documentName}, #{summary}, "
- + "#{documentType}, #{documentStatus}, #{securityLevel}, #{visibilityType}, "
- + "#{attachmentType}, #{categoryId}, #{categoryName}, #{categoryPath}, "
- + "#{organizationId}, #{organizationName}, #{parentDocumentId}, "
- + "#{rootDocumentId}, #{tags}, #{originalFileName}, #{fileRelativePath}, "
- + "#{fileExtension}, #{mimeType}, #{fileSize}, #{fileHash}, #{sortNo}, "
- + "#{contentText}, #{contentExtractStatus}, #{contentExtractError}, "
- + "#{contentExtractedAt}, "
- + "#{searchText}, #{createdBy}, #{updatedBy}, #{createdByName}, "
- + "#{updatedByName})")
- @Options(useGeneratedKeys = true, keyProperty = "id")
- int insert(DocumentEntity document);
- /** 上传查重锁定:同名同类型未删(SUB_PLAN 限定父内,MAIN 全局),对齐 Python duplicate 条件。 */
- @Select("<script>SELECT * FROM doc_document WHERE document_name = #{name} "
- + "AND document_type = #{type} AND is_deleted = 0 "
- + "<if test='parentId != null'>AND parent_document_id = #{parentId}</if> "
- + "FOR UPDATE</script>")
- DocumentEntity findDuplicateForUpdate(@Param("name") String name, @Param("type") String type,
- @Param("parentId") Long parentId);
- /** 覆盖上传:全文件与元数据字段 + search_text + rv+1(document_name 不变)。 */
- @Update("UPDATE doc_document SET summary = #{summary}, security_level = #{securityLevel}, "
- + "category_id = #{categoryId}, category_name = #{categoryName}, "
- + "category_path = #{categoryPath}, organization_id = #{organizationId}, "
- + "organization_name = #{organizationName}, tags = #{tags}, "
- + "original_file_name = #{originalFileName}, "
- + "file_relative_path = #{fileRelativePath}, file_extension = #{fileExtension}, "
- + "mime_type = #{mimeType}, file_size = #{fileSize}, file_hash = #{fileHash}, "
- + "content_text = #{contentText}, content_extract_status = #{contentExtractStatus}, "
- + "content_extract_error = #{contentExtractError}, "
- + "content_extracted_at = #{contentExtractedAt}, search_text = #{searchText}, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt}, row_version = row_version + 1 WHERE id = #{id}")
- int updateOverwrite(DocumentEntity document);
- /** 上传时密码锁写入:仅密码字段(rv 不递增,对齐 Python 上传后 apply_password_lock 语义)。 */
- @Update("UPDATE doc_document SET password_hash = #{passwordHash}, "
- + "protect_view = #{protectView}, protect_download = #{protectDownload}, "
- + "protect_edit = #{protectEdit}, protect_delete = #{protectDelete}, "
- + "protect_config_permission = #{protectConfigPermission}, "
- + "protect_bind_attachment = #{protectBindAttachment}, "
- + "protect_unbind_attachment = #{protectUnbindAttachment} WHERE id = #{id}")
- int insertPasswordLock(DocumentEntity document);
- /** MAIN 换分类:子方案分类快照级联(rv+1)。 */
- @Update("UPDATE doc_document SET category_id = #{categoryId}, "
- + "category_name = #{categoryName}, category_path = #{categoryPath}, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt}, row_version = row_version + 1 "
- + "WHERE parent_document_id = #{mainId} AND document_type = 'SUB_PLAN' "
- + "AND is_deleted = 0")
- int syncSubPlanCategory(@Param("mainId") long mainId, @Param("categoryId") Long categoryId,
- @Param("categoryName") String categoryName,
- @Param("categoryPath") String categoryPath,
- @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName,
- @Param("updatedAt") LocalDateTime updatedAt);
- /** 新建主案排序末尾:全局 MAIN 最大 sort_no。 */
- @Select("SELECT COALESCE(MAX(sort_no), 0) FROM doc_document "
- + "WHERE document_type = 'MAIN' AND parent_document_id IS NULL AND is_deleted = 0")
- int maxMainSortNo();
- /** 新建子方案排序末尾:父内 SUB_PLAN 最大 sort_no。 */
- @Select("SELECT COALESCE(MAX(sort_no), 0) FROM doc_document "
- + "WHERE document_type = 'SUB_PLAN' AND parent_document_id = #{parentId} "
- + "AND is_deleted = 0")
- int maxSubPlanSortNo(@Param("parentId") long parentId);
- /** 方案编辑:元数据字段 + search_text + rv+1(对齐 update_document)。 */
- @Update("UPDATE doc_document SET document_name = #{documentName}, summary = #{summary}, "
- + "tags = #{tags}, category_id = #{categoryId}, category_name = #{categoryName}, "
- + "category_path = #{categoryPath}, organization_id = #{organizationId}, "
- + "organization_name = #{organizationName}, security_level = #{securityLevel}, "
- + "visibility_type = #{visibilityType}, search_text = #{searchText}, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt}, row_version = row_version + 1 WHERE id = #{id}")
- int updateMeta(DocumentEntity document);
- /** 附件编辑:元数据字段 + search_text + rv+1(对齐 update_attachment)。 */
- @Update("UPDATE doc_document SET document_name = #{documentName}, "
- + "attachment_type = #{attachmentType}, summary = #{summary}, tags = #{tags}, "
- + "organization_id = #{organizationId}, organization_name = #{organizationName}, "
- + "search_text = #{searchText}, updated_by = #{updatedBy}, "
- + "updated_by_name = #{updatedByName}, updated_at = #{updatedAt}, "
- + "row_version = row_version + 1 WHERE id = #{id}")
- int updateAttachmentMeta(DocumentEntity document);
- /** 描述编辑:仅 description(rv+1,对齐 update_document_description)。 */
- @Update("UPDATE doc_document SET description = #{description}, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt}, row_version = row_version + 1 WHERE id = #{id}")
- int updateDescription(DocumentEntity document);
- /**
- * re_extract_content:覆盖写回提取结果(含失败原因)并同步重建 search_text,
- * touch updated_by/name/at 与 rv+1。
- */
- @Update("UPDATE doc_document SET content_text = #{contentText}, "
- + "content_extract_status = #{contentExtractStatus}, "
- + "content_extract_error = #{contentExtractError}, "
- + "content_extracted_at = #{contentExtractedAt}, search_text = #{searchText}, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt}, row_version = row_version + 1 WHERE id = #{id}")
- int updateContentExtraction(DocumentEntity document);
- /** update_document 锁定行(MAIN/SUB_PLAN)。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} "
- + "AND document_type IN ('MAIN', 'SUB_PLAN') AND is_deleted = 0 FOR UPDATE")
- DocumentEntity findPlanForUpdate(@Param("id") long id);
- /** update_attachment/delete_attachment 锁定行。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND document_type = 'ATTACHMENT' "
- + "AND is_deleted = 0 FOR UPDATE")
- DocumentEntity findAttachmentForUpdate(@Param("id") long id);
- /** get_attachment 读取行(无锁)。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND document_type = 'ATTACHMENT' "
- + "AND is_deleted = 0")
- DocumentEntity findActiveAttachmentById(@Param("id") long id);
- /** 对齐 session.get(Document, id):主键读取,不过滤任何状态。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id}")
- DocumentEntity findByIdNoFilter(@Param("id") long id);
- /** 新建子方案:父 child_count+1、rv+1、updated_by/by_name(不触碰 updated_at)。 */
- @Update("UPDATE doc_document SET child_count = child_count + 1, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "row_version = row_version + 1 WHERE id = #{id}")
- int incrementChildCount(@Param("id") long id, @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName);
- /** 删除子方案:父 child_count=GREATEST(0,-1)、rv+1、updated_*。 */
- @Update("UPDATE doc_document SET child_count = GREATEST(0, child_count - 1), "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt}, row_version = row_version + 1 WHERE id = #{id}")
- int decrementChildCount(@Param("id") long id, @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName,
- @Param("updatedAt") LocalDateTime updatedAt);
- /** 删除主案:attachment_count 清零。 */
- @Update("UPDATE doc_document SET attachment_count = 0 WHERE id = #{id}")
- int setAttachmentCountZero(@Param("id") long id);
- /** 删除主案前置:有效子方案计数(parent 或 root 指向主案)。 */
- @Select("SELECT COUNT(*) FROM doc_document WHERE (parent_document_id = #{id} "
- + "OR root_document_id = #{id}) AND document_type = 'SUB_PLAN' AND is_deleted = 0")
- long countSubPlansByMain(@Param("id") long id);
- /** 软删文档:is_deleted、deleted_at、updated_by、updated_at、rv+1。 */
- @Update("UPDATE doc_document SET is_deleted = 1, deleted_at = #{now}, "
- + "updated_at = #{now}, updated_by = #{actorId}, updated_by_name = #{actorName}, "
- + "row_version = row_version + 1 WHERE id = #{id}")
- int softDelete(@Param("id") long id, @Param("now") LocalDateTime now,
- @Param("actorId") Long actorId, @Param("actorName") String actorName);
- /** 重排:ids 集合行锁定(含软删行,对齐 Python select...in 不加 is_deleted 过滤)。 */
- @Select("<script>SELECT * FROM doc_document WHERE id IN "
- + "<foreach collection='ids' item='i' open='(' separator=',' close=')'>"
- + "#{i}</foreach> ORDER BY id ASC FOR UPDATE</script>")
- List<DocumentEntity> listForUpdateByIds(@Param("ids") List<Long> ids);
- /** 重排:兄弟全集锁定(MAIN 全局 / SUB_PLAN 父内)。 */
- @Select("<script>SELECT * FROM doc_document WHERE is_deleted = 0 "
- + "<choose><when test='parentId != null'>AND document_type = 'SUB_PLAN' "
- + "AND parent_document_id = #{parentId}</when>"
- + "<otherwise>AND document_type = 'MAIN' AND parent_document_id IS NULL"
- + "</otherwise></choose> ORDER BY sort_no ASC, id ASC FOR UPDATE</script>")
- List<DocumentEntity> listSiblingsForOrder(@Param("parentId") Long parentId);
- /** 重排:仅写变化行(sort_no + updated_* + rv+1)。 */
- @Update("UPDATE doc_document SET sort_no = #{sortNo}, updated_by = #{updatedBy}, "
- + "updated_by_name = #{updatedByName}, updated_at = #{updatedAt}, "
- + "row_version = row_version + 1 WHERE id = #{id}")
- int updateSortNo(@Param("id") long id, @Param("sortNo") int sortNo,
- @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName,
- @Param("updatedAt") LocalDateTime updatedAt);
- // ------------------------------------------------------------ 下载/预览与挂载域(阶段5.6)
- /** 预览/下载入口:三类通用有效行(对齐 _document)。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} AND is_deleted = 0 "
- + "AND document_type IN ('MAIN', 'SUB_PLAN', 'ATTACHMENT')")
- DocumentEntity findReadableById(@Param("id") long id);
- /** _record_download 查看计数同款:下载计数自增。 */
- @Update("UPDATE doc_document SET download_count = download_count + 1 WHERE id = #{id}")
- int incrementDownloadCount(@Param("id") long id);
- /** 挂载/解挂后主案回写:attachment_count + rv+1 + updated_*。 */
- @Update("UPDATE doc_document SET attachment_count = #{attachmentCount}, "
- + "row_version = row_version + 1, updated_by = #{updatedBy}, "
- + "updated_by_name = #{updatedByName}, updated_at = #{updatedAt} WHERE id = #{id}")
- int updateAttachmentCount(@Param("id") long id,
- @Param("attachmentCount") long attachmentCount,
- @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName,
- @Param("updatedAt") LocalDateTime updatedAt);
- /** 挂载重排后主案元数据触碰:rv+1 + updated_*(不动 attachment_count)。 */
- @Update("UPDATE doc_document SET row_version = row_version + 1, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{updatedAt} WHERE id = #{id}")
- int bumpMeta(@Param("id") long id, @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName,
- @Param("updatedAt") LocalDateTime updatedAt);
- /**
- * has_any_manage_action:任一"已发布主案"命中(密级×部门范围×查看权三来源)。
- * scope 为 OWN/OTHER;集合为空时调用方传 [-1] 占位(对齐 Python
- * in_(空集)恒假、not_in(空集)恒真语义);canView=1 时矩阵行自身即查看来源。
- */
- @Select("<script>SELECT EXISTS(SELECT 1 FROM doc_document d "
- + "WHERE d.is_deleted = 0 AND d.document_status = 'PUBLISHED' "
- + "AND d.document_type = 'MAIN' AND d.security_level = #{securityLevel} "
- + "<choose>"
- + "<when test='scope == \"OWN\"'>AND d.organization_id IN "
- + "<foreach item='i' collection='ownIds' open='(' separator=',' close=')'>#{i}</foreach></when>"
- + "<otherwise>AND (d.organization_id IS NULL OR d.organization_id NOT IN "
- + "<foreach item='i' collection='ownIds' open='(' separator=',' close=')'>#{i}</foreach>)</otherwise>"
- + "</choose>"
- + "AND (d.visibility_type = 'ALL_AUTHENTICATED' "
- + "OR EXISTS(SELECT 1 FROM doc_permission p WHERE p.document_id = d.id "
- + "AND p.is_deleted = 0 AND p.can_view = 1 AND ("
- + "(p.subject_type = 'ORG' AND p.subject_id IN "
- + "<foreach item='i' collection='ancestorIds' open='(' separator=',' close=')'>#{i}</foreach>) "
- + "OR (p.subject_type = 'USER' AND p.subject_id = #{userId} "
- + "AND d.visibility_type = 'CUSTOM')))"
- + "<if test='canView'>OR TRUE</if>))</script>")
- boolean existsManageHit(@Param("securityLevel") String securityLevel,
- @Param("scope") String scope,
- @Param("ownIds") List<Long> ownIds,
- @Param("ancestorIds") List<Long> ancestorIds,
- @Param("userId") long userId,
- @Param("canView") boolean canView);
- /** _current_version:挂载唯一键冲突兜底的当前版本读取。 */
- @Select("SELECT row_version FROM doc_document WHERE id = #{id} AND is_deleted = 0")
- Integer currentRowVersion(@Param("id") long id);
- /** 权限保存:visibility + updated_* + rv+1(对齐 save_permissions 的文档 touch)。 */
- @Update("UPDATE doc_document SET visibility_type = #{visibility}, "
- + "updated_by = #{updatedBy}, updated_by_name = #{updatedByName}, "
- + "updated_at = #{now}, row_version = row_version + 1 WHERE id = #{id}")
- int updateVisibility(@Param("id") long id, @Param("visibility") String visibility,
- @Param("updatedBy") Long updatedBy,
- @Param("updatedByName") String updatedByName,
- @Param("now") LocalDateTime now);
- // ------------------------------------------------------------ 回收站(阶段7,对齐 recycle_bin_service.py)
- /**
- * 回收站分页查询(对齐 list_recycle_bin_documents):软删 + deleted_at 非空 +
- * 类型白名单;可选 documentType/categoryId/keyword 四字段 LIKE/deletedFrom≥/deletedTo<。
- * orderBy/idOrder 为服务层白名单校验后的 "列 方向" 子串。
- */
- @Select("<script>SELECT * FROM doc_document WHERE is_deleted = 1 "
- + "AND deleted_at IS NOT NULL "
- + "AND document_type IN ('ATTACHMENT', 'MAIN', 'SUB_PLAN')"
- + "<if test='f.documentType != null'>AND document_type = #{f.documentType}</if>"
- + "<if test='f.categoryId != null'>AND category_id = #{f.categoryId}</if>"
- + "<if test='f.keywordPattern != null'>AND (document_name LIKE #{f.keywordPattern} "
- + "ESCAPE '\\\\' OR summary LIKE #{f.keywordPattern} ESCAPE '\\\\' "
- + "OR search_text LIKE #{f.keywordPattern} ESCAPE '\\\\' "
- + "OR original_file_name LIKE #{f.keywordPattern} ESCAPE '\\\\')</if>"
- + "<if test='f.deletedFrom != null'>AND deleted_at >= #{f.deletedFrom}</if>"
- + "<if test='f.deletedTo != null'>AND deleted_at < #{f.deletedTo}</if>"
- + " ORDER BY ${f.orderBy}, id ${f.idOrder} "
- + "LIMIT #{f.pageSize} OFFSET #{f.offset}</script>")
- List<DocumentEntity> listDeletedPage(@Param("f") Map<String, Object> filters);
- /** 回收站计数(与 listDeletedPage 同条件,无分页/排序)。 */
- @Select("<script>SELECT COUNT(*) FROM doc_document WHERE is_deleted = 1 "
- + "AND deleted_at IS NOT NULL "
- + "AND document_type IN ('ATTACHMENT', 'MAIN', 'SUB_PLAN')"
- + "<if test='f.documentType != null'>AND document_type = #{f.documentType}</if>"
- + "<if test='f.categoryId != null'>AND category_id = #{f.categoryId}</if>"
- + "<if test='f.keywordPattern != null'>AND (document_name LIKE #{f.keywordPattern} "
- + "ESCAPE '\\\\' OR summary LIKE #{f.keywordPattern} ESCAPE '\\\\' "
- + "OR search_text LIKE #{f.keywordPattern} ESCAPE '\\\\' "
- + "OR original_file_name LIKE #{f.keywordPattern} ESCAPE '\\\\')</if>"
- + "<if test='f.deletedFrom != null'>AND deleted_at >= #{f.deletedFrom}</if>"
- + "<if test='f.deletedTo != null'>AND deleted_at < #{f.deletedTo}</if>"
- + "</script>")
- long countDeletedPage(@Param("f") Map<String, Object> filters);
- /** 对齐 _restore_operation 行锁读:主键 FOR UPDATE,不过滤删除状态。 */
- @Select("SELECT * FROM doc_document WHERE id = #{id} FOR UPDATE")
- DocumentEntity findByIdForUpdate(@Param("id") long id);
- /** 恢复后主案子案重数(AND 语义,区别于 countSubPlansByMain 的 OR 版)。 */
- @Select("SELECT COUNT(*) FROM doc_document WHERE document_type = 'SUB_PLAN' "
- + "AND parent_document_id = #{mainId} AND root_document_id = #{mainId} "
- + "AND is_deleted = 0")
- long countActiveSubPlansUnderMain(@Param("mainId") long mainId);
- /** 主案恢复批次附件锁定(对齐 attachments 锁读:in + id 升序 FOR UPDATE,不过滤)。 */
- @Select("<script>SELECT * FROM doc_document WHERE id IN "
- + "<foreach item='item' collection='ids' open='(' separator=',' close=')'>#{item}"
- + "</foreach> ORDER BY id ASC FOR UPDATE</script>")
- List<DocumentEntity> listByIdsForUpdate(@Param("ids") java.util.Collection<Long> ids);
- /** 子方案恢复:继承主案分类快照(对齐 Python 对象赋值 flush)。 */
- @Update("UPDATE doc_document SET category_id = #{categoryId}, "
- + "category_name = #{categoryName}, category_path = #{categoryPath} "
- + "WHERE id = #{id}")
- int updateCategorySnapshot(@Param("id") long id, @Param("categoryId") Long categoryId,
- @Param("categoryName") String categoryName,
- @Param("categoryPath") String categoryPath);
- /** 文档复活:is_deleted=0、deleted_at=NULL、updated_*、rv+1。 */
- @Update("UPDATE doc_document SET is_deleted = 0, deleted_at = NULL, "
- + "updated_at = #{now}, updated_by = #{actorId}, "
- + "updated_by_name = #{actorName}, row_version = row_version + 1 "
- + "WHERE id = #{id}")
- int restoreDocument(@Param("id") long id, @Param("now") LocalDateTime now,
- @Param("actorId") Long actorId, @Param("actorName") String actorName);
- /**
- * 主案恢复后 child_count/attachment_count 重数(对齐 SQLAlchemy 仅变化时落库:
- * 值不变则 0 行受影响,不触碰 updated_*)。
- */
- @Update("UPDATE doc_document SET child_count = #{childCount}, "
- + "attachment_count = #{attachmentCount} WHERE id = #{id} "
- + "AND (child_count != #{childCount} OR attachment_count != #{attachmentCount})")
- int restoreMainCounts(@Param("id") long id,
- @Param("childCount") long childCount,
- @Param("attachmentCount") long attachmentCount);
- /** 子方案恢复后主案 touch:child_count 重数 + updated_* + rv+1(无条件)。 */
- @Update("UPDATE doc_document SET child_count = #{childCount}, "
- + "updated_at = #{now}, updated_by = #{actorId}, "
- + "updated_by_name = #{actorName}, row_version = row_version + 1 "
- + "WHERE id = #{id}")
- int restoreParentCounts(@Param("id") long id, @Param("childCount") long childCount,
- @Param("now") LocalDateTime now, @Param("actorId") Long actorId,
- @Param("actorName") String actorName);
- }
|