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;
}
}