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;
}
+
+
+
///
///
///