添加项目文件。

This commit is contained in:
chfei committed 2026-08-14 18:00:58 +08:00
1 parent 6a017c9547
commit 58abbb5b4f
86 files changed
+8729

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StoreHub.Framework.Commons;
using StoreHub.Framework.Security;
using StoreHub.Services;
using StoreHub.Services.Dtos;
using System.Security.Claims;
namespace StoreHub.Controllers
{
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
public class AuthController : ControllerBase
{
private readonly IUserService _userService;
private readonly ITokenService _tokenService;
private readonly ICurrentUser _currentUser;
private readonly IOnlineUserStore _onlineUserStore;
public AuthController(IUserService userService, ITokenService tokenService, ICurrentUser currentUser, IOnlineUserStore onlineUserStore)
{
_userService = userService;
_tokenService = tokenService;
_currentUser = currentUser;
_onlineUserStore = onlineUserStore;
}
/// <summary>
/// 登录:校验用户名/密码,签发 JWT(单点登录:同用户的新登录会使旧令牌失效)
/// </summary>
[HttpPost]
[AllowAnonymous]
public async Task<ApiResult<object?>> Login([FromBody] LoginRequest request)
{
if (request is null || string.IsNullOrWhiteSpace(request.UserName) || string.IsNullOrWhiteSpace(request.Password))
{
return ApiResult.Error("用户名或密码不能为空", 400);
}
UserDto? user = await _userService.LoginAsync(request.UserName, request.Password);
if (user is null)
{
return ApiResult.Error("用户名或密码错误", 401);
}
var claims = new List<Claim>
{
new(JwtClaimTypes.UserId, user.Id.ToString()),
new(JwtClaimTypes.UserName, user.UserName),
new(JwtClaimTypes.NickName, user.NickName),
};
TokenResult token = _tokenService.CreateToken(claims);
return ApiResult.Success<object?>(token);
}
/// <summary>
/// 退出登录:销毁当前用户的服务端在线会话,令牌立即失效
/// </summary>
[HttpPost]
[Authorize]
public ApiResult<object?> Logout()
{
if (_currentUser.Id is int userId)
{
_onlineUserStore.SignOut(userId);
}
return ApiResult.Success<object?>(null);
}
/// <summary>
/// 获取当前登录用户信息(需携带 Bearer Token)
/// </summary>
[HttpGet]
[Authorize]
public ApiResult<object?> Me()
{
return ApiResult.Success<object?>(new
{
UserId = _currentUser.Id,
UserName = _currentUser.UserName,
NickName = _currentUser.NickName,
});
}
/// <summary>
/// 在线人员列表(需携带 Bearer Token)
/// </summary>
[HttpGet]
[Authorize]
public ApiResult<object?> OnlineUsers()
{
IReadOnlyList<OnlineUserInfo> users = _onlineUserStore.GetOnlineUsers();
return ApiResult.Success<object?>(new
{
Count = users.Count,
Users = users,
});
}
}
public class LoginRequest
{
public string UserName { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
}
+28
View File
@@ -0,0 +1,28 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using StoreHub.Framework.Repositorys;
using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StoreHub.Controllers
{
[ApiController]
[Description("测试温度")]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UserController : ControllerBase
{
private readonly IUserRepository _userRepository;
public UserController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
[HttpGet]
public string GetUser()
{
return JsonSerializer.Serialize(_userRepository.Select.First());
}
}
}
@@ -0,0 +1,33 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using StoreHub.Framework.Commons;
using System.ComponentModel;
namespace StoreHub.Controllers
{
[ApiController]
[Description("测试温度")]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
[HttpGet(Name = "GetWeatherForecast")]
public ApiResult<IEnumerable<WeatherForecast>> Get()
{
IEnumerable<WeatherForecast> data = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
return ApiResult.Success(data);
}
}
}
@@ -0,0 +1,32 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
using StoreHub.Framework.Commons;
namespace StoreHub.Controllers.v2
{
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
[HttpGet(Name = "GetWeatherForecastV2")]
public ApiResult<IEnumerable<WeatherForecastV2>> Get()
{
IEnumerable<WeatherForecastV2> data = Enumerable.Range(1, 10).Select(index => new WeatherForecastV2
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
FeelsLikeC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
return ApiResult.Success(data);
}
}
}
+62
View File
@@ -0,0 +1,62 @@
using Asp.Versioning;
using Scalar.AspNetCore;
using StoreHub.Framework.DependencyInjection;
public partial class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// IOC 注册:自动注册 ISingletonDependency / IScopedDependency / ITransientDependency,并执行所有 IStartup(实现见 StoreHub.Framework)
builder.Services.AddStoreHub();
builder.Services.AddFreeSqlDB();
// JWT 验证:注册 JwtBearer 认证 + 令牌服务 + OpenAPI Bearer 安全方案(配置节 Jwt)
builder.Services.AddStoreHubJwt(builder.Configuration);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
}).AddMvc().AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'V";
options.SubstituteApiVersionInUrl = true;
}).AddOpenApi();
#region DB
#endregion
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi().WithDocumentPerVersion();
app.MapScalarApiReference(options =>
{
options.Title = "商铺管理API V1";
options.AddDocuments(new[] { "v1", "v2" });
});
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "scalar/v1",
"applicationUrl": "http://localhost:5295",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "scalar/v1",
"applicationUrl": "https://localhost:7241;http://localhost:5295",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
<PackageReference Include="Scalar.AspNetCore" Version="2.16.18" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StoreHub.Framework\StoreHub.Framework.csproj" />
<ProjectReference Include="..\StoreHub.KJDWebService\StoreHub.KJDWebService.csproj" />
<ProjectReference Include="..\StoreHub.Services\StoreHub.Services.csproj" />
</ItemGroup>
</Project>
+47
View File
@@ -0,0 +1,47 @@
@StoreHub_HostAddress = http://localhost:5295
# ============ 认证(JWT) ============
### 登录:返回 accessToken(单点登录,同用户新登录会使旧 token 失效)
# @name login
POST {{StoreHub_HostAddress}}/api/v1/Auth/Login
Content-Type: application/json
{
"userName": "18990011234",
"password": "18990011234"
}
### 获取当前登录用户信息(需携带 Bearer Token,先执行上面的 login)
GET {{StoreHub_HostAddress}}/api/v1/Auth/Me
Authorization: Bearer {{login.response.body.$.data.token}}
### 退出登录:销毁服务端会话,token 立即失效
POST {{StoreHub_HostAddress}}/api/v1/Auth/Logout
Authorization: Bearer {{login.response.body.$.data.token}}
### 在线人员列表(需携带 Bearer Token)
GET {{StoreHub_HostAddress}}/api/v1/Auth/OnlineUsers
Authorization: Bearer {{login.response.body.$.data.token}}
# ============ 示例接口 ============
### 模板自带的 WeatherForecast
GET {{StoreHub_HostAddress}}/api/v1/weatherforecast/
Accept: application/json
POST {{StoreHub_HostAddress}}/api/v1/Auth/Login
Content-Type: application/json
{
//LoginRequest
}
###
POST {{StoreHub_HostAddress}}/api/v1/Auth/Login
Content-Type: application/json
{
//LoginRequest
}
+13
View File
@@ -0,0 +1,13 @@
namespace StoreHub
{
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}
+15
View File
@@ -0,0 +1,15 @@
namespace StoreHub
{
public class WeatherForecastV2
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public int FeelsLikeC { get; set; }
public string? Summary { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+24
View File
@@ -0,0 +1,24 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Port=3306;Database=erp;Uid=root;Pwd=123456;",
"UserConnection": "Server=localhost;Port=3306;Database=wxxd;Uid=root;Pwd=123456;",
"KJDConnection": "Server=localhost;Port=3306;Database=mysql;Uid=root;Pwd=123456;"
},
"Jwt": {
"Issuer": "StoreHub",
"Audience": "StoreHub.Client",
"SigningKey": "StoreHub--Smxf8DtV8a5dFdaCVCXtFfWbmMRfw53ectEjr8JvhUjGpPP7fuBdRn",
"ExpiresMinutes": 120,
"ClockSkewMinutes": 5,
"ValidateIssuer": true,
"ValidateAudience": true,
"ValidateLifetime": true
},
"AllowedHosts": "*"
}