// ============================================================================
// 【文件说明】HwProductCaseInfoService.cs - 产品案例信息服务类
// ============================================================================
// 这个服务类负责处理产品案例信息的业务逻辑,包括:
// - 产品案例的 CRUD 操作
// - 典型案例查询
// - 关联查询(案例 + 其他信息)
//
// 【业务背景】
// 产品案例模块用于管理企业的产品案例展示,支持标记典型案例。
// 典型案例会在网站首页等重点位置展示。
//
// 【与 Java Spring Boot 的对比】
// Java Spring Boot:
// @Service
// public class HwProductCaseInfoServiceImpl implements HwProductCaseInfoService { ... }
//
// ASP.NET Core + Furion:
// public class HwProductCaseInfoService : ITransient { ... }
// ============================================================================
namespace Admin.NET.Plugin.HwPortal;
///
/// 产品案例信息服务类。
///
/// 【服务职责】
/// 1. 管理产品案例的增删改查
/// 2. 提供典型案例查询功能
/// 3. 支持关联查询获取完整案例信息
///
///
/// 【典型案例标记】
/// - HomeTypicalFlag:是否在首页展示("0"否,"1"是)
/// - TypicalFlag:是否典型案例("0"否,"1"是)
///
/// 典型案例优先级:
/// 1. 优先返回标记为 TypicalFlagYes 的案例
/// 2. 如果没有典型案例,返回第一个案例
/// 3. 如果列表为空,返回空对象
///
///
/// 【当前实现说明】
/// 这个服务目前仍使用 XML Mapper 方式访问数据。
/// 后续可以考虑迁移到 SqlSugar,以获得更好的类型安全和性能。
///
///
public class HwProductCaseInfoService : ITransient
{
///
/// MyBatis 映射器名称。
///
/// 【C# 语法知识点 - const 常量】
/// const 是"编译期常量",值在编译时就确定了。
///
/// 对比 Java:
/// Java: private static final String MAPPER = "HwProductCaseInfoMapper";
/// C#: private const string Mapper = "HwProductCaseInfoMapper";
///
/// C# 的命名约定:
/// - const 通常用 PascalCase(首字母大写)
/// - Java 的 static final 通常用 UPPER_SNAKE_CASE
///
///
private const string Mapper = "HwProductCaseInfoMapper";
///
/// MyBatis 执行器。
///
/// 【依赖注入】
/// 通过构造函数注入,和控制器注入服务的方式一样。
///
/// HwPortalMyBatisExecutor 是自定义的执行器,封装了 MyBatis 风格的 SQL 执行逻辑。
/// 它负责解析 XML 中的 SQL 语句,执行参数绑定,并将结果映射到实体对象。
///
/// 对比 Java MyBatis:
/// Java MyBatis 使用 Mapper 接口 + XML 配置:
/// @Mapper
/// public interface HwProductCaseInfoMapper {
/// @Select("SELECT * FROM hw_product_case_info WHERE case_info_id = #{caseInfoId}")
/// HwProductCaseInfo selectById(Long caseInfoId);
/// }
///
/// C# 这里使用执行器模式:
/// _executor.QuerySingleAsync<HwProductCaseInfo>(Mapper, "selectHwProductCaseInfoByCaseInfoId", new { caseInfoId });
///
/// 两种方式的对比:
/// - Java MyBatis:编译期检查,类型安全
/// - C# 执行器模式:更灵活,可以动态选择 SQL,但缺少编译期检查
///
///
private readonly HwPortalMyBatisExecutor _executor;
///
/// 构造函数(依赖注入)。
///
/// 【C# 语法知识点 - 构造函数】
/// public HwProductCaseInfoService(HwPortalMyBatisExecutor executor)
///
/// 对比 Java:
/// Java:
/// @Autowired
/// public HwProductCaseInfoService(HwPortalMyBatisExecutor executor) {
/// this.executor = executor;
/// }
///
/// C#:
/// public HwProductCaseInfoService(HwPortalMyBatisExecutor executor)
/// {
/// _executor = executor;
/// }
///
/// C# 的改进:
/// 1. 不需要 @Autowired 注解,框架自动识别构造函数
/// 2. 使用 _executor 命名约定表示私有字段
/// 3. 可以使用主构造函数(C# 12+)进一步简化
///
///
/// MyBatis 执行器
public HwProductCaseInfoService(HwPortalMyBatisExecutor executor)
{
_executor = executor;
}
///
/// 根据案例ID查询产品案例信息。
///
/// 【方法命名约定】
/// Select + 实体名 + By + 主键名:根据主键查询单条记录
///
/// 对比 Java 若依:
/// 若依通常使用:selectXxxById、getXxxById
/// 这里使用:SelectXxxByCaseInfoId,更符合 C# 的 PascalCase 命名规范
///
///
/// 【XML Mapper 调用】
/// _executor.QuerySingleAsync<T>(mapperName, statementId, parameters)
/// - mapperName:XML 文件中 mapper 的 namespace
/// - statementId:SQL 语句的 id
/// - parameters:参数对象(匿名对象或实体)
///
///
/// 案例ID
/// 产品案例实体
public Task SelectHwProductCaseInfoByCaseInfoId(long caseInfoId)
{
// 【匿名对象传参】
// new { caseInfoId } 是 C# 的"匿名对象"语法。
// 编译器会自动创建一个包含 caseInfoId 属性的临时类。
//
// 对比 Java:
// Java 没有匿名对象语法,通常用:
// - Map<String, Object> params = new HashMap<>();
// - params.put("caseInfoId", caseInfoId);
// 或者:
// - @Param("caseInfoId") Long caseInfoId(MyBatis 注解)
//
// C# 的匿名对象更简洁,编译器自动推断类型。
return _executor.QuerySingleAsync(Mapper, "selectHwProductCaseInfoByCaseInfoId", new { caseInfoId });
}
///
/// 查询产品案例列表。
///
/// 【动态查询条件】
/// 查询条件通过 input 对象传递,XML 中使用 <if> 标签动态构建 WHERE 子句。
///
/// 对比 Java MyBatis XML:
/// <select id="selectHwProductCaseInfoList" resultType="HwProductCaseInfo">
/// SELECT * FROM hw_product_case_info
/// <where>
/// <if test="caseName != null and caseName != ''">
/// AND case_name LIKE CONCAT('%', #{caseName}, '%')
/// </if>
/// </where>
/// </select>
///
///
/// 查询条件
/// 产品案例列表
public Task> SelectHwProductCaseInfoList(HwProductCaseInfo input)
{
// 【空合并运算符 ??】
// input ?? new HwProductCaseInfo() 含义:
// 如果 input 不为 null,就用 input;
// 否则 new 一个默认的 HwProductCaseInfo 对象。
//
// 对比 Java:
// Java 需要手写:
// if (input == null) input = new HwProductCaseInfo();
// 或者用 Optional:
// input = Optional.ofNullable(input).orElse(new HwProductCaseInfo());
//
// C# 的空合并运算符更简洁!
return _executor.QueryListAsync(Mapper, "selectHwProductCaseInfoList", input ?? new HwProductCaseInfo());
}
///
/// 新增产品案例信息。
///
/// 【审计字段自动填充】
/// CreateTime 由代码自动设置,不需要前端传递。
/// 这是常见的"审计字段"处理模式。
///
///
/// 【自增主键回填】
/// InsertReturnIdentityAsync 返回自增主键值,
/// 然后回填到 input 对象,方便调用方获取新记录的 ID。
///
///
/// 产品案例数据
/// 影响行数(1成功,0失败)
public async Task InsertHwProductCaseInfo(HwProductCaseInfo input)
{
// 【静态工具类调用】
// HwPortalContextHelper.Now() 获取当前时间。
// 这是"上下文助手"模式:封装了请求上下文相关的操作。
//
// 对比 Java Spring:
// Java 通常用 new Date() 或 LocalDateTime.now()。
// 这里封装一层的好处:可以统一控制时间来源(如测试时 mock)。
input.CreateTime = HwPortalContextHelper.Now();
// 【执行插入并获取自增主键】
// InsertReturnIdentityAsync 返回插入记录的自增主键值。
// 对比 Java MyBatis:
// Java 需要配置 useGeneratedKeys="true" keyProperty="caseInfoId",
// 然后通过 input.getCaseInfoId() 获取主键。
long identity = await _executor.InsertReturnIdentityAsync(Mapper, "insertHwProductCaseInfo", input);
// 【回填自增主键】
// 把自增主键回填到实体对象,方便调用方使用。
input.CaseInfoId = identity;
// 返回 1 表示成功,0 表示失败。
return identity > 0 ? 1 : 0;
}
///
/// 更新产品案例信息。
///
/// 【审计字段更新】
/// UpdateTime 由代码自动设置,标记记录更新时间。
///
///
/// 产品案例数据
/// 影响行数
public Task UpdateHwProductCaseInfo(HwProductCaseInfo input)
{
// 【审计字段】
input.UpdateTime = HwPortalContextHelper.Now();
// 【执行更新】
// ExecuteAsync 执行 UPDATE 语句,返回影响行数。
return _executor.ExecuteAsync(Mapper, "updateHwProductCaseInfo", input);
}
///
/// 批量删除产品案例信息。
///
/// 【批量删除】
/// 根据 ID 数组批量删除记录。
/// XML 中使用 <foreach> 标签生成 IN 语句。
///
/// 对比 Java MyBatis XML:
/// <delete id="deleteHwProductCaseInfoByCaseInfoIds">
/// DELETE FROM hw_product_case_info
/// WHERE case_info_id IN
/// <foreach collection="array" item="id" open="(" separator="," close=")">
/// #{id}
/// </foreach>
/// </delete>
///
///
/// 案例ID数组
/// 影响行数
public Task DeleteHwProductCaseInfoByCaseInfoIds(long[] caseInfoIds)
{
// 【匿名对象传参】
// new { array = caseInfoIds } 将数组包装为匿名对象的属性。
// XML 中通过 #{array} 引用整个数组,<foreach> 遍历它。
return _executor.ExecuteAsync(Mapper, "deleteHwProductCaseInfoByCaseInfoIds", new { array = caseInfoIds });
}
///
/// 获取首页典型案例信息。
///
/// 【业务逻辑】
/// 1. 设置查询条件:HomeTypicalFlag = "1"(首页展示)
/// 2. 查询符合条件的案例列表
/// 3. 优先返回标记为 TypicalFlagYes 的案例
/// 4. 如果没有典型案例,返回第一个案例
/// 5. 如果列表为空,返回空对象(不是 null)
///
/// 【设计模式:空对象模式】
/// 返回 new HwProductCaseInfo() 而不是 null,
/// 避免调用方出现 NullReferenceException。
/// 这是一种防御性编程实践。
///
///
/// 查询条件
/// 典型案例实体
public async Task GetTypicalHomeCaseInfo(HwProductCaseInfo input)
{
// 【空合并赋值运算符 ??=】
// input ??= new HwProductCaseInfo() 等价于:
// if (input == null) input = new HwProductCaseInfo();
//
// 这是 C# 8.0 引入的语法糖,让代码更简洁。
input ??= new HwProductCaseInfo();
// 【设置查询条件】
// 只查询首页展示的案例(HomeTypicalFlag = "1")
input.HomeTypicalFlag = HwPortalConstants.HomeTypicalFlagYes;
// 【执行查询】
List list = await SelectHwProductCaseInfoList(input);
// 【优先返回典型案例】
// .FirstOrDefault(predicate) 返回第一个满足条件的元素,如果没有则返回 null。
// string.Equals(a, b, StringComparison.Ordinal) 区分大小写的精确比较。
//
// 对比 Java:
// Java: list.stream().filter(u -> HwPortalConstants.TypicalFlagYes.equals(u.getTypicalFlag())).findFirst().orElse(null);
// C#: list.FirstOrDefault(u => string.Equals(u.TypicalFlag, HwPortalConstants.TypicalFlagYes, StringComparison.Ordinal));
HwProductCaseInfo typical = list.FirstOrDefault(u => string.Equals(u.TypicalFlag, HwPortalConstants.TypicalFlagYes, StringComparison.Ordinal));
// 【兜底策略】
// 如果没有典型案例,返回第一个案例。
// 如果列表为空,返回空对象。
// ?? 是空合并运算符:如果左边为 null,返回右边。
return typical ?? list.FirstOrDefault() ?? new HwProductCaseInfo();
}
///
/// 查询产品案例关联列表(JOIN查询)。
///
/// 【关联查询】
/// 这个方法执行 JOIN 查询,返回包含关联信息的完整案例数据。
/// 关联查询通常在 XML 中定义,使用 <resultMap> 映射结果。
///
/// 对比 Java MyBatis:
/// Java MyBatis 使用 <resultMap> + <association> 或 <collection> 处理关联。
/// C# 这里的实现类似,也是通过 XML 配置映射。
///
///
/// 查询条件
/// 产品案例列表(含关联信息)
public Task> SelectHwProductCaseInfoJoinList(HwProductCaseInfo input)
{
// 【空合并运算符 ??】
// 确保 input 不为 null,避免 XML 中出现问题。
return _executor.QueryListAsync(Mapper, "selectHwProductCaseInfoJoinList", input ?? new HwProductCaseInfo());
}
}