From 32989e1fa0baabbed9be832d664bb2b118c108e7 Mon Sep 17 00:00:00 2001 From: glz <694770232@qq.com> Date: Wed, 3 Jun 2026 16:09:56 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=B8=8E=E5=95=86=E5=93=81=E6=A8=A1=E5=9D=97=EF=BC=8C?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=9A=E5=8A=A1=E6=A8=A1=E5=9E=8B=E4=B8=8E?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增用户状态枚举UserStatusEnum,统一用户状态定义 2. 重构用户体系:合并WxUser与User实体为Users实体,统一用户管理 3. 新增微信小程序认证相关服务与控制器,实现一键登录功能 4. 新增商品管理完整服务与控制器,修复商品表字段映射问题 5. 删除冗余的WxUser相关服务与控制器代码 6. 新增Newtonsoft.Json依赖用于微信接口响应解析 7. 清理无用的文件夹引用配置 --- .../IProductService.cs | 45 +++ .../IUsersService.cs | 23 ++ .../IWeChatAuthService.cs | 17 ++ .../IWxUserService.cs | 33 --- .../Dto/Mall/ProductDto.cs | 156 +++++++++++ .../Dto/UsersDto.cs | 71 +++++ .../Dto/WeChat/WeChatDto.cs | 48 ++++ .../Entity/Product.cs | 5 +- .../Entity/User .cs | 58 ---- .../Entity/{WxUser.cs => Users.cs} | 57 ++-- .../Enum/UserStatusEnum.cs | 17 ++ .../QYZH.InteractiveMagazine.Models.csproj | 1 + .../ProductService.cs | 256 ++++++++++++++++++ .../UsersService.cs | 59 ++++ .../WeChatAuthService.cs | 160 +++++++++++ .../WxUserService.cs | 221 --------------- .../Controllers/ProductController.cs | 151 +++++++++++ .../Controllers/UsersController.cs | 51 ++++ .../WeChat/WeChatAuthController.cs | 49 ++++ .../WeChat/WeChatBaseController.cs | 63 +++++ .../Controllers/WxUserController.cs | 147 ---------- .../QYZH.InteractiveMagazine.WebApi.csproj | 3 - 22 files changed, 1200 insertions(+), 491 deletions(-) create mode 100644 QYZH.InteractiveMagazine.IService/IProductService.cs create mode 100644 QYZH.InteractiveMagazine.IService/IUsersService.cs create mode 100644 QYZH.InteractiveMagazine.IService/IWeChatAuthService.cs delete mode 100644 QYZH.InteractiveMagazine.IService/IWxUserService.cs create mode 100644 QYZH.InteractiveMagazine.Models/Dto/Mall/ProductDto.cs create mode 100644 QYZH.InteractiveMagazine.Models/Dto/UsersDto.cs delete mode 100644 QYZH.InteractiveMagazine.Models/Entity/User .cs rename QYZH.InteractiveMagazine.Models/Entity/{WxUser.cs => Users.cs} (78%) create mode 100644 QYZH.InteractiveMagazine.Models/Enum/UserStatusEnum.cs create mode 100644 QYZH.InteractiveMagazine.Service/ProductService.cs create mode 100644 QYZH.InteractiveMagazine.Service/UsersService.cs create mode 100644 QYZH.InteractiveMagazine.Service/WeChatAuthService.cs delete mode 100644 QYZH.InteractiveMagazine.Service/WxUserService.cs create mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/ProductController.cs create mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/UsersController.cs create mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatAuthController.cs create mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatBaseController.cs delete mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/WxUserController.cs diff --git a/QYZH.InteractiveMagazine.IService/IProductService.cs b/QYZH.InteractiveMagazine.IService/IProductService.cs new file mode 100644 index 0000000..142059b --- /dev/null +++ b/QYZH.InteractiveMagazine.IService/IProductService.cs @@ -0,0 +1,45 @@ +using QYZH.InteractiveMagazine.Models.Dto; +using QYZH.InteractiveMagazine.Models.Entity; + +namespace QYZH.InteractiveMagazine.IService; + +/// +/// 商品服务接口 +/// +public interface IProductService : IBaseService +{ + /// + /// 创建商品 + /// + /// 商品输入 + /// 创建的商品信息 + Task CreateAsync(ProductInput input); + + /// + /// 更新商品 + /// + /// 商品ID + /// 商品输入 + /// 更新后的商品信息 + Task UpdateAsync(long id, ProductInput input); + + /// + /// 删除商品(软删除) + /// + /// 商品ID + Task DeleteAsync(long id); + + /// + /// 根据ID获取商品 + /// + /// 商品ID + /// 商品信息 + Task GetByIdAsync(long id); + + /// + /// 分页查询商品列表 + /// + /// 查询条件 + /// 分页结果 + Task> GetListAsync(ProductQueryInput input); +} diff --git a/QYZH.InteractiveMagazine.IService/IUsersService.cs b/QYZH.InteractiveMagazine.IService/IUsersService.cs new file mode 100644 index 0000000..70edd91 --- /dev/null +++ b/QYZH.InteractiveMagazine.IService/IUsersService.cs @@ -0,0 +1,23 @@ +using QYZH.InteractiveMagazine.IService.Dto; +using QYZH.InteractiveMagazine.Models.Dto; +using QYZH.InteractiveMagazine.Models.Entity; + +namespace QYZH.InteractiveMagazine.IService; + +public interface IUsersService : IBaseService +{ + /// + /// 分页查询用户列表 + /// + Task>> GetListAsync(UsersQueryInput input); + + /// + /// 获取用户详情 + /// + Task> GetDetailAsync(long id); + + /// + /// 更新用户状态 + /// + Task UpdateStatusAsync(long id, UpdateUserStatusInput input); +} diff --git a/QYZH.InteractiveMagazine.IService/IWeChatAuthService.cs b/QYZH.InteractiveMagazine.IService/IWeChatAuthService.cs new file mode 100644 index 0000000..b1a82b0 --- /dev/null +++ b/QYZH.InteractiveMagazine.IService/IWeChatAuthService.cs @@ -0,0 +1,17 @@ +using QYZH.InteractiveMagazine.IService.Dto; +using QYZH.InteractiveMagazine.Models.Entity; + +namespace QYZH.InteractiveMagazine.IService; + +/// +/// 微信小程序认证服务 +/// +public interface IWeChatAuthService : IBaseService +{ + /// + /// 微信小程序一键登录 + /// + /// 登录输入(含微信 code) + /// 登录结果(含 Token 和用户信息) + Task LoginAsync(WeChatLoginInput input); +} diff --git a/QYZH.InteractiveMagazine.IService/IWxUserService.cs b/QYZH.InteractiveMagazine.IService/IWxUserService.cs deleted file mode 100644 index 3ed85e7..0000000 --- a/QYZH.InteractiveMagazine.IService/IWxUserService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using QYZH.InteractiveMagazine.IService.Dto; -using QYZH.InteractiveMagazine.Models.Dto; -using QYZH.InteractiveMagazine.Models.Entity; - -namespace QYZH.InteractiveMagazine.IService; - -public interface IWxUserService : IBaseService -{ - /// - /// 创建微信用户 - /// - Task CreateAsync(WxUserInput input); - - /// - /// 更新微信用户 - /// - Task UpdateAsync(long id, WxUserInput input); - - /// - /// 删除微信用户(软删除) - /// - Task DeleteAsync(long id); - - /// - /// 根据ID获取微信用户 - /// - Task GetByIdAsync(long id); - - /// - /// 分页查询微信用户列表 - /// - Task> GetListAsync(WxUserQueryInput input); -} diff --git a/QYZH.InteractiveMagazine.Models/Dto/Mall/ProductDto.cs b/QYZH.InteractiveMagazine.Models/Dto/Mall/ProductDto.cs new file mode 100644 index 0000000..3c4059f --- /dev/null +++ b/QYZH.InteractiveMagazine.Models/Dto/Mall/ProductDto.cs @@ -0,0 +1,156 @@ +using QYZH.InteractiveMagazine.Models.Dto; + +namespace QYZH.InteractiveMagazine.Models.Dto; + +/// +/// 商品创建/更新输入 +/// +public class ProductInput +{ + /// + /// 商品名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 描述 + /// + public string? Description { get; set; } + + /// + /// 商品图片 + /// + public string? ImageUrl { get; set; } + + /// + /// 所需积分 + /// + public int Price { get; set; } + + /// + /// 商品类型: MakeUpCard, PetBg + /// + public string Type { get; set; } = string.Empty; + + /// + /// 售卖状态: OnSale, OffSale + /// + public string SaleStatus { get; set; } = "OnSale"; + + /// + /// 扩展数据 + /// + public string? MetaData { get; set; } + + /// + /// 是否上架 + /// + public bool IsActive { get; set; } = true; + + /// + /// 库存(-1无限) + /// + public int Stock { get; set; } = -1; +} + +/// +/// 商品输出 +/// +public class ProductOutput +{ + /// + /// 主键ID + /// + public long Id { get; set; } + + /// + /// 商品名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 描述 + /// + public string? Description { get; set; } + + /// + /// 商品图片 + /// + public string? ImageUrl { get; set; } + + /// + /// 所需积分 + /// + public int Price { get; set; } + + /// + /// 商品类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 售卖状态 + /// + public string SaleStatus { get; set; } = string.Empty; + + /// + /// 扩展数据 + /// + public string? MetaData { get; set; } + + /// + /// 是否上架 + /// + public bool IsActive { get; set; } + + /// + /// 库存(-1无限) + /// + public int Stock { get; set; } + + /// + /// 创建人 + /// + public string? CreatedBy { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 更新人 + /// + public string? UpdatedBy { get; set; } + + /// + /// 更新时间 + /// + public DateTime? UpdatedAt { get; set; } +} + +/// +/// 商品分页查询输入 +/// +public class ProductQueryInput : PageQueryModel +{ + /// + /// 商品名称(模糊查询) + /// + public string? Name { get; set; } + + /// + /// 商品类型 + /// + public string? Type { get; set; } + + /// + /// 售卖状态: OnSale, OffSale + /// + public string? SaleStatus { get; set; } + + /// + /// 是否上架 + /// + public bool? IsActive { get; set; } +} diff --git a/QYZH.InteractiveMagazine.Models/Dto/UsersDto.cs b/QYZH.InteractiveMagazine.Models/Dto/UsersDto.cs new file mode 100644 index 0000000..50557ca --- /dev/null +++ b/QYZH.InteractiveMagazine.Models/Dto/UsersDto.cs @@ -0,0 +1,71 @@ +using QYZH.InteractiveMagazine.Models.Dto; + +namespace QYZH.InteractiveMagazine.IService.Dto; + +/// +/// Users查询输入DTO +/// +public class UsersQueryInput : PageQueryModel +{ + /// + /// 微信用户ID + /// + public string? WxUserId { get; set; } +} + +/// +/// Users输出DTO (基本信息) +/// +public class UsersOutput +{ + /// + /// 主键ID + /// + public long Id { get; set; } + + /// + /// 微信用户ID + /// + public string WxUserId { get; set; } = string.Empty; + + /// + /// 昵称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 头像地址 + /// + public string? AvatarUrl { get; set; } + + /// + /// 积分余额 + /// + public int Points { get; set; } + + /// + /// 用户类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 状态 + /// + public string Status { get; set; } = string.Empty; + + /// + /// 当前成长值 + /// + public int GrowthPoints { get; set; } +} + +/// +/// 更新用户状态输入DTO +/// +public class UpdateUserStatusInput +{ + /// + /// 用户状态: 1-启用, 2-冻结 + /// + public int Status { get; set; } +} diff --git a/QYZH.InteractiveMagazine.Models/Dto/WeChat/WeChatDto.cs b/QYZH.InteractiveMagazine.Models/Dto/WeChat/WeChatDto.cs index 9318662..54e06bd 100644 --- a/QYZH.InteractiveMagazine.Models/Dto/WeChat/WeChatDto.cs +++ b/QYZH.InteractiveMagazine.Models/Dto/WeChat/WeChatDto.cs @@ -1,7 +1,19 @@ using QYZH.InteractiveMagazine.Models.Dto; +using Newtonsoft.Json; namespace QYZH.InteractiveMagazine.IService.Dto; +/// +/// 微信小程序登录输入 +/// +public class WeChatLoginInput +{ + /// + /// 微信登录凭证(wx.login 获取的 code) + /// + public string Code { get; set; } = string.Empty; +} + /// /// 微信登录输出 /// @@ -177,3 +189,39 @@ public class WxUserQueryInput : PageQueryModel } +/// +/// 微信 code2session 接口响应 +/// +public class WxCode2SessionResponse +{ + /// + /// 用户唯一标识 + /// + [JsonProperty("openid")] + public string? OpenId { get; set; } + + /// + /// 会话密钥 + /// + [JsonProperty("session_key")] + public string? SessionKey { get; set; } + + /// + /// 用户统一标识(在开放平台绑定了多个应用时使用) + /// + [JsonProperty("unionid")] + public string? UnionId { get; set; } + + /// + /// 错误码 + /// + [JsonProperty("errcode")] + public int ErrCode { get; set; } + + /// + /// 错误信息 + /// + [JsonProperty("errmsg")] + public string? ErrMsg { get; set; } +} + diff --git a/QYZH.InteractiveMagazine.Models/Entity/Product.cs b/QYZH.InteractiveMagazine.Models/Entity/Product.cs index 0508825..aa882fe 100644 --- a/QYZH.InteractiveMagazine.Models/Entity/Product.cs +++ b/QYZH.InteractiveMagazine.Models/Entity/Product.cs @@ -49,11 +49,12 @@ namespace QYZH.InteractiveMagazine.Models.Entity public string Type {get;set;} /// - /// Desc:状态: OnSale, OffSale + /// Desc:售卖状态: OnSale, OffSale /// Default:OnSale /// Nullable:False /// - public string Status {get;set;} + [SugarColumn(ColumnName = "SaleStatus")] + public string SaleStatus {get;set;} /// /// Desc:扩展数据 diff --git a/QYZH.InteractiveMagazine.Models/Entity/User .cs b/QYZH.InteractiveMagazine.Models/Entity/User .cs deleted file mode 100644 index ce975e9..0000000 --- a/QYZH.InteractiveMagazine.Models/Entity/User .cs +++ /dev/null @@ -1,58 +0,0 @@ -using SqlSugar; - -namespace QYZH.InteractiveMagazine.Models.Entity -{ - /// - ///用户表 - /// - [SugarTable("User")] - public partial class User : SqlSugarBaseEntity - { - /// - /// Desc:微信用户ID - /// Default: - /// Nullable:True - /// - public string WxUserId { get; set; } - /// - /// Desc:昵称 - /// Default: - /// Nullable:True - /// - public string Name { get; set; } - /// - /// Desc:当前成长值 - /// Default:0 - /// Nullable:False - /// - public int GrowthPoints { get; set; } - /// - /// Desc:头像地址 - /// Default: - /// Nullable:True - /// - public string AvatarUrl { get; set; } - /// - /// Desc:积分余额 - /// Default:0 - /// Nullable:False - /// - public int Points { get; set; } - - /// - /// Desc:用户类型: Normal, VIP - /// Default:Normal - /// Nullable:False - /// - public string Type { get; set; } - - /// - /// Desc:状态: Active, Disabled - /// Default:Active - /// Nullable:False - /// - public string Status { get; set; } - - - } -} diff --git a/QYZH.InteractiveMagazine.Models/Entity/WxUser.cs b/QYZH.InteractiveMagazine.Models/Entity/Users.cs similarity index 78% rename from QYZH.InteractiveMagazine.Models/Entity/WxUser.cs rename to QYZH.InteractiveMagazine.Models/Entity/Users.cs index 79b985a..03b1fbb 100644 --- a/QYZH.InteractiveMagazine.Models/Entity/WxUser.cs +++ b/QYZH.InteractiveMagazine.Models/Entity/Users.cs @@ -5,44 +5,34 @@ namespace QYZH.InteractiveMagazine.Models.Entity /// ///用户表 /// - [SugarTable("WxUser")] - public partial class WxUser : SqlSugarBaseEntity + [SugarTable("Users")] + public partial class Users : SqlSugarBaseEntity { - /// - /// Desc:微信OpenId - /// Default: - /// Nullable:False - /// - public string OpenId { get; set; } - - /// - /// Desc:微信UnionId - /// Default: - /// Nullable:True - /// - public string UnionId { get; set; } /// /// Desc:昵称 /// Default: /// Nullable:True /// - public string NickName { get; set; } - + public string Name { get; set; } + /// + /// Desc:当前成长值 + /// Default:0 + /// Nullable:False + /// + public int GrowthPoints { get; set; } /// /// Desc:头像地址 /// Default: /// Nullable:True /// public string AvatarUrl { get; set; } - /// - /// Desc:手机号 - /// Default: - /// Nullable:True + /// Desc:积分余额 + /// Default:0 + /// Nullable:False /// - public string Phone { get; set; } - + public int Points { get; set; } /// /// Desc:用户类型: Normal, VIP @@ -57,12 +47,25 @@ namespace QYZH.InteractiveMagazine.Models.Entity /// Nullable:False /// public string Status { get; set; } - /// - /// Desc:密码,默认手机后4位 - /// Default:Active + /// Desc:微信OpenId + /// Default: /// Nullable:False /// - public string Pwd { get; set; } + public string OpenId { get; set; } + + /// + /// Desc:微信UnionId + /// Default: + /// Nullable:True + /// + public string UnionId { get; set; } + /// + /// Desc:手机号 + /// Default: + /// Nullable:True + /// + public string Phone { get; set; } + } } diff --git a/QYZH.InteractiveMagazine.Models/Enum/UserStatusEnum.cs b/QYZH.InteractiveMagazine.Models/Enum/UserStatusEnum.cs new file mode 100644 index 0000000..f215f63 --- /dev/null +++ b/QYZH.InteractiveMagazine.Models/Enum/UserStatusEnum.cs @@ -0,0 +1,17 @@ +namespace QYZH.InteractiveMagazine.Models.Enum; + +/// +/// 用户状态枚举 +/// +public enum UserStatusEnum +{ + /// + /// 启用 + /// + Active = 1, + + /// + /// 冻结 + /// + Frozen = 2 +} diff --git a/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj b/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj index f145742..e339258 100644 --- a/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj +++ b/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj @@ -9,6 +9,7 @@ + diff --git a/QYZH.InteractiveMagazine.Service/ProductService.cs b/QYZH.InteractiveMagazine.Service/ProductService.cs new file mode 100644 index 0000000..75dec02 --- /dev/null +++ b/QYZH.InteractiveMagazine.Service/ProductService.cs @@ -0,0 +1,256 @@ +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 ProductService(BaseRepository productRepository, ILogger logger) : BaseRepository, IProductService +{ + + /// + /// 创建商品 + /// + public async Task CreateAsync(ProductInput input) + { + logger.LogInformation("正在创建商品,商品名称: {Name}", input.Name); + + if (string.IsNullOrWhiteSpace(input.Name)) + { + throw new BusinessException("商品名称不能为空", 400); + } + + if (string.IsNullOrWhiteSpace(input.Type)) + { + throw new BusinessException("商品类型不能为空", 400); + } + + if (input.Price < 0) + { + throw new BusinessException("商品价格不能为负数", 400); + } + + var product = new Product + { + Name = input.Name.Trim(), + Description = input.Description, + ImageUrl = input.ImageUrl, + Price = input.Price, + Type = input.Type, + SaleStatus = input.SaleStatus, + MetaData = input.MetaData, + IsActive = input.IsActive, + Stock = input.Stock, + CreatedBy = "System", + UpdatedBy = "System", + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + IsDeleted = false + }; + + var result = await productRepository.InsertAsync(product); + if (!result) + { + logger.LogError("商品创建失败,商品名称: {Name}", input.Name); + throw new BusinessException("创建商品失败", 500); + } + + logger.LogInformation("商品创建成功,商品名称: {Name}, ID: {Id}", input.Name, product.Id); + + return new ProductOutput + { + Id = product.Id, + Name = product.Name, + Description = product.Description, + ImageUrl = product.ImageUrl, + Price = product.Price, + Type = product.Type, + SaleStatus = product.SaleStatus, + MetaData = product.MetaData, + IsActive = product.IsActive, + Stock = product.Stock, + CreatedBy = product.CreatedBy, + CreatedAt = product.CreatedAt, + UpdatedBy = product.UpdatedBy, + UpdatedAt = product.UpdatedAt + }; + } + + /// + /// 更新商品 + /// + public async Task UpdateAsync(long id, ProductInput input) + { + logger.LogInformation("正在更新商品,ID: {Id}", id); + + var product = await productRepository.GetByIdAsync(id); + if (product == 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); + } + + if (input.Price < 0) + { + throw new BusinessException("商品价格不能为负数", 400); + } + + product.Name = input.Name.Trim(); + product.Description = input.Description; + product.ImageUrl = input.ImageUrl; + product.Price = input.Price; + product.Type = input.Type; + product.SaleStatus = input.SaleStatus; + product.MetaData = input.MetaData; + product.IsActive = input.IsActive; + product.Stock = input.Stock; + product.UpdatedBy = "System"; + product.UpdatedAt = DateTime.Now; + + var result = await productRepository.UpdateAsync(product); + if (!result) + { + logger.LogError("商品更新失败,ID: {Id}", id); + throw new BusinessException("更新商品失败", 500); + } + + logger.LogInformation("商品更新成功,ID: {Id}", id); + + return new ProductOutput + { + Id = product.Id, + Name = product.Name, + Description = product.Description, + ImageUrl = product.ImageUrl, + Price = product.Price, + Type = product.Type, + SaleStatus = product.SaleStatus, + MetaData = product.MetaData, + IsActive = product.IsActive, + Stock = product.Stock, + CreatedBy = product.CreatedBy, + CreatedAt = product.CreatedAt, + UpdatedBy = product.UpdatedBy, + UpdatedAt = product.UpdatedAt + }; + } + + /// + /// 删除商品(软删除) + /// + public async Task DeleteAsync(long id) + { + logger.LogInformation("正在删除商品,ID: {Id}", id); + + var product = await productRepository.GetByIdAsync(id); + if (product == null) + { + logger.LogWarning("未找到要删除的商品,ID: {Id}", id); + throw new BusinessException("商品不存在", 404); + } + + var result = await productRepository.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 product = await productRepository.GetByIdAsync(id); + if (product == null) + { + logger.LogWarning("未找到商品,ID: {Id}", id); + throw new BusinessException("商品不存在", 404); + } + + return new ProductOutput + { + Id = product.Id, + Name = product.Name, + Description = product.Description, + ImageUrl = product.ImageUrl, + Price = product.Price, + Type = product.Type, + SaleStatus = product.SaleStatus, + MetaData = product.MetaData, + IsActive = product.IsActive, + Stock = product.Stock, + CreatedBy = product.CreatedBy, + CreatedAt = product.CreatedAt, + UpdatedBy = product.UpdatedBy, + UpdatedAt = product.UpdatedAt + }; + } + + /// + /// 分页查询商品列表 + /// + public async Task> GetListAsync(ProductQueryInput 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 productRepository.Queryable() + .WhereIF(!string.IsNullOrWhiteSpace(input.Name), p => p.Name.Contains(input.Name)) + .WhereIF(!string.IsNullOrWhiteSpace(input.Type), p => p.Type == input.Type) + .WhereIF(!string.IsNullOrWhiteSpace(input.SaleStatus), p => p.SaleStatus == input.SaleStatus) + .WhereIF(input.IsActive.HasValue, p => p.IsActive == input.IsActive.Value) + .OrderByDescending(p => p.CreatedAt) + .Select(p => new ProductOutput + { + Id = p.Id, + Name = p.Name, + Description = p.Description, + ImageUrl = p.ImageUrl, + Price = p.Price, + Type = p.Type, + SaleStatus = p.SaleStatus, + MetaData = p.MetaData, + IsActive = p.IsActive, + Stock = p.Stock, + CreatedBy = p.CreatedBy, + CreatedAt = p.CreatedAt, + UpdatedBy = p.UpdatedBy, + UpdatedAt = p.UpdatedAt + }, true) + .ToPageListAsync(input.PageIndex, input.PageSize, totalNumber); + + return new PageListModel(pageResult, input.PageIndex, input.PageSize, totalNumber); + } +} diff --git a/QYZH.InteractiveMagazine.Service/UsersService.cs b/QYZH.InteractiveMagazine.Service/UsersService.cs new file mode 100644 index 0000000..8d785d4 --- /dev/null +++ b/QYZH.InteractiveMagazine.Service/UsersService.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Logging; +using QYZH.InteractiveMagazine.IService; +using QYZH.InteractiveMagazine.IService.Dto; +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 UsersService(BaseRepository usersRepository, ILogger _logger) : BaseRepository, IUsersService +{ + /// + /// 分页查询用户列表 + /// + public async Task>> GetListAsync(UsersQueryInput input) + { + var page = Queryable() + .WhereIF(!string.IsNullOrEmpty(input.WxUserId), u => u.OpenId == input.WxUserId) + .OrderBy(u => u.Id, OrderByType.Desc) + .ToPage(input); + + return BaseResponse>.Success(page); + } + + /// + /// 获取用户详情 + /// + public async Task> GetDetailAsync(long id) + { + var user = await GetByIdAsync(u => u.Id == id); + if (user == null) + { + return BaseResponse.Fail("用户不存在"); + } + return BaseResponse.Success(user); + } + + /// + /// 更新用户状态 + /// + public async Task UpdateStatusAsync(long id, UpdateUserStatusInput input) + { + var exists = await Queryable().AnyAsync(u => u.Id == id); + if (!exists) + { + return BaseResponse.Fail("用户不存在"); + } + + var statusValue = input.Status == 1 ? "Active" : "Disabled"; + var result = await UpdateAsync( + u => new Users { Status = statusValue }, + u => u.Id == id + ); + + return result ? BaseResponse.Success() : BaseResponse.Fail("更新失败"); + } +} diff --git a/QYZH.InteractiveMagazine.Service/WeChatAuthService.cs b/QYZH.InteractiveMagazine.Service/WeChatAuthService.cs new file mode 100644 index 0000000..93debec --- /dev/null +++ b/QYZH.InteractiveMagazine.Service/WeChatAuthService.cs @@ -0,0 +1,160 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using QYZH.InteractiveMagazine.Common.Helpers; +using QYZH.InteractiveMagazine.Infrastructure.Auth; +using QYZH.InteractiveMagazine.Infrastructure.Cache; +using QYZH.InteractiveMagazine.IService; +using QYZH.InteractiveMagazine.IService.Dto; +using QYZH.InteractiveMagazine.Models.Common; +using QYZH.InteractiveMagazine.Models.Entity; +using QYZH.InteractiveMagazine.Models.Settings; +using QYZH.InteractiveMagazine.Repository; + +namespace QYZH.InteractiveMagazine.Service; + +/// +/// 微信小程序认证服务实现 +/// +public class WeChatAuthService(BaseRepository usersRepository, IConfiguration configuration, ILogger logger) : BaseRepository, IWeChatAuthService +{ + private const string TokenKeyPrefix = "InteractiveMagazine:WeChatAuth:Token"; + private const string Code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code"; + + /// + /// 微信小程序一键登录 + /// + /// 登录输入(含微信 code) + /// 登录结果(含 Token 和用户信息) + public async Task LoginAsync(WeChatLoginInput input) + { + logger.LogInformation("微信小程序登录尝试"); + + // 参数校验 + if (string.IsNullOrWhiteSpace(input.Code)) + { + throw new BusinessException("微信登录凭证 code 不能为空", 400); + } + + // 获取微信配置 + var weChatSettings = GetWeChatSettings(); + + // 调用微信 code2session 接口 + var wxResponse = await CallCode2SessionAsync(weChatSettings, input.Code); + if (wxResponse == null || wxResponse.ErrCode != 0 || string.IsNullOrWhiteSpace(wxResponse.OpenId)) + { + var errMsg = wxResponse?.ErrMsg ?? "未知错误"; + logger.LogWarning("微信 code2session 接口调用失败,errcode: {ErrCode}, errmsg: {ErrMsg}", wxResponse?.ErrCode, errMsg); + throw new BusinessException($"微信登录失败:{errMsg}", 400); + } + + logger.LogInformation("微信 code2session 成功,OpenId: {OpenId}", wxResponse.OpenId); + + // 查询用户是否已存在 + var user = await usersRepository.GetFirstAsync(u => u.OpenId == wxResponse.OpenId); + var isNewUser = user == null; + + if (isNewUser) + { + // 首次登录,创建新用户 + user = new Users + { + Name = $"wx_{wxResponse.OpenId[^8..]}", + OpenId = wxResponse.OpenId, + UnionId = wxResponse.UnionId, + Type = "Normal", + Status = "Active", + GrowthPoints = 0, + Points = 0 + }; + + var insertResult = await usersRepository.Insertable(user).ExecuteReturnIdentityAsync(); + if (insertResult <= 0) + { + logger.LogError("创建微信用户失败,OpenId: {OpenId}", wxResponse.OpenId); + throw new BusinessException("创建用户失败,请稍后重试", 500); + } + + user.Id = insertResult; + logger.LogInformation("微信新用户创建成功,UserId: {UserId}, OpenId: {OpenId}", user.Id, wxResponse.OpenId); + } + else + { + // 已有用户,校验状态 + if (user.Status == "Disabled") + { + logger.LogWarning("微信登录失败,用户已被禁用,UserId: {UserId}, OpenId: {OpenId}", user.Id, wxResponse.OpenId); + throw new BusinessException("账号已被禁用,请联系客服", 403); + } + + logger.LogInformation("微信老用户登录,UserId: {UserId}, OpenId: {OpenId}", user.Id, wxResponse.OpenId); + } + + // 生成 JWT Token + var jwtSettings = GetJwtSettings(); + var token = JwtHelper.GenerateToken((long)user.Id, user.Name, jwtSettings); + + // 缓存 Token 到 Redis + await RedisHelper.StringSetAsync($"{TokenKeyPrefix}:{user.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes)); + + return new WeChatLoginOutput + { + Token = token, + UserId = (long)user.Id, + UserName = user.Name, + OpenId = wxResponse.OpenId + }; + } + + /// + /// 调用微信 code2session 接口 + /// + private async Task CallCode2SessionAsync(WeChatSettings settings, string code) + { + var url = string.Format(Code2SessionUrl, settings.AppId, settings.AppSecret, code); + try + { + return await HttpHelper.GetAsync(url); + } + catch (Exception ex) + { + logger.LogError(ex, "调用微信 code2session 接口异常,URL: {Url}", url); + throw new BusinessException("微信服务请求失败,请稍后重试", 500); + } + } + + /// + /// 获取微信配置 + /// + private WeChatSettings GetWeChatSettings() + { + var settings = configuration.GetSection("WeChatSettings").Get(); + if (settings == null || string.IsNullOrWhiteSpace(settings.AppId) || string.IsNullOrWhiteSpace(settings.AppSecret)) + { + logger.LogError("微信配置不完整,请检查 appsettings.json 中的 WeChatSettings 节点"); + throw new BusinessException("微信配置不完整,请联系系统管理员", 500); + } + return settings; + } + + /// + /// 获取 JWT 配置 + /// + private JwtSettings GetJwtSettings() + { + var jwtSettings = configuration.GetSection("JwtSettings").Get() + ?? new JwtSettings + { + Issuer = "QYZH.InteractiveMagazine", + Audience = "QYZH.InteractiveMagazine", + SecretKey = "your-256-bit-secret-key-here-change-in-production", + ExpiryMinutes = 120 + }; + + if (string.IsNullOrWhiteSpace(jwtSettings.SecretKey)) + { + throw new BusinessException("JWT 配置不完整", 500); + } + + return jwtSettings; + } +} diff --git a/QYZH.InteractiveMagazine.Service/WxUserService.cs b/QYZH.InteractiveMagazine.Service/WxUserService.cs deleted file mode 100644 index ab42cdb..0000000 --- a/QYZH.InteractiveMagazine.Service/WxUserService.cs +++ /dev/null @@ -1,221 +0,0 @@ -using Microsoft.Extensions.Logging; -using QYZH.InteractiveMagazine.IService; -using QYZH.InteractiveMagazine.IService.Dto; -using QYZH.InteractiveMagazine.Models.Common; -using QYZH.InteractiveMagazine.Models.Dto; -using QYZH.InteractiveMagazine.Models.Entity; -using QYZH.InteractiveMagazine.Repository; -using SqlSugar; -using System.Linq.Expressions; - -namespace QYZH.InteractiveMagazine.Service; - -public class WxUserService(BaseRepository wxUserRepository, ILogger _logger) : BaseRepository, IWxUserService -{ - - public async Task CreateAsync(WxUserInput input) - { - _logger.LogInformation("正在创建微信用户,OpenId: {OpenId}", input.OpenId); - - if (string.IsNullOrWhiteSpace(input.OpenId)) - { - throw new BusinessException("OpenId不能为空", 400); - } - - var existingUser = await wxUserRepository.GetFirstAsync(a => a.OpenId == input.OpenId); - if (existingUser != null) - { - _logger.LogWarning("创建微信用户失败,OpenId已存在: {OpenId}", input.OpenId); - throw new BusinessException("OpenId已存在", 400); - } - - var wxUser = new WxUser - { - - OpenId = input.OpenId.Trim(), - UnionId = input.UnionId, - NickName = input.NickName, - AvatarUrl = input.AvatarUrl, - Phone = input.Phone, - Type = input.Type, - Status = input.Status, - Pwd = input.Pwd ?? string.Empty, - CreatedBy = "System", - UpdatedBy = "System", - CreatedAt = DateTime.Now, - UpdatedAt = DateTime.Now, - IsDeleted = false - }; - - var result = await wxUserRepository.InsertAsync(wxUser); - if (!result) - { - _logger.LogError("微信用户创建失败,OpenId: {OpenId}", input.OpenId); - throw new BusinessException("创建微信用户失败", 500); - } - - _logger.LogInformation("微信用户创建成功,OpenId: {OpenId}, ID: {Id}", input.OpenId, wxUser.Id); - - return new WxUserOutput - { - Id = wxUser.Id, - OpenId = wxUser.OpenId, - UnionId = wxUser.UnionId, - NickName = wxUser.NickName, - AvatarUrl = wxUser.AvatarUrl, - Phone = wxUser.Phone, - Type = wxUser.Type, - Status = wxUser.Status, - CreatedBy = wxUser.CreatedBy, - CreatedAt = wxUser.CreatedAt, - UpdatedBy = wxUser.UpdatedBy, - UpdatedAt = wxUser.UpdatedAt - }; - } - - public async Task UpdateAsync(long id, WxUserInput input) - { - _logger.LogInformation("正在更新微信用户,ID: {Id}", id); - - var wxUser = await wxUserRepository.GetByIdAsync(id); - if (wxUser == null) - { - _logger.LogWarning("未找到要更新的微信用户,ID: {Id}", id); - throw new BusinessException("微信用户不存在", 404); - } - - if (!string.IsNullOrWhiteSpace(input.OpenId) && input.OpenId != wxUser.OpenId) - { - var existingUser = await wxUserRepository.GetFirstAsync(a => a.OpenId == input.OpenId.Trim()); - if (existingUser != null && existingUser.Id != id) - { - _logger.LogWarning("更新微信用户失败,OpenId已存在: {OpenId}", input.OpenId); - throw new BusinessException("OpenId已存在", 400); - } - - wxUser.OpenId = input.OpenId.Trim(); - } - - wxUser.UnionId = input.UnionId ?? wxUser.UnionId; - wxUser.NickName = input.NickName ?? wxUser.NickName; - wxUser.AvatarUrl = input.AvatarUrl ?? wxUser.AvatarUrl; - wxUser.Phone = input.Phone ?? wxUser.Phone; - wxUser.Type = input.Type ?? wxUser.Type; - wxUser.Status = input.Status ?? wxUser.Status; - wxUser.Pwd = input.Pwd ?? wxUser.Pwd; - - wxUser.UpdatedBy = "System"; - wxUser.UpdatedAt = DateTime.Now; - - var result = await wxUserRepository.UpdateAsync(wxUser); - if (!result) - { - _logger.LogError("微信用户更新失败,ID: {Id}", id); - throw new BusinessException("更新微信用户失败", 500); - } - - _logger.LogInformation("微信用户更新成功,ID: {Id}", id); - - return new WxUserOutput - { - Id = wxUser.Id, - OpenId = wxUser.OpenId, - UnionId = wxUser.UnionId, - NickName = wxUser.NickName, - AvatarUrl = wxUser.AvatarUrl, - Phone = wxUser.Phone, - Type = wxUser.Type, - Status = wxUser.Status, - CreatedBy = wxUser.CreatedBy, - CreatedAt = wxUser.CreatedAt, - UpdatedBy = wxUser.UpdatedBy, - UpdatedAt = wxUser.UpdatedAt - }; - } - - public async Task DeleteAsync(long id) - { - _logger.LogInformation("正在删除微信用户,ID: {Id}", id); - - var wxUser = await wxUserRepository.GetByIdAsync(id); - if (wxUser == null) - { - _logger.LogWarning("未找到要删除的微信用户,ID: {Id}", id); - throw new BusinessException("微信用户不存在", 404); - } - - var result = await wxUserRepository.DeleteByIdAsync(id); - if (!result) - { - _logger.LogError("微信用户删除失败,ID: {Id}", id); - throw new BusinessException("删除微信用户失败", 500); - } - - _logger.LogInformation("微信用户删除成功,ID: {Id}", id); - } - - public async Task GetByIdAsync(long id) - { - var wxUser = await wxUserRepository.GetByIdAsync(id); - if (wxUser == null) - { - - throw new BusinessException("微信用户不存在", 404); - } - - return new WxUserOutput - { - Id = wxUser.Id, - OpenId = wxUser.OpenId, - UnionId = wxUser.UnionId, - NickName = wxUser.NickName, - AvatarUrl = wxUser.AvatarUrl, - Phone = wxUser.Phone, - Type = wxUser.Type, - Status = wxUser.Status, - CreatedBy = wxUser.CreatedBy, - CreatedAt = wxUser.CreatedAt, - UpdatedBy = wxUser.UpdatedBy, - UpdatedAt = wxUser.UpdatedAt - }; - } - - public async Task> GetListAsync(WxUserQueryInput input) - { - - 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 wxUserRepository.Queryable() - .WhereIF(!string.IsNullOrWhiteSpace(input.NickName), a => a.NickName == input.NickName) - .OrderByDescending(a => a.CreatedAt) - .Select(wxUser => new WxUserOutput - { - Id = wxUser.Id, - OpenId = wxUser.OpenId, - UnionId = wxUser.UnionId, - NickName = wxUser.NickName, - AvatarUrl = wxUser.AvatarUrl, - Phone = wxUser.Phone, - Type = wxUser.Type, - Status = wxUser.Status, - CreatedBy = wxUser.CreatedBy, - CreatedAt = wxUser.CreatedAt, - UpdatedBy = wxUser.UpdatedBy, - UpdatedAt = wxUser.UpdatedAt - }, true) - .ToPageListAsync(input.PageIndex, input.PageSize, totalNumber); - - return new PageListModel(pageResult, input.PageIndex, input.PageSize, totalNumber); - } - - -} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/ProductController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/ProductController.cs new file mode 100644 index 0000000..a3d5beb --- /dev/null +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/ProductController.cs @@ -0,0 +1,151 @@ +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 ProductController : BaseController +{ + private readonly IProductService _productService; + private readonly ILogger _logger; + + public ProductController(IProductService productService, ILogger logger) + { + _productService = productService; + _logger = logger; + } + + /// + /// 创建商品 + /// + /// 商品信息 + /// 创建的商品信息 + [HttpPost] + public async Task> CreateAsync([FromBody] ProductInput input) + { + try + { + var result = await _productService.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] ProductInput input) + { + try + { + var result = await _productService.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 _productService.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 _productService.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] ProductQueryInput input) + { + try + { + var result = await _productService.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("查询商品列表失败,请稍后重试"); + } + } +} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/UsersController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/UsersController.cs new file mode 100644 index 0000000..c3bb78b --- /dev/null +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/UsersController.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Mvc; +using QYZH.InteractiveMagazine.IService; +using QYZH.InteractiveMagazine.IService.Dto; +using QYZH.InteractiveMagazine.Models.Common; +using QYZH.InteractiveMagazine.Models.Dto; + +namespace QYZH.InteractiveMagazine.WebApi.Controllers; + +/// +/// 用户控制器 +/// +[Route("api/[controller]")] +[ApiController] +public class UsersController : BaseController +{ + private readonly IUsersService _usersService; + private readonly ILogger _logger; + + public UsersController(IUsersService usersService, ILogger logger) + { + _usersService = usersService; + _logger = logger; + } + + /// + /// 获取用户列表(分页) + /// + [HttpGet] + public async Task>> GetList([FromQuery] UsersQueryInput input) + { + return await _usersService.GetListAsync(input); + } + + /// + /// 获取用户详情 + /// + [HttpGet("{id}")] + public async Task> GetDetail(long id) + { + return await _usersService.GetDetailAsync(id); + } + + /// + /// 更新用户状态 + /// + [HttpPut("{id}/status")] + public async Task UpdateStatus(long id, [FromBody] UpdateUserStatusInput input) + { + return await _usersService.UpdateStatusAsync(id, input); + } +} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatAuthController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatAuthController.cs new file mode 100644 index 0000000..812bfc5 --- /dev/null +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatAuthController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using QYZH.InteractiveMagazine.IService; +using QYZH.InteractiveMagazine.IService.Dto; +using QYZH.InteractiveMagazine.Models.Common; +using QYZH.InteractiveMagazine.Models.Dto; + +namespace QYZH.InteractiveMagazine.WebApi.Controllers.WeChat; + +/// +/// 微信小程序认证控制器 +/// +public class WeChatAuthController : WeChatBaseController +{ + private readonly IWeChatAuthService _weChatAuthService; + private readonly ILogger _logger; + + public WeChatAuthController(IWeChatAuthService weChatAuthService, ILogger logger) + { + _weChatAuthService = weChatAuthService; + _logger = logger; + } + + /// + /// 微信小程序一键登录 + /// + /// 登录输入(含微信 code) + /// 登录结果(含 Token 和用户信息) + [AllowAnonymous] + [HttpPost("login")] + public async Task> LoginAsync([FromBody] WeChatLoginInput input) + { + try + { + var result = await _weChatAuthService.LoginAsync(input); + 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("微信登录失败,请稍后重试"); + } + } +} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatBaseController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatBaseController.cs new file mode 100644 index 0000000..0b71ac4 --- /dev/null +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/WeChatBaseController.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using QYZH.InteractiveMagazine.Models.Dto; +using QYZH.InteractiveMagazine.Models.Enum; +using System.Security.Claims; + +namespace QYZH.InteractiveMagazine.WebApi.Controllers.WeChat; + +/// +/// 小程序基础控制器 +/// +[Authorize] +[ApiController] +[Route("api/[controller]")] +[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Wechat))] +public abstract class WeChatBaseController : ControllerBase +{ + /// + /// 获取当前用户ID + /// + /// 用户ID + protected long? GetCurrentUserId() + { + var userIdClaim = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier); + if (userIdClaim != null && long.TryParse(userIdClaim.Value, out var userId)) + { + return userId; + } + return null; + } + + /// + /// 获取当前用户名 + /// + /// 用户名 + protected string? GetCurrentUserName() + { + return User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value; + } + + /// + /// 成功响应 + /// + /// 数据类型 + /// 数据 + /// 提示信息 + /// 统一响应对象 + protected BaseResponse Success(T data, string message = "操作成功") + { + return BaseResponse.Success(data, message); + } + + /// + /// 失败响应 + /// + /// 提示信息 + /// 状态码 + /// 统一响应对象 + protected BaseResponse Fail(string message, int code = 500) + { + return BaseResponse.Fail(ResultCode.GLOBAL_ERROR, message); + } +} diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/WxUserController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/WxUserController.cs deleted file mode 100644 index c07bf9a..0000000 --- a/QYZH.InteractiveMagazine.WebApi/Controllers/WxUserController.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using QYZH.InteractiveMagazine.IService; -using QYZH.InteractiveMagazine.IService.Dto; -using QYZH.InteractiveMagazine.Models.Common; -using QYZH.InteractiveMagazine.Models.Dto; - -namespace QYZH.InteractiveMagazine.WebApi.Controllers; - -/// -/// 微信用户控制器 -/// -[Route("api/[controller]")] -[ApiController] -public class WxUserController : BaseController -{ - private readonly IWxUserService _wxUserService; - private readonly ILogger _logger; - - public WxUserController(IWxUserService wxUserService, ILogger logger) - { - _wxUserService = wxUserService; - _logger = logger; - } - - /// - /// 创建微信用户 - /// - /// 创建微信用户输入参数 - /// 创建的微信用户输出参数 - [HttpPost("users")] - public async Task> CreateUserAsync([FromBody] WxUserInput input) - { - try - { - var result = await _wxUserService.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("users/{id}")] - public async Task> UpdateUserAsync(long id, [FromBody] WxUserInput input) - { - try - { - var result = await _wxUserService.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("users/{id}")] - public async Task> DeleteUserAsync(long id) - { - try - { - await _wxUserService.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 - /// 微信用户信息输出参数 - [HttpGet("users/{id}")] - public async Task> GetUserByIdAsync(long id) - { - try - { - var result = await _wxUserService.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("users/list")] - public async Task>> GetUsersListAsync([FromBody] WxUserQueryInput input) - { - try - { - var result = await _wxUserService.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("查询微信用户列表失败,请稍后重试"); - } - } -} diff --git a/QYZH.InteractiveMagazine.WebApi/QYZH.InteractiveMagazine.WebApi.csproj b/QYZH.InteractiveMagazine.WebApi/QYZH.InteractiveMagazine.WebApi.csproj index 387988d..473bacd 100644 --- a/QYZH.InteractiveMagazine.WebApi/QYZH.InteractiveMagazine.WebApi.csproj +++ b/QYZH.InteractiveMagazine.WebApi/QYZH.InteractiveMagazine.WebApi.csproj @@ -22,8 +22,5 @@ - - -