/** * PR-37 Part C — Industry Epic Generator * * Generates domain-specific epics for a given strategy package. * Replaces the generic epic generation in SF-02's strategy-to-requirement.mjs * with domain-aware templates from the registry. * * @module pr-37-domain-intelligence/industry-epic-generator * @since PR-37 */ import { getDomain, listDomains } from "./domain-registry.mjs"; // ══════════════════════════════════════════════════════════ // Internal: ID counter // ══════════════════════════════════════════════════════════ let _epicCounter = 0; /** * Reset the internal epic ID counter (useful in tests). */ export function resetEpicCounter() { _epicCounter = 0; } /** * Generate the next sequential epic ID. * * @returns {string} ID like "ep-001" */ function nextEpicId() { _epicCounter++; return `ep-${String(_epicCounter).padStart(3, "0")}`; } // ══════════════════════════════════════════════════════════ // Generic fallback template // ══════════════════════════════════════════════════════════ /** * Default generic epics used when no domain is found or for unknown domains. * * @type {object[]} */ const GENERIC_EPIC_TEMPLATES = Object.freeze([ { title: "系统管理", category: "系统管理", objective: "基础系统配置与用户管理", successMetric: "系统可配置率 ≥ 90%", }, { title: "数据管理", category: "数据管理", objective: "核心数据维护与管理", successMetric: "数据完整率 ≥ 95%", }, { title: "用户管理", category: "用户管理", objective: "用户身份与权限管控", successMetric: "用户管理效率提升 50%", }, { title: "流程管理", category: "流程管理", objective: "核心业务流程数字化", successMetric: "流程处理时间缩短 40%", }, { title: "报表分析", category: "报表分析", objective: "多维度数据分析与报表", successMetric: "报表生成时间 < 30s", }, { title: "安全管理", category: "安全管理", objective: "数据安全与合规管控", successMetric: "安全事件为零", }, { title: "接口集成", category: "接口集成", objective: "系统间数据集成与同步", successMetric: "集成成功率 ≥ 99.5%", }, { title: "运维管理", category: "运维管理", objective: "系统运维与监控", successMetric: "系统可用性 ≥ 99.9%", }, ]); // ══════════════════════════════════════════════════════════ // Public API // ══════════════════════════════════════════════════════════ /** * Generate industry-specific epics from a strategy package for a given domain. * * @param {string} domainName - Target domain (e.g., "ERP", "MES", "CRM") * @param {object|string} strategyPackage - Strategy context; can be: * - An object with `productName`, `description`, and/or `goals` * - A plain string used as product name * @returns {object[]} Array of epic objects, each with: * - id (string): "ep-xxx" * - title (string): Epic title in Chinese * - description (string): Context-enriched description * - category (string): Category grouping * - objective (string): What this epic aims to achieve * - successMetric (string): Measurable success criterion * * @example * generateIndustryEpics("MES", { productName: "智能车间MES", description: "半导体封测", goals: ["良率提升"] }) * // => [ { id: "ep-001", title: "生产执行", ... }, ... ] */ export function generateIndustryEpics(domainName, strategyPackage) { // Normalize strategyPackage const strategyName = typeof strategyPackage === "string" ? strategyPackage : strategyPackage?.productName || ""; const strategyDesc = typeof strategyPackage === "string" ? strategyPackage : strategyPackage?.description || ""; const strategyGoals = typeof strategyPackage === "object" && !Array.isArray(strategyPackage) ? strategyPackage.goals || [] : []; const domain = getDomain(domainName); if (!domain) { // Fall back to generic template return generateGenericEpics(domainName, strategyName, strategyDesc, strategyGoals); } // Use domain's coreEpics as templates return domain.coreEpics.map((template) => { const enriched = enrichEpic(template, domain, strategyName, strategyDesc, strategyGoals); return { id: nextEpicId(), ...enriched, }; }); } // ══════════════════════════════════════════════════════════ // Internal helpers // ══════════════════════════════════════════════════════════ /** * Enrich a template epic with strategy context. * * @param {object} template - Core epic template { title, category, objective, successMetric } * @param {object} domain - Domain definition * @param {string} strategyName - Product strategy name * @param {string} strategyDesc - Strategy description * @param {string[]} strategyGoals - Strategy goals * @returns {object} Enriched epic */ function enrichEpic(template, domain, strategyName, strategyDesc, strategyGoals) { const title = template.title; const category = template.category; // Build a contextual description const contextParts = [ `基于 ${domain.displayName} (${domain.name}) 领域`, strategyName ? `为 "${strategyName}" 产品` : "", `实现 ${template.objective}`, ]; // Add strategy goals context if applicable if (strategyGoals.length > 0) { contextParts.push(`目标: ${strategyGoals.join("、")}`); } // Add any relevant process context const relatedProcesses = domain.coreProcesses.filter((p) => p.toLowerCase().includes(title.slice(0, 4).toLowerCase()) ); if (relatedProcesses.length > 0) { contextParts.push(`相关流程: ${relatedProcesses.join("; ")}`); } const description = contextParts.filter(Boolean).join("。") + "。"; return { title, description, category, objective: template.objective, successMetric: strategyGoals.length > 0 ? enrichMetric(template.successMetric, strategyGoals) : template.successMetric, }; } /** * Optionally enrich a success metric with strategy goals. * * @param {string} metric - Original success metric * @param {string[]} goals - Strategy goals * @returns {string} */ function enrichMetric(metric, goals) { const extraGoals = goals .filter((g) => !metric.toLowerCase().includes(g.toLowerCase())) .slice(0, 2); if (extraGoals.length === 0) return metric; return `${metric},同时支撑 ${extraGoals.join("、")}`; } /** * Generate generic epics for unknown domains. * * @param {string} domainName - Original domain name (may be null/unknown) * @param {string} strategyName - Product strategy name * @param {string} strategyDesc - Strategy description * @param {string[]} strategyGoals - Strategy goals * @returns {object[]} */ function generateGenericEpics(domainName, strategyName, strategyDesc, strategyGoals) { const domainLabel = domainName || "通用系统"; const prefix = strategyName ? `${strategyName} ` : ""; return GENERIC_EPIC_TEMPLATES.map((template) => ({ id: nextEpicId(), title: template.title, description: `${prefix}${domainLabel}领域 — ${template.objective}。${ strategyDesc ? `背景: ${strategyDesc}。` : "" }${strategyGoals.length > 0 ? `目标: ${strategyGoals.join("、")}。` : ""}`, category: template.category, objective: template.objective, successMetric: template.successMetric, })); } /** * Describe a domain briefly (utility function). * * @param {string} domainName - Domain name * @returns {string} Brief domain description */ export function describeDomain(domainName) { const domain = getDomain(domainName); if (!domain) return `${domainName || "Unknown"}: domain not found in registry`; return `${domain.displayName} (${domain.name}): ${domain.description}`; } /** * List epic titles available for a given domain. * * @param {string} domainName - Domain name * @returns {string[]} Epic title array, or generic titles if domain unknown */ export function listDomainEpics(domainName) { const domain = getDomain(domainName); if (!domain) return GENERIC_EPIC_TEMPLATES.map((e) => e.title); return domain.coreEpics.map((e) => e.title); }