diff --git a/.gitignore b/.gitignore index 47d45e0..a2effb4 100644 --- a/.gitignore +++ b/.gitignore @@ -363,4 +363,5 @@ MigrationBackup/ FodyWeavers.xsd # Trae IDE related files -.trae/ \ No newline at end of file +.trae/ +PROJECT_STRUCTURE.md diff --git a/QYZH.InteractiveMagazine.IService/IMedalService.cs b/QYZH.InteractiveMagazine.IService/IMedalService.cs new file mode 100644 index 0000000..3bede1f --- /dev/null +++ b/QYZH.InteractiveMagazine.IService/IMedalService.cs @@ -0,0 +1,73 @@ +using QYZH.InteractiveMagazine.Models.Dto; +using QYZH.InteractiveMagazine.Models.Entity; + +namespace QYZH.InteractiveMagazine.IService; + +/// +/// 勋章服务接口 +/// +public interface IMedalService : IBaseService +{ + /// + /// 创建勋章 + /// + /// 勋章输入 + /// 创建的勋章信息 + Task CreateAsync(MedalInput input); + + /// + /// 更新勋章 + /// + /// 勋章ID + /// 勋章输入 + /// 更新后的勋章信息 + Task UpdateAsync(long id, MedalInput input); + + /// + /// 删除勋章(软删除) + /// + /// 勋章ID + Task DeleteAsync(long id); + + /// + /// 根据ID获取勋章 + /// + /// 勋章ID + /// 勋章信息 + Task GetByIdAsync(long id); + + /// + /// 分页查询勋章列表 + /// + /// 查询条件 + /// 分页结果 + Task> GetListAsync(MedalQueryInput input); + + /// + /// 更新勋章启用/禁用状态 + /// + /// 勋章ID + /// 状态: 0=禁用, 1=启用 + Task UpdateStatusAsync(long id, int status); + + /// + /// 获取所有启用勋章列表(含当前用户拥有状态) + /// + /// 当前用户ID + /// 勋章列表 + Task> GetAllMedalsAsync(long userId); + + /// + /// 获取用户已拥有的勋章列表 + /// + /// 用户ID + /// 用户勋章列表 + Task> GetUserMedalsAsync(long userId); + + /// + /// 激活/获得勋章 + /// + /// 用户ID + /// 激活输入 + Task ActivateMedalAsync(long userId, WxMedalActivateInput input); +} diff --git a/QYZH.InteractiveMagazine.Models/Dto/Medal/MedalDto.cs b/QYZH.InteractiveMagazine.Models/Dto/Medal/MedalDto.cs new file mode 100644 index 0000000..01371f9 --- /dev/null +++ b/QYZH.InteractiveMagazine.Models/Dto/Medal/MedalDto.cs @@ -0,0 +1,247 @@ +namespace QYZH.InteractiveMagazine.Models.Dto; + +/// +/// 勋章创建/更新输入 +/// +public class MedalInput +{ + /// + /// 勋章名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 获得条件描述 + /// + public string? Description { get; set; } + + /// + /// 图片地址 + /// + public string? ImageUrl { get; set; } + + /// + /// 条件类型: EvolutionCount, FeedingCount等 + /// + public string ConditionType { get; set; } = string.Empty; + + /// + /// 条件阈值 + /// + public int ConditionValue { get; set; } + + /// + /// 排序 + /// + public int SortOrder { get; set; } + + /// + /// 勋章的类型: Pet, Community + /// + public string Type { get; set; } = "Pet"; + + /// + /// 书id + /// + public long JournalId { get; set; } +} + +/// +/// 勋章输出 +/// +public class MedalOutput +{ + /// + /// 主键ID + /// + public long Id { get; set; } + + /// + /// 勋章名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 获得条件描述 + /// + public string? Description { get; set; } + + /// + /// 图片地址 + /// + public string? ImageUrl { get; set; } + + /// + /// 条件类型 + /// + public string ConditionType { get; set; } = string.Empty; + + /// + /// 条件阈值 + /// + public int ConditionValue { get; set; } + + /// + /// 排序 + /// + public int SortOrder { get; set; } + + /// + /// 勋章的类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 书id + /// + public long JournalId { get; set; } + + /// + /// 创建人 + /// + public string? CreatedBy { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 更新人 + /// + public string? UpdatedBy { get; set; } + + /// + /// 更新时间 + /// + public DateTime? UpdatedAt { get; set; } +} + +/// +/// 勋章分页查询输入 +/// +public class MedalQueryInput : PageQueryModel +{ + /// + /// 勋章名称(模糊查询) + /// + public string? Name { get; set; } + + /// + /// 勋章的类型: Pet, Community + /// + public string? Type { get; set; } + + /// + /// 条件类型 + /// + public string? ConditionType { get; set; } +} + +/// +/// 微信端勋章列表输出(含拥有状态) +/// +public class WxMedalListOutput +{ + /// + /// 勋章ID + /// + public long Id { get; set; } + + /// + /// 勋章名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 获得条件描述 + /// + public string? Description { get; set; } + + /// + /// 图片地址 + /// + public string? ImageUrl { get; set; } + + /// + /// 条件类型 + /// + public string ConditionType { get; set; } = string.Empty; + + /// + /// 条件阈值 + /// + public int ConditionValue { get; set; } + + /// + /// 排序 + /// + public int SortOrder { get; set; } + + /// + /// 勋章的类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 是否已拥有 + /// + public bool IsOwned { get; set; } + + /// + /// 获得时间(未拥有则为null) + /// + public DateTime? AwardedAt { get; set; } +} + +/// +/// 微信端用户已拥有勋章输出 +/// +public class WxUserMedalOutput +{ + /// + /// 勋章ID + /// + public long MedalId { get; set; } + + /// + /// 勋章名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 获得条件描述 + /// + public string? Description { get; set; } + + /// + /// 图片地址 + /// + public string? ImageUrl { get; set; } + + /// + /// 勋章的类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 获得时间 + /// + public DateTime? AwardedAt { get; set; } + + /// + /// 状态: Awarded, Revoked + /// + public string Status { get; set; } = string.Empty; +} + +/// +/// 微信端激活勋章输入 +/// +public class WxMedalActivateInput +{ + /// + /// 勋章ID + /// + public long MedalId { get; set; } +} diff --git a/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs index 5e7286e..df72a07 100644 --- a/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs +++ b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs @@ -8,58 +8,90 @@ namespace QYZH.InteractiveMagazine.Models.Entity [SugarTable("CommunityMessage")] public partial class CommunityMessage : SqlSugarBaseEntity { - public CommunityMessage(){ + public CommunityMessage() + { - } + } /// /// Desc:期刊Id /// Default: /// Nullable:False /// - public long JournalId {get;set;} + public long JournalId { get; set; } + /// + /// Desc:用户Id + /// Default: + /// Nullable:False + /// + public long UserId { get; set; } - /// - /// Desc:消息内容 - /// Default: - /// Nullable:False - /// - public string Content {get;set;} - /// - /// Desc:配图 - /// Default: - /// Nullable:True - /// - public string ImageUrl {get;set;} + /// + /// Desc:用户实例期刊Id + /// Default: + /// Nullable:False + /// + public long UserJournalId { get; set; } + /// + /// Desc:期刊任务Id + /// Default: + /// Nullable:False + /// + public long JournalTaskId { get; set; } + /// + /// Desc:用户期刊任务回答Id + /// Default: + /// Nullable:False + /// + public long JournalTaskAnswerId { get; set; } + /// + /// Desc:消息内容 + /// Default: + /// Nullable:False + /// + public string Content { get; set; } - /// - /// Desc:排序 - /// Default:0 - /// Nullable:False - /// - public int SortOrder {get;set;} + /// + /// Desc:配图 + /// Default: + /// Nullable:True + /// + public string ImageUrl { get; set; } - /// - /// Desc:是否启用 - /// Default:b'1' - /// Nullable:False - /// - public bool IsActive {get;set;} + /// + /// Desc:排序 + /// Default:0 + /// Nullable:False + /// + public int SortOrder { get; set; } - /// - /// Desc:消息类型: Article, Quote, Announcement - /// Default:Article - /// Nullable:False - /// - public string Type {get;set;} + /// + /// Desc:是否启用 + /// Default:b'1' + /// Nullable:False + /// + public bool IsActive { get; set; } - /// - /// Desc:状态: Draft, Published - /// Default:Draft - /// Nullable:False - /// - public string Status {get;set;} + /// + /// Desc:消息类型: Article, Quote, Announcement + /// Default:Article + /// Nullable:False + /// + public string Type { get; set; } + + /// + /// Desc:点赞数 + /// Default:0 + /// Nullable:False + /// + public int LikeCount { get; set; } + /// + /// Desc:是否是精选消息 + /// Default:0 + /// Nullable:False + /// + public int IsFeatured { get; set; } } } diff --git a/QYZH.InteractiveMagazine.Models/Entity/MessageComment.cs b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessageComment.cs similarity index 100% rename from QYZH.InteractiveMagazine.Models/Entity/MessageComment.cs rename to QYZH.InteractiveMagazine.Models/Entity/CommunityMessageComment.cs diff --git a/QYZH.InteractiveMagazine.Models/Entity/CommunityMessageLike.cs b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessageLike.cs new file mode 100644 index 0000000..7f29b78 --- /dev/null +++ b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessageLike.cs @@ -0,0 +1,38 @@ +using SqlSugar; + +namespace QYZH.InteractiveMagazine.Models.Entity +{ + /// + ///点赞记录表 + /// + [SugarTable("MessageLike")] + public partial class CommunityMessageLike : SqlSugarBaseEntity + { + public CommunityMessageLike() + { + + + } + /// + /// Desc:用户Id + /// Default: + /// Nullable:False + /// + public long UserId { get; set; } + + /// + /// Desc:消息Id + /// Default: + /// Nullable:False + /// + public long MessageId { get; set; } + + /// + /// Desc:点赞类型 + /// Default:Like + /// Nullable:False + /// + public string Type { get; set; } + + } +} diff --git a/QYZH.InteractiveMagazine.Models/Entity/TemplateSentence.cs b/QYZH.InteractiveMagazine.Models/Entity/CommunityTemplateSentence.cs similarity index 100% rename from QYZH.InteractiveMagazine.Models/Entity/TemplateSentence.cs rename to QYZH.InteractiveMagazine.Models/Entity/CommunityTemplateSentence.cs diff --git a/QYZH.InteractiveMagazine.Models/Entity/DotFile.cs b/QYZH.InteractiveMagazine.Models/Entity/DotFile.cs index 3f94716..a92a498 100644 --- a/QYZH.InteractiveMagazine.Models/Entity/DotFile.cs +++ b/QYZH.InteractiveMagazine.Models/Entity/DotFile.cs @@ -9,7 +9,7 @@ namespace QYZH.InteractiveMagazine.Models.Entity /// /// [SugarTable("DotFile")] - public partial class DotFile + public partial class DotFile : SqlSugarBaseEntity { public DotFile(){ diff --git a/QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs b/QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs deleted file mode 100644 index aa65c12..0000000 --- a/QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs +++ /dev/null @@ -1,43 +0,0 @@ -using SqlSugar; - -namespace QYZH.InteractiveMagazine.Models.Entity -{ - /// - ///点赞记录表 - /// - [SugarTable("MessageLike")] - public partial class MessageLike : SqlSugarBaseEntity - { - public MessageLike(){ - - - } - /// - /// Desc:用户Id - /// Default: - /// Nullable:False - /// - public long UserId {get;set;} - - /// - /// Desc:消息Id - /// Default: - /// Nullable:False - /// - public long MessageId {get;set;} - - /// - /// Desc:点赞类型 - /// Default:Like - /// Nullable:False - /// - public string Type {get;set;} - - /// - /// Desc:状态: Liked, Cancelled - /// Default:Liked - /// Nullable:False - /// - public string Status {get;set;} - } -} diff --git a/QYZH.InteractiveMagazine.Models/Entity/UserJournal.cs b/QYZH.InteractiveMagazine.Models/Entity/UserJournal.cs index bea9529..138409a 100644 --- a/QYZH.InteractiveMagazine.Models/Entity/UserJournal.cs +++ b/QYZH.InteractiveMagazine.Models/Entity/UserJournal.cs @@ -29,14 +29,6 @@ namespace QYZH.InteractiveMagazine.Models.Entity [SugarColumn(ColumnName = "JournalId")] public long JournalId { get; set; } - /// - /// Desc:实例化期刊Id(扫码获取的具体期刊实例,可为空表示绑定到期刊模板本身) - /// Default: - /// Nullable:True - /// - [SugarColumn(ColumnName = "JournalInstanceId", IsNullable = true)] - public long? JournalInstanceId { get; set; } - /// /// Desc:关联类型: Read(已读), Favorite(收藏), Subscribe(订阅) /// Default:Read diff --git a/QYZH.InteractiveMagazine.Service/MedalService.cs b/QYZH.InteractiveMagazine.Service/MedalService.cs new file mode 100644 index 0000000..1c0957f --- /dev/null +++ b/QYZH.InteractiveMagazine.Service/MedalService.cs @@ -0,0 +1,410 @@ +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; + +/// +/// 勋章服务实现 +/// +public class MedalService(BaseRepository medalRepository, ILogger logger) : BaseRepository, IMedalService +{ + + /// + /// 创建勋章 + /// + public async Task CreateAsync(MedalInput input) + { + logger.LogInformation("正在创建勋章,勋章名称: {Name}", input.Name); + + if (string.IsNullOrWhiteSpace(input.Name)) + { + 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, + CreatedBy = "System", + UpdatedBy = "System", + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + IsDeleted = false + }; + + var result = await medalRepository.InsertAsync(medal); + if (!result) + { + logger.LogError("勋章创建失败,勋章名称: {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 + }; + } + + /// + /// 更新勋章 + /// + public async Task 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.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) + { + logger.LogError("勋章更新失败,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 + }; + } + + /// + /// 删除勋章(软删除) + /// + 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); + } + + var result = await medalRepository.DeleteByIdAsync(id); + if (!result) + { + logger.LogError("勋章删除失败,ID: {Id}", id); + throw new BusinessException("删除勋章失败", 500); + } + + logger.LogInformation("勋章删除成功,ID: {Id}", id); + } + + /// + /// 根据ID获取勋章 + /// + public async Task 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); + } + + 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 + }; + } + + /// + /// 分页查询勋章列表 + /// + public async Task> 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 totalNumber = 0; + var pageResult = 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) + .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(pageResult, input.PageIndex, input.PageSize, totalNumber); + } + + /// + /// 更新勋章启用/禁用状态 + /// + 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); + } + + /// + /// 获取所有启用勋章列表(含当前用户拥有状态) + /// + public async Task> GetAllMedalsAsync(long userId) + { + logger.LogInformation("正在获取所有勋章列表,用户ID: {UserId}", userId); + + var medals = await medalRepository.Queryable() + .Where(m => m.Status == 1) + .OrderBy(m => m.SortOrder, SqlSugar.OrderByType.Asc) + .OrderByDescending(m => m.CreatedAt) + .ToListAsync(); + + var userMedals = await Context.Queryable() + .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, + ConditionType = m.ConditionType, + ConditionValue = m.ConditionValue, + SortOrder = m.SortOrder, + Type = m.Type, + IsOwned = userMedalDict.ContainsKey((int)m.Id), + AwardedAt = userMedalDict.TryGetValue((int)m.Id, out var awardedAt) ? awardedAt : null + }).ToList(); + } + + /// + /// 获取用户已拥有的勋章列表 + /// + public async Task> GetUserMedalsAsync(long userId) + { + logger.LogInformation("正在获取用户勋章列表,用户ID: {UserId}", userId); + + var result = await Context.Queryable() + .InnerJoin((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; + } + + /// + /// 激活/获得勋章 + /// + 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() + .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); + } +} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/MedalController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/MedalController.cs new file mode 100644 index 0000000..7d9eaa1 --- /dev/null +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/MedalController.cs @@ -0,0 +1,177 @@ +using Microsoft.AspNetCore.Mvc; +using QYZH.InteractiveMagazine.IService; +using QYZH.InteractiveMagazine.Models.Common; +using QYZH.InteractiveMagazine.Models.Dto; +using QYZH.InteractiveMagazine.Models.Enum; + +namespace QYZH.InteractiveMagazine.WebApi.Controllers; + +/// +/// 勋章管理控制器 +/// +[Route("api/[controller]")] +[ApiController] +[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Platform))] +public class MedalController : BaseController +{ + private readonly IMedalService _medalService; + private readonly ILogger _logger; + + public MedalController(IMedalService medalService, ILogger logger) + { + _medalService = medalService; + _logger = logger; + } + + /// + /// 创建勋章 + /// + /// 勋章信息 + /// 创建的勋章信息 + [HttpPost] + public async Task> CreateAsync([FromBody] MedalInput input) + { + try + { + var result = await _medalService.CreateAsync(input); + return Success(result, "创建勋章成功"); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "创建勋章业务异常: {Message}", ex.Message); + return BaseResponse.Fail(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "创建勋章系统异常,参数:{Input}", input); + return BaseResponse.Fail("创建勋章失败,请稍后重试"); + } + } + + /// + /// 更新勋章 + /// + /// 勋章ID + /// 勋章信息 + /// 更新后的勋章信息 + [HttpPut("{id}")] + public async Task> UpdateAsync(long id, [FromBody] MedalInput input) + { + try + { + var result = await _medalService.UpdateAsync(id, input); + return Success(result, "更新勋章成功"); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "更新勋章业务异常: {Message}", ex.Message); + return BaseResponse.Fail(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "更新勋章系统异常,ID:{Id},参数:{Input}", id, input); + return BaseResponse.Fail("更新勋章失败,请稍后重试"); + } + } + + /// + /// 删除勋章 + /// + /// 勋章ID + /// 操作结果 + [HttpDelete("{id}")] + public async Task> DeleteAsync(long id) + { + try + { + await _medalService.DeleteAsync(id); + return Success(new object(), "删除勋章成功"); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "删除勋章业务异常: {Message}", ex.Message); + return BaseResponse.Fail(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "删除勋章系统异常,ID:{Id}", id); + return BaseResponse.Fail("删除勋章失败,请稍后重试"); + } + } + + /// + /// 根据ID获取勋章 + /// + /// 勋章ID + /// 勋章信息 + [HttpGet("{id}")] + public async Task> GetByIdAsync(long id) + { + try + { + var result = await _medalService.GetByIdAsync(id); + return Success(result); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "获取勋章业务异常: {Message}", ex.Message); + return BaseResponse.Fail(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取勋章系统异常,ID:{Id}", id); + return BaseResponse.Fail("获取勋章信息失败,请稍后重试"); + } + } + + /// + /// 分页查询勋章列表 + /// + /// 查询条件 + /// 分页结果 + [HttpPost("list")] + public async Task>> GetListAsync([FromBody] MedalQueryInput input) + { + try + { + var result = await _medalService.GetListAsync(input); + return Success(result); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "查询勋章列表业务异常: {Message}", ex.Message); + return BaseResponse>.Fail(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "查询勋章列表系统异常,参数:{Input}", input); + return BaseResponse>.Fail("查询勋章列表失败,请稍后重试"); + } + } + + /// + /// 更新勋章启用/禁用状态 + /// + /// 勋章ID + /// 状态: 0=禁用, 1=启用 + /// 操作结果 + [HttpPut("{id}/status")] + public async Task> UpdateStatusAsync(long id, [FromBody] int status) + { + try + { + await _medalService.UpdateStatusAsync(id, status); + return Success(new object(), status == 1 ? "启用勋章成功" : "禁用勋章成功"); + } + catch (BusinessException ex) + { + _logger.LogWarning(ex, "更新勋章状态业务异常: {Message}", ex.Message); + return BaseResponse.Fail(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "更新勋章状态系统异常,ID:{Id},Status:{Status}", id, status); + return BaseResponse.Fail("更新勋章状态失败,请稍后重试"); + } + } +} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatMedalController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatMedalController.cs new file mode 100644 index 0000000..a7bc536 --- /dev/null +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatMedalController.cs @@ -0,0 +1,99 @@ +using Microsoft.AspNetCore.Mvc; +using QYZH.InteractiveMagazine.IService; +using QYZH.InteractiveMagazine.Models.Common; +using QYZH.InteractiveMagazine.Models.Dto; + +namespace QYZH.InteractiveMagazine.WebApi.Controllers.WeChat; + +/// +/// 小程序勋章控制器 +/// +public class WeChatMedalController(IMedalService medalService, ILogger logger) : WeChatBaseController +{ + /// + /// 获取所有勋章列表(含当前用户拥有状态) + /// + [HttpGet("all")] + public async Task>> GetAllMedals() + { + try + { + var userId = GetCurrentUserId(); + if (userId == null) + { + return BaseResponse>.Fail(ResultCode.DENY, "未获取到用户信息"); + } + + var result = await medalService.GetAllMedalsAsync(userId.Value); + return Success(result); + } + catch (BusinessException ex) + { + logger.LogWarning(ex, "获取勋章列表业务异常: {Message}", ex.Message); + return BaseResponse>.Fail(ex.Message); + } + catch (Exception ex) + { + logger.LogError(ex, "获取勋章列表系统异常"); + return BaseResponse>.Fail("获取勋章列表失败,请稍后重试"); + } + } + + /// + /// 获取用户已拥有的勋章列表 + /// + [HttpGet("my")] + public async Task>> GetUserMedals() + { + try + { + var userId = GetCurrentUserId(); + if (userId == null) + { + return BaseResponse>.Fail(ResultCode.DENY, "未获取到用户信息"); + } + + var result = await medalService.GetUserMedalsAsync(userId.Value); + return Success(result); + } + catch (BusinessException ex) + { + logger.LogWarning(ex, "获取用户勋章列表业务异常: {Message}", ex.Message); + return BaseResponse>.Fail(ex.Message); + } + catch (Exception ex) + { + logger.LogError(ex, "获取用户勋章列表系统异常"); + return BaseResponse>.Fail("获取用户勋章列表失败,请稍后重试"); + } + } + + /// + /// 激活/获得勋章 + /// + [HttpPost("activate")] + public async Task> ActivateMedal([FromBody] WxMedalActivateInput input) + { + try + { + var userId = GetCurrentUserId(); + if (userId == null) + { + return BaseResponse.Fail(ResultCode.DENY, "未获取到用户信息"); + } + + await medalService.ActivateMedalAsync(userId.Value, input); + return Success(null!, "勋章激活成功"); + } + catch (BusinessException ex) + { + logger.LogWarning(ex, "激活勋章业务异常: {Message}", ex.Message); + return BaseResponse.Fail(ex.Message); + } + catch (Exception ex) + { + logger.LogError(ex, "激活勋章系统异常,参数:{Input}", input); + return BaseResponse.Fail("激活勋章失败,请稍后重试"); + } + } +}