首页 理论 架构 工程 文档 白皮书 著作 研究 案例 下载 博客 关于 开始使用 →

第224章 RiskConflictRepository——风险、冲突、保护与处理记录及Domain—数据库字段映射

第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。

Leave a Reply

Your email address will not be published. Required fields are marked *