This commit is contained in:
GukSang.Jin
2026-03-18 13:53:44 +08:00
parent 54b3448e31
commit 0a884fa6cb
20 changed files with 1700 additions and 110 deletions

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PetWash.Api.Data;
using PetWash.Api.Models;
namespace PetWash.Api.Controllers;
@@ -18,7 +19,9 @@ public class PackagesController : ControllerBase
[HttpGet]
public async Task<IActionResult> GetPackages()
{
var packages = await _context.Packages.ToListAsync();
var packages = await _context.Packages
.OrderBy(x => x.Id)
.ToListAsync();
return Ok(packages);
}
@@ -31,4 +34,71 @@ public class PackagesController : ControllerBase
return Ok(package);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdatePackage(int id, [FromBody] UpdatePackageRequest request)
{
var package = await _context.Packages.FindAsync(id);
if (package == null)
{
return NotFound();
}
if (request.Price <= 0)
{
return BadRequest("套餐金额必须大于 0。");
}
if (request.FirstSprayWaterTime <= 0 ||
request.AfterShampoo1SprayTime <= 0 ||
request.AfterShampoo2SprayTime <= 0 ||
request.AfterShampoo3SprayTime <= 0 ||
request.SprayShampoo1Time <= 0 ||
request.SprayShampoo2Time <= 0 ||
request.SprayShampoo3Time <= 0 ||
request.HotAirTime <= 0 ||
request.ColdAirTime <= 0 ||
request.UvSterilizationTime <= 0)
{
return BadRequest("套餐流程时间必须全部大于 0。");
}
package.Price = request.Price;
package.FirstSprayWaterTime = request.FirstSprayWaterTime;
package.AfterShampoo1SprayTime = request.AfterShampoo1SprayTime;
package.AfterShampoo2SprayTime = request.AfterShampoo2SprayTime;
package.AfterShampoo3SprayTime = request.AfterShampoo3SprayTime;
package.SprayShampoo1Time = request.SprayShampoo1Time;
package.SprayShampoo2Time = request.SprayShampoo2Time;
package.SprayShampoo3Time = request.SprayShampoo3Time;
package.HotAirTime = request.HotAirTime;
package.ColdAirTime = request.ColdAirTime;
package.UvSterilizationTime = request.UvSterilizationTime;
package.DurationMinutes = request.FirstSprayWaterTime
+ request.AfterShampoo1SprayTime
+ request.AfterShampoo2SprayTime
+ request.AfterShampoo3SprayTime
+ request.SprayShampoo1Time
+ request.SprayShampoo2Time
+ request.SprayShampoo3Time
+ request.HotAirTime
+ request.ColdAirTime
+ request.UvSterilizationTime;
await _context.SaveChangesAsync();
return Ok(package);
}
}
public record UpdatePackageRequest(
decimal Price,
int FirstSprayWaterTime,
int AfterShampoo1SprayTime,
int AfterShampoo2SprayTime,
int AfterShampoo3SprayTime,
int SprayShampoo1Time,
int SprayShampoo2Time,
int SprayShampoo3Time,
int HotAirTime,
int ColdAirTime,
int UvSterilizationTime);

View File

@@ -13,12 +13,6 @@ public class PetWashDbContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 初始化套餐数据
modelBuilder.Entity<Package>().HasData(
new Package { Id = 1, Name = "套餐1", Price = 50, DurationMinutes = 38, Description = "适用于小型犬" },
new Package { Id = 2, Name = "套餐2", Price = 80, DurationMinutes = 48, Description = "适用于中型犬" },
new Package { Id = 3, Name = "套餐3", Price = 120, DurationMinutes = 60, Description = "适用于大型犬" }
);
modelBuilder.Entity<Package>().HasData(PackageCatalog.DefaultPackages.ToArray());
}
}

View File

@@ -7,4 +7,14 @@ public class Package
public decimal Price { get; set; }
public int DurationMinutes { get; set; }
public string Description { get; set; } = string.Empty;
public int FirstSprayWaterTime { get; set; }
public int AfterShampoo1SprayTime { get; set; }
public int AfterShampoo2SprayTime { get; set; }
public int AfterShampoo3SprayTime { get; set; }
public int SprayShampoo1Time { get; set; }
public int SprayShampoo2Time { get; set; }
public int SprayShampoo3Time { get; set; }
public int HotAirTime { get; set; }
public int ColdAirTime { get; set; }
public int UvSterilizationTime { get; set; }
}

View File

