feat: 完成宠物模块重构与社区功能开发
本次提交包含多项核心更新:
1. 重构宠物模块数据结构:拆分皮肤图片为独立表,优化Pet、PetEvolution实体,调整字段类型与冗余字段
2. 新增宠物皮肤图片管理表,支持多进化阶段多图片展示
3. 完善宠物DTO,新增当前形态名称、皮肤信息与图片序列返回
4. 新增社区功能模块:
- 微信端社区Feed流、点赞/取消点赞接口
- 后台社区消息管理接口与服务实现
5. 优化商城与背包模块,替换皮肤图片获取逻辑为从新表读取预览图
6. 重构勋章服务,新增规则校验逻辑
7. 调整命名规范,修复原有控制器命名问题
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using QYZH.InteractiveMagazine.IService;
|
||||
using QYZH.InteractiveMagazine.Models.Common;
|
||||
@ -380,6 +381,14 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
|
||||
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 == "Awarded")
|
||||
.FirstAsync();
|
||||
@ -498,5 +507,142 @@ public class MedalService(BaseRepository<Medal> medalRepository, ILogger<MedalSe
|
||||
};
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user