This commit is contained in:
Putoo
2026-07-07 18:43:01 +08:00
parent 7f29fd46b8
commit 037e845622
37 changed files with 854 additions and 106 deletions

View File

@@ -1,6 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRedisCache_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F55e5569a0f314a0db6a665eafad446dd6800_003F9e_003F88bbb06d_003FIRedisCache_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AISqlSugarClient_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6c0f22f0a47643a3b2a40899acd2044c2e3a00_003F5b_003F113fef0a_003FISqlSugarClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AITimerJobManager_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F16de42b578c24823a56e75bd74225f401c00_003Fbf_003Fb5f6dd44_003FITimerJobManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJwtExtensions_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6b089d08a87d4ddd82371ae63e754ba05000_003F26_003Fd6433acb_003FJwtExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJwtHelper_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6b089d08a87d4ddd82371ae63e754ba05000_003Fe2_003F86047f24_003FJwtHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJwtSecurityTokenHandler_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Ff9d3fc33fd2fba97d4e718534a67f1d3c61e492634cd51c4798ce5621f780f3_003FJwtSecurityTokenHandler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
@@ -15,4 +16,5 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F51505665371d472f8bdbc333fa4d888cf49938_003Fdf_003F23ca03dd_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATask_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F51505665371d472f8bdbc333fa4d888cf49938_003F14_003Fa1e9b3d3_003FTask_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimeAssist_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa380d649d5b2495d9bfa0fa24f13662b6400_003Fe6_003F9539602d_003FTimeAssist_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimerExtensions_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F16de42b578c24823a56e75bd74225f401c00_003Fdd_003Fb2b9eaf5_003FTimerExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATokenValidationParameters_002Ecs_002Fl_003AC_0021_003FUsers_003F29055_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffbb48859ccd84b0d884ab212a4cf9fdb5f820_003F55_003F417754e1_003FTokenValidationParameters_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -0,0 +1,93 @@
using SqlSugar;
using System;
namespace Application.Domain.Entity
{
[Tenant("Kg.SeaTime.Game")]
public class game_onhook
{
/// <summary>
/// userId
/// </summary>
[SugarColumn(IsPrimaryKey = true, Length = 50)]
public string userId { get; set; }
/// <summary>
/// 场景
/// </summary>
[SugarColumn(Length = 50, IsNullable = true)]
public string? scene { get; set; }
/// <summary>
/// copper
/// </summary>
[SugarColumn(IsNullable = true)]
public long? copper { get; set; }
/// <summary>
/// exp
/// </summary>
[SugarColumn(IsNullable = true)]
public long? exp { get; set; }
/// <summary>
/// status
/// </summary>
[SugarColumn(IsNullable = true)]
public int? status { get; set; }
/// <summary>
/// name
/// </summary>
[SugarColumn(Length = 50, IsNullable = true)]
public string? name { get; set; }
/// <summary>
/// award
/// </summary>
[SugarColumn(IsNullable = true)]
public string? award { get; set; }
/// <summary>
/// 平分
/// </summary>
[SugarColumn(Length = 60, IsNullable = true)]
public decimal? score { get; set; }
/// <summary>
/// 时间/秒
/// </summary>
[SugarColumn(IsNullable = true)]
public int? time { get; set; }
/// <summary>
/// uptime
/// </summary>
[SugarColumn(IsNullable = true)]
public DateTime? uptime { get; set; }
/// <summary>
/// endTime
/// </summary>
[SugarColumn(IsNullable = true)]
public DateTime? endTime { get; set; }
/// <summary>
/// 停止时间
/// </summary>
[SugarColumn(IsNullable = true)]
public long? stopTime { get; set; }
/// <summary>
/// sumCopper
/// </summary>
[SugarColumn(IsNullable = true)]
public long? sumCopper { get; set; }
/// <summary>
/// sumExp
/// </summary>
[SugarColumn(IsNullable = true)]
public long? sumExp { get; set; }
}
}

View File

@@ -0,0 +1,63 @@
using SqlSugar;
using System;
namespace Application.Domain.Entity
{
[Tenant("Kg.SeaTime.Resource")]
public class game_job
{
/// <summary>
/// jobId
/// </summary>
[SugarColumn(Length = 50, IsNullable = true)]
public string? jobId { get; set; }
/// <summary>
/// name
/// </summary>
[SugarColumn(Length = 50, IsNullable = true)]
public string? name { get; set; }
/// <summary>
/// code
/// </summary>
[SugarColumn(Length = 255, IsNullable = true)]
public string? code { get; set; }
/// <summary>
/// par
/// </summary>
[SugarColumn(IsNullable = true)]
public string? par { get; set; }
/// <summary>
/// opType
/// </summary>
[SugarColumn(Length = 50, IsNullable = true)]
public string? opType { get; set; }
/// <summary>
/// cron
/// </summary>
[SugarColumn(Length = 50, IsNullable = true)]
public string? cron { get; set; }
/// <summary>
/// state
/// </summary>
[SugarColumn(IsNullable = true)]
public int? state { get; set; }
/// <summary>
/// addTime
/// </summary>
[SugarColumn(IsNullable = true)]
public DateTime? addTime { get; set; }
/// <summary>
/// remark
/// </summary>
[SugarColumn(Length = 255, IsNullable = true)]
public string? remark { get; set; }
}
}

