refactor: 重构勋章模块,拆分规则独立管理,优化期刊绑定逻辑

1.  新增MedalRule实体类,将勋章条件拆分独立为规则管理
2.  重构Medal相关服务和DTO,支持勋章规则的增删改查
3.  简化Journal绑定逻辑,移除冗余的实例化期刊校验和字段
4.  重命名部分实体类,统一命名前缀
5.  为勋章相关操作添加事务管理,优化异常处理逻辑
This commit is contained in:
glz
2026-06-05 15:52:11 +08:00
parent f9b79417a0
commit eab84747a5
9 changed files with 471 additions and 193 deletions

View File

@ -50,6 +50,13 @@ public interface IMedalService : IBaseService<Medal>
/// <param name="status">状态: 0=禁用, 1=启用</param>
Task UpdateStatusAsync(long id, int status);
/// <summary>
/// 获取勋章的规则列表
/// </summary>
/// <param name="medalId">勋章ID</param>
/// <returns>规则列表</returns>
Task<List<MedalRuleOutput>> GetRulesByMedalIdAsync(long medalId);
/// <summary>
/// 获取所有启用勋章列表(含当前用户拥有状态)
/// </summary>

View File

@ -13,7 +13,7 @@ public class BindJournalInput
/// <summary>
/// 实例化期刊Id扫码解析的具体期刊实例Id可选
/// </summary>
public long? JournalInstanceId { get; set; }
public long Id { get; set; }
/// <summary>
/// 关联类型: Read(已读), Favorite(收藏), Subscribe(订阅),默认 Subscribe
@ -41,11 +41,6 @@ public class BindJournalOutput
/// </summary>
public long JournalId { get; set; }
/// <summary>
/// 实例化期刊Id
/// </summary>
public long? JournalInstanceId { get; set; }
/// <summary>
/// 关联类型
/// </summary>
@ -75,7 +70,7 @@ public class UserJournalQueryInput : PageQueryModel
/// <summary>
/// 实例化期刊Id
/// </summary>
public long? JournalInstanceId { get; set; }
public long Id { get; set; }
/// <summary>
/// 关联类型: Read, Favorite, Subscribe

View File

