1. 新增通用默认状态枚举 DefaultStatusEnum,替换原有分散的状态枚举 2. 重构用户体系:拆分 WxUser 独立表存储微信身份,Users 表改为角色子用户表并关联 WxUser 3. 重构勋章模块:新增系统/期刊勋章类型,调整 JournalId 为可空,新增勋章状态字段 4. 重构微信认证流程:基于 WxUser 生成 Token,支持多子用户管理 5. 清理冗余枚举文件,重构多处业务逻辑适配新的数据结构 6. 修复用户手机号关联逻辑,迁移手机号字段至 WxUser 表
682 lines
24 KiB
C#
682 lines
24 KiB
C#
using System.Text.Json;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Options;
|
||
using QYZH.InteractiveMagazine.IService;
|
||
using QYZH.InteractiveMagazine.Models.Common;
|
||
using QYZH.InteractiveMagazine.Models.Dto;
|
||
using QYZH.InteractiveMagazine.Models.Entity;
|
||
using QYZH.InteractiveMagazine.Models.Enum;
|
||
using QYZH.InteractiveMagazine.Models.Settings;
|
||
using QYZH.InteractiveMagazine.Repository;
|
||
using SqlSugar;
|
||
|
||
namespace QYZH.InteractiveMagazine.Service;
|
||
|
||
/// <summary>
|
||
/// 勋章服务实现
|
||
/// </summary>
|
||
public class MedalService(BaseRepository<Medal> medalRepository, IOptions<MedalRuleConfigSettings> medalRuleConfig, 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 = Enum.Parse<MedalTypeEnum>(input.Type, true),
|
||
JournalId = input.JournalId,
|
||
Status = (int)DefaultStatusEnum.Active,
|
||
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 = Enum.Parse<MedalTypeEnum>(input.Type, true);
|
||
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.ToString() == 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<bool> UpdateStatusAsync(long id)
|
||
{
|
||
var medal = await medalRepository.GetByIdAsync(id);
|
||
if (medal == null)
|
||
{
|
||
logger.LogWarning("未找到要更新状态的勋章,ID: {Id}", id);
|
||
throw new BusinessException("勋章不存在", 404);
|
||
}
|
||
|
||
medal.Status = medal.Status == 0 ? 1 : 0;
|
||
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, medal.Status);
|
||
return result;
|
||
|
||
}
|
||
|
||
/// <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 == (int)UserMedalStatusEnum.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.ToString(),
|
||
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 == (int)UserMedalStatusEnum.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.ToString(),
|
||
AwardedAt = um.AwardedAt,
|
||
Status = um.Status.ToString()
|
||
})
|
||
.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 (passed, failReason) = await ValidateMedalRulesAsync(medal.Id, userId);
|
||
if (!passed)
|
||
{
|
||
logger.LogWarning("勋章规则校验未通过,用户ID: {UserId}, 勋章ID: {MedalId}, 原因: {Reason}", userId, input.MedalId, failReason);
|
||
throw new BusinessException(failReason!, 400);
|
||
}
|
||
|
||
var existingUserMedal = await Context.Queryable<UserMedal>()
|
||
.Where(um => um.UserId == userId && um.MedalId == (int)input.MedalId && um.Status == (int)UserMedalStatusEnum.Awarded)
|
||
.FirstAsync();
|
||
|
||
if (existingUserMedal != null)
|
||
{
|
||
throw new BusinessException("您已拥有该勋章", 400);
|
||
}
|
||
|
||
var userMedal = new UserMedal
|
||
{
|
||
UserId = userId,
|
||
MedalId = (int)input.MedalId,
|
||
AwardedAt = DateTime.Now,
|
||
Type = (UserMedalTypeEnum)medal.Type,
|
||
Status = (int)UserMedalStatusEnum.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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取规则配置中可用的目标表列表
|
||
/// </summary>
|
||
public Task<List<MedalRuleTableOptionOutput>> GetAvailableTablesAsync()
|
||
{
|
||
var tables = medalRuleConfig.Value.Tables?.Select(t => new MedalRuleTableOptionOutput
|
||
{
|
||
TableName = t.TableName,
|
||
DisplayName = t.DisplayName
|
||
}).ToList() ?? new List<MedalRuleTableOptionOutput>();
|
||
|
||
return Task.FromResult(tables);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定表的可用字段列表
|
||
/// </summary>
|
||
public Task<List<MedalRuleFieldOptionOutput>> GetTableFieldsAsync(string tableName)
|
||
{
|
||
var table = medalRuleConfig.Value.Tables?.FirstOrDefault(t => t.TableName == tableName);
|
||
if (table == null)
|
||
{
|
||
return Task.FromResult(new List<MedalRuleFieldOptionOutput>());
|
||
}
|
||
|
||
var fields = table.Fields.Select(f => new MedalRuleFieldOptionOutput
|
||
{
|
||
FieldName = f.FieldName,
|
||
DisplayName = f.DisplayName
|
||
}).ToList();
|
||
|
||
return Task.FromResult(fields);
|
||
}
|
||
|
||
#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.ToString(),
|
||
JournalId = medal.JournalId,
|
||
Status = (DefaultStatusEnum)medal.Status,
|
||
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
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 校验勋章规则是否满足
|
||
/// </summary>
|
||
/// <param name="medalId">勋章ID</param>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <returns>(是否通过, 失败原因)</returns>
|
||
private async Task<(bool passed, string? failReason)> ValidateMedalRulesAsync(long medalId, long userId)
|
||
{
|
||
var rules = await Context.Queryable<MedalRule>()
|
||
.Where(r => r.MedalId == medalId && !r.IsDeleted)
|
||
.OrderBy(r => r.RuleOrder)
|
||
.ToListAsync();
|
||
|
||
if (!rules.Any())
|
||
{
|
||
return (true, null);
|
||
}
|
||
|
||
var failReasons = new List<string>();
|
||
|
||
foreach (var rule in rules)
|
||
{
|
||
var rulePassed = await EvaluateRuleAsync(rule, userId);
|
||
|
||
if (!rulePassed)
|
||
{
|
||
var reason = !string.IsNullOrWhiteSpace(rule.Description)
|
||
? rule.Description
|
||
: $"未满足条件: {rule.TargetField} {rule.Operator} {rule.ThresholdValue}";
|
||
failReasons.Add(reason);
|
||
}
|
||
|
||
var logicOp = rule.LogicOperator?.ToUpper() ?? "AND";
|
||
|
||
if (logicOp == "AND" && !rulePassed)
|
||
{
|
||
return (false, $"未满足获得条件:{string.Join(";", failReasons)}");
|
||
}
|
||
|
||
if (logicOp == "OR" && rulePassed)
|
||
{
|
||
return (true, null);
|
||
}
|
||
}
|
||
|
||
// AND 模式下所有规则都通过才到这里
|
||
// OR 模式下所有规则都没通过才到这里
|
||
var firstLogicOp = rules.First().LogicOperator?.ToUpper() ?? "AND";
|
||
if (firstLogicOp == "AND")
|
||
{
|
||
return (true, null);
|
||
}
|
||
else
|
||
{
|
||
return (false, $"未满足获得条件:{string.Join(";", failReasons)}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 评估单条规则是否满足
|
||
/// </summary>
|
||
/// <param name="rule">规则配置</param>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <returns>规则是否满足</returns>
|
||
private async Task<bool> EvaluateRuleAsync(MedalRule rule, long userId)
|
||
{
|
||
try
|
||
{
|
||
var sql = $"SELECT COUNT(1) FROM [{rule.TargetTable}] WHERE [{rule.TargetField}] {rule.Operator} @ThresholdValue AND IsDeleted = 0 AND UserId = @UserId";
|
||
var parameters = new List<SugarParameter>
|
||
{
|
||
new SugarParameter("@ThresholdValue", rule.ThresholdValue),
|
||
new SugarParameter("@UserId", userId)
|
||
};
|
||
|
||
// 处理额外筛选条件
|
||
if (!string.IsNullOrWhiteSpace(rule.FilterCondition))
|
||
{
|
||
var filters = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(rule.FilterCondition);
|
||
if (filters != null)
|
||
{
|
||
var index = 0;
|
||
foreach (var kv in filters)
|
||
{
|
||
var paramName = $"@Filter_{index}";
|
||
sql += $" AND [{kv.Key}] = {paramName}";
|
||
var paramValue = kv.Value.ValueKind switch
|
||
{
|
||
JsonValueKind.Number => kv.Value.GetInt32() as object,
|
||
JsonValueKind.String => kv.Value.GetString() as object,
|
||
JsonValueKind.True => true as object,
|
||
JsonValueKind.False => false as object,
|
||
_ => kv.Value.GetRawText()
|
||
};
|
||
parameters.Add(new SugarParameter(paramName, paramValue!));
|
||
index++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理时间范围
|
||
var dateRange = GetDateRange(rule);
|
||
if (dateRange.HasValue)
|
||
{
|
||
sql += " AND CreatedAt >= @DateStart AND CreatedAt <= @DateEnd";
|
||
parameters.Add(new SugarParameter("@DateStart", dateRange.Value.start));
|
||
parameters.Add(new SugarParameter("@DateEnd", dateRange.Value.end));
|
||
}
|
||
|
||
var count = await Context.Ado.GetIntAsync(sql, parameters.ToArray());
|
||
return count > 0;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "规则评估异常,规则ID: {RuleId}, 目标表: {Table}, 目标字段: {Field}",
|
||
rule.Id, rule.TargetTable, rule.TargetField);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据规则的时间范围类型计算起止时间
|
||
/// </summary>
|
||
private static (DateTime start, DateTime end)? GetDateRange(MedalRule rule)
|
||
{
|
||
var now = DateTime.Now;
|
||
|
||
return rule.DateRangeType?.ToLower() switch
|
||
{
|
||
"currentmonth" => (new DateTime(now.Year, now.Month, 1), now),
|
||
"currentweek" => (now.AddDays(-(int)now.DayOfWeek), now),
|
||
"last7days" => (now.AddDays(-7), now),
|
||
"custom" when rule.DateRangeStart.HasValue && rule.DateRangeEnd.HasValue => (rule.DateRangeStart.Value, rule.DateRangeEnd.Value),
|
||
_ => null
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
}
|