View File

@@ -10,6 +10,7 @@ namespace Application.Domain
{
public enum BusEventsName
{
HandleOnHook,//处理挂机
PutFightAward,//战斗奖励事件
}
}

View File

@@ -13,5 +13,12 @@
var fightService = App.GetService<IGameFightService>();
await fightService.HandleFight(data);
}
[EventSubscribe(BusEventsEnum.BusEventsName.HandleOnHook, NumRetries = 0,RetryTimeout = 999999999)]
public async Task HandleOnHook(EventHandlerExecutingContext context)
{
var hookService = App.GetService<IOnHookService>();
await hookService.HandleHook();
}
}
}

View File

@@ -2,6 +2,15 @@
public static class GameEnum
{
public enum JobType
{
single,//1次
alway,//永久
}
public enum JobCode
{
OnHook,//定时任务
}
public enum PropCode//游戏资源编码
{
Monster,//怪物

View File

@@ -10,4 +10,10 @@ public class MapEnum
add_blood,
add_copper,
}
public enum MapCode
{
DEF_MAP,
Dup_Map
}
}

View File

@@ -0,0 +1,10 @@
namespace Application.Domain;
public interface IGameAutoJobService
{
Task<List<game_job>> GetJobData();
Task<game_job> GetJobInfo(string jobId);
Task StartJob();
Task StopJob(string jobId);
Task HandleJob(game_job data);
}

View File

@@ -0,0 +1,12 @@
namespace Application.Domain;
public interface IOnHookService
{
Task<game_onhook> GetUserOnHook(string userId);
Task<bool> StartOnHook(string userId,string scene, long copper, long exp, string name, string award, decimal score,
int time, int onHookTime);
Task<bool> StopOnHook(string userId);
Task HandleHook();
}

View File

@@ -57,7 +57,7 @@ public interface IUnitUserAttrService
#region BUFF状态
Task<List<unit_user_state>> GetUserStateData(string userId, string scene = "Default");
Task<decimal> GetUserStateDataTotal(string userId, string scene = "Default");
Task<bool> AddUserState(string userId, string groupId, string name, string scene, string tips,
List<AttrItem> attr, int time, int count = 1);

View File

@@ -0,0 +1,64 @@
using Photon.Core.Timer;
namespace Application.Domain;
public class GameAutoJobService(ISqlSugarClient DbClient, IRedisCache redis,ITimerJobManager jobManager) : IGameAutoJobService, ITransient
{
public async Task<List<game_job>> GetJobData()
{
var db = DbClient.AsTenant().GetConnectionWithAttr<game_job>();
return await db.Queryable<game_job>().Where(it => it.state == 1).ToListAsync();
}
public async Task<game_job> GetJobInfo(string jobId)
{
string key = string.Format(BaseCache.BaseCacheKey,"JobData");
if (await redis.HExistsHashAsync(key, jobId))
{
return await redis.GetHashAsync<game_job>(key, jobId);
}
var db = DbClient.AsTenant().GetConnectionWithAttr<game_job>();
var data = await db.Queryable<game_job>().Where(it => it.jobId == jobId).SingleAsync();
await redis.AddHashAsync(key, jobId, data);
return data;
}
public async Task StartJob()
{
Console.WriteLine(">>>开始执行定时任务>>>");
var jobData = await GetJobData();
if (jobData.Count > 0)
{
foreach (var item in jobData)
{
await jobManager.CreateAndStartTask(item.jobId, item.code, item.cron);
Console.WriteLine($"-已启动--->{item.name}");
}
}
Console.WriteLine("<<<定时任务初始化完成<<<");
await Task.CompletedTask;
}
public async Task StopJob(string jobId)
{
var db = DbClient.AsTenant().GetConnectionWithAttr<game_job>();
bool result = await db.Updateable<game_job>().SetColumns(it => it.state == 0).Where(it => it.jobId == jobId)
.ExecuteCommandAsync() > 0;
if (result)
{
await jobManager.DeleteTask(jobId);
}
}
public async Task HandleJob(game_job data)
{
if (data.code == nameof(GameEnum.JobCode.OnHook))//挂机
{
var _eventPublisher = App.GetService<IEventPublisher>();
await _eventPublisher.PublishAsync(new ChannelEventSource(BusEventsEnum.BusEventsName.HandleOnHook,
data));
}
}
}

View File

