From 1d7836a173c8dd0cd67059be4dc74931fd14e7eb Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Thu, 4 Dec 2025 17:30:09 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=8D=87=E7=BA=A7=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E7=A7=8D=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../appsettings.Seed.Development.json | 3 +- src/Api/TakeoutSaaS.MiniApi/Program.cs | 11 ++ .../TakeoutSaaS.MiniApi.csproj | Bin 4490 -> 2293 bytes ...pApplicationServiceCollectionExtensions.cs | 2 +- .../TakeoutSaaS.Application.csproj | Bin 2782 -> 1364 bytes .../TakeoutSaaS.ApiGateway.csproj | Bin 1922 -> 1924 bytes .../Identity/Options/AdminSeedOptions.cs | 5 + .../Persistence/IdentityDataSeeder.cs | 111 ++++++++++++++---- .../Services/RabbitMqConnectionFactory.cs | 7 +- .../Services/RabbitMqMessagePublisher.cs | 27 +++-- .../Services/RabbitMqMessageSubscriber.cs | 29 +++-- .../TakeoutSaaS.Module.Messaging.csproj | 14 +-- .../TakeoutSaaS.Module.Scheduler.csproj | 14 +-- .../TakeoutSaaS.Module.Sms.csproj | 14 +-- .../TakeoutSaaS.Module.Storage.csproj | Bin 2134 -> 2130 bytes 16 files changed, 163 insertions(+), 75 deletions(-) diff --git a/.gitignore b/.gitignore index 16baddd..90a5c26 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ obj/ **/bin/ **/obj/ .claude/ +*.log diff --git a/src/Api/TakeoutSaaS.AdminApi/appsettings.Seed.Development.json b/src/Api/TakeoutSaaS.AdminApi/appsettings.Seed.Development.json index 5e78e4f..6bb47d4 100644 --- a/src/Api/TakeoutSaaS.AdminApi/appsettings.Seed.Development.json +++ b/src/Api/TakeoutSaaS.AdminApi/appsettings.Seed.Development.json @@ -1,7 +1,7 @@ { "App": { "Seed": { - "Enabled": true, + "Enabled": false, "DefaultTenant": { "TenantId": 1000000000001, "Code": "demo", @@ -39,6 +39,7 @@ }, "Identity": { "AdminSeed": { + "Enabled": false, "RoleTemplates": [ { "TemplateCode": "platform-admin", diff --git a/src/Api/TakeoutSaaS.MiniApi/Program.cs b/src/Api/TakeoutSaaS.MiniApi/Program.cs index 8369004..a6685cb 100644 --- a/src/Api/TakeoutSaaS.MiniApi/Program.cs +++ b/src/Api/TakeoutSaaS.MiniApi/Program.cs @@ -1,4 +1,6 @@ +using FluentValidation; using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.Extensions.Caching.StackExchangeRedis; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -14,6 +16,7 @@ using TakeoutSaaS.Shared.Abstractions.Ids; using TakeoutSaaS.Shared.Kernel.Ids; using TakeoutSaaS.Shared.Web.Extensions; using TakeoutSaaS.Shared.Web.Swagger; +using System.Reflection; // 1. 创建构建器与日志模板 var builder = WebApplication.CreateBuilder(args); @@ -43,6 +46,14 @@ builder.Services.AddSharedSwagger(options => options.Description = "小程序 API 文档"; options.EnableAuthorization = true; }); +builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); + +// 4. 注册 Redis 分布式缓存,供验证码等功能使用 +var redisConnection = builder.Configuration.GetValue("Redis"); +builder.Services.AddStackExchangeRedisCache(options => +{ + options.Configuration = redisConnection; +}); // 4. 注册多租户与业务模块 builder.Services.AddTenantResolution(builder.Configuration); diff --git a/src/Api/TakeoutSaaS.MiniApi/TakeoutSaaS.MiniApi.csproj b/src/Api/TakeoutSaaS.MiniApi/TakeoutSaaS.MiniApi.csproj index 3ee3e92c95e1eb69b410ec29832c45a51da842f9..1fd145f97ccb6bebd8f7f74bdd4b92d2a8cc3425 100644 GIT binary patch literal 2293 zcmcIlO>f#j5WPprf3SSdf}^S@Llh;DSQRviX?o$Z*aNt%cdgyo2L63#apJ_5p%l3i zlBJp9y_q*38{TYl;r#|}G9?Fo!1}%iGEr$JR|DVT@+$c1-wZD=FTy9SenEm>oNfmG zBulh1YKd$*nlUo5pRn*v!KkD zn(o?uu<-8$I0FGWpx&_$F!rbnQtLj0vfzsw!c#J@!$#6jAFIY55T-NuN5ta4mcHMNQg>Dlm|0>90%S>V=F`Ve7omt Gc<~34`~6G+ literal 4490 zcmdUzUvCmo5XI-&#P2|S@7AjE327RIwlUPEV140byZmV_1;XyCe0lYE?#%`eb_I&f znugu7cV_m?oVhb|`Sr7E$M(Y>Y-knxW<8r)YPcsfn8YL+T6WVU|-O? z!JfBPK|ixNarhFO*{QRU+GIh>OSUa!ZGdEA4gL%9t!LkP+Y}5nuxsTAujEL;n@UE#QJ-e1wEI~*F5<%*L_X|WQQ{T za3!~P!=?8q+GD)g4UgY}`@z$2QSo}fp(F&TgR?YzCn7Q(JaEM92F|Y{86$)i_$tP+pOqbu^ud#5Zb3nSX86IXAwU#G+? z-$&m!5~MX@3`^{TGDW*uD9apcA5mX9Z$X;6I=M|RT%Yr+bzXhHsH1Pz?-%FA&H8;n zMs@h;n(g0?o8|8DcZ|mgGeO8-3z-&kfu_73y)K(Dx04RYO-&LWY2JMBI6qFXpG}7I zj+~EkKk`rESrbOOaH3rk!Li}J3YgEy{;U=RE%$(rFgYbkI%9MSn9SEI_N}Nd&d服务集合。 public static IServiceCollection AddAppApplication(this IServiceCollection services) { - services.AddMediatR(Assembly.GetExecutingAssembly()); + services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); diff --git a/src/Application/TakeoutSaaS.Application/TakeoutSaaS.Application.csproj b/src/Application/TakeoutSaaS.Application/TakeoutSaaS.Application.csproj index 9b5901be43b39566733d808024c10ef2afdce8a5..73fff0ef4de215e8f14656d422a61b0ca8bda0d4 100644 GIT binary patch literal 1364 zcmb_cO;6)65WOSyKSa6jqye;hNrO;>A%OOv?NulEgw>( zC~xdHZ=PSAojq47`is^{qX%&c{WwC+jgWdih#Q*yNPos>d3IyX6LN}5u^7Z7$*pl_ zMlimdf`{U~*E@=$u&PE&%iq>C^$PRol-W7bm1PzFGj@?{q_hwHEZHu+lK2hdMk)57 zFh?C;S+W7vD~hUCl1uvIyiT`Sw*{|6B|B7Q1!2`ydBgaE&2fS=wB7-X3e8m`us!a% ztH($~V-c>2YB+yzWEm&#or|NpwTs7a5`0YZU`nv%0P4$ULLCmlK_O6+q$Pw#E!tq( zS9phvKYF|Nss0E(3dv~F)^>WZwky^6`RMp48PvJ%aYdlhv2q8*Jrj0{Ej#P-1}!_M5EB%PuT)ZLnUJ+tS~66eK2rW zd*0U(zTRhq&at^a(On`o#qaG|lCl0T+12r_^of2U=N-oKky#X-p#HD^iMAJ}Vp4aA U_d?tBodorcvT(_S?1oG41*y})cK`qY literal 2782 zcmd6pOK;jx5QWdWQvZXMeVwFLyP}DrJX#h+X-K=u#x;hd0fVsVOd`Bu)|p2u#Z^{)Wze@tvh(ezM5gtXEoHhPIbk-u-ajVmwJI$@UYXwG3END z5AHkDWX`Ivyi&y40LxrwJVo$H^n+8ToP7XZTiJ(+v1w_SGwVG{6vh7MnxW4SpRZgK zb~H80H`{O7VsVEyh$i@&0=HoK>?)g%*^1rjt;?P5+(PYojaq7RVgiC{1utL|qgCW8 zUSi$nJ9VBh*ML1N{zH`L@ou@XDxOF1nV^R0eeC)tX#SD6oZmwop>|j8ogrG@0(yKD*gm_q9r}t5aiGbx`*;$IrsfvX4t;gQAnMj;@ z_8X#{=to}7J?>=|hiZm-l?GKjtO7R8;}emv+O#^JR#cvy`PR>VT6xxW>iS8uZZkh4 znsu8HG}nCX+Hv&OlM^cS+PnE3&ttk%&Mb-ebg`Vbvl%m5+d8C6Sw}1IKGkA=PL+AN zEjAtJp22i*U-3 public sealed class AdminSeedOptions { + /// + /// 是否启用后台账号与权限种子。 + /// + public bool Enabled { get; set; } = true; + /// /// 初始用户列表。 /// diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs index 01c8da2..17727ae 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Identity/Persistence/IdentityDataSeeder.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Npgsql; using TakeoutSaaS.Infrastructure.Identity.Options; using TakeoutSaaS.Shared.Abstractions.Tenancy; using DomainIdentityUser = TakeoutSaaS.Domain.Identity.Entities.IdentityUser; @@ -29,6 +30,11 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger var passwordHasher = scope.ServiceProvider.GetRequiredService>(); var tenantContextAccessor = scope.ServiceProvider.GetRequiredService(); + if (!options.Enabled) + { + logger.LogInformation("AdminSeed 已禁用,跳过后台账号初始化"); + return; + } await context.Database.MigrateAsync(cancellationToken); if (options.Users is null or { Count: 0 }) @@ -127,15 +133,35 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger .Where(ur => ur.TenantId == userOptions.TenantId && ur.UserId == user.Id) .ToListAsync(cancellationToken); context.UserRoles.RemoveRange(existingUserRoles); + await context.SaveChangesAsync(cancellationToken); var roleIds = roleEntities.Select(r => r.Id).Distinct().ToArray(); - var userRoles = roleIds.Select(roleId => new DomainUserRole + foreach (var roleId in roleIds) { - TenantId = userOptions.TenantId, - UserId = user.Id, - RoleId = roleId - }); - await context.UserRoles.AddRangeAsync(userRoles, cancellationToken); + try + { + var alreadyExists = await context.UserRoles.AnyAsync( + ur => ur.TenantId == userOptions.TenantId && ur.UserId == user.Id && ur.RoleId == roleId, + cancellationToken); + if (alreadyExists) + { + continue; + } + + await context.UserRoles.AddAsync(new DomainUserRole + { + TenantId = userOptions.TenantId, + UserId = user.Id, + RoleId = roleId + }, cancellationToken); + + await context.SaveChangesAsync(cancellationToken); + } + catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation) + { + context.ChangeTracker.Clear(); + } + } // 为种子角色绑定种子权限 if (permissions.Length > 0 && roleIds.Length > 0) @@ -145,14 +171,41 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger .Where(rp => rp.TenantId == userOptions.TenantId && roleIds.Contains(rp.RoleId)) .ToListAsync(cancellationToken); context.RolePermissions.RemoveRange(existingRolePermissions); + await context.SaveChangesAsync(cancellationToken); - var newRolePermissions = roleIds.SelectMany(roleId => permissionIds.Select(permissionId => new DomainRolePermission + var distinctRoleIds = roleIds.Distinct().ToArray(); + var distinctPermissionIds = permissionIds.Distinct().ToArray(); + foreach (var roleId in distinctRoleIds) { - TenantId = userOptions.TenantId, - RoleId = roleId, - PermissionId = permissionId - })); - await context.RolePermissions.AddRangeAsync(newRolePermissions, cancellationToken); + foreach (var permissionId in distinctPermissionIds) + { + try + { + var exists = await context.RolePermissions.AnyAsync( + rp => rp.TenantId == userOptions.TenantId + && rp.RoleId == roleId + && rp.PermissionId == permissionId, + cancellationToken); + if (exists) + { + continue; + } + + await context.RolePermissions.AddAsync(new DomainRolePermission + { + TenantId = userOptions.TenantId, + RoleId = roleId, + PermissionId = permissionId + }, cancellationToken); + + await context.SaveChangesAsync(cancellationToken); + } + catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation) + { + context.ChangeTracker.Clear(); + } + } + } } } @@ -209,15 +262,33 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger .ToListAsync(cancellationToken); context.RoleTemplatePermissions.RemoveRange(existingPermissions); - - var toAdd = permissionCodes.Select(code => new DomainRoleTemplatePermission - { - RoleTemplateId = existing.Id, - PermissionCode = code - }); - - await context.RoleTemplatePermissions.AddRangeAsync(toAdd, cancellationToken); await context.SaveChangesAsync(cancellationToken); + var distinctPermissionCodes = permissionCodes.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); + foreach (var permissionCode in distinctPermissionCodes) + { + try + { + var alreadyExists = await context.RoleTemplatePermissions.AnyAsync( + x => x.RoleTemplateId == existing.Id && x.PermissionCode == permissionCode, + cancellationToken); + if (alreadyExists) + { + continue; + } + + await context.RoleTemplatePermissions.AddAsync(new DomainRoleTemplatePermission + { + RoleTemplateId = existing.Id, + PermissionCode = permissionCode + }, cancellationToken); + + await context.SaveChangesAsync(cancellationToken); + } + catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation) + { + context.ChangeTracker.Clear(); + } + } } } diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqConnectionFactory.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqConnectionFactory.cs index ad844c2..e015c72 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqConnectionFactory.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqConnectionFactory.cs @@ -12,7 +12,7 @@ public sealed class RabbitMqConnectionFactory(IOptionsMonitor o /// /// 创建连接。 /// - public IConnection CreateConnection() + public Task CreateConnectionAsync(CancellationToken cancellationToken = default) { var options = optionsMonitor.CurrentValue; var factory = new ConnectionFactory @@ -21,10 +21,9 @@ public sealed class RabbitMqConnectionFactory(IOptionsMonitor o Port = options.Port, UserName = options.Username, Password = options.Password, - VirtualHost = options.VirtualHost, - DispatchConsumersAsync = true + VirtualHost = options.VirtualHost }; - return factory.CreateConnection(); + return factory.CreateConnectionAsync(cancellationToken); } } diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs index 3f2178c..4a5b215 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessagePublisher.cs @@ -14,41 +14,42 @@ public sealed class RabbitMqMessagePublisher(RabbitMqConnectionFactory connectio : IMessagePublisher, IAsyncDisposable { private IConnection? _connection; - private IModel? _channel; + private IChannel? _channel; private bool _disposed; /// - public Task PublishAsync(string routingKey, T message, CancellationToken cancellationToken = default) + public async Task PublishAsync(string routingKey, T message, CancellationToken cancellationToken = default) { // 1. 确保通道可用 - EnsureChannel(); + await EnsureChannelAsync(cancellationToken); var options = optionsMonitor.CurrentValue; var channel = _channel ?? throw new InvalidOperationException("RabbitMQ channel is not available."); // 2. 声明交换机 - channel.ExchangeDeclare(options.Exchange, options.ExchangeType, durable: true, autoDelete: false); + await channel.ExchangeDeclareAsync(options.Exchange, options.ExchangeType, durable: true, autoDelete: false, cancellationToken: cancellationToken); // 3. 序列化消息并设置属性 var body = serializer.Serialize(message); - var props = channel.CreateBasicProperties(); - props.ContentType = "application/json"; - props.DeliveryMode = 2; - props.MessageId = Guid.NewGuid().ToString("N"); + var props = new BasicProperties + { + ContentType = "application/json", + DeliveryMode = DeliveryModes.Persistent, + MessageId = Guid.NewGuid().ToString("N") + }; // 4. 发布消息 - channel.BasicPublish(options.Exchange, routingKey, props, body); + await channel.BasicPublishAsync(options.Exchange, routingKey, mandatory: false, props, body, cancellationToken); logger.LogDebug("发布消息到交换机 {Exchange} RoutingKey {RoutingKey}", options.Exchange, routingKey); - return Task.CompletedTask; } - private void EnsureChannel() + private async Task EnsureChannelAsync(CancellationToken cancellationToken) { if (_channel != null && _channel.IsOpen) { return; } - _connection ??= connectionFactory.CreateConnection(); - _channel = _connection.CreateModel(); + _connection ??= await connectionFactory.CreateConnectionAsync(cancellationToken); + _channel = await _connection.CreateChannelAsync(cancellationToken: cancellationToken); } /// diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs index ef50289..6287c77 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs +++ b/src/Modules/TakeoutSaaS.Module.Messaging/Services/RabbitMqMessageSubscriber.cs @@ -15,32 +15,32 @@ public sealed class RabbitMqMessageSubscriber(RabbitMqConnectionFactory connecti : IMessageSubscriber { private IConnection? _connection; - private IModel? _channel; + private IChannel? _channel; private bool _disposed; /// public async Task SubscribeAsync(string queue, string routingKey, Func> handler, CancellationToken cancellationToken = default) { // 1. 确保通道可用 - EnsureChannel(); + await EnsureChannelAsync(cancellationToken); var options = optionsMonitor.CurrentValue; var channel = _channel ?? throw new InvalidOperationException("RabbitMQ channel is not available."); // 2. 声明交换机、队列及绑定 - channel.ExchangeDeclare(options.Exchange, options.ExchangeType, durable: true, autoDelete: false); - channel.QueueDeclare(queue, durable: true, exclusive: false, autoDelete: false); - channel.QueueBind(queue, options.Exchange, routingKey); - channel.BasicQos(0, options.PrefetchCount, global: false); + await channel.ExchangeDeclareAsync(options.Exchange, options.ExchangeType, durable: true, autoDelete: false, cancellationToken: cancellationToken); + await channel.QueueDeclareAsync(queue, durable: true, exclusive: false, autoDelete: false, cancellationToken: cancellationToken); + await channel.QueueBindAsync(queue, options.Exchange, routingKey, cancellationToken: cancellationToken); + await channel.BasicQosAsync(0, options.PrefetchCount, global: false, cancellationToken: cancellationToken); // 3. 设置消费者回调 var consumer = new AsyncEventingBasicConsumer(channel); - consumer.Received += async (_, ea) => + consumer.ReceivedAsync += async (_, ea) => { var message = serializer.Deserialize(ea.Body.ToArray()); if (message == null) { - channel.BasicAck(ea.DeliveryTag, multiple: false); + await channel.BasicAckAsync(ea.DeliveryTag, multiple: false, cancellationToken); return; } @@ -56,28 +56,27 @@ public sealed class RabbitMqMessageSubscriber(RabbitMqConnectionFactory connecti if (success) { - channel.BasicAck(ea.DeliveryTag, multiple: false); + await channel.BasicAckAsync(ea.DeliveryTag, multiple: false, cancellationToken); } else { - channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false); + await channel.BasicNackAsync(ea.DeliveryTag, multiple: false, requeue: false, cancellationToken); } }; // 4. 开始消费 - channel.BasicConsume(queue, autoAck: false, consumer); - await Task.CompletedTask.ConfigureAwait(false); + await channel.BasicConsumeAsync(queue, autoAck: false, consumer, cancellationToken); } - private void EnsureChannel() + private async Task EnsureChannelAsync(CancellationToken cancellationToken) { if (_channel != null && _channel.IsOpen) { return; } - _connection ??= connectionFactory.CreateConnection(); - _channel = _connection.CreateModel(); + _connection ??= await connectionFactory.CreateConnectionAsync(cancellationToken); + _channel = await _connection.CreateChannelAsync(cancellationToken: cancellationToken); } /// diff --git a/src/Modules/TakeoutSaaS.Module.Messaging/TakeoutSaaS.Module.Messaging.csproj b/src/Modules/TakeoutSaaS.Module.Messaging/TakeoutSaaS.Module.Messaging.csproj index ef94b34..f5650aa 100644 --- a/src/Modules/TakeoutSaaS.Module.Messaging/TakeoutSaaS.Module.Messaging.csproj +++ b/src/Modules/TakeoutSaaS.Module.Messaging/TakeoutSaaS.Module.Messaging.csproj @@ -5,13 +5,13 @@ enable - - - - - - - + + + + + + + diff --git a/src/Modules/TakeoutSaaS.Module.Scheduler/TakeoutSaaS.Module.Scheduler.csproj b/src/Modules/TakeoutSaaS.Module.Scheduler/TakeoutSaaS.Module.Scheduler.csproj index 6c7f697..5024078 100644 --- a/src/Modules/TakeoutSaaS.Module.Scheduler/TakeoutSaaS.Module.Scheduler.csproj +++ b/src/Modules/TakeoutSaaS.Module.Scheduler/TakeoutSaaS.Module.Scheduler.csproj @@ -5,14 +5,14 @@ enable - + - - - - - - + + + + + + diff --git a/src/Modules/TakeoutSaaS.Module.Sms/TakeoutSaaS.Module.Sms.csproj b/src/Modules/TakeoutSaaS.Module.Sms/TakeoutSaaS.Module.Sms.csproj index 7ac9fcd..7353ff4 100644 --- a/src/Modules/TakeoutSaaS.Module.Sms/TakeoutSaaS.Module.Sms.csproj +++ b/src/Modules/TakeoutSaaS.Module.Sms/TakeoutSaaS.Module.Sms.csproj @@ -5,13 +5,13 @@ enable - - - - - - - + + + + + + + diff --git a/src/Modules/TakeoutSaaS.Module.Storage/TakeoutSaaS.Module.Storage.csproj b/src/Modules/TakeoutSaaS.Module.Storage/TakeoutSaaS.Module.Storage.csproj index b8007bc279d1b63f438232e6f9b3f12e343077e2..733f714205aac15c59b71d6e4073f47d610f9b0a 100644 GIT binary patch delta 18 acmca6a7kc89uuRUumS);*#=+$ delta 22 ecmca4a7|!C9uuoEgC2w7