feat: 新增工作服务项目并调整部分接口路由
1. 新增QYZH.InteractiveMagazine.WorkService工作服务项目,包含队列消费者框架、Hangfire定时任务支持 2. 修复PetService中未定义FeedPetOutput变量的问题 3. 调整JournalController的路由前缀从api/icr改为api 4. 将工作服务项目添加到解决方案中
This commit is contained in:
@ -0,0 +1,56 @@
|
|||||||
|
namespace QYZH.InteractiveMagazine.Models.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hangfire 定时任务配置集合(对应 appsettings.json 中的 "HangfireJobs" 节点)
|
||||||
|
/// </summary>
|
||||||
|
public class HangfireJobSettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 定时任务列表,每一项对应一个 Hangfire RecurringJob
|
||||||
|
/// </summary>
|
||||||
|
public List<HangfireJobConfig> Jobs { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单个定时任务配置
|
||||||
|
/// </summary>
|
||||||
|
public class HangfireJobConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 任务名称(Hangfire Dashboard 中展示的唯一标识,建议英文短横线命名,如 "sample-job")
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Job 类的完整类型名称(含命名空间,如 "QYZH.InteractiveMagazine.WorkService.Jobs.SampleJob")
|
||||||
|
/// </summary>
|
||||||
|
public string JobType { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 要调用的方法名称(默认 "ExecuteAsync",方法必须为 public 无参 Task)
|
||||||
|
/// </summary>
|
||||||
|
public string MethodName { get; set; } = "ExecuteAsync";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cron 表达式,控制执行频率。常用示例:
|
||||||
|
/// "* * * * *" 每分钟
|
||||||
|
/// "*/5 * * * *" 每5分钟
|
||||||
|
/// "0 * * * *" 每小时整点
|
||||||
|
/// "0 0 * * *" 每天午夜
|
||||||
|
/// "0 9 * * *" 每天上午9点
|
||||||
|
/// "0 9 * * 1" 每周一上午9点
|
||||||
|
/// "0 0 1 * *" 每月1号午夜
|
||||||
|
/// 格式:分 时 日 月 星期(5位,不支持秒和年)
|
||||||
|
/// </summary>
|
||||||
|
public string Cron { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启用此任务(设为 false 将从 Hangfire 中移除该任务)
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 备注说明(仅供阅读理解,不参与运行逻辑)
|
||||||
|
/// </summary>
|
||||||
|
public string? Description { get; set; }
|
||||||
|
}
|
||||||
@ -252,7 +252,7 @@ public class PetService(
|
|||||||
logger.LogWarning("喂养失败,宠物未激活,PetId: {PetId}, Status: {Status}", input.PetId, pet.Status);
|
logger.LogWarning("喂养失败,宠物未激活,PetId: {PetId}, Status: {Status}", input.PetId, pet.Status);
|
||||||
throw new BusinessException("宠物未激活,无法喂养", 400);
|
throw new BusinessException("宠物未激活,无法喂养", 400);
|
||||||
}
|
}
|
||||||
|
FeedPetOutput result = new FeedPetOutput ();
|
||||||
// 事务保证一致性
|
// 事务保证一致性
|
||||||
await UseTranAsync(async () =>
|
await UseTranAsync(async () =>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -22,7 +22,7 @@ namespace QYZH.InteractiveMagazine.WebApi.Controllers
|
|||||||
/// 期刊管理
|
/// 期刊管理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Platform))]
|
[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Platform))]
|
||||||
[Route("api/icr")]
|
[Route("api")]
|
||||||
public class JournalController(
|
public class JournalController(
|
||||||
OssService ossService,
|
OssService ossService,
|
||||||
IJournalCatalogService JournalCatalogService,
|
IJournalCatalogService JournalCatalogService,
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
namespace QYZH.InteractiveMagazine.WorkService.Consumers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 队列消费者接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IQueueConsumer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 监听的队列名称
|
||||||
|
/// </summary>
|
||||||
|
string QueueName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理消息
|
||||||
|
/// </summary>
|
||||||
|
Task HandleAsync(byte[] message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理消费异常
|
||||||
|
/// </summary>
|
||||||
|
Task OnErrorAsync(byte[] message, Exception exception);
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.WorkService.Consumers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 期刊任务接收消费者(示例)
|
||||||
|
/// </summary>
|
||||||
|
public class JournalTaskReceiveConsumer : IQueueConsumer
|
||||||
|
{
|
||||||
|
private readonly ILogger<JournalTaskReceiveConsumer> _logger;
|
||||||
|
|
||||||
|
public JournalTaskReceiveConsumer(ILogger<JournalTaskReceiveConsumer> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string QueueName => "mq.Journal.task.receive";
|
||||||
|
|
||||||
|
public async Task HandleAsync(byte[] message)
|
||||||
|
{
|
||||||
|
var body = Encoding.UTF8.GetString(message);
|
||||||
|
_logger.LogInformation("收到期刊任务消息: {Message}", body);
|
||||||
|
|
||||||
|
// TODO: 在此编写具体的消息处理逻辑
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task OnErrorAsync(byte[] message, Exception exception)
|
||||||
|
{
|
||||||
|
var body = Encoding.UTF8.GetString(message);
|
||||||
|
_logger.LogError(exception, "处理期刊任务消息失败: {Message}", body);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.WorkService.Consumers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RabbitMQ 消费者后台服务,自动发现并启动所有已注册的 IQueueConsumer
|
||||||
|
/// </summary>
|
||||||
|
public class RabbitMQHostedService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly ILogger<RabbitMQHostedService> _logger;
|
||||||
|
|
||||||
|
public RabbitMQHostedService(IServiceProvider serviceProvider, ILogger<RabbitMQHostedService> logger)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
var consumers = scope.ServiceProvider.GetServices<IQueueConsumer>().ToList();
|
||||||
|
|
||||||
|
if (consumers.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("未注册任何队列消费者");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("发现 {Count} 个队列消费者,开始启动...", consumers.Count);
|
||||||
|
|
||||||
|
var tasks = consumers.Select(c => StartConsumerAsync(c.QueueName, stoppingToken)).ToList();
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartConsumerAsync(string queueName, CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("正在启动消费者: {QueueName}", queueName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var rabbitMQService = _serviceProvider.GetRequiredService<IRabbitMQService>();
|
||||||
|
|
||||||
|
await rabbitMQService.ReceiveAsync(queueName, async (channel, ea) =>
|
||||||
|
{
|
||||||
|
// 每条消息创建独立 scope,确保消费者内可注入 Scoped 服务
|
||||||
|
using var messageScope = _serviceProvider.CreateScope();
|
||||||
|
var consumer = messageScope.ServiceProvider
|
||||||
|
.GetServices<IQueueConsumer>()
|
||||||
|
.First(c => c.QueueName == queueName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await consumer.HandleAsync(ea.Body.ToArray());
|
||||||
|
await channel.BasicAckAsync(ea.DeliveryTag, false, stoppingToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "消费者 {QueueName} 处理消息异常", queueName);
|
||||||
|
await channel.BasicNackAsync(ea.DeliveryTag, false, true, stoppingToken);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await consumer.OnErrorAsync(ea.Body.ToArray(), ex);
|
||||||
|
}
|
||||||
|
catch (Exception errorEx)
|
||||||
|
{
|
||||||
|
_logger.LogError(errorEx, "消费者 {QueueName} OnErrorAsync 执行异常", queueName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, stoppingToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("消费者 {QueueName} 已停止", queueName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "消费者 {QueueName} 启动失败", queueName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
QYZH.InteractiveMagazine.WorkService/Jobs/SampleJob.cs
Normal file
30
QYZH.InteractiveMagazine.WorkService/Jobs/SampleJob.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.WorkService.Jobs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 示例定时任务
|
||||||
|
/// </summary>
|
||||||
|
public class SampleJob
|
||||||
|
{
|
||||||
|
private readonly ILogger<SampleJob> _logger;
|
||||||
|
|
||||||
|
public SampleJob(ILogger<SampleJob> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行定时任务(由Hangfire调度)
|
||||||
|
/// </summary>
|
||||||
|
public async Task ExecuteAsync()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("SampleJob 开始执行 {Time}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
|
||||||
|
// TODO: 在此编写具体的定时任务逻辑
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
|
||||||
|
_logger.LogInformation("SampleJob 执行完毕");
|
||||||
|
}
|
||||||
|
}
|
||||||
120
QYZH.InteractiveMagazine.WorkService/Program.cs
Normal file
120
QYZH.InteractiveMagazine.WorkService/Program.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using Hangfire;
|
||||||
|
using Hangfire.Dashboard;
|
||||||
|
using Hangfire.MemoryStorage;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Settings;
|
||||||
|
using QYZH.InteractiveMagazine.WorkService.Consumers;
|
||||||
|
using QYZH.InteractiveMagazine.WorkService.Jobs;
|
||||||
|
using Serilog;
|
||||||
|
using SqlSugar;
|
||||||
|
using SqlSugar.IOC;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// 加载配置
|
||||||
|
builder.Configuration
|
||||||
|
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||||
|
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
|
// 配置Serilog
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.ReadFrom.Configuration(builder.Configuration)
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.CreateLogger();
|
||||||
|
builder.Services.AddSerilog();
|
||||||
|
|
||||||
|
// 初始化雪花ID生成器
|
||||||
|
YitIdHelper.SetIdGenerator(new IdGeneratorOptions { WorkerId = 2 });
|
||||||
|
|
||||||
|
// 初始化MySQL(SqlSugar)
|
||||||
|
builder.Services.AddSqlSugar(new IocConfig
|
||||||
|
{
|
||||||
|
ConfigId = 0,
|
||||||
|
DbType = IocDbType.MySql,
|
||||||
|
ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"),
|
||||||
|
IsAutoCloseConnection = true
|
||||||
|
});
|
||||||
|
SugarIocServices.ConfigurationSugar(db =>
|
||||||
|
{
|
||||||
|
db.Aop.OnLogExecuting = (sql, pars) =>
|
||||||
|
{
|
||||||
|
Log.Information("[SQL] {Sql}", UtilMethods.GetSqlString((DbType)IocDbType.MySql, sql, pars));
|
||||||
|
};
|
||||||
|
db.Aop.OnError = ex =>
|
||||||
|
{
|
||||||
|
Log.Error(ex, "[SQL Error] {Message}", ex.Message);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 配置Hangfire(内存存储,后续可切换Redis)
|
||||||
|
builder.Services.AddHangfire(config => config
|
||||||
|
.UseMemoryStorage()
|
||||||
|
.UseSerializerSettings(new Newtonsoft.Json.JsonSerializerSettings
|
||||||
|
{
|
||||||
|
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All
|
||||||
|
}));
|
||||||
|
builder.Services.AddHangfireServer();
|
||||||
|
|
||||||
|
// 配置RabbitMQ
|
||||||
|
builder.Services.AddRabbitMQ(builder.Configuration);
|
||||||
|
|
||||||
|
// 注册队列消费者(新增消费者只需实现 IQueueConsumer 并在此注册)
|
||||||
|
builder.Services.AddScoped<IQueueConsumer, JournalTaskReceiveConsumer>();
|
||||||
|
|
||||||
|
// 注册消费者后台服务
|
||||||
|
builder.Services.AddHostedService<RabbitMQHostedService>();
|
||||||
|
|
||||||
|
// 从配置文件读取定时任务列表
|
||||||
|
var jobSettings = builder.Configuration.GetSection("HangfireJobs").Get<HangfireJobSettings>();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// 配置Hangfire Dashboard(仅本机访问)
|
||||||
|
app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
||||||
|
{
|
||||||
|
Authorization = new[] { new LocalRequestsOnlyAuthorizationFilter() }
|
||||||
|
});
|
||||||
|
|
||||||
|
// 根据配置动态注册定时任务
|
||||||
|
if (jobSettings?.Jobs != null)
|
||||||
|
{
|
||||||
|
foreach (var job in jobSettings.Jobs)
|
||||||
|
{
|
||||||
|
var jobType = Type.GetType(job.JobType);
|
||||||
|
if (jobType == null)
|
||||||
|
{
|
||||||
|
Log.Warning("定时任务 [{Name}] 类型未找到: {JobType},跳过注册", job.Name, job.JobType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!job.Enabled)
|
||||||
|
{
|
||||||
|
// 配置为关闭的任务,从 Hangfire 中移除
|
||||||
|
RecurringJob.RemoveIfExists(job.Name);
|
||||||
|
Log.Information("定时任务 [{Name}] 已禁用,已移除", job.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造表达式:job => job.MethodName()
|
||||||
|
var method = jobType.GetMethod(job.MethodName);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Log.Warning("定时任务 [{Name}] 方法未找到: {MethodName},跳过注册", job.Name, job.MethodName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var param = Expression.Parameter(jobType, "job");
|
||||||
|
var call = Expression.Call(param, method);
|
||||||
|
var lambda = Expression.Lambda<Action>(call, param);
|
||||||
|
|
||||||
|
RecurringJob.AddOrUpdate(job.Name, lambda, job.Cron);
|
||||||
|
Log.Information("定时任务 [{Name}] 已注册,Cron: {Cron}", job.Name, job.Cron);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Information("WorkService 已启动,Hangfire Dashboard: /hangfire");
|
||||||
|
|
||||||
|
app.Run();
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"QYZH.InteractiveMagazine.WorkService": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:53991;http://localhost:53992"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\QYZH.InteractiveMagazine.Common\QYZH.InteractiveMagazine.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\QYZH.InteractiveMagazine.Models\QYZH.InteractiveMagazine.Models.csproj" />
|
||||||
|
<ProjectReference Include="..\QYZH.InteractiveMagazine.Infrastructure\QYZH.InteractiveMagazine.Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.17" />
|
||||||
|
<PackageReference Include="Hangfire.MemoryStorage" Version="1.8.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||||
|
<PackageReference Include="SqlSugar.IOC" Version="2.0.1" />
|
||||||
|
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.213" />
|
||||||
|
<PackageReference Include="MySqlConnector" Version="2.5.0" />
|
||||||
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
48
QYZH.InteractiveMagazine.WorkService/appsettings.json
Normal file
48
QYZH.InteractiveMagazine.WorkService/appsettings.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "server=192.168.20.150;port=13306;database=InteractiveMagazine;user=user;password=n68792bu!y99r905;charset=utf8mb4;"
|
||||||
|
},
|
||||||
|
"RabbitMq": {
|
||||||
|
"HostName": "192.168.20.150",
|
||||||
|
"Port": 5672,
|
||||||
|
"UserName": "smartschool",
|
||||||
|
"Password": "@ss%&*otz%d*pq2S",
|
||||||
|
"VirtualHost": "InteractiveMagazine"
|
||||||
|
},
|
||||||
|
"Serilog": {
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"System": "Warning",
|
||||||
|
"Hangfire": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"WriteTo": [
|
||||||
|
{
|
||||||
|
"Name": "Console"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "File",
|
||||||
|
"Args": {
|
||||||
|
"path": "logs/worker-log-.txt",
|
||||||
|
"rollingInterval": "Day"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"HangfireJobs": {
|
||||||
|
"_说明": "定时任务配置。新增任务只需在 Jobs 数组中追加一项。Cron格式:分 时 日 月 星期(5位)。常用:* * * * * 每分钟 | */5 * * * * 每5分钟 | 0 * * * * 每小时 | 0 9 * * * 每天9点 | 0 0 * * 1 每周一午夜",
|
||||||
|
"Jobs": [
|
||||||
|
{
|
||||||
|
"Name": "sample-job",
|
||||||
|
"JobType": "QYZH.InteractiveMagazine.WorkService.Jobs.SampleJob",
|
||||||
|
"MethodName": "ExecuteAsync",
|
||||||
|
"Cron": "* * * * *",
|
||||||
|
"Enabled": false,
|
||||||
|
"Description": "示例任务(默认关闭,仅用于验证框架运行)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
@ -6,4 +6,5 @@
|
|||||||
<Project Path="QYZH.InteractiveMagazine.Repository/QYZH.InteractiveMagazine.Repository.csproj" />
|
<Project Path="QYZH.InteractiveMagazine.Repository/QYZH.InteractiveMagazine.Repository.csproj" />
|
||||||
<Project Path="QYZH.InteractiveMagazine.Service/QYZH.InteractiveMagazine.Service.csproj" />
|
<Project Path="QYZH.InteractiveMagazine.Service/QYZH.InteractiveMagazine.Service.csproj" />
|
||||||
<Project Path="QYZH.InteractiveMagazine.WebApi/QYZH.InteractiveMagazine.WebApi.csproj" />
|
<Project Path="QYZH.InteractiveMagazine.WebApi/QYZH.InteractiveMagazine.WebApi.csproj" />
|
||||||
|
<Project Path="QYZH.InteractiveMagazine.WorkService/QYZH.InteractiveMagazine.WorkService.csproj" />
|
||||||
</Solution>
|
</Solution>
|
||||||
|
|||||||
Reference in New Issue
Block a user