first commit
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.Data;
|
||||
|
||||
/// <summary>业务基础数据种子(部门/工艺庄口/生丝规格/生丝定级指标/机组车台)</summary>
|
||||
public static class BusinessSeedData
|
||||
{
|
||||
public static void Seed(IFreeSql fsql)
|
||||
{
|
||||
SeedMenus(fsql);
|
||||
SeedCraftZhuangKou(fsql);
|
||||
SeedShengsiGuige(fsql);
|
||||
SeedShengsiDingjizhibiao(fsql);
|
||||
SeedDepartmentsAndJizuChetai(fsql);
|
||||
SeedEmployees(fsql);
|
||||
}
|
||||
|
||||
// ==================== 部门 + 机组车台(真实组织架构) ====================
|
||||
private static readonly (long Id, string Name, long ParentId, int Sort, string Code)[] DeptData =
|
||||
{
|
||||
(10920411881541632, "安泰茧丝绸", 0, 0, "123"),
|
||||
(10929439026188288, "丝业生产中心", 10920411881541632, 100, "100"),
|
||||
(19667471837433856, "主干运维", 10920411881541632, 9999, "9999"),
|
||||
(22305637455106048, "人力资源部", 10920411881541632, 800, "800"),
|
||||
(22305758536273920, "财务部", 10920411881541632, 400, "400"),
|
||||
(22305841730293760, "营销中心", 10920411881541632, 500, "500"),
|
||||
(22305998261719040, "计划运营部", 10920411881541632, 200, "200"),
|
||||
(22306589042020352, "品管部", 10920411881541632, 700, "700"),
|
||||
(22306677265010688, "采购部", 10920411881541632, 300, "300"),
|
||||
(26900271946797056, "安全环保后勤部", 10920411881541632, 1000, "1000"),
|
||||
(26901050904547328, "经理会", 10920411881541632, 1500, "1500"),
|
||||
(26901341515288576, "监察室", 10920411881541632, 3000, "3000"),
|
||||
(26901475166785536, "融资部", 10920411881541632, 2000, "2000"),
|
||||
(34638098675535872, "武胜蚕业公司", 10920411881541632, 4000, "4000"),
|
||||
(35981234102341632, "安泰丝绸人南店", 10920411881541632, 6200, "6200"),
|
||||
(35981427686248448, "绸业生产中心", 10920411881541632, 5000, "5000"),
|
||||
(35981496963567616, "夹江蚕业公司", 10920411881541632, 4100, "4100"),
|
||||
(35982738976673792, "安泰丝绸融恒店", 10920411881541632, 6000, "6000"),
|
||||
(35982891737419776, "帛锦汇", 10920411881541632, 6100, "6100"),
|
||||
(35982956409393152, "蚕业技术服务部", 10920411881541632, 4900, "4900"),
|
||||
(36277179469795328, "广安蚕业公司", 10920411881541632, 4200, "4200"),
|
||||
(10929739564847104, "仓储", 10929439026188288, 1000, "1000"),
|
||||
(22307875854487552, "质检科", 10929439026188288, 1100, "1100"),
|
||||
(22308241237086208, "试样室", 10929439026188288, 1200, "1200"),
|
||||
(22308361408090112, "选剥茧车间", 10929439026188288, 1300, "1300"),
|
||||
(25256354205667328, "前缫车间", 10929439026188288, 1350, "1350"),
|
||||
(22308805823959040, "后缫车间", 10929439026188288, 1800, "1800"),
|
||||
(22309110082965504, "设备车间", 10929439026188288, 2100, "2100"),
|
||||
(22309340027293696, "副产品车间", 10929439026188288, 2200, "2200"),
|
||||
(68179345423732736, "临时", 10929439026188288, 3000, "3000"),
|
||||
(16043931070369792, "前缫一车间", 25256354205667328, 1400, "1400"),
|
||||
(16047387197837312, "前缫二车间", 25256354205667328, 1500, "1500"),
|
||||
(22308600831545344, "前缫三车间", 25256354205667328, 1600, "1600"),
|
||||
(22308683820044288, "前缫四车间", 25256354205667328, 1700, "1700"),
|
||||
(25379428280307712, "煮茧室", 25256354205667328, 1350, "1350"),
|
||||
(22308918461992960, "检验室", 22308805823959040, 1900, "1900"),
|
||||
(22309003073687552, "整理室", 22308805823959040, 2000, "2000"),
|
||||
(24029387057795072, "丝棉车间", 22309340027293696, 0, "999"),
|
||||
};
|
||||
|
||||
private static readonly (long DeptId, int Jizu, int Start, int End)[] JizuData =
|
||||
{
|
||||
(16043931070369792, 1, 1, 20), (16043931070369792, 2, 21, 40), (16043931070369792, 3, 41, 60),
|
||||
(16043931070369792, 4, 61, 80), (16043931070369792, 5, 81, 100), (16043931070369792, 6, 101, 120),
|
||||
(16047387197837312, 1, 1, 20), (16047387197837312, 2, 21, 40), (16047387197837312, 3, 41, 60),
|
||||
(16047387197837312, 4, 61, 80), (16047387197837312, 5, 81, 100), (16047387197837312, 6, 101, 120),
|
||||
(22308600831545344, 1, 1, 20), (22308600831545344, 2, 21, 40), (22308600831545344, 3, 41, 60),
|
||||
(22308600831545344, 4, 61, 80), (22308600831545344, 5, 81, 100), (22308600831545344, 6, 101, 120),
|
||||
(22308683820044288, 1, 1, 20), (22308683820044288, 2, 21, 40), (22308683820044288, 3, 41, 60),
|
||||
(22308683820044288, 4, 61, 80), (22308683820044288, 5, 81, 100), (22308683820044288, 6, 101, 120),
|
||||
};
|
||||
|
||||
private static void SeedDepartmentsAndJizuChetai(IFreeSql fsql)
|
||||
{
|
||||
// 检测是否已有真实部门(以"安泰茧丝绸"为标记)
|
||||
if (fsql.Select<Department>().Any(d => d.Name == "安泰茧丝绸")) return;
|
||||
|
||||
// 物理清空假数据(总公司/技术部等)及关联
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM JizuChetais");
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM AttendanceRecords");
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM LeaveRequests");
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM Employees");
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM Departments");
|
||||
|
||||
// 第一遍:创建所有部门(ParentId 暂不设)
|
||||
var idMap = new Dictionary<long, long>();
|
||||
foreach (var d in DeptData)
|
||||
{
|
||||
var dept = new Department { Name = d.Name, Code = d.Code, Sort = d.Sort };
|
||||
var newId = DbSeeder.InsertAndGetId(fsql, dept);
|
||||
idMap[d.Id] = newId;
|
||||
}
|
||||
|
||||
// 第二遍:设置 ParentId
|
||||
foreach (var d in DeptData)
|
||||
{
|
||||
if (d.ParentId != 0 && idMap.ContainsKey(d.ParentId))
|
||||
{
|
||||
var dept = fsql.Select<Department>().Where(x => x.Id == idMap[d.Id]).First();
|
||||
dept.ParentId = idMap[d.ParentId];
|
||||
fsql.Update<Department>().SetSource(dept).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
// 机组车台
|
||||
foreach (var j in JizuData)
|
||||
{
|
||||
if (idMap.ContainsKey(j.DeptId))
|
||||
{
|
||||
fsql.Insert(new JizuChetai
|
||||
{
|
||||
DepartmentId = idMap[j.DeptId],
|
||||
Jizu = j.Jizu,
|
||||
ChetaiStart = j.Start,
|
||||
ChetaiEnd = j.End
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 职工 ====================
|
||||
private static readonly (long DeptId, string Name, string Phone, string Login)[] EmpData =
|
||||
{
|
||||
(16043931070369792, "主干-何启旭", "18840854628", "18840854628"),
|
||||
(10920411881541632, "测试用户", "0", "test"),
|
||||
(10920411881541632, "秦燊", "13982683923", "13982683923"),
|
||||
(10920411881541632, "陈勇", "18908283908", "18908283908"),
|
||||
(22305758536273920, "汪菊", "18782619006", "18782619006"),
|
||||
(22305841730293760, "陈林", "18580851234", "18580851234"),
|
||||
(10929439026188288, "李正平", "15378405846", "15378405846"),
|
||||
(22305637455106048, "朱莹", "18908283738", "18908283738"),
|
||||
(22305637455106048, "毛银春", "18180268728", "18180268728"),
|
||||
(22305637455106048, "陆国君", "13696173949", "13696173949"),
|
||||
(22305637455106048, "华静", "13882636653", "13882636653"),
|
||||
(22305637455106048, "余思洁", "18398508220", "18398508220"),
|
||||
(22305758536273920, "孙瑞", "15982664679", "15982664679"),
|
||||
(22305758536273920, "唐琳", "18048103256", "18048103256"),
|
||||
(22305758536273920, "蒋宜", "18982626800", "18982626800"),
|
||||
(22305758536273920, "王清华", "15282625323", "15282625323"),
|
||||
(22305998261719040, "李西宴", "13688261711", "13688261711"),
|
||||
(22305998261719040, "文杨", "13699654826", "13699654826"),
|
||||
(22306589042020352, "曹庆", "13541993164", "13541993164"),
|
||||
(22306677265010688, "蒙海波", "15884923167", "15884923167"),
|
||||
(22305841730293760, "陈菊", "13696182387", "13696182387"),
|
||||
(22305841730293760, "刘婷", "18382610528", "18382610528"),
|
||||
(19667471837433856, "主干-张建", "15398380348", "15398380348"),
|
||||
(19667471837433856, "主干-严鹏", "19162687174", "19162687174"),
|
||||
(10929739564847104, "游建梅", "13629091099", "13629091099"),
|
||||
(22307875854487552, "邓滟旖", "18181821168", "18181821168"),
|
||||
(10929439026188288, "李志颖", "14708217874", "14708217874"),
|
||||
(22308241237086208, "曹志君", "13882695209", "13882695209"),
|
||||
(22308241237086208, "胡小琴", "18160129785", "18160129785"),
|
||||
(22308361408090112, "唐小红", "15983458330", "15983458330"),
|
||||
(25256354205667328, "余华", "13547542219", "13547542219"),
|
||||
(22308683820044288, "陈建红", "15182698365", "15182698365"),
|
||||
(22308683820044288, "谢军辉", "19957339779", "19957339779"),
|
||||
(22308600831545344, "陈丽华", "15982605862", "15982605862"),
|
||||
(16047387197837312, "杨静", "15982696700", "15982696700"),
|
||||
(25256354205667328, "刘芳", "13882653025", "13882653025"),
|
||||
(22308805823959040, "刘春兰", "13982624682", "13982624682"),
|
||||
(22309110082965504, "朱再民", "15328288332", "15328288332"),
|
||||
(22309340027293696, "舒文", "13551613659", "13551613659"),
|
||||
(10929439026188288, "汪瑜琳", "13541553564", "13541553564"),
|
||||
(19667471837433856, "范鸿才", "18981977117", "18981977117"),
|
||||
(22305758536273920, "谭波", "18282636380", "18282636380"),
|
||||
(22308918461992960, "董宗华", "18384503138", "18384503138"),
|
||||
(22308918461992960, "易林梅", "18381112479", "18381112479"),
|
||||
(22309003073687552, "舒红群", "18398526317", "18398526317"),
|
||||
(25379428280307712, "彭卫东", "18382668003", "18382668003"),
|
||||
(25379428280307712, "王远辉", "15982666491", "15982666491"),
|
||||
(25379428280307712, "姚光林", "19160637815", "19160637815"),
|
||||
(19667471837433856, "李学兴", "18117362915", "18117362915"),
|
||||
(16043931070369792, "李远玲", "13982638574", "13982638574"),
|
||||
(26900271946797056, "杨福锋", "13982638165", "13982638165"),
|
||||
(26901341515288576, "杨利容", "15928659870", "15928659870"),
|
||||
(19667471837433856, "主干-张罗", "18109064045", "18109064045"),
|
||||
(26900271946797056, "袁鑫", "14781753888", "14781753888"),
|
||||
(19667471837433856, "刘香君", "15808107846", "15808107846"),
|
||||
(22308805823959040, "沈跃成", "13822244141", "13822244141"),
|
||||
(19667471837433856, "杨凤琼", "18011528128", "18011528128"),
|
||||
(22308600831545344, "谭爱君", "13699658634", "13699658634"),
|
||||
};
|
||||
|
||||
private static void SeedEmployees(IFreeSql fsql)
|
||||
{
|
||||
var defaultPwd = BCrypt.Net.BCrypt.HashPassword("123456");
|
||||
var deptNameMap = DeptData.ToDictionary(d => d.Id, d => d.Name);
|
||||
|
||||
foreach (var e in EmpData)
|
||||
{
|
||||
if (fsql.Select<Employee>().Any(x => x.Username == e.Login)) continue;
|
||||
|
||||
long deptId = 0;
|
||||
if (deptNameMap.TryGetValue(e.DeptId, out var deptName))
|
||||
{
|
||||
var dept = fsql.Select<Department>().Where(d => d.Name == deptName).First();
|
||||
if (dept != null) deptId = dept.Id;
|
||||
}
|
||||
|
||||
fsql.Insert(new Employee
|
||||
{
|
||||
Username = e.Login,
|
||||
Password = defaultPwd,
|
||||
RealName = e.Name,
|
||||
Nickname = e.Name,
|
||||
Phone = e.Phone,
|
||||
DepartmentId = deptId,
|
||||
Status = 1
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 菜单 ====================
|
||||
private static void SeedMenus(IFreeSql fsql)
|
||||
{
|
||||
var baseDirId = fsql.Select<Menu>().Where(m => m.Path == "/base").First()?.Id ?? 0;
|
||||
if (baseDirId == 0)
|
||||
{
|
||||
baseDirId = DbSeeder.InsertAndGetId(fsql, new Menu { Name = "基础数据", Type = "directory", Path = "/base", Icon = "Files", Sort = 3 });
|
||||
}
|
||||
|
||||
AddMenu(fsql, baseDirId, "工艺庄口", "/base/zhuangkou", "base/zhuangkou/index", "Collection", "base:zhuangkou:list", 1);
|
||||
AddMenu(fsql, baseDirId, "生丝规格", "/base/guige", "base/guige/index", "Grid", "base:guige:list", 2);
|
||||
AddMenu(fsql, baseDirId, "生丝定级指标", "/base/dingjizhibiao", "base/dingjizhibiao/index", "TrendCharts", "base:dingjizhibiao:list", 3);
|
||||
AddMenu(fsql, baseDirId, "机组车台", "/base/jizuchetai", "base/jizuchetai/index", "SetUp", "base:jizuchetai:list", 4);
|
||||
|
||||
// 生产管理目录
|
||||
var prodDirId = fsql.Select<Menu>().Where(m => m.Path == "/prod").First()?.Id ?? 0;
|
||||
if (prodDirId == 0)
|
||||
{
|
||||
prodDirId = DbSeeder.InsertAndGetId(fsql, new Menu { Name = "生产管理", Type = "directory", Path = "/prod", Icon = "Cpu", Sort = 4 });
|
||||
}
|
||||
AddMenu(fsql, prodDirId, "落丝记录", "/prod/lsjl", "prod/lsjl/index", "Document", "prod:lsjl:list", 1);
|
||||
AddMenu(fsql, prodDirId, "纤度检验", "/prod/xdjy", "prod/xdjy/index", "EditPen", "prod:xdjy:list", 2);
|
||||
AddMenu(fsql, prodDirId, "黑板检验", "/prod/hbjy", "prod/hbjy/index", "Notebook", "prod:hbjy:list", 3);
|
||||
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (adminRole != null)
|
||||
{
|
||||
var baseMenus = fsql.Select<Menu>().Where(m => m.Path.StartsWith("/base") || m.Path.StartsWith("/prod")).ToList();
|
||||
foreach (var m in baseMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddMenu(IFreeSql fsql, long parentId, string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
if (!fsql.Select<Menu>().Any(m => m.Path == path))
|
||||
{
|
||||
fsql.Insert(new Menu { ParentId = parentId, Name = name, Type = "menu", Path = path, Component = component, Icon = icon, Permission = permission, Sort = sort }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SeedCraftZhuangKou(IFreeSql fsql)
|
||||
{
|
||||
if (fsql.Select<CraftZhuangKou>().Any()) return;
|
||||
fsql.Insert(new CraftZhuangKou[]
|
||||
{
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 1, JidiWaigou = "基地", ZhuangKouCode = "SZJ-2026-武工艺20", ZhuangKouName = "2026年基地春一批(青松皓月)", ProductionDisplayName = "2026年基地春一批(青松皓月)", Chandi = "基地", Sort = 20 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 1, JidiWaigou = "基地", ZhuangKouCode = "SZJ-2026-武工艺24", ZhuangKouName = "2026年基地春(雄)", ProductionDisplayName = "2026年基地春(雄)", Chandi = "大面积", Sort = 24 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-2026-武工艺-22", ZhuangKouName = "2026年会东春(7532)", ProductionDisplayName = "2026年会东春(7532)", Chandi = "会东", Sort = 22 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-2026-武工艺-23", ZhuangKouName = "2026年珙县九丝春(雄)", ProductionDisplayName = "2026年珙县九丝春(雄)", Chandi = "珙县", Sort = 23 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-2026-武工艺-7", ZhuangKouName = "2026会东二类茧(7532)宁兴", ProductionDisplayName = "2026会东二类茧(7532)宁兴", Chandi = "会东", Sort = 7 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 1, JidiWaigou = "基地", ZhuangKouCode = "SZJ-武工艺-2026-25", ZhuangKouName = "2026年广安春(青松皓月)", ProductionDisplayName = "2026年广安春(青松皓月)", Chandi = "基地", Sort = 25 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-2026-武工艺-26", ZhuangKouName = "2026年珙县罗渡春(雄)", ProductionDisplayName = "2026年珙县罗渡春(雄)", Chandi = "珙县", Sort = 26 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 1, JidiWaigou = "基地", ZhuangKouCode = "SZJ-武工艺-2026-9", ZhuangKouName = "2026年广安春(青松皓月)宁兴", ProductionDisplayName = "2026年广安春(青松皓月)宁兴", Chandi = "广安", Sort = 9 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 1, JidiWaigou = "基地", ZhuangKouCode = "SZJ-武工艺-2026-28", ZhuangKouName = "2026年基地春三批(青松皓月)", ProductionDisplayName = "2026年基地春三批(青松皓月)", Chandi = "基地", Sort = 28 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 1, JidiWaigou = "基地", ZhuangKouCode = "SZJ-武工艺-2026-11", ZhuangKouName = "2026年基地春三批(青松皓月)宁兴", ProductionDisplayName = "2026年基地春三批(青松皓月)宁兴", Chandi = "基地", Sort = 11 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-武工艺-2026-29", ZhuangKouName = "2026年珙县宝山等混六庄春一批(雄)", ProductionDisplayName = "2026年珙县宝山等混六庄春一批(雄)", Chandi = "珙县", Sort = 29 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-武工艺-2026-27", ZhuangKouName = "2026年珙县曹营等混四庄春一批(雄)", ProductionDisplayName = "2026年珙县曹营等混四庄春一批(雄)", Chandi = "珙县", Sort = 27 },
|
||||
new CraftZhuangKou { BelongingYear = 2026, BendiWaidi = 2, JidiWaigou = "外购", ZhuangKouCode = "SZJ-武工艺-2026-10", ZhuangKouName = "2026年珙县罗渡春(雄)宁兴", ProductionDisplayName = "2026年珙县罗渡春(雄)宁兴", Chandi = "珙县", Sort = 10 }
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
private static void SeedShengsiGuige(IFreeSql fsql)
|
||||
{
|
||||
if (fsql.Select<ShengsiGuige>().Any()) return;
|
||||
fsql.Insert(new ShengsiGuige[]
|
||||
{
|
||||
new ShengsiGuige { Guige = "20/22", CenterrangeLolimit = 19.5, CenterrangeUplimit = 21.5, CenterXiandu = 20.5, ValidLolimit = 15.0, ValidUplimit = 27.0, XiaoyeLolimit = 17.5, XiaoyeUplimit = 24.5, DayeLolimit = 16.5, DayeUplimit = 25.5, TeyeLolimit = 15.5, TeyeUplimit = 26.5, YexianNkLolimitLeft = 15.0, YexianNkLolimitRight = 17.5, YexianNkUplimitLeft = 24.0, YexianNkUplimitRight = 27.5, LaodongJingsaiYexianLolimit = 17.5, LaodongJingsaiYexianUplimit = 23.5, Sort = 0 },
|
||||
new ShengsiGuige { Guige = "27/29", CenterrangeLolimit = 26.5, CenterrangeUplimit = 28.5, CenterXiandu = 27.5, ValidLolimit = 23.0, ValidUplimit = 34.0, XiaoyeLolimit = 25.0, XiaoyeUplimit = 32.0, DayeLolimit = 24.0, DayeUplimit = 33.0, TeyeLolimit = 23.0, TeyeUplimit = 34.0, YexianNkLolimitLeft = 22.0, YexianNkLolimitRight = 24.5, YexianNkUplimitLeft = 31.0, YexianNkUplimitRight = 34.5, LaodongJingsaiYexianLolimit = 24.5, LaodongJingsaiYexianUplimit = 30.5, Sort = 100 },
|
||||
new ShengsiGuige { Guige = "40/44", CenterrangeLolimit = 40.5, CenterrangeUplimit = 42.5, CenterXiandu = 41.5, ValidLolimit = 23.0, ValidUplimit = 34.0, XiaoyeLolimit = 35.0, XiaoyeUplimit = 49.0, DayeLolimit = 33.0, DayeUplimit = 51.0, TeyeLolimit = 31.0, TeyeUplimit = 53.0, YexianNkLolimitLeft = 0.0, YexianNkLolimitRight = 0.0, YexianNkUplimitLeft = 0.0, YexianNkUplimitRight = 0.0, LaodongJingsaiYexianLolimit = 40.0, LaodongJingsaiYexianUplimit = 42.0, Sort = 200 }
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
private static void SeedShengsiDingjizhibiao(IFreeSql fsql)
|
||||
{
|
||||
if (fsql.Select<ShengsiDingjizhibiao>().Any()) return;
|
||||
|
||||
var list = new List<ShengsiDingjizhibiao>();
|
||||
void Add(int std, string guige, string grade, double score, double piancha, double dacha, double erdu, double sandu, double qingjie, double jiejing)
|
||||
{
|
||||
list.Add(new ShengsiDingjizhibiao { Standard = std, Guige = guige, Grade = grade, GradeScore = score, Piancha = piancha, Dacha = dacha, Erdu = erdu, Sandu = sandu, Qingjie = qingjie, Jiejing = jiejing });
|
||||
}
|
||||
|
||||
// ===== 厂标 (standard=0) =====
|
||||
Add(0, "20/22", "A", 7.0, 2.0, 6.0, 6.0, 2.0, 90.0, 86.0);
|
||||
Add(0, "20/22", "A50", 7.5, 1.85, 5.6, 6.0, 2.0, 91.5, 87.0);
|
||||
Add(0, "20/22", "2A", 8.0, 1.7, 5.2, 5.0, 1.0, 93.0, 88.0);
|
||||
Add(0, "20/22", "2A50", 8.5, 1.58, 4.8, 5.0, 0.0, 94.5, 89.0);
|
||||
Add(0, "20/22", "3A", 9.0, 1.45, 4.4, 4.0, 0.0, 95.0, 90.0);
|
||||
Add(0, "20/22", "3A50", 9.5, 1.33, 4.0, 4.0, 0.0, 96.0, 91.0);
|
||||
Add(0, "20/22", "4A", 10.0, 1.2, 3.6, 3.0, 0.0, 96.5, 92.0);
|
||||
Add(0, "20/22", "4A50", 10.5, 1.13, 3.3, 3.0, 0.0, 97.0, 93.0);
|
||||
Add(0, "20/22", "5A", 11.0, 1.05, 3.0, 2.0, 0.0, 97.5, 94.0);
|
||||
Add(0, "20/22", "5A50", 11.5, 0.98, 2.8, 2.0, 0.0, 97.8, 94.5);
|
||||
Add(0, "20/22", "6A", 12.0, 0.9, 2.6, 1.0, 0.0, 98.0, 95.0);
|
||||
Add(0, "27/29", "A", 7.0, 2.05, 6.1, 6.0, 0.0, 90.0, 86.0);
|
||||
Add(0, "27/29", "A50", 7.5, 1.9, 5.6, 6.0, 0.0, 91.5, 87.0);
|
||||
Add(0, "27/29", "2A", 8.0, 1.75, 5.1, 5.0, 0.0, 93.0, 88.0);
|
||||
Add(0, "27/29", "2A50", 8.5, 1.65, 4.7, 5.0, 0.0, 94.5, 89.0);
|
||||
Add(0, "27/29", "3A", 9.0, 1.55, 4.3, 4.0, 0.0, 95.0, 90.0);
|
||||
Add(0, "27/29", "3A50", 9.5, 1.45, 4.0, 4.0, 0.0, 96.0, 91.0);
|
||||
Add(0, "27/29", "4A", 10.0, 1.3, 3.7, 3.0, 0.0, 96.5, 92.0);
|
||||
Add(0, "27/29", "4A50", 10.5, 1.25, 3.5, 3.0, 0.0, 97.0, 93.0);
|
||||
Add(0, "27/29", "5A", 11.0, 1.15, 3.3, 2.0, 0.0, 97.5, 94.0);
|
||||
Add(0, "27/29", "5A50", 11.5, 1.1, 3.1, 2.0, 0.0, 97.8, 94.5);
|
||||
Add(0, "27/29", "6A", 12.0, 1.05, 3.0, 1.0, 0.0, 98.0, 95.0);
|
||||
Add(0, "40/44", "A", 7.0, 2.85, 8.45, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "A50", 7.5, 2.69, 7.88, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "2A", 8.0, 2.53, 7.3, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "2A50", 8.5, 2.36, 6.79, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "3A", 9.0, 2.18, 6.28, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "3A50", 9.5, 2.04, 5.89, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "4A", 10.0, 1.9, 5.5, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "4A50", 10.5, 1.8, 5.2, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "5A", 11.0, 1.7, 4.9, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "5A50", 11.5, 1.6, 4.6, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "6A", 12.0, 1.5, 4.3, 0.0, 0.0, 0.0, 0.0);
|
||||
Add(0, "20/22", "等外", 6.0, 9.99, 99.99, 100.0, 100.0, 0.0, 0.0);
|
||||
Add(0, "27/29", "等外", 6.0, 9.99, 99.99, 100.0, 100.0, 0.0, 0.0);
|
||||
Add(0, "40/44", "等外", 6.0, 9.99, 99.99, 100.0, 100.0, 0.0, 0.0);
|
||||
|
||||
// ===== 个人 (standard=2) =====
|
||||
Add(2, "20/22", "A00", 7.0, 2.0, 6.0, 6.0, 2.0, 90.0, 86.0);
|
||||
Add(2, "20/22", "A10", 7.1, 1.97, 5.92, 6.0, 2.0, 90.3, 86.2);
|
||||
Add(2, "20/22", "A20", 7.2, 1.94, 5.84, 6.0, 2.0, 90.6, 86.4);
|
||||
Add(2, "20/22", "A30", 7.3, 1.91, 5.76, 6.0, 2.0, 90.9, 86.6);
|
||||
Add(2, "20/22", "A40", 7.4, 1.88, 5.68, 6.0, 2.0, 91.2, 86.8);
|
||||
Add(2, "20/22", "A50", 7.5, 1.85, 5.6, 6.0, 2.0, 91.5, 87.0);
|
||||
Add(2, "20/22", "A60", 7.6, 1.82, 5.52, 5.8, 1.8, 91.8, 87.2);
|
||||
Add(2, "20/22", "A70", 7.7, 1.79, 5.44, 5.6, 1.6, 92.1, 87.4);
|
||||
Add(2, "20/22", "A80", 7.8, 1.76, 5.36, 5.4, 1.4, 92.4, 87.6);
|
||||
Add(2, "20/22", "A90", 7.9, 1.73, 5.28, 5.2, 1.2, 92.7, 87.8);
|
||||
Add(2, "20/22", "2A00", 8.0, 1.7, 5.2, 5.0, 1.0, 93.0, 88.0);
|
||||
Add(2, "20/22", "2A10", 8.1, 1.68, 5.12, 5.0, 0.8, 93.3, 88.2);
|
||||
Add(2, "20/22", "2A20", 8.2, 1.65, 5.04, 5.0, 0.6, 93.6, 88.4);
|
||||
Add(2, "20/22", "2A30", 8.3, 1.63, 4.96, 5.0, 0.4, 93.9, 88.6);
|
||||
Add(2, "20/22", "2A40", 8.4, 1.6, 4.88, 5.0, 0.2, 94.2, 88.8);
|
||||
Add(2, "20/22", "2A50", 8.5, 1.58, 4.8, 5.0, 0.0, 94.5, 89.0);
|
||||
Add(2, "20/22", "2A60", 8.6, 1.55, 4.72, 4.8, 0.0, 94.6, 89.2);
|
||||
Add(2, "20/22", "2A70", 8.7, 1.53, 4.64, 4.6, 0.0, 94.7, 89.4);
|
||||
Add(2, "20/22", "2A80", 8.8, 1.5, 4.56, 4.4, 0.0, 94.8, 89.6);
|
||||
Add(2, "20/22", "2A90", 8.9, 1.48, 4.48, 4.2, 0.0, 94.9, 89.8);
|
||||
Add(2, "20/22", "3A00", 9.0, 1.45, 4.4, 4.0, 0.0, 95.0, 90.0);
|
||||
Add(2, "20/22", "3A10", 9.1, 1.43, 4.32, 4.0, 0.0, 95.2, 90.2);
|
||||
Add(2, "20/22", "3A20", 9.2, 1.4, 4.24, 4.0, 0.0, 95.4, 90.4);
|
||||
Add(2, "20/22", "3A30", 9.3, 1.38, 4.16, 4.0, 0.0, 95.6, 90.6);
|
||||
Add(2, "20/22", "3A40", 9.4, 1.35, 4.08, 4.0, 0.0, 95.8, 90.8);
|
||||
Add(2, "20/22", "3A50", 9.5, 1.33, 4.0, 4.0, 0.0, 96.0, 91.0);
|
||||
Add(2, "20/22", "3A60", 9.6, 1.3, 3.92, 3.8, 0.0, 96.1, 91.2);
|
||||
Add(2, "20/22", "3A70", 9.7, 1.28, 3.84, 3.6, 0.0, 96.2, 91.4);
|
||||
Add(2, "20/22", "3A80", 9.8, 1.25, 3.76, 3.4, 0.0, 96.3, 91.6);
|
||||
Add(2, "20/22", "3A90", 9.9, 1.23, 3.68, 3.2, 0.0, 96.4, 91.8);
|
||||
Add(2, "20/22", "4A00", 10.0, 1.2, 3.6, 3.0, 0.0, 96.5, 92.0);
|
||||
Add(2, "20/22", "4A10", 10.1, 1.19, 3.54, 3.0, 0.0, 96.6, 92.2);
|
||||
Add(2, "20/22", "4A20", 10.2, 1.17, 3.48, 3.0, 0.0, 96.7, 92.4);
|
||||
Add(2, "20/22", "4A30", 10.3, 1.16, 3.42, 3.0, 0.0, 96.8, 92.6);
|
||||
Add(2, "20/22", "4A40", 10.4, 1.14, 3.36, 3.0, 0.0, 96.9, 92.8);
|
||||
Add(2, "20/22", "4A50", 10.5, 1.13, 3.3, 3.0, 0.0, 97.0, 93.0);
|
||||
Add(2, "20/22", "4A60", 10.6, 1.11, 3.24, 2.8, 0.0, 97.1, 93.2);
|
||||
Add(2, "20/22", "4A70", 10.7, 1.1, 3.18, 2.6, 0.0, 97.2, 93.4);
|
||||
Add(2, "20/22", "4A80", 10.8, 1.08, 3.12, 2.4, 0.0, 97.3, 93.6);
|
||||
Add(2, "20/22", "4A90", 10.9, 1.07, 3.06, 2.2, 0.0, 97.4, 93.8);
|
||||
Add(2, "20/22", "5A00", 11.0, 1.05, 3.0, 2.0, 0.0, 97.5, 94.0);
|
||||
Add(2, "20/22", "5A10", 11.1, 1.04, 2.96, 2.0, 0.0, 97.56, 94.1);
|
||||
Add(2, "20/22", "5A20", 11.2, 1.03, 2.92, 2.0, 0.0, 97.62, 94.2);
|
||||
Add(2, "20/22", "5A30", 11.3, 1.01, 2.88, 2.0, 0.0, 97.68, 94.3);
|
||||
Add(2, "20/22", "5A40", 11.4, 0.99, 2.84, 2.0, 0.0, 97.74, 94.4);
|
||||
Add(2, "20/22", "5A50", 11.5, 0.98, 2.8, 2.0, 0.0, 97.8, 94.5);
|
||||
Add(2, "20/22", "5A60", 11.6, 0.96, 2.76, 1.8, 0.0, 97.84, 94.6);
|
||||
Add(2, "20/22", "5A70", 11.7, 0.95, 2.72, 1.6, 0.0, 97.88, 94.7);
|
||||
Add(2, "20/22", "5A80", 11.8, 0.93, 2.68, 1.4, 0.0, 97.92, 94.8);
|
||||
Add(2, "20/22", "5A90", 11.9, 0.92, 2.64, 1.2, 0.0, 97.96, 94.9);
|
||||
Add(2, "20/22", "6A00", 12.0, 0.9, 2.6, 1.0, 0.0, 98.0, 95.0);
|
||||
|
||||
Add(2, "27/29", "A00", 7.0, 2.05, 6.0, 12.0, 0.0, 90.0, 86.0);
|
||||
Add(2, "27/29", "A10", 7.1, 2.02, 5.9, 11.6, 0.0, 90.3, 86.2);
|
||||
Add(2, "27/29", "A20", 7.2, 1.99, 5.8, 11.2, 0.0, 90.6, 86.4);
|
||||
Add(2, "27/29", "A30", 7.3, 1.96, 5.7, 10.8, 0.0, 90.9, 86.6);
|
||||
Add(2, "27/29", "A40", 7.4, 1.93, 5.6, 10.4, 0.0, 91.2, 86.8);
|
||||
Add(2, "27/29", "A50", 7.5, 1.9, 5.5, 10.0, 0.0, 91.5, 87.0);
|
||||
Add(2, "27/29", "A60", 7.6, 1.87, 5.4, 9.6, 0.0, 91.8, 87.2);
|
||||
Add(2, "27/29", "A70", 7.7, 1.84, 5.3, 9.2, 0.0, 92.1, 87.4);
|
||||
Add(2, "27/29", "A80", 7.8, 1.81, 5.2, 8.8, 0.0, 92.4, 87.6);
|
||||
Add(2, "27/29", "A90", 7.9, 1.78, 5.1, 8.4, 0.0, 92.7, 87.8);
|
||||
Add(2, "27/29", "2A00", 8.0, 1.75, 5.0, 8.0, 0.0, 93.0, 88.0);
|
||||
Add(2, "27/29", "2A10", 8.1, 1.73, 4.92, 7.7, 0.0, 93.2, 88.2);
|
||||
Add(2, "27/29", "2A20", 8.2, 1.69, 4.76, 7.1, 0.0, 93.6, 88.6);
|
||||
Add(2, "27/29", "2A30", 8.3, 1.67, 4.68, 6.8, 0.0, 93.8, 88.8);
|
||||
Add(2, "27/29", "2A40", 8.4, 1.65, 4.6, 6.5, 0.0, 94.0, 89.0);
|
||||
Add(2, "27/29", "2A50", 8.5, 1.63, 4.52, 6.2, 0.0, 94.2, 89.2);
|
||||
Add(2, "27/29", "2A60", 8.6, 1.61, 4.44, 5.9, 0.0, 94.4, 89.4);
|
||||
Add(2, "27/29", "2A70", 8.7, 1.59, 4.36, 5.6, 0.0, 94.6, 89.6);
|
||||
Add(2, "27/29", "2A80", 8.8, 1.57, 4.28, 5.3, 0.0, 94.8, 89.8);
|
||||
Add(2, "27/29", "2A90", 8.9, 1.55, 4.2, 5.0, 0.0, 95.0, 90.0);
|
||||
Add(2, "27/29", "3A00", 9.0, 1.53, 4.14, 4.8, 0.0, 95.15, 90.2);
|
||||
Add(2, "27/29", "3A10", 9.1, 1.51, 4.08, 4.6, 0.0, 95.3, 90.4);
|
||||
Add(2, "27/29", "3A20", 9.2, 1.49, 4.02, 4.4, 0.0, 95.45, 90.6);
|
||||
Add(2, "27/29", "3A30", 9.3, 1.47, 3.96, 4.2, 0.0, 95.6, 90.8);
|
||||
Add(2, "27/29", "3A40", 9.4, 1.45, 3.9, 4.0, 0.0, 95.75, 91.0);
|
||||
Add(2, "27/29", "3A50", 9.5, 1.43, 3.84, 3.8, 0.0, 95.9, 91.2);
|
||||
Add(2, "27/29", "3A60", 9.6, 1.41, 3.78, 3.6, 0.0, 96.05, 91.4);
|
||||
Add(2, "27/29", "3A70", 9.7, 1.39, 3.72, 3.4, 0.0, 96.2, 91.6);
|
||||
Add(2, "27/29", "3A80", 9.8, 1.37, 3.66, 3.2, 0.0, 96.35, 91.8);
|
||||
Add(2, "27/29", "3A90", 9.9, 1.35, 3.6, 3.0, 0.0, 96.5, 92.0);
|
||||
Add(2, "27/29", "4A00", 10.0, 1.33, 3.66, 2.85, 0.0, 96.6, 92.2);
|
||||
Add(2, "27/29", "4A10", 10.1, 1.31, 3.62, 2.7, 0.0, 96.7, 92.4);
|
||||
Add(2, "27/29", "4A20", 10.2, 1.29, 3.58, 2.55, 0.0, 96.8, 92.6);
|
||||
Add(2, "27/29", "4A30", 10.3, 1.27, 3.54, 2.4, 0.0, 96.9, 92.8);
|
||||
Add(2, "27/29", "4A40", 10.4, 1.25, 3.5, 2.25, 0.0, 97.0, 93.0);
|
||||
Add(2, "27/29", "4A50", 10.5, 1.23, 3.46, 2.1, 0.0, 97.1, 93.2);
|
||||
Add(2, "27/29", "4A60", 10.6, 1.21, 3.42, 1.95, 0.0, 97.2, 93.4);
|
||||
Add(2, "27/29", "4A70", 10.7, 1.19, 3.38, 1.8, 0.0, 97.3, 93.6);
|
||||
Add(2, "27/29", "4A80", 10.8, 1.17, 3.34, 1.65, 0.0, 97.4, 93.8);
|
||||
Add(2, "27/29", "4A90", 10.9, 1.15, 3.3, 1.5, 0.0, 97.5, 94.0);
|
||||
Add(2, "27/29", "5A00", 11.0, 1.14, 3.27, 1.45, 0.0, 97.55, 94.1);
|
||||
Add(2, "27/29", "5A10", 11.1, 1.13, 3.24, 1.4, 0.0, 97.6, 94.2);
|
||||
Add(2, "27/29", "5A20", 11.2, 1.12, 3.21, 1.35, 0.0, 97.65, 94.3);
|
||||
Add(2, "27/29", "5A30", 11.3, 1.11, 3.18, 1.3, 0.0, 97.7, 94.4);
|
||||
Add(2, "27/29", "5A40", 11.4, 1.1, 3.15, 1.25, 0.0, 97.75, 94.5);
|
||||
Add(2, "27/29", "5A50", 11.5, 1.09, 3.12, 1.2, 0.0, 97.8, 94.6);
|
||||
Add(2, "27/29", "5A60", 11.6, 1.08, 3.09, 1.15, 0.0, 97.85, 94.7);
|
||||
Add(2, "27/29", "5A70", 11.7, 1.07, 3.06, 1.1, 0.0, 97.9, 94.8);
|
||||
Add(2, "27/29", "5A80", 11.8, 1.06, 3.03, 1.05, 0.0, 97.95, 94.9);
|
||||
Add(2, "27/29", "5A90", 11.9, 1.05, 3.0, 1.0, 0.0, 98.0, 95.0);
|
||||
Add(2, "27/29", "6A00", 12.0, 1.05, 3.0, 1.0, 0.0, 98.0, 95.0);
|
||||
|
||||
Add(2, "40/44", "A00", 7.0, 2.6, 7.55, 8.0, 0.0, 90.0, 86.0);
|
||||
Add(2, "40/44", "A10", 7.1, 2.57, 7.43, 7.7, 0.0, 90.3, 86.2);
|
||||
Add(2, "40/44", "A20", 7.2, 2.53, 7.31, 7.4, 0.0, 90.6, 86.4);
|
||||
Add(2, "40/44", "A30", 7.3, 2.5, 7.19, 7.1, 0.0, 90.9, 86.6);
|
||||
Add(2, "40/44", "A40", 7.4, 2.46, 7.07, 6.8, 0.0, 91.2, 86.8);
|
||||
Add(2, "40/44", "A50", 7.5, 2.43, 6.95, 6.5, 0.0, 91.5, 87.0);
|
||||
Add(2, "40/44", "A60", 7.6, 2.39, 6.83, 6.2, 0.0, 91.8, 87.2);
|
||||
Add(2, "40/44", "A70", 7.7, 2.36, 6.71, 5.9, 0.0, 92.1, 87.4);
|
||||
Add(2, "40/44", "A80", 7.8, 2.32, 6.59, 5.6, 0.0, 92.4, 87.6);
|
||||
Add(2, "40/44", "A90", 7.9, 2.29, 6.47, 5.3, 0.0, 92.7, 87.8);
|
||||
Add(2, "40/44", "2A00", 8.0, 2.25, 6.35, 5.0, 0.0, 93.0, 88.0);
|
||||
Add(2, "40/44", "2A10", 8.1, 2.22, 6.24, 4.8, 0.0, 93.2, 88.2);
|
||||
Add(2, "40/44", "2A20", 8.2, 2.18, 6.13, 4.6, 0.0, 93.4, 88.4);
|
||||
Add(2, "40/44", "2A30", 8.3, 2.15, 6.02, 4.4, 0.0, 93.6, 88.6);
|
||||
Add(2, "40/44", "2A40", 8.4, 2.11, 5.91, 4.2, 0.0, 93.8, 88.8);
|
||||
Add(2, "40/44", "2A50", 8.5, 2.08, 5.8, 4.0, 0.0, 94.0, 89.0);
|
||||
Add(2, "40/44", "2A60", 8.6, 2.04, 5.69, 3.8, 0.0, 94.2, 89.2);
|
||||
Add(2, "40/44", "2A70", 8.7, 2.01, 5.58, 3.6, 0.0, 94.4, 89.4);
|
||||
Add(2, "40/44", "2A80", 8.8, 1.97, 5.47, 3.4, 0.0, 94.6, 89.6);
|
||||
Add(2, "40/44", "2A90", 8.9, 1.94, 5.36, 3.2, 0.0, 94.8, 89.8);
|
||||
Add(2, "40/44", "3A00", 9.0, 1.9, 5.25, 3.0, 0.0, 95.0, 90.0);
|
||||
Add(2, "40/44", "3A10", 9.1, 1.87, 5.16, 1.95, 0.0, 95.15, 90.2);
|
||||
Add(2, "40/44", "3A20", 9.2, 1.83, 5.06, 1.9, 0.0, 95.3, 90.4);
|
||||
Add(2, "40/44", "3A30", 9.3, 1.8, 4.97, 1.85, 0.0, 95.45, 90.6);
|
||||
Add(2, "40/44", "3A40", 9.4, 1.76, 4.87, 1.8, 0.0, 95.6, 90.8);
|
||||
Add(2, "40/44", "3A50", 9.5, 1.73, 4.78, 1.75, 0.0, 95.75, 91.0);
|
||||
Add(2, "40/44", "3A60", 9.6, 1.69, 4.68, 1.7, 0.0, 95.9, 91.2);
|
||||
Add(2, "40/44", "3A70", 9.7, 1.66, 4.59, 1.65, 0.0, 96.05, 91.4);
|
||||
Add(2, "40/44", "3A80", 9.8, 1.62, 4.49, 1.6, 0.0, 96.2, 91.6);
|
||||
Add(2, "40/44", "3A90", 9.9, 1.59, 4.4, 1.55, 0.0, 96.35, 91.8);
|
||||
Add(2, "40/44", "4A00", 10.0, 1.55, 4.3, 1.5, 0.0, 96.5, 92.0);
|
||||
Add(2, "40/44", "4A10", 10.1, 1.53, 4.24, 1.45, 0.0, 96.6, 92.2);
|
||||
Add(2, "40/44", "4A20", 10.2, 1.51, 4.18, 1.4, 0.0, 96.7, 92.4);
|
||||
Add(2, "40/44", "4A30", 10.3, 1.49, 4.12, 1.35, 0.0, 96.8, 92.6);
|
||||
Add(2, "40/44", "4A40", 10.4, 1.47, 4.06, 1.3, 0.0, 96.9, 92.8);
|
||||
Add(2, "40/44", "4A50", 10.5, 1.45, 4.0, 1.25, 0.0, 97.0, 93.0);
|
||||
Add(2, "40/44", "4A60", 10.6, 1.43, 3.94, 1.2, 0.0, 97.1, 93.2);
|
||||
Add(2, "40/44", "4A70", 10.7, 1.41, 3.88, 1.15, 0.0, 97.2, 93.4);
|
||||
Add(2, "40/44", "4A80", 10.8, 1.39, 3.82, 1.1, 0.0, 97.3, 93.6);
|
||||
Add(2, "40/44", "4A90", 10.9, 1.37, 3.76, 1.05, 0.0, 97.4, 93.8);
|
||||
Add(2, "40/44", "5A00", 11.0, 1.35, 3.7, 1.0, 0.0, 97.5, 94.0);
|
||||
Add(2, "40/44", "5A10", 11.1, 1.33, 3.64, 0.0, 0.0, 97.55, 94.1);
|
||||
Add(2, "40/44", "5A20", 11.2, 1.31, 3.58, 0.0, 0.0, 97.6, 94.2);
|
||||
Add(2, "40/44", "5A30", 11.3, 1.29, 3.52, 0.0, 0.0, 97.65, 94.3);
|
||||
Add(2, "40/44", "5A40", 11.4, 1.27, 3.46, 0.0, 0.0, 97.7, 94.4);
|
||||
Add(2, "40/44", "5A50", 11.5, 1.25, 3.4, 0.0, 0.0, 97.75, 94.5);
|
||||
Add(2, "40/44", "5A60", 11.6, 1.23, 3.34, 0.0, 0.0, 97.8, 94.6);
|
||||
Add(2, "40/44", "5A70", 11.7, 1.21, 3.28, 0.0, 0.0, 97.85, 94.7);
|
||||
Add(2, "40/44", "5A80", 11.8, 1.19, 3.22, 0.0, 0.0, 97.9, 94.8);
|
||||
Add(2, "40/44", "5A90", 11.9, 1.17, 3.16, 0.0, 0.0, 97.95, 94.9);
|
||||
Add(2, "40/44", "6A00", 12.0, 1.15, 3.1, 0.0, 0.0, 98.0, 95.0);
|
||||
Add(2, "20/22", "等外", 6.0, 9.99, 99.99, 100.0, 100.0, 0.0, 0.0);
|
||||
Add(2, "27/29", "等外", 6.0, 9.99, 99.99, 100.0, 100.0, 0.0, 0.0);
|
||||
Add(2, "40/44", "等外", 6.0, 9.99, 99.99, 100.0, 100.0, 0.0, 0.0);
|
||||
|
||||
fsql.Insert(list).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
using System.Data;
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Interfaces.SnowflakeID;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Entities.Base;
|
||||
using F9WebAiTest.Domain.Entities.Notice;
|
||||
using F9WebAiTest.Domain.Enums.SYS.RBAC;
|
||||
using SysMenu = F9WebAiTest.Domain.Entities.SYS.RBAC.Menu;
|
||||
using SysPermission = F9WebAiTest.Domain.Entities.SYS.RBAC.Permission;
|
||||
using SysRolePermission = F9WebAiTest.Domain.Entities.SYS.RBAC.RolePermission;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.Data;
|
||||
|
||||
public static class DbSeeder
|
||||
{
|
||||
public static void Seed(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
// 1. 建表(CodeFirst SyncStructure,增量同步,兼容旧库)
|
||||
SyncStructure(fsql);
|
||||
|
||||
// 2. 默认角色
|
||||
if (!fsql.Select<Role>().Any())
|
||||
{
|
||||
fsql.Insert(new Role { Name = "超级管理员", Code = "admin", Description = "拥有所有权限", Sort = 1 }).ExecuteAffrows();
|
||||
fsql.Insert(new Role { Name = "普通用户", Code = "user", Description = "普通员工", Sort = 2 }).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 3. 默认菜单(系统管理)
|
||||
if (!fsql.Select<Menu>().Any())
|
||||
{
|
||||
var systemId = InsertAndGetId(fsql, new Menu { Name = "系统管理", Type = "directory", Path = "/system", Icon = "Setting", Sort = 1 });
|
||||
var deptId = InsertAndGetId(fsql, new Menu { Name = "部门管理", Type = "menu", Path = "/system/department", Component = "system/department/index", Icon = "OfficeBuilding", Permission = "system:department:list", Sort = 1, ParentId = systemId });
|
||||
var empId = InsertAndGetId(fsql, new Menu { Name = "职工管理", Type = "menu", Path = "/system/employee", Component = "system/employee/index", Icon = "User", Permission = "system:employee:list", Sort = 2, ParentId = systemId });
|
||||
var roleId = InsertAndGetId(fsql, new Menu { Name = "角色管理", Type = "menu", Path = "/system/role", Component = "system/role/index", Icon = "UserFilled", Permission = "system:role:list", Sort = 3, ParentId = systemId });
|
||||
var menuId = InsertAndGetId(fsql, new Menu { Name = "菜单管理", Type = "menu", Path = "/system/menu", Component = "system/menu/index", Icon = "Menu", Permission = "system:menu:list", Sort = 4, ParentId = systemId });
|
||||
var logId = InsertAndGetId(fsql, new Menu { Name = "操作日志", Type = "menu", Path = "/system/log", Component = "system/log/index", Icon = "Document", Permission = "system:log:list", Sort = 5, ParentId = systemId });
|
||||
fsql.Insert(new Menu[]
|
||||
{
|
||||
new Menu { Name = "部门操作", Type = "button", Permission = "system:department:btn", Sort = 1, ParentId = deptId },
|
||||
new Menu { Name = "职工操作", Type = "button", Permission = "system:employee:btn", Sort = 1, ParentId = empId },
|
||||
new Menu { Name = "角色操作", Type = "button", Permission = "system:role:btn", Sort = 1, ParentId = roleId },
|
||||
new Menu { Name = "菜单操作", Type = "button", Permission = "system:menu:btn", Sort = 1, ParentId = menuId },
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 4. 考勤菜单(增量)
|
||||
SeedAttendanceMenus(fsql);
|
||||
|
||||
// 5. 业务基础数据(部门树 + 机组车台 + 庄口 + 规格 + 定级指标 + 菜单)
|
||||
BusinessSeedData.Seed(fsql);
|
||||
|
||||
// 6. 通知:消息类型种子 + 菜单
|
||||
SeedNotificationData(fsql, snowflake);
|
||||
|
||||
// 6.5 数据迁移:旧 RBAC 表 → 新 DDD RBAC 表(先迁移,确保 admin 角色/用户已在新表)
|
||||
MigrateRbacData(fsql);
|
||||
|
||||
// 6.6 F9 风格 RBAC 菜单(用户/角色/菜单/部门管理,写入新 sys_rbac_menus 表)
|
||||
SeedSysRbacMenus(fsql, snowflake);
|
||||
|
||||
// 6.7 为菜单创建权限单元(Permission),并修正旧菜单权限关联(统一权限模型)
|
||||
FixMenuPermissions(fsql, snowflake);
|
||||
|
||||
// 7. 管理员账号(幂等)
|
||||
var admin = fsql.Select<Employee>().Where(e => e.Username == "admin").First();
|
||||
if (admin == null)
|
||||
{
|
||||
var dept = fsql.Select<Department>().Where(d => d.Name == "主干运维").First()
|
||||
?? fsql.Select<Department>().First();
|
||||
var adminId = InsertAndGetId(fsql, new Employee
|
||||
{
|
||||
Username = "admin",
|
||||
Password = BCrypt.Net.BCrypt.HashPassword("123456"),
|
||||
RealName = "系统管理员",
|
||||
Nickname = "管理员",
|
||||
DepartmentId = dept.Id,
|
||||
Email = "admin@company.com",
|
||||
Phone = "13800000000",
|
||||
Status = 1
|
||||
});
|
||||
admin = fsql.Select<Employee>().Where(e => e.Id == adminId).First();
|
||||
}
|
||||
|
||||
// 8. admin 关联 admin 角色
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (!fsql.Select<EmployeeRole>().Any(er => er.EmployeeId == admin.Id && er.RoleId == adminRole.Id))
|
||||
{
|
||||
fsql.Insert(new EmployeeRole { EmployeeId = admin.Id, RoleId = adminRole.Id }).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 9. admin 角色分配所有菜单(幂等补充)
|
||||
var allMenus = fsql.Select<Menu>().ToList();
|
||||
foreach (var m in allMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
{
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>CodeFirst 建表(新表建表,旧表同步失败则跳过)+ 旧库加列兜底</summary>
|
||||
private static void SyncStructure(IFreeSql fsql)
|
||||
{
|
||||
// 扫描 BaseEntity(旧)与 Entity(新 DDD 基类)两类实体的具体子类建表
|
||||
var entityTypes = typeof(BaseEntity).Assembly.GetTypes()
|
||||
.Where(t => t.IsClass && !t.IsAbstract &&
|
||||
(t.IsSubclassOf(typeof(BaseEntity)) || t.IsSubclassOf(typeof(Entity))));
|
||||
foreach (var t in entityTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
fsql.CodeFirst.SyncStructure(t);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 旧表同步失败(如唯一索引与历史数据冲突),跳过,保持旧库兼容
|
||||
Console.WriteLine($"[DbSeeder] 表 {t.Name} 结构同步跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 旧库加列兜底(SyncStructure 不会为已有表加列)
|
||||
EnsureColumn(fsql, "LeaveRequests", "CurrentOrderNo", "INTEGER NOT NULL DEFAULT 1");
|
||||
EnsureColumn(fsql, "NotificationTypes", "Category", "INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(fsql, "NotificationTypes", "Icon", "TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(fsql, "NotificationTypes", "RouteTemplate", "TEXT NOT NULL DEFAULT ''");
|
||||
EnsureColumn(fsql, "NotificationMessages", "Category", "INTEGER NOT NULL DEFAULT 0");
|
||||
EnsureColumn(fsql, "NotificationMessages", "RoutePath", "TEXT NOT NULL DEFAULT ''");
|
||||
}
|
||||
|
||||
/// <summary>为已有表补加列(列已存在则忽略)</summary>
|
||||
private static void EnsureColumn(IFreeSql fsql, string table, string column, string definition)
|
||||
{
|
||||
try
|
||||
{
|
||||
fsql.Ado.ExecuteNonQuery($"ALTER TABLE \"{table}\" ADD COLUMN \"{column}\" {definition};");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 列已存在,忽略
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>插入并返回自增主键 ID(SQLite 用 last_insert_rowid 可靠获取)</summary>
|
||||
public static long InsertAndGetId<T>(IFreeSql fsql, T entity) where T : BaseEntity
|
||||
{
|
||||
fsql.Insert(entity).ExecuteAffrows();
|
||||
return Convert.ToInt64(fsql.Ado.ExecuteScalar("SELECT last_insert_rowid();"));
|
||||
}
|
||||
|
||||
/// <summary>通知种子:消息类型 + 消息中心菜单</summary>
|
||||
private static void SeedNotificationData(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
// 消息类型:发现特野
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "TeyeFound"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "TeyeFound", Name = "发现特野", Description = "纤度检验发现特野时自动推送提醒",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 1,
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/xdjy"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:发现特大糙
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "TdcFound"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "TdcFound", Name = "发现特大糙", Description = "黑板检验发现特大糙时自动推送提醒",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 2,
|
||||
Category = NotificationCategory.Notification, Icon = "Warning", RouteTemplate = "/prod/hbjy"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:请假审批(订阅权限用独立码,避免与业务按钮权限冲突)
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "LeaveApproval"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "LeaveApproval", Name = "请假审批", Description = "员工提交请假申请后,推送待办给审批人",
|
||||
RequiredPermission = "notification:subscribe:leaveapproval", IsActive = true, CreatedBy = 0, Sort = 3,
|
||||
Category = NotificationCategory.Todo, Icon = "Checked", RouteTemplate = "/attendance/approve?leaveId={businessId}"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:请假结果通知
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "LeaveResult"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "LeaveResult", Name = "请假结果通知", Description = "请假审批完成后通知申请人结果",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 4,
|
||||
Category = NotificationCategory.Notification, Icon = "Message", RouteTemplate = "/attendance/my"
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 消息类型:广播通知(有权限的用户主动发送的广播,推送给所有订阅者)
|
||||
if (!fsql.Select<NotificationType>().Any(t => t.Code == "Broadcast"))
|
||||
{
|
||||
fsql.Insert(new NotificationType
|
||||
{
|
||||
Code = "Broadcast", Name = "广播通知", Description = "用户发送的广播通知,推送给所有订阅该类型的用户",
|
||||
RequiredPermission = "", IsActive = true, CreatedBy = 0, Sort = 5,
|
||||
Category = NotificationCategory.Announcement, Icon = "Bell", RouteTemplate = ""
|
||||
}).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 广播发送权限单元(ResourceType=操作,Code=notification:broadcast,admin 自动拥有,普通用户可在角色管理分配)
|
||||
var broadcastPerm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.Code == "notification:broadcast").First();
|
||||
if (broadcastPerm == null)
|
||||
{
|
||||
var perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Enable, Resource_Type_Enum.操作, "broadcast", 0);
|
||||
perm.SetCode("notification:broadcast");
|
||||
fsql.Insert(perm).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 菜单:消息中心
|
||||
var notiDirId = fsql.Select<Menu>().Where(m => m.Path == "/notification").First()?.Id ?? 0;
|
||||
if (notiDirId == 0)
|
||||
{
|
||||
notiDirId = InsertAndGetId(fsql, new Menu { Name = "消息中心", Type = "directory", Path = "/notification", Icon = "Bell", Sort = 5 });
|
||||
}
|
||||
|
||||
AddNotificationMenu(fsql, notiDirId, "我的消息", "/notification/messages", "notification/messages/index", "Message", "notification:messages:list", 1);
|
||||
AddNotificationMenu(fsql, notiDirId, "消息订阅", "/notification/subscribe", "notification/subscribe/index", "BellFilled", "notification:subscribe:list", 2);
|
||||
AddNotificationMenu(fsql, notiDirId, "消息类型管理", "/notification/type", "notification/type/index", "SetUp", "notification:type:list", 3);
|
||||
|
||||
// 清理历史遗留:发现特野/发现特大糙 改为「所有人可订阅」
|
||||
foreach (var (code, oldPerm) in new[] { ("TeyeFound", "notification:subscribe:foundteye"), ("TdcFound", "notification:subscribe:foundtedacao") })
|
||||
{
|
||||
var t = fsql.Select<NotificationType>().Where(x => x.Code == code).First();
|
||||
if (t == null) continue;
|
||||
|
||||
// 1. RequiredPermission 改为空(所有人可订阅)
|
||||
if (!string.IsNullOrEmpty(t.RequiredPermission))
|
||||
{
|
||||
t.RequiredPermission = "";
|
||||
fsql.Update<NotificationType>().SetSource(t).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 2. 删除订阅权限单元及其角色关联(新 Permission 表)
|
||||
var perm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.消息订阅 && p.ResourceId == t.Id).First();
|
||||
if (perm != null)
|
||||
{
|
||||
fsql.Delete<SysRolePermission>().Where(rp => rp.PermissionId == perm.Id).ExecuteAffrows();
|
||||
fsql.Delete<SysPermission>().Where(p => p.Id == perm.Id).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 3. 删除历史遗留的旧"订阅按钮"菜单及其角色关联(新表 sys_rbac_menus + 旧表 Menus)
|
||||
fsql.Ado.ExecuteNonQuery($@"
|
||||
DELETE FROM sys_rbac_role_permissions WHERE permission_id IN
|
||||
(SELECT id FROM sys_rbac_menus WHERE permission = '{oldPerm}');
|
||||
DELETE FROM sys_rbac_menus WHERE permission = '{oldPerm}';
|
||||
DELETE FROM RoleMenus WHERE MenuId IN (SELECT Id FROM Menus WHERE Permission = '{oldPerm}');
|
||||
DELETE FROM Menus WHERE Permission = '{oldPerm}';");
|
||||
}
|
||||
|
||||
// 清理所有旧的"订阅按钮"菜单(已被 Permission 表的消息订阅权限替代,避免与消息订阅权限重复冗余)
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
DELETE FROM sys_rbac_role_permissions WHERE permission_id IN
|
||||
(SELECT id FROM sys_rbac_menus WHERE name LIKE '订阅:%' AND menu_type = 0);
|
||||
DELETE FROM sys_rbac_menus WHERE name LIKE '订阅:%' AND menu_type = 0;
|
||||
DELETE FROM RoleMenus WHERE MenuId IN (SELECT Id FROM Menus WHERE Name LIKE '订阅:%' AND Type = 'button');
|
||||
DELETE FROM Menus WHERE Name LIKE '订阅:%' AND Type = 'button';");
|
||||
|
||||
// 清理 RequiredPermission 为空的消息类型的订阅权限(历史遗留,所有人可订阅无需权限单元)
|
||||
foreach (var t in fsql.Select<NotificationType>().Where(x => x.RequiredPermission == "").ToList())
|
||||
{
|
||||
var perm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.消息订阅 && p.ResourceId == t.Id).First();
|
||||
if (perm != null)
|
||||
{
|
||||
fsql.Delete<SysRolePermission>().Where(rp => rp.PermissionId == perm.Id).ExecuteAffrows();
|
||||
fsql.Delete<SysPermission>().Where(p => p.Id == perm.Id).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
// 同步订阅权限到 RBAC 权限单元(ResourceType=消息订阅,Code=RequiredPermission)
|
||||
foreach (var t in fsql.Select<NotificationType>().ToList())
|
||||
{
|
||||
if (string.IsNullOrEmpty(t.RequiredPermission)) continue;
|
||||
var existPerm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.消息订阅 && p.ResourceId == t.Id).First();
|
||||
if (existPerm == null)
|
||||
{
|
||||
var perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Visble, Resource_Type_Enum.消息订阅, t.Code, t.Id);
|
||||
perm.SetCode(t.RequiredPermission);
|
||||
fsql.Insert(perm).ExecuteAffrows();
|
||||
}
|
||||
else if (existPerm.Code != t.RequiredPermission)
|
||||
{
|
||||
// 修正 Code 为业务权限码(覆盖旧的自动生成码)
|
||||
existPerm.SetCode(t.RequiredPermission);
|
||||
fsql.Update<SysPermission>().SetSource(existPerm).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
// admin 角色分配消息中心菜单权限
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (adminRole != null)
|
||||
{
|
||||
var notiMenus = fsql.Select<Menu>().Where(m => m.Path.StartsWith("/notification")).ToList();
|
||||
foreach (var m in notiMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddNotificationMenu(IFreeSql fsql, long parentId, string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
if (!fsql.Select<Menu>().Any(m => m.Path == path))
|
||||
{
|
||||
fsql.Insert(new Menu { ParentId = parentId, Name = name, Type = "menu", Path = path, Component = component, Icon = icon, Permission = permission, Sort = sort }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SeedAttendanceMenus(IFreeSql fsql)
|
||||
{
|
||||
var attDirId = fsql.Select<Menu>().Where(m => m.Path == "/attendance").First()?.Id ?? 0;
|
||||
if (attDirId == 0)
|
||||
{
|
||||
attDirId = InsertAndGetId(fsql, new Menu { Name = "考勤管理", Type = "directory", Path = "/attendance", Icon = "Clock", Sort = 2 });
|
||||
}
|
||||
|
||||
void AddMenu(string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
if (!fsql.Select<Menu>().Any(m => m.Path == path))
|
||||
{
|
||||
fsql.Insert(new Menu { Name = name, Type = "menu", Path = path, Component = component, Icon = icon, Permission = permission, Sort = sort, ParentId = attDirId }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
AddMenu("考勤打卡", "/attendance/punch", "attendance/punch/index", "AlarmClock", "attendance:punch:list", 1);
|
||||
AddMenu("我的考勤", "/attendance/my", "attendance/my/index", "Calendar", "attendance:my:list", 2);
|
||||
AddMenu("请假申请", "/attendance/leave", "attendance/leave/index", "Suitcase", "attendance:leave:list", 3);
|
||||
AddMenu("考勤记录", "/attendance/records", "attendance/records/index", "List", "attendance:records:list", 4);
|
||||
AddMenu("请假审批", "/attendance/approve", "attendance/approve/index", "Checked", "attendance:approve:list", 5);
|
||||
|
||||
void AddButton(string menuPath, string name, string permission)
|
||||
{
|
||||
var menu = fsql.Select<Menu>().Where(m => m.Path == menuPath).First();
|
||||
if (menu != null && !fsql.Select<Menu>().Any(m => m.Permission == permission))
|
||||
{
|
||||
fsql.Insert(new Menu { Name = name, Type = "button", Permission = permission, Sort = 1, ParentId = menu.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
AddButton("/attendance/punch", "打卡操作", "attendance:punch:btn");
|
||||
AddButton("/attendance/leave", "请假操作", "attendance:leave:btn");
|
||||
AddButton("/attendance/approve", "审批操作", "attendance:approve:btn");
|
||||
|
||||
var adminRole = fsql.Select<Role>().Where(r => r.Code == "admin").First();
|
||||
if (adminRole != null)
|
||||
{
|
||||
var attMenus = fsql.Select<Menu>().Where(m => m.Path.StartsWith("/attendance") || m.Permission.StartsWith("attendance:")).ToList();
|
||||
foreach (var m in attMenus)
|
||||
{
|
||||
if (!fsql.Select<RoleMenu>().Any(rm => rm.RoleId == adminRole.Id && rm.MenuId == m.Id))
|
||||
fsql.Insert(new RoleMenu { RoleId = adminRole.Id, MenuId = m.Id }).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>数据迁移:旧 RBAC 表 → 新 DDD RBAC 表(幂等,仅迁移尚不存在的记录)</summary>
|
||||
private static void MigrateRbacData(IFreeSql fsql)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 部门:Departments → sys_departments
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_departments (id, code, name, parent_id, manage_type, duty_type, description, is_active, sort_order, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, COALESCE(Code, ''), Name, COALESCE(ParentId, 0), 10, 5, '', Status, Sort, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Departments WHERE Id NOT IN (SELECT id FROM sys_departments);");
|
||||
|
||||
// 2. 角色:Roles → sys_rbac_roles
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_roles (id, name, code, sort_order, description, is_active, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, Name, Code, Sort, COALESCE(Description, ''), Status, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Roles WHERE Id NOT IN (SELECT id FROM sys_rbac_roles);");
|
||||
|
||||
// 3. 菜单:Menus → sys_rbac_menus
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_menus (id, icon_svg, code, name, menu_type, assembly_name, type_full_name, parent_id, level, is_active, sort_order, path, component, permission, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, COALESCE(Icon, ''), '', Name, CASE WHEN Type = 'directory' THEN 1 WHEN Type = 'menu' THEN 2 ELSE 0 END, '', '', COALESCE(ParentId, 0), 1, Status, Sort, COALESCE(Path, ''), COALESCE(Component, ''), COALESCE(Permission, ''), '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Menus WHERE Id NOT IN (SELECT id FROM sys_rbac_menus);");
|
||||
|
||||
// 4. 用户:Employees → sys_rbac_users
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_users (id, department_id, user_name, phone_number, login_name, password_hash, is_active, sort_order, lastlogin_at, nickname, avatar, email, gender, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, DepartmentId, RealName, COALESCE(Phone, ''), Username, Password, Status, 0, NULL, COALESCE(Nickname, ''), COALESCE(Avatar, ''), COALESCE(Email, ''), COALESCE(Gender, '男'), '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM Employees WHERE Id NOT IN (SELECT id FROM sys_rbac_users);");
|
||||
|
||||
// 5. 用户角色:EmployeeRoles → sys_rbac_user_roles
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_user_roles (id, user_id, role_id, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, EmployeeId, RoleId, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM EmployeeRoles WHERE Id NOT IN (SELECT id FROM sys_rbac_user_roles);");
|
||||
|
||||
// 6. 角色权限:RoleMenus → sys_rbac_role_permissions
|
||||
fsql.Ado.ExecuteNonQuery(@"
|
||||
INSERT INTO sys_rbac_role_permissions (id, role_id, permission_id, created_by, created_at, update_by, updated_at, is_deleted)
|
||||
SELECT Id, RoleId, MenuId, '', COALESCE(CreatedAt, datetime('now')), '', UpdatedAt, IsDeleted
|
||||
FROM RoleMenus WHERE Id NOT IN (SELECT id FROM sys_rbac_role_permissions);");
|
||||
|
||||
Console.WriteLine("[DbSeeder] RBAC 数据迁移完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] RBAC 数据迁移跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>F9 风格 RBAC 前端菜单种子(写入新 sys_rbac_menus 表,驱动前端导航)</summary>
|
||||
private static void SeedSysRbacMenus(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 删除旧"系统管理"菜单(/system/*,对接已下线的旧接口),仅保留新的 /sys 管理菜单
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM sys_rbac_role_permissions WHERE permission_id IN (SELECT id FROM sys_rbac_menus WHERE path LIKE '/system%')");
|
||||
fsql.Ado.ExecuteNonQuery("DELETE FROM sys_rbac_menus WHERE path LIKE '/system%'");
|
||||
|
||||
// sys 目录
|
||||
long sysDirId;
|
||||
var sysDir = fsql.Ado.ExecuteDataTable("SELECT id FROM sys_rbac_menus WHERE path = '/sys'");
|
||||
if (sysDir.Rows.Count == 0)
|
||||
{
|
||||
sysDirId = snowflake.CreateId();
|
||||
fsql.Ado.ExecuteNonQuery($"INSERT INTO sys_rbac_menus (id, icon_svg, name, menu_type, parent_id, level, is_active, sort_order, path, component, permission) VALUES ({sysDirId}, 'UserFilled', '系统管理(RBAC)', 1, 0, 1, 0, 6, '/sys', '', '')");
|
||||
}
|
||||
else
|
||||
{
|
||||
sysDirId = Convert.ToInt64(sysDir.Rows[0]["id"]);
|
||||
}
|
||||
|
||||
void AddMenu(string name, string path, string component, string icon, string permission, int sort)
|
||||
{
|
||||
var exist = fsql.Ado.ExecuteDataTable($"SELECT id FROM sys_rbac_menus WHERE path = '{path}'");
|
||||
if (exist.Rows.Count > 0) return;
|
||||
var id = snowflake.CreateId();
|
||||
fsql.Ado.ExecuteNonQuery($"INSERT INTO sys_rbac_menus (id, icon_svg, name, menu_type, parent_id, level, is_active, sort_order, path, component, permission) VALUES ({id}, '{icon}', '{name}', 2, {sysDirId}, 2, 0, {sort}, '{path}', '{component}', '{permission}')");
|
||||
}
|
||||
|
||||
AddMenu("用户管理", "/sys/user", "sys/user/index", "User", "sys:user:list", 1);
|
||||
AddMenu("角色管理", "/sys/role", "sys/role/index", "UserFilled", "sys:role:list", 2);
|
||||
AddMenu("菜单管理", "/sys/menu", "sys/menu/index", "Menu", "sys:menu:list", 3);
|
||||
AddMenu("部门管理", "/sys/department", "sys/department/index", "OfficeBuilding", "sys:department:list", 4);
|
||||
|
||||
// admin 角色分配 sys 菜单权限
|
||||
var adminRole = fsql.Ado.ExecuteDataTable("SELECT id FROM sys_rbac_roles WHERE code = 'admin'");
|
||||
if (adminRole.Rows.Count > 0)
|
||||
{
|
||||
var roleId = Convert.ToInt64(adminRole.Rows[0]["id"]);
|
||||
var menus = fsql.Ado.ExecuteDataTable("SELECT id FROM sys_rbac_menus WHERE path LIKE '/sys%'");
|
||||
foreach (DataRow row in menus.Rows)
|
||||
{
|
||||
var menuId = Convert.ToInt64(row["id"]);
|
||||
var exist = fsql.Ado.ExecuteDataTable($"SELECT id FROM sys_rbac_role_permissions WHERE role_id = {roleId} AND permission_id = {menuId}");
|
||||
if (exist.Rows.Count == 0)
|
||||
{
|
||||
fsql.Ado.ExecuteNonQuery($"INSERT INTO sys_rbac_role_permissions (id, role_id, permission_id) VALUES ({snowflake.CreateId()}, {roleId}, {menuId})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] RBAC 菜单种子跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>为菜单创建权限单元(Permission),并修正旧菜单权限关联(RolePermission.PermissionId 从 Menu.Id 改为 Permission.Id)</summary>
|
||||
private static void FixMenuPermissions(IFreeSql fsql, ISnowflakeIdGenerator snowflake)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var menu in fsql.Select<SysMenu>().ToList())
|
||||
{
|
||||
// 1. 查或创建菜单权限单元(ResourceType=菜单,ResourceId=Menu.Id)
|
||||
var perm = fsql.Select<SysPermission>()
|
||||
.Where(p => p.ResourceType == Resource_Type_Enum.菜单 && p.ResourceId == menu.Id).First();
|
||||
if (perm == null)
|
||||
{
|
||||
var resourceIdentity = string.IsNullOrEmpty(menu.Code) ? menu.Id.ToString() : menu.Code;
|
||||
perm = new SysPermission(snowflake.CreateId(), Permission_Type_Enum.Visble, Resource_Type_Enum.菜单, resourceIdentity, menu.Id);
|
||||
if (!string.IsNullOrEmpty(menu.Permission))
|
||||
perm.SetCode(menu.Permission);
|
||||
fsql.Insert(perm).ExecuteAffrows();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(menu.Permission) && perm.Code != menu.Permission)
|
||||
{
|
||||
perm.SetCode(menu.Permission);
|
||||
fsql.Update<SysPermission>().SetSource(perm).ExecuteAffrows();
|
||||
}
|
||||
|
||||
// 2. 修正旧菜单权限关联(permission_id 旧值 = Menu.Id,改为 Permission.Id)
|
||||
fsql.Ado.ExecuteNonQuery($"UPDATE sys_rbac_role_permissions SET permission_id = {perm.Id} WHERE permission_id = {menu.Id};");
|
||||
}
|
||||
Console.WriteLine("[DbSeeder] 菜单权限单元同步完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DbSeeder] 菜单权限单元同步跳过:{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using FreeSql;
|
||||
using FreeSql.Aop;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.Departments;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.RBAC.Menus;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.RBAC.Roles;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.RBAC.Users;
|
||||
using F9WebAiTest.Application.Interfaces.Security;
|
||||
using F9WebAiTest.Application.Interfaces.SnowflakeID;
|
||||
using F9WebAiTest.Application.Interfaces.UOW;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
using F9WebAiTest.Domain.Entities;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.Departments;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Menus;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Permissions;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Roles;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Users;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Persistence;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.Departments;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Menus;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Permissions;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Roles;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Users;
|
||||
using F9WebAiTest.Infrastructure.InterfaceImplements.Security;
|
||||
using F9WebAiTest.Infrastructure.InterfaceImplements.SnowflakeID;
|
||||
using F9WebAiTest.Infrastructure.Repositories;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// 创建 FreeSql 实例(SQLite),关闭自动同步结构(只建新表,不修改旧库)
|
||||
var connStr = configuration.GetConnectionString("Default");
|
||||
var fsql = new FreeSqlBuilder()
|
||||
.UseConnectionString(DataType.Sqlite, connStr)
|
||||
.UseAutoSyncStructure(false)
|
||||
.Build();
|
||||
|
||||
// 软删除全局过滤器:自动应用到新旧两套实体基类
|
||||
fsql.GlobalFilter.Apply<BaseEntity>("软删除", a => a.IsDeleted == false);
|
||||
fsql.GlobalFilter.Apply<Entity>("软删除", a => a.IsDeleted == false);
|
||||
|
||||
// 枚举统一映射为 int(与旧库 INTEGER 存储保持一致)
|
||||
fsql.Aop.ConfigEntityProperty += (_, e) =>
|
||||
{
|
||||
if (e.Property.PropertyType.IsEnum)
|
||||
e.ModifyResult.MapType = typeof(int);
|
||||
};
|
||||
|
||||
services.AddSingleton(fsql);
|
||||
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||
|
||||
// ==================== F9 风格 RBAC(DDD + CQRS)依赖注册 ====================
|
||||
|
||||
// 雪花 ID(有状态,单例)
|
||||
services.AddSingleton<ISnowflakeIdGenerator, SnowflakeIdGenerator>();
|
||||
// 密码哈希
|
||||
services.AddScoped<IPasswordHasher, PasswordHasher>();
|
||||
// 工作单元(事务)
|
||||
services.AddScoped<IUnitOfWork_F9, UnitOfWork_F9>();
|
||||
|
||||
// 写仓储(Domain 接口)
|
||||
services.AddScoped<IUserWriteRepository, UserWriteRepository>();
|
||||
services.AddScoped<IUserRoleWriteRepository, UserRoleWriteRepository>();
|
||||
services.AddScoped<IRoleWriteRepository, RoleWriteRepository>();
|
||||
services.AddScoped<IRolePermissionWriteRepository, RolePermissionWriteRepository>();
|
||||
services.AddScoped<IMenuWriteRepository, MenuWriteRepository>();
|
||||
services.AddScoped<IPermissionWriteRepository, PermissionWriteRepository>();
|
||||
services.AddScoped<IDepartmentWriteRepository, DepartmentWriteRepository>();
|
||||
services.AddScoped<IDepartmentUserRepository, DepartmentUserRepository>();
|
||||
|
||||
// 读仓储(Application 接口)
|
||||
services.AddScoped<IUserReadRepository, UserReadRepository>();
|
||||
services.AddScoped<IRoleReadRepository, RoleReadRepository>();
|
||||
services.AddScoped<IMenuReadRepository, MenuReadRepository>();
|
||||
services.AddScoped<IDepartmentReadRepository, DepartmentReadRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<RootNamespace>F9WebAiTest.Infrastructure</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\F9WebAiTest.Domain\F9WebAiTest.Domain.csproj" />
|
||||
<ProjectReference Include="..\F9WebAiTest.Application\F9WebAiTest.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="FreeSql" Version="3.5.213" />
|
||||
<PackageReference Include="FreeSql.DbContext" Version="3.5.213" />
|
||||
<PackageReference Include="FreeSql.Provider.Sqlite" Version="3.5.213" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,46 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Interfaces.UOW;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// 工作单元实现:基于 FreeSql 的 UnitOfWorkManager,事务内 Orm 通过 AsyncLocal 暴露给仓储。
|
||||
/// 仓储通过 UnitOfWork.CurrentOrm 获取事务感知的 Orm。
|
||||
/// </summary>
|
||||
public class UnitOfWork_F9 : IUnitOfWork_F9
|
||||
{
|
||||
private readonly IFreeSql _fsql;
|
||||
private readonly UnitOfWorkManager _uowManager;
|
||||
private static readonly AsyncLocal<IFreeSql> _currentOrm = new();
|
||||
|
||||
/// <summary>当前事务内的 Orm(非事务上下文返回 null)</summary>
|
||||
public static IFreeSql CurrentOrm => _currentOrm.Value;
|
||||
|
||||
public UnitOfWork_F9(IFreeSql fsql)
|
||||
{
|
||||
_fsql = fsql;
|
||||
_uowManager = new UnitOfWorkManager(fsql);
|
||||
}
|
||||
|
||||
public async Task<bool> ExecuteWithUow(Func<Task<bool>> func)
|
||||
{
|
||||
using var uow = _uowManager.Begin();
|
||||
_currentOrm.Value = uow.Orm;
|
||||
try
|
||||
{
|
||||
var result = await func();
|
||||
if (result) uow.Commit();
|
||||
else uow.Rollback();
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
uow.Rollback();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_currentOrm.Value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Infrastructure.FreeSqls.Persistence;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories;
|
||||
|
||||
/// <summary>F9 风格仓储基类:提供事务感知的 Orm(事务内用事务 Orm,事务外用外部 Orm)</summary>
|
||||
public abstract class F9RepositoryBase
|
||||
{
|
||||
protected readonly IFreeSql _fsql;
|
||||
|
||||
protected F9RepositoryBase(IFreeSql fsql) => _fsql = fsql;
|
||||
|
||||
/// <summary>事务感知的 Orm</summary>
|
||||
protected IFreeSql Orm => UnitOfWork_F9.CurrentOrm ?? _fsql;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Dtos.SYS.Departments;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.Departments;
|
||||
using F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.Departments;
|
||||
|
||||
internal class DepartmentReadRepository : F9RepositoryBase, IDepartmentReadRepository
|
||||
{
|
||||
public DepartmentReadRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<List<DepartmentReadDto_View>> GetAllAsync()
|
||||
=> await Orm.Select<Department>()
|
||||
.Where(x => x.DeletedAt == null)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ToListAsync(x => new DepartmentReadDto_View(x.Id, x.Code, x.Name, x.ParentId, x.ManageType, x.DutyType, x.Description, x.IsActive, x.SortOrder));
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.Departments;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.Departments;
|
||||
|
||||
internal class DepartmentUserRepository : F9RepositoryBase, IDepartmentUserRepository
|
||||
{
|
||||
public DepartmentUserRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<bool> AssignAsync(DepartmentUser depUser)
|
||||
{
|
||||
depUser.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(depUser).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveAsync(long depId, long userId)
|
||||
=> await Orm.Delete<DepartmentUser>()
|
||||
.Where(x => x.DepartmentId == depId && x.UserId == userId)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.Departments;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.Departments;
|
||||
|
||||
internal class DepartmentWriteRepository : F9RepositoryBase, IDepartmentWriteRepository
|
||||
{
|
||||
public DepartmentWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<Department> GetByIdAsync(long id, CancellationToken cancellationToken)
|
||||
=> await Orm.Select<Department>().Where(x => x.Id == id ).FirstAsync(cancellationToken);
|
||||
|
||||
public async Task<bool> AddAsync(Department dep)
|
||||
{
|
||||
dep.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(dep).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(Department dep)
|
||||
{
|
||||
dep.UpdatedAt = DateTime.Now;
|
||||
return await Orm.Update<Department>().SetSource(dep).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> SoftDeleteAsync(long id)
|
||||
=> await Orm.Update<Department>()
|
||||
.Set(x => x.IsDeleted, true)
|
||||
.Where(x => x.Id == id )
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> ActiveOrDeactive(long id, bool active)
|
||||
=> await Orm.Update<Department>()
|
||||
.Set(x => x.IsActive, active)
|
||||
.Set(x => x.UpdatedAt, DateTime.Now)
|
||||
.Where(x => x.Id == id)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Dtos.SYS.RBAC.Menus;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.RBAC.Menus;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Menus;
|
||||
|
||||
internal class MenuReadRepository : F9RepositoryBase, IMenuReadRepository
|
||||
{
|
||||
public MenuReadRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<MenuReadDto_Menu_Edit?> GetMenuByIdForEditAsync(long id)
|
||||
=> await Orm.Select<Menu>()
|
||||
.Where(x => x.Id == id && x.DeletedAt == null)
|
||||
.FirstAsync(x => new MenuReadDto_Menu_Edit(x.Id, x.Code, x.Name, x.IconSvg, x.MenuType, x.AssemblyName, x.TypeFullName, x.IsActive, x.ParentId, x.SortOrder));
|
||||
|
||||
public async Task<bool> ExistMenuByTypeFullnameExcludeIdAsync(string typeFullname, long? excludeId)
|
||||
{
|
||||
var query = Orm.Select<Menu>().Where(x => x.TypeFullName == typeFullname && x.DeletedAt == null);
|
||||
if (excludeId.HasValue)
|
||||
query = query.Where(x => x.Id != excludeId.Value);
|
||||
return await query.AnyAsync();
|
||||
}
|
||||
|
||||
public async Task<List<MenuReadDto_Menu_Table>> GetAllMenuAsync(string like, long? parentId)
|
||||
{
|
||||
var all = await Orm.Select<Menu>().Where(x => x.DeletedAt == null).ToListAsync();
|
||||
var dict = all.ToDictionary(x => x.Id, x => x.Name);
|
||||
|
||||
var list = all.AsEnumerable();
|
||||
if (parentId.HasValue)
|
||||
list = list.Where(x => x.ParentId == parentId.Value);
|
||||
if (!string.IsNullOrWhiteSpace(like))
|
||||
list = list.Where(x => x.Name.Contains(like) || x.Code.Contains(like));
|
||||
|
||||
return list.OrderBy(x => x.SortOrder).Select(x => ToTable(x, dict)).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<MenuReadDto_Menu_Table>> GetTreeMenuAsync()
|
||||
{
|
||||
var all = await Orm.Select<Menu>().Where(x => x.DeletedAt == null).OrderBy(x => x.SortOrder).ToListAsync();
|
||||
var dict = all.ToDictionary(x => x.Id, x => x.Name);
|
||||
return BuildTree(all, 0, dict);
|
||||
}
|
||||
|
||||
private static MenuReadDto_Menu_Table ToTable(Menu x, Dictionary<long, string> dict)
|
||||
=> new(x.Id, x.ParentId, x.Code, x.Name, x.IconSvg, x.MenuType.ToString(),
|
||||
x.AssemblyName, x.TypeFullName, dict.GetValueOrDefault(x.ParentId, ""), x.Level,
|
||||
x.IsActive ? "启用" : "禁用", x.SortOrder);
|
||||
|
||||
private static List<MenuReadDto_Menu_Table> BuildTree(List<Menu> all, long parentId, Dictionary<long, string> dict)
|
||||
=> all.Where(x => x.ParentId == parentId).OrderBy(x => x.SortOrder).Select(x =>
|
||||
{
|
||||
var children = BuildTree(all, x.Id, dict);
|
||||
return new MenuReadDto_Menu_Table(
|
||||
x.Id, x.ParentId, x.Code, x.Name, x.IconSvg, x.MenuType.ToString(),
|
||||
x.AssemblyName, x.TypeFullName, dict.GetValueOrDefault(x.ParentId, ""), x.Level,
|
||||
x.IsActive ? "启用" : "禁用", x.SortOrder, false,
|
||||
children.Count > 0 ? children.ToArray() : default);
|
||||
}).ToList();
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Menus;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Menus;
|
||||
|
||||
internal class MenuWriteRepository : F9RepositoryBase, IMenuWriteRepository
|
||||
{
|
||||
public MenuWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<Menu> GetByIdAsync(long id)
|
||||
=> await Orm.Select<Menu>().Where(x => x.Id == id ).FirstAsync();
|
||||
|
||||
public async Task<bool> AddAsync(Menu menu)
|
||||
{
|
||||
menu.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(menu).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(Menu menu)
|
||||
{
|
||||
menu.UpdatedAt = DateTime.Now;
|
||||
return await Orm.Update<Menu>().SetSource(menu).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> SoftDeleteAsync(long id)
|
||||
=> await Orm.Update<Menu>()
|
||||
.Set(x => x.IsDeleted, true)
|
||||
.Where(x => x.Id == id )
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> ActiveOrDeactive(long id, bool active)
|
||||
=> await Orm.Update<Menu>()
|
||||
.Set(x => x.IsActive, active)
|
||||
.Set(x => x.UpdatedAt, DateTime.Now)
|
||||
.Where(x => x.Id == id)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Enums.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Permissions;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Permissions;
|
||||
|
||||
internal class PermissionWriteRepository : F9RepositoryBase, IPermissionWriteRepository
|
||||
{
|
||||
public PermissionWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<Permission> GetByIdAsync(Permission_Type_Enum permissionType, Resource_Type_Enum resourceType, long resourceId)
|
||||
=> await Orm.Select<Permission>()
|
||||
.Where(x => x.PermissionType == permissionType && x.ResourceType == resourceType && x.ResourceId == resourceId )
|
||||
.FirstAsync();
|
||||
|
||||
public async Task<bool> AddRangeAsync(Permission[] entites)
|
||||
=> await Orm.Insert(entites).ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> DeleteByResourceIdAndTypeAsync(Permission_Type_Enum permissionType, Resource_Type_Enum resourceType, long resourceId)
|
||||
=> await Orm.Delete<Permission>()
|
||||
.Where(x => x.PermissionType == permissionType && x.ResourceType == resourceType && x.ResourceId == resourceId)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> DeleteByResourceIdAsync(long resourceId)
|
||||
=> await Orm.Delete<Permission>().Where(x => x.ResourceId == resourceId).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Roles;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Roles;
|
||||
|
||||
internal class RolePermissionWriteRepository : F9RepositoryBase, IRolePermissionWriteRepository
|
||||
{
|
||||
public RolePermissionWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<RolePermission> GetByIdAsync(long id)
|
||||
=> await Orm.Select<RolePermission>().Where(x => x.Id == id).FirstAsync();
|
||||
|
||||
public async Task<bool> AssignAsync(RolePermission rolePermission)
|
||||
{
|
||||
rolePermission.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(rolePermission).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveAsync(long id)
|
||||
=> await Orm.Delete<RolePermission>().Where(x => x.Id == id).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Dtos.SYS.RBAC.Roles;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.RBAC.Roles;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Enums.SYS.RBAC;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Roles;
|
||||
|
||||
internal class RoleReadRepository : F9RepositoryBase, IRoleReadRepository
|
||||
{
|
||||
public RoleReadRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<RoleReadDto_Edit> GetByIdAsync(long id)
|
||||
=> await Orm.Select<Role>()
|
||||
.Where(x => x.Id == id && x.DeletedAt == null)
|
||||
.FirstAsync(x => new RoleReadDto_Edit(x.Id, x.Name, x.Code, x.SortOrder, x.Description));
|
||||
|
||||
public async Task<List<RoleReadDto_View>> GetAllAsync()
|
||||
{
|
||||
var roles = await Orm.Select<Role>()
|
||||
.Where(x => x.DeletedAt == null)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ToListAsync(x => new { x.Id, x.Name, x.Code, x.SortOrder, x.IsActive });
|
||||
|
||||
var roleIds = roles.Select(x => x.Id).ToList();
|
||||
var permCounts = await GetPermissionCountsAsync(roleIds);
|
||||
|
||||
return roles.Select(x =>
|
||||
{
|
||||
var c = permCounts.GetValueOrDefault(x.Id);
|
||||
return new RoleReadDto_View(x.Id, x.Name, x.Code, x.SortOrder, c.Menu, c.Report, c.Dep, x.IsActive);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<RoleReadDto_Cache>> GetAllForCacheAsync()
|
||||
=> await Orm.Select<Role>()
|
||||
.Where(x => x.DeletedAt == null)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ToListAsync(x => new RoleReadDto_Cache(x.Id, x.Name, x.Code, x.SortOrder));
|
||||
|
||||
public async Task<List<RolePermissionReadDto_View>> GetRolePermissionsByResourceType(long roleId, Resource_Type_Enum resourceType)
|
||||
=> await Orm.Select<RolePermission, Permission>()
|
||||
.InnerJoin((rp, p) => rp.PermissionId == p.Id && p.DeletedAt == null)
|
||||
.Where((rp, p) => rp.RoleId == roleId && p.ResourceType == resourceType)
|
||||
.ToListAsync((rp, p) => new RolePermissionReadDto_View(rp.Id, rp.RoleId, rp.PermissionId, p.ResourceId, p.PermissionType));
|
||||
|
||||
private async Task<Dictionary<long, (int Menu, int Report, int Dep)>> GetPermissionCountsAsync(List<long> roleIds)
|
||||
{
|
||||
var result = new Dictionary<long, (int, int, int)>();
|
||||
if (roleIds.Count == 0) return result;
|
||||
|
||||
var perms = await Orm.Select<RolePermission, Permission>()
|
||||
.InnerJoin((rp, p) => rp.PermissionId == p.Id && p.DeletedAt == null)
|
||||
.Where((rp, p) => roleIds.Contains(rp.RoleId))
|
||||
.ToListAsync((rp, p) => new { rp.RoleId, p.ResourceType });
|
||||
|
||||
foreach (var g in perms.GroupBy(x => x.RoleId))
|
||||
{
|
||||
result[g.Key] = (
|
||||
g.Count(x => x.ResourceType == Resource_Type_Enum.菜单),
|
||||
g.Count(x => x.ResourceType == Resource_Type_Enum.报表),
|
||||
g.Count(x => x.ResourceType == Resource_Type_Enum.部门));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Roles;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Roles;
|
||||
|
||||
internal class RoleWriteRepository : F9RepositoryBase, IRoleWriteRepository
|
||||
{
|
||||
public RoleWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<Role> GetByIdAsync(long id)
|
||||
=> await Orm.Select<Role>().Where(x => x.Id == id ).FirstAsync();
|
||||
|
||||
public async Task<bool> AddAsync(Role role)
|
||||
{
|
||||
role.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(role).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(Role role)
|
||||
{
|
||||
role.UpdatedAt = DateTime.Now;
|
||||
return await Orm.Update<Role>().SetSource(role).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> SoftDeleteAsync(long id)
|
||||
=> await Orm.Update<Role>()
|
||||
.Set(x => x.IsDeleted, true)
|
||||
.Where(x => x.Id == id )
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> ActiveOrDeactive(long id, bool active)
|
||||
=> await Orm.Update<Role>()
|
||||
.Set(x => x.IsActive, active)
|
||||
.Set(x => x.UpdatedAt, DateTime.Now)
|
||||
.Where(x => x.Id == id)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Application.Dtos.Common;
|
||||
using F9WebAiTest.Application.Dtos.SYS.RBAC.Users;
|
||||
using F9WebAiTest.Application.Interfaces.Repositories.SYS.RBAC.Users;
|
||||
using F9WebAiTest.Domain.Entities.SYS.Departments;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Users;
|
||||
|
||||
internal class UserReadRepository : F9RepositoryBase, IUserReadRepository
|
||||
{
|
||||
public UserReadRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<UserReadDto_Login> GetByLoginNameAsync(string loginName)
|
||||
=> await Orm.Select<User>()
|
||||
.Where(x => x.LoginName == loginName && x.DeletedAt == null)
|
||||
.FirstAsync(x => new UserReadDto_Login(x.Id, x.UserName, x.LoginName, x.PasswordHash, x.DepartmentId));
|
||||
|
||||
public async Task<UserReadDto_Login> GetByPhoneAsync(string phone)
|
||||
=> await Orm.Select<User>()
|
||||
.Where(x => x.PhoneNumber == phone && x.DeletedAt == null)
|
||||
.FirstAsync(x => new UserReadDto_Login(x.Id, x.UserName, x.LoginName, x.PasswordHash, x.DepartmentId));
|
||||
|
||||
public async Task<UserReadDto_Edit> GetByIdAsync(long id)
|
||||
=> await Orm.Select<User>()
|
||||
.Where(x => x.Id == id && x.DeletedAt == null)
|
||||
.FirstAsync(x => new UserReadDto_Edit(x.Id, x.UserName, x.PhoneNumber, x.LoginName, x.DepartmentId, x.SortOrder));
|
||||
|
||||
public async Task<List<UserReadDto_Edit>> GetByIdsAsync(List<long> ids)
|
||||
=> await Orm.Select<User>()
|
||||
.Where(x => ids.Contains(x.Id) && x.DeletedAt == null)
|
||||
.ToListAsync(x => new UserReadDto_Edit(x.Id, x.UserName, x.PhoneNumber, x.LoginName, x.DepartmentId, x.SortOrder));
|
||||
|
||||
public async Task<PagedResult<UserReadDto_View>> GetPagedAsync(long departmentId, string like, int page = 1, int pageSize = 10)
|
||||
{
|
||||
var query = Orm.Select<User, Department>()
|
||||
.LeftJoin((u, d) => u.DepartmentId == d.Id)
|
||||
.Where((u, d) => u.DeletedAt == null);
|
||||
|
||||
if (departmentId > 0)
|
||||
query = query.Where((u, d) => u.DepartmentId == departmentId);
|
||||
if (!string.IsNullOrWhiteSpace(like))
|
||||
query = query.Where((u, d) => u.UserName.Contains(like) || u.LoginName.Contains(like) || u.PhoneNumber.Contains(like));
|
||||
|
||||
var total = await query.CountAsync();
|
||||
|
||||
var list = await query
|
||||
.OrderBy((u, d) => u.SortOrder)
|
||||
.Page(page, pageSize)
|
||||
.ToListAsync((u, d) => new
|
||||
{
|
||||
u.Id, u.UserName, u.PhoneNumber, u.LoginName,
|
||||
DepartmentName = d.Name,
|
||||
u.LastLoginAt, u.SortOrder, u.IsActive
|
||||
});
|
||||
|
||||
// 批量查角色名,避免 N+1
|
||||
var userIds = list.Select(x => x.Id).ToList();
|
||||
var roleDict = await GetRoleNameDictAsync(userIds);
|
||||
|
||||
var items = list.Select(x => new UserReadDto_View(
|
||||
x.Id, x.UserName, x.PhoneNumber, x.LoginName,
|
||||
x.DepartmentName,
|
||||
roleDict.TryGetValue(x.Id, out var roleName) ? roleName : "",
|
||||
x.LastLoginAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
|
||||
x.SortOrder, x.IsActive)).ToList();
|
||||
|
||||
return new PagedResult<UserReadDto_View>(items, page, pageSize, total);
|
||||
}
|
||||
|
||||
public async Task<List<UserRoleReadDto_Cache>> GetUserRolesByUserId(long userId)
|
||||
=> await Orm.Select<UserRole, Role>()
|
||||
.InnerJoin((x, y) => x.RoleId == y.Id && y.DeletedAt == null)
|
||||
.Where((x, y) => x.UserId == userId)
|
||||
.ToListAsync((x, y) => new UserRoleReadDto_Cache(x.Id, x.UserId, x.RoleId, y.Name));
|
||||
|
||||
public async Task<List<UserReadDto_Edit>> GetByRoleIdsAsync(List<long> roleIds)
|
||||
=> await Orm.Select<User, UserRole, Role>()
|
||||
.InnerJoin((u, x, y) => u.Id == x.UserId)
|
||||
.InnerJoin((u, x, y) => x.RoleId == y.Id && y.DeletedAt == null)
|
||||
.Where((u, x, y) => roleIds.Contains(x.RoleId) && u.DeletedAt == null)
|
||||
.ToListAsync((u, x, y) => new UserReadDto_Edit(u.Id, u.UserName, u.PhoneNumber, u.LoginName, u.DepartmentId, u.SortOrder));
|
||||
|
||||
private async Task<Dictionary<long, string>> GetRoleNameDictAsync(List<long> userIds)
|
||||
{
|
||||
if (userIds.Count == 0) return new Dictionary<long, string>();
|
||||
|
||||
var list = await Orm.Select<UserRole, Role>()
|
||||
.InnerJoin((x, y) => x.RoleId == y.Id && y.DeletedAt == null)
|
||||
.Where((x, y) => userIds.Contains(x.UserId))
|
||||
.ToListAsync((x, y) => new { x.UserId, y.Name });
|
||||
|
||||
return list.GroupBy(x => x.UserId)
|
||||
.ToDictionary(g => g.Key, g => string.Join(",", g.Select(v => v.Name)));
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Users;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Users;
|
||||
|
||||
internal class UserRoleWriteRepository : F9RepositoryBase, IUserRoleWriteRepository
|
||||
{
|
||||
public UserRoleWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<bool> AssignAsync(UserRole userRole)
|
||||
{
|
||||
userRole.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(userRole).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveAsync(long userid, long roleId)
|
||||
=> await Orm.Delete<UserRole>()
|
||||
.Where(x => x.UserId == userid && x.RoleId == roleId)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Entities.SYS.RBAC;
|
||||
using F9WebAiTest.Domain.Interfaces.SYS.RBAC.Users;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.FreeSqls.Repositories.SYS.RBAC.Users;
|
||||
|
||||
internal class UserWriteRepository : F9RepositoryBase, IUserWriteRepository
|
||||
{
|
||||
public UserWriteRepository(IFreeSql fsql) : base(fsql) { }
|
||||
|
||||
public async Task<User> GetByIdAsync(long id)
|
||||
=> await Orm.Select<User>().Where(x => x.Id == id ).FirstAsync();
|
||||
|
||||
public async Task<bool> AddAsync(User user)
|
||||
{
|
||||
user.CreatedAt = DateTime.Now;
|
||||
return await Orm.Insert(user).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(User user)
|
||||
{
|
||||
user.UpdatedAt = DateTime.Now;
|
||||
return await Orm.Update<User>().SetSource(user).ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> SoftDeleteAsync(long id)
|
||||
=> await Orm.Update<User>()
|
||||
.Set(x => x.IsDeleted, true)
|
||||
.Where(x => x.Id == id )
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> ActiveOrDeactive(long id, bool active)
|
||||
=> await Orm.Update<User>()
|
||||
.Set(x => x.IsActive, active)
|
||||
.Set(x => x.UpdatedAt, DateTime.Now)
|
||||
.Where(x => x.Id == id)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> UpdatePassworkdAsync(long id, string passwordHash)
|
||||
=> await Orm.Update<User>()
|
||||
.Set(x => x.PasswordHash, passwordHash)
|
||||
.Set(x => x.UpdatedAt, DateTime.Now)
|
||||
.Where(x => x.Id == id)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
|
||||
public async Task<bool> UpdateLastLoginAtAsync(long id, DateTime dt)
|
||||
=> await Orm.Update<User>()
|
||||
.Set(x => x.LastLoginAt, dt)
|
||||
.Where(x => x.Id == id)
|
||||
.ExecuteAffrowsAsync() > 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using F9WebAiTest.Application.Interfaces.Security;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.InterfaceImplements.Security;
|
||||
|
||||
/// <summary>密码哈希服务实现(BCrypt)</summary>
|
||||
public class PasswordHasher : IPasswordHasher
|
||||
{
|
||||
public string Hash256(string input)
|
||||
{
|
||||
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input ?? ""));
|
||||
return Convert.ToHexString(bytes).ToLowerInvariant();
|
||||
}
|
||||
|
||||
public string GetMd5Hash(string input)
|
||||
{
|
||||
var bytes = MD5.HashData(Encoding.UTF8.GetBytes(input ?? ""));
|
||||
return Convert.ToHexString(bytes).ToLowerInvariant();
|
||||
}
|
||||
|
||||
public string HashPassword(string password)
|
||||
=> BCrypt.Net.BCrypt.HashPassword(password);
|
||||
|
||||
public bool VerifyPassword(string password, string hashedPassword)
|
||||
=> BCrypt.Net.BCrypt.Verify(password, hashedPassword);
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using F9WebAiTest.Application.Interfaces.SnowflakeID;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.InterfaceImplements.SnowflakeID;
|
||||
|
||||
/// <summary>
|
||||
/// 雪花 ID 生成器(自实现,41 时间戳 + 10 worker + 12 序列)。
|
||||
/// 参考 BBIT.F9.Infrastructure.InterfaceImplements.SnowflakeID.SnowflakeIdGenerator
|
||||
/// </summary>
|
||||
public class SnowflakeIdGenerator : ISnowflakeIdGenerator
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly long _workerId;
|
||||
private readonly long _epoch;
|
||||
private long _lastTimestamp = -1L;
|
||||
private long _sequence;
|
||||
|
||||
private const long WorkerIdBits = 10L;
|
||||
private const long SequenceBits = 12L;
|
||||
private const long WorkerIdShift = SequenceBits;
|
||||
private const long TimestampShift = SequenceBits + WorkerIdBits;
|
||||
private const long SequenceMask = -1L ^ (-1L << (int)SequenceBits);
|
||||
|
||||
public SnowflakeIdGenerator()
|
||||
{
|
||||
_workerId = 1; // 单节点默认 workerId = 1
|
||||
_epoch = new DateTimeOffset(2025, 10, 1, 0, 0, 0, TimeSpan.Zero).ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
public long CreateId()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - _epoch;
|
||||
if (timestamp < _lastTimestamp)
|
||||
throw new InvalidOperationException("时钟回拨,拒绝生成 ID");
|
||||
|
||||
if (timestamp == _lastTimestamp)
|
||||
{
|
||||
_sequence = (_sequence + 1) & SequenceMask;
|
||||
if (_sequence == 0)
|
||||
{
|
||||
// 序列溢出,等待下一毫秒
|
||||
while (timestamp <= _lastTimestamp)
|
||||
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - _epoch;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sequence = 0;
|
||||
}
|
||||
|
||||
_lastTimestamp = timestamp;
|
||||
|
||||
return (timestamp << (int)TimestampShift)
|
||||
| (_workerId << (int)WorkerIdShift)
|
||||
| _sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Linq.Expressions;
|
||||
using FreeSql;
|
||||
using F9WebAiTest.Domain.Common;
|
||||
|
||||
namespace F9WebAiTest.Infrastructure.Repositories;
|
||||
|
||||
/// <summary>通用仓储实现(基于 FreeSql)</summary>
|
||||
public class Repository<T> : IRepository<T> where T : class, IEntity
|
||||
{
|
||||
protected readonly IFreeSql _fsql;
|
||||
|
||||
public Repository(IFreeSql fsql)
|
||||
{
|
||||
_fsql = fsql;
|
||||
}
|
||||
|
||||
public virtual async Task<T> GetByIdAsync(long id)
|
||||
=> await _fsql.Select<T>().Where(e => e.Id == id).FirstAsync();
|
||||
|
||||
public virtual async Task<List<T>> GetAllAsync()
|
||||
=> await _fsql.Select<T>().ToListAsync();
|
||||
|
||||
public virtual async Task<List<T>> FindAsync(Expression<Func<T, bool>> predicate)
|
||||
=> await _fsql.Select<T>().Where(predicate).ToListAsync();
|
||||
|
||||
public virtual async Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate)
|
||||
=> await _fsql.Select<T>().Where(predicate).FirstAsync();
|
||||
|
||||
public virtual async Task<bool> AnyAsync(Expression<Func<T, bool>> predicate)
|
||||
=> await _fsql.Select<T>().Where(predicate).AnyAsync();
|
||||
|
||||
public virtual async Task<long> CountAsync(Expression<Func<T, bool>> predicate)
|
||||
=> await _fsql.Select<T>().Where(predicate).CountAsync();
|
||||
|
||||
public virtual async Task<T> AddAsync(T entity)
|
||||
{
|
||||
entity.CreatedAt = DateTime.Now;
|
||||
await _fsql.Insert(entity).ExecuteAffrowsAsync();
|
||||
// FreeSql 在 SQLite 下不回填自增主键,手动用 last_insert_rowid 获取并回填
|
||||
if (entity.Id == 0)
|
||||
{
|
||||
entity.Id = Convert.ToInt64(await _fsql.Ado.ExecuteScalarAsync("SELECT last_insert_rowid();"));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public virtual async Task AddRangeAsync(IEnumerable<T> entities)
|
||||
{
|
||||
foreach (var e in entities) e.CreatedAt = DateTime.Now;
|
||||
await _fsql.Insert(entities).ExecuteAffrowsAsync();
|
||||
}
|
||||
|
||||
public virtual async Task UpdateAsync(T entity)
|
||||
{
|
||||
entity.UpdatedAt = DateTime.Now;
|
||||
await _fsql.Update<T>().SetSource(entity).ExecuteAffrowsAsync();
|
||||
}
|
||||
|
||||
public virtual async Task DeleteAsync(T entity)
|
||||
{
|
||||
entity.IsDeleted = true;
|
||||
entity.UpdatedAt = DateTime.Now;
|
||||
await _fsql.Update<T>().SetSource(entity).ExecuteAffrowsAsync();
|
||||
}
|
||||
|
||||
public virtual async Task DeleteRangeAsync(IEnumerable<T> entities)
|
||||
{
|
||||
foreach (var e in entities)
|
||||
{
|
||||
e.IsDeleted = true;
|
||||
e.UpdatedAt = DateTime.Now;
|
||||
}
|
||||
await _fsql.Update<T>().SetSource(entities).ExecuteAffrowsAsync();
|
||||
}
|
||||
|
||||
public virtual async Task HardDeleteAsync(T entity)
|
||||
=> await _fsql.Delete<T>().Where(e => e.Id == entity.Id).ExecuteAffrowsAsync();
|
||||
|
||||
public virtual async Task HardDeleteRangeAsync(IEnumerable<T> entities)
|
||||
{
|
||||
var ids = entities.Select(e => e.Id).ToList();
|
||||
if (ids.Count > 0)
|
||||
await _fsql.Delete<T>().Where(e => ids.Contains(e.Id)).ExecuteAffrowsAsync();
|
||||
}
|
||||
|
||||
public virtual Task<int> SaveChangesAsync()
|
||||
=> Task.FromResult(1); // FreeSql 即时提交,无 SaveChanges 语义,保持接口兼容
|
||||
}
|
||||
Reference in New Issue
Block a user