添加定时器统计
This commit is contained in:
parent
c6db4d9294
commit
cfd1f964bf
|
|
@ -20,6 +20,5 @@ namespace CloudGaming.Api.Base
|
|||
/// 数据库使用
|
||||
/// </summary>
|
||||
public IServiceProvider ServiceProvider { get; set; } = _serviceProvider;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CloudGaming.ExtApi.Base;
|
||||
|
||||
/// <summary>
|
||||
/// 基础父类
|
||||
/// </summary>
|
||||
/// <param name="_serviceProvider"></param>
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
|
||||
public class CloudGamingControllerBase(IServiceProvider _serviceProvider) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库使用
|
||||
/// </summary>
|
||||
public IServiceProvider ServiceProvider { get; set; } = _serviceProvider;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>..\..</DockerfileContext>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,29 @@
|
|||
using CloudGaming.AppConfigModel;
|
||||
using CloudGaming.Code.Monitor;
|
||||
using CloudGaming.DtoModel.Other;
|
||||
using CloudGaming.ExtApi.Base;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CloudGaming.ExtApi.Controllers
|
||||
namespace CloudGaming.ExtApi.Controllers;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 监控数据
|
||||
/// </summary>
|
||||
public class MonitorController : CloudGamingControllerBase
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class MonitorController : ControllerBase
|
||||
public MonitorController(IServiceProvider _serviceProvider) : base(_serviceProvider)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取监控数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public AppBasicStatistics GetAppMonitorInfo()
|
||||
{
|
||||
return new MonitorBLL(ServiceProvider).GetAppMonitorInfo();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,11 @@ namespace CloudGaming.Code.Account
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static string UserInfoRedisKeyPrefix { get => "user:userInfo"; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户缓存的key
|
||||
/// </summary>
|
||||
|
|
@ -89,7 +94,7 @@ namespace CloudGaming.Code.Account
|
|||
/// <returns></returns>
|
||||
public static string GetUserInfoRedisKey(int userId)
|
||||
{
|
||||
return $"user:userInfo:{userId}";
|
||||
return $"{UserInfoRedisKeyPrefix}:{userId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
using CloudGaming.Code.Monitor;
|
||||
using CloudGaming.DtoModel.Other;
|
||||
|
||||
using Quartz;
|
||||
|
||||
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CloudGaming.Code.AppExtend
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 定时使用
|
||||
/// </summary>
|
||||
public abstract class AppJobBase : IJob
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected readonly IServiceScopeFactory scopeFactory;
|
||||
public AppJobBase(IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
this.scopeFactory = scopeFactory;
|
||||
}
|
||||
|
||||
public Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
if (AppConfigurationExtend.AppConfigs != null && AppConfigurationExtend.AppConfigs.Count > 0)
|
||||
{
|
||||
var appConfigs = AppConfigurationExtend.AppConfigs.Where(it => it.Key != "default")
|
||||
.Select(it => it.Value)
|
||||
.ToList();
|
||||
|
||||
// 为每个项目启动一个独立的任务
|
||||
foreach (var appConfig in appConfigs)
|
||||
{
|
||||
|
||||
|
||||
_ = Task.Run(() => AppConfigProcessLoop(appConfig));
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="appConfig"></param>
|
||||
/// <returns></returns>
|
||||
public async Task AppConfigProcessLoop(AppConfig appConfig)
|
||||
{
|
||||
AppMonitorInfo appMonitorInfo = MonitorExtend.AppMonitorConfigs.GetOrAdd(appConfig.Identifier, new AppMonitorInfo());
|
||||
using var scope = scopeFactory.CreateScope();
|
||||
var scopedProvider = scope.ServiceProvider;
|
||||
var app = scopedProvider.GetRequiredService<AppConfig>();
|
||||
appConfig.ToAppConfig(app);
|
||||
CloudGamingBase cloudGamingBase = new CloudGamingBase(scopedProvider);
|
||||
await AppConfigProcessLoop(appConfig, appMonitorInfo, scopedProvider, cloudGamingBase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="appConfig"></param>
|
||||
/// <param name="appMonitorInfo"></param>
|
||||
/// <param name="serviceProvider"></param>
|
||||
/// <param name="cloudGamingBase"></param>
|
||||
/// <returns></returns>
|
||||
public abstract Task AppConfigProcessLoop(AppConfig appConfig, AppMonitorInfo appMonitorInfo, IServiceProvider serviceProvider, CloudGamingBase cloudGamingBase);
|
||||
|
||||
}
|
||||
}
|
||||
28
src/CloudGaming/Code/CloudGaming.Code/Monitor/MonitorBLL.cs
Normal file
28
src/CloudGaming/Code/CloudGaming.Code/Monitor/MonitorBLL.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using CloudGaming.DtoModel.Other;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CloudGaming.Code.Monitor;
|
||||
|
||||
/// <summary>
|
||||
/// 监控类
|
||||
/// </summary>
|
||||
public class MonitorBLL : CloudGamingBase
|
||||
{
|
||||
public MonitorBLL(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取监控数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AppBasicStatistics GetAppMonitorInfo()
|
||||
{
|
||||
return AppConfig.GetAppMonitorInfo().ToAppBasicStatistics();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global.Models.QueryPartnerRefundsResponse.Types;
|
||||
using HuanMeng.DotNetCore.QuartzExtend;
|
||||
|
||||
namespace CloudGaming.Code.Monitor;
|
||||
|
||||
|
|
@ -27,27 +29,28 @@ public static class MonitorExtend
|
|||
public static ConcurrentDictionary<string, AppMonitorInfo> AppMonitorConfigs { get; set; } = new ConcurrentDictionary<string, AppMonitorInfo>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加监控服务
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static WebApplicationBuilder AddMonitorConfig(this WebApplicationBuilder builder)
|
||||
{
|
||||
// 添加 Quartz 配置
|
||||
builder.Services.AddQuartz(q =>
|
||||
{
|
||||
// 注册作业和触发器
|
||||
var jobKey = new JobKey("MonitorProcessor");
|
||||
q.AddJob<MonitorProcessor>(opts => opts.WithIdentity(jobKey));
|
||||
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(jobKey)
|
||||
.WithIdentity("monitorTrigger")
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever()));
|
||||
});
|
||||
|
||||
// 添加 Quartz 托管服务
|
||||
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
|
||||
|
||||
builder.Services.AddQuartzWithAttributes(typeof(MonitorExtend).Assembly);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取app监控数据
|
||||
/// </summary>
|
||||
/// <param name="appConfig"></param>
|
||||
/// <returns></returns>
|
||||
public static AppMonitorInfo GetAppMonitorInfo(this AppConfig appConfig)
|
||||
{
|
||||
AppMonitorInfo appMonitorInfo = MonitorExtend.AppMonitorConfigs.GetOrAdd(appConfig.Identifier, new AppMonitorInfo());
|
||||
return appMonitorInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
|
@ -57,12 +60,12 @@ public static class MonitorExtend
|
|||
{
|
||||
return new AppBasicStatistics()
|
||||
{
|
||||
CurrentOnlineUsers= appMonitorInfo.CurrentOnlineUsers,
|
||||
CurrentOnlineUsers = appMonitorInfo.CurrentOnlineUsers,
|
||||
CurrentPlayingUsers = appMonitorInfo.CurrentPlayingUsers,
|
||||
CurrentQueuedUsers = appMonitorInfo.CurrentQueuedUsers,
|
||||
TodayIntendedOrders = appMonitorInfo.TodayIntendedOrders,
|
||||
TodayLoggedInUsers = appMonitorInfo.TodayLoggedInUsers,
|
||||
TodayPaidOrders= appMonitorInfo.TodayPaidOrders,
|
||||
TodayPaidOrders = appMonitorInfo.TodayPaidOrders,
|
||||
TodayRechargeAmount = appMonitorInfo.TodayRechargeAmount,
|
||||
TodayRegisteredUsers = appMonitorInfo.TodayRegisteredUsers
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
using CloudGaming.Code.Account;
|
||||
using CloudGaming.DtoModel.Other;
|
||||
using CloudGaming.DtoModel.PlayGame;
|
||||
|
||||
using HuanMeng.DotNetCore.QuartzExtend;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Quartz;
|
||||
|
|
@ -15,53 +18,22 @@ namespace CloudGaming.Code.Monitor;
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// 定时监控
|
||||
/// 基础信息定时监控
|
||||
/// </summary>
|
||||
public class MonitorProcessor : IJob
|
||||
[QuartzTrigger("MonitorProcessor", "3/10 * * * * ?")]
|
||||
public class MonitorProcessor : AppJobBase
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IServiceScopeFactory scopeFactory;
|
||||
public MonitorProcessor(IServiceScopeFactory scopeFactory)
|
||||
public MonitorProcessor(IServiceScopeFactory scopeFactory) : base(scopeFactory)
|
||||
{
|
||||
this.scopeFactory = scopeFactory;
|
||||
}
|
||||
|
||||
public Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
if (AppConfigurationExtend.AppConfigs != null && AppConfigurationExtend.AppConfigs.Count > 0)
|
||||
{
|
||||
var appConfigs = AppConfigurationExtend.AppConfigs.Where(it => it.Key != "default")
|
||||
.Select(it => it.Value)
|
||||
.ToList();
|
||||
|
||||
// 为每个项目启动一个独立的任务
|
||||
foreach (var appConfig in appConfigs)
|
||||
{
|
||||
|
||||
AppMonitorInfo appMonitorInfo = MonitorExtend.AppMonitorConfigs.GetOrAdd(appConfig.Identifier, new AppMonitorInfo());
|
||||
_ = Task.Run(() => ProcessAppConfigLoop(appConfig, appMonitorInfo));
|
||||
}
|
||||
}
|
||||
|
||||
//throw new NotImplementedException();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
/// <summary>
|
||||
/// 单个项目的处理循环
|
||||
/// </summary>
|
||||
public async Task ProcessAppConfigLoop(AppConfig appConfig, AppMonitorInfo appMonitorInfo)
|
||||
public override async Task AppConfigProcessLoop(AppConfig appConfig, AppMonitorInfo appMonitorInfo, IServiceProvider serviceProvider, CloudGamingBase cloudGamingBase)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
using var scope = scopeFactory.CreateScope();
|
||||
var scopedProvider = scope.ServiceProvider;
|
||||
int day = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
|
||||
var nowDay = DateOnly.FromDateTime(DateTime.Now);
|
||||
|
||||
|
||||
var app = scopedProvider.GetRequiredService<AppConfig>();
|
||||
appConfig.ToAppConfig(app);
|
||||
CloudGamingBase cloudGamingBase = new CloudGamingBase(scopedProvider);
|
||||
|
||||
//今日登录人数
|
||||
var todayLoggedInUsers = await cloudGamingBase.Dao.DaoExt.Context.T_User_LoginDay_Log.Where(it => it.CreateTimeDay == day).CountAsync();
|
||||
//今日注册人数
|
||||
|
|
@ -71,39 +43,24 @@ public class MonitorProcessor : IJob
|
|||
//今日支付订单
|
||||
var todayPaidOrders = await cloudGamingBase.Dao.DaoUser.Context.T_User_Order.Where(it => it.PaymentDay == nowDay).CountAsync();
|
||||
//今日支付金额
|
||||
var todayRechargeAmount =await cloudGamingBase.Dao.DaoUser.Context.T_User_Order.Where(it => it.PaymentDay == nowDay).SumAsync(it => (decimal?)it.TotalPrice);
|
||||
var todayRechargeAmount = await cloudGamingBase.Dao.DaoUser.Context.T_User_Order.Where(it => it.PaymentDay == nowDay).SumAsync(it => (decimal?)it.TotalPrice);
|
||||
// 查询游戏数据
|
||||
var playGameStatusStatistics = appMonitorInfo.PlayGameStatusStatistics();
|
||||
//排队数据
|
||||
int currentQueuedUsers = 0;
|
||||
if (playGameStatusStatistics.TryGetValue(PlayGameStatus.排队中, out var _currentQueuedUsers))
|
||||
{
|
||||
currentQueuedUsers += _currentQueuedUsers;
|
||||
}
|
||||
int currentQueuedUsers = playGameStatusStatistics.GetValueOrDefault(PlayGameStatus.排队中);
|
||||
//开始游戏
|
||||
int currentPlayingUsers = 0;
|
||||
if (playGameStatusStatistics.TryGetValue(PlayGameStatus.开始游戏, out var _currentPlayingUsers))
|
||||
{
|
||||
currentPlayingUsers += _currentPlayingUsers;
|
||||
}
|
||||
if (playGameStatusStatistics.TryGetValue(PlayGameStatus.游戏中, out var _currentPlayingUsers1))
|
||||
{
|
||||
currentPlayingUsers += _currentPlayingUsers1;
|
||||
}
|
||||
//appMonitorInfo.CurrentQueuedUsers=
|
||||
sw.Stop();
|
||||
int currentPlayingUsers = playGameStatusStatistics.GetValueOrDefault(PlayGameStatus.开始游戏) + playGameStatusStatistics.GetValueOrDefault(PlayGameStatus.游戏中);
|
||||
//查询在线人数
|
||||
//ScanKeysCount user:userInfo:*
|
||||
var currentOnlineUsers = await cloudGamingBase.RedisServerCache.ScanKeysCountAsync($"{AccountExtend.UserInfoRedisKeyPrefix}:*");
|
||||
|
||||
appMonitorInfo.TodayLoggedInUsers = todayLoggedInUsers;
|
||||
appMonitorInfo.TodayRegisteredUsers = todayRegisteredUsers;
|
||||
appMonitorInfo.TodayIntendedOrders = todayIntendedOrders;
|
||||
appMonitorInfo.TodayPaidOrders = todayPaidOrders;
|
||||
appMonitorInfo.TodayRechargeAmount = todayRechargeAmount ?? 0;
|
||||
appMonitorInfo.CurrentOnlineUsers = 0;
|
||||
appMonitorInfo.CurrentOnlineUsers = currentOnlineUsers;
|
||||
appMonitorInfo.CurrentQueuedUsers = currentQueuedUsers;
|
||||
appMonitorInfo.CurrentPlayingUsers = currentPlayingUsers;
|
||||
|
||||
var t = appMonitorInfo.ToAppBasicStatistics();
|
||||
Console.WriteLine(JsonConvert.SerializeObject(t));
|
||||
Console.WriteLine($"消耗{sw.Elapsed.TotalMilliseconds.ToString("0.###")}");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
using CloudGaming.DtoModel.Other;
|
||||
|
||||
using HuanMeng.DotNetCore.QuartzExtend;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CloudGaming.Code.Monitor;
|
||||
|
||||
/// <summary>
|
||||
/// 用户定时类
|
||||
/// </summary>
|
||||
[QuartzTrigger("UserMonitorProcessor", "* 15 4 * * ?")]
|
||||
public class UserMonitorProcessor : AppJobBase
|
||||
{
|
||||
public UserMonitorProcessor(IServiceScopeFactory scopeFactory) : base(scopeFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task AppConfigProcessLoop(AppConfig appConfig, AppMonitorInfo appMonitorInfo, IServiceProvider serviceProvider, CloudGamingBase cloudGamingBase)
|
||||
{
|
||||
var dao = cloudGamingBase.Dao;
|
||||
var now = DateTime.Now.AddDays(-1);
|
||||
int day = int.Parse(now.ToString("yyyyMMdd"));
|
||||
var nowDay = DateOnly.FromDateTime(now);
|
||||
|
||||
//今日登录人数
|
||||
var todayLoggedInUsers = await cloudGamingBase.Dao.DaoExt.Context.T_User_LoginDay_Log.Where(it => it.CreateTimeDay == day)
|
||||
.GroupBy(it => it.Channel ?? "")
|
||||
.ToDictionaryAsync(it => it.Key, it => it.Count());
|
||||
//今日注册人数
|
||||
var todayRegisteredUsers = await cloudGamingBase.Dao.DaoExt.Context.T_User_LoginDay_Log.Where(it => it.CreateTimeDay == day && it.IsNew)
|
||||
.GroupBy(it => it.Channel ?? "")
|
||||
.ToDictionaryAsync(it => it.Key, it => it.Count());
|
||||
//今日活跃人数
|
||||
var todayActionUsers = await cloudGamingBase.Dao.DaoExt.Context.T_User_LoginDay_Log.Where(it => it.CreateTimeDay == day && it.LogInDay > 1)
|
||||
.GroupBy(it => it.Channel ?? "")
|
||||
.ToDictionaryAsync(it => it.Key, it => it.Count());
|
||||
//数据库中的数据
|
||||
var userStatistics = await dao.DaoExt.Context.T_Statistics_User.Where(it => it.LoginDay == day).ToDictionaryAsync(it => it.Channel ?? "");//.ToListAsync();
|
||||
//登录
|
||||
if (todayLoggedInUsers != null)
|
||||
{
|
||||
foreach (var item in todayLoggedInUsers)
|
||||
{
|
||||
if (!userStatistics.TryGetValue(item.Key, out var statisticsUser))
|
||||
{
|
||||
statisticsUser = new T_Statistics_User()
|
||||
{
|
||||
ActiveCount = 0,
|
||||
Channel = item.Key,
|
||||
CreatedAt = DateTime.Now,
|
||||
LoginCount = 0,
|
||||
RegistrCount = 0,
|
||||
LoginDate = nowDay,
|
||||
LoginDay = day,
|
||||
UpdatedAt = DateTime.Now,
|
||||
};
|
||||
await dao.DaoExt.Context.T_Statistics_User.AddAsync(statisticsUser);
|
||||
userStatistics.Add(item.Key, statisticsUser);
|
||||
}
|
||||
statisticsUser.LoginDay = item.Value;
|
||||
}
|
||||
}
|
||||
//注册
|
||||
if (todayRegisteredUsers != null)
|
||||
{
|
||||
foreach (var item in todayRegisteredUsers)
|
||||
{
|
||||
if (!userStatistics.TryGetValue(item.Key, out var statisticsUser))
|
||||
{
|
||||
statisticsUser = new T_Statistics_User()
|
||||
{
|
||||
ActiveCount = 0,
|
||||
Channel = item.Key,
|
||||
CreatedAt = DateTime.Now,
|
||||
LoginCount = 0,
|
||||
RegistrCount = 0,
|
||||
LoginDate = nowDay,
|
||||
LoginDay = day,
|
||||
UpdatedAt = DateTime.Now,
|
||||
};
|
||||
await dao.DaoExt.Context.T_Statistics_User.AddAsync(statisticsUser);
|
||||
userStatistics.Add(item.Key, statisticsUser);
|
||||
}
|
||||
statisticsUser.RegistrCount = item.Value;
|
||||
}
|
||||
}
|
||||
//活跃
|
||||
if (todayActionUsers != null)
|
||||
{
|
||||
foreach (var item in todayActionUsers)
|
||||
{
|
||||
if (!userStatistics.TryGetValue(item.Key, out var statisticsUser))
|
||||
{
|
||||
statisticsUser = new T_Statistics_User()
|
||||
{
|
||||
ActiveCount = 0,
|
||||
Channel = item.Key,
|
||||
CreatedAt = DateTime.Now,
|
||||
LoginCount = 0,
|
||||
RegistrCount = 0,
|
||||
LoginDate = nowDay,
|
||||
LoginDay = day,
|
||||
UpdatedAt = DateTime.Now,
|
||||
};
|
||||
await dao.DaoExt.Context.T_Statistics_User.AddAsync(statisticsUser);
|
||||
userStatistics.Add(item.Key, statisticsUser);
|
||||
}
|
||||
statisticsUser.ActiveCount = item.Value;
|
||||
}
|
||||
}
|
||||
await dao.DaoExt.Context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CloudGaming.Code.Other
|
||||
{
|
||||
/// <summary>
|
||||
/// 监控类
|
||||
/// </summary>
|
||||
public class MonitorBLL : CloudGamingBase
|
||||
{
|
||||
public MonitorBLL(IServiceProvider serviceProvider) : base(serviceProvider)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +59,21 @@ public partial class CloudGamingCBTContext : DbContext
|
|||
/// </summary>
|
||||
public virtual DbSet<T_Sms_Log> T_Sms_Log { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 游戏统计数据
|
||||
/// </summary>
|
||||
public virtual DbSet<T_Statistics_Game> T_Statistics_Game { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单统计表
|
||||
/// </summary>
|
||||
public virtual DbSet<T_Statistics_Order> T_Statistics_Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户统计表
|
||||
/// </summary>
|
||||
public virtual DbSet<T_Statistics_User> T_Statistics_User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录日志表,每天每个用户只有一条数据
|
||||
/// </summary>
|
||||
|
|
@ -185,6 +200,75 @@ public partial class CloudGamingCBTContext : DbContext
|
|||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<T_Statistics_Game>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("_copy_1");
|
||||
|
||||
entity.ToTable(tb => tb.HasComment("游戏统计数据"));
|
||||
|
||||
entity.Property(e => e.Channel)
|
||||
.HasMaxLength(10)
|
||||
.HasComment("渠道号");
|
||||
entity.Property(e => e.CreatedAt)
|
||||
.HasComment("创建时间")
|
||||
.HasColumnType("datetime");
|
||||
entity.Property(e => e.LoginDate).HasComment("日期");
|
||||
entity.Property(e => e.LoginDay).HasComment("天");
|
||||
entity.Property(e => e.PlayGameCount).HasComment("用户玩游戏人数");
|
||||
entity.Property(e => e.PlayGameTimeCount).HasComment("用户玩游戏时长");
|
||||
entity.Property(e => e.StartGameCount).HasComment("启动游戏次数");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasComment("修改时间")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<T_Statistics_Order>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("_copy_3");
|
||||
|
||||
entity.ToTable(tb => tb.HasComment("订单统计表"));
|
||||
|
||||
entity.Property(e => e.Channel)
|
||||
.HasMaxLength(10)
|
||||
.HasComment("渠道号");
|
||||
entity.Property(e => e.CreatedAt)
|
||||
.HasComment("创建时间")
|
||||
.HasColumnType("datetime");
|
||||
entity.Property(e => e.IntendedOrderCount).HasComment("意向订单次数");
|
||||
entity.Property(e => e.LoginDate).HasComment("日期");
|
||||
entity.Property(e => e.LoginDay).HasComment("天");
|
||||
entity.Property(e => e.PaidOrders).HasComment("支付订单次数");
|
||||
entity.Property(e => e.RechargeAmount).HasComment("支付订单金额");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasComment("修改时间")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<T_Statistics_User>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("PK__T_Statis__3214EC07FE2CF78F");
|
||||
|
||||
entity.ToTable(tb => tb.HasComment("用户统计表"));
|
||||
|
||||
entity.Property(e => e.ActiveCount).HasComment("用户活跃数");
|
||||
entity.Property(e => e.Channel)
|
||||
.HasMaxLength(10)
|
||||
.HasComment("渠道号");
|
||||
entity.Property(e => e.CreatedAt)
|
||||
.HasComment("创建时间")
|
||||
.HasColumnType("datetime");
|
||||
entity.Property(e => e.LoginCount).HasComment("用户登录数");
|
||||
entity.Property(e => e.LoginDate).HasComment("日期");
|
||||
entity.Property(e => e.LoginDay).HasComment("天");
|
||||
entity.Property(e => e.RegistrCount).HasComment("用户注册数");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasComment("修改时间")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<T_User_LoginDay_Log>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("PK__T_User_L__3214EC0786EBC26A");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
|
||||
namespace CloudGaming.GameModel.Db.Db_Ext;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏统计数据
|
||||
/// </summary>
|
||||
public partial class T_Statistics_Game
|
||||
{
|
||||
public T_Statistics_Game() { }
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期
|
||||
/// </summary>
|
||||
public virtual DateOnly LoginDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 天
|
||||
/// </summary>
|
||||
public virtual int LoginDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户玩游戏时长
|
||||
/// </summary>
|
||||
public virtual int PlayGameTimeCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户玩游戏人数
|
||||
/// </summary>
|
||||
public virtual int PlayGameCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动游戏次数
|
||||
/// </summary>
|
||||
public virtual int StartGameCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 渠道号
|
||||
/// </summary>
|
||||
public virtual string Channel { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public virtual DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 修改时间
|
||||
/// </summary>
|
||||
public virtual DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
|
||||
namespace CloudGaming.GameModel.Db.Db_Ext;
|
||||
|
||||
/// <summary>
|
||||
/// 订单统计表
|
||||
/// </summary>
|
||||
public partial class T_Statistics_Order
|
||||
{
|
||||
public T_Statistics_Order() { }
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期
|
||||
/// </summary>
|
||||
public virtual DateOnly LoginDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 天
|
||||
/// </summary>
|
||||
public virtual int LoginDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 意向订单次数
|
||||
/// </summary>
|
||||
public virtual int IntendedOrderCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付订单次数
|
||||
/// </summary>
|
||||
public virtual int PaidOrders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付订单金额
|
||||
/// </summary>
|
||||
public virtual int RechargeAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 渠道号
|
||||
/// </summary>
|
||||
public virtual string Channel { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public virtual DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 修改时间
|
||||
/// </summary>
|
||||
public virtual DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
|
||||
namespace CloudGaming.GameModel.Db.Db_Ext;
|
||||
|
||||
/// <summary>
|
||||
/// 用户统计表
|
||||
/// </summary>
|
||||
public partial class T_Statistics_User
|
||||
{
|
||||
public T_Statistics_User() { }
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日期
|
||||
/// </summary>
|
||||
public virtual DateOnly LoginDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 天
|
||||
/// </summary>
|
||||
public virtual int LoginDay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录数
|
||||
/// </summary>
|
||||
public virtual int LoginCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户注册数
|
||||
/// </summary>
|
||||
public virtual int RegistrCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户活跃数
|
||||
/// </summary>
|
||||
public virtual int ActiveCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 渠道号
|
||||
/// </summary>
|
||||
public virtual string Channel { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public virtual DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 修改时间
|
||||
/// </summary>
|
||||
public virtual DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
namespace CloudGaming.GameModel.Db.Db_Ext;
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public partial class CloudGamingGameContext : DbContext
|
|||
public virtual DbSet<T_Game_UserShare> T_Game_UserShare { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{// => optionsBuilder.UseSqlServer("Server=192.168.195.6;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
|
||||
{// => optionsBuilder.UseSqlServer("Server=192.168.195.8;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;");
|
||||
}
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
内网
|
||||
dotnet ef dbcontext scaffold "Server=192.168.1.17;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Game/ --use-database-names --no-pluralize --force
|
||||
内网穿透
|
||||
dotnet ef dbcontext scaffold "Server=192.168.195.6;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Game/ --use-database-names --no-pluralize --force
|
||||
dotnet ef dbcontext scaffold "Server=192.168.195.8;Database=CloudGamingGame;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Game/ --use-database-names --no-pluralize --force
|
||||
--Ext
|
||||
dotnet ef dbcontext scaffold "Server=192.168.1.17;Database=CloudGamingCBT;User Id=sa;Password=Dbt@com@123;TrustServerCertificate=true;" Microsoft.EntityFrameworkCore.SqlServer -o Db/Db_Ext/ --use-database-names --no-pluralize --force
|
||||
内网穿透
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.8.1" />
|
||||
<PackageReference Include="Quartz" Version="3.13.1" />
|
||||
<PackageReference Include="Quartz.AspNetCore" Version="3.13.1" />
|
||||
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.13.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Quartz;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
namespace HuanMeng.DotNetCore.QuartzExtend;
|
||||
|
||||
/// <summary>
|
||||
/// 提供扩展方法,用于基于 QuartzTriggerAttribute 注册 Quartz 作业和触发器。
|
||||
/// </summary>
|
||||
public static class QuartzExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 扫描指定程序集中的所有带有 QuartzTriggerAttribute 的作业类,并将它们注册到 Quartz 调度器中。
|
||||
/// </summary>
|
||||
/// <param name="services">依赖注入服务集合。</param>
|
||||
/// <param name="assembly">要扫描的程序集。</param>
|
||||
/// <returns>更新后的服务集合。</returns>
|
||||
public static IServiceCollection AddQuartzWithAttributes(this IServiceCollection services, Assembly assembly)
|
||||
{
|
||||
services.AddQuartz(q =>
|
||||
{
|
||||
// 查找实现了 IJob 接口并带有 QuartzTriggerAttribute 特性的所有类
|
||||
var jobTypes = assembly.GetTypes()
|
||||
.Where(t => t.IsClass && !t.IsAbstract && typeof(IJob).IsAssignableFrom(t));
|
||||
|
||||
foreach (var jobType in jobTypes)
|
||||
{
|
||||
// 获取 QuartzTriggerAttribute 特性实例
|
||||
var attr = jobType.GetCustomAttribute<QuartzTriggerAttribute>();
|
||||
if (attr != null)
|
||||
{
|
||||
// 注册作业
|
||||
var jobKey = new JobKey(jobType.Name); // 使用类名作为作业的唯一标识
|
||||
q.AddJob(jobType, jobKey, opts => opts.WithIdentity(jobKey)); // 将作业注册到调度器
|
||||
|
||||
// 根据特性中定义的调度配置注册触发器
|
||||
if (!string.IsNullOrEmpty(attr.CronExpression))
|
||||
{
|
||||
// 如果定义了 Cron 表达式,使用 Cron 调度
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(jobKey)
|
||||
.WithIdentity(attr.TriggerName)
|
||||
.WithCronSchedule(attr.CronExpression));
|
||||
}
|
||||
else if (attr.IntervalInSeconds.HasValue && attr.IntervalInSeconds > 0)
|
||||
{
|
||||
// 如果定义了简单时间间隔,使用简单调度
|
||||
q.AddTrigger(opts => opts
|
||||
.ForJob(jobKey)
|
||||
.WithIdentity(attr.TriggerName)
|
||||
.StartNow()
|
||||
.WithSimpleSchedule(x => x.WithIntervalInSeconds(attr.IntervalInSeconds.Value).RepeatForever()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 注册 Quartz 的托管服务
|
||||
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.QuartzExtend;
|
||||
|
||||
/// <summary>
|
||||
/// 自定义特性,用于标识 Quartz 作业的触发器配置。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class QuartzTriggerAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取触发器的名称。
|
||||
/// </summary>
|
||||
public string TriggerName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取触发器的时间间隔(秒)。
|
||||
/// </summary>
|
||||
public int? IntervalInSeconds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取 Cron 表达式。
|
||||
/// </summary>
|
||||
public string CronExpression { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 使用 Cron 表达式初始化特性。
|
||||
/// </summary>
|
||||
/// <param name="triggerName">触发器名称。</param>
|
||||
/// <param name="cronExpression">Cron 表达式。</param>
|
||||
public QuartzTriggerAttribute(string triggerName, string cronExpression = "0/1 * * * * ?")
|
||||
{
|
||||
TriggerName = triggerName;
|
||||
IntervalInSeconds = null;
|
||||
CronExpression = cronExpression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用时间间隔初始化特性。
|
||||
/// </summary>
|
||||
/// <param name="triggerName">触发器名称。</param>
|
||||
/// <param name="intervalInSeconds">时间间隔(秒)。</param>
|
||||
public QuartzTriggerAttribute(string triggerName, int intervalInSeconds)
|
||||
{
|
||||
TriggerName = triggerName;
|
||||
IntervalInSeconds = intervalInSeconds;
|
||||
CronExpression = "";
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ namespace HuanMeng.DotNetCore.Redis
|
|||
return database;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
|
@ -195,6 +195,7 @@ namespace HuanMeng.DotNetCore.Redis
|
|||
/// <returns></returns>
|
||||
public static async Task<T?> StringGetAsync<T>(this IDatabase database, string key)
|
||||
{
|
||||
|
||||
var value = await database.StringGetAsync(key);
|
||||
// 检查值是否为空
|
||||
if (!value.HasValue)
|
||||
|
|
@ -232,21 +233,80 @@ namespace HuanMeng.DotNetCore.Redis
|
|||
/// <summary>
|
||||
/// 异步模糊查询key
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="pattern"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="database"></param>
|
||||
/// <param name="server">Redis 服务器实例</param>
|
||||
/// <param name="pattern">匹配的模式(支持通配符)</param>
|
||||
/// <param name="pageSize">每次扫描的页大小</param>
|
||||
/// <param name="database">数据库编号,默认值为 -1 表示全部数据库</param>
|
||||
/// <returns>匹配的键的字符串列表</returns>
|
||||
public static async Task<List<string>> ScanKeysAsync(this IServer server, string pattern, int pageSize = 100, int database = -1)
|
||||
{
|
||||
var matchingKeys = new List<string>();
|
||||
|
||||
await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
|
||||
try
|
||||
{
|
||||
matchingKeys.Add(key.ToString());
|
||||
await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
|
||||
{
|
||||
matchingKeys.Add(key.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 可以记录日志或者处理异常
|
||||
Console.WriteLine($"Error while scanning keys: {ex.Message}");
|
||||
}
|
||||
|
||||
return matchingKeys;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 模糊查询所有匹配的键的数量
|
||||
/// </summary>
|
||||
/// <param name="server">Redis 服务器实例</param>
|
||||
/// <param name="pattern">匹配的模式(支持通配符)</param>
|
||||
/// <returns>匹配的键的数量</returns>
|
||||
public static int ScanKeysCount(this IServer server, string pattern,int database = -1)
|
||||
{
|
||||
int count = 0;
|
||||
long cursor = 0; // 游标,用于记录扫描进度
|
||||
|
||||
do
|
||||
{
|
||||
// 使用 Keys 方法进行分页查询
|
||||
var keys = server.Keys(database: database, cursor: cursor, pattern: pattern, pageSize: 1000).ToArray();
|
||||
|
||||
// 累计匹配到的键的数量
|
||||
count += keys.Length;
|
||||
|
||||
// 如果还有更多键,则获取新的游标;否则,循环终止
|
||||
cursor = keys.Length == 1000 ? 1 : 0;
|
||||
|
||||
} while (cursor != 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步模糊查询所有匹配的键的数量
|
||||
/// </summary>
|
||||
/// <param name="server">Redis 服务器实例</param>
|
||||
/// <param name="pattern">匹配的模式(支持通配符)</param>
|
||||
/// <param name="pageSize">每次扫描的页大小</param>
|
||||
/// <param name="database">数据库编号,默认为 -1 表示当前数据库</param>
|
||||
/// <returns>匹配的键的数量</returns>
|
||||
public static async Task<int> ScanKeysCountAsync(this IServer server, string pattern, int pageSize = 1000, int database = -1)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// 异步遍历 KeysAsync
|
||||
await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user