ModelRepository.java 17 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
package com.onto.engine.model;

import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * 本体元模型仓库:启动时(及热重载时)从 models 目录读取全部 YAML 本体文件,
 * 构建内存元模型,并向引擎其余部分提供"按 M0-M8/MetaRule 语义"的类型化查询。
 *
 * <p>这是"架构引擎负责解释与执行"的解释根:所有下游逻辑都只读本仓库,不硬编码任何业务字段。
 */
@Component
public class ModelRepository {

    private static final Logger log = LoggerFactory.getLogger(ModelRepository.class);

    /** 层短码 -> 文件名。顺序即模型查看器展示顺序。 */
    private static final LinkedHashMap<String, String> LAYER_FILES = new LinkedHashMap<>();
    static {
        LAYER_FILES.put("M0", "M0-meta-schema.yaml");
        LAYER_FILES.put("M1", "M1-domain.yaml");
        LAYER_FILES.put("M2", "M2-command.yaml");
        LAYER_FILES.put("ME", "ME-event.yaml");
        LAYER_FILES.put("M3", "M3-deploy.yaml");
        LAYER_FILES.put("M4", "M4-scene.yaml");
        LAYER_FILES.put("M5", "M5-security.yaml");
        LAYER_FILES.put("M6", "M6-monitor.yaml");
        LAYER_FILES.put("M7", "M7-sla.yaml");
        LAYER_FILES.put("MetaRule", "MetaRule-business.yaml");
        LAYER_FILES.put("M8", "M8-front-schema.yaml");
    }

    @Value("${onto.models-dir:../models}")
    private String modelsDir;

    private volatile Path resolvedDir;
    // 不可变快照 + volatile 原子发布:热重载与在途请求并发时,读取方要么看到旧快照、要么看到新快照,
    // 绝不会看到"半空"中间态(修复 reload 与在途 execute 的数据竞争)。
    private volatile Map<String, Object> layers = Map.of();

    @PostConstruct
    public synchronized void load() {
        Path dir = resolveModelsDir();
        Map<String, Object> fresh = new LinkedHashMap<>();
        for (var e : LAYER_FILES.entrySet()) {
            Path f = dir.resolve(e.getValue());
            try (InputStream in = Files.newInputStream(f)) {
                fresh.put(e.getKey(), new Yaml(new LoaderOptions()).load(in));
            } catch (Exception ex) {
                throw new IllegalStateException("加载本体模型失败: " + f + " -> " + ex.getMessage(), ex);
            }
        }
        this.resolvedDir = dir;
        this.layers = Collections.unmodifiableMap(fresh); // 原子发布
        log.info("本体模型已加载: dir={}, layers={}", dir.toAbsolutePath(), fresh.keySet());
    }

    /** 热重载:重新读取磁盘上的 YAML,使模型改动即时生效(无需重启)。 */
    public synchronized void reload() {
        load();
    }

    private Path resolveModelsDir() {
        List<String> candidates = new ArrayList<>();
        if (modelsDir != null && !modelsDir.isBlank()) candidates.add(modelsDir);
        candidates.addAll(List.of("../models", "models", "../../models"));
        for (String c : candidates) {
            Path p = Paths.get(c).toAbsolutePath().normalize();
            if (Files.isDirectory(p) && Files.exists(p.resolve("M1-domain.yaml"))) {
                return p;
            }
        }
        throw new IllegalStateException("找不到 models 目录,已尝试: " + candidates
                + " (cwd=" + Paths.get("").toAbsolutePath() + ")");
    }

    public Path modelsDir() { return resolvedDir; }

    // ===================== 通用访问器 =====================

    public Set<String> layerCodes() { return LAYER_FILES.keySet(); }

    public String fileNameOf(String code) { return LAYER_FILES.get(code); }

    /** 返回某层解析后的原始对象(供模型查看器/前端"运行原理"面板展示)。 */
    public Object rawLayer(String code) { return layers.get(code); }

    /** 返回某层 YAML 源文件的原始文本(保留注释/顺序,供模型编辑器编辑)。 */
    public String rawFileText(String code) {
        String fileName = LAYER_FILES.get(code);
        if (fileName == null) return null;
        Path dir = resolvedDir != null ? resolvedDir : resolveModelsDir();
        try {
            return Files.readString(dir.resolve(fileName));
        } catch (Exception ex) {
            log.warn("读取模型源文件失败: {} -> {}", code, ex.getMessage());
            return null;
        }
    }

