修复涵盖 6 大类问题: 1. api-gateway - 路径前缀剥离 /api 而非 /api/v1,保留下游 /v1/ controller 前缀 - JWKS URL 默认值修复 - publicPaths 白名单对齐 /v1/iam/* 2. iam - iam.module.ts exports 补充 PermissionCacheService 和 IamRepository - main.ts resolveProtoPath() 多路径探测 proto 文件 3. core-edu - app.module.ts AuthMiddleware 全局注册 4. BFF 层 GraphQL 端点(teacher-bff / parent-bff / student-bff) - teacher-bff: mock dataScope OWN→SELF 对齐 GraphQL enum;WHATWG Request header .get() 提取 - parent-bff: handleNodeRequestAndResponse 不存在 → 直接 yoga(req,res);WHATWG Request header .get() 提取 - student-bff: auth.resolver 移除 ActionState 信封返回扁平对象;WHATWG Request header .get() 提取 5. ai - Kafka 事务降级 + 10s 超时 - gRPC 拦截器降级 - dev mode 禁用事务模式 6. 前端 + 共享包 - teacher-portal: MF 插件条件实例化 + transpilePackages + extensionAlias - ui-components: error-boundary.tsx 添加 use client - ui-tokens: tailwind-theme.css 移除 @layer base - shared-ts: 导出从源码改为 dist 编译产物;OutboxModule global:true 7. infra - .gitignore 补充 keys/ *.pem *.key secrets/ 排除规则 - infra/init-sql/02-all-services-schema.sql 36 张表 DDL 验证结果: - TS typecheck: 19 个 workspace 项目全部通过 - Go vet + Ruff: 通过 - 15 服务全部启动成功 - 3 个 BFF GraphQL 端点 + 4 个前端页面全部 200 - Gateway → iam → core-edu 端到端链路验证通过 AI identity: trae-main(集成测试修复会话)
91 lines
2.2 KiB
TypeScript
91 lines
2.2 KiB
TypeScript
"use client";
|
||
|
||
import { Component, type ErrorInfo, type ReactNode } from "react";
|
||
|
||
/**
|
||
* ErrorBoundary - React 渲染异常兜底(fallback UI)
|
||
*
|
||
* 用途:捕获子组件树渲染异常,展示降级 UI,避免整页白屏。
|
||
*
|
||
* @example
|
||
* <ErrorBoundary fallback={<ErrorFallback />}>
|
||
* <Dashboard />
|
||
* </ErrorBoundary>
|
||
*/
|
||
|
||
export interface ErrorBoundaryProps {
|
||
children: ReactNode;
|
||
/** 自定义降级 UI;未提供时使用默认 fallback */
|
||
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
||
/** 错误回调(上报 / 日志) */
|
||
onError?: (error: Error, info: ErrorInfo) => void;
|
||
/** 重置回调(用于外部触发重试) */
|
||
onReset?: () => void;
|
||
}
|
||
|
||
export interface ErrorBoundaryState {
|
||
error: Error | null;
|
||
}
|
||
|
||
export class ErrorBoundary extends Component<
|
||
ErrorBoundaryProps,
|
||
ErrorBoundaryState
|
||
> {
|
||
override state: ErrorBoundaryState = { error: null };
|
||
|
||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||
return { error };
|
||
}
|
||
|
||
override componentDidCatch(error: Error, info: ErrorInfo): void {
|
||
this.props.onError?.(error, info);
|
||
}
|
||
|
||
reset = (): void => {
|
||
this.props.onReset?.();
|
||
this.setState({ error: null });
|
||
};
|
||
|
||
override render(): ReactNode {
|
||
const { error } = this.state;
|
||
const { children, fallback } = this.props;
|
||
|
||
if (error) {
|
||
if (typeof fallback === "function") {
|
||
return fallback(error, this.reset);
|
||
}
|
||
if (fallback !== undefined) {
|
||
return fallback;
|
||
}
|
||
return <DefaultErrorFallback error={error} onReset={this.reset} />;
|
||
}
|
||
|
||
return children;
|
||
}
|
||
}
|
||
|
||
function DefaultErrorFallback({
|
||
error,
|
||
onReset,
|
||
}: {
|
||
error: Error;
|
||
onReset: () => void;
|
||
}): ReactNode {
|
||
return (
|
||
<div
|
||
role="alert"
|
||
className="flex min-h-[200px] flex-col items-center justify-center gap-4 p-8"
|
||
>
|
||
<h2 className="text-lg font-semibold">出错了</h2>
|
||
<p className="text-sm text-muted-foreground">{error.message}</p>
|
||
<button
|
||
type="button"
|
||
onClick={onReset}
|
||
className="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90"
|
||
>
|
||
重试
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|