diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/entity/UserRole.java b/backend/mci-server/src/main/java/com/sino/mci/admin/entity/UserRole.java index 1f23dfb..2db48d9 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/admin/entity/UserRole.java +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/entity/UserRole.java @@ -1,24 +1,15 @@ package com.sino.mci.admin.entity; -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.sino.mci.shared.entity.BaseEntity; import lombok.Data; - -import java.time.LocalDateTime; +import lombok.EqualsAndHashCode; @Data +@EqualsAndHashCode(callSuper = true) @TableName("mci_user_role") -public class UserRole { - - @TableId(type = IdType.AUTO) - private Long id; +public class UserRole extends BaseEntity { private Long userId; private Long roleId; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; } diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/entity/Conversation.java b/backend/mci-server/src/main/java/com/sino/mci/channel/entity/Conversation.java index ae775a7..6fae84d 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/entity/Conversation.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/entity/Conversation.java @@ -5,6 +5,12 @@ import com.sino.mci.shared.entity.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; +/** + * 会话表(mci_conversation)。 + *
+ * 该表在 V15 已按 create_time 做 RANGE 月分区;MySQL 分区表不支持数据库级外键, + * 因此与会话相关的外键已在 V15 全部移除。引用完整性由应用层(MyBatis-Plus 逻辑删除 + 业务校验)维护。 + */ @Data @EqualsAndHashCode(callSuper = true) @TableName("mci_conversation") diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationParticipant.java b/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationParticipant.java index b50cbb4..7f2be24 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationParticipant.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationParticipant.java @@ -1,23 +1,20 @@ package com.sino.mci.channel.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.sino.mci.shared.entity.BaseEntity; import lombok.Data; +import lombok.EqualsAndHashCode; import java.time.LocalDateTime; @Data +@EqualsAndHashCode(callSuper = true) @TableName("mci_conversation_participant") -public class ConversationParticipant { - - @TableId(type = IdType.AUTO) - private Long id; +public class ConversationParticipant extends BaseEntity { private Long conversationId; private Long personId; private Long channelIdentityId; private Integer participantType; private LocalDateTime joinTime; - private LocalDateTime createTime; } diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationTagBinding.java b/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationTagBinding.java index 61cf603..d63267c 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationTagBinding.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/entity/ConversationTagBinding.java @@ -1,21 +1,16 @@ package com.sino.mci.channel.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.sino.mci.shared.entity.BaseEntity; import lombok.Data; - -import java.time.LocalDateTime; +import lombok.EqualsAndHashCode; @Data +@EqualsAndHashCode(callSuper = true) @TableName("mci_conversation_tag_binding") -public class ConversationTagBinding { - - @TableId(type = IdType.AUTO) - private Long id; +public class ConversationTagBinding extends BaseEntity { private String tenantCode; private Long tagId; private Long conversationId; - private LocalDateTime createTime; } diff --git a/backend/mci-server/src/main/java/com/sino/mci/crm/entity/PersonTagBinding.java b/backend/mci-server/src/main/java/com/sino/mci/crm/entity/PersonTagBinding.java index 0dc3bee..b8d3fcd 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/crm/entity/PersonTagBinding.java +++ b/backend/mci-server/src/main/java/com/sino/mci/crm/entity/PersonTagBinding.java @@ -1,21 +1,16 @@ package com.sino.mci.crm.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.sino.mci.shared.entity.BaseEntity; import lombok.Data; - -import java.time.LocalDateTime; +import lombok.EqualsAndHashCode; @Data +@EqualsAndHashCode(callSuper = true) @TableName("mci_person_tag_binding") -public class PersonTagBinding { - - @TableId(type = IdType.AUTO) - private Long id; +public class PersonTagBinding extends BaseEntity { private String tenantCode; private Long tagId; private Long personId; - private LocalDateTime createTime; } diff --git a/backend/mci-server/src/main/java/com/sino/mci/inbound/entity/FlowExecutionTrace.java b/backend/mci-server/src/main/java/com/sino/mci/inbound/entity/FlowExecutionTrace.java index 5a40577..fbed501 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/inbound/entity/FlowExecutionTrace.java +++ b/backend/mci-server/src/main/java/com/sino/mci/inbound/entity/FlowExecutionTrace.java @@ -1,13 +1,15 @@ package com.sino.mci.inbound.entity; import com.baomidou.mybatisplus.annotation.TableName; +import com.sino.mci.shared.entity.BaseEntity; import lombok.Data; -import java.time.LocalDateTime; +import lombok.EqualsAndHashCode; @Data +@EqualsAndHashCode(callSuper = true) @TableName("mci_flow_execution_trace") -public class FlowExecutionTrace { - private Long id; +public class FlowExecutionTrace extends BaseEntity { + private String eventId; private Long flowDefinitionId; private String nodeType; @@ -17,5 +19,4 @@ public class FlowExecutionTrace { private Integer status; private Integer durationMs; private String errorMsg; - private LocalDateTime createTime; } diff --git a/backend/mci-server/src/main/resources/db/migration/V17__audit_soft_delete_and_fk.sql b/backend/mci-server/src/main/resources/db/migration/V17__audit_soft_delete_and_fk.sql new file mode 100644 index 0000000..146f7fc --- /dev/null +++ b/backend/mci-server/src/main/resources/db/migration/V17__audit_soft_delete_and_fk.sql @@ -0,0 +1,93 @@ +-- Phase 1.6: 审计并补齐软删除/FK 一致性 +-- 目标: +-- 1. 所有业务表必须具备 deleted 字段;对应实体继承 BaseEntity(含 @TableLogic 与自动填充)。 +-- 2. 唯一索引在软删除表中必须包含 deleted,避免已删除记录导致唯一冲突。 +-- 3. 记录 V15 分区改造后 mci_conversation 及其关联表不再支持数据库级外键的事实 +-- (MySQL 分区表不支持外键,应用层通过 MyBatis-Plus 逻辑删除保证引用完整性)。 + +-- ============================ +-- 1. 补齐缺少 deleted/update_time 的业务表 +-- ============================ + +-- 用户角色关系表 +ALTER TABLE mci_user_role + ADD COLUMN update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER create_time, + ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除 1已删除' AFTER update_time; + +-- 会话参与者表 +ALTER TABLE mci_conversation_participant + ADD COLUMN update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER create_time, + ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除 1已删除' AFTER update_time; + +-- 人员标签绑定表 +ALTER TABLE mci_person_tag_binding + ADD COLUMN update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER create_time, + ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除 1已删除' AFTER update_time; + +-- 会话标签绑定表 +ALTER TABLE mci_conversation_tag_binding + ADD COLUMN update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER create_time, + ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除 1已删除' AFTER update_time; + +-- 流程执行轨迹表 +ALTER TABLE mci_flow_execution_trace + ADD COLUMN update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER create_time, + ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删除 1已删除' AFTER update_time; + +-- ============================ +-- 2. 统一唯一索引:软删除表的唯一索引必须包含 deleted +-- ============================ + +-- mci_tenant: 租户编码唯一(排除已删除) +ALTER TABLE mci_tenant + DROP INDEX tenant_code, + ADD UNIQUE KEY uk_tenant_code (tenant_code, deleted); + +-- mci_platform_role: 角色编码唯一(排除已删除) +ALTER TABLE mci_platform_role + DROP INDEX role_code, + ADD UNIQUE KEY uk_role_code (role_code, deleted); + +-- mci_user_role: 同一用户同一角色仅允许一条生效记录 +ALTER TABLE mci_user_role + DROP INDEX uk_user_role, + ADD UNIQUE KEY uk_user_role (user_id, role_id, deleted); + +-- mci_conversation_participant: 同一身份在同一会话中仅允许一条生效记录 +ALTER TABLE mci_conversation_participant + DROP INDEX uk_conv_participant, + ADD UNIQUE KEY uk_conv_participant (conversation_id, person_id, channel_identity_id, deleted); + +-- mci_channel_account_conversation: 同一账号同一会话同一绑定类型仅允许一条生效记录 +ALTER TABLE mci_channel_account_conversation + DROP INDEX uk_account_conv_binding, + ADD UNIQUE KEY uk_account_conv_binding (account_id, conversation_id, binding_type, deleted); + +-- mci_person_tag_binding: 同一标签同一人员仅允许一条生效记录 +ALTER TABLE mci_person_tag_binding + DROP INDEX uk_person_tag, + ADD UNIQUE KEY uk_person_tag (tag_id, person_id, deleted); + +-- mci_conversation_tag_binding: 同一标签同一会话仅允许一条生效记录 +ALTER TABLE mci_conversation_tag_binding + DROP INDEX uk_conversation_tag, + ADD UNIQUE KEY uk_conversation_tag (tag_id, conversation_id, deleted); + +-- mci_message_event: event_id 唯一(保留分区键 create_time) +ALTER TABLE mci_message_event + DROP INDEX uk_event_id, + ADD UNIQUE KEY uk_event_id (event_id, deleted, create_time); + +-- mci_send_record: 任务维度唯一(保留分区键 create_time) +ALTER TABLE mci_send_record + DROP INDEX uk_task_target, + ADD UNIQUE KEY uk_task_target (task_id, conversation_id, person_id, deleted, create_time); + +-- ============================ +-- 3. 分区表外键声明 +-- ============================ +-- V15 已完成 mci_conversation 分区改造,并删除了与会话表相关的数据库级外键约束: +-- fk_participant_conversation_id、fk_binding_conversation_id、fk_conv_tag_conversation_id、 +-- fk_conversation_owner_account、fk_conversation_subject_id。 +-- 本迁移再次明确:mci_conversation 为 RANGE 分区表,MySQL 分区表不支持外键; +-- 应用层通过 MyBatis-Plus 逻辑删除与业务校验维护引用完整性,不再恢复数据库级级联约束。