    /**
     * 覆盖写入某层 YAML 源文件(模型编辑器保存用)。先校验能被解析,避免把源文件写坏、
     * 导致后续 reload 全线失败。写入后需由调用方触发 reload 才会生效。返回写入的文件路径。
     */
    public synchronized Path writeRawLayer(String code, String yamlText) {
        String fileName = LAYER_FILES.get(code);
        if (fileName == null) throw new IllegalArgumentException("未知模型层: " + code);
        try {
            new Yaml(new LoaderOptions()).load(yamlText); // 解析校验:不通过则拒绝写入
        } catch (Exception ex) {
            throw new IllegalArgumentException("YAML 解析失败,未写入: " + ex.getMessage(), ex);
        }
        Path dir = resolvedDir != null ? resolvedDir : resolveModelsDir();
        Path f = dir.resolve(fileName);
        try {
            Files.writeString(f, yamlText);
        } catch (Exception ex) {
            throw new IllegalStateException("写入模型文件失败: " + f + " -> " + ex.getMessage(), ex);
        }
        log.info("模型层已保存: {} -> {}", code, f.toAbsolutePath());
        return f;
    }

    @SuppressWarnings("unchecked")
    static Map<String, Object> asMap(Object o) {
        return o instanceof Map ? (Map<String, Object>) o : Collections.emptyMap();
    }

    @SuppressWarnings("unchecked")
    static List<Object> asList(Object o) {
        return o instanceof List ? (List<Object>) o : Collections.emptyList();
    }

    // ---------- M1 领域模型 ----------

    public List<Object> aggregates() {
        return asList(asMap(layers.get("M1")).get("aggregates"));
    }

    public Map<String, Object> aggregate(String aggregateId) {
        for (Object a : aggregates()) {
            Map<String, Object> m = asMap(a);
            if (aggregateId.equals(m.get("aggregateId"))) return m;
        }
        return Collections.emptyMap();
    }

    public Map<String, Object> aggregateRoot(String aggregateId) {
        return asMap(aggregate(aggregateId).get("aggregateRoot"));
    }

    /** 聚合根唯一标识字段名(如 orderId)。 */
    public String rootIdentifier(String aggregateId) {
        return String.valueOf(aggregateRoot(aggregateId).get("identifier"));
    }

    public String rootName(String aggregateId) {
        return String.valueOf(aggregateRoot(aggregateId).get("name"));
    }

    public List<Object> rootAttributes(String aggregateId) {
        return asList(aggregateRoot(aggregateId).get("attributes"));
    }

    public List<Object> entities(String aggregateId) {
        return asList(aggregate(aggregateId).get("entities"));
    }

    public Map<String, Object> entity(String aggregateId, String entityName) {
        for (Object e : entities(aggregateId)) {
            Map<String, Object> m = asMap(e);
            if (entityName.equals(m.get("name"))) return m;
        }
        return Collections.emptyMap();
    }

    /**
     * 查询某(聚合, [子实体])下某领域字段的 M1 类型。用于前端组件类型推导、DDL 生成、值转换。
     * entityName 为 null 时查聚合根,否则查子实体;标识字段默认 string。
     */
    public String fieldType(String aggregateId, String entityName, String fieldName) {
        List<Object> attrs;
        String identifier;
        if (entityName == null) {
            attrs = rootAttributes(aggregateId);
            identifier = rootIdentifier(aggregateId);
        } else {
            Map<String, Object> ent = entity(aggregateId, entityName);
            attrs = asList(ent.get("attributes"));
            identifier = String.valueOf(ent.get("localIdentifier"));
        }
        if (fieldName.equals(identifier)) return "string";
        for (Object at : attrs) {
            Map<String, Object> am = asMap(at);
            if (fieldName.equals(am.get("name"))) return String.valueOf(am.get("type"));
        }
        return "string";
    }