@@ -0,0 +1,62 @@
namespace PetWash.Api.Models;
public static class PackageCatalog
{
public static IReadOnlyList<Package> DefaultPackages { get; } =
[
new Package
{
Id = 1,
Name = "小型犬套餐",
Price = 0.01m,
DurationMinutes = 20,
Description = "适用于小型犬,洗护流程较短",
FirstSprayWaterTime = 2,
AfterShampoo1SprayTime = 2,
AfterShampoo2SprayTime = 2,
AfterShampoo3SprayTime = 2,
SprayShampoo1Time = 1,
SprayShampoo2Time = 1,
SprayShampoo3Time = 1,
HotAirTime = 5,
ColdAirTime = 2,
UvSterilizationTime = 2
},
new Package
{
Id = 2,
Name = "中型犬套餐",
Price = 0.02m,
DurationMinutes = 26,
Description = "适用于中型犬,洗护流程适中",
FirstSprayWaterTime = 3,
AfterShampoo1SprayTime = 3,
AfterShampoo2SprayTime = 3,
AfterShampoo3SprayTime = 3,
SprayShampoo1Time = 1,
SprayShampoo2Time = 1,
SprayShampoo3Time = 1,
HotAirTime = 6,
ColdAirTime = 3,
UvSterilizationTime = 2
},
new Package
{
Id = 3,
Name = "大型犬套餐",
Price = 0.03m,
DurationMinutes = 37,
Description = "适用于大型犬,洗护流程更长",
FirstSprayWaterTime = 4,
AfterShampoo1SprayTime = 4,
AfterShampoo2SprayTime = 4,
AfterShampoo3SprayTime = 4,
SprayShampoo1Time = 2,
SprayShampoo2Time = 2,
SprayShampoo3Time = 2,
HotAirTime = 8,
ColdAirTime = 4,
UvSterilizationTime = 3
}
];
}

View File

@@ -1,12 +1,14 @@
using System.Data;
using System.Data.Common;
using Microsoft.EntityFrameworkCore;
using PetWash.Api.Data;
using PetWash.Api.Models;
using PetWash.Api.Services;
var builder = WebApplication.CreateBuilder(args);
// 配置数据库(支持 SQLite 和 MySQL
var dbProvider = builder.Configuration.GetValue<string>("DatabaseProvider") ?? "Sqlite";
var connectionString = dbProvider == "MySql"
var connectionString = dbProvider == "MySql"
? builder.Configuration.GetConnectionString("MySqlConnection")
: builder.Configuration.GetConnectionString("DefaultConnection");
@@ -23,7 +25,6 @@ builder.Services.AddDbContext<PetWashDbContext>(options =>
}
});
// 添加服务
builder.Services.AddSingleton<MqttService>();
builder.Services.AddHostedService(provider => provider.GetRequiredService<MqttService>());
builder.Services.AddScoped<OrderService>();
@@ -34,7 +35,6 @@ builder.Services.AddHttpClient<WeChatPayService>(client =>
client.Timeout = TimeSpan.FromSeconds(15);
});
// 添加CORS
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
@@ -51,36 +51,79 @@ builder.Services.AddSwaggerGen();
var app = builder.Build();
// 初始化数据库
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<PetWashDbContext>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
try
{
logger.LogInformation("开始初始化数据库...");
// 确保数据库已创建
logger.LogInformation("Starting database initialization.");
var created = db.Database.EnsureCreated();
if (created)
{
logger.LogInformation("数据库创建成功,种子数据已插入");
logger.LogInformation("Database created and seed data inserted.");
}
else
{
logger.LogInformation("数据库已存在");
logger.LogInformation("Database already exists.");
}
EnsurePackageTimingColumns(db, logger);
foreach (var package in PackageCatalog.DefaultPackages)
{
var existingPackage = db.Packages.FirstOrDefault(x => x.Id == package.Id);
if (existingPackage is null)
{
db.Packages.Add(new Package
{
Id = package.Id,
Name = package.Name,
Price = package.Price,
DurationMinutes = package.DurationMinutes,
Description = package.Description,
FirstSprayWaterTime = package.FirstSprayWaterTime,
AfterShampoo1SprayTime = package.AfterShampoo1SprayTime,
AfterShampoo2SprayTime = package.AfterShampoo2SprayTime,
AfterShampoo3SprayTime = package.AfterShampoo3SprayTime,
SprayShampoo1Time = package.SprayShampoo1Time,
SprayShampoo2Time = package.SprayShampoo2Time,
SprayShampoo3Time = package.SprayShampoo3Time,
HotAirTime = package.HotAirTime,
ColdAirTime = package.ColdAirTime,
UvSterilizationTime = package.UvSterilizationTime
});
}
else
{
existingPackage.Name = package.Name;
existingPackage.Price = package.Price;
existingPackage.DurationMinutes = package.DurationMinutes;
existingPackage.Description = package.Description;
existingPackage.FirstSprayWaterTime = package.FirstSprayWaterTime;
existingPackage.AfterShampoo1SprayTime = package.AfterShampoo1SprayTime;
existingPackage.AfterShampoo2SprayTime = package.AfterShampoo2SprayTime;
existingPackage.AfterShampoo3SprayTime = package.AfterShampoo3SprayTime;
existingPackage.SprayShampoo1Time = package.SprayShampoo1Time;
existingPackage.SprayShampoo2Time = package.SprayShampoo2Time;
existingPackage.SprayShampoo3Time = package.SprayShampoo3Time;
existingPackage.HotAirTime = package.HotAirTime;
existingPackage.ColdAirTime = package.ColdAirTime;
existingPackage.UvSterilizationTime = package.UvSterilizationTime;
}
}
db.SaveChanges();
}
catch (Exception ex)
{
logger.LogError(ex, "数据库初始化失败");
logger.LogError(ex, "Database initialization failed.");
throw;
}
}
// 启用 Swagger所有环境
app.UseSwagger();
app.UseSwaggerUI();
@@ -90,3 +133,56 @@ app.UseAuthorization();
app.MapControllers();
app.Run();
static void EnsurePackageTimingColumns(PetWashDbContext db, ILogger logger)
{
var providerName = db.Database.ProviderName ?? string.Empty;
var isSqlite = providerName.Contains("Sqlite", StringComparison.OrdinalIgnoreCase);
var missingColumns = GetMissingPackageTimingColumns(db.Database.GetDbConnection(), isSqlite);
foreach (var column in missingColumns)
{
var sql = isSqlite
? $"ALTER TABLE Packages ADD COLUMN {column.Name} INTEGER NOT NULL DEFAULT {column.DefaultValue};"
: $"ALTER TABLE Packages ADD COLUMN {column.Name} INT NOT NULL DEFAULT {column.DefaultValue};";
db.Database.ExecuteSqlRaw(sql);
logger.LogInformation("Added missing package timing column {ColumnName}", column.Name);
}
}
static List<(string Name, int DefaultValue)> GetMissingPackageTimingColumns(DbConnection connection, bool isSqlite)
{
var expectedColumns = new List<(string Name, int DefaultValue)>
{
("FirstSprayWaterTime", 2),
("AfterShampoo1SprayTime", 2),
("AfterShampoo2SprayTime", 2),
("AfterShampoo3SprayTime", 2),
("SprayShampoo1Time", 1),
("SprayShampoo2Time", 1),
("SprayShampoo3Time", 1),
("HotAirTime", 5),
("ColdAirTime", 2),
("UvSterilizationTime", 2)
};
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
using var command = connection.CreateCommand();
command.CommandText = isSqlite ? "PRAGMA table_info(Packages);" : "SHOW COLUMNS FROM Packages;";
using var reader = command.ExecuteReader();
var existingColumns = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
while (reader.Read())
{
existingColumns.Add(reader.GetString(isSqlite ? 1 : 0));
}
return expectedColumns
.Where(column => !existingColumns.Contains(column.Name))
.ToList();
}

