配置数据库、redis、控制器、api、Windows服务维护等基础架构

This commit is contained in:
falcon 2020-11-03 10:05:21 +08:00
parent 23a86dc534
commit 83647bd607
17 changed files with 507 additions and 12 deletions

View File

@ -0,0 +1,2 @@
sc create StaffManagement start= auto binPath= "C:\Services\FAuth\FAuth.exe"
pause

2
Tools/StartService.bat Normal file
View File

@ -0,0 +1,2 @@
sc start StaffManagement
pause

2
Tools/StopService.bat Normal file
View File

@ -0,0 +1,2 @@
sc stop StaffManagement
pause

View File

@ -0,0 +1,2 @@
sc delete StaffManagement
pause

View File

@ -0,0 +1,41 @@
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StaffManagement.Database;
using StaffManagement.Extensions;
namespace StaffManagement.Controllers
{
/// <summary>
/// api控制器基类
/// </summary>
[Area("api")]
//[ApiController]
[Route("api/[Controller]/[Action]")]
[ServiceFilter(typeof(ApiExceptionFilterAttribute))]
[ProducesResponseType(typeof(ApiErrorResult),500)]
[ProducesResponseType(typeof(ApiErrorResult),400)]
public abstract class ApiControllerBase<LoggerType>:ControllerBase<LoggerType>
{
public ApiControllerBase(ILogger<LoggerType> logger,IServiceProvider service) : base(logger,service) {
}
/// <summary>
/// 记录保存请求和响应日志
/// </summary>
/// <typeparam name="TRequest">请求信息类型</typeparam>
/// <typeparam name="TResponse">响应信息类型</typeparam>
/// <param name="data">请求数据</param>
/// <param name="result">响应数据</param>
protected void SaveLogger<TRequest, TResponse>(TRequest data,TResponse result) {
var requestStr = this.JsonSerialize(data);
var responseStr = this.JsonSerialize(result);
var logmsg = $"{this.Prefix}\n请求消息:{requestStr}\n响应消息{responseStr}";
this.Logger.LogInformation(logmsg);
}
}
}

View File

@ -0,0 +1,79 @@
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StaffManagement.Database;
using StaffManagement.Extensions;
namespace StaffManagement.Controllers
{
/// <summary>
/// 控制器类基类
/// </summary>
//[Route("[Controller]/[Action]")]
public abstract class ControllerBase<LoggerType>:Controller
{
/// <summary>
/// 日志记录服务
/// </summary>
public ILogger Logger { get; set; }
/// <summary>
/// 服务集合
/// </summary>
public IServiceProvider Services { get; set; }
/// <summary>
/// 数据库
/// </summary>
public SMDbContext Db { get; set; }
/// <summary>
/// 缓冲帮助器
/// </summary>
public CacheHelper Cache { get; private set; }
/// <summary>
/// 获取请求的方法前缀
/// </summary>
protected string Prefix {
get {
var con = this.RouteData.Values["controller"];
var ac = this.RouteData.Values["action"];
return $":{con}:{ac}";
}
}
/// <summary>
/// 通过日志组件和服务集合生成控制器
/// </summary>
/// <param name="logger">控制器日志组件</param>
/// <param name="service">服务集合</param>
public ControllerBase(ILogger<LoggerType> logger,IServiceProvider service) {
this.Logger = logger;
this.Services = service;
this.Db = service.GetService<SMDbContext>();
this.Cache = service.GetService<CacheHelper>();
}
/// <summary>
/// 从对象序列化字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="obj">要序列化的对象</param>
/// <returns>字符串</returns>
protected string JsonSerialize<T>(T obj) {
return JsonSerializer.Serialize<T>(obj,new JsonSerializerOptions {
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
});
}
/// <summary>
/// 从字符串反序列化对象
/// </summary>
/// <typeparam name="T">对象的类型</typeparam>
/// <param name="json">json字符串</param>
/// <returns>对象实例</returns>
protected T JsonDeserialize<T>(string json) {
return JsonSerializer.Deserialize<T>(json);
}
}
}

