// ============================================================================ // 【文件说明】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; /// /// 产品信息明细服务类。 /// /// 【服务职责】 /// 1. 管理产品明细的增删改查 /// 2. 自动计算和维护树形结构的祖先路径(Ancestors) /// 3. 处理空字符串规范化 /// /// /// 【树形结构说明】 /// - 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") /// /// public class HwProductInfoDetailService : ITransient { /// /// MyBatis 映射器名称(保留用于回滚)。 /// private const string Mapper = "HwProductInfoDetailMapper"; /// /// MyBatis 执行器(保留用于回滚)。 /// private readonly HwPortalMyBatisExecutor _executor; /// /// SqlSugar 数据访问对象。 /// private readonly ISqlSugarClient _db; /// /// 构造函数(依赖注入)。 /// /// MyBatis 执行器 /// SqlSugar 数据访问对象 public HwProductInfoDetailService(HwPortalMyBatisExecutor executor, ISqlSugarClient db) { _executor = executor; _db = db; } /// /// 根据明细ID查询产品明细信息。 /// /// 明细ID /// 产品明细实体 public async Task SelectHwProductInfoDetailByProductInfoDetailId(long productInfoDetailId) { // 回滚到 XML 方案时可直接恢复: // return await _executor.QuerySingleAsync(Mapper, "selectHwProductInfoDetailByProductInfoDetailId", new { productInfoDetailId }); return await _db.Queryable() .Where(item => item.ProductInfoDetailId == productInfoDetailId) .FirstAsync(); } /// /// 查询产品明细列表。 /// /// 【动态查询条件】 /// 支持按父ID、产品ID、配置模式、标题、描述、排序号、图片、祖先路径等条件筛选。 /// 所有条件都是可选的,有值时才添加到 WHERE 子句。 /// /// /// 查询条件 /// 产品明细列表 public async Task> SelectHwProductInfoDetailList(HwProductInfoDetail input) { HwProductInfoDetail query = input ?? new HwProductInfoDetail(); // 回滚到 XML 方案时可直接恢复: // return await _executor.QueryListAsync(Mapper, "selectHwProductInfoDetailList", query); return await _db.Queryable() .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(); } /// /// 新增产品明细信息。 /// /// 【树形结构处理】 /// 1. 如果没有指定父ID或父ID为0,设置为顶级节点(ParentId=0, Ancestors="0") /// 2. 如果指定了父ID,查询父节点的祖先路径,拼接成新的祖先路径 /// /// 示例: /// 父节点:Ancestors="0,1",当前 ParentId=2 /// 新节点:Ancestors="0,1,2" /// /// /// 【空字符串规范化】 /// 将标题、描述、祖先路径的空字符串转为 null,与 XML 方案保持一致。 /// /// /// 产品明细数据 /// 影响行数 public async Task 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; } /// /// 更新产品明细信息。 /// /// 【字段级更新策略】 /// 1. 先查询现有记录 /// 2. 对非空字段进行更新 /// 3. 空字符串也视为有效值 /// /// /// 更新的数据 /// 影响行数 public async Task 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(); } /// /// 批量删除产品明细信息。 /// /// 明细ID数组 /// 影响行数 public async Task DeleteHwProductInfoDetailByProductInfoDetailIds(long[] productInfoDetailIds) { // 回滚到 XML 方案时可直接恢复: // return await _executor.ExecuteAsync(Mapper, "deleteHwProductInfoDetailByProductInfoDetailIds", new { array = productInfoDetailIds }); return await _db.Deleteable() .In(productInfoDetailIds) .ExecuteCommandAsync(); } /// /// 将空字符串规范化转为 null。 /// /// 【辅助方法】 /// 用于保持与 XML 方案的数据一致性。 /// 原 XML 中 <if> 标签会跳过空字符串, /// 这里主动将空字符串转为 null,达到同样效果。 /// /// /// 输入字符串 /// null 或原值 private static string NormalizeEmptyToNull(string value) { return string.IsNullOrWhiteSpace(value) ? null : value; } }