// ============================================================================
// 【文件说明】HwPortalSearchDoc.cs - 搜索索引实体类
// ============================================================================
// 这是一个 Entity Framework Core (EF Core) 的实体类,用于存储搜索索引数据。
//
// 【什么是搜索索引?】
// 搜索索引是"读模型"(Read Model),用于优化搜索查询性能:
// 1. 把分散在多个业务表的数据聚合到一个表
// 2. 预处理搜索内容(如拼接标题、正文)
// 3. 建立索引加速查询
//
// 【与 SqlSugar 实体的区别】
// - SqlSugar 实体(如 HwWeb):映射到业务表,用于 CRUD 操作
// - EF Core 实体(如 HwPortalSearchDoc):映射到索引表,用于搜索查询
//
// 为什么搜索用 EF Core 而不是 SqlSugar?
// 1. EF Core 的 LINQ 查询更强大
// 2. EF Core 对复杂查询的支持更好
// 3. 项目技术栈混合使用,各取所长
//
// 【与 Java Spring Boot 的对比】
// Java Spring Data JPA:
// @Entity
// @Table(name = "hw_portal_search_doc")
// @Index(name = "uk_doc_id", columnList = "doc_id", unique = true)
// public class HwPortalSearchDoc { ... }
//
// EF Core:
// [Table("hw_portal_search_doc")]
// [Index(nameof(DocId), IsUnique = true)]
// public class HwPortalSearchDoc { ... }
//
// 两者概念相同,只是注解/特性的写法不同。
// ============================================================================
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Admin.NET.Plugin.HwPortal;
///
/// hw-portal 搜索索引实体。
///
/// 【设计模式 - CQRS(命令查询职责分离)】
/// 这个实体体现了 CQRS 的"读模型"概念:
/// - 命令模型(HwWeb 等):负责增删改,保证数据一致性
/// - 查询模型(HwPortalSearchDoc):负责查询,优化读取性能
///
/// 好处:
/// 1. 读写分离,各自优化
/// 2. 搜索失败不影响主业务
/// 3. 可以针对搜索场景做特殊处理
///
///
[Table("hw_portal_search_doc")]
// 【EF Core 索引特性】
// [Index(nameof(DocId), IsUnique = true)] 创建唯一索引。
// nameof(DocId) 获取属性名称字符串,避免硬编码。
//
// 对比 Java JPA:
// Java: @Index(name = "uk_doc_id", columnList = "doc_id", unique = true)
// C#: [Index(nameof(DocId), IsUnique = true)]
//
// C# 的 nameof 更安全,重构时编译器会检查。
[Index(nameof(DocId), IsUnique = true)]
[Index(nameof(SourceType))]
[Index(nameof(UpdatedAt))]
public class HwPortalSearchDoc
{
///
/// 主键ID。
///
/// 【C# 语法知识点 - [Key] 特性】
/// [Key] 标记主键字段。
///
/// 对比 Java JPA:
/// Java: @Id
/// @GeneratedValue(strategy = GenerationType.IDENTITY)
/// private Long id;
///
/// EF Core 会自动识别名为 Id 或 {类名}Id 的属性为主键,
/// 但显式标注 [Key] 更清晰。
///
///
[Key]
public long Id { get; set; }
///
/// 搜索文档唯一键。
///
/// 【业务说明】
/// DocId 是文档的唯一标识,格式通常是:{SourceType}_{BizId}
/// 例如:hw_web_100, hw_product_200
///
/// 为什么需要 DocId?
/// 1. 全局唯一:不同来源的文档可以区分
/// 2. 幂等更新:相同 DocId 的文档会被更新而不是重复插入
///
///
/// 【C# 语法知识点 - [Required] 和 [MaxLength] 特性】
/// [Required]:必填字段,不能为 null
/// [MaxLength(128)]:最大长度 128 字符
///
/// 对比 Java JPA:
/// Java: @Column(name = "doc_id", nullable = false, length = 128)
/// @NotNull
/// private String docId;
///
///
[Required]
[MaxLength(128)]
public string DocId { get; set; } = string.Empty;
///
/// 来源类型(如 hw_web, hw_product 等)。
///
[Required]
[MaxLength(32)]
public string SourceType { get; set; } = string.Empty;
///
/// 业务主键(如 web_id, product_info_id 等)。
///
[MaxLength(64)]
public string BizId { get; set; }
///
/// 搜索标题。
///
[MaxLength(500)]
public string Title { get; set; }
///
/// 搜索正文内容。
///
/// 【EF Core 列类型映射】
/// Content 字段在 DbContext.OnModelCreating 中被映射为 longtext:
/// entity.Property(x => x.Content).HasColumnType("longtext");
///
/// 这是因为搜索内容可能很长,需要大文本类型。
///
///
public string Content { get; set; }
///
/// 页面编码(用于跳转到详情页)。
///
[MaxLength(64)]
public string WebCode { get; set; }
///
/// 类型ID。
///
[MaxLength(64)]
public string TypeId { get; set; }
///
/// 设备ID。
///
[MaxLength(64)]
public string DeviceId { get; set; }
///
/// 菜单ID。
///
[MaxLength(64)]
public string MenuId { get; set; }
///
/// 文档ID。
///
[MaxLength(64)]
public string DocumentId { get; set; }
///
/// 基础分值(用于排序)。
///
public int BaseScore { get; set; }
///
/// 前台路由(用户点击搜索结果跳转的页面)。
///
[MaxLength(255)]
public string Route { get; set; }
///
/// 前台路由参数(JSON 格式)。
///
/// 【JSON 列类型】
/// 在 DbContext 中映射为 json 类型:
/// entity.Property(x => x.RouteQueryJson).HasColumnType("json");
///
/// MySQL 5.7+ 支持 JSON 类型,可以直接存储 JSON 数据。
///
///
public string RouteQueryJson { get; set; }
///
/// 编辑路由(后台编辑页面的路由)。
///
[MaxLength(255)]
public string EditRoute { get; set; }
///
/// 逻辑删除标记。
///
/// 【逻辑删除】
/// "0":正常
/// "1":已删除
///
/// 搜索索引也需要逻辑删除,因为:
/// 1. 删除业务数据时,索引也要标记删除
/// 2. 可以保留历史记录用于审计
///
///
[MaxLength(1)]
public string IsDelete { get; set; } = "0";
///
/// 业务更新时间(来源数据的更新时间)。
///
public DateTime? UpdatedAt { get; set; }
///
/// 索引创建时间。
///
public DateTime CreatedAt { get; set; }
///
/// 索引更新时间。
///
public DateTime ModifiedAt { get; set; }
}