@@ -0,0 +1,167 @@
using Newtonsoft.Json;
namespace Application.Domain;
public class OnHookService(ISqlSugarClient DbClient, IRedisCache redis) : IOnHookService, ITransient
{
public async Task<game_onhook> GetUserOnHook(string userId)
{
string key = string.Format(UserCache.BaseCacheKey, "OnHook");
if (await redis.HExistsHashAsync(key, userId))
{
return await redis.GetHashAsync<game_onhook>(key, userId);
}
var db = DbClient.AsTenant().GetConnectionWithAttr<game_onhook>();
var data = await db.Queryable<game_onhook>().Where(it => it.userId == userId).SingleAsync();
if (data == null)
{
data = new game_onhook();
data.userId = userId;
data.scene = "";
data.copper = 0;
data.exp = 0;
data.status = 0;
data.name = "";
data.award = "";
data.score = 0;
data.time = 0;
data.uptime = DateTime.Now;
data.endTime = DateTime.Now;
data.stopTime = TimeExtend.GetTimeStampSeconds;
data.sumExp = 0;
data.sumCopper = 0;
await db.Insertable(data).ExecuteCommandAsync();
}
await redis.AddHashAsync(key, userId, data);
return data;
}
private async Task ClearOnHookCache(string userId)
{
string key = string.Format(UserCache.BaseCacheKey, "OnHook");
await redis.DelHashAsync(key, userId);
}
public async Task<bool> StartOnHook(string userId, string scene, long copper, long exp, string name, string award,
decimal score,
int time, int onHookTime)
{
game_onhook data = new game_onhook();
data.userId = userId;
data.scene = scene;
data.copper = copper;
data.exp = exp;
data.status = 1;
data.name = name;
data.award = award;
data.score = score;
data.time = time;
data.uptime = DateTime.Now;
DateTime end = DateTime.Now.AddHours(onHookTime);
data.endTime = end;
data.stopTime = TimeExtend.GetTimeStampBySeconds(end);
data.sumExp = 0;
data.sumCopper = 0;
var db = DbClient.AsTenant().GetConnectionWithAttr<game_onhook>();
bool result = await db.Saveable(data).ExecuteCommandAsync() > 0;
if (result)
{
await ClearOnHookCache(userId);
}
return result;
}
public async Task<bool> StopOnHook(string userId)
{
bool result = true;
var onHook = await GetUserOnHook(userId);
if (onHook.status == 1)
{
var db = DbClient.AsTenant().GetConnectionWithAttr<game_onhook>();
result = await db.Updateable<game_onhook>().SetColumns(it => it.status == 0)
.Where(it => it.userId == userId).ExecuteCommandAsync() > 0;
if (result)
{
await ClearOnHookCache(userId);
}
}
return result;
}
private async Task<bool> UpdateOnHook(string userId, long exp, long copper)
{
var db = DbClient.AsTenant().GetConnectionWithAttr<game_onhook>();
var result = await db.Updateable<game_onhook>()
.SetColumnsIF(exp > 0, it => it.sumExp == it.sumExp + exp)
.SetColumnsIF(copper > 0, it => it.sumCopper == it.sumCopper + copper)
.SetColumns(it => it.uptime == DateTime.Now)
.Where(it => it.userId == userId).ExecuteCommandAsync() > 0;
if (result)
{
await ClearOnHookCache(userId);
}
return result;
}
public async Task HandleHook()
{
long onTime = TimeExtend.GetTimeStampSeconds;
var accService = App.GetService<IUnitUserAccService>();
var attrService = App.GetService<IUnitUserAttrService>();
var db = DbClient.AsTenant().GetConnectionWithAttr<game_onhook>();
var data = await db.Queryable<game_onhook>().Where(it => it.status == 1).ToListAsync();
foreach (var hook in data)
{
if (hook.stopTime < onTime)
{
await StopOnHook(hook.userId);
continue;
}
var userAttr = await attrService.GetUserAttrModel(hook.userId, hook.scene);
if (userAttr.score < hook.score)
{
await StopOnHook(hook.userId);
continue;
}
int allTime = TimeAssist.TimeDiffSeconds((DateTime)hook.uptime, DateTime.Now) * 1000;
int opCount = Convert.ToInt32(allTime / hook.time);
long exp = Convert.ToInt64(hook.exp * (1.0m + userAttr.addExp) * opCount);
long copper = Convert.ToInt64(hook.copper * (1.0m + userAttr.addGold) * opCount);
List<TowerGet> award = new List<TowerGet>();
if (!string.IsNullOrEmpty(hook.award))
{
var awardModel = JsonConvert.DeserializeObject<MonsterAwardModel>(hook.award);
if (awardModel.type == nameof(MonsterEnum.AwardCode.Default))
{
var getAward = GameBus.GetRandomGoods(awardModel.award, opCount, userAttr.burst);
award.AddRange(getAward);
}
}
if (await UpdateOnHook(hook.userId, exp, copper))
{
if (exp > 0)
{
await attrService.UpdateUserExp(hook.userId, exp);
}
if (copper > 0)
{
await accService.UpdateUserCopper(hook.userId, 1, copper, "挂机获得");
}
if (award.Count > 0)
{
await GameBus.UpdateBag(hook.userId, 1, award, "挂机获得");
}
}
}
}
}

View File

