You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
7.1 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// ============================================================================
// 【文件说明】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;
/// <summary>
/// hw-portal 搜索索引实体。
/// <para>
/// 【设计模式 - CQRS命令查询职责分离
/// 这个实体体现了 CQRS 的"读模型"概念:
/// - 命令模型HwWeb 等):负责增删改,保证数据一致性
/// - 查询模型HwPortalSearchDoc负责查询优化读取性能
///
/// 好处:
/// 1. 读写分离,各自优化
/// 2. 搜索失败不影响主业务
/// 3. 可以针对搜索场景做特殊处理
/// </para>
/// </summary>
[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
{
/// <summary>
/// 主键ID。
/// <para>
/// 【C# 语法知识点 - [Key] 特性】
/// [Key] 标记主键字段。
///
/// 对比 Java JPA
/// Java: @Id
/// @GeneratedValue(strategy = GenerationType.IDENTITY)
/// private Long id;
///
/// EF Core 会自动识别名为 Id 或 {类名}Id 的属性为主键,
/// 但显式标注 [Key] 更清晰。
/// </para>
/// </summary>
[Key]
public long Id { get; set; }
/// <summary>
/// 搜索文档唯一键。
/// <para>
/// 【业务说明】
/// DocId 是文档的唯一标识,格式通常是:{SourceType}_{BizId}
/// 例如hw_web_100, hw_product_200
///
/// 为什么需要 DocId
/// 1. 全局唯一:不同来源的文档可以区分
/// 2. 幂等更新:相同 DocId 的文档会被更新而不是重复插入
/// </para>
/// <para>
/// 【C# 语法知识点 - [Required] 和 [MaxLength] 特性】
/// [Required]:必填字段,不能为 null
/// [MaxLength(128)]:最大长度 128 字符
///
/// 对比 Java JPA
/// Java: @Column(name = "doc_id", nullable = false, length = 128)
/// @NotNull
/// private String docId;
/// </para>
/// </summary>
[Required]
[MaxLength(128)]
public string DocId { get; set; } = string.Empty;
/// <summary>
/// 来源类型(如 hw_web, hw_product 等)。
/// </summary>
[Required]
[MaxLength(32)]
public string SourceType { get; set; } = string.Empty;
/// <summary>
/// 业务主键(如 web_id, product_info_id 等)。
/// </summary>
[MaxLength(64)]
public string BizId { get; set; }
/// <summary>
/// 搜索标题。
/// </summary>
[MaxLength(500)]
public string Title { get; set; }
/// <summary>
/// 搜索正文内容。
/// <para>
/// 【EF Core 列类型映射】
/// Content 字段在 DbContext.OnModelCreating 中被映射为 longtext
/// entity.Property(x => x.Content).HasColumnType("longtext");
///
/// 这是因为搜索内容可能很长,需要大文本类型。
/// </para>
/// </summary>
public string Content { get; set; }
/// <summary>
/// 页面编码(用于跳转到详情页)。
/// </summary>
[MaxLength(64)]
public string WebCode { get; set; }
/// <summary>
/// 类型ID。
/// </summary>
[MaxLength(64)]
public string TypeId { get; set; }
/// <summary>
/// 设备ID。
/// </summary>
[MaxLength(64)]
public string DeviceId { get; set; }
/// <summary>
/// 菜单ID。
/// </summary>
[MaxLength(64)]
public string MenuId { get; set; }
/// <summary>
/// 文档ID。
/// </summary>
[MaxLength(64)]
public string DocumentId { get; set; }
/// <summary>
/// 基础分值(用于排序)。
/// </summary>
public int BaseScore { get; set; }
/// <summary>
/// 前台路由(用户点击搜索结果跳转的页面)。
/// </summary>
[MaxLength(255)]
public string Route { get; set; }
/// <summary>
/// 前台路由参数JSON 格式)。
/// <para>
/// 【JSON 列类型】
/// 在 DbContext 中映射为 json 类型:
/// entity.Property(x => x.RouteQueryJson).HasColumnType("json");
///
/// MySQL 5.7+ 支持 JSON 类型,可以直接存储 JSON 数据。
/// </para>
/// </summary>
public string RouteQueryJson { get; set; }
/// <summary>
/// 编辑路由(后台编辑页面的路由)。
/// </summary>
[MaxLength(255)]
public string EditRoute { get; set; }
/// <summary>
/// 逻辑删除标记。
/// <para>
/// 【逻辑删除】
/// "0":正常
/// "1":已删除
///
/// 搜索索引也需要逻辑删除,因为:
/// 1. 删除业务数据时,索引也要标记删除
/// 2. 可以保留历史记录用于审计
/// </para>
/// </summary>
[MaxLength(1)]
public string IsDelete { get; set; } = "0";
/// <summary>
/// 业务更新时间(来源数据的更新时间)。
/// </summary>
public DateTime? UpdatedAt { get; set; }
/// <summary>
/// 索引创建时间。
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 索引更新时间。
/// </summary>
public DateTime ModifiedAt { get; set; }
}