添加支付中心
This commit is contained in:
parent
f2ad570efe
commit
565c5e0e26
|
|
@ -0,0 +1,27 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>..\..</DockerfileContext>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Code\CloudGaming.Code\CloudGaming.Code.csproj" />
|
||||
<ProjectReference Include="..\..\Model\CloudGaming.DtoModel\CloudGaming.DtoModel.csproj" />
|
||||
<ProjectReference Include="..\..\Model\CloudGaming.GameModel\CloudGaming.GameModel.csproj" />
|
||||
<ProjectReference Include="..\..\Model\CloudGaming.Model\CloudGaming.Model.csproj" />
|
||||
<ProjectReference Include="..\..\Utile\HuanMeng.DotNetCore\HuanMeng.DotNetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@CloudGaming.PayApi_HostAddress = http://localhost:5229
|
||||
|
||||
GET {{CloudGaming.PayApi_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付中心
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="httpContextAccessor"></param>
|
||||
/// <param name="serviceProvider"></param>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class PayController(ILogger<PayController> logger, IHttpContextAccessor httpContextAccessor, IServiceProvider serviceProvider) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 支付中心回调
|
||||
/// </summary>
|
||||
/// <param name="tenant"></param>
|
||||
/// <param name="pay"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <param name="sign"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{tenant?}/{pay?}/{orderId?}/{sign?}")]
|
||||
public async Task<string> 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/CloudGaming/Api/CloudGaming.PayApi/Dockerfile
Normal file
29
src/CloudGaming/Api/CloudGaming.PayApi/Dockerfile
Normal file
|
|
@ -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"]
|
||||
139
src/CloudGaming/Api/CloudGaming.PayApi/Program.cs
Normal file
139
src/CloudGaming/Api/CloudGaming.PayApi/Program.cs
Normal file
|
|
@ -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<CloudGamingBase>), serviceProvider =>
|
||||
{
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
return loggerFactory.CreateLogger<CloudGamingBase>();
|
||||
});
|
||||
//
|
||||
builder.Services.AddSingleton(typeof(ILogger<ExceptionMiddleware>), serviceProvider =>
|
||||
{
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
return loggerFactory.CreateLogger<ExceptionMiddleware>();
|
||||
});
|
||||
#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<ObjectResultExecutor, CustomObjectResultExecutor>();
|
||||
|
||||
#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<LowercaseParameterFilter>();
|
||||
c.RequestBodyFilter<LowercaseRequestFilter>();
|
||||
});
|
||||
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<AssemblyInformationalVersionAttribute>().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();
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/CloudGaming/Api/CloudGaming.PayApi/WeatherForecast.cs
Normal file
13
src/CloudGaming/Api/CloudGaming.PayApi/WeatherForecast.cs
Normal file
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/CloudGaming/Api/CloudGaming.PayApi/appsettings.json
Normal file
71
src/CloudGaming/Api/CloudGaming.PayApi/appsettings.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新用户钻石,和是否支付缓存
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="database"></param>
|
||||
/// <param name="diamond"></param>
|
||||
/// <param name="isPay"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task RefreshUserInfo(int userId, IDatabase database, int diamond, bool isPay)
|
||||
{
|
||||
string key = GetUserInfoRedisKey(userId);
|
||||
var userInfo = await database.StringGetAsync<UserInfoCache>(key);
|
||||
if (userInfo != null)
|
||||
{
|
||||
userInfo.Diamond = diamond;
|
||||
userInfo.IsPay = isPay;
|
||||
await database.StringSetAsync(key, userInfo, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户信息
|
||||
/// </summary>
|
||||
|
|
@ -241,7 +263,7 @@ namespace CloudGaming.Code.Account
|
|||
return userInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
|
|
@ -274,7 +296,7 @@ namespace CloudGaming.Code.Account
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户购买过的产品
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -80,20 +80,16 @@ namespace CloudGaming.Code.Account
|
|||
/// <param name="title"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
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<bool> 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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 扣除当前用户钻石
|
||||
|
|
@ -135,7 +131,40 @@ namespace CloudGaming.Code.Account
|
|||
public static async Task<bool> UserConsumeDiamondMoneyAsync(this CloudGamingBase cloudGamingBase, decimal money, Action<T_User_DiamondList>? 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 扣除或者添加当前用户钻石
|
||||
/// </summary>
|
||||
/// <param name="dao"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="money">负数扣除,正数添加</param>
|
||||
/// <param name="userDoamondAction"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<bool> UserConsumeDiamondMoneyAsync(DAO dao, int userId, decimal money, Action<T_User_DiamondList>? 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; });
|
||||
}
|
||||
/// <summary>
|
||||
/// 充值或者消耗用户货币
|
||||
/// </summary>
|
||||
/// <param name="dao"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="userCurrencyType"></param>
|
||||
/// <param name="money"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static async Task<bool> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户玩游戏消耗钻石
|
||||
/// </summary>
|
||||
|
|
|
|||
88
src/CloudGaming/Code/CloudGaming.Code/Payment/OrderExtend.cs
Normal file
88
src/CloudGaming/Code/CloudGaming.Code/Payment/OrderExtend.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 不带锁,出现异常需要自己处理
|
||||
/// </summary>
|
||||
/// <param name="product"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="pay"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <param name="price"></param>
|
||||
/// <param name="intentDate"></param>
|
||||
/// <param name="dao"></param>
|
||||
/// <returns></returns>
|
||||
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<string> tips = new List<string>();
|
||||
//List<T_User_Currency> user_Currencies = new List<T_User_Currency>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -49,6 +49,9 @@ namespace HuanMeng.DotNetCore.Redis
|
|||
}
|
||||
return database;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user