添加项目文件。

This commit is contained in:
glz
2026-06-01 13:42:40 +08:00
parent 435474c5fe
commit bba985f937
56 changed files with 3758 additions and 0 deletions

View File

@ -0,0 +1,173 @@
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;
/// <summary>
/// 认证服务实现
/// </summary>
public class AuthService : IAuthService
{
private readonly IConfiguration _configuration;
private readonly ILogger<AuthService> _logger;
/// <summary>
/// 演示用管理员账号
/// </summary>
private const string DemoAccount = "admin";
/// <summary>
/// 演示用管理员密码
/// </summary>
private const string DemoPassword = "123456";
/// <summary>
/// 构造函数
/// </summary>
/// <param name="configuration">配置</param>
/// <param name="logger">日志记录器</param>
public AuthService(IConfiguration configuration, ILogger<AuthService> logger)
{
_configuration = configuration;
_logger = logger;
}
/// <summary>
/// 用户登录
/// </summary>
/// <param name="account">账号</param>
/// <param name="password">密码</param>
/// <returns>登录结果</returns>
public async Task<LoginOutput> 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 = "管理员"
};
}
/// <summary>
/// 用户注册
/// </summary>
/// <param name="input">注册输入参数</param>
/// <returns>是否成功</returns>
public async Task<bool> 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);
}
/// <summary>
/// 刷新令牌
/// </summary>
/// <param name="refreshToken">刷新令牌</param>
/// <returns>新的登录结果</returns>
public async Task<LoginOutput> 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 = "管理员"
};
}
/// <summary>
/// 获取JWT配置
/// </summary>
/// <returns>JWT配置对象</returns>
private JwtSettings GetJwtSettings()
{
var jwtSettings = _configuration.GetSection("JwtSettings").Get<JwtSettings>()
?? 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;
}
}

View File

