diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/CloudGaming.PayApi.csproj b/src/CloudGaming/Api/CloudGaming.PayApi/CloudGaming.PayApi.csproj new file mode 100644 index 0000000..1632ffa --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/CloudGaming.PayApi.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + Linux + ..\.. + True + + + + + + + + + + + + + + + + + + diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/CloudGaming.PayApi.http b/src/CloudGaming/Api/CloudGaming.PayApi/CloudGaming.PayApi.http new file mode 100644 index 0000000..14c96ca --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/CloudGaming.PayApi.http @@ -0,0 +1,6 @@ +@CloudGaming.PayApi_HostAddress = http://localhost:5229 + +GET {{CloudGaming.PayApi_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/Controllers/PayController.cs b/src/CloudGaming/Api/CloudGaming.PayApi/Controllers/PayController.cs new file mode 100644 index 0000000..170dd47 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/Controllers/PayController.cs @@ -0,0 +1,136 @@ +using CloudGaming.Code.Account; +using CloudGaming.Code.AppExtend; +using CloudGaming.Code.DataAccess; +using CloudGaming.Code.Payment; +using CloudGaming.DtoModel.Account.User; +using CloudGaming.DtoModel.Payment; +using CloudGaming.Model.DbSqlServer.Db_User; + +using HuanMeng.DotNetCore.MultiTenant.Contract; +using HuanMeng.DotNetCore.Redis; +using HuanMeng.DotNetCore.Utility; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; + +using System.Text; + +namespace CloudGaming.PayApi.Controllers +{ + /// + /// 支付中心 + /// + /// + /// + /// + [Route("api/[controller]")] + [ApiController] + public class PayController(ILogger logger, IHttpContextAccessor httpContextAccessor, IServiceProvider serviceProvider) : ControllerBase + { + /// + /// 支付中心回调 + /// + /// + /// + /// + /// + /// + [HttpPost("{tenant?}/{pay?}/{orderId?}/{sign?}")] + public async Task Post(string? tenant, string? pay, string? orderId, string? sign) + { + var context = httpContextAccessor.HttpContext; + context.Request.EnableBuffering(); // Enable buffering to allow the body to be read multiple times + using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, leaveOpen: true)) + { + var bodyContent = await reader.ReadToEndAsync(); + logger.LogInformation($"请求支付回调接口,请求路径: {context.Request.Path}, 请求Body: {bodyContent}"); + context.Request.Body.Position = 0; + } + #region 验证签名 + + var orderInfo = PaymentExtend.ParseCustomString(orderId); + string newSign = $"{tenant}{orderId}{orderInfo.UserId}"; + newSign = MD5Encryption.ComputeMD5Hash(newSign); + //签名不对 + if (newSign != sign) + { + logger.LogError($"{orderId}订单支付签名不对==>{newSign}==>{context.Request.Path}"); + return "error;签名不对"; + } + #endregion + var appConfig = AppConfigurationExtend.GetAppConfigIdentifier(tenant); + if (appConfig == null) + { + return "error;租户不存在"; + } + //重复请求锁 + var baseKey = $"pay:lock:{orderId}"; + + var redis = appConfig.GetRedisDataBase(); + if (!redis.StringSetLock(baseKey, "", 10)) + { + return "success"; + } + DAO dao = new DAO(serviceProvider, appConfig); + var intentOrder = await dao.DaoUser.Context.T_User_IntentOrder.FirstOrDefaultAsync(it => it.OrderId == orderId); + if (intentOrder == null) + { + redis.KeyDelete(baseKey); + return "error;订单不存在"; + } + if (intentOrder.Status != (int)OrderState.已下单) + { + redis.KeyDelete(baseKey); + return $"success"; + } + intentOrder.Status = (int)OrderState.正在发货; + await dao.DaoUser.Context.SaveChangesAsync(); + + var product = await dao.DaoPhone.Context.T_Products.Where(it => it.ProductId == intentOrder.ProductId).FirstOrDefaultAsync(); + if (product == null) + { + intentOrder.Status = (int)OrderState.发货失败; + await dao.DaoUser.Context.SaveChangesAsync(); + return $"error;订单不存在"; + } + var user = await dao.DaoUser.Context.T_User.FirstOrDefaultAsync(it => it.Id == intentOrder.UserId); + if (user == null) + { + intentOrder.Status = (int)OrderState.发货失败; + await dao.DaoUser.Context.SaveChangesAsync(); + return $"error;用户不存在"; + } + using (IDbContextTransaction transaction = dao.DaoUser.Context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted)) + { + try + { + T_User_OrderItems orderItems = new T_User_OrderItems(); + orderItems.PayUrl = context.Request.Path; + //orderItems. + await product.OrderRewardsNoWorkAsync(user, pay, orderId, intentOrder.Price, intentOrder.IntentAt, dao, orderItems); + //intentOrder = await dao.daoDbMiaoYu.context.T_User_IntentOrder.FirstOrDefaultAsync(it => it.OrderId == orderId); + intentOrder.Status = (int)OrderState.已完成; + await dao.DaoUser.Context.SaveChangesAsync(); + await transaction.CommitAsync(); + } + catch (Exception ex) + { + logger.LogError($"请求支付回调接口,发货失败,请求路径: {context.Request.Path}", ex); + await transaction.RollbackAsync(); + intentOrder.Status = (int)OrderState.发货失败; + await dao.DaoUser.Context.SaveChangesAsync(); + redis.KeyDelete(baseKey); + return $"error;出现异常{ex.Message}"; + } + } + redis.KeyDelete(baseKey); + var userDiamond = await dao.DaoUser.Context.T_User_Currency.Where(it => it.UserId == user.Id && it.CurrencyType == (int)UserCurrencyType.钻石).FirstOrDefaultAsync(); + //刷新钻石缓存 + await AccountExtend.RefreshUserInfo(user.Id, redis, (int)(userDiamond?.CurrencyMoney ?? 0), intentOrder.Price > 0 ? true : false); + + return $"success"; + } + } +} diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/Controllers/WeatherForecastController.cs b/src/CloudGaming/Api/CloudGaming.PayApi/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..92c7b98 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/Controllers/WeatherForecastController.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; + +namespace CloudGaming.PayApi.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return 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(); + } + } +} diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/Dockerfile b/src/CloudGaming/Api/CloudGaming.PayApi/Dockerfile new file mode 100644 index 0000000..6c9daec --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/Dockerfile @@ -0,0 +1,29 @@ +# 请参阅 https://aka.ms/customizecontainer 以了解如何自定义调试容器,以及 Visual Studio 如何使用此 Dockerfile 生成映像以更快地进行调试。 + +# 此阶段用于在快速模式(默认为调试配置)下从 VS 运行时 +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER $APP_UID +WORKDIR /app +EXPOSE 80 + + +# 此阶段用于生成服务项目 +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["Api/CloudGaming.PayApi/CloudGaming.PayApi.csproj", "Api/CloudGaming.PayApi/"] +RUN dotnet restore "./Api/CloudGaming.PayApi/CloudGaming.PayApi.csproj" +COPY . . +WORKDIR "/src/Api/CloudGaming.PayApi" +RUN dotnet build "./CloudGaming.PayApi.csproj" -c $BUILD_CONFIGURATION -o /app/build + +# 此阶段用于发布要复制到最终阶段的服务项目 +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./CloudGaming.PayApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +# 此阶段在生产中使用,或在常规模式下从 VS 运行时使用(在不使用调试配置时为默认值) +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "CloudGaming.PayApi.dll"] \ No newline at end of file diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs b/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs new file mode 100644 index 0000000..bfe0681 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/Program.cs @@ -0,0 +1,139 @@ +using CloudGaming.Code.AppExtend; +using CloudGaming.Code.DataAccess.MultiTenantUtil; +using CloudGaming.Code.Filter; + +using HuanMeng.DotNetCore.MiddlewareExtend; +using HuanMeng.DotNetCore.SwaggerUtile; + +using Microsoft.AspNetCore.Authentication.JwtBearer; + +using Microsoft.OpenApi.Models; + +using Newtonsoft.Json.Serialization; + +using Serilog; + +using System.Diagnostics; +using System.Reflection; + +var builder = WebApplication.CreateBuilder(args); + +#region 日志 +// Add services to the container. +builder.Host.UseSerilog((context, services, configuration) => configuration + .ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext()); + +builder.Services.AddSingleton(typeof(ILogger), serviceProvider => +{ + var loggerFactory = serviceProvider.GetRequiredService(); + return loggerFactory.CreateLogger(); +}); +// +builder.Services.AddSingleton(typeof(ILogger), serviceProvider => +{ + var loggerFactory = serviceProvider.GetRequiredService(); + return loggerFactory.CreateLogger(); +}); +#endregion + +builder.Services.AddHttpContextAccessor(); //添加httpContext注入访问 +#region 返回数据解析 +//builder.Services.AddControllers(); +builder.Services.AddControllers(options => +{ +}) + .AddNewtonsoftJson(options => + { + // 配置 Newtonsoft.Json 选项 + options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; // 忽略循环引用 + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();// 首字母小写(驼峰样式) + //options.SerializerSettings.ContractResolver = new LanguageContractResolver(builder.Services); + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";// 时间格式化 + options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None; + }); +//CustomResultFilter +//builder.Services.AddSingleton(); + +#endregion + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + + + string description = ""; + var filePath = Path.GetFullPath(".versionDescribe"); + if (File.Exists(filePath)) + { + description = File.ReadAllText(filePath); + } + c.SwaggerDoc("v1", new OpenApiInfo { Title = "蒸汽云游戏支付", Version = "0.0.1", Description = description }); + foreach (var assemblies in AppDomain.CurrentDomain.GetAssemblies()) + { + // 添加 XML 注释文件路径 + var xmlFile = $"{assemblies.GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + if (File.Exists(xmlPath)) + { + c.IncludeXmlComments(xmlPath); + } + } + c.ParameterFilter(); + c.RequestBodyFilter(); +}); +builder.AddAppConfigClient(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +//if (app.Environment.IsDevelopment()) +//{ + app.UseSwagger(); + app.UseSwaggerUI(c => + { + + c.EnableDeepLinking(); + c.DefaultModelsExpandDepth(3); + c.DefaultModelExpandDepth(3); + c.EnableFilter("true"); + //c.RoutePrefix = string.Empty; + c.SwaggerEndpoint("/swagger/v1/swagger.json", "蒸汽云游戏支付 API V1"); + // 使用自定义CSS + c.InjectStylesheet("/custom.css"); + }); +//} + +app.UseAuthorization(); +//数据库中间件 +app.UseMultiTenant(); +app.MapControllers(); +app.UseMiddlewareAll(); +#region 默认请求 +app.MapGet("/", () => "请求成功").WithName("默认请求"); + +var startDateTime = DateTime.Now; +var InformationalVersion = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; +Console.WriteLine($"version:{InformationalVersion}"); +app.MapGet("/system", () => +{ + + using Process currentProcess = Process.GetCurrentProcess(); + // CPU使用率 (一般是一个0-100之间的值,但实际是时间占比,需要转换) + double cpuUsage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.TickCount * 100; + // 已用内存 (字节) + long memoryUsage = currentProcess.WorkingSet64; + return new + { + msg = $"系统信息:启动时间:{startDateTime.ToString("yyyy-MM-dd HH:mm:ss")},已安全运行时间:{DateTime.Now.Subtract(startDateTime).TotalMinutes.ToString("0.##")}分钟", + + startDateTime, + MemoryUsage = $"{memoryUsage / (1024.0 * 1024.0):F2}MB", + CPUUsage = $"{cpuUsage:F2}%" + + }; +}).WithName("获取系统数据"); +#endregion +app.Run(); diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/Properties/launchSettings.json b/src/CloudGaming/Api/CloudGaming.PayApi/Properties/launchSettings.json new file mode 100644 index 0000000..421c022 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5229" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": false + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:40520", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/WeatherForecast.cs b/src/CloudGaming/Api/CloudGaming.PayApi/WeatherForecast.cs new file mode 100644 index 0000000..bd5fd21 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace CloudGaming.PayApi +{ + 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; } + } +} diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/appsettings.Development.json b/src/CloudGaming/Api/CloudGaming.PayApi/appsettings.Development.json new file mode 100644 index 0000000..dc69270 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/appsettings.Development.json @@ -0,0 +1,26 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AgileConfig": { + "appId": "CloudGaming", + "secret": "95BB717C61D1ECB0E9FB82C932CC77FF", + "nodes": "http://124.220.55.158:94", //多个节点使用逗号分隔 + "url": "http://124.220.55.158:94", + "env": "DEV", + "name": "payClient", + "UserName": "admin", + "Password": "dbt@com@1234" + }, + //服务器配置 + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://*:802" + } + } + } +} diff --git a/src/CloudGaming/Api/CloudGaming.PayApi/appsettings.json b/src/CloudGaming/Api/CloudGaming.PayApi/appsettings.json new file mode 100644 index 0000000..2f20fd1 --- /dev/null +++ b/src/CloudGaming/Api/CloudGaming.PayApi/appsettings.json @@ -0,0 +1,71 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { "Name": "Console" }, + { + "Name": "File", + "Args": { + "path": "../output/paylogs/info/log-.txt", + "rollingInterval": "Day", + "restrictedToMinimumLevel": "Information", //写入日志的级别 + "shared": true + //"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "../output/paylogs/error/log-.txt", + "rollingInterval": "Day", //日志文件按天滚动生成。 + "restrictedToMinimumLevel": "Error", //写入日志的级别 //包括 Verbose、Debug、Information、Warning、Error 和 Fatal + "shared": true //不占用文件 + // "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "../output/paylogs/debug/log-.txt", + "rollingInterval": "Day", //日志文件按天滚动生成。 + "restrictedToMinimumLevel": "Debug", //写入日志的级别 //包括 Verbose、Debug、Information、Warning、Error 和 Fatal + "shared": true //不占用文件 + // "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}" + } + } + ], + "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ] + }, + "AgileConfig": { + "appId": "CloudGaming", + "secret": "95BB717C61D1ECB0E9FB82C932CC77FF", + "nodes": "http://124.220.55.158:94", //多个节点使用逗号分隔 + "url": "http://124.220.55.158:94", + "env": "TEST", + "name": "payClient", + "UserName": "admin", + "Password": "dbt@com@1234" + }, + //服务器配置 + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://*:80" + } + } + } +} diff --git a/src/CloudGaming/CloudGaming.sln b/src/CloudGaming/CloudGaming.sln index 5752d53..556ab14 100644 --- a/src/CloudGaming/CloudGaming.sln +++ b/src/CloudGaming/CloudGaming.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# 17 +# Visual Studio Version 17 VisualStudioVersion = 17.10.35027.167 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1-core", "1-core", "{FCA3CA4B-1993-429A-B2E9-2B05DB44F10E}" @@ -36,6 +36,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudGaming.DtoModel", "Mod EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.Test", "Console\CloudGaming.Test\CloudGaming.Test.csproj", "{830841B9-E013-4FD5-8D31-D85545870C1C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.PayApi", "Api\CloudGaming.PayApi\CloudGaming.PayApi.csproj", "{452D87B5-A7E0-4EBD-9CF2-1AFF5941065B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -74,6 +76,10 @@ Global {830841B9-E013-4FD5-8D31-D85545870C1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {830841B9-E013-4FD5-8D31-D85545870C1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {830841B9-E013-4FD5-8D31-D85545870C1C}.Release|Any CPU.Build.0 = Release|Any CPU + {452D87B5-A7E0-4EBD-9CF2-1AFF5941065B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {452D87B5-A7E0-4EBD-9CF2-1AFF5941065B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {452D87B5-A7E0-4EBD-9CF2-1AFF5941065B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {452D87B5-A7E0-4EBD-9CF2-1AFF5941065B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -87,6 +93,7 @@ Global {1120C146-6B83-4E4E-8A39-BD09466C7E1B} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9} {96CD0865-0AD5-41B3-89A2-374FF17CDD16} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9} {830841B9-E013-4FD5-8D31-D85545870C1C} = {9F7EF36C-17BB-4F93-927E-F462FE3C9337} + {452D87B5-A7E0-4EBD-9CF2-1AFF5941065B} = {51CB40D2-99F5-43E8-95B4-3A75C91736A6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1D299D92-FA27-47A0-8D78-43D1FAFE7628} diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs index 9ecd4b6..94c678d 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Account/AccountExtend.cs @@ -3,6 +3,7 @@ using Bogus.DataSets; using CloudGaming.Code.Account.Contract; using CloudGaming.Code.Account.Login; using CloudGaming.Code.Account.UserCurrency; +using CloudGaming.Code.AppExtend; using CloudGaming.Code.DataAccess; using CloudGaming.Code.Other; using CloudGaming.DtoModel.Account.Login; @@ -103,6 +104,27 @@ namespace CloudGaming.Code.Account userInfoCache = await GetUserInfo(userId, cloudGamingBase) ?? new UserInfoCache(); return userInfoCache; } + + /// + /// 刷新用户钻石,和是否支付缓存 + /// + /// + /// + /// + /// + /// + public static async Task RefreshUserInfo(int userId, IDatabase database, int diamond, bool isPay) + { + string key = GetUserInfoRedisKey(userId); + var userInfo = await database.StringGetAsync(key); + if (userInfo != null) + { + userInfo.Diamond = diamond; + userInfo.IsPay = isPay; + await database.StringSetAsync(key, userInfo, TimeSpan.FromMinutes(30)); + } + } + /// /// 获取用户信息 /// @@ -241,7 +263,7 @@ namespace CloudGaming.Code.Account return userInfo; } - + /// /// 发送消息 @@ -274,7 +296,7 @@ namespace CloudGaming.Code.Account } - + /// /// 获取用户购买过的产品 /// diff --git a/src/CloudGaming/Code/CloudGaming.Code/Account/UserCurrencyExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Account/UserCurrencyExtend.cs index 89909c5..33c1962 100644 --- a/src/CloudGaming/Code/CloudGaming.Code/Account/UserCurrencyExtend.cs +++ b/src/CloudGaming/Code/CloudGaming.Code/Account/UserCurrencyExtend.cs @@ -80,20 +80,16 @@ namespace CloudGaming.Code.Account /// /// /// - public static bool ConsumeMoneyNoWork(this T_User user, UserCurrencyType userCurrencyType, decimal money, DAO dao, T_User_Currency? _currency = null, string orderId = "", string title = "") + public static async Task ConsumeMoneyNoWork(this T_User user, UserCurrencyType userCurrencyType, decimal money, DAO dao, T_User_Currency? _currency = null, string orderId = "", string title = "") { if (user == null || user.Id == 0) { throw new ArgumentNullException("用户不能为空"); } int userId = user.Id; - return ConsumeMoneyNoWork(userId, userCurrencyType, money, dao, orderId: orderId, title: title); - } - public static bool ConsumeMoneyNoWork(int userId, UserCurrencyType userCurrencyType, decimal money, DAO dao, T_User_Currency? _currency = null, string remarks = "", string title = "", string orderId = "") - { - - return true; + return await UserConsumeDiamondMoneyAsync(dao, userId, userCurrencyType, money, title, orderId, _currency); } + /// /// 扣除当前用户钻石 @@ -135,7 +131,40 @@ namespace CloudGaming.Code.Account public static async Task UserConsumeDiamondMoneyAsync(this CloudGamingBase cloudGamingBase, decimal money, Action? userDoamondAction = null) { T_User_Currency currency = new T_User_Currency(); - var userConsumeMoney = new DiamondConsumeMoney(cloudGamingBase.Dao, cloudGamingBase.UserInfo.UserId, UserCurrencyType.钻石, currency); + try + { + var isSuccess = await UserConsumeDiamondMoneyAsync(cloudGamingBase.Dao, cloudGamingBase.UserInfo.UserId, money, userDoamondAction, currency);// + if (!isSuccess) + { + return false; + } + cloudGamingBase.UserInfo.Diamond = (int)currency.CurrencyMoney; + await cloudGamingBase.SaveUserInfoCacheChangesAsync(); + } + catch (Exception ex) + { + + return false; + } + return true; + } + + + + + + /// + /// 扣除或者添加当前用户钻石 + /// + /// + /// + /// 负数扣除,正数添加 + /// + /// + public static async Task UserConsumeDiamondMoneyAsync(DAO dao, int userId, decimal money, Action? userDoamondAction = null, T_User_Currency userCurrency = null) + { + + var userConsumeMoney = new DiamondConsumeMoney(dao, userId, UserCurrencyType.钻石, userCurrency); try { var isSuccess = await userConsumeMoney.ConsumeMoneyAsync(money); @@ -143,8 +172,6 @@ namespace CloudGaming.Code.Account { return false; } - cloudGamingBase.UserInfo.Diamond = (int)currency.CurrencyMoney; - await cloudGamingBase.SaveUserInfoCacheChangesAsync(); UserCurrencyConsumeType consumeType = money >= 0 ? UserCurrencyConsumeType.收入 : UserCurrencyConsumeType.消耗; T_User_DiamondList userDiamondList = new T_User_DiamondList() { @@ -155,14 +182,14 @@ namespace CloudGaming.Code.Account OrderCode = "", Title = "", UpdateAt = DateTime.Now, - UserId = cloudGamingBase.UserInfo.UserId, + UserId = userId, }; if (userDoamondAction != null) { userDoamondAction(userDiamondList); } - await cloudGamingBase.Dao.DaoUser.Context.AddAsync(userDiamondList); - await cloudGamingBase.Dao.DaoUser.Context.SaveChangesAsync(); + await dao.DaoUser.Context.AddAsync(userDiamondList); + await dao.DaoUser.Context.SaveChangesAsync(); } catch (Exception ex) { @@ -203,6 +230,26 @@ namespace CloudGaming.Code.Account } return await UserConsumeDiamondMoneyAsync(cloudGamingBase, money, it => { it.Title = title; it.OrderCode = orderId; }); } + /// + /// 充值或者消耗用户货币 + /// + /// + /// + /// + /// + /// + /// + /// + + public static async Task UserConsumeDiamondMoneyAsync(DAO dao, int userId, UserCurrencyType userCurrencyType, decimal money, string title = "", string orderId = "", T_User_Currency currency = null) + { + if (userCurrencyType == UserCurrencyType.钻石) + { + return await UserConsumeDiamondMoneyAsync(dao, userId, money, it => { it.Title = title; it.OrderCode = orderId; }, currency); + } + return await UserConsumeDiamondMoneyAsync(dao, userId, money, it => { it.Title = title; it.OrderCode = orderId; }, currency); + } + /// /// 用户玩游戏消耗钻石 /// diff --git a/src/CloudGaming/Code/CloudGaming.Code/Payment/OrderExtend.cs b/src/CloudGaming/Code/CloudGaming.Code/Payment/OrderExtend.cs new file mode 100644 index 0000000..c4b190e --- /dev/null +++ b/src/CloudGaming/Code/CloudGaming.Code/Payment/OrderExtend.cs @@ -0,0 +1,88 @@ +using CloudGaming.Code.Account; +using CloudGaming.Code.DataAccess; +using CloudGaming.DtoModel.Account.User; +using CloudGaming.DtoModel.Payment; + +using Newtonsoft.Json; + +using StackExchange.Redis; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CloudGaming.Code.Payment; + +public static class OrderExtend +{ + + /// + /// 不带锁,出现异常需要自己处理 + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task OrderRewardsNoWorkAsync(this T_Products product, T_User user, string pay, string orderId, decimal price, DateTime intentDate, DAO dao, T_User_OrderItems orderItems = null) + { + var userId = user.Id; + var chargeMoneyCount = dao.DaoUser.Context.T_User_Order.Count(it => it.UserId == userId && it.ProductId == product.ProductId); + var productReward = await dao.DaoPhone.Context.T_Products_Reward.Where(it => it.ProductId == product.ProductId).ToListAsync(); + + if (productReward != null && productReward.Count > 0) + { + List tips = new List(); + //List user_Currencies = new List(); + foreach (var reward in productReward) + { + var money = reward.Money; + var currency = (UserCurrencyType)reward.CurrencyType; + var userCurrency = new T_User_Currency(); + await user.ConsumeMoneyNoWork(currency, money, dao, userCurrency, orderId); + tips.Add($"获得{currency}*{money}"); + if (product.IsFirstCharge && chargeMoneyCount == 0 && reward.FirstChargeMoney > 0) + { + await user.ConsumeMoneyNoWork(currency, reward.FirstChargeMoney ?? 0, dao, userCurrency, orderId, $"首充赠送{currency}{reward.FirstChargeMoney}"); + tips.Add($"首充赠送{currency}*{money}"); + } + } + var rewardTips = string.Join(',', tips.ToArray()); + T_User_Order order = new T_User_Order() + { + OrderId = orderId, + CreatedAt = DateTime.Now, + OrderDate = intentDate, + PaymentDate = DateTime.Now, + PaymentDay = DateOnly.FromDateTime(DateTime.Now), + PaymentMethod = pay, + ProductId = product.ProductId, + Status = (int)OrderState.已完成, + TenantId = product.TenantId, + TotalPrice = price, + UpdatedAt = DateTime.Now, + UserId = userId, + }; + if (orderItems == null) + { + orderItems = new T_User_OrderItems(); + } + orderItems.OrderId = orderId; + orderItems.RewardTips = rewardTips; + orderItems.Product = product.Id; + orderItems.TenantId = product.TenantId; + orderItems.ProductId = product.ProductId; + orderItems.RewardInfo = JsonConvert.SerializeObject(productReward); + orderItems.RewardTips = rewardTips; + dao.DaoUser.Context.T_User_OrderItems.Add(orderItems); + dao.DaoUser.Context.T_User_Order.Add(order); + await dao.DaoUser.Context.SaveChangesAsync(); + } + } + +} diff --git a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs index 6c6db1f..13ca5de 100644 --- a/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs +++ b/src/CloudGaming/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs @@ -49,6 +49,9 @@ namespace HuanMeng.DotNetCore.Redis } return database; } + + + /// /// ///