@@ -121,17 +121,17 @@ public class GameChatService : IGameChatService, ITransient
chat.sign = sign;
chat.state = 1;
chat.addTime = DateTime.Now;
chat.sort = TimeExtend.GetTimeStampSeconds;
chat.sort = TimeExtend.GetTimeStampMilliseconds;
chat.delTime = TimeExtend.GetTimeStampBySeconds(DateTime.Now.AddDays(3));
chat.opUser = "";
switch (code)
{
case "Region":
chat.sort += 300; //五分钟
chat.sort += 300 * 1000; //五分钟
break;
case "Dress":
chat.sort += 600; //10分钟
chat.sort += 600 * 1000; //10分钟
break;
case "System":
chat.delTime = TimeExtend.GetTimeStampBySeconds(DateTime.Now.AddHours(6));

View File

@@ -102,8 +102,8 @@ public class GameFightService(ISqlSugarClient DbClient, IRedisCache redis) : IGa
string scene, string mapId,
string userId, long exp = 0, long copper = 0, long petExp = 0, string award = "", string pars = "")
{
long addTime = TimeExtend.GetTimeStampSeconds;
long endTime = addTime + 24 * 60 * 60;
long addTime = TimeExtend.GetTimeStampMilliseconds;
long endTime = TimeExtend.GetTimeStampSeconds + 24 * 60 * 60;
List<game_fight_data> fights = new List<game_fight_data>();
@@ -170,7 +170,7 @@ public class GameFightService(ISqlSugarClient DbClient, IRedisCache redis) : IGa
public async Task<bool> SetFightWin(game_fight_data fightData, string winUser,bool setDefMap=true)
{
bool result = false;
long okTime = TimeExtend.GetTimeStampSeconds;
long okTime = TimeExtend.GetTimeStampMilliseconds;
var db = DbClient.AsTenant().GetConnectionWithAttr<game_fight_data>();
if (fightData.code == nameof(GameEnum.FightCode.PVE))
{

View File

@@ -243,6 +243,8 @@ public class GameMapService(ISqlSugarClient DbClient, IRedisCache redis) : IGame
else
{
//更新挂机,结束挂机
var hookService = App.GetService<IOnHookService>();
await hookService.StopOnHook(userId);
}
if (mapInfo == null)

View File

@@ -649,6 +649,17 @@ public class UnitUserAttrService(ISqlSugarClient DbClient, IRedisCache redis) :
await redis.DelHashAsync(key, userId);
}
public async Task<decimal> GetUserStateDataTotal(string userId, string scene = "Default")
{
var data = await GetUserStateData(userId, scene);
List<AttrItem> result = new List<AttrItem>();
foreach (var item in data)
{
result.AddRange(item.attr);
}
return GameAttrTool.GetAttrItemValue(nameof(GameEnum.AttrCode.AddExp), result, 0);
}
#endregion
#region

View File

@@ -0,0 +1,24 @@
using Photon.Core.Timer;
using Quartz;
namespace Application.Domain;
public class AutoJob: ITimerAutoJob
{
public async Task Execute(IJobExecutionContext context)
{
string jobId = context.JobDetail.Key.Name;
if (!string.IsNullOrEmpty(jobId))
{
var jobService = App.GetService<IGameAutoJobService>();
var jobData = await jobService.GetJobInfo(jobId);
if (jobData != null)
{
await jobService.HandleJob(jobData);
if (jobData.opType == nameof(GameEnum.JobType.single))
{
await jobService.StopJob(jobId);
}
}
}
}
}

View File

@@ -1,7 +1,8 @@
using Quartz;
using Photon.Core.Timer;
using Quartz;
using Quartz.Spi;
namespace Application.Web;
namespace Application.Domain;
public class JobStart: ITimerAutoStart
{
private readonly ISchedulerFactory _schedulerFactory;

View File

@@ -1,6 +1,7 @@
using Quartz;
using Photon.Core.Timer;
using Quartz;
namespace Application.Web;
namespace Application.Domain;
public class TimerJobManager : ITimerJobManager
{
@@ -33,7 +34,7 @@ public class TimerJobManager : ITimerJobManager
await _scheduler.Start();
}
}
public async Task PauseTask(string jobId)
{
await _scheduler.PauseJob(new JobKey(jobId, "default"));

View File

@@ -0,0 +1,25 @@
namespace Application.Web;
public class AppBackgroundService:BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
Console.WriteLine("应用启动完成,开始执行初始化后台任务");
// ========== 你要执行的启动方法写在这里 ==========
var jobService = App.GetService<IGameAutoJobService>();
await jobService.StartJob();
// ==========================================
Console.WriteLine("启动初始化任务执行完成");
// 如果只需要执行一次直接退出如果是常驻定时任务就写while循环
return;
}
catch (Exception ex)
{
Console.WriteLine("启动后台任务执行异常");
}
}
}

View File

@@ -23,11 +23,13 @@ public class MapController : ControllerBase
private readonly IGameTeamService _teamService;
private readonly IGameMonsterService _monsterService;
private readonly IGameFightService _fightService;
private readonly IOnHookService _hookService;
public MapController(IUnitUserService userService, IGameMapService mapService, IGameChatService chatService,
IUnitUserAttrService attrService, IMessageService messageService, IUnitUserWeight weightService,
IUnitUserAccService accService, IGameSkillService skillService, IGameGoodsService goodsService,
IGameTeamService teamService, IGameMonsterService monsterService, IGameFightService fightService)
IGameTeamService teamService, IGameMonsterService monsterService, IGameFightService fightService,
IOnHookService hookService)
{
_userService = userService;
_mapService = mapService;
@@ -41,6 +43,7 @@ public class MapController : ControllerBase
_teamService = teamService;
_monsterService = monsterService;
_fightService = fightService;
_hookService = hookService;
}
#region
@@ -150,6 +153,10 @@ public class MapController : ControllerBase
var mapGoods = await _fightService.GetMapFightGoods(mapInfo.mapId);
mapGoods = mapGoods.FindAll(it => it.userId == "0" || it.userId == userId).Take(3).ToList();
int isHook = 0; //挂机处理
var onHook = await _hookService.GetUserOnHook(userId);
isHook = (int)onHook.status;
object ret = new
{
mapInfo,
@@ -165,9 +172,11 @@ public class MapController : ControllerBase
broadcast,
monster = monsterData,
fightId,
gameTips
gameTips,
isHook
};
return PoAction.Ok(ret);
}