View File

@@ -12,13 +12,13 @@
},
"DatabaseProvider": "Sqlite",
"WeChatPay": {
"AppId": "",
"AppId": "wxa27a3e3cfce7ae19",
"MerchantId": "1107066208",
"CertificateSerialNumber": "",
"CertificateSerialNumber": "3243AE8427384A692FBAA92C5EC5887BEF1988FD",
"PrivateKeyPath": "",
"PrivateKeyPem": "",
"NotifyUrl": "",
"ApiV3Key": "",
"ApiV3Key": "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDMlj1LkO9Cfg3LWBYpe9GBn7vWgLE6kqEG1ohaxbaPxA6OwuGn0XQZfRBbJmncSXGLYahQ7T0OvFBIp8SyYm6q9kol8c9naxd+KxjMrx/qSWqwEJ76meBNK6LBYBVFTobg47cexpyR1TOZK0EFBGJQU2yQ1nsuQczVvq+WaSn4+kVENWf+o2g2nFS1VXNBIjL0/C8vXbz/0Y8k6ecH5mbmy/t+YR6X4TsiIAzIxIcfMMNhVCwqKLsu3D20N0ViYbKToHWIXi8wS8dyruHqQ1lZVJV/fF7pdI36HFI94enksCZrDb1LVFjL+4ccE04MJLIEZSH73RrOFkLaRzn8pwBbAgMBAAECggEAY7kD7baa+XVKMgkg3F2vVJjQzZDzUpKwjQ27b0uaXl95nRrfNZcCGX59n4CM70SZZRBYJAJP1cP",
"PlatformPublicKeyPath": "",
"PlatformPublicKeyPem": "",
"PlatformPublicKeySerial": "",