using Microsoft.EntityFrameworkCore; using Tiku.Application.Security; using Tiku.Domain.Operations; using Tiku.Domain.Platform; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.Bootstrap; public sealed class BuiltinBackofficeCatalogSeeder(TikuDbContext dbContext) { private static readonly BuiltinFeature[] Features = [ new(SaasFeatureCatalog.CoreBackoffice, "后台基础能力", "core", true, 0), new(SaasFeatureCatalog.PrivateQuestionBank, "私有题库", "question_bank", false, 10), new(SaasFeatureCatalog.Practice, "题库练习", "learning", false, 20), new(SaasFeatureCatalog.Assignment, "作业", "learning", false, 30), new(SaasFeatureCatalog.Exam, "考试", "learning", false, 40), new(SaasFeatureCatalog.Vocabulary, "词汇", "content", false, 50), new(SaasFeatureCatalog.Handbook, "知识手册", "content", false, 60), new(SaasFeatureCatalog.Video, "视频", "content", false, 70), new(SaasFeatureCatalog.Scoreline, "分数线", "content", false, 80), new(SaasFeatureCatalog.SiteContent, "站点运营内容", "marketing", false, 90), new(SaasFeatureCatalog.StudentManagement, "学生管理", "student", false, 100), new(SaasFeatureCatalog.StudentStore, "学生商城", "commerce", false, 110), new(SaasFeatureCatalog.Crm, "学生跟进", "crm", false, 120), new(SaasFeatureCatalog.ReferralCommission, "推广与分佣", "growth", false, 130), new(SaasFeatureCatalog.TeacherAi, "教师 AI 助手", "ai", false, 140) ]; private static readonly BuiltinPermissionModule[] PermissionModules = [ new("tenant_dashboard", "租户总览", BackendPermissionArea.Tenant, null, 10), new("tenant_staff", "员工与角色", BackendPermissionArea.Tenant, null, 20), new("tenant_settings", "租户设置", BackendPermissionArea.Tenant, null, 30), new("tenant_provider", "外部服务", BackendPermissionArea.Tenant, null, 40), new("tenant_job", "后台任务", BackendPermissionArea.Tenant, null, 50), new("tenant_billing", "SaaS 账务", BackendPermissionArea.Tenant, null, 60), new("tenant_student", "学生管理", BackendPermissionArea.Tenant, SaasFeatureCatalog.StudentManagement, 100), new("tenant_question_bank", "私有题库", BackendPermissionArea.Tenant, SaasFeatureCatalog.PrivateQuestionBank, 110), new("tenant_vocabulary", "词汇", BackendPermissionArea.Tenant, SaasFeatureCatalog.Vocabulary, 120), new("tenant_handbook", "知识手册", BackendPermissionArea.Tenant, SaasFeatureCatalog.Handbook, 130), new("tenant_video", "视频", BackendPermissionArea.Tenant, SaasFeatureCatalog.Video, 140), new("tenant_scoreline", "分数线", BackendPermissionArea.Tenant, SaasFeatureCatalog.Scoreline, 150), new("tenant_site_content", "站点运营内容", BackendPermissionArea.Tenant, SaasFeatureCatalog.SiteContent, 160), new("tenant_commerce", "学生商城", BackendPermissionArea.Tenant, SaasFeatureCatalog.StudentStore, 170), new("tenant_crm", "学生跟进", BackendPermissionArea.Tenant, SaasFeatureCatalog.Crm, 180), new("tenant_commission", "推广分佣", BackendPermissionArea.Tenant, SaasFeatureCatalog.ReferralCommission, 190), new("platform_dashboard", "平台总览", BackendPermissionArea.Platform, null, 200), new("platform_tenant", "平台租户", BackendPermissionArea.Platform, null, 210), new("platform_staff", "平台员工", BackendPermissionArea.Platform, null, 220), new("platform_content", "公共题库", BackendPermissionArea.Platform, null, 230), new("platform_audit", "平台审计", BackendPermissionArea.Platform, null, 240), new("platform_billing", "平台 SaaS 商城", BackendPermissionArea.Platform, null, 250), new("platform_crm", "CRM 接入代管", BackendPermissionArea.Platform, null, 260), new("platform_sms", "短信服务", BackendPermissionArea.Platform, null, 270), new("platform_payment", "支付设置", BackendPermissionArea.Platform, null, 280), new("commerce", "交易运营", BackendPermissionArea.Both, SaasFeatureCatalog.StudentStore, 300) ]; private static readonly BuiltinPermission[] Permissions = [ new(BackendPermissions.TenantDashboardView, "租户总览", BackendPermissionArea.Tenant, "tenant_dashboard"), new(BackendPermissions.TenantStaffManage, "租户员工管理", BackendPermissionArea.Tenant, "tenant_staff"), new(BackendPermissions.TenantRoleManage, "租户角色权限管理", BackendPermissionArea.Tenant, "tenant_staff"), new(BackendPermissions.TenantStudentManage, "学生与班级管理", BackendPermissionArea.Tenant, "tenant_student"), new(BackendPermissions.TenantContentManage, "租户题库管理", BackendPermissionArea.Tenant, "tenant_question_bank"), new(BackendPermissions.TenantVocabularyManage, "租户词汇管理", BackendPermissionArea.Tenant, "tenant_vocabulary"), new(BackendPermissions.TenantHandbookManage, "租户手册管理", BackendPermissionArea.Tenant, "tenant_handbook"), new(BackendPermissions.TenantVideoManage, "租户视频管理", BackendPermissionArea.Tenant, "tenant_video"), new(BackendPermissions.TenantScorelineManage, "租户分数线管理", BackendPermissionArea.Tenant, "tenant_scoreline"), new(BackendPermissions.TenantSiteContentManage, "租户运营内容管理", BackendPermissionArea.Tenant, "tenant_site_content"), new(BackendPermissions.TenantSettingsManage, "租户设置管理", BackendPermissionArea.Tenant, "tenant_settings"), new(BackendPermissions.TenantProviderManage, "租户外部服务配置", BackendPermissionArea.Tenant, "tenant_provider"), new(BackendPermissions.TenantCommerceOperate, "租户交易运营", BackendPermissionArea.Tenant, "tenant_commerce"), new(BackendPermissions.TenantCrmManage, "租户客户管理", BackendPermissionArea.Tenant, "tenant_crm"), new(BackendPermissions.TenantCommissionManage, "租户佣金管理", BackendPermissionArea.Tenant, "tenant_commission"), new(BackendPermissions.TenantJobManage, "租户任务管理", BackendPermissionArea.Tenant, "tenant_job"), new(BackendPermissions.TenantBillingManage, "租户 SaaS 账务", BackendPermissionArea.Tenant, "tenant_billing"), new(BackendPermissions.PlatformDashboardView, "平台总览", BackendPermissionArea.Platform, "platform_dashboard"), new(BackendPermissions.PlatformTenantManage, "平台租户管理", BackendPermissionArea.Platform, "platform_tenant"), new(BackendPermissions.PlatformStaffManage, "平台员工管理", BackendPermissionArea.Platform, "platform_staff"), new(BackendPermissions.PlatformRoleManage, "平台角色权限管理", BackendPermissionArea.Platform, "platform_staff"), new(BackendPermissions.PlatformQuestionBankManage, "平台公共题库运营", BackendPermissionArea.Platform, "platform_content"), new(BackendPermissions.PlatformAuditView, "平台审计查询", BackendPermissionArea.Platform, "platform_audit"), new(BackendPermissions.PlatformBillingNotification, "平台催缴通知", BackendPermissionArea.Platform, "platform_billing"), new(BackendPermissions.PlatformSaasCatalogManage, "SaaS 商品管理", BackendPermissionArea.Platform, "platform_billing"), new(BackendPermissions.PlatformSaasBillingManage, "SaaS 交易管理", BackendPermissionArea.Platform, "platform_billing"), new(BackendPermissions.PlatformCrmRead, "平台 CRM 查询", BackendPermissionArea.Platform, "platform_crm"), new(BackendPermissions.PlatformCrmWrite, "平台 CRM 管理", BackendPermissionArea.Platform, "platform_crm"), new(BackendPermissions.PlatformSmsRead, "平台短信查询", BackendPermissionArea.Platform, "platform_sms"), new(BackendPermissions.PlatformSmsWrite, "平台短信管理", BackendPermissionArea.Platform, "platform_sms"), new(BackendPermissions.PlatformPaymentRead, "平台支付查询", BackendPermissionArea.Platform, "platform_payment"), new(BackendPermissions.PlatformPaymentWrite, "平台支付管理", BackendPermissionArea.Platform, "platform_payment"), new("commerce:refund:approve", "退款审核", BackendPermissionArea.Both, "commerce"), new("commerce:reconciliation:manage", "对账管理", BackendPermissionArea.Both, "commerce"), new("commerce:adjustment:manage", "调账管理", BackendPermissionArea.Both, "commerce") ]; private static readonly BuiltinMenu[] Menus = [ new("tenant.dashboard", null, "租户总览", BackendPermissionArea.Tenant, "/tenant/dashboard", "tenant:dashboard:view", 10), new("tenant.staff", null, "员工与权限", BackendPermissionArea.Tenant, "/tenant/staff", "tenant:staff:manage", 20), new("tenant.students", null, "班级与学生", BackendPermissionArea.Tenant, "/tenant/students", "tenant:student:manage", 30), new("tenant.question-bank", null, "私有题库", BackendPermissionArea.Tenant, "/tenant/question-bank", BackendPermissions.TenantContentManage, 40), new("tenant.vocabulary", null, "词汇", BackendPermissionArea.Tenant, "/tenant/vocabulary", BackendPermissions.TenantVocabularyManage, 50), new("tenant.handbook", null, "知识手册", BackendPermissionArea.Tenant, "/tenant/handbook", BackendPermissions.TenantHandbookManage, 60), new("tenant.video", null, "视频", BackendPermissionArea.Tenant, "/tenant/video", BackendPermissions.TenantVideoManage, 70), new("tenant.scoreline", null, "分数线", BackendPermissionArea.Tenant, "/tenant/scoreline", BackendPermissions.TenantScorelineManage, 80), new("tenant.site-content", null, "运营内容", BackendPermissionArea.Tenant, "/tenant/site-content", BackendPermissions.TenantSiteContentManage, 90), new("tenant.providers", null, "外部服务", BackendPermissionArea.Tenant, "/tenant/providers", BackendPermissions.TenantProviderManage, 100), new("tenant.commerce", null, "交易运营", BackendPermissionArea.Tenant, "/tenant/commerce", BackendPermissions.TenantCommerceOperate, 110), new("tenant.billing", null, "SaaS 账务", BackendPermissionArea.Tenant, "/tenant/billing", BackendPermissions.TenantBillingManage, 120), new("platform.dashboard", null, "平台总览", BackendPermissionArea.Platform, "/platform/dashboard", "platform:dashboard:view", 10), new("platform.tenants", null, "租户管理", BackendPermissionArea.Platform, "/platform/tenants", "platform:tenant:manage", 20), new("platform.staff", null, "平台员工", BackendPermissionArea.Platform, "/platform/staff", "platform:staff:manage", 30), new("platform.content", null, "公共题库", BackendPermissionArea.Platform, "/platform/question-banks", "platform:question-bank:manage", 40), new("platform.audit", null, "平台审计", BackendPermissionArea.Platform, "/platform/audit", "platform:audit:view", 50), new("platform.saas", null, "SaaS 商城", BackendPermissionArea.Platform, "/platform/saas", "platform:saas-catalog:manage", 60), new("platform.crm", null, "CRM 接入", BackendPermissionArea.Platform, "/platform/crm", BackendPermissions.PlatformCrmRead, 70), new("platform.sms", null, "短信服务", BackendPermissionArea.Platform, "/platform/sms", BackendPermissions.PlatformSmsRead, 80), new("platform.payment", null, "支付设置", BackendPermissionArea.Platform, "/platform/payment-settings", BackendPermissions.PlatformPaymentRead, 90) ]; public async Task SeedAsync(CancellationToken cancellationToken = default) { var featureCodes = Features.Select(item => item.Code).ToArray(); var existingFeatureCodes = await dbContext.SaasFeatures.AsNoTracking() .Where(item => featureCodes.Contains(item.Code)) .Select(item => item.Code) .ToHashSetAsync(StringComparer.Ordinal, cancellationToken); var moduleCodes = PermissionModules.Select(item => item.Code).ToArray(); var existingModuleCodes = await dbContext.PermissionModules.AsNoTracking() .Where(item => moduleCodes.Contains(item.Code)) .Select(item => item.Code) .ToHashSetAsync(StringComparer.Ordinal, cancellationToken); var permissionCodes = Permissions.Select(item => item.Code).ToArray(); var existingPermissionCodes = await dbContext.BackendPermissions.AsNoTracking() .Where(item => permissionCodes.Contains(item.Code)) .Select(item => item.Code) .ToHashSetAsync(StringComparer.Ordinal, cancellationToken); var menuCodes = Menus.Select(item => item.Code).ToArray(); var existingMenuCodes = await dbContext.BackendMenus.AsNoTracking() .Where(item => menuCodes.Contains(item.Code)) .Select(item => item.Code) .ToHashSetAsync(StringComparer.Ordinal, cancellationToken); dbContext.SaasFeatures.AddRange(Features .Where(item => !existingFeatureCodes.Contains(item.Code)) .Select(item => new SaasFeature { Code = item.Code, Name = item.Name, Category = item.Category, IsCore = item.IsCore, Status = SaasFeatureStatus.Active, SortOrder = item.SortOrder })); dbContext.PermissionModules.AddRange(PermissionModules .Where(item => !existingModuleCodes.Contains(item.Code)) .Select(item => new PermissionModule { Code = item.Code, Name = item.Name, Area = item.Area, RequiredFeatureCode = item.RequiredFeatureCode, SortOrder = item.SortOrder })); dbContext.BackendPermissions.AddRange(Permissions .Where(item => !existingPermissionCodes.Contains(item.Code)) .Select(item => new BackendPermission { Code = item.Code, Name = item.Name, Area = item.Area, PermissionModuleCode = item.PermissionModuleCode, IsSystem = true })); dbContext.BackendMenus.AddRange(Menus .Where(item => !existingMenuCodes.Contains(item.Code)) .Select(item => new BackendMenu { Code = item.Code, ParentCode = item.ParentCode, Title = item.Title, Area = item.Area, Path = item.Path, PermissionCode = item.PermissionCode, SortOrder = item.SortOrder, IsActive = true })); await dbContext.SaveChangesAsync(cancellationToken); } private sealed record BuiltinFeature(string Code, string Name, string Category, bool IsCore, int SortOrder); private sealed record BuiltinPermissionModule(string Code, string Name, BackendPermissionArea Area, string? RequiredFeatureCode, int SortOrder); private sealed record BuiltinPermission(string Code, string Name, BackendPermissionArea Area, string PermissionModuleCode); private sealed record BuiltinMenu(string Code, string? ParentCode, string Title, BackendPermissionArea Area, string Path, string PermissionCode, int SortOrder); }