From 831f8ba7f56bc3c13f6d4ddb7a8bbdcdecbb373e Mon Sep 17 00:00:00 2001
From: glz <694770232@qq.com>
Date: Mon, 1 Jun 2026 17:59:23 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96=E5=90=8E?=
=?UTF-8?q?=E5=8F=B0=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E4=B8=8E=E5=9F=BA?=
=?UTF-8?q?=E7=A1=80=E4=B8=9A=E5=8A=A1=E6=A1=86=E6=9E=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1. 新增依赖注入生命周期标记接口、基础仓储与管理员仓储实现
2. 新增管理员认证与用户服务接口,补充认证相关DTO
3. 重构实体审计字段命名,统一Created/UpdatedAt规范
4. 新增大量业务实体类与API版本枚举配置
5. 集成Autofac依赖注入、JWT自动刷新与跨域配置
6. 替换原有微信小程序与旧认证服务为后台管理系统架构
7. 完善Swagger文档配置与项目基础部署配置
---
.../Dto/AdminUserDto.cs | 96 +++++++
.../Dto/AuthDto.cs | 29 ++
.../IAdminAuthService.cs | 14 +
.../IAdminUserService.cs | 45 +++
.../IAuthService.cs | 31 --
.../IDependency.cs | 15 +
.../IWeChatMiniProgramService.cs | 23 --
.../Auth/JwtHelper.cs | 57 ++--
.../Autofac/AutofacExtension.cs | 67 +++++
.../Autofac/AutofacModuleRegister.cs | 42 +++
.../DependencyInjectionExtensions.cs | 55 +---
.../Middleware/CorsExtension.cs | 42 +++
.../Middleware/JwtAutoRefreshMiddleware.cs | 79 ++++++
....InteractiveMagazine.Infrastructure.csproj | 5 +-
.../Entity/AdminUser.cs | 51 ++++
.../Entity/BaseEntity.cs | 8 +-
.../Entity/CheckInConfig.cs | 58 ++++
.../Entity/CheckInRecord.cs | 57 ++++
.../Entity/CommunityMessage.cs | 64 +++++
.../Entity/Journal.cs | 92 ++++++
.../Entity/Medal.cs | 79 ++++++
.../Entity/MessageComment.cs | 50 ++++
.../Entity/MessageLike.cs | 43 +++
QYZH.InteractiveMagazine.Models/Entity/Pet.cs | 64 +++++
.../Entity/PetEvolution.cs | 100 +++++++
.../Entity/PetFeedingRecord.cs | 71 +++++
.../Entity/PointsRecord.cs | 71 +++++
.../Entity/Product.cs | 86 ++++++
.../Entity/TemplateSentence.cs | 51 ++++
.../Entity/User.cs | 71 +++++
.../Entity/UserBag.cs | 64 +++++
.../Entity/UserMedal.cs | 50 ++++
.../Enum/ApiVersionEnum.cs | 26 ++
.../QYZH.InteractiveMagazine.Models.csproj | 3 +-
.../AdminUserRepository.cs | 13 +
.../BaseRepository.cs | 5 +
.../IAdminUserRepository.cs | 8 +
.../AdminAuthService.cs | 168 +++++++++++
.../AdminUserService.cs | 267 ++++++++++++++++++
.../AuthService.cs | 173 ------------
.../BaseService.cs | 10 +-
.../QYZH.InteractiveMagazine.Service.csproj | 1 +
.../WeChatMiniProgramService.cs | 167 -----------
.../Controllers/AdminController.cs | 205 ++++++++++++++
.../Controllers/AuthController.cs | 61 ----
.../Controllers/BaseController.cs | 2 +
.../Controllers/WeChatController.cs | 35 ---
QYZH.InteractiveMagazine.WebApi/Dockerfile | 19 ++
QYZH.InteractiveMagazine.WebApi/Program.cs | 96 ++++++-
.../QYZH.InteractiveMagazine.WebApi.csproj | 8 +
.../appsettings.json | 16 +-
51 files changed, 2417 insertions(+), 596 deletions(-)
create mode 100644 QYZH.InteractiveMagazine.IService/Dto/AdminUserDto.cs
create mode 100644 QYZH.InteractiveMagazine.IService/IAdminAuthService.cs
create mode 100644 QYZH.InteractiveMagazine.IService/IAdminUserService.cs
delete mode 100644 QYZH.InteractiveMagazine.IService/IAuthService.cs
create mode 100644 QYZH.InteractiveMagazine.IService/IDependency.cs
delete mode 100644 QYZH.InteractiveMagazine.IService/IWeChatMiniProgramService.cs
create mode 100644 QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacExtension.cs
create mode 100644 QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacModuleRegister.cs
create mode 100644 QYZH.InteractiveMagazine.Infrastructure/Middleware/CorsExtension.cs
create mode 100644 QYZH.InteractiveMagazine.Infrastructure/Middleware/JwtAutoRefreshMiddleware.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/AdminUser.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/CheckInConfig.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/CheckInRecord.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/Journal.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/Medal.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/MessageComment.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/Pet.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/PetEvolution.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/PetFeedingRecord.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/PointsRecord.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/Product.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/TemplateSentence.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/User.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/UserBag.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Entity/UserMedal.cs
create mode 100644 QYZH.InteractiveMagazine.Models/Enum/ApiVersionEnum.cs
create mode 100644 QYZH.InteractiveMagazine.Repository/AdminUserRepository.cs
create mode 100644 QYZH.InteractiveMagazine.Repository/IAdminUserRepository.cs
create mode 100644 QYZH.InteractiveMagazine.Service/AdminAuthService.cs
create mode 100644 QYZH.InteractiveMagazine.Service/AdminUserService.cs
delete mode 100644 QYZH.InteractiveMagazine.Service/AuthService.cs
delete mode 100644 QYZH.InteractiveMagazine.Service/WeChatMiniProgramService.cs
create mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/AdminController.cs
delete mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/AuthController.cs
delete mode 100644 QYZH.InteractiveMagazine.WebApi/Controllers/WeChatController.cs
create mode 100644 QYZH.InteractiveMagazine.WebApi/Dockerfile
diff --git a/QYZH.InteractiveMagazine.IService/Dto/AdminUserDto.cs b/QYZH.InteractiveMagazine.IService/Dto/AdminUserDto.cs
new file mode 100644
index 0000000..8f331e5
--- /dev/null
+++ b/QYZH.InteractiveMagazine.IService/Dto/AdminUserDto.cs
@@ -0,0 +1,96 @@
+using QYZH.InteractiveMagazine.Models.Dto;
+
+namespace QYZH.InteractiveMagazine.IService.Dto;
+
+///
+/// 管理员创建/更新输入
+///
+public class AdminUserInput
+{
+ ///
+ /// 用户名
+ ///
+ public string UserName { get; set; } = string.Empty;
+
+ ///
+ /// 密码(创建时必填,更新时为空表示不修改密码)
+ ///
+ public string? Password { get; set; }
+
+ ///
+ /// 管理员类型: SuperAdmin, Editor
+ ///
+ public string Type { get; set; } = "Editor";
+
+ ///
+ /// 状态: Active, Inactive
+ ///
+ public string Status { get; set; } = "Active";
+}
+
+///
+/// 管理员输出
+///
+public class AdminUserOutput
+{
+ ///
+ /// 主键ID
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// 用户名
+ ///
+ public string UserName { get; set; } = string.Empty;
+
+ ///
+ /// 管理员类型
+ ///
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// 状态
+ ///
+ public string Status { get; set; } = string.Empty;
+
+ ///
+ /// 创建人
+ ///
+ public string? CreatedBy { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// 更新人
+ ///
+ public string? UpdatedBy { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ public DateTime? UpdatedAt { get; set; }
+}
+
+///
+/// 管理员分页查询输入
+///
+public class AdminUserQueryInput : PageQueryModel
+{
+ ///
+ /// 用户名(模糊查询)
+ ///
+ public string? UserName { get; set; }
+
+ ///
+ /// 管理员类型
+ ///
+ public string? Type { get; set; }
+
+ ///
+ /// 状态
+ ///
+ public string? Status { get; set; }
+}
diff --git a/QYZH.InteractiveMagazine.IService/Dto/AuthDto.cs b/QYZH.InteractiveMagazine.IService/Dto/AuthDto.cs
index 08a0d41..512eb85 100644
--- a/QYZH.InteractiveMagazine.IService/Dto/AuthDto.cs
+++ b/QYZH.InteractiveMagazine.IService/Dto/AuthDto.cs
@@ -62,3 +62,32 @@ public class LoginOutput
///
public string RefreshToken { get; set; } = string.Empty;
}
+
+public class AdminLoginInput
+{
+ public string UserName { get; set; } = string.Empty;
+
+ public string Password { get; set; } = string.Empty;
+}
+
+public class AdminLoginOutput
+{
+ public string Token { get; set; } = string.Empty;
+
+ public long UserId { get; set; }
+
+ public string UserName { get; set; } = string.Empty;
+
+ public string Type { get; set; } = string.Empty;
+}
+
+public class AdminUserInfoOutput
+{
+ public long UserId { get; set; }
+
+ public string UserName { get; set; } = string.Empty;
+
+ public string Type { get; set; } = string.Empty;
+
+ public string Status { get; set; } = string.Empty;
+}
diff --git a/QYZH.InteractiveMagazine.IService/IAdminAuthService.cs b/QYZH.InteractiveMagazine.IService/IAdminAuthService.cs
new file mode 100644
index 0000000..d04a986
--- /dev/null
+++ b/QYZH.InteractiveMagazine.IService/IAdminAuthService.cs
@@ -0,0 +1,14 @@
+using QYZH.InteractiveMagazine.IService.Dto;
+
+namespace QYZH.InteractiveMagazine.IService;
+
+public interface IAdminAuthService
+{
+ Task LoginAsync(AdminLoginInput input);
+
+ Task LogoutAsync(long userId);
+
+ Task GetAdminInfoAsync(long userId);
+
+ Task ChangePasswordAsync(long userId, string oldPassword, string newPassword);
+}
diff --git a/QYZH.InteractiveMagazine.IService/IAdminUserService.cs b/QYZH.InteractiveMagazine.IService/IAdminUserService.cs
new file mode 100644
index 0000000..9849629
--- /dev/null
+++ b/QYZH.InteractiveMagazine.IService/IAdminUserService.cs
@@ -0,0 +1,45 @@
+using QYZH.InteractiveMagazine.IService.Dto;
+using QYZH.InteractiveMagazine.Models.Dto;
+
+namespace QYZH.InteractiveMagazine.IService;
+
+///
+/// 管理员用户服务接口
+///
+public interface IAdminUserService
+{
+ ///
+ /// 创建管理员
+ ///
+ /// 管理员输入
+ /// 创建的管理员信息
+ Task CreateAsync(AdminUserInput input);
+
+ ///
+ /// 更新管理员
+ ///
+ /// 管理员ID
+ /// 管理员输入
+ /// 更新后的管理员信息
+ Task UpdateAsync(long id, AdminUserInput input);
+
+ ///
+ /// 删除管理员(软删除)
+ ///
+ /// 管理员ID
+ Task DeleteAsync(long id);
+
+ ///
+ /// 根据ID获取管理员
+ ///
+ /// 管理员ID
+ /// 管理员信息
+ Task GetByIdAsync(long id);
+
+ ///
+ /// 分页查询管理员列表
+ ///
+ /// 查询条件
+ /// 分页结果
+ Task> GetListAsync(AdminUserQueryInput input);
+}
diff --git a/QYZH.InteractiveMagazine.IService/IAuthService.cs b/QYZH.InteractiveMagazine.IService/IAuthService.cs
deleted file mode 100644
index f1ea5a2..0000000
--- a/QYZH.InteractiveMagazine.IService/IAuthService.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using QYZH.InteractiveMagazine.IService.Dto;
-
-namespace QYZH.InteractiveMagazine.IService;
-
-///
-/// 认证服务接口
-///
-public interface IAuthService
-{
- ///
- /// 用户登录
- ///
- /// 账号
- /// 密码
- /// 登录结果
- Task LoginAsync(string account, string password);
-
- ///
- /// 用户注册
- ///
- /// 注册输入参数
- /// 是否成功
- Task RegisterAsync(RegisterInput input);
-
- ///
- /// 刷新令牌
- ///
- /// 刷新令牌
- /// 新的登录结果
- Task RefreshTokenAsync(string refreshToken);
-}
diff --git a/QYZH.InteractiveMagazine.IService/IDependency.cs b/QYZH.InteractiveMagazine.IService/IDependency.cs
new file mode 100644
index 0000000..6ebb143
--- /dev/null
+++ b/QYZH.InteractiveMagazine.IService/IDependency.cs
@@ -0,0 +1,15 @@
+namespace QYZH.InteractiveMagazine.IService;
+
+///
+/// 单例生命周期标记接口
+///
+public interface ISingletonDependency
+{
+}
+
+///
+/// 瞬时生命周期标记接口
+///
+public interface ITransientDependency
+{
+}
diff --git a/QYZH.InteractiveMagazine.IService/IWeChatMiniProgramService.cs b/QYZH.InteractiveMagazine.IService/IWeChatMiniProgramService.cs
deleted file mode 100644
index 6559d8c..0000000
--- a/QYZH.InteractiveMagazine.IService/IWeChatMiniProgramService.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using QYZH.InteractiveMagazine.IService.Dto;
-
-namespace QYZH.InteractiveMagazine.IService;
-
-///
-/// 微信小程序服务接口
-///
-public interface IWeChatMiniProgramService
-{
- ///
- /// 微信登录
- ///
- /// 微信登录凭证
- /// 微信登录结果
- Task WeChatLoginAsync(string code);
-
- ///
- /// 获取手机号
- ///
- /// 获取手机号凭证
- /// 手机号信息
- Task GetPhoneNumberAsync(string code);
-}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/Auth/JwtHelper.cs b/QYZH.InteractiveMagazine.Infrastructure/Auth/JwtHelper.cs
index 683426f..71b8f0a 100644
--- a/QYZH.InteractiveMagazine.Infrastructure/Auth/JwtHelper.cs
+++ b/QYZH.InteractiveMagazine.Infrastructure/Auth/JwtHelper.cs
@@ -44,28 +44,51 @@ public static class JwtHelper
}
///
- /// 验证JWT令牌
+ /// 获取Token过期时间
///
/// JWT令牌
- /// JWT配置
- /// ClaimsPrincipal对象
- public static ClaimsPrincipal ValidateToken(string token, JwtSettings settings)
+ /// 过期时间
+ public static DateTime? GetTokenExpiry(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
- var key = Encoding.UTF8.GetBytes(settings.SecretKey!);
-
- var validationParameters = new TokenValidationParameters
+ if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken)
{
- ValidateIssuer = true,
- ValidIssuer = settings.Issuer,
- ValidateAudience = true,
- ValidAudience = settings.Audience,
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(key),
- ValidateLifetime = true,
- ClockSkew = TimeSpan.Zero
- };
+ return jwtToken.ValidTo;
+ }
+ return null;
+ }
- return tokenHandler.ValidateToken(token, validationParameters, out _);
+ ///
+ /// 从Token中获取用户ID
+ ///
+ /// JWT令牌
+ /// 用户ID
+ public static long? GetUserIdFromToken(string token)
+ {
+ var tokenHandler = new JwtSecurityTokenHandler();
+ if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken)
+ {
+ var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
+ if (long.TryParse(userIdClaim?.Value, out long userId))
+ {
+ return userId;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// 从Token中获取用户名
+ ///
+ /// JWT令牌
+ /// 用户名
+ public static string GetUserNameFromToken(string token)
+ {
+ var tokenHandler = new JwtSecurityTokenHandler();
+ if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken)
+ {
+ return jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? string.Empty;
+ }
+ return string.Empty;
}
}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacExtension.cs b/QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacExtension.cs
new file mode 100644
index 0000000..e0a86be
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacExtension.cs
@@ -0,0 +1,67 @@
+using Autofac;
+using Autofac.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Configuration;
+using QYZH.InteractiveMagazine.Infrastructure.Cache;
+using QYZH.InteractiveMagazine.Infrastructure.MessageQueue;
+using QYZH.InteractiveMagazine.Models.Settings;
+using RabbitMQ.Client;
+using StackExchange.Redis;
+
+namespace QYZH.InteractiveMagazine.Infrastructure.Autofacs
+{
+ public static class AutofacExtension
+ {
+
+ ///
+ /// 根据程序集名称获取程序集
+ ///
+ /// Web应用程序构建器
+ public static void UseAutofac(this WebApplicationBuilder builder)
+ {
+ builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
+ .ConfigureContainer((c, containerBuilder) =>
+ {
+ var friendlyName = AppDomain.CurrentDomain.FriendlyName;
+ var source = friendlyName.Split('.');
+ var assemblyNames = string.Join(".", source.Take(source.Length - 1));
+ containerBuilder.RegisterModule(new AutofacModuleRegister(assemblyNames));
+
+ InitializeRedis(c.Configuration);
+ InitializeRabbitMQ(c.Configuration, containerBuilder);
+ });
+ }
+
+ private static void InitializeRedis(IConfiguration configuration)
+ {
+ var redisSettings = configuration.GetSection("RedisSettings").Get();
+ if (redisSettings != null && !string.IsNullOrWhiteSpace(redisSettings.ConnectionString))
+ {
+ var multiplexer = ConnectionMultiplexer.Connect(redisSettings.ConnectionString);
+ RedisHelper.Connection = multiplexer;
+ RedisHelper.SetKeyPrefix(redisSettings.InstanceName ?? string.Empty);
+ }
+ }
+
+ private static void InitializeRabbitMQ(IConfiguration configuration, ContainerBuilder containerBuilder)
+ {
+ var rabbitMQSettings = configuration.GetSection("RabbitMQSettings").Get();
+ if (rabbitMQSettings != null && !string.IsNullOrWhiteSpace(rabbitMQSettings.HostName))
+ {
+ var factory = new ConnectionFactory
+ {
+ HostName = rabbitMQSettings.HostName,
+ Port = rabbitMQSettings.Port,
+ UserName = rabbitMQSettings.UserName ?? string.Empty,
+ Password = rabbitMQSettings.Password ?? string.Empty,
+ VirtualHost = rabbitMQSettings.VirtualHost ?? string.Empty
+ };
+
+ var connection = factory.CreateConnectionAsync().GetAwaiter().GetResult();
+
+ containerBuilder.RegisterInstance(connection).As().SingleInstance();
+ containerBuilder.RegisterType().InstancePerDependency();
+ }
+ }
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacModuleRegister.cs b/QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacModuleRegister.cs
new file mode 100644
index 0000000..06fcdfe
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Infrastructure/Autofac/AutofacModuleRegister.cs
@@ -0,0 +1,42 @@
+using Autofac;
+using System.Reflection;
+
+namespace QYZH.InteractiveMagazine.Infrastructure.Autofacs
+{
+ public class AutofacModuleRegister : Autofac.Module
+ {
+ private readonly string _assemblyName;
+ public AutofacModuleRegister(string assemblyNames)
+ {
+ _assemblyName = assemblyNames;
+ }
+
+ ///
+ /// 加在程序集
+ ///
+ ///
+ protected override void Load(ContainerBuilder builder)
+ {
+ //注册Repository(只注册接口,遵循依赖倒置原则)
+ builder.RegisterAssemblyTypes(GetAssemblyByName($"{_assemblyName}.Repository"))
+ .Where(t => t.Name.EndsWith("Repository") && !t.IsAbstract)
+ .AsImplementedInterfaces()
+ .InstancePerLifetimeScope();
+
+ //注册Service
+ builder.RegisterAssemblyTypes(GetAssemblyByName($"{_assemblyName}.Service"))
+ .Where(t => t.Name.EndsWith("Service") && !t.IsAbstract)
+ .AsImplementedInterfaces()
+ .InstancePerLifetimeScope();
+ }
+
+ ///
+ /// 根据程序集名称获取程序集
+ ///
+ /// 程序集名称
+ public static Assembly GetAssemblyByName(string AssemblyName)
+ {
+ return Assembly.Load(AssemblyName);
+ }
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/Extensions/DependencyInjectionExtensions.cs b/QYZH.InteractiveMagazine.Infrastructure/Extensions/DependencyInjectionExtensions.cs
index a172a40..9a97769 100644
--- a/QYZH.InteractiveMagazine.Infrastructure/Extensions/DependencyInjectionExtensions.cs
+++ b/QYZH.InteractiveMagazine.Infrastructure/Extensions/DependencyInjectionExtensions.cs
@@ -2,12 +2,8 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
-using QYZH.InteractiveMagazine.Infrastructure.Cache;
-using QYZH.InteractiveMagazine.Infrastructure.MessageQueue;
using QYZH.InteractiveMagazine.Infrastructure.Middleware;
using QYZH.InteractiveMagazine.Models.Settings;
-using RabbitMQ.Client;
-using StackExchange.Redis;
using System.Text;
namespace QYZH.InteractiveMagazine.Infrastructure.Extensions;
@@ -25,11 +21,10 @@ public static class DependencyInjectionExtensions
public static void AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration)
{
AddJwtAuthentication(services, configuration);
- AddRedisCache(services, configuration);
- AddRabbitMQ(services, configuration);
services.AddTransient();
services.AddTransient();
+
}
///
@@ -59,52 +54,4 @@ public static class DependencyInjectionExtensions
};
});
}
-
- ///
- /// 注册Redis缓存
- ///
- /// 服务集合
- /// 配置
- private static void AddRedisCache(IServiceCollection services, IConfiguration configuration)
- {
- var redisSettings = configuration.GetSection("RedisSettings").Get()!;
-
- services.AddSingleton(redisSettings);
-
- services.AddSingleton(sp =>
- {
- var multiplexer = ConnectionMultiplexer.Connect(redisSettings.ConnectionString!);
- RedisHelper.Connection = multiplexer;
- RedisHelper.SetKeyPrefix(redisSettings.InstanceName!);
- return multiplexer;
- });
- }
-
- ///
- /// 注册RabbitMQ
- ///
- /// 服务集合
- /// 配置
- private static void AddRabbitMQ(IServiceCollection services, IConfiguration configuration)
- {
- var rabbitMQSettings = configuration.GetSection("RabbitMQSettings").Get()!;
-
- services.AddSingleton(rabbitMQSettings);
-
- services.AddSingleton(sp =>
- {
- var factory = new ConnectionFactory
- {
- HostName = rabbitMQSettings.HostName!,
- Port = rabbitMQSettings.Port,
- UserName = rabbitMQSettings.UserName!,
- Password = rabbitMQSettings.Password!,
- VirtualHost = rabbitMQSettings.VirtualHost!
- };
-
- return factory.CreateConnectionAsync().GetAwaiter().GetResult();
- });
-
- services.AddTransient();
- }
}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/Middleware/CorsExtension.cs b/QYZH.InteractiveMagazine.Infrastructure/Middleware/CorsExtension.cs
new file mode 100644
index 0000000..c1be727
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Infrastructure/Middleware/CorsExtension.cs
@@ -0,0 +1,42 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace QYZH.InteractiveMagazine.Infrastructure.Middleware
+{
+ ///
+ /// 跨域扩展
+ ///
+ public static class CorsExtension
+ {
+ ///
+ /// 跨域配置
+ ///
+ ///
+ ///
+ public static void AddCorsRegister(this WebApplicationBuilder builder)
+ {
+ bool isCorsAll = builder.Configuration["corsUrls"] == "*";
+ builder.Services.AddCors(options =>
+ {
+ options.AddPolicy("defaultCors", policy =>
+ {
+ if (isCorsAll)
+ {
+ policy.SetIsOriginAllowed(origin => true);
+ }
+ else
+ {
+ var corsUrls = builder.Configuration.GetSection("corsUrls").Get();
+ policy.WithOrigins(corsUrls ?? Array.Empty());
+ }
+ policy.AllowAnyHeader()
+ .AllowAnyMethod()
+ .AllowCredentials()
+ .WithExposedHeaders("X-New-Token");
+
+ });
+ });
+ }
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/Middleware/JwtAutoRefreshMiddleware.cs b/QYZH.InteractiveMagazine.Infrastructure/Middleware/JwtAutoRefreshMiddleware.cs
new file mode 100644
index 0000000..6679d05
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Infrastructure/Middleware/JwtAutoRefreshMiddleware.cs
@@ -0,0 +1,79 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using QYZH.InteractiveMagazine.Infrastructure.Cache;
+using QYZH.InteractiveMagazine.Models.Settings;
+using System.IdentityModel.Tokens.Jwt;
+
+namespace QYZH.InteractiveMagazine.Infrastructure.Middleware;
+
+public class JwtAutoRefreshMiddleware
+{
+ private readonly RequestDelegate _next;
+ private readonly IConfiguration _configuration;
+ private const string TokenKeyPrefix = "InteractiveMagazine:AdminAuth:Token";
+ private const int RefreshThresholdMinutes = 10;
+
+ public JwtAutoRefreshMiddleware(RequestDelegate next, IConfiguration configuration)
+ {
+ _next = next;
+ _configuration = configuration;
+ }
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ var authHeader = context.Request.Headers.Authorization.FirstOrDefault();
+ if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer "))
+ {
+ var token = authHeader.Substring("Bearer ".Length).Trim();
+ await TryAutoRefreshTokenAsync(context, token);
+ }
+
+ await _next(context);
+ }
+
+ private async Task TryAutoRefreshTokenAsync(HttpContext context, string token)
+ {
+ try
+ {
+ var tokenHandler = new JwtSecurityTokenHandler();
+ if (tokenHandler.ReadToken(token) is not JwtSecurityToken jwtToken)
+ {
+ return;
+ }
+
+ var expiryTime = jwtToken.ValidTo;
+ var remainingTime = expiryTime - DateTime.Now;
+
+ if (remainingTime <= TimeSpan.FromMinutes(RefreshThresholdMinutes) && remainingTime > TimeSpan.Zero)
+ {
+ var userId = jwtToken.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
+ var userName = jwtToken.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Name)?.Value;
+
+ if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(userName))
+ {
+ return;
+ }
+
+ var jwtSettings = _configuration.GetSection("JwtSettings").Get();
+ if (jwtSettings == null)
+ {
+ return;
+ }
+
+ var newToken = QYZH.InteractiveMagazine.Infrastructure.Auth.JwtHelper.GenerateToken(
+ long.Parse(userId), userName, jwtSettings);
+
+ await RedisHelper.StringSetAsync(
+ $"{TokenKeyPrefix}:{userId}",
+ newToken,
+ TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
+
+ context.Response.Headers["X-New-Token"] = newToken;
+ }
+ }
+ catch
+ {
+ // 忽略自动刷新异常,由后续认证中间件处理
+ }
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Infrastructure/QYZH.InteractiveMagazine.Infrastructure.csproj b/QYZH.InteractiveMagazine.Infrastructure/QYZH.InteractiveMagazine.Infrastructure.csproj
index 14eab45..61c1cf7 100644
--- a/QYZH.InteractiveMagazine.Infrastructure/QYZH.InteractiveMagazine.Infrastructure.csproj
+++ b/QYZH.InteractiveMagazine.Infrastructure/QYZH.InteractiveMagazine.Infrastructure.csproj
@@ -1,11 +1,14 @@
-
+
+
+
+
diff --git a/QYZH.InteractiveMagazine.Models/Entity/AdminUser.cs b/QYZH.InteractiveMagazine.Models/Entity/AdminUser.cs
new file mode 100644
index 0000000..9e391b6
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/AdminUser.cs
@@ -0,0 +1,51 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///后台管理员表
+ ///
+ [SugarTable("AdminUser")]
+ public partial class AdminUser : BaseEntity
+ {
+ public AdminUser(){
+
+
+ }
+ ///
+ /// Desc:主键
+ /// Default:
+ /// Nullable:False
+ ///
+ [SugarColumn(IsPrimaryKey=true,IsIdentity=true)]
+ public new int Id { get; set; }
+
+ ///
+ /// Desc:用户名
+ /// Default:
+ /// Nullable:False
+ ///
+ public string UserName {get;set;}
+
+ ///
+ /// Desc:密码哈希
+ /// Default:
+ /// Nullable:False
+ ///
+ public string PasswordHash {get;set;}
+
+ ///
+ /// Desc:管理员类型: SuperAdmin, Editor
+ /// Default:Editor
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Active, Inactive
+ /// Default:Active
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/BaseEntity.cs b/QYZH.InteractiveMagazine.Models/Entity/BaseEntity.cs
index 8d0678f..9558235 100644
--- a/QYZH.InteractiveMagazine.Models/Entity/BaseEntity.cs
+++ b/QYZH.InteractiveMagazine.Models/Entity/BaseEntity.cs
@@ -23,21 +23,21 @@ public abstract class BaseEntity
/// 创建人
///
[SugarColumn(Length = 50)]
- public string? CreatedBy { get; set; }
+ public string? CreatedBy { get; set; } = "System";
///
/// 创建时间
///
- public DateTime CreatedTime { get; set; } = DateTime.Now;
+ public DateTime CreatedAt { get; set; } = DateTime.Now;
///
/// 更新人
///
[SugarColumn(Length = 50)]
- public string? UpdatedBy { get; set; }
+ public string? UpdatedBy { get; set; } = "System";
///
/// 更新时间
///
- public DateTime UpdatedTime { get; set; } = DateTime.Now;
+ public DateTime? UpdatedAt { get; set; } = DateTime.Now;
}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/CheckInConfig.cs b/QYZH.InteractiveMagazine.Models/Entity/CheckInConfig.cs
new file mode 100644
index 0000000..c3e21c7
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/CheckInConfig.cs
@@ -0,0 +1,58 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///签到配置表
+ ///
+ [SugarTable("CheckInConfig")]
+ public partial class CheckInConfig : BaseEntity
+ {
+ public CheckInConfig(){
+
+
+ }
+ ///
+ /// Desc:主键
+ /// Default:
+ /// Nullable:False
+ ///
+ [SugarColumn(IsPrimaryKey=true,IsIdentity=true)]
+ public new int Id {get;set;}
+
+ ///
+ /// Desc:连续签到天数
+ /// Default:
+ /// Nullable:False
+ ///
+ public int DayNumber {get;set;}
+
+ ///
+ /// Desc:奖励积分数
+ /// Default:
+ /// Nullable:False
+ ///
+ public int RewardPoints {get;set;}
+
+ ///
+ /// Desc:额外奖励积分
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int BonusPoints {get;set;}
+
+ ///
+ /// Desc:配置类型: Daily, Streak
+ /// Default:Daily
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Active, Inactive
+ /// Default:Active
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/CheckInRecord.cs b/QYZH.InteractiveMagazine.Models/Entity/CheckInRecord.cs
new file mode 100644
index 0000000..6e45624
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/CheckInRecord.cs
@@ -0,0 +1,57 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///签到记录表
+ ///
+ [SugarTable("CheckInRecord")]
+ public partial class CheckInRecord : BaseEntity
+ {
+ public CheckInRecord(){
+
+
+ }
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:签到日期
+ /// Default:
+ /// Nullable:False
+ ///
+ public DateTime CheckInDate {get;set;}
+
+ ///
+ /// Desc:本次签到获得积分
+ /// Default:
+ /// Nullable:False
+ ///
+ public int PointsAwarded {get;set;}
+
+ ///
+ /// Desc:连续签到天数
+ /// Default:
+ /// Nullable:False
+ ///
+ public int ConsecutiveDays {get;set;}
+
+ ///
+ /// Desc:签到类型: Normal, MakeUp
+ /// Default:Normal
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Success
+ /// Default:Success
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs
new file mode 100644
index 0000000..bf99952
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/CommunityMessage.cs
@@ -0,0 +1,64 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///社区预置消息表
+ ///
+ [SugarTable("CommunityMessage")]
+ public partial class CommunityMessage : BaseEntity
+ {
+ public CommunityMessage(){
+
+
+ }
+ ///
+ /// Desc:期刊Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long JournalId {get;set;}
+
+ ///
+ /// Desc:消息内容
+ /// Default:
+ /// Nullable:False
+ ///
+ public string Content {get;set;}
+
+ ///
+ /// Desc:配图
+ /// Default:
+ /// Nullable:True
+ ///
+ public string ImageUrl {get;set;}
+
+ ///
+ /// Desc:排序
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int SortOrder {get;set;}
+
+ ///
+ /// Desc:是否启用
+ /// Default:b'1'
+ /// Nullable:False
+ ///
+ public bool IsActive {get;set;}
+
+ ///
+ /// Desc:消息类型: Article, Quote, Announcement
+ /// Default:Article
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Draft, Published
+ /// Default:Draft
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/Journal.cs b/QYZH.InteractiveMagazine.Models/Entity/Journal.cs
new file mode 100644
index 0000000..05d39ec
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/Journal.cs
@@ -0,0 +1,92 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///期刊表
+ ///
+ [SugarTable("Journal")]
+ public partial class Journal : BaseEntity
+ {
+ public Journal(){
+
+
+ }
+ ///
+ /// Desc:期刊号
+ /// Default:
+ /// Nullable:False
+ ///
+ public int IssueNumber {get;set;}
+
+ ///
+ /// Desc:期刊标题
+ /// Default:
+ /// Nullable:False
+ ///
+ public string Title {get;set;}
+
+ ///
+ /// Desc:封面图地址
+ /// Default:
+ /// Nullable:True
+ ///
+ public string CoverImageUrl {get;set;}
+
+ ///
+ /// Desc:摘要
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Summary {get;set;}
+
+ ///
+ /// Desc:发布时间
+ /// Default:
+ /// Nullable:True
+ ///
+ public DateTime? PublishDate {get;set;}
+
+ ///
+ /// Desc:是否启用
+ /// Default:b'1'
+ /// Nullable:False
+ ///
+ public bool IsActive {get;set;}
+
+ ///
+ /// Desc:排序权重
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int SortOrder {get;set;}
+
+ ///
+ /// Desc:编者寄语
+ /// Default:
+ /// Nullable:True
+ ///
+ public string EditorNote {get;set;}
+
+ ///
+ /// Desc:主题色
+ /// Default:
+ /// Nullable:True
+ ///
+ public string ThemeColor {get;set;}
+
+ ///
+ /// Desc:期刊类型: Normal, Special
+ /// Default:Normal
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Draft, Published, Archived
+ /// Default:Draft
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/Medal.cs b/QYZH.InteractiveMagazine.Models/Entity/Medal.cs
new file mode 100644
index 0000000..aa48eaa
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/Medal.cs
@@ -0,0 +1,79 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///勋章定义表
+ ///
+ [SugarTable("Medal")]
+ public partial class Medal : BaseEntity
+ {
+ public Medal(){
+
+
+ }
+ ///
+ /// Desc:主键
+ /// Default:
+ /// Nullable:False
+ ///
+ [SugarColumn(IsPrimaryKey=true,IsIdentity=true)]
+ public new int Id {get;set;}
+
+ ///
+ /// Desc:勋章名称
+ /// Default:
+ /// Nullable:False
+ ///
+ public string Name {get;set;}
+
+ ///
+ /// Desc:获得条件描述
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Description {get;set;}
+
+ ///
+ /// Desc:图片地址
+ /// Default:
+ /// Nullable:True
+ ///
+ public string ImageUrl {get;set;}
+
+ ///
+ /// Desc:条件类型: EvolutionCount, FeedingCount等
+ /// Default:
+ /// Nullable:False
+ ///
+ public string ConditionType {get;set;}
+
+ ///
+ /// Desc:条件阈值
+ /// Default:
+ /// Nullable:False
+ ///
+ public int ConditionValue {get;set;}
+
+ ///
+ /// Desc:排序
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int SortOrder {get;set;}
+
+ ///
+ /// Desc:勋章的类型: Pet, Community
+ /// Default:Pet
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Active, Inactive
+ /// Default:Active
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/MessageComment.cs b/QYZH.InteractiveMagazine.Models/Entity/MessageComment.cs
new file mode 100644
index 0000000..a1d0f03
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/MessageComment.cs
@@ -0,0 +1,50 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///模板评论表
+ ///
+ [SugarTable("MessageComment")]
+ public partial class MessageComment : BaseEntity
+ {
+ public MessageComment(){
+
+
+ }
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:消息Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long MessageId {get;set;}
+
+ ///
+ /// Desc:模板Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public int TemplateId {get;set;}
+
+ ///
+ /// Desc:评论类型
+ /// Default:TemplateComment
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Published, Hidden
+ /// Default:Published
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs b/QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs
new file mode 100644
index 0000000..55a87db
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/MessageLike.cs
@@ -0,0 +1,43 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///点赞记录表
+ ///
+ [SugarTable("MessageLike")]
+ public partial class MessageLike : BaseEntity
+ {
+ 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/Pet.cs b/QYZH.InteractiveMagazine.Models/Entity/Pet.cs
new file mode 100644
index 0000000..180175d
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/Pet.cs
@@ -0,0 +1,64 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///宠物实例表
+ ///
+ [SugarTable("Pet")]
+ public partial class Pet : BaseEntity
+ {
+ public Pet(){
+
+
+ }
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:宠物昵称
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Name {get;set;}
+
+ ///
+ /// Desc:当前进化形态Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public int CurrentEvolutionId {get;set;}
+
+ ///
+ /// Desc:当前成长值
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int GrowthPoints {get;set;}
+
+ ///
+ /// Desc:累计喂养次数
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int FeedingCount {get;set;}
+
+ ///
+ /// Desc:宠物类型
+ /// Default:Normal
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Active, Sleeping
+ /// Default:Active
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/PetEvolution.cs b/QYZH.InteractiveMagazine.Models/Entity/PetEvolution.cs
new file mode 100644
index 0000000..7b8f9f0
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/PetEvolution.cs
@@ -0,0 +1,100 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///宠物进化链定义表
+ ///
+ [SugarTable("PetEvolution")]
+ public partial class PetEvolution : BaseEntity
+ {
+ public PetEvolution(){
+
+
+ }
+ ///
+ /// Desc:主键
+ /// Default:
+ /// Nullable:False
+ ///
+ [SugarColumn(IsPrimaryKey=true,IsIdentity=true)]
+ public new int Id {get;set;}
+
+ ///
+ /// Desc:阶段名称
+ /// Default:
+ /// Nullable:False
+ ///
+ public string StageName {get;set;}
+
+ ///
+ /// Desc:阶段等级
+ /// Default:
+ /// Nullable:False
+ ///
+ public int StageLevel {get;set;}
+
+ ///
+ /// Desc:进化所需成长值
+ /// Default:
+ /// Nullable:False
+ ///
+ public int RequiredGrowth {get;set;}
+
+ ///
+ /// Desc:形态图片
+ /// Default:
+ /// Nullable:True
+ ///
+ public string ImageUrl {get;set;}
+
+ ///
+ /// Desc:前一形态Id
+ /// Default:
+ /// Nullable:True
+ ///
+ public int? PreviousEvolutionId {get;set;}
+
+ ///
+ /// Desc:基础力量
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int BaseStrength {get;set;}
+
+ ///
+ /// Desc:基础敏捷
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int BaseAgility {get;set;}
+
+ ///
+ /// Desc:基础智力
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int BaseIntelligence {get;set;}
+
+ ///
+ /// Desc:基础魅力
+ /// Default:0
+ /// Nullable:False
+ ///
+ public int BaseCharm {get;set;}
+
+ ///
+ /// Desc:进化类型: Normal, Special
+ /// Default:Normal
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Active, Inactive
+ /// Default:Active
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/PetFeedingRecord.cs b/QYZH.InteractiveMagazine.Models/Entity/PetFeedingRecord.cs
new file mode 100644
index 0000000..02465db
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/PetFeedingRecord.cs
@@ -0,0 +1,71 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///宠物喂养记录表
+ ///
+ [SugarTable("PetFeedingRecord")]
+ public partial class PetFeedingRecord : BaseEntity
+ {
+ public PetFeedingRecord(){
+
+
+ }
+ ///
+ /// Desc:宠物Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long PetId {get;set;}
+
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:消耗积分
+ /// Default:
+ /// Nullable:False
+ ///
+ public int PointsUsed {get;set;}
+
+ ///
+ /// Desc:成长值变化量
+ /// Default:
+ /// Nullable:False
+ ///
+ public int GrowthChange {get;set;}
+
+ ///
+ /// Desc:喂养前成长值
+ /// Default:
+ /// Nullable:False
+ ///
+ public int GrowthBefore {get;set;}
+
+ ///
+ /// Desc:喂养后成长值
+ /// Default:
+ /// Nullable:False
+ ///
+ public int GrowthAfter {get;set;}
+
+ ///
+ /// Desc:喂养类型: Normal, Special
+ /// Default:Normal
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态
+ /// Default:Success
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/PointsRecord.cs b/QYZH.InteractiveMagazine.Models/Entity/PointsRecord.cs
new file mode 100644
index 0000000..30cb5eb
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/PointsRecord.cs
@@ -0,0 +1,71 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///积分流水记录表
+ ///
+ [SugarTable("PointsRecord")]
+ public partial class PointsRecord : BaseEntity
+ {
+ public PointsRecord(){
+
+
+ }
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:变动数值
+ /// Default:
+ /// Nullable:False
+ ///
+ public int ChangeAmount {get;set;}
+
+ ///
+ /// Desc:变动后余额
+ /// Default:
+ /// Nullable:False
+ ///
+ public int BalanceAfter {get;set;}
+
+ ///
+ /// Desc:变动类型: Exchange, FeedPet, SignIn, TaskReward
+ /// Default:
+ /// Nullable:False
+ ///
+ public string ChangeType {get;set;}
+
+ ///
+ /// Desc:关联业务Id
+ /// Default:
+ /// Nullable:True
+ ///
+ public long? RelatedId {get;set;}
+
+ ///
+ /// Desc:备注
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Description {get;set;}
+
+ ///
+ /// Desc:流水分类
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Success, Failed, Pending
+ /// Default:Success
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/Product.cs b/QYZH.InteractiveMagazine.Models/Entity/Product.cs
new file mode 100644
index 0000000..2160df3
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/Product.cs
@@ -0,0 +1,86 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///虚拟商品表
+ ///
+ [SugarTable("Product")]
+ public partial class Product : BaseEntity
+ {
+ public Product(){
+
+
+ }
+ ///
+ /// Desc:主键
+ /// Default:
+ /// Nullable:False
+ ///
+ [SugarColumn(IsPrimaryKey=true,IsIdentity=true)]
+ public new int Id {get;set;}
+
+ ///
+ /// Desc:商品名称
+ /// Default:
+ /// Nullable:False
+ ///
+ public string Name {get;set;}
+
+ ///
+ /// Desc:描述
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Description {get;set;}
+
+ ///
+ /// Desc:商品图片
+ /// Default:
+ /// Nullable:True
+ ///
+ public string ImageUrl {get;set;}
+
+ ///
+ /// Desc:所需积分
+ /// Default:
+ /// Nullable:False
+ ///
+ public int Price {get;set;}
+
+ ///
+ /// Desc:商品类型: MakeUpCard, PetBg
+ /// Default:
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: OnSale, OffSale
+ /// Default:OnSale
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+
+ ///
+ /// Desc:扩展数据
+ /// Default:
+ /// Nullable:True
+ ///
+ public string MetaData {get;set;}
+
+ ///
+ /// Desc:是否上架
+ /// Default:b'1'
+ /// Nullable:False
+ ///
+ public bool IsActive {get;set;}
+
+ ///
+ /// Desc:库存(-1无限)
+ /// Default:-1
+ /// Nullable:False
+ ///
+ public int Stock {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/TemplateSentence.cs b/QYZH.InteractiveMagazine.Models/Entity/TemplateSentence.cs
new file mode 100644
index 0000000..acf5f66
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/TemplateSentence.cs
@@ -0,0 +1,51 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///固定模板言论表
+ ///
+ [SugarTable("TemplateSentence")]
+ public partial class TemplateSentence : BaseEntity
+ {
+ public TemplateSentence(){
+
+
+ }
+ ///
+ /// Desc:主键
+ /// Default:
+ /// Nullable:False
+ ///
+ [SugarColumn(IsPrimaryKey=true,IsIdentity=true)]
+ public new int Id {get;set;}
+
+ ///
+ /// Desc:模板内容
+ /// Default:
+ /// Nullable:False
+ ///
+ public string Sentence {get;set;}
+
+ ///
+ /// Desc:是否启用
+ /// Default:b'1'
+ /// Nullable:False
+ ///
+ public bool IsActive {get;set;}
+
+ ///
+ /// Desc:分类: Positive, Neutral, Negative
+ /// Default:Neutral
+ /// Nullable:False
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Active, Inactive
+ /// Default:Active
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/User.cs b/QYZH.InteractiveMagazine.Models/Entity/User.cs
new file mode 100644
index 0000000..67f617a
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/User.cs
@@ -0,0 +1,71 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///用户表
+ ///
+ [SugarTable("User")]
+ public partial class User : BaseEntity
+ {
+ public User(){
+
+
+ }
+ ///
+ /// 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;}
+
+ ///
+ /// Desc:头像地址
+ /// Default:
+ /// Nullable:True
+ ///
+ public string AvatarUrl {get;set;}
+
+ ///
+ /// Desc:手机号
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Phone {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/UserBag.cs b/QYZH.InteractiveMagazine.Models/Entity/UserBag.cs
new file mode 100644
index 0000000..de6a09e
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/UserBag.cs
@@ -0,0 +1,64 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///用户背包表
+ ///
+ [SugarTable("UserBag")]
+ public partial class UserBag : BaseEntity
+ {
+ public UserBag(){
+
+
+ }
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:物品类型: MakeUpCard, PetBg
+ /// Default:
+ /// Nullable:False
+ ///
+ public string ItemType {get;set;}
+
+ ///
+ /// Desc:关联商品Id或资源Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public int ItemId {get;set;}
+
+ ///
+ /// Desc:数量
+ /// Default:1
+ /// Nullable:False
+ ///
+ public int Quantity {get;set;}
+
+ ///
+ /// Desc:扩展信息
+ /// Default:
+ /// Nullable:True
+ ///
+ public string MetaData {get;set;}
+
+ ///
+ /// Desc:背包物品分类
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Available, Expired
+ /// Default:Available
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Entity/UserMedal.cs b/QYZH.InteractiveMagazine.Models/Entity/UserMedal.cs
new file mode 100644
index 0000000..c30a3b9
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Entity/UserMedal.cs
@@ -0,0 +1,50 @@
+using SqlSugar;
+
+namespace QYZH.InteractiveMagazine.Models.Entity
+{
+ ///
+ ///用户勋章表
+ ///
+ [SugarTable("UserMedal")]
+ public partial class UserMedal : BaseEntity
+ {
+ public UserMedal(){
+
+
+ }
+ ///
+ /// Desc:用户Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public long UserId {get;set;}
+
+ ///
+ /// Desc:勋章Id
+ /// Default:
+ /// Nullable:False
+ ///
+ public int MedalId {get;set;}
+
+ ///
+ /// Desc:获得时间
+ /// Default:
+ /// Nullable:True
+ ///
+ public DateTime? AwardedAt {get;set;}
+
+ ///
+ /// Desc:勋章记录类型
+ /// Default:
+ /// Nullable:True
+ ///
+ public string Type {get;set;}
+
+ ///
+ /// Desc:状态: Awarded, Revoked
+ /// Default:Awarded
+ /// Nullable:False
+ ///
+ public string Status {get;set;}
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/Enum/ApiVersionEnum.cs b/QYZH.InteractiveMagazine.Models/Enum/ApiVersionEnum.cs
new file mode 100644
index 0000000..9363da6
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Models/Enum/ApiVersionEnum.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace QYZH.InteractiveMagazine.Models.Enum
+{
+ public enum ApiVersionEnum
+ {
+
+
+ ///
+ /// Platform
+ ///
+ [Description("平台管理")]
+ Platform = 10,
+ ///
+ /// Wechat
+ ///
+ [Description("小程序管理")]
+ Wechat = 20,
+
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj b/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj
index 33a2e64..0bb8e11 100644
--- a/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj
+++ b/QYZH.InteractiveMagazine.Models/QYZH.InteractiveMagazine.Models.csproj
@@ -1,9 +1,10 @@
-
+
net8.0
enable
enable
+ True
diff --git a/QYZH.InteractiveMagazine.Repository/AdminUserRepository.cs b/QYZH.InteractiveMagazine.Repository/AdminUserRepository.cs
new file mode 100644
index 0000000..e81d9a9
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Repository/AdminUserRepository.cs
@@ -0,0 +1,13 @@
+using QYZH.InteractiveMagazine.Models.Entity;
+
+namespace QYZH.InteractiveMagazine.Repository;
+
+public class AdminUserRepository : BaseRepository, IAdminUserRepository
+{
+ public async Task GetByUserNameAsync(string userName)
+ {
+ return await Db.Queryable()
+ .Where(x => x.UserName == userName)
+ .FirstAsync();
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Repository/BaseRepository.cs b/QYZH.InteractiveMagazine.Repository/BaseRepository.cs
index 4045146..cbb2414 100644
--- a/QYZH.InteractiveMagazine.Repository/BaseRepository.cs
+++ b/QYZH.InteractiveMagazine.Repository/BaseRepository.cs
@@ -146,4 +146,9 @@ public class BaseRepository : IBaseRepository where T : class, new()
{
return await Db.Queryable().Where(where).CountAsync();
}
+
+ public ISugarQueryable Queryable()
+ {
+ return Db.Queryable();
+ }
}
diff --git a/QYZH.InteractiveMagazine.Repository/IAdminUserRepository.cs b/QYZH.InteractiveMagazine.Repository/IAdminUserRepository.cs
new file mode 100644
index 0000000..37537eb
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Repository/IAdminUserRepository.cs
@@ -0,0 +1,8 @@
+using QYZH.InteractiveMagazine.Models.Entity;
+
+namespace QYZH.InteractiveMagazine.Repository;
+
+public interface IAdminUserRepository : IBaseRepository
+{
+ Task GetByUserNameAsync(string userName);
+}
diff --git a/QYZH.InteractiveMagazine.Service/AdminAuthService.cs b/QYZH.InteractiveMagazine.Service/AdminAuthService.cs
new file mode 100644
index 0000000..515bda5
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Service/AdminAuthService.cs
@@ -0,0 +1,168 @@
+using BCrypt.Net;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+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 AdminAuthService : IAdminAuthService
+{
+ private readonly IAdminUserRepository _adminUserRepository;
+ private readonly IConfiguration _configuration;
+ private readonly ILogger _logger;
+
+ private const string TokenKeyPrefix = "InteractiveMagazine:AdminAuth:Token";
+ private const string UserInfoKeyPrefix = "InteractiveMagazine:AdminAuth:UserInfo";
+
+ public AdminAuthService(IAdminUserRepository adminUserRepository, IConfiguration configuration, ILogger logger)
+ {
+ _adminUserRepository = adminUserRepository;
+ _configuration = configuration;
+ _logger = logger;
+ }
+
+ public async Task LoginAsync(AdminLoginInput input)
+ {
+ _logger.LogInformation("管理员登录尝试,用户名: {UserName}", input.UserName);
+
+ if (string.IsNullOrWhiteSpace(input.UserName))
+ {
+ throw new BusinessException("用户名不能为空", 400);
+ }
+
+ if (string.IsNullOrWhiteSpace(input.Password))
+ {
+ throw new BusinessException("密码不能为空", 400);
+ }
+
+ var adminUser = await _adminUserRepository.GetByUserNameAsync(input.UserName);
+ if (adminUser == null)
+ {
+ _logger.LogWarning("管理员登录失败,用户名不存在: {UserName}", input.UserName);
+ throw new BusinessException("用户名或密码错误", 401);
+ }
+
+ if (!BCrypt.Net.BCrypt.Verify(input.Password, adminUser.PasswordHash))
+ {
+ _logger.LogWarning("管理员登录失败,密码错误: {UserName}", input.UserName);
+ throw new BusinessException("用户名或密码错误", 401);
+ }
+
+ if (adminUser.Status != "Active")
+ {
+ _logger.LogWarning("管理员登录失败,账号已禁用: {UserName}", input.UserName);
+ throw new BusinessException("账号已被禁用,请联系系统管理员", 403);
+ }
+
+ var jwtSettings = GetJwtSettings();
+
+ var token = JwtHelper.GenerateToken((long)adminUser.Id, adminUser.UserName, jwtSettings);
+
+ await RedisHelper.StringSetAsync($"{TokenKeyPrefix}:{adminUser.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
+
+ _logger.LogInformation("管理员登录成功,用户名: {UserName}, ID: {UserId}", input.UserName, adminUser.Id);
+
+ return new AdminLoginOutput
+ {
+ Token = token,
+ UserId = (long)adminUser.Id,
+ UserName = adminUser.UserName,
+ Type = adminUser.Type
+ };
+ }
+
+ public async Task LogoutAsync(long userId)
+ {
+ _logger.LogInformation("管理员登出,ID: {UserId}", userId);
+
+ await RedisHelper.KeyDeleteAsync($"{TokenKeyPrefix}:{userId}");
+
+ _logger.LogInformation("管理员登出成功,ID: {UserId}", userId);
+ }
+
+ public async Task GetAdminInfoAsync(long userId)
+ {
+ _logger.LogInformation("获取管理员信息,ID: {UserId}", userId);
+
+ var adminUser = await _adminUserRepository.GetByIdAsync(userId);
+ if (adminUser == null)
+ {
+ _logger.LogWarning("未找到管理员,ID: {UserId}", userId);
+ throw new BusinessException("用户不存在", 404);
+ }
+
+ return new AdminUserInfoOutput
+ {
+ UserId = adminUser.Id,
+ UserName = adminUser.UserName,
+ Type = adminUser.Type,
+ Status = adminUser.Status
+ };
+ }
+
+ public async Task ChangePasswordAsync(long userId, string oldPassword, string newPassword)
+ {
+ _logger.LogInformation("管理员修改密码尝试,ID: {UserId}", userId);
+
+ if (string.IsNullOrWhiteSpace(oldPassword))
+ {
+ throw new BusinessException("原密码不能为空", 400);
+ }
+
+ if (string.IsNullOrWhiteSpace(newPassword))
+ {
+ throw new BusinessException("新密码不能为空", 400);
+ }
+
+ var adminUser = await _adminUserRepository.GetByIdAsync(userId);
+ if (adminUser == null)
+ {
+ _logger.LogWarning("未找到管理员,ID: {UserId}", userId);
+ throw new BusinessException("用户不存在", 404);
+ }
+
+ if (!BCrypt.Net.BCrypt.Verify(oldPassword, adminUser.PasswordHash))
+ {
+ _logger.LogWarning("管理员修改密码失败,原密码错误,ID: {UserId}", userId);
+ throw new BusinessException("原密码错误", 400);
+ }
+
+ adminUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);
+
+ var result = await _adminUserRepository.UpdateAsync(adminUser);
+ if (!result)
+ {
+ throw new BusinessException("修改密码失败", 500);
+ }
+
+ await RedisHelper.KeyDeleteAsync($"{TokenKeyPrefix}:{userId}");
+
+ _logger.LogInformation("管理员修改密码成功,ID: {UserId}", userId);
+ }
+
+ 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/AdminUserService.cs b/QYZH.InteractiveMagazine.Service/AdminUserService.cs
new file mode 100644
index 0000000..a1dd6d6
--- /dev/null
+++ b/QYZH.InteractiveMagazine.Service/AdminUserService.cs
@@ -0,0 +1,267 @@
+using System.Linq.Expressions;
+using BCrypt.Net;
+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;
+
+namespace QYZH.InteractiveMagazine.Service;
+
+///
+/// 管理员用户服务实现
+///
+public class AdminUserService : IAdminUserService
+{
+ private readonly IAdminUserRepository _adminUserRepository;
+ private readonly ILogger _logger;
+
+ public AdminUserService(IAdminUserRepository adminUserRepository, ILogger logger)
+ {
+ _adminUserRepository = adminUserRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 创建管理员
+ ///
+ public async Task CreateAsync(AdminUserInput input)
+ {
+ _logger.LogInformation("正在创建管理员,用户名: {UserName}", input.UserName);
+
+ if (string.IsNullOrWhiteSpace(input.UserName))
+ {
+ throw new BusinessException("用户名不能为空", 400);
+ }
+
+ if (string.IsNullOrWhiteSpace(input.Password))
+ {
+ throw new BusinessException("密码不能为空", 400);
+ }
+
+ // 检查用户名是否已存在
+ var existingUser = await _adminUserRepository.GetByUserNameAsync(input.UserName);
+ if (existingUser != null)
+ {
+ _logger.LogWarning("创建管理员失败,用户名已存在: {UserName}", input.UserName);
+ throw new BusinessException("用户名已存在", 400);
+ }
+
+ var adminUser = new AdminUser
+ {
+ UserName = input.UserName.Trim(),
+ PasswordHash = BCrypt.Net.BCrypt.HashPassword(input.Password),
+ Type = input.Type,
+ Status = input.Status,
+ CreatedBy = "System",
+ UpdatedBy = "System",
+ CreatedAt = DateTime.Now,
+ UpdatedAt = DateTime.Now,
+ IsDeleted = false
+ };
+
+ var result = await _adminUserRepository.InsertAsync(adminUser);
+ if (!result)
+ {
+ _logger.LogError("管理员创建失败,用户名: {UserName}", input.UserName);
+ throw new BusinessException("创建管理员失败", 500);
+ }
+
+ _logger.LogInformation("管理员创建成功,用户名: {UserName}, ID: {Id}", input.UserName, adminUser.Id);
+
+ return MapToOutput(adminUser);
+ }
+
+ ///
+ /// 更新管理员
+ ///
+ public async Task UpdateAsync(long id, AdminUserInput input)
+ {
+ _logger.LogInformation("正在更新管理员,ID: {Id}", id);
+
+ var adminUser = await _adminUserRepository.GetByIdAsync(id);
+ if (adminUser == null)
+ {
+ _logger.LogWarning("未找到要更新的管理员,ID: {Id}", id);
+ throw new BusinessException("管理员不存在", 404);
+ }
+
+ // 如果用户名有变更,检查是否与其他用户重复
+ if (!string.IsNullOrWhiteSpace(input.UserName) && input.UserName != adminUser.UserName)
+ {
+ var existingUser = await _adminUserRepository.GetByUserNameAsync(input.UserName.Trim());
+ if (existingUser != null && existingUser.Id != id)
+ {
+ _logger.LogWarning("更新管理员失败,用户名已存在: {UserName}", input.UserName);
+ throw new BusinessException("用户名已存在", 400);
+ }
+
+ adminUser.UserName = input.UserName.Trim();
+ }
+
+ // 如果提供了密码,则更新密码
+ if (!string.IsNullOrWhiteSpace(input.Password))
+ {
+ adminUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword(input.Password);
+ }
+
+ if (!string.IsNullOrWhiteSpace(input.Type))
+ {
+ adminUser.Type = input.Type;
+ }
+
+ if (!string.IsNullOrWhiteSpace(input.Status))
+ {
+ adminUser.Status = input.Status;
+ }
+
+ adminUser.UpdatedBy = "System";
+ adminUser.UpdatedAt = DateTime.Now;
+
+ var result = await _adminUserRepository.UpdateAsync(adminUser);
+ if (!result)
+ {
+ _logger.LogError("管理员更新失败,ID: {Id}", id);
+ throw new BusinessException("更新管理员失败", 500);
+ }
+
+ _logger.LogInformation("管理员更新成功,ID: {Id}", id);
+
+ return MapToOutput(adminUser);
+ }
+
+ ///
+ /// 删除管理员(软删除)
+ ///
+ public async Task DeleteAsync(long id)
+ {
+ _logger.LogInformation("正在删除管理员,ID: {Id}", id);
+
+ var adminUser = await _adminUserRepository.GetByIdAsync(id);
+ if (adminUser == null)
+ {
+ _logger.LogWarning("未找到要删除的管理员,ID: {Id}", id);
+ throw new BusinessException("管理员不存在", 404);
+ }
+
+ var result = await _adminUserRepository.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 adminUser = await _adminUserRepository.GetByIdAsync(id);
+ if (adminUser == null)
+ {
+ _logger.LogWarning("未找到管理员,ID: {Id}", id);
+ throw new BusinessException("管理员不存在", 404);
+ }
+
+ return MapToOutput(adminUser);
+ }
+
+ ///
+ /// 分页查询管理员列表
+ ///
+ public async Task> GetListAsync(AdminUserQueryInput 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);
+ }
+
+ var pageResult = await _adminUserRepository.GetPageListAsync(
+ BuildQueryExpression(input),
+ input
+ );
+
+ // 转换为输出DTO
+ var outputList = pageResult.List?.Select(MapToOutput).ToList() ?? new List();
+
+ return new PageListModel
+ {
+ List = outputList,
+ TotalCount = pageResult.TotalCount,
+ PageIndex = pageResult.PageIndex,
+ PageSize = pageResult.PageSize
+ };
+ }
+
+ ///
+ /// 构建查询表达式
+ ///
+ private static Expression> BuildQueryExpression(AdminUserQueryInput input)
+ {
+ Expression> where = x => !x.IsDeleted;
+
+ if (!string.IsNullOrWhiteSpace(input.UserName))
+ {
+ var userName = input.UserName;
+ Expression> userNameCondition = x => x.UserName.Contains(userName);
+ where = Expression.Lambda>(
+ Expression.AndAlso(where.Body,
+ Expression.Invoke(userNameCondition, where.Parameters[0])),
+ where.Parameters);
+ }
+
+ if (!string.IsNullOrWhiteSpace(input.Type))
+ {
+ var type = input.Type;
+ Expression> typeCondition = x => x.Type == type;
+ where = Expression.Lambda>(
+ Expression.AndAlso(where.Body,
+ Expression.Invoke(typeCondition, where.Parameters[0])),
+ where.Parameters);
+ }
+
+ if (!string.IsNullOrWhiteSpace(input.Status))
+ {
+ var status = input.Status;
+ Expression> statusCondition = x => x.Status == status;
+ where = Expression.Lambda>(
+ Expression.AndAlso(where.Body,
+ Expression.Invoke(statusCondition, where.Parameters[0])),
+ where.Parameters);
+ }
+
+ return where;
+ }
+
+ ///
+ /// 将实体映射为输出DTO
+ ///
+ private static AdminUserOutput MapToOutput(AdminUser adminUser)
+ {
+ return new AdminUserOutput
+ {
+ Id = adminUser.Id,
+ UserName = adminUser.UserName,
+ Type = adminUser.Type,
+ Status = adminUser.Status,
+ CreatedBy = adminUser.CreatedBy,
+ CreatedAt = adminUser.CreatedAt,
+ UpdatedBy = adminUser.UpdatedBy,
+ UpdatedAt = adminUser.UpdatedAt
+ };
+ }
+}
diff --git a/QYZH.InteractiveMagazine.Service/AuthService.cs b/QYZH.InteractiveMagazine.Service/AuthService.cs
deleted file mode 100644
index eb4b569..0000000
--- a/QYZH.InteractiveMagazine.Service/AuthService.cs
+++ /dev/null
@@ -1,173 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using QYZH.InteractiveMagazine.IService;
-using QYZH.InteractiveMagazine.IService.Dto;
-using QYZH.InteractiveMagazine.Models.Common;
-using QYZH.InteractiveMagazine.Models.Settings;
-using QYZH.InteractiveMagazine.Infrastructure.Auth;
-
-namespace QYZH.InteractiveMagazine.Service;
-
-///
-/// 认证服务实现
-///
-public class AuthService : IAuthService
-{
- private readonly IConfiguration _configuration;
- private readonly ILogger _logger;
-
- ///
- /// 演示用管理员账号
- ///
- private const string DemoAccount = "admin";
-
- ///
- /// 演示用管理员密码
- ///
- private const string DemoPassword = "123456";
-
- ///
- /// 构造函数
- ///
- /// 配置
- /// 日志记录器
- public AuthService(IConfiguration configuration, ILogger logger)
- {
- _configuration = configuration;
- _logger = logger;
- }
-
- ///
- /// 用户登录
- ///
- /// 账号
- /// 密码
- /// 登录结果
- public async Task LoginAsync(string account, string password)
- {
- _logger.LogInformation("用户登录尝试,账号: {Account}", account);
-
- if (string.IsNullOrWhiteSpace(account))
- {
- throw new BusinessException("账号不能为空", 400);
- }
-
- if (string.IsNullOrWhiteSpace(password))
- {
- throw new BusinessException("密码不能为空", 400);
- }
-
- // 演示版本:硬编码验证账号密码
- if (account != DemoAccount || password != DemoPassword)
- {
- _logger.LogWarning("用户登录失败,账号: {Account}", account);
- throw new BusinessException("账号或密码错误", 401);
- }
-
- // 获取JWT配置
- var jwtSettings = GetJwtSettings();
-
- // 生成令牌
- var token = JwtHelper.GenerateToken(1, "管理员", jwtSettings);
- var refreshToken = JwtHelper.GenerateToken(1, "管理员", jwtSettings);
-
- _logger.LogInformation("用户登录成功,账号: {Account}", account);
-
- return new LoginOutput
- {
- Token = token,
- RefreshToken = refreshToken,
- UserId = 1,
- UserName = "管理员"
- };
- }
-
- ///
- /// 用户注册
- ///
- /// 注册输入参数
- /// 是否成功
- public async Task RegisterAsync(RegisterInput input)
- {
- _logger.LogInformation("用户注册尝试,账号: {Account}", input?.Account);
-
- if (input == null)
- {
- throw new BusinessException("注册参数不能为空", 400);
- }
-
- if (string.IsNullOrWhiteSpace(input.Account))
- {
- throw new BusinessException("账号不能为空", 400);
- }
-
- if (string.IsNullOrWhiteSpace(input.Password))
- {
- throw new BusinessException("密码不能为空", 400);
- }
-
- if (string.IsNullOrWhiteSpace(input.UserName))
- {
- throw new BusinessException("用户名不能为空", 400);
- }
-
- // 演示版本:模拟注册成功
- _logger.LogInformation("用户注册成功,账号: {Account}, 用户名: {UserName}", input.Account, input.UserName);
-
- return await Task.FromResult(true);
- }
-
- ///
- /// 刷新令牌
- ///
- /// 刷新令牌
- /// 新的登录结果
- public async Task RefreshTokenAsync(string refreshToken)
- {
- _logger.LogInformation("令牌刷新尝试");
-
- if (string.IsNullOrWhiteSpace(refreshToken))
- {
- throw new BusinessException("刷新令牌不能为空", 400);
- }
-
- var jwtSettings = GetJwtSettings();
-
- // 生成新的令牌
- var newToken = JwtHelper.GenerateToken(1, "管理员", jwtSettings);
- var newRefreshToken = JwtHelper.GenerateToken(1, "管理员", jwtSettings);
-
- _logger.LogInformation("令牌刷新成功");
-
- return new LoginOutput
- {
- Token = newToken,
- RefreshToken = newRefreshToken,
- UserId = 1,
- UserName = "管理员"
- };
- }
-
- ///
- /// 获取JWT配置
- ///
- /// JWT配置对象
- private JwtSettings GetJwtSettings()
- {
- var jwtSettings = _configuration.GetSection("JwtSettings").Get()
- ?? new JwtSettings
- {
- Issuer = "QYZH.InteractiveMagazine",
- Audience = "QYZH.InteractiveMagazine.Client",
- SecretKey = "QYZH_InteractiveMagazine_SecretKey_2024",
- ExpiryMinutes = 120
- };
-
- if (string.IsNullOrWhiteSpace(jwtSettings.SecretKey))
- {
- throw new BusinessException("JWT配置不完整", 500);
- }
-
- return jwtSettings;
- }
-}
diff --git a/QYZH.InteractiveMagazine.Service/BaseService.cs b/QYZH.InteractiveMagazine.Service/BaseService.cs
index b4685f2..a94d2d5 100644
--- a/QYZH.InteractiveMagazine.Service/BaseService.cs
+++ b/QYZH.InteractiveMagazine.Service/BaseService.cs
@@ -182,23 +182,19 @@ public class BaseService : IBaseService where T : class, new()
if (entity is BaseEntity baseEntity)
{
var now = DateTime.Now;
- baseEntity.CreatedTime = now;
- baseEntity.UpdatedTime = now;
+ baseEntity.CreatedAt = now;
+ baseEntity.UpdatedAt = now;
baseEntity.CreatedBy = baseEntity.CreatedBy ?? "system";
baseEntity.UpdatedBy = baseEntity.UpdatedBy ?? "system";
baseEntity.IsDeleted = false;
}
}
- ///
- /// 设置更新时的审计字段
- ///
- /// 实体对象
private static void SetAuditFieldsOnUpdate(T entity)
{
if (entity is BaseEntity baseEntity)
{
- baseEntity.UpdatedTime = DateTime.Now;
+ baseEntity.UpdatedAt = DateTime.Now;
baseEntity.UpdatedBy = baseEntity.UpdatedBy ?? "system";
}
}
diff --git a/QYZH.InteractiveMagazine.Service/QYZH.InteractiveMagazine.Service.csproj b/QYZH.InteractiveMagazine.Service/QYZH.InteractiveMagazine.Service.csproj
index c4f7b10..4bc31cd 100644
--- a/QYZH.InteractiveMagazine.Service/QYZH.InteractiveMagazine.Service.csproj
+++ b/QYZH.InteractiveMagazine.Service/QYZH.InteractiveMagazine.Service.csproj
@@ -10,6 +10,7 @@
+
diff --git a/QYZH.InteractiveMagazine.Service/WeChatMiniProgramService.cs b/QYZH.InteractiveMagazine.Service/WeChatMiniProgramService.cs
deleted file mode 100644
index 2f586d1..0000000
--- a/QYZH.InteractiveMagazine.Service/WeChatMiniProgramService.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using QYZH.InteractiveMagazine.Common.Helpers;
-using QYZH.InteractiveMagazine.IService;
-using QYZH.InteractiveMagazine.IService.Dto;
-using QYZH.InteractiveMagazine.Models.Common;
-using QYZH.InteractiveMagazine.Models.Settings;
-using QYZH.InteractiveMagazine.Infrastructure.Auth;
-
-namespace QYZH.InteractiveMagazine.Service;
-
-///
-/// 微信小程序服务实现
-///
-public class WeChatMiniProgramService : IWeChatMiniProgramService
-{
- private readonly IConfiguration _configuration;
- private readonly ILogger _logger;
- private const string Code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session";
- private const string GetPhoneNumberUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
-
- ///
- /// 构造函数
- ///
- /// 配置
- /// 日志记录器
- public WeChatMiniProgramService(IConfiguration configuration, ILogger logger)
- {
- _configuration = configuration;
- _logger = logger;
- }
-
- ///
- /// 微信登录
- ///
- /// 微信登录凭证
- /// 微信登录结果
- public async Task WeChatLoginAsync(string code)
- {
- _logger.LogInformation("微信登录尝试,code: {Code}", code);
-
- if (string.IsNullOrWhiteSpace(code))
- {
- throw new BusinessException("登录凭证不能为空", 400);
- }
-
- // 获取微信配置
- var weChatSettings = GetWeChatSettings();
-
- if (string.IsNullOrWhiteSpace(weChatSettings.AppId) || string.IsNullOrWhiteSpace(weChatSettings.AppSecret))
- {
- throw new BusinessException("微信配置不完整", 500);
- }
-
- // 演示版本:模拟调用微信code2session接口
- var openId = await SimulateCode2SessionAsync(code, weChatSettings);
-
- // 获取JWT配置并生成令牌
- var jwtSettings = GetJwtSettings();
- var token = JwtHelper.GenerateToken(1, "微信用户", jwtSettings);
-
- _logger.LogInformation("微信登录成功,openId: {OpenId}", openId);
-
- return new WeChatLoginOutput
- {
- Token = token,
- UserId = 1,
- UserName = "微信用户",
- OpenId = openId
- };
- }
-
- ///
- /// 获取手机号
- ///
- /// 获取手机号凭证
- /// 手机号信息
- public async Task GetPhoneNumberAsync(string code)
- {
- _logger.LogInformation("获取微信手机号尝试,code: {Code}", code);
-
- if (string.IsNullOrWhiteSpace(code))
- {
- throw new BusinessException("获取手机号凭证不能为空", 400);
- }
-
- // 演示版本:模拟返回手机号
- var phoneNumber = await SimulateGetPhoneNumberAsync(code);
-
- _logger.LogInformation("获取微信手机号成功");
-
- return new WeChatPhoneNumberOutput
- {
- PhoneNumber = phoneNumber
- };
- }
-
- ///
- /// 模拟调用微信code2session接口
- ///
- /// 登录凭证
- /// 微信配置
- /// openId
- private async Task SimulateCode2SessionAsync(string code, WeChatSettings weChatSettings)
- {
- // 演示版本:模拟返回openId
- // 实际实现应调用微信API:
- // var url = $"{Code2SessionUrl}?appid={weChatSettings.AppId}&secret={weChatSettings.AppSecret}&js_code={code}&grant_type=authorization_code";
- // var response = await HttpHelper.GetAsync(url);
- // if (response?.errcode == 0) return response.openid;
-
- await Task.Delay(100); // 模拟网络请求延迟
-
- return $"demo_openid_{code.GetHashCode():X}";
- }
-
- ///
- /// 模拟调用微信获取手机号接口
- ///
- /// 获取手机号凭证
- /// 手机号
- private async Task SimulateGetPhoneNumberAsync(string code)
- {
- // 演示版本:模拟返回手机号
- // 实际实现应调用微信API获取access_token,然后调用获取手机号接口:
- // var tokenUrl = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={appSecret}";
- // var tokenResponse = await HttpHelper.GetAsync(tokenUrl);
- // var accessToken = tokenResponse.access_token;
- // var phoneUrl = $"{GetPhoneNumberUrl}?access_token={accessToken}";
- // var phoneResponse = await HttpHelper.PostAsync(phoneUrl, new { code });
-
- await Task.Delay(100); // 模拟网络请求延迟
-
- return "13800138000";
- }
-
- ///
- /// 获取微信配置
- ///
- /// 微信配置对象
- private WeChatSettings GetWeChatSettings()
- {
- return _configuration.GetSection("WeChatSettings").Get()
- ?? new WeChatSettings
- {
- AppId = "demo_app_id",
- AppSecret = "demo_app_secret"
- };
- }
-
- ///
- /// 获取JWT配置
- ///
- /// JWT配置对象
- private JwtSettings GetJwtSettings()
- {
- return _configuration.GetSection("JwtSettings").Get()
- ?? new JwtSettings
- {
- Issuer = "QYZH.InteractiveMagazine",
- Audience = "QYZH.InteractiveMagazine.Client",
- SecretKey = "QYZH_InteractiveMagazine_SecretKey_2024",
- ExpiryMinutes = 120
- };
- }
-}
diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/AdminController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/AdminController.cs
new file mode 100644
index 0000000..f2e9623
--- /dev/null
+++ b/QYZH.InteractiveMagazine.WebApi/Controllers/AdminController.cs
@@ -0,0 +1,205 @@
+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;
+using QYZH.InteractiveMagazine.Models.Enum;
+
+namespace QYZH.InteractiveMagazine.WebApi.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class AdminController : BaseController
+{
+ private readonly IAdminAuthService _adminAuthService;
+ private readonly IAdminUserService _adminUserService;
+ private readonly ILogger _logger;
+
+ public AdminController(IAdminAuthService adminAuthService, IAdminUserService adminUserService, ILogger logger)
+ {
+ _adminAuthService = adminAuthService;
+ _adminUserService = adminUserService;
+ _logger = logger;
+ }
+
+ [AllowAnonymous]
+ [HttpPost("login")]
+ public async Task> LoginAsync([FromBody] AdminLoginInput input)
+ {
+ var result = await _adminAuthService.LoginAsync(input);
+ return Success(result);
+ }
+
+ [HttpPost("logout")]
+ public async Task> LogoutAsync()
+ {
+ var userId = GetCurrentUserId();
+ if (userId == null)
+ {
+ return Fail("未获取到用户信息", 401);
+ }
+
+ await _adminAuthService.LogoutAsync(userId.Value);
+ return Success(new object(), "登出成功");
+ }
+
+ [HttpGet("info")]
+ public async Task> GetAdminInfoAsync()
+ {
+ var userId = GetCurrentUserId();
+ if (userId == null)
+ {
+ return BaseResponse.Fail("未获取到用户信息", 401);
+ }
+
+ var result = await _adminAuthService.GetAdminInfoAsync(userId.Value);
+ return Success(result);
+ }
+
+ [HttpPost("changePassword")]
+ public async Task> ChangePasswordAsync([FromBody] ChangePasswordInput input)
+ {
+ var userId = GetCurrentUserId();
+ if (userId == null)
+ {
+ return Fail("未获取到用户信息", 401);
+ }
+
+ await _adminAuthService.ChangePasswordAsync(userId.Value, input.OldPassword, input.NewPassword);
+ return Success(new object(), "密码修改成功");
+ }
+
+ ///
+ /// 创建管理员
+ ///
+ /// 管理员输入
+ /// 创建的管理员信息
+ [HttpPost("users")]
+ public async Task> CreateUserAsync([FromBody] AdminUserInput input)
+ {
+ try
+ {
+ var result = await _adminUserService.CreateAsync(input);
+ return Success(result, "创建管理员成功");
+ }
+ catch (BusinessException ex)
+ {
+ _logger.LogWarning(ex, "创建管理员业务异常: {Message}", ex.Message);
+ return BaseResponse.Fail(ex.Message, ex.Code);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "创建管理员系统异常,参数:{Input}", input);
+ return BaseResponse.Fail("创建管理员失败,请稍后重试", 500);
+ }
+ }
+
+ ///
+ /// 更新管理员
+ ///
+ /// 管理员ID
+ /// 管理员输入
+ /// 更新后的管理员信息
+ [HttpPut("users/{id}")]
+ public async Task> UpdateUserAsync(long id, [FromBody] AdminUserInput input)
+ {
+ try
+ {
+ var result = await _adminUserService.UpdateAsync(id, input);
+ return Success(result, "更新管理员成功");
+ }
+ catch (BusinessException ex)
+ {
+ _logger.LogWarning(ex, "更新管理员业务异常: {Message}", ex.Message);
+ return BaseResponse.Fail(ex.Message, ex.Code);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新管理员系统异常,ID:{Id},参数:{Input}", id, input);
+ return BaseResponse.Fail("更新管理员失败,请稍后重试", 500);
+ }
+ }
+
+ ///
+ /// 删除管理员
+ ///
+ /// 管理员ID
+ /// 操作结果
+ [HttpDelete("users/{id}")]
+ public async Task> DeleteUserAsync(long id)
+ {
+ try
+ {
+ await _adminUserService.DeleteAsync(id);
+ return Success(new object(), "删除管理员成功");
+ }
+ catch (BusinessException ex)
+ {
+ _logger.LogWarning(ex, "删除管理员业务异常: {Message}", ex.Message);
+ return BaseResponse
+
+
+
+
diff --git a/QYZH.InteractiveMagazine.WebApi/appsettings.json b/QYZH.InteractiveMagazine.WebApi/appsettings.json
index c6c73cc..250560b 100644
--- a/QYZH.InteractiveMagazine.WebApi/appsettings.json
+++ b/QYZH.InteractiveMagazine.WebApi/appsettings.json
@@ -1,23 +1,23 @@
{
"ConnectionStrings": {
- "DefaultConnection": "Server=localhost;Database=interactive_magazine;Uid=root;Pwd=root;Charset=utf8mb4;"
+ "DefaultConnection": "server=192.168.20.150;port=13306;database=InteractiveMagazine;user=user;password=n68792bu!y99r905;charset=utf8mb4;"
},
"JwtSettings": {
"Issuer": "QYZH.InteractiveMagazine",
- "Audience": "QYZH.InteractiveMagazine.Client",
- "SecretKey": "your-256-bit-secret-key-here-change-in-production",
+ "Audience": "QYZH.InteractiveMagazine",
+ "SecretKey": "zG7pLqR9xVw2bN8fYtHk3mPc5sA1dF6eUjW4gXhC7vB",
"ExpiryMinutes": 120
},
"RedisSettings": {
- "ConnectionString": "localhost:6379",
+ "ConnectionString": "192.168.20.150:16379,defaultDatabase=5",
"InstanceName": "interactive_magazine"
},
"RabbitMQSettings": {
- "HostName": "localhost",
+ "HostName": "192.168.20.150",
"Port": 5672,
- "UserName": "guest",
- "Password": "guest",
- "VirtualHost": "/"
+ "UserName": "smartschool",
+ "Password": "@ss%&*otz%d*pq2S",
+ "VirtualHost": "InteractiveMagazine"
},
"WeChatSettings": {
"AppId": "your-wechat-appid",