    /** 组合关系子实体名列表(如 OrderItem/PaymentTerm/DeliveryAddressEntity)。 */
    public List<String> compositionChildren(String aggregateId) {
        List<String> out = new ArrayList<>();
        for (Object r : asList(aggregateRoot(aggregateId).get("relations"))) {
            Map<String, Object> rm = asMap(r);
            if ("composition".equals(rm.get("relationType"))) out.add(String.valueOf(rm.get("ref")));
        }
        return out;
    }

    // ---------- M2 命令 ----------

    public Map<String, Object> command(String aggregateId, String cmdId) {
        for (Object b : asList(asMap(layers.get("M2")).get("behaviors"))) {
            Map<String, Object> bm = asMap(b);
            if (aggregateId.equals(bm.get("aggregateId"))) {
                for (Object c : asList(bm.get("commands"))) {
                    Map<String, Object> cm = asMap(c);
                    if (cmdId.equals(cm.get("cmdId"))) return cm;
                }
            }
        }
        return Collections.emptyMap();
    }

    // ---------- M3 部署映射 ----------

    public Map<String, Object> deployOf(String aggregateId) {
        Object dm = asMap(layers.get("M3")).get("deploymentMappings");
        for (Object a : asList(asMap(dm).get("aggregateMappingList"))) {
            Map<String, Object> am = asMap(a);
            if (aggregateId.equals(am.get("aggregateId"))) return am;
        }
        return Collections.emptyMap();
    }

    public List<Object> tableMappings(String aggregateId) {
        return asList(deployOf(aggregateId).get("tableMappings"));
    }

    public Map<String, Object> tableMapping(String aggregateId, String domainEntity) {
        for (Object t : tableMappings(aggregateId)) {
            Map<String, Object> tm = asMap(t);
            if (domainEntity.equals(tm.get("domainEntity"))) return tm;
        }
        return Collections.emptyMap();
    }

    // ---------- M4 场景 ----------

    public List<Object> scenes() {
        return asList(asMap(asMap(layers.get("M4")).get("sceneModel")).get("sceneDefinitions"));
    }

    public Map<String, Object> scene(String sceneId) {
        for (Object s : scenes()) {
            Map<String, Object> sm = asMap(s);
            if (sceneId.equals(sm.get("sceneId"))) return sm;
        }
        return Collections.emptyMap();
    }

    // ---------- M5 安全 ----------

    public Map<String, Object> securityModel() {
        return asMap(asMap(layers.get("M5")).get("securityModel"));
    }

    public Map<String, Object> maskRules() {
        return asMap(securityModel().get("maskRules"));
    }

    public Map<String, Object> functionPermission(String permId) {
        for (Object p : asList(securityModel().get("functionPermissions"))) {
            Map<String, Object> pm = asMap(p);
            if (permId.equals(pm.get("permId"))) return pm;
        }
        return Collections.emptyMap();
    }

    // ---------- M8 前端模板 ----------

    public List<Object> pageTemplates() {
        return asList(asMap(asMap(layers.get("M8")).get("frontModel")).get("pageTemplates"));
    }

    public Map<String, Object> frontGlobalConfig() {
        return asMap(asMap(asMap(layers.get("M8")).get("frontModel")).get("reactGlobalConfig"));
    }

    public Map<String, Object> templateByScene(String sceneId) {
        for (Object t : pageTemplates()) {
            Map<String, Object> tm = asMap(t);
            if (sceneId.equals(tm.get("bindSceneId"))) return tm;
        }
        return Collections.emptyMap();
    }

    // ---------- ME 事件 ----------

    public Map<String, Object> eventModel() {
        return asMap(asMap(layers.get("ME")).get("eventModel"));
    }

    public Map<String, Object> eventDef(String aggregateId, String eventName) {
        for (Object a : asList(eventModel().get("aggregateEventDefinitions"))) {
            Map<String, Object> am = asMap(a);
            if (aggregateId.equals(am.get("aggregateId"))) {
                Map<String, Object> events = asMap(am.get("events"));
                return asMap(events.get(eventName));
            }
        }
        return Collections.emptyMap();
    }

    public List<Object> crossConsistencyRules() {
        return asList(eventModel().get("crossAggConsistencyRules"));
    }

    // ---------- MetaRule 规则 ----------