@ -20,16 +20,6 @@ public class MedalInput
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// 条件类型: EvolutionCount, FeedingCount等
/// </summary>
public string ConditionType { get; set; } = string.Empty;
/// <summary>
/// 条件阈值
/// </summary>
public int ConditionValue { get; set; }
/// <summary>
/// 排序
/// </summary>
@ -44,6 +34,11 @@ public class MedalInput
/// 书id
/// </summary>
public long JournalId { get; set; }
/// <summary>
/// 勋章规则列表
/// </summary>
public List<MedalRuleInput>? Rules { get; set; }
}
/// <summary>
@ -71,16 +66,6 @@ public class MedalOutput
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// 条件类型
/// </summary>
public string ConditionType { get; set; } = string.Empty;
/// <summary>
/// 条件阈值
/// </summary>
public int ConditionValue { get; set; }
/// <summary>
/// 排序
/// </summary>
@ -96,6 +81,11 @@ public class MedalOutput
/// </summary>
public long JournalId { get; set; }
/// <summary>
/// 勋章规则列表
/// </summary>
public List<MedalRuleOutput>? Rules { get; set; }
/// <summary>
/// 创建人
/// </summary>
@ -131,11 +121,148 @@ public class MedalQueryInput : PageQueryModel
/// 勋章的类型: Pet, Community
/// </summary>
public string? Type { get; set; }
}
/// <summary>
/// 勋章规则输入
/// </summary>
public class MedalRuleInput
{
/// <summary>
/// 目标表名JournalPageTask、UserAction等
/// </summary>
public string TargetTable { get; set; } = string.Empty;
/// <summary>
/// 条件类型
/// 目标字段名TotalScore、LoginDays等
/// </summary>
public string? ConditionType { get; set; }
public string TargetField { get; set; } = string.Empty;
/// <summary>
/// 比较运算符: >=, >, =, <=, <
/// </summary>
public string Operator { get; set; } = ">=";
/// <summary>
/// 阈值数量
/// </summary>
public int ThresholdValue { get; set; }
/// <summary>
/// 额外筛选条件JSON格式
/// </summary>
public string? FilterCondition { get; set; }
/// <summary>
/// 时间范围类型: All/CurrentMonth/CurrentWeek/Last7Days/Custom
/// </summary>
public string? DateRangeType { get; set; }
/// <summary>
/// 自定义起始时间
/// </summary>
public DateTime? DateRangeStart { get; set; }
/// <summary>
/// 自定义结束时间
/// </summary>
public DateTime? DateRangeEnd { get; set; }
/// <summary>
/// 多规则间的逻辑关系: AND/OR
/// </summary>
public string LogicOperator { get; set; } = "AND";
/// <summary>
/// 规则执行顺序
/// </summary>
public int RuleOrder { get; set; }
/// <summary>
/// 规则说明
/// </summary>
public string? Description { get; set; }
}
/// <summary>
/// 勋章规则输出
/// </summary>
public class MedalRuleOutput
{
/// <summary>
/// 规则ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 关联的勋章ID
/// </summary>
public long MedalId { get; set; }
/// <summary>
/// 目标表名
/// </summary>
public string TargetTable { get; set; } = string.Empty;
/// <summary>
/// 目标字段名
/// </summary>
public string TargetField { get; set; } = string.Empty;
/// <summary>
/// 比较运算符
/// </summary>
public string Operator { get; set; } = string.Empty;
/// <summary>
/// 阈值数量
/// </summary>
public int ThresholdValue { get; set; }
/// <summary>
/// 额外筛选条件
/// </summary>
public string? FilterCondition { get; set; }
/// <summary>
/// 时间范围类型
/// </summary>
public string? DateRangeType { get; set; }
/// <summary>
/// 自定义起始时间
/// </summary>
public DateTime? DateRangeStart { get; set; }
/// <summary>
/// 自定义结束时间
/// </summary>
public DateTime? DateRangeEnd { get; set; }
/// <summary>
/// 多规则间的逻辑关系
/// </summary>
public string LogicOperator { get; set; } = string.Empty;
/// <summary>
/// 规则执行顺序
/// </summary>
public int RuleOrder { get; set; }
/// <summary>
/// 规则说明
/// </summary>
public string? Description { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedAt { get; set; }
}
/// <summary>
@ -163,16 +290,6 @@ public class WxMedalListOutput
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// 条件类型
/// </summary>
public string ConditionType { get; set; } = string.Empty;
/// <summary>
/// 条件阈值
/// </summary>
public int ConditionValue { get; set; }
/// <summary>
/// 排序
/// </summary>

View File

@ -6,9 +6,9 @@ namespace QYZH.InteractiveMagazine.Models.Entity
///模板评论表
///</summary>
[SugarTable("MessageComment")]
public partial class MessageComment : SqlSugarBaseEntity
public partial class CommunityMessageComment : SqlSugarBaseEntity
{
public MessageComment(){
public CommunityMessageComment(){
}

View File

@ -6,9 +6,9 @@ namespace QYZH.InteractiveMagazine.Models.Entity
///固定模板言论表
///</summary>
[SugarTable("TemplateSentence")]
public partial class TemplateSentence : SqlSugarBaseEntity
public partial class CommunityTemplateSentence : SqlSugarBaseEntity
{
public TemplateSentence(){
public CommunityTemplateSentence(){
}

View File

@ -35,20 +35,6 @@ namespace QYZH.InteractiveMagazine.Models.Entity
/// </summary>
public string ImageUrl { get; set; }
/// <summary>
/// Desc:条件类型: EvolutionCount, FeedingCount等
/// Default:
/// Nullable:False
/// </summary>
public string ConditionType { get; set; }
/// <summary>
/// Desc:条件阈值
/// Default:
/// Nullable:False
/// </summary>
public int ConditionValue { get; set; }
/// <summary>
/// Desc:排序
/// Default:0

View File

@ -0,0 +1,101 @@
using System;
using System.Linq;
using System.Text;
using SqlSugar;
namespace QYZH.InteractiveMagazine.Models.Entity
{
///<summary>
///勋章获取规则配置表
///</summary>
[SugarTable("MedalRule")]
public partial class MedalRule : SqlSugarBaseEntity
{
/// <summary>
/// Desc:关联的勋章ID
/// Default:
/// Nullable:False
/// </summary>
public long MedalId {get;set;}
/// <summary>
/// Desc:目标表名JournalPageTask、UserAction等
/// Default:
/// Nullable:False
/// </summary>
public string TargetTable {get;set;}
/// <summary>
/// Desc:目标字段名TotalScore、LoginDays等
/// Default:
/// Nullable:False
/// </summary>
public string TargetField {get;set;}
/// <summary>
/// Desc:比较运算符: >=, >, =, <=, <
/// Default:>=
/// Nullable:False
/// </summary>
public string Operator {get;set;}
/// <summary>
/// Desc:阈值数量
/// Default:
/// Nullable:False
/// </summary>
public int ThresholdValue {get;set;}
/// <summary>
/// Desc:额外筛选条件JSON格式{"Type": 1, "Status": 2}
/// Default:
/// Nullable:True
/// </summary>
public string FilterCondition {get;set;}
/// <summary>
/// Desc:时间范围类型: All/CurrentMonth/CurrentWeek/Last7Days/Custom
/// Default:
/// Nullable:True
/// </summary>
public string DateRangeType {get;set;}
/// <summary>
/// Desc:自定义起始时间
/// Default:
/// Nullable:True
/// </summary>
public DateTime? DateRangeStart {get;set;}
/// <summary>
/// Desc:自定义结束时间
/// Default:
/// Nullable:True
/// </summary>
public DateTime? DateRangeEnd {get;set;}
/// <summary>
/// Desc:多规则间的逻辑关系: AND/OR
/// Default:AND
/// Nullable:False
/// </summary>
public string LogicOperator {get;set;}
/// <summary>
/// Desc:规则执行顺序(多个规则时生效)
/// Default:0
/// Nullable:False
/// </summary>
public int RuleOrder {get;set;}
/// <summary>
/// Desc:规则说明
/// Default:
/// Nullable:True
/// </summary>
public string Description {get;set;}
}
}

View File

@ -15,7 +15,7 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
{
/// <summary>
/// 创建勋章
/// 创建勋章(含规则,事务)
/// </summary>
public async Task<MedalOutput> CreateAsync(MedalInput input)
{
@ -26,28 +26,16 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
throw new BusinessException("勋章名称不能为空", 400);
}
if (string.IsNullOrWhiteSpace(input.ConditionType))
{
throw new BusinessException("条件类型不能为空", 400);
}
if (string.IsNullOrWhiteSpace(input.Type))
{
throw new BusinessException("勋章类型不能为空", 400);
}
if (input.ConditionValue < 0)
{
throw new BusinessException("条件阈值不能为负数", 400);
}
var medal = new Medal
{
Name = input.Name.Trim(),
Description = input.Description,
ImageUrl = input.ImageUrl,
ConditionType = input.ConditionType,
ConditionValue = input.ConditionValue,
SortOrder = input.SortOrder,
Type = input.Type,
JournalId = input.JournalId,
@ -58,35 +46,40 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
IsDeleted = false
};
var result = await medalRepository.InsertAsync(medal);
if (!result)
try
{
logger.LogError("勋章创建失败,勋章名称: {Name}", input.Name);
await UseTranAsync(async () =>
{
var result = await medalRepository.InsertAsync(medal);
if (!result)
{
throw new BusinessException("创建勋章失败", 500);
}
if (input.Rules != null && input.Rules.Count > 0)
{
await InsertRulesAsync(medal.Id, input.Rules);
}
});
}
catch (BusinessException)
{
throw;
}
catch (Exception ex)
{
logger.LogError(ex, "勋章创建事务失败,勋章名称: {Name}", input.Name);
throw new BusinessException("创建勋章失败", 500);
}
logger.LogInformation("勋章创建成功,勋章名称: {Name}, ID: {Id}", input.Name, medal.Id);
return new MedalOutput
{
Id = medal.Id,
Name = medal.Name,
Description = medal.Description,
ImageUrl = medal.ImageUrl,
ConditionType = medal.ConditionType,
ConditionValue = medal.ConditionValue,
SortOrder = medal.SortOrder,
Type = medal.Type,
JournalId = medal.JournalId,
CreatedBy = medal.CreatedBy,
CreatedAt = medal.CreatedAt,
UpdatedBy = medal.UpdatedBy,
UpdatedAt = medal.UpdatedAt
};
var rules = await GetRulesByMedalIdAsync(medal.Id);
return BuildMedalOutput(medal, rules);
}
/// <summary>
/// 更新勋章
/// 更新勋章(含规则,事务)
/// </summary>
public async Task<MedalOutput> UpdateAsync(long id, MedalInput input)
{
@ -104,61 +97,60 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
throw new BusinessException("勋章名称不能为空", 400);
}
if (string.IsNullOrWhiteSpace(input.ConditionType))
{
throw new BusinessException("条件类型不能为空", 400);
}
if (string.IsNullOrWhiteSpace(input.Type))
{
throw new BusinessException("勋章类型不能为空", 400);
}
if (input.ConditionValue < 0)
{
throw new BusinessException("条件阈值不能为负数", 400);
}
medal.Name = input.Name.Trim();
medal.Description = input.Description;
medal.ImageUrl = input.ImageUrl;
medal.ConditionType = input.ConditionType;
medal.ConditionValue = input.ConditionValue;
medal.SortOrder = input.SortOrder;
medal.Type = input.Type;
medal.JournalId = input.JournalId;
medal.UpdatedBy = "System";
medal.UpdatedAt = DateTime.Now;
var result = await medalRepository.UpdateAsync(medal);
if (!result)
try
{
logger.LogError("勋章更新失败ID: {Id}", id);
await UseTranAsync(async () =>
{
var result = await medalRepository.UpdateAsync(medal);
if (!result)
{
throw new BusinessException("更新勋章失败", 500);
}
// 删除旧规则,重新插入新规则
await Context.Updateable<MedalRule>()
.SetColumns(r => new MedalRule { IsDeleted = true, UpdatedBy = "System", UpdatedAt = DateTime.Now })
.Where(r => r.MedalId == medal.Id)
.ExecuteCommandAsync();
if (input.Rules != null && input.Rules.Count > 0)
{
await InsertRulesAsync(medal.Id, input.Rules);
}
});
}
catch (BusinessException)
{
throw;
}
catch (Exception ex)
{
logger.LogError(ex, "勋章更新事务失败ID: {Id}", id);
throw new BusinessException("更新勋章失败", 500);
}
logger.LogInformation("勋章更新成功ID: {Id}", id);
return new MedalOutput
{
Id = medal.Id,
Name = medal.Name,
Description = medal.Description,
ImageUrl = medal.ImageUrl,
ConditionType = medal.ConditionType,
ConditionValue = medal.ConditionValue,
SortOrder = medal.SortOrder,
Type = medal.Type,
JournalId = medal.JournalId,
CreatedBy = medal.CreatedBy,
CreatedAt = medal.CreatedAt,
UpdatedBy = medal.UpdatedBy,
UpdatedAt = medal.UpdatedAt
};
var rules = await GetRulesByMedalIdAsync(medal.Id);
return BuildMedalOutput(medal, rules);
}
/// <summary>
/// 删除勋章(软删除)
/// 删除勋章(软删除,含规则
/// </summary>
public async Task DeleteAsync(long id)
{
@ -171,10 +163,30 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
throw new BusinessException("勋章不存在", 404);
}
var result = await medalRepository.DeleteByIdAsync(id);
if (!result)
try
{
logger.LogError("勋章删除失败ID: {Id}", id);
await UseTranAsync(async () =>
{
var result = await medalRepository.DeleteByIdAsync(id);
if (!result)
{
throw new BusinessException("删除勋章失败", 500);
}
// 同时软删除关联规则
await Context.Updateable<MedalRule>()
.SetColumns(r => new MedalRule { IsDeleted = true, UpdatedBy = "System", UpdatedAt = DateTime.Now })
.Where(r => r.MedalId == id)
.ExecuteCommandAsync();
});
}
catch (BusinessException)
{
throw;
}
catch (Exception ex)
{
logger.LogError(ex, "勋章删除事务失败ID: {Id}", id);
throw new BusinessException("删除勋章失败", 500);
}
@ -182,7 +194,7 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
}
/// <summary>
/// 根据ID获取勋章
/// 根据ID获取勋章(含规则)
/// </summary>
public async Task<MedalOutput> GetByIdAsync(long id)
{
@ -195,26 +207,12 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
throw new BusinessException("勋章不存在", 404);
}
return new MedalOutput
{
Id = medal.Id,
Name = medal.Name,
Description = medal.Description,
ImageUrl = medal.ImageUrl,
ConditionType = medal.ConditionType,
ConditionValue = medal.ConditionValue,
SortOrder = medal.SortOrder,
Type = medal.Type,
JournalId = medal.JournalId,
CreatedBy = medal.CreatedBy,
CreatedAt = medal.CreatedAt,
UpdatedBy = medal.UpdatedBy,
UpdatedAt = medal.UpdatedAt
};
var rules = await GetRulesByMedalIdAsync(medal.Id);
return BuildMedalOutput(medal, rules);
}
/// <summary>
/// 分页查询勋章列表
/// 分页查询勋章列表(含规则)
/// </summary>
public async Task<PageListModel<MedalOutput>> GetListAsync(MedalQueryInput input)
{
@ -231,31 +229,27 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
}
RefAsync<int> totalNumber = 0;
var pageResult = await medalRepository.Queryable()
var medals = await medalRepository.Queryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), m => m.Name.Contains(input.Name))
.WhereIF(!string.IsNullOrWhiteSpace(input.Type), m => m.Type == input.Type)
.WhereIF(!string.IsNullOrWhiteSpace(input.ConditionType), m => m.ConditionType == input.ConditionType)
.OrderBy(m => m.SortOrder, SqlSugar.OrderByType.Asc)
.OrderBy(m => m.SortOrder)
.OrderByDescending(m => m.CreatedAt)
.Select(m => new MedalOutput
{
Id = m.Id,
Name = m.Name,
Description = m.Description,
ImageUrl = m.ImageUrl,
ConditionType = m.ConditionType,
ConditionValue = m.ConditionValue,
SortOrder = m.SortOrder,
Type = m.Type,
JournalId = m.JournalId,
CreatedBy = m.CreatedBy,
CreatedAt = m.CreatedAt,
UpdatedBy = m.UpdatedBy,
UpdatedAt = m.UpdatedAt
}, true)
.ToPageListAsync(input.PageIndex, input.PageSize, totalNumber);
return new PageListModel<MedalOutput>(pageResult, input.PageIndex, input.PageSize, totalNumber);
// 批量查询所有勋章的规则
var medalIds = medals.Select(m => m.Id).ToList();
var allRules = await Context.Queryable<MedalRule>()
.Where(r => medalIds.Contains(r.MedalId) && !r.IsDeleted)
.ToListAsync();
var rulesDict = allRules.GroupBy(r => r.MedalId)
.ToDictionary(g => g.Key, g => g.OrderBy(r => r.RuleOrder).Select(r => MapRuleOutput(r)).ToList());
var pageResult = medals.Select(m => BuildMedalOutput(m, rulesDict.GetValueOrDefault(m.Id))).ToList();
var result = new PageListModel<MedalOutput>(new List<MedalOutput>(), input.PageIndex, input.PageSize, totalNumber);
result.Result = pageResult;
return result;
}
/// <summary>
@ -291,6 +285,19 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
logger.LogInformation("勋章状态更新成功ID: {Id}, Status: {Status}", id, status);
}
/// <summary>
/// 获取勋章的规则列表
/// </summary>
public async Task<List<MedalRuleOutput>> GetRulesByMedalIdAsync(long medalId)
{
var rules = await Context.Queryable<MedalRule>()
.Where(r => r.MedalId == medalId && !r.IsDeleted)
.OrderBy(r => r.RuleOrder)
.ToListAsync();
return rules.Select(r => MapRuleOutput(r)).ToList();
}
/// <summary>
/// 获取所有启用勋章列表(含当前用户拥有状态)
/// </summary>
@ -300,7 +307,7 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
var medals = await medalRepository.Queryable()
.Where(m => m.Status == 1)
.OrderBy(m => m.SortOrder, SqlSugar.OrderByType.Asc)
.OrderBy(m => m.SortOrder)
.OrderByDescending(m => m.CreatedAt)
.ToListAsync();
@ -316,8 +323,6 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
Name = m.Name,
Description = m.Description,
ImageUrl = m.ImageUrl,
ConditionType = m.ConditionType,
ConditionValue = m.ConditionValue,
SortOrder = m.SortOrder,
Type = m.Type,
IsOwned = userMedalDict.ContainsKey((int)m.Id),
@ -407,4 +412,91 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
logger.LogInformation("勋章激活成功用户ID: {UserId}, 勋章ID: {MedalId}", userId, input.MedalId);
}
#region
/// <summary>
/// 批量插入规则
/// </summary>
private async Task InsertRulesAsync(long medalId, List<MedalRuleInput> ruleInputs)
{
var rules = ruleInputs.Select(r => new MedalRule
{
MedalId = medalId,
TargetTable = r.TargetTable,
TargetField = r.TargetField,
Operator = r.Operator,
ThresholdValue = r.ThresholdValue,
FilterCondition = r.FilterCondition,
DateRangeType = r.DateRangeType,
DateRangeStart = r.DateRangeStart,
DateRangeEnd = r.DateRangeEnd,
LogicOperator = r.LogicOperator,
RuleOrder = r.RuleOrder,
Description = r.Description,
CreatedBy = "System",
UpdatedBy = "System",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsDeleted = false
}).ToList();
var count = await Context.Insertable(rules).ExecuteCommandAsync();
if (count <= 0)
{
logger.LogError("勋章规则插入失败MedalId: {MedalId}", medalId);
throw new BusinessException("创建勋章规则失败", 500);
}
logger.LogInformation("勋章规则创建成功MedalId: {MedalId}, 规则数: {Count}", medalId, rules.Count);
}
/// <summary>
/// 构建MedalOutput
/// </summary>
private static MedalOutput BuildMedalOutput(Medal medal, List<MedalRuleOutput>? rules)
{
return new MedalOutput
{
Id = medal.Id,
Name = medal.Name,
Description = medal.Description,
ImageUrl = medal.ImageUrl,
SortOrder = medal.SortOrder,
Type = medal.Type,
JournalId = medal.JournalId,
Rules = rules,
CreatedBy = medal.CreatedBy,
CreatedAt = medal.CreatedAt,
UpdatedBy = medal.UpdatedBy,
UpdatedAt = medal.UpdatedAt
};
}
/// <summary>
/// 映射规则实体到输出DTO
/// </summary>
private static MedalRuleOutput MapRuleOutput(MedalRule r)
{
return new MedalRuleOutput
{
Id = r.Id,
MedalId = r.MedalId,
TargetTable = r.TargetTable,
TargetField = r.TargetField,
Operator = r.Operator,
ThresholdValue = r.ThresholdValue,
FilterCondition = r.FilterCondition,
DateRangeType = r.DateRangeType,
DateRangeStart = r.DateRangeStart,
DateRangeEnd = r.DateRangeEnd,
LogicOperator = r.LogicOperator,
RuleOrder = r.RuleOrder,
Description = r.Description,
CreatedAt = r.CreatedAt,
UpdatedAt = r.UpdatedAt
};
}
#endregion
}