View File

@@ -18,10 +18,11 @@ public class FightController : ControllerBase
private readonly IUnitUserAccService _accService;
private readonly IGameGoodsService _goodsService;
private readonly IUnitUserService _userService;
private readonly IOnHookService _hookService;
public FightController(IGameFightService fightService, IGameMonsterService monsterService,
IUnitUserAttrService attrService, IGameMapService mapService, IUnitUserAccService accService,
IGameGoodsService goodsService, IUnitUserService userService)
IGameGoodsService goodsService, IUnitUserService userService,IOnHookService hookService)
{
_fightService = fightService;
_monsterService = monsterService;
@@ -30,6 +31,7 @@ public class FightController : ControllerBase
_accService = accService;
_goodsService = goodsService;
_userService = userService;
_hookService = hookService;
}
/// <summary>
@@ -99,6 +101,7 @@ public class FightController : ControllerBase
copper, petExp, award, pars);
if (result)
{
await _hookService.StopOnHook(userId);
return PoAction.Ok(fightId);
}
else
@@ -398,8 +401,6 @@ public class FightController : ControllerBase
public async Task<IPoAction> GetFightMsg(string fightId)
{
string userId = StateHelper.userId;
var myFight = await _fightService.GetUserFight(userId);
var fight = await _fightService.GetFightInfo(fightId);
if (fight == null)
{
@@ -430,6 +431,7 @@ public class FightController : ControllerBase
isWin = 2;
}
int isCopy = 0;
string otName = string.Empty;
if (isWin == 0)
{
@@ -455,6 +457,7 @@ public class FightController : ControllerBase
{
var monsterInfo = await _monsterService.GetMonsterInfo(fight.mainId);
otName = monsterInfo.name;
isCopy = (int)monsterInfo.isCopy;
}
}
else
@@ -474,6 +477,104 @@ public class FightController : ControllerBase
var myAttr = await _attrService.GetUserAttrModel(userId, fight.scene);
return PoAction.Ok(new { isWin, otName, blood = myAttr.blood, upBlood = myAttr.upBlood, fight });
return PoAction.Ok(new { isWin, otName, blood = myAttr.blood, upBlood = myAttr.upBlood, fight, isCopy });
}
#region
/// <summary>
/// 个人挂机信息
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<IPoAction> GetUserHook()
{
string userId = StateHelper.userId;
var onHook = await _hookService.GetUserOnHook(userId);
decimal expAdd = 0;
decimal copperAdd = 0;
if (onHook.status == 1)
{
var myAttr = await _attrService.GetUserAttrModel(userId, onHook.scene);
expAdd = myAttr.addExp;
copperAdd = myAttr.addGold;
onHook.exp = Convert.ToInt32(onHook.exp * (1.0M + expAdd));
onHook.copper = Convert.ToInt32(onHook.copper * (1.0M + copperAdd));
}
return PoAction.Ok(new { data = onHook, expAdd, copperAdd });
}
/// <summary>
/// 挂机
/// </summary>
/// <param name="fightId"></param>
/// <returns></returns>
[HttpGet]
public async Task<IPoAction> OnHook(string fightId)
{
string userId = StateHelper.userId;
var fight = await _fightService.GetFightInfo(fightId);
if (fight == null)
{
return PoAction.Message("战斗不存在!");
}
if (fight.userId != userId)
{
return PoAction.Message("战斗不存在!");
}
if (fight.code != nameof(GameEnum.FightCode.PVE))
{
return PoAction.Message("该战斗不可复刻挂机!");
}
var monster = await _monsterService.GetMonsterInfo(fight.mainId);
if (monster == null)
{
return PoAction.Message("该战斗不可复刻挂机!");
}
if (monster.isCopy == 0)
{
return PoAction.Message("该战斗不可复刻挂机!");
}
var myAttr = await _attrService.GetUserAttrModel(userId, fight.scene);
int diffTime = Convert.ToInt32(fight.okTime - fight.addTime);
decimal score = myAttr.score * 0.7M;
bool result = await _hookService.StartOnHook(userId, fight.scene, (long)monster.copper, (long)monster.exp,
monster.name,
monster.award, score, diffTime, 6);
if (result)
{
return PoAction.Ok("挂机成功!");
}
else
{
return PoAction.Message("挂机失败,请稍后尝试!");
}
}
/// <summary>
/// 结束挂机
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<IPoAction> StopOnHook()
{
string userId = StateHelper.userId;
bool result = await _hookService.StopOnHook(userId);
if (result)
{
return PoAction.Ok(true);
}
else
{
return PoAction.Message("操作失败,请稍后尝试!");
}
}
#endregion
}

View File

