206 lines
6.0 KiB
C#
206 lines
6.0 KiB
C#
using System.Globalization;
|
||
using ApiClient;
|
||
using ApiClient.Grpc;
|
||
using ApiClient.Http;
|
||
using ConsoleApp.Data;
|
||
using ConsoleApp.Logging;
|
||
using Domain.Entities;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Configuration;
|
||
|
||
var configuration = new ConfigurationBuilder()
|
||
.SetBasePath(AppContext.BaseDirectory)
|
||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
|
||
.AddEnvironmentVariables()
|
||
.Build();
|
||
|
||
var connectionString = configuration.GetConnectionString("Default")
|
||
?? throw new InvalidOperationException("Connection string 'Default' не задана.");
|
||
|
||
var httpOptions = configuration.GetSection("Http").Get<HttpSmsClientOptions>() ?? new();
|
||
var grpcOptions = configuration.GetSection("Grpc").Get<GrpcSmsClientOptions>() ?? new();
|
||
|
||
using var log = ConsoleLog.Open();
|
||
|
||
var smsClient = AskSmsClient(log, httpOptions, grpcOptions);
|
||
try
|
||
{
|
||
log.WriteLine("Инициализация базы данных...");
|
||
await using var db = CreateDbContext(connectionString);
|
||
await db.Database.MigrateAsync();
|
||
log.WriteLine("База данных готова.");
|
||
|
||
log.WriteLine("Запрос меню с сервера...");
|
||
var menuResponse = await smsClient.GetMenuAsync(withPrice: true);
|
||
|
||
if (!menuResponse.Success)
|
||
{
|
||
log.WriteLine(menuResponse.ToString());
|
||
return;
|
||
}
|
||
|
||
var dishes = menuResponse.Data?.MenuItems ?? [];
|
||
await SaveDishesAsync(db, dishes);
|
||
|
||
log.WriteLine("Меню:");
|
||
foreach (var dish in dishes)
|
||
{
|
||
log.WriteLine(dish.ToString());
|
||
}
|
||
|
||
var order = ReadOrder(log, dishes);
|
||
|
||
log.WriteLine(order.ToString());
|
||
log.WriteLine("Отправка заказа на сервер...");
|
||
var sendResponse = await smsClient.SendOrderAsync(order);
|
||
|
||
if (sendResponse.Success)
|
||
{
|
||
log.WriteLine("УСПЕХ");
|
||
return;
|
||
}
|
||
|
||
log.WriteLine(sendResponse.ToString());
|
||
}
|
||
finally
|
||
{
|
||
(smsClient as IDisposable)?.Dispose();
|
||
log.WriteLine($"Файл лога (запуск {log.StartedAt:yyyy-MM-dd HH:mm:ss}): {log.FilePath}");
|
||
}
|
||
|
||
static ISmsClient AskSmsClient(ConsoleLog log, HttpSmsClientOptions http, GrpcSmsClientOptions grpc)
|
||
{
|
||
log.WriteLine("Выберите протокол: 1 — HTTP, 2 — gRPC");
|
||
|
||
while (true)
|
||
{
|
||
var input = log.ReadLine("> ")?.Trim();
|
||
|
||
if (input is "1" or "http" or "Http" or "HTTP")
|
||
{
|
||
return SmsClientFactory.CreateHttp(http);
|
||
}
|
||
|
||
if (input is "2" or "grpc" or "Grpc" or "gRPC")
|
||
{
|
||
return SmsClientFactory.CreateGrpc(grpc);
|
||
}
|
||
|
||
log.WriteLine("Введите 1 (HTTP) или 2 (gRPC).");
|
||
}
|
||
}
|
||
|
||
static AppDbContext CreateDbContext(string connectionString)
|
||
{
|
||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||
.UseNpgsql(connectionString)
|
||
.UseSnakeCaseNamingConvention()
|
||
.Options;
|
||
return new AppDbContext(options);
|
||
}
|
||
|
||
static async Task SaveDishesAsync(AppDbContext db, IReadOnlyList<Dish> dishes)
|
||
{
|
||
await db.Dishes.ExecuteDeleteAsync();
|
||
db.Dishes.AddRange(dishes.Select(d => new MenuDish
|
||
{
|
||
Id = d.Id,
|
||
Article = d.Article,
|
||
Name = d.Name,
|
||
Price = d.Price,
|
||
IsWeighted = d.IsWeighted,
|
||
FullPath = d.FullPath,
|
||
Barcodes = d.Barcodes.ToList(),
|
||
}));
|
||
await db.SaveChangesAsync();
|
||
}
|
||
|
||
static Order ReadOrder(ConsoleLog log, IReadOnlyList<Dish> dishes)
|
||
{
|
||
var dishesByArticle = dishes.ToDictionary(d => d.Article, StringComparer.OrdinalIgnoreCase);
|
||
|
||
log.WriteLine();
|
||
log.WriteLine("Введите позиции заказа в формате Код:Количество;Код:Количество;...");
|
||
|
||
while (true)
|
||
{
|
||
var input = log.ReadLine("> ");
|
||
|
||
if (!TryParseOrderInput(input, out var lines, out var parseError))
|
||
{
|
||
log.WriteLine(parseError);
|
||
continue;
|
||
}
|
||
|
||
var unknownArticles = lines
|
||
.Select(line => line.Article)
|
||
.Where(article => !dishesByArticle.ContainsKey(article))
|
||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||
.ToList();
|
||
|
||
if (unknownArticles.Count > 0)
|
||
{
|
||
log.WriteLine($"Неизвестные коды: {string.Join(", ", unknownArticles)}");
|
||
continue;
|
||
}
|
||
|
||
Order order = new();
|
||
foreach (var (article, quantity) in lines)
|
||
{
|
||
order = order.AddItem(dishesByArticle[article].Id, quantity);
|
||
}
|
||
|
||
return order;
|
||
}
|
||
}
|
||
|
||
static bool TryParseOrderInput(
|
||
string? input,
|
||
out List<(string Article, decimal Quantity)> items,
|
||
out string errorMessage)
|
||
{
|
||
items = [];
|
||
|
||
if (string.IsNullOrWhiteSpace(input))
|
||
{
|
||
errorMessage = "Строка заказа не может быть пустой.";
|
||
return false;
|
||
}
|
||
|
||
var parts = input.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||
foreach (var part in parts)
|
||
{
|
||
var pair = part.Split(':', 2);
|
||
if (pair.Length != 2)
|
||
{
|
||
errorMessage = $"Некорректный формат позиции: '{part}'. Ожидается Код:Количество.";
|
||
return false;
|
||
}
|
||
|
||
var article = pair[0].Trim();
|
||
if (string.IsNullOrWhiteSpace(article))
|
||
{
|
||
errorMessage = "Артикул не может быть пустым.";
|
||
return false;
|
||
}
|
||
|
||
if (!decimal.TryParse(pair[1].Trim(), NumberStyles.Number, CultureInfo.InvariantCulture, out var quantity)
|
||
&& !decimal.TryParse(pair[1].Trim(), NumberStyles.Number, CultureInfo.CurrentCulture, out quantity))
|
||
{
|
||
errorMessage = $"Некорректное количество для артикула '{article}'.";
|
||
return false;
|
||
}
|
||
|
||
if (quantity <= 0)
|
||
{
|
||
errorMessage = $"Количество для артикула '{article}' должно быть больше нуля.";
|
||
return false;
|
||
}
|
||
|
||
items.Add((article, quantity));
|
||
}
|
||
|
||
errorMessage = "";
|
||
return true;
|
||
}
|