@ -0,0 +1,205 @@
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;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 基础服务实现
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
public class BaseService<T> : IBaseService<T> where T : class, new()
{
protected readonly IBaseRepository<T> _repository;
protected readonly ILogger<BaseService<T>> _logger;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="repository">基础仓储</param>
/// <param name="logger">日志记录器</param>
public BaseService(IBaseRepository<T> repository, ILogger<BaseService<T>> logger)
{
_repository = repository;
_logger = logger;
}
/// <summary>
/// 根据ID获取实体
/// </summary>
/// <param name="id">实体ID</param>
/// <returns>实体对象</returns>
public async Task<T?> GetByIdAsync(long id)
{
_logger.LogInformation("正在获取实体ID: {Id}", id);
var entity = await _repository.GetByIdAsync(id);
if (entity == null)
{
_logger.LogWarning("未找到实体ID: {Id}", id);
throw new BusinessException($"未找到ID为{id}的记录", 404);
}
return entity;
}
/// <summary>
/// 获取所有实体列表
/// </summary>
/// <returns>实体列表</returns>
public async Task<List<T>> GetListAsync()
{
_logger.LogInformation("正在获取所有实体列表");
return await _repository.GetListAsync();
}
/// <summary>
/// 获取分页列表
/// </summary>
/// <param name="pageQuery">分页查询参数</param>
/// <returns>分页数据</returns>
public async Task<PageListModel<T>> GetPageListAsync(PageQueryModel pageQuery)
{
_logger.LogInformation("正在获取分页列表,页码: {PageIndex}, 每页条数: {PageSize}", pageQuery.PageIndex, pageQuery.PageSize);
if (pageQuery.PageIndex <= 0)
{
throw new BusinessException("页码必须大于0", 400);
}
if (pageQuery.PageSize <= 0 || pageQuery.PageSize > 100)
{
throw new BusinessException("每页条数必须在1-100之间", 400);
}
return await _repository.GetPageListAsync(x => true, pageQuery);
}
/// <summary>
/// 新增实体
/// </summary>
/// <param name="entity">实体对象</param>
/// <returns>是否成功</returns>
public async Task<bool> InsertAsync(T entity)
{
if (entity == null)
{
throw new BusinessException("实体对象不能为空", 400);
}
_logger.LogInformation("正在新增实体,类型: {EntityType}", typeof(T).Name);
SetAuditFieldsOnInsert(entity);
var result = await _repository.InsertAsync(entity);
if (result)
{
_logger.LogInformation("实体新增成功");
}
else
{
_logger.LogError("实体新增失败");
throw new BusinessException("新增记录失败", 500);
}
return result;
}
/// <summary>
/// 更新实体
/// </summary>
/// <param name="entity">实体对象</param>
/// <returns>是否成功</returns>
public async Task<bool> UpdateAsync(T entity)
{
if (entity == null)
{
throw new BusinessException("实体对象不能为空", 400);
}
_logger.LogInformation("正在更新实体,类型: {EntityType}", typeof(T).Name);
SetAuditFieldsOnUpdate(entity);
var result = await _repository.UpdateAsync(entity);
if (result)
{
_logger.LogInformation("实体更新成功");
}
else
{
_logger.LogError("实体更新失败");
throw new BusinessException("更新记录失败", 500);
}
return result;
}
/// <summary>
/// 根据ID删除实体
/// </summary>
/// <param name="id">实体ID</param>
/// <returns>是否成功</returns>
public async Task<bool> DeleteByIdAsync(long id)
{
_logger.LogInformation("正在删除实体ID: {Id}", id);
var entity = await _repository.GetByIdAsync(id);
if (entity == null)
{
_logger.LogWarning("未找到要删除的实体ID: {Id}", id);
throw new BusinessException($"未找到ID为{id}的记录", 404);
}
var result = await _repository.DeleteByIdAsync(id);
if (result)
{
_logger.LogInformation("实体删除成功ID: {Id}", id);
}
else
{
_logger.LogError("实体删除失败ID: {Id}", id);
throw new BusinessException("删除记录失败", 500);
}
return result;
}
/// <summary>
/// 设置插入时的审计字段
/// </summary>
/// <param name="entity">实体对象</param>
private static void SetAuditFieldsOnInsert(T entity)
{
if (entity is BaseEntity baseEntity)
{
var now = DateTime.Now;
baseEntity.CreatedTime = now;
baseEntity.UpdatedTime = now;
baseEntity.CreatedBy = baseEntity.CreatedBy ?? "system";
baseEntity.UpdatedBy = baseEntity.UpdatedBy ?? "system";
baseEntity.IsDeleted = false;
}
}
/// <summary>
/// 设置更新时的审计字段
/// </summary>
/// <param name="entity">实体对象</param>
private static void SetAuditFieldsOnUpdate(T entity)
{
if (entity is BaseEntity baseEntity)
{
baseEntity.UpdatedTime = DateTime.Now;
baseEntity.UpdatedBy = baseEntity.UpdatedBy ?? "system";
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\QYZH.InteractiveMagazine.IService\QYZH.InteractiveMagazine.IService.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Repository\QYZH.InteractiveMagazine.Repository.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Common\QYZH.InteractiveMagazine.Common.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Models\QYZH.InteractiveMagazine.Models.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Infrastructure\QYZH.InteractiveMagazine.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.8" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,167 @@
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;
/// <summary>
/// 微信小程序服务实现
/// </summary>
public class WeChatMiniProgramService : IWeChatMiniProgramService
{
private readonly IConfiguration _configuration;
private readonly ILogger<WeChatMiniProgramService> _logger;
private const string Code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session";
private const string GetPhoneNumberUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
/// <summary>
/// 构造函数
/// </summary>
/// <param name="configuration">配置</param>
/// <param name="logger">日志记录器</param>
public WeChatMiniProgramService(IConfiguration configuration, ILogger<WeChatMiniProgramService> logger)
{
_configuration = configuration;
_logger = logger;
}
/// <summary>
/// 微信登录
/// </summary>
/// <param name="code">微信登录凭证</param>
/// <returns>微信登录结果</returns>
public async Task<WeChatLoginOutput> 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
};
}
/// <summary>
/// 获取手机号
/// </summary>
/// <param name="code">获取手机号凭证</param>
/// <returns>手机号信息</returns>
public async Task<WeChatPhoneNumberOutput> 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
};
}
/// <summary>
/// 模拟调用微信code2session接口
/// </summary>
/// <param name="code">登录凭证</param>
/// <param name="weChatSettings">微信配置</param>
/// <returns>openId</returns>
private async Task<string> 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<dynamic>(url);
// if (response?.errcode == 0) return response.openid;
await Task.Delay(100); // 模拟网络请求延迟
return $"demo_openid_{code.GetHashCode():X}";
}
/// <summary>
/// 模拟调用微信获取手机号接口
/// </summary>
/// <param name="code">获取手机号凭证</param>
/// <returns>手机号</returns>
private async Task<string> 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<dynamic>(tokenUrl);
// var accessToken = tokenResponse.access_token;
// var phoneUrl = $"{GetPhoneNumberUrl}?access_token={accessToken}";
// var phoneResponse = await HttpHelper.PostAsync<dynamic>(phoneUrl, new { code });
await Task.Delay(100); // 模拟网络请求延迟
return "13800138000";
}
/// <summary>
/// 获取微信配置
/// </summary>
/// <returns>微信配置对象</returns>
private WeChatSettings GetWeChatSettings()
{
return _configuration.GetSection("WeChatSettings").Get<WeChatSettings>()
?? new WeChatSettings
{
AppId = "demo_app_id",
AppSecret = "demo_app_secret"
};
}
/// <summary>
/// 获取JWT配置
/// </summary>
/// <returns>JWT配置对象</returns>
private JwtSettings GetJwtSettings()
{
return _configuration.GetSection("JwtSettings").Get<JwtSettings>()
?? new JwtSettings
{
Issuer = "QYZH.InteractiveMagazine",
Audience = "QYZH.InteractiveMagazine.Client",
SecretKey = "QYZH_InteractiveMagazine_SecretKey_2024",
ExpiryMinutes = 120
};
}
}