|
|
// ============================================================================
|
|
|
// 【文件说明】HwProductInfoDetailService.cs - 产品信息明细服务类
|
|
|
// ============================================================================
|
|
|
// 这个服务类负责处理产品信息明细的业务逻辑,包括:
|
|
|
// - 产品明细的 CRUD 操作
|
|
|
// - 树形结构(父子关系)管理
|
|
|
// - 祖先路径(Ancestors)自动计算
|
|
|
//
|
|
|
// 【业务背景】
|
|
|
// 产品信息明细用于管理产品的详细配置项,支持多级树形结构。
|
|
|
// 例如:产品 -> 版本 -> 配置项 -> 子配置项
|
|
|
//
|
|
|
// 【与 Java Spring Boot 的对比】
|
|
|
// Java Spring Boot:
|
|
|
// @Service
|
|
|
// public class HwProductInfoDetailServiceImpl implements HwProductInfoDetailService { ... }
|
|
|
//
|
|
|
// ASP.NET Core + Furion:
|
|
|
// public class HwProductInfoDetailService : ITransient { ... }
|
|
|
// ============================================================================
|
|
|
|
|
|
namespace Admin.NET.Plugin.HwPortal;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 产品信息明细服务类。
|
|
|
/// <para>
|
|
|
/// 【服务职责】
|
|
|
/// 1. 管理产品明细的增删改查
|
|
|
/// 2. 自动计算和维护树形结构的祖先路径(Ancestors)
|
|
|
/// 3. 处理空字符串规范化
|
|
|
/// </para>
|
|
|
/// <para>
|
|
|
/// 【树形结构说明】
|
|
|
/// - ParentId:父节点ID,0表示顶级节点
|
|
|
/// - Ancestors:祖先路径,格式为"0,1,2"表示从根到父节点的路径
|
|
|
///
|
|
|
/// 示例:
|
|
|
/// 产品A(ID=1, ParentId=0, Ancestors="0")
|
|
|
/// └── 版本B(ID=2, ParentId=1, Ancestors="0,1")
|
|
|
/// └── 配置C(ID=3, ParentId=2, Ancestors="0,1,2")
|
|
|
/// </para>
|
|
|
/// </summary>
|
|
|
public class HwProductInfoDetailService : ITransient
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// MyBatis 映射器名称(保留用于回滚)。
|
|
|
/// </summary>
|
|
|
private const string Mapper = "HwProductInfoDetailMapper";
|
|
|
|
|
|
/// <summary>
|
|
|
/// MyBatis 执行器(保留用于回滚)。
|
|
|
/// </summary>
|
|
|
private readonly HwPortalMyBatisExecutor _executor;
|
|
|
|
|
|
/// <summary>
|
|
|
/// SqlSugar 数据访问对象。
|
|
|
/// </summary>
|
|
|
private readonly ISqlSugarClient _db;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 构造函数(依赖注入)。
|
|
|
/// </summary>
|
|
|
/// <param name="executor">MyBatis 执行器</param>
|
|
|
/// <param name="db">SqlSugar 数据访问对象</param>
|
|
|
public HwProductInfoDetailService(HwPortalMyBatisExecutor executor, ISqlSugarClient db)
|
|
|
{
|
|
|
_executor = executor;
|
|
|
_db = db;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 根据明细ID查询产品明细信息。
|
|
|
/// </summary>
|
|
|
/// <param name="productInfoDetailId">明细ID</param>
|
|
|
/// <returns>产品明细实体</returns>
|
|
|
public async Task<HwProductInfoDetail> SelectHwProductInfoDetailByProductInfoDetailId(long productInfoDetailId)
|
|
|
{
|
|
|
// 回滚到 XML 方案时可直接恢复:
|
|
|
// return await _executor.QuerySingleAsync<HwProductInfoDetail>(Mapper, "selectHwProductInfoDetailByProductInfoDetailId", new { productInfoDetailId });
|
|
|
return await _db.Queryable<HwProductInfoDetail>()
|
|
|
.Where(item => item.ProductInfoDetailId == productInfoDetailId)
|
|
|
.FirstAsync();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 查询产品明细列表。
|
|
|
/// <para>
|
|
|
/// 【动态查询条件】
|
|
|
/// 支持按父ID、产品ID、配置模式、标题、描述、排序号、图片、祖先路径等条件筛选。
|
|
|
/// 所有条件都是可选的,有值时才添加到 WHERE 子句。
|
|
|
/// </para>
|
|
|
/// </summary>
|
|
|
/// <param name="input">查询条件</param>
|
|
|
/// <returns>产品明细列表</returns>
|
|
|
public async Task<List<HwProductInfoDetail>> SelectHwProductInfoDetailList(HwProductInfoDetail input)
|
|
|
{
|
|
|
HwProductInfoDetail query = input ?? new HwProductInfoDetail();
|
|
|
// 回滚到 XML 方案时可直接恢复:
|
|
|
// return await _executor.QueryListAsync<HwProductInfoDetail>(Mapper, "selectHwProductInfoDetailList", query);
|
|
|
return await _db.Queryable<HwProductInfoDetail>()
|
|
|
.WhereIF(query.ParentId.HasValue, item => item.ParentId == query.ParentId)
|
|
|
.WhereIF(query.ProductInfoId.HasValue, item => item.ProductInfoId == query.ProductInfoId)
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(query.ConfigModal), item => item.ConfigModal == query.ConfigModal)
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(query.ProductInfoDetailTitle), item => item.ProductInfoDetailTitle.Contains(query.ProductInfoDetailTitle))
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(query.ProductInfoDetailDesc), item => item.ProductInfoDetailDesc.Contains(query.ProductInfoDetailDesc))
|
|
|
.WhereIF(query.ProductInfoDetailOrder.HasValue, item => item.ProductInfoDetailOrder == query.ProductInfoDetailOrder)
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(query.ProductInfoDetailPic), item => item.ProductInfoDetailPic == query.ProductInfoDetailPic)
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(query.Ancestors), item => item.Ancestors == query.Ancestors)
|
|
|
.ToListAsync();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 新增产品明细信息。
|
|
|
/// <para>
|
|
|
/// 【树形结构处理】
|
|
|
/// 1. 如果没有指定父ID或父ID为0,设置为顶级节点(ParentId=0, Ancestors="0")
|
|
|
/// 2. 如果指定了父ID,查询父节点的祖先路径,拼接成新的祖先路径
|
|
|
///
|
|
|
/// 示例:
|
|
|
/// 父节点:Ancestors="0,1",当前 ParentId=2
|
|
|
/// 新节点:Ancestors="0,1,2"
|
|
|
/// </para>
|
|
|
/// <para>
|
|
|
/// 【空字符串规范化】
|
|
|
/// 将标题、描述、祖先路径的空字符串转为 null,与 XML 方案保持一致。
|
|
|
/// </para>
|
|
|
/// </summary>
|
|
|
/// <param name="input">产品明细数据</param>
|
|
|
/// <returns>影响行数</returns>
|
|
|
public async Task<int> InsertHwProductInfoDetail(HwProductInfoDetail input)
|
|
|
{
|
|
|
// 【树形结构处理】
|
|
|
// 如果没有父ID或父ID为0,设置为顶级节点
|
|
|
if (!input.ParentId.HasValue || input.ParentId == 0)
|
|
|
{
|
|
|
input.ParentId = 0;
|
|
|
input.Ancestors = "0";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// 查询父节点的祖先路径,拼接成新的祖先路径
|
|
|
HwProductInfoDetail info = await SelectHwProductInfoDetailByProductInfoDetailId(input.ParentId.Value);
|
|
|
input.Ancestors = $"{info?.Ancestors},{input.ParentId}";
|
|
|
}
|
|
|
|
|
|
// 【数据规范化】
|
|
|
// 将空字符串转为 null,与 XML 方案保持一致
|
|
|
input.ProductInfoDetailTitle = NormalizeEmptyToNull(input.ProductInfoDetailTitle);
|
|
|
input.ProductInfoDetailDesc = NormalizeEmptyToNull(input.ProductInfoDetailDesc);
|
|
|
input.Ancestors = NormalizeEmptyToNull(input.Ancestors);
|
|
|
|
|
|
// 【审计字段】
|
|
|
input.CreateTime = HwPortalContextHelper.Now();
|
|
|
input.CreateBy = HwPortalContextHelper.CurrentUserName();
|
|
|
|
|
|
// 回滚到 XML 方案时可直接恢复:
|
|
|
// long identity = await _executor.InsertReturnIdentityAsync(Mapper, "insertHwProductInfoDetail", input);
|
|
|
HwProductInfoDetail entity = await _db.Insertable(input).ExecuteReturnEntityAsync();
|
|
|
input.ProductInfoDetailId = entity.ProductInfoDetailId;
|
|
|
return input.ProductInfoDetailId > 0 ? 1 : 0;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 更新产品明细信息。
|
|
|
/// <para>
|
|
|
/// 【字段级更新策略】
|
|
|
/// 1. 先查询现有记录
|
|
|
/// 2. 对非空字段进行更新
|
|
|
/// 3. 空字符串也视为有效值
|
|
|
/// </para>
|
|
|
/// </summary>
|
|
|
/// <param name="input">更新的数据</param>
|
|
|
/// <returns>影响行数</returns>
|
|
|
public async Task<int> UpdateHwProductInfoDetail(HwProductInfoDetail input)
|
|
|
{
|
|
|
// 【审计字段】
|
|
|
input.UpdateTime = HwPortalContextHelper.Now();
|
|
|
|
|
|
// 回滚到 XML 方案时可直接恢复:
|
|
|
// return await _executor.ExecuteAsync(Mapper, "updateHwProductInfoDetail", input);
|
|
|
HwProductInfoDetail current = await SelectHwProductInfoDetailByProductInfoDetailId(input.ProductInfoDetailId ?? 0);
|
|
|
if (current == null)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 【父ID】
|
|
|
if (input.ParentId.HasValue)
|
|
|
{
|
|
|
current.ParentId = input.ParentId;
|
|
|
}
|
|
|
|
|
|
// 【产品ID】
|
|
|
if (input.ProductInfoId.HasValue)
|
|
|
{
|
|
|
current.ProductInfoId = input.ProductInfoId;
|
|
|
}
|
|
|
|
|
|
// 【配置模式】
|
|
|
if (input.ConfigModal != null)
|
|
|
{
|
|
|
current.ConfigModal = input.ConfigModal;
|
|
|
}
|
|
|
|
|
|
// 【标题】
|
|
|
if (!string.IsNullOrWhiteSpace(input.ProductInfoDetailTitle))
|
|
|
{
|
|
|
current.ProductInfoDetailTitle = input.ProductInfoDetailTitle;
|
|
|
}
|
|
|
|
|
|
// 【描述】
|
|
|
if (!string.IsNullOrWhiteSpace(input.ProductInfoDetailDesc))
|
|
|
{
|
|
|
current.ProductInfoDetailDesc = input.ProductInfoDetailDesc;
|
|
|
}
|
|
|
|
|
|
// 【排序号】
|
|
|
if (input.ProductInfoDetailOrder.HasValue)
|
|
|
{
|
|
|
current.ProductInfoDetailOrder = input.ProductInfoDetailOrder;
|
|
|
}
|
|
|
|
|
|
// 【图片】
|
|
|
if (input.ProductInfoDetailPic != null)
|
|
|
{
|
|
|
current.ProductInfoDetailPic = input.ProductInfoDetailPic;
|
|
|
}
|
|
|
|
|
|
// 【祖先路径】
|
|
|
if (!string.IsNullOrWhiteSpace(input.Ancestors))
|
|
|
{
|
|
|
current.Ancestors = input.Ancestors;
|
|
|
}
|
|
|
|
|
|
// 【审计字段】
|
|
|
if (input.CreateTime.HasValue)
|
|
|
{
|
|
|
current.CreateTime = input.CreateTime;
|
|
|
}
|
|
|
|
|
|
if (input.CreateBy != null)
|
|
|
{
|
|
|
current.CreateBy = input.CreateBy;
|
|
|
}
|
|
|
|
|
|
current.UpdateTime = input.UpdateTime;
|
|
|
if (input.UpdateBy != null)
|
|
|
{
|
|
|
current.UpdateBy = input.UpdateBy;
|
|
|
}
|
|
|
|
|
|
return await _db.Updateable(current).ExecuteCommandAsync();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 批量删除产品明细信息。
|
|
|
/// </summary>
|
|
|
/// <param name="productInfoDetailIds">明细ID数组</param>
|
|
|
/// <returns>影响行数</returns>
|
|
|
public async Task<int> DeleteHwProductInfoDetailByProductInfoDetailIds(long[] productInfoDetailIds)
|
|
|
{
|
|
|
// 回滚到 XML 方案时可直接恢复:
|
|
|
// return await _executor.ExecuteAsync(Mapper, "deleteHwProductInfoDetailByProductInfoDetailIds", new { array = productInfoDetailIds });
|
|
|
return await _db.Deleteable<HwProductInfoDetail>()
|
|
|
.In(productInfoDetailIds)
|
|
|
.ExecuteCommandAsync();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 将空字符串规范化转为 null。
|
|
|
/// <para>
|
|
|
/// 【辅助方法】
|
|
|
/// 用于保持与 XML 方案的数据一致性。
|
|
|
/// 原 XML 中 <if> 标签会跳过空字符串,
|
|
|
/// 这里主动将空字符串转为 null,达到同样效果。
|
|
|
/// </para>
|
|
|
/// </summary>
|
|
|
/// <param name="value">输入字符串</param>
|
|
|
/// <returns>null 或原值</returns>
|
|
|
private static string NormalizeEmptyToNull(string value)
|
|
|
{
|
|
|
return string.IsNullOrWhiteSpace(value) ? null : value;
|
|
|
}
|
|
|
}
|