From 1c6d9a55e5897bdead62b2a5e6a53e65c6c7c16d Mon Sep 17 00:00:00 2001 From: zhangxiaofeng Date: Thu, 21 Dec 2023 16:40:46 +0800 Subject: [PATCH] =?UTF-8?q?AFR=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/djy.Model/ResponseModel.cs | 148 +++++++++ web/djy_AfrApi/Controllers/AfrController.cs | 55 ++++ web/djy_AfrApi/Controllers/ApiBase.cs | 301 ++++++++++++++++++ .../Filter/GlobalExceptionsFilter.cs | 58 ++++ web/djy_AfrApi/HttpContextUser/AspNetUser.cs | 42 +++ web/djy_AfrApi/HttpContextUser/IUser.cs | 13 + .../ExceptionHandlerMiddleware.cs | 60 ++++ .../Milldlewares/MiddlewareHelpers.cs | 38 +++ .../NextAuthorizationMiddleware.cs | 63 ++++ .../Milldlewares/RequRespLogMiddleware.cs | 102 ++++++ .../Milldlewares/UnifyResultMiddleware.cs | 37 +++ web/djy_AfrApi/NLog.config | 64 ++++ web/djy_AfrApi/Program.cs | 29 ++ web/djy_AfrApi/Properties/launchSettings.json | 30 ++ web/djy_AfrApi/Startup.cs | 161 ++++++++++ web/djy_AfrApi/appsettings.Development.json | 68 ++++ web/djy_AfrApi/appsettings.Production.json | 69 ++++ web/djy_AfrApi/appsettings.Staging.json | 68 ++++ web/djy_AfrApi/appsettings.json | 1 + web/djy_AfrApi/djy_AfrApi.csproj | 34 ++ web/djy_AfrApi/djy_AfrApi.csproj.user | 11 + web/djy_IsfApi/MytMiddleware.cs | 15 +- web/djy_ams.sln | 9 +- 23 files changed, 1473 insertions(+), 3 deletions(-) create mode 100644 web/djy.Model/ResponseModel.cs create mode 100644 web/djy_AfrApi/Controllers/AfrController.cs create mode 100644 web/djy_AfrApi/Controllers/ApiBase.cs create mode 100644 web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs create mode 100644 web/djy_AfrApi/HttpContextUser/AspNetUser.cs create mode 100644 web/djy_AfrApi/HttpContextUser/IUser.cs create mode 100644 web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs create mode 100644 web/djy_AfrApi/Milldlewares/MiddlewareHelpers.cs create mode 100644 web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs create mode 100644 web/djy_AfrApi/Milldlewares/RequRespLogMiddleware.cs create mode 100644 web/djy_AfrApi/Milldlewares/UnifyResultMiddleware.cs create mode 100644 web/djy_AfrApi/NLog.config create mode 100644 web/djy_AfrApi/Program.cs create mode 100644 web/djy_AfrApi/Properties/launchSettings.json create mode 100644 web/djy_AfrApi/Startup.cs create mode 100644 web/djy_AfrApi/appsettings.Development.json create mode 100644 web/djy_AfrApi/appsettings.Production.json create mode 100644 web/djy_AfrApi/appsettings.Staging.json create mode 100644 web/djy_AfrApi/appsettings.json create mode 100644 web/djy_AfrApi/djy_AfrApi.csproj create mode 100644 web/djy_AfrApi/djy_AfrApi.csproj.user diff --git a/web/djy.Model/ResponseModel.cs b/web/djy.Model/ResponseModel.cs new file mode 100644 index 0000000..656b3e2 --- /dev/null +++ b/web/djy.Model/ResponseModel.cs @@ -0,0 +1,148 @@ +using NPOI.SS.Formula.Functions; + +namespace djy.Model +{ + /// + /// 通用返回信息类 + /// + public class MessageModel + { + /// + /// 状态码 + /// + public int code { get; set; } = 200; + /// + /// 操作是否成功 + /// + public bool success { get; set; } = false; + /// + /// 返回信息 + /// + public string message { get; set; } = ""; + /// + /// 开发者信息 + /// + public string msgDev { get; set; } + /// + /// 返回数据集合 + /// + public T data { get; set; } + + /// + /// 返回成功 + /// + /// 消息 + /// + public static MessageModel Success(string msg) + { + return Message(true, msg, default); + } + /// + /// 返回成功 + /// + /// 消息 + /// 数据 + /// + public static MessageModel Success(string msg, T data) + { + return Message(true, msg, data); + } + /// + /// 返回失败 + /// + /// 消息 + /// + public static MessageModel Fail(string msg) + { + return Message(false, msg, default); + } + /// + /// 返回失败 + /// + /// 消息 + /// 数据 + /// + public static MessageModel Fail(string msg, T data) + { + return Message(false, msg, data); + } + /// + /// 返回消息 + /// + /// 失败/成功 + /// 消息 + /// 数据 + /// + private static MessageModel Message(bool success, string msg, T data) + { + return new MessageModel() { message = msg, data = data, success = success }; + } + } + /// + /// + /// + public class MessageModel + { + /// + /// 状态码 + /// + public int code { get; set; } = 200; + /// + /// 操作是否成功 + /// + public bool success { get; set; } = false; + /// + /// 返回信息 + /// + public string message { get; set; } = ""; + /// + /// 返回数据集合 + /// + public object data { get; set; } + + + /// + /// 返回成功 + /// + /// 消息 + /// + public static MessageModel Success(string msg) + { + return Message(true, msg, default); + } + + /// + /// 返回成功 + /// + /// 消息 + /// 数据 + /// + public static MessageModel Success(string msg, object data) + { + return Message(true, msg, data); + } + + /// + /// 返回失败 + /// + /// 消息 + /// + public static MessageModel Fail(string msg) + { + return Message(false, msg, default); + } + + /// + /// 返回消息 + /// + /// 失败/成功 + /// 消息 + /// 数据 + /// + private static MessageModel Message(bool success, string msg, object data) + { + return new MessageModel() { message = msg, data = data, success = success }; + } + } + +} diff --git a/web/djy_AfrApi/Controllers/AfrController.cs b/web/djy_AfrApi/Controllers/AfrController.cs new file mode 100644 index 0000000..c8f6bb6 --- /dev/null +++ b/web/djy_AfrApi/Controllers/AfrController.cs @@ -0,0 +1,55 @@ +using Common.Utilities; +using djy.Model; +using djy_AfrApi.HttpContextUser; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace djy_AfrApi.Controllers +{ + public class AfrController : ApiBase + { + private readonly ILogger logger; + private readonly IUser user; + private readonly ILogger bigLogger; + + public AfrController(ILogger logger, ILoggerFactory loggerFactory, IUser user) + { + this.logger = logger; + this.user = user; + + // 获取ILogger对象 + bigLogger = loggerFactory.CreateLogger("BigDataLogger"); // 通过指定名称获取ILogger对象 + } + + #region 查询接口 + [HttpGet("Load")] + public async Task Load() + { + return MessageModel.Success("12312", new { ff = "11", fff = "22" }); + } + + [HttpGet("[action]")] + [AllowAnonymous] + public async Task Reveive() + { + var r = MessageModel.Success("555", new { ff = "44", fff = "33" }); + return r; + } + #endregion + + #region 新增/编辑 + + #endregion + + #region 删除 + + #endregion + + #region 第三方接口 + + #endregion + } +} diff --git a/web/djy_AfrApi/Controllers/ApiBase.cs b/web/djy_AfrApi/Controllers/ApiBase.cs new file mode 100644 index 0000000..629f2e0 --- /dev/null +++ b/web/djy_AfrApi/Controllers/ApiBase.cs @@ -0,0 +1,301 @@ +using Common; +using Common.DJYModel; +using Common.Extensions; +using Common.Tools; +using djy.IService.Djy; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Linq; + +namespace djy_AfrApi.Controllers +{ + + /// + /// api接口基类 + /// + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class ApiBase : Controller + { + #region http数据获取 + + + /// + /// 创建日志 + /// + /// 内容 + /// + /// + /// + /// + protected void _LogsAdd(string Message, string GroupName = null, object DataJson = null, string ObjGid = null, object ResultJson = null) + { + var toolsserver = IOC.AddServer(); + toolsserver.LogsAdd(Message, GroupName, DataJson, ObjGid, ResultJson); + } + + /// + /// 获取大简云设置权限 + /// + /// keyname标识 + /// + protected DJyUserAuthorityDto GetDJyUserAuthority(string KeyName) { + var _djyserver = IOC.AddServer(); + var rs= _djyserver.GetUserAuthority(GetLoginId,KeyName); + if (rs.Status) + { + return rs.Data; + } + else { + return null; + } + } + /// + /// 获取登录详情信息 + /// + /// + protected User GetUserInfo(Guid? UserGid = null) + { + var _suser = IOC.AddServer(); + if (UserGid == null) + { UserGid = GetLoginId; } + var rs = _suser.GetUserInfo(UserGid.ToString()); + if (rs.Status) + { + return rs.Data; + } + else + { return null; } + } + + /// + /// 统一获取大简云权限查询权限的userid companyid 没有权限则指定userid和companyid 为不存的guid值 + /// + /// + /// + /// 模块keyname标识 + /// 0 查询查看权限 1 操作更新权限 默认 0 + /// + protected UserAuthorityDto GetUserAuthorityToFormDto(string KeyName,int type=0 ) { + //本人的绑定UserId 全部 userid 和 compayid不做绑定 注册公司的 绑定 companyid 没有权限则指定userid和companyid 为不存的guid值 + var RetrunData = new UserAuthorityDto(); + var _djyserver = IOC.AddServer(); + User User; + User = null; + var uuid = GetLoginId.ToString(); + var userrs = _djyserver.GetUserInfo(GetLoginId.ToString()); + + var notguid = Guid.Parse("00000000-0000-0000-0000-000000000001"); + + RetrunData.CompayId = null; + + if (userrs.Status) + { + User = userrs.Data; + RetrunData.CompayId = Guid.Parse(User.CompId); + } + if (User == null) + { + RetrunData.UserId = notguid; + RetrunData.CompayId = notguid; + } + + var aut = GetDJyUserAuthority(KeyName); + if (aut != null) + {//根据权限处理 _userid 和 _companyid 值 + RetrunData.IsPower = true; + var _useraut= aut.Visiblerange; + if (type == 1) { + _useraut = aut.Operaterange; + } + switch (_useraut) { + + case 0://全部 + RetrunData.UserId = null; + RetrunData.CompayId = null; + break; + case 1://本公司 + RetrunData.UserId = null; + RetrunData.CompayId = Guid.Parse(User.CompId); + break; + case 3://本人 + RetrunData.UserId = GetLoginId; + RetrunData.CompayId = null; + break; + case 4://无权限或或默认权限 + //_uerid = notguid; + //_companyid = notguid; + RetrunData.UserId = null; + RetrunData.CompayId = null; + break; + + case 7://注册公司 + RetrunData.UserId = null; + RetrunData.CompayId = Guid.Parse( User.CompId); + break; + default: + RetrunData.UserId = notguid; + RetrunData.CompayId = notguid; + break; + + } + } + else + { + RetrunData.UserId = GetLoginId; + RetrunData.CompayId = null; + RetrunData.IsPower = true; + } + + //if (sysOptionConfig.Webconfig.IsDev) + //{ + // RetrunData.UserId = null; + // RetrunData.CompayId =null; + // RetrunData.IsPower = true; + //} + + return RetrunData; + } + + + + /// + /// 获取登录Id + /// + protected Guid? GetLoginId { get { return Guid.Parse(GetClaimsValue("loginid")); } } + + /// + /// 获取登录类型 + /// + protected EnumUser.UserType GetLoginType + { + get + { + var type = GetClaimsValue("logintype"); + if (type.Isint()) + { + return (EnumUser.UserType)int.Parse(type); + } + else { return EnumUser.UserType.All; } + + } + } + + /// + /// 根据key获取claims值 没有则返回null + /// + /// + /// 是否是加密 + /// + protected string GetClaimsValue(string Key, bool IsDecrtypt = true) + { + try + { + var claims = HttpContext.User.Claims; + + var id = claims.SingleOrDefault(s => s.Type == Key); + if (id == null) + { + return "0"; + } + + return IsDecrtypt ? _DecryptDES(id.Value) : id.Value; + } + catch { + return null; + } + + } + /// + /// DES解密 + /// + /// + /// + protected static string _DecryptDES(string value) + { + return SafeTools.DecryptDES(value, sysOptionConfig.Webconfig.DesKey); + } + #endregion + } + + + /// + ///api接口基类 + /// + /// 接口类型比如 Iservice + public class ApiBase : ApiBase + { + /// + /// + /// + protected IS _server = IOC.AddServer(); + + /// + /// 执行指定的方法 + /// + /// 方法名称 + /// 参数对象队列 + /// + protected object _InvokeServer(string methodName, object[] parameters) + { + return _server.GetType().GetMethod(methodName).Invoke(_server, parameters); + + } + } + + /// + /// api接口基类 + /// + /// 接口类型比如 Iservice + /// Dto Model + /// Tables数据表model + public class ApiBase : ApiBase + { + /// + /// 根据Id获取实体 + /// + /// + /// + protected virtual object _GetId(long Id) + { + return _InvokeServer("GetId", new object[] { Id }); + } + /// + ///基础的创建接口 提交创建对象 + /// + /// + /// + protected virtual object _Add(T Dto) + { + return _InvokeServer("Add", new object[] { Dto }); + } + + /// + /// 最基础的更新接口 传递要更新的数据对象 必须有Id + /// + /// + /// + protected virtual object _Up(T Dto) + { + return _InvokeServer("Up", new object[] { Dto, null, null }); + } + + /// + /// 最基础的删除接口 [1,2,3] + /// + /// + /// + protected virtual object _Del(long[] Idlist) + { + return _InvokeServer("Del", new object[] { Idlist }); + } + + + } + + + +} diff --git a/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs b/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs new file mode 100644 index 0000000..0bacec7 --- /dev/null +++ b/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs @@ -0,0 +1,58 @@ +using Common.Helpers; +using Common.Utilities; +using djy.Service.DjyService; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; + +namespace djy_AfrApi.Filter +{ + public class GlobalExceptionsFilter : IExceptionFilter + { + private readonly IHostEnvironment env; + private readonly ILogger _logger; + + public GlobalExceptionsFilter(IHostEnvironment env, ILogger logger) + { + this.env = env; + this._logger = logger; + } + + public void OnException(ExceptionContext context) + { + var json = new Response + { + Message = context.Exception.Message,//错误信息 + Code = 500 //500异常 + }; + //if (env.IsDevelopment()) + //{ + // json.MessageDev = context.Exception.StackTrace;//堆栈信息 + //} + var res = new ContentResult(); + res.Content = JsonConvert.SerializeObject(json); + + context.Result = res; + + //进行错误日志记录 + _logger.LogError(WriteLog("GlobalExceptionsFilter中捕获的全局异常", context.Exception)); + } + + /// + /// 自定义返回格式 + /// + /// + /// + /// + public string WriteLog(string throwMsg, Exception ex) + { + return string.Format("\r\n【自定义错误】:{0} \r\n【异常类型】:{1} \r\n【异常信息】:{2} \r\n【堆栈调用】:{3}\r\n自定义异常结束", new object[] { throwMsg, + ex.GetType().Name, ex.Message, ex.StackTrace }); + } + } +} diff --git a/web/djy_AfrApi/HttpContextUser/AspNetUser.cs b/web/djy_AfrApi/HttpContextUser/AspNetUser.cs new file mode 100644 index 0000000..fd022aa --- /dev/null +++ b/web/djy_AfrApi/HttpContextUser/AspNetUser.cs @@ -0,0 +1,42 @@ +using Common.DJYModel; +using djy.IService.Djy; +using Microsoft.AspNetCore.Http; +using System; +using System.Linq; + +namespace djy_AfrApi.HttpContextUser +{ + public class AspNetUser : IUser + { + private readonly IHttpContextAccessor accessor; + private readonly IDjyUserService userService; + + public AspNetUser(IHttpContextAccessor accessor, IDjyUserService userService) + { + this.accessor = accessor; + this.userService = userService; + } + public User CurrentUser + { + get + { + var user = accessor.HttpContext.Items["CurrentUser"] as User; + if (user != null) + { + accessor.HttpContext.Items["CurrentUser"] = user; + return user; + } + + var userId = accessor.HttpContext.User?.Claims?.FirstOrDefault(c => c.Type == "loginid")?.Value; + var user2 = userService.GetUserInfo(userId); + if (user2.Data != null) return user2.Data; + + throw new Exception("获取当前登录用户时发生异常"); + } + } + public string CompId => CurrentUser.CompId; + public string CompName => CurrentUser.COMNAME; + public string GID => CurrentUser.GID; + public string ShowName => CurrentUser.SHOWNAME; + } +} \ No newline at end of file diff --git a/web/djy_AfrApi/HttpContextUser/IUser.cs b/web/djy_AfrApi/HttpContextUser/IUser.cs new file mode 100644 index 0000000..ef9bc2c --- /dev/null +++ b/web/djy_AfrApi/HttpContextUser/IUser.cs @@ -0,0 +1,13 @@ +using Common.DJYModel; + +namespace djy_AfrApi.HttpContextUser +{ + public interface IUser + { + User CurrentUser { get; } + string CompName { get; } + string CompId { get; } + string GID { get; } + string ShowName { get; } + } +} \ No newline at end of file diff --git a/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs b/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs new file mode 100644 index 0000000..81bbca7 --- /dev/null +++ b/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs @@ -0,0 +1,60 @@ +using djy.Model; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System; +using System.Net; +using System.Threading.Tasks; + +namespace djy_AfrApi.Middlewares +{ + public class ExceptionHandlerMiddleware + { + private readonly RequestDelegate _next; + + public ExceptionHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + try + { + //_ = int.Parse(""); + await _next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception e) + { + if (e == null) return; + // TODO :记录日志 + + await WriteExceptionAsync(context, e).ConfigureAwait(false); + } + + private static async Task WriteExceptionAsync(HttpContext context, Exception e) + { + var message = e.Message; + switch (e) + { + case UnauthorizedAccessException: + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + break; + default: + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + break; + } + + context.Response.ContentType = "application/json"; + + await context.Response + .WriteAsync(JsonConvert.SerializeObject(new MessageModel() { code = 500, message = message })) + .ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/web/djy_AfrApi/Milldlewares/MiddlewareHelpers.cs b/web/djy_AfrApi/Milldlewares/MiddlewareHelpers.cs new file mode 100644 index 0000000..1a1c303 --- /dev/null +++ b/web/djy_AfrApi/Milldlewares/MiddlewareHelpers.cs @@ -0,0 +1,38 @@ +using djy_AfrApi.Milldlewares; +using Microsoft.AspNetCore.Builder; + +namespace djy_AfrApi.Middlewares +{ + public static class MiddlewareHelpers + { + /// + /// 异常处理 + /// + public static IApplicationBuilder UseExceptionHandlerMiddle(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } + + /// + /// 处理特殊情况下的响应格式(401、403) + /// + public static IApplicationBuilder UseUnifyResultMiddleware(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } + + /// + /// 二次验证授权,并保存当前登录人User对象(为了和ISF、AMS逻辑保持一致) + /// + public static IApplicationBuilder UseNextAuthorizationMiddle(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } + + // 记录请求和响应数据 + public static IApplicationBuilder UseRequRespLogMiddleware(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } + } +} diff --git a/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs b/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs new file mode 100644 index 0000000..b59a9f1 --- /dev/null +++ b/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs @@ -0,0 +1,63 @@ +using Common.DJYModel; +using Common.Utilities; +using djy.IService.Djy; +using djy.Model; +using djy.Service; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace djy_AfrApi.Milldlewares +{ + public class NextAuthorizationMiddleware + { + private readonly RequestDelegate _next; + private readonly IDjyUserService _userService; + + public NextAuthorizationMiddleware(RequestDelegate next, IDjyUserService userService) + { + _next = next; + _userService = userService; + } + public async Task InvokeAsync(HttpContext context) + { + var endpoint = context.GetEndpoint(); + if (endpoint?.Metadata.GetMetadata() == null) + { + // 因为ISF/AMS这步验证始终都无效,所以这里先不做验证 + //if (context.Request.Path.Value.Contains("/Load")) + //{ + // var userId = context.User?.Claims?.FirstOrDefault(c => c.Type == "loginid")?.Value; + // var aut = _userService.GetUserAuthority(Guid.Parse(userId), "modAfrList"); + //} + + var userId = context.User?.Claims?.FirstOrDefault(c => c.Type == "loginid")?.Value; + var user = _userService.GetUserInfo(userId); + if (user.Data == null) + { + MessageModel result = new MessageModel() + { + code = 401, + message = "登录过期(未查询到此用户),请重新登录!" + }; + + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync(JsonConvert.SerializeObject(result)).ConfigureAwait(false); + } + else + { + context.Items["CurrentUser"] = user.Data; + await _next(context); + } + } + else + { + await _next(context); + } + } + } +} diff --git a/web/djy_AfrApi/Milldlewares/RequRespLogMiddleware.cs b/web/djy_AfrApi/Milldlewares/RequRespLogMiddleware.cs new file mode 100644 index 0000000..7249f37 --- /dev/null +++ b/web/djy_AfrApi/Milldlewares/RequRespLogMiddleware.cs @@ -0,0 +1,102 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; + +namespace djy_AfrApi.Milldlewares +{ + public class RequRespLogMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private Stopwatch _stopwatch; + + public RequRespLogMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) + { + _next = next; + _logger = loggerFactory.CreateLogger("RequRespLogger"); + _stopwatch = new Stopwatch(); + } + public async Task InvokeAsync(HttpContext context) + { + if (context.Request.Path.Value.Contains("api")) + { + _stopwatch.Restart(); + + context.Request.EnableBuffering(); + + //// 存储请求数据 + var request = context.Request; + var sr = new StreamReader(request.Body); + RequestLogInfo requestResponse = new RequestLogInfo() + { + Path = request.Path, + QueryString = request.QueryString.ToString(), + BodyData = await sr.ReadToEndAsync() + }; + var content = JsonConvert.SerializeObject(requestResponse); + + if (!string.IsNullOrEmpty(content)) + { + _logger.LogInformation($"请求 - {context.TraceIdentifier}{Environment.NewLine}{content}"); + request.Body.Position = 0; + } + + // 存储响应数据 + using (MemoryStream memoryStream = new MemoryStream()) + { + // 使用自定义的响应流,将所有写入重定向到内存流 + var originalBodyStream = context.Response.Body; + context.Response.Body = memoryStream; + + try + { + await _next(context); + + _stopwatch.Stop(); + // 从内存流读取响应内容 + memoryStream.Seek(0, SeekOrigin.Begin); + string responseBody = new StreamReader(memoryStream).ReadToEnd(); + + // 记录响应内容 + _logger.LogInformation($"响应 - {context.TraceIdentifier} - {_stopwatch.ElapsedMilliseconds}ms{Environment.NewLine}StatusCode:{context.Response.StatusCode}{Environment.NewLine}{responseBody}"); + + // 将响应内容写回原始响应流 + memoryStream.Seek(0, SeekOrigin.Begin); + await memoryStream.CopyToAsync(originalBodyStream); + } + finally + { + // 恢复原始响应流 + context.Response.Body = originalBodyStream; + } + } + } + else + { + await _next(context); + } + } + } + + class RequestLogInfo + { + /// + /// 请求地址 + /// + public string Path { get; set; } + + /// + /// 请求参数 + /// + public string QueryString { get; set; } + + /// + /// Body参数 + /// + public string BodyData { get; set; } + } +} diff --git a/web/djy_AfrApi/Milldlewares/UnifyResultMiddleware.cs b/web/djy_AfrApi/Milldlewares/UnifyResultMiddleware.cs new file mode 100644 index 0000000..a3eab7f --- /dev/null +++ b/web/djy_AfrApi/Milldlewares/UnifyResultMiddleware.cs @@ -0,0 +1,37 @@ +using djy.Model; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System.Threading.Tasks; + +namespace djy_AfrApi.Milldlewares +{ + /// + /// 处理特殊情况下的响应格式(401、403) + /// + public class UnifyResultMiddleware + { + private readonly RequestDelegate _next; + public UnifyResultMiddleware(RequestDelegate next) + { + _next = next; + } + public async Task InvokeAsync(HttpContext context) + { + await _next(context); + + if (context.Response.StatusCode == 401 || context.Response.StatusCode == 403) + { + context.Response.ContentType = "application/json"; + + await context.Response + .WriteAsync(JsonConvert.SerializeObject( + new MessageModel() + { + code = context.Response.StatusCode, + message = "授权验证失败或已过期,请重新登录!" + })) + .ConfigureAwait(false); + } + } + } +} diff --git a/web/djy_AfrApi/NLog.config b/web/djy_AfrApi/NLog.config new file mode 100644 index 0000000..08ce953 --- /dev/null +++ b/web/djy_AfrApi/NLog.config @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO Log(Timestamp,Level,Message,Action,Amount,StackTrace) VALUES(@time_stamp, @level, @message, @action, @amount, @stacktrace); + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/djy_AfrApi/Program.cs b/web/djy_AfrApi/Program.cs new file mode 100644 index 0000000..189463d --- /dev/null +++ b/web/djy_AfrApi/Program.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; +using NLog.Web; + +namespace djy_AfrApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }).ConfigureLogging(logging => + { + logging.ClearProviders(); + //logging.SetMinimumLevel(LogLevel.Information); + logging.AddConsole(); + logging.AddDebug(); + }).UseNLog(); + } +} diff --git a/web/djy_AfrApi/Properties/launchSettings.json b/web/djy_AfrApi/Properties/launchSettings.json new file mode 100644 index 0000000..fa0d31b --- /dev/null +++ b/web/djy_AfrApi/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "profiles": { + "djy_AfrApi": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:30818" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:31094", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/web/djy_AfrApi/Startup.cs b/web/djy_AfrApi/Startup.cs new file mode 100644 index 0000000..3cad23c --- /dev/null +++ b/web/djy_AfrApi/Startup.cs @@ -0,0 +1,161 @@ +using Common; +using djy.Service.DjyService; +using djy_AfrApi.Filter; +using djy_AfrApi.HttpContextUser; +using djy_AfrApi.Middlewares; +using FreeRedis; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.IdentityModel.Logging; +using Microsoft.OpenApi.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using System.Linq; +using System.Reflection; + +namespace djy_AfrApi +{ + public class Startup + { + public Startup(IConfiguration configuration,IHostEnvironment hostEnvironment) + { + Configuration = configuration; + } + + 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.AddHttpContextAccessor(); + services.AddScoped(); + + services.AddControllers(o => + { + o.Filters.Add(typeof(GlobalExceptionsFilter)); + }) + .AddNewtonsoftJson(options => + { + //ѭ + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + //Ƿʹշʽkey + //options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + + //ʱʽ + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + //ModelΪnull + //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + //ñʱUTCʱ + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; + //Enumתstring + options.SerializerSettings.Converters.Add(new StringEnumConverter()); + }) + .ConfigureApiBehaviorOptions(options => + { + options.InvalidModelStateResponseFactory = context => + { + var result = new BadRequestObjectResult(context.HttpContext); + return result; + }; + }); + + services.AddHttpClient(); + + //ȡϢ + Configuration.Bind("WebConfig", sysOptionConfig.Webconfig); + sysOptionConfig._Configuration = Configuration; + + // עredisͻ + RedisClient cli = new RedisClient(sysOptionConfig.Webconfig.Redis + ",defaultDatabase=" + sysOptionConfig.Webconfig.RedisDb); + services.AddSingleton(cli); + + services.AddAuthentication("Bearer").AddJwtBearer("Bearer", option => + { + option.Authority = sysOptionConfig.Webconfig.IdentServerUrl;//ȨURlַ + option.RequireHttpsMetadata = false;//Ƿʹhttps + option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters { ValidateAudience = false }; + IdentityModelEventSource.ShowPII = true; + }); + + //ʼݿӳ + DbContext.DbBusInit(); + + ////ѭע + var ISList = Assembly.Load("djy.IService").GetTypes().Where(w => w.Name.EndsWith("Service") && w.Name.StartsWith("I")); + var Slist = Assembly.Load("djy.Service").GetTypes().Where(w => w.Name.EndsWith("Service")); + foreach (var Is in ISList) + { + var sname = Slist.FirstOrDefault(w => w.Name == Is.Name.Substring(1)); + if (sname != null) + { + services.AddTransient(Is, sname); + } + } + + services.AddAutoMapper(typeof(AutoMapperConfig)); + + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "djy_AfrApi", Version = "v1" }); + }); + IOC.container = services; + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "djy_AfrApi v1")); + } + // ¼Ӧ + app.UseRequRespLogMiddleware(); + + // µӦʽ401403 + app.UseUnifyResultMiddleware(); + + // 쳣м + app.UseExceptionHandlerMiddle(); + + app.UseRouting(); + + //DefaultFilesOptions defaultFiles = new DefaultFilesOptions(); + //defaultFiles.DefaultFileNames.Clear(); + //defaultFiles.DefaultFileNames.Add("index.html"); + //app.UseDefaultFiles(defaultFiles); + + //app.UseStaticFiles(); + + // + app.UseCors(builder => + { + builder.AllowAnyHeader(); + builder.AllowAnyMethod(); + //builder.WithOrigins("http://www.baidu.com");// + builder.AllowAnyOrigin(); + }); + + //֤ + app.UseAuthentication(); + + //Ȩ + app.UseAuthorization(); + + // ֤Ȩ浱ǰ¼UserΪ˺ISFAMS߼һ£ + app.UseNextAuthorizationMiddle(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/web/djy_AfrApi/appsettings.Development.json b/web/djy_AfrApi/appsettings.Development.json new file mode 100644 index 0000000..1d17f19 --- /dev/null +++ b/web/djy_AfrApi/appsettings.Development.json @@ -0,0 +1,68 @@ +{ + // 此文件为开发环境使用的配置文件 + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "Urls": "http://*:30818", + "WebConfig": { + "IdentServerUrl": "http://60.209.125.238:40501", + "WebId": 236, + "Area": "山东省|青岛市|市南区", + "DesKey": "2Fv6I7oV6AlCFEbN", + "jwt_Audience": "admin", + "jwt_Issuer": "shuosoft", + "jwt_Secretkey": "IBFoxBpwAW2rSDIsYiHJL5aZ3Rpr5Uaph4t6Eqm2Fv6I7oV6AlCFEbNWRXyJT653iOFIbWOcOF3sMRSSUelRSbIL6RzOCOfIk3hhxyn9Aj4HxEE08zqlRloA0CWX7MQ0", + "jwt_Expiration": 1440, + "StrFilte": "肏,妈妈,操你妈,草泥马,屌,傻逼,骚逼,王八蛋,你妈的,+,=,-,_,/,*,&,@,.,,,*", + "ShopPower": 1, + "ConnName": "sqldb", + "cache_time": 10, + "WebHostUrl": "http://127.0.0.1:30818", + "Redis": "192.168.0.80:6379,password=,defaultDatabase=12", + "RedisDb": 12, + "Rbmq_Host": "", + "Rbmq_UserName": "admin", + "Rbmq_Password": "admin", + "Rbmq_Sqlhost": "Data Source =60.209.125.238; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true", + "DapperDbString": "Data Source =60.209.125.238,32009; Initial Catalog=DevAMS; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true;", + "DataConnList": [ + { + "SysKey": "AMS", + "TitleName": "AMS", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=DevAMS; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true;" + }, + { + "SysKey": "Common", + "TitleName": "Common", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=DevCommonDB; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true;" + }, + { + "SysKey": "djydb", + "TitleName": "pingtai", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=TestDsPingTai; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true" + }, + { + "SysKey": "logsdb", + "TitleName": "日志库", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=TestDjyLogs; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true" + } + ] + } +} diff --git a/web/djy_AfrApi/appsettings.Production.json b/web/djy_AfrApi/appsettings.Production.json new file mode 100644 index 0000000..b6fd8be --- /dev/null +++ b/web/djy_AfrApi/appsettings.Production.json @@ -0,0 +1,69 @@ +{ + // 此文件为正式环境使用的配置文件 + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "Urls": "http://*:待配置待配置待配置待配置待配置待配置待配置待配置", + "WebConfig": { + "IdentServerUrl": "http://djy-identity.myshipping.net", + "WebId": 236, + "Area": "山东省|青岛市|市南区", + "DesKey": "2Fv6I7oV6AlCFEbN", + "jwt_Audience": "admin", + "jwt_Issuer": "shuosoft", + "jwt_Secretkey": "IBFoxBpwAW2rSDIsYiHJL5aZ3Rpr5Uaph4t6Eqm2Fv6I7oV6AlCFEbNWRXyJT653iOFIbWOcOF3sMRSSUelRSbIL6RzOCOfIk3hhxyn9Aj4HxEE08zqlRloA0CWX7MQ0", + "jwt_Expiration": 1440, + "StrFilte": "肏,妈妈,操你妈,草泥马,屌,傻逼,骚逼,王八蛋,你妈的,+,=,-,_,/,*,&,@,.,,,*", + "ShopPower": 1, + "ConnName": "sqldb", + "cache_time": 10, + "WebHostUrl": "https://zh-userapi.jingyiji.net", + "Redis": "172.31.85.169:6379,defaultDatabase=待配置,", + "RedisDb": "待配置", + "Rbmq_Host": "172.31.85.169:13866", + "Rbmq_UserName": "ams", + "Rbmq_Password": "djy_ams", + "Rbmq_Sqlhost": "Data Source =172.31.85.154; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true", + "DapperDbString": "Data Source =172.31.85.161; Initial Catalog=AMS; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true;", + "DataConnList": [ + { + "SysKey": "AMS", + "TitleName": "AMS", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source=172.31.85.161; Initial Catalog=AMS; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true;" + }, + { + "SysKey": "Common", + "TitleName": "Common", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source=172.31.85.161; Initial Catalog=CommonDB; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true;" + }, + { + "SysKey": "djydb", + "TitleName": "pingtai", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =172.31.85.154; Initial Catalog=DsPingTai; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" + + }, + { + "SysKey": "logsdb", + "TitleName": "日志库", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source=172.31.85.154,1433; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" + } + ] + } +} diff --git a/web/djy_AfrApi/appsettings.Staging.json b/web/djy_AfrApi/appsettings.Staging.json new file mode 100644 index 0000000..b2f34f6 --- /dev/null +++ b/web/djy_AfrApi/appsettings.Staging.json @@ -0,0 +1,68 @@ +{ + // 此文件为测试环境使用的配置文件 + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "Urls": "http://*:待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置", + "WebConfig": { + "IdentServerUrl": "http://60.209.125.238:40501", + "WebId": 236, + "Area": "山东省|青岛市|市南区", + "DesKey": "2Fv6I7oV6AlCFEbN", + "jwt_Audience": "admin", + "jwt_Issuer": "shuosoft", + "jwt_Secretkey": "IBFoxBpwAW2rSDIsYiHJL5aZ3Rpr5Uaph4t6Eqm2Fv6I7oV6AlCFEbNWRXyJT653iOFIbWOcOF3sMRSSUelRSbIL6RzOCOfIk3hhxyn9Aj4HxEE08zqlRloA0CWX7MQ0", + "jwt_Expiration": 1440, + "StrFilte": "肏,妈妈,操你妈,草泥马,屌,傻逼,骚逼,王八蛋,你妈的,+,=,-,_,/,*,&,@,.,,,*", + "ShopPower": 1, + "ConnName": "sqldb", + "cache_time": 10, + "WebHostUrl": "http://127.0.0.1:30818", + "Redis": "192.168.0.80:6379,password=,defaultDatabase=待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置", + "RedisDb": "待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置", + "Rbmq_Host": "", + "Rbmq_UserName": "admin", + "Rbmq_Password": "admin", + "Rbmq_Sqlhost": "Data Source =60.209.125.238; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true", + "DapperDbString": "Data Source =60.209.125.238,32009; Initial Catalog=DevAMS; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true;", + "DataConnList": [ + { + "SysKey": "AMS", + "TitleName": "AMS", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=DevAMS; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true;" + }, + { + "SysKey": "Common", + "TitleName": "Common", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=DevCommonDB; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true;" + }, + { + "SysKey": "djydb", + "TitleName": "pingtai", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=TestDsPingTai; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true" + }, + { + "SysKey": "logsdb", + "TitleName": "日志库", + "Index": 100, + "DataType": 1, + "Status": 0, + "ConnString": "Data Source =60.209.125.238,32009; Initial Catalog=TestDjyLogs; Persist Security Info=True; User ID =sa; Password=sa@djy.net;pooling=true" + } + ] + } +} diff --git a/web/djy_AfrApi/appsettings.json b/web/djy_AfrApi/appsettings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/web/djy_AfrApi/appsettings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/web/djy_AfrApi/djy_AfrApi.csproj b/web/djy_AfrApi/djy_AfrApi.csproj new file mode 100644 index 0000000..2fa0869 --- /dev/null +++ b/web/djy_AfrApi/djy_AfrApi.csproj @@ -0,0 +1,34 @@ + + + + net5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/web/djy_AfrApi/djy_AfrApi.csproj.user b/web/djy_AfrApi/djy_AfrApi.csproj.user new file mode 100644 index 0000000..ad27d65 --- /dev/null +++ b/web/djy_AfrApi/djy_AfrApi.csproj.user @@ -0,0 +1,11 @@ + + + + ApiControllerEmptyScaffolder + root/Common/Api + djy_AfrApi + + + ProjectDebugger + + \ No newline at end of file diff --git a/web/djy_IsfApi/MytMiddleware.cs b/web/djy_IsfApi/MytMiddleware.cs index d84c391..5fc3163 100644 --- a/web/djy_IsfApi/MytMiddleware.cs +++ b/web/djy_IsfApi/MytMiddleware.cs @@ -75,9 +75,20 @@ namespace djy_IsfApi } response.ContentType = "application/json"; await response.WriteAsync(JsonSerializer.Serialize(rr, new JsonSerializerOptions(JsonSerializerDefaults.Web))).ConfigureAwait(false); - + /* + * 响应格式: + { + "code": 401, + "Status": false, + "message": "授权验证失败或已过期,请重新登录", + "getTime": 1703135086, + "runTime": 1238.5565, + "data": null, + "memoData": null + } + */ } - + } diff --git a/web/djy_ams.sln b/web/djy_ams.sln index 66eb4b4..f264b5f 100644 --- a/web/djy_ams.sln +++ b/web/djy_ams.sln @@ -25,7 +25,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "djy.Service", "djy.Service\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "djy_IsfApi", "djy_IsfApi\djy_IsfApi.csproj", "{B9B3283E-FA06-4B06-88F1-5680AC4E5F26}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "djy_Report", "djy_Report\djy_Report.csproj", "{A201194B-42CA-41BF-8123-7DB5D332C940}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "djy_Report", "djy_Report\djy_Report.csproj", "{A201194B-42CA-41BF-8123-7DB5D332C940}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "djy_AfrApi", "djy_AfrApi\djy_AfrApi.csproj", "{E798A4EC-13E8-4681-8DB7-CB9F3C32A3EE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -61,6 +63,10 @@ Global {A201194B-42CA-41BF-8123-7DB5D332C940}.Debug|Any CPU.Build.0 = Debug|Any CPU {A201194B-42CA-41BF-8123-7DB5D332C940}.Release|Any CPU.ActiveCfg = Release|Any CPU {A201194B-42CA-41BF-8123-7DB5D332C940}.Release|Any CPU.Build.0 = Release|Any CPU + {E798A4EC-13E8-4681-8DB7-CB9F3C32A3EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E798A4EC-13E8-4681-8DB7-CB9F3C32A3EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E798A4EC-13E8-4681-8DB7-CB9F3C32A3EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E798A4EC-13E8-4681-8DB7-CB9F3C32A3EE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -73,6 +79,7 @@ Global {263A1D6E-4453-419E-8FFB-06F3AC18AAC9} = {1E6EFB04-DE56-4879-81CB-0DDEAB09EBCB} {B9B3283E-FA06-4B06-88F1-5680AC4E5F26} = {E3B4DD84-D374-432C-AEA9-782D33748578} {A201194B-42CA-41BF-8123-7DB5D332C940} = {E3B4DD84-D374-432C-AEA9-782D33748578} + {E798A4EC-13E8-4681-8DB7-CB9F3C32A3EE} = {E3B4DD84-D374-432C-AEA9-782D33748578} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {822B0661-DA88-4792-AAF3-C508FB88AF3B}