第224章 RiskConflictRepository——风险、冲突、保护与处理记录及Domain—数据库字段映射
224.1 RiskConflictRepository定义
在ICAI系统中,RiskEngine负责风险计算,ConflictEngine负责冲突计算,ProtectionEngine负责保护方案计算,DecisionEngine负责处理方案选择,而RiskConflictRepository负责这些领域对象的持久化、查询、更新以及历史记录保存。
因此:
RCR=RiskPersistence+ConflictPersistence+ProtectionPersistence+HandlingHistoryPersistence+Query+Update+HistoryRCR= RiskPersistence+ ConflictPersistence+ ProtectionPersistence+ HandlingHistoryPersistence+ Query+ Update+ History
其中:
- RiskPersistenceRiskPersistence:风险持久化;
- ConflictPersistenceConflictPersistence:冲突持久化;
- ProtectionPersistenceProtectionPersistence:保护持久化;
- HandlingHistoryPersistenceHandlingHistoryPersistence:处理历史持久化;
- QueryQuery:领域数据查询;
- UpdateUpdate:领域数据更新;
- HistoryHistory:状态和处理历史保存。
核心架构为:
Domain Object
↓
Mapper
↓
Persistence Data
↓
Repository
↓
PDO
↓
MySQL
读取过程相反:
MySQL
↓
PDO
↓
Persistence Data
↓
Mapper
↓
Domain Object
因此必须明确:
DomainObject≠DatabaseRowDomainObject \neq DatabaseRow
数据库字段只是持久化表示,不能直接作为ICAI Domain Object的定义。
224.2 Domain Object与数据库字段映射原则
此前Repository设计中最容易出现的问题,是直接把:
$risk['condition']
当作:
condition
数据库字段。
这种方式会使Domain层和Persistence层耦合。
正确结构应该是:
Risk Domain
↓
RiskMapper
↓
Risk Persistence
↓
risks
例如Domain中的:
$risk['condition']
对应数据库:
condition_data
Domain中的:
$risk['event']
对应数据库:
event_name
Domain中的:
$risk['state']
对应数据库:
state
因此:
DomainField≠DatabaseFieldDomainField \neq DatabaseField
但存在:
DomainField→MapperDatabaseFieldDomainField \xrightarrow{Mapper} DatabaseField
Mapper承担这种结构转换。
224.3 Risk Domain Object
Risk的理论模型为:
R=(C,E,P,I,S)R=(C,E,P,I,S)
其中:
- CC:Condition,风险发生条件;
- EE:Event,风险事件;
- PP:Probability,风险概率;
- II:Impact,风险影响;
- SS:State,风险状态。
工程Domain Object还需要增加身份和追踪信息:
Rd=(ID,O,C,E,P,I,L,S,Ev,Tc,Tu)R_d=(ID,O,C,E,P,I,L,S,Ev,T_c,T_u)
其中:
- IDID:Domain Object身份;
- OO:Object,风险所属对象;
- CC:Condition;
- EE:Event;
- PP:Probability;
- II:Impact;
- LL:Risk Level;
- SS:State;
- EvEv:Evidence;
- TcT_c:Created Time;
- TuT_u:Updated Time。
例如:
$risk = array(
'id' => 'R-001',
'object_id' => 1001,
'condition' => array(
'resource' => '<2'
),
'event' => 'execution_failure',
'probability' => 0.80,
'impact' => 5,
'risk_level' => 'high',
'state' => 'evaluated',
'evidence' => array(),
'created_at' => '2026-09-11 15:00:00',
'updated_at' => '2026-09-11 15:00:00'
);
这里的id是Domain层的风险身份,不应该因为数据库使用自增整数就强迫Domain层也使用同样的语义。
224.4 Risk Domain与MySQL字段映射
风险Domain与数据库之间建立明确映射:
| Domain字段 | 含义 | MySQL字段 | 类型 |
|---|---|---|---|
id |
风险业务身份 | risk_code |
VARCHAR |
object_id |
所属对象 | object_id |
BIGINT |
condition |
风险条件 | condition_data |
TEXT |
event |
风险事件 | event_name |
VARCHAR |
probability |
风险概率 | probability |
DECIMAL |
impact |
风险影响 | impact |
DECIMAL |
risk_score |
风险分数 | risk_score |
DECIMAL |
risk_level |
风险等级 | risk_level |
VARCHAR |
state |
风险状态 | state |
VARCHAR |
evidence |
风险证据 | evidence |
TEXT |
created_at |
创建时间 | created_at |
DATETIME |
updated_at |
更新时间 | updated_at |
DATETIME |
因此:
RiskDomain→RiskMapperRiskPersistenceRiskDomain \xrightarrow{RiskMapper} RiskPersistence
例如:
class RiskMapper
{
public function toPersistence($risk)
{
return array(
'risk_code' => $risk['id'],
'object_id' => $risk['object_id'],
'condition_data' => json_encode($risk['condition']),
'event_name' => $risk['event'],
'probability' => isset($risk['probability'])
? $risk['probability'] : null,
'impact' => isset($risk['impact'])
? $risk['impact'] : null,
'risk_score' => isset($risk['risk_score'])
? $risk['risk_score'] : null,
'risk_level' => isset($risk['risk_level'])
? $risk['risk_level'] : null,
'state' => $risk['state'],
'evidence' => isset($risk['evidence'])
? json_encode($risk['evidence'])
: null
);
}
public function toDomain($row)
{
return array(
'id' => $row['risk_code'],
'object_id' => $row['object_id'],
'condition' => $this->decode($row['condition_data']),
'event' => $row['event_name'],
'probability' => $row['probability'],
'impact' => $row['impact'],
'risk_score' => $row['risk_score'],
'risk_level' => $row['risk_level'],
'state' => $row['state'],
'evidence' => $this->decode($row['evidence']),
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at']
);
}
protected function decode($value)
{
if ($value === null || $value === '') {
return array();
}
$data = json_decode($value, true);
if (!is_array($data)) {
return array();
}
return $data;
}
}
这里修正了此前最重要的映射错误:
Domain id
↓
risk_code
而不是:
Domain id
↓
MySQL id
数据库内部的id只是Persistence层主键。
224.5 Persistence ID与Domain ID分离
数据库:
id BIGINT AUTO_INCREMENT
是Persistence Identity。
Domain:
risk_code = R-001
是Domain Identity。
因此:
PersistenceID≠DomainIDPersistenceID \neq DomainID
完整关系:
Risk Domain
id = R-001
↓
RiskMapper
↓
risk_code = R-001
↓
MySQL
id = 15
读取:
MySQL
id = 15
risk_code = R-001
↓
RiskMapper
↓
Risk Domain
id = R-001
这样可以避免数据库内部主键泄漏到Domain模型。
同样原则适用于:
Conflict
Protection
Handling
224.6 Conflict Domain Object
Conflict理论模型:
Cf=(O1,T,O2,K,S)C_f=(O_1,T,O_2,K,S)
工程Domain模型:
Cd=(ID,O1,T,O2,K,S,Ev,Tc,Tu)C_d=(ID,O_1,T,O_2,K,S,Ev,T_c,T_u)
其中:
- IDID:冲突业务身份;
- O1O_1:对象1;
- TT:冲突类型;
- O2O_2:对象2;
- KK:冲突条件;
- SS:冲突状态;
- EvEv:证据;
- TcT_c:创建时间;
- TuT_u:更新时间。
例如:
$conflict = array(
'id' => 'C-001',
'object1_id' => 1001,
'conflict_type' => 'resource',
'object2_id' => 2001,
'condition' => array(
'required_resource' => 2,
'current_resource' => 1
),
'state' => 'detected',
'evidence' => array()
);
224.7 Conflict Domain与MySQL字段映射
| Domain字段 | 含义 | MySQL字段 |
|---|---|---|
id |
冲突业务身份 | conflict_code |
object1_id |
对象1 | object1_id |
conflict_type |
冲突类型 | conflict_type |
object2_id |
对象2 | object2_id |
condition |
冲突条件 | condition_data |
state |
冲突状态 | state |
evidence |
证据 | evidence |
created_at |
创建时间 | created_at |
updated_at |
更新时间 | updated_at |
Mapper:
class ConflictMapper
{
public function toPersistence($conflict)
{
return array(
'conflict_code' => $conflict['id'],
'object1_id' => $conflict['object1_id'],
'conflict_type' => $conflict['conflict_type'],
'object2_id' => $conflict['object2_id'],
'condition_data' => json_encode(
$conflict['condition']
),
'state' => $conflict['state'],
'evidence' => isset($conflict['evidence'])
? json_encode($conflict['evidence'])
: null
);
}
public function toDomain($row)
{
return array(
'id' => $row['conflict_code'],
'object1_id' => $row['object1_id'],
'conflict_type' => $row['conflict_type'],
'object2_id' => $row['object2_id'],
'condition' => $this->decode(
$row['condition_data']
),
'state' => $row['state'],
'evidence' => $this->decode(
$row['evidence']
),
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at']
);
}
protected function decode($value)
{
if ($value === null || $value === '') {
return array();
}
$data = json_decode($value, true);
return is_array($data) ? $data : array();
}
}
224.8 Protection Domain Object
Protection模型:
Pr=(R,C,A,S)P_r=(R,C,A,S)
工程模型:
Pd=(ID,R,C,A,S,Re,V,Tc,Tu)P_d=(ID,R,C,A,S,Re,V,T_c,T_u)
其中:
- IDID:保护业务身份;
- RR:关联风险;
- CC:保护条件;
- AA:保护动作;
- SS:保护状态;
- ReRe:实际结果;
- VV:验证结果;
- TcT_c:创建时间;
- TuT_u:更新时间。
例如:
$protection = array(
'id' => 'P-001',
'risk_id' => 'R-001',
'condition' => array(
'resource' => '<2'
),
'action_type' => 'increase_resource',
'state' => 'ready',
'result' => array(),
'verification' => array()
);
224.9 Protection Domain与MySQL字段映射
| Domain字段 | MySQL字段 |
|---|---|
id |
protection_code |
risk_id |
risk_id |
condition |
condition_data |
action_type |
action_type |
state |
state |
result |
result_data |
verification |
verification_data |
created_at |
created_at |
updated_at |
updated_at |
Mapper:
class ProtectionMapper
{
public function toPersistence($protection)
{
return array(
'protection_code' => $protection['id'],
'risk_id' => $protection['risk_id'],
'condition_data' => json_encode(
$protection['condition']
),
'action_type' => $protection['action_type'],
'state' => $protection['state'],
'result_data' => isset($protection['result'])
? json_encode($protection['result'])
: null,
'verification_data' => isset(
$protection['verification']
)
? json_encode($protection['verification'])
: null
);
}
public function toDomain($row)
{
return array(
'id' => $row['protection_code'],
'risk_id' => $row['risk_id'],
'condition' => $this->decode(
$row['condition_data']
),
'action_type' => $row['action_type'],
'state' => $row['state'],
'result' => $this->decode(
$row['result_data']
),
'verification' => $this->decode(
$row['verification_data']
),
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at']
);
}
protected function decode($value)
{
if ($value === null || $value === '') {
return array();
}
$data = json_decode($value, true);
return is_array($data) ? $data : array();
}
}
224.10 Handling History Domain Object
处理记录不是Risk、Conflict或Protection本身。
定义:
Hd=(ID,O,R,C,P,T,Sb,A,Ex,Re,Sa,V,Tm)H_d=(ID,O,R,C,P,T,S_b,A,E_x,R_e,S_a,V,T_m)
其中:
- IDID:处理记录身份;
- OO:处理对象;
- RR:关联风险;
- CC:关联冲突;
- PP:关联保护;
- TT:处理类型;
- SbS_b:处理前状态;
- AA:处理动作;
- ExE_x:实际执行;
- ReR_e:处理实际结果;
- SaS_a:处理后状态;
- VV:验证结果;
- TmT_m:处理时间。
注意这里使用:
Re=ActualResultR_e=ActualResult
避免与Risk的符号R产生混淆。
例如:
$history = array(
'id' => 'H-001',
'object_id' => 1001,
'risk_id' => 'R-001',
'conflict_id' => null,
'protection_id' => 'P-001',
'handling_type' => 'protection',
'state_before' => 'active',
'action' => array(
'type' => 'increase_resource'
),
'execution_id' => 'E-010',
'actual_result' => array(
'status' => 'success'
),
'state_after' => 'controlled',
'verification' => array(
'status' => 'passed'
),
'created_at' => '2026-09-11 15:20:00'
);
224.11 Handling History字段映射
| Domain字段 | MySQL字段 |
|---|---|
id |
handling_code |
object_id |
object_id |
risk_id |
risk_id |
conflict_id |
conflict_id |
protection_id |
protection_id |
handling_type |
handling_type |
state_before |
state_before |
action |
action_data |
execution_id |
execution_data |
actual_result |
result_data |
state_after |
state_after |
verification |
verification_data |
created_at |
created_at |
这里:
execution_id
如果只需要保存Execution身份,可以直接保存字符串。
如果需要保存Execution的完整快照,则使用:
execution_data
二者不能在Domain层混为一谈。
更推荐:
HandlingHistory
↓
execution_id
↓
ExecutionRepository
这样可以避免把Execution完整对象重复存入处理记录。
224.12 修正后的数据库设计
风险:
CREATE TABLE risks (
id BIGINT NOT NULL AUTO_INCREMENT,
risk_code VARCHAR(100) NOT NULL,
object_id BIGINT NOT NULL,
condition_data TEXT,
event_name VARCHAR(255) NOT NULL,
probability DECIMAL(12,6) NULL,
impact DECIMAL(12,4) NULL,
risk_score DECIMAL(12,4) NULL,
risk_level VARCHAR(50) NULL,
state VARCHAR(50) NOT NULL,
evidence TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_risk_code (risk_code),
KEY idx_risk_object (object_id),
KEY idx_risk_state (state)
);
这里:
id
是数据库内部主键。
risk_code
才对应Domain:
Risk.id
224.13 修正后的Conflict表
CREATE TABLE conflicts (
id BIGINT NOT NULL AUTO_INCREMENT,
conflict_code VARCHAR(100) NOT NULL,
object1_id BIGINT NOT NULL,
conflict_type VARCHAR(50) NOT NULL,
object2_id BIGINT NOT NULL,
condition_data TEXT,
state VARCHAR(50) NOT NULL,
evidence TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_conflict_code (conflict_code),
KEY idx_conflict_object1 (object1_id),
KEY idx_conflict_object2 (object2_id),
KEY idx_conflict_type (conflict_type),
KEY idx_conflict_state (state)
);
映射:
Conflict.id→conflict_codeConflict.id \rightarrow conflict\_code
而不是:
Conflict.id→conflicts.idConflict.id \rightarrow conflicts.id
224.14 修正后的Protection表
CREATE TABLE protections (
id BIGINT NOT NULL AUTO_INCREMENT,
protection_code VARCHAR(100) NOT NULL,
risk_id BIGINT NOT NULL,
condition_data TEXT,
action_type VARCHAR(100) NOT NULL,
state VARCHAR(50) NOT NULL,
result_data TEXT,
verification_data TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_protection_code (protection_code),
KEY idx_protection_risk (risk_id),
KEY idx_protection_state (state)
);
这里需要进一步区分:
protections.risk_id
是数据库外键语义。
如果Domain使用:
risk_id = R-001
而数据库risks.id是:
15
那么直接保存R-001到BIGINT字段是不正确的。
因此存在两种设计。
设计A:数据库内部外键
protections.risk_id → risks.id
那么Mapper必须进行:
Risk Domain ID R-001
↓
查询 risks
↓
Persistence ID 15
↓
protections.risk_id = 15
设计B:使用业务编码关联
将:
risk_id BIGINT
改成:
risk_code VARCHAR(100)
对于ICAI当前的Domain设计,推荐设计B,因为Domain使用业务身份更直接。
修正为:
CREATE TABLE protections (
id BIGINT NOT NULL AUTO_INCREMENT,
protection_code VARCHAR(100) NOT NULL,
risk_code VARCHAR(100) NOT NULL,
condition_data TEXT,
action_type VARCHAR(100) NOT NULL,
state VARCHAR(50) NOT NULL,
result_data TEXT,
verification_data TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_protection_code (protection_code),
KEY idx_protection_risk_code (risk_code),
KEY idx_protection_state (state)
);
这样:
Protection.risk_id→protections.risk_codeProtection.risk\_id \rightarrow protections.risk\_code
语义更加统一。
224.15 修正后的Handling History表
CREATE TABLE risk_conflict_handling_history (
id BIGINT NOT NULL AUTO_INCREMENT,
handling_code VARCHAR(100) NOT NULL,
object_id BIGINT NULL,
risk_code VARCHAR(100) NULL,
conflict_code VARCHAR(100) NULL,
protection_code VARCHAR(100) NULL,
handling_type VARCHAR(100) NOT NULL,
state_before VARCHAR(50) NULL,
action_data TEXT,
execution_id VARCHAR(100) NULL,
result_data TEXT,
state_after VARCHAR(50) NULL,
verification_data TEXT,
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_handling_code (handling_code),
KEY idx_handling_object (object_id),
KEY idx_handling_risk (risk_code),
KEY idx_handling_conflict (conflict_code),
KEY idx_handling_protection (protection_code)
);
这样Domain:
risk_id = R-001
映射为:
risk_code = R-001
Conflict同样:
conflict_id = C-001
映射:
conflict_code = C-001
Protection:
protection_id = P-001
映射:
protection_code = P-001
避免业务ID和数据库自增ID混用。
224.16 风险历史映射
Risk History:
RH=(R,Sb,Sa,Δ,Rs,T)RH=(R,S_b,S_a,\Delta,R_s,T)
其中:
- RR:Risk Domain ID;
- SbS_b:Before State;
- SaS_a:After State;
- Δ\Delta:变化;
- RsR_s:变化原因;
- TT:时间。
数据库:
CREATE TABLE risk_history (
id BIGINT NOT NULL AUTO_INCREMENT,
risk_code VARCHAR(100) NOT NULL,
state_before VARCHAR(50),
state_after VARCHAR(50),
change_data TEXT,
reason TEXT,
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY idx_risk_history_code (risk_code)
);
因此:
Risk.id
↓
risk_code
而不是:
Risk.id
↓
risk_history.risk_id
224.17 Conflict History映射
CREATE TABLE conflict_history (
id BIGINT NOT NULL AUTO_INCREMENT,
conflict_code VARCHAR(100) NOT NULL,
state_before VARCHAR(50),
state_after VARCHAR(50),
change_data TEXT,
reason TEXT,
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY idx_conflict_history_code (conflict_code)
);
映射:
Conflict.id→conflict_codeConflict.id\rightarrow conflict\_code
这样整个RiskConflict领域统一采用:
Domain Business ID
↓
*_code
↓
MySQL
224.18 完整Mapper结构
Repository层可以建立:
Repositories/
└── RiskConflict/
├── RiskConflictRepositoryInterface.php
├── MySQLRiskConflictRepository.php
└── Mapper/
├── RiskMapper.php
├── ConflictMapper.php
├── ProtectionMapper.php
└── HandlingHistoryMapper.php
每一个Domain Object对应一个Mapper。
即:
OneDomainObject→OneMapperOneDomainObject \rightarrow OneMapper
而不是建立一个巨大Mapper处理所有领域对象。
224.19 Repository接口修正
interface RiskConflictRepositoryInterface
{
public function findRiskById($id);
public function findRisksByObjectId($objectId);
public function findRisksByState($state);
public function findRisksByLevel($level);
public function findActiveRisks($objectId);
public function saveRisk($risk);
public function updateRisk($risk);
public function findConflictById($id);
public function findConflictsByObjectId($objectId);
public function findConflictsByType($type);
public function findConflictsByState($state);
public function findActiveConflicts($objectId);
public function findConflictsBetween(
$object1Id,
$object2Id
);
public function saveConflict($conflict);
public function updateConflict($conflict);
public function findProtectionById($id);
public function findProtectionsByRiskId($riskId);
public function findProtectionsByState($state);
public function saveProtection($protection);
public function updateProtection($protection);
public function saveHandlingHistory($history);
public function findHandlingHistory($objectId);
public function findRiskHistory($riskId);
public function findConflictHistory($conflictId);
}
这里的:
findRiskById($id)
参数是Domain业务ID:
R-001
而不是数据库自增ID:
15
如果需要数据库内部ID查询,应将其作为Infrastructure层内部操作,而不是暴露给Domain接口。
224.20 Repository保存流程
正确保存流程:
Domain→Validate→Mapper→PersistenceData→PDO→MySQLDomain \rightarrow Validate \rightarrow Mapper \rightarrow PersistenceData \rightarrow PDO \rightarrow MySQL
例如:
public function saveRisk($risk)
{
$mapper = new RiskMapper();
$data = $mapper->toPersistence($risk);
$sql = "
INSERT INTO risks
(
risk_code,
object_id,
condition_data,
event_name,
probability,
impact,
risk_score,
risk_level,
state,
evidence,
created_at,
updated_at
)
VALUES
(
:risk_code,
:object_id,
:condition_data,
:event_name,
:probability,
:impact,
:risk_score,
:risk_level,
:state,
:evidence,
NOW(),
NOW()
)
";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute(array(
':risk_code' => $data['risk_code'],
':object_id' => $data['object_id'],
':condition_data' => $data['condition_data'],
':event_name' => $data['event_name'],
':probability' => $data['probability'],
':impact' => $data['impact'],
':risk_score' => $data['risk_score'],
':risk_level' => $data['risk_level'],
':state' => $data['state'],
':evidence' => $data['evidence']
));
}
这里不再直接访问:
$risk['condition']
然后把它当作数据库字段。
所有Persistence字段均由Mapper产生。
224.21 Repository读取流程
读取:
public function findRiskById($id)
{
$sql = "
SELECT *
FROM risks
WHERE risk_code = :risk_code
LIMIT 1
";
$stmt = $this->pdo->prepare($sql);
$stmt->execute(array(
':risk_code' => $id
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
return null;
}
$mapper = new RiskMapper();
return $mapper->toDomain($row);
}
此时:
R-001
被用于:
WHERE risk_code = :risk_code
而不是:
WHERE id = :id
这与Domain身份定义保持一致。
224.22 Domain、Persistence、Database三层对象
现在可以严格区分三种结构:
第一层:Domain Object
$risk = array(
'id' => 'R-001',
'object_id' => 1001,
'condition' => array(
'resource' => '<2'
),
'event' => 'execution_failure',
'probability' => 0.8,
'impact' => 5,
'state' => 'evaluated'
);
第二层:Persistence Data
$data = array(
'risk_code' => 'R-001',
'object_id' => 1001,
'condition_data' => '{"resource":"<2"}',
'event_name' => 'execution_failure',
'probability' => 0.8,
'impact' => 5,
'state' => 'evaluated'
);
第三层:Database Row
id = 15
risk_code = R-001
object_id = 1001
condition_data = {"resource":"<2"}
event_name = execution_failure
probability = 0.8
impact = 5
state = evaluated
三者关系:
Domain→MapperPersistence→PDODatabaseDomain \xrightarrow{Mapper} Persistence \xrightarrow{PDO} Database
读取:
Database→PDOPersistence→MapperDomainDatabase \xrightarrow{PDO} Persistence \xrightarrow{Mapper} Domain
224.23 为什么不能直接使用数据库Row作为Domain Object
如果直接使用:
$row['risk_code']
$row['condition_data']
$row['event_name']
那么Domain层将被数据库字段名称污染。
后续如果数据库字段从:
event_name
修改为:
risk_event
Domain层也必须修改。
这是错误的依赖方向:
Domain→DatabaseSchemaDomain\rightarrow DatabaseSchema
正确方向应该是:
Domain←Mapper←Persistence←DatabaseDomain \leftarrow Mapper \leftarrow Persistence \leftarrow Database
数据库结构变化主要由Mapper和Repository吸收。
224.24 风险、冲突、保护的统一映射模型
三个核心Domain Object:
RiskRisk ConflictConflict ProtectionProtection
统一使用:
Domain Object
↓
Mapper
↓
Persistence Object
↓
Repository
↓
MySQL
对应关系:
Risk.id
↓
risk_code
Conflict.id
↓
conflict_code
Protection.id
↓
protection_code
处理记录:
Handling.id
↓
handling_code
这样形成统一身份体系:
DomainID→BusinessCodeDomainID\rightarrow BusinessCode BusinessCode→DatabaseBusinessCode\rightarrow Database
数据库自增id只作为Persistence层主键存在。
224.25 风险与保护关系映射
Domain:
Risk R-001
↓
Protection P-001
数据库:
risks.risk_code
↑
│
protections.risk_code
因此:
Protection.risk_id
在Domain层表示:
R-001
而Persistence层字段:
risk_code
这样不会把:
Risk Domain ID
错误地解释成:
MySQL Auto Increment ID
224.26 处理记录的多态关联
一次处理可能针对:
Risk
也可能针对:
Conflict
也可能通过:
Protection
因此Handling History可以采用:
risk_code NULL
conflict_code NULL
protection_code NULL
例如风险处理:
H-001
risk_code = R-001
conflict_code = NULL
protection_code = P-001
冲突处理:
H-002
risk_code = NULL
conflict_code = C-001
protection_code = NULL
这样一条处理记录可以明确表示自己的处理对象。
但Repository不应该根据:
risk_code != NULL
自动推导:
handling_type = risk
处理类型应该由Domain对象明确提供。
224.27 Query与Mapping的职责
Repository查询:
MySQL
↓
Row
Mapper转换:
Row
↓
Domain Object
因此:
Query≠MappingQuery \neq Mapping
Repository负责:
Query
Persistence
Mapper负责:
Structure Conversion
Engine负责:
Calculation
Service负责:
Orchestration
形成:
Service
↓
Engine
↓
Domain Object
↓
Repository
↓
Mapper
↓
PDO
↓
MySQL
实际工程中Mapper通常由Repository内部调用,但职责仍然应该保持独立。
224.28 状态字段映射
Risk、Conflict、Protection都有:
state
但是三个Domain的State语义不同。
Risk:
unknown
detected
evaluated
active
controlled
resolved
rejected
expired
escalated
Conflict:
detected
active
handling
resolved
blocked
expired
Protection:
created
ready
executing
completed
failed
verified
rejected
因此数据库虽然都可以使用:
state VARCHAR(50)
但不能因此认为三者共享同一个状态集合。
StateRisk≠StateConflict≠StateProtectionState_{Risk} \neq State_{Conflict} \neq State_{Protection}
Mapper只负责保存和读取,合法状态由Domain/StateEngine验证。
224.29 条件字段映射
风险条件:
'condition' => array(
'resource' => '<2'
)
数据库:
condition_data
冲突条件:
'condition' => array(
'required_resource' => 2,
'current_resource' => 1
)
数据库:
condition_data
保护条件:
'condition' => array(
'resource' => '<2'
)
数据库:
condition_data
虽然三者都叫:
condition_data
但语义仍然由各自Domain定义。
因此:
SamePersistenceFieldName≠SameDomainMeaningSamePersistenceFieldName \neq SameDomainMeaning
224.30 Evidence字段映射
Evidence属于结构化认知证据:
Ev=(Source,Type,Value,T)Ev=(Source,Type,Value,T)
Domain:
'evidence' => array(
array(
'source' => 'Execution-E001',
'type' => 'result',
'value' => 'failed',
'time' => '2026-09-11 15:20:00'
)
)
Persistence:
evidence TEXT
Mapper:
json_encode($risk['evidence'])
读取:
json_decode($row['evidence'], true)
但必须注意:
JSON只是持久化格式,不是Domain模型。
因此:
EvidenceDomain≠JSONEvidenceDomain \neq JSON
JSON只是:
EvidenceDomain→MapperJSONEvidenceDomain \xrightarrow{Mapper} JSON
224.31 Result字段映射
Protection的:
result
不是ProtectionEngine计算结果本身,而是实际保护执行结果的持久化表示。
例如:
'result' => array(
'status' => 'success',
'resource_before' => 1,
'resource_after' => 2
)
Persistence:
result_data
同样:
verification
映射:
verification_data
因此:
Protection.result→Mapperresult_dataProtection.result \xrightarrow{Mapper} result\_data Protection.verification→Mapperverification_dataProtection.verification \xrightarrow{Mapper} verification\_data
224.32 完整风险处理案例
假设当前:
Object-A
Resource = 1
方法要求:
Resource >= 2
RiskEngine计算:
Risk
id = R-001
event = execution_failure
probability = 0.8
impact = 5
state = evaluated
Repository保存:
Risk Domain
↓
RiskMapper
↓
risk_code = R-001
↓
risks
ProtectionEngine产生:
Protection
id = P-001
risk_id = R-001
action = increase_resource
state = ready
Mapper转换:
Protection Domain
↓
ProtectionMapper
↓
protection_code = P-001
risk_code = R-001
↓
protections
实际执行:
Execution E-010
↓
Resource 1 → 2
↓
Result Success
↓
Verification Passed
形成:
Handling H-001
保存:
handling_code = H-001
risk_code = R-001
protection_code = P-001
execution_id = E-010
state_before = active
state_after = controlled
最终:
R-001
active
↓
controlled
Risk History:
R-001
active → controlled
也被保存。
224.33 完整持久化闭环
整个风险处理的Domain到数据库过程:
RiskEngine
↓
Risk Domain
↓
RiskMapper
↓
Risk Persistence
↓
RiskConflictRepository
↓
MySQL
保护:
ProtectionEngine
↓
Protection Domain
↓
ProtectionMapper
↓
Protection Persistence
↓
RiskConflictRepository
↓
MySQL
处理:
Execution / Feedback / Verification
↓
Handling History Domain
↓
HandlingHistoryMapper
↓
Handling Persistence
↓
RiskConflictRepository
↓
MySQL
224.34 RiskConflictRepository最终类结构
推荐结构:
app/
├── Domain/
│ ├── Risk/
│ │ └── Risk.php
│ ├── Conflict/
│ │ └── Conflict.php
│ ├── Protection/
│ │ └── Protection.php
│ └── Handling/
│ └── HandlingHistory.php
│
├── Engines/
│ ├── RiskEngine.php
│ ├── ConflictEngine.php
│ └── ProtectionEngine.php
│
├── Services/
│ ├── RiskService.php
│ ├── ConflictService.php
│ └── ProtectionService.php
│
├── Repositories/
│ └── RiskConflict/
│ ├── RiskConflictRepositoryInterface.php
│ ├── MySQLRiskConflictRepository.php
│ └── Mapper/
│ ├── RiskMapper.php
│ ├── ConflictMapper.php
│ ├── ProtectionMapper.php
│ └── HandlingHistoryMapper.php
│
└── Infrastructure/
└── Database/
└── PDOFactory.php
这样Domain、Engine、Service、Repository和Database完全分层。
224.35 四层字段关系
Risk领域完整结构:
Risk Domain
↓
RiskMapper
↓
Risk Persistence
↓
risks Table
具体:
id
↓
risk_code
condition
↓
condition_data
event
↓
event_name
evidence
↓
evidence
state
↓
state
Conflict:
id
↓
conflict_code
condition
↓
condition_data
conflict_type
↓
conflict_type
Protection:
id
↓
protection_code
risk_id
↓
risk_code
condition
↓
condition_data
result
↓
result_data
verification
↓
verification_data
Handling:
id
↓
handling_code
risk_id
↓
risk_code
conflict_id
↓
conflict_code
protection_id
↓
protection_code
actual_result
↓
result_data
verification
↓
verification_data
224.36 最终映射原则
本章修正后,ICAI Repository统一采用以下原则:
第一,Domain字段优先
Domain Object首先按照认知理论定义:
DomainModelDomainModel
数据库不能反过来定义Domain。
第二,Mapper负责转换
Domain→MapperPersistenceDomain \xrightarrow{Mapper} Persistence
第三,Persistence字段可以不同名
例如:
Domain:
event
Database:
event_name
这是允许且推荐的。
第四,业务ID与数据库ID分离
DomainID≠PersistenceIDDomainID \neq PersistenceID
例如:
R-001
与:
15
分别属于Domain和Database。
第五,关联使用明确业务身份
Risk、Conflict、Protection之间优先使用:
risk_code
conflict_code
protection_code
保持Domain语义一致。
第六,JSON只是Persistence格式
condition
evidence
result
verification
可以在MySQL中使用TEXT保存JSON,但:
JSON≠DomainObjectJSON \neq DomainObject
第七,Repository不负责认知计算
Repository负责:
Save
Query
Update
History
而不负责:
Risk Calculation
Conflict Calculation
Protection Calculation
Decision
Diagnosis
Learning
224.37 ICAI统一Repository结构
到第224章,Repository层形成:
IndividualRepository
ObjectRepository
StateRepository
RelationRepository
KnowledgeRepository
GoalCapabilityRepository
MethodDecisionRepository
BehaviorActionRepository
MemoryExperienceRepository
RiskConflictRepository
其共同模式为:
Repository=DomainPersistence+Query+Update+HistoryRepository = DomainPersistence + Query + Update + History
而每个Repository内部采用:
Domain→Mapper→Persistence→PDO→MySQLDomain \rightarrow Mapper \rightarrow Persistence \rightarrow PDO \rightarrow MySQL
224.38 Service、Engine、Domain、Repository、Database最终边界
最终边界严格定义为:
Service=OrchestrationService=Orchestration
Service决定:
先做什么
后做什么
调用哪个Service
什么时候进入事务
Engine:
Engine=CalculationEngine=Calculation
Engine决定:
如何计算
如何匹配
如何判断
如何推导
Domain Object:
DomainObject=StructureDomainObject=Structure
Domain描述:
是什么
有哪些属性
当前是什么状态
与什么对象存在关系
Mapper:
Mapper=TransformationMapper=Transformation
Mapper负责:
Domain字段
↔
Persistence字段
Repository:
Repository=Persistence+QueryRepository=Persistence+Query
Repository负责:
保存
查询
更新
历史
MySQL:
MySQL=StorageMySQL=Storage
MySQL负责:
实际数据存储
索引
约束
事务
224.39 最终完整架构
因此,RiskConflict领域最终形成:
RiskEngine
↓
Risk Domain Object
↓
RiskMapper
↓
RiskConflictRepository
↓
PDO
↓
MySQL
冲突:
ConflictEngine
↓
Conflict Domain Object
↓
ConflictMapper
↓
RiskConflictRepository
↓
PDO
↓
MySQL
保护:
ProtectionEngine
↓
Protection Domain Object
↓
ProtectionMapper
↓
RiskConflictRepository
↓
PDO
↓
MySQL
处理记录:
Execution
↓
Result
↓
Feedback
↓
Verification
↓
HandlingHistory Domain
↓
HandlingHistoryMapper
↓
RiskConflictRepository
↓
MySQL
224.40 本章总结
本章对RiskConflictRepository进行了关键的Domain—Database映射修正。
RiskConflictRepository管理四类核心持久化数据:
RiskRisk ConflictConflict ProtectionProtection HandlingHistoryHandlingHistory
但这四类Domain Object不能直接等同于MySQL Row。
正确模型是:
Domain→Mapper→Persistence→Repository→MySQL\boxed{ Domain \rightarrow Mapper \rightarrow Persistence \rightarrow Repository \rightarrow MySQL }
其中:
DomainID≠PersistenceIDDomainID \neq PersistenceID
例如:
Risk Domain
id = R-001
数据库:
id = 15
risk_code = R-001
二者语义完全不同。
本章最终确定:
Risk.id
↓
risks.risk_code
Conflict.id
↓
conflicts.conflict_code
Protection.id
↓
protections.protection_code
HandlingHistory.id
↓
risk_conflict_handling_history.handling_code
同时:
Risk.condition
↓
risks.condition_data
Risk.event
↓
risks.event_name
Conflict.condition
↓
conflicts.condition_data
Protection.condition
↓
protections.condition_data
Protection.result
↓
protections.result_data
Protection.verification
↓
protections.verification_data
Handling.actual_result
↓
handling.result_data
Handling.verification
↓
handling.verification_data
这样,Domain层可以保持稳定的ICAI认知模型,而数据库可以采用适合MySQL存储、索引和查询的字段结构。
最终形成:
Service
↓
Engine
↓
Domain Object
↓
Mapper
↓
Repository
↓
PDO
↓
MySQL
其中:
Service=编排Service=编排 Engine=计算Engine=计算 Domain=领域结构Domain=领域结构 Mapper=字段转换Mapper=字段转换 Repository=持久化与查询Repository=持久化与查询 MySQL=数据存储MySQL=数据存储
因此,数据库表不再反向定义ICAI Domain Object,Repository也不再把数据库Row直接冒充Domain Object。这一修正使Risk、Conflict、Protection和Handling History可以与前面第214—223章的Repository体系保持一致,并为后续Repository和完整MySQL数据库Schema统一提供基础。
整个风险—冲突—保护—处理数据闭环最终为:
Risk / Conflict
↓
Engine Calculation
↓
Domain Object
↓
Mapper
↓
RiskConflictRepository
↓
MySQL
↓
Repository Query
↓
Mapper
↓
Domain Object
↓
Decision / Protection / Handling
↓
Execution
↓
Result
↓
Feedback
↓
History
↓
Memory / Experience / Learning
整个体系仍然基于符号对象、规则、条件、离散计算、状态、关系、历史和PHP OOP实现,不依赖LLM、Transformer、Embedding、Vector Search、Prompt Engineering、神经网络或LLM API。