mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
feat(wiki): PR-7 archived soft-archive
This commit is contained in:
parent
850d59c04f
commit
1e62dbad47
@ -65,6 +65,14 @@ public class WikiPageEntity {
|
||||
*/
|
||||
private Integer locked;
|
||||
|
||||
/**
|
||||
* RFC-051 PR-7: soft-archive flag. {@code archived=1} hides the page from
|
||||
* default list / search / related results without destroying it. Used to
|
||||
* tuck away pages that are no longer relevant but whose history (citations,
|
||||
* source-raw lineage) should stay queryable.
|
||||
*/
|
||||
private Integer archived;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
@ -22,9 +22,9 @@ public interface WikiPageMapper extends BaseMapper<WikiPageEntity> {
|
||||
* DB keyword search (H2 + MySQL compatible LIKE).
|
||||
* Does not SELECT content CLOB to avoid loading large blobs into Java heap.
|
||||
*/
|
||||
@Select("SELECT id, kb_id, slug, title, summary, source_raw_ids, last_updated_by " +
|
||||
@Select("SELECT id, kb_id, slug, title, summary, source_raw_ids, last_updated_by, page_type " +
|
||||
"FROM mate_wiki_page " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 AND archived = 0 " +
|
||||
"AND (LOWER(title) LIKE #{pattern} OR LOWER(summary) LIKE #{pattern} " +
|
||||
" OR LOWER(content) LIKE #{pattern}) " +
|
||||
"ORDER BY title LIMIT 20")
|
||||
@ -37,14 +37,14 @@ public interface WikiPageMapper extends BaseMapper<WikiPageEntity> {
|
||||
*/
|
||||
@Select("<script>SELECT id, slug, title, summary, page_type AS pageType FROM mate_wiki_page " +
|
||||
"WHERE id IN <foreach collection='ids' item='id' open='(' separator=',' close=')'>#{id}</foreach> " +
|
||||
"AND deleted = 0</script>")
|
||||
"AND deleted = 0 AND archived = 0</script>")
|
||||
List<WikiPageLite> selectBatchLite(@Param("ids") Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* List all pages as lightweight projections (no content).
|
||||
*/
|
||||
@Select("SELECT id, slug, title, summary, page_type AS pageType FROM mate_wiki_page " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 ORDER BY update_time DESC")
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 AND archived = 0 ORDER BY update_time DESC")
|
||||
List<WikiPageLite> selectAllLite(@Param("kbId") Long kbId);
|
||||
|
||||
/**
|
||||
@ -59,7 +59,7 @@ public interface WikiPageMapper extends BaseMapper<WikiPageEntity> {
|
||||
* Phase 1 (fast): search only title + summary columns.
|
||||
*/
|
||||
@Select("SELECT id FROM mate_wiki_page " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 AND archived = 0 " +
|
||||
"AND (LOWER(title) LIKE #{kw} OR LOWER(summary) LIKE #{kw}) " +
|
||||
"LIMIT #{limit}")
|
||||
List<Long> searchFastIds(@Param("kbId") Long kbId,
|
||||
@ -70,7 +70,7 @@ public interface WikiPageMapper extends BaseMapper<WikiPageEntity> {
|
||||
* Phase 2 (slow): search full content, excluding already-found IDs.
|
||||
*/
|
||||
@Select("<script>SELECT id FROM mate_wiki_page " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 " +
|
||||
"WHERE kb_id = #{kbId} AND deleted = 0 AND archived = 0 " +
|
||||
"AND LOWER(content) LIKE #{kw} " +
|
||||
"<if test='excludeIds != null and !excludeIds.isEmpty()'>" +
|
||||
"AND id NOT IN <foreach collection='excludeIds' item='id' open='(' separator=',' close=')'>#{id}</foreach>" +
|
||||
|
||||
@ -98,11 +98,15 @@ public class WikiPageService {
|
||||
if (cached != null && !cached.isExpired()) {
|
||||
return cached.data;
|
||||
}
|
||||
// RFC-051 PR-7: archived pages are hidden from default summary listings;
|
||||
// PR-2 added page_type so callers can filter system pages too.
|
||||
List<WikiPageEntity> pages = pageMapper.selectList(
|
||||
new LambdaQueryWrapper<WikiPageEntity>()
|
||||
.select(WikiPageEntity::getSlug, WikiPageEntity::getTitle,
|
||||
WikiPageEntity::getSummary, WikiPageEntity::getLastUpdatedBy)
|
||||
WikiPageEntity::getSummary, WikiPageEntity::getLastUpdatedBy,
|
||||
WikiPageEntity::getPageType)
|
||||
.eq(WikiPageEntity::getKbId, kbId)
|
||||
.ne(WikiPageEntity::getArchived, 1)
|
||||
.orderByAsc(WikiPageEntity::getTitle));
|
||||
summaryCache.put(kbId, new CachedSummaries(pages, System.currentTimeMillis() + SUMMARY_CACHE_TTL_MS));
|
||||
return pages;
|
||||
@ -405,6 +409,33 @@ public class WikiPageService {
|
||||
evictSummaryCache(kbId);
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC-051 PR-7: flip the {@code archived} flag.
|
||||
* <p>
|
||||
* Archive hides the page from default list/search/related results without
|
||||
* destroying it. Citation lineage and source-raw links survive, so an
|
||||
* archived page can still be unarchived later or audited from raw history.
|
||||
* Refuses to archive a system page since those are part of the KB's spine.
|
||||
*
|
||||
* @param archive true to archive, false to unarchive
|
||||
* @return true on a state change, false if no-op (page missing or already in target state)
|
||||
*/
|
||||
@Transactional
|
||||
public boolean setArchived(Long kbId, String slug, boolean archive) {
|
||||
WikiPageEntity existing = getBySlug(kbId, slug);
|
||||
if (existing == null) return false;
|
||||
if ("system".equals(existing.getPageType())) {
|
||||
log.warn("[Wiki] Refusing to archive system page kbId={}, slug={}", kbId, slug);
|
||||
return false;
|
||||
}
|
||||
int target = archive ? 1 : 0;
|
||||
if (existing.getArchived() != null && existing.getArchived() == target) return false;
|
||||
existing.setArchived(target);
|
||||
pageMapper.updateById(existing);
|
||||
evictSummaryCache(kbId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除页面(按 slug 列表)
|
||||
*/
|
||||
|
||||
@ -450,6 +450,46 @@ public class WikiTool {
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Tool(description = """
|
||||
Archive a wiki page so it stops showing up in list / search / related
|
||||
results, without destroying it. Use this when a page is no longer
|
||||
relevant but its history (citations, raw lineage) should stay queryable.
|
||||
System pages (overview / log) cannot be archived.
|
||||
""")
|
||||
public String wiki_archive_page(
|
||||
@ToolParam(description = "Agent ID") Long agentId,
|
||||
@ToolParam(description = "Page slug to archive") String slug) {
|
||||
return setArchivedTool(agentId, slug, true, "archived");
|
||||
}
|
||||
|
||||
@Tool(description = """
|
||||
Unarchive a previously archived wiki page so it shows up in default
|
||||
list / search / related results again. No-op when the page wasn't archived.
|
||||
""")
|
||||
public String wiki_unarchive_page(
|
||||
@ToolParam(description = "Agent ID") Long agentId,
|
||||
@ToolParam(description = "Page slug to unarchive") String slug) {
|
||||
return setArchivedTool(agentId, slug, false, "unarchived");
|
||||
}
|
||||
|
||||
private String setArchivedTool(Long agentId, String slug, boolean archive, String verb) {
|
||||
if (slug == null || slug.isBlank()) return error("slug is required");
|
||||
Long kbId = resolveKbId(agentId);
|
||||
if (kbId == null) return error("No wiki knowledge base found for this agent");
|
||||
boolean changed;
|
||||
try {
|
||||
changed = pageService.setArchived(kbId, slug, archive);
|
||||
} catch (Exception e) {
|
||||
return error(verb + " failed: " + e.getMessage());
|
||||
}
|
||||
return JSONUtil.createObj()
|
||||
.set("ok", true)
|
||||
.set("slug", slug)
|
||||
.set("changed", changed)
|
||||
.set("message", changed ? "Page " + verb : "Page already in that state (or not found)")
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Tool(description = """
|
||||
Delete an AI-generated wiki page. Cannot delete manually curated pages.
|
||||
""")
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
-- V41: RFC-051 PR-7 — soft-archive flag.
|
||||
-- archived=1 hides a page from list / search / related results without
|
||||
-- destroying it, so re-ingesting the source raw doesn't regenerate it.
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS archived TINYINT NOT NULL DEFAULT 0;
|
||||
@ -0,0 +1,4 @@
|
||||
-- V41: RFC-051 PR-7 — soft-archive flag.
|
||||
SET @c := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mate_wiki_page' AND COLUMN_NAME = 'archived');
|
||||
SET @s := IF(@c = 0, 'ALTER TABLE mate_wiki_page ADD COLUMN archived TINYINT NOT NULL DEFAULT 0', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
Loading…
Reference in New Issue
Block a user