admin-migration.component.ts 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import { CommonModule } from '@angular/common';
  2. import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';
  3. import { FormsModule } from '@angular/forms';
  4. import { CheckCircle2, CircleAlert, CloudCog, Database, LoaderCircle, LucideAngularModule } from 'lucide-angular';
  5. import { AppMigrationStatus } from '../admin.models';
  6. import { CloudFunctionsService } from '../cloud-functions.service';
  7. import { LEGACY_ADMIN_COVERAGE, LegacyAdminCoverageStatus } from '../legacy-admin-coverage.generated';
  8. interface MigrationMeta {
  9. classes: number;
  10. cloudFunctions: number;
  11. identity: { company?: unknown; isSuperAdmin: boolean };
  12. }
  13. @Component({
  14. selector: 'app-admin-migration',
  15. standalone: true,
  16. imports: [CommonModule, FormsModule, LucideAngularModule],
  17. templateUrl: './admin-migration.component.html',
  18. styleUrl: './admin-migration.component.scss',
  19. changeDetection: ChangeDetectionStrategy.OnPush,
  20. })
  21. export class AdminMigrationComponent implements OnInit {
  22. readonly meta = signal<MigrationMeta | null>(null);
  23. readonly appStatus = signal<AppMigrationStatus | null>(null);
  24. readonly blockedEntries = computed(() => Object.entries(this.appStatus()?.blocked ?? {}).sort(([left], [right]) => left.localeCompare(right)));
  25. readonly adminSearch = signal('');
  26. readonly adminStatusFilter = signal<'all' | LegacyAdminCoverageStatus>('all');
  27. readonly adminCounts = computed(() => LEGACY_ADMIN_COVERAGE.reduce((counts, entry) => { counts[entry.status] += 1; return counts; }, { implemented: 0, partial: 0, 'blocked-data': 0, 'blocked-infrastructure': 0, pending: 0 } as Record<LegacyAdminCoverageStatus, number>));
  28. readonly filteredAdminCoverage = computed(() => {
  29. const keyword = this.adminSearch().trim().toLocaleLowerCase();
  30. const status = this.adminStatusFilter();
  31. return LEGACY_ADMIN_COVERAGE.filter((entry) => (status === 'all' || entry.status === status) && (!keyword || `${entry.module} ${entry.action} ${entry.reason}`.toLocaleLowerCase().includes(keyword)));
  32. });
  33. readonly error = signal('');
  34. readonly icons = { CheckCircle2, CircleAlert, CloudCog, Database, LoaderCircle };
  35. readonly rewrites = [
  36. ['内容 HTML 规范化', 'cms.content-normalizer', 'implemented'],
  37. ['购物车投影', '/xiaoshu/cms/cart/items', 'blocked'],
  38. ['用户扩展资料', '/xiaoshu/cms/users/extended', 'implemented'],
  39. ['考试班级投影', '/xiaoshu/cms/exams/classes', 'implemented'],
  40. ['互动社区投影', '/xiaoshu/cms/guest/bar', 'implemented'],
  41. ['已支付订单', '/xiaoshu/cms/orders/paid', 'blocked'],
  42. ['商品销售投影', '/xiaoshu/cms/orders/product-sales', 'blocked'],
  43. ['订单分享投影', '/xiaoshu/cms/orders/share', 'blocked'],
  44. ['博客列表', '/xiaoshu/cms/blogs', 'blocked'],
  45. ['统一内容搜索', '/xiaoshu/cms/search', 'implemented'],
  46. ['用户平台资料', '/xiaoshu/cms/users/platform', 'implemented'],
  47. ['用户微信资料', '/xiaoshu/cms/users/wechat', 'implemented'],
  48. ] as const;
  49. private readonly functions = inject(CloudFunctionsService);
  50. adminStatusLabel(status: LegacyAdminCoverageStatus): string {
  51. return ({ implemented: '已实现', partial: '部分实现', 'blocked-data': '数据阻塞', 'blocked-infrastructure': '基础设施阻塞', pending: '待核验' })[status];
  52. }
  53. async ngOnInit(): Promise<void> {
  54. try {
  55. const [meta, appStatus] = await Promise.all([
  56. this.functions.admin<MigrationMeta>('meta'),
  57. this.functions.app<AppMigrationStatus>('migration_status'),
  58. ]);
  59. this.meta.set(meta);
  60. this.appStatus.set(appStatus);
  61. }
  62. catch (error) { this.error.set(error instanceof Error ? error.message : '迁移状态加载失败'); }
  63. }
  64. }