View File

@ -7,14 +7,11 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using StaffManagement.Models;
namespace StaffManagement.Controllers
namespace StaffManagement.Controllers.Web
{
public class HomeController:Controller
public class HomeController:WebControllerBase<HomeController>
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger) {
_logger = logger;
public HomeController(ILogger<HomeController> logger,IServiceProvider service) : base(logger,service) {
}
public IActionResult Index() {

View File

@ -0,0 +1,15 @@
using System;
using Microsoft.Extensions.Logging;
namespace StaffManagement.Controllers
{
/// <summary>
/// 控制器类基类
/// </summary>
//[Route("[Controller]/[Action]")]
public abstract class WebControllerBase<LoggerType>:ControllerBase<LoggerType>
{
public WebControllerBase(ILogger<LoggerType> logger,IServiceProvider service) : base(logger,service) {
}
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
namespace StaffManagement.Database
{
/// <summary>
/// 员工管理数据上下文
/// </summary>
public class SMDbContext:DbContext
{
/// <summary>
/// 通过配置的数据库选项获取数据库上下文
/// </summary>
/// <param name="options"></param>
public SMDbContext(DbContextOptions options) : base(options) { }
}
}

View File

@ -0,0 +1,17 @@
namespace StaffManagement.Extensions
{
/// <summary>
/// api返回异常
/// </summary>
public class ApiErrorResult
{
/// <summary>
/// 异常信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 异常编号
/// </summary>
public string Id { get; set; }
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace StaffManagement.Extensions
{
/// <summary>
/// 表示请求错误,服务器无法处理
/// </summary>
public class ApiException:Exception
{
public object Param { get; set; }
/// <summary>
/// 通过提供异常信息返回api错误异常
/// </summary>
/// <param name="msg">错误消息</param>
public ApiException(string msg) : base(msg) {
this.Param = null;
}
/// <summary>
/// 通过提供异常信息返回api错误异常
/// </summary>
/// <param name="msg">错误消息</param>
/// <param name="param">请求绑定参数</param>
public ApiException(object param,string msg) : base(msg) {
this.Param = param;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
namespace StaffManagement.Extensions
{
/// <summary>
/// Api控制器返回异常
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = true,Inherited = true)]
public class ApiExceptionFilterAttribute:ExceptionFilterAttribute
{
public ILogger Logger { get; set; }
public ApiExceptionFilterAttribute(ILogger<ApiExceptionFilterAttribute> logger) {
this.Logger = logger;
}
public override void OnException(ExceptionContext context) {
var id = Guid.NewGuid().ToString("N");
int code;
var errMsg = new StringBuilder();
errMsg.Append($"错误ID{id}\n");
if(context.Exception is ApiException ae) {
code = StatusCodes.Status400BadRequest;
var rqStr = JsonSerializer.Serialize(ae.Param);
errMsg.Append($"请求参数:{rqStr}\n");
} else {
code = StatusCodes.Status500InternalServerError;
}
errMsg.Append($"异常信息:{ context.Exception}\n");
this.Logger.LogError(errMsg.ToString());
var result = new ApiErrorResult {
Message = context.Exception.Message,
Id = id,
};
context.Result = new JsonResult(result) { StatusCode = code };
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Text;
using Falcon.Extend;
using Microsoft.Extensions.Logging;
namespace StaffManagement.Extensions
{
/// <summary>
/// 缓冲帮助器
/// </summary>
public class CacheHelper
{
/// <summary>
/// 基础缓冲
/// </summary>
public ICacheProvider Cache { get; set; }
/// <summary>
/// Json序列化
/// </summary>
public IJsonProvider JsonProvider { get; set; }
/// <summary>
/// 日志记录
/// </summary>
public ILogger Logger { get; set; }
public CacheHelper(ICacheProvider c,IJsonProvider j,ILogger<CacheHelper> logger) {
this.Cache = c;
this.JsonProvider = j;
this.Logger = logger;
}
/// <summary>
/// 从缓冲中获取数据
/// </summary>
/// <typeparam name="TKey">用于生成key的对象类型</typeparam>
/// <typeparam name="T">数据对象类型</typeparam>
/// <param name="prefix">缓冲方法前缀</param>
/// <param name="keyObj">用于生成key的对象</param>
/// <returns>数据对象</returns>
public T GetT<TKey, T>(string prefix,TKey keyObj) where T : class, new() {
if(this.Cache == null) {
return null;
}
if(this.JsonProvider == null) {
return null;
}
var key = generateKey(prefix,keyObj);
if(string.IsNullOrEmpty(key)) {
return null;
}
var result = this.Cache.GetObj<T>(key);
if(result != null) {
this.Logger.LogInformation($"Get data cache by [{key}]");
}
return result;
}
/// <summary>
/// 保存缓冲
/// </summary>
/// <typeparam name="TKey">用于生成key的对象类型</typeparam>
/// <typeparam name="T">数据对象类型</typeparam>
/// <param name="prefix">缓冲方法前缀</param>
/// <param name="keyObj">用于生成key的对象</param>
/// <param name="obj">数据对象</param>
/// <param name="timeSpan">缓冲时间</param>
/// <returns></returns>
public T SetT<TKey, T>(string prefix,TKey keyObj,T obj,TimeSpan timeSpan) where T : class {
if(this.Cache == null) {
return obj;
}
if(this.JsonProvider == null) {
return obj;
}
var key = generateKey(prefix,keyObj);
if(string.IsNullOrEmpty(key)) {
return obj;
}
this.Cache.SetCache(key,obj,timeSpan);
this.Logger.LogInformation($"Set data cache by [{key}]");
return obj;
}
/// <summary>
/// 根据对象生成Key
/// </summary>
/// <typeparam name="TK">Key的类型</typeparam>
/// <param name="prefix">前缀</param>
/// <param name="keyObj">Key对象</param>
/// <returns>对应的Key字符串</returns>
private string generateKey<TK>(string prefix,TK keyObj) {
var key = new StringBuilder();
key.Append(prefix);
foreach(var p in keyObj.GetType().GetProperties()) {
var pn = p.Name;
var pv = p.GetValue(keyObj) ?? "nul";
key.Append($":{pn}:{pv}");
}
return key.ToString();
}
}
}

View File

@ -0,0 +1,79 @@
using System;
using Microsoft.AspNetCore.CookiePolicy;
using Microsoft.AspNetCore.Http;
namespace StaffManagement.Extensions
{
/// <summary>
/// cookie帮助类
/// </summary>
public static class CookieHelper
{
/// <summary>
/// 设置Cookie值
/// </summary>
/// <param name="httpContext">http上下文</param>
/// <param name="key">Cookie的键</param>
/// <param name="value">cookie值</param>
/// <param name="minutes">cookie有效期。分钟无限传null或不传</param>
public static void SetCookies(this HttpContext httpContext,string key,string value,int? minutes = null) {
SetCookies(httpContext.Response,key,value,minutes);
}
/// <summary>
/// 设置Cookie值
/// </summary>
/// <param name="response">http响应</param>
/// <param name="key">Cookie的键</param>
/// <param name="value">cookie值</param>
/// <param name="minutes">cookie有效期。分钟无限传null或不传</param>
public static void SetCookies(this HttpResponse response,string key,string value,int? minutes = null) {
if(minutes.HasValue) {
response.Cookies.Append(key,value,new CookieOptions {
Expires = DateTime.Now.AddMinutes(minutes.Value)
});
} else {
response.Cookies.Append(key,value);
}
}
/// <summary>
/// 删除cookie
/// </summary>
/// <param name="httpContext">http上下文</param>
/// <param name="key">要删除的键</param>
public static void DeleteCookies(this HttpContext httpContext,string key) {
DeleteCookies(httpContext.Response,key);
}
/// <summary>
/// 删除cookie
/// </summary>
/// <param name="response">http响应</param>
/// <param name="key">要删除的键</param>
public static void DeleteCookies(this HttpResponse response,string key) {
response.Cookies.Delete(key);
}
/// <summary>
/// 获取cookie中的值
/// </summary>
/// <param name="httpContext">http上下文</param>
/// <param name="key">cookie的键</param>
/// <returns>保存的值</returns>
public static string GetCookiesValue(this HttpContext httpContext,string key) {
return GetCookiesValue(httpContext.Request,key);
}
/// <summary>
/// 获取cookie中的值
/// </summary>
/// <param name="request">http请求</param>
/// <param name="key">cookie的键</param>
/// <returns>保存的值</returns>
public static string GetCookiesValue(this HttpRequest request,string key) {
if(request.Cookies.TryGetValue(key,out string value)) {
return value;
}
return string.Empty;
}
}
}

View File

@ -22,6 +22,12 @@
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.5" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\Api\" />
<Folder Include="Database\Tables\" />
</ItemGroup>
</Project>

View File

@ -1,12 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using Falcon.Extend;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Redis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using StaffManagement.Database;
using StaffManagement.Extensions;
namespace StaffManagement
{
@ -18,9 +24,40 @@ namespace StaffManagement
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
services.AddControllersWithViews();
//注册Json序列化
services.AddMsJsonProvider();
//注册数据库
services.AddDbContext<SMDbContext>(option => {
//option.UseSqlServer(this.Configuration.GetValue<string>("Database:connStr"));
option.UseMySql(this.Configuration.GetValue<string>("Database:dbConnStr"));
});
//注册Redis
var rop = this.Configuration.GetSection("Redis").Get<RedisCacheOptions>();
services.AddRedis(rop);
//注册缓冲帮助器
services.AddSingleton<CacheHelper>();
//注册MVC
services.AddControllersWithViews()
.AddJsonOptions(option => {
option.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
option.JsonSerializerOptions.PropertyNamingPolicy = null;
});
//注册Swagger
services.AddSwaggerGen(c => {
var option = this.Configuration.GetSection("SwaggerDoc");
c.SwaggerDoc("V1",new OpenApiInfo {
Title = option.GetValue<string>("Title"),
Version = option.GetValue<string>("Version"),
Description = option.GetValue<string>("Description"),
});
var basePath = AppContext.BaseDirectory;
c.IncludeXmlComments(Path.Combine(basePath,"StaffManagement.xml"),true);
c.AddXmlEnumEnable(Path.Combine(basePath,"StaffManagement.xml"));
});
//注册api错误处理器
services.AddScoped<ApiExceptionFilterAttribute>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -32,6 +69,16 @@ namespace StaffManagement
}
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/V1/swagger.json","接口文档");
//c.RoutePrefix = "";
c.DocumentTitle = "一站式医疗救助接口文档";
c.InjectStylesheet("/css/swagger.css");
c.InjectJavascript("/lib/jquery/dist/jquery.min.js");
c.InjectJavascript("/js/swagger.js");
});
app.UseRouting();
app.UseAuthorization();

View File

@ -6,5 +6,21 @@
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"Database": {
"dbConnStr": "Server=localhost;Port=3306;Database=FAuth;Uid=falcon;Pwd=falcon;"
},
"Redis": {
"InstanceName": "",
"Configuration": "127.0.0.1:7001,password=123654"
},
"SwaggerDoc": {
"Title": "员工管理API接口",
"Version": "1.0",
"Description": "统一采用API接口设计查看下面详细接口规范。",
"Contact": {
"Name": "技术支持(点击链接,发布工单)",
"Url": ""
}
}
}