Platform upgrade: login auth fix, OCR/OSS upload, system menus and dicts, weather dashboard, weighing and invoice modules
This commit is contained in:
@@ -12,9 +12,22 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
|
||||
public DbSet<Region> Regions => Set<Region>();
|
||||
public DbSet<Product> Products => Set<Product>();
|
||||
public DbSet<PurchaseOrder> PurchaseOrders => Set<PurchaseOrder>();
|
||||
public DbSet<PurchaseWeigh> PurchaseWeighs => Set<PurchaseWeigh>();
|
||||
public DbSet<PurchaseTransfer> PurchaseTransfers => Set<PurchaseTransfer>();
|
||||
public DbSet<PaymentRecord> PaymentRecords => Set<PaymentRecord>();
|
||||
public DbSet<Invoice> Invoices => Set<Invoice>();
|
||||
|
||||
// 系统管理(RBAC)
|
||||
public DbSet<SysDict> SysDicts => Set<SysDict>();
|
||||
public DbSet<SysDictItem> SysDictItems => Set<SysDictItem>();
|
||||
public DbSet<SysMenu> SysMenus => Set<SysMenu>();
|
||||
public DbSet<SysRole> SysRoles => Set<SysRole>();
|
||||
public DbSet<SysRoleMenu> SysRoleMenus => Set<SysRoleMenu>();
|
||||
|
||||
// 通知公告
|
||||
public DbSet<Announcement> Announcements => Set<Announcement>();
|
||||
public DbSet<AnnouncementRead> AnnouncementReads => Set<AnnouncementRead>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
@@ -41,6 +54,9 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
|
||||
modelBuilder.Entity<User>()
|
||||
.HasOne(u => u.Org).WithMany().HasForeignKey(u => u.OrgId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
modelBuilder.Entity<User>()
|
||||
.HasOne(u => u.SysRole).WithMany().HasForeignKey(u => u.RoleId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// 组织
|
||||
modelBuilder.Entity<Organization>()
|
||||
@@ -119,6 +135,70 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
|
||||
.HasOne(p => p.Operator).WithMany().HasForeignKey(p => p.OperatorId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// 收购单-磅次
|
||||
modelBuilder.Entity<PurchaseWeigh>()
|
||||
.HasOne(w => w.PurchaseOrder).WithMany(p => p.Weighs)
|
||||
.HasForeignKey(w => w.PurchaseOrderId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// 票据过户
|
||||
modelBuilder.Entity<PurchaseTransfer>()
|
||||
.HasOne(t => t.PurchaseOrder).WithMany(p => p.Transfers)
|
||||
.HasForeignKey(t => t.PurchaseOrderId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
modelBuilder.Entity<PurchaseTransfer>()
|
||||
.HasOne(t => t.Operator).WithMany().HasForeignKey(t => t.OperatorId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// 数据字典
|
||||
modelBuilder.Entity<SysDict>()
|
||||
.Property(d => d.Code).HasMaxLength(64);
|
||||
modelBuilder.Entity<SysDict>()
|
||||
.HasIndex(d => d.Code).IsUnique();
|
||||
modelBuilder.Entity<SysDict>()
|
||||
.Property(d => d.Name).HasMaxLength(100);
|
||||
modelBuilder.Entity<SysDictItem>()
|
||||
.Property(i => i.Label).HasMaxLength(100);
|
||||
modelBuilder.Entity<SysDictItem>()
|
||||
.Property(i => i.Value).HasMaxLength(64);
|
||||
modelBuilder.Entity<SysDictItem>()
|
||||
.HasIndex(i => new { i.DictId, i.Value }).IsUnique();
|
||||
modelBuilder.Entity<SysDict>()
|
||||
.HasMany(d => d.Items).WithOne().HasForeignKey(i => i.DictId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// 菜单(自引用父子关系显式指定外键,避免 EF 推断额外列)
|
||||
modelBuilder.Entity<SysMenu>()
|
||||
.Property(m => m.Name).HasMaxLength(100);
|
||||
modelBuilder.Entity<SysMenu>()
|
||||
.Property(m => m.Path).HasMaxLength(200);
|
||||
modelBuilder.Entity<SysMenu>()
|
||||
.Property(m => m.Permission).HasMaxLength(100);
|
||||
modelBuilder.Entity<SysMenu>()
|
||||
.HasIndex(m => m.Permission);
|
||||
modelBuilder.Entity<SysMenu>()
|
||||
.HasMany(m => m.Children).WithOne().HasForeignKey(m => m.ParentId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// 角色
|
||||
modelBuilder.Entity<SysRole>()
|
||||
.Property(r => r.Name).HasMaxLength(100);
|
||||
modelBuilder.Entity<SysRole>()
|
||||
.HasIndex(r => r.Code).IsUnique();
|
||||
modelBuilder.Entity<SysRoleMenu>()
|
||||
.HasKey(rm => new { rm.RoleId, rm.MenuId });
|
||||
|
||||
// 公告
|
||||
modelBuilder.Entity<Announcement>()
|
||||
.Property(a => a.Title).HasMaxLength(200);
|
||||
modelBuilder.Entity<Announcement>()
|
||||
.HasIndex(a => new { a.Type, a.PublishedAt });
|
||||
modelBuilder.Entity<Announcement>()
|
||||
.HasOne(a => a.Publisher).WithMany().HasForeignKey(a => a.PublisherId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
modelBuilder.Entity<AnnouncementRead>()
|
||||
.HasIndex(r => new { r.AnnouncementId, r.UserId }).IsUnique();
|
||||
|
||||
// 支付
|
||||
modelBuilder.Entity<PaymentRecord>()
|
||||
.Property(p => p.PayNo).HasMaxLength(32);
|
||||
|
||||
@@ -57,14 +57,87 @@ public static class DbSeeder
|
||||
stationWest.ParentId = company.Id;
|
||||
db.SaveChanges();
|
||||
|
||||
// ---------- 角色(RBAC) ----------
|
||||
var roleAdmin = new SysRole { Name = "超级管理员", Code = "super_admin", Description = "系统内置:拥有全部权限", IsSystem = true, CreatedAt = now };
|
||||
var roleCompany = new SysRole { Name = "公司管理员", Code = "company_admin", Description = "管理本公司及下属收购站业务", IsSystem = true, CreatedAt = now };
|
||||
var roleStation = new SysRole { Name = "收购站员工", Code = "station_staff", Description = "负责过磅称重、收购单录入", IsSystem = true, CreatedAt = now };
|
||||
var roleIndividual = new SysRole { Name = "收购个体", Code = "individual", Description = "个体收购户", IsSystem = true, CreatedAt = now };
|
||||
db.SysRoles.AddRange(roleAdmin, roleCompany, roleStation, roleIndividual);
|
||||
db.SaveChanges();
|
||||
|
||||
// ---------- 菜单/权限(RBAC 资源,Path 与前端路由一致) ----------
|
||||
var menus = new List<SysMenu>
|
||||
{
|
||||
// 首页
|
||||
new() { Name = "首页", Path = "/home", Component = "views/Home.vue", Icon = "HomeFilled", Type = "menu", Permission = "home:view", Sort = 1, CreatedAt = now },
|
||||
// 收购业务
|
||||
new() { Name = "收购业务", Path = "/weighing", Icon = "Van", Type = "directory", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "过磅称重", Path = "/weighing", Component = "views/weighing/WeighingList.vue", Icon = "Odometer", Type = "menu", Permission = "weighing:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "触摸屏过磅", Path = "/weighing/touch", Component = "views/weighing/WeighingTouch.vue", Icon = "Iphone", Type = "menu", Permission = "weighing:touch", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "电子支付", Path = "/payments", Component = "views/payments/PaymentList.vue", Icon = "Wallet", Type = "menu", Permission = "payment:list", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "反向开票", Path = "/invoices", Component = "views/invoices/InvoiceList.vue", Icon = "Tickets", Type = "menu", Permission = "invoice:list", Sort = 4, CreatedAt = now },
|
||||
// 基础资料
|
||||
new() { Name = "基础资料", Path = "/base", Icon = "FolderOpened", Type = "directory", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "农户管理", Path = "/farmers", Component = "views/farmers/FarmerList.vue", Icon = "User", Type = "menu", Permission = "farmer:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "品种管理", Path = "/products", Component = "views/products/ProductList.vue", Icon = "Goods", Type = "menu", Permission = "product:list", Sort = 2, CreatedAt = now },
|
||||
// 统计报表
|
||||
new() { Name = "统计报表", Path = "/reports", Icon = "DataAnalysis", Type = "directory", Sort = 4, CreatedAt = now },
|
||||
new() { Name = "收购报表", Path = "/reports/purchase", Component = "views/reports/ReportPurchase.vue", Icon = "TrendCharts", Type = "menu", Permission = "report:purchase", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "付款报表", Path = "/reports/payment", Component = "views/reports/ReportPayment.vue", Icon = "Money", Type = "menu", Permission = "report:payment", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "开票报表", Path = "/reports/invoice", Component = "views/reports/ReportInvoice.vue", Icon = "Document", Type = "menu", Permission = "report:invoice", Sort = 3, CreatedAt = now },
|
||||
// 系统管理
|
||||
new() { Name = "系统管理", Path = "/system", Icon = "Setting", Type = "directory", Sort = 5, CreatedAt = now },
|
||||
new() { Name = "组织管理", Path = "/orgs", Component = "views/orgs/OrgList.vue", Icon = "OfficeBuilding", Type = "menu", Permission = "system:org:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "用户管理", Path = "/users", Component = "views/users/UserList.vue", Icon = "Avatar", Type = "menu", Permission = "system:user:list", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "角色权限", Path = "/system/roles", Component = "views/system/RoleList.vue", Icon = "UserFilled", Type = "menu", Permission = "system:role:list", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "菜单管理", Path = "/system/menus", Component = "views/system/MenuList.vue", Icon = "Menu", Type = "menu", Permission = "system:menu:list", Sort = 4, CreatedAt = now },
|
||||
new() { Name = "数据字典", Path = "/system/dicts", Component = "views/system/DictList.vue", Icon = "Notebook", Type = "menu", Permission = "system:dict:list", Sort = 5, CreatedAt = now },
|
||||
// 通知公告
|
||||
new() { Name = "通知公告", Path = "/notify", Icon = "Bell", Type = "directory", Sort = 6, CreatedAt = now },
|
||||
new() { Name = "公告管理", Path = "/notify/announcements", Component = "views/notify/AnnouncementList.vue", Icon = "Bell", Type = "menu", Permission = "announcement:list", Sort = 1, CreatedAt = now },
|
||||
// 关于
|
||||
new() { Name = "关于系统", Path = "/about", Component = "views/about/About.vue", Icon = "InfoFilled", Type = "menu", Permission = "about:view", Sort = 7, CreatedAt = now },
|
||||
};
|
||||
db.SysMenus.AddRange(menus);
|
||||
db.SaveChanges();
|
||||
|
||||
// 目录菜单的父级关系(按路径匹配;目录路径与子菜单路径相同时以 Type 区分)
|
||||
SetChild(menus, "/weighing", "/weighing");
|
||||
SetChild(menus, "/weighing/touch", "/weighing");
|
||||
SetChild(menus, "/payments", "/weighing");
|
||||
SetChild(menus, "/invoices", "/weighing");
|
||||
SetChild(menus, "/farmers", "/base");
|
||||
SetChild(menus, "/products", "/base");
|
||||
SetChild(menus, "/reports/purchase", "/reports");
|
||||
SetChild(menus, "/reports/payment", "/reports");
|
||||
SetChild(menus, "/reports/invoice", "/reports");
|
||||
SetChild(menus, "/orgs", "/system");
|
||||
SetChild(menus, "/users", "/system");
|
||||
SetChild(menus, "/system/roles", "/system");
|
||||
SetChild(menus, "/system/menus", "/system");
|
||||
SetChild(menus, "/system/dicts", "/system");
|
||||
SetChild(menus, "/notify/announcements", "/notify");
|
||||
db.SaveChanges();
|
||||
|
||||
// 给超级管理员分配全部菜单;收购站员工分配常用菜单
|
||||
var allMenuIds = menus.Select(m => m.Id).ToArray();
|
||||
db.SysRoleMenus.AddRange(allMenuIds.Select(mid => new SysRoleMenu { RoleId = roleAdmin.Id, MenuId = mid }));
|
||||
var stationMenuIds = menus.Where(m => m.Type == "menu" && m.Permission is "home:view" or "weighing:list" or "weighing:touch" or "farmer:list" or "about:view").Select(m => m.Id);
|
||||
db.SysRoleMenus.AddRange(stationMenuIds.Select(mid => new SysRoleMenu { RoleId = roleStation.Id, MenuId = mid }));
|
||||
|
||||
// 公司管理员:除系统管理外的全部业务菜单
|
||||
var companyMenuIds = menus.Where(m => m.Type == "menu" && !m.Permission.StartsWith("system:")).Select(m => m.Id);
|
||||
db.SysRoleMenus.AddRange(companyMenuIds.Select(mid => new SysRoleMenu { RoleId = roleCompany.Id, MenuId = mid }));
|
||||
db.SaveChanges();
|
||||
|
||||
// ---------- 用户(默认密码均为 123456) ----------
|
||||
var users = new List<User>
|
||||
{
|
||||
new() { Username = "admin", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "系统管理员", Phone = "13800000000", Role = UserRole.SuperAdmin, CreatedAt = now },
|
||||
new() { Username = "company", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "王建国", Phone = "13800000001", Role = UserRole.CompanyAdmin, OrgId = company.Id, CreatedAt = now },
|
||||
new() { Username = "station1", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "李强", Phone = "13800000002", Role = UserRole.StationStaff, OrgId = stationEast.Id, CreatedAt = now },
|
||||
new() { Username = "station2", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "赵敏", Phone = "13800000003", Role = UserRole.StationStaff, OrgId = stationWest.Id, CreatedAt = now },
|
||||
new() { Username = "individual", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "张伟", Phone = "13800000004", Role = UserRole.Individual, OrgId = individual.Id, CreatedAt = now }
|
||||
new() { Username = "admin", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "系统管理员", Phone = "13800000000", Role = UserRole.SuperAdmin, RoleId = roleAdmin.Id, CreatedAt = now },
|
||||
new() { Username = "company", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "王建国", Phone = "13800000001", Role = UserRole.CompanyAdmin, RoleId = roleCompany.Id, OrgId = company.Id, CreatedAt = now },
|
||||
new() { Username = "station1", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "李强", Phone = "13800000002", Role = UserRole.StationStaff, RoleId = roleStation.Id, OrgId = stationEast.Id, CreatedAt = now },
|
||||
new() { Username = "station2", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "赵敏", Phone = "13800000003", Role = UserRole.StationStaff, RoleId = roleStation.Id, OrgId = stationWest.Id, CreatedAt = now },
|
||||
new() { Username = "individual", PasswordHash = BCrypt.Net.BCrypt.HashPassword("123456"), RealName = "张伟", Phone = "13800000004", Role = UserRole.Individual, RoleId = roleIndividual.Id, OrgId = individual.Id, CreatedAt = now }
|
||||
};
|
||||
db.Users.AddRange(users);
|
||||
db.SaveChanges();
|
||||
@@ -146,6 +219,75 @@ public static class DbSeeder
|
||||
db.PurchaseOrders.AddRange(orders);
|
||||
db.SaveChanges();
|
||||
|
||||
// ---------- 数据字典(收购等级 / 价格类型 / 票据格式 / 公告类型) ----------
|
||||
var dicts = new List<SysDict>
|
||||
{
|
||||
new() { Name = "收购等级", Code = "purchase_grade", Remark = "收购单磅次等级", IsSystem = true, Sort = 1, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "一等", Value = "一等", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "二等", Value = "二等", Sort = 2 },
|
||||
new() { Label = "三等", Value = "三等", Sort = 3 },
|
||||
new() { Label = "级外", Value = "级外", Sort = 4 },
|
||||
}
|
||||
},
|
||||
new() { Name = "价格类型", Code = "price_type", Remark = "过磅计价方式", IsSystem = true, Sort = 2, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "固定价格", Value = "Fixed", Ext = "按产品默认价", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "现场议价", Value = "Manual", Ext = "与农户协商价", Sort = 2 },
|
||||
new() { Label = "价格区间", Value = "Range", Ext = "区间内浮动价", Sort = 3 },
|
||||
}
|
||||
},
|
||||
new() { Name = "票据格式", Code = "receipt_format", Remark = "小票/票据打印格式", IsSystem = true, Sort = 3, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "58mm 热敏小票", Value = "thermal_58", Ext = "58mm", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "80mm 热敏小票", Value = "thermal_80", Ext = "80mm", Sort = 2 },
|
||||
new() { Label = "三等分针式票据", Value = "dotmatrix_3part", Ext = "241mm×139.7mm/联", Sort = 3 },
|
||||
new() { Label = "A5 激光打印", Value = "laser_a5", Ext = "A5", Sort = 4 },
|
||||
}
|
||||
},
|
||||
new() { Name = "公告类型", Code = "announcement_type", Remark = "通知公告类型", IsSystem = true, Sort = 4, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "通知", Value = "notice", Sort = 1 },
|
||||
new() { Label = "公告", Value = "announce", Sort = 2 },
|
||||
new() { Label = "价格行情", Value = "price", Sort = 3 },
|
||||
new() { Label = "政策法规", Value = "policy", Sort = 4 },
|
||||
}
|
||||
},
|
||||
new() { Name = "系统图标", Code = "system_logo", Remark = "主标题系统图标,可选大部分农产品图标", IsSystem = true, Sort = 5, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "苹果", Value = "Apple", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "葡萄", Value = "Grape", Sort = 2 },
|
||||
new() { Label = "西瓜", Value = "Watermelon", Sort = 3 },
|
||||
new() { Label = "樱桃", Value = "Cherry", Sort = 4 },
|
||||
new() { Label = "梨", Value = "Pear", Sort = 5 },
|
||||
new() { Label = "桃", Value = "Peach", Sort = 6 },
|
||||
new() { Label = "橙子", Value = "Orange", Sort = 7 },
|
||||
new() { Label = "农作物", Value = "Food", Sort = 8 },
|
||||
new() { Label = "时蔬", Value = "Dish", Sort = 9 },
|
||||
new() { Label = "牛奶", Value = "Milk", Sort = 10 },
|
||||
new() { Label = "冰淇淋", Value = "IceCream", Sort = 11 },
|
||||
new() { Label = "生鲜礼盒", Value = "TakeawayBox", Sort = 12 },
|
||||
}
|
||||
},
|
||||
};
|
||||
db.SysDicts.AddRange(dicts);
|
||||
db.SaveChanges();
|
||||
|
||||
// ---------- 通知公告(演示数据) ----------
|
||||
var announcements = new List<Announcement>
|
||||
{
|
||||
new() { Title = "欢迎使用农易富农产品收购交易平台", Content = "系统已完成升级,新增数据字典、角色权限、通知公告等功能。默认账号 admin / 123456。", Type = "announce", IsPinned = true, PublisherId = users[0].Id, PublishedAt = now, CreatedAt = now },
|
||||
new() { Title = "关于2026年夏季收购价格调整的通知", Content = "自8月15日起,一等品收购价调整为每公斤3.6元,请各收购站及时更新价格配置。", Type = "price", PublisherId = users[0].Id, PublishedAt = now.AddHours(-3), CreatedAt = now.AddHours(-3) },
|
||||
new() { Title = "电子秤串口连接使用说明", Content = "请在收购单新建界面点击电子秤区域连接串口,支持标准ASCII输出协议(如 ST,GS,+00123.45kg)。无串口设备时可使用手动输入模式。", Type = "notice", PublisherId = users[0].Id, PublishedAt = now.AddDays(-1), CreatedAt = now.AddDays(-1) },
|
||||
};
|
||||
db.Announcements.AddRange(announcements);
|
||||
db.SaveChanges();
|
||||
|
||||
// ---------- 历史付款(对应部分订单) ----------
|
||||
var payOrders = orders.Where(o => o.Status == PurchaseStatus.Completed)
|
||||
.OrderBy(o => o.CreatedAt).Take(120).ToList();
|
||||
@@ -192,4 +334,12 @@ public static class DbSeeder
|
||||
db.Invoices.AddRange(invoices);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
private static void SetChild(List<SysMenu> menus, string childPath, string parentPath)
|
||||
{
|
||||
var parent = menus.FirstOrDefault(m => m.Type == "directory" && m.Path == parentPath);
|
||||
if (parent is null) return;
|
||||
foreach (var child in menus.Where(m => m.Type != "directory" && m.Path == childPath && m.ParentId == null))
|
||||
child.ParentId = parent.Id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using AgriculturalPlatform.Api.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AgriculturalPlatform.Api.Data;
|
||||
@@ -27,6 +28,7 @@ public static class SchemaMigrator
|
||||
("IdCardFrontUrl", "IdCardFrontUrl NVARCHAR(500) NULL"),
|
||||
("IdCardBackUrl", "IdCardBackUrl NVARCHAR(500) NULL"),
|
||||
("AvatarUrl", "AvatarUrl NVARCHAR(500) NULL"),
|
||||
("PinyinInitials", "PinyinInitials VARCHAR(50) NULL"),
|
||||
];
|
||||
|
||||
foreach (var col in columns)
|
||||
@@ -41,7 +43,366 @@ public static class SchemaMigrator
|
||||
"Township = COALESCE(Township,''), GroupName = COALESCE(GroupName,''), " +
|
||||
"FarmerType = COALESCE(FarmerType,'农户'), " +
|
||||
"IdCardFrontUrl = COALESCE(IdCardFrontUrl,''), IdCardBackUrl = COALESCE(IdCardBackUrl,''), " +
|
||||
"AvatarUrl = COALESCE(AvatarUrl,'')");
|
||||
"AvatarUrl = COALESCE(AvatarUrl,''), PinyinInitials = COALESCE(PinyinInitials,'')");
|
||||
|
||||
// Users 表补充 RoleId 列(RBAC 角色外键)
|
||||
var userCols = new HashSet<string>(
|
||||
await db.Database
|
||||
.SqlQuery<string>($"SELECT COLUMN_NAME AS Value FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'Users'")
|
||||
.ToListAsync(),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
if (!userCols.Contains("RoleId"))
|
||||
await db.Database.ExecuteSqlRawAsync("ALTER TABLE Users ADD COLUMN RoleId INT NULL");
|
||||
|
||||
// PurchaseOrders 表补充 BoxCount 列(容器/框数合计)
|
||||
var poCols = new HashSet<string>(
|
||||
await db.Database
|
||||
.SqlQuery<string>($"SELECT COLUMN_NAME AS Value FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'PurchaseOrders'")
|
||||
.ToListAsync(),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
if (!poCols.Contains("BoxCount"))
|
||||
await db.Database.ExecuteSqlRawAsync("ALTER TABLE PurchaseOrders ADD COLUMN BoxCount INT NOT NULL DEFAULT 0");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保 RBAC/数据字典/多磅/公告等新增表存在(兼容旧库 EnsureCreated 不会补建新表)。
|
||||
/// </summary>
|
||||
public static async Task EnsureSystemTablesAsync(AppDbContext db)
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS SysDicts (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
Name VARCHAR(100) NOT NULL,
|
||||
Code VARCHAR(64) NOT NULL,
|
||||
Remark VARCHAR(500) NULL,
|
||||
IsSystem TINYINT(1) NOT NULL DEFAULT 0,
|
||||
Sort INT NOT NULL DEFAULT 0,
|
||||
CreatedAt DATETIME(6) NOT NULL,
|
||||
UNIQUE KEY uk_dicts_code (Code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS SysDictItems (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
DictId INT NOT NULL,
|
||||
Label VARCHAR(100) NOT NULL,
|
||||
Value VARCHAR(64) NOT NULL,
|
||||
Ext VARCHAR(255) NULL,
|
||||
IsDefault TINYINT(1) NOT NULL DEFAULT 0,
|
||||
Enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
Sort INT NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uk_dictitems (DictId, Value),
|
||||
KEY idx_dictitems_dict (DictId)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS SysMenus (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
ParentId INT NULL,
|
||||
Name VARCHAR(100) NOT NULL,
|
||||
Path VARCHAR(200) NOT NULL DEFAULT '',
|
||||
Component VARCHAR(200) NOT NULL DEFAULT '',
|
||||
Icon VARCHAR(50) NOT NULL DEFAULT '',
|
||||
Type VARCHAR(20) NOT NULL DEFAULT 'menu',
|
||||
Permission VARCHAR(100) NOT NULL DEFAULT '',
|
||||
Visible TINYINT(1) NOT NULL DEFAULT 1,
|
||||
Sort INT NOT NULL DEFAULT 0,
|
||||
CreatedAt DATETIME(6) NOT NULL,
|
||||
KEY idx_menus_permission (Permission)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS SysRoles (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
Name VARCHAR(100) NOT NULL,
|
||||
Code VARCHAR(100) NOT NULL,
|
||||
Description VARCHAR(500) NULL,
|
||||
IsSystem TINYINT(1) NOT NULL DEFAULT 0,
|
||||
CreatedAt DATETIME(6) NOT NULL,
|
||||
UNIQUE KEY uk_roles_code (Code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS SysRoleMenus (
|
||||
RoleId INT NOT NULL,
|
||||
MenuId INT NOT NULL,
|
||||
PRIMARY KEY (RoleId, MenuId)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS PurchaseWeighs (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
PurchaseOrderId INT NOT NULL,
|
||||
SortNo INT NOT NULL DEFAULT 1,
|
||||
Grade VARCHAR(20) NOT NULL DEFAULT '',
|
||||
PriceType VARCHAR(20) NOT NULL DEFAULT 'Manual',
|
||||
UnitPrice DECIMAL(18,2) NOT NULL DEFAULT 0,
|
||||
GrossWeight DECIMAL(18,2) NOT NULL DEFAULT 0,
|
||||
TareWeight DECIMAL(18,2) NOT NULL DEFAULT 0,
|
||||
NetWeight DECIMAL(18,2) NOT NULL DEFAULT 0,
|
||||
BoxCount INT NOT NULL DEFAULT 0,
|
||||
Amount DECIMAL(18,2) NOT NULL DEFAULT 0,
|
||||
CreatedAt DATETIME(6) NOT NULL,
|
||||
KEY idx_weighs_order (PurchaseOrderId)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS PurchaseTransfers (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
PurchaseOrderId INT NOT NULL,
|
||||
TransferNo VARCHAR(32) NOT NULL,
|
||||
FromFarmer VARCHAR(100) NOT NULL DEFAULT '',
|
||||
ToFarmer VARCHAR(100) NOT NULL DEFAULT '',
|
||||
ToIdCard VARCHAR(20) NOT NULL DEFAULT '',
|
||||
Reason VARCHAR(500) NOT NULL DEFAULT '',
|
||||
OperatorId INT NULL,
|
||||
CreatedAt DATETIME(6) NOT NULL,
|
||||
KEY idx_transfers_order (PurchaseOrderId)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS Announcements (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
Title VARCHAR(200) NOT NULL,
|
||||
Content LONGTEXT NOT NULL,
|
||||
Type VARCHAR(20) NOT NULL DEFAULT 'notice',
|
||||
ScopeOrgIds VARCHAR(500) NOT NULL DEFAULT '',
|
||||
IsPinned TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PublisherId INT NULL,
|
||||
PublishedAt DATETIME(6) NOT NULL,
|
||||
CreatedAt DATETIME(6) NOT NULL,
|
||||
KEY idx_announcements_type (Type, PublishedAt)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
await db.Database.ExecuteSqlRawAsync("""
|
||||
CREATE TABLE IF NOT EXISTS AnnouncementReads (
|
||||
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
AnnouncementId INT NOT NULL,
|
||||
UserId INT NOT NULL,
|
||||
ReadAt DATETIME(6) NOT NULL,
|
||||
UNIQUE KEY uk_ann_read (AnnouncementId, UserId)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
""");
|
||||
|
||||
// 幂等系统数据种子(兼容旧库:DbSeeder 仅在空库执行,老库需要补齐角色/菜单/字典/公告)
|
||||
await EnsureSystemSeedAsync(db);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为已有数据的旧库补齐 RBAC/数据字典/公告等系统种子数据(幂等,按表是否为空判断)。
|
||||
/// </summary>
|
||||
public static async Task EnsureSystemSeedAsync(AppDbContext db)
|
||||
{
|
||||
// 全新库由 DbSeeder 负责完整种子,这里仅补齐旧库(已有用户数据)缺失的系统数据
|
||||
if (!await db.Users.AnyAsync()) return;
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
||||
// ---------- 角色 ----------
|
||||
var roles = new List<SysRole>();
|
||||
if (!await db.SysRoles.AnyAsync())
|
||||
{
|
||||
roles =
|
||||
[
|
||||
new() { Name = "超级管理员", Code = "super_admin", Description = "系统内置:拥有全部权限", IsSystem = true, CreatedAt = now },
|
||||
new() { Name = "公司管理员", Code = "company_admin", Description = "管理本公司及下属收购站业务", IsSystem = true, CreatedAt = now },
|
||||
new() { Name = "收购站员工", Code = "station_staff", Description = "负责过磅称重、收购单录入", IsSystem = true, CreatedAt = now },
|
||||
new() { Name = "收购个体", Code = "individual", Description = "个体收购户", IsSystem = true, CreatedAt = now },
|
||||
];
|
||||
db.SysRoles.AddRange(roles);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
roles = await db.SysRoles.ToListAsync();
|
||||
}
|
||||
|
||||
// ---------- 菜单 / 权限 ----------
|
||||
if (!await db.SysMenus.AnyAsync())
|
||||
{
|
||||
var menus = new List<SysMenu>
|
||||
{
|
||||
new() { Name = "首页", Path = "/home", Component = "views/Home.vue", Icon = "HomeFilled", Type = "menu", Permission = "home:view", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "收购业务", Path = "/weighing", Icon = "Van", Type = "directory", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "过磅称重", Path = "/weighing", Component = "views/weighing/WeighingList.vue", Icon = "Odometer", Type = "menu", Permission = "weighing:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "触摸屏过磅", Path = "/weighing/touch", Component = "views/weighing/WeighingTouch.vue", Icon = "Iphone", Type = "menu", Permission = "weighing:touch", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "电子支付", Path = "/payments", Component = "views/payments/PaymentList.vue", Icon = "Wallet", Type = "menu", Permission = "payment:list", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "反向开票", Path = "/invoices", Component = "views/invoices/InvoiceList.vue", Icon = "Tickets", Type = "menu", Permission = "invoice:list", Sort = 4, CreatedAt = now },
|
||||
new() { Name = "基础资料", Path = "/base", Icon = "FolderOpened", Type = "directory", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "农户管理", Path = "/farmers", Component = "views/farmers/FarmerList.vue", Icon = "User", Type = "menu", Permission = "farmer:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "品种管理", Path = "/products", Component = "views/products/ProductList.vue", Icon = "Goods", Type = "menu", Permission = "product:list", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "统计报表", Path = "/reports", Icon = "DataAnalysis", Type = "directory", Sort = 4, CreatedAt = now },
|
||||
new() { Name = "收购报表", Path = "/reports/purchase", Component = "views/reports/ReportPurchase.vue", Icon = "TrendCharts", Type = "menu", Permission = "report:purchase", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "付款报表", Path = "/reports/payment", Component = "views/reports/ReportPayment.vue", Icon = "Money", Type = "menu", Permission = "report:payment", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "开票报表", Path = "/reports/invoice", Component = "views/reports/ReportInvoice.vue", Icon = "Document", Type = "menu", Permission = "report:invoice", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "系统管理", Path = "/system", Icon = "Setting", Type = "directory", Sort = 5, CreatedAt = now },
|
||||
new() { Name = "组织管理", Path = "/orgs", Component = "views/orgs/OrgList.vue", Icon = "OfficeBuilding", Type = "menu", Permission = "system:org:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "用户管理", Path = "/users", Component = "views/users/UserList.vue", Icon = "Avatar", Type = "menu", Permission = "system:user:list", Sort = 2, CreatedAt = now },
|
||||
new() { Name = "角色权限", Path = "/system/roles", Component = "views/system/RoleList.vue", Icon = "UserFilled", Type = "menu", Permission = "system:role:list", Sort = 3, CreatedAt = now },
|
||||
new() { Name = "菜单管理", Path = "/system/menus", Component = "views/system/MenuList.vue", Icon = "Menu", Type = "menu", Permission = "system:menu:list", Sort = 4, CreatedAt = now },
|
||||
new() { Name = "数据字典", Path = "/system/dicts", Component = "views/system/DictList.vue", Icon = "Notebook", Type = "menu", Permission = "system:dict:list", Sort = 5, CreatedAt = now },
|
||||
new() { Name = "通知公告", Path = "/notify", Icon = "Bell", Type = "directory", Sort = 6, CreatedAt = now },
|
||||
new() { Name = "公告管理", Path = "/notify/announcements", Component = "views/notify/AnnouncementList.vue", Icon = "Bell", Type = "menu", Permission = "announcement:list", Sort = 1, CreatedAt = now },
|
||||
new() { Name = "关于系统", Path = "/about", Component = "views/about/About.vue", Icon = "InfoFilled", Type = "menu", Permission = "about:view", Sort = 7, CreatedAt = now },
|
||||
};
|
||||
db.SysMenus.AddRange(menus);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
SetChild(menus, "/weighing", "/weighing");
|
||||
SetChild(menus, "/weighing/touch", "/weighing");
|
||||
SetChild(menus, "/payments", "/weighing");
|
||||
SetChild(menus, "/invoices", "/weighing");
|
||||
SetChild(menus, "/farmers", "/base");
|
||||
SetChild(menus, "/products", "/base");
|
||||
SetChild(menus, "/reports/purchase", "/reports");
|
||||
SetChild(menus, "/reports/payment", "/reports");
|
||||
SetChild(menus, "/reports/invoice", "/reports");
|
||||
SetChild(menus, "/orgs", "/system");
|
||||
SetChild(menus, "/users", "/system");
|
||||
SetChild(menus, "/system/roles", "/system");
|
||||
SetChild(menus, "/system/menus", "/system");
|
||||
SetChild(menus, "/system/dicts", "/system");
|
||||
SetChild(menus, "/notify/announcements", "/notify");
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
// 角色-菜单分配:超级管理员全部;公司管理员除系统管理外全部;收购站员工常用菜单
|
||||
var allMenuIds = menus.Select(m => m.Id).ToArray();
|
||||
var admin = roles.First(r => r.Code == "super_admin");
|
||||
var company = roles.First(r => r.Code == "company_admin");
|
||||
var station = roles.First(r => r.Code == "station_staff");
|
||||
db.SysRoleMenus.AddRange(allMenuIds.Select(mid => new SysRoleMenu { RoleId = admin.Id, MenuId = mid }));
|
||||
db.SysRoleMenus.AddRange(menus.Where(m => m.Type == "menu" && !m.Permission.StartsWith("system:"))
|
||||
.Select(m => new SysRoleMenu { RoleId = company.Id, MenuId = m.Id }));
|
||||
db.SysRoleMenus.AddRange(menus.Where(m => m.Type == "menu" && m.Permission is "home:view" or "weighing:list" or "weighing:touch" or "farmer:list" or "about:view")
|
||||
.Select(m => new SysRoleMenu { RoleId = station.Id, MenuId = m.Id }));
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// ---------- 菜单层级修复:过磅称重/触摸屏过磅 归入 收购业务 目录(幂等,兼容已有数据的库) ----------
|
||||
var weighingDir = await db.SysMenus.FirstOrDefaultAsync(m => m.Type == "directory" && m.Path == "/weighing");
|
||||
if (weighingDir != null)
|
||||
{
|
||||
var moveTargets = await db.SysMenus
|
||||
.Where(m => m.Type == "menu" && (m.Path == "/weighing" || m.Path == "/weighing/touch"))
|
||||
.ToListAsync();
|
||||
var menuChanged = false;
|
||||
foreach (var m in moveTargets)
|
||||
{
|
||||
if (m.ParentId != weighingDir.Id)
|
||||
{
|
||||
m.ParentId = weighingDir.Id;
|
||||
menuChanged = true;
|
||||
}
|
||||
}
|
||||
if (menuChanged) await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// ---------- 已有用户按用户名绑定角色 ----------
|
||||
var roleByUser = new Dictionary<string, string>
|
||||
{
|
||||
["admin"] = "super_admin",
|
||||
["company"] = "company_admin",
|
||||
["station1"] = "station_staff",
|
||||
["station2"] = "station_staff",
|
||||
["individual"] = "individual",
|
||||
};
|
||||
var users = await db.Users.Where(u => u.RoleId == null).ToListAsync();
|
||||
foreach (var u in users)
|
||||
{
|
||||
if (roleByUser.TryGetValue(u.Username, out var code))
|
||||
{
|
||||
var role = roles.FirstOrDefault(r => r.Code == code);
|
||||
if (role != null) u.RoleId = role.Id;
|
||||
}
|
||||
}
|
||||
if (users.Count > 0) await db.SaveChangesAsync();
|
||||
|
||||
// ---------- 数据字典 ----------
|
||||
if (!await db.SysDicts.AnyAsync())
|
||||
{
|
||||
var dicts = new List<SysDict>
|
||||
{
|
||||
new() { Name = "收购等级", Code = "purchase_grade", Remark = "收购单磅次等级", IsSystem = true, Sort = 1, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "一等", Value = "一等", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "二等", Value = "二等", Sort = 2 },
|
||||
new() { Label = "三等", Value = "三等", Sort = 3 },
|
||||
new() { Label = "级外", Value = "级外", Sort = 4 },
|
||||
} },
|
||||
new() { Name = "价格类型", Code = "price_type", Remark = "过磅计价方式", IsSystem = true, Sort = 2, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "固定价格", Value = "Fixed", Ext = "按产品默认价", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "现场议价", Value = "Manual", Ext = "与农户协商价", Sort = 2 },
|
||||
new() { Label = "价格区间", Value = "Range", Ext = "区间内浮动价", Sort = 3 },
|
||||
} },
|
||||
new() { Name = "票据格式", Code = "receipt_format", Remark = "小票/票据打印格式", IsSystem = true, Sort = 3, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "58mm 热敏小票", Value = "thermal_58", Ext = "58mm", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "80mm 热敏小票", Value = "thermal_80", Ext = "80mm", Sort = 2 },
|
||||
new() { Label = "三等分针式票据", Value = "dotmatrix_3part", Ext = "241mm×139.7mm/联", Sort = 3 },
|
||||
new() { Label = "A5 激光打印", Value = "laser_a5", Ext = "A5", Sort = 4 },
|
||||
} },
|
||||
new() { Name = "公告类型", Code = "announcement_type", Remark = "通知公告类型", IsSystem = true, Sort = 4, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "通知", Value = "notice", Sort = 1 },
|
||||
new() { Label = "公告", Value = "announce", Sort = 2 },
|
||||
new() { Label = "价格行情", Value = "price", Sort = 3 },
|
||||
new() { Label = "政策法规", Value = "policy", Sort = 4 },
|
||||
} },
|
||||
};
|
||||
db.SysDicts.AddRange(dicts);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// ---------- 系统图标字典(幂等补齐,兼容已有数据的库) ----------
|
||||
if (!await db.SysDicts.AnyAsync(d => d.Code == "system_logo"))
|
||||
{
|
||||
db.SysDicts.Add(new SysDict
|
||||
{
|
||||
Name = "系统图标", Code = "system_logo", Remark = "主标题系统图标,可选大部分农产品图标", IsSystem = true, Sort = 5, CreatedAt = now,
|
||||
Items =
|
||||
{
|
||||
new() { Label = "苹果", Value = "Apple", IsDefault = true, Sort = 1 },
|
||||
new() { Label = "葡萄", Value = "Grape", Sort = 2 },
|
||||
new() { Label = "西瓜", Value = "Watermelon", Sort = 3 },
|
||||
new() { Label = "樱桃", Value = "Cherry", Sort = 4 },
|
||||
new() { Label = "梨", Value = "Pear", Sort = 5 },
|
||||
new() { Label = "桃", Value = "Peach", Sort = 6 },
|
||||
new() { Label = "橙子", Value = "Orange", Sort = 7 },
|
||||
new() { Label = "农作物", Value = "Food", Sort = 8 },
|
||||
new() { Label = "时蔬", Value = "Dish", Sort = 9 },
|
||||
new() { Label = "牛奶", Value = "Milk", Sort = 10 },
|
||||
new() { Label = "冰淇淋", Value = "IceCream", Sort = 11 },
|
||||
new() { Label = "生鲜礼盒", Value = "TakeawayBox", Sort = 12 },
|
||||
}
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// ---------- 通知公告(演示数据) ----------
|
||||
if (!await db.Announcements.AnyAsync())
|
||||
{
|
||||
var publisher = await db.Users.FirstOrDefaultAsync(u => u.Username == "admin");
|
||||
var adminRole = roles.FirstOrDefault(r => r.Code == "super_admin");
|
||||
db.Announcements.AddRange(
|
||||
new Announcement { Title = "欢迎使用农易富农产品收购交易平台", Content = "系统已完成升级,新增数据字典、角色权限、通知公告等功能。默认账号 admin / 123456。", Type = "announce", IsPinned = true, PublisherId = publisher?.Id ?? (adminRole != null ? db.Users.FirstOrDefault()?.Id : null), PublishedAt = now, CreatedAt = now },
|
||||
new Announcement { Title = "关于2026年夏季收购价格调整的通知", Content = "自8月15日起,一等品收购价调整为每公斤3.6元,请各收购站及时更新价格配置。", Type = "price", PublisherId = publisher?.Id, PublishedAt = now.AddHours(-3), CreatedAt = now.AddHours(-3) },
|
||||
new Announcement { Title = "电子秤串口连接使用说明", Content = "请在收购单新建界面点击电子秤区域连接串口,支持标准ASCII输出协议(如 ST,GS,+00123.45kg)。无串口设备时可使用手动输入模式。", Type = "notice", PublisherId = publisher?.Id, PublishedAt = now.AddDays(-1), CreatedAt = now.AddDays(-1) }
|
||||
);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetChild(List<SysMenu> menus, string childPath, string parentPath)
|
||||
{
|
||||
var parent = menus.FirstOrDefault(m => m.Type == "directory" && m.Path == parentPath);
|
||||
if (parent is null) return;
|
||||
foreach (var child in menus.Where(m => m.Type != "directory" && m.Path == childPath && m.ParentId == null))
|
||||
child.ParentId = parent.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user