// ============================================================================
// 【文件说明】HwAboutUsInfoDetailService.cs - 关于我们明细信息服务类
// ============================================================================
// 这个服务类负责处理"关于我们"模块的明细数据业务逻辑,包括:
// - 明细信息的 CRUD 操作
// - 与主信息的关联查询
//
// 【分层架构说明】
// Controller(控制器层)-> Service(服务层)-> Repository(数据访问层)
//
// 控制器只负责:接收请求、调用服务、返回响应
// 服务层负责:业务逻辑、数据组装、事务控制
// 数据访问层负责:数据库 CRUD 操作
//
// 【与 Java Spring Boot 的对比】
// Java Spring Boot:
// @Service
// public class HwAboutUsInfoDetailServiceImpl implements HwAboutUsInfoDetailService { ... }
//
// ASP.NET Core + Furion:
// public class HwAboutUsInfoDetailService : ITransient { ... }
//
// ITransient 是 Furion 的"生命周期接口",表示"瞬态服务":
// - 每次请求都创建新实例
// - 类似 Java Spring 的 @Scope("prototype")
//
// Furion 支持三种生命周期:
// - ITransient:瞬态,每次请求新实例
// - IScoped:作用域,同一请求内共享实例
// - ISingleton:单例,全局共享一个实例
// ============================================================================
namespace Admin.NET.Plugin.HwPortal;
///
/// 关于我们明细信息服务类。
///
/// 【C# 语法知识点 - 接口实现】
/// public class HwAboutUsInfoDetailService : ITransient
///
/// ITransient 是 Furion 框架的"标记接口"(Marker Interface)。
/// 它没有任何方法,只是用来标记服务的生命周期。
///
/// 对比 Java Spring:
/// Java Spring 用 @Service + @Scope 注解来定义服务:
/// @Service
/// @Scope("prototype") // 等价于 ITransient
/// public class HwAboutUsInfoDetailService { ... }
///
/// C# Furion 用接口来标记,更符合"接口隔离原则"。
///
///
/// 【SqlSugar ORM 数据访问】
/// 这个服务使用 ISqlSugarClient 进行数据库操作。
/// SqlSugar 是 .NET 生态中流行的 ORM 框架,类似于 Java 的 MyBatis-Plus。
///
/// 对比 Java MyBatis:
// Java MyBatis:
// @Mapper
// public interface HwAboutUsInfoDetailMapper {
// HwAboutUsInfoDetail selectById(@Param("id") Long id);
// }
//
// C# SqlSugar:
// _db.Queryable().Where(...).FirstAsync();
//
// SqlSugar 的优势:
// - 强类型,编译期检查
// - Lambda 表达式,IDE 智能提示
// - 链式调用,代码更流畅
// - 性能优秀,接近原生 SQL
///
///
public class HwAboutUsInfoDetailService : ITransient
{
///
/// MyBatis 映射器名称。
///
/// 【C# 语法知识点 - const 常量】
/// const 是"编译期常量",值在编译时就确定了。
///
/// 对比 Java:
/// Java: private static final String MAPPER = "HwAboutUsInfoDetailMapper";
/// C#: private const string Mapper = "HwAboutUsInfoDetailMapper";
///
/// C# 的命名约定:
/// - const 通常用 PascalCase(首字母大写)
/// - Java 的 static final 通常用 UPPER_SNAKE_CASE
///
///
/// 【为什么保留这个常量?】
/// 虽然当前使用 SqlSugar,但保留了 XML Mapper 的回滚能力。
/// 这个常量用于指定 XML 文件中 mapper 的 namespace。
///
///
private const string Mapper = "HwAboutUsInfoDetailMapper";
///
/// MyBatis 执行器。
///
/// 【依赖注入】
/// 通过构造函数注入,和控制器注入服务的方式一样。
///
/// HwPortalMyBatisExecutor 是自定义的执行器,封装了 MyBatis 风格的 SQL 执行逻辑。
/// 当前代码中已注释掉使用,但保留以备回滚。
///
///
private readonly HwPortalMyBatisExecutor _executor;
///
/// SqlSugar 数据访问对象。
///
/// 【ISqlSugarClient 接口】
/// 这是 SqlSugar 的核心接口,提供数据库访问能力。
///
/// 对比 Java:
/// Java MyBatis: SqlSession 或 Mapper 接口
/// C# SqlSugar: ISqlSugarClient
///
/// 通过依赖注入获取,由框架在启动时配置并注册到 DI 容器。
///
///
private readonly ISqlSugarClient _db;
///
/// 构造函数(依赖注入)。
///
/// 【C# 语法知识点 - 构造函数】
/// public HwAboutUsInfoDetailService(HwPortalMyBatisExecutor executor, ISqlSugarClient db)
///
/// 对比 Java:
/// Java:
/// @Autowired
/// public HwAboutUsInfoDetailService(HwPortalMyBatisExecutor executor, ISqlSugarClient db) {
/// this.executor = executor;
/// this.db = db;
/// }
///
/// C#:
/// public HwAboutUsInfoDetailService(HwPortalMyBatisExecutor executor, ISqlSugarClient db)
/// {
/// _executor = executor;
/// _db = db;
/// }
///
/// C# 的改进:
/// 1. 不需要 @Autowired 注解,框架自动识别构造函数
/// 2. 使用 _executor 命名约定表示私有字段
/// 3. 可以使用主构造函数(C# 12+)进一步简化
///
///
/// MyBatis 执行器(保留用于回滚)
/// SqlSugar 数据访问对象
public HwAboutUsInfoDetailService(HwPortalMyBatisExecutor executor, ISqlSugarClient db)
{
_executor = executor;
_db = db;
}
///
/// 根据明细ID查询关于我们明细信息。
///
/// 【方法命名约定】
/// Select + 实体名 + By + 主键名:根据主键查询单条记录
///
/// 对比 Java 若依:
/// 若依通常使用:selectXxxById、getXxxById
/// 这里使用:SelectXxxByXxxId,更符合 C# 的 PascalCase 命名规范
///
///
/// 【async/await 异步编程】
/// public async Task Select...
///
/// 对比 Java:
/// Java:
/// public CompletableFuture select...(...) { ... }
/// 或
/// public HwAboutUsInfoDetail select...(...) { ... } // 同步
///
/// C#:
/// public async Task Select...(...) { ... }
///
/// async/await 的优势:
/// - 代码看起来像同步,但底层是异步
/// - 避免回调地狱
/// - 更好的性能(不阻塞线程)
///
///
/// 明细ID
/// 明细实体,不存在则返回 null
public async Task SelectHwAboutUsInfoDetailByUsInfoDetailId(long usInfoDetailId)
{
// 【回滚注释说明】
// 这行注释说明如何回滚到 XML Mapper 方案:
// 只需取消注释下面这行,注释掉 SqlSugar 代码即可
// 这种设计保留了"后悔药",便于调试和问题排查
//
// 回滚到 XML 方案时可直接恢复:
// return await _executor.QuerySingleAsync(Mapper, "selectHwAboutUsInfoDetailByUsInfoDetailId", new { usInfoDetailId });
// 【SqlSugar 查询语法】
// _db.Queryable():创建一个针对 HwAboutUsInfoDetail 表的查询
// .Where(item => item.UsInfoDetailId == usInfoDetailId):添加 WHERE 条件
// .FirstAsync():执行查询,返回第一条记录,如果没有则返回 null
//
// 生成的 SQL 类似于:
// SELECT * FROM hw_about_us_info_detail WHERE UsInfoDetailId = @usInfoDetailId LIMIT 1
//
// 对比 Java MyBatis:
// Java: mapper.selectOne(new QueryWrapper().eq("UsInfoDetailId", usInfoDetailId));
// C#: _db.Queryable().Where(item => item.UsInfoDetailId == usInfoDetailId).FirstAsync();
//
// C# 的优势:Lambda 表达式是强类型的,拼写错误在编译期就能发现
return await _db.Queryable()
.Where(item => item.UsInfoDetailId == usInfoDetailId)
.FirstAsync();
}
///
/// 查询关于我们明细列表。
///
/// 【动态查询条件构建】
/// 这个方法展示了如何根据输入参数动态构建查询条件。
/// 只有参数有值时,才添加对应的 WHERE 条件。
///
///
/// 【空合并运算符 ??】
/// input ?? new HwAboutUsInfoDetail()
///
/// 如果 input 为 null,则创建一个新的默认对象。
/// 这样可以避免后面的空引用异常。
///
/// 对比 Java:
/// Java: input != null ? input : new HwAboutUsInfoDetail();
/// C#: input ?? new HwAboutUsInfoDetail();
///
/// C# 更简洁!
///
///
/// 查询条件
/// 明细列表
public async Task> SelectHwAboutUsInfoDetailList(HwAboutUsInfoDetail input)
{
// 【防御性编程】
// 确保 query 不为 null,避免后续空引用异常
HwAboutUsInfoDetail query = input ?? new HwAboutUsInfoDetail();
// 回滚到 XML 方案时可直接恢复:
// return await _executor.QueryListAsync(Mapper, "selectHwAboutUsInfoDetailList", query);
// 【SqlSugar WhereIF 动态条件】
// .WhereIF(condition, expression):只有当 condition 为 true 时,才添加 WHERE 条件
//
// 对比 Java MyBatis 的 XML:
//
// AND AboutUsInfoId = #{aboutUsInfoId}
//
//
// C# SqlSugar 的 WhereIF 更直观,而且类型安全
//
// 【HasValue 可空类型检查】
// query.AboutUsInfoId.HasValue 检查可空 long? 是否有值
//
// 对比 Java:
// Java: query.getAboutUsInfoId() != null
// C#: query.AboutUsInfoId.HasValue
//
// 【string.IsNullOrWhiteSpace】
// 检查字符串是否为 null、空字符串 "" 或仅包含空白字符
//
// 对比 Java:
// Java: StringUtils.isBlank(str)(需要 Apache Commons Lang)
// C#: string.IsNullOrWhiteSpace(str)(内置)
//
// 【Contains 模糊查询】
// item.UsInfoDetailTitle.Contains(query.UsInfoDetailTitle)
// 生成 SQL:WHERE UsInfoDetailTitle LIKE '%xxx%'
//
// 【ToListAsync】
// 执行查询并返回列表,异步版本
return await _db.Queryable()
.WhereIF(query.AboutUsInfoId.HasValue, item => item.AboutUsInfoId == query.AboutUsInfoId)
.WhereIF(!string.IsNullOrWhiteSpace(query.UsInfoDetailTitle), item => item.UsInfoDetailTitle.Contains(query.UsInfoDetailTitle))
.WhereIF(!string.IsNullOrWhiteSpace(query.UsInfoDetailDesc), item => item.UsInfoDetailDesc.Contains(query.UsInfoDetailDesc))
.WhereIF(query.UsInfoDetailOrder.HasValue, item => item.UsInfoDetailOrder == query.UsInfoDetailOrder)
.WhereIF(!string.IsNullOrWhiteSpace(query.UsInfoDetailPic), item => item.UsInfoDetailPic == query.UsInfoDetailPic)
.ToListAsync();
}
///
/// 新增关于我们明细信息。
///
/// 【插入操作返回值】
/// 返回 int 表示影响行数(1 表示成功,0 表示失败)。
/// 这是为了兼容原有 XML Mapper 的返回类型。
///
///
/// 【时间戳处理】
/// HwPortalContextHelper.Now() 获取当前时间。
/// 使用助手类的好处:
/// 1. 可以统一控制时间来源(如测试时 mock)
/// 2. 可以统一时区处理
/// 3. 便于后续扩展(如使用 UTC 时间)
///
///
/// 明细数据
/// 影响行数(1成功,0失败)
public async Task InsertHwAboutUsInfoDetail(HwAboutUsInfoDetail input)
{
// 【审计字段自动填充】
// CreateTime 由代码自动设置,不需要前端传递
// 这是常见的"审计字段"处理模式
input.CreateTime = HwPortalContextHelper.Now();
// 回滚到 XML 方案时可直接恢复:
// long identity = await _executor.InsertReturnIdentityAsync(Mapper, "insertHwAboutUsInfoDetail", input);
// 【SqlSugar 插入语法】
// _db.Insertable(input):创建一个插入操作
// .ExecuteReturnEntityAsync():执行插入并返回插入后的实体(包含自增主键)
//
// 对比 Java MyBatis:
// Java:
// mapper.insert(input);
// Long identity = input.getUsInfoDetailId(); // 需要配置 useGeneratedKeys
//
// C#:
// var entity = await _db.Insertable(input).ExecuteReturnEntityAsync();
// input.UsInfoDetailId = entity.UsInfoDetailId;
//
// C# 的优势:ExecuteReturnEntityAsync 直接返回插入后的完整实体
HwAboutUsInfoDetail entity = await _db.Insertable(input).ExecuteReturnEntityAsync();
// 【回填主键】
// 将生成的自增主键回填到输入对象
// 这样调用方可以获取到新插入记录的 ID
input.UsInfoDetailId = entity.UsInfoDetailId;
// 【返回影响行数】
// 为了兼容原有接口,返回 1 或 0
// 而不是返回主键值
return input.UsInfoDetailId > 0 ? 1 : 0;
}
///
/// 更新关于我们明细信息。
///
/// 【更新策略】
/// 这里使用"先查询后更新"的策略:
/// 1. 先根据 ID 查询现有记录
/// 2. 如果记录不存在,返回 0
/// 3. 只更新输入对象中不为 null 的字段
/// 4. 执行更新
///
/// 这种策略的好处:
/// - 避免全字段更新(减少并发冲突)
/// - 可以处理部分字段更新的场景
/// - 保持原有数据不变
///
///
/// 【对比 Java 若依】
/// 若依通常使用 MyBatis 的 标签实现动态更新:
///
/// UPDATE table
///
/// field1 = #{field1},
/// field2 = #{field2},
///
/// WHERE id = #{id}
///
///
/// C# 这里手动控制,更灵活但需要更多代码。
/// 也可以用 SqlSugar 的 .UpdateColumns() 实现类似效果。
///
///
/// 更新的数据
/// 影响行数
public async Task UpdateHwAboutUsInfoDetail(HwAboutUsInfoDetail input)
{
// 【审计字段更新】
// 自动设置更新时间
input.UpdateTime = HwPortalContextHelper.Now();
// 回滚到 XML 方案时可直接恢复:
// return await _executor.ExecuteAsync(Mapper, "updateHwAboutUsInfoDetail", input);
// 【查询现有记录】
// 先查出数据库中的当前记录
// 如果记录不存在,返回 0 表示更新失败
HwAboutUsInfoDetail current = await SelectHwAboutUsInfoDetailByUsInfoDetailId(input.UsInfoDetailId ?? 0);
if (current == null)
{
return 0;
}
// 【字段级更新】
// 只更新输入对象中明确有值的字段
// null 值表示"不更新此字段"
//
// 这种设计支持部分更新(PATCH 语义)
// 对比全量更新(PUT 语义),更灵活
// 更新关联主表ID
if (input.AboutUsInfoId.HasValue)
{
current.AboutUsInfoId = input.AboutUsInfoId;
}
// 更新标题(null 检查避免覆盖为 null)
if (input.UsInfoDetailTitle != null)
{
current.UsInfoDetailTitle = input.UsInfoDetailTitle;
}
// 更新描述
if (input.UsInfoDetailDesc != null)
{
current.UsInfoDetailDesc = input.UsInfoDetailDesc;
}
// 更新排序号
if (input.UsInfoDetailOrder.HasValue)
{
current.UsInfoDetailOrder = input.UsInfoDetailOrder;
}
// 更新图片
if (input.UsInfoDetailPic != null)
{
current.UsInfoDetailPic = input.UsInfoDetailPic;
}
// 更新创建时间(通常不应该更新,但保留兼容)
if (input.CreateTime.HasValue)
{
current.CreateTime = input.CreateTime;
}
// 更新创建人
if (input.CreateBy != null)
{
current.CreateBy = input.CreateBy;
}
// 【必须更新的字段】
// UpdateTime 每次更新都必须更新
current.UpdateTime = input.UpdateTime;
// 更新更新人
if (input.UpdateBy != null)
{
current.UpdateBy = input.UpdateBy;
}
// 【执行更新】
// _db.Updateable(current):创建更新操作
// .ExecuteCommandAsync():执行更新,返回影响行数
//
// 生成的 SQL 类似于:
// UPDATE hw_about_us_info_detail
// SET AboutUsInfoId = @AboutUsInfoId, UsInfoDetailTitle = @UsInfoDetailTitle, ...
// WHERE UsInfoDetailId = @UsInfoDetailId
return await _db.Updateable(current).ExecuteCommandAsync();
}
///
/// 批量删除关于我们明细信息。
///
/// 【批量删除】
/// 根据 ID 数组批量删除记录。
///
///
/// 【IN 语句】
// .In(usInfoDetailIds) 生成 SQL:WHERE UsInfoDetailId IN (1, 2, 3)
//
// 对比 Java MyBatis:
// Java XML:
//
// #{id}
//
//
// C# SqlSugar:
// .In(usInfoDetailIds)
//
// C# 更简洁!
///
///
/// 明细ID数组
/// 影响行数
public async Task DeleteHwAboutUsInfoDetailByUsInfoDetailIds(long[] usInfoDetailIds)
{
// 回滚到 XML 方案时可直接恢复:
// return await _executor.ExecuteAsync(Mapper, "deleteHwAboutUsInfoDetailByUsInfoDetailIds", new { array = usInfoDetailIds });
// 【SqlSugar 删除语法】
// _db.Deleteable():创建删除操作
// .In(usInfoDetailIds):添加 IN 条件
// .ExecuteCommandAsync():执行删除,返回影响行数
//
// 生成的 SQL:
// DELETE FROM hw_about_us_info_detail WHERE UsInfoDetailId IN (@p1, @p2, @p3)
//
// 【注意】这里是物理删除,不是软删除
// 如果需要软删除,应该使用 Updateable 更新 IsDelete 字段
return await _db.Deleteable()
.In(usInfoDetailIds)
.ExecuteCommandAsync();
}
}