View File

@ -24,15 +24,14 @@ public class UserJournalService(
/// </summary>
public async Task<BindJournalOutput> BindJournalAsync(long userId, BindJournalInput input)
{
logger.LogInformation("用户绑定期刊UserId: {UserId}, JournalId: {JournalId}, JournalInstanceId: {JournalInstanceId}, Type: {Type}",
userId, input.JournalId, input.JournalInstanceId, input.Type);
logger.LogInformation("用户绑定期刊UserId: {UserId}, JournalId: {JournalId}, Id: {Id}, Type: {Type}",
userId, input.JournalId, input.Id, input.Type);
// 校验参数
if (input.JournalId <= 0)
if (input.JournalId <= 0|| input.Id <= 0)
{
throw new BusinessException("期刊Id不能为空", 400);
throw new BusinessException("参数错误,未获取到期刊", 400);
}
// 校验用户是否存在
var user = await usersRepository.GetByIdAsync(userId);
if (user == null || user.IsDeleted)
@ -56,29 +55,14 @@ public class UserJournalService(
throw new BusinessException("该期刊暂未发布,无法绑定", 400);
}
// 校验实例化期刊是否存在(如果传入了 JournalInstanceId
if (input.JournalInstanceId.HasValue && input.JournalInstanceId.Value > 0)
{
var instance = await journalRepository.GetByIdAsync(input.JournalInstanceId.Value);
if (instance == null || instance.IsDeleted)
{
logger.LogWarning("绑定期刊失败实例化期刊不存在JournalInstanceId: {JournalInstanceId}", input.JournalInstanceId);
throw new BusinessException("实例化期刊不存在", 404);
}
}
// 防重复绑定:同一用户 + 期刊 + 实例 + 类型
var isExist = userJournalRepository.Any(uj =>
uj.UserId == userId &&
uj.JournalId == input.JournalId &&
uj.JournalInstanceId == input.JournalInstanceId &&
uj.Type == input.Type &&
!uj.IsDeleted);
uj.Id == input.Id);
if (isExist)
{
logger.LogWarning("重复绑定期刊UserId: {UserId}, JournalId: {JournalId}, Type: {Type}", userId, input.JournalId, input.Type);
throw new BusinessException("您已绑定过该期刊,无需重复操作", 400);
throw new BusinessException("该期刊已被绑定", 400);
}
// 检查是否为首次绑定期刊(用于激活宠物)
@ -90,7 +74,7 @@ public class UserJournalService(
{
UserId = userId,
JournalId = input.JournalId,
JournalInstanceId = input.JournalInstanceId,
Id = input.Id,
Type = input.Type,
Status = "Active",
IsDeleted = false,
@ -128,7 +112,6 @@ public class UserJournalService(
Id = userJournal.Id,
UserId = userJournal.UserId,
JournalId = userJournal.JournalId,
JournalInstanceId = userJournal.JournalInstanceId,
Type = userJournal.Type,
Status = userJournal.Status,
CreatedAt = userJournal.CreatedAt
@ -156,8 +139,6 @@ public class UserJournalService(
RefAsync<int> totalNumber = 0;
var pageResult = await userJournalRepository.Queryable()
.Where(uj => uj.UserId == userId)
.WhereIF(input.JournalId.HasValue, uj => uj.JournalId == input.JournalId.Value)
.WhereIF(input.JournalInstanceId.HasValue, uj => uj.JournalInstanceId == input.JournalInstanceId.Value)
.WhereIF(!string.IsNullOrWhiteSpace(input.Type), uj => uj.Type == input.Type)
.OrderByDescending(uj => uj.CreatedAt)
.Select(uj => new BindJournalOutput
@ -165,7 +146,6 @@ public class UserJournalService(
Id = uj.Id,
UserId = uj.UserId,
JournalId = uj.JournalId,
JournalInstanceId = uj.JournalInstanceId,
Type = uj.Type,
Status = uj.Status,
CreatedAt = uj.CreatedAt