@@ -130,6 +130,21 @@ public class UserController : ControllerBase
return PoAction.Ok(model);
}
/// <summary>
/// 获取个人特性
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<IPoAction> GetUserBaseAttrData()
{
string userId = StateHelper.userId;
var model = await _attrService.GetUserAttrModel(userId);
var unitExp = await _attrService.GetUserStateDataTotal(userId, nameof(MapEnum.MapCode.DEF_MAP));
var dupExp = await _attrService.GetUserStateDataTotal(userId, nameof(MapEnum.MapCode.Dup_Map));
return PoAction.Ok(new { data = model, unitExp, dupExp });
}
/// <summary>
/// 获取用户资料
/// </summary>
@@ -405,7 +420,7 @@ public class UserController : ControllerBase
temp.id = StringAssist.NewGuid;
temp.goodsId = goodsId;
temp.name = myGoods.goodsName;
temp.code =Convert.ToString(_drugConfig.cls);
temp.code = Convert.ToString(_drugConfig.cls);
temp.count = count;
myDrug.drug.Add(temp);
}

View File

@@ -1,22 +0,0 @@
using Quartz;
namespace Application.Web;
public class AutoJob: ITimerAutoJob
{
public async Task Execute(IJobExecutionContext context)
{
string jobId = context.JobDetail.Key.Name;
if (jobId == "DayHandle")
{
////处理日红包
//await businessService.HandleRedPack();
//Console.WriteLine($"{DateTime.Now.ToString("yyyy-MMMM-dd")}:红包奖金池已处理!");
}
else
{
Console.WriteLine("定时程序测试执行");
}
}
}

View File

@@ -1,5 +1,6 @@
using Microsoft.OpenApi.Models;
using Photon.Core.Redis;
using Photon.Core.Timer;
var builder = WebApplication.CreateBuilder(args);
@@ -45,14 +46,19 @@ builder.Services.InjectJwt(builder.Configuration, new OpenApiInfo
}, groups);
#endregion
//定时功能
builder.Services.InjectTimer(services =>
//自动程序
string autoConfig = builder.Configuration["AutoProgram"]!;
if (autoConfig == "1")
{
services.AddTransient<ITimerJobManager, TimerJobManager>();//注册管理器
services.AddSingleton<AutoJob>();
services.AddHostedService<JobStart>();
});
//定时功能
builder.Services.InjectTimer(services =>
{
services.AddTransient<ITimerJobManager, TimerJobManager>();//注册管理器
services.AddSingleton<AutoJob>();
});
builder.Services.AddHostedService<AppBackgroundService>();
}
//日志
builder.Logging.InjectLog();

View File