    public Map<String, Object> metaRuleModel() {
        return asMap(asMap(layers.get("MetaRule")).get("metaRuleModel"));
    }

    /** 全局规则参数 paramId -> defaultValue。 */
    public Map<String, Object> ruleGlobalParams() {
        Map<String, Object> out = new LinkedHashMap<>();
        for (Object p : asList(metaRuleModel().get("ruleGlobalParams"))) {
            Map<String, Object> pm = asMap(p);
            out.put(String.valueOf(pm.get("paramId")), pm.get("defaultValue"));
        }
        return out;
    }

    /** 绑定到指定场景的规则组列表。 */
    public List<Map<String, Object>> ruleGroupsByScene(String sceneId) {
        List<Map<String, Object>> out = new ArrayList<>();
        for (Object g : asList(metaRuleModel().get("ruleGroups"))) {
            Map<String, Object> gm = asMap(g);
            if (sceneId.equals(gm.get("bindScene"))) out.add(gm);
        }
        return out;
    }

    // ---------- M2 命令扩展元数据(commandType / effect / derivations) ----------

    /** 命令类型:create / update / cancel(缺省 create)。 */
    public String commandType(String aggregateId, String cmdId) {
        Object t = command(aggregateId, cmdId).get("commandType");
        return t == null ? "create" : String.valueOf(t);
    }

    /** 命令的结构化副作用(op/targetField/keyParam/amountParam/guardField)。 */
    public Map<String, Object> commandEffect(String aggregateId, String cmdId) {
        return asMap(command(aggregateId, cmdId).get("effect"));
    }

    /** 命令的服务端派生规则(如 totalAmount = sum(quantity*itemPrice))。 */
    public List<Object> commandDerivations(String aggregateId, String cmdId) {
        return asList(command(aggregateId, cmdId).get("derivations"));
    }

    // ---------- M1 字段约束 / 属性 / 引用 ----------

    public List<Object> attributesOf(String aggregateId, String entityName) {
        return entityName == null ? rootAttributes(aggregateId)
                : asList(entity(aggregateId, entityName).get("attributes"));
    }

    /** 字段的结构化约束(min/max/pattern/...),无则空。 */
    public Map<String, Object> attributeConstraints(String aggregateId, String entityName, String fieldName) {
        for (Object at : attributesOf(aggregateId, entityName)) {
            Map<String, Object> am = asMap(at);
            if (fieldName.equals(am.get("name"))) return asMap(am.get("constraints"));
        }
        return Collections.emptyMap();
    }

    /** 若字段是跨聚合引用,返回 {refAggregate, refRoot, refIdentifier};否则空。 */
    public Map<String, Object> refInfo(String aggregateId, String entityName, String fieldName) {
        for (Object at : attributesOf(aggregateId, entityName)) {
            Map<String, Object> am = asMap(at);
            if (fieldName.equals(am.get("name")) && am.get("refAggregate") != null) {
                Map<String, Object> r = new LinkedHashMap<>();
                r.put("refAggregate", am.get("refAggregate"));
                r.put("refRoot", am.get("refRoot"));
                r.put("refIdentifier", am.get("refIdentifier"));
                return r;
            }
        }
        return Collections.emptyMap();
    }

    /** 聚合根展示字段:首个 string 属性,否则用标识字段。 */
    public String displayField(String aggregateId) {
        for (Object at : rootAttributes(aggregateId)) {
            Map<String, Object> am = asMap(at);
            if ("string".equals(am.get("type"))) return String.valueOf(am.get("name"));
        }
        return rootIdentifier(aggregateId);
    }

    // ---------- ME / M5 扩展 ----------

    /** ME 声明的 Outbox 表名(缺省 t_domain_outbox)。 */
    public String outboxTable() {
        Object ob = asMap(eventModel().get("globalConfig")).get("outbox");
        Object t = asMap(ob).get("table");
        return t == null ? "t_domain_outbox" : String.valueOf(t);
    }

    /** M5 声明需存储加密的领域字段集合。 */
    public Set<String> encryptFields() {
        Set<String> out = new LinkedHashSet<>();
        for (Object f : asList(asMap(securityModel().get("encryptRules")).get("storageEncrypt"))) {
            out.add(String.valueOf(f));
        }
        return out;
    }
}