Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/MedalService.cs
glz eab84747a5 refactor: 重构勋章模块,拆分规则独立管理,优化期刊绑定逻辑
1.  新增MedalRule实体类,将勋章条件拆分独立为规则管理
2.  重构Medal相关服务和DTO,支持勋章规则的增删改查
3.  简化Journal绑定逻辑,移除冗余的实例化期刊校验和字段
4.  重命名部分实体类,统一命名前缀
5.  为勋章相关操作添加事务管理,优化异常处理逻辑
2026-06-05 15:52:11 +08:00

503 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 勋章服务实现
/// </summary>
public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalService> logger) : BaseRepository<Medal>, IMedalService
{
/// <summary>
/// 创建勋章(含规则,事务)
/// </summary>
public async Task<MedalOutput> CreateAsync(MedalInput input)
{
logger.LogInformation("正在创建勋章,勋章名称: {Name}", input.Name);
if (string.IsNullOrWhiteSpace(input.Name))
{
throw new BusinessException("勋章名称不能为空", 400);
}
if (string.IsNullOrWhiteSpace(input.Type))
{
throw new BusinessException("勋章类型不能为空", 400);
}
var medal = new Medal
{
Name = input.Name.Trim(),
Description = input.Description,
ImageUrl = input.ImageUrl,
SortOrder = input.SortOrder,
Type = input.Type,
JournalId = input.JournalId,
CreatedBy = "System",
UpdatedBy = "System",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsDeleted = false
};
try
{
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);
var rules = await GetRulesByMedalIdAsync(medal.Id);
return BuildMedalOutput(medal, rules);
}
/// <summary>
/// 更新勋章(含规则,事务)
/// </summary>
public async Task<MedalOutput> UpdateAsync(long id, MedalInput input)
{
logger.LogInformation("正在更新勋章ID: {Id}", id);
var medal = await medalRepository.GetByIdAsync(id);
if (medal == null)
{
logger.LogWarning("未找到要更新的勋章ID: {Id}", id);
throw new BusinessException("勋章不存在", 404);
}
if (string.IsNullOrWhiteSpace(input.Name))
{
throw new BusinessException("勋章名称不能为空", 400);
}
if (string.IsNullOrWhiteSpace(input.Type))
{
throw new BusinessException("勋章类型不能为空", 400);
}
medal.Name = input.Name.Trim();
medal.Description = input.Description;
medal.ImageUrl = input.ImageUrl;
medal.SortOrder = input.SortOrder;
medal.Type = input.Type;
medal.JournalId = input.JournalId;
medal.UpdatedBy = "System";
medal.UpdatedAt = DateTime.Now;
try
{
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);
var rules = await GetRulesByMedalIdAsync(medal.Id);
return BuildMedalOutput(medal, rules);
}
/// <summary>
/// 删除勋章(软删除,含规则)
/// </summary>
public async Task DeleteAsync(long id)
{
logger.LogInformation("正在删除勋章ID: {Id}", id);
var medal = await medalRepository.GetByIdAsync(id);
if (medal == null)
{
logger.LogWarning("未找到要删除的勋章ID: {Id}", id);
throw new BusinessException("勋章不存在", 404);
}
try
{
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);
}
logger.LogInformation("勋章删除成功ID: {Id}", id);
}
/// <summary>
/// 根据ID获取勋章含规则
/// </summary>
public async Task<MedalOutput> GetByIdAsync(long id)
{
logger.LogInformation("正在获取勋章信息ID: {Id}", id);
var medal = await medalRepository.GetByIdAsync(id);
if (medal == null)
{
logger.LogWarning("未找到勋章ID: {Id}", id);
throw new BusinessException("勋章不存在", 404);
}
var rules = await GetRulesByMedalIdAsync(medal.Id);
return BuildMedalOutput(medal, rules);
}
/// <summary>
/// 分页查询勋章列表(含规则)
/// </summary>
public async Task<PageListModel<MedalOutput>> GetListAsync(MedalQueryInput input)
{
logger.LogInformation("正在查询勋章列表,页码: {PageIndex}, 每页条数: {PageSize}", input.PageIndex, input.PageSize);
if (input.PageIndex <= 0)
{
throw new BusinessException("页码必须大于0", 400);
}
if (input.PageSize <= 0 || input.PageSize > 100)
{
throw new BusinessException("每页条数必须在1-100之间", 400);
}
RefAsync<int> totalNumber = 0;
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)
.OrderBy(m => m.SortOrder)
.OrderByDescending(m => m.CreatedAt)
.ToPageListAsync(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>
/// 更新勋章启用/禁用状态
/// </summary>
public async Task UpdateStatusAsync(long id, int status)
{
logger.LogInformation("正在更新勋章状态ID: {Id}, Status: {Status}", id, status);
if (status != 0 && status != 1)
{
throw new BusinessException("状态值无效只能为0(禁用)或1(启用)", 400);
}
var medal = await medalRepository.GetByIdAsync(id);
if (medal == null)
{
logger.LogWarning("未找到要更新状态的勋章ID: {Id}", id);
throw new BusinessException("勋章不存在", 404);
}
medal.Status = status;
medal.UpdatedBy = "System";
medal.UpdatedAt = DateTime.Now;
var result = await medalRepository.UpdateAsync(medal);
if (!result)
{
logger.LogError("勋章状态更新失败ID: {Id}", id);
throw new BusinessException("更新勋章状态失败", 500);
}
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>
public async Task<List<WxMedalListOutput>> GetAllMedalsAsync(long userId)
{
logger.LogInformation("正在获取所有勋章列表用户ID: {UserId}", userId);
var medals = await medalRepository.Queryable()
.Where(m => m.Status == 1)
.OrderBy(m => m.SortOrder)
.OrderByDescending(m => m.CreatedAt)
.ToListAsync();
var userMedals = await Context.Queryable<UserMedal>()
.Where(um => um.UserId == userId && um.Status == "Awarded")
.ToListAsync();
var userMedalDict = userMedals.ToDictionary(um => um.MedalId, um => um.AwardedAt);
return medals.Select(m => new WxMedalListOutput
{
Id = m.Id,
Name = m.Name,
Description = m.Description,
ImageUrl = m.ImageUrl,
SortOrder = m.SortOrder,
Type = m.Type,
IsOwned = userMedalDict.ContainsKey((int)m.Id),
AwardedAt = userMedalDict.TryGetValue((int)m.Id, out var awardedAt) ? awardedAt : null
}).ToList();
}
/// <summary>
/// 获取用户已拥有的勋章列表
/// </summary>
public async Task<List<WxUserMedalOutput>> GetUserMedalsAsync(long userId)
{
logger.LogInformation("正在获取用户勋章列表用户ID: {UserId}", userId);
var result = await Context.Queryable<UserMedal>()
.InnerJoin<Medal>((um, m) => um.MedalId == m.Id)
.Where((um, m) => um.UserId == userId && um.Status == "Awarded")
.OrderByDescending((um, m) => um.AwardedAt)
.Select((um, m) => new WxUserMedalOutput
{
MedalId = m.Id,
Name = m.Name,
Description = m.Description,
ImageUrl = m.ImageUrl,
Type = m.Type,
AwardedAt = um.AwardedAt,
Status = um.Status
})
.ToListAsync();
return result;
}
/// <summary>
/// 激活/获得勋章
/// </summary>
public async Task ActivateMedalAsync(long userId, WxMedalActivateInput input)
{
logger.LogInformation("用户正在激活勋章用户ID: {UserId}, 勋章ID: {MedalId}", userId, input.MedalId);
if (input.MedalId <= 0)
{
throw new BusinessException("勋章ID无效", 400);
}
var medal = await medalRepository.GetByIdAsync(input.MedalId);
if (medal == null)
{
logger.LogWarning("未找到要激活的勋章勋章ID: {MedalId}", input.MedalId);
throw new BusinessException("勋章不存在", 404);
}
if (medal.Status != 1)
{
throw new BusinessException("该勋章当前不可获得", 400);
}
var existingUserMedal = await Context.Queryable<UserMedal>()
.Where(um => um.UserId == userId && um.MedalId == (int)input.MedalId && um.Status == "Awarded")
.FirstAsync();
if (existingUserMedal != null)
{
throw new BusinessException("您已拥有该勋章", 400);
}
var userMedal = new UserMedal
{
UserId = userId,
MedalId = (int)input.MedalId,
AwardedAt = DateTime.Now,
Type = medal.Type,
Status = "Awarded",
CreatedBy = "System",
UpdatedBy = "System",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now,
IsDeleted = false
};
var insertResult = await Context.Insertable(userMedal).ExecuteCommandAsync();
if (insertResult <= 0)
{
logger.LogError("勋章激活失败用户ID: {UserId}, 勋章ID: {MedalId}", userId, input.MedalId);
throw new BusinessException("激活勋章失败", 500);
}
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
}