feat: add 客户端
This commit is contained in:
88
PetWashControl/Services/ApiService.cs
Normal file
88
PetWashControl/Services/ApiService.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using PetWashControl.Models;
|
||||
|
||||
namespace PetWashControl.Services;
|
||||
|
||||
public class ApiService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ConfigurationService _config;
|
||||
|
||||
public ApiService(ConfigurationService? config = null)
|
||||
{
|
||||
_config = config ?? new ConfigurationService();
|
||||
_httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(_config.ApiBaseUrl),
|
||||
Timeout = TimeSpan.FromSeconds(30)
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<Package>> GetPackagesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _httpClient.GetFromJsonAsync<List<Package>>("api/packages")
|
||||
?? new List<Package>();
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new Exception($"无法连接到服务器: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Order?> CreateOrderAsync(int packageId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.PostAsJsonAsync("api/orders", new { packageId });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<Order>();
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new Exception($"创建订单失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Order?> ConfirmPaymentAsync(int orderId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.PostAsync($"api/orders/{orderId}/payment", null);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<Order>();
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new Exception($"确认支付失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Order?> GetOrderAsync(int orderId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _httpClient.GetFromJsonAsync<Order>($"api/orders/{orderId}");
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new Exception($"获取订单失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Order?> UpdateOrderStatusAsync(int orderId, OrderStatus status)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.PutAsJsonAsync($"api/orders/{orderId}/status", new { status });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<Order>();
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new Exception($"更新订单状态失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
PetWashControl/Services/ConfigurationService.cs
Normal file
12
PetWashControl/Services/ConfigurationService.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace PetWashControl.Services;
|
||||
|
||||
public class ConfigurationService
|
||||
{
|
||||
public string ApiBaseUrl { get; set; } = "https://localhost:7001/";
|
||||
public string MqttBrokerHost { get; set; } = "localhost";
|
||||
public int MqttBrokerPort { get; set; } = 1883;
|
||||
public string MqttClientId { get; set; } = "PetWashControl";
|
||||
|
||||
public int PaymentCheckIntervalSeconds { get; set; } = 2;
|
||||
public int WashSimulationSeconds { get; set; } = 10;
|
||||
}
|
||||
50
PetWashControl/Services/LogService.cs
Normal file
50
PetWashControl/Services/LogService.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace PetWashControl.Services;
|
||||
|
||||
public class LogService
|
||||
{
|
||||
private readonly string _logFilePath;
|
||||
|
||||
public LogService()
|
||||
{
|
||||
var logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||
Directory.CreateDirectory(logDirectory);
|
||||
_logFilePath = Path.Combine(logDirectory, $"log_{DateTime.Now:yyyyMMdd}.txt");
|
||||
}
|
||||
|
||||
public void LogInfo(string message)
|
||||
{
|
||||
Log("INFO", message);
|
||||
}
|
||||
|
||||
public void LogWarning(string message)
|
||||
{
|
||||
Log("WARN", message);
|
||||
}
|
||||
|
||||
public void LogError(string message, Exception? ex = null)
|
||||
{
|
||||
var fullMessage = ex != null ? $"{message}\n{ex}" : message;
|
||||
Log("ERROR", fullMessage);
|
||||
}
|
||||
|
||||
private void Log(string level, string message)
|
||||
{
|
||||
var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
|
||||
|
||||
// 输出到调试窗口
|
||||
Debug.WriteLine(logMessage);
|
||||
|
||||
// 写入文件
|
||||
try
|
||||
{
|
||||
File.AppendAllText(_logFilePath, logMessage + Environment.NewLine);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略日志写入失败
|
||||
}
|
||||
}
|
||||
}
|
||||
109
PetWashControl/Services/MqttClientService.cs
Normal file
109
PetWashControl/Services/MqttClientService.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using MQTTnet;
|
||||
using MQTTnet.Client;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PetWashControl.Services;
|
||||
|
||||
public class MqttClientService
|
||||
{
|
||||
private readonly IMqttClient _mqttClient;
|
||||
private readonly MqttClientOptions _options;
|
||||
private readonly ConfigurationService _config;
|
||||
|
||||
public event Action<string, string>? MessageReceived;
|
||||
public bool IsConnected => _mqttClient.IsConnected;
|
||||
|
||||
public MqttClientService(ConfigurationService? config = null)
|
||||
{
|
||||
_config = config ?? new ConfigurationService();
|
||||
|
||||
var factory = new MqttFactory();
|
||||
_mqttClient = factory.CreateMqttClient();
|
||||
|
||||
_options = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer(_config.MqttBrokerHost, _config.MqttBrokerPort)
|
||||
.WithClientId(_config.MqttClientId)
|
||||
.WithCleanSession()
|
||||
.Build();
|
||||
|
||||
_mqttClient.ApplicationMessageReceivedAsync += OnMessageReceived;
|
||||
_mqttClient.DisconnectedAsync += OnDisconnected;
|
||||
}
|
||||
|
||||
public async Task ConnectAsync()
|
||||
{
|
||||
if (_mqttClient.IsConnected)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await _mqttClient.ConnectAsync(_options, CancellationToken.None);
|
||||
|
||||
// 订阅设备状态和命令主题
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter("device/status")
|
||||
.WithTopicFilter("device/command")
|
||||
.Build();
|
||||
|
||||
await _mqttClient.SubscribeAsync(subscribeOptions, CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"MQTT连接失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (_mqttClient.IsConnected)
|
||||
{
|
||||
await _mqttClient.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PublishAsync(string topic, object payload)
|
||||
{
|
||||
if (!_mqttClient.IsConnected)
|
||||
{
|
||||
throw new InvalidOperationException("MQTT客户端未连接");
|
||||
}
|
||||
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(json)
|
||||
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build();
|
||||
|
||||
await _mqttClient.PublishAsync(message, CancellationToken.None);
|
||||
}
|
||||
|
||||
private Task OnMessageReceived(MqttApplicationMessageReceivedEventArgs args)
|
||||
{
|
||||
var topic = args.ApplicationMessage.Topic;
|
||||
var payloadBytes = args.ApplicationMessage.PayloadSegment.ToArray();
|
||||
var payload = Encoding.UTF8.GetString(payloadBytes);
|
||||
|
||||
MessageReceived?.Invoke(topic, payload);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnDisconnected(MqttClientDisconnectedEventArgs args)
|
||||
{
|
||||
// 自动重连
|
||||
if (!args.ClientWasConnected)
|
||||
return;
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 重连失败,等待下次重试
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user