@@ -1,5 +1,5 @@
{
"AutoProgram": 0,
"AutoProgram": 1,
"Redis": {
"connection": "127.0.0.1:6379,password=,defaultdatabase=5"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -57,6 +57,6 @@ onMounted(() => {
/* 全局样式已移至 src/assets/css/style.css */
:root {
--van-base-font: initial !important;
--van-base-font: initial !important;
}
</style>

View File

@@ -143,9 +143,10 @@ a:focus {
}
.search-ipt {
width: 80px;
font-size: 11px;
width: 80px;
padding: 0;
font:caption;
font-size: 16px;
}
.ipt-btn {
@@ -351,4 +352,12 @@ a:focus {
.equ_glod {
color: #e6b020;
}
.dup {
background-image: url(/images/site/dup.gif);
background-repeat: no-repeat;
display: inline-block;
width: 18px;
height: 18px;
background-size: 18px;
}

View File

@@ -3,7 +3,7 @@
<div class="foot">
<div class="common">
<Abar href="/">首页</Abar>-
<Abar href="/">挂机</Abar>-
<Abar href="/user/onhook">挂机</Abar>-
<!-- <Abar href="/treasure">聚宝盆</Abar>- -->
<a target="_blank" href="https://work.weixin.qq.com/kfid/kfc86bc348120aea3e7" style="text-decoration: underline;">反馈</a>
</div>

View File

@@ -2,8 +2,8 @@
统一配置中心
*/
export class BaseConfig {
public static BaseUrl: string = 'https://localhost:7198'
public static BaseMediaUrl: string = 'https://localhost:7198/'
// public static BaseUrl:string="http://kx.iyba.cn:5291";
// public static BaseMediaUrl:string="http://kx.iyba.cn:5291";
// public static BaseUrl: string = 'https://localhost:7198'
// public static BaseMediaUrl: string = 'https://localhost:7198/'
public static BaseUrl:string="http://kx.iyba.cn:5291";
public static BaseMediaUrl:string="http://kx.iyba.cn:5291";
}

View File

@@ -8,6 +8,9 @@
<div class="common">
<Abar href="/map">继续</Abar>
</div>
<div class="common" v-if="isCopy == 1">
[<Abutton @click="onHook">立即挂机</Abutton>]
</div>
<div class="common">
你体力:{{ blood }}/{{ upBlood }}
</div>
@@ -38,6 +41,7 @@ const blood = ref(0);
const upBlood = ref(0);
const fight = ref<any>({});
const AwardTips = ref('');
const isCopy = ref(0);
let fightId = PageExtend.QueryString("f");
onMounted(async () => {
@@ -57,6 +61,7 @@ const BindData = async (): Promise<void> => {
blood.value = result.data.blood;
upBlood.value = result.data.upBlood;
fight.value = result.data.fight;
isCopy.value = result.data.isCopy;
if (fight.value.award != '') {
AwardTips.value = awardTipsStr(JSON.parse(fight.value.award))
}
@@ -89,4 +94,18 @@ const awardTipsStr = (data: any) => {
return result;
}
/**挂机 */
const onHook = async () => {
MessageExtend.LoadingToast("正在进行挂机...");
let result = await FightService.OnHook(fightId);
MessageExtend.LoadingClose();
if (result.code == 0) {
isCopy.value = 0;
MessageExtend.Notify("挂机成功!", "success");
}
else {
MessageExtend.Notify(result.msg, "danger");
}
}
</script>

View File

@@ -1,5 +1,11 @@
<template>
<GameBroadcast :data="broadcast"></GameBroadcast>
<div class="content" v-if="isHook==1">
<div class="item" style="font-size:15px;font-weight:bold;color:green">
<i class="dup"></i>
<Abar href="/user/onhook" style="color:green">小宝贝,正在挂机中...</Abar>
</div>
</div>
<div class="content">
{{ cityInfo.cityName }}·{{ mapInfo.mapName }}({{ mapInfo.x }},{{ mapInfo.y }})
<Abutton @click="Refresh">刷新</Abutton>
@@ -136,6 +142,7 @@ const monster = ref<Array<any>>([]);
const messageCount = ref(0);
const broadcast = ref<Array<any>>([]);
const gameTips = ref('');
const isHook = ref(0);
const mapGoods = ref<Array<any>>([]);
// 城内地图显示
const showCity = ref(false);
@@ -171,6 +178,7 @@ const BindData = async (map: string): Promise<void> => {
onMap.value = mapInfo.value.mapId;
gameTips.value = result.data.gameTips;
mapGoods.value = result.data.mapGoods;
isHook.value = result.data.isHook;
// console.log(result)
}
else if (result.code == 103) {

View File

@@ -1,58 +1,55 @@
<template>
<div class="content">特殊属性</div>
<div class="content">
<span>幸运:0%</span>&nbsp;&nbsp;&nbsp;<span>爆率:0%</span><br>
<span>升空:0</span>&nbsp;&nbsp;&nbsp;<span>下潜:0</span>
</div>
<div class="content">经验铜贝</div>
<div class="content">
<div class="module-title">特殊属性</div>
<div class="module-content">
<span>幸运:{{ data.luck * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>爆率:{{ data.burst * 100 }}%</span><br>
<!-- <span>升空:0</span>&nbsp;&nbsp;&nbsp;<span>下潜:0</span> -->
<span>普通经验:0%</span>&nbsp;&nbsp;&nbsp;<span>副本经验:0%</span><br>
<span>铜贝增益:0%</span>
<span>铜贝增益:{{ data.addGold * 100 }}%</span>
</div>
<div class="content">基础属性</div>
<div class="content">
<span>吸血:{{data.inBlood * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>制裁:{{data.punish * 100}}%</span><br>
<span>暴击:{{data.crit * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>闪避:{{data.dodge * 100}}%</span><br>
<span>免伤:{{data.noHarm * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>额外伤害:{{data.harm_Add}}</span><br>
<span>反弹:{{data.rebound * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗反弹:{{data.reboundLess * 100}}%</span><br>
<span>迟缓:{{data.slow * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗迟缓:{{data.slowLess * 100}}%</span><br>
<span>中毒:{{data.poison * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗中毒:{{data.poisonLess * 100}}%</span><br>
<span>诅咒:{{data.curse * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗诅咒:{{data.curseLess * 100}}%</span><br>
<span>弱化:{{data.weaken * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗弱化:{{data.weakenLess * 100}}%</span><br>
<span>致命:{{data.deadly * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗致命:{{data.deadlyLess * 100}}%</span><br>
<span>沮丧:{{data.depressed * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗沮丧:{{data.depressedLess * 100}}%</span><br>
<span>突破:{{data.breach * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗突破:{{data.breachLess * 100}}%</span><br>
<span>麻痹:{{data.palsyAtk * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>抗麻痹:{{data.palsyAtkLess * 100}}%</span><br>
<span>卸时装:{{data.del_Fashion * 100}}%</span>&nbsp;&nbsp;&nbsp;<span>连击:{{data.atk_Next * 100}}%</span><br>
<div class="module-title">基础属性</div>
<div class="module-content">
<span>吸血:{{ data.inBlood * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>制裁:{{ data.punish * 100 }}%</span><br>
<span>暴击:{{ data.crit * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>闪避:{{ data.dodge * 100 }}%</span><br>
<span>免伤:{{ data.noHarm * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>额外伤害:{{ data.harm_Add }}</span><br>
<span>反弹:{{ data.rebound * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗反弹:{{ data.reboundLess * 100 }}%</span><br>
<span>迟缓:{{ data.slow * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗迟缓:{{ data.slowLess * 100 }}%</span><br>
<span>中毒:{{ data.poison * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗中毒:{{ data.poisonLess * 100 }}%</span><br>
<span>诅咒:{{ data.curse * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗诅咒:{{ data.curseLess * 100 }}%</span><br>
<span>弱化:{{ data.weaken * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗弱化:{{ data.weakenLess * 100 }}%</span><br>
<span>致命:{{ data.deadly * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗致命:{{ data.deadlyLess * 100 }}%</span><br>
<span>沮丧:{{ data.depressed * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗沮丧:{{ data.depressedLess * 100 }}%</span><br>
<span>突破:{{ data.breach * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗突破:{{ data.breachLess * 100 }}%</span><br>
<span>麻痹:{{ data.palsyAtk * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>抗麻痹:{{ data.palsyAtkLess * 100 }}%</span><br>
<span>卸时装:{{ data.del_Fashion * 100 }}%</span>&nbsp;&nbsp;&nbsp;<span>连击:{{ data.atk_Next * 100 }}%</span><br>
</div>
</template>
<script setup lang="ts">
import { UserService } from '../../services/user/UserService';
definePageMeta({
layout: layout.default,
middleware: middleware.loading
})
definePageMeta({
layout: layout.default,
middleware: middleware.loading
})
const data = ref<any>([]);
onMounted(async () => {
try {
await BindData();
}
finally {
PageLoading.Close();
}
})
const BindData = async () => {
let result = await UserService.GetUserBaseAttrInfo();
if (result.code == 0) {
data.value = result.data;
console.log(data)
}
else {
MessageExtend.ShowDialog("特性", result.msg);
}
const data = ref<any>([]);
const unitExp = ref(0);
const dupExp = ref(0);
onMounted(async () => {
try {
await BindData();
}
finally {
PageLoading.Close();
}
})
const BindData = async () => {
let result = await UserService.GetUserBaseAttrData();
if (result.code == 0) {
data.value = result.data.data;
unitExp.value = result.data.unitExp;
dupExp.value = result.data.dupExp;
}
else {
MessageExtend.ShowDialog("特性", result.msg);
}
}
</script>

View File

@@ -0,0 +1,76 @@
<template>
<div class="module-title">挂机状态</div>
<div class="module-content" v-if="data.status == 1">
挂机对象{{ data.name }}<br>
战斗用时{{ data.time }} <span style="color: green;font-size: 14px;">ms</span> <br>
战力要求{{ Number(data.score).toFixed(2) }}<br>
挂机经验{{ data.exp }}经验/(+{{ Number(expAdd)*100 }}%)<br>
挂机铜贝{{ data.copper }}铜贝/(+{{ Number(copperAdd)*100 }}%)<br>
获得经验{{data.sumExp}}<br>
获得铜贝{{data.sumCopper}}<br>
上次更新{{TimeExtend.Format(data.uptime)}}<br>
结束时间{{TimeExtend.Format(data.endTime)}}<br>
[<Abutton @click="StopHook">结束挂机</Abutton>]
</div>
<div class="module-content" v-else>
未进行挂机!
</div>
<div class="module-title">挂机说明</div>
<div class="module-content" style="font-size: 16px;">
1.挂机状态每5分钟自动结算一次<br>
2.挂机状态中主动攻击怪物或玩家,自动结束挂机状态<br>
3.挂机状态获得的经验高低与战力有关,同时与受普通经验卡等倍数加层,具体加层倍数可通过状态->特性中查看<br>
4.挂机时间普通挂机6个小时紫钻挂机12个小时中间被击杀(体力0)会被结束挂机状态<br>
5.当结束挂机后玩家需要重新在可挂机的怪物处击杀怪物并完成战斗复刻<br>
6.挂机状态下在挂机地点不可移动<br>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: layout.default,
middleware: 'page-loading'
})
const data = ref<any>({});
const expAdd = ref(0);
const copperAdd = ref(0);
onMounted(async () => {
try {
await BindData();
}
finally {
PageLoading.Close();
}
})
const BindData = async (): Promise<void> => {
let result = await FightService.GetUserHook();
if (result.code == 0) {
data.value = result.data.data;
expAdd.value = result.data.expAdd;
copperAdd.value = result.data.copperAdd;
}
else {
MessageExtend.ShowDialog("提示", result.msg);
}
};
/**结束挂机 */
const StopHook = async () => {
MessageExtend.LoadingToast("正在结束挂机...");
let result = await FightService.StopOnHook();
MessageExtend.LoadingClose();
if (result.code == 0) {
await BindData();
MessageExtend.Notify("挂机已结束!", "success");
}
else {
MessageExtend.Notify(result.msg, "danger");
}
}
</script>

View File

@@ -43,4 +43,28 @@ export class FightService {
static async GetFightMsg(fightId: string) {
return await ApiService.Request("get", "/Fight/GetFightMsg", { fightId });
}
/**
* 个人挂机信息
* GET /Fight/GetUserHook
*/
static async GetUserHook() {
return await ApiService.Request("get", "/Fight/GetUserHook");
}
/**
* 挂机
* GET /Fight/OnHook
*/
static async OnHook(fightId: string) {
return await ApiService.Request("get", "/Fight/OnHook", { fightId });
}
/**
* 结束挂机
* GET /Fight/StopOnHook
*/
static async StopOnHook() {
return await ApiService.Request("get", "/Fight/StopOnHook");
}
}

View File

@@ -23,6 +23,14 @@ export class UserService {
return await ApiService.Request("get", "/User/User/GetUserBaseAttrInfo", { no });
}
/**
* 获取个人特性
* GET /User/User/GetUserBaseAttrData
*/
static async GetUserBaseAttrData() {
return await ApiService.Request("get", "/User/User/GetUserBaseAttrData");
}
/**
* 获取用户资料
* GET /User/User/GetHomeInfo