/** * 分行业知识库存储:按行业标识分区存储五类内容并校验完备性(Req 14.1, 14.4)。 * * - 按行业标识分区存储 Indicator、权重模板、Redline、典型案例、追问话术五类内容(Req 14.1)。 * - 新增分区缺任一类内容时拒绝创建、抛出指明缺失类别的校验错误, * 且保持已有分区不变(Req 14.4,{@link IncompletePartitionError})。 */ import type { Industry } from '../domain/common.js'; import type { IndustryPartition, KnowledgeBase } from '../domain/knowledge.js'; import { findMissingCategories } from './category.js'; import { IncompletePartitionError } from './errors.js'; /** 规范化行业标识用于分区键匹配(首尾空白不敏感)。 */ function normalizeIndustryId(industryId: Industry): string { return industryId.trim(); } /** * 分行业知识库存储(Knowledge_Base,Req 14.1, 14.4)。 * * 以行业标识为键登记 {@link IndustryPartition};新增分区前强制完备性校验, * 校验不通过则拒绝并保持已有分区不变。 */ export class KnowledgeBaseStore { /** 以规范化行业标识为键的分区表。 */ private readonly partitions = new Map(); /** * @param initial 初始登记的行业分区集合(默认空)。 * @throws {IncompletePartitionError} 当任一初始分区缺少五类必备内容时。 */ constructor(initial: readonly IndustryPartition[] = []) { for (const partition of initial) { this.addPartition(partition); } } /** * 新增一个行业分区(Req 14.1, 14.4)。 * * 先做五类内容完备性校验:缺任一类则拒绝创建、抛出 * {@link IncompletePartitionError}(指明缺失类别),且不修改任何已有分区。 * 校验通过后按行业标识登记该分区(同一行业再次新增将覆盖其内容)。 * * @throws {IncompletePartitionError} 当该分区缺少五类必备内容中的任一类时。 */ addPartition(partition: IndustryPartition): void { const missing = findMissingCategories(partition); if (missing.length > 0) { // 校验失败:拒绝创建,不改动 this.partitions(保持已有分区不变)。 throw new IncompletePartitionError(partition.industryId, missing); } this.partitions.set(normalizeIndustryId(partition.industryId), partition); } /** * 判断是否存在与给定行业标识匹配的分区。 */ has(industryId: Industry): boolean { return this.partitions.has(normalizeIndustryId(industryId)); } /** * 按行业标识获取分区;不存在时返回 undefined。 */ get(industryId: Industry): IndustryPartition | undefined { return this.partitions.get(normalizeIndustryId(industryId)); } /** 已登记分区数量。 */ get size(): number { return this.partitions.size; } /** * 导出当前知识库的不可变快照(Req 14.1)。 */ snapshot(): KnowledgeBase { return { partitions: [...this.partitions.values()] }; } }