Program.cs 7.55 KB
using FreeSql.Internal;
using HaHRCS.Rcs.Dal.Repository;
using HaHRCS.Rcs.Executor.PLC;
using HHECS.Communication;
using MassTransit.JobService;
using Microsoft.Extensions.DependencyInjection;
using Rcs.Api;
using Rcs.Api.BackgroundServices;
using Rcs.Api.Hubs;
using Rcs.Infrastructure.DB.Repositories;
using Rcs.Infrastructure.Installs;
using Rcs.Infrastructure.PathFinding;
using Serilog;

// 配置 Serilog 日志
Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
        .Build())
    .CreateLogger();

try
{
    var builder = WebApplication.CreateBuilder(args);
    string externalBaseUrl = builder.Configuration["AppSettings:ExternalBaseUrl"];
    // 结果:"http://localhost:5000"

    // 2. 读取嵌套的数据库连接字符串
    string sqlConnStr = builder.Configuration["AppSettings:ConnSql:ConnectionString"];
    if (string.IsNullOrEmpty(sqlConnStr))
    {
        throw new ArgumentNullException("FreeSqlConnection", "FreeSql主连接字符串不能为空");
    }

    // 2. 初始化FreeSql(核心:调用UseConnectionString并传入非空值)
    var fsql = new FreeSql.FreeSqlBuilder()
        .UseConnectionString(FreeSql.DataType.PostgreSQL, sqlConnStr) // 指定数据库类型+连接字符串
        .UseAutoSyncStructure(true) // 自动同步实体结构(开发环境可用,生产谨慎)
        .UseNoneCommandParameter(true)
        .UseNameConvert(NameConvertType.ToLower)
        .Build();

    // 3. 注册为全局单例(方便依赖注入)
    builder.Services.AddSingleton<IFreeSql>(fsql);


    //builder.Services.AddSingleton<EquipmentRepository>(new EquipmentRepository(fsql));
    //builder.Services.AddSingleton<EquipmentPropRepository>(new EquipmentPropRepository(fsql));
    builder.Services.AddSingleton<EquipmentRepository>();
    builder.Services.AddSingleton<EquipmentPropRepository>();
    builder.Services.AddSingleton<EquipmentTypeRepository>();
    builder.Services.AddSingleton<EquipmentTypePropTemplateRepository>();

    builder.Services.AddSingleton<EquipmentExecutor>();

    // 使用 Serilog 替代默认日志
    builder.Host.UseSerilog();

    // Add services to the container.
    builder.Services.AddControllers();
    // 添加 CORS 策略,允许跨域访问(根据需要修改策略)
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("DefaultCorsPolicy", policy =>
        {
            policy.SetIsOriginAllowed(_ => true)
                  .AllowAnyMethod()
                  .AllowAnyHeader()
                  .AllowCredentials();
        });
    });

    // 添加 SignalR(使用 camelCase JSON 序列化)
    builder.Services.AddSignalR().AddJsonProtocol(options =>
    {
        options.PayloadSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
    });

    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
        {
            Title = "HHRCS API",
            Version = "v1",
            Description = "长沙华恒机器人控制系统 API"
        });
    }); 
    // 安装应用配置
    builder.InstallApplicationSettings();

    // 安装Entity Framework
    builder.InstallEntityFramework();

    // 安装依赖注入
    builder.InstallDependencyInjection();

    // 安装 AutoMapper
    builder.Services.InstallAutoMapper();

    // 安装 Redis
    builder.InstallRedis();
    // 安装 Mqtt
    builder.InstallMqtt();
    // 安装过滤器
    builder.InstallFilters();

    // 安装消息总线 (MassTransit + RabbitMQ)
    builder.InstallMessageBus();
    // 安装 LanYin 服务
    builder.Services.InstallLanYinService();
    builder.Services.InstallHttpClient();
    // 交通管制服务
    builder.Services.InstallerPathService();

    // 添加机器人状态推送后台服务
    builder.Services.AddHostedService<RobotStatusPushService>();

    builder.Services.AddHostedService(provider => provider.GetRequiredService<EquipmentExecutor>());
   
    //builder.Services.AddHostedService<EquipmentExecutor>(
    ////t =>
    ////{
    ////    var mainExecutor = new EquipmentExecutor(t.GetService<EquipmentRepository>(), t.GetService<EquipmentPropRepository>(),t.GetService<EquipmentTypeRepository>(),t.GetService<EquipmentTypePropTemplateRepository>());
    ////    return mainExecutor;
    ////}
    //);

    //调度相关
    var ebResult = EquipmentCommunicationHubBuilder.Build("HubBuilder3.x.json");
    if (!ebResult.Success)
    {
        Console.WriteLine("BuilderError:" + ebResult.Msg);
        return;
    }
    var app = builder.Build();

    // 自动运行数据库迁移
    using (var scope = app.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            app.Logger.LogInformation("开始初始化数据库...");
            var dbContext = services.GetRequiredService<Rcs.Infrastructure.DB.MsSql.AppDbContext>();
            EntityFrameworkInstaller.SeedDatabase(dbContext);
            app.Logger.LogInformation("✓ 数据库初始化成功完成");
        }
        catch (Exception ex)
        {
            app.Logger.LogError(ex, "✗ 数据库初始化失败");
            throw;
        }
    }

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    app.UseHttpsRedirection();

    // 使用 CORS 策略
    app.UseCors("DefaultCorsPolicy");

    // 配置静态文件服务,允许访问上传的文件
    app.UseStaticFiles();
    // 配置上传文件的静态访问路径(使用内容根目录确保 Debug 和 Release 模式一致)
    var uploadsPath = Path.Combine(app.Environment.ContentRootPath, "uploads");
    if (!Directory.Exists(uploadsPath))
    {
        Directory.CreateDirectory(uploadsPath);
    }
    app.Logger.LogInformation("静态文件路径: {UploadsPath}", uploadsPath);
    app.UseStaticFiles(new Microsoft.AspNetCore.Builder.StaticFileOptions
    {
        FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(uploadsPath),
        RequestPath = "/uploads",
        OnPrepareResponse = ctx =>
        {
            // 禁用缓存,确保每次都获取最新文件
            ctx.Context.Response.Headers.Append("Cache-Control", "no-cache, no-store, must-revalidate");
            ctx.Context.Response.Headers.Append("Pragma", "no-cache");
            ctx.Context.Response.Headers.Append("Expires", "0");
            // 添加 CORS 头,允许 SVG 图片跨域访问
            ctx.Context.Response.Headers.Append("Access-Control-Allow-Origin", "*");
            ctx.Context.Response.Headers.Append("Access-Control-Allow-Methods", "GET, OPTIONS");
            ctx.Context.Response.Headers.Append("Access-Control-Allow-Headers", "*");
        }
    });

    app.MapControllers();

    // 映射 SignalR Hub(支持地图隔离)
    app.MapHub<HHRCSHub>("/{mapId}/hubs/HHRCS");

    app.Run();
}
finally
{
    // 确保 Serilog 日志在程序关闭时正确刷新
    Log.CloseAndFlush();
}

namespace Rcs.Api
{
    record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
    {
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}