feat: 接入运维真实数据与三角色权限链路
- 新增 model_deploy 与 model_operations 双库查询,支持模型列表、模型详情、单月监控结果和运维工作台真实接口。 - 关闭运维模块生产 Mock 数据,补充缺表/空数据降级、工作台空状态和首条纵向链路测试。 - 按业务团队、模型团队、管理员权限矩阵接入菜单、页面、操作按钮和后端接口权限校验,支持 business_team 角色。 - 更新工作台布局、深色欢迎卡片、全宽页面适配、顶部回退,以及权限分组展示。 - 新增架构实现基线、周目标完成情况和角色权限矩阵初始化 SQL 文档。
This commit is contained in:
@@ -7,12 +7,11 @@ import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle }
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table";
|
||||
import { exportRowsToExcel } from "~/lib/exportExcel";
|
||||
import { usePersistentState } from "~/hooks/use-persistent-state";
|
||||
import { AbnormalBadge, FilterSelect, GradeBadge, OperationsPageHeader } from "./OperationsUi";
|
||||
import { MODEL_CATEGORIES, type ModelCategoryId, type ModelGrade, type ModelRecord } from "./modelData";
|
||||
import { BASE_THRESHOLDS, MONITORING_RULES, RULE_VERSIONS, type MonitoringRule, type ThresholdConfig } from "./governanceData";
|
||||
import { useOperationsData } from "./OperationsDataContext";
|
||||
import { isOperationsActionVisibleForRole, useOperationsRole } from "./operationsRole";
|
||||
import { useCanOperationsAction } from "./operationsRole";
|
||||
|
||||
type CategoryKey = "all" | ModelCategoryId;
|
||||
|
||||
@@ -42,13 +41,12 @@ function NumberField({ label, value, disabled, onChange }: { label: string; valu
|
||||
|
||||
export default function RuleManagementPage() {
|
||||
const { models } = useOperationsData();
|
||||
const operationsRole = useOperationsRole();
|
||||
const canManage = isOperationsActionVisibleForRole(operationsRole, "rules:manage");
|
||||
const canManage = useCanOperationsAction("rules:manage");
|
||||
const initialConfigs: Record<CategoryKey, ThresholdConfig | null> = { all: { ...BASE_THRESHOLDS }, std: null, bai: null, big: null, afd: null };
|
||||
const [category, setCategory] = useState<CategoryKey>("all");
|
||||
const [configs, setConfigs] = usePersistentState("a-card-rule-configs", initialConfigs);
|
||||
const [configs, setConfigs] = useState(initialConfigs);
|
||||
const [simulation, setSimulation] = useState<ThresholdConfig>({ ...BASE_THRESHOLDS });
|
||||
const [versions, setVersions] = usePersistentState("a-card-rule-versions", RULE_VERSIONS);
|
||||
const [versions] = useState(RULE_VERSIONS);
|
||||
const activeCut = configs[category] ?? configs.all ?? BASE_THRESHOLDS;
|
||||
const editable = canManage && (category === "all" || configs[category] !== null);
|
||||
const scope = models.filter((model) => model.status !== "下线" && (category === "all" || model.category === category));
|
||||
@@ -86,13 +84,13 @@ export default function RuleManagementPage() {
|
||||
|
||||
const publishVersion = () => {
|
||||
const version = `V${versions.length + 1}`;
|
||||
setVersions((current) => [{ version, category: category === "all" ? "全部大类" : MODEL_CATEGORIES.find((item) => item.id === category)?.name ?? category, author: "管理员 王芳", createdAt: "2026-08-31 10:30", current: true, note: "手动发布(Mock)" }, ...current.map((item) => ({ ...item, current: false }))]);
|
||||
toast.success(`已模拟发布规则 ${version}`);
|
||||
void version;
|
||||
toast.info("规则发布接口尚未接入");
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="h-full overflow-auto bg-bg p-6">
|
||||
<div className="mx-auto flex max-w-screen-2xl flex-col gap-6 pb-8">
|
||||
<div className="flex w-full flex-col gap-6 pb-8">
|
||||
<OperationsPageHeader title="监控结果等级规则" description={canManage ? "维护规则矩阵、模型大类切点、版本留痕与阈值影响测算。" : "模型团队只读查看当前规则矩阵与版本记录。"} actions={canManage ? <Button onClick={publishVersion}><Save />发布新版本</Button> : undefined} />
|
||||
|
||||
<Card>
|
||||
@@ -113,7 +111,7 @@ export default function RuleManagementPage() {
|
||||
<div className="grid grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)] gap-6">
|
||||
<Card>
|
||||
<CardHeader><CardTitle>版本管理</CardTitle><CardDescription>发布、留痕与一键回滚</CardDescription></CardHeader>
|
||||
<CardContent className="space-y-3">{versions.map((version) => <div className="flex items-center gap-3 rounded-xl border border-border p-4" key={version.version}><b className="text-sm text-foreground">{version.version}</b><span className="rounded-full bg-muted px-2.5 py-1 text-xs">{version.category}</span><span className="min-w-0 flex-1"><small className="block truncate text-xs text-muted-foreground">{version.note}</small><small className="text-3xs text-muted-foreground">{version.author} · {version.createdAt}</small></span>{version.current ? <span className="rounded-full bg-success-soft px-2.5 py-1 text-xs text-success-strong">当前生效</span> : canManage ? <Button variant="outline" size="xs" onClick={() => { setVersions((current) => current.map((item) => ({ ...item, current: item.version === version.version }))); toast.success(`已模拟回滚至 ${version.version}`); }}>一键回滚</Button> : <span className="rounded-full bg-muted px-2.5 py-1 text-xs text-muted-foreground">历史</span>}</div>)}</CardContent>
|
||||
<CardContent className="space-y-3">{versions.length ? versions.map((version) => <div className="flex items-center gap-3 rounded-xl border border-border p-4" key={version.version}><b className="text-sm text-foreground">{version.version}</b><span className="rounded-full bg-muted px-2.5 py-1 text-xs">{version.category}</span><span className="min-w-0 flex-1"><small className="block truncate text-xs text-muted-foreground">{version.note}</small><small className="text-3xs text-muted-foreground">{version.author} · {version.createdAt}</small></span>{version.current ? <span className="rounded-full bg-success-soft px-2.5 py-1 text-xs text-success-strong">当前生效</span> : canManage ? <Button variant="outline" size="xs" onClick={() => toast.info("规则回滚接口尚未接入")}>一键回滚</Button> : <span className="rounded-full bg-muted px-2.5 py-1 text-xs text-muted-foreground">历史</span>}</div>) : <div className="py-10 text-center text-sm text-muted-foreground">暂无规则版本数据</div>}</CardContent>
|
||||
</Card>
|
||||
|
||||
{canManage && <Card>
|
||||
|
||||
Reference in New Issue
Block a user