From 3f7f88cf91cc8f2429d2c967f7e457f5184c6617 Mon Sep 17 00:00:00 2001 From: qiaoxinjiu Date: Fri, 29 May 2026 14:33:56 +0800 Subject: [PATCH] docs: update WishFulfilled knowledge base --- .../scripts/sync-wishfulfilled-graph.mjs | 217 ++- .../dashboard/src/components/CodeViewer.tsx | 87 +- .../packages/dashboard/src/index.css | 184 ++ .../assets/CodeViewer-Boblkcge.js | 14 + .../assets/KeyboardShortcutsHelp-CcG7Ga98.js | 1 + .../assets/LearnPanel-DahajoWG.js | 1 + .../assets/OnboardingOverlay-BCZlWHWy.js | 1 + .../assets/PathFinderModal-BHT2q_Em.js | 1 + .../assets/RagAssistant-BEmyfNge.js | 4 + .../assets/index-C3mrB48A.js | 42 + .../assets/index-L1cEqadP.css | 1 + wishfulfilled-dashboard/index.html | 4 +- wishfulfilled-dashboard/knowledge-graph.json | 466 ++++- wishfulfilled-dashboard/meta.json | 4 +- .../.understand-anything/knowledge-graph.json | 466 ++++- .../.understand-anything/meta.json | 4 +- ...60527_USER_ERP_0-1需求重构_01_主流程说明_v1.md | 261 +++ ...USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md | 198 +++ ...ER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md | 190 ++ ...R_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md | 352 ++++ ...R_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md | 138 ++ ...0-1需求重构_06_VibeCoding页面验证记录_v1(1).md | 209 +++ ...RP_0-1需求重构_06_VibeCoding页面验证记录_v1.md | 209 +++ ...28_USER_ERP_0-1需求重构_可点击参考原型_v1.html | 1568 +++++++++++++++++ .../20260528_USER_ERP_完整参考原型_v2.html | 1107 ++++++++++++ .../05_需求文档/通用EDM业务流程说明.md | 398 +++++ .../通用IM业务流程与接口频率限制说明.md | 309 ++++ .../01-子系统-identity-数据库表关系.md | 430 +++++ .../USER用户运营系统_原型逐页详细测试用例集.xlsx | Bin 0 -> 182957 bytes 29 files changed, 6679 insertions(+), 187 deletions(-) create mode 100644 wishfulfilled-dashboard/assets/CodeViewer-Boblkcge.js create mode 100644 wishfulfilled-dashboard/assets/KeyboardShortcutsHelp-CcG7Ga98.js create mode 100644 wishfulfilled-dashboard/assets/LearnPanel-DahajoWG.js create mode 100644 wishfulfilled-dashboard/assets/OnboardingOverlay-BCZlWHWy.js create mode 100644 wishfulfilled-dashboard/assets/PathFinderModal-BHT2q_Em.js create mode 100644 wishfulfilled-dashboard/assets/RagAssistant-BEmyfNge.js create mode 100644 wishfulfilled-dashboard/assets/index-C3mrB48A.js create mode 100644 wishfulfilled-dashboard/assets/index-L1cEqadP.css create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1).md create mode 100644 wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md create mode 100644 wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1.html create mode 100644 wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_完整参考原型_v2.html create mode 100644 wishfulfilled-wiki/05_需求文档/通用EDM业务流程说明.md create mode 100644 wishfulfilled-wiki/05_需求文档/通用IM业务流程与接口频率限制说明.md create mode 100644 wishfulfilled-wiki/07_技术文档/01-子系统-identity-数据库表关系.md create mode 100644 wishfulfilled-wiki/08_测试相关/USER用户运营系统_原型逐页详细测试用例集.xlsx diff --git a/Understand-Anything-main/understand-anything-plugin/packages/dashboard/scripts/sync-wishfulfilled-graph.mjs b/Understand-Anything-main/understand-anything-plugin/packages/dashboard/scripts/sync-wishfulfilled-graph.mjs index 6a9e131..16c22d5 100644 --- a/Understand-Anything-main/understand-anything-plugin/packages/dashboard/scripts/sync-wishfulfilled-graph.mjs +++ b/Understand-Anything-main/understand-anything-plugin/packages/dashboard/scripts/sync-wishfulfilled-graph.mjs @@ -1,9 +1,9 @@ import fs from 'node:fs'; import path from 'node:path'; +import { execFileSync } from 'node:child_process'; const root = 'D:/AIcoding/WishFulfilled/知识库/under-anything'; const wiki = `${root}/wishfulfilled-wiki`; -const reqDir = `${wiki}/05_需求文档`; const graphPaths = [ `${wiki}/.understand-anything/knowledge-graph.json`, `${root}/wishfulfilled-dashboard/knowledge-graph.json`, @@ -13,14 +13,73 @@ const metaPaths = [ `${root}/wishfulfilled-dashboard/meta.json`, ]; -const files = fs.readdirSync(reqDir) - .filter((name) => /\.(md|html?)$/i.test(name)) - .sort((a, b) => a.localeCompare(b, 'zh-Hans-CN')); +const directoryConfigs = [ + { + dir: `${wiki}/05_需求文档`, + relDir: '05_需求文档', + layerId: 'layer-requirements', + flowId: 'flow:layer-requirements', + layerName: '需求文档', + layerDescription: '所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。', + defaultTags: ['05_需求文档', '需求文档'], + category: 'layer-requirements', + fallbackSummary: '需求文档。', + }, + { + dir: `${wiki}/07_技术文档`, + relDir: '07_技术文档', + layerId: 'layer-technical', + flowId: 'flow:layer-technical', + layerName: '技术文档', + layerDescription: '系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。', + defaultTags: ['07_技术文档', '技术文档'], + category: 'layer-technical', + fallbackSummary: '技术文档。', + }, + { + dir: `${wiki}/08_测试相关`, + relDir: '08_测试相关', + layerId: 'layer-testing', + flowId: 'flow:layer-testing', + layerName: '测试相关', + layerDescription: '测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。', + defaultTags: ['08_测试相关', '测试相关'], + category: 'layer-testing', + fallbackSummary: '测试相关文档。', + }, +]; + +function listFiles(dir) { + if (!fs.existsSync(dir)) return []; + return fs.readdirSync(dir) + .filter((name) => /\.(md|html?|xlsx)$/i.test(name)) + .sort((a, b) => a.localeCompare(b, 'zh-Hans-CN')); +} function readText(filePath) { + if (/\.xlsx$/i.test(filePath)) return readXlsxText(filePath); return fs.readFileSync(filePath, 'utf8'); } +function readXlsxText(filePath) { + const script = [ + 'import json, sys', + 'from openpyxl import load_workbook', + 'path = sys.argv[1]', + 'wb = load_workbook(path, data_only=True, read_only=True)', + 'out = []', + 'for ws in wb.worksheets:', + ' out.append(f"# Sheet: {ws.title}")', + ' for row in ws.iter_rows(values_only=True):', + ' vals = [str(v).strip() for v in row if v is not None and str(v).strip()]', + ' if vals:', + ' out.append(" | ".join(vals))', + 'print(json.dumps("\\n".join(out), ensure_ascii=False))', + ].join('\n'); + const stdout = execFileSync('python', ['-c', script, filePath], { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024 }); + return JSON.parse(stdout); +} + function cleanLineNumbers(text) { const lines = text.split(/\r?\n/); let changed = 0; @@ -56,15 +115,15 @@ function titleFor(fileName, text) { return path.basename(fileName, path.extname(fileName)).replace(/^\d+[-_]/, '').replace(/_/g, ' '); } -function summaryFor(fileName, text) { +function summaryFor(fileName, text, fallbackSummary) { const plain = /\.html?$/i.test(fileName) ? stripHtml(text) : text.replace(/[`*_>#|\-\[\]()]/g, ' ').replace(/\s+/g, ' ').trim(); - return plain ? plain.slice(0, 180) : '需求文档。'; + return plain ? plain.slice(0, 180) : fallbackSummary; } -function tagsFor(text) { - const tags = ['05_需求文档', '需求文档']; +function tagsFor(text, defaultTags) { + const tags = [...defaultTags]; const match = text.match(/^tags:\s*\[(.*?)\]/m); if (!match) return tags; for (const item of match[1].split(/[,,]/)) { @@ -80,6 +139,36 @@ function complexityFor(text) { return 'simple'; } +function ensureLayer(graph, config) { + let layer = graph.layers.find((item) => item.id === config.layerId); + if (!layer) { + layer = { + id: config.layerId, + name: config.layerName, + description: config.layerDescription, + nodeIds: [config.flowId], + }; + graph.layers.push(layer); + } + layer.name = config.layerName; + layer.description = config.layerDescription; + layer.nodeIds ??= []; + if (!layer.nodeIds.includes(config.flowId)) layer.nodeIds.unshift(config.flowId); + return layer; +} + +function updateFlowNode(byId, layer, config) { + const count = layer.nodeIds.filter((id) => id !== config.flowId).length; + const flow = byId.get(config.flowId); + if (flow) { + flow.summary = config.layerDescription; + flow.knowledgeMeta ??= {}; + flow.knowledgeMeta.content = `# ${config.layerName}\n\n${config.layerDescription}\n\n本层包含 ${count} 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。`; + flow.knowledgeMeta.category = config.category; + } + return count; +} + function updateGraph(graphPath) { const graph = JSON.parse(fs.readFileSync(graphPath, 'utf8')); graph.nodes ??= []; @@ -88,77 +177,65 @@ function updateGraph(graphPath) { const byId = new Map(graph.nodes.map((node) => [node.id, node])); const edgeKeys = new Set(graph.edges.map((edge) => `${edge.source}|${edge.target}|${edge.type}`)); - let layer = graph.layers.find((item) => item.id === 'layer-requirements'); - if (!layer) { - layer = { - id: 'layer-requirements', - name: '需求文档', - description: '所有正式需求、业务规则、需求变更和需求索引。', - nodeIds: ['flow:layer-requirements'], - }; - graph.layers.push(layer); - } - layer.nodeIds ??= []; + const stats = []; - let added = 0; - let updated = 0; - for (const fileName of files) { - const absolutePath = `${reqDir}/${fileName}`; - const relPath = `05_需求文档/${fileName}`; - const nodeId = `doc:${relPath.replace(/\.[^.]+$/, '')}`; - const text = cleanLineNumbers(readText(absolutePath)); - const node = { - id: nodeId, - type: 'document', - name: titleFor(fileName, text), - filePath: relPath, - summary: summaryFor(fileName, text), - tags: tagsFor(text), - complexity: complexityFor(text), - knowledgeMeta: { - content: text, - wikilinks: [...text.matchAll(/\[\[([^\]]+)\]\]/g)].map((match) => match[1]), - category: 'layer-requirements', - }, - }; + for (const config of directoryConfigs) { + const files = listFiles(config.dir); + const layer = ensureLayer(graph, config); + let added = 0; + let updated = 0; - if (byId.has(nodeId)) { - Object.assign(byId.get(nodeId), node); - updated += 1; - } else { - graph.nodes.push(node); - byId.set(nodeId, node); - added += 1; + for (const fileName of files) { + const absolutePath = `${config.dir}/${fileName}`; + const relPath = `${config.relDir}/${fileName}`; + const nodeId = `doc:${relPath.replace(/\.[^.]+$/, '')}`; + const text = cleanLineNumbers(readText(absolutePath)); + const node = { + id: nodeId, + type: 'document', + name: titleFor(fileName, text), + filePath: relPath, + summary: summaryFor(fileName, text, config.fallbackSummary), + tags: tagsFor(text, config.defaultTags), + complexity: complexityFor(text), + knowledgeMeta: { + content: text, + wikilinks: [...text.matchAll(/\[\[([^\]]+)\]\]/g)].map((match) => match[1]), + category: config.category, + }, + }; + + if (byId.has(nodeId)) { + Object.assign(byId.get(nodeId), node); + updated += 1; + } else { + graph.nodes.push(node); + byId.set(nodeId, node); + added += 1; + } + + if (!layer.nodeIds.includes(nodeId)) layer.nodeIds.push(nodeId); + const edgeKey = `${config.flowId}|${nodeId}|documents`; + if (!edgeKeys.has(edgeKey)) { + graph.edges.push({ + source: config.flowId, + target: nodeId, + type: 'documents', + direction: 'forward', + description: '本层文档', + weight: 0.65, + }); + edgeKeys.add(edgeKey); + } } - if (!layer.nodeIds.includes(nodeId)) layer.nodeIds.push(nodeId); - const edgeKey = `flow:layer-requirements|${nodeId}|documents`; - if (!edgeKeys.has(edgeKey)) { - graph.edges.push({ - source: 'flow:layer-requirements', - target: nodeId, - type: 'documents', - direction: 'forward', - description: '本层文档', - weight: 0.65, - }); - edgeKeys.add(edgeKey); - } - } - - const count = layer.nodeIds.filter((id) => id !== 'flow:layer-requirements').length; - const flow = byId.get('flow:layer-requirements'); - if (flow) { - flow.summary = '所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。'; - flow.knowledgeMeta ??= {}; - flow.knowledgeMeta.content = `# 需求文档\n\n所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。\n\n本层包含 ${count} 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。`; - flow.knowledgeMeta.category = 'layer-requirements'; + stats.push({ layer: config.layerName, added, updated, count: updateFlowNode(byId, layer, config) }); } graph.project ??= {}; graph.project.analyzedAt = new Date().toISOString(); fs.writeFileSync(graphPath, `${JSON.stringify(graph, null, 2)}\n`, 'utf8'); - return { graphPath, added, updated, requirements: count, nodes: graph.nodes.length }; + return { graphPath, stats, nodes: graph.nodes.length }; } const results = graphPaths.map(updateGraph); diff --git a/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx b/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx index bd14013..c3c0b5b 100644 --- a/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx +++ b/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo, useState } from "react"; import { Highlight, themes } from "prism-react-renderer"; +import ReactMarkdown from "react-markdown"; import { useDashboardStore } from "../store"; import { useI18n } from "../contexts/I18nContext"; @@ -76,6 +77,11 @@ function formatBytes(bytes: number): string { return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } +function isMarkdownSource(source: SourceFile | null, language: string): boolean { + if (!source) return false; + return language === "markdown" || /\.md$/i.test(source.path); +} + export default function CodeViewer({ accessToken, presentation = "sidebar", @@ -165,6 +171,7 @@ export default function CodeViewer({ const source = state.source; const language = source?.language ?? fallbackLanguage(node.filePath); + const shouldRenderMarkdown = isMarkdownSource(source, language); const lineInfo = highlightedRange ? `${t.codeViewer.lines} ${highlightedRange.start}-${highlightedRange.end}` : t.codeViewer.fullFile; @@ -245,43 +252,49 @@ export default function CodeViewer({ {source.lineCount} {t.codeViewer.linesLabel} {formatBytes(source.sizeBytes)} - - {({ className, style, tokens, getLineProps, getTokenProps }) => ( -
-                  {tokens.map((line, index) => {
-                    const lineNumber = index + 1;
-                    const isHighlighted =
-                      highlightedRange !== null &&
-                      lineNumber >= highlightedRange.start &&
-                      lineNumber <= highlightedRange.end;
-                    const lineProps = getLineProps({ line });
-                    return (
-                      
- - {lineNumber} - - - {line.map((token, key) => ( - - ))} - -
- ); - })} -
- )} -
+ {shouldRenderMarkdown ? ( +
+ {source.content} +
+ ) : ( + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+                    {tokens.map((line, index) => {
+                      const lineNumber = index + 1;
+                      const isHighlighted =
+                        highlightedRange !== null &&
+                        lineNumber >= highlightedRange.start &&
+                        lineNumber <= highlightedRange.end;
+                      const lineProps = getLineProps({ line });
+                      return (
+                        
+ + {lineNumber} + + + {line.map((token, key) => ( + + ))} + +
+ ); + })} +
+ )} +
+ )} )} diff --git a/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/index.css b/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/index.css index 8cb8719..ea52b13 100644 --- a/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/index.css +++ b/Understand-Anything-main/understand-anything-plugin/packages/dashboard/src/index.css @@ -218,6 +218,190 @@ body { overflow: hidden; } +/* Markdown document reader */ +.markdown-reader { + width: min(100%, 980px); + margin: 0 auto; + padding: 32px 40px 56px; + color: var(--color-text-primary); + font-family: var(--font-sans); + font-size: 15px; + line-height: 1.85; + letter-spacing: 0.01em; +} + +.markdown-reader-modal { + width: min(100%, 1040px); + padding: 40px 56px 72px; + font-size: 16px; +} + +.markdown-reader h1, +.markdown-reader h2, +.markdown-reader h3, +.markdown-reader h4, +.markdown-reader h5, +.markdown-reader h6 { + color: var(--color-text-primary); + font-family: var(--font-sans); + font-weight: 750; + line-height: 1.32; + letter-spacing: -0.01em; + scroll-margin-top: 24px; +} + +.markdown-reader h1 { + margin: 0 0 24px; + padding-bottom: 16px; + border-bottom: 1px solid var(--color-border-medium); + font-size: 2rem; +} + +.markdown-reader h2 { + margin: 42px 0 18px; + padding-left: 14px; + border-left: 3px solid var(--color-accent); + font-size: 1.5rem; +} + +.markdown-reader h3 { + margin: 32px 0 14px; + color: var(--color-accent-bright); + font-size: 1.18rem; +} + +.markdown-reader h4, +.markdown-reader h5, +.markdown-reader h6 { + margin: 24px 0 10px; + color: var(--color-text-secondary); + font-size: 1rem; +} + +.markdown-reader p, +.markdown-reader ul, +.markdown-reader ol, +.markdown-reader blockquote, +.markdown-reader table, +.markdown-reader pre { + margin: 0 0 18px; +} + +.markdown-reader p { + color: var(--color-text-secondary); +} + +.markdown-reader strong { + color: var(--color-text-primary); + font-weight: 700; +} + +.markdown-reader a { + color: var(--color-node-document); + text-decoration: none; + border-bottom: 1px solid color-mix(in srgb, var(--color-node-document) 45%, transparent); +} + +.markdown-reader ul, +.markdown-reader ol { + padding-left: 1.5rem; + color: var(--color-text-secondary); +} + +.markdown-reader li { + margin: 7px 0; + padding-left: 4px; +} + +.markdown-reader li::marker { + color: var(--color-accent); +} + +.markdown-reader blockquote { + border-left: 3px solid var(--color-border-medium); + background: color-mix(in srgb, var(--color-accent) 7%, transparent); + padding: 14px 18px; + border-radius: 0 10px 10px 0; + color: var(--color-text-secondary); +} + +.markdown-reader code { + color: var(--color-accent-bright); + background: color-mix(in srgb, var(--color-accent) 12%, transparent); + border: 1px solid var(--color-border-subtle); + border-radius: 5px; + padding: 0.12em 0.36em; + font-family: var(--font-mono); + font-size: 0.9em; +} + +.markdown-reader pre { + overflow: auto; + border: 1px solid var(--color-border-subtle); + border-radius: 12px; + background: color-mix(in srgb, var(--color-root) 82%, var(--color-elevated)); + padding: 18px 20px; + line-height: 1.65; +} + +.markdown-reader pre code { + display: block; + padding: 0; + border: 0; + background: transparent; + color: var(--color-text-primary); + font-size: 0.88em; + white-space: pre; +} + +.markdown-reader hr { + margin: 34px 0; + border: 0; + border-top: 1px solid var(--color-border-subtle); +} + +.markdown-reader table { + display: block; + width: 100%; + overflow: auto; + border-collapse: collapse; + border: 1px solid var(--color-border-subtle); + border-radius: 10px; +} + +.markdown-reader th, +.markdown-reader td { + border: 1px solid var(--color-border-subtle); + padding: 10px 12px; + vertical-align: top; +} + +.markdown-reader th { + background: color-mix(in srgb, var(--color-accent) 11%, transparent); + color: var(--color-text-primary); + font-weight: 700; +} + +.markdown-reader td { + color: var(--color-text-secondary); +} + +@media (max-width: 768px) { + .markdown-reader, + .markdown-reader-modal { + padding: 24px 20px 44px; + font-size: 15px; + } + + .markdown-reader h1 { + font-size: 1.6rem; + } + + .markdown-reader h2 { + font-size: 1.28rem; + } +} + /* Hide scrollbar but keep scroll functionality */ .scrollbar-hide { -ms-overflow-style: none; diff --git a/wishfulfilled-dashboard/assets/CodeViewer-Boblkcge.js b/wishfulfilled-dashboard/assets/CodeViewer-Boblkcge.js new file mode 100644 index 0000000..8a3c3ed --- /dev/null +++ b/wishfulfilled-dashboard/assets/CodeViewer-Boblkcge.js @@ -0,0 +1,14 @@ +import{a as $,j as x}from"./react-vendor-BVoutfaX.js";import{u as M,a as ye}from"./index-C3mrB48A.js";import{M as fe}from"./markdown-DpllbSd9.js";import"./xyflow-CYMCcsWN.js";import"./graph-layout-7tFr_anw.js";import"./elk-CXeXGyKz.js";import"./graphology-BgTy_cc3.js";function ae(e){var t,n,l="";if(typeof e=="string"||typeof e=="number")l+=e;else if(typeof e=="object")if(Array.isArray(e)){var c=e.length;for(t=0;tt in e?W(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,D=(e,t)=>{for(var n in t||(t={}))Q.call(t,n)&&ee(e,n,t[n]);if(q)for(var n of q(t))le.call(t,n)&&ee(e,n,t[n]);return e},H=(e,t)=>me(e,ve(t)),ie=(e,t)=>{var n={};for(var l in e)Q.call(e,l)&&t.indexOf(l)<0&&(n[l]=e[l]);if(e!=null&&q)for(var l of q(e))t.indexOf(l)<0&&le.call(e,l)&&(n[l]=e[l]);return n},ke=(e,t)=>function(){return t||(0,e[oe(e)[0]])((t={exports:{}}).exports,t),t.exports},xe=(e,t)=>{for(var n in t)W(e,n,{get:t[n],enumerable:!0})},Ee=(e,t,n,l)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of oe(t))!Q.call(e,c)&&c!==n&&W(e,c,{get:()=>t[c],enumerable:!(l=he(t,c))||l.enumerable});return e},Ae=(e,t,n)=>(n=e!=null?be(we(e)):{},Ee(!e||!e.__esModule?W(n,"default",{value:e,enumerable:!0}):n,e)),Se=ke({"../../node_modules/.pnpm/prismjs@1.29.0_patch_hash=vrxx3pzkik6jpmgpayxfjunetu/node_modules/prismjs/prism.js"(e,t){var n=(function(){var l=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,c=0,h={},d={util:{encode:function a(r){return r instanceof m?new m(r.type,a(r.content),r.alias):Array.isArray(r)?r.map(a):r.replace(/&/g,"&").replace(/"+i.content+""};function f(a,r,o,p){a.lastIndex=r;var i=a.exec(o);if(i&&p&&i[1]){var y=i[1].length;i.index+=y,i[0]=i[0].slice(y)}return i}function b(a,r,o,p,i,y){for(var E in o)if(!(!o.hasOwnProperty(E)||!o[E])){var v=o[E];v=Array.isArray(v)?v:[v];for(var S=0;S=y.reach);N+=F.value.length,F=F.next){var B=F.value;if(r.length>a.length)return;if(!(B instanceof m)){var U=1,C;if(L){if(C=f(z,N,a,_),!C||C.index>=a.length)break;var G=C.index,de=C.index+C[0].length,j=N;for(j+=F.value.length;G>=j;)F=F.next,j+=F.value.length;if(j-=F.value.length,N=j,F.value instanceof m)continue;for(var P=F;P!==r.tail&&(jy.reach&&(y.reach=K);var V=F.prev;Y&&(V=g(r,V,Y),N+=Y.length),w(r,V,U);var ge=new m(E,R?d.tokenize(Z,R):Z,O,Z);if(F=g(r,V,ge),J&&g(r,F,J),U>1){var X={cause:E+","+S,reach:K};b(a,r,o,F.prev,N,X),y&&X.reach>y.reach&&(y.reach=X.reach)}}}}}}function u(){var a={value:null,prev:null,next:null},r={value:null,prev:a,next:null};a.next=r,this.head=a,this.tail=r,this.length=0}function g(a,r,o){var p=r.next,i={value:o,prev:r,next:p};return r.next=i,p.prev=i,a.length++,i}function w(a,r,o){for(var p=r.next,i=0;i/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},s.languages.markup.tag.inside["attr-value"].inside.entity=s.languages.markup.entity,s.languages.markup.doctype.inside["internal-subset"].inside=s.languages.markup,s.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(s.languages.markup.tag,"addInlined",{value:function(e,l){var n={},n=(n["language-"+l]={pattern:/(^$)/i,lookbehind:!0,inside:s.languages[l]},n.cdata=/^$/i,{"included-cdata":{pattern://i,inside:n}}),l=(n["language-"+l]={pattern:/[\s\S]+/,inside:s.languages[l]},{});l[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:n},s.languages.insertBefore("markup","cdata",l)}}),Object.defineProperty(s.languages.markup.tag,"addAttribute",{value:function(e,t){s.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:s.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),s.languages.html=s.languages.markup,s.languages.mathml=s.languages.markup,s.languages.svg=s.languages.markup,s.languages.xml=s.languages.extend("markup",{}),s.languages.ssml=s.languages.xml,s.languages.atom=s.languages.xml,s.languages.rss=s.languages.xml,(function(e){var t={pattern:/\\[\\(){}[\]^$+*?|.]/,alias:"escape"},n=/\\(?:x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]+\}|0[0-7]{0,2}|[123][0-7]{2}|c[a-zA-Z]|.)/,l="(?:[^\\\\-]|"+n.source+")",l=RegExp(l+"-"+l),c={pattern:/(<|')[^<>']+(?=[>']$)/,lookbehind:!0,alias:"variable"};e.languages.regex={"char-class":{pattern:/((?:^|[^\\])(?:\\\\)*)\[(?:[^\\\]]|\\[\s\S])*\]/,lookbehind:!0,inside:{"char-class-negation":{pattern:/(^\[)\^/,lookbehind:!0,alias:"operator"},"char-class-punctuation":{pattern:/^\[|\]$/,alias:"punctuation"},range:{pattern:l,inside:{escape:n,"range-punctuation":{pattern:/-/,alias:"operator"}}},"special-escape":t,"char-set":{pattern:/\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},escape:n}},"special-escape":t,"char-set":{pattern:/\.|\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},backreference:[{pattern:/\\(?![123][0-7]{2})[1-9]/,alias:"keyword"},{pattern:/\\k<[^<>']+>/,alias:"keyword",inside:{"group-name":c}}],anchor:{pattern:/[$^]|\\[ABbGZz]/,alias:"function"},escape:n,group:[{pattern:/\((?:\?(?:<[^<>']+>|'[^<>']+'|[>:]|]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},s.languages.javascript=s.languages.extend("clike",{"class-name":[s.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),s.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,s.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:s.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:s.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:s.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:s.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:s.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),s.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:s.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),s.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),s.languages.markup&&(s.languages.markup.tag.addInlined("script","javascript"),s.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),s.languages.js=s.languages.javascript,s.languages.actionscript=s.languages.extend("javascript",{keyword:/\b(?:as|break|case|catch|class|const|default|delete|do|dynamic|each|else|extends|final|finally|for|function|get|if|implements|import|in|include|instanceof|interface|internal|is|namespace|native|new|null|override|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|use|var|void|while|with)\b/,operator:/\+\+|--|(?:[+\-*\/%^]|&&?|\|\|?|<>?>?|[!=]=?)=?|[~?@]/}),s.languages.actionscript["class-name"].alias="function",delete s.languages.actionscript.parameter,delete s.languages.actionscript["literal-property"],s.languages.markup&&s.languages.insertBefore("actionscript","string",{xml:{pattern:/(^|[^.])<\/?\w+(?:\s+[^\s>\/=]+=("|')(?:\\[\s\S]|(?!\2)[^\\])*\2)*\s*\/?>/,lookbehind:!0,inside:s.languages.markup}}),(function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript})(s),(function(e){var t=e.languages.javadoclike={parameter:{pattern:/(^[\t ]*(?:\/{3}|\*|\/\*\*)\s*@(?:arg|arguments|param)\s+)\w+/m,lookbehind:!0},keyword:{pattern:/(^[\t ]*(?:\/{3}|\*|\/\*\*)\s*|\{)@[a-z][a-zA-Z-]+\b/m,lookbehind:!0},punctuation:/[{}]/};Object.defineProperty(t,"addSupport",{value:function(n,l){(n=typeof n=="string"?[n]:n).forEach(function(c){var h=function(g){g.inside||(g.inside={}),g.inside.rest=l},d="doc-comment";if(m=e.languages[c]){var m,f=m[d];if((f=f||(m=e.languages.insertBefore(c,"comment",{"doc-comment":{pattern:/(^|[^\\])\/\*\*[^/][\s\S]*?(?:\*\/|$)/,lookbehind:!0,alias:"comment"}}))[d])instanceof RegExp&&(f=m[d]={pattern:f}),Array.isArray(f))for(var b=0,u=f.length;b|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}}),{pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0}),n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|RebeccaPurple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,number:n})})(s),(function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,l="(?:"+n.source+"(?:[ ]+"+t.source+")?|"+t.source+"(?:[ ]+"+n.source+")?)",c=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source}),h=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function d(m,f){f=(f||"").replace(/m/g,"")+"m";var b=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,function(){return l}).replace(/<>/g,function(){return m});return RegExp(b,f)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,function(){return l})),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,function(){return l}).replace(/<>/g,function(){return"(?:"+c+"|"+h+")"})),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:d(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:d(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:d(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:d(h),lookbehind:!0,greedy:!0},number:{pattern:d(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml})(s),(function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(b){return b=b.replace(//g,function(){return t}),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+b+")")}var l=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,c=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,function(){return l}),h=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source,d=(e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+c+h+"(?:"+c+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+c+h+")(?:"+c+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(l),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+c+")"+h+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+c+"$"),inside:{"table-header":{pattern:RegExp(l),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach(function(b){["url","bold","italic","strike","code-snippet"].forEach(function(u){b!==u&&(e.languages.markdown[b].inside.content.inside[u]=e.languages.markdown[u])})}),e.hooks.add("after-tokenize",function(b){b.language!=="markdown"&&b.language!=="md"||(function u(g){if(g&&typeof g!="string")for(var w=0,k=g.length;w",quot:'"'},f=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown})(s),s.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:s.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},s.hooks.add("after-tokenize",function(e){if(e.language==="graphql")for(var t=e.tokens.filter(function(a){return typeof a!="string"&&a.type!=="comment"&&a.type!=="scalar"}),n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},(function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,l=t.inside.interpolation,c=l.inside["interpolation-punctuation"],h=l.pattern.source;function d(g,w){if(e.languages[g])return{pattern:RegExp("((?:"+w+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:g}}}}function m(g,w,k){return g={code:g,grammar:w,language:k},e.hooks.run("before-tokenize",g),g.tokens=e.tokenize(g.code,g.grammar),e.hooks.run("after-tokenize",g),g.tokens}function f(g,w,k){var o=e.tokenize(g,{interpolation:{pattern:RegExp(h),lookbehind:!0}}),a=0,r={},o=m(o.map(function(i){if(typeof i=="string")return i;for(var y,E,i=i.content;g.indexOf((E=a++,y="___"+k.toUpperCase()+"_"+E+"___"))!==-1;);return r[y]=i,y}).join(""),w,k),p=Object.keys(r);return a=0,(function i(y){for(var E=0;E=p.length)return;var v,S,A,R,_,L,O,T=y[E];typeof T=="string"||typeof T.content=="string"?(v=p[a],(O=(L=typeof T=="string"?T:T.content).indexOf(v))!==-1&&(++a,S=L.substring(0,O),_=r[v],A=void 0,(R={})["interpolation-punctuation"]=c,(R=e.tokenize(_,R)).length===3&&((A=[1,1]).push.apply(A,m(R[1],e.languages.javascript,"javascript")),R.splice.apply(R,A)),A=new e.Token("interpolation",R,l.alias,_),R=L.substring(O+v.length),_=[],S&&_.push(S),_.push(A),R&&(i(L=[R]),_.push.apply(_,L)),typeof T=="string"?(y.splice.apply(y,[E,1].concat(_)),E+=_.length-1):T.content=_)):(O=T.content,Array.isArray(O)?i(O):i([O]))}})(o),new e.Token(k,o,"language-"+k,g)}e.languages.javascript["template-string"]=[d("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),d("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),d("svg",/\bsvg/.source),d("markdown",/\b(?:markdown|md)/.source),d("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),d("sql",/\bsql/.source),t].filter(Boolean);var b={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function u(g){return typeof g=="string"?g:Array.isArray(g)?g.map(u).join(""):u(g.content)}e.hooks.add("after-tokenize",function(g){g.language in b&&(function w(k){for(var a=0,r=k.length;a]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript})(s),(function(e){var t=e.languages.javascript,n=/\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})+\}/.source,l="(@(?:arg|argument|param|property)\\s+(?:"+n+"\\s+)?)";e.languages.jsdoc=e.languages.extend("javadoclike",{parameter:{pattern:RegExp(l+/(?:(?!\s)[$\w\xA0-\uFFFF.])+(?=\s|$)/.source),lookbehind:!0,inside:{punctuation:/\./}}}),e.languages.insertBefore("jsdoc","keyword",{"optional-parameter":{pattern:RegExp(l+/\[(?:(?!\s)[$\w\xA0-\uFFFF.])+(?:=[^[\]]+)?\](?=\s|$)/.source),lookbehind:!0,inside:{parameter:{pattern:/(^\[)[$\w\xA0-\uFFFF\.]+/,lookbehind:!0,inside:{punctuation:/\./}},code:{pattern:/(=)[\s\S]*(?=\]$)/,lookbehind:!0,inside:t,alias:"language-javascript"},punctuation:/[=[\]]/}},"class-name":[{pattern:RegExp(/(@(?:augments|class|extends|interface|memberof!?|template|this|typedef)\s+(?:\s+)?)[A-Z]\w*(?:\.[A-Z]\w*)*/.source.replace(//g,function(){return n})),lookbehind:!0,inside:{punctuation:/\./}},{pattern:RegExp("(@[a-z]+\\s+)"+n),lookbehind:!0,inside:{string:t.string,number:t.number,boolean:t.boolean,keyword:e.languages.typescript.keyword,operator:/=>|\.\.\.|[&|?:*]/,punctuation:/[.,;=<>{}()[\]]/}}],example:{pattern:/(@example\s+(?!\s))(?:[^@\s]|\s+(?!\s))+?(?=\s*(?:\*\s*)?(?:@\w|\*\/))/,lookbehind:!0,inside:{code:{pattern:/^([\t ]*(?:\*\s*)?)\S.*$/m,lookbehind:!0,inside:t,alias:"language-javascript"}}}}),e.languages.javadoclike.addSupport("javascript",e.languages.jsdoc)})(s),(function(e){e.languages.flow=e.languages.extend("javascript",{}),e.languages.insertBefore("flow","keyword",{type:[{pattern:/\b(?:[Bb]oolean|Function|[Nn]umber|[Ss]tring|[Ss]ymbol|any|mixed|null|void)\b/,alias:"class-name"}]}),e.languages.flow["function-variable"].pattern=/(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=\s*(?:function\b|(?:\([^()]*\)(?:\s*:\s*\w+)?|(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/i,delete e.languages.flow.parameter,e.languages.insertBefore("flow","operator",{"flow-punctuation":{pattern:/\{\||\|\}/,alias:"punctuation"}}),Array.isArray(e.languages.flow.keyword)||(e.languages.flow.keyword=[e.languages.flow.keyword]),e.languages.flow.keyword.unshift({pattern:/(^|[^$]\b)(?:Class|declare|opaque|type)\b(?!\$)/,lookbehind:!0},{pattern:/(^|[^$]\B)\$(?:Diff|Enum|Exact|Keys|ObjMap|PropertyType|Record|Shape|Subtype|Supertype|await)\b(?!\$)/,lookbehind:!0})})(s),s.languages.n4js=s.languages.extend("javascript",{keyword:/\b(?:Array|any|boolean|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|module|new|null|number|package|private|protected|public|return|set|static|string|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/}),s.languages.insertBefore("n4js","constant",{annotation:{pattern:/@+\w+/,alias:"operator"}}),s.languages.n4jsd=s.languages.n4js,(function(e){function t(d,m){return RegExp(d.replace(//g,function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source}),m)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],l=0;l*\.{3}(?:[^{}]|)*\})/.source;function h(f,b){return f=f.replace(//g,function(){return n}).replace(//g,function(){return l}).replace(//g,function(){return c}),RegExp(f,b)}c=h(c).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=h(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:h(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:h(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);function d(f){for(var b=[],u=0;u"&&b.push({tagName:m(g.content[0].content[1]),openedBraces:0}):0]=?/,punctuation:/[(),]/}},literal:{pattern:/#(?:colorLiteral|column|dsohandle|file(?:ID|Literal|Path)?|function|imageLiteral|line)\b/,alias:"constant"},"other-directive":{pattern:/#\w+\b/,alias:"property"},attribute:{pattern:/@\w+/,alias:"atrule"},"function-definition":{pattern:/(\bfunc\s+)\w+/,lookbehind:!0,alias:"function"},label:{pattern:/\b(break|continue)\s+\w+|\b[a-zA-Z_]\w*(?=\s*:\s*(?:for|repeat|while)\b)/,lookbehind:!0,alias:"important"},keyword:/\b(?:Any|Protocol|Self|Type|actor|as|assignment|associatedtype|associativity|async|await|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|else|enum|extension|fallthrough|fileprivate|final|for|func|get|guard|higherThan|if|import|in|indirect|infix|init|inout|internal|is|isolated|lazy|left|let|lowerThan|mutating|none|nonisolated|nonmutating|open|operator|optional|override|postfix|precedencegroup|prefix|private|protocol|public|repeat|required|rethrows|return|right|safe|self|set|some|static|struct|subscript|super|switch|throw|throws|try|typealias|unowned|unsafe|var|weak|where|while|willSet)\b/,boolean:/\b(?:false|true)\b/,nil:{pattern:/\bnil\b/,alias:"constant"},"short-argument":/\$\d+\b/,omit:{pattern:/\b_\b/,alias:"keyword"},number:/\b(?:[\d_]+(?:\.[\de_]+)?|0x[a-f0-9_]+(?:\.[a-f0-9p_]+)?|0b[01_]+|0o[0-7_]+)\b/i,"class-name":/\b[A-Z](?:[A-Z_\d]*[a-z]\w*)?\b/,function:/\b[a-z_]\w*(?=\s*\()/i,constant:/\b(?:[A-Z_]{2,}|k[A-Z][A-Za-z_]+)\b/,operator:/[-+*/%=!<>&|^~?]+|\.[.\-+*/%=!<>&|^~?]+/,punctuation:/[{}[\]();,.:\\]/},s.languages.swift["string-literal"].forEach(function(e){e.inside.interpolation.inside=s.languages.swift}),(function(e){e.languages.kotlin=e.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete e.languages.kotlin["class-name"];var t={"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:e.languages.kotlin}};e.languages.insertBefore("kotlin","string",{"string-literal":[{pattern:/"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,alias:"multiline",inside:{interpolation:{pattern:/\$(?:[a-z_]\w*|\{[^{}]*\})/i,inside:t},string:/[\s\S]+/}},{pattern:/"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,alias:"singleline",inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,lookbehind:!0,inside:t},string:/[\s\S]+/}}],char:{pattern:/'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,greedy:!0}}),delete e.languages.kotlin.string,e.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),e.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),e.languages.kt=e.languages.kotlin,e.languages.kts=e.languages.kotlin})(s),s.languages.c=s.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),s.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),s.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},s.languages.c.string],char:s.languages.c.char,comment:s.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:s.languages.c}}}}),s.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete s.languages.c.boolean,s.languages.objectivec=s.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete s.languages.objectivec["class-name"],s.languages.objc=s.languages.objectivec,s.languages.reason=s.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),s.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete s.languages.reason.function,(function(e){for(var t=/\/\*(?:[^*/]|\*(?!\/)|\/(?!\*)|)*\*\//.source,n=0;n<2;n++)t=t.replace(//g,function(){return t});t=t.replace(//g,function(){return/[^\s\S]/.source}),e.languages.rust={comment:[{pattern:RegExp(/(^|[^\\])/.source+t),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string})(s),s.languages.go=s.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),s.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete s.languages.go["class-name"],(function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,function(){return t.source});e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,function(){return t.source})),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,function(){return n})+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])})(s),s.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},s.languages.python["string-interpolation"].inside.interpolation.inside.rest=s.languages.python,s.languages.py=s.languages.python,s.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},s.languages.webmanifest=s.languages.json;var ue={};xe(ue,{dracula:()=>Fe,duotoneDark:()=>Te,duotoneLight:()=>Ne,github:()=>Oe,gruvboxMaterialDark:()=>it,gruvboxMaterialLight:()=>ct,jettwaveDark:()=>et,jettwaveLight:()=>nt,nightOwl:()=>De,nightOwlLight:()=>je,oceanicNext:()=>Pe,okaidia:()=>ze,oneDark:()=>at,oneLight:()=>ot,palenight:()=>Ge,shadesOfPurple:()=>Ve,synthwave84:()=>We,ultramin:()=>Ye,vsDark:()=>ce,vsLight:()=>Qe});var _e={plain:{color:"#F8F8F2",backgroundColor:"#282A36"},styles:[{types:["prolog","constant","builtin"],style:{color:"rgb(189, 147, 249)"}},{types:["inserted","function"],style:{color:"rgb(80, 250, 123)"}},{types:["deleted"],style:{color:"rgb(255, 85, 85)"}},{types:["changed"],style:{color:"rgb(255, 184, 108)"}},{types:["punctuation","symbol"],style:{color:"rgb(248, 248, 242)"}},{types:["string","char","tag","selector"],style:{color:"rgb(255, 121, 198)"}},{types:["keyword","variable"],style:{color:"rgb(189, 147, 249)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(98, 114, 164)"}},{types:["attr-name"],style:{color:"rgb(241, 250, 140)"}}]},Fe=_e,Re={plain:{backgroundColor:"#2a2734",color:"#9a86fd"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#6c6783"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#e09142"}},{types:["property","function"],style:{color:"#9a86fd"}},{types:["tag-id","selector","atrule-id"],style:{color:"#eeebff"}},{types:["attr-name"],style:{color:"#c4b9fe"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule","placeholder","variable"],style:{color:"#ffcc99"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#c4b9fe"}}]},Te=Re,Ie={plain:{backgroundColor:"#faf8f5",color:"#728fcb"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#b6ad9a"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#063289"}},{types:["property","function"],style:{color:"#b29762"}},{types:["tag-id","selector","atrule-id"],style:{color:"#2d2006"}},{types:["attr-name"],style:{color:"#896724"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule"],style:{color:"#728fcb"}},{types:["placeholder","variable"],style:{color:"#93abdc"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#896724"}}]},Ne=Ie,Le={plain:{color:"#393A34",backgroundColor:"#f6f8fa"},styles:[{types:["comment","prolog","doctype","cdata"],style:{color:"#999988",fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}},{types:["string","attr-value"],style:{color:"#e3116c"}},{types:["punctuation","operator"],style:{color:"#393A34"}},{types:["entity","url","symbol","number","boolean","variable","constant","property","regex","inserted"],style:{color:"#36acaa"}},{types:["atrule","keyword","attr-name","selector"],style:{color:"#00a4db"}},{types:["function","deleted","tag"],style:{color:"#d73a49"}},{types:["function-variable"],style:{color:"#6f42c1"}},{types:["tag","selector","keyword"],style:{color:"#00009f"}}]},Oe=Le,Ce={plain:{color:"#d6deeb",backgroundColor:"#011627"},styles:[{types:["changed"],style:{color:"rgb(162, 191, 252)",fontStyle:"italic"}},{types:["deleted"],style:{color:"rgba(239, 83, 80, 0.56)",fontStyle:"italic"}},{types:["inserted","attr-name"],style:{color:"rgb(173, 219, 103)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(99, 119, 119)",fontStyle:"italic"}},{types:["string","url"],style:{color:"rgb(173, 219, 103)"}},{types:["variable"],style:{color:"rgb(214, 222, 235)"}},{types:["number"],style:{color:"rgb(247, 140, 108)"}},{types:["builtin","char","constant","function"],style:{color:"rgb(130, 170, 255)"}},{types:["punctuation"],style:{color:"rgb(199, 146, 234)"}},{types:["selector","doctype"],style:{color:"rgb(199, 146, 234)",fontStyle:"italic"}},{types:["class-name"],style:{color:"rgb(255, 203, 139)"}},{types:["tag","operator","keyword"],style:{color:"rgb(127, 219, 202)"}},{types:["boolean"],style:{color:"rgb(255, 88, 116)"}},{types:["property"],style:{color:"rgb(128, 203, 196)"}},{types:["namespace"],style:{color:"rgb(178, 204, 214)"}}]},De=Ce,Be={plain:{color:"#403f53",backgroundColor:"#FBFBFB"},styles:[{types:["changed"],style:{color:"rgb(162, 191, 252)",fontStyle:"italic"}},{types:["deleted"],style:{color:"rgba(239, 83, 80, 0.56)",fontStyle:"italic"}},{types:["inserted","attr-name"],style:{color:"rgb(72, 118, 214)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(152, 159, 177)",fontStyle:"italic"}},{types:["string","builtin","char","constant","url"],style:{color:"rgb(72, 118, 214)"}},{types:["variable"],style:{color:"rgb(201, 103, 101)"}},{types:["number"],style:{color:"rgb(170, 9, 130)"}},{types:["punctuation"],style:{color:"rgb(153, 76, 195)"}},{types:["function","selector","doctype"],style:{color:"rgb(153, 76, 195)",fontStyle:"italic"}},{types:["class-name"],style:{color:"rgb(17, 17, 17)"}},{types:["tag"],style:{color:"rgb(153, 76, 195)"}},{types:["operator","property","keyword","namespace"],style:{color:"rgb(12, 150, 155)"}},{types:["boolean"],style:{color:"rgb(188, 84, 84)"}}]},je=Be,I={char:"#D8DEE9",comment:"#999999",keyword:"#c5a5c5",primitive:"#5a9bcf",string:"#8dc891",variable:"#d7deea",boolean:"#ff8b50",tag:"#fc929e",function:"#79b6f2",className:"#FAC863"},$e={plain:{backgroundColor:"#282c34",color:"#ffffff"},styles:[{types:["attr-name"],style:{color:I.keyword}},{types:["attr-value"],style:{color:I.string}},{types:["comment","block-comment","prolog","doctype","cdata","shebang"],style:{color:I.comment}},{types:["property","number","function-name","constant","symbol","deleted"],style:{color:I.primitive}},{types:["boolean"],style:{color:I.boolean}},{types:["tag"],style:{color:I.tag}},{types:["string"],style:{color:I.string}},{types:["punctuation"],style:{color:I.string}},{types:["selector","char","builtin","inserted"],style:{color:I.char}},{types:["function"],style:{color:I.function}},{types:["operator","entity","url","variable"],style:{color:I.variable}},{types:["keyword"],style:{color:I.keyword}},{types:["atrule","class-name"],style:{color:I.className}},{types:["important"],style:{fontWeight:"400"}},{types:["bold"],style:{fontWeight:"bold"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}}]},Pe=$e,Me={plain:{color:"#f8f8f2",backgroundColor:"#272822"},styles:[{types:["changed"],style:{color:"rgb(162, 191, 252)",fontStyle:"italic"}},{types:["deleted"],style:{color:"#f92672",fontStyle:"italic"}},{types:["inserted"],style:{color:"rgb(173, 219, 103)",fontStyle:"italic"}},{types:["comment"],style:{color:"#8292a2",fontStyle:"italic"}},{types:["string","url"],style:{color:"#a6e22e"}},{types:["variable"],style:{color:"#f8f8f2"}},{types:["number"],style:{color:"#ae81ff"}},{types:["builtin","char","constant","function","class-name"],style:{color:"#e6db74"}},{types:["punctuation"],style:{color:"#f8f8f2"}},{types:["selector","doctype"],style:{color:"#a6e22e",fontStyle:"italic"}},{types:["tag","operator","keyword"],style:{color:"#66d9ef"}},{types:["boolean"],style:{color:"#ae81ff"}},{types:["namespace"],style:{color:"rgb(178, 204, 214)",opacity:.7}},{types:["tag","property"],style:{color:"#f92672"}},{types:["attr-name"],style:{color:"#a6e22e !important"}},{types:["doctype"],style:{color:"#8292a2"}},{types:["rule"],style:{color:"#e6db74"}}]},ze=Me,Ue={plain:{color:"#bfc7d5",backgroundColor:"#292d3e"},styles:[{types:["comment"],style:{color:"rgb(105, 112, 152)",fontStyle:"italic"}},{types:["string","inserted"],style:{color:"rgb(195, 232, 141)"}},{types:["number"],style:{color:"rgb(247, 140, 108)"}},{types:["builtin","char","constant","function"],style:{color:"rgb(130, 170, 255)"}},{types:["punctuation","selector"],style:{color:"rgb(199, 146, 234)"}},{types:["variable"],style:{color:"rgb(191, 199, 213)"}},{types:["class-name","attr-name"],style:{color:"rgb(255, 203, 107)"}},{types:["tag","deleted"],style:{color:"rgb(255, 85, 114)"}},{types:["operator"],style:{color:"rgb(137, 221, 255)"}},{types:["boolean"],style:{color:"rgb(255, 88, 116)"}},{types:["keyword"],style:{fontStyle:"italic"}},{types:["doctype"],style:{color:"rgb(199, 146, 234)",fontStyle:"italic"}},{types:["namespace"],style:{color:"rgb(178, 204, 214)"}},{types:["url"],style:{color:"rgb(221, 221, 221)"}}]},Ge=Ue,Ze={plain:{color:"#9EFEFF",backgroundColor:"#2D2A55"},styles:[{types:["changed"],style:{color:"rgb(255, 238, 128)"}},{types:["deleted"],style:{color:"rgba(239, 83, 80, 0.56)"}},{types:["inserted"],style:{color:"rgb(173, 219, 103)"}},{types:["comment"],style:{color:"rgb(179, 98, 255)",fontStyle:"italic"}},{types:["punctuation"],style:{color:"rgb(255, 255, 255)"}},{types:["constant"],style:{color:"rgb(255, 98, 140)"}},{types:["string","url"],style:{color:"rgb(165, 255, 144)"}},{types:["variable"],style:{color:"rgb(255, 238, 128)"}},{types:["number","boolean"],style:{color:"rgb(255, 98, 140)"}},{types:["attr-name"],style:{color:"rgb(255, 180, 84)"}},{types:["keyword","operator","property","namespace","tag","selector","doctype"],style:{color:"rgb(255, 157, 0)"}},{types:["builtin","char","constant","function","class-name"],style:{color:"rgb(250, 208, 0)"}}]},Ve=Ze,qe={plain:{backgroundColor:"linear-gradient(to bottom, #2a2139 75%, #34294f)",backgroundImage:"#34294f",color:"#f92aad",textShadow:"0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3"},styles:[{types:["comment","block-comment","prolog","doctype","cdata"],style:{color:"#495495",fontStyle:"italic"}},{types:["punctuation"],style:{color:"#ccc"}},{types:["tag","attr-name","namespace","number","unit","hexcode","deleted"],style:{color:"#e2777a"}},{types:["property","selector"],style:{color:"#72f1b8",textShadow:"0 0 2px #100c0f, 0 0 10px #257c5575, 0 0 35px #21272475"}},{types:["function-name"],style:{color:"#6196cc"}},{types:["boolean","selector-id","function"],style:{color:"#fdfdfd",textShadow:"0 0 2px #001716, 0 0 3px #03edf975, 0 0 5px #03edf975, 0 0 8px #03edf975"}},{types:["class-name","maybe-class-name","builtin"],style:{color:"#fff5f6",textShadow:"0 0 2px #000, 0 0 10px #fc1f2c75, 0 0 5px #fc1f2c75, 0 0 25px #fc1f2c75"}},{types:["constant","symbol"],style:{color:"#f92aad",textShadow:"0 0 2px #100c0f, 0 0 5px #dc078e33, 0 0 10px #fff3"}},{types:["important","atrule","keyword","selector-class"],style:{color:"#f4eee4",textShadow:"0 0 2px #393a33, 0 0 8px #f39f0575, 0 0 2px #f39f0575"}},{types:["string","char","attr-value","regex","variable"],style:{color:"#f87c32"}},{types:["parameter"],style:{fontStyle:"italic"}},{types:["entity","url"],style:{color:"#67cdcc"}},{types:["operator"],style:{color:"ffffffee"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["entity"],style:{cursor:"help"}},{types:["inserted"],style:{color:"green"}}]},We=qe,He={plain:{color:"#282a2e",backgroundColor:"#ffffff"},styles:[{types:["comment"],style:{color:"rgb(197, 200, 198)"}},{types:["string","number","builtin","variable"],style:{color:"rgb(150, 152, 150)"}},{types:["class-name","function","tag","attr-name"],style:{color:"rgb(40, 42, 46)"}}]},Ye=He,Ke={plain:{color:"#9CDCFE",backgroundColor:"#1E1E1E"},styles:[{types:["prolog"],style:{color:"rgb(0, 0, 128)"}},{types:["comment"],style:{color:"rgb(106, 153, 85)"}},{types:["builtin","changed","keyword","interpolation-punctuation"],style:{color:"rgb(86, 156, 214)"}},{types:["number","inserted"],style:{color:"rgb(181, 206, 168)"}},{types:["constant"],style:{color:"rgb(100, 102, 149)"}},{types:["attr-name","variable"],style:{color:"rgb(156, 220, 254)"}},{types:["deleted","string","attr-value","template-punctuation"],style:{color:"rgb(206, 145, 120)"}},{types:["selector"],style:{color:"rgb(215, 186, 125)"}},{types:["tag"],style:{color:"rgb(78, 201, 176)"}},{types:["tag"],languages:["markup"],style:{color:"rgb(86, 156, 214)"}},{types:["punctuation","operator"],style:{color:"rgb(212, 212, 212)"}},{types:["punctuation"],languages:["markup"],style:{color:"#808080"}},{types:["function"],style:{color:"rgb(220, 220, 170)"}},{types:["class-name"],style:{color:"rgb(78, 201, 176)"}},{types:["char"],style:{color:"rgb(209, 105, 105)"}}]},ce=Ke,Xe={plain:{color:"#000000",backgroundColor:"#ffffff"},styles:[{types:["comment"],style:{color:"rgb(0, 128, 0)"}},{types:["builtin"],style:{color:"rgb(0, 112, 193)"}},{types:["number","variable","inserted"],style:{color:"rgb(9, 134, 88)"}},{types:["operator"],style:{color:"rgb(0, 0, 0)"}},{types:["constant","char"],style:{color:"rgb(129, 31, 63)"}},{types:["tag"],style:{color:"rgb(128, 0, 0)"}},{types:["attr-name"],style:{color:"rgb(255, 0, 0)"}},{types:["deleted","string"],style:{color:"rgb(163, 21, 21)"}},{types:["changed","punctuation"],style:{color:"rgb(4, 81, 165)"}},{types:["function","keyword"],style:{color:"rgb(0, 0, 255)"}},{types:["class-name"],style:{color:"rgb(38, 127, 153)"}}]},Qe=Xe,Je={plain:{color:"#f8fafc",backgroundColor:"#011627"},styles:[{types:["prolog"],style:{color:"#000080"}},{types:["comment"],style:{color:"#6A9955"}},{types:["builtin","changed","keyword","interpolation-punctuation"],style:{color:"#569CD6"}},{types:["number","inserted"],style:{color:"#B5CEA8"}},{types:["constant"],style:{color:"#f8fafc"}},{types:["attr-name","variable"],style:{color:"#9CDCFE"}},{types:["deleted","string","attr-value","template-punctuation"],style:{color:"#cbd5e1"}},{types:["selector"],style:{color:"#D7BA7D"}},{types:["tag"],style:{color:"#0ea5e9"}},{types:["tag"],languages:["markup"],style:{color:"#0ea5e9"}},{types:["punctuation","operator"],style:{color:"#D4D4D4"}},{types:["punctuation"],languages:["markup"],style:{color:"#808080"}},{types:["function"],style:{color:"#7dd3fc"}},{types:["class-name"],style:{color:"#0ea5e9"}},{types:["char"],style:{color:"#D16969"}}]},et=Je,tt={plain:{color:"#0f172a",backgroundColor:"#f1f5f9"},styles:[{types:["prolog"],style:{color:"#000080"}},{types:["comment"],style:{color:"#6A9955"}},{types:["builtin","changed","keyword","interpolation-punctuation"],style:{color:"#0c4a6e"}},{types:["number","inserted"],style:{color:"#B5CEA8"}},{types:["constant"],style:{color:"#0f172a"}},{types:["attr-name","variable"],style:{color:"#0c4a6e"}},{types:["deleted","string","attr-value","template-punctuation"],style:{color:"#64748b"}},{types:["selector"],style:{color:"#D7BA7D"}},{types:["tag"],style:{color:"#0ea5e9"}},{types:["tag"],languages:["markup"],style:{color:"#0ea5e9"}},{types:["punctuation","operator"],style:{color:"#475569"}},{types:["punctuation"],languages:["markup"],style:{color:"#808080"}},{types:["function"],style:{color:"#0e7490"}},{types:["class-name"],style:{color:"#0ea5e9"}},{types:["char"],style:{color:"#D16969"}}]},nt=tt,rt={plain:{backgroundColor:"hsl(220, 13%, 18%)",color:"hsl(220, 14%, 71%)",textShadow:"0 1px rgba(0, 0, 0, 0.3)"},styles:[{types:["comment","prolog","cdata"],style:{color:"hsl(220, 10%, 40%)"}},{types:["doctype","punctuation","entity"],style:{color:"hsl(220, 14%, 71%)"}},{types:["attr-name","class-name","maybe-class-name","boolean","constant","number","atrule"],style:{color:"hsl(29, 54%, 61%)"}},{types:["keyword"],style:{color:"hsl(286, 60%, 67%)"}},{types:["property","tag","symbol","deleted","important"],style:{color:"hsl(355, 65%, 65%)"}},{types:["selector","string","char","builtin","inserted","regex","attr-value"],style:{color:"hsl(95, 38%, 62%)"}},{types:["variable","operator","function"],style:{color:"hsl(207, 82%, 66%)"}},{types:["url"],style:{color:"hsl(187, 47%, 55%)"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"hsl(220, 14%, 71%)"}}]},at=rt,st={plain:{backgroundColor:"hsl(230, 1%, 98%)",color:"hsl(230, 8%, 24%)"},styles:[{types:["comment","prolog","cdata"],style:{color:"hsl(230, 4%, 64%)"}},{types:["doctype","punctuation","entity"],style:{color:"hsl(230, 8%, 24%)"}},{types:["attr-name","class-name","boolean","constant","number","atrule"],style:{color:"hsl(35, 99%, 36%)"}},{types:["keyword"],style:{color:"hsl(301, 63%, 40%)"}},{types:["property","tag","symbol","deleted","important"],style:{color:"hsl(5, 74%, 59%)"}},{types:["selector","string","char","builtin","inserted","regex","attr-value","punctuation"],style:{color:"hsl(119, 34%, 47%)"}},{types:["variable","operator","function"],style:{color:"hsl(221, 87%, 60%)"}},{types:["url"],style:{color:"hsl(198, 99%, 37%)"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"hsl(230, 8%, 24%)"}}]},ot=st,lt={plain:{color:"#ebdbb2",backgroundColor:"#292828"},styles:[{types:["imports","class-name","maybe-class-name","constant","doctype","builtin","function"],style:{color:"#d8a657"}},{types:["property-access"],style:{color:"#7daea3"}},{types:["tag"],style:{color:"#e78a4e"}},{types:["attr-name","char","url","regex"],style:{color:"#a9b665"}},{types:["attr-value","string"],style:{color:"#89b482"}},{types:["comment","prolog","cdata","operator","inserted"],style:{color:"#a89984"}},{types:["delimiter","boolean","keyword","selector","important","atrule","property","variable","deleted"],style:{color:"#ea6962"}},{types:["entity","number","symbol"],style:{color:"#d3869b"}}]},it=lt,ut={plain:{color:"#654735",backgroundColor:"#f9f5d7"},styles:[{types:["delimiter","boolean","keyword","selector","important","atrule","property","variable","deleted"],style:{color:"#af2528"}},{types:["imports","class-name","maybe-class-name","constant","doctype","builtin"],style:{color:"#b4730e"}},{types:["string","attr-value"],style:{color:"#477a5b"}},{types:["property-access"],style:{color:"#266b79"}},{types:["function","attr-name","char","url"],style:{color:"#72761e"}},{types:["tag"],style:{color:"#b94c07"}},{types:["comment","prolog","cdata","operator","inserted"],style:{color:"#a89984"}},{types:["entity","number","symbol"],style:{color:"#924f79"}}]},ct=ut,pt=e=>$.useCallback(t=>{var n=t,{className:l,style:c,line:h}=n,d=ie(n,["className","style","line"]);const m=H(D({},d),{className:se("token-line",l)});return typeof e=="object"&&"plain"in e&&(m.style=e.plain),typeof c=="object"&&(m.style=D(D({},m.style||{}),c)),m},[e]),dt=e=>{const t=$.useCallback(({types:n,empty:l})=>{if(e!=null){{if(n.length===1&&n[0]==="plain")return l!=null?{display:"inline-block"}:void 0;if(n.length===1&&l!=null)return e[n[0]]}return Object.assign(l!=null?{display:"inline-block"}:{},...n.map(c=>e[c]))}},[e]);return $.useCallback(n=>{var l=n,{token:c,className:h,style:d}=l,m=ie(l,["token","className","style"]);const f=H(D({},m),{className:se("token",...c.types,h),children:c.content,style:t(c)});return d!=null&&(f.style=D(D({},f.style||{}),d)),f},[t])},gt=/\r\n|\r|\n/,te=e=>{e.length===0?e.push({types:["plain"],content:` +`,empty:!0}):e.length===1&&e[0].content===""&&(e[0].content=` +`,e[0].empty=!0)},ne=(e,t)=>{const n=e.length;return n>0&&e[n-1]===t?e:e.concat(t)},yt=e=>{const t=[[]],n=[e],l=[0],c=[e.length];let h=0,d=0,m=[];const f=[m];for(;d>-1;){for(;(h=l[d]++)0?u:["plain"],b=w):(u=ne(u,w.type),w.alias&&(u=ne(u,w.alias)),b=w.content),typeof b!="string"){d++,t.push(u),n.push(b),l.push(0),c.push(b.length);continue}const k=b.split(gt),a=k.length;m.push({types:u,content:k[0]});for(let r=1;r$.useMemo(()=>{if(n==null)return re([t]);const c={code:t,grammar:n,language:l,tokens:[]};return e.hooks.run("before-tokenize",c),c.tokens=e.tokenize(t,n),e.hooks.run("after-tokenize",c),re(c.tokens)},[t,n,l,e]),bt=(e,t)=>{const{plain:n}=e,l=e.styles.reduce((c,h)=>{const{languages:d,style:m}=h;return d&&!d.includes(t)||h.types.forEach(f=>{const b=D(D({},c[f]),m);c[f]=b}),c},{});return l.root=n,l.plain=H(D({},n),{backgroundColor:void 0}),l},mt=bt,ht=({children:e,language:t,code:n,theme:l,prism:c})=>{const h=t.toLowerCase(),d=mt(l,h),m=pt(d),f=dt(d),b=c.languages[h],u=ft({prism:c,language:h,code:n,grammar:b});return e({tokens:u,className:`prism-code language-${h}`,style:d!=null?d.root:{},getLineProps:m,getTokenProps:f})},vt=e=>$.createElement(ht,H(D({},e),{prism:e.prism||s,theme:e.theme||ce,code:e.code,language:e.language}));/*! Bundled license information: + +prismjs/prism.js: + (** + * Prism: Lightweight, robust, elegant syntax highlighting + * + * @license MIT + * @author Lea Verou + * @namespace + * @public + *) +*/function wt(e,t){return`/file-content.json?${new URLSearchParams({token:t,path:e}).toString()}`}function kt(e){var n;const t=(n=e.knowledgeMeta)==null?void 0:n.content;return typeof t!="string"?null:{path:e.filePath??"",language:pe(e.filePath),content:t,sizeBytes:new TextEncoder().encode(t).byteLength,lineCount:t.length===0?0:t.split(/\r\n|\n|\r/).length}}function pe(e){var l;const t=(l=e==null?void 0:e.split(".").pop())==null?void 0:l.toLowerCase();return t?{css:"css",go:"go",html:"markup",js:"javascript",jsx:"jsx",json:"json",md:"markdown",py:"python",rb:"ruby",rs:"rust",sh:"bash",ts:"typescript",tsx:"tsx",yaml:"yaml",yml:"yaml"}[t]??"text":"text"}function xt(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function Et(e,t){return e?t==="markdown"||/\.md$/i.test(e.path):!1}function Nt({accessToken:e,presentation:t="sidebar",onClose:n,onExpand:l}){const c=M(v=>v.graph),h=M(v=>v.domainGraph),d=M(v=>v.viewMode),m=M(v=>v.codeViewerNodeId),f=M(v=>v.closeCodeViewer),b=d==="domain"&&h?h:c,u=(b==null?void 0:b.nodes.find(v=>v.id===m))??(c==null?void 0:c.nodes.find(v=>v.id===m))??null,[g,w]=$.useState({status:"idle",source:null,error:null}),{t:k}=ye();$.useEffect(()=>{if(!(u!=null&&u.filePath)){w({status:"error",source:null,error:"This node does not have a file path."});return}const v=kt(u);if(v){w({status:"loaded",source:v,error:null});return}if(e==="__demo__"){w({status:"error",source:null,error:"Source preview is available only when the local dashboard server is running."});return}const S=new AbortController;return w({status:"loading",source:null,error:null}),fetch(wt(u.filePath,e),{signal:S.signal}).then(async A=>{if(!(A.headers.get("content-type")??"").includes("application/json"))throw new Error("Source endpoint returned non-JSON content. Run the local dashboard server or use embedded graph content.");const _=await A.json();if(!A.ok)throw new Error("error"in _&&_.error?_.error:"Source unavailable");w({status:"loaded",source:_,error:null})}).catch(A=>{S.signal.aborted||w({status:"error",source:null,error:A instanceof Error?A.message:String(A)})}),()=>S.abort()},[e,u]);const a=$.useMemo(()=>u!=null&&u.lineRange?{start:u.lineRange[0],end:u.lineRange[1]}:null,[u==null?void 0:u.lineRange]);if(!u)return x.jsx("div",{className:"h-full w-full flex items-center justify-center bg-surface",children:x.jsx("p",{className:"text-text-muted text-sm",children:k.codeViewer.noFile})});const r=g.source,o=(r==null?void 0:r.language)??pe(u.filePath),p=Et(r,o),i=a?`${k.codeViewer.lines} ${a.start}-${a.end}`:k.codeViewer.fullFile,y=t==="modal",E=n??f;return x.jsxs("div",{className:"h-full w-full flex flex-col bg-surface overflow-hidden",children:[x.jsxs("div",{className:"flex items-start gap-3 px-4 py-3 bg-elevated border-b border-border-subtle shrink-0",children:[x.jsxs("div",{className:"min-w-0 flex-1",children:[x.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[x.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-wider px-2 py-0.5 rounded border",style:{color:"var(--color-node-file)",borderColor:"color-mix(in srgb, var(--color-node-file) 30%, transparent)",backgroundColor:"color-mix(in srgb, var(--color-node-file) 10%, transparent)"},children:o}),x.jsx("span",{className:"text-[10px] text-text-muted",children:i})]}),x.jsx("div",{className:"text-sm font-heading text-text-primary truncate",title:u.name,children:u.name}),u.filePath&&x.jsx("div",{className:"text-[11px] font-mono text-text-muted truncate mt-0.5",title:u.filePath,children:u.filePath})]}),x.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[l&&x.jsx("button",{type:"button",onClick:l,className:"text-text-muted hover:text-text-primary transition-colors",title:k.codeViewer.openLarger,"aria-label":k.codeViewer.openLarger,children:x.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:x.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 9V4h5M20 15v5h-5M4 4l6 6M20 20l-6-6"})})}),x.jsx("button",{type:"button",onClick:E,className:"text-text-muted hover:text-text-primary transition-colors",title:y?k.codeViewer.closeExpanded:k.codeViewer.closeViewer,"aria-label":y?k.codeViewer.closeExpanded:k.codeViewer.closeViewer,children:x.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:x.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})]}),x.jsxs("div",{className:"flex-1 min-h-0 overflow-auto bg-root",children:[g.status==="loading"&&x.jsx("div",{className:"p-5 text-sm text-text-muted",children:k.codeViewer.loading}),g.status==="error"&&x.jsx("div",{className:"p-5",children:x.jsxs("div",{className:"rounded-lg border border-border-subtle bg-elevated p-4",children:[x.jsx("div",{className:"text-sm font-medium text-text-primary mb-2",children:k.codeViewer.sourceUnavailable}),x.jsx("p",{className:"text-sm text-text-secondary leading-relaxed",children:g.error})]})}),r&&x.jsxs(x.Fragment,{children:[x.jsxs("div",{className:"px-4 py-2 border-b border-border-subtle bg-surface text-[11px] text-text-muted flex items-center justify-between",children:[x.jsxs("span",{children:[r.lineCount," ",k.codeViewer.linesLabel]}),x.jsx("span",{children:xt(r.sizeBytes)})]}),p?x.jsx("article",{className:`markdown-reader ${y?"markdown-reader-modal":""}`,children:x.jsx(fe,{children:r.content})}):x.jsx(vt,{code:r.content,language:o,theme:ue.vsDark,children:({className:v,style:S,tokens:A,getLineProps:R,getTokenProps:_})=>x.jsx("pre",{className:`${v} min-w-max p-0 m-0 ${y?"text-xs leading-5":"text-[11px] leading-5"} font-mono`,style:{...S,background:"transparent"},children:A.map((L,O)=>{const T=O+1,z=a!==null&&T>=a.start&&T<=a.end,F=R({line:L});return x.jsxs("div",{...F,className:`${F.className} flex ${z?"bg-accent/15":"hover:bg-elevated/40"}`,children:[x.jsx("span",{className:"w-12 shrink-0 select-none border-r border-border-subtle pr-3 text-right text-text-muted bg-surface/60",children:T}),x.jsx("span",{className:"pl-3 pr-6 whitespace-pre",children:L.map((N,B)=>x.jsx("span",{..._({token:N})},B))})]},T)})})})]})]})]})}export{Nt as default}; diff --git a/wishfulfilled-dashboard/assets/KeyboardShortcutsHelp-CcG7Ga98.js b/wishfulfilled-dashboard/assets/KeyboardShortcutsHelp-CcG7Ga98.js new file mode 100644 index 0000000..ee440cd --- /dev/null +++ b/wishfulfilled-dashboard/assets/KeyboardShortcutsHelp-CcG7Ga98.js @@ -0,0 +1 @@ +import{j as e}from"./react-vendor-BVoutfaX.js";import{a as d,f as x}from"./index-C3mrB48A.js";import"./xyflow-CYMCcsWN.js";import"./graph-layout-7tFr_anw.js";import"./elk-CXeXGyKz.js";import"./graphology-BgTy_cc3.js";function j({shortcuts:i,onClose:o}){const{t:s}=d(),n=i.reduce((t,r)=>(t[r.category]||(t[r.category]=[]),t[r.category].push(r),t),{}),l={General:s.keyboardShortcuts.general,Navigation:s.keyboardShortcuts.navigation,Tour:s.keyboardShortcuts.tour,View:s.keyboardShortcuts.view};return e.jsx("div",{className:"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50",onClick:o,children:e.jsxs("div",{className:"glass rounded-lg shadow-2xl max-w-2xl w-full max-h-[80vh] overflow-auto m-4",onClick:t=>t.stopPropagation(),children:[e.jsxs("div",{className:"sticky top-0 glass-heavy border-b border-border-subtle px-6 py-4 flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-xl font-heading text-text-primary",children:s.keyboardShortcuts.title}),e.jsx("p",{className:"text-xs text-text-muted mt-1",children:s.keyboardShortcuts.toggleHint})]}),e.jsx("button",{onClick:o,className:"text-text-muted hover:text-text-primary transition-colors",children:e.jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),e.jsx("div",{className:"p-6 space-y-6",children:Object.entries(n).map(([t,r])=>e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-semibold text-accent uppercase tracking-wider mb-3",children:l[t]??t}),e.jsx("div",{className:"space-y-2",children:r.map((a,c)=>e.jsxs("div",{className:"flex items-center justify-between py-2 px-3 rounded hover:bg-elevated transition-colors",children:[e.jsx("span",{className:"text-sm text-text-secondary",children:a.description}),e.jsx("kbd",{className:"kbd",children:x(a)})]},c))})]},t))}),e.jsx("div",{className:"sticky bottom-0 glass-heavy border-t border-border-subtle px-6 py-3 text-center",children:e.jsx("p",{className:"text-xs text-text-muted",children:s.keyboardShortcuts.closeHint})})]})})}export{j as default}; diff --git a/wishfulfilled-dashboard/assets/LearnPanel-DahajoWG.js b/wishfulfilled-dashboard/assets/LearnPanel-DahajoWG.js new file mode 100644 index 0000000..f311aca --- /dev/null +++ b/wishfulfilled-dashboard/assets/LearnPanel-DahajoWG.js @@ -0,0 +1 @@ +import{a as v,j as e}from"./react-vendor-BVoutfaX.js";import{u as n,a as y}from"./index-C3mrB48A.js";import{M as k}from"./markdown-DpllbSd9.js";import"./xyflow-CYMCcsWN.js";import"./graph-layout-7tFr_anw.js";import"./elk-CXeXGyKz.js";import"./graphology-BgTy_cc3.js";function A(){const a=n(t=>t.graph),u=n(t=>t.tourActive),o=n(t=>t.currentTourStep),p=n(t=>t.startTour),d=n(t=>t.stopTour),h=n(t=>t.setTourStep),b=n(t=>t.nextTourStep),f=n(t=>t.prevTourStep),j=n(t=>t.selectNode),{t:r}=y(),c=v.useMemo(()=>a!=null&&a.tour?[...a.tour].sort((t,s)=>t.order-s.order):[],[a==null?void 0:a.tour]);if(!(c.length>0))return e.jsx("div",{className:"h-full w-full flex items-center justify-center",children:e.jsxs("div",{className:"text-center px-4",children:[e.jsx("div",{className:"text-2xl mb-2 text-text-muted",children:"🧭"}),e.jsx("p",{className:"text-text-muted text-sm",children:r.learnPanel.noTour}),e.jsx("p",{className:"text-text-muted text-xs mt-1",children:r.learnPanel.noTourHint})]})});if(!u)return e.jsxs("div",{className:"h-full w-full overflow-auto p-5",children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("h2",{className:"text-lg font-heading text-text-primary mb-1",children:r.learnPanel.projectTour}),e.jsxs("p",{className:"text-xs text-text-muted",children:[c.length," ",r.learnPanel.steps," · ",r.learnPanel.guidedWalkthrough]})]}),e.jsx("button",{onClick:p,className:"w-full mb-4 bg-accent/10 border border-accent/30 text-accent text-sm font-medium py-2.5 px-4 rounded-lg hover:bg-accent/20 transition-colors",children:r.learnPanel.startTour}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:r.learnPanel.steps}),c.map((t,s)=>e.jsxs("div",{className:"flex items-start gap-2 text-xs bg-elevated rounded-lg px-3 py-2 border border-border-subtle",children:[e.jsxs("span",{className:"text-accent font-mono shrink-0 mt-0.5",children:[s+1,"."]}),e.jsx("span",{className:"text-text-secondary",children:t.title})]},t.order))]})]});const l=c[o];if(!l)return null;const i=c.length,g=(o+1)/i*100,N=o===0,x=o===i-1;return e.jsxs("div",{className:"h-full w-full flex flex-col overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between px-3 py-2 border-b border-border-subtle shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider",children:r.learnPanel.tour}),e.jsxs("span",{className:"text-xs text-text-muted",children:[o+1," / ",i]})]}),e.jsx("button",{onClick:d,className:"text-[10px] text-text-muted hover:text-text-secondary transition-colors",children:r.learnPanel.exitTour})]}),e.jsx("div",{className:"h-1 bg-elevated shrink-0",children:e.jsx("div",{className:"h-full bg-accent transition-all duration-300",style:{width:`${g}%`}})}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 min-h-0",children:[e.jsx("h2",{className:"text-lg font-heading text-text-primary mb-3",children:l.title}),e.jsx("div",{className:"text-sm text-text-secondary leading-relaxed mb-4 tour-markdown",children:e.jsx(k,{components:{p:({children:t})=>e.jsx("p",{className:"mb-1.5 last:mb-0",children:t}),strong:({children:t})=>e.jsx("strong",{className:"font-semibold text-text-primary",children:t}),code:({className:t,children:s})=>(t==null?void 0:t.includes("language-"))?e.jsx("code",{className:"block bg-elevated rounded px-2 py-1.5 mb-1.5 overflow-x-auto text-[11px] leading-relaxed",children:s}):e.jsx("code",{className:"bg-elevated rounded px-1 py-0.5 text-[11px]",children:s}),ul:({children:t})=>e.jsx("ul",{className:"list-disc list-inside mb-1.5 space-y-0.5",children:t}),ol:({children:t})=>e.jsx("ol",{className:"list-decimal list-inside mb-1.5 space-y-0.5",children:t})},children:l.description})}),l.languageLesson&&e.jsxs("div",{className:"bg-accent/5 border border-accent/20 rounded p-3 mb-4",children:[e.jsx("h4",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-1.5",children:"Language Lesson"}),e.jsx("p",{className:"text-sm text-text-secondary leading-relaxed",children:l.languageLesson})]}),l.nodeIds.length>0&&e.jsxs("div",{className:"mb-4",children:[e.jsx("h4",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:"Referenced Components"}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:l.nodeIds.map(t=>{const s=a==null?void 0:a.nodes.find(m=>m.id===t);return e.jsx("button",{onClick:()=>j(t),className:"text-[11px] glass text-text-secondary px-2.5 py-1 rounded-full hover:text-text-primary transition-colors cursor-pointer",children:(s==null?void 0:s.name)??t},t)})})]})]}),e.jsxs("div",{className:"px-3 py-2 border-t border-border-subtle shrink-0",children:[e.jsx("div",{className:"flex justify-center gap-1.5 mb-2",children:c.map((t,s)=>e.jsx("button",{onClick:()=>h(s),className:`w-2 h-2 rounded-full transition-colors ${s===o?"bg-accent":"bg-elevated hover:bg-surface"}`,"aria-label":`Go to step ${s+1}`},s))}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("button",{onClick:f,disabled:N,className:"flex-1 text-xs bg-elevated text-text-secondary py-1.5 rounded-lg hover:bg-surface disabled:opacity-40 disabled:cursor-not-allowed transition-colors",children:r.learnPanel.prev}),e.jsx("button",{onClick:x?d:b,className:"flex-1 text-xs bg-accent/10 border border-accent/30 text-accent py-1.5 rounded-lg hover:bg-accent/20 transition-colors",children:x?r.learnPanel.finish:r.learnPanel.next})]})]})]})}export{A as default}; diff --git a/wishfulfilled-dashboard/assets/OnboardingOverlay-BCZlWHWy.js b/wishfulfilled-dashboard/assets/OnboardingOverlay-BCZlWHWy.js new file mode 100644 index 0000000..2322181 --- /dev/null +++ b/wishfulfilled-dashboard/assets/OnboardingOverlay-BCZlWHWy.js @@ -0,0 +1 @@ +import{a as d,j as t}from"./react-vendor-BVoutfaX.js";import{a as b}from"./index-C3mrB48A.js";import"./xyflow-CYMCcsWN.js";import"./graph-layout-7tFr_anw.js";import"./elk-CXeXGyKz.js";import"./graphology-BgTy_cc3.js";const p="ua-onboarding-title";function P({onDismiss:a}){const{t:o}=b(),i=o.onboarding.steps,[e,s]=d.useState(0);d.useEffect(()=>{const r=n=>{n.key==="Escape"&&(n.stopPropagation(),a(!1))};return document.addEventListener("keydown",r,!0),()=>document.removeEventListener("keydown",r,!0)},[a]);const m=e===0,x=e===i.length-1,l=i[e];return t.jsxs("div",{style:h,onClick:r=>{r.target===r.currentTarget&&a(!1)},children:[t.jsx("style",{children:u}),t.jsxs("div",{role:"dialog","aria-modal":"true","aria-labelledby":p,style:f,children:[t.jsxs("div",{style:v,children:[t.jsxs("span",{style:S,children:["0",e+1]}),t.jsxs("span",{children:[" / 0",i.length]}),t.jsx("span",{style:j}),t.jsx("span",{children:o.onboarding.header})]}),t.jsx("h2",{id:p,style:k,children:l.title}),t.jsx("p",{style:z,children:l.body}),l.hint&&t.jsxs("blockquote",{style:E,children:[t.jsx("span",{style:{color:"var(--color-accent)",marginRight:8},children:"·"}),l.hint]}),t.jsx("div",{style:I,children:i.map((r,n)=>t.jsx("div",{style:{...T,background:n===e?"var(--color-accent)":"var(--color-border-medium)",width:n===e?28:6}},n))}),t.jsxs("div",{style:w,children:[t.jsx("button",{type:"button",onClick:()=>a(!0),style:{...c,...y},children:o.onboarding.skipForever}),t.jsx("div",{style:{flex:1}}),!m&&t.jsx("button",{type:"button",onClick:()=>s(e-1),style:{...c,...y},children:o.onboarding.prev}),x?t.jsx("button",{type:"button",onClick:()=>a(!0),style:{...c,...g},children:o.onboarding.finish}):t.jsx("button",{type:"button",onClick:()=>s(e+1),style:{...c,...g},children:o.onboarding.next})]})]})]})}const u="@keyframes ua-fade-in { from { opacity: 0 } to { opacity: 1 } }",h={position:"fixed",inset:0,background:"rgba(0, 0, 0, 0.78)",backdropFilter:"blur(6px)",zIndex:9999,display:"flex",alignItems:"center",justifyContent:"center",padding:16,fontFamily:"var(--font-sans)",animation:"ua-fade-in 0.4s cubic-bezier(0.22, 1, 0.36, 1)"},f={background:"var(--color-elevated)",color:"var(--color-text-primary)",maxWidth:580,width:"100%",padding:"48px 48px 36px",border:"1px solid var(--color-border-subtle)",borderTop:"2px solid var(--color-accent)",position:"relative"},v={fontSize:"0.72rem",letterSpacing:"0.3em",color:"var(--color-text-muted)",textTransform:"uppercase",marginBottom:24,display:"flex",alignItems:"center",flexWrap:"wrap",gap:4},S={fontFamily:"var(--font-heading)",color:"var(--color-accent)",fontSize:"0.9rem",letterSpacing:"0.1em",marginRight:4},j={width:4,height:4,background:"var(--color-accent)",borderRadius:"50%",margin:"0 12px"},k={fontFamily:"var(--font-heading)",fontSize:"1.7rem",fontWeight:400,letterSpacing:"0.02em",lineHeight:1.3,marginBottom:16,color:"var(--color-text-primary)"},z={fontSize:"0.98rem",lineHeight:1.7,color:"var(--color-text-secondary)",marginBottom:0},E={margin:"20px 0 0",padding:"12px 18px",borderLeft:"2px solid var(--color-border-medium)",background:"var(--color-accent-overlay-bg)",fontSize:"0.86rem",color:"var(--color-accent)",fontStyle:"italic"},I={display:"flex",gap:6,marginTop:36,marginBottom:28},T={height:4,borderRadius:2,transition:"width 0.5s cubic-bezier(0.22, 1, 0.36, 1), background 0.3s"},w={display:"flex",alignItems:"center",gap:10},c={padding:"10px 22px",fontSize:"0.82rem",letterSpacing:"0.12em",textTransform:"uppercase",border:"1px solid",cursor:"pointer",fontFamily:"inherit",transition:"all 0.3s cubic-bezier(0.22, 1, 0.36, 1)",fontWeight:400},y={background:"transparent",borderColor:"var(--color-border-medium)",color:"var(--color-text-muted)"},g={background:"var(--color-accent)",borderColor:"var(--color-accent)",color:"var(--color-root)",fontWeight:500};export{P as default}; diff --git a/wishfulfilled-dashboard/assets/PathFinderModal-BHT2q_Em.js b/wishfulfilled-dashboard/assets/PathFinderModal-BHT2q_Em.js new file mode 100644 index 0000000..53ee4e3 --- /dev/null +++ b/wishfulfilled-dashboard/assets/PathFinderModal-BHT2q_Em.js @@ -0,0 +1 @@ +import{a as d,j as e}from"./react-vendor-BVoutfaX.js";import{u as j}from"./index-C3mrB48A.js";import"./xyflow-CYMCcsWN.js";import"./graph-layout-7tFr_anw.js";import"./elk-CXeXGyKz.js";import"./graphology-BgTy_cc3.js";function I({isOpen:i,onClose:o}){const h=j(t=>t.graph),v=j(t=>t.selectNode),[n,N]=d.useState(""),[l,k]=d.useState(""),[c,x]=d.useState(null),[g,m]=d.useState(!1),p=d.useRef(null);if(d.useEffect(()=>{if(!i)return;const t=r=>{p.current&&!p.current.contains(r.target)&&o()};return document.addEventListener("mousedown",t),()=>document.removeEventListener("mousedown",t)},[i,o]),d.useEffect(()=>{if(!i)return;const t=r=>{r.key==="Escape"&&o()};return document.addEventListener("keydown",t),()=>document.removeEventListener("keydown",t)},[i,o]),!i||!h)return null;const f=h.nodes,w=h.edges,y=()=>{if(!n||!l||n===l){x(null);return}m(!0);const t=new Map;for(const s of w)t.has(s.source)||t.set(s.source,[]),t.get(s.source).push(s.target),t.has(s.target)||t.set(s.target,[]),t.get(s.target).push(s.source);const r=[{nodeId:n,path:[n]}],a=new Set([n]);for(;r.length>0;){const{nodeId:s,path:b}=r.shift();if(s===l){x(b),m(!1);return}const E=t.get(s)??[];for(const u of E)a.has(u)||(a.add(u),r.push({nodeId:u,path:[...b,u]}))}x([]),m(!1)},L=t=>{v(t),o()},M=new Map(f.map(t=>[t.id,t]));return e.jsx("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-root/80 backdrop-blur-sm",children:e.jsxs("div",{ref:p,className:"glass-heavy rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] overflow-hidden animate-fade-slide-in",children:[e.jsxs("div",{className:"flex items-center justify-between px-5 py-4 border-b border-border-subtle",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("svg",{className:"w-5 h-5 text-gold",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"})}),e.jsx("h2",{className:"font-heading text-xl text-text-primary",children:"Dependency Path Finder"})]}),e.jsx("button",{onClick:o,className:"text-text-muted hover:text-text-primary transition-colors",children:e.jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),e.jsxs("div",{className:"p-5 space-y-4 overflow-y-auto max-h-[calc(80vh-180px)]",children:[e.jsx("p",{className:"text-sm text-text-secondary",children:"Find the shortest path between two nodes in the dependency graph."}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2",children:"From Node"}),e.jsxs("select",{value:n,onChange:t=>{N(t.target.value),x(null)},className:"w-full bg-elevated text-text-primary text-sm rounded-lg px-3 py-2 border border-border-subtle focus:outline-none focus:border-gold/50",children:[e.jsx("option",{value:"",children:"Select a node..."}),f.map(t=>e.jsxs("option",{value:t.id,children:[t.name," (",t.type,")"]},t.id))]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2",children:"To Node"}),e.jsxs("select",{value:l,onChange:t=>{k(t.target.value),x(null)},className:"w-full bg-elevated text-text-primary text-sm rounded-lg px-3 py-2 border border-border-subtle focus:outline-none focus:border-gold/50",children:[e.jsx("option",{value:"",children:"Select a node..."}),f.map(t=>e.jsxs("option",{value:t.id,children:[t.name," (",t.type,")"]},t.id))]})]}),e.jsx("button",{onClick:y,disabled:!n||!l||n===l||g,className:"w-full bg-gold/10 border border-gold/30 text-gold text-sm font-medium py-2.5 px-4 rounded-lg hover:bg-gold/20 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed",children:g?"Searching...":"Find Path"}),c!==null&&e.jsx("div",{className:"mt-4",children:c.length===0?e.jsxs("div",{className:"bg-red-900/20 border border-red-700/50 rounded-lg p-4 text-center",children:[e.jsx("svg",{className:"w-8 h-8 text-red-400 mx-auto mb-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"})}),e.jsx("p",{className:"text-sm text-red-200",children:"No path found between these nodes."})]}):e.jsxs("div",{className:"bg-elevated border border-border-subtle rounded-lg p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx("svg",{className:"w-4 h-4 text-green-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"})}),e.jsxs("h3",{className:"text-sm font-semibold text-text-primary",children:["Path Found (",c.length," nodes)"]})]}),e.jsx("div",{className:"space-y-2",children:c.map((t,r)=>{const a=M.get(t);if(!a)return null;const s=r===c.length-1;return e.jsxs("div",{children:[e.jsxs("button",{onClick:()=>L(t),className:"w-full flex items-center gap-3 p-2 bg-surface rounded-lg hover:bg-elevated transition-colors text-left",children:[e.jsx("div",{className:"w-6 h-6 shrink-0 rounded-full bg-gold/20 flex items-center justify-center text-xs font-bold text-gold",children:r+1}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-sm text-text-primary truncate",children:a.name}),e.jsx("div",{className:"text-xs text-text-muted capitalize",children:a.type})]}),e.jsx("svg",{className:"w-4 h-4 text-text-muted",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})})]}),!s&&e.jsx("div",{className:"flex items-center justify-center my-1",children:e.jsx("svg",{className:"w-4 h-4 text-gold",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 14l-7 7m0 0l-7-7m7 7V3"})})})]},t)})})]})})]}),e.jsx("div",{className:"flex items-center justify-end gap-3 px-5 py-4 border-t border-border-subtle",children:e.jsx("button",{onClick:o,className:"px-4 py-2 text-sm text-text-secondary hover:text-text-primary transition-colors",children:"Close"})})]})})}export{I as default}; diff --git a/wishfulfilled-dashboard/assets/RagAssistant-BEmyfNge.js b/wishfulfilled-dashboard/assets/RagAssistant-BEmyfNge.js new file mode 100644 index 0000000..a46c128 --- /dev/null +++ b/wishfulfilled-dashboard/assets/RagAssistant-BEmyfNge.js @@ -0,0 +1,4 @@ +import{a,j as e}from"./react-vendor-BVoutfaX.js";import{u as O}from"./index-C3mrB48A.js";import"./xyflow-CYMCcsWN.js";import"./graph-layout-7tFr_anw.js";import"./elk-CXeXGyKz.js";import"./graphology-BgTy_cc3.js";const A="ua-rag-llm-settings-v2";function L(){if(typeof window>"u")return{enabled:!1,provider:"auto",endpoint:"http://localhost:11434/v1",model:"qwen2.5:7b",apiKey:""};try{const n=window.localStorage.getItem(A);if(n)return{enabled:!1,provider:"auto",endpoint:"http://localhost:11434/v1",model:"qwen2.5:7b",apiKey:"",...JSON.parse(n)}}catch{}return{enabled:!1,provider:"auto",endpoint:"http://localhost:11434/v1",model:"qwen2.5:7b",apiKey:""}}function Q(n){typeof window>"u"||window.localStorage.setItem(A,JSON.stringify(n))}function G(n){switch(n){case"search":return"本地检索中";case"evidence_rejected":return"证据不足,未调用模型";case"local_answer":return"本地回答";case"llm_done":return"模型回答完成";case"llm_failed":return"模型调用失败";default:return n||"待请求"}}async function q(n,o){const f=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}),i=await f.text();let b;try{b=i?JSON.parse(i):{}}catch{b={ok:!1,error:{message:i||"响应不是 JSON"}}}return{status:f.status,data:b}}function ee({onClose:n}){const o=O(t=>t.graph),f=O(t=>t.navigateToNodeInLayer),[i,b]=a.useState(""),[v,D]=a.useState(""),[x,w]=a.useState("search"),[d,R]=a.useState(L),[S,h]=a.useState(""),[k,C]=a.useState([]),[j,T]=a.useState(null),[l,c]=a.useState({}),[_,I]=a.useState(!1),[M,E]=a.useState(!1),[K,p]=a.useState(null),[y,N]=a.useState(!1),[u,$]=a.useState(null),z=(o==null?void 0:o.nodes.length)??0,F=a.useMemo(()=>(o==null?void 0:o.nodes.filter(t=>t.id.startsWith("doc:")).length)??0,[o]),g=t=>{const r={...d,...t};R(r),Q(r)},P=async()=>{var r;const t=i.trim();if(D(t),p(null),h(""),C([]),$(null),T(null),c({stage:"search",startedAt:new Date().toISOString()}),!t){h("请输入要检索或提问的内容。");return}I(!0);try{const s=x==="llm"?"/api/rag/answer":"/api/rag/search",{status:B,data:m}=await q(s,{query:t,topK:16,llm:d});if(c(m.requestStatus||{stage:x==="llm"?"llm_done":"local_answer"}),T(m.decision||null),C(m.hits||[]),h(m.answer||"暂无该需求描述。"),!m.ok){const H=((r=m.error)==null?void 0:r.message)||`请求失败:HTTP ${B}`;p(H)}}catch(s){p(s instanceof Error?s.message:String(s)),c({stage:"llm_failed"}),h("请求失败,未能获取检索结果。")}finally{I(!1)}},J=async()=>{var t;E(!0),p(null),c({stage:"模型连接测试中",startedAt:new Date().toISOString()});try{const{status:r,data:s}=await q("/api/llm/test",d);s.ok?(c({stage:"llm_done",endpoint:s.endpoint,elapsedMs:s.elapsedMs}),h(`模型连接成功。 +Endpoint:${s.endpoint||d.endpoint} +耗时:${s.elapsedMs??"?"} ms +返回:${s.answer||""}`)):(c({stage:"llm_failed",endpoint:s.endpoint,elapsedMs:s.elapsedMs}),p(((t=s.error)==null?void 0:t.message)||`模型连接测试失败:HTTP ${r}`))}catch(r){c({stage:"llm_failed"}),p(r instanceof Error?r.message:String(r))}finally{E(!1)}};return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/65 backdrop-blur-sm p-4 sm:p-6",onMouseDown:n,children:e.jsxs("div",{className:"w-[calc(100vw-32px)] max-w-[1180px] h-[calc(100vh-64px)] max-h-[860px] rounded-lg border border-border-medium bg-surface shadow-2xl overflow-hidden flex flex-col",onMouseDown:t=>t.stopPropagation(),children:[e.jsxs("div",{className:"flex items-center justify-between px-5 py-4 border-b border-border-subtle bg-elevated/40",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"font-heading text-lg text-text-primary",children:"知识库混合 RAG 检索 / 问答"}),e.jsx("p",{className:"text-xs text-text-muted mt-1",children:"前端只发请求到本地后端;后端先检索证据,再代理调用模型并返回明确错误。"})]}),e.jsx("button",{type:"button",onClick:n,className:"text-text-muted hover:text-accent transition-colors text-xl leading-none",children:"×"})]}),e.jsxs("div",{className:"p-5 border-b border-border-subtle space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:()=>{w("search"),N(!1)},className:`px-3 py-1.5 rounded-md border text-xs font-semibold ${x==="search"&&!y?"border-accent/50 bg-accent/20 text-accent":"border-border-subtle text-text-muted hover:text-text-primary"}`,children:"本地检索"}),e.jsx("button",{type:"button",onClick:()=>{w("llm"),N(!1)},className:`px-3 py-1.5 rounded-md border text-xs font-semibold ${x==="llm"&&!y?"border-accent/50 bg-accent/20 text-accent":"border-border-subtle text-text-muted hover:text-text-primary"}`,children:"大模型回答"}),e.jsx("button",{type:"button",onClick:()=>N(t=>!t),className:`px-3 py-1.5 rounded-md border text-xs font-semibold ${y?"border-accent/50 bg-accent/20 text-accent":"border-border-subtle text-text-muted hover:text-text-primary"}`,children:"模型设置"}),e.jsxs("div",{className:"text-[11px] text-text-muted",children:["图谱节点:",z,",文档节点:",F]})]}),y&&e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-[120px_160px_1fr_160px_100px] gap-2 border border-border-subtle rounded-lg p-3 bg-elevated/30",children:[e.jsxs("label",{className:"flex items-center gap-2 text-xs text-text-secondary",children:[e.jsx("input",{type:"checkbox",checked:d.enabled,onChange:t=>g({enabled:t.target.checked})}),"启用模型"]}),e.jsxs("select",{value:d.provider,onChange:t=>g({provider:t.target.value}),className:"bg-surface text-text-primary text-xs rounded px-2 py-1.5 border border-border-subtle focus:outline-none focus:border-accent/50",children:[e.jsx("option",{value:"auto",children:"自动识别"}),e.jsx("option",{value:"openai",children:"OpenAI兼容"}),e.jsx("option",{value:"routin-plan",children:"RoutIn Plan"})]}),e.jsx("input",{value:d.endpoint,onChange:t=>g({endpoint:t.target.value}),placeholder:"Base URL;RoutIn Plan 填 https://api.routin.ai/plan/v1",className:"bg-surface text-text-primary text-xs rounded px-2 py-1.5 border border-border-subtle focus:outline-none focus:border-accent/50"}),e.jsx("input",{value:d.model,onChange:t=>g({model:t.target.value}),placeholder:"model",className:"bg-surface text-text-primary text-xs rounded px-2 py-1.5 border border-border-subtle focus:outline-none focus:border-accent/50"}),e.jsx("button",{type:"button",onClick:()=>void J(),disabled:M,className:"px-2 py-1.5 rounded border border-accent/30 text-xs text-accent disabled:opacity-60",children:M?"测试中":"测试连接"}),e.jsx("div",{className:"hidden md:block"}),e.jsx("div",{className:"hidden md:block text-[10px] text-text-muted pt-1",children:"plan- key 会走 /messages"}),e.jsx("input",{type:"password",value:d.apiKey,onChange:t=>g({apiKey:t.target.value}),placeholder:"API Key;通过本地后端代理发送,不从浏览器直连模型",className:"md:col-span-3 bg-surface text-text-primary text-xs rounded px-2 py-1.5 border border-border-subtle focus:outline-none focus:border-accent/50"})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("textarea",{value:i,onChange:t=>b(t.target.value),onKeyDown:t=>{(t.ctrlKey||t.metaKey)&&t.key==="Enter"&&P()},placeholder:"例如:黑名单是什么?回评是什么?对外 API 契约(草案);风险与反欺诈有哪些接口?",className:"flex-1 min-h-[76px] resize-none bg-elevated text-text-primary text-sm rounded-lg px-3 py-2 border border-border-subtle focus:outline-none focus:border-accent/50 placeholder-text-muted"}),e.jsx("button",{type:"button",onClick:()=>void P(),disabled:_,className:"px-4 py-2 rounded-lg bg-accent/20 text-accent hover:text-accent-bright border border-accent/30 text-sm font-semibold transition-colors disabled:opacity-60",children:_?"处理中":x==="llm"?"问答":"检索"})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3 text-[11px] text-text-muted",children:[e.jsx("span",{children:"快捷键:Ctrl + Enter 提交。"}),e.jsxs("span",{children:["状态:",e.jsx("span",{className:"text-accent",children:G(l.stage)})]}),l.endpoint&&e.jsxs("span",{children:["Endpoint:",l.endpoint]}),l.protocol&&e.jsxs("span",{children:["协议:",l.protocol]}),typeof l.elapsedMs=="number"&&e.jsxs("span",{children:["耗时:",l.elapsedMs," ms"]})]})]}),e.jsxs("div",{className:"flex-1 min-h-0 overflow-auto p-5 grid grid-cols-1 lg:grid-cols-[1fr_440px] gap-5",children:[e.jsxs("section",{className:"min-w-0 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider",children:"回答 / 请求状态"}),v&&j&&e.jsxs("span",{className:`text-[11px] ${j.allowed?"text-accent":"text-text-muted"}`,children:["证据判断:",j.reason]})]}),K&&e.jsxs("div",{className:"text-xs text-red-300 border border-red-400/30 bg-red-500/10 rounded p-2",children:["错误:",K]}),e.jsx("pre",{className:"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary bg-elevated/50 border border-border-subtle rounded-lg p-4 font-sans min-h-[260px]",children:v||S?S||"等待后端返回。":"输入问题后点击“检索”或“问答”。"}),u&&e.jsxs("div",{className:"border border-border-subtle rounded-lg bg-elevated/40 p-3",children:[e.jsx("div",{className:"text-xs text-accent mb-1",children:"当前证据内容"}),e.jsxs("div",{className:"text-sm text-text-primary",children:[u.docTitle," / ",u.sectionTitle]}),e.jsx("div",{className:"text-[11px] text-text-muted mt-1",children:u.docPath}),e.jsx("pre",{className:"mt-2 max-h-[260px] overflow-auto whitespace-pre-wrap text-xs leading-relaxed text-text-secondary font-sans",children:u.evidenceContent||u.snippet})]})]}),e.jsxs("section",{className:"min-w-0",children:[e.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:"本地命中证据与排序依据"}),e.jsxs("div",{className:"space-y-2",children:[k.length===0&&v&&e.jsx("div",{className:"text-sm text-text-muted border border-border-subtle rounded-lg p-3",children:"暂无命中。"}),k.slice(0,12).map(t=>e.jsxs("div",{className:"border border-border-subtle bg-elevated/40 hover:border-accent/40 rounded-lg p-3 transition-colors",children:[e.jsxs("button",{type:"button",onClick:()=>f(t.nodeId),className:"w-full text-left",children:[e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm text-text-primary truncate",children:t.docTitle}),e.jsx("div",{className:"text-[11px] text-accent mt-1 truncate",children:t.sectionTitle})]}),e.jsx("div",{className:"text-[11px] text-accent shrink-0",children:t.score.toFixed(1)})]}),e.jsx("div",{className:"text-[11px] text-text-muted mt-1 truncate",children:t.docPath})]}),e.jsx("button",{type:"button",onClick:()=>$(t),className:"mt-2 text-left text-xs text-text-secondary line-clamp-4 hover:text-text-primary",children:t.snippet}),e.jsxs("div",{className:"mt-2 grid grid-cols-4 gap-1 text-[10px] text-text-muted",children:[e.jsxs("span",{children:["exact ",t.scores.exact.toFixed(0)]}),e.jsxs("span",{children:["fuzzy ",t.scores.fuzzy.toFixed(0)]}),e.jsxs("span",{children:["semantic ",t.scores.semantic.toFixed(0)]}),e.jsxs("span",{children:["boost ",(t.scores.titleBoost+t.scores.sectionBoost+t.scores.directoryBoost+t.scores.chunkTypeBoost).toFixed(0)]})]}),e.jsx("div",{className:"mt-2 flex flex-wrap gap-1",children:t.reasons.slice(0,5).map((r,s)=>e.jsxs("span",{className:"text-[10px] text-text-muted border border-border-subtle rounded px-1.5 py-0.5",children:[r.message," +",r.score.toFixed(0)]},`${t.chunkId}-${s}`))})]},t.chunkId))]})]})]})]})})}export{ee as default}; diff --git a/wishfulfilled-dashboard/assets/index-C3mrB48A.js b/wishfulfilled-dashboard/assets/index-C3mrB48A.js new file mode 100644 index 0000000..aa4872f --- /dev/null +++ b/wishfulfilled-dashboard/assets/index-C3mrB48A.js @@ -0,0 +1,42 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodeViewer-Boblkcge.js","assets/react-vendor-BVoutfaX.js","assets/markdown-DpllbSd9.js","assets/xyflow-CYMCcsWN.js","assets/xyflow-BZV40eAE.css","assets/graph-layout-7tFr_anw.js","assets/elk-CXeXGyKz.js","assets/graphology-BgTy_cc3.js","assets/LearnPanel-DahajoWG.js","assets/PathFinderModal-BHT2q_Em.js","assets/KeyboardShortcutsHelp-CcG7Ga98.js","assets/RagAssistant-BEmyfNge.js","assets/OnboardingOverlay-BCZlWHWy.js"])))=>i.map(i=>d[i]); +var br=Object.defineProperty;var yr=(e,t,n)=>t in e?br(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var St=(e,t,n)=>yr(e,typeof t!="symbol"?t+"":t,n);import{R as Xe,a as v,j as a,b as wr}from"./react-vendor-BVoutfaX.js";import{H as fe,P as pe,R as Ht,u as vr,a as kr,b as Ut,i as Wt,B as Kt,c as Jt,C as Yt,M as Xt,e as Nr}from"./xyflow-CYMCcsWN.js";import{f as jr,a as _r,b as Cr,c as Sr,d as Ir,e as Tr,g as Er}from"./graph-layout-7tFr_anw.js";import{E as $r}from"./elk-CXeXGyKz.js";import{G as Lr,l as zr}from"./graphology-BgTy_cc3.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const s of r)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const s={};return r.integrity&&(s.integrity=r.integrity),r.referrerPolicy&&(s.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?s.credentials="include":r.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(r){if(r.ep)return;r.ep=!0;const s=n(r);fetch(r.href,s)}})();const Ar="modulepreload",Or=function(e){return"/"+e},dn={},xe=function(t,n,o){let r=Promise.resolve();if(n&&n.length>0){let i=function(l){return Promise.all(l.map(u=>Promise.resolve(u).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),d=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));r=i(n.map(l=>{if(l=Or(l),l in dn)return;dn[l]=!0;const u=l.endsWith(".css"),f=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${f}`))return;const h=document.createElement("link");if(h.rel=u?"stylesheet":Ar,u||(h.as="script"),h.crossOrigin="",h.href=l,d&&h.setAttribute("nonce",d),document.head.appendChild(h),u)return new Promise((g,m)=>{h.addEventListener("load",g),h.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${l}`)))})}))}function s(i){const c=new Event("vite:preloadError",{cancelable:!0});if(c.payload=i,window.dispatchEvent(c),!c.defaultPrevented)throw i}return r.then(i=>{for(const c of i||[])c.status==="rejected"&&s(c.reason);return t().catch(s)})};function _(e,t,n){function o(c,d){if(c._zod||Object.defineProperty(c,"_zod",{value:{def:d,constr:i,traits:new Set},enumerable:!1}),c._zod.traits.has(e))return;c._zod.traits.add(e),t(c,d);const l=i.prototype,u=Object.keys(l);for(let f=0;f{var d,l;return n!=null&&n.Parent&&c instanceof n.Parent?!0:(l=(d=c==null?void 0:c._zod)==null?void 0:d.traits)==null?void 0:l.has(e)}}),Object.defineProperty(i,"name",{value:e}),i}class Fe extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}class no extends Error{constructor(t){super(`Encountered unidirectional transform during encode: ${t}`),this.name="ZodEncodeError"}}const oo={};function Ie(e){return oo}function ro(e){const t=Object.values(e).filter(o=>typeof o=="number");return Object.entries(e).filter(([o,r])=>t.indexOf(+o)===-1).map(([o,r])=>r)}function Lt(e,t){return typeof t=="bigint"?t.toString():t}function qt(e){return{get value(){{const t=e();return Object.defineProperty(this,"value",{value:t}),t}}}}function Qt(e){return e==null}function en(e){const t=e.startsWith("^")?1:0,n=e.endsWith("$")?e.length-1:e.length;return e.slice(t,n)}function Mr(e,t){const n=(e.toString().split(".")[1]||"").length,o=t.toString();let r=(o.split(".")[1]||"").length;if(r===0&&/\d?e-\d?/.test(o)){const d=o.match(/\d?e-(\d?)/);d!=null&&d[1]&&(r=Number.parseInt(d[1]))}const s=n>r?n:r,i=Number.parseInt(e.toFixed(s).replace(".","")),c=Number.parseInt(t.toFixed(s).replace(".",""));return i%c/10**s}const ln=Symbol("evaluating");function W(e,t,n){let o;Object.defineProperty(e,t,{get(){if(o!==ln)return o===void 0&&(o=ln,o=n()),o},set(r){Object.defineProperty(e,t,{value:r})},configurable:!0})}function Le(e,t,n){Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0})}function ke(...e){const t={};for(const n of e){const o=Object.getOwnPropertyDescriptors(n);Object.assign(t,o)}return Object.defineProperties({},t)}function un(e){return JSON.stringify(e)}function Fr(e){return e.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")}const so="captureStackTrace"in Error?Error.captureStackTrace:(...e)=>{};function dt(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}const Dr=qt(()=>{var e;if(typeof navigator<"u"&&((e=navigator==null?void 0:navigator.userAgent)!=null&&e.includes("Cloudflare")))return!1;try{const t=Function;return new t(""),!0}catch{return!1}});function Ue(e){if(dt(e)===!1)return!1;const t=e.constructor;if(t===void 0||typeof t!="function")return!0;const n=t.prototype;return!(dt(n)===!1||Object.prototype.hasOwnProperty.call(n,"isPrototypeOf")===!1)}function ao(e){return Ue(e)?{...e}:Array.isArray(e)?[...e]:e}const Pr=new Set(["string","number","symbol"]);function wt(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Ne(e,t,n){const o=new e._zod.constr(t??e._zod.def);return(!t||n!=null&&n.parent)&&(o._zod.parent=e),o}function O(e){const t=e;if(!t)return{};if(typeof t=="string")return{error:()=>t};if((t==null?void 0:t.message)!==void 0){if((t==null?void 0:t.error)!==void 0)throw new Error("Cannot specify both `message` and `error` params");t.error=t.message}return delete t.message,typeof t.error=="string"?{...t,error:()=>t.error}:t}function Rr(e){return Object.keys(e).filter(t=>e[t]._zod.optin==="optional"&&e[t]._zod.optout==="optional")}const Zr={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function Br(e,t){const n=e._zod.def,o=n.checks;if(o&&o.length>0)throw new Error(".pick() cannot be used on object schemas containing refinements");const s=ke(e._zod.def,{get shape(){const i={};for(const c in t){if(!(c in n.shape))throw new Error(`Unrecognized key: "${c}"`);t[c]&&(i[c]=n.shape[c])}return Le(this,"shape",i),i},checks:[]});return Ne(e,s)}function Vr(e,t){const n=e._zod.def,o=n.checks;if(o&&o.length>0)throw new Error(".omit() cannot be used on object schemas containing refinements");const s=ke(e._zod.def,{get shape(){const i={...e._zod.def.shape};for(const c in t){if(!(c in n.shape))throw new Error(`Unrecognized key: "${c}"`);t[c]&&delete i[c]}return Le(this,"shape",i),i},checks:[]});return Ne(e,s)}function Gr(e,t){if(!Ue(t))throw new Error("Invalid input to extend: expected a plain object");const n=e._zod.def.checks;if(n&&n.length>0){const s=e._zod.def.shape;for(const i in t)if(Object.getOwnPropertyDescriptor(s,i)!==void 0)throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}const r=ke(e._zod.def,{get shape(){const s={...e._zod.def.shape,...t};return Le(this,"shape",s),s}});return Ne(e,r)}function Hr(e,t){if(!Ue(t))throw new Error("Invalid input to safeExtend: expected a plain object");const n=ke(e._zod.def,{get shape(){const o={...e._zod.def.shape,...t};return Le(this,"shape",o),o}});return Ne(e,n)}function Ur(e,t){const n=ke(e._zod.def,{get shape(){const o={...e._zod.def.shape,...t._zod.def.shape};return Le(this,"shape",o),o},get catchall(){return t._zod.def.catchall},checks:[]});return Ne(e,n)}function Wr(e,t,n){const r=t._zod.def.checks;if(r&&r.length>0)throw new Error(".partial() cannot be used on object schemas containing refinements");const i=ke(t._zod.def,{get shape(){const c=t._zod.def.shape,d={...c};if(n)for(const l in n){if(!(l in c))throw new Error(`Unrecognized key: "${l}"`);n[l]&&(d[l]=e?new e({type:"optional",innerType:c[l]}):c[l])}else for(const l in c)d[l]=e?new e({type:"optional",innerType:c[l]}):c[l];return Le(this,"shape",d),d},checks:[]});return Ne(t,i)}function Kr(e,t,n){const o=ke(t._zod.def,{get shape(){const r=t._zod.def.shape,s={...r};if(n)for(const i in n){if(!(i in s))throw new Error(`Unrecognized key: "${i}"`);n[i]&&(s[i]=new e({type:"nonoptional",innerType:r[i]}))}else for(const i in r)s[i]=new e({type:"nonoptional",innerType:r[i]});return Le(this,"shape",s),s}});return Ne(t,o)}function Me(e,t=0){var n;if(e.aborted===!0)return!0;for(let o=t;o{var o;return(o=n).path??(o.path=[]),n.path.unshift(e),n})}function qe(e){return typeof e=="string"?e:e==null?void 0:e.message}function Te(e,t,n){var r,s,i,c,d,l;const o={...e,path:e.path??[]};if(!e.message){const u=qe((i=(s=(r=e.inst)==null?void 0:r._zod.def)==null?void 0:s.error)==null?void 0:i.call(s,e))??qe((c=t==null?void 0:t.error)==null?void 0:c.call(t,e))??qe((d=n.customError)==null?void 0:d.call(n,e))??qe((l=n.localeError)==null?void 0:l.call(n,e))??"Invalid input";o.message=u}return delete o.inst,delete o.continue,t!=null&&t.reportInput||delete o.input,o}function nn(e){return Array.isArray(e)?"array":typeof e=="string"?"string":"unknown"}function We(...e){const[t,n,o]=e;return typeof t=="string"?{message:t,code:"custom",input:n,inst:o}:{...t}}const io=(e,t)=>{e.name="$ZodError",Object.defineProperty(e,"_zod",{value:e._zod,enumerable:!1}),Object.defineProperty(e,"issues",{value:t,enumerable:!1}),e.message=JSON.stringify(t,Lt,2),Object.defineProperty(e,"toString",{value:()=>e.message,enumerable:!1})},co=_("$ZodError",io),lo=_("$ZodError",io,{Parent:Error});function Jr(e,t=n=>n.message){const n={},o=[];for(const r of e.issues)r.path.length>0?(n[r.path[0]]=n[r.path[0]]||[],n[r.path[0]].push(t(r))):o.push(t(r));return{formErrors:o,fieldErrors:n}}function Yr(e,t=n=>n.message){const n={_errors:[]},o=r=>{for(const s of r.issues)if(s.code==="invalid_union"&&s.errors.length)s.errors.map(i=>o({issues:i}));else if(s.code==="invalid_key")o({issues:s.issues});else if(s.code==="invalid_element")o({issues:s.issues});else if(s.path.length===0)n._errors.push(t(s));else{let i=n,c=0;for(;c(t,n,o,r)=>{const s=o?Object.assign(o,{async:!1}):{async:!1},i=t._zod.run({value:n,issues:[]},s);if(i instanceof Promise)throw new Fe;if(i.issues.length){const c=new((r==null?void 0:r.Err)??e)(i.issues.map(d=>Te(d,s,Ie())));throw so(c,r==null?void 0:r.callee),c}return i.value},rn=e=>async(t,n,o,r)=>{const s=o?Object.assign(o,{async:!0}):{async:!0};let i=t._zod.run({value:n,issues:[]},s);if(i instanceof Promise&&(i=await i),i.issues.length){const c=new((r==null?void 0:r.Err)??e)(i.issues.map(d=>Te(d,s,Ie())));throw so(c,r==null?void 0:r.callee),c}return i.value},vt=e=>(t,n,o)=>{const r=o?{...o,async:!1}:{async:!1},s=t._zod.run({value:n,issues:[]},r);if(s instanceof Promise)throw new Fe;return s.issues.length?{success:!1,error:new(e??co)(s.issues.map(i=>Te(i,r,Ie())))}:{success:!0,data:s.value}},Xr=vt(lo),kt=e=>async(t,n,o)=>{const r=o?Object.assign(o,{async:!0}):{async:!0};let s=t._zod.run({value:n,issues:[]},r);return s instanceof Promise&&(s=await s),s.issues.length?{success:!1,error:new e(s.issues.map(i=>Te(i,r,Ie())))}:{success:!0,data:s.value}},qr=kt(lo),Qr=e=>(t,n,o)=>{const r=o?Object.assign(o,{direction:"backward"}):{direction:"backward"};return on(e)(t,n,r)},es=e=>(t,n,o)=>on(e)(t,n,o),ts=e=>async(t,n,o)=>{const r=o?Object.assign(o,{direction:"backward"}):{direction:"backward"};return rn(e)(t,n,r)},ns=e=>async(t,n,o)=>rn(e)(t,n,o),os=e=>(t,n,o)=>{const r=o?Object.assign(o,{direction:"backward"}):{direction:"backward"};return vt(e)(t,n,r)},rs=e=>(t,n,o)=>vt(e)(t,n,o),ss=e=>async(t,n,o)=>{const r=o?Object.assign(o,{direction:"backward"}):{direction:"backward"};return kt(e)(t,n,r)},as=e=>async(t,n,o)=>kt(e)(t,n,o),is=/^[cC][^\s-]{8,}$/,cs=/^[0-9a-z]+$/,ds=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,ls=/^[0-9a-vA-V]{20}$/,us=/^[A-Za-z0-9]{27}$/,fs=/^[a-zA-Z0-9_-]{21}$/,ps=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,hs=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,fn=e=>e?new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${e}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,ms=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,gs="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";function xs(){return new RegExp(gs,"u")}const bs=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,ys=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,ws=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,vs=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,ks=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,uo=/^[A-Za-z0-9_-]*$/,Ns=/^\+[1-9]\d{6,14}$/,fo="(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))",js=new RegExp(`^${fo}$`);function po(e){const t="(?:[01]\\d|2[0-3]):[0-5]\\d";return typeof e.precision=="number"?e.precision===-1?`${t}`:e.precision===0?`${t}:[0-5]\\d`:`${t}:[0-5]\\d\\.\\d{${e.precision}}`:`${t}(?::[0-5]\\d(?:\\.\\d+)?)?`}function _s(e){return new RegExp(`^${po(e)}$`)}function Cs(e){const t=po({precision:e.precision}),n=["Z"];e.local&&n.push(""),e.offset&&n.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)");const o=`${t}(?:${n.join("|")})`;return new RegExp(`^${fo}T(?:${o})$`)}const Ss=e=>{const t=e?`[\\s\\S]{${(e==null?void 0:e.minimum)??0},${(e==null?void 0:e.maximum)??""}}`:"[\\s\\S]*";return new RegExp(`^${t}$`)},Is=/^-?\d+$/,Ts=/^-?\d+(?:\.\d+)?$/,Es=/^[^A-Z]*$/,$s=/^[^a-z]*$/,ce=_("$ZodCheck",(e,t)=>{var n;e._zod??(e._zod={}),e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])}),ho={number:"number",bigint:"bigint",object:"date"},mo=_("$ZodCheckLessThan",(e,t)=>{ce.init(e,t);const n=ho[typeof t.value];e._zod.onattach.push(o=>{const r=o._zod.bag,s=(t.inclusive?r.maximum:r.exclusiveMaximum)??Number.POSITIVE_INFINITY;t.value{(t.inclusive?o.value<=t.value:o.value{ce.init(e,t);const n=ho[typeof t.value];e._zod.onattach.push(o=>{const r=o._zod.bag,s=(t.inclusive?r.minimum:r.exclusiveMinimum)??Number.NEGATIVE_INFINITY;t.value>s&&(t.inclusive?r.minimum=t.value:r.exclusiveMinimum=t.value)}),e._zod.check=o=>{(t.inclusive?o.value>=t.value:o.value>t.value)||o.issues.push({origin:n,code:"too_small",minimum:typeof t.value=="object"?t.value.getTime():t.value,input:o.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}}),Ls=_("$ZodCheckMultipleOf",(e,t)=>{ce.init(e,t),e._zod.onattach.push(n=>{var o;(o=n._zod.bag).multipleOf??(o.multipleOf=t.value)}),e._zod.check=n=>{if(typeof n.value!=typeof t.value)throw new Error("Cannot mix number and bigint in multiple_of check.");(typeof n.value=="bigint"?n.value%t.value===BigInt(0):Mr(n.value,t.value)===0)||n.issues.push({origin:typeof n.value,code:"not_multiple_of",divisor:t.value,input:n.value,inst:e,continue:!t.abort})}}),zs=_("$ZodCheckNumberFormat",(e,t)=>{var i;ce.init(e,t),t.format=t.format||"float64";const n=(i=t.format)==null?void 0:i.includes("int"),o=n?"int":"number",[r,s]=Zr[t.format];e._zod.onattach.push(c=>{const d=c._zod.bag;d.format=t.format,d.minimum=r,d.maximum=s,n&&(d.pattern=Is)}),e._zod.check=c=>{const d=c.value;if(n){if(!Number.isInteger(d)){c.issues.push({expected:o,format:t.format,code:"invalid_type",continue:!1,input:d,inst:e});return}if(!Number.isSafeInteger(d)){d>0?c.issues.push({input:d,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:e,origin:o,inclusive:!0,continue:!t.abort}):c.issues.push({input:d,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:e,origin:o,inclusive:!0,continue:!t.abort});return}}ds&&c.issues.push({origin:"number",input:d,code:"too_big",maximum:s,inclusive:!0,inst:e,continue:!t.abort})}}),As=_("$ZodCheckMaxLength",(e,t)=>{var n;ce.init(e,t),(n=e._zod.def).when??(n.when=o=>{const r=o.value;return!Qt(r)&&r.length!==void 0}),e._zod.onattach.push(o=>{const r=o._zod.bag.maximum??Number.POSITIVE_INFINITY;t.maximum{const r=o.value;if(r.length<=t.maximum)return;const i=nn(r);o.issues.push({origin:i,code:"too_big",maximum:t.maximum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),Os=_("$ZodCheckMinLength",(e,t)=>{var n;ce.init(e,t),(n=e._zod.def).when??(n.when=o=>{const r=o.value;return!Qt(r)&&r.length!==void 0}),e._zod.onattach.push(o=>{const r=o._zod.bag.minimum??Number.NEGATIVE_INFINITY;t.minimum>r&&(o._zod.bag.minimum=t.minimum)}),e._zod.check=o=>{const r=o.value;if(r.length>=t.minimum)return;const i=nn(r);o.issues.push({origin:i,code:"too_small",minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),Ms=_("$ZodCheckLengthEquals",(e,t)=>{var n;ce.init(e,t),(n=e._zod.def).when??(n.when=o=>{const r=o.value;return!Qt(r)&&r.length!==void 0}),e._zod.onattach.push(o=>{const r=o._zod.bag;r.minimum=t.length,r.maximum=t.length,r.length=t.length}),e._zod.check=o=>{const r=o.value,s=r.length;if(s===t.length)return;const i=nn(r),c=s>t.length;o.issues.push({origin:i,...c?{code:"too_big",maximum:t.length}:{code:"too_small",minimum:t.length},inclusive:!0,exact:!0,input:o.value,inst:e,continue:!t.abort})}}),Nt=_("$ZodCheckStringFormat",(e,t)=>{var n,o;ce.init(e,t),e._zod.onattach.push(r=>{const s=r._zod.bag;s.format=t.format,t.pattern&&(s.patterns??(s.patterns=new Set),s.patterns.add(t.pattern))}),t.pattern?(n=e._zod).check??(n.check=r=>{t.pattern.lastIndex=0,!t.pattern.test(r.value)&&r.issues.push({origin:"string",code:"invalid_format",format:t.format,input:r.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(o=e._zod).check??(o.check=()=>{})}),Fs=_("$ZodCheckRegex",(e,t)=>{Nt.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:"string",code:"invalid_format",format:"regex",input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}}),Ds=_("$ZodCheckLowerCase",(e,t)=>{t.pattern??(t.pattern=Es),Nt.init(e,t)}),Ps=_("$ZodCheckUpperCase",(e,t)=>{t.pattern??(t.pattern=$s),Nt.init(e,t)}),Rs=_("$ZodCheckIncludes",(e,t)=>{ce.init(e,t);const n=wt(t.includes),o=new RegExp(typeof t.position=="number"?`^.{${t.position}}${n}`:n);t.pattern=o,e._zod.onattach.push(r=>{const s=r._zod.bag;s.patterns??(s.patterns=new Set),s.patterns.add(o)}),e._zod.check=r=>{r.value.includes(t.includes,t.position)||r.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:t.includes,input:r.value,inst:e,continue:!t.abort})}}),Zs=_("$ZodCheckStartsWith",(e,t)=>{ce.init(e,t);const n=new RegExp(`^${wt(t.prefix)}.*`);t.pattern??(t.pattern=n),e._zod.onattach.push(o=>{const r=o._zod.bag;r.patterns??(r.patterns=new Set),r.patterns.add(n)}),e._zod.check=o=>{o.value.startsWith(t.prefix)||o.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:t.prefix,input:o.value,inst:e,continue:!t.abort})}}),Bs=_("$ZodCheckEndsWith",(e,t)=>{ce.init(e,t);const n=new RegExp(`.*${wt(t.suffix)}$`);t.pattern??(t.pattern=n),e._zod.onattach.push(o=>{const r=o._zod.bag;r.patterns??(r.patterns=new Set),r.patterns.add(n)}),e._zod.check=o=>{o.value.endsWith(t.suffix)||o.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:t.suffix,input:o.value,inst:e,continue:!t.abort})}}),Vs=_("$ZodCheckOverwrite",(e,t)=>{ce.init(e,t),e._zod.check=n=>{n.value=t.tx(n.value)}});class Gs{constructor(t=[]){this.content=[],this.indent=0,this&&(this.args=t)}indented(t){this.indent+=1,t(this),this.indent-=1}write(t){if(typeof t=="function"){t(this,{execution:"sync"}),t(this,{execution:"async"});return}const o=t.split(` +`).filter(i=>i),r=Math.min(...o.map(i=>i.length-i.trimStart().length)),s=o.map(i=>i.slice(r)).map(i=>" ".repeat(this.indent*2)+i);for(const i of s)this.content.push(i)}compile(){const t=Function,n=this==null?void 0:this.args,r=[...((this==null?void 0:this.content)??[""]).map(s=>` ${s}`)];return new t(...n,r.join(` +`))}}const Hs={major:4,minor:3,patch:6},ee=_("$ZodType",(e,t)=>{var r;var n;e??(e={}),e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=Hs;const o=[...e._zod.def.checks??[]];e._zod.traits.has("$ZodCheck")&&o.unshift(e);for(const s of o)for(const i of s._zod.onattach)i(e);if(o.length===0)(n=e._zod).deferred??(n.deferred=[]),(r=e._zod.deferred)==null||r.push(()=>{e._zod.run=e._zod.parse});else{const s=(c,d,l)=>{let u=Me(c),f;for(const h of d){if(h._zod.def.when){if(!h._zod.def.when(c))continue}else if(u)continue;const g=c.issues.length,m=h._zod.check(c);if(m instanceof Promise&&(l==null?void 0:l.async)===!1)throw new Fe;if(f||m instanceof Promise)f=(f??Promise.resolve()).then(async()=>{await m,c.issues.length!==g&&(u||(u=Me(c,g)))});else{if(c.issues.length===g)continue;u||(u=Me(c,g))}}return f?f.then(()=>c):c},i=(c,d,l)=>{if(Me(c))return c.aborted=!0,c;const u=s(d,o,l);if(u instanceof Promise){if(l.async===!1)throw new Fe;return u.then(f=>e._zod.parse(f,l))}return e._zod.parse(u,l)};e._zod.run=(c,d)=>{if(d.skipChecks)return e._zod.parse(c,d);if(d.direction==="backward"){const u=e._zod.parse({value:c.value,issues:[]},{...d,skipChecks:!0});return u instanceof Promise?u.then(f=>i(f,c,d)):i(u,c,d)}const l=e._zod.parse(c,d);if(l instanceof Promise){if(d.async===!1)throw new Fe;return l.then(u=>s(u,o,d))}return s(l,o,d)}}W(e,"~standard",()=>({validate:s=>{var i;try{const c=Xr(e,s);return c.success?{value:c.data}:{issues:(i=c.error)==null?void 0:i.issues}}catch{return qr(e,s).then(d=>{var l;return d.success?{value:d.data}:{issues:(l=d.error)==null?void 0:l.issues}})}},vendor:"zod",version:1}))}),sn=_("$ZodString",(e,t)=>{var n;ee.init(e,t),e._zod.pattern=[...((n=e==null?void 0:e._zod.bag)==null?void 0:n.patterns)??[]].pop()??Ss(e._zod.bag),e._zod.parse=(o,r)=>{if(t.coerce)try{o.value=String(o.value)}catch{}return typeof o.value=="string"||o.issues.push({expected:"string",code:"invalid_type",input:o.value,inst:e}),o}}),X=_("$ZodStringFormat",(e,t)=>{Nt.init(e,t),sn.init(e,t)}),Us=_("$ZodGUID",(e,t)=>{t.pattern??(t.pattern=hs),X.init(e,t)}),Ws=_("$ZodUUID",(e,t)=>{if(t.version){const o={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[t.version];if(o===void 0)throw new Error(`Invalid UUID version: "${t.version}"`);t.pattern??(t.pattern=fn(o))}else t.pattern??(t.pattern=fn());X.init(e,t)}),Ks=_("$ZodEmail",(e,t)=>{t.pattern??(t.pattern=ms),X.init(e,t)}),Js=_("$ZodURL",(e,t)=>{X.init(e,t),e._zod.check=n=>{try{const o=n.value.trim(),r=new URL(o);t.hostname&&(t.hostname.lastIndex=0,t.hostname.test(r.hostname)||n.issues.push({code:"invalid_format",format:"url",note:"Invalid hostname",pattern:t.hostname.source,input:n.value,inst:e,continue:!t.abort})),t.protocol&&(t.protocol.lastIndex=0,t.protocol.test(r.protocol.endsWith(":")?r.protocol.slice(0,-1):r.protocol)||n.issues.push({code:"invalid_format",format:"url",note:"Invalid protocol",pattern:t.protocol.source,input:n.value,inst:e,continue:!t.abort})),t.normalize?n.value=r.href:n.value=o;return}catch{n.issues.push({code:"invalid_format",format:"url",input:n.value,inst:e,continue:!t.abort})}}}),Ys=_("$ZodEmoji",(e,t)=>{t.pattern??(t.pattern=xs()),X.init(e,t)}),Xs=_("$ZodNanoID",(e,t)=>{t.pattern??(t.pattern=fs),X.init(e,t)}),qs=_("$ZodCUID",(e,t)=>{t.pattern??(t.pattern=is),X.init(e,t)}),Qs=_("$ZodCUID2",(e,t)=>{t.pattern??(t.pattern=cs),X.init(e,t)}),ea=_("$ZodULID",(e,t)=>{t.pattern??(t.pattern=ds),X.init(e,t)}),ta=_("$ZodXID",(e,t)=>{t.pattern??(t.pattern=ls),X.init(e,t)}),na=_("$ZodKSUID",(e,t)=>{t.pattern??(t.pattern=us),X.init(e,t)}),oa=_("$ZodISODateTime",(e,t)=>{t.pattern??(t.pattern=Cs(t)),X.init(e,t)}),ra=_("$ZodISODate",(e,t)=>{t.pattern??(t.pattern=js),X.init(e,t)}),sa=_("$ZodISOTime",(e,t)=>{t.pattern??(t.pattern=_s(t)),X.init(e,t)}),aa=_("$ZodISODuration",(e,t)=>{t.pattern??(t.pattern=ps),X.init(e,t)}),ia=_("$ZodIPv4",(e,t)=>{t.pattern??(t.pattern=bs),X.init(e,t),e._zod.bag.format="ipv4"}),ca=_("$ZodIPv6",(e,t)=>{t.pattern??(t.pattern=ys),X.init(e,t),e._zod.bag.format="ipv6",e._zod.check=n=>{try{new URL(`http://[${n.value}]`)}catch{n.issues.push({code:"invalid_format",format:"ipv6",input:n.value,inst:e,continue:!t.abort})}}}),da=_("$ZodCIDRv4",(e,t)=>{t.pattern??(t.pattern=ws),X.init(e,t)}),la=_("$ZodCIDRv6",(e,t)=>{t.pattern??(t.pattern=vs),X.init(e,t),e._zod.check=n=>{const o=n.value.split("/");try{if(o.length!==2)throw new Error;const[r,s]=o;if(!s)throw new Error;const i=Number(s);if(`${i}`!==s)throw new Error;if(i<0||i>128)throw new Error;new URL(`http://[${r}]`)}catch{n.issues.push({code:"invalid_format",format:"cidrv6",input:n.value,inst:e,continue:!t.abort})}}});function xo(e){if(e==="")return!0;if(e.length%4!==0)return!1;try{return atob(e),!0}catch{return!1}}const ua=_("$ZodBase64",(e,t)=>{t.pattern??(t.pattern=ks),X.init(e,t),e._zod.bag.contentEncoding="base64",e._zod.check=n=>{xo(n.value)||n.issues.push({code:"invalid_format",format:"base64",input:n.value,inst:e,continue:!t.abort})}});function fa(e){if(!uo.test(e))return!1;const t=e.replace(/[-_]/g,o=>o==="-"?"+":"/"),n=t.padEnd(Math.ceil(t.length/4)*4,"=");return xo(n)}const pa=_("$ZodBase64URL",(e,t)=>{t.pattern??(t.pattern=uo),X.init(e,t),e._zod.bag.contentEncoding="base64url",e._zod.check=n=>{fa(n.value)||n.issues.push({code:"invalid_format",format:"base64url",input:n.value,inst:e,continue:!t.abort})}}),ha=_("$ZodE164",(e,t)=>{t.pattern??(t.pattern=Ns),X.init(e,t)});function ma(e,t=null){try{const n=e.split(".");if(n.length!==3)return!1;const[o]=n;if(!o)return!1;const r=JSON.parse(atob(o));return!("typ"in r&&(r==null?void 0:r.typ)!=="JWT"||!r.alg||t&&(!("alg"in r)||r.alg!==t))}catch{return!1}}const ga=_("$ZodJWT",(e,t)=>{X.init(e,t),e._zod.check=n=>{ma(n.value,t.alg)||n.issues.push({code:"invalid_format",format:"jwt",input:n.value,inst:e,continue:!t.abort})}}),bo=_("$ZodNumber",(e,t)=>{ee.init(e,t),e._zod.pattern=e._zod.bag.pattern??Ts,e._zod.parse=(n,o)=>{if(t.coerce)try{n.value=Number(n.value)}catch{}const r=n.value;if(typeof r=="number"&&!Number.isNaN(r)&&Number.isFinite(r))return n;const s=typeof r=="number"?Number.isNaN(r)?"NaN":Number.isFinite(r)?void 0:"Infinity":void 0;return n.issues.push({expected:"number",code:"invalid_type",input:r,inst:e,...s?{received:s}:{}}),n}}),xa=_("$ZodNumberFormat",(e,t)=>{zs.init(e,t),bo.init(e,t)}),ba=_("$ZodUnknown",(e,t)=>{ee.init(e,t),e._zod.parse=n=>n}),ya=_("$ZodNever",(e,t)=>{ee.init(e,t),e._zod.parse=(n,o)=>(n.issues.push({expected:"never",code:"invalid_type",input:n.value,inst:e}),n)});function pn(e,t,n){e.issues.length&&t.issues.push(...tn(n,e.issues)),t.value[n]=e.value}const wa=_("$ZodArray",(e,t)=>{ee.init(e,t),e._zod.parse=(n,o)=>{const r=n.value;if(!Array.isArray(r))return n.issues.push({expected:"array",code:"invalid_type",input:r,inst:e}),n;n.value=Array(r.length);const s=[];for(let i=0;ipn(l,n,i))):pn(d,n,i)}return s.length?Promise.all(s).then(()=>n):n}});function lt(e,t,n,o,r){if(e.issues.length){if(r&&!(n in o))return;t.issues.push(...tn(n,e.issues))}e.value===void 0?n in o&&(t.value[n]=void 0):t.value[n]=e.value}function yo(e){var o,r,s,i;const t=Object.keys(e.shape);for(const c of t)if(!((i=(s=(r=(o=e.shape)==null?void 0:o[c])==null?void 0:r._zod)==null?void 0:s.traits)!=null&&i.has("$ZodType")))throw new Error(`Invalid element at key "${c}": expected a Zod schema`);const n=Rr(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function wo(e,t,n,o,r,s){const i=[],c=r.keySet,d=r.catchall._zod,l=d.def.type,u=d.optout==="optional";for(const f in t){if(c.has(f))continue;if(l==="never"){i.push(f);continue}const h=d.run({value:t[f],issues:[]},o);h instanceof Promise?e.push(h.then(g=>lt(g,n,f,t,u))):lt(h,n,f,t,u)}return i.length&&n.issues.push({code:"unrecognized_keys",keys:i,input:t,inst:s}),e.length?Promise.all(e).then(()=>n):n}const va=_("$ZodObject",(e,t)=>{ee.init(e,t);const n=Object.getOwnPropertyDescriptor(t,"shape");if(!(n!=null&&n.get)){const c=t.shape;Object.defineProperty(t,"shape",{get:()=>{const d={...c};return Object.defineProperty(t,"shape",{value:d}),d}})}const o=qt(()=>yo(t));W(e._zod,"propValues",()=>{const c=t.shape,d={};for(const l in c){const u=c[l]._zod;if(u.values){d[l]??(d[l]=new Set);for(const f of u.values)d[l].add(f)}}return d});const r=dt,s=t.catchall;let i;e._zod.parse=(c,d)=>{i??(i=o.value);const l=c.value;if(!r(l))return c.issues.push({expected:"object",code:"invalid_type",input:l,inst:e}),c;c.value={};const u=[],f=i.shape;for(const h of i.keys){const g=f[h],m=g._zod.optout==="optional",p=g._zod.run({value:l[h],issues:[]},d);p instanceof Promise?u.push(p.then(N=>lt(N,c,h,l,m))):lt(p,c,h,l,m)}return s?wo(u,l,c,d,o.value,e):u.length?Promise.all(u).then(()=>c):c}}),ka=_("$ZodObjectJIT",(e,t)=>{va.init(e,t);const n=e._zod.parse,o=qt(()=>yo(t)),r=h=>{var k;const g=new Gs(["shape","payload","ctx"]),m=o.value,p=T=>{const j=un(T);return`shape[${j}]._zod.run({ value: input[${j}], issues: [] }, ctx)`};g.write("const input = payload.value;");const N=Object.create(null);let y=0;for(const T of m.keys)N[T]=`key_${y++}`;g.write("const newResult = {};");for(const T of m.keys){const j=N[T],C=un(T),I=h[T],E=((k=I==null?void 0:I._zod)==null?void 0:k.optout)==="optional";g.write(`const ${j} = ${p(T)};`),E?g.write(` + if (${j}.issues.length) { + if (${C} in input) { + payload.issues = payload.issues.concat(${j}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${C}, ...iss.path] : [${C}] + }))); + } + } + + if (${j}.value === undefined) { + if (${C} in input) { + newResult[${C}] = undefined; + } + } else { + newResult[${C}] = ${j}.value; + } + + `):g.write(` + if (${j}.issues.length) { + payload.issues = payload.issues.concat(${j}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${C}, ...iss.path] : [${C}] + }))); + } + + if (${j}.value === undefined) { + if (${C} in input) { + newResult[${C}] = undefined; + } + } else { + newResult[${C}] = ${j}.value; + } + + `)}g.write("payload.value = newResult;"),g.write("return payload;");const x=g.compile();return(T,j)=>x(h,T,j)};let s;const i=dt,c=!oo.jitless,l=c&&Dr.value,u=t.catchall;let f;e._zod.parse=(h,g)=>{f??(f=o.value);const m=h.value;return i(m)?c&&l&&(g==null?void 0:g.async)===!1&&g.jitless!==!0?(s||(s=r(t.shape)),h=s(h,g),u?wo([],m,h,g,f,e):h):n(h,g):(h.issues.push({expected:"object",code:"invalid_type",input:m,inst:e}),h)}});function hn(e,t,n,o){for(const s of e)if(s.issues.length===0)return t.value=s.value,t;const r=e.filter(s=>!Me(s));return r.length===1?(t.value=r[0].value,r[0]):(t.issues.push({code:"invalid_union",input:t.value,inst:n,errors:e.map(s=>s.issues.map(i=>Te(i,o,Ie())))}),t)}const Na=_("$ZodUnion",(e,t)=>{ee.init(e,t),W(e._zod,"optin",()=>t.options.some(r=>r._zod.optin==="optional")?"optional":void 0),W(e._zod,"optout",()=>t.options.some(r=>r._zod.optout==="optional")?"optional":void 0),W(e._zod,"values",()=>{if(t.options.every(r=>r._zod.values))return new Set(t.options.flatMap(r=>Array.from(r._zod.values)))}),W(e._zod,"pattern",()=>{if(t.options.every(r=>r._zod.pattern)){const r=t.options.map(s=>s._zod.pattern);return new RegExp(`^(${r.map(s=>en(s.source)).join("|")})$`)}});const n=t.options.length===1,o=t.options[0]._zod.run;e._zod.parse=(r,s)=>{if(n)return o(r,s);let i=!1;const c=[];for(const d of t.options){const l=d._zod.run({value:r.value,issues:[]},s);if(l instanceof Promise)c.push(l),i=!0;else{if(l.issues.length===0)return l;c.push(l)}}return i?Promise.all(c).then(d=>hn(d,r,e,s)):hn(c,r,e,s)}}),ja=_("$ZodIntersection",(e,t)=>{ee.init(e,t),e._zod.parse=(n,o)=>{const r=n.value,s=t.left._zod.run({value:r,issues:[]},o),i=t.right._zod.run({value:r,issues:[]},o);return s instanceof Promise||i instanceof Promise?Promise.all([s,i]).then(([d,l])=>mn(n,d,l)):mn(n,s,i)}});function zt(e,t){if(e===t)return{valid:!0,data:e};if(e instanceof Date&&t instanceof Date&&+e==+t)return{valid:!0,data:e};if(Ue(e)&&Ue(t)){const n=Object.keys(t),o=Object.keys(e).filter(s=>n.indexOf(s)!==-1),r={...e,...t};for(const s of o){const i=zt(e[s],t[s]);if(!i.valid)return{valid:!1,mergeErrorPath:[s,...i.mergeErrorPath]};r[s]=i.data}return{valid:!0,data:r}}if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return{valid:!1,mergeErrorPath:[]};const n=[];for(let o=0;oc.l&&c.r).map(([c])=>c);if(s.length&&r&&e.issues.push({...r,keys:s}),Me(e))return e;const i=zt(t.value,n.value);if(!i.valid)throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(i.mergeErrorPath)}`);return e.value=i.data,e}const _a=_("$ZodTuple",(e,t)=>{ee.init(e,t);const n=t.items;e._zod.parse=(o,r)=>{const s=o.value;if(!Array.isArray(s))return o.issues.push({input:s,inst:e,expected:"tuple",code:"invalid_type"}),o;o.value=[];const i=[],c=[...n].reverse().findIndex(u=>u._zod.optin!=="optional"),d=c===-1?0:n.length-c;if(!t.rest){const u=s.length>n.length,f=s.length=s.length&&l>=d)continue;const f=u._zod.run({value:s[l],issues:[]},r);f instanceof Promise?i.push(f.then(h=>Qe(h,o,l))):Qe(f,o,l)}if(t.rest){const u=s.slice(n.length);for(const f of u){l++;const h=t.rest._zod.run({value:f,issues:[]},r);h instanceof Promise?i.push(h.then(g=>Qe(g,o,l))):Qe(h,o,l)}}return i.length?Promise.all(i).then(()=>o):o}});function Qe(e,t,n){e.issues.length&&t.issues.push(...tn(n,e.issues)),t.value[n]=e.value}const Ca=_("$ZodEnum",(e,t)=>{ee.init(e,t);const n=ro(t.entries),o=new Set(n);e._zod.values=o,e._zod.pattern=new RegExp(`^(${n.filter(r=>Pr.has(typeof r)).map(r=>typeof r=="string"?wt(r):r.toString()).join("|")})$`),e._zod.parse=(r,s)=>{const i=r.value;return o.has(i)||r.issues.push({code:"invalid_value",values:n,input:i,inst:e}),r}}),Sa=_("$ZodTransform",(e,t)=>{ee.init(e,t),e._zod.parse=(n,o)=>{if(o.direction==="backward")throw new no(e.constructor.name);const r=t.transform(n.value,n);if(o.async)return(r instanceof Promise?r:Promise.resolve(r)).then(i=>(n.value=i,n));if(r instanceof Promise)throw new Fe;return n.value=r,n}});function gn(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const vo=_("$ZodOptional",(e,t)=>{ee.init(e,t),e._zod.optin="optional",e._zod.optout="optional",W(e._zod,"values",()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),W(e._zod,"pattern",()=>{const n=t.innerType._zod.pattern;return n?new RegExp(`^(${en(n.source)})?$`):void 0}),e._zod.parse=(n,o)=>{if(t.innerType._zod.optin==="optional"){const r=t.innerType._zod.run(n,o);return r instanceof Promise?r.then(s=>gn(s,n.value)):gn(r,n.value)}return n.value===void 0?n:t.innerType._zod.run(n,o)}}),Ia=_("$ZodExactOptional",(e,t)=>{vo.init(e,t),W(e._zod,"values",()=>t.innerType._zod.values),W(e._zod,"pattern",()=>t.innerType._zod.pattern),e._zod.parse=(n,o)=>t.innerType._zod.run(n,o)}),Ta=_("$ZodNullable",(e,t)=>{ee.init(e,t),W(e._zod,"optin",()=>t.innerType._zod.optin),W(e._zod,"optout",()=>t.innerType._zod.optout),W(e._zod,"pattern",()=>{const n=t.innerType._zod.pattern;return n?new RegExp(`^(${en(n.source)}|null)$`):void 0}),W(e._zod,"values",()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0),e._zod.parse=(n,o)=>n.value===null?n:t.innerType._zod.run(n,o)}),Ea=_("$ZodDefault",(e,t)=>{ee.init(e,t),e._zod.optin="optional",W(e._zod,"values",()=>t.innerType._zod.values),e._zod.parse=(n,o)=>{if(o.direction==="backward")return t.innerType._zod.run(n,o);if(n.value===void 0)return n.value=t.defaultValue,n;const r=t.innerType._zod.run(n,o);return r instanceof Promise?r.then(s=>xn(s,t)):xn(r,t)}});function xn(e,t){return e.value===void 0&&(e.value=t.defaultValue),e}const $a=_("$ZodPrefault",(e,t)=>{ee.init(e,t),e._zod.optin="optional",W(e._zod,"values",()=>t.innerType._zod.values),e._zod.parse=(n,o)=>(o.direction==="backward"||n.value===void 0&&(n.value=t.defaultValue),t.innerType._zod.run(n,o))}),La=_("$ZodNonOptional",(e,t)=>{ee.init(e,t),W(e._zod,"values",()=>{const n=t.innerType._zod.values;return n?new Set([...n].filter(o=>o!==void 0)):void 0}),e._zod.parse=(n,o)=>{const r=t.innerType._zod.run(n,o);return r instanceof Promise?r.then(s=>bn(s,e)):bn(r,e)}});function bn(e,t){return!e.issues.length&&e.value===void 0&&e.issues.push({code:"invalid_type",expected:"nonoptional",input:e.value,inst:t}),e}const za=_("$ZodCatch",(e,t)=>{ee.init(e,t),W(e._zod,"optin",()=>t.innerType._zod.optin),W(e._zod,"optout",()=>t.innerType._zod.optout),W(e._zod,"values",()=>t.innerType._zod.values),e._zod.parse=(n,o)=>{if(o.direction==="backward")return t.innerType._zod.run(n,o);const r=t.innerType._zod.run(n,o);return r instanceof Promise?r.then(s=>(n.value=s.value,s.issues.length&&(n.value=t.catchValue({...n,error:{issues:s.issues.map(i=>Te(i,o,Ie()))},input:n.value}),n.issues=[]),n)):(n.value=r.value,r.issues.length&&(n.value=t.catchValue({...n,error:{issues:r.issues.map(s=>Te(s,o,Ie()))},input:n.value}),n.issues=[]),n)}}),Aa=_("$ZodPipe",(e,t)=>{ee.init(e,t),W(e._zod,"values",()=>t.in._zod.values),W(e._zod,"optin",()=>t.in._zod.optin),W(e._zod,"optout",()=>t.out._zod.optout),W(e._zod,"propValues",()=>t.in._zod.propValues),e._zod.parse=(n,o)=>{if(o.direction==="backward"){const s=t.out._zod.run(n,o);return s instanceof Promise?s.then(i=>et(i,t.in,o)):et(s,t.in,o)}const r=t.in._zod.run(n,o);return r instanceof Promise?r.then(s=>et(s,t.out,o)):et(r,t.out,o)}});function et(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues},n)}const Oa=_("$ZodReadonly",(e,t)=>{ee.init(e,t),W(e._zod,"propValues",()=>t.innerType._zod.propValues),W(e._zod,"values",()=>t.innerType._zod.values),W(e._zod,"optin",()=>{var n,o;return(o=(n=t.innerType)==null?void 0:n._zod)==null?void 0:o.optin}),W(e._zod,"optout",()=>{var n,o;return(o=(n=t.innerType)==null?void 0:n._zod)==null?void 0:o.optout}),e._zod.parse=(n,o)=>{if(o.direction==="backward")return t.innerType._zod.run(n,o);const r=t.innerType._zod.run(n,o);return r instanceof Promise?r.then(yn):yn(r)}});function yn(e){return e.value=Object.freeze(e.value),e}const Ma=_("$ZodCustom",(e,t)=>{ce.init(e,t),ee.init(e,t),e._zod.parse=(n,o)=>n,e._zod.check=n=>{const o=n.value,r=t.fn(o);if(r instanceof Promise)return r.then(s=>wn(s,n,o,e));wn(r,n,o,e)}});function wn(e,t,n,o){if(!e){const r={code:"custom",input:n,inst:o,path:[...o._zod.def.path??[]],continue:!o._zod.def.abort};o._zod.def.params&&(r.params=o._zod.def.params),t.issues.push(We(r))}}var vn;class Fa{constructor(){this._map=new WeakMap,this._idmap=new Map}add(t,...n){const o=n[0];return this._map.set(t,o),o&&typeof o=="object"&&"id"in o&&this._idmap.set(o.id,t),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(t){const n=this._map.get(t);return n&&typeof n=="object"&&"id"in n&&this._idmap.delete(n.id),this._map.delete(t),this}get(t){const n=t._zod.parent;if(n){const o={...this.get(n)??{}};delete o.id;const r={...o,...this._map.get(t)};return Object.keys(r).length?r:void 0}return this._map.get(t)}has(t){return this._map.has(t)}}function Da(){return new Fa}(vn=globalThis).__zod_globalRegistry??(vn.__zod_globalRegistry=Da());const He=globalThis.__zod_globalRegistry;function Pa(e,t){return new e({type:"string",...O(t)})}function Ra(e,t){return new e({type:"string",format:"email",check:"string_format",abort:!1,...O(t)})}function kn(e,t){return new e({type:"string",format:"guid",check:"string_format",abort:!1,...O(t)})}function Za(e,t){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,...O(t)})}function Ba(e,t){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v4",...O(t)})}function Va(e,t){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v6",...O(t)})}function Ga(e,t){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v7",...O(t)})}function Ha(e,t){return new e({type:"string",format:"url",check:"string_format",abort:!1,...O(t)})}function Ua(e,t){return new e({type:"string",format:"emoji",check:"string_format",abort:!1,...O(t)})}function Wa(e,t){return new e({type:"string",format:"nanoid",check:"string_format",abort:!1,...O(t)})}function Ka(e,t){return new e({type:"string",format:"cuid",check:"string_format",abort:!1,...O(t)})}function Ja(e,t){return new e({type:"string",format:"cuid2",check:"string_format",abort:!1,...O(t)})}function Ya(e,t){return new e({type:"string",format:"ulid",check:"string_format",abort:!1,...O(t)})}function Xa(e,t){return new e({type:"string",format:"xid",check:"string_format",abort:!1,...O(t)})}function qa(e,t){return new e({type:"string",format:"ksuid",check:"string_format",abort:!1,...O(t)})}function Qa(e,t){return new e({type:"string",format:"ipv4",check:"string_format",abort:!1,...O(t)})}function ei(e,t){return new e({type:"string",format:"ipv6",check:"string_format",abort:!1,...O(t)})}function ti(e,t){return new e({type:"string",format:"cidrv4",check:"string_format",abort:!1,...O(t)})}function ni(e,t){return new e({type:"string",format:"cidrv6",check:"string_format",abort:!1,...O(t)})}function oi(e,t){return new e({type:"string",format:"base64",check:"string_format",abort:!1,...O(t)})}function ri(e,t){return new e({type:"string",format:"base64url",check:"string_format",abort:!1,...O(t)})}function si(e,t){return new e({type:"string",format:"e164",check:"string_format",abort:!1,...O(t)})}function ai(e,t){return new e({type:"string",format:"jwt",check:"string_format",abort:!1,...O(t)})}function ii(e,t){return new e({type:"string",format:"datetime",check:"string_format",offset:!1,local:!1,precision:null,...O(t)})}function ci(e,t){return new e({type:"string",format:"date",check:"string_format",...O(t)})}function di(e,t){return new e({type:"string",format:"time",check:"string_format",precision:null,...O(t)})}function li(e,t){return new e({type:"string",format:"duration",check:"string_format",...O(t)})}function ui(e,t){return new e({type:"number",checks:[],...O(t)})}function fi(e,t){return new e({type:"number",check:"number_format",abort:!1,format:"safeint",...O(t)})}function pi(e){return new e({type:"unknown"})}function hi(e,t){return new e({type:"never",...O(t)})}function Nn(e,t){return new mo({check:"less_than",...O(t),value:e,inclusive:!1})}function It(e,t){return new mo({check:"less_than",...O(t),value:e,inclusive:!0})}function jn(e,t){return new go({check:"greater_than",...O(t),value:e,inclusive:!1})}function Tt(e,t){return new go({check:"greater_than",...O(t),value:e,inclusive:!0})}function _n(e,t){return new Ls({check:"multiple_of",...O(t),value:e})}function ko(e,t){return new As({check:"max_length",...O(t),maximum:e})}function ut(e,t){return new Os({check:"min_length",...O(t),minimum:e})}function No(e,t){return new Ms({check:"length_equals",...O(t),length:e})}function mi(e,t){return new Fs({check:"string_format",format:"regex",...O(t),pattern:e})}function gi(e){return new Ds({check:"string_format",format:"lowercase",...O(e)})}function xi(e){return new Ps({check:"string_format",format:"uppercase",...O(e)})}function bi(e,t){return new Rs({check:"string_format",format:"includes",...O(t),includes:e})}function yi(e,t){return new Zs({check:"string_format",format:"starts_with",...O(t),prefix:e})}function wi(e,t){return new Bs({check:"string_format",format:"ends_with",...O(t),suffix:e})}function De(e){return new Vs({check:"overwrite",tx:e})}function vi(e){return De(t=>t.normalize(e))}function ki(){return De(e=>e.trim())}function Ni(){return De(e=>e.toLowerCase())}function ji(){return De(e=>e.toUpperCase())}function _i(){return De(e=>Fr(e))}function Ci(e,t,n){return new e({type:"array",element:t,...O(n)})}function Si(e,t,n){return new e({type:"custom",check:"custom",fn:t,...O(n)})}function Ii(e){const t=Ti(n=>(n.addIssue=o=>{if(typeof o=="string")n.issues.push(We(o,n.value,t._zod.def));else{const r=o;r.fatal&&(r.continue=!1),r.code??(r.code="custom"),r.input??(r.input=n.value),r.inst??(r.inst=t),r.continue??(r.continue=!t._zod.def.abort),n.issues.push(We(r))}},e(n.value,n)));return t}function Ti(e,t){const n=new ce({check:"custom",...O(t)});return n._zod.check=e,n}function jo(e){let t=(e==null?void 0:e.target)??"draft-2020-12";return t==="draft-4"&&(t="draft-04"),t==="draft-7"&&(t="draft-07"),{processors:e.processors??{},metadataRegistry:(e==null?void 0:e.metadata)??He,target:t,unrepresentable:(e==null?void 0:e.unrepresentable)??"throw",override:(e==null?void 0:e.override)??(()=>{}),io:(e==null?void 0:e.io)??"output",counter:0,seen:new Map,cycles:(e==null?void 0:e.cycles)??"ref",reused:(e==null?void 0:e.reused)??"inline",external:(e==null?void 0:e.external)??void 0}}function oe(e,t,n={path:[],schemaPath:[]}){var u,f;var o;const r=e._zod.def,s=t.seen.get(e);if(s)return s.count++,n.schemaPath.includes(e)&&(s.cycle=n.path),s.schema;const i={schema:{},count:1,cycle:void 0,path:n.path};t.seen.set(e,i);const c=(f=(u=e._zod).toJSONSchema)==null?void 0:f.call(u);if(c)i.schema=c;else{const h={...n,schemaPath:[...n.schemaPath,e],path:n.path};if(e._zod.processJSONSchema)e._zod.processJSONSchema(t,i.schema,h);else{const m=i.schema,p=t.processors[r.type];if(!p)throw new Error(`[toJSONSchema]: Non-representable type encountered: ${r.type}`);p(e,t,m,h)}const g=e._zod.parent;g&&(i.ref||(i.ref=g),oe(g,t,h),t.seen.get(g).isParent=!0)}const d=t.metadataRegistry.get(e);return d&&Object.assign(i.schema,d),t.io==="input"&&ae(e)&&(delete i.schema.examples,delete i.schema.default),t.io==="input"&&i.schema._prefault&&((o=i.schema).default??(o.default=i.schema._prefault)),delete i.schema._prefault,t.seen.get(e).schema}function _o(e,t){var i,c,d,l;const n=e.seen.get(t);if(!n)throw new Error("Unprocessed schema. This is a bug in Zod.");const o=new Map;for(const u of e.seen.entries()){const f=(i=e.metadataRegistry.get(u[0]))==null?void 0:i.id;if(f){const h=o.get(f);if(h&&h!==u[0])throw new Error(`Duplicate schema id "${f}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);o.set(f,u[0])}}const r=u=>{var p;const f=e.target==="draft-2020-12"?"$defs":"definitions";if(e.external){const N=(p=e.external.registry.get(u[0]))==null?void 0:p.id,y=e.external.uri??(k=>k);if(N)return{ref:y(N)};const x=u[1].defId??u[1].schema.id??`schema${e.counter++}`;return u[1].defId=x,{defId:x,ref:`${y("__shared")}#/${f}/${x}`}}if(u[1]===n)return{ref:"#"};const g=`#/${f}/`,m=u[1].schema.id??`__schema${e.counter++}`;return{defId:m,ref:g+m}},s=u=>{if(u[1].schema.$ref)return;const f=u[1],{ref:h,defId:g}=r(u);f.def={...f.schema},g&&(f.defId=g);const m=f.schema;for(const p in m)delete m[p];m.$ref=h};if(e.cycles==="throw")for(const u of e.seen.entries()){const f=u[1];if(f.cycle)throw new Error(`Cycle detected: #/${(c=f.cycle)==null?void 0:c.join("/")}/ + +Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`)}for(const u of e.seen.entries()){const f=u[1];if(t===u[0]){s(u);continue}if(e.external){const g=(d=e.external.registry.get(u[0]))==null?void 0:d.id;if(t!==u[0]&&g){s(u);continue}}if((l=e.metadataRegistry.get(u[0]))==null?void 0:l.id){s(u);continue}if(f.cycle){s(u);continue}if(f.count>1&&e.reused==="ref"){s(u);continue}}}function Co(e,t){var i,c,d;const n=e.seen.get(t);if(!n)throw new Error("Unprocessed schema. This is a bug in Zod.");const o=l=>{const u=e.seen.get(l);if(u.ref===null)return;const f=u.def??u.schema,h={...f},g=u.ref;if(u.ref=null,g){o(g);const p=e.seen.get(g),N=p.schema;if(N.$ref&&(e.target==="draft-07"||e.target==="draft-04"||e.target==="openapi-3.0")?(f.allOf=f.allOf??[],f.allOf.push(N)):Object.assign(f,N),Object.assign(f,h),l._zod.parent===g)for(const x in f)x==="$ref"||x==="allOf"||x in h||delete f[x];if(N.$ref&&p.def)for(const x in f)x==="$ref"||x==="allOf"||x in p.def&&JSON.stringify(f[x])===JSON.stringify(p.def[x])&&delete f[x]}const m=l._zod.parent;if(m&&m!==g){o(m);const p=e.seen.get(m);if(p!=null&&p.schema.$ref&&(f.$ref=p.schema.$ref,p.def))for(const N in f)N==="$ref"||N==="allOf"||N in p.def&&JSON.stringify(f[N])===JSON.stringify(p.def[N])&&delete f[N]}e.override({zodSchema:l,jsonSchema:f,path:u.path??[]})};for(const l of[...e.seen.entries()].reverse())o(l[0]);const r={};if(e.target==="draft-2020-12"?r.$schema="https://json-schema.org/draft/2020-12/schema":e.target==="draft-07"?r.$schema="http://json-schema.org/draft-07/schema#":e.target==="draft-04"?r.$schema="http://json-schema.org/draft-04/schema#":e.target,(i=e.external)!=null&&i.uri){const l=(c=e.external.registry.get(t))==null?void 0:c.id;if(!l)throw new Error("Schema is missing an `id` property");r.$id=e.external.uri(l)}Object.assign(r,n.def??n.schema);const s=((d=e.external)==null?void 0:d.defs)??{};for(const l of e.seen.entries()){const u=l[1];u.def&&u.defId&&(s[u.defId]=u.def)}e.external||Object.keys(s).length>0&&(e.target==="draft-2020-12"?r.$defs=s:r.definitions=s);try{const l=JSON.parse(JSON.stringify(r));return Object.defineProperty(l,"~standard",{value:{...t["~standard"],jsonSchema:{input:ft(t,"input",e.processors),output:ft(t,"output",e.processors)}},enumerable:!1,writable:!1}),l}catch{throw new Error("Error converting schema to JSON.")}}function ae(e,t){const n=t??{seen:new Set};if(n.seen.has(e))return!1;n.seen.add(e);const o=e._zod.def;if(o.type==="transform")return!0;if(o.type==="array")return ae(o.element,n);if(o.type==="set")return ae(o.valueType,n);if(o.type==="lazy")return ae(o.getter(),n);if(o.type==="promise"||o.type==="optional"||o.type==="nonoptional"||o.type==="nullable"||o.type==="readonly"||o.type==="default"||o.type==="prefault")return ae(o.innerType,n);if(o.type==="intersection")return ae(o.left,n)||ae(o.right,n);if(o.type==="record"||o.type==="map")return ae(o.keyType,n)||ae(o.valueType,n);if(o.type==="pipe")return ae(o.in,n)||ae(o.out,n);if(o.type==="object"){for(const r in o.shape)if(ae(o.shape[r],n))return!0;return!1}if(o.type==="union"){for(const r of o.options)if(ae(r,n))return!0;return!1}if(o.type==="tuple"){for(const r of o.items)if(ae(r,n))return!0;return!!(o.rest&&ae(o.rest,n))}return!1}const Ei=(e,t={})=>n=>{const o=jo({...n,processors:t});return oe(e,o),_o(o,e),Co(o,e)},ft=(e,t,n={})=>o=>{const{libraryOptions:r,target:s}=o??{},i=jo({...r??{},target:s,io:t,processors:n});return oe(e,i),_o(i,e),Co(i,e)},$i={guid:"uuid",url:"uri",datetime:"date-time",json_string:"json-string",regex:""},Li=(e,t,n,o)=>{const r=n;r.type="string";const{minimum:s,maximum:i,format:c,patterns:d,contentEncoding:l}=e._zod.bag;if(typeof s=="number"&&(r.minLength=s),typeof i=="number"&&(r.maxLength=i),c&&(r.format=$i[c]??c,r.format===""&&delete r.format,c==="time"&&delete r.format),l&&(r.contentEncoding=l),d&&d.size>0){const u=[...d];u.length===1?r.pattern=u[0].source:u.length>1&&(r.allOf=[...u.map(f=>({...t.target==="draft-07"||t.target==="draft-04"||t.target==="openapi-3.0"?{type:"string"}:{},pattern:f.source}))])}},zi=(e,t,n,o)=>{const r=n,{minimum:s,maximum:i,format:c,multipleOf:d,exclusiveMaximum:l,exclusiveMinimum:u}=e._zod.bag;typeof c=="string"&&c.includes("int")?r.type="integer":r.type="number",typeof u=="number"&&(t.target==="draft-04"||t.target==="openapi-3.0"?(r.minimum=u,r.exclusiveMinimum=!0):r.exclusiveMinimum=u),typeof s=="number"&&(r.minimum=s,typeof u=="number"&&t.target!=="draft-04"&&(u>=s?delete r.minimum:delete r.exclusiveMinimum)),typeof l=="number"&&(t.target==="draft-04"||t.target==="openapi-3.0"?(r.maximum=l,r.exclusiveMaximum=!0):r.exclusiveMaximum=l),typeof i=="number"&&(r.maximum=i,typeof l=="number"&&t.target!=="draft-04"&&(l<=i?delete r.maximum:delete r.exclusiveMaximum)),typeof d=="number"&&(r.multipleOf=d)},Ai=(e,t,n,o)=>{n.not={}},Oi=(e,t,n,o)=>{},Mi=(e,t,n,o)=>{const r=e._zod.def,s=ro(r.entries);s.every(i=>typeof i=="number")&&(n.type="number"),s.every(i=>typeof i=="string")&&(n.type="string"),n.enum=s},Fi=(e,t,n,o)=>{if(t.unrepresentable==="throw")throw new Error("Custom types cannot be represented in JSON Schema")},Di=(e,t,n,o)=>{if(t.unrepresentable==="throw")throw new Error("Transforms cannot be represented in JSON Schema")},Pi=(e,t,n,o)=>{const r=n,s=e._zod.def,{minimum:i,maximum:c}=e._zod.bag;typeof i=="number"&&(r.minItems=i),typeof c=="number"&&(r.maxItems=c),r.type="array",r.items=oe(s.element,t,{...o,path:[...o.path,"items"]})},Ri=(e,t,n,o)=>{var l;const r=n,s=e._zod.def;r.type="object",r.properties={};const i=s.shape;for(const u in i)r.properties[u]=oe(i[u],t,{...o,path:[...o.path,"properties",u]});const c=new Set(Object.keys(i)),d=new Set([...c].filter(u=>{const f=s.shape[u]._zod;return t.io==="input"?f.optin===void 0:f.optout===void 0}));d.size>0&&(r.required=Array.from(d)),((l=s.catchall)==null?void 0:l._zod.def.type)==="never"?r.additionalProperties=!1:s.catchall?s.catchall&&(r.additionalProperties=oe(s.catchall,t,{...o,path:[...o.path,"additionalProperties"]})):t.io==="output"&&(r.additionalProperties=!1)},Zi=(e,t,n,o)=>{const r=e._zod.def,s=r.inclusive===!1,i=r.options.map((c,d)=>oe(c,t,{...o,path:[...o.path,s?"oneOf":"anyOf",d]}));s?n.oneOf=i:n.anyOf=i},Bi=(e,t,n,o)=>{const r=e._zod.def,s=oe(r.left,t,{...o,path:[...o.path,"allOf",0]}),i=oe(r.right,t,{...o,path:[...o.path,"allOf",1]}),c=l=>"allOf"in l&&Object.keys(l).length===1,d=[...c(s)?s.allOf:[s],...c(i)?i.allOf:[i]];n.allOf=d},Vi=(e,t,n,o)=>{const r=n,s=e._zod.def;r.type="array";const i=t.target==="draft-2020-12"?"prefixItems":"items",c=t.target==="draft-2020-12"||t.target==="openapi-3.0"?"items":"additionalItems",d=s.items.map((h,g)=>oe(h,t,{...o,path:[...o.path,i,g]})),l=s.rest?oe(s.rest,t,{...o,path:[...o.path,c,...t.target==="openapi-3.0"?[s.items.length]:[]]}):null;t.target==="draft-2020-12"?(r.prefixItems=d,l&&(r.items=l)):t.target==="openapi-3.0"?(r.items={anyOf:d},l&&r.items.anyOf.push(l),r.minItems=d.length,l||(r.maxItems=d.length)):(r.items=d,l&&(r.additionalItems=l));const{minimum:u,maximum:f}=e._zod.bag;typeof u=="number"&&(r.minItems=u),typeof f=="number"&&(r.maxItems=f)},Gi=(e,t,n,o)=>{const r=e._zod.def,s=oe(r.innerType,t,o),i=t.seen.get(e);t.target==="openapi-3.0"?(i.ref=r.innerType,n.nullable=!0):n.anyOf=[s,{type:"null"}]},Hi=(e,t,n,o)=>{const r=e._zod.def;oe(r.innerType,t,o);const s=t.seen.get(e);s.ref=r.innerType},Ui=(e,t,n,o)=>{const r=e._zod.def;oe(r.innerType,t,o);const s=t.seen.get(e);s.ref=r.innerType,n.default=JSON.parse(JSON.stringify(r.defaultValue))},Wi=(e,t,n,o)=>{const r=e._zod.def;oe(r.innerType,t,o);const s=t.seen.get(e);s.ref=r.innerType,t.io==="input"&&(n._prefault=JSON.parse(JSON.stringify(r.defaultValue)))},Ki=(e,t,n,o)=>{const r=e._zod.def;oe(r.innerType,t,o);const s=t.seen.get(e);s.ref=r.innerType;let i;try{i=r.catchValue(void 0)}catch{throw new Error("Dynamic catch values are not supported in JSON Schema")}n.default=i},Ji=(e,t,n,o)=>{const r=e._zod.def,s=t.io==="input"?r.in._zod.def.type==="transform"?r.out:r.in:r.out;oe(s,t,o);const i=t.seen.get(e);i.ref=s},Yi=(e,t,n,o)=>{const r=e._zod.def;oe(r.innerType,t,o);const s=t.seen.get(e);s.ref=r.innerType,n.readOnly=!0},So=(e,t,n,o)=>{const r=e._zod.def;oe(r.innerType,t,o);const s=t.seen.get(e);s.ref=r.innerType},Xi=_("ZodISODateTime",(e,t)=>{oa.init(e,t),q.init(e,t)});function qi(e){return ii(Xi,e)}const Qi=_("ZodISODate",(e,t)=>{ra.init(e,t),q.init(e,t)});function ec(e){return ci(Qi,e)}const tc=_("ZodISOTime",(e,t)=>{sa.init(e,t),q.init(e,t)});function nc(e){return di(tc,e)}const oc=_("ZodISODuration",(e,t)=>{aa.init(e,t),q.init(e,t)});function rc(e){return li(oc,e)}const sc=(e,t)=>{co.init(e,t),e.name="ZodError",Object.defineProperties(e,{format:{value:n=>Yr(e,n)},flatten:{value:n=>Jr(e,n)},addIssue:{value:n=>{e.issues.push(n),e.message=JSON.stringify(e.issues,Lt,2)}},addIssues:{value:n=>{e.issues.push(...n),e.message=JSON.stringify(e.issues,Lt,2)}},isEmpty:{get(){return e.issues.length===0}}})},he=_("ZodError",sc,{Parent:Error}),ac=on(he),ic=rn(he),cc=vt(he),dc=kt(he),lc=Qr(he),uc=es(he),fc=ts(he),pc=ns(he),hc=os(he),mc=rs(he),gc=ss(he),xc=as(he),ne=_("ZodType",(e,t)=>(ee.init(e,t),Object.assign(e["~standard"],{jsonSchema:{input:ft(e,"input"),output:ft(e,"output")}}),e.toJSONSchema=Ei(e,{}),e.def=t,e.type=t.type,Object.defineProperty(e,"_def",{value:t}),e.check=(...n)=>e.clone(ke(t,{checks:[...t.checks??[],...n.map(o=>typeof o=="function"?{_zod:{check:o,def:{check:"custom"},onattach:[]}}:o)]}),{parent:!0}),e.with=e.check,e.clone=(n,o)=>Ne(e,n,o),e.brand=()=>e,e.register=((n,o)=>(n.add(e,o),e)),e.parse=(n,o)=>ac(e,n,o,{callee:e.parse}),e.safeParse=(n,o)=>cc(e,n,o),e.parseAsync=async(n,o)=>ic(e,n,o,{callee:e.parseAsync}),e.safeParseAsync=async(n,o)=>dc(e,n,o),e.spa=e.safeParseAsync,e.encode=(n,o)=>lc(e,n,o),e.decode=(n,o)=>uc(e,n,o),e.encodeAsync=async(n,o)=>fc(e,n,o),e.decodeAsync=async(n,o)=>pc(e,n,o),e.safeEncode=(n,o)=>hc(e,n,o),e.safeDecode=(n,o)=>mc(e,n,o),e.safeEncodeAsync=async(n,o)=>gc(e,n,o),e.safeDecodeAsync=async(n,o)=>xc(e,n,o),e.refine=(n,o)=>e.check(ld(n,o)),e.superRefine=n=>e.check(ud(n)),e.overwrite=n=>e.check(De(n)),e.optional=()=>Tn(e),e.exactOptional=()=>Xc(e),e.nullable=()=>En(e),e.nullish=()=>Tn(En(e)),e.nonoptional=n=>od(e,n),e.array=()=>ie(e),e.or=n=>Vc([e,n]),e.and=n=>Hc(e,n),e.transform=n=>$n(e,Jc(n)),e.default=n=>ed(e,n),e.prefault=n=>nd(e,n),e.catch=n=>sd(e,n),e.pipe=n=>$n(e,n),e.readonly=()=>cd(e),e.describe=n=>{const o=e.clone();return He.add(o,{description:n}),o},Object.defineProperty(e,"description",{get(){var n;return(n=He.get(e))==null?void 0:n.description},configurable:!0}),e.meta=(...n)=>{if(n.length===0)return He.get(e);const o=e.clone();return He.add(o,n[0]),o},e.isOptional=()=>e.safeParse(void 0).success,e.isNullable=()=>e.safeParse(null).success,e.apply=n=>n(e),e)),Io=_("_ZodString",(e,t)=>{sn.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(o,r,s)=>Li(e,o,r);const n=e._zod.bag;e.format=n.format??null,e.minLength=n.minimum??null,e.maxLength=n.maximum??null,e.regex=(...o)=>e.check(mi(...o)),e.includes=(...o)=>e.check(bi(...o)),e.startsWith=(...o)=>e.check(yi(...o)),e.endsWith=(...o)=>e.check(wi(...o)),e.min=(...o)=>e.check(ut(...o)),e.max=(...o)=>e.check(ko(...o)),e.length=(...o)=>e.check(No(...o)),e.nonempty=(...o)=>e.check(ut(1,...o)),e.lowercase=o=>e.check(gi(o)),e.uppercase=o=>e.check(xi(o)),e.trim=()=>e.check(ki()),e.normalize=(...o)=>e.check(vi(...o)),e.toLowerCase=()=>e.check(Ni()),e.toUpperCase=()=>e.check(ji()),e.slugify=()=>e.check(_i())}),bc=_("ZodString",(e,t)=>{sn.init(e,t),Io.init(e,t),e.email=n=>e.check(Ra(yc,n)),e.url=n=>e.check(Ha(wc,n)),e.jwt=n=>e.check(ai(Oc,n)),e.emoji=n=>e.check(Ua(vc,n)),e.guid=n=>e.check(kn(Cn,n)),e.uuid=n=>e.check(Za(tt,n)),e.uuidv4=n=>e.check(Ba(tt,n)),e.uuidv6=n=>e.check(Va(tt,n)),e.uuidv7=n=>e.check(Ga(tt,n)),e.nanoid=n=>e.check(Wa(kc,n)),e.guid=n=>e.check(kn(Cn,n)),e.cuid=n=>e.check(Ka(Nc,n)),e.cuid2=n=>e.check(Ja(jc,n)),e.ulid=n=>e.check(Ya(_c,n)),e.base64=n=>e.check(oi(Lc,n)),e.base64url=n=>e.check(ri(zc,n)),e.xid=n=>e.check(Xa(Cc,n)),e.ksuid=n=>e.check(qa(Sc,n)),e.ipv4=n=>e.check(Qa(Ic,n)),e.ipv6=n=>e.check(ei(Tc,n)),e.cidrv4=n=>e.check(ti(Ec,n)),e.cidrv6=n=>e.check(ni($c,n)),e.e164=n=>e.check(si(Ac,n)),e.datetime=n=>e.check(qi(n)),e.date=n=>e.check(ec(n)),e.time=n=>e.check(nc(n)),e.duration=n=>e.check(rc(n))});function H(e){return Pa(bc,e)}const q=_("ZodStringFormat",(e,t)=>{X.init(e,t),Io.init(e,t)}),yc=_("ZodEmail",(e,t)=>{Ks.init(e,t),q.init(e,t)}),Cn=_("ZodGUID",(e,t)=>{Us.init(e,t),q.init(e,t)}),tt=_("ZodUUID",(e,t)=>{Ws.init(e,t),q.init(e,t)}),wc=_("ZodURL",(e,t)=>{Js.init(e,t),q.init(e,t)}),vc=_("ZodEmoji",(e,t)=>{Ys.init(e,t),q.init(e,t)}),kc=_("ZodNanoID",(e,t)=>{Xs.init(e,t),q.init(e,t)}),Nc=_("ZodCUID",(e,t)=>{qs.init(e,t),q.init(e,t)}),jc=_("ZodCUID2",(e,t)=>{Qs.init(e,t),q.init(e,t)}),_c=_("ZodULID",(e,t)=>{ea.init(e,t),q.init(e,t)}),Cc=_("ZodXID",(e,t)=>{ta.init(e,t),q.init(e,t)}),Sc=_("ZodKSUID",(e,t)=>{na.init(e,t),q.init(e,t)}),Ic=_("ZodIPv4",(e,t)=>{ia.init(e,t),q.init(e,t)}),Tc=_("ZodIPv6",(e,t)=>{ca.init(e,t),q.init(e,t)}),Ec=_("ZodCIDRv4",(e,t)=>{da.init(e,t),q.init(e,t)}),$c=_("ZodCIDRv6",(e,t)=>{la.init(e,t),q.init(e,t)}),Lc=_("ZodBase64",(e,t)=>{ua.init(e,t),q.init(e,t)}),zc=_("ZodBase64URL",(e,t)=>{pa.init(e,t),q.init(e,t)}),Ac=_("ZodE164",(e,t)=>{ha.init(e,t),q.init(e,t)}),Oc=_("ZodJWT",(e,t)=>{ga.init(e,t),q.init(e,t)}),To=_("ZodNumber",(e,t)=>{bo.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(o,r,s)=>zi(e,o,r),e.gt=(o,r)=>e.check(jn(o,r)),e.gte=(o,r)=>e.check(Tt(o,r)),e.min=(o,r)=>e.check(Tt(o,r)),e.lt=(o,r)=>e.check(Nn(o,r)),e.lte=(o,r)=>e.check(It(o,r)),e.max=(o,r)=>e.check(It(o,r)),e.int=o=>e.check(Sn(o)),e.safe=o=>e.check(Sn(o)),e.positive=o=>e.check(jn(0,o)),e.nonnegative=o=>e.check(Tt(0,o)),e.negative=o=>e.check(Nn(0,o)),e.nonpositive=o=>e.check(It(0,o)),e.multipleOf=(o,r)=>e.check(_n(o,r)),e.step=(o,r)=>e.check(_n(o,r)),e.finite=()=>e;const n=e._zod.bag;e.minValue=Math.max(n.minimum??Number.NEGATIVE_INFINITY,n.exclusiveMinimum??Number.NEGATIVE_INFINITY)??null,e.maxValue=Math.min(n.maximum??Number.POSITIVE_INFINITY,n.exclusiveMaximum??Number.POSITIVE_INFINITY)??null,e.isInt=(n.format??"").includes("int")||Number.isSafeInteger(n.multipleOf??.5),e.isFinite=!0,e.format=n.format??null});function pt(e){return ui(To,e)}const Mc=_("ZodNumberFormat",(e,t)=>{xa.init(e,t),To.init(e,t)});function Sn(e){return fi(Mc,e)}const Fc=_("ZodUnknown",(e,t)=>{ba.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Oi()});function In(){return pi(Fc)}const Dc=_("ZodNever",(e,t)=>{ya.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Ai(e,n,o)});function Pc(e){return hi(Dc,e)}const Rc=_("ZodArray",(e,t)=>{wa.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Pi(e,n,o,r),e.element=t.element,e.min=(n,o)=>e.check(ut(n,o)),e.nonempty=n=>e.check(ut(1,n)),e.max=(n,o)=>e.check(ko(n,o)),e.length=(n,o)=>e.check(No(n,o)),e.unwrap=()=>e.element});function ie(e,t){return Ci(Rc,e,t)}const Zc=_("ZodObject",(e,t)=>{ka.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Ri(e,n,o,r),W(e,"shape",()=>t.shape),e.keyof=()=>Ee(Object.keys(e._zod.def.shape)),e.catchall=n=>e.clone({...e._zod.def,catchall:n}),e.passthrough=()=>e.clone({...e._zod.def,catchall:In()}),e.loose=()=>e.clone({...e._zod.def,catchall:In()}),e.strict=()=>e.clone({...e._zod.def,catchall:Pc()}),e.strip=()=>e.clone({...e._zod.def,catchall:void 0}),e.extend=n=>Gr(e,n),e.safeExtend=n=>Hr(e,n),e.merge=n=>Ur(e,n),e.pick=n=>Br(e,n),e.omit=n=>Vr(e,n),e.partial=(...n)=>Wr(Eo,e,n[0]),e.required=(...n)=>Kr($o,e,n[0])});function je(e,t){const n={type:"object",shape:e??{},...O(t)};return new Zc(n)}const Bc=_("ZodUnion",(e,t)=>{Na.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Zi(e,n,o,r),e.options=t.options});function Vc(e,t){return new Bc({type:"union",options:e,...O(t)})}const Gc=_("ZodIntersection",(e,t)=>{ja.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Bi(e,n,o,r)});function Hc(e,t){return new Gc({type:"intersection",left:e,right:t})}const Uc=_("ZodTuple",(e,t)=>{_a.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Vi(e,n,o,r),e.rest=n=>e.clone({...e._zod.def,rest:n})});function Wc(e,t,n){const o=t instanceof ee,r=o?n:t,s=o?t:null;return new Uc({type:"tuple",items:e,rest:s,...O(r)})}const At=_("ZodEnum",(e,t)=>{Ca.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(o,r,s)=>Mi(e,o,r),e.enum=t.entries,e.options=Object.values(t.entries);const n=new Set(Object.keys(t.entries));e.extract=(o,r)=>{const s={};for(const i of o)if(n.has(i))s[i]=t.entries[i];else throw new Error(`Key ${i} not found in enum`);return new At({...t,checks:[],...O(r),entries:s})},e.exclude=(o,r)=>{const s={...t.entries};for(const i of o)if(n.has(i))delete s[i];else throw new Error(`Key ${i} not found in enum`);return new At({...t,checks:[],...O(r),entries:s})}});function Ee(e,t){const n=Array.isArray(e)?Object.fromEntries(e.map(o=>[o,o])):e;return new At({type:"enum",entries:n,...O(t)})}const Kc=_("ZodTransform",(e,t)=>{Sa.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Di(e,n),e._zod.parse=(n,o)=>{if(o.direction==="backward")throw new no(e.constructor.name);n.addIssue=s=>{if(typeof s=="string")n.issues.push(We(s,n.value,t));else{const i=s;i.fatal&&(i.continue=!1),i.code??(i.code="custom"),i.input??(i.input=n.value),i.inst??(i.inst=e),n.issues.push(We(i))}};const r=t.transform(n.value,n);return r instanceof Promise?r.then(s=>(n.value=s,n)):(n.value=r,n)}});function Jc(e){return new Kc({type:"transform",transform:e})}const Eo=_("ZodOptional",(e,t)=>{vo.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>So(e,n,o,r),e.unwrap=()=>e._zod.def.innerType});function Tn(e){return new Eo({type:"optional",innerType:e})}const Yc=_("ZodExactOptional",(e,t)=>{Ia.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>So(e,n,o,r),e.unwrap=()=>e._zod.def.innerType});function Xc(e){return new Yc({type:"optional",innerType:e})}const qc=_("ZodNullable",(e,t)=>{Ta.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Gi(e,n,o,r),e.unwrap=()=>e._zod.def.innerType});function En(e){return new qc({type:"nullable",innerType:e})}const Qc=_("ZodDefault",(e,t)=>{Ea.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Ui(e,n,o,r),e.unwrap=()=>e._zod.def.innerType,e.removeDefault=e.unwrap});function ed(e,t){return new Qc({type:"default",innerType:e,get defaultValue(){return typeof t=="function"?t():ao(t)}})}const td=_("ZodPrefault",(e,t)=>{$a.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Wi(e,n,o,r),e.unwrap=()=>e._zod.def.innerType});function nd(e,t){return new td({type:"prefault",innerType:e,get defaultValue(){return typeof t=="function"?t():ao(t)}})}const $o=_("ZodNonOptional",(e,t)=>{La.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Hi(e,n,o,r),e.unwrap=()=>e._zod.def.innerType});function od(e,t){return new $o({type:"nonoptional",innerType:e,...O(t)})}const rd=_("ZodCatch",(e,t)=>{za.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Ki(e,n,o,r),e.unwrap=()=>e._zod.def.innerType,e.removeCatch=e.unwrap});function sd(e,t){return new rd({type:"catch",innerType:e,catchValue:typeof t=="function"?t:()=>t})}const ad=_("ZodPipe",(e,t)=>{Aa.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Ji(e,n,o,r),e.in=t.in,e.out=t.out});function $n(e,t){return new ad({type:"pipe",in:e,out:t})}const id=_("ZodReadonly",(e,t)=>{Oa.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Yi(e,n,o,r),e.unwrap=()=>e._zod.def.innerType});function cd(e){return new id({type:"readonly",innerType:e})}const dd=_("ZodCustom",(e,t)=>{Ma.init(e,t),ne.init(e,t),e._zod.processJSONSchema=(n,o,r)=>Fi(e,n)});function ld(e,t={}){return Si(dd,e,t)}function ud(e){return Ii(e)}const fd=Ee(["imports","exports","contains","inherits","implements","calls","subscribes","publishes","middleware","reads_from","writes_to","transforms","validates","depends_on","tested_by","configures","related","similar_to","deploys","serves","provisions","triggers","migrates","documents","routes","defines_schema","contains_flow","flow_step","cross_domain","cites","contradicts","builds_on","exemplifies","categorized_under","authored_by"]),Ln={func:"function",fn:"function",method:"function",interface:"class",struct:"class",mod:"module",pkg:"module",package:"module",container:"service",deployment:"service",pod:"service",doc:"document",readme:"document",docs:"document",job:"pipeline",ci:"pipeline",route:"endpoint",api:"endpoint",query:"endpoint",mutation:"endpoint",setting:"config",env:"config",configuration:"config",infra:"resource",infrastructure:"resource",terraform:"resource",migration:"table",database:"table",db:"table",view:"table",proto:"schema",protobuf:"schema",definition:"schema",typedef:"schema",business_domain:"domain",business_flow:"flow",business_process:"flow",task:"step",business_step:"step",note:"article",page:"article",wiki_page:"article",person:"entity",actor:"entity",organization:"entity",tag:"topic",category:"topic",theme:"topic",assertion:"claim",decision:"claim",thesis:"claim",reference:"source",raw:"source",paper:"source"},zn={extends:"inherits",invokes:"calls",invoke:"calls",uses:"depends_on",requires:"depends_on",relates_to:"related",related_to:"related",similar:"similar_to",import:"imports",export:"exports",contain:"contains",publish:"publishes",subscribe:"subscribes",describes:"documents",documented_by:"documents",creates:"provisions",exposes:"serves",listens:"serves",deploys_to:"deploys",migrates_to:"migrates",routes_to:"routes",triggers_on:"triggers",fires:"triggers",defines:"defines_schema",has_flow:"contains_flow",next_step:"flow_step",interacts_with:"cross_domain",references:"cites",cites_source:"cites",conflicts_with:"contradicts",disagrees_with:"contradicts",refines:"builds_on",elaborates:"builds_on",illustrates:"exemplifies",instance_of:"exemplifies",example_of:"exemplifies",belongs_to:"categorized_under",tagged_with:"categorized_under",written_by:"authored_by",created_by:"authored_by"},An={low:"simple",easy:"simple",medium:"moderate",intermediate:"moderate",high:"complex",hard:"complex",difficult:"complex"},On={to:"forward",outbound:"forward",from:"backward",inbound:"backward",both:"bidirectional",mutual:"bidirectional"};function pd(e){const t={...e};return(e.tour===null||e.tour===void 0)&&(t.tour=[]),(e.layers===null||e.layers===void 0)&&(t.layers=[]),Array.isArray(e.nodes)&&(t.nodes=e.nodes.map(n=>{if(typeof n!="object"||n===null)return n;const o={...n};return o.filePath===null&&delete o.filePath,o.lineRange===null&&delete o.lineRange,o.languageNotes===null&&delete o.languageNotes,typeof o.type=="string"&&(o.type=o.type.toLowerCase()),typeof o.complexity=="string"&&(o.complexity=o.complexity.toLowerCase()),o})),Array.isArray(e.edges)&&(t.edges=e.edges.map(n=>{if(typeof n!="object"||n===null)return n;const o={...n};return o.description===null&&delete o.description,typeof o.type=="string"&&(o.type=o.type.toLowerCase()),typeof o.direction=="string"&&(o.direction=o.direction.toLowerCase()),o})),Array.isArray(t.tour)&&(t.tour=t.tour.map(n=>{if(typeof n!="object"||n===null)return n;const o={...n};return o.languageLesson===null&&delete o.languageLesson,o})),t}function hd(e){const t=[],n={...e};return Array.isArray(e.nodes)&&(n.nodes=e.nodes.map((o,r)=>{if(typeof o!="object"||o===null)return o;const s={...o},i=s.name||s.id||`index ${r}`;if((!s.type||typeof s.type!="string")&&(s.type="file",t.push({level:"auto-corrected",category:"missing-field",message:`nodes[${r}] ("${i}"): missing "type" — defaulted to "file"`,path:`nodes[${r}].type`})),!s.complexity||s.complexity==="")s.complexity="moderate",t.push({level:"auto-corrected",category:"missing-field",message:`nodes[${r}] ("${i}"): missing "complexity" — defaulted to "moderate"`,path:`nodes[${r}].complexity`});else if(typeof s.complexity=="string"&&s.complexity in An){const c=s.complexity;s.complexity=An[s.complexity],t.push({level:"auto-corrected",category:"alias",message:`nodes[${r}] ("${i}"): complexity "${c}" — mapped to "${s.complexity}"`,path:`nodes[${r}].complexity`})}return Array.isArray(s.tags)||(s.tags=[],t.push({level:"auto-corrected",category:"missing-field",message:`nodes[${r}] ("${i}"): missing "tags" — defaulted to []`,path:`nodes[${r}].tags`})),(!s.summary||typeof s.summary!="string")&&(s.summary=s.name||"No summary",t.push({level:"auto-corrected",category:"missing-field",message:`nodes[${r}] ("${i}"): missing "summary" — defaulted to name`,path:`nodes[${r}].summary`})),s})),Array.isArray(e.edges)&&(n.edges=e.edges.map((o,r)=>{if(typeof o!="object"||o===null)return o;const s={...o};if((!s.type||typeof s.type!="string")&&(s.type="depends_on",t.push({level:"auto-corrected",category:"missing-field",message:`edges[${r}]: missing "type" — defaulted to "depends_on"`,path:`edges[${r}].type`})),!s.direction||typeof s.direction!="string")s.direction="forward",t.push({level:"auto-corrected",category:"missing-field",message:`edges[${r}]: missing "direction" — defaulted to "forward"`,path:`edges[${r}].direction`});else if(s.direction in On){const i=s.direction;s.direction=On[s.direction],t.push({level:"auto-corrected",category:"alias",message:`edges[${r}]: direction "${i}" — mapped to "${s.direction}"`,path:`edges[${r}].direction`})}if(s.weight===void 0||s.weight===null)s.weight=.5,t.push({level:"auto-corrected",category:"missing-field",message:`edges[${r}]: missing "weight" — defaulted to 0.5`,path:`edges[${r}].weight`});else if(typeof s.weight=="string"){const i=parseFloat(s.weight);if(isNaN(i)){const c=s.weight;s.weight=.5,t.push({level:"auto-corrected",category:"type-coercion",message:`edges[${r}]: weight "${c}" is not a valid number — defaulted to 0.5`,path:`edges[${r}].weight`})}else{const c=s.weight;s.weight=i,t.push({level:"auto-corrected",category:"type-coercion",message:`edges[${r}]: weight was string "${c}" — coerced to number`,path:`edges[${r}].weight`})}}if(typeof s.weight=="number"&&(s.weight<0||s.weight>1)){const i=s.weight;s.weight=Math.max(0,Math.min(1,s.weight)),t.push({level:"auto-corrected",category:"out-of-range",message:`edges[${r}]: weight ${i} clamped to ${s.weight}`,path:`edges[${r}].weight`})}return s})),{data:n,issues:t}}const md=je({entities:ie(H()).optional(),businessRules:ie(H()).optional(),crossDomainInteractions:ie(H()).optional(),entryPoint:H().optional(),entryType:Ee(["http","cli","event","cron","manual"]).optional()}).passthrough(),gd=je({wikilinks:ie(H()).optional(),backlinks:ie(H()).optional(),category:H().optional(),content:H().optional()}).passthrough(),Lo=je({id:H(),type:Ee(["file","function","class","module","concept","config","document","service","table","endpoint","pipeline","schema","resource","domain","flow","step","article","entity","topic","claim","source"]),name:H(),filePath:H().optional(),lineRange:Wc([pt(),pt()]).optional(),summary:H(),tags:ie(H()),complexity:Ee(["simple","moderate","complex"]),languageNotes:H().optional(),domainMeta:md.optional(),knowledgeMeta:gd.optional()}).passthrough(),zo=je({source:H(),target:H(),type:fd,direction:Ee(["forward","backward","bidirectional"]),description:H().optional(),weight:pt().min(0).max(1)}),Ao=je({id:H(),name:H(),description:H(),nodeIds:ie(H())}),Oo=je({order:pt(),title:H(),description:H(),nodeIds:ie(H()),languageLesson:H().optional()}),Mo=je({name:H(),languages:ie(H()),frameworks:ie(H()),description:H(),analyzedAt:H(),gitCommitHash:H()});je({version:H(),kind:Ee(["codebase","knowledge"]).optional(),project:Mo,nodes:ie(Lo),edges:ie(zo),layers:ie(Ao),tour:ie(Oo)});function xd(e){return{level:"fatal",category:"invalid-collection",message:`"${e}" must be an array when present`,path:e}}function Re(e,t){const n=e.map(o=>o.message);return t&&!n.includes(t)&&n.unshift(t),n.length>0?n:void 0}function bd(e){if(typeof e!="object"||e===null)return e;const t=e,n={...t};return Array.isArray(t.nodes)&&(n.nodes=t.nodes.map(o=>typeof o=="object"&&o!==null&&typeof o.type=="string"&&o.type in Ln?{...o,type:Ln[o.type]}:o)),Array.isArray(t.edges)&&(n.edges=t.edges.map(o=>typeof o=="object"&&o!==null&&typeof o.type=="string"&&o.type in zn?{...o,type:zn[o.type]}:o)),n}function Mn(e){var m,p,N,y;if(typeof e!="object"||e===null){const x="Invalid input: not an object";return{success:!1,issues:[],fatal:x,errors:Re([],x)}}const n=pd(e),o=bd(n),{data:r,issues:s}=hd(o),i=["nodes","edges","layers","tour"];for(const x of i)if(x in r&&r[x]!==void 0&&!Array.isArray(r[x])){const k=xd(x);return s.push(k),{success:!1,errors:Re(s,k.message),issues:s,fatal:k.message}}const c=Mo.safeParse(r.project);if(!c.success)return{success:!1,errors:Re(s,"Missing or invalid project metadata"),issues:s,fatal:"Missing or invalid project metadata"};const d=[];if(Array.isArray(r.nodes))for(let x=0;xx.id)),u=[];if(Array.isArray(r.edges))for(let x=0;xl.has(T))}):s.push({level:"dropped",category:"invalid-layer",message:`layers[${x}]: ${((N=k.error.issues[0])==null?void 0:N.message)??"validation failed"} — removed`,path:`layers[${x}]`})}const h=[];if(Array.isArray(r.tour))for(let x=0;xl.has(T))}):s.push({level:"dropped",category:"invalid-tour-step",message:`tour[${x}]: ${((y=k.error.issues[0])==null?void 0:y.message)??"validation failed"} — removed`,path:`tour[${x}]`})}return{success:!0,data:{version:typeof r.version=="string"?r.version:"1.0.0",project:c.data,nodes:d,edges:u,layers:f,tour:h},issues:s,errors:Re(s)}}const Fn=e=>{let t;const n=new Set,o=(l,u)=>{const f=typeof l=="function"?l(t):l;if(!Object.is(f,t)){const h=t;t=u??(typeof f!="object"||f===null)?f:Object.assign({},t,f),n.forEach(g=>g(t,h))}},r=()=>t,c={setState:o,getState:r,getInitialState:()=>d,subscribe:l=>(n.add(l),()=>n.delete(l))},d=t=e(o,r,c);return c},yd=(e=>e?Fn(e):Fn),wd=e=>e;function vd(e,t=wd){const n=Xe.useSyncExternalStore(e.subscribe,Xe.useCallback(()=>t(e.getState()),[e,t]),Xe.useCallback(()=>t(e.getInitialState()),[e,t]));return Xe.useDebugValue(n),n}const kd=e=>{const t=yd(e),n=o=>vd(t,o);return Object.assign(n,t),n},Nd=(e=>kd);function ye(e){return Array.isArray?Array.isArray(e):Po(e)==="[object Array]"}function jd(e){if(typeof e=="string")return e;let t=e+"";return t=="0"&&1/e==-1/0?"-0":t}function _d(e){return e==null?"":jd(e)}function ge(e){return typeof e=="string"}function Fo(e){return typeof e=="number"}function Cd(e){return e===!0||e===!1||Sd(e)&&Po(e)=="[object Boolean]"}function Do(e){return typeof e=="object"}function Sd(e){return Do(e)&&e!==null}function le(e){return e!=null}function Et(e){return!e.trim().length}function Po(e){return e==null?e===void 0?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}const Id="Incorrect 'index' type",Td=e=>`Invalid value for key ${e}`,Ed=e=>`Pattern length exceeds max of ${e}.`,$d=e=>`Missing ${e} property in key`,Ld=e=>`Property 'weight' in key '${e}' must be a positive integer`,Dn=Object.prototype.hasOwnProperty;class zd{constructor(t){this._keys=[],this._keyMap={};let n=0;t.forEach(o=>{let r=Ro(o);this._keys.push(r),this._keyMap[r.id]=r,n+=r.weight}),this._keys.forEach(o=>{o.weight/=n})}get(t){return this._keyMap[t]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function Ro(e){let t=null,n=null,o=null,r=1,s=null;if(ge(e)||ye(e))o=e,t=Pn(e),n=Ot(e);else{if(!Dn.call(e,"name"))throw new Error($d("name"));const i=e.name;if(o=i,Dn.call(e,"weight")&&(r=e.weight,r<=0))throw new Error(Ld(i));t=Pn(i),n=Ot(i),s=e.getFn}return{path:t,id:n,weight:r,src:o,getFn:s}}function Pn(e){return ye(e)?e:e.split(".")}function Ot(e){return ye(e)?e.join("."):e}function Ad(e,t){let n=[],o=!1;const r=(s,i,c)=>{if(le(s))if(!i[c])n.push(s);else{let d=i[c];const l=s[d];if(!le(l))return;if(c===i.length-1&&(ge(l)||Fo(l)||Cd(l)))n.push(_d(l));else if(ye(l)){o=!0;for(let u=0,f=l.length;ue.score===t.score?e.idx{this._keysMap[n.id]=o})}create(){this.isCreated||!this.docs.length||(this.isCreated=!0,ge(this.docs[0])?this.docs.forEach((t,n)=>{this._addString(t,n)}):this.docs.forEach((t,n)=>{this._addObject(t,n)}),this.norm.clear())}add(t){const n=this.size();ge(t)?this._addString(t,n):this._addObject(t,n)}removeAt(t){this.records.splice(t,1);for(let n=t,o=this.size();n{let i=r.getFn?r.getFn(t):this.getFn(t,r.path);if(le(i)){if(ye(i)){let c=[];const d=[{nestedArrIndex:-1,value:i}];for(;d.length;){const{nestedArrIndex:l,value:u}=d.pop();if(le(u))if(ge(u)&&!Et(u)){let f={v:u,i:l,n:this.norm.get(u)};c.push(f)}else ye(u)&&u.forEach((f,h)=>{d.push({nestedArrIndex:h,value:f})})}o.$[s]=c}else if(ge(i)&&!Et(i)){let c={v:i,n:this.norm.get(i)};o.$[s]=c}}}),this.records.push(o)}toJSON(){return{keys:this.keys,records:this.records}}}function Zo(e,t,{getFn:n=F.getFn,fieldNormWeight:o=F.fieldNormWeight}={}){const r=new an({getFn:n,fieldNormWeight:o});return r.setKeys(e.map(Ro)),r.setSources(t),r.create(),r}function Zd(e,{getFn:t=F.getFn,fieldNormWeight:n=F.fieldNormWeight}={}){const{keys:o,records:r}=e,s=new an({getFn:t,fieldNormWeight:n});return s.setKeys(o),s.setIndexRecords(r),s}function nt(e,{errors:t=0,currentLocation:n=0,expectedLocation:o=0,distance:r=F.distance,ignoreLocation:s=F.ignoreLocation}={}){const i=t/e.length;if(s)return i;const c=Math.abs(o-n);return r?i+c/r:c?1:i}function Bd(e=[],t=F.minMatchCharLength){let n=[],o=-1,r=-1,s=0;for(let i=e.length;s=t&&n.push([o,r]),o=-1)}return e[s-1]&&s-o>=t&&n.push([o,s-1]),n}const Se=32;function Vd(e,t,n,{location:o=F.location,distance:r=F.distance,threshold:s=F.threshold,findAllMatches:i=F.findAllMatches,minMatchCharLength:c=F.minMatchCharLength,includeMatches:d=F.includeMatches,ignoreLocation:l=F.ignoreLocation}={}){if(t.length>Se)throw new Error(Ed(Se));const u=t.length,f=e.length,h=Math.max(0,Math.min(o,f));let g=s,m=h;const p=c>1||d,N=p?Array(f):[];let y;for(;(y=e.indexOf(t,m))>-1;){let I=nt(t,{currentLocation:y,expectedLocation:h,distance:r,ignoreLocation:l});if(g=Math.min(I,g),m=y+u,p){let E=0;for(;E=M;P-=1){let V=P-1,R=n[e.charAt(V)];if(p&&(N[V]=+!!R),D[P]=(D[P+1]<<1|1)&R,I&&(D[P]|=(x[P+1]|x[P])<<1|1|x[P+1]),D[P]&j&&(k=nt(t,{errors:I,currentLocation:V,expectedLocation:h,distance:r,ignoreLocation:l}),k<=g)){if(g=k,m=V,m<=h)break;M=Math.max(1,2*h-m)}}if(nt(t,{errors:I+1,currentLocation:h,expectedLocation:h,distance:r,ignoreLocation:l})>g)break;x=D}const C={isMatch:m>=0,score:Math.max(.001,k)};if(p){const I=Bd(N,c);I.length?d&&(C.indices=I):C.isMatch=!1}return C}function Gd(e){let t={};for(let n=0,o=e.length;ne.normalize("NFD").replace(/[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D3-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C04\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA8FF\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F]/g,"")):(e=>e);class Bo{constructor(t,{location:n=F.location,threshold:o=F.threshold,distance:r=F.distance,includeMatches:s=F.includeMatches,findAllMatches:i=F.findAllMatches,minMatchCharLength:c=F.minMatchCharLength,isCaseSensitive:d=F.isCaseSensitive,ignoreDiacritics:l=F.ignoreDiacritics,ignoreLocation:u=F.ignoreLocation}={}){if(this.options={location:n,threshold:o,distance:r,includeMatches:s,findAllMatches:i,minMatchCharLength:c,isCaseSensitive:d,ignoreDiacritics:l,ignoreLocation:u},t=d?t:t.toLowerCase(),t=l?ht(t):t,this.pattern=t,this.chunks=[],!this.pattern.length)return;const f=(g,m)=>{this.chunks.push({pattern:g,alphabet:Gd(g),startIndex:m})},h=this.pattern.length;if(h>Se){let g=0;const m=h%Se,p=h-m;for(;g{const{isMatch:x,score:k,indices:T}=Vd(t,p,N,{location:s+y,distance:i,threshold:c,findAllMatches:d,minMatchCharLength:l,includeMatches:r,ignoreLocation:u});x&&(g=!0),h+=k,x&&T&&(f=[...f,...T])});let m={isMatch:g,score:g?h/this.chunks.length:1};return g&&r&&(m.indices=f),m}}class _e{constructor(t){this.pattern=t}static isMultiMatch(t){return Rn(t,this.multiRegex)}static isSingleMatch(t){return Rn(t,this.singleRegex)}search(){}}function Rn(e,t){const n=e.match(t);return n?n[1]:null}class Hd extends _e{constructor(t){super(t)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(t){const n=t===this.pattern;return{isMatch:n,score:n?0:1,indices:[0,this.pattern.length-1]}}}class Ud extends _e{constructor(t){super(t)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(t){const o=t.indexOf(this.pattern)===-1;return{isMatch:o,score:o?0:1,indices:[0,t.length-1]}}}class Wd extends _e{constructor(t){super(t)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(t){const n=t.startsWith(this.pattern);return{isMatch:n,score:n?0:1,indices:[0,this.pattern.length-1]}}}class Kd extends _e{constructor(t){super(t)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(t){const n=!t.startsWith(this.pattern);return{isMatch:n,score:n?0:1,indices:[0,t.length-1]}}}class Jd extends _e{constructor(t){super(t)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(t){const n=t.endsWith(this.pattern);return{isMatch:n,score:n?0:1,indices:[t.length-this.pattern.length,t.length-1]}}}class Yd extends _e{constructor(t){super(t)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(t){const n=!t.endsWith(this.pattern);return{isMatch:n,score:n?0:1,indices:[0,t.length-1]}}}class Vo extends _e{constructor(t,{location:n=F.location,threshold:o=F.threshold,distance:r=F.distance,includeMatches:s=F.includeMatches,findAllMatches:i=F.findAllMatches,minMatchCharLength:c=F.minMatchCharLength,isCaseSensitive:d=F.isCaseSensitive,ignoreDiacritics:l=F.ignoreDiacritics,ignoreLocation:u=F.ignoreLocation}={}){super(t),this._bitapSearch=new Bo(t,{location:n,threshold:o,distance:r,includeMatches:s,findAllMatches:i,minMatchCharLength:c,isCaseSensitive:d,ignoreDiacritics:l,ignoreLocation:u})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(t){return this._bitapSearch.searchIn(t)}}class Go extends _e{constructor(t){super(t)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(t){let n=0,o;const r=[],s=this.pattern.length;for(;(o=t.indexOf(this.pattern,n))>-1;)n=o+s,r.push([o,n-1]);const i=!!r.length;return{isMatch:i,score:i?0:1,indices:r}}}const Mt=[Hd,Go,Wd,Kd,Yd,Jd,Ud,Vo],Zn=Mt.length,Xd=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,qd="|";function Qd(e,t={}){return e.split(qd).map(n=>{let o=n.trim().split(Xd).filter(s=>s&&!!s.trim()),r=[];for(let s=0,i=o.length;s!!(e[mt.AND]||e[mt.OR]),ol=e=>!!e[Pt.PATH],rl=e=>!ye(e)&&Do(e)&&!Rt(e),Bn=e=>({[mt.AND]:Object.keys(e).map(t=>({[t]:e[t]}))});function Ho(e,t,{auto:n=!0}={}){const o=r=>{let s=Object.keys(r);const i=ol(r);if(!i&&s.length>1&&!Rt(r))return o(Bn(r));if(rl(r)){const d=i?r[Pt.PATH]:s[0],l=i?r[Pt.PATTERN]:r[d];if(!ge(l))throw new Error(Td(d));const u={keyId:Ot(d),pattern:l};return n&&(u.searcher=Dt(l,t)),u}let c={children:[],operator:s[0]};return s.forEach(d=>{const l=r[d];ye(l)&&l.forEach(u=>{c.children.push(o(u))})}),c};return Rt(e)||(e=Bn(e)),o(e)}function sl(e,{ignoreFieldNorm:t=F.ignoreFieldNorm}){e.forEach(n=>{let o=1;n.matches.forEach(({key:r,norm:s,score:i})=>{const c=r?r.weight:null;o*=Math.pow(i===0&&c?Number.EPSILON:i,(c||1)*(t?1:s))}),n.score=o})}function al(e,t){const n=e.matches;t.matches=[],le(n)&&n.forEach(o=>{if(!le(o.indices)||!o.indices.length)return;const{indices:r,value:s}=o;let i={indices:r,value:s};o.key&&(i.key=o.key.src),o.idx>-1&&(i.refIndex=o.idx),t.matches.push(i)})}function il(e,t){t.score=e.score}function cl(e,t,{includeMatches:n=F.includeMatches,includeScore:o=F.includeScore}={}){const r=[];return n&&r.push(al),o&&r.push(il),e.map(s=>{const{idx:i}=s,c={item:t[i],refIndex:i};return r.length&&r.forEach(d=>{d(s,c)}),c})}class $e{constructor(t,n={},o){this.options={...F,...n},this.options.useExtendedSearch,this._keyStore=new zd(this.options.keys),this.setCollection(t,o)}setCollection(t,n){if(this._docs=t,n&&!(n instanceof an))throw new Error(Id);this._myIndex=n||Zo(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(t){le(t)&&(this._docs.push(t),this._myIndex.add(t))}remove(t=()=>!1){const n=[];for(let o=0,r=this._docs.length;o-1&&(d=d.slice(0,n)),cl(d,this._docs,{includeMatches:o,includeScore:r})}_searchStringList(t){const n=Dt(t,this.options),{records:o}=this._myIndex,r=[];return o.forEach(({v:s,i,n:c})=>{if(!le(s))return;const{isMatch:d,score:l,indices:u}=n.searchIn(s);d&&r.push({item:s,idx:i,matches:[{score:l,value:s,norm:c,indices:u}]})}),r}_searchLogical(t){const n=Ho(t,this.options),o=(c,d,l)=>{if(!c.children){const{keyId:f,searcher:h}=c,g=this._findMatches({key:this._keyStore.get(f),value:this._myIndex.getValueForItemAtKeyId(d,f),searcher:h});return g&&g.length?[{idx:l,item:d,matches:g}]:[]}const u=[];for(let f=0,h=c.children.length;f{if(le(c)){let l=o(n,c,d);l.length&&(s[d]||(s[d]={idx:d,item:c,matches:[]},i.push(s[d])),l.forEach(({matches:u})=>{s[d].matches.push(...u)}))}}),i}_searchObjectList(t){const n=Dt(t,this.options),{keys:o,records:r}=this._myIndex,s=[];return r.forEach(({$:i,i:c})=>{if(!le(i))return;let d=[];o.forEach((l,u)=>{d.push(...this._findMatches({key:l,value:i[u],searcher:n}))}),d.length&&s.push({idx:c,item:i,matches:d})}),s}_findMatches({key:t,value:n,searcher:o}){if(!le(n))return[];let r=[];if(ye(n))n.forEach(({v:s,i,n:c})=>{if(!le(s))return;const{isMatch:d,score:l,indices:u}=o.searchIn(s);d&&r.push({score:l,key:t,value:s,idx:i,norm:c,indices:u})});else{const{v:s,n:i}=n,{isMatch:c,score:d,indices:l}=o.searchIn(s);c&&r.push({score:d,key:t,value:s,norm:i,indices:l})}return r}}$e.version="7.1.0";$e.createIndex=Zo;$e.parseIndex=Zd;$e.config=F;$e.parseQuery=Ho;nl(tl);const Vn={keys:[{name:"name",weight:.4},{name:"tags",weight:.3},{name:"summary",weight:.2},{name:"languageNotes",weight:.1}],threshold:.4,includeScore:!0,ignoreLocation:!0,useExtendedSearch:!0};class dl{constructor(t){St(this,"fuse");St(this,"nodes");this.nodes=t,this.fuse=new $e(t,Vn)}search(t,n){const o=t.trim();if(!o)return[];const r=(n==null?void 0:n.limit)??50,s=o.split(/\s+/).join(" | ");let c=this.fuse.search(s);if(n!=null&&n.types&&n.types.length>0){const d=new Set(n.types);c=c.filter(l=>d.has(l.item.type))}return c.slice(0,r).map(d=>({nodeId:d.item.id,score:d.score??0}))}updateNodes(t){this.nodes=t,this.fuse=new $e(t,Vn)}}const gt=["file","function","class","module","concept","config","document","service","table","endpoint","pipeline","schema","resource","domain","flow","step","article","entity","topic","claim","source"],xt=["simple","moderate","complex"],bt=["structural","behavioral","data-flow","dependencies","semantic","infrastructure","domain","knowledge"],ll={structural:["imports","exports","contains","inherits","implements"],behavioral:["calls","subscribes","publishes","middleware"],"data-flow":["reads_from","writes_to","transforms","validates"],dependencies:["depends_on","tested_by","configures"],semantic:["related","similar_to"],infrastructure:["deploys","serves","provisions","triggers","migrates","documents","routes","defines_schema"],domain:["contains_flow","flow_step","cross_domain"],knowledge:["cites","contradicts","builds_on","exemplifies","categorized_under","authored_by"]},Ze={nodeTypes:new Set(gt),complexities:new Set(xt),layerIds:new Set,edgeCategories:new Set(bt)};function ul(e){const t=new Map;for(const r of e.nodes)t.set(r.id,r);const n=new Map,o=new Map;for(const r of e.layers)for(const s of r.nodeIds){n.has(s)||n.set(s,r.id);let i=o.get(s);i||(i=new Set,o.set(s,i)),i.add(r.id)}return{nodesById:t,nodeIdToLayerId:n,nodeIdToLayerIds:o}}function ot(e){return[...e.tour??[]].sort((n,o)=>n.order-o.order)}function rt(e,t){if(t.length===0)return{};const n=e.get(t[0]);return n?{navigationLevel:"layer-detail",activeLayerId:n}:{}}function st(e,t){const n=e.activeLayerId;return!n||n===t?{}:{containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null}}const b=Nd()((e,t)=>({graph:null,nodesById:new Map,nodeIdToLayerId:new Map,nodeIdToLayerIds:new Map,selectedNodeId:null,searchQuery:"",searchResults:[],searchEngine:null,searchMode:"fuzzy",navigationLevel:"overview",activeLayerId:null,codeViewerOpen:!1,codeViewerNodeId:null,codeViewerExpanded:!1,tourActive:!1,currentTourStep:0,tourHighlightedNodeIds:[],persona:"junior",diffMode:!1,changedNodeIds:new Set,affectedNodeIds:new Set,focusNodeId:null,nodeHistory:[],filters:{...Ze,nodeTypes:new Set(Ze.nodeTypes),complexities:new Set(Ze.complexities),layerIds:new Set(Ze.layerIds),edgeCategories:new Set(Ze.edgeCategories)},filterPanelOpen:!1,exportMenuOpen:!1,pathFinderOpen:!1,reactFlowInstance:null,nodeTypeFilters:{code:!0,config:!0,docs:!0,infra:!0,data:!0,domain:!0,knowledge:!0},toggleNodeTypeFilter:n=>e(o=>({nodeTypeFilters:{...o.nodeTypeFilters,[n]:!o.nodeTypeFilters[n]},containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null})),detailLevel:"file",setDetailLevel:n=>e({detailLevel:n,showFunctionsInClassView:!1,containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null}),showFunctionsInClassView:!1,toggleShowFunctionsInClassView:()=>e(n=>({showFunctionsInClassView:!n.showFunctionsInClassView,containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null})),setGraph:n=>{const o=new dl(n.nodes),r=t().searchQuery,s=r.trim()?o.search(r):[],{viewMode:i,domainGraph:c,activeDomainId:d}=t(),l=i==="domain"&&c!==null,{nodesById:u,nodeIdToLayerId:f,nodeIdToLayerIds:h}=ul(n);e({graph:n,nodesById:u,nodeIdToLayerId:f,nodeIdToLayerIds:h,searchEngine:o,searchResults:s,navigationLevel:"overview",activeLayerId:null,selectedNodeId:null,focusNodeId:null,nodeHistory:[],viewMode:l?"domain":"structural",activeDomainId:l?d:null,containerLayoutCache:new Map,expandedContainers:new Set,pendingFocusContainer:null,containerSizeMemory:new Map,stage1Tick:0,layoutIssues:[]})},selectNode:n=>{const{selectedNodeId:o,nodeHistory:r}=t();e(n&&o&&n!==o?{selectedNodeId:n,nodeHistory:[...r,o].slice(-50)}:{selectedNodeId:n})},navigateToNode:n=>{t().navigateToNodeInLayer(n)},navigateToNodeInLayer:n=>{const{graph:o,selectedNodeId:r,nodeHistory:s,nodeIdToLayerId:i}=t();if(!o)return;const c=i.get(n)??null,d=r&&n!==r?[...s,r].slice(-50):s;e(c?{navigationLevel:"layer-detail",activeLayerId:c,selectedNodeId:n,focusNodeId:null,codeViewerOpen:!1,codeViewerNodeId:null,codeViewerExpanded:!1,nodeHistory:d}:{selectedNodeId:n,nodeHistory:d})},navigateToHistoryIndex:n=>{const{nodeHistory:o,graph:r,nodeIdToLayerId:s}=t();if(!r||n<0||n>=o.length)return;const i=o[n],c=o.slice(0,n),d=s.get(i)??null;e({selectedNodeId:i,nodeHistory:c,...d?{navigationLevel:"layer-detail",activeLayerId:d}:{}})},goBackNode:()=>{const{nodeHistory:n,graph:o,nodeIdToLayerId:r}=t();if(n.length===0||!o)return;const s=n[n.length-1],i=n.slice(0,-1),c=r.get(s)??null;e(c?{navigationLevel:"layer-detail",activeLayerId:c,selectedNodeId:s,nodeHistory:i}:{selectedNodeId:s,nodeHistory:i})},drillIntoLayer:n=>e({navigationLevel:"layer-detail",activeLayerId:n,selectedNodeId:null,focusNodeId:null,codeViewerOpen:!1,codeViewerNodeId:null,codeViewerExpanded:!1,containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null}),navigateToOverview:()=>e({navigationLevel:"overview",activeLayerId:null,selectedNodeId:null,focusNodeId:null,codeViewerOpen:!1,codeViewerNodeId:null,codeViewerExpanded:!1,containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null}),setFocusNode:n=>e({focusNodeId:n,selectedNodeId:n,containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null}),setSearchMode:n=>e({searchMode:n}),setSearchQuery:n=>{const o=t().searchEngine;if(t().searchMode,!o||!n.trim()){e({searchQuery:n,searchResults:[]});return}const r=o.search(n);e({searchQuery:n,searchResults:r})},setPersona:n=>e({persona:n,containerLayoutCache:new Map,containerSizeMemory:new Map,expandedContainers:new Set,pendingFocusContainer:null}),openCodeViewer:n=>e({codeViewerOpen:!0,codeViewerNodeId:n,codeViewerExpanded:!1}),closeCodeViewer:()=>e({codeViewerOpen:!1,codeViewerNodeId:null,codeViewerExpanded:!1}),expandCodeViewer:()=>e({codeViewerExpanded:!0}),collapseCodeViewer:()=>e({codeViewerExpanded:!1}),setDiffOverlay:(n,o)=>e({diffMode:!0,changedNodeIds:new Set(n),affectedNodeIds:new Set(o)}),toggleDiffMode:()=>e(n=>({diffMode:!n.diffMode})),clearDiffOverlay:()=>e({diffMode:!1,changedNodeIds:new Set,affectedNodeIds:new Set}),toggleFilterPanel:()=>e(n=>({filterPanelOpen:!n.filterPanelOpen,exportMenuOpen:!1})),toggleExportMenu:()=>e(n=>({exportMenuOpen:!n.exportMenuOpen,filterPanelOpen:!1})),togglePathFinder:()=>e(n=>({pathFinderOpen:!n.pathFinderOpen})),setReactFlowInstance:n=>e({reactFlowInstance:n}),setFilters:n=>e(o=>({filters:{...o.filters,...n}})),resetFilters:()=>e({filters:{nodeTypes:new Set(gt),complexities:new Set(xt),layerIds:new Set,edgeCategories:new Set(bt)}}),hasActiveFilters:()=>{const{filters:n}=t();return n.nodeTypes.size!==gt.length||n.complexities.size!==xt.length||n.layerIds.size>0||n.edgeCategories.size!==bt.length},startTour:()=>{const{graph:n,nodeIdToLayerId:o,activeLayerId:r}=t();if(!n||!n.tour||n.tour.length===0)return;const s=ot(n),i=rt(o,s[0].nodeIds);e({tourActive:!0,currentTourStep:0,tourHighlightedNodeIds:s[0].nodeIds,selectedNodeId:null,...i,...st(i,r)})},stopTour:()=>e({tourActive:!1,currentTourStep:0,tourHighlightedNodeIds:[]}),setTourStep:n=>{const{graph:o,nodeIdToLayerId:r,activeLayerId:s}=t();if(!o||!o.tour||o.tour.length===0)return;const i=ot(o);if(n<0||n>=i.length)return;const c=rt(r,i[n].nodeIds);e({currentTourStep:n,tourHighlightedNodeIds:i[n].nodeIds,...c,...st(c,s)})},nextTourStep:()=>{const{graph:n,currentTourStep:o,nodeIdToLayerId:r,activeLayerId:s}=t();if(!n||!n.tour||n.tour.length===0)return;const i=ot(n);if(o{const{graph:n,currentTourStep:o,nodeIdToLayerId:r,activeLayerId:s}=t();if(!(!n||!n.tour||n.tour.length===0)&&o>0){const i=ot(n),c=o-1,d=rt(r,i[c].nodeIds);e({currentTourStep:c,tourHighlightedNodeIds:i[c].nodeIds,...d,...st(d,s)})}},viewMode:"structural",isKnowledgeGraph:!1,domainGraph:null,activeDomainId:null,setDomainGraph:n=>{e({domainGraph:n})},setIsKnowledgeGraph:n=>{e({isKnowledgeGraph:n})},setViewMode:n=>{e({viewMode:n,selectedNodeId:null,focusNodeId:null,codeViewerOpen:!1,codeViewerNodeId:null,codeViewerExpanded:!1})},navigateToDomain:n=>{const{selectedNodeId:o,nodeHistory:r}=t(),s=o?[...r,o].slice(-50):r;e({viewMode:"domain",activeDomainId:n,focusNodeId:null,nodeHistory:s})},clearActiveDomain:()=>{e({activeDomainId:null,selectedNodeId:null,focusNodeId:null})},expandedContainers:new Set,pendingFocusContainer:null,setPendingFocusContainer:n=>e({pendingFocusContainer:n}),tourFitPending:!1,setTourFitPending:n=>e({tourFitPending:n}),toggleContainer:n=>e(o=>{const r=new Set(o.expandedContainers),s=!r.has(n);return s?r.add(n):r.delete(n),{expandedContainers:r,pendingFocusContainer:s?n:o.pendingFocusContainer}}),expandContainer:n=>e(o=>{if(o.expandedContainers.has(n))return{};const r=new Set(o.expandedContainers);return r.add(n),{expandedContainers:r}}),collapseContainer:n=>e(o=>{if(!o.expandedContainers.has(n))return{};const r=new Set(o.expandedContainers);return r.delete(n),{expandedContainers:r}}),collapseAllContainers:()=>e({expandedContainers:new Set}),containerLayoutCache:new Map,setContainerLayout:(n,o,r)=>e(s=>{const i=new Map(s.containerLayoutCache);i.set(n,{childPositions:o,actualSize:r});const c=new Map(s.containerSizeMemory);return c.set(n,r),{containerLayoutCache:i,containerSizeMemory:c}}),clearContainerLayouts:()=>e({containerLayoutCache:new Map,expandedContainers:new Set,pendingFocusContainer:null}),containerSizeMemory:new Map,stage1Tick:0,bumpStage1Tick:()=>e(n=>({stage1Tick:n.stage1Tick+1})),layoutIssues:[],appendLayoutIssues:n=>e(o=>{if(n.length===0)return{};const r=new Set(o.layoutIssues.map(i=>`${i.level}|${i.message}`)),s=n.filter(i=>!r.has(`${i.level}|${i.message}`));return s.length===0?{}:{layoutIssues:[...o.layoutIssues,...s]}}),clearLayoutIssues:()=>e({layoutIssues:[]})})),fl={common:{loading:"Loading project...",noGraphLoaded:"No graph loaded",selectNode:"Select a node to see details",back:"Back",focus:"Focus",unfocus:"Unfocus",openCode:"Open code",file:"File",tags:"Tags",connections:"Connections",filter:"Filter",resetAll:"Reset All",analyzed:"Analyzed",startGuidedTour:"Start Guided Tour",truncated:"(truncated)",preview:"Preview",doubleClickToOpen:"double-click to open",appName:"Understand Anything",pressKeyboard:"Press ? for keyboard shortcuts",path:"Path",theme:"Theme"},projectOverview:{nodes:"Nodes",edges:"Edges",layers:"Layers",types:"Types",fileTypes:"File Types",code:"Code",config:"Config",docs:"Docs",infra:"Infra",data:"Data",domain:"Domain",knowledge:"Knowledge",languages:"Languages",frameworks:"Frameworks",nodeTypeDistribution:"Node Type Distribution",complexityDistribution:"Complexity Distribution",simple:"Simple",moderate:"Moderate",complex:"Complex",mostConnectedNodes:"Most Connected Nodes",avgConnectionsPerNode:"Avg Connections per Node"},nodeInfo:{definedInThisFile:"Defined in this file",languageConcepts:"Language Concepts",category:"Category",wikilinks:"Wikilinks",backlinks:"Backlinks",entities:"Entities",businessRules:"Business Rules",crossDomain:"Cross-Domain",flows:"Flows",entryPoint:"Entry Point",steps:"Steps",implementation:"Implementation"},fileExplorer:{analyzedFiles:"Analyzed Files",filesFromGraph:"files from the current knowledge graph",noFilePathsFound:"No file paths found."},filterPanel:{nodeTypes:"Node Types",complexity:"Complexity",layers:"Layers",edgeCategories:"Edge Categories"},personaSelector:{overview:"Overview",overviewDesc:"High-level architecture view",learn:"Learn",learnDesc:"Full dashboard with guided learning",deepDive:"Deep Dive",deepDiveDesc:"Code-focused with chat"},sidebar:{info:"Info",files:"Files"},mobile:{graph:"Graph",info:"Info",files:"Files"},drawer:{controls:"Controls",dashboard:"Dashboard",role:"Role",view:"View",diffOverlay:"Diff overlay",nodeTypes:"Node types",layers:"Layers",tools:"Tools",path:"Path",help:"Help",structural:"Structural",domain:"Domain"},domainView:{backToDomains:"Back to domains"},detailLevel:{filesTitle:"Files only — architecture-level dependencies (fast)",classesTitle:"Files + Classes — code structure with inheritance",files:"Files",classes:"+Classes",fnTitle:"Toggle function nodes (may slow down rendering)",fn:"fn"},nodeTypeLabels:{all:"All",code:"Code",config:"Config",docs:"Docs",infra:"Infra",data:"Data",domain:"Domain",knowledge:"Knowledge"},tokenGate:{validating:"Validating...",continue:"Continue"},diffToggle:{hideOverlay:"Hide diff overlay",showOverlay:"Show diff overlay",noData:"No diff data loaded",changed:"Changed",affected:"Affected"},learnPanel:{finish:"Finish",next:"Next",prev:"Prev",noTour:"No tour available",noTourHint:"Generate a tour from your knowledge graph to get a guided walkthrough",projectTour:"Project Tour",steps:"steps",stepsTitle:"Steps",guidedWalkthrough:"Guided walkthrough of the codebase",startTour:"Start Tour",tour:"Tour",exitTour:"Exit Tour"},layer:{defaultName:"Layer",label:"layers"},breadcrumb:{projectOverview:"Project Overview",project:"Project",escBack:"Esc to go back"},warningBanner:{dropped:"Dropped",fatal:"Fatal"},themePicker:{changeTheme:"Change theme",theme:"Theme",accentColor:"Accent Color",headingFont:"Heading Font",serif:"Serif",sans:"Sans",mono:"Mono"},codeViewer:{fullFile:"Full file",lines:"Lines",linesLabel:"lines",noFile:"No file selected",loading:"Loading source...",openLarger:"Open larger code viewer",closeExpanded:"Close expanded code viewer",closeViewer:"Close code viewer",sourceUnavailable:"Source unavailable"},customNode:{tested:"Tested",hasTests:"Has tests"},ariaLabels:{openMenu:"Open menu",closeMenu:"Close menu",settings:"Settings",hideSearch:"Hide search",showSearch:"Show search"},nodeTypeFilter:{hide:"Hide",show:"Show",nodesLabel:"nodes"},keyboardShortcuts:{showHelp:"Show keyboard shortcuts",general:"General",navigation:"Navigation",tour:"Tour",view:"View",focusSearch:"Focus search bar",nextStep:"Next tour step",prevStep:"Previous tour step",toggleDiff:"Toggle diff mode",toggleFilter:"Toggle filter panel",toggleExport:"Toggle export menu",openPathFinder:"Open path finder",title:"Keyboard Shortcuts",toggleHint:"Press ? anytime to toggle this help",closeHint:"Press ESC to close",escapeDesc:"Close panels and modals / go back to overview"},search:{placeholder:"Search nodes by name, summary, or tags...",fuzzy:"Fuzzy",semantic:"Semantic",result:"result"},export:{label:"Export",title:"Export graph (E)",asPNG:"Export as PNG",asSVG:"Export as SVG",asJSON:"Export as JSON"},edgeLabels:{imports:{forward:"imports",backward:"imported by"},exports:{forward:"exports to",backward:"exported by"},contains:{forward:"contains",backward:"contained in"},inherits:{forward:"inherits from",backward:"inherited by"},implements:{forward:"implements",backward:"implemented by"},calls:{forward:"calls",backward:"called by"},subscribes:{forward:"subscribes to",backward:"subscribed by"},publishes:{forward:"publishes to",backward:"consumed by"},middleware:{forward:"middleware for",backward:"uses middleware"},reads_from:{forward:"reads from",backward:"read by"},writes_to:{forward:"writes to",backward:"written by"},transforms:{forward:"transforms",backward:"transformed by"},validates:{forward:"validates",backward:"validated by"},depends_on:{forward:"depends on",backward:"depended on by"},tested_by:{forward:"tested by",backward:"tests"},configures:{forward:"configures",backward:"configured by"},related:{forward:"related to",backward:"related to"},similar_to:{forward:"similar to",backward:"similar to"},deploys:{forward:"deploys",backward:"deployed by"},serves:{forward:"serves",backward:"served by"},migrates:{forward:"migrates",backward:"migrated by"},documents:{forward:"documents",backward:"documented by"},provisions:{forward:"provisions",backward:"provisioned by"},routes:{forward:"routes to",backward:"routed from"},defines_schema:{forward:"defines schema for",backward:"schema defined by"},triggers:{forward:"triggers",backward:"triggered by"},contains_flow:{forward:"contains flow",backward:"flow in"},flow_step:{forward:"flow step",backward:"step of"},cross_domain:{forward:"cross-domain to",backward:"cross-domain from"},cites:{forward:"cites",backward:"cited by"},contradicts:{forward:"contradicts",backward:"contradicted by"},builds_on:{forward:"builds on",backward:"built upon by"},exemplifies:{forward:"exemplifies",backward:"exemplified by"},categorized_under:{forward:"categorized under",backward:"categorizes"},authored_by:{forward:"authored by",backward:"authored"}},pathFinder:{title:"Find path between nodes (P)"},onboarding:{header:"UNDERSTAND-ANYTHING · GET STARTED",skipForever:"Don't show again",prev:"Previous",next:"Next",finish:"Start exploring",steps:[{title:"Welcome to the knowledge graph",body:"The dots and lines you see are entities and relations Understand-Anything extracted from this project. A node can be a file, class, or function from the code — or a concept, entity, or claim from a knowledge wiki.",hint:"Five steps to cover the core operations"},{title:"Three views at the top",body:"Overview shows the big picture (force-directed). Learn follows a preset learning path. Deep Dive shows type and complexity stats. Each view answers a different question.",hint:"Decide what you're asking before you switch"},{title:"Search + click a node",body:"The top search box fuzzy-matches node name / summary / tags. Click any node and the right panel opens with summary, neighbors, and Open Article.",hint:"Search centers and highlights; clicking a node highlights its edges"},{title:"Layer switch + Project Tour",body:"The layer tabs next to All filter the graph to one category, sourced from index.md. Project Tour on the right walks you through the editor's preset sequence.",hint:"Use Layer when nodes are too dense; start Tour when you have no entry point"},{title:"More hidden features",body:"The top bar also has Filter (by type / complexity), Export (export the graph), Path (find a path between two nodes), and Theme. Press Shift + ? for the full keyboard shortcuts.",hint:"Expand them when you need them — no need to memorize all at once"}]}},pl={common:{loading:"加载项目...",noGraphLoaded:"未加载知识图谱",selectNode:"选择节点查看详情",back:"返回",focus:"聚焦",unfocus:"取消聚焦",openCode:"打开代码",file:"文件",tags:"标签",connections:"连接",filter:"筛选",resetAll:"重置全部",analyzed:"分析时间",startGuidedTour:"开始导览",truncated:"(已截断)",preview:"预览",doubleClickToOpen:"双击打开",appName:"Understand Anything",pressKeyboard:"按 ? 查看键盘快捷键",path:"路径",theme:"主题"},projectOverview:{nodes:"节点",edges:"边",layers:"层级",types:"类型",fileTypes:"文件类型",code:"代码",config:"配置",docs:"文档",infra:"基础设施",data:"数据",domain:"领域",knowledge:"知识",languages:"编程语言",frameworks:"框架",nodeTypeDistribution:"节点类型分布",complexityDistribution:"复杂度分布",simple:"简单",moderate:"中等",complex:"复杂",mostConnectedNodes:"连接最多的节点",avgConnectionsPerNode:"节点平均连接数"},nodeInfo:{definedInThisFile:"在此文件中定义",languageConcepts:"语言概念",category:"分类",wikilinks:"维基链接",backlinks:"反向链接",entities:"实体",businessRules:"业务规则",crossDomain:"跨领域",flows:"流程",entryPoint:"入口点",steps:"步骤",implementation:"实现"},fileExplorer:{analyzedFiles:"已分析文件",filesFromGraph:"来自当前知识图谱的文件",noFilePathsFound:"未找到文件路径。"},filterPanel:{nodeTypes:"节点类型",complexity:"复杂度",layers:"层级",edgeCategories:"边类别"},personaSelector:{overview:"概览",overviewDesc:"高层次架构视图",learn:"学习",learnDesc:"完整仪表盘与导览学习",deepDive:"深入",deepDiveDesc:"代码聚焦与对话"},sidebar:{info:"信息",files:"文件"},mobile:{graph:"图谱",info:"信息",files:"文件"},drawer:{controls:"控制",dashboard:"仪表盘",role:"角色",view:"视图",diffOverlay:"差异覆盖",nodeTypes:"节点类型",layers:"层级",tools:"工具",path:"路径",help:"帮助",structural:"结构",domain:"领域"},domainView:{backToDomains:"返回领域列表"},detailLevel:{filesTitle:"仅文件 — 架构级依赖(快速)",classesTitle:"文件 + 类 — 代码结构及继承关系",files:"文件",classes:"+类",fnTitle:"切换函数节点(可能降低渲染速度)",fn:"函数"},nodeTypeLabels:{all:"全部",code:"代码",config:"配置",docs:"文档",infra:"基础设施",data:"数据",domain:"领域",knowledge:"知识"},tokenGate:{validating:"验证中...",continue:"继续"},diffToggle:{hideOverlay:"隐藏差异覆盖",showOverlay:"显示差异覆盖",noData:"未加载差异数据",changed:"已修改",affected:"受影响"},learnPanel:{finish:"完成",next:"下一步",prev:"上一步",noTour:"无导览可用",noTourHint:"从知识图谱生成导览以获取代码库的引导式讲解",projectTour:"项目导览",steps:"步",stepsTitle:"步骤",guidedWalkthrough:"代码库引导式讲解",startTour:"开始导览",tour:"导览",exitTour:"退出导览"},layer:{defaultName:"层级",label:"层"},breadcrumb:{projectOverview:"项目概览",project:"项目",escBack:"按 Esc 返回"},warningBanner:{dropped:"已丢弃",fatal:"致命错误"},themePicker:{changeTheme:"更换主题",theme:"主题",accentColor:"强调色",headingFont:"标题字体",serif:"衬线",sans:"无衬线",mono:"等宽"},codeViewer:{fullFile:"完整文件",lines:"行",linesLabel:"行",noFile:"未选择文件",loading:"加载源码中...",openLarger:"打开更大的代码查看器",closeExpanded:"关闭展开的代码查看器",closeViewer:"关闭代码查看器",sourceUnavailable:"源码不可用"},customNode:{tested:"已测试",hasTests:"有测试"},ariaLabels:{openMenu:"打开菜单",closeMenu:"关闭菜单",settings:"设置",hideSearch:"隐藏搜索",showSearch:"显示搜索"},nodeTypeFilter:{hide:"隐藏",show:"显示",nodesLabel:"节点"},keyboardShortcuts:{showHelp:"显示键盘快捷键",general:"通用",navigation:"导航",tour:"导览",view:"视图",focusSearch:"聚焦搜索栏",nextStep:"下一步导览",prevStep:"上一步导览",toggleDiff:"切换差异模式",toggleFilter:"切换筛选面板",toggleExport:"切换导出菜单",openPathFinder:"打开路径查找器",title:"键盘快捷键",toggleHint:"按 ? 随时切换此帮助",closeHint:"按 ESC 关闭",escapeDesc:"关闭面板和弹窗 / 返回概览"},search:{placeholder:"搜索节点名称、摘要或标签...",fuzzy:"模糊",semantic:"语义",result:"结果"},export:{label:"导出",title:"导出图谱 (E)",asPNG:"导出为 PNG",asSVG:"导出为 SVG",asJSON:"导出为 JSON"},edgeLabels:{imports:{forward:"导入",backward:"被导入"},exports:{forward:"导出到",backward:"被导出"},contains:{forward:"包含",backward:"被包含"},inherits:{forward:"继承自",backward:"被继承"},implements:{forward:"实现",backward:"被实现"},calls:{forward:"调用",backward:"被调用"},subscribes:{forward:"订阅",backward:"被订阅"},publishes:{forward:"发布到",backward:"被消费"},middleware:{forward:"中间件",backward:"使用中间件"},reads_from:{forward:"读取",backward:"被读取"},writes_to:{forward:"写入",backward:"被写入"},transforms:{forward:"转换",backward:"被转换"},validates:{forward:"验证",backward:"被验证"},depends_on:{forward:"依赖",backward:"被依赖"},tested_by:{forward:"被测试",backward:"测试"},configures:{forward:"配置",backward:"被配置"},related:{forward:"相关",backward:"相关"},similar_to:{forward:"相似",backward:"相似"},deploys:{forward:"部署",backward:"被部署"},serves:{forward:"服务",backward:"被服务"},migrates:{forward:"迁移",backward:"被迁移"},documents:{forward:"文档化",backward:"被文档化"},provisions:{forward:"提供",backward:"被提供"},routes:{forward:"路由到",backward:"被路由"},defines_schema:{forward:"定义架构",backward:"架构被定义"},triggers:{forward:"触发",backward:"被触发"},contains_flow:{forward:"包含流程",backward:"流程所在"},flow_step:{forward:"流程步骤",backward:"步骤所属"},cross_domain:{forward:"跨领域到",backward:"跨领域来自"},cites:{forward:"引用",backward:"被引用"},contradicts:{forward:"反驳",backward:"被反驳"},builds_on:{forward:"基于",backward:"作为基础"},exemplifies:{forward:"例证",backward:"被例证"},categorized_under:{forward:"归类于",backward:"归类"},authored_by:{forward:"作者",backward:"著作"}},pathFinder:{title:"查找节点间路径 (P)"},onboarding:{header:"UNDERSTAND-ANYTHING · 入门",skipForever:"不再显示",prev:"上一步",next:"下一步",finish:"开始探索",steps:[{title:"欢迎进入知识图",body:"你看到的圆点和连线是 Understand-Anything 把这份项目抽出来的实体和关系。节点可以是代码里的文件、类、函数,也可以是知识 wiki 里的概念、实体或断言。",hint:"5 步以内带你过完核心操作"},{title:"顶部三个视图",body:"Overview 看全貌(力导向图)· Learn 跟随预设学习路径 · Deep Dive 看类型 / 复杂度统计。每个视图回答一种不同的问法。",hint:"切视图前先想清楚自己在问什么"},{title:"搜索 + 点节点",body:"顶部搜索框模糊匹配节点名 / summary / tags。点任意节点 → 右侧详情面板出现 summary + 邻居列表 + Open Article 按钮。",hint:"搜索高亮居中,点节点高亮邻居边"},{title:"Layer 切换 + Tour",body:"顶部 All 旁边的 layer 标签按 index.md 分类只显示部分节点。右侧 Project Tour 自动按编辑者预设顺序导览。",hint:"节点太密看不清就用 Layer,没头绪就启 Tour"},{title:"更多隐藏功能",body:"顶栏还有 Filter(按类型 / 复杂度过滤)、Export(导出图)、Path(找两个节点之间的路径)、Theme(切换主题)。Shift + ? 看完整快捷键。",hint:"需要时再展开,不要一次记完"}]}},hl={common:{loading:"載入專案...",noGraphLoaded:"未載入知識圖谱",selectNode:"選擇節點查看詳情",back:"返回",focus:"聚焦",unfocus:"取消聚焦",openCode:"開啟程式碼",file:"檔案",tags:"標籤",connections:"連結",filter:"篩選",resetAll:"重置全部",analyzed:"分析時間",startGuidedTour:"開始導覽",truncated:"(已截斷)",preview:"預覽",doubleClickToOpen:"雙擊開啟",appName:"Understand Anything",pressKeyboard:"按 ? 查看鍵盤快捷鍵",path:"路徑",theme:"主題"},projectOverview:{nodes:"節點",edges:"邊",layers:"層級",types:"類型",fileTypes:"檔案類型",code:"程式碼",config:"配置",docs:"文件",infra:"基礎設施",data:"資料",domain:"領域",knowledge:"知識",languages:"程式語言",frameworks:"框架",nodeTypeDistribution:"節點類型分布",complexityDistribution:"複雜度分布",simple:"簡單",moderate:"中等",complex:"複雜",mostConnectedNodes:"連結最多的節點",avgConnectionsPerNode:"節點平均連結數"},nodeInfo:{definedInThisFile:"在此檔案中定義",languageConcepts:"語言概念",category:"分類",wikilinks:"維基連結",backlinks:"反向連結",entities:"實體",businessRules:"業務規則",crossDomain:"跨領域",flows:"流程",entryPoint:"入口點",steps:"步驟",implementation:"實作"},fileExplorer:{analyzedFiles:"已分析檔案",filesFromGraph:"來自目前知識圖谱的檔案",noFilePathsFound:"未找到檔案路徑。"},filterPanel:{nodeTypes:"節點類型",complexity:"複雜度",layers:"層級",edgeCategories:"邊類別"},personaSelector:{overview:"概覽",overviewDesc:"高層次架構視圖",learn:"學習",learnDesc:"完整儀表板與導覽學習",deepDive:"深入",deepDiveDesc:"程式碼聚焦與對話"},sidebar:{info:"資訊",files:"檔案"},mobile:{graph:"圖谱",info:"資訊",files:"檔案"},drawer:{controls:"控制",dashboard:"儀表板",role:"角色",view:"視圖",diffOverlay:"差異覆蓋",nodeTypes:"節點類型",layers:"層級",tools:"工具",path:"路徑",help:"幫助",structural:"結構",domain:"領域"},domainView:{backToDomains:"返回領域列表"},detailLevel:{filesTitle:"僅檔案 — 架構級依賴(快速)",classesTitle:"檔案 + 類別 — 程式碼結構及繼承關係",files:"檔案",classes:"+類別",fnTitle:"切換函數節點(可能降低渲染速度)",fn:"函數"},nodeTypeLabels:{all:"全部",code:"程式碼",config:"配置",docs:"文件",infra:"基礎設施",data:"資料",domain:"領域",knowledge:"知識"},tokenGate:{validating:"驗證中...",continue:"繼續"},diffToggle:{hideOverlay:"隱藏差異覆蓋",showOverlay:"顯示差異覆蓋",noData:"未載入差異資料",changed:"已修改",affected:"受影響"},learnPanel:{finish:"完成",next:"下一步",prev:"上一步",noTour:"無導覽可用",noTourHint:"從知識圖谱生成導覽以獲取程式碼庫的引導式講解",projectTour:"專案導覽",steps:"步",stepsTitle:"步驟",guidedWalkthrough:"程式碼庫引導式講解",startTour:"開始導覽",tour:"導覽",exitTour:"退出導覽"},layer:{defaultName:"層級",label:"層"},breadcrumb:{projectOverview:"專案概覽",project:"專案",escBack:"按 Esc 返回"},warningBanner:{dropped:"已捨棄",fatal:"致命錯誤"},themePicker:{changeTheme:"變更主題",theme:"主題",accentColor:"強調色",headingFont:"標題字型",serif:"襯線",sans:"無襯線",mono:"等寬"},codeViewer:{fullFile:"完整檔案",lines:"行",linesLabel:"行",noFile:"未選擇檔案",loading:"載入原始碼中...",openLarger:"開啟更大的程式碼檢視器",closeExpanded:"關閉展開的程式碼檢視器",closeViewer:"關閉程式碼檢視器",sourceUnavailable:"原始碼不可用"},customNode:{tested:"已測試",hasTests:"有測試"},ariaLabels:{openMenu:"開啟選單",closeMenu:"關閉選單",settings:"設定",hideSearch:"隱藏搜尋",showSearch:"顯示搜尋"},nodeTypeFilter:{hide:"隱藏",show:"顯示",nodesLabel:"節點"},keyboardShortcuts:{showHelp:"顯示鍵盤快捷鍵",general:"一般",navigation:"導航",tour:"導覽",view:"檢視",focusSearch:"聚焦搜尋列",nextStep:"下一步導覽",prevStep:"上一步導覽",toggleDiff:"切換差異模式",toggleFilter:"切換篩選面板",toggleExport:"切換匯出選單",openPathFinder:"開啟路徑尋找器",title:"鍵盤快捷鍵",toggleHint:"按 ? 隨時切換此幫助",closeHint:"按 ESC 關閉",escapeDesc:"關閉面板和彈窗 / 返回概覽"},search:{placeholder:"搜尋節點名稱、摘要或標籤...",fuzzy:"模糊",semantic:"語意",result:"結果"},export:{label:"匯出",title:"匯出圖谱 (E)",asPNG:"匯出為 PNG",asSVG:"匯出為 SVG",asJSON:"匯出為 JSON"},edgeLabels:{imports:{forward:"導入",backward:"被導入"},exports:{forward:"導出到",backward:"被導出"},contains:{forward:"包含",backward:"被包含"},inherits:{forward:"繼承自",backward:"被繼承"},implements:{forward:"實作",backward:"被實作"},calls:{forward:"呼叫",backward:"被呼叫"},subscribes:{forward:"訂閱",backward:"被訂閱"},publishes:{forward:"發布到",backward:"被消費"},middleware:{forward:"中介軟體",backward:"使用中介軟體"},reads_from:{forward:"讀取",backward:"被讀取"},writes_to:{forward:"寫入",backward:"被寫入"},transforms:{forward:"轉換",backward:"被轉換"},validates:{forward:"驗證",backward:"被驗證"},depends_on:{forward:"依賴",backward:"被依賴"},tested_by:{forward:"被測試",backward:"測試"},configures:{forward:"配置",backward:"被配置"},related:{forward:"相關",backward:"相關"},similar_to:{forward:"相似",backward:"相似"},deploys:{forward:"部署",backward:"被部署"},serves:{forward:"服務",backward:"被服務"},migrates:{forward:"遷移",backward:"被遷移"},documents:{forward:"文件化",backward:"被文件化"},provisions:{forward:"提供",backward:"被提供"},routes:{forward:"路由到",backward:"被路由"},defines_schema:{forward:"定義架構",backward:"架構被定義"},triggers:{forward:"觸發",backward:"被觸發"},contains_flow:{forward:"包含流程",backward:"流程所在"},flow_step:{forward:"流程步驟",backward:"步驟所属"},cross_domain:{forward:"跨領域到",backward:"跨領域来自"},cites:{forward:"引用",backward:"被引用"},contradicts:{forward:"反駁",backward:"被反駁"},builds_on:{forward:"基於",backward:"作為基礎"},exemplifies:{forward:"例證",backward:"被例證"},categorized_under:{forward:"归类於",backward:"归类"},authored_by:{forward:"作者",backward:"著作"}},pathFinder:{title:"尋找節點間路徑 (P)"},onboarding:{header:"UNDERSTAND-ANYTHING · 入門",skipForever:"不再顯示",prev:"上一步",next:"下一步",finish:"開始探索",steps:[{title:"歡迎進入知識圖",body:"你看到的圓點和連線是 Understand-Anything 把這份專案抽出來的實體和關係。節點可以是程式碼裡的檔案、類別、函式,也可以是知識 wiki 裡的概念、實體或斷言。",hint:"5 步以內帶你過完核心操作"},{title:"頂部三個視圖",body:"Overview 看全貌(力導向圖)· Learn 跟隨預設學習路徑 · Deep Dive 看類型 / 複雜度統計。每個視圖回答一種不同的問法。",hint:"切視圖前先想清楚自己在問什麼"},{title:"搜尋 + 點節點",body:"頂部搜尋框模糊匹配節點名 / summary / tags。點任意節點 → 右側詳情面板出現 summary + 鄰居列表 + Open Article 按鈕。",hint:"搜尋高亮置中,點節點高亮鄰居邊"},{title:"Layer 切換 + Tour",body:"頂部 All 旁邊的 layer 標籤按 index.md 分類只顯示部分節點。右側 Project Tour 自動按編輯者預設順序導覽。",hint:"節點太密看不清就用 Layer,沒頭緒就啟 Tour"},{title:"更多隱藏功能",body:"頂欄還有 Filter(按類型 / 複雜度過濾)、Export(匯出圖)、Path(找兩個節點之間的路徑)、Theme(切換主題)。Shift + ? 看完整快捷鍵。",hint:"需要時再展開,不要一次記完"}]}},ml={common:{loading:"プロジェクトを読み込み中...",noGraphLoaded:"知識グラフが読み込まれていません",selectNode:"ノードを選択して詳細を表示",back:"戻る",focus:"フォーカス",unfocus:"フォーカス解除",openCode:"コードを開く",file:"ファイル",tags:"タグ",connections:"接続",filter:"フィルター",resetAll:"すべてリセット",analyzed:"分析日時",startGuidedTour:"ガイド付きツアーを開始",truncated:"(省略)",preview:"プレビュー",doubleClickToOpen:"ダブルクリックで開く",appName:"Understand Anything",pressKeyboard:"? を押してキーボードショートカットを表示",path:"パス",theme:"テーマ"},projectOverview:{nodes:"ノード",edges:"エッジ",layers:"レイヤー",types:"タイプ",fileTypes:"ファイルタイプ",code:"コード",config:"設定",docs:"ドキュメント",infra:"インフラ",data:"データ",domain:"ドメイン",knowledge:"ナレッジ",languages:"プログラミング言語",frameworks:"フレームワーク",nodeTypeDistribution:"ノードタイプ分布",complexityDistribution:"複雑度分布",simple:"単純",moderate:"中程度",complex:"複雑",mostConnectedNodes:"最も接続されているノード",avgConnectionsPerNode:"ノード平均接続数"},nodeInfo:{definedInThisFile:"このファイルで定義",languageConcepts:"言語概念",category:"カテゴリ",wikilinks:"Wikilinks",backlinks:"Backlinks",entities:"エンティティ",businessRules:"ビジネスルール",crossDomain:"クロスドメイン",flows:"フロー",entryPoint:"エントリポイント",steps:"ステップ",implementation:"実装"},fileExplorer:{analyzedFiles:"分析済みファイル",filesFromGraph:"現在の知識グラフからのファイル",noFilePathsFound:"ファイルパスが見つかりません。"},filterPanel:{nodeTypes:"ノードタイプ",complexity:"複雑度",layers:"レイヤー",edgeCategories:"エッジカテゴリ"},personaSelector:{overview:"概要",overviewDesc:"高レベルアーキテクチャビュー",learn:"学習",learnDesc:"ガイド付き学習付き完全ダッシュボード",deepDive:"詳細",deepDiveDesc:"コード中心のチャット"},sidebar:{info:"情報",files:"ファイル"},mobile:{graph:"グラフ",info:"情報",files:"ファイル"},drawer:{controls:"コントロール",dashboard:"ダッシュボード",role:"ロール",view:"ビュー",diffOverlay:"差分オーバーレイ",nodeTypes:"ノードタイプ",layers:"レイヤー",tools:"ツール",path:"パス",help:"ヘルプ",structural:"構造",domain:"ドメイン"},domainView:{backToDomains:"ドメインに戻る"},detailLevel:{filesTitle:"ファイルのみ — アーキテクチャレベルの依存関係(高速)",classesTitle:"ファイル + クラス — 継承を含むコード構造",files:"ファイル",classes:"+クラス",fnTitle:"関数ノードを切り替え(レンダリングが遅くなる可能性)",fn:"fn"},nodeTypeLabels:{all:"すべて",code:"コード",config:"設定",docs:"ドキュメント",infra:"インフラ",data:"データ",domain:"ドメイン",knowledge:"ナレッジ"},tokenGate:{validating:"検証中...",continue:"続行"},diffToggle:{hideOverlay:"差分オーバーレイを非表示",showOverlay:"差分オーバーレイを表示",noData:"差分データが読み込まれていません",changed:"変更済み",affected:"影響あり"},learnPanel:{finish:"完了",next:"次へ",prev:"前へ",noTour:"ツアーがありません",noTourHint:"知識グラフからツアーを生成してコードベースのガイド付きウォークスルーを取得",projectTour:"プロジェクトツアー",steps:"ステップ",stepsTitle:"ステップ",guidedWalkthrough:"コードベースのガイド付きウォークスルー",startTour:"ツアー開始",tour:"ツアー",exitTour:"ツアー終了"},layer:{defaultName:"レイヤー",label:"レイヤー"},breadcrumb:{projectOverview:"プロジェクト概要",project:"プロジェクト",escBack:"Escで戻る"},warningBanner:{dropped:"削除済み",fatal:"致命的"},themePicker:{changeTheme:"テーマ変更",theme:"テーマ",accentColor:"アクセント色",headingFont:"見出しフォント",serif:"セリフ",sans:"サン",mono:"モノ"},codeViewer:{fullFile:"ファイル全体",lines:"行",linesLabel:"行",noFile:"ファイル未選択",loading:"ソース読み込み中...",openLarger:"大きなコードビューアを開く",closeExpanded:"展開したコードビューアを閉じる",closeViewer:"コードビューアを閉じる",sourceUnavailable:"ソースが利用できません"},customNode:{tested:"テスト済み",hasTests:"テストあり"},ariaLabels:{openMenu:"メニューを開く",closeMenu:"メニューを閉じる",settings:"設定",hideSearch:"検索を非表示",showSearch:"検索を表示"},nodeTypeFilter:{hide:"非表示",show:"表示",nodesLabel:"ノード"},keyboardShortcuts:{showHelp:"キーボードショートカットを表示",general:"一般",navigation:"ナビゲーション",tour:"ツアー",view:"ビュー",focusSearch:"検索バーにフォーカス",nextStep:"次のツアーステップ",prevStep:"前のツアーステップ",toggleDiff:"差分モード切り替え",toggleFilter:"フィルターパネル切り替え",toggleExport:"エクスポートメニュー切り替え",openPathFinder:"パスファインダーを開く",title:"キーボードショートカット",toggleHint:"いつでも ? を押してこのヘルプを切り替え",closeHint:"ESC を押して閉じる",escapeDesc:"パネルとモーダルを閉じる / 概要に戻る"},search:{placeholder:"ノード名、概要、タグで検索...",fuzzy:"ファジー",semantic:"セマンティック",result:"結果"},export:{label:"エクスポート",title:"グラフをエクスポート (E)",asPNG:"PNGでエクスポート",asSVG:"SVGでエクスポート",asJSON:"JSONでエクスポート"},edgeLabels:{imports:{forward:"インポート",backward:"インポートされる"},exports:{forward:"エクスポート",backward:"エクスポートされる"},contains:{forward:"含む",backward:"含まれる"},inherits:{forward:"継承",backward:"継承される"},implements:{forward:"実装",backward:"実装される"},calls:{forward:"呼び出す",backward:"呼び出される"},subscribes:{forward:"購読",backward:"購読される"},publishes:{forward:"公開",backward:"消費される"},middleware:{forward:"ミドルウェア",backward:"ミドルウェアを使用"},reads_from:{forward:"読み取り",backward:"読み取られる"},writes_to:{forward:"書き込み",backward:"書き込まれる"},transforms:{forward:"変換",backward:"変換される"},validates:{forward:"検証",backward:"検証される"},depends_on:{forward:"依存",backward:"依存される"},tested_by:{forward:"テストされる",backward:"テスト"},configures:{forward:"設定",backward:"設定される"},related:{forward:"関連",backward:"関連"},similar_to:{forward:"類似",backward:"類似"},deploys:{forward:"デプロイ",backward:"デプロイされる"},serves:{forward:"提供",backward:"提供される"},migrates:{forward:"移行",backward:"移行される"},documents:{forward:"ドキュメント化",backward:"ドキュメント化される"},provisions:{forward:"提供",backward:"提供される"},routes:{forward:"ルーティング",backward:"ルーティングされる"},defines_schema:{forward:"スキーマ定義",backward:"スキーマ定義される"},triggers:{forward:"トリガー",backward:"トリガーされる"},contains_flow:{forward:"フローを含む",backward:"フロー内"},flow_step:{forward:"フローステップ",backward:"ステップの"},cross_domain:{forward:"クロスドメイン",backward:"クロスドメインから"},cites:{forward:"引用",backward:"引用される"},contradicts:{forward:"矛盾",backward:"矛盾される"},builds_on:{forward:"基礎",backward:"基礎となる"},exemplifies:{forward:"例示",backward:"例示される"},categorized_under:{forward:"カテゴリ化",backward:"カテゴリ化する"},authored_by:{forward:"作成者",backward:"作成"}},pathFinder:{title:"ノード間のパスを検索 (P)"},onboarding:{header:"UNDERSTAND-ANYTHING · はじめに",skipForever:"次回から表示しない",prev:"前へ",next:"次へ",finish:"探索を始める",steps:[{title:"知識グラフへようこそ",body:"表示されているノードとエッジは、Understand-Anything がこのプロジェクトから抽出したエンティティと関係です。ノードはコード側のファイル・クラス・関数のこともあれば、知識 wiki 側の概念・エンティティ・記述のこともあります。",hint:"5 ステップで主要な操作を確認します"},{title:"上部の 3 つのビュー",body:"Overview は全体像(力学的レイアウト)、Learn はあらかじめ用意された学習パス、Deep Dive はタイプ / 複雑度の統計を表示します。それぞれ異なる問いに答えるためのビューです。",hint:"切り替える前に、何を知りたいかを明確に"},{title:"検索 + ノードクリック",body:"上部の検索ボックスはノード名 / summary / タグをあいまい検索します。任意のノードをクリックすると、右側のパネルに summary、隣接ノード、Open Article ボタンが表示されます。",hint:"検索はノードを中央寄せ・ハイライト、クリックは隣接エッジをハイライトします"},{title:"Layer 切替 + Project Tour",body:"上部 All の隣にある layer タブは index.md に基づいて 1 つのカテゴリだけを表示します。右側の Project Tour は編集者が用意した順序でガイドします。",hint:"ノードが多すぎるときは Layer、入り口がわからないときは Tour"},{title:"その他の隠れた機能",body:"上部バーには Filter(タイプ / 複雑度で絞り込み)、Export(グラフを書き出す)、Path(2 つのノード間のパスを検索)、Theme(テーマ切替)もあります。Shift + ? で全キーボードショートカットを確認できます。",hint:"必要になったときに開けば十分。一度に覚える必要はありません"}]}},gl={common:{loading:"프로젝트 로딩 중...",noGraphLoaded:"지식 그래프가 로드되지 않음",selectNode:"노드를 선택하여 상세 정보 확인",back:"뒤로",focus:"포커스",unfocus:"포커스 해제",openCode:"코드 열기",file:"파일",tags:"태그",connections:"연결",filter:"필터",resetAll:"모두 재설정",analyzed:"분석 시간",startGuidedTour:"가이드 투어 시작",truncated:"(생략)",preview:"미리보기",doubleClickToOpen:"두 번 클릭하여 열기",appName:"Understand Anything",pressKeyboard:"? 키를 눌러 키보드 단축키 보기",path:"경로",theme:"테마"},projectOverview:{nodes:"노드",edges:"엣지",layers:"레이어",types:"타입",fileTypes:"파일 타입",code:"코드",config:"설정",docs:"문서",infra:"인프라",data:"데이터",domain:"도메인",knowledge:"지식",languages:"프로그래밍 언어",frameworks:"프레임워크",nodeTypeDistribution:"노드 타입 분포",complexityDistribution:"복잡도 분포",simple:"단순",moderate:"중간",complex:"복잡",mostConnectedNodes:"가장 많이 연결된 노드",avgConnectionsPerNode:"노드 평균 연결 수"},nodeInfo:{definedInThisFile:"이 파일에 정義",languageConcepts:"언어 개념",category:"카테고리",wikilinks:"Wikilinks",backlinks:"Backlinks",entities:"엔티티",businessRules:"비즈니스 규칙",crossDomain:"크로스 도메인",flows:"플로우",entryPoint:"진입점",steps:"단계",implementation:"구현"},fileExplorer:{analyzedFiles:"분석된 파일",filesFromGraph:"현재 지식 그래프의 파일",noFilePathsFound:"파일 경로를 찾을 수 없습니다."},filterPanel:{nodeTypes:"노드 타입",complexity:"복잡도",layers:"레이어",edgeCategories:"엣지 카테고리"},personaSelector:{overview:"개요",overviewDesc:"고수준 아키텍처 뷰",learn:"학습",learnDesc:"가이드 학습 포함 완전 대시보드",deepDive:"심층",deepDiveDesc:"코드 중심 채팅"},sidebar:{info:"정보",files:"파일"},mobile:{graph:"그래프",info:"정보",files:"파일"},drawer:{controls:"컨트롤",dashboard:"대시보드",role:"역할",view:"보기",diffOverlay:"차분 오버레이",nodeTypes:"노드 타입",layers:"레이어",tools:"도구",path:"경로",help:"도움말",structural:"구조",domain:"도메인"},domainView:{backToDomains:"도메인으로 돌아가기"},detailLevel:{filesTitle:"파일만 — 아키텍처 레벨 의존성 (빠름)",classesTitle:"파일 + 클래스 — 상속 포함 코드 구조",files:"파일",classes:"+클래스",fnTitle:"함수 노드 토글 (렌더링 속도 저하 가능)",fn:"fn"},nodeTypeLabels:{all:"모두",code:"코드",config:"설정",docs:"문서",infra:"인프라",data:"데이터",domain:"도메인",knowledge:"지식"},tokenGate:{validating:"검증 중...",continue:"계속"},diffToggle:{hideOverlay:"차분 오버레이 숨기기",showOverlay:"차분 오버레이 표시",noData:"차분 데이터가 로드되지 않음",changed:"변경됨",affected:"영향받음"},learnPanel:{finish:"완료",next:"다음",prev:"이전",noTour:"투어 없음",noTourHint:"지식 그래프에서 투어를 생성하여 코드베이스의 가이드 워크스루를 얻으세요",projectTour:"프로젝트 투어",steps:"단계",stepsTitle:"단계",guidedWalkthrough:"코드베이스 가이드 워크스루",startTour:"투어 시작",tour:"투어",exitTour:"투어 종료"},layer:{defaultName:"레이어",label:"레이어"},breadcrumb:{projectOverview:"프로젝트 개요",project:"프로젝트",escBack:"Esc로 돌아가기"},warningBanner:{dropped:"삭제됨",fatal:"치명적"},themePicker:{changeTheme:"테마 변경",theme:"테마",accentColor:"강조색",headingFont:"제목 폰트",serif:"세리프",sans:"산스",mono:"모노"},codeViewer:{fullFile:"전체 파일",lines:"행",linesLabel:"행",noFile:"파일 선택 안 됨",loading:"소스 로딩 중...",openLarger:"더 큰 코드 뷰어 열기",closeExpanded:"확장된 코드 뷰어 닫기",closeViewer:"코드 뷰어 닫기",sourceUnavailable:"소스 사용 불가"},customNode:{tested:"테스트됨",hasTests:"테스트 있음"},ariaLabels:{openMenu:"메뉴 열기",closeMenu:"메뉴 닫기",settings:"설정",hideSearch:"검색 숨기기",showSearch:"검색 표시"},nodeTypeFilter:{hide:"숨기기",show:"표시",nodesLabel:"노드"},keyboardShortcuts:{showHelp:"키보드 단축키 표시",general:"일반",navigation:"탐색",tour:"투어",view:"보기",focusSearch:"검색창 포커스",nextStep:"다음 투어 단계",prevStep:"이전 투어 단계",toggleDiff:"차분 모드 전환",toggleFilter:"필터 패널 전환",toggleExport:"내보내기 메뉴 전환",openPathFinder:"경로 찾기 열기",title:"키보드 단축키",toggleHint:"언제든 ?를 눌러 이 도움말을 토글",closeHint:"ESC를 눌러 닫기",escapeDesc:"패널 및 모달 닫기 / 개요로 돌아가기"},search:{placeholder:"노드 이름, 요약, 태그로 검색...",fuzzy:"퍼지",semantic:"시맨틱",result:"결과"},export:{label:"내보내기",title:"그래프 내보내기 (E)",asPNG:"PNG로 내보내기",asSVG:"SVG로 내보내기",asJSON:"JSON으로 내보내기"},edgeLabels:{imports:{forward:"임포트",backward:"임포트됨"},exports:{forward:"내보내기",backward:"내보내기됨"},contains:{forward:"포함",backward:"포함됨"},inherits:{forward:"상속",backward:"상속됨"},implements:{forward:"구현",backward:"구현됨"},calls:{forward:"호출",backward:"호출됨"},subscribes:{forward:"구독",backward:"구독됨"},publishes:{forward:"게시",backward:"소비됨"},middleware:{forward:"미들웨어",backward:"미들웨어 사용"},reads_from:{forward:"읽기",backward:"읽기됨"},writes_to:{forward:"쓰기",backward:"쓰기됨"},transforms:{forward:"변환",backward:"변환됨"},validates:{forward:"검증",backward:"검증됨"},depends_on:{forward:"종속",backward:"종속됨"},tested_by:{forward:"테스트됨",backward:"테스트"},configures:{forward:"설정",backward:"설정됨"},related:{forward:"관련",backward:"관련"},similar_to:{forward:"유사",backward:"유사"},deploys:{forward:"배포",backward:"배포됨"},serves:{forward:"서비스",backward:"서비스됨"},migrates:{forward:"마이그레이션",backward:"마이그레이션됨"},documents:{forward:"문서화",backward:"문서화됨"},provisions:{forward:"제공",backward:"제공됨"},routes:{forward:"라우팅",backward:"라우팅됨"},defines_schema:{forward:"스키마 정의",backward:"스키마 정의됨"},triggers:{forward:"트리거",backward:"트리거됨"},contains_flow:{forward:"플로우 포함",backward:"플로우 내"},flow_step:{forward:"플로우 단계",backward:"단계의"},cross_domain:{forward:"크로스 도메인",backward:"크로스 도메인에서"},cites:{forward:"인용",backward:"인용됨"},contradicts:{forward:"반박",backward:"반박됨"},builds_on:{forward:"기반",backward:"기반됨"},exemplifies:{forward:"예시",backward:"예시됨"},categorized_under:{forward:"카테고리화",backward:"카테고리화함"},authored_by:{forward:"작성자",backward:"작성"}},pathFinder:{title:"노드 간 경로 찾기 (P)"},onboarding:{header:"UNDERSTAND-ANYTHING · 시작하기",skipForever:"다시 보지 않기",prev:"이전",next:"다음",finish:"탐색 시작",steps:[{title:"지식 그래프에 오신 것을 환영합니다",body:"보이는 점과 선은 Understand-Anything이 이 프로젝트에서 추출한 엔티티와 관계입니다. 노드는 코드 쪽의 파일·클래스·함수일 수도 있고, 지식 위키 쪽의 개념·엔티티·진술일 수도 있습니다.",hint:"5단계로 핵심 조작을 살펴봅니다"},{title:"상단의 세 가지 뷰",body:"Overview는 전체 모습(포스 디렉티드), Learn은 미리 정의된 학습 경로, Deep Dive는 타입 / 복잡도 통계를 보여줍니다. 각 뷰는 서로 다른 질문에 답합니다.",hint:"전환하기 전에 무엇을 묻고 싶은지 정하세요"},{title:"검색 + 노드 클릭",body:"상단 검색창은 노드 이름 / summary / 태그를 퍼지 매칭합니다. 노드를 클릭하면 오른쪽 패널에 summary, 이웃 목록, Open Article 버튼이 나타납니다.",hint:"검색은 노드를 중앙 정렬·강조하고, 클릭은 인접 엣지를 강조합니다"},{title:"Layer 전환 + Project Tour",body:"상단 All 옆의 layer 탭은 index.md를 기반으로 한 카테고리만 표시합니다. 오른쪽의 Project Tour는 편집자가 설정한 순서대로 안내합니다.",hint:"노드가 너무 빽빽하면 Layer, 시작점이 없으면 Tour를 사용하세요"},{title:"숨겨진 추가 기능",body:"상단 바에는 Filter(타입 / 복잡도로 필터링), Export(그래프 내보내기), Path(두 노드 사이 경로 찾기), Theme(테마 전환)도 있습니다. Shift + ?를 누르면 전체 키보드 단축키를 볼 수 있습니다.",hint:"필요할 때 펼쳐 보면 됩니다. 한 번에 다 외울 필요는 없습니다"}]}},xl={common:{loading:"Загрузка проекта...",noGraphLoaded:"Граф знаний не загружен",selectNode:"Выберите узел, чтобы увидеть подробности",back:"Назад",focus:"Фокус",unfocus:"Снять фокус",openCode:"Открыть код",file:"Файл",tags:"Теги",connections:"Связи",filter:"Фильтр",resetAll:"Сбросить всё",analyzed:"Проанализировано",startGuidedTour:"Начать обзор",truncated:"(сокращено)",preview:"Предпросмотр",doubleClickToOpen:"двойной клик, чтобы открыть",appName:"Understand Anything",pressKeyboard:"Нажмите ? для горячих клавиш",path:"Путь",theme:"Тема"},projectOverview:{nodes:"Узлы",edges:"Рёбра",layers:"Слои",types:"Типы",fileTypes:"Типы файлов",code:"Код",config:"Конфиг",docs:"Документация",infra:"Инфраструктура",data:"Данные",domain:"Домен",knowledge:"Знания",languages:"Языки",frameworks:"Фреймворки",nodeTypeDistribution:"Распределение типов узлов",complexityDistribution:"Распределение сложности",simple:"Простой",moderate:"Средний",complex:"Сложный",mostConnectedNodes:"Самые связанные узлы",avgConnectionsPerNode:"Среднее число связей на узел"},nodeInfo:{definedInThisFile:"Определено в этом файле",languageConcepts:"Концепции языка",category:"Категория",wikilinks:"Wiki-ссылки",backlinks:"Обратные ссылки",entities:"Сущности",businessRules:"Бизнес-правила",crossDomain:"Междоменные связи",flows:"Потоки",entryPoint:"Точка входа",steps:"Шаги",implementation:"Реализация"},fileExplorer:{analyzedFiles:"Проанализированные файлы",filesFromGraph:"файлы из текущего графа знаний",noFilePathsFound:"Пути файлов не найдены."},filterPanel:{nodeTypes:"Типы узлов",complexity:"Сложность",layers:"Слои",edgeCategories:"Категории рёбер"},personaSelector:{overview:"Обзор",overviewDesc:"Высокоуровневый архитектурный вид",learn:"Обучение",learnDesc:"Полная панель с пошаговым обучением",deepDive:"Погружение",deepDiveDesc:"Фокус на коде с чатом"},sidebar:{info:"Информация",files:"Файлы"},mobile:{graph:"Граф",info:"Информация",files:"Файлы"},drawer:{controls:"Управление",dashboard:"Панель",role:"Роль",view:"Вид",diffOverlay:"Наложение изменений",nodeTypes:"Типы узлов",layers:"Слои",tools:"Инструменты",path:"Путь",help:"Помощь",structural:"Структура",domain:"Домен"},domainView:{backToDomains:"Назад к доменам"},detailLevel:{filesTitle:"Только файлы — зависимости архитектурного уровня (быстро)",classesTitle:"Файлы + классы — структура кода с наследованием",files:"Файлы",classes:"+Классы",fnTitle:"Переключить узлы функций (может замедлить отрисовку)",fn:"fn"},nodeTypeLabels:{all:"Все",code:"Код",config:"Конфиг",docs:"Документация",infra:"Инфраструктура",data:"Данные",domain:"Домен",knowledge:"Знания"},tokenGate:{validating:"Проверка...",continue:"Продолжить"},diffToggle:{hideOverlay:"Скрыть наложение изменений",showOverlay:"Показать наложение изменений",noData:"Данные об изменениях не загружены",changed:"Изменено",affected:"Затронуто"},learnPanel:{finish:"Завершить",next:"Далее",prev:"Назад",noTour:"Обзор недоступен",noTourHint:"Сгенерируйте обзор из графа знаний, чтобы получить пошаговое руководство по кодовой базе",projectTour:"Обзор проекта",steps:"шагов",stepsTitle:"Шаги",guidedWalkthrough:"Пошаговое знакомство с кодовой базой",startTour:"Начать обзор",tour:"Обзор",exitTour:"Завершить обзор"},layer:{defaultName:"Слой",label:"слои"},breadcrumb:{projectOverview:"Обзор проекта",project:"Проект",escBack:"Esc — назад"},warningBanner:{dropped:"Отброшено",fatal:"Критично"},themePicker:{changeTheme:"Сменить тему",theme:"Тема",accentColor:"Акцентный цвет",headingFont:"Шрифт заголовков",serif:"Серифный",sans:"Без засечек",mono:"Моноширинный"},codeViewer:{fullFile:"Весь файл",lines:"Строки",linesLabel:"строк",noFile:"Файл не выбран",loading:"Загрузка исходного кода...",openLarger:"Открыть увеличенный просмотрщик кода",closeExpanded:"Закрыть расширенный просмотрщик кода",closeViewer:"Закрыть просмотрщик кода",sourceUnavailable:"Исходный код недоступен"},customNode:{tested:"Покрыт тестами",hasTests:"Есть тесты"},ariaLabels:{openMenu:"Открыть меню",closeMenu:"Закрыть меню",settings:"Настройки",hideSearch:"Скрыть поиск",showSearch:"Показать поиск"},nodeTypeFilter:{hide:"Скрыть",show:"Показать",nodesLabel:"узлов"},keyboardShortcuts:{showHelp:"Показать горячие клавиши",general:"Общие",navigation:"Навигация",tour:"Обзор",view:"Вид",focusSearch:"Перейти к строке поиска",nextStep:"Следующий шаг обзора",prevStep:"Предыдущий шаг обзора",toggleDiff:"Переключить режим изменений",toggleFilter:"Переключить панель фильтров",toggleExport:"Переключить меню экспорта",openPathFinder:"Открыть поиск пути",title:"Горячие клавиши",toggleHint:"Нажмите ?, чтобы открыть или закрыть эту справку",closeHint:"Нажмите ESC, чтобы закрыть",escapeDesc:"Закрыть панели и модальные окна / вернуться к обзору"},search:{placeholder:"Поиск узлов по имени, описанию или тегам...",fuzzy:"Нечёткий",semantic:"Семантический",result:"результат"},export:{label:"Экспорт",title:"Экспортировать граф (E)",asPNG:"Экспортировать как PNG",asSVG:"Экспортировать как SVG",asJSON:"Экспортировать как JSON"},edgeLabels:{imports:{forward:"импортирует",backward:"импортируется"},exports:{forward:"экспортирует в",backward:"экспортируется"},contains:{forward:"содержит",backward:"содержится в"},inherits:{forward:"наследует от",backward:"наследуется"},implements:{forward:"реализует",backward:"реализуется"},calls:{forward:"вызывает",backward:"вызывается"},subscribes:{forward:"подписывается на",backward:"подписан"},publishes:{forward:"публикует в",backward:"получает события"},middleware:{forward:"middleware для",backward:"использует middleware"},reads_from:{forward:"читает из",backward:"читается"},writes_to:{forward:"пишет в",backward:"записывается"},transforms:{forward:"преобразует",backward:"преобразуется"},validates:{forward:"валидирует",backward:"валидируется"},depends_on:{forward:"зависит от",backward:"является зависимостью"},tested_by:{forward:"тестируется",backward:"тестирует"},configures:{forward:"конфигурирует",backward:"конфигурируется"},related:{forward:"связан с",backward:"связан с"},similar_to:{forward:"похож на",backward:"похож на"},deploys:{forward:"разворачивает",backward:"разворачивается"},serves:{forward:"обслуживает",backward:"обслуживается"},migrates:{forward:"мигрирует",backward:"мигрируется"},documents:{forward:"документирует",backward:"документируется"},provisions:{forward:"обеспечивает",backward:"обеспечивается"},routes:{forward:"маршрутизирует в",backward:"маршрутизируется из"},defines_schema:{forward:"определяет схему для",backward:"схема определена"},triggers:{forward:"запускает",backward:"запускается"},contains_flow:{forward:"содержит поток",backward:"поток в"},flow_step:{forward:"шаг потока",backward:"шаг"},cross_domain:{forward:"междоменно к",backward:"междоменно из"},cites:{forward:"цитирует",backward:"цитируется"},contradicts:{forward:"противоречит",backward:"опровергается"},builds_on:{forward:"основан на",backward:"основа для"},exemplifies:{forward:"иллюстрирует",backward:"иллюстрируется"},categorized_under:{forward:"относится к",backward:"категоризирует"},authored_by:{forward:"автор",backward:"автор"}},pathFinder:{title:"Найти путь между узлами (P)"},onboarding:{header:"UNDERSTAND-ANYTHING · НАЧАЛО РАБОТЫ",skipForever:"Больше не показывать",prev:"Назад",next:"Далее",finish:"Начать исследование",steps:[{title:"Добро пожаловать в граф знаний",body:"Точки и линии — это сущности и связи, извлечённые Understand-Anything из этого проекта. Узлом может быть файл, класс или функция из кода — либо концепция, сущность или утверждение из вики знаний.",hint:"Пять шагов охватят основные операции"},{title:"Три вида сверху",body:"Overview показывает общую картину (force-directed). Learn ведёт по заранее заданному учебному пути. Deep Dive показывает статистику по типам и сложности. Каждый вид отвечает на свой вопрос.",hint:"Перед переключением определитесь, о чём вы спрашиваете"},{title:"Поиск + клик по узлу",body:"Поисковая строка сверху делает нечёткое совпадение по имени узла, summary и тегам. Кликните по узлу — справа откроется панель с summary, соседями и кнопкой Open Article.",hint:"Поиск центрирует и подсвечивает; клик подсвечивает соседние рёбра"},{title:"Переключение Layer + Project Tour",body:"Вкладки layer рядом с All фильтруют граф по одной категории на основе index.md. Project Tour справа проводит вас по заранее заданной последовательности.",hint:"Используйте Layer, когда узлов слишком много; запустите Tour, если непонятно с чего начать"},{title:"Другие скрытые возможности",body:"В верхней панели также есть Filter (фильтр по типу / сложности), Export (экспорт графа), Path (поиск пути между двумя узлами) и Theme (смена темы). Нажмите Shift + ?, чтобы увидеть полный список горячих клавиш.",hint:"Открывайте их по мере необходимости — не нужно запоминать всё сразу"}]}},Gn={en:fl,zh:pl,"zh-TW":hl,ja:ml,ko:gl,ru:xl};function bl(e){return Gn[e]??Gn.en}function yl(e){if(!e)return"en";const t=e.toLowerCase().replace(/[_\s]/g,"-");return t==="zh"||t==="chinese"||t==="zh-cn"?"zh":t==="zh-tw"||t==="traditional-chinese"?"zh-TW":t==="ja"||t==="japanese"?"ja":t==="ko"||t==="korean"?"ko":t==="ru"||t==="russian"||t==="ru-ru"?"ru":"en"}const Uo=v.createContext(null);function re(){const e=v.useContext(Uo);if(!e)throw new Error("useI18n must be used within an I18nProvider");return e}function wl({language:e,children:t}){const n=v.useMemo(()=>yl(e),[e]),o=v.useMemo(()=>bl(n),[n]),r=v.useMemo(()=>({locale:o,localeKey:n,t:o}),[o,n]);return a.jsx(Uo.Provider,{value:r,children:t})}const Hn={file:"var(--color-node-file)",function:"var(--color-node-function)",class:"var(--color-node-class)",module:"var(--color-node-module)",concept:"var(--color-node-concept)",config:"var(--color-node-config)",document:"var(--color-node-document)",service:"var(--color-node-service)",table:"var(--color-node-table)",endpoint:"var(--color-node-endpoint)",pipeline:"var(--color-node-pipeline)",schema:"var(--color-node-schema)",resource:"var(--color-node-resource)",domain:"var(--color-node-concept)",flow:"var(--color-node-pipeline)",step:"var(--color-node-function)",article:"var(--color-node-article)",entity:"var(--color-node-entity)",topic:"var(--color-node-topic)",claim:"var(--color-node-claim)",source:"var(--color-node-source)"},Un={file:"text-node-file",function:"text-node-function",class:"text-node-class",module:"text-node-module",concept:"text-node-concept",config:"text-node-config",document:"text-node-document",service:"text-node-service",table:"text-node-table",endpoint:"text-node-endpoint",pipeline:"text-node-pipeline",schema:"text-node-schema",resource:"text-node-resource",domain:"text-node-concept",flow:"text-node-pipeline",step:"text-node-function",article:"text-node-article",entity:"text-node-entity",topic:"text-node-topic",claim:"text-node-claim",source:"text-node-source"},Wn={simple:"text-node-function",moderate:"text-accent-dim",complex:"text-[#c97070]"};function vl({id:e,data:t}){var u;const n=t.nodeType,o=Hn[n]??Hn.file,r=Un[n]??Un.file,s=Wn[t.complexity]??Wn.simple,{t:i}=re();let c="";if(t.isSelected)c="ring-2 ring-accent node-glow";else if(t.isTourHighlighted)c="ring-2 ring-accent-dim animate-accent-pulse";else if(t.isHighlighted){const f=t.searchScore??1;f<=.1?c="ring-2 ring-accent-bright":f<=.3?c="ring-2 ring-accent":c="ring-1 ring-accent-dim/60"}t.isDiffChanged?c+=" ring-2 ring-[var(--color-diff-changed)] diff-changed-glow":t.isDiffAffected?c+=" ring-1 ring-[var(--color-diff-affected)] diff-affected-glow":t.isDiffFaded&&(c+=" diff-faded"),t.isSelectionFaded?c+=" opacity-20 pointer-events-auto":t.isNeighbor&&(c+=" ring-1 ring-gold-dim/50");const d=t.label??"unnamed",l=d.length>24?d.slice(0,22)+"...":d;return a.jsxs("div",{className:`relative rounded-lg bg-elevated border border-border-subtle ${c} min-w-[180px] max-w-[220px] overflow-hidden transition-[box-shadow,outline,opacity,filter] duration-200 cursor-pointer shadow-[0_2px_8px_rgba(0,0,0,0.3)]`,onClick:()=>{var f;return(f=t.onNodeClick)==null?void 0:f.call(t,e)},children:[a.jsx("div",{className:"absolute left-0 top-0 bottom-0 w-1 rounded-l-lg",style:{backgroundColor:o}}),a.jsx(fe,{type:"target",position:pe.Top,className:"!bg-text-muted !w-2 !h-2"}),a.jsxs("div",{className:"pl-4 pr-3 py-2",children:[a.jsxs("div",{className:"flex items-center justify-between mb-1",children:[a.jsx("span",{className:`text-[10px] font-semibold uppercase tracking-wider ${r}`,children:t.nodeType}),a.jsxs("div",{className:"flex items-center gap-1.5",children:[a.jsx("span",{className:`text-[9px] font-mono ${s}`,children:t.complexity}),((u=t.tags)==null?void 0:u.includes("tested"))&&a.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-node-function shadow-[0_0_4px_rgba(90,158,111,0.6)]",role:"img","aria-label":i.customNode.tested,title:i.customNode.hasTests})]})]}),a.jsx("div",{className:"text-sm font-heading text-text-primary truncate",title:t.label,children:l}),a.jsx("div",{className:"text-[11px] text-text-secondary mt-1 line-clamp-2 leading-tight",children:t.summary})]}),a.jsx(fe,{type:"source",position:pe.Bottom,className:"!bg-text-muted !w-2 !h-2"})]})}const Wo=v.memo(vl),Kn=[{bg:"rgba(74, 124, 155, 0.12)",border:"rgba(74, 124, 155, 0.4)",label:"#4a7c9b"},{bg:"rgba(90, 158, 111, 0.12)",border:"rgba(90, 158, 111, 0.4)",label:"#5a9e6f"},{bg:"rgba(139, 111, 176, 0.12)",border:"rgba(139, 111, 176, 0.4)",label:"#8b6fb0"},{bg:"rgba(201, 160, 108, 0.12)",border:"rgba(201, 160, 108, 0.4)",label:"#c9a06c"},{bg:"rgba(176, 122, 138, 0.12)",border:"rgba(176, 122, 138, 0.4)",label:"#b07a8a"},{bg:"rgba(74, 155, 140, 0.12)",border:"rgba(74, 155, 140, 0.4)",label:"#4a9b8c"},{bg:"rgba(120, 130, 145, 0.12)",border:"rgba(120, 130, 145, 0.4)",label:"#788291"}];function jt(e){return Kn[e%Kn.length]}function Ko(){const e=b(c=>c.graph),t=b(c=>c.navigationLevel),n=b(c=>c.activeLayerId),{t:o}=re(),r=(e==null?void 0:e.layers)??[];if(!(r.length>0))return null;const i=r.find(c=>c.id===n);return a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:"text-[11px] font-medium text-text-secondary whitespace-nowrap",children:t==="overview"?`${r.length} ${o.layer.label}`:(i==null?void 0:i.name)??o.layer.defaultName}),a.jsx("div",{className:"flex items-center gap-3",children:r.map((c,d)=>{const l=jt(d),u=t==="layer-detail"&&c.id===n;return a.jsxs("div",{className:"flex items-center gap-1 whitespace-nowrap",children:[a.jsx("span",{className:"inline-block w-2 h-2 rounded-full",style:{backgroundColor:l.label,opacity:t==="layer-detail"&&!u?.3:1}}),a.jsxs("span",{className:`text-[11px] ${u?"text-text-primary font-medium":"text-text-secondary"}`,style:{opacity:t==="layer-detail"&&!u?.4:1},children:[c.name,a.jsxs("span",{className:"text-text-muted ml-0.5",children:["(",c.nodeIds.length,")"]})]})]},c.id)})})]})}const Jn={simple:"text-node-function",moderate:"text-gold-dim",complex:"text-[#c97070]"};function kl({data:e}){const t=jt(e.layerColorIndex),n=Jn[e.aggregateComplexity]??Jn.simple;return a.jsxs("div",{className:"relative rounded-xl bg-elevated border border-border-subtle overflow-hidden cursor-pointer transition-all duration-200 hover:border-gold/40 hover:shadow-lg group",style:{width:300,boxShadow:"0 4px 16px rgba(0,0,0,0.4)"},onClick:()=>e.onDrillIn(e.layerId),children:[a.jsx("div",{className:"absolute left-0 top-0 bottom-0 w-1.5 rounded-l-xl",style:{backgroundColor:t.label}}),a.jsx(fe,{type:"target",position:pe.Top,className:"!bg-text-muted !w-2 !h-2"}),a.jsxs("div",{className:"pl-5 pr-4 py-4",children:[a.jsxs("div",{className:"flex items-center justify-between mb-2",children:[a.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-wider",style:{color:t.label},children:"流程"}),a.jsxs("div",{className:"flex items-center gap-2",children:[e.searchMatchCount!=null&&e.searchMatchCount>0&&a.jsxs("span",{className:"text-[10px] font-mono bg-gold/20 text-gold px-1.5 py-0.5 rounded",children:[e.searchMatchCount," match",e.searchMatchCount!==1?"es":""]}),a.jsx("span",{className:`text-[10px] font-mono ${n}`,children:e.aggregateComplexity})]})]}),a.jsx("div",{className:"text-lg font-heading text-text-primary mb-1",children:e.layerName}),a.jsx("div",{className:"text-[11px] text-text-secondary line-clamp-2 leading-tight mb-3",children:e.layerDescription}),a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("span",{className:"text-[11px] text-text-muted",children:[e.fileCount," 个文档"]}),a.jsx("span",{className:"text-[10px] text-text-muted opacity-0 group-hover:opacity-100 transition-opacity",children:"点击查看 →"})]})]}),a.jsx(fe,{type:"source",position:pe.Bottom,className:"!bg-text-muted !w-2 !h-2"})]})}const Nl=v.memo(kl);function jl({data:e}){const t=jt(e.layerColorIndex);return a.jsxs("div",{className:"relative rounded-lg bg-elevated/60 overflow-hidden cursor-pointer transition-all duration-200 hover:bg-elevated/80",style:{width:220,border:`2px dashed ${t.border}`,boxShadow:"0 2px 8px rgba(0,0,0,0.2)"},onClick:()=>e.onNavigate(e.targetLayerId),children:[a.jsx(fe,{type:"target",position:pe.Top,className:"!bg-text-muted !w-2 !h-2"}),a.jsxs("div",{className:"px-3 py-2.5",children:[a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[a.jsx("span",{className:"inline-block w-2 h-2 rounded-full shrink-0",style:{backgroundColor:t.label}}),a.jsx("span",{className:"text-sm text-text-primary truncate",children:e.targetLayerName})]}),a.jsx("span",{className:"text-text-muted ml-2 shrink-0",children:"→"})]}),a.jsxs("div",{className:"text-[10px] text-text-muted mt-1 pl-4",children:[e.connectionCount," connection",e.connectionCount!==1?"s":""]})]}),a.jsx(fe,{type:"source",position:pe.Bottom,className:"!bg-text-muted !w-2 !h-2"})]})}const _l=v.memo(jl);function Cl({data:e,width:t,height:n}){const o=jt(e.colorIndex),r=e.isDiffAffected?"var(--color-diff-changed)":e.isExpanded||e.isFocusedViaChild?"rgba(212,165,116,0.6)":"rgba(212,165,116,0.25)",s=e.isExpanded||e.isFocusedViaChild?1.5:1,i=e.name==="~",c=i?"(root)":e.name,d=l=>{l.stopPropagation(),e.onToggle(e.containerId)};return a.jsx("div",{role:"button",tabIndex:0,"aria-expanded":e.isExpanded,"aria-label":`${c} container, ${e.childCount} item${e.childCount!==1?"s":""}, ${e.isExpanded?"expanded":"collapsed"}`,className:"rounded-xl cursor-pointer transition-all focus:outline-none focus:ring-2 focus:ring-[rgba(212,165,116,0.6)]",style:{width:t,height:n,background:"rgba(255,255,255,0.02)",border:`${s}px solid ${r}`,position:"relative"},onClick:d,onKeyDown:l=>{(l.key==="Enter"||l.key===" ")&&(l.preventDefault(),d(l))},children:a.jsxs("div",{className:"flex items-center justify-between font-heading",style:{padding:"12px 16px",color:o.label,fontSize:14,fontWeight:400},children:[a.jsxs("span",{className:i?"opacity-50":"",style:{display:"flex",alignItems:"center",gap:6},children:[e.isExpanded&&a.jsx("span",{style:{fontSize:10},children:"▾"}),c,e.searchHitCount!=null&&e.searchHitCount>0&&a.jsxs("span",{className:"font-mono",style:{marginLeft:6,fontSize:10,background:"rgba(212,165,116,0.2)",color:"var(--color-gold, #d4a574)",padding:"1px 6px",borderRadius:8},children:[e.searchHitCount," hit",e.searchHitCount!==1?"s":""]})]}),a.jsx("span",{style:{color:"#a39787",fontSize:11},children:e.childCount})]})})}const Jo=v.memo(Cl);Jo.displayName="ContainerNode";function Sl(){const e=b(i=>i.navigationLevel),t=b(i=>i.activeLayerId),n=b(i=>i.graph),o=b(i=>i.navigateToOverview),{t:r}=re(),s=n==null?void 0:n.layers.find(i=>i.id===t);return a.jsxs("div",{className:"absolute top-4 left-4 z-10 flex items-center gap-2",children:[e==="overview"&&a.jsx("div",{className:"px-4 py-2 rounded-full bg-elevated border border-border-subtle text-xs font-semibold tracking-wider uppercase text-text-secondary shadow-lg",children:r.breadcrumb.projectOverview}),e==="layer-detail"&&a.jsxs("div",{className:"flex items-center gap-1.5 px-4 py-2 rounded-full bg-elevated border border-gold/30 text-xs font-semibold tracking-wider uppercase shadow-lg",children:[a.jsx("button",{onClick:o,className:"text-gold hover:text-gold-bright transition-colors",children:r.breadcrumb.project}),a.jsx("span",{className:"text-text-muted",children:"›"}),a.jsx("span",{className:"text-text-primary",children:(s==null?void 0:s.name)??r.layer.defaultName}),a.jsxs("span",{className:"text-text-muted ml-1 text-[10px] normal-case tracking-normal",children:["(",r.breadcrumb.escBack,")"]})]})]})}const Il={presetId:"dark-gold",accentId:"gold"},at=[{id:"gold",name:"Gold",accent:"#d4a574",accentDim:"#c9a96e",accentBright:"#e8c49a"},{id:"ocean",name:"Ocean",accent:"#5ba4cf",accentDim:"#4e93ba",accentBright:"#7abce0"},{id:"emerald",name:"Emerald",accent:"#5ea67a",accentDim:"#4e9468",accentBright:"#78c492"},{id:"rose",name:"Rose",accent:"#cf7a8a",accentDim:"#b96e7e",accentBright:"#e094a4"},{id:"purple",name:"Purple",accent:"#9b7abf",accentDim:"#876bb0",accentBright:"#b494d4"},{id:"amber",name:"Amber",accent:"#c9963a",accentDim:"#b5862e",accentBright:"#ddb05c"},{id:"teal",name:"Teal",accent:"#4aab9a",accentDim:"#3d9686",accentBright:"#68c4b4"},{id:"silver",name:"Silver",accent:"#a0a8b0",accentDim:"#8e959c",accentBright:"#b8bfc6"}],Tl=[{id:"indigo",name:"Indigo",accent:"#4a6fa5",accentDim:"#3d5f8f",accentBright:"#6088bf"},{id:"ocean",name:"Ocean",accent:"#3a8ab5",accentDim:"#2e7aa0",accentBright:"#55a0cc"},{id:"emerald",name:"Emerald",accent:"#3a8a5c",accentDim:"#2e7a4e",accentBright:"#55a878"},{id:"rose",name:"Rose",accent:"#a5566a",accentDim:"#8f4a5c",accentBright:"#bf6e82"},{id:"purple",name:"Purple",accent:"#6b5a9e",accentDim:"#5c4d8a",accentBright:"#8474b5"},{id:"amber",name:"Amber",accent:"#9e7a30",accentDim:"#8a6a28",accentBright:"#b5923e"},{id:"teal",name:"Teal",accent:"#2e8a7a",accentDim:"#267a6c",accentBright:"#45a595"},{id:"slate",name:"Slate",accent:"#5a6570",accentDim:"#4e5860",accentBright:"#6e7a85"}],Zt=[{id:"dark-gold",name:"Dark Gold",isDark:!0,defaultAccentId:"gold",accentSwatches:at,colors:{root:"#0a0a0a",surface:"#111111",elevated:"#1a1a1a",panel:"#141414","text-primary":"#f5f0eb","text-secondary":"#a39787","text-muted":"#6b5f53","node-file":"#4a7c9b","node-function":"#5a9e6f","node-class":"#8b6fb0","node-module":"#c9a06c","node-concept":"#b07a8a","node-config":"#5eead4","node-document":"#7dd3fc","node-service":"#a78bfa","node-table":"#6ee7b7","node-endpoint":"#fdba74","node-pipeline":"#fda4af","node-schema":"#fcd34d","node-resource":"#a5b4fc"}},{id:"dark-ocean",name:"Dark Ocean",isDark:!0,defaultAccentId:"ocean",accentSwatches:at,colors:{root:"#0a0e14",surface:"#111820",elevated:"#1a222c",panel:"#141c24","text-primary":"#e8edf2","text-secondary":"#87939f","text-muted":"#536b7a","node-file":"#4a7c9b","node-function":"#5a9e6f","node-class":"#8b6fb0","node-module":"#c9a06c","node-concept":"#b07a8a","node-config":"#5eead4","node-document":"#7dd3fc","node-service":"#a78bfa","node-table":"#6ee7b7","node-endpoint":"#fdba74","node-pipeline":"#fda4af","node-schema":"#fcd34d","node-resource":"#a5b4fc"}},{id:"dark-forest",name:"Dark Forest",isDark:!0,defaultAccentId:"emerald",accentSwatches:at,colors:{root:"#0a100a",surface:"#111811",elevated:"#1a241a",panel:"#141c14","text-primary":"#ebf0eb","text-secondary":"#87a38f","text-muted":"#536b5a","node-file":"#4a7c9b","node-function":"#5a9e6f","node-class":"#8b6fb0","node-module":"#c9a06c","node-concept":"#b07a8a","node-config":"#5eead4","node-document":"#7dd3fc","node-service":"#a78bfa","node-table":"#6ee7b7","node-endpoint":"#fdba74","node-pipeline":"#fda4af","node-schema":"#fcd34d","node-resource":"#a5b4fc"}},{id:"dark-rose",name:"Dark Rose",isDark:!0,defaultAccentId:"rose",accentSwatches:at,colors:{root:"#100a0a",surface:"#181111",elevated:"#221a1a",panel:"#1c1414","text-primary":"#f2e8ea","text-secondary":"#9f8790","text-muted":"#6b535a","node-file":"#4a7c9b","node-function":"#5a9e6f","node-class":"#8b6fb0","node-module":"#c9a06c","node-concept":"#b07a8a","node-config":"#5eead4","node-document":"#7dd3fc","node-service":"#a78bfa","node-table":"#6ee7b7","node-endpoint":"#fdba74","node-pipeline":"#fda4af","node-schema":"#fcd34d","node-resource":"#a5b4fc"}},{id:"light-minimal",name:"Light Minimal",isDark:!1,defaultAccentId:"indigo",accentSwatches:Tl,colors:{root:"#f5f3f0",surface:"#eae7e3",elevated:"#ffffff",panel:"#f0ede9","text-primary":"#1a1a1a","text-secondary":"#6b6b6b","text-muted":"#a0a0a0","node-file":"#3a6a87","node-function":"#488a5b","node-class":"#755d99","node-module":"#a88a56","node-concept":"#966674","node-config":"#14b8a6","node-document":"#38bdf8","node-service":"#8b5cf6","node-table":"#34d399","node-endpoint":"#fb923c","node-pipeline":"#fb7185","node-schema":"#facc15","node-resource":"#818cf8"}}];function Bt(e){return Zt.find(t=>t.id===e)??Zt[0]}function El(e,t){return e.accentSwatches.find(n=>n.id===t)??e.accentSwatches.find(n=>n.id===e.defaultAccentId)??e.accentSwatches[0]}function $l(e){const t=e.replace("#",""),n=parseInt(t,16);return`${n>>16&255}, ${n>>8&255}, ${n&255}`}function Ll(e,t){const n=$l(e);return{"color-border-subtle":`rgba(${n}, ${t?.12:.1})`,"color-border-medium":`rgba(${n}, ${t?.25:.18})`,"glass-bg":t?"rgba(20, 20, 20, 0.8)":"rgba(255, 255, 255, 0.8)","glass-bg-heavy":t?"rgba(20, 20, 20, 0.95)":"rgba(255, 255, 255, 0.95)","glass-border":`rgba(${n}, ${t?.1:.08})`,"glass-border-heavy":`rgba(${n}, ${t?.15:.12})`,"scrollbar-thumb":`rgba(${n}, 0.2)`,"scrollbar-thumb-hover":`rgba(${n}, 0.35)`,"glow-accent":`rgba(${n}, 0.15)`,"glow-accent-strong":`rgba(${n}, 0.4)`,"glow-accent-pulse":`rgba(${n}, 0.6)`,"color-edge":`rgba(${n}, 0.3)`,"color-edge-dim":`rgba(${n}, 0.08)`,"color-edge-dot":`rgba(${n}, 0.15)`,"color-accent-overlay-bg":`rgba(${n}, 0.05)`,"color-accent-overlay-border":`rgba(${n}, 0.25)`,"kbd-bg":`rgba(${n}, 0.1)`}}function zl(e){const t=Bt(e.presetId),n=El(t,e.accentId),o=document.documentElement.style;for(const[c,d]of Object.entries(t.colors))o.setProperty(`--color-${c}`,d);o.setProperty("--color-accent",n.accent),o.setProperty("--color-accent-dim",n.accentDim),o.setProperty("--color-accent-bright",n.accentBright);const r=Ll(n.accent,t.isDark);for(const[c,d]of Object.entries(r))o.setProperty(`--${c}`,d);document.documentElement.setAttribute("data-theme",t.isDark?"dark":"light");const s={serif:"var(--font-serif)",sans:"var(--font-sans)",mono:"var(--font-mono)"},i=e.headingFont??"serif";o.setProperty("--font-heading",s[i]??s.serif)}const Yo="ua-theme",Xo=v.createContext(null);function qo(){try{const e=localStorage.getItem(Yo);if(!e)return null;const t=JSON.parse(e);return t&&typeof t.presetId=="string"&&typeof t.accentId=="string"?t:null}catch{return null}}function Al(e){try{localStorage.setItem(Yo,JSON.stringify(e))}catch{}}function Ol(e){return qo()??e??Il}function Ml({metaTheme:e,children:t}){const[n,o]=v.useState(()=>Ol(e)),r=v.useRef(!1);v.useEffect(()=>{zl(n),r.current&&Al(n),r.current=!0},[n]),v.useEffect(()=>{e&&!qo()&&o(e)},[e]);const s=v.useCallback(l=>{o(u=>{const f=Bt(l);return{presetId:l,accentId:f.defaultAccentId}})},[]),i=v.useCallback(l=>{o(u=>({...u,accentId:l}))},[]),c=v.useCallback(l=>{o(u=>({...u,headingFont:l}))},[]),d=Bt(n.presetId);return a.jsx(Xo.Provider,{value:{config:n,preset:d,setPreset:s,setAccent:i,setHeadingFont:c},children:t})}function Qo(){const e=v.useContext(Xo);if(!e)throw new Error("useTheme must be used within ThemeProvider");return e}const de=280,ue=120,Fl=320,Dl=180,Pl=240,Rl=80;function Zl(e,t,n,o){if(e.length===0)return{nodes:e,edges:t};const r=e.map(y=>({id:y.id,x:Math.random()*800-400,y:Math.random()*800-400,community:o==null?void 0:o.get(y.id)})),s=new Set(r.map(y=>y.id)),i=t.filter(y=>s.has(y.source)&&s.has(y.target)).map(y=>({source:y.source,target:y.target})),c=o?Math.max(1,new Set(o.values()).size):1,d=y=>2*Math.PI*y/c,l=Math.max(600,e.length*5),u=e.length>100,f=u?-600:-350,h=u?250:150,g=jr(r).force("link",_r(i).id(y=>y.id).distance(h).strength(.2)).force("charge",Cr().strength(f).distanceMax(1500)).force("center",Sr(0,0).strength(.03)).force("collide",Ir().radius(y=>{const x=n==null?void 0:n.get(y.id);return Math.max(20,(((x==null?void 0:x.width)??de)+40)/2)}).strength(.8));o&&c>1&&(g.force("clusterX",Tr(y=>{const x=y.community??0;return Math.cos(d(x))*l}).strength(.3)),g.force("clusterY",Er(y=>{const x=y.community??0;return Math.sin(d(x))*l}).strength(.3)));const m=Math.min(300,Math.max(100,e.length));g.tick(m),g.stop();const p=new Map(r.map(y=>[y.id,{x:y.x??0,y:y.y??0}]));return{nodes:e.map(y=>{const x=p.get(y.id)??{x:0,y:0},k=n==null?void 0:n.get(y.id),T=(k==null?void 0:k.width)??de,j=(k==null?void 0:k.height)??ue;return{...y,position:{x:x.x-T/2,y:x.y-j/2}}}),edges:t}}const Vt={algorithm:"layered","elk.direction":"DOWN","elk.layered.spacing.nodeNodeBetweenLayers":"80","elk.spacing.nodeNode":"60","elk.layered.crossingMinimization.strategy":"LAYER_SWEEP","elk.edgeRouting":"ORTHOGONAL","elk.layered.compaction.postCompaction.strategy":"LEFT","elk.padding":"[top=40,left=20,right=20,bottom=20]"};function er(e,t,n,o){return{id:"root",layoutOptions:{...Vt,...o},children:e.map(r=>{const s=n.get(r.id);return{id:r.id,width:(s==null?void 0:s.width)??de,height:(s==null?void 0:s.height)??ue}}),edges:t.map((r,s)=>({id:r.id??`e${s}`,sources:[String(r.source)],targets:[String(r.target)]}))}}function cn(e,t){const n=new Map;for(const o of t.children??[])n.set(o.id,{x:o.x??0,y:o.y??0,width:o.width,height:o.height});return e.map(o=>{const r=n.get(o.id);return r?{...o,position:{x:r.x,y:r.y},...r.width!=null?{width:r.width}:{},...r.height!=null?{height:r.height}:{}}:{...o,position:o.position??{x:0,y:0}}})}const Bl=de,Vl=ue;function Be(e,t,n){return{level:e,category:t,message:n}}function Ve(e,t){if(e)throw new Error(`[ELK repair] ${t.level}: ${t.message}`)}function Gl(e,t={}){const n=[],o=t.strict;let r=0;const s=C=>C.map(I=>{const E={...I};return(E.width==null||E.height==null)&&(E.width=E.width??Bl,E.height=E.height??Vl,r++),E.children&&(E.children=s(E.children)),E}),i=s(e.children);if(r>0){const C=Be("auto-corrected","elk-missing-dimensions",`Set default dimensions on ${r} node(s) missing width/height.`);n.push(C),Ve(o,C)}let c=0;const d=C=>{const I=new Set,E=[];for(const w of C){if(I.has(w.id)){c++;continue}I.add(w.id),E.push({...w,children:w.children?d(w.children):void 0})}return E},l=d(i);if(c>0){const C=Be("auto-corrected","elk-duplicate-id",`Removed ${c} duplicate child id(s).`);n.push(C),Ve(o,C)}const u=new Set,f=C=>{for(const I of C)u.add(I.id),I.children&&f(I.children)};f(l);let h=0;const g=l.filter(C=>C.parentId&&!u.has(C.parentId)?(h++,!1):!0);if(h>0){const C=Be("dropped","elk-orphan-parent",`Dropped ${h} child(ren) with missing parent reference.`);n.push(C),Ve(o,C)}let m=0;const p=e.edges.filter(C=>C.sources.every(E=>u.has(E))&&C.targets.every(E=>u.has(E))?!0:(m++,!1));if(m>0){const C=Be("dropped","elk-orphan-edge",`Dropped ${m} edge(s) referencing nonexistent nodes.`);n.push(C),Ve(o,C)}const N=new Map,y=(C,I)=>{for(const E of C)I&&N.set(E.id,I),E.children&&y(E.children,E.id)};y(g);let x=0;const k=C=>{const I=new Set;let E=N.get(C);for(;E;){if(E===C||I.has(E))return!0;I.add(E),E=N.get(E)}return!1},T=C=>C.filter(I=>k(I.id)?(x++,!1):!0).map(I=>({...I,children:I.children?T(I.children):void 0})),j=T(g);if(x>0){const C=Be("dropped","elk-containment-cycle",`Dropped ${x} node(s) in containment cycles.`);n.push(C),Ve(o,C)}return{input:{...e,children:j,edges:p},issues:n}}const Hl=new $r;async function yt(e,t={}){const{input:n,issues:o}=Gl(e,t);try{return{positioned:await Hl.layout(n),issues:o}}catch(r){const s={level:"fatal",category:"elk-layout-failed",message:`ELK layout failed: ${r instanceof Error?r.message:String(r)}. This looks like a dashboard rendering bug — please file an issue with the copied error.`};if(t.strict)throw r;return{positioned:{...n,children:[],edges:[]},issues:[...o,s]}}}function tr(e){const t=new Map;for(const o of e.layers)for(const r of o.nodeIds)t.set(r,o.id);const n=new Map;for(const o of e.edges){const r=t.get(o.source),s=t.get(o.target);if(!r||!s||r===s)continue;const[i,c]=r({sourceLayerId:o.sourceLayerId,targetLayerId:o.targetLayerId,count:o.count,edgeTypes:Array.from(o.edgeTypes)}))}function Ul(e,t,n){const o=tr(e),r=new Map(e.layers.map(i=>[i.id,i.name])),s=new Map;for(const i of o)i.sourceLayerId===t?s.set(i.targetLayerId,(s.get(i.targetLayerId)??0)+i.count):i.targetLayerId===t&&s.set(i.sourceLayerId,(s.get(i.sourceLayerId)??0)+i.count);return Array.from(s.entries()).map(([i,c])=>({layerId:i,layerName:r.get(i)??i,connectionCount:c}))}function Wl(e,t,n){var i,c;const o=new Set(((i=e.layers.find(d=>d.id===t))==null?void 0:i.nodeIds)??[]),r=new Set(((c=e.layers.find(d=>d.id===n))==null?void 0:c.nodeIds)??[]),s=new Set;for(const d of e.edges)o.has(d.source)&&r.has(d.target)&&s.add(d.source),o.has(d.target)&&r.has(d.source)&&s.add(d.target);return s}function Kl(e,t){const n=[],o=new Map;for(const s of e){const i=t.get(s.source),c=t.get(s.target);if(!i||!c)continue;if(i===c){n.push(s);continue}const d=`${i.length}:${i}\0${c}`,l=o.get(d);l?(l.count++,l.edgeTypes.add(s.type)):o.set(d,{sourceContainerId:i,targetContainerId:c,count:1,edgeTypes:new Set([s.type])})}const r=[...o.values()].map(s=>({sourceContainerId:s.sourceContainerId,targetContainerId:s.targetContainerId,count:s.count,edgeTypes:[...s.edgeTypes]}));return{intraContainer:n,interContainerAggregated:r}}function Jl(e,t){const n=new Set(e),o=new Lr({type:"undirected",multi:!1});for(const c of e)o.addNode(c);for(const c of t)!n.has(c.source)||!n.has(c.target)||c.source!==c.target&&(o.hasEdge(c.source,c.target)||o.addEdge(c.source,c.target));const r=zr(o),s=new Map;for(const c of e)s.set(c,r[c]??-1);let i=Math.max(...Array.from(s.values()).filter(c=>c>=0),-1)+1;for(const[c,d]of s)d===-1&&s.set(c,i++);return s}const Yl=2,Yn=.7,Xl=3,Xn="~";function ql(e){if(e.length===0)return"";const t=e.map(r=>{const s=r.lastIndexOf("/");return s>=0?r.slice(0,s):""});let n=t[0];for(const r of t)for(;!r.startsWith(n);)if(n=n.slice(0,-1),!n)return"";const o=n.lastIndexOf("/");return o>=0?n.slice(0,o+1):""}function Ql(e){const t=e.indexOf("/");return t>=0?e.slice(0,t):e}function eu(e){const t=e.filter(s=>s.filePath),n=ql(t.map(s=>s.filePath)),o=new Map,r=[];for(const s of e){if(!s.filePath){r.push(s.id);continue}const i=s.filePath.slice(n.length);if(!i.includes("/")){r.push(s.id);continue}const c=Ql(i),d=o.get(c)??[];d.push(s.id),o.set(c,d)}return{groups:o,rooted:r}}function tu(e,t,n){if(e.size+(t.length>0?1:0)Yn)return!0;return t.length/n>Yn}function nu(e,t){if(e.length===0)return{containers:[],ungrouped:[]};const{groups:n,rooted:o}=eu(e),r=tu(n,o,e.length);let s;if(r){const c=Jl(e.map(u=>u.id),t),d=new Map;for(const[u,f]of c){const h=d.get(f)??[];h.push(u),d.set(f,h)}s=[...d.entries()].sort((u,f)=>u[0]-f[0]).map(([u,f],h)=>({id:`container:cluster-${u}`,name:h<26?`Cluster ${String.fromCharCode(65+h)}`:`Cluster ${h+1}`,nodeIds:f,strategy:"community"}))}else s=[...n.entries()].map(([c,d])=>({id:`container:${c}`,name:c,nodeIds:d,strategy:"folder"})),o.length>0&&s.push({id:`container:${Xn}`,name:Xn,nodeIds:o,strategy:"folder"});const i=[];return e.length>=Xl&&(s=s.filter(c=>c.nodeIds.length===1?(i.push(c.nodeIds[0]),!1):!0)),{containers:s,ungrouped:i}}function ou(e,t){const n={simple:0,moderate:0,complex:0};let o=0;for(const s of e.nodeIds){const i=t.get(s);i&&(o++,n[i.complexity]++)}const r=n.complex>o*.3?"complex":n.moderate>o*.3?"moderate":"simple";return{resolvedCount:o,aggregateComplexity:r}}const ru={custom:Wo,"layer-cluster":Nl,portal:_l,container:Jo},su={file:"code",function:"code",class:"code",module:"code",concept:"code",config:"config",document:"docs",service:"infra",resource:"infra",pipeline:"infra",table:"data",endpoint:"data",schema:"data",domain:"domain",flow:"domain",step:"domain",article:"knowledge",entity:"knowledge",topic:"knowledge",claim:"knowledge",source:"knowledge"};function au(){const e=b(c=>c.tourHighlightedNodeIds),t=b(c=>c.setTourFitPending),{fitView:n,getInternalNode:o}=Ut(),r=Nr(),s=v.useRef(""),i=v.useRef("");return v.useEffect(()=>{const c=e.join(` +`);if(c===""){s.current="",i.current="",t(!1);return}if(c===s.current)return;const d=240;let l=0,u=!1,f=0;i.current!==c&&t(!0);const h=()=>{var m,p;if(u)return;let g=!0;for(const N of e){const y=o(N);if(!y||!((m=y.measured)!=null&&m.width)||!((p=y.measured)!=null&&p.height)){g=!1;break}}if(g){n({nodes:e.map(N=>({id:N})),duration:500,padding:.3,maxZoom:1.2,minZoom:.4}),s.current=c,i.current="",t(!1);return}if(++l{u=!0,cancelAnimationFrame(f)}},[e,r,n,o,t]),null}function iu(){const e=b(o=>o.selectedNodeId),{fitView:t}=Ut(),n=v.useRef(null);return v.useEffect(()=>{if(e&&e!==n.current){const o=setTimeout(()=>{t({nodes:[{id:e}],duration:500,padding:.3,maxZoom:1.2,minZoom:.01})},100);return n.current=e,()=>clearTimeout(o)}n.current=e},[e,t]),null}function cu(){const e=b(u=>u.graph),t=b(u=>u.nodesById),n=b(u=>u.nodeIdToLayerId),o=b(u=>u.searchResults),r=b(u=>u.drillIntoLayer),s=v.useMemo(()=>{if(!e)return null;const u=e.layers??[];if(u.length===0)return null;const f=new Map;if(o.length>0)for(const N of o){const y=n.get(N.nodeId);y&&f.set(y,(f.get(y)??0)+1)}const h=u.map((N,y)=>{const{aggregateComplexity:x}=ou(N,t);return{id:N.id,type:"layer-cluster",position:{x:0,y:0},data:{layerId:N.id,layerName:N.name,layerDescription:N.description,fileCount:N.nodeIds.length,aggregateComplexity:x,layerColorIndex:y,searchMatchCount:f.get(N.id),onDrillIn:r}}}),m=tr(e).map((N,y)=>({id:`le-${y}`,source:N.sourceLayerId,target:N.targetLayerId,label:`${N.count}`,style:{stroke:"rgba(212,165,116,0.4)",strokeWidth:Math.min(1+Math.log2(N.count+1),5)},labelStyle:{fill:"#a39787",fontSize:11,fontWeight:600}})),p=new Map;for(const N of h)p.set(N.id,{width:Fl,height:Dl});return{clusterNodes:h,flowEdges:m,dims:p}},[e,t,n,o,r]),[i,c]=v.useState({nodes:[],edges:[]}),[d,l]=v.useState("ready");return v.useEffect(()=>{if(!s){c({nodes:[],edges:[]}),l("ready");return}let u=!1;const{clusterNodes:f,flowEdges:h,dims:g}=s,m=f,p=er(m,h,g,{"elk.direction":"RIGHT","elk.layered.spacing.nodeNodeBetweenLayers":"150","elk.spacing.nodeNode":"80"});return l("computing"),yt(p,{strict:!1}).then(({positioned:N,issues:y})=>{if(u)return;y.length>0&&b.getState().appendLayoutIssues(y);const x=cn(m,N);c({nodes:x,edges:h}),l("ready")}).catch(N=>{u||(console.error("[overview ELK] layout failed:",N),l("ready"))}),()=>{u=!0}},[s]),{...i,layoutStatus:d}}const qn={nodes:[],edges:[],portalNodes:[],portalEdges:[],filteredEdges:[],filteredNodes:[],containers:[],nodeToContainer:new Map,intraContainer:[]};function du(){const e=b($=>$.graph),t=b($=>$.nodesById),n=b($=>$.activeLayerId),o=b($=>$.selectNode),r=b($=>$.persona),s=b($=>$.diffMode),i=b($=>$.changedNodeIds),c=b($=>$.affectedNodeIds),d=b($=>$.focusNodeId),l=b($=>$.nodeTypeFilters),u=b($=>$.drillIntoLayer),f=b($=>$.detailLevel),h=b($=>$.showFunctionsInClassView),g=v.useCallback($=>{o($)},[o]),m=v.useCallback($=>b.getState().toggleContainer($),[]),p=v.useMemo(()=>{if(!e||!n)return null;const $=e.layers.find(L=>L.id===n);if(!$)return null;const D=new Set($.nodeIds),Z=new Set(D);if(f!=="file"){for(const L of e.edges)if(L.type==="contains"&&D.has(L.source)){const B=t.get(L.target);if(!B)continue;(B.type==="class"||B.type==="function"&&h)&&Z.add(L.target)}}const P=new Set(["function","class"]),V=new Set(["file","module","concept","config","document","service","table","endpoint","pipeline","schema","resource","domain","flow","step","function","class"]);let R=e.nodes.filter(L=>!(!Z.has(L.id)||!V.has(L.type)||r==="non-technical"&&P.has(L.type)));R=R.filter(L=>{const me=su[L.type]??"code";return l[me]!==!1});let U=new Set(R.map(L=>L.id)),A=e.edges.filter(L=>U.has(L.source)&&U.has(L.target));if(d&&U.has(d)){const L=new Set([d]);for(const B of A)B.source===d&&L.add(B.target),B.target===d&&L.add(B.source);R=R.filter(B=>L.has(B.id)),U=new Set(R.map(B=>B.id)),A=A.filter(B=>U.has(B.source)&&U.has(B.target))}const{containers:Q,ungrouped:se}=nu(R,A),K=new Set(se),z=new Map;for(const L of Q)for(const B of L.nodeIds)z.set(B,L.id);for(const L of K)z.set(L,L);const{intraContainer:J,interContainerAggregated:S}=Kl(A,z),Y=800,G=600,te=b.getState().containerSizeMemory,we=L=>{var be;const B=(be=te.get(L.id))==null?void 0:be.width;if(B)return B;const me=Math.sqrt(L.nodeIds.length)*de*1.2;return Math.min(Y,Math.max(de,me))},ve=L=>{var be;const B=(be=te.get(L.id))==null?void 0:be.height;if(B)return B;const me=Math.sqrt(L.nodeIds.length)*ue*1.2;return Math.min(G,Math.max(ue,me))},ze=Q.map((L,B)=>({id:L.id,type:"container",position:{x:0,y:0},width:we(L),height:ve(L),data:{containerId:L.id,name:L.name,childCount:L.nodeIds.length,strategy:L.strategy,colorIndex:B%12,isExpanded:!1,hasSearchHits:!1,isDiffAffected:!1,isFocusedViaChild:!1,onToggle:m}})),Ke=R.filter(L=>K.has(L.id)).map(L=>{var B;return{id:L.id,type:"custom",position:{x:0,y:0},data:{label:L.name??((B=L.filePath)==null?void 0:B.split("/").pop())??L.id,nodeType:L.type,summary:L.summary,complexity:L.complexity,tags:L.tags,isHighlighted:!1,searchScore:void 0,isSelected:!1,isTourHighlighted:!1,isDiffChanged:s&&i.has(L.id),isDiffAffected:s&&c.has(L.id),isDiffFaded:s&&!i.has(L.id)&&!c.has(L.id),isNeighbor:!1,isSelectionFaded:!1,onNodeClick:g}}}),Je=S.map((L,B)=>{const me=s?{stroke:"rgba(212,165,116,0.08)",strokeWidth:1}:{stroke:"rgba(212,165,116,0.4)",strokeWidth:Math.min(1+Math.log2(L.count+1),5)};return{id:`agg-${B}`,source:L.sourceContainerId,target:L.targetContainerId,label:String(L.count),style:me,labelStyle:{fill:s?"rgba(163,151,135,0.3)":"#a39787",fontSize:11}}}),Ye=Ul(e,n),_t=new Map(e.layers.map((L,B)=>[L.id,B])),Ce=Ye.map(L=>({id:`portal:${L.layerId}`,type:"portal",position:{x:0,y:0},data:{targetLayerId:L.layerId,targetLayerName:L.layerName,connectionCount:L.connectionCount,layerColorIndex:_t.get(L.layerId)??0,onNavigate:u}})),Ae=[];let Pe=Je.length;for(const L of Ye){const B=Wl(e,n,L.layerId),me=new Set;for(const be of B){if(!U.has(be))continue;const Ct=z.get(be)??be;me.has(Ct)||(me.add(Ct),Ae.push({id:`e-${Pe++}`,source:Ct,target:`portal:${L.layerId}`,style:{stroke:"rgba(212,165,116,0.2)",strokeWidth:1,strokeDasharray:"4 4"},animated:!1}))}}return{containers:Q,ungrouped:se,nodeToContainer:z,intraContainer:J,filteredGraphNodes:R,filteredGraphEdges:A,containerFlowNodes:ze,ungroupedFlowNodes:Ke,aggEdges:Je,portalNodes:Ce,portalEdges:Ae}},[e,t,n,r,s,i,c,d,l,u,f,h,g,m]),N=b($=>$.stage1Tick),[y,x]=v.useState(qn),[k,T]=v.useState("ready");v.useEffect(()=>{if(!p){x(qn),T("ready");return}let $=!1;const{containers:D,nodeToContainer:Z,intraContainer:P,filteredGraphNodes:V,filteredGraphEdges:R,containerFlowNodes:U,ungroupedFlowNodes:A,aggEdges:Q,portalNodes:se,portalEdges:K}=p,z=b.getState().containerSizeMemory,J=[...U.map(G=>{const te=z.get(G.id);return{id:G.id,width:(te==null?void 0:te.width)??G.width??de,height:(te==null?void 0:te.height)??G.height??ue}}),...A.map(G=>({id:G.id,width:de,height:ue})),...se.map(G=>({id:G.id,width:Pl,height:Rl}))],S=[...Q.map(G=>({id:G.id,sources:[String(G.source)],targets:[String(G.target)]})),...K.map(G=>({id:G.id,sources:[String(G.source)],targets:[String(G.target)]}))],Y={id:"layer",layoutOptions:Vt,children:J,edges:S};return T("computing"),yt(Y,{strict:!1}).then(({positioned:G,issues:te})=>{if($)return;te.length>0&&b.getState().appendLayoutIssues(te);const we=[...U,...A,...se],ve=cn(we,G);x({nodes:ve,edges:Q,portalNodes:se,portalEdges:K,filteredEdges:R,filteredNodes:V,containers:D,nodeToContainer:Z,intraContainer:P}),T("ready")}).catch(G=>{$||(console.error("[layer-detail Stage 1 ELK] layout failed:",G),T("ready"))}),()=>{$=!0}},[p,N]);const j=b($=>$.expandedContainers),C=b($=>$.containerLayoutCache),I=b($=>$.setContainerLayout),E=b($=>$.bumpStage1Tick),w=y.containers,M=y.intraContainer;return v.useEffect(()=>{if(w.length===0)return;const $=[...j].filter(P=>!C.has(P));if($.length===0)return;let D=!1;const Z=b.getState().containerSizeMemory;return Promise.all($.map(async P=>{const V=w.find(K=>K.id===P);if(!V)return null;const R=new Set(V.nodeIds),U=M.filter(K=>R.has(K.source)&&R.has(K.target)),A=V.nodeIds.map(K=>({id:K,width:de,height:ue})),Q=U.map((K,z)=>({id:`${P}-e${z}`,sources:[K.source],targets:[K.target]})),se={id:P,layoutOptions:Vt,children:A,edges:Q};try{const{positioned:K,issues:z}=await yt(se,{strict:!1});z.length>0&&b.getState().appendLayoutIssues(z);const J=new Map;let S=0,Y=0;for(const Ce of K.children??[]){const Ae=Ce.x??0,Pe=Ce.y??0,L=Ce.width??de,B=Ce.height??ue;J.set(Ce.id,{x:Ae,y:Pe}),Ae+L>S&&(S=Ae+L),Pe+B>Y&&(Y=Pe+B)}const G={width:S+40,height:Y+60},te=Z.get(P),we=800,ve=600,ze=(te==null?void 0:te.width)??Math.min(we,Math.max(de,Math.sqrt(V.nodeIds.length)*de*1.2)),Ke=(te==null?void 0:te.height)??Math.min(ve,Math.max(ue,Math.sqrt(V.nodeIds.length)*ue*1.2)),Je=Math.abs(G.width-ze)/ze,Ye=Math.abs(G.height-Ke)/Ke,_t=Je>.2||Ye>.2;return{containerId:P,childPositions:J,actualSize:G,deviated:_t}}catch(K){return console.error(`[Stage 2 ${P}] layout failed:`,K),null}})).then(P=>{if(D)return;let V=!1;for(const R of P)R&&(I(R.containerId,R.childPositions,R.actualSize),R.deviated&&(V=!0));V&&E()}),()=>{D=!0}},[j,w,M,C,I,E]),{...y,layoutStatus:k}}function lu(e,t){var n;return{id:e.id,type:"custom",position:{x:0,y:0},data:{label:e.name??((n=e.filePath)==null?void 0:n.split("/").pop())??e.id,nodeType:e.type,summary:e.summary,complexity:e.complexity,tags:e.tags,isHighlighted:!1,searchScore:void 0,isSelected:!1,isTourHighlighted:!1,isDiffChanged:t.diffMode&&t.changedNodeIds.has(e.id),isDiffAffected:t.diffMode&&t.affectedNodeIds.has(e.id),isDiffFaded:t.diffMode&&!t.changedNodeIds.has(e.id)&&!t.affectedNodeIds.has(e.id),isNeighbor:!1,isSelectionFaded:!1,onNodeClick:t.onNodeClick}}}function uu(){const e=b(j=>j.selectedNodeId),t=b(j=>j.searchResults),n=b(j=>j.tourHighlightedNodeIds),o=b(j=>j.expandedContainers),r=b(j=>j.containerLayoutCache),s=b(j=>j.diffMode),i=b(j=>j.changedNodeIds),c=b(j=>j.affectedNodeIds),d=b(j=>j.focusNodeId),l=b(j=>j.selectNode),u=v.useCallback(j=>l(j),[l]),f=du(),h=v.useMemo(()=>{if(o.size===0)return[];const j=[],C=new Map(f.filteredNodes.map(I=>[I.id,I]));for(const I of o){const E=r.get(I),w=f.containers.find(M=>M.id===I);if(!(!E||!w))for(const M of w.nodeIds){const $=C.get(M),D=E.childPositions.get(M);if(!$||!D)continue;const Z=lu($,{diffMode:s,changedNodeIds:i,affectedNodeIds:c,onNodeClick:u});j.push({...Z,parentId:I,extent:"parent",position:D})}}return j},[o,r,f.containers,f.filteredNodes,s,i,c,u]),g=v.useMemo(()=>{const j=new Map;if(t.length===0)return j;for(const C of t){const I=f.nodeToContainer.get(C.nodeId);!I||I===C.nodeId||j.set(I,(j.get(I)??0)+1)}return j},[t,f.nodeToContainer]),m=v.useMemo(()=>{const j=new Set;if(!s)return j;for(const C of i){const I=f.nodeToContainer.get(C);I&&I!==C&&j.add(I)}for(const C of c){const I=f.nodeToContainer.get(C);I&&I!==C&&j.add(I)}return j},[s,i,c,f.nodeToContainer]),p=v.useMemo(()=>{const j=new Set;if(!d)return j;const C=f.nodeToContainer.get(d);C&&C!==d&&j.add(C);for(const I of f.filteredEdges)if(I.source===d){const E=f.nodeToContainer.get(I.target);E&&E!==I.target&&j.add(E)}else if(I.target===d){const E=f.nodeToContainer.get(I.source);E&&E!==I.source&&j.add(E)}return j},[d,f.filteredEdges,f.nodeToContainer]),N=v.useMemo(()=>{const j=new Set;if(!e)return j;const C=f.nodeToContainer.get(e);C&&C!==e&&j.add(C);for(const I of f.filteredEdges)if(I.source===e){const E=f.nodeToContainer.get(I.target);E&&E!==I.target&&j.add(E)}else if(I.target===e){const E=f.nodeToContainer.get(I.source);E&&E!==I.source&&j.add(E)}return j},[e,f.filteredEdges,f.nodeToContainer]),y=v.useMemo(()=>{const j=[...f.nodes,...h],C=new Map(t.map(w=>[w.nodeId,w.score])),I=new Set(n),E=new Set;if(e){for(const w of f.filteredEdges)w.source===e&&E.add(w.target),w.target===e&&E.add(w.source);E.add(e)}return j.map(w=>{if(w.type==="portal")return w;if(w.type==="container"){const A=String(w.id),Q=w.data,se=o.has(A),K=g.get(A)??0,z=K>0,J=z?K:void 0,S=m.has(A),Y=p.has(A)||N.has(A);return Q.isExpanded===se&&Q.hasSearchHits===z&&Q.searchHitCount===J&&Q.isDiffAffected===S&&Q.isFocusedViaChild===Y?w:{...w,data:{...Q,isExpanded:se,hasSearchHits:z,searchHitCount:J,isDiffAffected:S,isFocusedViaChild:Y}}}const M=C.get(w.id),$=M!==void 0,D=e===w.id,Z=I.has(w.id),P=!!e,V=P&&E.has(w.id)&&!D,R=P&&!E.has(w.id),U=w.data;return U.isHighlighted===$&&U.searchScore===M&&U.isSelected===D&&U.isTourHighlighted===Z&&U.isNeighbor===V&&U.isSelectionFaded===R?w:{...w,data:{...U,isHighlighted:$,searchScore:M,isSelected:D,isTourHighlighted:Z,isNeighbor:V,isSelectionFaded:R}}})},[f.nodes,h,f.filteredEdges,e,t,n,o,g,m,p,N]),x=v.useMemo(()=>{if(o.size===0)return f.edges;const j=[],C=new Set;for(const I of f.edges){const E=String(I.source),w=String(I.target),M=o.has(E),$=o.has(w);if(!M&&!$){j.push(I);continue}const D=f.filteredEdges.filter(Z=>{const P=f.nodeToContainer.get(Z.source),V=f.nodeToContainer.get(Z.target);return P===E&&V===w});for(const Z of D){const P=M?Z.source:E,V=$?Z.target:w,R=`${P}|${V}|${Z.type}`;C.has(R)||(C.add(R),j.push({id:`inflated-${R}`,source:P,target:V,label:Z.type,style:{stroke:"rgba(212,165,116,0.5)",strokeWidth:1.5},labelStyle:{fill:"#a39787",fontSize:10}}))}}for(const I of f.intraContainer){const E=f.nodeToContainer.get(I.source);if(!E||!o.has(E))continue;const w=`intra|${I.source}|${I.target}|${I.type}`;C.has(w)||(C.add(w),j.push({id:w,source:I.source,target:I.target,label:I.type,style:{stroke:"rgba(212,165,116,0.5)",strokeWidth:1.5},labelStyle:{fill:"#a39787",fontSize:10}}))}return j},[f.edges,f.filteredEdges,f.intraContainer,f.nodeToContainer,o]),k=v.useMemo(()=>{const j=[...x,...f.portalEdges];return e?j.map(C=>{var E;const I=C.source===e||C.target===e;return(E=C.style)!=null&&E.strokeDasharray?C:I?{...C,animated:!0,style:{stroke:"rgba(212,165,116,0.8)",strokeWidth:2.5},labelStyle:{fill:"#d4a574",fontSize:11,fontWeight:600}}:{...C,animated:!1,style:{stroke:"rgba(212,165,116,0.08)",strokeWidth:1},labelStyle:{fill:"rgba(163,151,135,0.2)",fontSize:10}}}):j},[x,f.portalEdges,e]),T=v.useMemo(()=>f.containers.map(j=>j.id),[f.containers]);return{nodes:y,edges:k,nodeToContainer:f.nodeToContainer,containerIds:T,layoutStatus:f.layoutStatus}}function fu(){const e=b(z=>z.graph),t=b(z=>z.navigationLevel),n=b(z=>z.activeLayerId),o=b(z=>z.selectNode),r=b(z=>z.drillIntoLayer),s=b(z=>z.focusNodeId),i=b(z=>z.setFocusNode),c=b(z=>z.setReactFlowInstance),d=b(z=>z.tourHighlightedNodeIds),l=b(z=>z.expandContainer),u=b(z=>z.collapseContainer),f=b(z=>z.pendingFocusContainer),h=b(z=>z.setPendingFocusContainer),g=b(z=>z.tourFitPending),{preset:m}=Qo(),p=cu(),N=uu(),{nodes:y,edges:x,nodeToContainer:k,containerIds:T,layoutStatus:j}=t==="overview"?{...p,nodeToContainer:void 0,containerIds:void 0}:N,[C,I,E]=vr(y),[w,M,$]=kr(x),{fitView:D,getViewport:Z,setCenter:P}=Ut();v.useEffect(()=>{I(y)},[y,I]),v.useEffect(()=>{M(x)},[x,M]);const V=v.useRef(!1);v.useEffect(()=>{V.current=!0},[t,n]),v.useEffect(()=>{if(!V.current||C.length===0)return;V.current=!1;const z=requestAnimationFrame(()=>{D({duration:400,padding:.2})});return()=>cancelAnimationFrame(z)},[C,D]),v.useEffect(()=>{var we,ve;if(!f)return;const z=C.find(ze=>ze.id===f);if(!z)return;const J=z.width??((we=z.style)==null?void 0:we.width)??0,S=z.height??((ve=z.style)==null?void 0:ve.height)??0,Y=z.position.x+J/2,G=z.position.y+S/2,{zoom:te}=Z();P(Y,G,{zoom:te,duration:0})},[f,C,Z,P]),v.useEffect(()=>{if(!f)return;const z=window.setTimeout(()=>h(null),1200);return()=>window.clearTimeout(z)},[f,h]),v.useEffect(()=>{if(!s||!k)return;const z=k.get(s);z&&z!==s&&l(z)},[s,k,l]);const R=v.useRef(new Set);v.useEffect(()=>{if(!k)return;const z=new Set;for(const Y of d){const G=k.get(Y);G&&G!==Y&&z.add(G)}const J=new Set;for(const Y of R.current)z.has(Y)?J.add(Y):u(Y);const S=b.getState().expandedContainers;for(const Y of z)S.has(Y)||(l(Y),J.add(Y));R.current=J},[d,k,l,u]);const U=v.useRef(null),A=v.useRef(null),Q=v.useCallback(z=>{z!==null&&(!T||T.length===0||(U.current!==null&&window.clearTimeout(U.current),U.current=window.setTimeout(()=>{const J=Z(),S=A.current;if(A.current=J.zoom,J.zoom<=1||S!==null&&J.zoom<=S)return;const Y=b.getState().expandedContainers;for(const G of T)Y.has(G)||l(G)},200)))},[T,Z,l]);v.useEffect(()=>()=>{U.current!==null&&(window.clearTimeout(U.current),U.current=null)},[Q]);const se=v.useCallback((z,J)=>{if(t==="overview")r(J.id);else if(J.id.startsWith("portal:")){const S=J.id.replace("portal:","");r(S)}else o(J.id)},[t,r,o]),K=v.useCallback(()=>{o(null)},[o]);return e?a.jsxs("div",{className:"h-full w-full relative",children:[a.jsx(Sl,{}),s&&t==="layer-detail"&&a.jsx("div",{className:"absolute top-14 left-1/2 -translate-x-1/2 z-10",children:a.jsxs("button",{onClick:()=>i(null),className:"px-4 py-2 rounded-full bg-elevated border border-gold/30 text-gold text-xs font-semibold tracking-wider uppercase hover:bg-gold/10 transition-colors flex items-center gap-2 shadow-lg",children:[a.jsx("span",{children:"Showing neighborhood"}),a.jsx("span",{className:"text-text-muted",children:"×"})]})}),a.jsxs(Wt,{nodes:C,edges:w,onNodesChange:E,onEdgesChange:$,onNodeClick:se,onPaneClick:K,onMove:t==="layer-detail"?Q:void 0,onInit:c,nodeTypes:ru,nodesDraggable:!1,nodesConnectable:!1,edgesFocusable:!1,edgesReconnectable:!1,elementsSelectable:!1,fitView:!0,fitViewOptions:{minZoom:.01,padding:.1},minZoom:.01,maxZoom:2,colorMode:m.isDark?"dark":"light",children:[a.jsx(Kt,{variant:Jt.Dots,color:"var(--color-edge-dot)",gap:20,size:1}),a.jsx(Yt,{}),a.jsx(Xt,{nodeColor:"var(--color-elevated)",maskColor:"var(--glass-bg)",className:"!bg-surface !border !border-border-subtle"}),a.jsx(au,{}),a.jsx(iu,{})]}),(j==="computing"||g)&&a.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(10,10,10,0.5)",pointerEvents:"none",zIndex:10},children:a.jsx("span",{style:{color:"#d4a574",fontSize:14},children:g?"Locating tour highlight…":"Computing layout…"})})]}):a.jsx("div",{className:"h-full w-full flex items-center justify-center bg-root rounded-lg",children:a.jsx("p",{className:"text-text-muted text-sm",children:"No knowledge graph loaded"})})}function nr(){return a.jsx(Ht,{children:a.jsx(fu,{})})}function pu({data:e}){const t=b(s=>s.navigateToDomain),n=b(s=>s.selectedNodeId),o=b(s=>s.selectNode),r=n===e.domainId;return a.jsxs("div",{className:`rounded-xl border-2 px-5 py-4 min-w-[280px] max-w-[360px] cursor-pointer transition-all ${r?"border-accent bg-accent/10 shadow-lg shadow-accent/10":"border-accent/40 bg-surface hover:border-accent/70"}`,onClick:()=>o(e.domainId),onDoubleClick:()=>t(e.domainId),children:[a.jsx(fe,{type:"target",position:pe.Left,className:"!bg-accent/60 !w-2 !h-2"}),a.jsx(fe,{type:"source",position:pe.Right,className:"!bg-accent/60 !w-2 !h-2"}),a.jsx("div",{className:"font-heading text-sm text-accent font-semibold mb-1 truncate",children:e.label}),a.jsx("div",{className:"text-[11px] text-text-secondary line-clamp-2 mb-2",children:e.summary}),e.entities&&e.entities.length>0&&a.jsxs("div",{className:"mb-2",children:[a.jsx("div",{className:"text-[9px] uppercase tracking-wider text-text-muted mb-1",children:"Entities"}),a.jsxs("div",{className:"flex flex-wrap gap-1",children:[e.entities.slice(0,5).map(s=>a.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-elevated text-text-secondary",children:s},s)),e.entities.length>5&&a.jsxs("span",{className:"text-[10px] text-text-muted",children:["+",e.entities.length-5]})]})]}),a.jsxs("div",{className:"text-[10px] text-text-muted",children:[e.flowCount," flow",e.flowCount!==1?"s":""]})]})}const hu=v.memo(pu);function mu({data:e}){const t=b(r=>r.selectNode),o=b(r=>r.selectedNodeId)===e.flowId;return a.jsxs("div",{className:`rounded-lg border px-4 py-3 min-w-[240px] max-w-[320px] cursor-pointer transition-all ${o?"border-accent bg-accent/10":"border-border-medium bg-surface hover:border-accent/50"}`,onClick:()=>t(e.flowId),children:[a.jsx(fe,{type:"target",position:pe.Left,className:"!bg-accent/60 !w-2 !h-2"}),a.jsx(fe,{type:"source",position:pe.Right,className:"!bg-accent/60 !w-2 !h-2"}),e.entryPoint&&a.jsx("div",{className:"text-[9px] font-mono text-accent/70 mb-1 truncate",children:e.entryPoint}),a.jsx("div",{className:"text-xs font-semibold text-text-primary mb-1 truncate",children:e.label}),a.jsx("div",{className:"text-[10px] text-text-secondary line-clamp-2",children:e.summary}),a.jsxs("div",{className:"text-[9px] text-text-muted mt-1",children:[e.stepCount," step",e.stepCount!==1?"s":""]})]})}const gu=v.memo(mu);function xu({data:e}){const t=b(r=>r.selectNode),o=b(r=>r.selectedNodeId)===e.stepId;return a.jsxs("div",{className:`rounded-lg border px-3 py-2.5 min-w-[180px] max-w-[240px] cursor-pointer transition-all ${o?"border-accent bg-accent/10":"border-border-subtle bg-elevated hover:border-accent/40"}`,onClick:()=>t(e.stepId),children:[a.jsx(fe,{type:"target",position:pe.Left,className:"!bg-text-muted/40 !w-1.5 !h-1.5"}),a.jsx(fe,{type:"source",position:pe.Right,className:"!bg-text-muted/40 !w-1.5 !h-1.5"}),a.jsxs("div",{className:"flex items-center gap-1.5 mb-1",children:[a.jsx("span",{className:"text-[9px] font-mono text-accent/60 shrink-0",children:e.order}),a.jsx("span",{className:"text-[11px] font-medium text-text-primary truncate",children:e.label})]}),a.jsx("div",{className:"text-[10px] text-text-secondary line-clamp-2",children:e.summary}),e.filePath&&a.jsx("div",{className:"text-[9px] font-mono text-text-muted mt-1 truncate",children:e.filePath})]})}const bu=v.memo(xu),yu={"domain-cluster":hu,"flow-node":gu,"step-node":bu};function or(e){return e.domainMeta}function wu(e){const t=new Map,n=e.nodes.filter(i=>i.type==="domain"),o=new Map;for(const i of e.edges)i.type==="contains_flow"&&o.set(i.source,(o.get(i.source)??0)+1);const r=n.map(i=>{const c=or(i),d={label:i.name,summary:i.summary,entities:c==null?void 0:c.entities,flowCount:o.get(i.id)??0,businessRules:c==null?void 0:c.businessRules,domainId:i.id};return t.set(i.id,{width:320,height:180}),{id:i.id,type:"domain-cluster",position:{x:0,y:0},data:d}}),s=e.edges.filter(i=>i.type==="cross_domain").map((i,c)=>({id:`cd-${c}-${i.source}-${i.target}`,source:i.source,target:i.target,label:i.description??"",style:{stroke:"var(--color-accent)",strokeDasharray:"6 3",strokeWidth:2},labelStyle:{fill:"var(--color-text-muted)",fontSize:10},labelBgStyle:{fill:"var(--color-surface)",fillOpacity:.9},labelBgPadding:[6,4],labelBgBorderRadius:4,animated:!0}));return{nodes:r,edges:s,dims:t}}function vu(e,t){const n=new Set(e.edges.filter(m=>m.type==="contains_flow"&&m.source===t).map(m=>m.target)),o=e.nodes.filter(m=>n.has(m.id)),r=e.edges.filter(m=>m.type==="flow_step"&&n.has(m.source)),s=new Set(r.map(m=>m.target)),i=e.nodes.filter(m=>s.has(m.id)),c=new Map;for(const m of r)c.set(m.target,m.weight);const d=new Map;for(const m of r)d.set(m.source,(d.get(m.source)??0)+1);const l=new Map,u=o.map(m=>{const p=or(m);return l.set(m.id,{width:260,height:120}),{id:m.id,type:"flow-node",position:{x:0,y:0},data:{label:m.name,summary:m.summary,entryPoint:p==null?void 0:p.entryPoint,entryType:p==null?void 0:p.entryType,stepCount:d.get(m.id)??0,flowId:m.id}}}),f=i.map(m=>(l.set(m.id,{width:200,height:90}),{id:m.id,type:"step-node",position:{x:0,y:0},data:{label:m.name,summary:m.summary,filePath:m.filePath,stepId:m.id,order:Math.round((c.get(m.id)??0)*10)}})),h=[...u,...f],g=r.map((m,p)=>({id:`fs-${p}-${m.source}-${m.target}`,source:m.source,target:m.target,style:{stroke:"var(--color-border-medium)",strokeWidth:1.5},animated:!1}));return{nodes:h,edges:g,dims:l}}function ku(){const e=b(l=>l.domainGraph),t=b(l=>l.activeDomainId),n=b(l=>l.clearActiveDomain),{t:o}=re(),r=v.useMemo(()=>e?t?vu(e,t):wu(e):null,[e,t]),[s,i]=v.useState({nodes:[],edges:[]});v.useEffect(()=>{if(!r){i({nodes:[],edges:[]});return}let l=!1;const{nodes:u,edges:f,dims:h}=r,g=er(u,f,h,{"elk.direction":"RIGHT"});return yt(g,{strict:!1}).then(({positioned:m,issues:p})=>{l||(p.length>0&&b.getState().appendLayoutIssues(p),i({nodes:cn(u,m),edges:f}))}).catch(m=>{l||console.error("[domain ELK] layout failed:",m)}),()=>{l=!0}},[r]);const{nodes:c,edges:d}=s;return e?a.jsxs("div",{className:"h-full w-full relative",children:[t&&a.jsx("div",{className:"absolute top-3 left-3 z-10",children:a.jsx("button",{type:"button",onClick:()=>n(),className:"px-3 py-1.5 text-xs rounded-lg bg-elevated border border-border-subtle text-text-secondary hover:text-text-primary transition-colors",children:o.domainView.backToDomains})}),a.jsxs(Wt,{nodes:c,edges:d,nodeTypes:yu,fitView:!0,fitViewOptions:{padding:.2},minZoom:.1,maxZoom:2,proOptions:{hideAttribution:!0},children:[a.jsx(Kt,{variant:Jt.Dots,gap:20,size:1,color:"var(--color-border-subtle)"}),a.jsx(Yt,{}),a.jsx(Xt,{nodeColor:"var(--color-accent)",maskColor:"var(--glass-bg)",className:"!bg-surface !border !border-border-subtle"})]})]}):a.jsx("div",{className:"h-full flex items-center justify-center text-text-muted text-sm",children:"No domain graph available. Run /understand-domain to generate one."})}function rr(){return a.jsx(Ht,{children:a.jsx(ku,{})})}const Nu={custom:Wo},Qn={related:{stroke:"var(--color-border-medium)",strokeWidth:.5,opacity:.12},cites:{stroke:"var(--color-node-source)",strokeWidth:1.5,strokeDasharray:"6 3"},contradicts:{stroke:"#c97070",strokeWidth:2},builds_on:{stroke:"var(--color-node-claim)",strokeWidth:1.5},exemplifies:{stroke:"var(--color-node-entity)",strokeWidth:1,strokeDasharray:"3 3"},categorized_under:{stroke:"var(--color-border-medium)",strokeWidth:.5,opacity:.08},authored_by:{stroke:"var(--color-node-entity)",strokeWidth:1,strokeDasharray:"4 4"},implements:{stroke:"var(--color-node-function)",strokeWidth:1,opacity:.4},depends_on:{stroke:"var(--color-node-module)",strokeWidth:1,opacity:.4}};function ju(e){const t=Math.min(1.5,Math.max(.85,.85+e*.03));return{width:Math.round(de*t),height:Math.round(ue*t)}}function _u(e){const t=new Map;for(const d of e.edges)t.set(d.source,(t.get(d.source)??0)+1),t.set(d.target,(t.get(d.target)??0)+1);const n=new Map;e.layers.forEach((d,l)=>{for(const u of d.nodeIds)n.set(u,l)});const o=new Map;for(const d of e.nodes)o.set(d.id,ju(t.get(d.id)??0));const r=e.nodes.map(d=>({id:d.id,type:"custom",position:{x:0,y:0},data:{}})),s=e.edges.map((d,l)=>({id:`ke-${l}`,source:d.source,target:d.target})),{nodes:i}=Zl(r,s,o,n),c=new Map;for(const d of i)c.set(d.id,d.position);return{positionMap:c,edgeCounts:t,communityMap:n}}function Cu(){const e=b(p=>p.graph),t=b(p=>p.selectedNodeId),n=b(p=>p.focusNodeId),o=b(p=>p.selectNode),r=b(p=>p.searchResults),s=b(p=>p.tourHighlightedNodeIds),i=b(p=>p.nodeTypeFilters),c=v.useCallback(p=>o(p),[o]),d=v.useMemo(()=>new Map(r.map(p=>[p.nodeId,p.score])),[r]),l=v.useMemo(()=>new Set(s),[s]),u=v.useMemo(()=>{if(!e)return null;const p=e.nodes.filter(x=>["article","entity","topic","claim","source"].includes(x.type)?i.knowledge!==!1:!0),N=new Set(p.map(x=>x.id)),y=e.edges.filter(x=>N.has(x.source)&&N.has(x.target));return{...e,nodes:p,edges:y}},[e,i]),{positionMap:f,edgeCounts:h}=v.useMemo(()=>u?_u(u):{positionMap:new Map,edgeCounts:new Map},[u]),{nodes:g,edges:m}=v.useMemo(()=>{if(!u)return{nodes:[],edges:[]};const p=new Set;if(n||t){const k=n??t;for(const T of u.edges)T.source===k&&p.add(T.target),T.target===k&&p.add(T.source)}const N=u.nodes.map(k=>{const T=k.id===t,j=k.id===n,C=p.has(k.id),I=(n||t)&&!T&&!j&&!C,E=d.get(k.id),w=E!==void 0,M=l.has(k.id),$={label:k.name,nodeType:k.type,summary:k.summary,complexity:k.complexity,isHighlighted:w,searchScore:E,isSelected:T,isTourHighlighted:M,isDiffChanged:!1,isDiffAffected:!1,isDiffFaded:!1,isNeighbor:C,isSelectionFaded:!!I,onNodeClick:c,incomingCount:h.get(k.id)??0,tags:k.tags};return{id:k.id,type:"custom",position:f.get(k.id)??{x:0,y:0},data:$}}),y=n??t,x=u.edges.map(k=>{const T=Qn[k.type]??Qn.related,j=y&&(k.source===y||k.target===y);let C;return y?j?C={...T,strokeWidth:Math.max(2,(T.strokeWidth??1)*1.5),opacity:1}:C={...T,opacity:.04}:C=T,{id:`ke-${k.source}-${k.target}-${k.type}`,source:k.source,target:k.target,style:C,animated:k.type==="contradicts"&&(!y||!!j),label:j&&k.type!=="related"&&k.type!=="categorized_under"?k.type.replace(/_/g," "):void 0,labelStyle:{fill:"var(--color-text-muted)",fontSize:9,opacity:.7},labelBgStyle:{fill:"var(--color-surface)",fillOpacity:.9},labelBgPadding:[4,2],labelBgBorderRadius:3}});return{nodes:N,edges:x}},[u,t,n,d,l,c,f,h]);return e?a.jsx("div",{className:"h-full w-full relative",children:a.jsxs(Wt,{nodes:g,edges:m,nodeTypes:Nu,fitView:!0,fitViewOptions:{padding:.15},minZoom:.05,maxZoom:2,proOptions:{hideAttribution:!0},children:[a.jsx(Kt,{variant:Jt.Dots,gap:20,size:1,color:"var(--color-border-subtle)"}),a.jsx(Yt,{}),a.jsx(Xt,{nodeColor:p=>{const N=p.data,y=(N==null?void 0:N.nodeType)??"article";return{article:"var(--color-node-article)",entity:"var(--color-node-entity)",topic:"var(--color-node-topic)",claim:"var(--color-node-claim)",source:"var(--color-node-source)"}[y]??"var(--color-accent)"},maskColor:"var(--glass-bg)",className:"!bg-surface !border !border-border-subtle"})]})}):a.jsx("div",{className:"h-full flex items-center justify-center text-text-muted text-sm",children:"No knowledge graph available. Run /understand-knowledge to generate one."})}function sr(){return a.jsx(Ht,{children:a.jsx(Cu,{})})}const eo={file:"text-node-file border border-node-file/30 bg-node-file/10",function:"text-node-function border border-node-function/30 bg-node-function/10",class:"text-node-class border border-node-class/30 bg-node-class/10",module:"text-node-module border border-node-module/30 bg-node-module/10",concept:"text-node-concept border border-node-concept/30 bg-node-concept/10",config:"text-node-config border border-node-config/30 bg-node-config/10",document:"text-node-document border border-node-document/30 bg-node-document/10",service:"text-node-service border border-node-service/30 bg-node-service/10",table:"text-node-table border border-node-table/30 bg-node-table/10",endpoint:"text-node-endpoint border border-node-endpoint/30 bg-node-endpoint/10",pipeline:"text-node-pipeline border border-node-pipeline/30 bg-node-pipeline/10",schema:"text-node-schema border border-node-schema/30 bg-node-schema/10",resource:"text-node-resource border border-node-resource/30 bg-node-resource/10",domain:"text-node-concept border border-node-concept/30 bg-node-concept/10",flow:"text-node-pipeline border border-node-pipeline/30 bg-node-pipeline/10",step:"text-node-function border border-node-function/30 bg-node-function/10",article:"text-node-article border border-node-article/30 bg-node-article/10",entity:"text-node-entity border border-node-entity/30 bg-node-entity/10",topic:"text-node-topic border border-node-topic/30 bg-node-topic/10",claim:"text-node-claim border border-node-claim/30 bg-node-claim/10",source:"text-node-source border border-node-source/30 bg-node-source/10"};function ar(){const e=b(y=>y.searchQuery),t=b(y=>y.searchResults),n=b(y=>y.graph),o=b(y=>y.setSearchQuery),r=b(y=>y.navigateToNodeInLayer),s=b(y=>y.searchMode),i=b(y=>y.setSearchMode),{t:c}=re(),[d,l]=v.useState(!1),u=v.useRef(null),f=v.useRef(null),h=v.useMemo(()=>new Map(((n==null?void 0:n.nodes)??[]).map(y=>[y.id,y])),[n]),g=t.slice(0,5),m=v.useCallback(y=>{o(y.target.value),l(!0)},[o]),p=v.useCallback(y=>{r(y),l(!1)},[r]);v.useEffect(()=>{const y=x=>{var k;x.key==="Escape"&&(l(!1),(k=f.current)==null||k.blur())};return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[]),v.useEffect(()=>{const y=x=>{u.current&&!u.current.contains(x.target)&&l(!1)};return document.addEventListener("mousedown",y),()=>document.removeEventListener("mousedown",y)},[]);const N=d&&e.trim()&&g.length>0;return a.jsxs("div",{ref:u,className:"relative z-30",children:[a.jsxs("div",{className:"flex items-center gap-2 px-3 sm:px-4 py-2 bg-surface border-b border-border-subtle",children:[a.jsx("svg",{className:"w-4 h-4 text-text-muted shrink-0",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"})}),a.jsx("input",{ref:f,type:"text",value:e,onChange:m,onFocus:()=>l(!0),placeholder:c.search.placeholder,"data-testid":"search-input",className:"flex-1 min-w-0 bg-elevated text-text-primary text-sm rounded-lg px-3 py-1.5 border border-border-subtle focus:outline-none focus:border-accent/50 placeholder-text-muted"}),a.jsxs("div",{className:"flex items-center gap-1 bg-elevated rounded-lg p-0.5 shrink-0",children:[a.jsx("button",{onClick:()=>i("fuzzy"),className:`text-[10px] px-1.5 py-0.5 rounded transition-colors ${s==="fuzzy"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:c.search.fuzzy}),a.jsx("button",{onClick:()=>i("semantic"),className:`text-[10px] px-1.5 py-0.5 rounded transition-colors ${s==="semantic"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:c.search.semantic})]}),e.trim()&&a.jsxs("span",{className:"hidden sm:inline text-xs text-text-muted shrink-0",children:[t.length," ",c.search.result,t.length!==1?"s":""," ",a.jsxs("span",{className:"text-text-muted",children:["(",s,")"]})]})]}),N&&a.jsx("div",{className:"absolute left-4 right-4 top-full mt-0.5 glass rounded-lg shadow-xl overflow-hidden",children:g.map(y=>{const x=h.get(y.nodeId);if(!x)return null;const k=Math.round((1-y.score)*100),T=eo[x.type]??eo.file;return a.jsxs("button",{type:"button",onClick:()=>p(y.nodeId),className:"w-full flex items-center gap-3 px-3 py-2 hover:bg-elevated transition-colors text-left",children:[a.jsx("span",{className:`text-[10px] font-semibold uppercase tracking-wider px-1.5 py-0.5 rounded ${T} shrink-0`,children:x.type}),a.jsx("span",{className:"text-sm text-text-primary truncate flex-1",children:x.name}),a.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[a.jsx("div",{className:"w-16 h-1.5 bg-elevated rounded-full overflow-hidden",children:a.jsx("div",{className:"h-full bg-accent rounded-full",style:{width:`${k}%`}})}),a.jsxs("span",{className:"text-[10px] text-text-muted w-7 text-right",children:[k,"%"]})]})]},y.nodeId)})})]})}const it={file:"text-node-file border border-node-file/30 bg-node-file/10",function:"text-node-function border border-node-function/30 bg-node-function/10",class:"text-node-class border border-node-class/30 bg-node-class/10",module:"text-node-module border border-node-module/30 bg-node-module/10",concept:"text-node-concept border border-node-concept/30 bg-node-concept/10",config:"text-node-config border border-node-config/30 bg-node-config/10",document:"text-node-document border border-node-document/30 bg-node-document/10",service:"text-node-service border border-node-service/30 bg-node-service/10",table:"text-node-table border border-node-table/30 bg-node-table/10",endpoint:"text-node-endpoint border border-node-endpoint/30 bg-node-endpoint/10",pipeline:"text-node-pipeline border border-node-pipeline/30 bg-node-pipeline/10",schema:"text-node-schema border border-node-schema/30 bg-node-schema/10",resource:"text-node-resource border border-node-resource/30 bg-node-resource/10",domain:"text-node-concept border border-node-concept/30 bg-node-concept/10",flow:"text-node-pipeline border border-node-pipeline/30 bg-node-pipeline/10",step:"text-node-function border border-node-function/30 bg-node-function/10",article:"text-node-article border border-node-article/30 bg-node-article/10",entity:"text-node-entity border border-node-entity/30 bg-node-entity/10",topic:"text-node-topic border border-node-topic/30 bg-node-topic/10",claim:"text-node-claim border border-node-claim/30 bg-node-claim/10",source:"text-node-source border border-node-source/30 bg-node-source/10"},ct={simple:"text-node-function border border-node-function/30 bg-node-function/10",moderate:"text-accent-dim border border-accent-dim/30 bg-accent-dim/10",complex:"text-[#c97070] border border-[#c97070]/30 bg-[#c97070]/10"};function Su(e,t,n){const o=n.edgeLabels[e];if(!o){const r=e.replace(/_/g," ").replace(/\b\w/g,s=>s.toUpperCase());return t?r:`${r} (reverse)`}return t?o.forward:o.backward}function Iu({node:e,graph:t}){const n=b(l=>l.navigateToNode),{t:o}=re(),r=e.knowledgeMeta,s=t.edges.filter(l=>l.type==="related"&&l.source===e.id).map(l=>t.nodes.find(u=>u.id===l.target)).filter(l=>l!==void 0),i=t.edges.filter(l=>l.type==="related"&&l.target===e.id).map(l=>t.nodes.find(u=>u.id===l.source)).filter(l=>l!==void 0),c=t.edges.find(l=>l.type==="categorized_under"&&l.source===e.id),d=c?t.nodes.find(l=>l.id===c.target):null;return a.jsxs("div",{className:"space-y-3",children:[d&&a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:o.nodeInfo.category}),a.jsx("button",{type:"button",onClick:()=>n(d.id),className:"text-[11px] px-2 py-0.5 rounded bg-elevated text-accent hover:text-accent-bright transition-colors",children:d.name})]}),(r==null?void 0:r.wikilinks)&&r.wikilinks.length>0&&a.jsxs("div",{children:[a.jsxs("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:[o.nodeInfo.wikilinks," (",s.length,")"]}),a.jsx("div",{className:"space-y-1 max-h-[200px] overflow-auto",children:s.map(l=>a.jsx("button",{type:"button",onClick:()=>n(l.id),className:"block w-full text-left px-2 py-1.5 rounded bg-elevated hover:bg-accent/10 text-[11px] text-text-secondary hover:text-accent transition-colors truncate",children:l.name},l.id))})]}),i.length>0&&a.jsxs("div",{children:[a.jsxs("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:[o.nodeInfo.backlinks," (",i.length,")"]}),a.jsx("div",{className:"space-y-1 max-h-[200px] overflow-auto",children:i.map(l=>a.jsx("button",{type:"button",onClick:()=>n(l.id),className:"block w-full text-left px-2 py-1.5 rounded bg-elevated hover:bg-accent/10 text-[11px] text-text-secondary hover:text-accent transition-colors truncate",children:l.name},l.id))})]}),(r==null?void 0:r.content)&&a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:o.common.preview}),a.jsx("div",{className:"text-[11px] text-text-secondary leading-relaxed bg-elevated rounded-lg p-3 max-h-[520px] overflow-auto whitespace-pre-wrap font-mono",children:r.content})]})]})}function Tu({node:e,graph:t}){const n=b(i=>i.navigateToDomain),o=b(i=>i.selectNode),{t:r}=re(),s=e.domainMeta;if(e.type==="domain"){const i=t.edges.filter(c=>c.type==="contains_flow"&&c.source===e.id).map(c=>t.nodes.find(d=>d.id===c.target)).filter(c=>c!==void 0);return a.jsxs("div",{className:"space-y-3",children:[Array.isArray(s==null?void 0:s.entities)&&s.entities.length>0?a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.entities}),a.jsx("div",{className:"flex flex-wrap gap-1",children:s.entities.map(c=>a.jsx("span",{className:"text-[11px] px-2 py-0.5 rounded bg-elevated text-text-secondary",children:c},c))})]}):null,Array.isArray(s==null?void 0:s.businessRules)&&s.businessRules.length>0?a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.businessRules}),a.jsx("ul",{className:"text-[11px] text-text-secondary space-y-1",children:s.businessRules.map((c,d)=>a.jsxs("li",{className:"flex gap-1.5",children:[a.jsx("span",{className:"text-accent shrink-0",children:"-"}),c]},d))})]}):null,Array.isArray(s==null?void 0:s.crossDomainInteractions)&&s.crossDomainInteractions.length>0?a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.crossDomain}),a.jsx("ul",{className:"text-[11px] text-text-secondary space-y-1",children:s.crossDomainInteractions.map((c,d)=>a.jsx("li",{children:c},d))})]}):null,i.length>0&&a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.flows}),a.jsx("div",{className:"space-y-1",children:i.map(c=>a.jsx("button",{type:"button",onClick:()=>{n(e.id),o(c.id)},className:"block w-full text-left px-2 py-1.5 rounded bg-elevated hover:bg-accent/10 text-[11px] text-text-secondary hover:text-accent transition-colors",children:c.name},c.id))})]})]})}if(e.type==="flow"){const i=t.edges.filter(c=>c.type==="flow_step"&&c.source===e.id).sort((c,d)=>c.weight-d.weight).map(c=>t.nodes.find(d=>d.id===c.target)).filter(c=>c!==void 0);return a.jsxs("div",{className:"space-y-3",children:[s!=null&&s.entryPoint?a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.entryPoint}),a.jsx("div",{className:"text-[11px] font-mono text-accent",children:s.entryPoint})]}):null,i.length>0&&a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.steps}),a.jsx("ol",{className:"space-y-1",children:i.map((c,d)=>a.jsx("li",{children:a.jsxs("button",{type:"button",onClick:()=>o(c.id),className:"block w-full text-left px-2 py-1.5 rounded bg-elevated hover:bg-accent/10 text-[11px] transition-colors",children:[a.jsxs("span",{className:"text-accent/60 mr-1.5",children:[d+1,"."]}),a.jsx("span",{className:"text-text-secondary hover:text-accent",children:c.name})]})},c.id))})]})]})}return e.type==="step"&&e.filePath?a.jsx("div",{className:"space-y-3",children:a.jsxs("div",{children:[a.jsx("h4",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1",children:r.nodeInfo.implementation}),a.jsxs("div",{className:"text-[11px] font-mono text-text-secondary",children:[e.filePath,e.lineRange&&a.jsxs("span",{className:"text-text-muted",children:[":",e.lineRange[0],"-",e.lineRange[1]]})]})]})}):null}function ir(){const e=b(w=>w.graph),t=b(w=>w.selectedNodeId),n=b(w=>w.nodeHistory),o=b(w=>w.goBackNode),[r,s]=v.useState(!0),{t:i}=re(),c=b(w=>w.navigateToNode),d=b(w=>w.navigateToHistoryIndex),l=b(w=>w.setFocusNode),u=b(w=>w.openCodeViewer),f=b(w=>w.focusNodeId),h=b(w=>w.viewMode),g=b(w=>w.domainGraph),m=h==="domain"&&g?g:e,p=(m==null?void 0:m.nodes.find(w=>w.id===t))??null,N=n.map(w=>{const M=m==null?void 0:m.nodes.find($=>$.id===w);return{id:w,name:(M==null?void 0:M.name)??w}});if(!p)return a.jsx("div",{className:"h-full w-full flex items-center justify-center bg-surface",children:a.jsx("p",{className:"text-text-muted text-sm",children:i.common.selectNode})});const x=((m==null?void 0:m.edges)??[]).filter(w=>w.source===p.id||w.target===p.id),k=x.filter(w=>w.type==="contains"&&w.source===p.id),T=x.filter(w=>!(w.type==="contains"&&w.source===p.id)),j=k.map(w=>m==null?void 0:m.nodes.find(M=>M.id===w.target)).filter(w=>w!==void 0),C=p.type,I=it[C]??it.file,E=ct[p.complexity]??ct.simple;return a.jsxs("div",{className:"h-full w-full overflow-auto p-5 animate-fade-slide-in",children:[N.length>0&&a.jsxs("div",{className:"mb-3 flex items-center gap-1 flex-wrap",children:[a.jsxs("button",{onClick:o,className:"text-[10px] font-semibold text-gold hover:text-gold-bright transition-colors flex items-center gap-1",children:[a.jsx("span",{children:"←"}),a.jsx("span",{children:i.common.back})]}),a.jsx("span",{className:"text-text-muted text-[10px]",children:"│"}),N.slice(-3).map((w,M,$)=>a.jsxs("span",{className:"flex items-center gap-1",children:[a.jsx("button",{onClick:()=>{const D=N.length-$.length+M;d(D)},className:"text-[10px] text-text-muted hover:text-gold transition-colors truncate max-w-[80px]",title:w.name,children:w.name}),M<$.length-1&&a.jsx("span",{className:"text-text-muted text-[10px]",children:"›"})]},`${w.id}-${M}`)),a.jsx("span",{className:"text-text-muted text-[10px]",children:"›"}),a.jsx("span",{className:"text-[10px] text-text-primary font-medium truncate max-w-[80px]",children:p.name})]}),a.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[a.jsx("span",{className:`text-[10px] font-semibold uppercase tracking-wider px-2 py-0.5 rounded ${I}`,children:p.type}),a.jsx("span",{className:`text-[10px] font-semibold px-2 py-0.5 rounded ${E}`,children:p.complexity})]}),a.jsxs("div",{className:"flex items-center justify-between mb-2",children:[a.jsx("h2",{className:"text-lg font-heading text-text-primary",children:p.name}),a.jsx("button",{onClick:()=>l(f===p.id?null:p.id),className:`text-[10px] font-semibold uppercase tracking-wider px-2.5 py-1 rounded transition-colors ${f===p.id?"bg-gold/20 text-gold border border-gold/40":"text-text-muted border border-border-subtle hover:text-gold hover:border-gold/30"}`,children:f===p.id?i.common.unfocus:i.common.focus})]}),a.jsx("p",{className:"text-sm text-text-secondary mb-4 leading-relaxed",children:p.summary}),p.filePath&&a.jsx("div",{className:"text-xs text-text-secondary mb-4 rounded-lg border border-border-subtle bg-elevated/60 p-3",children:a.jsxs("div",{className:"flex items-start justify-between gap-3",children:[a.jsxs("div",{className:"min-w-0",children:[a.jsx("div",{className:"font-medium text-text-muted mb-1",children:i.common.file}),a.jsxs("div",{className:"font-mono truncate",title:p.filePath,children:[p.filePath,p.lineRange&&a.jsxs("span",{className:"ml-2 text-text-muted",children:["L",p.lineRange[0],"-",p.lineRange[1]]})]})]}),a.jsx("button",{type:"button",onClick:()=>u(p.id),className:"shrink-0 text-[10px] font-semibold uppercase tracking-wider px-2.5 py-1 rounded border border-accent/30 text-accent hover:text-accent-bright hover:border-accent/60 transition-colors",children:"查看文档"})]})}),p.languageNotes&&a.jsxs("div",{className:"mb-4",children:[a.jsxs("button",{onClick:()=>s(!r),className:"flex items-center gap-1.5 text-xs font-semibold text-accent uppercase tracking-wider mb-2 hover:text-accent-bright transition-colors",children:[a.jsx("svg",{className:`w-3 h-3 transition-transform ${r?"rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),i.nodeInfo.languageConcepts]}),r&&a.jsx("div",{className:"bg-accent/5 border border-accent/20 rounded-lg p-3",children:a.jsx("p",{className:"text-sm text-text-secondary leading-relaxed",children:p.languageNotes})})]}),p.tags.length>0&&a.jsxs("div",{className:"mb-4",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:i.common.tags}),a.jsx("div",{className:"flex flex-wrap gap-1.5",children:p.tags.map(w=>a.jsx("span",{className:"text-[11px] glass text-text-secondary px-2.5 py-1 rounded-full",children:w},w))})]}),m&&p&&(p.type==="article"||p.type==="entity"||p.type==="topic"||p.type==="claim"||p.type==="source"||p.knowledgeMeta)&&a.jsx(Iu,{node:p,graph:m}),m&&p&&(p.type==="domain"||p.type==="flow"||p.type==="step")&&a.jsx(Tu,{node:p,graph:m}),j.length>0&&a.jsxs("div",{className:"mb-4",children:[a.jsxs("h3",{className:"text-[11px] font-semibold text-gold uppercase tracking-wider mb-2",children:[i.nodeInfo.definedInThisFile," (",j.length,")"]}),a.jsx("div",{className:"space-y-1",children:j.map(w=>{if(!w)return null;const M=it[w.type]??it.file,$=ct[w.complexity]??ct.simple;return a.jsxs("div",{className:"text-xs bg-elevated rounded-lg px-3 py-2 border border-border-subtle cursor-pointer hover:border-gold/40 hover:bg-gold/5 transition-colors",onClick:()=>c(w.id),children:[a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:`text-[9px] font-semibold uppercase tracking-wider px-1.5 py-0.5 rounded ${M}`,children:w.type}),a.jsx("span",{className:"text-text-primary truncate",children:w.name}),a.jsx("span",{className:`text-[9px] ml-auto ${$} px-1 py-0.5 rounded`,children:w.complexity})]}),w.summary&&a.jsx("p",{className:"text-[11px] text-text-muted mt-1 line-clamp-1 pl-1",children:w.summary})]},w.id)})})]}),T.length>0&&a.jsxs("div",{children:[a.jsxs("h3",{className:"text-[11px] font-semibold text-gold uppercase tracking-wider mb-2",children:[i.common.connections," (",T.length,")"]}),a.jsx("div",{className:"space-y-1.5",children:T.map((w,M)=>{const $=w.source===p.id,D=$?w.target:w.source,Z=m==null?void 0:m.nodes.find(R=>R.id===D),P=Su(w.type,$,i),V=$?"→":"←";return a.jsxs("div",{className:"text-xs bg-elevated rounded-lg px-3 py-2 border border-border-subtle flex items-center gap-2 cursor-pointer hover:border-gold/40 hover:bg-gold/5 transition-colors",onClick:()=>{c(D)},children:[a.jsx("span",{className:"text-gold font-mono",children:V}),a.jsx("span",{className:"text-text-muted",children:P}),a.jsx("span",{className:"text-text-primary truncate",children:(Z==null?void 0:Z.name)??D})]},M)})})]})]})}function cr(){const e=b(i=>i.diffMode),t=b(i=>i.toggleDiffMode),n=b(i=>i.changedNodeIds),o=b(i=>i.affectedNodeIds),{t:r}=re(),s=n.size>0;return a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsxs("button",{onClick:t,disabled:!s,className:`px-2 py-0.5 rounded text-[11px] font-medium transition-colors ${e&&s?"bg-[var(--color-diff-changed-dim)] text-[var(--color-diff-changed)]":s?"bg-elevated text-text-secondary hover:bg-surface":"bg-elevated text-text-muted cursor-not-allowed"}`,title:s?e?r.diffToggle.hideOverlay:r.diffToggle.showOverlay:r.diffToggle.noData,children:["Diff ",e&&s?"ON":"OFF"]}),e&&s&&a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsxs("div",{className:"flex items-center gap-1",children:[a.jsx("span",{className:"inline-block w-2 h-2 rounded-full",style:{backgroundColor:"var(--color-diff-changed)"}}),a.jsxs("span",{className:"text-text-secondary text-[11px]",children:[r.diffToggle.changed,a.jsxs("span",{className:"text-text-muted ml-0.5",children:["(",n.size,")"]})]})]}),a.jsxs("div",{className:"flex items-center gap-1",children:[a.jsx("span",{className:"inline-block w-2 h-2 rounded-full",style:{backgroundColor:"var(--color-diff-affected)"}}),a.jsxs("span",{className:"text-text-secondary text-[11px]",children:[r.diffToggle.affected,a.jsxs("span",{className:"text-text-muted ml-0.5",children:["(",o.size,")"]})]})]})]})]})}function dr(){const e=b(x=>x.graph),t=b(x=>x.filters),n=b(x=>x.setFilters),o=b(x=>x.resetFilters),r=b(x=>x.hasActiveFilters),s=b(x=>x.filterPanelOpen),i=b(x=>x.toggleFilterPanel),{t:c}=re(),d=v.useRef(null),l=gt,u=xt,f=bt,h=(e==null?void 0:e.layers)??[];v.useEffect(()=>{const x=k=>{d.current&&!d.current.contains(k.target)&&s&&i()};return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[s,i]);const g=x=>{const k=new Set(t.nodeTypes);k.has(x)?k.delete(x):k.add(x),n({nodeTypes:k})},m=x=>{const k=new Set(t.complexities);k.has(x)?k.delete(x):k.add(x),n({complexities:k})},p=x=>{const k=new Set(t.layerIds);k.has(x)?k.delete(x):k.add(x),n({layerIds:k})},N=x=>{const k=new Set(t.edgeCategories);k.has(x)?k.delete(x):k.add(x),n({edgeCategories:k})},y=r();return a.jsxs("div",{ref:d,className:"relative",children:[a.jsxs("button",{onClick:i,className:`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm transition-colors ${y?"bg-gold/20 text-gold hover:bg-gold/30":"bg-elevated text-text-secondary hover:text-text-primary"}`,title:"Filter graph (F)",children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"})}),c.common.filter]}),s&&a.jsx("div",{className:"absolute right-0 top-full mt-2 w-72 glass rounded-lg shadow-xl overflow-hidden animate-fade-slide-in z-50",children:a.jsxs("div",{className:"p-4 space-y-4",children:[a.jsxs("div",{children:[a.jsx("h3",{className:"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2",children:c.filterPanel.nodeTypes}),a.jsx("div",{className:"space-y-1.5",children:l.map(x=>a.jsxs("label",{className:"flex items-center gap-2 cursor-pointer hover:bg-elevated/50 rounded px-2 py-1 transition-colors",children:[a.jsx("input",{type:"checkbox",checked:t.nodeTypes.has(x),onChange:()=>g(x),className:"w-3.5 h-3.5 rounded border-border-subtle bg-elevated checked:bg-gold checked:border-gold focus:ring-0 focus:ring-offset-0 cursor-pointer"}),a.jsx("span",{className:"text-sm text-text-primary capitalize",children:x})]},x))})]}),a.jsxs("div",{children:[a.jsx("h3",{className:"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2",children:c.filterPanel.complexity}),a.jsx("div",{className:"space-y-1.5",children:u.map(x=>a.jsxs("label",{className:"flex items-center gap-2 cursor-pointer hover:bg-elevated/50 rounded px-2 py-1 transition-colors",children:[a.jsx("input",{type:"checkbox",checked:t.complexities.has(x),onChange:()=>m(x),className:"w-3.5 h-3.5 rounded border-border-subtle bg-elevated checked:bg-gold checked:border-gold focus:ring-0 focus:ring-offset-0 cursor-pointer"}),a.jsx("span",{className:"text-sm text-text-primary capitalize",children:x})]},x))})]}),h.length>0&&a.jsxs("div",{children:[a.jsx("h3",{className:"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2",children:c.filterPanel.layers}),a.jsx("div",{className:"space-y-1.5",children:h.map(x=>a.jsxs("label",{className:"flex items-center gap-2 cursor-pointer hover:bg-elevated/50 rounded px-2 py-1 transition-colors",children:[a.jsx("input",{type:"checkbox",checked:t.layerIds.has(x.id),onChange:()=>p(x.id),className:"w-3.5 h-3.5 rounded border-border-subtle bg-elevated checked:bg-gold checked:border-gold focus:ring-0 focus:ring-offset-0 cursor-pointer"}),a.jsx("div",{className:"w-2 h-2 rounded-full bg-gold/50 shrink-0"}),a.jsx("span",{className:"text-sm text-text-primary",children:x.name})]},x.id))})]}),a.jsxs("div",{children:[a.jsx("h3",{className:"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2",children:c.filterPanel.edgeCategories}),a.jsx("div",{className:"space-y-1.5",children:f.map(x=>a.jsxs("label",{className:"flex items-center gap-2 cursor-pointer hover:bg-elevated/50 rounded px-2 py-1 transition-colors",children:[a.jsx("input",{type:"checkbox",checked:t.edgeCategories.has(x),onChange:()=>N(x),className:"w-3.5 h-3.5 rounded border-border-subtle bg-elevated checked:bg-gold checked:border-gold focus:ring-0 focus:ring-offset-0 cursor-pointer"}),a.jsx("span",{className:"text-sm text-text-primary capitalize",children:x.replace(/-/g," ")})]},x))})]}),y&&a.jsx("button",{onClick:o,className:"w-full px-3 py-1.5 text-sm bg-elevated hover:bg-gold/20 text-text-secondary hover:text-gold rounded-lg transition-colors",children:c.common.resetAll})]})})]})}function Eu(e,t,n){const o=n.layerIds.size>0;return e.filter(r=>{if(!n.nodeTypes.has(r.type)||r.complexity&&!n.complexities.has(r.complexity))return!1;if(o){const s=t.get(r.id);if(!s)return!1;let i=!1;for(const c of s)if(n.layerIds.has(c)){i=!0;break}if(!i)return!1}return!0})}function $u(e,t,n){return e.filter(o=>{if(!t.has(o.source)||!t.has(o.target))return!1;const r=Lu(o.type);return!(r&&!n.edgeCategories.has(r))})}function Lu(e){for(const[t,n]of Object.entries(ll))if(n.includes(e))return t;return null}function zu(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function $t(e,t){const n=URL.createObjectURL(e),o=document.createElement("a");o.href=n,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n)}function lr(){const e=b(g=>g.graph),t=b(g=>g.nodeIdToLayerIds),n=b(g=>g.filters),o=b(g=>g.exportMenuOpen),r=b(g=>g.toggleExportMenu),s=b(g=>g.reactFlowInstance),i=b(g=>g.persona),{t:c}=re(),d=v.useRef(null);v.useEffect(()=>{const g=m=>{d.current&&!d.current.contains(m.target)&&o&&r()};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[o,r]);const l=()=>{if(!s)return null;const g=s.getNodes(),m=s.getEdges();if(g.length===0)return null;let p=1/0,N=1/0,y=-1/0,x=-1/0;g.forEach(w=>{const M=w.position.x,$=w.position.y,D=w.width??200,Z=w.height??80;p=Math.min(p,M),N=Math.min(N,$),y=Math.max(y,M+D),x=Math.max(x,$+Z)});const k=40,T=y-p+k*2,j=x-N+k*2,C=-p+k,I=-N+k;let E=``;return E+='',m.forEach(w=>{const M=g.find(R=>R.id===w.source),$=g.find(R=>R.id===w.target);if(!M||!$)return;const D=M.position.x+(M.width??200)/2+C,Z=M.position.y+(M.height??80)/2+I,P=$.position.x+($.width??200)/2+C,V=$.position.y+($.height??80)/2+I;E+=``}),g.forEach(w=>{if(w.type==="group")return;const M=w.position.x+C,$=w.position.y+I,D=w.width??200,Z=w.height??80;E+=``,E+=`${zu(String(w.data.label??w.id))}`}),E+="",{svgContent:E,width:T,height:j}},u=async()=>{if(!s){alert("Graph not ready for export");return}try{const g=l();if(!g){alert("No nodes to export");return}const{svgContent:m,width:p,height:N}=g,y=new Blob([m],{type:"image/svg+xml;charset=utf-8"}),x=URL.createObjectURL(y),k=new Image;k.onerror=()=>{URL.revokeObjectURL(x),alert("Failed to export PNG: could not render graph as image.")},k.onload=()=>{const T=document.createElement("canvas");T.width=p*2,T.height=N*2;const j=T.getContext("2d");if(!j){URL.revokeObjectURL(x),alert("Failed to create canvas context");return}j.drawImage(k,0,0,p*2,N*2),URL.revokeObjectURL(x);const C=`${(e==null?void 0:e.project.name)??"knowledge-graph"}-export.png`;T.toBlob(I=>{I?($t(I,C),r()):alert("Failed to export PNG: image encoding failed.")},"image/png")},k.src=x}catch(g){console.error("PNG export failed:",g),alert(`Failed to export PNG: ${g instanceof Error?g.message:String(g)}`)}},f=()=>{if(!s){alert("Graph not ready for export");return}try{const g=l();if(!g){alert("No nodes to export");return}const m=new Blob([g.svgContent],{type:"image/svg+xml;charset=utf-8"}),p=`${(e==null?void 0:e.project.name)??"knowledge-graph"}-export.svg`;$t(m,p),r()}catch(g){console.error("SVG export failed:",g),alert(`Failed to export SVG: ${g instanceof Error?g.message:String(g)}`)}},h=()=>{if(!e){alert("No graph loaded");return}try{const g=new Set(["function","class"]);let m=i==="non-technical"?e.nodes.filter(j=>!g.has(j.type)):e.nodes;m=Eu(m,t,n);const p=new Set(m.map(j=>j.id));let N=e.edges.filter(j=>p.has(j.source)&&p.has(j.target));N=$u(N,p,n);const y={...e,nodes:m,edges:N},x=JSON.stringify(y,null,2),k=new Blob([x],{type:"application/json"}),T=`${e.project.name??"knowledge-graph"}-export.json`;$t(k,T),r()}catch(g){console.error("JSON export failed:",g),alert(`Failed to export JSON: ${g instanceof Error?g.message:String(g)}`)}};return a.jsxs("div",{ref:d,className:"relative",children:[a.jsxs("button",{onClick:r,className:"flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm bg-elevated text-text-secondary hover:text-text-primary transition-colors",title:c.export.title,children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"})}),c.export.label]}),o&&a.jsx("div",{className:"absolute right-0 top-full mt-2 w-52 glass rounded-lg shadow-xl overflow-hidden animate-fade-slide-in z-50",children:a.jsxs("div",{className:"p-2",children:[a.jsxs("button",{onClick:u,disabled:!s,className:"w-full flex items-center gap-3 px-3 py-2 text-sm text-text-primary hover:bg-elevated transition-colors rounded-lg text-left disabled:opacity-50 disabled:cursor-not-allowed",children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"})}),a.jsx("span",{children:c.export.asPNG})]}),a.jsxs("button",{onClick:f,disabled:!s,className:"w-full flex items-center gap-3 px-3 py-2 text-sm text-text-primary hover:bg-elevated transition-colors rounded-lg text-left disabled:opacity-50 disabled:cursor-not-allowed",children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"})}),a.jsx("span",{children:c.export.asSVG})]}),a.jsxs("button",{onClick:h,disabled:!e,className:"w-full flex items-center gap-3 px-3 py-2 text-sm text-text-primary hover:bg-elevated transition-colors rounded-lg text-left disabled:opacity-50 disabled:cursor-not-allowed",children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"})}),a.jsx("span",{children:c.export.asJSON})]})]})})]})}function ur(){const e=b(r=>r.persona),t=b(r=>r.setPersona),{t:n}=re(),o=[{id:"non-technical",label:n.personaSelector.overview,description:n.personaSelector.overviewDesc},{id:"junior",label:n.personaSelector.learn,description:n.personaSelector.learnDesc},{id:"experienced",label:n.personaSelector.deepDive,description:n.personaSelector.deepDiveDesc}];return a.jsx("div",{className:"flex items-center gap-1 bg-elevated rounded-lg p-0.5",children:o.map(r=>a.jsx("button",{onClick:()=>t(r.id),title:r.description,className:`px-2.5 py-1 rounded text-[11px] font-medium transition-colors ${e===r.id?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary hover:bg-surface"}`,children:r.label},r.id))})}function fr(){const e=b(p=>p.graph),t=b(p=>p.startTour),{t:n}=re();if(!e)return a.jsx("div",{className:"h-full w-full flex items-center justify-center",children:a.jsx("p",{className:"text-text-muted text-sm",children:n.common.loading})});const{project:o,nodes:r,edges:s,layers:i}=e,c=e.tour.length>0,d={};for(const p of r)d[p.type]=(d[p.type]??0)+1;const l={simple:0,moderate:0,complex:0};for(const p of r)p.complexity&&(l[p.complexity]=(l[p.complexity]??0)+1);const u=new Map;for(const p of s)u.set(p.source,(u.get(p.source)??0)+1),u.set(p.target,(u.get(p.target)??0)+1);const f=Array.from(u.entries()).sort((p,N)=>N[1]-p[1]).slice(0,5).map(([p,N])=>{const y=r.find(x=>x.id===p);return{id:p,name:(y==null?void 0:y.name)??p,count:N}}),h=r.length>0?(s.length*2/r.length).toFixed(1):"0",g=[{label:n.projectOverview.code,color:"var(--color-node-file)",count:(d.file??0)+(d.function??0)+(d.class??0)+(d.module??0)+(d.concept??0)},{label:n.projectOverview.config,color:"var(--color-node-config)",count:d.config??0},{label:n.projectOverview.docs,color:"var(--color-node-document)",count:d.document??0},{label:n.projectOverview.infra,color:"var(--color-node-service)",count:(d.service??0)+(d.resource??0)+(d.pipeline??0)},{label:n.projectOverview.data,color:"var(--color-node-table)",count:(d.table??0)+(d.endpoint??0)+(d.schema??0)},{label:n.projectOverview.domain,color:"var(--color-node-concept)",count:(d.domain??0)+(d.flow??0)+(d.step??0)}],m=g.some(p=>p.label!==n.projectOverview.code&&p.count>0);return a.jsxs("div",{className:"h-full w-full overflow-auto p-5 animate-fade-slide-in",children:[a.jsx("h2",{className:"font-heading text-2xl text-text-primary mb-1",children:o.name}),a.jsx("p",{className:"text-sm text-text-secondary leading-relaxed mb-6",children:o.description}),a.jsxs("div",{className:"grid grid-cols-2 gap-3 mb-6",children:[a.jsxs("div",{className:"bg-elevated rounded-lg p-3 border border-border-subtle",children:[a.jsx("div",{className:"text-2xl font-mono font-medium text-accent",children:r.length}),a.jsx("div",{className:"text-[11px] text-text-muted uppercase tracking-wider mt-1",children:n.projectOverview.nodes})]}),a.jsxs("div",{className:"bg-elevated rounded-lg p-3 border border-border-subtle",children:[a.jsx("div",{className:"text-2xl font-mono font-medium text-accent",children:s.length}),a.jsx("div",{className:"text-[11px] text-text-muted uppercase tracking-wider mt-1",children:n.projectOverview.edges})]}),a.jsxs("div",{className:"bg-elevated rounded-lg p-3 border border-border-subtle",children:[a.jsx("div",{className:"text-2xl font-mono font-medium text-accent",children:i.length}),a.jsx("div",{className:"text-[11px] text-text-muted uppercase tracking-wider mt-1",children:n.projectOverview.layers})]}),a.jsxs("div",{className:"bg-elevated rounded-lg p-3 border border-border-subtle",children:[a.jsx("div",{className:"text-2xl font-mono font-medium text-accent",children:Object.keys(d).length}),a.jsx("div",{className:"text-[11px] text-text-muted uppercase tracking-wider mt-1",children:n.projectOverview.types})]})]}),m&&a.jsxs("div",{className:"mb-5",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:n.projectOverview.fileTypes}),a.jsx("div",{className:"space-y-1.5",children:g.filter(p=>p.count>0).map(p=>a.jsxs("div",{className:"flex items-center gap-2",children:[a.jsx("span",{className:"w-2.5 h-2.5 rounded-full shrink-0",style:{backgroundColor:p.color}}),a.jsx("span",{className:"text-xs text-text-secondary flex-1",children:p.label}),a.jsx("span",{className:"text-xs font-mono text-text-muted",children:p.count})]},p.label))})]}),o.languages.length>0&&a.jsxs("div",{className:"mb-5",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:n.projectOverview.languages}),a.jsx("div",{className:"flex flex-wrap gap-1.5",children:o.languages.map(p=>a.jsx("span",{className:"text-[11px] glass text-text-secondary px-2.5 py-1 rounded-full",children:p},p))})]}),o.frameworks.length>0&&a.jsxs("div",{className:"mb-5",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-2",children:n.projectOverview.frameworks}),a.jsx("div",{className:"flex flex-wrap gap-1.5",children:o.frameworks.map(p=>a.jsx("span",{className:"text-[11px] glass text-text-secondary px-2.5 py-1 rounded-full",children:p},p))})]}),a.jsxs("div",{className:"mb-5",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-3",children:n.projectOverview.nodeTypeDistribution}),a.jsx("div",{className:"space-y-2",children:Object.entries(d).sort((p,N)=>N[1]-p[1]).map(([p,N])=>{const y=(N/r.length*100).toFixed(0);return a.jsxs("div",{children:[a.jsxs("div",{className:"flex items-center justify-between text-xs mb-1",children:[a.jsx("span",{className:"text-text-secondary capitalize",children:p}),a.jsxs("span",{className:"text-text-muted font-mono",children:[N," (",y,"%)"]})]}),a.jsx("div",{className:"w-full h-1.5 bg-elevated rounded-full overflow-hidden",children:a.jsx("div",{className:"h-full bg-accent/50 rounded-full transition-all duration-500",style:{width:`${y}%`}})})]},p)})})]}),Object.values(l).some(p=>p>0)&&a.jsxs("div",{className:"mb-5",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-3",children:n.projectOverview.complexityDistribution}),a.jsxs("div",{className:"grid grid-cols-3 gap-2",children:[a.jsxs("div",{className:"bg-elevated rounded-lg p-2 border border-border-subtle text-center",children:[a.jsx("div",{className:"text-lg font-mono font-medium text-green-400",children:l.simple}),a.jsx("div",{className:"text-[10px] text-text-muted uppercase tracking-wider mt-0.5",children:n.projectOverview.simple})]}),a.jsxs("div",{className:"bg-elevated rounded-lg p-2 border border-border-subtle text-center",children:[a.jsx("div",{className:"text-lg font-mono font-medium text-yellow-400",children:l.moderate}),a.jsx("div",{className:"text-[10px] text-text-muted uppercase tracking-wider mt-0.5",children:n.projectOverview.moderate})]}),a.jsxs("div",{className:"bg-elevated rounded-lg p-2 border border-border-subtle text-center",children:[a.jsx("div",{className:"text-lg font-mono font-medium text-red-400",children:l.complex}),a.jsx("div",{className:"text-[10px] text-text-muted uppercase tracking-wider mt-0.5",children:n.projectOverview.complex})]})]})]}),f.length>0&&a.jsxs("div",{className:"mb-5",children:[a.jsx("h3",{className:"text-[11px] font-semibold text-accent uppercase tracking-wider mb-3",children:n.projectOverview.mostConnectedNodes}),a.jsx("div",{className:"space-y-2",children:f.map((p,N)=>a.jsxs("div",{className:"flex items-center gap-2 text-xs bg-elevated rounded-lg p-2 border border-border-subtle",children:[a.jsx("div",{className:"w-5 h-5 shrink-0 rounded-full bg-accent/20 flex items-center justify-center text-[10px] font-bold text-accent",children:N+1}),a.jsx("span",{className:"flex-1 text-text-primary truncate",children:p.name}),a.jsx("span",{className:"text-text-muted font-mono shrink-0",children:p.count})]},p.id))})]}),a.jsx("div",{className:"mb-5 bg-elevated rounded-lg p-3 border border-border-subtle",children:a.jsxs("div",{className:"flex items-center justify-between",children:[a.jsx("span",{className:"text-xs text-text-secondary",children:n.projectOverview.avgConnectionsPerNode}),a.jsx("span",{className:"text-lg font-mono font-medium text-accent",children:h})]})}),a.jsxs("div",{className:"text-[11px] text-text-muted mb-6",children:[n.common.analyzed,": ",new Date(o.analyzedAt).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"})]}),c&&a.jsx("button",{onClick:t,className:"w-full bg-accent/10 border border-accent/30 text-accent text-sm font-medium py-2.5 px-4 rounded-lg hover:bg-accent/20 transition-all duration-200",children:n.common.startGuidedTour})]})}function Au(e){const t=e.replace(/\\/g,"/").replace(/^\/+/,"").replace(/^\.\//,"");return!t||t==="."||t.includes("\0")||t.split("/").some(n=>n==="..")?null:t}function Ou(e,t){return!e||e.type!=="file"&&t.type==="file"?t:e}function Mu(e,t){const n=new Map;for(const i of e){if(t&&!t.has(i.id)||!i.filePath)continue;const c=Au(i.filePath);c&&n.set(c,Ou(n.get(c),i))}const o={name:"",path:"",type:"folder",children:[]},r=new Map([["",o]]);for(const[i,c]of n){const d=i.split("/");let l=o,u="";for(let f=0;fi.sort((c,d)=>c.type!==d.type?c.type==="folder"?-1:1:c.name.localeCompare(d.name)).map(c=>({...c,children:s(c.children)}));return s(o.children)}function pr({entry:e,depth:t,expanded:n,toggleFolder:o,openFile:r}){const s=n.has(e.path),i=12+t*14;return e.type==="folder"?a.jsxs(a.Fragment,{children:[a.jsxs("button",{type:"button",onClick:()=>o(e.path),className:"w-full flex items-center gap-1.5 py-1.5 pr-3 text-left text-xs text-text-secondary hover:text-text-primary hover:bg-elevated transition-colors",style:{paddingLeft:i},title:e.path,children:[a.jsx("span",{className:"w-3 text-text-muted",children:s?"v":">"}),a.jsx("span",{className:"truncate font-medium",children:e.name})]}),s&&e.children.map(c=>a.jsx(pr,{entry:c,depth:t+1,expanded:n,toggleFolder:o,openFile:r},c.path))]}):a.jsxs("button",{type:"button",onClick:()=>e.nodeId&&r(e.nodeId),className:"w-full flex items-center gap-1.5 py-1.5 pr-3 text-left text-xs text-text-secondary hover:text-accent hover:bg-accent/5 transition-colors",style:{paddingLeft:i},title:e.path,children:[a.jsx("span",{className:"w-3 text-text-muted",children:"-"}),a.jsx("span",{className:"truncate font-mono",children:e.name})]})}function hr(){const e=b(m=>m.graph),t=b(m=>m.activeLayerId),n=b(m=>m.navigationLevel),o=b(m=>m.openCodeViewer),r=b(m=>m.navigateToNode),{t:s}=re(),i=e==null?void 0:e.layers.find(m=>m.id===t),c=v.useMemo(()=>n==="layer-detail"&&i?new Set(i.nodeIds):void 0,[n,i]),d=v.useMemo(()=>Mu((e==null?void 0:e.nodes)??[],c),[e,c]),[l,u]=v.useState(()=>new Set),f=m=>{r(m),o(m)},h=m=>{u(p=>{const N=new Set(p);return N.has(m)?N.delete(m):N.add(m),N})},g=v.useMemo(()=>{const m=p=>p.reduce((N,y)=>N+(y.type==="file"?1:m(y.children)),0);return m(d)},[d]);return e?a.jsxs("div",{className:"h-full flex flex-col min-h-0",children:[a.jsxs("div",{className:"px-4 py-3 border-b border-border-subtle shrink-0",children:[a.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-accent",children:i?i.name:s.fileExplorer.analyzedFiles}),a.jsxs("div",{className:"text-xs text-text-muted mt-1",children:[g," ",i?"个文档":s.fileExplorer.filesFromGraph]})]}),a.jsx("div",{className:"flex-1 overflow-auto py-2",children:d.length===0?a.jsx("div",{className:"px-4 py-6 text-sm text-text-muted",children:s.fileExplorer.noFilePathsFound}):d.map(m=>a.jsx(pr,{entry:m,depth:0,expanded:l,toggleFolder:h,openFile:f},m.path))})]}):a.jsx("div",{className:"h-full flex items-center justify-center p-5 text-sm text-text-muted",children:s.common.noGraphLoaded})}function Fu(e){const n=e.some(r=>r.level==="fatal")?["Some of these issues look like dashboard rendering bugs.","Please file an issue at github.com/Lum1104/Understand-Anything/issues with the text below.",""]:["The following issues were found in your knowledge-graph.json.","These are LLM generation errors — not a system bug.","You can ask your agent to fix these specific issues in the knowledge-graph.json file:",""],o=[...e].sort((r,s)=>{const i={fatal:0,dropped:1,"auto-corrected":2};return(i[r.level]??3)-(i[s.level]??3)});for(const r of o){const s=r.level==="auto-corrected"?"Auto-corrected":r.level==="dropped"?"Dropped":"Fatal";n.push(`[${s}] ${r.message}`)}return n.join(` +`)}function mr({issues:e}){const[t,n]=v.useState(!1),[o,r]=v.useState(!1),s=e.filter(T=>T.level==="fatal"),i=e.filter(T=>T.level==="auto-corrected"),c=e.filter(T=>T.level==="dropped"),d=s.length>0,l=[];s.length>0&&l.push(`${s.length} fatal error${s.length!==1?"s":""}`),i.length>0&&l.push(`${i.length} auto-correction${i.length!==1?"s":""}`),c.length>0&&l.push(`${c.length} dropped item${c.length!==1?"s":""}`);const u=d?`Dashboard hit ${l.join(", ")}`:`Knowledge graph loaded with ${l.join(" and ")}`,f=v.useCallback(async()=>{const T=Fu(e);try{await navigator.clipboard.writeText(T),r(!0),setTimeout(()=>r(!1),2e3)}catch{console.warn("Clipboard write failed — copy text manually from the expanded issue list")}},[e]);if(e.length===0)return null;const h=d?"bg-red-900/25 border-b border-red-700 text-red-200 text-sm":"bg-amber-900/20 border-b border-amber-700 text-amber-200 text-sm",g=d?"hover:bg-red-900/15":"hover:bg-amber-900/10",m=d?"text-red-400":"text-amber-400",p=d?"text-red-400/60":"text-amber-400/60",N=d?"border-red-700/50":"border-amber-700/50",y=d?"text-red-200/70":"text-amber-200/60",x=d?"bg-red-800/40 text-red-200 hover:bg-red-800/60":"bg-amber-800/40 text-amber-200 hover:bg-amber-800/60",k=d?"Copy these issues and file a bug report on GitHub":"Copy these issues and ask your agent to fix them in knowledge-graph.json";return a.jsxs("div",{className:h,children:[a.jsxs("button",{type:"button","aria-expanded":t,onClick:()=>n(T=>!T),className:`w-full flex items-center gap-2 px-5 py-3 text-left transition-colors ${g}`,children:[a.jsx("svg",{className:`w-4 h-4 shrink-0 ${m} transition-transform duration-200 ${t?"rotate-90":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})}),a.jsx("svg",{className:`w-4 h-4 shrink-0 ${m}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"})}),a.jsx("span",{className:"flex-1",children:u}),a.jsx("span",{className:`text-xs shrink-0 ${p}`,children:t?"click to collapse":"click to expand"})]}),t&&a.jsxs("div",{className:"px-5 pb-4",children:[a.jsxs("div",{className:"space-y-1 mb-3",children:[s.length>0&&a.jsxs("div",{children:[a.jsxs("h4",{className:"text-xs font-semibold uppercase tracking-wider text-red-400 mb-1",children:["Fatal (",s.length,")"]}),s.map((T,j)=>a.jsxs("div",{className:"flex items-start gap-2 py-0.5 pl-2 text-red-200",children:[a.jsx("span",{className:"text-red-400 shrink-0 mt-0.5",children:a.jsx("svg",{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z"})})}),a.jsx("span",{className:"text-xs",children:T.message})]},`ft-${j}`))]}),i.length>0&&a.jsxs("div",{className:s.length>0?"mt-2":"",children:[a.jsxs("h4",{className:"text-xs font-semibold uppercase tracking-wider text-amber-400 mb-1",children:["Auto-corrected (",i.length,")"]}),i.map((T,j)=>a.jsxs("div",{className:"flex items-start gap-2 py-0.5 pl-2 text-amber-200/80",children:[a.jsx("span",{className:"text-amber-400 shrink-0 mt-0.5",children:a.jsx("svg",{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})}),a.jsx("span",{className:"text-xs",children:T.message})]},`ac-${j}`))]}),c.length>0&&a.jsxs("div",{className:s.length>0||i.length>0?"mt-2":"",children:[a.jsxs("h4",{className:"text-xs font-semibold uppercase tracking-wider text-orange-400 mb-1",children:["Dropped (",c.length,")"]}),c.map((T,j)=>a.jsxs("div",{className:"flex items-start gap-2 py-0.5 pl-2 text-orange-300/80",children:[a.jsx("span",{className:"text-orange-400 shrink-0 mt-0.5",children:a.jsx("svg",{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})}),a.jsx("span",{className:"text-xs",children:T.message})]},`dr-${j}`))]})]}),a.jsxs("div",{className:`flex items-center justify-between pt-2 border-t ${N}`,children:[a.jsx("p",{className:`text-xs ${y}`,children:k}),a.jsx("button",{type:"button",onClick:f,className:`flex items-center gap-1.5 px-3 py-1 rounded text-xs font-medium transition-colors shrink-0 ml-4 ${x}`,children:o?a.jsxs(a.Fragment,{children:[a.jsx("svg",{className:"w-3.5 h-3.5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})}),"Copied!"]}):a.jsxs(a.Fragment,{children:[a.jsx("svg",{className:"w-3.5 h-3.5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})}),"Copy Issues"]})})]})]})]})}function Du({onTokenValid:e}){const[t,n]=v.useState(""),[o,r]=v.useState(null),[s,i]=v.useState(!1),c=async d=>{d.preventDefault();const l=t.trim();if(l){i(!0),r(null);try{const u=await fetch(`/knowledge-graph.json?token=${encodeURIComponent(l)}`);u.ok?e(l):u.status===403?r("Invalid token. Please check and try again."):r(`Unexpected response (${u.status}). Is the dashboard server running?`)}catch(u){r(`Could not reach the server: ${u instanceof Error?u.message:String(u)}`)}finally{i(!1)}}};return a.jsx("div",{className:"h-screen w-screen flex items-center justify-center bg-root noise-overlay",children:a.jsxs("div",{className:"w-full max-w-md px-8 py-10 bg-surface border border-border-subtle rounded-lg shadow-2xl",children:[a.jsx("h1",{className:"font-heading text-2xl text-text-primary tracking-wide text-center mb-2",children:"Access Token Required"}),a.jsxs("p",{className:"text-text-muted text-sm text-center mb-8",children:["Paste the access token from your terminal. Look for the"," ",a.jsx("span",{role:"img","aria-label":"key",children:"🔑"})," line."]}),a.jsxs("form",{onSubmit:c,className:"flex flex-col gap-4",children:[a.jsx("input",{type:"text",value:t,onChange:d=>{n(d.target.value),o&&r(null)},placeholder:"Paste token here...",autoFocus:!0,className:"w-full px-4 py-3 bg-elevated border border-border-subtle rounded text-text-primary placeholder:text-text-muted/50 font-mono text-sm focus:outline-none focus:border-accent transition-colors"}),o&&a.jsx("p",{className:"text-red-400 text-sm",children:o}),a.jsx("button",{type:"submit",disabled:s||!t.trim(),className:"w-full py-3 bg-accent text-root font-semibold rounded transition-all hover:brightness-110 disabled:opacity-40 disabled:cursor-not-allowed",children:s?"Validating...":"Continue"})]})]})})}const Pu={graph:a.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.6,children:[a.jsx("circle",{cx:"6",cy:"7",r:"2"}),a.jsx("circle",{cx:"18",cy:"7",r:"2"}),a.jsx("circle",{cx:"12",cy:"17",r:"2"}),a.jsx("path",{strokeLinecap:"round",d:"M7.6 8.5L11 15.5M16.4 8.5L13 15.5M8 7h8"})]}),info:a.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.6,children:[a.jsx("circle",{cx:"12",cy:"12",r:"9"}),a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 11v5M12 8h.01"})]}),files:a.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.6,children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4 6.5A1.5 1.5 0 0 1 5.5 5h3.382a1.5 1.5 0 0 1 1.342.83l.671 1.34A1.5 1.5 0 0 0 12.236 8H18.5A1.5 1.5 0 0 1 20 9.5v8a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 17.5z"})})},Ru=["graph","info","files"];function Zu({activeTab:e,onTabChange:t}){const{t:n}=re(),o={graph:n.mobile.graph,info:n.mobile.info,files:n.mobile.files};return a.jsx("nav",{className:"flex shrink-0 bg-surface border-t border-border-subtle",children:Ru.map(r=>{const s=e===r;return a.jsxs("button",{type:"button",onClick:()=>t(r),className:`relative flex-1 flex flex-col items-center justify-center gap-1 py-2.5 text-[10px] font-semibold uppercase tracking-[0.14em] transition-colors ${s?"text-accent":"text-text-muted hover:text-text-secondary"}`,"aria-current":s?"page":void 0,children:[a.jsx("span",{className:"w-5 h-5",children:Pu[r]}),o[r],s&&a.jsx("span",{className:"absolute top-0 left-1/2 -translate-x-1/2 w-8 h-px bg-accent"})]},r)})})}function gr(){const{config:e,preset:t,setPreset:n,setAccent:o,setHeadingFont:r}=Qo(),[s,i]=v.useState(!1),c=v.useRef(null),{t:d}=re();v.useEffect(()=>{if(!s)return;function u(f){c.current&&!c.current.contains(f.target)&&i(!1)}return document.addEventListener("mousedown",u),()=>document.removeEventListener("mousedown",u)},[s]),v.useEffect(()=>{if(!s)return;function u(f){f.key==="Escape"&&i(!1)}return document.addEventListener("keydown",u),()=>document.removeEventListener("keydown",u)},[s]);const l=v.useCallback(u=>{n(u)},[n]);return a.jsxs("div",{ref:c,className:"relative",children:[a.jsxs("button",{onClick:()=>i(u=>!u),className:"flex items-center gap-1.5 px-2 py-1 rounded text-xs text-text-secondary hover:text-text-primary transition-colors",title:d.themePicker.changeTheme,children:[a.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[a.jsx("circle",{cx:"12",cy:"12",r:"10"}),a.jsx("path",{d:"M12 2a7 7 0 0 0 0 14 4 4 0 0 1 0 8 10 10 0 0 0 0-20z"}),a.jsx("circle",{cx:"8",cy:"10",r:"1.5",fill:"currentColor"}),a.jsx("circle",{cx:"12",cy:"7",r:"1.5",fill:"currentColor"}),a.jsx("circle",{cx:"16",cy:"10",r:"1.5",fill:"currentColor"})]}),a.jsx("span",{className:"hidden sm:inline",children:d.common.theme})]}),s&&a.jsxs("div",{className:"absolute right-0 top-full mt-2 w-64 rounded-lg glass-heavy shadow-xl z-50 p-3 space-y-3",children:[a.jsxs("div",{children:[a.jsx("div",{className:"text-[10px] font-semibold text-text-muted uppercase tracking-wider mb-2",children:d.themePicker.theme}),a.jsx("div",{className:"space-y-1",children:Zt.map(u=>{var f;return a.jsxs("button",{onClick:()=>l(u.id),className:`w-full flex items-center gap-2.5 px-2.5 py-1.5 rounded text-xs transition-colors ${u.id===e.presetId?"bg-accent/15 text-accent":"text-text-secondary hover:text-text-primary hover:bg-elevated"}`,children:[a.jsxs("div",{className:"flex gap-1",children:[a.jsx("span",{className:"w-3 h-3 rounded-full border border-border-subtle",style:{backgroundColor:u.colors.root}}),a.jsx("span",{className:"w-3 h-3 rounded-full border border-border-subtle",style:{backgroundColor:u.colors.surface}}),a.jsx("span",{className:"w-3 h-3 rounded-full border border-border-subtle",style:{backgroundColor:((f=u.accentSwatches.find(h=>h.id===u.defaultAccentId))==null?void 0:f.accent)??u.accentSwatches[0].accent}})]}),a.jsx("span",{children:u.name}),u.id===e.presetId&&a.jsx("svg",{className:"ml-auto w-3.5 h-3.5 text-accent",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3",children:a.jsx("polyline",{points:"20 6 9 17 4 12"})})]},u.id)})})]}),a.jsxs("div",{children:[a.jsx("div",{className:"text-[10px] font-semibold text-text-muted uppercase tracking-wider mb-2",children:d.themePicker.accentColor}),a.jsx("div",{className:"flex gap-2 flex-wrap",children:t.accentSwatches.map(u=>a.jsx("button",{onClick:()=>o(u.id),className:`w-6 h-6 rounded-full transition-transform hover:scale-110 ${u.id===e.accentId?"ring-2 ring-text-primary ring-offset-1 ring-offset-root":""}`,style:{backgroundColor:u.accent},title:u.name},u.id))})]}),a.jsxs("div",{children:[a.jsx("div",{className:"text-[10px] font-semibold text-text-muted uppercase tracking-wider mb-2",children:d.themePicker.headingFont}),a.jsx("div",{className:"flex gap-1",children:[{id:"serif",label:d.themePicker.serif,sample:"Aa"},{id:"sans",label:d.themePicker.sans,sample:"Aa"},{id:"mono",label:d.themePicker.mono,sample:"Aa"}].map(u=>a.jsx("button",{onClick:()=>r(u.id),className:`flex-1 px-2 py-1.5 rounded text-xs transition-colors ${(e.headingFont??"serif")===u.id?"bg-accent/15 text-accent":"text-text-secondary hover:text-text-primary hover:bg-elevated"}`,style:{fontFamily:u.id==="serif"?"var(--font-serif)":u.id==="mono"?"var(--font-mono)":"var(--font-sans)"},children:u.label},u.id))})]})]})]})}function Oe({children:e}){return a.jsx("h3",{className:"text-[10px] font-semibold uppercase tracking-[0.18em] text-text-muted mb-3",children:e})}function Bu({open:e,onClose:t,onTogglePathFinder:n,onShowKeyboardHelp:o}){var N;const r=b(y=>y.graph),s=b(y=>y.isKnowledgeGraph),i=b(y=>y.domainGraph),c=b(y=>y.viewMode),d=b(y=>y.setViewMode),l=b(y=>y.nodeTypeFilters),u=b(y=>y.toggleNodeTypeFilter),{t:f}=re(),h=[{key:"code",label:f.nodeTypeLabels.code,color:"var(--color-node-file)"},{key:"config",label:f.nodeTypeLabels.config,color:"var(--color-node-config)"},{key:"docs",label:f.nodeTypeLabels.docs,color:"var(--color-node-document)"},{key:"infra",label:f.nodeTypeLabels.infra,color:"var(--color-node-service)"},{key:"data",label:f.nodeTypeLabels.data,color:"var(--color-node-table)"},{key:"domain",label:f.nodeTypeLabels.domain,color:"var(--color-node-concept)"},{key:"knowledge",label:f.nodeTypeLabels.knowledge,color:"var(--color-node-article)"}],g=[{key:"knowledge",label:f.nodeTypeLabels.all,color:"var(--color-node-article)"}];v.useEffect(()=>{if(!e)return;const y=x=>{x.key==="Escape"&&t()};return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[e,t]),v.useEffect(()=>{if(!e)return;const y=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=y}},[e]);const m=s?g:h,p=!!(r&&!s&&i);return a.jsxs("div",{className:`fixed inset-0 z-40 ${e?"pointer-events-auto":"pointer-events-none"}`,"aria-hidden":!e,children:[a.jsx("button",{type:"button","aria-label":"Close menu",onClick:t,className:`absolute inset-0 bg-black/65 backdrop-blur-sm transition-opacity duration-300 ${e?"opacity-100":"opacity-0"}`}),a.jsxs("aside",{className:`absolute left-0 top-0 bottom-0 w-[86%] max-w-[360px] bg-surface border-r border-border-subtle flex flex-col transition-transform duration-300 ease-out ${e?"translate-x-0":"-translate-x-full"}`,role:"dialog","aria-label":"Settings",children:[a.jsxs("header",{className:"flex items-center justify-between px-5 py-4 border-b border-border-subtle shrink-0",children:[a.jsxs("div",{children:[a.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-[0.2em] text-accent",children:f.drawer.controls}),a.jsx("h2",{className:"font-heading text-lg text-text-primary mt-0.5 leading-none",children:(r==null?void 0:r.project.name)??f.drawer.dashboard})]}),a.jsx("button",{type:"button",onClick:t,"aria-label":"Close menu",className:"w-9 h-9 flex items-center justify-center rounded-lg text-text-muted hover:text-text-primary hover:bg-elevated transition-colors",children:a.jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",strokeWidth:2,children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 6l12 12M6 18L18 6"})})})]}),a.jsxs("div",{className:"flex-1 overflow-auto px-5 py-5 space-y-7",children:[a.jsxs("section",{children:[a.jsx(Oe,{children:f.drawer.role}),a.jsx(ur,{})]}),p&&a.jsxs("section",{children:[a.jsx(Oe,{children:f.drawer.view}),a.jsxs("div",{className:"inline-flex items-center bg-elevated rounded-lg p-0.5",children:[a.jsx("button",{type:"button",onClick:()=>d("domain"),className:`px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${c==="domain"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:f.drawer.domain}),a.jsx("button",{type:"button",onClick:()=>d("structural"),className:`px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${c==="structural"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:f.drawer.structural})]})]}),a.jsxs("section",{children:[a.jsx(Oe,{children:f.drawer.diffOverlay}),a.jsx(cr,{})]}),a.jsxs("section",{children:[a.jsx(Oe,{children:f.drawer.nodeTypes}),a.jsx("div",{className:"flex flex-wrap gap-1.5",children:m.map(y=>{const x=l[y.key]!==!1;return a.jsxs("button",{type:"button",onClick:()=>u(y.key),className:`text-[10px] font-semibold uppercase tracking-wider px-2 py-1 rounded border transition-colors flex items-center gap-1.5 whitespace-nowrap ${x?"border-border-medium bg-elevated text-text-secondary":"border-transparent bg-transparent text-text-muted/40 line-through"}`,children:[a.jsx("span",{className:"w-2 h-2 rounded-full shrink-0",style:{backgroundColor:y.color,opacity:x?1:.3}}),y.label]},y.key)})})]}),r&&(((N=r.layers)==null?void 0:N.length)??0)>0&&a.jsxs("section",{children:[a.jsx(Oe,{children:f.drawer.layers}),a.jsx("div",{className:"-mx-1",children:a.jsx(Ko,{})})]}),a.jsxs("section",{children:[a.jsx(Oe,{children:f.drawer.tools}),a.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[a.jsx(dr,{}),a.jsx(lr,{}),a.jsxs("button",{type:"button",onClick:()=>{n(),t()},className:"flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm bg-elevated text-text-secondary hover:text-text-primary transition-colors",children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"})}),f.drawer.path]}),a.jsx(gr,{}),a.jsxs("button",{type:"button",onClick:()=>{o(),t()},className:"flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm bg-elevated text-text-secondary hover:text-text-primary transition-colors","aria-label":f.drawer.help,children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"})}),f.drawer.help]})]})]})]})]})]})}const Vu=v.lazy(()=>xe(()=>import("./CodeViewer-Boblkcge.js"),__vite__mapDeps([0,1,2,3,4,5,6,7]))),Gu=v.lazy(()=>xe(()=>import("./LearnPanel-DahajoWG.js"),__vite__mapDeps([8,1,2,3,4,5,6,7]))),Hu=v.lazy(()=>xe(()=>import("./PathFinderModal-BHT2q_Em.js"),__vite__mapDeps([9,1,3,4,5,6,7]))),Uu=v.lazy(()=>xe(()=>import("./KeyboardShortcutsHelp-CcG7Ga98.js"),__vite__mapDeps([10,1,3,4,5,6,7])));function Wu({accessToken:e,showKeyboardHelp:t,setShowKeyboardHelp:n,loadError:o,allIssues:r,shortcuts:s}){const i=b(w=>w.graph),c=b(w=>w.selectedNodeId),d=b(w=>w.tourActive),l=b(w=>w.persona),u=b(w=>w.viewMode),f=b(w=>w.domainGraph),h=b(w=>w.codeViewerOpen),g=b(w=>w.closeCodeViewer),m=b(w=>w.pathFinderOpen),p=b(w=>w.togglePathFinder),{t:N}=re(),[y,x]=v.useState("graph"),[k,T]=v.useState(!1),[j,C]=v.useState(!1);v.useEffect(()=>{c&&x("info")},[c]),v.useEffect(()=>{h&&C(!1)},[h]);const I=d||l==="junior",E=a.jsxs(a.Fragment,{children:[c&&a.jsx(ir,{}),I&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(Gu,{})}),!c&&!I&&a.jsx(fr,{})]});return a.jsxs("div",{className:"h-screen w-screen flex flex-col bg-root text-text-primary noise-overlay",children:[a.jsxs("header",{className:"flex items-center gap-2 px-3 h-12 shrink-0 bg-surface border-b border-border-subtle",children:[a.jsx("button",{type:"button",onClick:()=>T(!0),className:"w-9 h-9 flex items-center justify-center rounded-lg text-text-secondary hover:text-text-primary hover:bg-elevated transition-colors -ml-1","aria-label":"Open menu",children:a.jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",strokeWidth:1.8,viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",d:"M4 7h16M4 12h16M4 17h16"})})}),a.jsx("h1",{className:"font-heading text-base flex-1 min-w-0 truncate text-center text-text-primary tracking-wide",children:(i==null?void 0:i.project.name)??N.common.appName}),a.jsx("button",{type:"button",onClick:()=>C(w=>!w),className:`w-9 h-9 flex items-center justify-center rounded-lg transition-colors -mr-1 ${j?"text-accent bg-accent/15":"text-text-secondary hover:text-text-primary hover:bg-elevated"}`,"aria-label":j?"Hide search":"Show search","aria-pressed":j,children:a.jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",strokeWidth:1.8,viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"})})})]}),j&&a.jsx(ar,{}),r.length>0&&!o&&a.jsx(mr,{issues:r}),o&&a.jsx("div",{className:"px-4 py-3 bg-red-900/30 border-b border-red-700 text-red-200 text-sm",children:o}),a.jsxs("div",{className:"flex-1 min-h-0 relative",children:[a.jsx("div",{className:`absolute inset-0 ${y==="graph"?"":"invisible pointer-events-none"}`,"aria-hidden":y!=="graph",children:u==="knowledge"?a.jsx(sr,{}):u==="domain"&&f?a.jsx(rr,{}):a.jsx(nr,{})}),a.jsx("div",{className:`absolute inset-0 overflow-auto bg-surface ${y==="info"?"":"invisible pointer-events-none"}`,"aria-hidden":y!=="info",children:E}),a.jsx("div",{className:`absolute inset-0 overflow-auto bg-surface ${y==="files"?"":"invisible pointer-events-none"}`,"aria-hidden":y!=="files",children:a.jsx(hr,{})})]}),a.jsx(Zu,{activeTab:y,onTabChange:x}),a.jsx(Bu,{open:k,onClose:()=>T(!1),onTogglePathFinder:p,onShowKeyboardHelp:()=>n(!0)}),h&&a.jsx("div",{className:"fixed inset-0 z-50 flex bg-black/70 backdrop-blur-sm p-2 sm:p-4",onMouseDown:g,children:a.jsx("div",{className:"flex-1 rounded-lg border border-border-medium bg-surface shadow-2xl overflow-hidden",onMouseDown:w=>w.stopPropagation(),children:a.jsx(v.Suspense,{fallback:null,children:a.jsx(Vu,{accessToken:e,presentation:"modal",onClose:g})})})}),t&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(Uu,{shortcuts:s,onClose:()=>n(!1)})}),m&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(Hu,{isOpen:m,onClose:p})})]})}const Ku=768;function Ju(e=Ku){const t=`(max-width: ${e-1}px)`,[n,o]=v.useState(()=>typeof window>"u"?!1:window.matchMedia(t).matches);return v.useEffect(()=>{const r=window.matchMedia(t),s=i=>o(i.matches);return o(r.matches),r.addEventListener("change",s),()=>r.removeEventListener("change",s)},[t]),n}function Yu(e,t=!0){v.useEffect(()=>{if(!t)return;const n=o=>{const r=o.target,s=r.tagName.toLowerCase();if(!((s==="input"||s==="textarea"||r.isContentEditable)&&o.key!=="Escape"))for(const i of e){const c=o.key.toLowerCase()===i.key.toLowerCase(),d=i.ctrlKey?o.ctrlKey:!o.ctrlKey,l=i.shiftKey?o.shiftKey:!o.shiftKey,u=i.altKey?o.altKey:!o.altKey,f=i.metaKey?o.metaKey:!o.metaKey;if(c&&d&&l&&u&&f){(o.ctrlKey||o.metaKey||o.altKey)&&o.preventDefault(),i.action();break}}};return document.addEventListener("keydown",n),()=>document.removeEventListener("keydown",n)},[e,t])}function hf(e){var r;const t=[],n=(r=navigator.userAgentData)!=null&&r.platform?navigator.userAgentData.platform==="macOS":navigator.platform.includes("Mac");(e.ctrlKey||e.metaKey)&&t.push(n?"⌘":"Ctrl");const o=e.key.length===1&&/[^a-zA-Z0-9]/.test(e.key);return e.shiftKey&&!o&&t.push("⇧"),e.altKey&&t.push(n?"⌥":"Alt"),t.push(o?e.key:e.key.toUpperCase()),t.join(" + ")}const to=v.lazy(()=>xe(()=>import("./CodeViewer-Boblkcge.js"),__vite__mapDeps([0,1,2,3,4,5,6,7]))),Xu=v.lazy(()=>xe(()=>import("./LearnPanel-DahajoWG.js"),__vite__mapDeps([8,1,2,3,4,5,6,7]))),qu=v.lazy(()=>xe(()=>import("./PathFinderModal-BHT2q_Em.js"),__vite__mapDeps([9,1,3,4,5,6,7]))),Qu=v.lazy(()=>xe(()=>import("./KeyboardShortcutsHelp-CcG7Ga98.js"),__vite__mapDeps([10,1,3,4,5,6,7]))),ef=v.lazy(()=>xe(()=>import("./RagAssistant-BEmyfNge.js"),__vite__mapDeps([11,1,3,4,5,6,7]))),tf=v.lazy(()=>xe(()=>import("./OnboardingOverlay-BCZlWHWy.js"),__vite__mapDeps([12,1,3,4,5,6,7]))),Gt="understand-anything-token",xr="ua-onboarding-dismissed-v1";function nf(){return typeof window>"u"?!1:new URLSearchParams(window.location.search).get("onboard")==="force"?!0:window.localStorage.getItem(xr)!=="1"}function Ge(e,t){const n=`/${e}`;return t?`${n}?token=${encodeURIComponent(t)}`:n}function of(){const e=new URLSearchParams(window.location.search),t=e.get("token");if(t){sessionStorage.setItem(Gt,t),e.delete("token");const n=e.toString(),o=window.location.pathname+(n?`?${n}`:"")+window.location.hash;return window.history.replaceState(null,"",o),t}return sessionStorage.getItem(Gt)}function rf(){const[e,t]=v.useState(of),n=v.useCallback(o=>{sessionStorage.setItem(Gt,o),t(o)},[]);return e===null?a.jsx(Du,{onTokenValid:n}):a.jsx(sf,{accessToken:e})}function sf({accessToken:e}){const t=b(h=>h.setGraph),n=b(h=>h.setDomainGraph),o=b(h=>h.setDiffOverlay),[r,s]=v.useState(null),[i,c]=v.useState([]),[d,l]=v.useState(null),[u,f]=v.useState();return v.useEffect(()=>{fetch(Ge("meta.json",e)).then(h=>h.ok?h.json():null).then(h=>{h!=null&&h.theme&&l(h.theme)}).catch(()=>{}),fetch(Ge("config.json",e)).then(h=>h.ok?h.json():null).then(h=>{h!=null&&h.outputLanguage&&f(h.outputLanguage)}).catch(()=>{})},[]),v.useEffect(()=>{fetch(Ge("knowledge-graph.json",e)).then(h=>h.json()).then(h=>{const g=Mn(h);if(g.success&&g.data){t(g.data),c(g.issues),h.kind==="knowledge"&&(b.getState().setViewMode("knowledge"),b.getState().setIsKnowledgeGraph(!0));for(const m of g.issues)m.level==="auto-corrected"?console.warn(`[graph] auto-corrected: ${m.message}`):m.level==="dropped"&&console.error(`[graph] dropped: ${m.message}`)}else g.fatal?(console.error("Knowledge graph validation failed:",g.fatal),s(`Invalid knowledge graph: ${g.fatal}`)):(console.error("Knowledge graph validation failed: unknown error"),s("Invalid knowledge graph: unknown validation error"))}).catch(h=>{console.error("Failed to load knowledge graph:",h),s(`Failed to load knowledge graph: ${h instanceof Error?h.message:String(h)}`)})},[t]),v.useEffect(()=>{fetch(Ge("diff-overlay.json",e)).then(h=>h.ok?h.json():null).then(h=>{if(h&&typeof h=="object"&&"changedNodeIds"in h&&"affectedNodeIds"in h&&Array.isArray(h.changedNodeIds)&&Array.isArray(h.affectedNodeIds)){const g=h;g.changedNodeIds.length>0&&o(g.changedNodeIds,g.affectedNodeIds)}}).catch(()=>{})},[o]),v.useEffect(()=>{fetch(Ge("domain-graph.json",e)).then(h=>h.ok?h.json():null).then(h=>{if(!h)return;const g=Mn(h);g.success&&g.data?n(g.data):g.fatal&&console.warn(`[domain-graph] validation failed: ${g.fatal}`)}).catch(()=>{})},[n]),a.jsx(wl,{language:u??"en",children:a.jsx(Ml,{metaTheme:d,children:a.jsx(af,{accessToken:e,loadError:r,graphIssues:i})})})}function af({accessToken:e,loadError:t,graphIssues:n}){const o=b(S=>S.graph),r=b(S=>S.selectedNodeId),s=b(S=>S.tourActive),i=b(S=>S.persona),c=b(S=>S.codeViewerOpen),d=b(S=>S.codeViewerExpanded),l=b(S=>S.expandCodeViewer),u=b(S=>S.collapseCodeViewer),f=b(S=>S.pathFinderOpen),h=b(S=>S.togglePathFinder),g=b(S=>S.nodeTypeFilters),m=b(S=>S.toggleNodeTypeFilter),p=b(S=>S.detailLevel),N=b(S=>S.setDetailLevel),y=b(S=>S.showFunctionsInClassView),x=b(S=>S.toggleShowFunctionsInClassView),[k,T]=v.useState(!1),[j,C]=v.useState(!1),[I,E]=v.useState("info"),[w,M]=v.useState(nf),$=v.useCallback(S=>{S&&typeof window<"u"&&window.localStorage.setItem(xr,"1"),M(!1)},[]),D=b(S=>S.viewMode),Z=b(S=>S.setViewMode),P=b(S=>S.isKnowledgeGraph),V=b(S=>S.domainGraph),R=b(S=>S.layoutIssues),U=Ju(),{t:A}=re(),Q=v.useMemo(()=>[...n,...R],[n,R]);v.useEffect(()=>{r&&E("info")},[r]);const se=v.useMemo(()=>[{key:"?",shiftKey:!0,description:A.keyboardShortcuts.showHelp,action:()=>T(S=>!S),category:"General"},{key:"Escape",description:A.keyboardShortcuts.escapeDesc,action:()=>{const S=b.getState();S.pathFinderOpen?S.togglePathFinder():S.filterPanelOpen?S.toggleFilterPanel():S.exportMenuOpen?S.toggleExportMenu():S.codeViewerExpanded?S.collapseCodeViewer():S.codeViewerOpen?S.closeCodeViewer():S.selectedNodeId?S.selectNode(null):S.navigationLevel==="layer-detail"?S.navigateToOverview():S.tourActive?S.stopTour():T(!1)},category:"Navigation"},{key:"/",description:A.keyboardShortcuts.focusSearch,action:()=>{const S=document.querySelector('[data-testid="search-input"]');S==null||S.focus()},category:"Navigation"},{key:"ArrowRight",description:A.keyboardShortcuts.nextStep,action:()=>{const S=b.getState();S.tourActive&&S.nextTourStep()},category:"Tour"},{key:"ArrowLeft",description:A.keyboardShortcuts.prevStep,action:()=>{const S=b.getState();S.tourActive&&S.prevTourStep()},category:"Tour"},{key:"d",description:A.keyboardShortcuts.toggleDiff,action:()=>{b.getState().toggleDiffMode()},category:"View"},{key:"f",description:A.keyboardShortcuts.toggleFilter,action:()=>{b.getState().toggleFilterPanel()},category:"View"},{key:"e",description:A.keyboardShortcuts.toggleExport,action:()=>{b.getState().toggleExportMenu()},category:"View"},{key:"p",description:A.keyboardShortcuts.openPathFinder,action:()=>{b.getState().togglePathFinder()},category:"View"},{key:"r",description:"打开知识库 RAG 问答",action:()=>C(S=>!S),category:"View"}],[A]);Yu(se);const K=s||i==="junior",z=a.jsxs(a.Fragment,{children:[r&&a.jsx(ir,{}),K&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(Xu,{})}),!r&&!K&&a.jsx(fr,{})]}),J=a.jsxs("div",{className:"h-full flex flex-col min-h-0",children:[a.jsx("div",{className:"flex items-center gap-1 p-2 border-b border-border-subtle bg-surface shrink-0",children:["info","files"].map(S=>a.jsx("button",{type:"button",onClick:()=>E(S),className:`flex-1 px-3 py-1.5 rounded-md text-xs font-semibold uppercase tracking-wider transition-colors ${I===S?"bg-accent/15 text-accent":"text-text-muted hover:text-text-primary hover:bg-elevated"}`,children:S==="info"?A.sidebar.info:A.sidebar.files},S))}),a.jsx("div",{className:"flex-1 min-h-0 overflow-auto",children:I==="files"?a.jsx(hr,{}):z})]});return U?a.jsx(Wu,{accessToken:e,showKeyboardHelp:k,setShowKeyboardHelp:T,loadError:t,allIssues:Q,shortcuts:se}):a.jsxs("div",{className:"h-screen w-screen flex flex-col bg-root text-text-primary noise-overlay",children:[a.jsxs("header",{className:"flex items-center px-3 sm:px-5 py-3 bg-surface border-b border-border-subtle shrink-0 gap-2 sm:gap-4",children:[a.jsxs("div",{className:"flex items-center gap-3 sm:gap-5 shrink-0 min-w-0",children:[a.jsx("h1",{className:"font-heading text-base sm:text-lg text-text-primary tracking-wide truncate max-w-[160px] sm:max-w-[220px] lg:max-w-none",children:(o==null?void 0:o.project.name)??A.common.appName}),a.jsx("div",{className:"w-px h-5 bg-border-subtle hidden sm:block"}),a.jsx(ur,{}),o&&!P&&V&&a.jsxs(a.Fragment,{children:[a.jsx("div",{className:"w-px h-5 bg-border-subtle"}),a.jsxs("div",{className:"flex items-center bg-elevated rounded-lg p-0.5",children:[a.jsx("button",{type:"button",onClick:()=>Z("domain"),title:A.drawer.domain,className:`px-3 py-1 text-xs font-medium rounded-md transition-colors ${D==="domain"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:A.drawer.domain}),a.jsx("button",{type:"button",onClick:()=>Z("structural"),title:A.drawer.structural,className:`px-3 py-1 text-xs font-medium rounded-md transition-colors ${D==="structural"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:A.drawer.structural})]})]})]}),a.jsx("div",{className:"flex-1 min-w-0 overflow-x-auto scrollbar-hide",children:a.jsxs("div",{className:"flex items-center gap-4 w-max",children:[a.jsx(cr,{}),!P&&D!=="domain"&&a.jsxs(a.Fragment,{children:[a.jsx("div",{className:"w-px h-5 bg-border-subtle"}),a.jsxs("div",{className:"flex items-center bg-elevated rounded-lg p-0.5",children:[a.jsx("button",{type:"button",onClick:()=>N("file"),title:A.detailLevel.filesTitle,className:`px-3 py-1 text-xs font-medium rounded-md transition-colors ${p==="file"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:A.detailLevel.files}),a.jsx("button",{type:"button",onClick:()=>N("class"),title:A.detailLevel.classesTitle,className:`px-3 py-1 text-xs font-medium rounded-md transition-colors ${p==="class"?"bg-accent/20 text-accent":"text-text-muted hover:text-text-secondary"}`,children:A.detailLevel.classes})]}),p==="class"&&a.jsx("button",{type:"button",onClick:x,title:A.detailLevel.fnTitle,className:`text-[10px] font-semibold uppercase tracking-wider px-2 py-1 rounded border transition-colors ${y?"border-amber-500/50 bg-amber-500/10 text-amber-400":"border-border-medium bg-elevated text-text-muted hover:text-text-secondary"}`,children:A.detailLevel.fn})]}),a.jsx("div",{className:"flex items-center gap-1",children:(P?[{key:"knowledge",label:A.nodeTypeLabels.all,color:"var(--color-node-article)"}]:[{key:"code",label:A.nodeTypeLabels.code,color:"var(--color-node-file)"},{key:"config",label:A.nodeTypeLabels.config,color:"var(--color-node-config)"},{key:"docs",label:A.nodeTypeLabels.docs,color:"var(--color-node-document)"},{key:"infra",label:A.nodeTypeLabels.infra,color:"var(--color-node-service)"},{key:"data",label:A.nodeTypeLabels.data,color:"var(--color-node-table)"},{key:"domain",label:A.nodeTypeLabels.domain,color:"var(--color-node-concept)"},{key:"knowledge",label:A.nodeTypeLabels.knowledge,color:"var(--color-node-article)"}]).map(S=>a.jsxs("button",{onClick:()=>m(S.key),className:`text-[10px] font-semibold uppercase tracking-wider px-2 py-1 rounded border transition-colors flex items-center gap-1.5 whitespace-nowrap ${g[S.key]!==!1?"border-border-medium bg-elevated text-text-secondary hover:text-text-primary":"border-transparent bg-transparent text-text-muted/40 line-through hover:text-text-muted"}`,title:`${g[S.key]!==!1?"Hide":"Show"} ${S.label} nodes`,children:[a.jsx("span",{className:"w-2 h-2 rounded-full shrink-0",style:{backgroundColor:S.color,opacity:g[S.key]!==!1?1:.3}}),S.label]},S.key))}),a.jsx(Ko,{})]})}),a.jsxs("div",{className:"flex items-center gap-2 sm:gap-4 shrink-0",children:[a.jsx(dr,{}),a.jsx(lr,{}),a.jsxs("button",{type:"button",onClick:()=>C(!0),className:"flex items-center gap-1.5 px-2 sm:px-3 py-1.5 rounded-lg text-sm bg-elevated text-text-secondary hover:text-text-primary transition-colors",title:"知识库 RAG 问答",children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M8 10h8M8 14h5m8-2a9 9 0 11-3.219-6.89L21 5v7z"})}),a.jsx("span",{className:"hidden md:inline",children:"RAG"})]}),a.jsxs("button",{onClick:h,className:"flex items-center gap-1.5 px-2 sm:px-3 py-1.5 rounded-lg text-sm bg-elevated text-text-secondary hover:text-text-primary transition-colors",title:A.pathFinder.title,children:[a.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"})}),a.jsx("span",{className:"hidden md:inline",children:A.common.path})]}),a.jsx(gr,{}),a.jsx("button",{onClick:()=>T(!0),className:"text-text-muted hover:text-accent transition-colors",title:A.keyboardShortcuts.showHelp,children:a.jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"})})})]})]}),a.jsx(ar,{}),Q.length>0&&!t&&a.jsx(mr,{issues:Q}),t&&a.jsx("div",{className:"px-5 py-3 bg-red-900/30 border-b border-red-700 text-red-200 text-sm",children:t}),a.jsxs("div",{className:"flex-1 flex min-h-0 relative",children:[a.jsxs("div",{className:"flex-1 min-w-0 min-h-0 relative",children:[D==="knowledge"?a.jsx(sr,{}):D==="domain"&&V?a.jsx(rr,{}):a.jsx(nr,{}),a.jsx("div",{className:"absolute top-3 right-3 text-sm text-text-muted/60 pointer-events-none select-none",children:A.common.pressKeyboard})]}),a.jsx("aside",{className:"w-[260px] md:w-[300px] lg:w-[360px] shrink-0 bg-surface border-l border-border-subtle overflow-auto",children:J}),c&&!d&&a.jsx("div",{className:"absolute bottom-0 left-0 right-0 h-[40vh] bg-surface border-t border-border-subtle animate-slide-up z-20 overflow-hidden",children:a.jsx(v.Suspense,{fallback:null,children:a.jsx(to,{accessToken:e,onExpand:l})})})]}),c&&d&&a.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/65 backdrop-blur-sm p-4 sm:p-6",onMouseDown:u,children:a.jsx("div",{className:"w-[calc(100vw-32px)] max-w-[1120px] h-[calc(100vh-32px)] sm:h-[calc(100vh-48px)] max-h-[820px] rounded-lg border border-border-medium bg-surface shadow-2xl overflow-hidden",onMouseDown:S=>S.stopPropagation(),children:a.jsx(v.Suspense,{fallback:null,children:a.jsx(to,{accessToken:e,presentation:"modal",onClose:u})})})}),k&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(Qu,{shortcuts:se,onClose:()=>T(!1)})}),j&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(ef,{onClose:()=>C(!1)})}),f&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(qu,{isOpen:f,onClose:h})}),w&&a.jsx(v.Suspense,{fallback:null,children:a.jsx(tf,{onDismiss:$})})]})}wr.createRoot(document.getElementById("root")).render(a.jsx(v.StrictMode,{children:a.jsx(rf,{})}));export{re as a,hf as f,b as u}; diff --git a/wishfulfilled-dashboard/assets/index-L1cEqadP.css b/wishfulfilled-dashboard/assets/index-L1cEqadP.css new file mode 100644 index 0000000..28b79d1 --- /dev/null +++ b/wishfulfilled-dashboard/assets/index-L1cEqadP.css @@ -0,0 +1 @@ +/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:"Inter", system-ui, sans-serif;--font-serif:"DM Serif Display", Georgia, serif;--font-mono:"JetBrains Mono", "Fira Code", monospace;--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-yellow-400:oklch(85.2% .199 91.936);--color-green-400:oklch(79.2% .209 151.711);--color-blue-400:oklch(70.7% .165 254.624);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-md:28rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-normal:0em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-root:#0a0a0a;--color-surface:#111;--color-elevated:#1a1a1a;--color-accent:#d4a574;--color-accent-dim:#c9a96e;--color-accent-bright:#e8c49a;--color-text-primary:#f5f0eb;--color-text-secondary:#a39787;--color-text-muted:#6b5f53;--color-border-subtle:#d4a5741f;--color-border-medium:#d4a57440;--color-node-file:#4a7c9b;--color-node-function:#5a9e6f;--color-node-class:#8b6fb0;--color-node-module:#c9a06c;--color-node-concept:#b07a8a;--color-node-config:#5eead4;--color-node-document:#7dd3fc;--color-node-service:#a78bfa;--color-node-table:#6ee7b7;--color-node-endpoint:#fdba74;--color-node-pipeline:#fda4af;--color-node-schema:#fcd34d;--color-node-resource:#a5b4fc;--color-node-article:#d4a574;--color-node-entity:#7ba4c9;--color-node-topic:#c9b06c;--color-node-claim:#6fb07a;--color-node-source:#8a8a8a;--color-diff-changed:#e05252;--color-diff-affected:#d4a030;--color-diff-changed-dim:#e0525240;--glass-bg:#141414cc;--glass-bg-heavy:#141414f2;--glass-border:#d4a5741a;--glass-border-heavy:#d4a57426;--scrollbar-thumb:#d4a57433;--scrollbar-thumb-hover:#d4a57459;--glow-accent:#d4a57426;--glow-accent-strong:#d4a57466;--glow-accent-pulse:#d4a57499;--color-edge-dot:#d4a57426;--color-accent-overlay-bg:#d4a5740d;--kbd-bg:#d4a5741a;--font-heading:var(--font-serif)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-3{top:calc(var(--spacing) * 3)}.top-4{top:calc(var(--spacing) * 4)}.top-14{top:calc(var(--spacing) * 14)}.top-full{top:100%}.right-0{right:calc(var(--spacing) * 0)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.left-4{left:calc(var(--spacing) * 4)}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[9999\]{z-index:9999}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.m-4{margin:calc(var(--spacing) * 4)}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing) * 1)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-4{-webkit-line-clamp:4;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.\!h-1\.5{height:calc(var(--spacing) * 1.5)!important}.\!h-2{height:calc(var(--spacing) * 2)!important}.h-1{height:calc(var(--spacing) * 1)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-12{height:calc(var(--spacing) * 12)}.h-\[40vh\]{height:40vh}.h-\[calc\(100vh-32px\)\]{height:calc(100vh - 32px)}.h-\[calc\(100vh-64px\)\]{height:calc(100vh - 64px)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[200px\]{max-height:200px}.max-h-\[260px\]{max-height:260px}.max-h-\[520px\]{max-height:520px}.max-h-\[820px\]{max-height:820px}.max-h-\[860px\]{max-height:860px}.max-h-\[calc\(80vh-180px\)\]{max-height:calc(80vh - 180px)}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[76px\]{min-height:76px}.min-h-\[260px\]{min-height:260px}.\!w-1\.5{width:calc(var(--spacing) * 1.5)!important}.\!w-2{width:calc(var(--spacing) * 2)!important}.w-1{width:calc(var(--spacing) * 1)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-12{width:calc(var(--spacing) * 12)}.w-16{width:calc(var(--spacing) * 16)}.w-52{width:calc(var(--spacing) * 52)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-\[86\%\]{width:86%}.w-\[260px\]{width:260px}.w-\[calc\(100vw-32px\)\]{width:calc(100vw - 32px)}.w-full{width:100%}.w-max{width:max-content}.w-px{width:1px}.w-screen{width:100vw}.max-w-2xl{max-width:var(--container-2xl)}.max-w-\[80px\]{max-width:80px}.max-w-\[160px\]{max-width:160px}.max-w-\[220px\]{max-width:220px}.max-w-\[240px\]{max-width:240px}.max-w-\[320px\]{max-width:320px}.max-w-\[360px\]{max-width:360px}.max-w-\[1120px\]{max-width:1120px}.max-w-\[1180px\]{max-width:1180px}.max-w-md{max-width:var(--container-md)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[180px\]{min-width:180px}.min-w-\[240px\]{min-width:240px}.min-w-\[280px\]{min-width:280px}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-7>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 7) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 7) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-l-xl{border-top-left-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.\!border{border-style:var(--tw-border-style)!important;border-width:1px!important}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.\!border-border-subtle{border-color:var(--color-border-subtle)!important}.border-\[\#c97070\]\/30{border-color:#c970704d}.border-accent{border-color:var(--color-accent)}.border-accent-dim\/30{border-color:#c9a96e4d}@supports (color:color-mix(in lab,red,red)){.border-accent-dim\/30{border-color:color-mix(in oklab,var(--color-accent-dim) 30%,transparent)}}.border-accent\/20{border-color:#d4a57433}@supports (color:color-mix(in lab,red,red)){.border-accent\/20{border-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.border-accent\/30{border-color:#d4a5744d}@supports (color:color-mix(in lab,red,red)){.border-accent\/30{border-color:color-mix(in oklab,var(--color-accent) 30%,transparent)}}.border-accent\/40{border-color:#d4a57466}@supports (color:color-mix(in lab,red,red)){.border-accent\/40{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.border-accent\/50{border-color:#d4a57480}@supports (color:color-mix(in lab,red,red)){.border-accent\/50{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.border-amber-500\/50{border-color:#f99c0080}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/50{border-color:color-mix(in oklab,var(--color-amber-500) 50%,transparent)}}.border-amber-700{border-color:var(--color-amber-700)}.border-amber-700\/50{border-color:#b7500080}@supports (color:color-mix(in lab,red,red)){.border-amber-700\/50{border-color:color-mix(in oklab,var(--color-amber-700) 50%,transparent)}}.border-border-medium{border-color:var(--color-border-medium)}.border-border-subtle{border-color:var(--color-border-subtle)}.border-node-article\/30{border-color:#d4a5744d}@supports (color:color-mix(in lab,red,red)){.border-node-article\/30{border-color:color-mix(in oklab,var(--color-node-article) 30%,transparent)}}.border-node-claim\/30{border-color:#6fb07a4d}@supports (color:color-mix(in lab,red,red)){.border-node-claim\/30{border-color:color-mix(in oklab,var(--color-node-claim) 30%,transparent)}}.border-node-class\/30{border-color:#8b6fb04d}@supports (color:color-mix(in lab,red,red)){.border-node-class\/30{border-color:color-mix(in oklab,var(--color-node-class) 30%,transparent)}}.border-node-concept\/30{border-color:#b07a8a4d}@supports (color:color-mix(in lab,red,red)){.border-node-concept\/30{border-color:color-mix(in oklab,var(--color-node-concept) 30%,transparent)}}.border-node-config\/30{border-color:#5eead44d}@supports (color:color-mix(in lab,red,red)){.border-node-config\/30{border-color:color-mix(in oklab,var(--color-node-config) 30%,transparent)}}.border-node-document\/30{border-color:#7dd3fc4d}@supports (color:color-mix(in lab,red,red)){.border-node-document\/30{border-color:color-mix(in oklab,var(--color-node-document) 30%,transparent)}}.border-node-endpoint\/30{border-color:#fdba744d}@supports (color:color-mix(in lab,red,red)){.border-node-endpoint\/30{border-color:color-mix(in oklab,var(--color-node-endpoint) 30%,transparent)}}.border-node-entity\/30{border-color:#7ba4c94d}@supports (color:color-mix(in lab,red,red)){.border-node-entity\/30{border-color:color-mix(in oklab,var(--color-node-entity) 30%,transparent)}}.border-node-file\/30{border-color:#4a7c9b4d}@supports (color:color-mix(in lab,red,red)){.border-node-file\/30{border-color:color-mix(in oklab,var(--color-node-file) 30%,transparent)}}.border-node-function\/30{border-color:#5a9e6f4d}@supports (color:color-mix(in lab,red,red)){.border-node-function\/30{border-color:color-mix(in oklab,var(--color-node-function) 30%,transparent)}}.border-node-module\/30{border-color:#c9a06c4d}@supports (color:color-mix(in lab,red,red)){.border-node-module\/30{border-color:color-mix(in oklab,var(--color-node-module) 30%,transparent)}}.border-node-pipeline\/30{border-color:#fda4af4d}@supports (color:color-mix(in lab,red,red)){.border-node-pipeline\/30{border-color:color-mix(in oklab,var(--color-node-pipeline) 30%,transparent)}}.border-node-resource\/30{border-color:#a5b4fc4d}@supports (color:color-mix(in lab,red,red)){.border-node-resource\/30{border-color:color-mix(in oklab,var(--color-node-resource) 30%,transparent)}}.border-node-schema\/30{border-color:#fcd34d4d}@supports (color:color-mix(in lab,red,red)){.border-node-schema\/30{border-color:color-mix(in oklab,var(--color-node-schema) 30%,transparent)}}.border-node-service\/30{border-color:#a78bfa4d}@supports (color:color-mix(in lab,red,red)){.border-node-service\/30{border-color:color-mix(in oklab,var(--color-node-service) 30%,transparent)}}.border-node-source\/30{border-color:#8a8a8a4d}@supports (color:color-mix(in lab,red,red)){.border-node-source\/30{border-color:color-mix(in oklab,var(--color-node-source) 30%,transparent)}}.border-node-table\/30{border-color:#6ee7b74d}@supports (color:color-mix(in lab,red,red)){.border-node-table\/30{border-color:color-mix(in oklab,var(--color-node-table) 30%,transparent)}}.border-node-topic\/30{border-color:#c9b06c4d}@supports (color:color-mix(in lab,red,red)){.border-node-topic\/30{border-color:color-mix(in oklab,var(--color-node-topic) 30%,transparent)}}.border-red-400\/30{border-color:#ff65684d}@supports (color:color-mix(in lab,red,red)){.border-red-400\/30{border-color:color-mix(in oklab,var(--color-red-400) 30%,transparent)}}.border-red-700{border-color:var(--color-red-700)}.border-red-700\/50{border-color:#bf000f80}@supports (color:color-mix(in lab,red,red)){.border-red-700\/50{border-color:color-mix(in oklab,var(--color-red-700) 50%,transparent)}}.border-transparent{border-color:#0000}.\!bg-accent\/60{background-color:#d4a57499!important}@supports (color:color-mix(in lab,red,red)){.\!bg-accent\/60{background-color:color-mix(in oklab,var(--color-accent) 60%,transparent)!important}}.\!bg-surface{background-color:var(--color-surface)!important}.\!bg-text-muted{background-color:var(--color-text-muted)!important}.\!bg-text-muted\/40{background-color:#6b5f5366!important}@supports (color:color-mix(in lab,red,red)){.\!bg-text-muted\/40{background-color:color-mix(in oklab,var(--color-text-muted) 40%,transparent)!important}}.bg-\[\#c97070\]\/10{background-color:#c970701a}.bg-\[var\(--color-diff-changed-dim\)\]{background-color:var(--color-diff-changed-dim)}.bg-accent{background-color:var(--color-accent)}.bg-accent-dim\/10{background-color:#c9a96e1a}@supports (color:color-mix(in lab,red,red)){.bg-accent-dim\/10{background-color:color-mix(in oklab,var(--color-accent-dim) 10%,transparent)}}.bg-accent\/5{background-color:#d4a5740d}@supports (color:color-mix(in lab,red,red)){.bg-accent\/5{background-color:color-mix(in oklab,var(--color-accent) 5%,transparent)}}.bg-accent\/10{background-color:#d4a5741a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-accent\/15{background-color:#d4a57426}@supports (color:color-mix(in lab,red,red)){.bg-accent\/15{background-color:color-mix(in oklab,var(--color-accent) 15%,transparent)}}.bg-accent\/20{background-color:#d4a57433}@supports (color:color-mix(in lab,red,red)){.bg-accent\/20{background-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.bg-accent\/50{background-color:#d4a57480}@supports (color:color-mix(in lab,red,red)){.bg-accent\/50{background-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500) 10%,transparent)}}.bg-amber-800\/40{background-color:#953d0066}@supports (color:color-mix(in lab,red,red)){.bg-amber-800\/40{background-color:color-mix(in oklab,var(--color-amber-800) 40%,transparent)}}.bg-amber-900\/20{background-color:#7b330633}@supports (color:color-mix(in lab,red,red)){.bg-amber-900\/20{background-color:color-mix(in oklab,var(--color-amber-900) 20%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-black\/65{background-color:#000000a6}@supports (color:color-mix(in lab,red,red)){.bg-black\/65{background-color:color-mix(in oklab,var(--color-black) 65%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.bg-border-subtle{background-color:var(--color-border-subtle)}.bg-elevated{background-color:var(--color-elevated)}.bg-elevated\/30{background-color:#1a1a1a4d}@supports (color:color-mix(in lab,red,red)){.bg-elevated\/30{background-color:color-mix(in oklab,var(--color-elevated) 30%,transparent)}}.bg-elevated\/40{background-color:#1a1a1a66}@supports (color:color-mix(in lab,red,red)){.bg-elevated\/40{background-color:color-mix(in oklab,var(--color-elevated) 40%,transparent)}}.bg-elevated\/50{background-color:#1a1a1a80}@supports (color:color-mix(in lab,red,red)){.bg-elevated\/50{background-color:color-mix(in oklab,var(--color-elevated) 50%,transparent)}}.bg-elevated\/60{background-color:#1a1a1a99}@supports (color:color-mix(in lab,red,red)){.bg-elevated\/60{background-color:color-mix(in oklab,var(--color-elevated) 60%,transparent)}}.bg-node-article\/10{background-color:#d4a5741a}@supports (color:color-mix(in lab,red,red)){.bg-node-article\/10{background-color:color-mix(in oklab,var(--color-node-article) 10%,transparent)}}.bg-node-claim\/10{background-color:#6fb07a1a}@supports (color:color-mix(in lab,red,red)){.bg-node-claim\/10{background-color:color-mix(in oklab,var(--color-node-claim) 10%,transparent)}}.bg-node-class\/10{background-color:#8b6fb01a}@supports (color:color-mix(in lab,red,red)){.bg-node-class\/10{background-color:color-mix(in oklab,var(--color-node-class) 10%,transparent)}}.bg-node-concept\/10{background-color:#b07a8a1a}@supports (color:color-mix(in lab,red,red)){.bg-node-concept\/10{background-color:color-mix(in oklab,var(--color-node-concept) 10%,transparent)}}.bg-node-config\/10{background-color:#5eead41a}@supports (color:color-mix(in lab,red,red)){.bg-node-config\/10{background-color:color-mix(in oklab,var(--color-node-config) 10%,transparent)}}.bg-node-document\/10{background-color:#7dd3fc1a}@supports (color:color-mix(in lab,red,red)){.bg-node-document\/10{background-color:color-mix(in oklab,var(--color-node-document) 10%,transparent)}}.bg-node-endpoint\/10{background-color:#fdba741a}@supports (color:color-mix(in lab,red,red)){.bg-node-endpoint\/10{background-color:color-mix(in oklab,var(--color-node-endpoint) 10%,transparent)}}.bg-node-entity\/10{background-color:#7ba4c91a}@supports (color:color-mix(in lab,red,red)){.bg-node-entity\/10{background-color:color-mix(in oklab,var(--color-node-entity) 10%,transparent)}}.bg-node-file\/10{background-color:#4a7c9b1a}@supports (color:color-mix(in lab,red,red)){.bg-node-file\/10{background-color:color-mix(in oklab,var(--color-node-file) 10%,transparent)}}.bg-node-function{background-color:var(--color-node-function)}.bg-node-function\/10{background-color:#5a9e6f1a}@supports (color:color-mix(in lab,red,red)){.bg-node-function\/10{background-color:color-mix(in oklab,var(--color-node-function) 10%,transparent)}}.bg-node-module\/10{background-color:#c9a06c1a}@supports (color:color-mix(in lab,red,red)){.bg-node-module\/10{background-color:color-mix(in oklab,var(--color-node-module) 10%,transparent)}}.bg-node-pipeline\/10{background-color:#fda4af1a}@supports (color:color-mix(in lab,red,red)){.bg-node-pipeline\/10{background-color:color-mix(in oklab,var(--color-node-pipeline) 10%,transparent)}}.bg-node-resource\/10{background-color:#a5b4fc1a}@supports (color:color-mix(in lab,red,red)){.bg-node-resource\/10{background-color:color-mix(in oklab,var(--color-node-resource) 10%,transparent)}}.bg-node-schema\/10{background-color:#fcd34d1a}@supports (color:color-mix(in lab,red,red)){.bg-node-schema\/10{background-color:color-mix(in oklab,var(--color-node-schema) 10%,transparent)}}.bg-node-service\/10{background-color:#a78bfa1a}@supports (color:color-mix(in lab,red,red)){.bg-node-service\/10{background-color:color-mix(in oklab,var(--color-node-service) 10%,transparent)}}.bg-node-source\/10{background-color:#8a8a8a1a}@supports (color:color-mix(in lab,red,red)){.bg-node-source\/10{background-color:color-mix(in oklab,var(--color-node-source) 10%,transparent)}}.bg-node-table\/10{background-color:#6ee7b71a}@supports (color:color-mix(in lab,red,red)){.bg-node-table\/10{background-color:color-mix(in oklab,var(--color-node-table) 10%,transparent)}}.bg-node-topic\/10{background-color:#c9b06c1a}@supports (color:color-mix(in lab,red,red)){.bg-node-topic\/10{background-color:color-mix(in oklab,var(--color-node-topic) 10%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.bg-red-800\/40{background-color:#9f071266}@supports (color:color-mix(in lab,red,red)){.bg-red-800\/40{background-color:color-mix(in oklab,var(--color-red-800) 40%,transparent)}}.bg-red-900\/20{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/20{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.bg-red-900\/25{background-color:#82181a40}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/25{background-color:color-mix(in oklab,var(--color-red-900) 25%,transparent)}}.bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.bg-root{background-color:var(--color-root)}.bg-root\/80{background-color:#0a0a0acc}@supports (color:color-mix(in lab,red,red)){.bg-root\/80{background-color:color-mix(in oklab,var(--color-root) 80%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface\/60{background-color:#1119}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-transparent{background-color:#0000}.p-0{padding:calc(var(--spacing) * 0)}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-6{padding-right:calc(var(--spacing) * 6)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-heading{font-family:var(--font-heading)}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.2em\]{--tw-tracking:.2em;letter-spacing:.2em}.tracking-\[0\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#c97070\]{color:#c97070}.text-\[var\(--color-diff-changed\)\]{color:var(--color-diff-changed)}.text-accent{color:var(--color-accent)}.text-accent-dim{color:var(--color-accent-dim)}.text-accent\/60{color:#d4a57499}@supports (color:color-mix(in lab,red,red)){.text-accent\/60{color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}.text-accent\/70{color:#d4a574b3}@supports (color:color-mix(in lab,red,red)){.text-accent\/70{color:color-mix(in oklab,var(--color-accent) 70%,transparent)}}.text-amber-200{color:var(--color-amber-200)}.text-amber-200\/60{color:#fee68599}@supports (color:color-mix(in lab,red,red)){.text-amber-200\/60{color:color-mix(in oklab,var(--color-amber-200) 60%,transparent)}}.text-amber-200\/80{color:#fee685cc}@supports (color:color-mix(in lab,red,red)){.text-amber-200\/80{color:color-mix(in oklab,var(--color-amber-200) 80%,transparent)}}.text-amber-400{color:var(--color-amber-400)}.text-amber-400\/60{color:#fcbb0099}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/60{color:color-mix(in oklab,var(--color-amber-400) 60%,transparent)}}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-node-article{color:var(--color-node-article)}.text-node-claim{color:var(--color-node-claim)}.text-node-class{color:var(--color-node-class)}.text-node-concept{color:var(--color-node-concept)}.text-node-config{color:var(--color-node-config)}.text-node-document{color:var(--color-node-document)}.text-node-endpoint{color:var(--color-node-endpoint)}.text-node-entity{color:var(--color-node-entity)}.text-node-file{color:var(--color-node-file)}.text-node-function{color:var(--color-node-function)}.text-node-module{color:var(--color-node-module)}.text-node-pipeline{color:var(--color-node-pipeline)}.text-node-resource{color:var(--color-node-resource)}.text-node-schema{color:var(--color-node-schema)}.text-node-service{color:var(--color-node-service)}.text-node-source{color:var(--color-node-source)}.text-node-table{color:var(--color-node-table)}.text-node-topic{color:var(--color-node-topic)}.text-orange-300\/80{color:#ffb96dcc}@supports (color:color-mix(in lab,red,red)){.text-orange-300\/80{color:color-mix(in oklab,var(--color-orange-300) 80%,transparent)}}.text-orange-400{color:var(--color-orange-400)}.text-red-200{color:var(--color-red-200)}.text-red-200\/70{color:#ffcacab3}@supports (color:color-mix(in lab,red,red)){.text-red-200\/70{color:color-mix(in oklab,var(--color-red-200) 70%,transparent)}}.text-red-300{color:var(--color-red-300)}.text-red-400{color:var(--color-red-400)}.text-red-400\/60{color:#ff656899}@supports (color:color-mix(in lab,red,red)){.text-red-400\/60{color:color-mix(in oklab,var(--color-red-400) 60%,transparent)}}.text-root{color:var(--color-root)}.text-text-muted{color:var(--color-text-muted)}.text-text-muted\/40{color:#6b5f5366}@supports (color:color-mix(in lab,red,red)){.text-text-muted\/40{color:color-mix(in oklab,var(--color-text-muted) 40%,transparent)}}.text-text-muted\/60{color:#6b5f5399}@supports (color:color-mix(in lab,red,red)){.text-text-muted\/60{color:color-mix(in oklab,var(--color-text-muted) 60%,transparent)}}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-yellow-400{color:var(--color-yellow-400)}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.line-through{text-decoration-line:line-through}.placeholder-text-muted::placeholder{color:var(--color-text-muted)}.opacity-0{opacity:0}.opacity-20{opacity:.2}.opacity-50{opacity:.5}.opacity-100{opacity:1}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_4px_rgba\(90\,158\,111\,0\.6\)\]{--tw-shadow:0 0 4px var(--tw-shadow-color,#5a9e6f99);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_2px_8px_rgba\(0\,0\,0\,0\.3\)\]{--tw-shadow:0 2px 8px var(--tw-shadow-color,#0000004d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-accent\/10{--tw-shadow-color:#d4a5741a}@supports (color:color-mix(in lab,red,red)){.shadow-accent\/10{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-accent) 10%, transparent) var(--tw-shadow-alpha), transparent)}}.ring-\[var\(--color-diff-affected\)\]{--tw-ring-color:var(--color-diff-affected)}.ring-\[var\(--color-diff-changed\)\]{--tw-ring-color:var(--color-diff-changed)}.ring-accent{--tw-ring-color:var(--color-accent)}.ring-accent-bright{--tw-ring-color:var(--color-accent-bright)}.ring-accent-dim{--tw-ring-color:var(--color-accent-dim)}.ring-accent-dim\/60{--tw-ring-color:#c9a96e99}@supports (color:color-mix(in lab,red,red)){.ring-accent-dim\/60{--tw-ring-color:color-mix(in oklab, var(--color-accent-dim) 60%, transparent)}}.ring-text-primary{--tw-ring-color:var(--color-text-primary)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-root{--tw-ring-offset-color:var(--color-root)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[box-shadow\,outline\,opacity\,filter\]{transition-property:box-shadow,outline,opacity,filter;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-text-muted\/50::placeholder{color:#6b5f5380}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-text-muted\/50::placeholder{color:color-mix(in oklab,var(--color-text-muted) 50%,transparent)}}.last\:mb-0:last-child{margin-bottom:calc(var(--spacing) * 0)}@media(hover:hover){.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:border-accent\/40:hover{border-color:#d4a57466}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/40:hover{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.hover\:border-accent\/50:hover{border-color:#d4a57480}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/50:hover{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.hover\:border-accent\/60:hover{border-color:#d4a57499}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/60:hover{border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}.hover\:border-accent\/70:hover{border-color:#d4a574b3}@supports (color:color-mix(in lab,red,red)){.hover\:border-accent\/70:hover{border-color:color-mix(in oklab,var(--color-accent) 70%,transparent)}}.hover\:bg-accent\/5:hover{background-color:#d4a5740d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/5:hover{background-color:color-mix(in oklab,var(--color-accent) 5%,transparent)}}.hover\:bg-accent\/10:hover{background-color:#d4a5741a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/10:hover{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.hover\:bg-accent\/20:hover{background-color:#d4a57433}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/20:hover{background-color:color-mix(in oklab,var(--color-accent) 20%,transparent)}}.hover\:bg-amber-800\/60:hover{background-color:#953d0099}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-800\/60:hover{background-color:color-mix(in oklab,var(--color-amber-800) 60%,transparent)}}.hover\:bg-amber-900\/10:hover{background-color:#7b33061a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-900\/10:hover{background-color:color-mix(in oklab,var(--color-amber-900) 10%,transparent)}}.hover\:bg-elevated:hover{background-color:var(--color-elevated)}.hover\:bg-elevated\/40:hover{background-color:#1a1a1a66}@supports (color:color-mix(in lab,red,red)){.hover\:bg-elevated\/40:hover{background-color:color-mix(in oklab,var(--color-elevated) 40%,transparent)}}.hover\:bg-elevated\/50:hover{background-color:#1a1a1a80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-elevated\/50:hover{background-color:color-mix(in oklab,var(--color-elevated) 50%,transparent)}}.hover\:bg-elevated\/80:hover{background-color:#1a1a1acc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-elevated\/80:hover{background-color:color-mix(in oklab,var(--color-elevated) 80%,transparent)}}.hover\:bg-red-800\/60:hover{background-color:#9f071299}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-800\/60:hover{background-color:color-mix(in oklab,var(--color-red-800) 60%,transparent)}}.hover\:bg-red-900\/15:hover{background-color:#82181a26}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-900\/15:hover{background-color:color-mix(in oklab,var(--color-red-900) 15%,transparent)}}.hover\:bg-surface:hover{background-color:var(--color-surface)}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-accent-bright:hover{color:var(--color-accent-bright)}.hover\:text-text-muted:hover{color:var(--color-text-muted)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:text-text-secondary:hover{color:var(--color-text-secondary)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:brightness-110:hover{--tw-brightness:brightness(110%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:border-accent\/50:focus{border-color:#d4a57480}@supports (color:color-mix(in lab,red,red)){.focus\:border-accent\/50:focus{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.focus\:ring-0:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-\[rgba\(212\,165\,116\,0\.6\)\]:focus{--tw-ring-color:#d4a57499}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media(min-width:40rem){.sm\:block{display:block}.sm\:inline{display:inline}.sm\:h-\[calc\(100vh-48px\)\]{height:calc(100vh - 48px)}.sm\:max-w-\[220px\]{max-width:220px}.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:gap-5{gap:calc(var(--spacing) * 5)}.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:px-3{padding-inline:calc(var(--spacing) * 3)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media(min-width:48rem){.md\:col-span-3{grid-column:span 3/span 3}.md\:block{display:block}.md\:inline{display:inline}.md\:w-\[300px\]{width:300px}.md\:grid-cols-\[120px_160px_1fr_160px_100px\]{grid-template-columns:120px 160px 1fr 160px 100px}}@media(min-width:64rem){.lg\:w-\[360px\]{width:360px}.lg\:max-w-none{max-width:none}.lg\:grid-cols-\[1fr_440px\]{grid-template-columns:1fr 440px}}}.font-heading{font-family:var(--font-heading)}html{transition:background-color .2s,color .2s}body{font-family:var(--font-sans);background-color:var(--color-root);color:var(--color-text-primary);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.noise-overlay:before{content:"";opacity:.03;pointer-events:none;z-index:9999;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");width:100%;height:100%;position:fixed;top:0;left:0}.glass{background:var(--glass-bg);border:1px solid var(--glass-border);-webkit-backdrop-filter:blur(12px)}.glass-heavy{background:var(--glass-bg-heavy);border:1px solid var(--glass-border-heavy);-webkit-backdrop-filter:blur(16px)}.kbd{min-width:1.75rem;height:1.75rem;font-family:var(--font-mono);color:var(--color-accent);background:var(--kbd-bg);border:1px solid var(--color-border-medium);box-shadow:0 1px 0 var(--scrollbar-thumb);border-radius:.25rem;justify-content:center;align-items:center;padding:0 .5rem;font-size:.75rem;font-weight:600;display:inline-flex}@keyframes fadeSlideIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes accentPulse{0%,to{box-shadow:0 0 8px var(--glow-accent-strong)}50%{box-shadow:0 0 20px var(--glow-accent-pulse)}}.animate-fade-slide-in{animation:.3s ease-out forwards fadeSlideIn}.animate-slide-up{animation:.3s ease-out forwards slideUp}.animate-accent-pulse{animation:2s ease-in-out infinite accentPulse}.node-glow{box-shadow:0 0 20px var(--glow-accent)}.diff-changed-glow{box-shadow:0 0 16px #e0525240}.diff-affected-glow{box-shadow:0 0 12px #d4a03033}.diff-faded{opacity:.25;filter:saturate(.3);transition:opacity .3s,filter .3s}.line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.markdown-reader{width:min(100%,980px);color:var(--color-text-primary);font-family:var(--font-sans);letter-spacing:.01em;margin:0 auto;padding:32px 40px 56px;font-size:15px;line-height:1.85}.markdown-reader-modal{width:min(100%,1040px);padding:40px 56px 72px;font-size:16px}.markdown-reader h1,.markdown-reader h2,.markdown-reader h3,.markdown-reader h4,.markdown-reader h5,.markdown-reader h6{color:var(--color-text-primary);font-family:var(--font-sans);letter-spacing:-.01em;scroll-margin-top:24px;font-weight:750;line-height:1.32}.markdown-reader h1{border-bottom:1px solid var(--color-border-medium);margin:0 0 24px;padding-bottom:16px;font-size:2rem}.markdown-reader h2{border-left:3px solid var(--color-accent);margin:42px 0 18px;padding-left:14px;font-size:1.5rem}.markdown-reader h3{color:var(--color-accent-bright);margin:32px 0 14px;font-size:1.18rem}.markdown-reader h4,.markdown-reader h5,.markdown-reader h6{color:var(--color-text-secondary);margin:24px 0 10px;font-size:1rem}.markdown-reader p,.markdown-reader ul,.markdown-reader ol,.markdown-reader blockquote,.markdown-reader table,.markdown-reader pre{margin:0 0 18px}.markdown-reader p{color:var(--color-text-secondary)}.markdown-reader strong{color:var(--color-text-primary);font-weight:700}.markdown-reader a{color:var(--color-node-document);border-bottom:1px solid #7dd3fc73;text-decoration:none}@supports (color:color-mix(in lab,red,red)){.markdown-reader a{border-bottom:1px solid color-mix(in srgb,var(--color-node-document) 45%,transparent)}}.markdown-reader ul,.markdown-reader ol{color:var(--color-text-secondary);padding-left:1.5rem}.markdown-reader li{margin:7px 0;padding-left:4px}.markdown-reader li::marker{color:var(--color-accent)}.markdown-reader blockquote{border-left:3px solid var(--color-border-medium);background:#d4a57412}@supports (color:color-mix(in lab,red,red)){.markdown-reader blockquote{background:color-mix(in srgb,var(--color-accent) 7%,transparent)}}.markdown-reader blockquote{color:var(--color-text-secondary);border-radius:0 10px 10px 0;padding:14px 18px}.markdown-reader code{color:var(--color-accent-bright);background:#d4a5741f}@supports (color:color-mix(in lab,red,red)){.markdown-reader code{background:color-mix(in srgb,var(--color-accent) 12%,transparent)}}.markdown-reader code{border:1px solid var(--color-border-subtle);font-family:var(--font-mono);border-radius:5px;padding:.12em .36em;font-size:.9em}.markdown-reader pre{border:1px solid var(--color-border-subtle);background:#0d0d0d;border-radius:12px;overflow:auto}@supports (color:color-mix(in lab,red,red)){.markdown-reader pre{background:color-mix(in srgb,var(--color-root) 82%,var(--color-elevated))}}.markdown-reader pre{padding:18px 20px;line-height:1.65}.markdown-reader pre code{color:var(--color-text-primary);white-space:pre;background:0 0;border:0;padding:0;font-size:.88em;display:block}.markdown-reader hr{border:0;border-top:1px solid var(--color-border-subtle);margin:34px 0}.markdown-reader table{border-collapse:collapse;border:1px solid var(--color-border-subtle);border-radius:10px;width:100%;display:block;overflow:auto}.markdown-reader th,.markdown-reader td{border:1px solid var(--color-border-subtle);vertical-align:top;padding:10px 12px}.markdown-reader th{background:#d4a5741c}@supports (color:color-mix(in lab,red,red)){.markdown-reader th{background:color-mix(in srgb,var(--color-accent) 11%,transparent)}}.markdown-reader th{color:var(--color-text-primary);font-weight:700}.markdown-reader td{color:var(--color-text-secondary)}@media(max-width:768px){.markdown-reader,.markdown-reader-modal{padding:24px 20px 44px;font-size:15px}.markdown-reader h1{font-size:1.6rem}.markdown-reader h2{font-size:1.28rem}}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}.react-flow__background{background-color:var(--color-root)!important}[data-theme=light]{color-scheme:light}[data-theme=light] .diff-faded{opacity:.35}[data-theme=light] ::-webkit-scrollbar-track{background:#0000000d}[data-theme=light] .warning-banner{color:#92600a;background:#b4821e1a;border-color:#b4821e4d}[data-theme=dark]{color-scheme:dark}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1} diff --git a/wishfulfilled-dashboard/index.html b/wishfulfilled-dashboard/index.html index 883f698..1deca25 100644 --- a/wishfulfilled-dashboard/index.html +++ b/wishfulfilled-dashboard/index.html @@ -11,14 +11,14 @@ href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" /> - + - +
diff --git a/wishfulfilled-dashboard/knowledge-graph.json b/wishfulfilled-dashboard/knowledge-graph.json index 556fd59..f4b3865 100644 --- a/wishfulfilled-dashboard/knowledge-graph.json +++ b/wishfulfilled-dashboard/knowledge-graph.json @@ -11,7 +11,7 @@ "Obsidian" ], "description": "按需求文档、里程碑、技术文档、测试相关、Agent检索组织的流程式知识库。", - "analyzedAt": "2026-05-27T07:14:44.968Z", + "analyzedAt": "2026-05-29T06:03:33.440Z", "gitCommitHash": "" }, "nodes": [ @@ -490,15 +490,26 @@ "type": "document", "name": "技术文档", "filePath": "07_技术文档/README.md", - "summary": "本目录用于存放系统架构、数据模型、接口说明、实现方案、部署说明和技术决策记录。", + "summary": "type: technical docs home tags: 技术文档, 架构, 开发, 知识库 aliases: 技术文档入口, 技术资料 source: manual status: active owner: 技术负责人 updated: 2026 05 技术文档 本目录用于存放系统架构、数据模型、接口说明、实现方案、部署说明和技术决策记录。 二级入", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "架构", + "开发", + "知识库" ], "complexity": "simple", "knowledgeMeta": { "content": "---\ntype: technical_docs_home\ntags: [技术文档, 架构, 开发, 知识库]\naliases: [技术文档入口, 技术资料]\nsource: manual\nstatus: active\nowner: 技术负责人\nupdated: 2026-05\n---\n\n# 技术文档\n\n本目录用于存放系统架构、数据模型、接口说明、实现方案、部署说明和技术决策记录。\n\n## 二级入口\n\n- [[技术文档索引]]\n- [[系统架构说明模板]]\n- [[接口说明模板]]\n- [[技术决策记录]]\n\n## 存放内容\n\n- 系统架构说明\n- 模块设计说明\n- 数据表/业务对象设计\n- API 接口说明\n- 权限与安全设计\n- 部署与配置说明\n- 技术决策记录\n\n## 命名建议\n\n```text\n系统或模块_技术方案_YYYYMMDD.md\n系统或模块_接口说明_YYYYMMDD.md\n系统或模块_数据模型_YYYYMMDD.md\n```\n\n## 关联目录\n\n- 需求依据:[[../05_需求文档/README|需求文档]]\n- 测试依据:[[../08_测试相关/README|测试相关]]\n- 里程碑:[[../06_里程碑/README|里程碑]]\n", - "wikilinks": [], + "wikilinks": [ + "技术文档索引", + "系统架构说明模板", + "接口说明模板", + "技术决策记录", + "../05_需求文档/README|需求文档", + "../08_测试相关/README|测试相关", + "../06_里程碑/README|里程碑" + ], "category": "layer-technical" } }, @@ -507,10 +518,12 @@ "type": "document", "name": "技术决策记录", "filePath": "07_技术文档/技术决策记录.md", - "summary": "知识库文档。", + "summary": "type: adr log tags: 技术文档, 技术决策, ADR aliases: 技术决策, ADR source: manual status: active owner: 技术负责人 updated: 2026 05 技术决策记录 日期 决策主题 背景 决策结论 影响范围 关联需求/技术文档", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "技术决策", + "ADR" ], "complexity": "simple", "knowledgeMeta": { @@ -524,10 +537,12 @@ "type": "document", "name": "技术文档索引", "filePath": "07_技术文档/技术文档索引.md", - "summary": "- 新增技术方案、接口说明、数据模型后,在本索引登记。", + "summary": "type: technical docs index tags: 技术文档, 索引, Agent检索 aliases: 技术索引, 技术资料清单 source: manual status: active owner: 技术负责人 updated: 2026 05 技术文档索引 技术文档清单 模块/系统 文档类型 文件 关联需求 负责人 更新时间 状态 Ag", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "索引", + "Agent检索" ], "complexity": "simple", "knowledgeMeta": { @@ -541,10 +556,12 @@ "type": "document", "name": "接口说明模板", "filePath": "07_技术文档/接口说明模板.md", - "summary": "- 关键词:", + "summary": "type: api template tags: 技术文档, 接口, 模板 aliases: 接口模板, API说明模板 source: manual status: active owner: 技术负责人 updated: 2026 05 接口说明模板 基本信息 项目 内容 接口名称 所属模块 关联需求 负责人 状态 draft 接口用途 请求说明 字段 ", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "接口", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -558,10 +575,12 @@ "type": "document", "name": "系统架构说明模板", "filePath": "07_技术文档/系统架构说明模板.md", - "summary": "- 关键词:", + "summary": "type: architecture template tags: 技术文档, 架构, 模板 aliases: 架构说明模板 source: manual status: active owner: 技术负责人 updated: 2026 05 系统架构说明模板 基本信息 项目 内容 系统/模块 关联需求 负责人 状态 draft 背景与目标 架构说明 模块", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "架构", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -575,15 +594,31 @@ "type": "document", "name": "测试相关", "filePath": "08_测试相关/README.md", - "summary": "本目录用于存放测试计划、测试用例、测试报告、缺陷记录、验收记录和上线检查材料。", + "summary": "type: testing home tags: 测试, 测试用例, 验收, 知识库 aliases: 测试相关入口, 测试文档 source: manual status: active owner: 测试负责人 updated: 2026 05 测试相关 本目录用于存放测试计划、测试用例、测试报告、缺陷记录、验收记录和上线检查材料。 二级入口 测试用例索", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试", + "测试用例", + "验收", + "知识库" ], "complexity": "simple", "knowledgeMeta": { "content": "---\ntype: testing_home\ntags: [测试, 测试用例, 验收, 知识库]\naliases: [测试相关入口, 测试文档]\nsource: manual\nstatus: active\nowner: 测试负责人\nupdated: 2026-05\n---\n\n# 测试相关\n\n本目录用于存放测试计划、测试用例、测试报告、缺陷记录、验收记录和上线检查材料。\n\n## 二级入口\n\n- [[测试用例索引]]\n- [[测试用例模板]]\n- [[测试计划模板]]\n- [[缺陷记录模板]]\n- [[验收记录模板]]\n- [[上线检查模板]]\n\n## 存放内容\n\n- 测试计划\n- 测试用例\n- 测试执行记录\n- 缺陷记录\n- 验收记录\n- 上线检查记录\n- 回归测试说明\n\n## 命名建议\n\n```text\n项目或模块_测试用例_YYYYMMDD.md\n项目或模块_测试计划_YYYYMMDD.md\n项目或模块_缺陷记录_YYYYMMDD.md\n项目或模块_验收记录_YYYYMMDD.md\n```\n\n## 关联目录\n\n- 需求依据:[[../05_需求文档/README|需求文档]]\n- 技术依据:[[../07_技术文档/README|技术文档]]\n- 里程碑依据:[[../06_里程碑/README|里程碑]]\n- 流程依据:[[../02_项目管理流程/阶段2.5_测试提前补漏|阶段2.5 测试提前补漏]]、[[../02_项目管理流程/阶段4_测试培训上线回流|阶段4 测试培训上线回流]]\n", - "wikilinks": [], + "wikilinks": [ + "测试用例索引", + "测试用例模板", + "测试计划模板", + "缺陷记录模板", + "验收记录模板", + "上线检查模板", + "../05_需求文档/README|需求文档", + "../07_技术文档/README|技术文档", + "../06_里程碑/README|里程碑", + "../02_项目管理流程/阶段2.5_测试提前补漏|阶段2.5 测试提前补漏", + "../02_项目管理流程/阶段4_测试培训上线回流|阶段4 测试培训上线回流" + ], "category": "layer-testing" } }, @@ -592,10 +627,13 @@ "type": "document", "name": "上线检查模板", "filePath": "08_测试相关/上线检查模板.md", - "summary": "- 关键词:", + "summary": "type: go live checklist template tags: 上线检查, 测试, 模板 aliases: 上线检查, 发布检查 source: manual status: active owner: 测试负责人 / 项目经理 updated: 2026 05 上线检查模板 基本信息 项目 内容 项目/模块 关联需求 关联里程碑 负责人 检查", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "上线检查", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -609,10 +647,13 @@ "type": "document", "name": "测试用例模板", "filePath": "08_测试相关/测试用例模板.md", - "summary": "- 关键词:", + "summary": "type: test case template tags: 测试用例, 测试, 模板 aliases: 用例模板, 测试用例 source: manual status: active owner: 测试负责人 updated: 2026 05 测试用例模板 基本信息 项目 内容 项目/模块 关联需求 关联技术文档 测试负责人 状态 draft 测试范围 ", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试用例", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -626,10 +667,14 @@ "type": "document", "name": "测试用例索引", "filePath": "08_测试相关/测试用例索引.md", - "summary": "- 新增测试用例后,必须在本索引登记。", + "summary": "type: test case index tags: 测试用例, 测试, 索引, Agent检索 aliases: 测试用例清单, 用例索引 source: manual status: active owner: 测试负责人 updated: 2026 05 测试用例索引 测试用例清单 编号 项目/模块 用例集名称 文件 关联需求 关联技术文档 负责人 ", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试用例", + "测试", + "索引", + "Agent检索" ], "complexity": "simple", "knowledgeMeta": { @@ -643,10 +688,13 @@ "type": "document", "name": "测试计划模板", "filePath": "08_测试相关/测试计划模板.md", - "summary": "- 关键词:", + "summary": "type: test plan template tags: 测试计划, 测试, 模板 aliases: 测试计划模板 source: manual status: active owner: 测试负责人 updated: 2026 05 测试计划模板 基本信息 项目 内容 项目/模块 关联需求 关联里程碑 测试负责人 计划周期 测试目标 测试范围 不在范围", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试计划", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -660,10 +708,13 @@ "type": "document", "name": "缺陷记录模板", "filePath": "08_测试相关/缺陷记录模板.md", - "summary": "1.", + "summary": "type: defect template tags: 缺陷, 测试, 模板 aliases: Bug记录模板, 缺陷记录 source: manual status: active owner: 测试负责人 updated: 2026 05 缺陷记录模板 基本信息 项目 内容 缺陷编号 BUG 项目/模块 关联需求 关联用例 严重级别 当前状态 open ", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "缺陷", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -677,10 +728,13 @@ "type": "document", "name": "验收记录模板", "filePath": "08_测试相关/验收记录模板.md", - "summary": "- 关键词:", + "summary": "type: acceptance template tags: 验收, 测试, 模板 aliases: 验收记录, UAT模板 source: manual status: active owner: 测试负责人 / 业务负责人 updated: 2026 05 验收记录模板 基本信息 项目 内容 项目/模块 关联需求 关联测试用例 验收负责人 验收日期 验", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "验收", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -748,7 +802,7 @@ ], "complexity": "simple", "knowledgeMeta": { - "content": "# 需求文档\n\n所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。\n\n本层包含 21 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", + "content": "# 需求文档\n\n所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。\n\n本层包含 32 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", "wikilinks": [], "category": "layer-requirements" } @@ -773,14 +827,14 @@ "id": "flow:layer-technical", "type": "document", "name": "4. 技术文档", - "summary": "系统架构、数据模型、接口说明、技术方案和技术决策。", + "summary": "系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。", "tags": [ "流程入口", "技术文档" ], "complexity": "simple", "knowledgeMeta": { - "content": "# 技术文档\n\n系统架构、数据模型、接口说明、技术方案和技术决策。\n\n本层包含 5 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", + "content": "# 技术文档\n\n系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。\n\n本层包含 6 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", "wikilinks": [], "category": "layer-technical" } @@ -789,14 +843,14 @@ "id": "flow:layer-testing", "type": "document", "name": "5. 测试相关", - "summary": "测试计划、测试用例、缺陷记录、验收记录和上线检查。", + "summary": "测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。", "tags": [ "流程入口", "测试相关" ], "complexity": "simple", "knowledgeMeta": { - "content": "# 测试相关\n\n测试计划、测试用例、缺陷记录、验收记录和上线检查。\n\n本层包含 7 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", + "content": "# 测试相关\n\n测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。\n\n本层包含 8 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", "wikilinks": [], "category": "layer-testing" } @@ -1139,6 +1193,227 @@ "wikilinks": [], "category": "layer-requirements" } + }, + { + "id": "doc:05_需求文档/通用EDM业务流程说明", + "type": "document", + "name": "通用 EDM 业务流程说明", + "filePath": "05_需求文档/通用EDM业务流程说明.md", + "summary": "通用 EDM 业务流程说明 更新时间:2026 05 26 1. 文档目标 本文用于新的 EDM 子系统设计或重构,目标是在功能保持一致的前提下,将现有 EDM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、接入邮件发送通道和处理邮件客服工单。 2. 业务范围 通用 EDM 子系统建议分为三条业务线: 业务线 说明 批量营销", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# 通用 EDM 业务流程说明\n\n更新时间:2026-05-26\n\n## 1. 文档目标\n\n本文用于新的 EDM 子系统设计或重构,目标是在功能保持一致的前提下,将现有 EDM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、接入邮件发送通道和处理邮件客服工单。\n\n## 2. 业务范围\n\n通用 EDM 子系统建议分为三条业务线:\n\n| 业务线 | 说明 |\n| --- | --- |\n| 批量营销邮件 | 管理后台创建邮件任务,按标签、站点、产品、用户状态筛选目标用户,生成待发送邮件记录,通过队列异步发送 |\n| 自动 / 实时策略邮件 | 根据用户注册、访问、在线、站点、产品、行为、无消息等规则自动筛选用户,并生成策略邮件 |\n| 邮件工单 | 用户来信、表单提交或外部收信服务进入后台后,生成或更新邮件工单,由客服处理、回复、转发、关闭 |\n\n如果新系统还需要普通邮箱功能,可以作为独立模块处理。普通邮箱收发不一定进入 EDM 工单链路,是否合并需要单独确认。\n\n## 3. 总体架构\n\n```mermaid\nflowchart TD\n A[\"管理后台\"] --> B[\"EDM 任务服务\"]\n B --> C[\"目标用户筛选服务\"]\n C --> D[\"邮件记录生成服务\"]\n D --> E[\"Kafka / 队列\"]\n E --> F[\"邮件发送消费者\"]\n F --> G[\"外部发送通道\"]\n G --> H[\"AWS SES / SMTP / Gmail / Microsoft\"]\n H --> I[\"事件回调\"]\n I --> J[\"事件处理服务\"]\n J --> K[\"统计与黑名单\"]\n\n L[\"用户来信 / 表单提交\"] --> M[\"入站接收服务\"]\n M --> N[\"Kafka / 入站队列\"]\n N --> O[\"EDM 工单服务\"]\n O --> P[\"客服工作台\"]\n P --> E\n```\n\n核心组件职责:\n\n| 组件 | 职责 |\n| --- | --- |\n| 管理后台 | 创建邮件任务、审核任务、查看统计、处理邮件工单 |\n| 任务服务 | 保存任务配置、正文、模板、发送时间、审核状态 |\n| 用户筛选服务 | 根据标签、站点、产品、黑名单、订阅状态、发送频率等规则筛选目标用户 |\n| 邮件记录服务 | 按用户生成单封待发送邮件记录和正文快照 |\n| Kafka / 队列 | 解耦任务生成、邮件发送、入站消息、事件统计 |\n| 发送消费者 | 消费待发送邮件,调用外部发送通道,并保存发送结果 |\n| 入站接收服务 | 接收表单、用户来信或外部邮件服务回调,写入入站队列 |\n| 工单服务 | 根据来信生成或更新邮件工单,维护状态、负责人、未读数和处理记录 |\n| 事件处理服务 | 处理送达、打开、点击、退信、投诉、拒信等邮件事件 |\n| Redis / 缓存 | 保存并发锁、游标、限流计数、近期任务统计、临时筛选集合 |\n\n## 4. 核心数据模型\n\n新子系统建议至少抽象以下对象:\n\n| 对象 | 说明 |\n| --- | --- |\n| 邮件任务 EmailTask | 批量营销或策略邮件任务,保存任务名称、类型、发送时间、审核状态、目标条件 |\n| 邮件内容 EmailContent | 任务级正文、标题、模板、发件人、回复地址、附件配置 |\n| 目标用户 TaskRecipient | 任务命中的用户关系,便于统计和去重 |\n| 单封邮件 EmailMessage | 最终发送或接收的一封邮件记录,包含方向、收件人、发件人、状态、message_id、工单 ID |\n| 邮件正文 EmailBody | 单封邮件正文快照,避免模板后续变化影响历史邮件 |\n| 工单 EmailTicket | 用户来信或客服主动发起的一次处理过程 |\n| 分配记录 Assignment | 工单分配、移交、释放、代班等操作记录 |\n| 节点日志 NodeLog | 创建、分配、首次回复、关闭、未解决、转化中等关键节点 |\n| 发送事件 EmailEvent | 送达、打开、点击、退信、投诉、拒信、渲染失败等事件 |\n| 黑名单 / 退订名单 Suppression | 退信、投诉、退订、风险用户等不可发送或需谨慎发送的人群 |\n\n## 5. 批量营销邮件流程\n\n```mermaid\nflowchart TD\n A[\"后台创建邮件任务\"] --> B[\"校验任务配置\"]\n B --> C[\"写入任务、内容、标签条件\"]\n C --> D[\"进入待审核\"]\n D --> E{\"审核结果\"}\n E -->|通过| F[\"进入待执行\"]\n E -->|驳回| G[\"记录驳回原因并结束\"]\n F --> H[\"到达发送时间\"]\n H --> I[\"筛选目标用户\"]\n I --> J[\"生成单封待发送邮件记录\"]\n J --> K[\"投递 Kafka\"]\n K --> L[\"发送消费者调用外部邮件通道\"]\n L --> M[\"更新发送状态和 message_id\"]\n```\n\n创建任务时建议校验:\n\n1. 任务名称不能重复。\n2. 邮件模板或正文必须存在。\n3. 发件邮箱必须存在并可用。\n4. 发件域名必须在允许范围内。\n5. 必须选择目标人群或策略条件。\n6. 发送时间必须符合业务规则。\n7. 如果绑定活动,发送时间需要满足活动时间约束。\n8. 目标人数需要预估,避免误发全量用户。\n\n目标用户筛选建议包含:\n\n1. 标签包含和标签排除。\n2. 站点、产品、品牌、语言、地区。\n3. 订阅状态、退订状态、黑名单、投诉用户、永久退信用户。\n4. 近期发送频率限制,避免短时间重复触达。\n5. 任务级去重,避免同一用户重复生成同一任务邮件。\n\n## 6. 自动 / 实时策略邮件流程\n\n```mermaid\nflowchart TD\n A[\"策略配置\"] --> B[\"定时任务生成当日策略任务\"]\n B --> C[\"实时策略扫描\"]\n C --> D[\"按用户行为和条件筛选\"]\n D --> E[\"应用黑名单、退订、频率控制\"]\n E --> F[\"生成待发送邮件\"]\n F --> G[\"投递发送队列\"]\n G --> H[\"发送消费者调用邮件通道\"]\n H --> I[\"事件回调更新统计\"]\n```\n\n策略邮件与批量邮件的区别:\n\n1. 批量邮件通常由运营手动创建,发送时间明确。\n2. 策略邮件通常由系统按规则自动生成,可能按分钟或按天扫描。\n3. 策略邮件更依赖幂等和频率控制,避免同一用户在同一策略下反复触发。\n4. 策略邮件应记录策略 ID、触发原因、触发时间,便于归因。\n\n建议策略执行时做并发锁,避免多个任务实例重复生成邮件。\n\n## 7. 邮件发送链路\n\n通用发送链路:\n\n```mermaid\nflowchart TD\n A[\"待发送邮件记录\"] --> B[\"写入 Kafka\"]\n B --> C[\"发送消费者\"]\n C --> D[\"读取发件通道配置\"]\n D --> E{\"发送通道\"}\n E -->|批量营销| F[\"AWS SES 或批量发送通道\"]\n E -->|客服回复| G[\"SMTP / Gmail / Microsoft\"]\n F --> H[\"保存发送结果\"]\n G --> H\n H --> I[\"通知前端或更新统计\"]\n```\n\n发送消费者需要处理:\n\n1. 队列消息反序列化。\n2. 邮件正文、标题、收件人、发件人、回复地址、附件组装。\n3. 发送通道选择。\n4. 调用外部服务。\n5. 成功后保存 `message_id`、发送时间和成功状态。\n6. 失败后保存错误信息、失败状态和重试次数。\n\n发送通道建议按场景区分:\n\n| 场景 | 推荐处理 |\n| --- | --- |\n| 批量营销邮件 | 走支持批量和事件回调的邮件服务,例如 AWS SES |\n| 策略邮件 | 可复用批量发送通道,但必须做频率和幂等控制 |\n| 工单客服回复 | 按发件邮箱配置选择 SMTP、Gmail API 或 Microsoft Graph |\n| 普通邮箱回复 | 可独立于工单链路,同步或异步发送均可 |\n\n## 8. 邮件事件回调与统计\n\n邮件发送后,外部服务会产生事件。通用事件包括:\n\n| 事件 | 处理建议 |\n| --- | --- |\n| Delivery / 送达 | 标记邮件已送达,记录送达时间和发送 IP |\n| Bounce / 退信 | 区分永久退信和临时退信,更新任务统计;永久退信可加入黑名单 |\n| Open / 打开 | 标记打开时间,更新任务打开统计 |\n| Click / 点击 | 记录点击链接和点击时间,更新点击统计 |\n| Complaint / 投诉 | 记录投诉,加入抑制名单或黑名单 |\n| Subscription / 订阅变更 | 更新订阅或退订状态 |\n| Reject / 拒信 | 记录拒信原因,更新失败统计 |\n| Rendering Failure / 渲染失败 | 记录模板或内容渲染失败 |\n| DeliveryDelay / 延迟 | 可记录延迟事件,是否统计需业务确认 |\n\n事件处理要点:\n\n1. 事件必须通过 `message_id` 或自定义追踪 ID 关联到本地邮件记录。\n2. 同一事件可能重复回调,需要幂等处理。\n3. 打开和点击事件存在图片加载、隐私保护、客户端屏蔽等不确定性,统计只能作为参考指标。\n4. 投诉、退订、永久退信应优先进入发送抑制规则。\n\n## 9. 入站邮件 / 表单进入工单流程\n\n入站来源可以有多种:\n\n1. 网站表单提交。\n2. 用户真实邮件来信。\n3. 外部收信服务回调。\n4. IM 或其他渠道转入邮件客服。\n\n通用流程:\n\n```mermaid\nflowchart TD\n A[\"用户来信或表单提交\"] --> B[\"入站接收服务\"]\n B --> C[\"写入 Kafka 入站队列\"]\n C --> D[\"EDM 工单消费者\"]\n D --> E[\"保存入站邮件和正文\"]\n E --> F{\"是否存在未关闭工单\"}\n F -->|否| G[\"创建新工单\"]\n F -->|是| H[\"绑定到原工单并更新未读数\"]\n G --> I[\"写入节点日志\"]\n H --> I\n I --> J[\"通知客服工作台\"]\n```\n\n创建或更新工单时建议:\n\n1. 以发件邮箱、收件邮箱、业务用户 ID、会话标识等组合判断是否复用未关闭工单。\n2. 新工单记录来源、用户邮箱、发件邮箱、团队、状态、未读数、最后来信时间。\n3. 已有工单更新最后来信时间、未读数、用户来信数。\n4. 如果当前客服离线,可以释放负责人,让工单重新进入分配池。\n5. 入站正文应保存原始内容和清洗后的展示内容。\n\n## 10. 工单客服处理流程\n\n### 10.1 工单状态\n\n通用状态建议:\n\n| 状态 | 说明 |\n| --- | --- |\n| 待处理 | 新入站邮件或表单生成工单,等待客服处理 |\n| 服务中 | 客服已接手并正在处理 |\n| 未解决 | 客服标记暂未解决,需要后续跟进 |\n| 转化中 | 进入销售或转化跟进阶段 |\n| 已关闭 | 本次邮件工单处理结束 |\n\n状态值可以由新系统自行定义,但需要保证列表筛选、统计、自动关闭和权限校验口径统一。\n\n### 10.2 自动分配\n\n自动分配建议流程:\n\n1. 找到待处理且未分配的工单。\n2. 根据收件邮箱、团队、站点、语言或业务线确定可服务团队。\n3. 获取在线客服。\n4. 按接单上限、当前处理数、最近分配时间选择客服。\n5. 更新工单负责人。\n6. 写入分配记录和节点日志。\n7. 通知客服工作台。\n\n### 10.3 客服回复\n\n```mermaid\nflowchart TD\n A[\"客服点击发送\"] --> B[\"校验客服在线、权限、工单状态\"]\n B --> C[\"写入待发送邮件记录和正文\"]\n C --> D[\"更新工单未读数、首次响应、回复耗时\"]\n D --> E[\"投递客服回复队列\"]\n E --> F[\"发送消费者选择 SMTP / Gmail / Microsoft\"]\n F --> G[\"保存发送成功或失败结果\"]\n G --> H[\"通知客服工作台\"]\n```\n\n发送前建议校验:\n\n1. 客服必须在线。\n2. 工单必须存在且未关闭。\n3. 当前客服必须是工单处理人,或具备接手权限。\n4. 工单必须属于当前客服可处理团队。\n5. 主题、正文、收件人、回复地址必须合法。\n6. 附件大小、类型、数量需要符合业务规则和发送通道限制。\n\n### 10.4 转发和主动开工单\n\n转发:\n\n1. 需要填写新的收件人。\n2. 转发邮件可以不绑定到原工单作为普通回复。\n3. 原邮件和转发邮件需要建立关联,方便追溯。\n4. 发送链路仍可复用客服回复队列。\n\n主动开工单:\n\n1. 客服选择发件邮箱和目标用户邮箱。\n2. 系统校验发件邮箱归属团队。\n3. 如果同一发件邮箱和用户邮箱已有未关闭工单,应拒绝重复创建或要求接手原工单。\n4. 创建服务中工单,负责人为当前客服。\n5. 写入节点日志和分配记录。\n6. 发送第一封邮件。\n\n## 11. 工单辅助任务\n\n新子系统可按需要保留以下后台任务:\n\n| 任务 | 说明 |\n| --- | --- |\n| 自动分配 | 将未分配待处理工单分配给在线客服 |\n| 自动移交 | 当前负责人离线且有新来信时,按代班或团队规则重新分配 |\n| DDL 释放 | 工单分配后超过配置时间未处理,释放为未分配 |\n| 未回复提醒 | 用户新来信超过配置时间未回复,提醒负责人 |\n| 自动关闭 | 服务中工单超过配置时间无新用户来信时自动关闭 |\n| 未分配告警 | 未分配工单数量超过阈值时通知团队管理员 |\n| 统计同步 | 定时刷新任务发送数、回复数、打开数、点击数等统计 |\n\n具体调度频率和启用范围需要按新系统 SLA 确认。\n\n## 12. 通用限制与风控点\n\n### 12.1 任务和发送限制\n\n建议配置化管理:\n\n1. 单任务最大目标人数。\n2. 单轮投递队列数量。\n3. 单发件邮箱每分钟、每小时、每天发送上限。\n4. 单用户每天或一段时间内最大触达次数。\n5. 单域名发送上限。\n6. 批量邮件和客服回复是否共享额度。\n\n### 12.2 内容限制\n\n建议校验:\n\n1. 邮件主题最大长度。\n2. 正文最小和最大长度。\n3. 附件大小、类型、数量。\n4. 发件邮箱和回复邮箱格式。\n5. 链接合法性和追踪参数。\n6. 必要的退订入口和合规声明。\n\n### 12.3 人群抑制\n\n发送前应排除:\n\n1. 退订用户。\n2. 投诉用户。\n3. 永久退信用户。\n4. 风险用户。\n5. 明确不允许触达的用户。\n6. 已达到频率上限的用户。\n\n### 12.4 幂等和重试\n\n需要幂等的场景:\n\n1. 任务生成邮件记录。\n2. 邮件记录投递 Kafka。\n3. Kafka 消费发送。\n4. 外部事件回调。\n5. 入站邮件生成工单。\n\n失败重试建议区分:\n\n1. 可重试:网络超时、临时服务不可用、临时退信、限流。\n2. 不可重试:邮箱格式错误、发件权限错误、账号不存在、永久退信、投诉抑制。\n\n## 13. 可观测性\n\n建议至少记录以下指标:\n\n| 指标 | 说明 |\n| --- | --- |\n| 任务创建数 | 按类型、状态统计 |\n| 目标用户数 | 预估人数、实际生成邮件数、过滤人数 |\n| 队列积压 | 批量发送队列、客服回复队列、入站队列 |\n| 发送成功率 | 按通道、发件邮箱、任务统计 |\n| 失败原因分布 | 发送失败、退信、拒信、限流 |\n| 送达率、打开率、点击率 | 以事件回调统计,注意打开和点击有误差 |\n| 投诉率、退订率 | 作为发送风控核心指标 |\n| 工单响应时长 | 首次响应、最近响应、关闭时长 |\n| 未分配工单数 | 用于团队容量和告警 |\n\n\n## 14. 参考代码位置\n\n以下为现有项目中可参考的代码位置,重构时可按新架构重新命名和拆分:\n\n| 模块 | 现有参考 |\n| --- | --- |\n| 邮件任务后台入口 | `app/admin/controller/MailTaskController.php` |\n| 邮件任务服务 | `app/service/MailTaskService.php` |\n| 批量邮件生成与投递参考 | `app/service/UserService.php` |\n| 批量邮件发送消费者 | `app/command/kafkaConsumer/BatchMailCommand.php` |\n| 工单后台入口 | `app/admin/controller/EdmChatController.php` |\n| 工单服务 | `app/service/EdmChatService.php` |\n| 客服回复消费者 | `app/command/kafkaConsumer/MailSendCommand.php` |\n| 表单数据消费者 | `app/command/kafkaConsumer/QaForm.php` |\n| 表单创建工单 | `app/service/WebFormDataService.php` |\n| 邮件事件回调 | `app/controller/AmazonEmailController.php` |\n| 普通邮箱服务 | `app/admin/controller/MailboxController.php`、`app/service/MailboxService.php` |\n| 邮件发送封装 | `app/service/Mail.php` |\n| Microsoft 邮件服务 | `app/service/MicrosoftService.php` |\n| 策略邮件命令 | `app/command/EdmDoRealTimeStrategy.php`、`app/command/EdmSendRealTimeStrategy.php`、`app/command/EdmDayCurrentTask.php` |\n| 工单辅助命令 | `app/command/EdmAllocate.php`、`app/command/EdmMove.php`、`app/command/EdmDealLine.php`、`app/command/EdmTimeout.php`、`app/command/EdmWorkOrderAutoClose.php` |\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/通用IM业务流程与接口频率限制说明", + "type": "document", + "name": "通用 IM 业务流程与接口频率限制说明", + "filePath": "05_需求文档/通用IM业务流程与接口频率限制说明.md", + "summary": "通用 IM 业务流程与接口频率限制说明 更新时间:2026 05 26 1. 文档目标 本文用于新的 IM 子系统重构设计,目标是在功能保持一致的前提下,将现有 IM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、控制腾讯云 IM REST API 调用频率。 2. 总体架构 通用 IM 链路由以下组件组成: 组件 职责 A", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# 通用 IM 业务流程与接口频率限制说明\n\n更新时间:2026-05-26\n\n## 1. 文档目标\n\n本文用于新的 IM 子系统重构设计,目标是在功能保持一致的前提下,将现有 IM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、控制腾讯云 IM REST API 调用频率。\n\n## 2. 总体架构\n\n通用 IM 链路由以下组件组成:\n\n| 组件 | 职责 |\n| --- | --- |\n| App 客户端 | 用户通过 App 使用腾讯云 IM SDK 发送和接收消息 |\n| 腾讯云 IM | 负责即时消息投递、账号体系、消息格式、离线推送、服务端 REST API、回调触发 |\n| App 后台 | 接收腾讯云 IM 回调,识别需要管理后台处理的消息,并写入 Kafka |\n| Kafka | 解耦 App 后台与管理后台 IM 子系统,承接入站消息、推送消息、异步任务 |\n| IM 子系统 / 管理后台 | 消费 Kafka 消息,维护本地会话、消息、工单、分配、状态流转和客服操作 |\n| 本地数据库 | 作为管理后台 IM 业务的长期数据源,保存消息流水、会话状态、工单状态、操作记录 |\n| Redis / 缓存 | 保存在线状态、分配触发标记、限流计数、幂等键、临时游标 |\n| WebSocket / 站内通知 | 将新消息、分配变化、发送结果、状态变化推送给管理后台前端 |\n\n## 3. 总体消息流\n\n```mermaid\nflowchart TD\n A[\"用户在 App 发送 IM 消息\"] --> B[\"腾讯云 IM SDK 投递消息\"]\n B --> C[\"腾讯云 IM 回调 App 后台\"]\n C --> D[\"App 后台校验回调并识别管理后台相关消息\"]\n D --> E[\"App 后台写入 Kafka\"]\n E --> F[\"IM 子系统消费 Kafka\"]\n F --> G[\"幂等校验与消息解析\"]\n G --> H[\"写入本地消息表\"]\n H --> I[\"更新会话、工单、未读数、最后消息时间\"]\n I --> J[\"触发分配、提醒或状态流转\"]\n J --> K[\"管理后台客服前端展示\"]\n```\n\n核心原则:\n\n1. App 客户端消息先进入腾讯云 IM。\n2. 腾讯云 IM 将消息回调给 App 后台。\n3. App 后台不直接写管理后台业务库,而是将管理后台关心的消息写入 Kafka。\n4. 管理后台 IM 子系统消费 Kafka 后入库,并维护本地会话、工单和客服状态。\n5. 管理后台后续展示、检索、统计、客服处理,应以本地数据库为主数据源。\n\n## 4. 核心数据模型\n\n新子系统建议至少抽象以下数据对象:\n\n| 对象 | 说明 |\n| --- | --- |\n| IM 账号 | 业务用户、品牌账号、客服账号在腾讯云 IM 中的账号映射 |\n| 消息 Message | 本地保存的消息流水,包含方向、发送方、接收方、消息类型、消息体、腾讯消息 ID、业务扩展字段 |\n| 会话 Conversation | 用户与品牌/客服之间的当前会话指针,包含未读数、最后消息时间、当前工单 ID |\n| 工单 Ticket / ChatRecord | 一次客服处理过程,包含状态、负责人、团队、首次响应时间、关闭时间 |\n| 分配记录 Assignment | 自动分配、手动接手、转接、释放、代班等操作记录 |\n| 节点日志 NodeLog | 工单生成、分配、首次回复、转接、未解决、转化中、关闭等关键节点 |\n| 推送任务 PushTask | 运营或策略创建的 IM 推送任务 |\n| 推送记录 PushRecord | 单个用户、单条内容的实际待发送记录和发送结果 |\n\n## 5. 入站消息流程\n\n### 5.1 用户发送消息\n\n1. 用户在 App 中通过腾讯云 IM SDK 发送文本、图片、视频、自定义卡片或其他消息。\n2. 腾讯云 IM 完成消息投递,并根据配置触发服务端回调。\n3. App 后台接收腾讯云 IM 回调,完成签名、来源、事件类型和消息结构校验。\n4. App 后台判断该消息是否需要管理后台处理。\n5. 需要管理后台处理的消息,由 App 后台写入 Kafka。\n6. IM 子系统消费 Kafka,执行幂等校验,解析消息体。\n7. IM 子系统写入本地消息流水,并更新会话和工单状态。\n8. 若消息产生新的待处理工单,则进入分配流程。\n9. 管理后台前端通过列表查询、WebSocket 或站内通知看到新消息。\n\n### 5.2 幂等与顺序\n\n腾讯云回调、App 后台写 Kafka、Kafka 消费都可能出现重试,因此 IM 子系统必须做幂等处理。\n\n建议幂等键优先级:\n\n1. 腾讯云 IM 消息唯一标识。\n2. 发送方、接收方、消息随机数、消息序列号、消息时间组合。\n3. App 后台写入 Kafka 时生成的业务事件 ID。\n\n同一会话内需要尽量按消息时间和腾讯云消息顺序展示。若存在乱序到达,应允许入库后按消息时间、序列号或腾讯消息 ID 排序展示。\n\n## 6. 客服处理流程\n\n### 6.1 会话和工单状态\n\n通用状态建议如下:\n\n| 状态 | 说明 |\n| --- | --- |\n| 待接入 | 用户有新消息,尚未分配或尚未被客服处理 |\n| 服务中 | 客服已接入并正在处理 |\n| 已转接 | 工单被转给其他客服或团队,等待新处理人接手 |\n| 未解决 | 客服标记暂未解决,需要后续跟进 |\n| 转化中 | 进入转化或商机跟进阶段 |\n| 已关闭 | 本次客服处理结束 |\n\n状态值可以由新子系统自行定义,但需要保证列表筛选、统计口径、自动关闭、转接和重新打开逻辑一致。\n\n### 6.2 自动分配\n\n自动分配通常在新消息入库后触发:\n\n1. 找到待接入工单。\n2. 根据品牌、站点、团队、语言、业务线等条件确定可服务团队。\n3. 获取在线客服列表。\n4. 按客服接单上限、当前处理数、最近分配时间等规则筛选。\n5. 选择目标客服并更新工单负责人。\n6. 写入分配记录和节点日志。\n7. 通知管理后台前端。\n\n建议将分配能力独立成可重试任务,避免 Kafka 入站消费被复杂分配逻辑拖慢。\n\n### 6.3 客服打开会话\n\n客服打开会话时,系统通常需要:\n\n1. 校验客服是否在线、是否有处理权限。\n2. 查询会话和工单详情。\n3. 拉取本地消息列表。\n4. 清理当前客服视角下的未读数。\n5. 必要时将工单从待接入或已转接推进到服务中。\n6. 记录读取、接手或首次处理节点。\n\n### 6.4 客服发送消息\n\n客服发送消息的通用链路:\n\n```mermaid\nflowchart TD\n A[\"客服在管理后台发送消息\"] --> B[\"IM 子系统校验权限、会话、工单状态\"]\n B --> C[\"组装腾讯云 IM 消息体\"]\n C --> D[\"调用腾讯云 REST API\"]\n D --> E{\"腾讯云返回结果\"}\n E -->|成功| F[\"写入本地消息流水并更新会话\"]\n E -->|失败| G[\"记录失败原因并进入重试或人工处理\"]\n F --> H[\"通知管理后台前端发送成功\"]\n G --> I[\"通知管理后台前端发送失败\"]\n```\n\n发送前建议校验:\n\n1. 当前客服必须具备客服身份。\n2. 当前客服必须有该会话或工单处理权限。\n3. 工单不能处于已关闭状态,除非业务允许重新打开。\n4. 消息体大小、消息类型、媒体文件大小必须符合业务和腾讯云限制。\n5. 对运营推送和客服消息应区分业务来源,方便统计和风控。\n\n### 6.5 转接、释放和自动关闭\n\n通用处理规则:\n\n1. 手动转接给个人:校验当前处理人权限,更新负责人,记录转接节点。\n2. 手动转接给团队:记录目标团队,等待团队内客服接手或自动分配。\n3. 客服离线释放:如果负责人离线且有未读用户消息,可释放为待分配。\n4. 超时未处理释放:待接入或已转接超过配置时间后,重新进入分配池。\n5. 自动关闭:服务中会话在配置时间内无新用户消息或无新客服动作时,自动关闭。\n\n## 7. 运营 IM 推送流程\n\n运营推送与客服消息都可能调用腾讯云 IM REST API,但应在业务上区分:\n\n```mermaid\nflowchart TD\n A[\"后台创建推送任务\"] --> B[\"审核或直接进入待发送\"]\n B --> C[\"按标签、站点、产品、用户状态筛选目标人群\"]\n C --> D[\"生成 PushRecord\"]\n D --> E[\"写入 Kafka 推送队列\"]\n E --> F[\"推送消费者限速发送\"]\n F --> G[\"调用 sendmsg 或 batchsendmsg\"]\n G --> H[\"保存腾讯云返回结果\"]\n H --> I[\"汇总任务完成状态\"]\n```\n\n建议:\n\n1. 大批量运营推送优先评估 `batchsendmsg`,减少单发接口压力。\n2. 推送消费者必须按 SDKAppID 和接口维度做限速。\n3. 推送记录需要保存发送状态、错误码、重试次数、腾讯云返回结果。\n4. 发送失败应区分可重试错误和不可重试错误。\n5. 推送消息应写入业务扩展字段,便于后续归因和统计。\n\n## 8. 本地存储与历史消息\n\n管理后台 IM 子系统建议以本地数据库作为长期主数据源。\n\n腾讯云 IM 漫游消息适合用于消息补偿、短期核对或异常排查,不建议作为客服记录、统计报表和审计的唯一长期来源。原因是历史消息存储时长与套餐、增值服务和控制台配置有关,且可能产生额外费用。\n\n建议本地保存:\n\n1. 入站用户消息。\n2. 客服发送消息。\n3. 系统提示消息。\n4. 运营推送消息及发送结果。\n5. 转接、关闭、分配、未解决、转化等节点日志。\n\n## 9. 腾讯云 IM 接口频率限制说明\n\n以下为腾讯云官方文档口径,实际限制可能随套餐、数据中心、控制台配置和商务协议变化,最终以腾讯云控制台和合同为准。\n\n### 9.1 通用限制\n\n| 项目 | 限制 |\n| --- | --- |\n| 单聊、群聊单条消息长度 | 最大 12KB |\n| UserID | 长度不超过 32 字节,不支持特殊字符,建议使用英文字母或数字 |\n| REST API 通用调用频率 | 多数 REST API 默认最高 200 次/秒 |\n| 导入多个账号、删除账号、查询账号 | 默认最高 100 次/秒 |\n| 查询在线状态 | 单次请求最多查询 500 个用户 |\n| 批量发单聊消息 | 单次最多给 500 个用户发送 |\n| 导入多个账号 | 单次最多导入 100 个用户名 |\n| 单个用户好友数 | 默认支持 3000 个好友 |\n| 单个用户黑名单数 | 最多 1000 人 |\n\n### 9.2 常用 REST API 频率\n\n| 接口 | 默认频率限制 | 叠加包增量 | 说明 |\n| --- | --- | --- | --- |\n| `v4/openim/sendmsg` 单发单聊消息 | 200 次/秒 | 每个叠加包 +100 次/秒 | 体验版和开发版每日累计发送量限制为 1000 条/天 |\n| `v4/openim/batchsendmsg` 批量发单聊消息 | 200 次/秒,同时 12000 条/分钟 | 每个叠加包 +6000 条/分钟 | 条数按接收方数量计算,一次发给 500 人计 500 条 |\n| `v4/profile/portrait_set` 设置资料 | 200 次/秒 | 每个叠加包 +100 次/秒 | 用于更新头像、昵称等资料 |\n| `v4/profile/portrait_get` 拉取资料 | 200 次/秒 | 每个叠加包 +100 次/秒 | 用于查询资料 |\n| `v4/openim/query_online_status` 查询在线状态 | 200 次/秒 | 每个叠加包 +100 次/秒 | 单次最多查询 500 个用户 |\n| `v4/openim/get_c2c_unread_msg_num` 查询单聊未读数 | 200 次/秒 | 每个叠加包 +100 次/秒 | 如新系统需要同步未读数需注意限频 |\n| `v4/group_open_http_svc/send_group_msg` 群内发普通消息 | 200 次/秒 | 每个叠加包 +100 次/秒 | 单个群发送频率上限为 40 条/秒 |\n| `v4/sns/black_list_get` 拉取黑名单 | 200 次/秒 | 每个叠加包 +100 次/秒 | 关系链相关能力需控制调用频率 |\n\n### 9.3 调频收费说明\n\n腾讯云 IM 服务端 API 调频属于增值服务。官方公告口径为:\n\n| 数据中心 | 服务端 API 调用频率叠加包价格 |\n| --- | --- |\n| 国内数据中心 | 10 元/个/日,日结后付费 |\n| 境外数据中心 | 20 元/个/日,日结后付费 |\n\n注意事项:\n\n1. 调频对单个 SDKAppID 生效,多个 SDKAppID 需要分别配置。\n2. 每个调频项按当日配置的最高数值计费,次日出账。\n3. 体验版不支持调整服务端 API 调用频率上限。\n4. 如果新子系统存在大规模运营推送,必须提前评估 `sendmsg` 与 `batchsendmsg` 的峰值。\n\n## 10. 新子系统设计建议\n\n### 10.1 按接口维度做限流\n\n建议在 IM 子系统服务端增加统一腾讯云 IM Client,并在 Client 内按以下维度做限流:\n\n1. SDKAppID。\n2. API 路径,例如 `openim/sendmsg`、`openim/batchsendmsg`。\n3. 业务来源,例如客服消息、运营推送、系统通知。\n\nKafka 消费者不能只依赖消费并发控制,否则高峰期可能瞬间打满腾讯云 API 限频。\n\n### 10.2 单发和批量发送分流\n\n建议策略:\n\n| 场景 | 推荐接口 |\n| --- | --- |\n| 客服一对一即时回复 | `openim/sendmsg` |\n| 系统给单个用户发送提示 | `openim/sendmsg` |\n| 运营批量推送相同或相似内容 | 优先评估 `openim/batchsendmsg` |\n| 需要强个性化卡片、每人内容不同 | 可用 `sendmsg`,但必须限速 |\n\n### 10.3 错误处理和重试\n\n建议将发送结果分为:\n\n1. 成功:腾讯云返回 `ErrorCode=0` 且业务认为发送完成。\n2. 部分成功:批量发送时部分账号失败,需要保存失败账号列表。\n3. 可重试失败:网络异常、超时、腾讯云内部错误、限频。\n4. 不可重试失败:账号不存在、消息体非法、权限错误、消息包超长。\n\n重试建议采用延迟重试,并设置最大重试次数。超过次数后进入失败表或死信队列,供人工排查。\n\n### 10.4 回调链路可观测性\n\n入站链路建议记录以下指标:\n\n1. 腾讯云回调接收量。\n2. App 后台写 Kafka 成功量和失败量。\n3. Kafka 积压量。\n4. IM 子系统消费延迟。\n5. 入库成功量和幂等重复量。\n6. 会话创建量、工单创建量、分配成功量。\n\n发送链路建议记录:\n\n1. 每个 API 的 QPS。\n2. 限流等待时间。\n3. 腾讯云错误码分布。\n4. 发送成功率。\n5. 可重试失败量和最终失败量。\n\n## 11. 参考来源\n\n- 腾讯云 IM 使用限制:https://cloud.tencent.com/document/product/269/32429\n- 腾讯云 IM 单发单聊消息:https://cloud.tencent.com/document/product/269/2282\n- 腾讯云 IM 批量发单聊消息:https://cloud.tencent.com/document/product/269/1612\n- 腾讯云 IM 服务端 API 调频收费公告:https://cloud.tencent.com/document/product/269/93324\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:07_技术文档/01-子系统-identity-数据库表关系", + "type": "document", + "name": "identity 子系统 — doris数据库相关表与关联关系(供参考)", + "filePath": "07_技术文档/01-子系统-identity-数据库表关系.md", + "summary": "identity 子系统 — doris数据库相关表与关联关系(供参考) 1. 数据库全景 数据库 说明 与 identity 的关系 ods app base data APP基础数据(用户、设备、好友、产品) 核心 — 用户身份主数据 ods app app community 社区数据(帖子、评论、关注) 行为数据,辅助归并 ods app jh da", + "tags": [ + "07_技术文档", + "技术文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# identity 子系统 — doris数据库相关表与关联关系(供参考)\n\n\n---\n\n## 1. 数据库全景\n\n| 数据库 | 说明 | 与 identity 的关系 |\n|--------|------|-------------------|\n| `ods_app_base_data` | APP基础数据(用户、设备、好友、产品) | **核心** — 用户身份主数据 |\n| `ods_app_app_community` | 社区数据(帖子、评论、关注) | 行为数据,辅助归并 |\n| `ods_app_jh_data` | JOYHUB事件数据 | 行为数据,辅助归并 |\n| `ods_oa_oaaftersales` | OA售后系统(客户、订单、测评) | **核心** — 非APP用户身份线索 |\n| `app_tag_data` | 标签数据 | **关键** — 已有 OneID 归并表 |\n\n---\n\n## 2. 核心发现:已存在的 OneID 归并表\n\n### `app_tag_data.user_oneid` — 用户唯一标识归并表(6 字段,已有数据)\n\n> **这是 identity 子系统 M2(归并引擎)的核心参考**,已实现 uuid → one_id 的归并逻辑\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `uuid` | VARCHAR(64) | UNI, NOT NULL | 原始客户唯一标识符 |\n| `one_id` | VARCHAR(64) | NOT NULL | 用户唯一标识(归并后的ID) |\n| `bridge_uuid` | STRING | | 当前 uuid 对应的非当前桥接 uuid |\n| `association_fields` | STRING | | 关联字段 |\n| `detail` | STRING | | uuid 指向 one_id 的证据说明(JSON) |\n| `update_time` | DATETIME(3) | | 同步更新时间 |\n\n**关键设计点**:\n- `uuid` → `one_id` 是多对一关系(多个 uuid 可归并到同一个 one_id)\n- `bridge_uuid` 记录桥接关联,用于跨系统身份串联\n- `detail` 字段存储归并证据(JSON),与设计文档中 `person_profiles.merge_evidence` 概念一致\n- `association_fields` 记录关联字段,对应设计文档中的线索类型\n\n---\n\n## 3. 核心表状态\n\n设计文档定义的 4 张核心表 **尚未在数据库中创建**:\n\n| 表名 | 设计文档定义 | 数据库状态 | 与现有表的关系 |\n|------|-------------|-----------|--------------|\n| `person_profiles` | 真实人主表 | **不存在** | 可参考 `app_tag_data.user_oneid`(one_id) |\n| `person_identity_links` | 身份线索关联表 | **不存在** | 可参考 `ods_oa_oaaftersales.customer_platform_info`(type映射线索类型) |\n| `contact_context_snapshots` | 上下文快照 | **不存在** | 需聚合多表新建 |\n| `device_records` | 设备变化记录 | **不存在** | 可参考 `user_device_token_log`(已有252万条记录) |\n\n---\n\n## 4. 已存在的数据源表\n\n### 4.1 用户与身份核心表\n\n#### `ods_app_base_data.users` — 用户主表(36 字段)\n\n> identity 子系统的核心用户数据源,提供 JOYHUB ID、邮箱、设备号等身份线索\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 用户ID(JOYHUB ID) |\n| `userName` | STRING | | 用户名 |\n| `email` | STRING | | 邮箱 |\n| `deviceToken` | VARCHAR(300) | | 设备推送令牌 |\n| `IMEI` | STRING | | 设备IMEI |\n| `sysType` | VARCHAR(765) | | 系统类型(安卓/IOS/Windows Phone) |\n| `deviceId` | STRING | | 设备ID |\n| `appVersion` | VARCHAR(90) | | APP版本 |\n| `contact_information` | VARCHAR(765) | | 联系方式(电话号码) |\n| `mobile` | STRING | | 手机号 |\n| `area_code` | BIGINT | | 区域代码(美国1,中国86) |\n| `status` | TINYINT | | 1活跃/2封禁/3注销 |\n| `sysTime` | DATETIME | | 系统时间(注册时间) |\n| `created_at` | DATETIME | | 创建时间 |\n\n#### `ods_app_base_data.user_login_last` — 最近登录信息(21 字段)\n\n> 提供设备型号、系统版本、APP版本、国家等信息,是设备变化识别(M4)的重要数据源\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `UserId` | BIGINT | UNI | 用户ID → 关联 `users.id` |\n| `deviceId` | STRING | | 设备ID |\n| `deviceModel` | VARCHAR(150) | | 手机型号 |\n| `device` | VARCHAR(150) | | 手机系统 |\n| `sysType` | VARCHAR(150) | | 系统设备信息与版本 |\n| `appVersion` | VARCHAR(45) | | APP版本号 |\n| `appChannel` | INT | | 渠道 |\n| `countryName` | VARCHAR(600) | | 国家名称 |\n| `countryCode` | VARCHAR(30) | | 国家缩写 |\n| `Time` | DATETIME | | 登录时间 |\n| `Ip` | STRING | | IP地址 |\n\n#### `ods_app_base_data.user_device_token_log` — 设备令牌变更日志(7 字段,252万行)\n\n> 记录设备令牌的添加和更新,可用于追踪设备变化\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | INT | UNI | 主键 |\n| `user_id` | INT | | 用户ID → 关联 `users.id` |\n| `type` | TINYINT | | 0添加/1更新 |\n| `device_id` | STRING | | 设备ID |\n| `new_device_token` | VARCHAR(300) | | 新设备令牌 |\n| `created_at` | DATETIME | | 创建时间 |\n| `client_time` | DATETIME | | 客户端时间 |\n\n#### `ods_app_base_data.user_contact_information_history` — 联系方式变更历史(11 字段,20万行)\n\n> 记录用户手机号等联系方式的变更历史,可用于身份线索追踪\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `user_id` | BIGINT | | 用户ID → 关联 `users.id` |\n| `user_type` | VARCHAR(150) | | 用户角色 |\n| `area_code` | BIGINT | | 区域代码 |\n| `mobile` | STRING | | 手机号 |\n| `area_id` | INT | | 区域ID |\n| `marketing_phone` | TINYINT | | 营销电话开关 |\n| `marketing_sms` | TINYINT | | 个性化广告开关 |\n| `status` | SMALLINT | | 1生效中/2已过期 |\n| `verify_status` | SMALLINT | | 短信验证状态:1通过/2未通过 |\n| `created_at` | BIGINT | | 创建时间 |\n\n#### `ods_app_base_data.banned_device_id` — 设备封禁表(3 字段,6831行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | INT | UNI | 主键 |\n| `device_id` | VARCHAR(765) | | 封禁设备ID |\n| `created_at` | INT | | 创建时间 |\n\n#### `ods_app_base_data.blacklist_users_aggregate` — 用户黑名单汇总(8 字段)\n\n> 按 uid、设备、IP 维度的黑名单,用于风险判断\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | INT | UNI | 主键 |\n| `target_id` | INT | | 1=uid, 2=设备, 3=IP |\n| `target_value` | VARCHAR(1500) | | 字段值 |\n| `category_id` | INT | | 黑名单类别ID |\n| `describe` | VARCHAR(1500) | | 加入原因 |\n\n\n### 4.2 OA 售后系统 — 客户身份数据\n\n#### `ods_oa_oaaftersales.customer` — 客户主表(22 字段,23万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 客户ID |\n| `name` | STRING | | 客户名 |\n| `country` | VARCHAR(60) | | 国家 |\n| `is_black` | TINYINT | | 是否黑名单 |\n| `high_risk` | TINYINT | | 是否高风险 |\n| `erp_contact` | STRING | | ERP联系方式 |\n| `erp_pay_account` | VARCHAR(1500) | | ERP付款账号 |\n\n#### `ods_oa_oaaftersales.customer_platform_info` — 客户平台信息(8 字段,26万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `type` | TINYINT | | **1电话/2邮箱/3joyhub_id/4邮箱编码/5twitter/6facebook** |\n| `customer_id` | INT | | 客户ID → 关联 `customer.id` |\n| `account` | STRING | | 账号值 |\n| `is_delete` | TINYINT | | 是否删除 |\n\n#### `ods_oa_oaaftersales.customer_address` — 客户地址(18 字段,5631行)\n\n> 提供姓名+地址组合,可用于 ORDER_NAME_ADDRESS 线索归并\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `customer_id` | INT | | 客户ID → 关联 `customer.id` |\n| `recipient_name` | STRING | | 收件人姓名 |\n| `phone` | STRING | | 电话 |\n| `zip_code` | STRING | | 邮编 |\n| `country` | VARCHAR(300) | | 国家 |\n| `city` | STRING | | 城市 |\n| `state` | STRING | | 州/省 |\n| `detail` | VARCHAR(1500) | | 详细地址 |\n\n#### `ods_oa_oaaftersales.customer_payment_account` — 客户付款账号(12 字段,10万行)\n\n> 提供收款信息(银行卡、PayPal等),可作为身份归并线索\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `ct_id` | INT | | 客户ID → 关联 `customer.id` |\n| `pay_name` | VARCHAR(150) | | 支付方式 |\n| `account_number` | STRING | | 账号 |\n| `account_name` | STRING | | 账户名 |\n| `card_no` | VARCHAR(300) | | 卡号 |\n\n#### `ods_oa_oaaftersales.customer_bind` — 客户绑定关系(6 字段,4980行)\n\n> 客户间的绑定关系,已有归并概念\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `customer_ids` | STRING | | 绑定的客户ID集合 |\n| `unbind_time` | DATETIME | | 解绑时间 |\n| `is_deleted` | TINYINT | | 是否删除 |\n\n#### `ods_oa_oaaftersales.customer_bind_log` — 客户绑定日志(6 字段,1.2万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `user_id` | INT | | 操作人ID |\n| `bind_customer_ids` | STRING | | 绑定的客户ID |\n| `type` | VARCHAR(765) | | 操作类型 |\n\n#### `ods_oa_oaaftersales.evaluation_order` — 测评订单(55+ 字段,45万行)\n\n> 包含丰富的身份线索:邮箱、电话、JOYHUB ID、社交媒体账号\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 订单ID |\n| `ct_id` | INT | | 客户ID → 关联 `customer.id` |\n| `amazon_order_id` | STRING | | 亚马逊订单号 |\n| `email` | STRING | | 邮箱 |\n| `phone` | STRING | | 电话 |\n| `joyhub_id` | VARCHAR(150) | | JOYHUB ID |\n| `twitter` | STRING | | Twitter账号 |\n| `facebook` | STRING | | Facebook账号 |\n\n#### `ods_oa_oaaftersales.lingxing_order` — 亚马逊订单(30+ 字段,2142万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 订单ID |\n| `amazon_order_id` | VARCHAR(150) | | 亚马逊订单号 |\n| `buyer_name` | VARCHAR(765) | | 买家姓名 |\n| `buyer_email` | VARCHAR(765) | | 买家邮箱 |\n| `phone` | VARCHAR(90) | | 电话 |\n| `postal_code` | VARCHAR(765) | | 邮编 |\n| `address` | VARCHAR(765) | | 地址 |\n\n#### `ods_oa_oaaftersales.phone_records` — 电话记录(30+ 字段,8万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `ct_id` | INT | | 客户ID → 关联 `customer.id` |\n| `phone` | STRING | | 电话 |\n| `email` | STRING | | 邮箱 |\n| `joyhub_id` | VARCHAR(150) | | JOYHUB ID |\n\n### 4.3 社区行为表\n\n#### `ods_app_app_community.posts` — 帖子表(35 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 帖子ID |\n| `user_id` | INT | | 用户ID → 关联 `users.id` |\n| `status` | SMALLINT | | 10待审核/20拒绝/30通过 |\n| `deleted_at` | INT | | 删除时间(0=未删除) |\n\n#### `ods_app_app_community.post_likes` — 点赞表(5 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `post_id` | BIGINT | | 帖子ID → 关联 `posts.id` |\n| `user_id` | INT | | 用户ID → 关联 `users.id` |\n\n#### `ods_app_app_community.comments` — 评论表(14 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 评论ID |\n| `post_id` | DECIMAL(20,0) | | 帖子ID → 关联 `posts.id` |\n| `user_id` | DECIMAL(20,0) | | 用户ID → 关联 `users.id` |\n\n#### `ods_app_app_community.follows` — 关注关系表(7 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `user_id` | INT | | 关注者ID → 关联 `users.id` |\n| `following_user_id` | INT | | 被关注者ID → 关联 `users.id` |\n\n#### `ods_app_base_data.friends` — 好友关系表(6 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `user_id` | DECIMAL(20,0) | | 用户ID → 关联 `users.id` |\n| `friend_id` | DECIMAL(20,0) | | 好友ID → 关联 `users.id` |\n\n### 4.4 事件行为表\n\n#### `ods_app_jh_data.events` — APP事件表(18 字段)\n\n> event_type: 13=home, 8=玩具连接, 5=视频等\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 事件ID |\n| `add_date` | DATE | UNI | 记录日期 |\n| `uid` | BIGINT | | 用户ID → 关联 `users.id` |\n| `event_type` | INT | | 事件类型 |\n| `pid` | BIGINT | | 产品ID → 关联 `def_product_list.id` |\n\n#### `ods_app_jh_data.remote_events` — 远程连接事件表(15 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 事件ID |\n| `uid` | BIGINT | | 用户ID → 关联 `users.id` |\n| `call_sn` | VARCHAR(600) | | 远程序列号(格式:uid1_uid2_uuid) |\n| `mode` | INT | | 1文字/2语音/3视频 |\n\n\n---\n\n## 5. 表关联关系图\n\n```\n┌─────────────────────────────────────────────────────────────────────────┐\n│ identity 子系统数据关系 │\n└─────────────────────────────────────────────────────────────────────────┘\n\n ┌───────────────────────┐\n │ app_tag_data │\n │ .user_oneid │\n │ uuid ──► one_id │\n │ (已有归并逻辑) │\n └───────────┬───────────┘\n │ uuid 可能是 email/phone/device\n ▼\n┌──────────────────────────────────────────────────────────────────────┐\n│ ods_app_base_data(APP用户体系) │\n│ │\n│ users ◄── user_profiles (user_id) │\n│ │ user_login_last (UserId) ── deviceId, deviceModel │\n│ │ user_device_token_log (user_id) ── 252万条设备变更记录 │\n│ │ user_contact_information_history (user_id) ── 20万条变更 │\n│ │ friends (user_id / friend_id) │\n│ │ banned_device_id (device_id) ── 设备封禁 │\n│ │ blacklist_users_aggregate (target_id+target_value) │\n│ │ │\n│ └── id = JOYHUB_ID │\n└──────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ ods_oa_oaaftersales(OA售后体系) │\n│ │\n│ customer ◄── customer_platform_info (customer_id) │\n│ │ type: 1电话/2邮箱/3joyhub_id/5twitter/6facebook │\n│ │ customer_address (customer_id) ── 姓名+地址+电话 │\n│ │ customer_payment_account (ct_id) ── 收款账号 │\n│ │ customer_bind (customer_ids) ── 客户绑定关系 │\n│ │ customer_bind_log ── 绑定操作日志 │\n│ │ evaluation_order (ct_id) ── 邮箱/电话/joyhub_id/社媒 │\n│ │ phone_records (ct_id) ── 电话/邮箱/joyhub_id │\n│ │ order_refund (ct_id) ── 返款详情 │\n│ │ │\n│ lingxing_order ── buyer_name + buyer_email + phone + address │\n│ └── lingxing_order_item (amazon_order_id) │\n└──────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ 社区行为(ods_app_app_community) │\n│ │\n│ posts ◄── post_likes (post_id, user_id) │\n│ │ comments (post_id, user_id) │\n│ └── follows (user_id, following_user_id) │\n└──────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ 事件行为(ods_app_jh_data) |\n│ │\n│ events (uid, event_type, pid) │\n│ communities (uid, event_type) │\n│ remote_events (uid, call_sn ── 含对端uid) │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## 6. identity 设计表与现有表的字段映射\n\n### 6.1 person_identity_links(身份线索关联表)— 待建\n\n| clue_type | 设计文档定义 | 数据来源表 | 来源字段 | 数据量级 |\n|-----------|-------------|-----------|---------|---------|\n| JOYHUB_ID | JOYHUB用户ID | `users.id` | id | - |\n| EMAIL | 邮箱 | `users.email` / `evaluation_order.email` / `lingxing_order.buyer_email` / `edm_contact_user.email` / `customer_platform_info`(type=2) | email | - |\n| PHONE | 电话 | `users.contact_information` / `users.mobile` / `evaluation_order.phone` / `lingxing_order.phone` / `customer_platform_info`(type=1) | phone | - |\n| DEVICE | 设备号 | `users.deviceId` / `user_login_last.deviceId` / `user_device_token_log.device_id` | deviceId | 252万条日志 |\n| ORDER_NAME_ADDRESS | 订单姓名+地址 | `lingxing_order.buyer_name` + `lingxing_order.address` / `customer_address.recipient_name` + `customer_address.detail` | name+address | 5631条地址 |\n| SOCIAL_ACCOUNT | 社交媒体(扩展) | `evaluation_order.twitter` / `evaluation_order.facebook` / `customer_platform_info`(type=5,6) | twitter/facebook | - |\n| PAYMENT_ACCOUNT | 收款账号(扩展) | `customer_payment_account.account_number` / `customer.erp_pay_account` | account | 10万条 |\n\n### 6.2 device_records(设备变化记录)— 待建\n\n| 设计字段 | 数据来源表 | 来源字段 |\n|---------|-----------|---------|\n| `joyhub_id` | `users.id` | id |\n| `device_id` | `users.deviceId` / `user_login_last.deviceId` / `user_device_token_log.device_id` | deviceId |\n| `device_model` | `user_login_last.deviceModel` | deviceModel |\n| `os_version` | `user_login_last.device` / `user_login_last.sysType` | device/sysType |\n| `app_version` | `user_login_last.appVersion` / `users.appVersion` | appVersion |\n| `change_type` | `user_device_token_log.type` | 0=NEW, 1=UPDATE |\n\n### 6.3 contact_context_snapshots(上下文快照)— 待建\n\n| 设计字段 | 数据来源子系统 | 来源表 |\n|---------|--------------|--------|\n| `identity_snapshot` | identity | person_profiles + person_identity_links |\n| `transaction_snapshot` | planning | lingxing_order, lingxing_order_item |\n| `service_snapshot` | support | order_refund, evaluation_order, phone_records |\n| `risk_snapshot` | risk | customer.is_black, customer.high_risk, banned_device_id, blacklist_users_aggregate |\n| `device_snapshot` | identity(M4) | device_records, user_login_last |\n| `outreach_snapshot` | outreach | (待确认) |\n\n\n", + "wikilinks": [], + "category": "layer-technical" + } + }, + { + "id": "doc:08_测试相关/USER用户运营系统_原型逐页详细测试用例集", + "type": "document", + "name": "Sheet: 总览", + "filePath": "08_测试相关/USER用户运营系统_原型逐页详细测试用例集.xlsx", + "summary": "Sheet: 总览 项目 内容 生成方式 按4个HTML原型逐页/逐交互拆解,结合全部需求文档与流程图 覆盖HTML 20260504 USER后台ERP MVP管理员首页高保真原型 v7.html;user erp mvp admin prototype v10 1 .html;客服执行.html;用户运营系统 单文件.html 用例粒度 页面、按钮、列表", + "tags": [ + "08_测试相关", + "测试相关" + ], + "complexity": "complex", + "knowledgeMeta": { + "content": "# Sheet: 总览\n项目 | 内容\n生成方式 | 按4个HTML原型逐页/逐交互拆解,结合全部需求文档与流程图\n覆盖HTML | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html;user_erp_mvp_admin_prototype_v10(1).html;客服执行.html;用户运营系统-单文件.html\n用例粒度 | 页面、按钮、列表字段、筛选、详情弹窗、状态流转、异常、权限、数据、端到端验收\n用例总数 | 308\n说明 | 每条用例均单独编写前置条件、操作步骤、预期结果、数据校验、权限校验和验收标准;不使用统一占位模板。\n# Sheet: HTML1-v7管理员首页\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0001 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看测评需求审核卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在Amazon 运营提交的测评需求模拟数据。 | 卡片=测评需求审核;指标=申请 18 / 已批 8;状态=正常;目标页面=需求中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“测评需求审核”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入需求中心待审核入口”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“测评需求审核”和“申请 18 / 已批 8”,状态为“正常”。\n2. 点击后进入或打开与“需求中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对测评需求审核的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留需求中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:测评需求审核;状态:正常;操作:点击卡片进入需求中心待审核入口 | 待执行\nTC-PROTO-0002 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 多渠道触达引擎 | 功能测试 | 管理员工作台查看渠道推送风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在退订率高于基线的推送风险模拟数据。 | 卡片=渠道推送风险;指标=IM、EDM、TEL、App Push 日周月风险与反馈;状态=偏高;目标页面=推送中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“渠道推送风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入推送风险复核”。\n5. 返回首页后再次查看该卡片是否 | 1. 卡片展示“渠道推送风险”和“IM、EDM、TEL、App Push 日周月风险与反馈”,状态为“偏高”。\n2. 点击后进入或打开与“推送中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对渠道推送风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留推送中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:渠道推送风险;状态:偏高;操作:点击卡片进入推送风险复核 | 待执行\nTC-PROTO-0003 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看新增诈骗事件卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在诈骗同步与黑名单待同步事件模拟数据。 | 卡片=新增诈骗事件;指标=昨 5 / 周 18;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“新增诈骗事件”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入风险中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“新增诈骗事件”和“昨 5 / 周 18”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对新增诈骗事件的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:新增诈骗事件;状态:需复核;操作:点击卡片进入风险中心 | 待执行\nTC-PROTO-0004 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看紧急 Listing卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在评分接近 4.2 的 Listing模拟数据。 | 卡片=紧急 Listing;指标=新 3 / 未处理 7;状态=紧急;目标页面=Listing 管理 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“紧急 Listing”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入紧急 Listing 策略”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“紧急 Listing”和“新 3 / 未处理 7”,状态为“紧急”。\n2. 点击后进入或打开与“Listing 管理”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对紧急 Listing的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留Listing 管理语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:紧急 Listing;状态:紧急;操作:点击卡片进入紧急 Listing 策略 | 待执行\nTC-PROTO-0005 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看推广计划与紧急策略卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已确认需求生成的推广计划模拟数据。 | 卡片=推广计划与紧急策略;指标=日 12 / 周 38;状态=注意审核积压;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“推广计划与紧急策略”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入计划中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“推广计划与紧急策略”和“日 12 / 周 38”,状态为“注意审核积压”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对推广计划与紧急策略的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:推广计划与紧急策略;状态:注意审核积压;操作:点击卡片进入计划中心 | 待执行\nTC-PROTO-0006 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 评价结果追踪 | 功能测试 | 管理员工作台查看评价产出趋势卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在真实消费者回评完成趋势模拟数据。 | 卡片=评价产出趋势;指标=日 18 / 周 96;状态=稳定;目标页面=数据中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“评价产出趋势”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入评价产出趋势”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“评价产出趋势”和“日 18 / 周 96”,状态为“稳定”。\n2. 点击后进入或打开与“数据中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对评价产出趋势的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留数据中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:评价产出趋势;状态:稳定;操作:点击卡片进入评价产出趋势 | 待执行\nTC-PROTO-0007 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看黑名单同步严重度卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在黑名单系统接口超时模拟数据。 | 卡片=黑名单同步严重度;指标=失败 2 / 高危 1;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“黑名单同步严重度”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入黑名单同步”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“黑名单同步严重度”和“失败 2 / 高危 1”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对黑名单同步严重度的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:黑名单同步严重度;状态:需复核;操作:点击卡片进入黑名单同步 | 待执行\nTC-PROTO-0008 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | KOC/KOL协作 | 功能测试 | 管理员工作台查看KOC/KOL 对接卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在PR 对外联系、价格、CODE、返点和提款进度模拟数据。 | 卡片=KOC/KOL 对接;指标=2 个逾期;状态=逾期;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“KOC/KOL 对接”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入对外合作跟进”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“KOC/KOL 对接”和“2 个逾期”,状态为“逾期”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对KOC/KOL 对接的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:KOC/KOL 对接;状态:逾期;操作:点击卡片进入对外合作跟进 | 待执行\nTC-PROTO-0009 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 客服工单与管理 | 功能测试 | 管理员工作台查看菲律宾团队管理卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在菲律宾团队工作时长、请假、缺席、人均产出模拟数据。 | 卡片=菲律宾团队管理;指标=风险 2 / 缺口 1;状态=排班风险;目标页面=客服中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“菲律宾团队管理”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入客服中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“菲律宾团队管理”和“风险 2 / 缺口 1”,状态为“排班风险”。\n2. 点击后进入或打开与“客服中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对菲律宾团队管理的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留客服中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:菲律宾团队管理;状态:排班风险;操作:点击卡片进入客服中心 | 待执行\nTC-PROTO-0010 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 审计与通知中心 | 功能测试 | 管理员工作台查看审核积压与风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已发现问题汇总到总页面模拟数据。 | 卡片=审核积压与风险;指标=卡点 4;状态=影响进度;目标页面=工作台 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“审核积压与风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入处理卡点”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“审核积压与风险”和“卡点 4”,状态为“影响进度”。\n2. 点击后进入或打开与“工作台”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对审核积压与风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留工作台语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:审核积压与风险;状态:影响进度;操作:点击卡片进入处理卡点 | 待执行\nTC-PROTO-0011 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对测评需求执行接收动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“测评需求”;当前环节为“Amazon 已批准”,负责人为“用户运营负责人”。 | 事项=测评需求;来源=飞书需求表单 DEMO-001;截止=今日 18:00;处理动作=接收;描述=评分 4.46,低于 4.5,需要生成用户互动与真实评价跟踪计划 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“测评需求”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“接收”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:测评需求已核对”。\n7. 点击确认提交。 | 1. 队列行展示测评需求、Amazon 已批准、用户运营负责人、今日 18:00。\n2. 详情弹窗打开,展示来源“飞书需求表单 DEMO-001”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:测评需求;操作:接收 | 待执行\nTC-PROTO-0012 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 测评需求处理意见为空时阻止提交 | P2 | 已打开“测评需求”详情弹窗;当前用户有“接收”权限。 | 动作类型=通过/确认;处理意见=空;事项=测评需求 | 1. 在“测评需求”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0013 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对昨日推送风险执行复核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“昨日推送风险”;当前环节为“待复核”,负责人为“用户运营组长”。 | 事项=昨日推送风险;来源=推送风险自动单 DEMO-006;截止=今日 12:00;处理动作=复核;描述=昨日推送退订率高于基线,需复核人群、素材和文案 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“昨日推送风险”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“复核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:昨日推送风险已核对”。\n7. 点击确认提交。 | 1. 队列行展示昨日推送风险、待复核、用户运营组长、今日 12:00。\n2. 详情弹窗打开,展示来源“推送风险自动单 DEMO-006”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:昨日推送风险;操作:复核 | 待执行\nTC-PROTO-0014 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 昨日推送风险处理意见为空时阻止提交 | P2 | 已打开“昨日推送风险”详情弹窗;当前用户有“复核”权限。 | 动作类型=通过/确认;处理意见=空;事项=昨日推送风险 | 1. 在“昨日推送风险”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0015 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对待同步黑名单执行审核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“待同步黑名单”;当前环节为“待审核”,负责人为“风险负责人”。 | 事项=待同步黑名单;来源=客服升级表单 DEMO-003;截止=今日 14:00;处理动作=审核;描述=同一 JOYHUB ID 与多个 Profile ID 关联异常样品申请,邮箱和设备号已脱敏 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“待同步黑名单”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:待同步黑名单已核对”。\n7. 点击确认提交。 | 1. 队列行展示待同步黑名单、待审核、风险负责人、今日 14:00。\n2. 详情弹窗打开,展示来源“客服升级表单 DEMO-003”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:待同步黑名单;操作:审核 | 待执行\nTC-PROTO-0016 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 待同步黑名单处理意见为空时阻止提交 | P2 | 已打开“待同步黑名单”详情弹窗;当前用户有“审核”权限。 | 动作类型=通过/确认;处理意见=空;事项=待同步黑名单 | 1. 在“待同步黑名单”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0017 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对紧急策略审批执行审批动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“紧急策略审批”;当前环节为“待系统管理员确认”,负责人为“Amazon 运营总监”。 | 事项=紧急策略审批;来源=紧急 Listing 表单 DEMO-004;截止=今日 11:30;处理动作=审批;描述=当前评分 4.21,接近 4.2 紧急阈值,需要 Amazon 与用户运营联合策略 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“紧急策略审批”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审批”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:紧急策略审批已核对”。\n7. 点击确认提交。 | 1. 队列行展示紧急策略审批、待系统管理员确认、Amazon 运营总监、今日 11:30。\n2. 详情弹窗打开,展示来源“紧急 Listing 表单 DEMO-004”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:紧急策略审批;操作:审批 | 待执行\nTC-PROTO-0018 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 紧急策略审批处理意见为空时阻止提交 | P2 | 已打开“紧急策略审批”详情弹窗;当前用户有“审批”权限。 | 动作类型=通过/确认;处理意见=空;事项=紧急策略审批 | 1. 在“紧急策略审批”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0019 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对差评跟进执行分配动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“差评跟进”;当前环节为“客服升级”,负责人为“客服负责人”。 | 事项=差评跟进;来源=飞书客服需求 DEMO-005;截止=明日 10:00;处理动作=分配;描述=用户反馈产品说明理解偏差,需要客服跟进并回传产品改进建议 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“差评跟进”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“分配”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:差评跟进已核对”。\n7. 点击确认提交。 | 1. 队列行展示差评跟进、客服升级、客服负责人、明日 10:00。\n2. 详情弹窗打开,展示来源“飞书客服需求 DEMO-005”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:差评跟进;操作:分配 | 待执行\nTC-PROTO-0020 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 差评跟进处理意见为空时阻止提交 | P2 | 已打开“差评跟进”详情弹窗;当前用户有“分配”权限。 | 动作类型=通过/确认;处理意见=空;事项=差评跟进 | 1. 在“差评跟进”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0021 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入需求中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“需求中心”;模拟数据已加载。 | 页面=需求中心;主按钮=Amazon 提交测评需求;辅助按钮=待审核入口;字段=需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 1. 在管理员首页左侧导航点击“需求中心”。\n2. 观察页面标题是否切换为“需求中心列表”。\n3. 检查列表表头是否包含:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作。\n4. 点击页面主按钮“Amazon 提交测评需求”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“待审核入口”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“需求中心”。\n2. 列表字段与原型定义一致。\n3. “Amazon 提交测评需求”和“待审核入口”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问需求中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合需求中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:需求中心;按钮:Amazon 提交测评需求/待审核入口;字段:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 待执行\nTC-PROTO-0022 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入Listing 管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“Listing 管理”;模拟数据已加载。 | 页面=Listing 管理;主按钮=创建紧急策略;辅助按钮=更多;字段=站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 1. 在管理员首页左侧导航点击“Listing 管理”。\n2. 观察页面标题是否切换为“Listing 管理列表”。\n3. 检查列表表头是否包含:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度。\n4. 点击页面主按钮“创建紧急策略”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“更多”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“Listing 管理”。\n2. 列表字段与原型定义一致。\n3. “创建紧急策略”和“更多”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问Listing 管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合Listing 管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:Listing 管理;按钮:创建紧急策略/更多;字段:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 待执行\nTC-PROTO-0023 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入计划中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“计划中心”;模拟数据已加载。 | 页面=计划中心;主按钮=生成计划;辅助按钮=批量审批;字段=计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 1. 在管理员首页左侧导航点击“计划中心”。\n2. 观察页面标题是否切换为“计划中心列表”。\n3. 检查列表表头是否包含:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人。\n4. 点击页面主按钮“生成计划”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“批量审批”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“计划中心”。\n2. 列表字段与原型定义一致。\n3. “生成计划”和“批量审批”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问计划中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合计划中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:计划中心;按钮:生成计划/批量审批;字段:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 待执行\nTC-PROTO-0024 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入推送中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“推送中心”;模拟数据已加载。 | 页面=推送中心;主按钮=计划与推送分配;辅助按钮=风险复核;字段=推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 1. 在管理员首页左侧导航点击“推送中心”。\n2. 观察页面标题是否切换为“推送中心列表”。\n3. 检查列表表头是否包含:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订。\n4. 点击页面主按钮“计划与推送分配”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“风险复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“推送中心”。\n2. 列表字段与原型定义一致。\n3. “计划与推送分配”和“风险复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问推送中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合推送中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:推送中心;按钮:计划与推送分配/风险复核;字段:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 待执行\nTC-PROTO-0025 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入客服中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“客服中心”;模拟数据已加载。 | 页面=客服中心;主按钮=分配工单;辅助按钮=流转;字段=工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 1. 在管理员首页左侧导航点击“客服中心”。\n2. 观察页面标题是否切换为“客服中心列表”。\n3. 检查列表表头是否包含:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出。\n4. 点击页面主按钮“分配工单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“流转”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“客服中心”。\n2. 列表字段与原型定义一致。\n3. “分配工单”和“流转”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问客服中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合客服中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:客服中心;按钮:分配工单/流转;字段:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 待执行\nTC-PROTO-0026 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入风险中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“风险中心”;模拟数据已加载。 | 页面=风险中心;主按钮=同步黑名单;辅助按钮=规则复核;字段=事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“风险中心”。\n2. 观察页面标题是否切换为“风险中心列表”。\n3. 检查列表表头是否包含:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“同步黑名单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“规则复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“风险中心”。\n2. 列表字段与原型定义一致。\n3. “同步黑名单”和“规则复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问风险中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合风险中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:风险中心;按钮:同步黑名单/规则复核;字段:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0027 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 数据中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入数据中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“数据中心”;模拟数据已加载。 | 页面=数据中心;主按钮=立即同步;辅助按钮=导出;字段=来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“数据中心”。\n2. 观察页面标题是否切换为“数据中心列表”。\n3. 检查列表表头是否包含:来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“立即同步”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“导出”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“数据中心”。\n2. 列表字段与原型定义一致。\n3. “立即同步”和“导出”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问数据中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合数据中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:数据中心;按钮:立即同步/导出;字段:来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0028 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入报表中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“报表中心”;模拟数据已加载。 | 页面=报表中心;主按钮=生成/下载报表;辅助按钮=上传记录;字段=报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 1. 在管理员首页左侧导航点击“报表中心”。\n2. 观察页面标题是否切换为“报表中心列表”。\n3. 检查列表表头是否包含:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏。\n4. 点击页面主按钮“生成/下载报表”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“上传记录”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“报表中心”。\n2. 列表字段与原型定义一致。\n3. “生成/下载报表”和“上传记录”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问报表中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合报表中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:报表中心;按钮:生成/下载报表/上传记录;字段:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 待执行\nTC-PROTO-0029 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入系统管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“系统管理”;模拟数据已加载。 | 页面=系统管理;主按钮=新建账号;辅助按钮=离职管理;字段=配置ID、模块、说明、权限分配、审计日志 | 1. 在管理员首页左侧导航点击“系统管理”。\n2. 观察页面标题是否切换为“系统管理列表”。\n3. 检查列表表头是否包含:配置ID、模块、说明、权限分配、审计日志。\n4. 点击页面主按钮“新建账号”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“离职管理”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“系统管理”。\n2. 列表字段与原型定义一致。\n3. “新建账号”和“离职管理”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问系统管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合系统管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:系统管理;按钮:新建账号/离职管理;字段:配置ID、模块、说明、权限分配、审计日志 | 待执行\nTC-PROTO-0030 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 7 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 7 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 7 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 7 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0031 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 30 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 30 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 30 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 30 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0032 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换本月时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=本月;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“本月”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“本月”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0033 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换自定义时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=自定义;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“自定义”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“自定义”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0170 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换全部标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“全部”。 | 标签=全部;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“全部”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“全部”。 | 1. 队列只展示与“全部”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:全部 | 待执行\nTC-PROTO-0171 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换审核标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“审核”。 | 标签=审核;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“审核”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“审核”。 | 1. 队列只展示与“审核”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:审核 | 待执行\nTC-PROTO-0172 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换黑名单标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“黑名单”。 | 标签=黑名单;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“黑名单”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“黑名单”。 | 1. 队列只展示与“黑名单”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:黑名单 | 待执行\nTC-PROTO-0173 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换推送标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“推送”。 | 标签=推送;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“推送”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“推送”。 | 1. 队列只展示与“推送”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:推送 | 待执行\nTC-PROTO-0174 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门全部部门状态全部状态风险全部风险负责人全部负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=全部部门;状态=全部状态;风险=全部风险;负责人=全部负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“全部部门”。\n3. 选择状态“全部状态”、风险“全部风险”、负责人“全部负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合全部部门/全部状态/全部风险/全部负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0175 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门Amazon 运营状态待审批风险全部风险负责人Amazon 总监组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=Amazon 运营;状态=待审批;风险=全部风险;负责人=Amazon 总监 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“Amazon 运营”。\n3. 选择状态“待审批”、风险“全部风险”、负责人“Amazon 总监”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合Amazon 运营/待审批/全部风险/Amazon 总监的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0176 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门用户运营状态待复核风险偏高负责人用户运营组长组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=用户运营;状态=待复核;风险=偏高;负责人=用户运营组长 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“用户运营”。\n3. 选择状态“待复核”、风险“偏高”、负责人“用户运营组长”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合用户运营/待复核/偏高/用户运营组长的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0177 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门客服状态客服升级风险高风险负责人客服负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=客服;状态=客服升级;风险=高风险;负责人=客服负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“客服”。\n3. 选择状态“客服升级”、风险“高风险”、负责人“客服负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合客服/客服升级/高风险/客服负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0178 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门系统管理员状态待系统管理员确认风险紧急负责人系统管理员组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=系统管理员;状态=待系统管理员确认;风险=紧急;负责人=系统管理员 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“系统管理员”。\n3. 选择状态“待系统管理员确认”、风险“紧急”、负责人“系统管理员”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合系统管理员/待系统管理员确认/紧急/系统管理员的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0220 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“需求中心”;存在业务条件:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。 | 按钮=查看完整信息;条件=JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:打开详情后点击查看完整信息。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0221 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:分配 | P2 | 系统管理员已进入“需求中心”;存在业务条件:测评需求Amazon已批准待用户运营接收。 | 按钮=分配;条件=测评需求Amazon已批准待用户运营接收 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:测评需求Amazon已批准待用户运营接收。\n3. 点击该行或页面上的“分配”按钮。\n4. 按页面提示执行:选择下一负责人为用户运营负责人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配”入口可用且文案正确。\n2. 操作后结果为:负责人变更并通知。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=分配 | 待执行\nTC-PROTO-0222 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:审批/确认 | P2 | 系统管理员已进入“需求中心”;存在业务条件:需求评分4.46低于4.5。 | 按钮=审批/确认;条件=需求评分4.46低于4.5 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:需求评分4.46低于4.5。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:选择通过/确认并填写计划建议。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:需求进入待生成计划。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=审批/确认 | 待执行\nTC-PROTO-0223 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:查看完整信息 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:评分4.21接近4.2紧急阈值。 | 按钮=查看完整信息;条件=评分4.21接近4.2紧急阈值 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:评分4.21接近4.2紧急阈值。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:查看ASIN完整站点与评价数据。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:敏感数据按权限展示。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=查看完整信息 | 待执行\nTC-PROTO-0224 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:创建紧急策略 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:紧急Listing未处理7条。 | 按钮=创建紧急策略;条件=紧急Listing未处理7条 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:紧急Listing未处理7条。\n3. 点击该行或页面上的“创建紧急策略”按钮。\n4. 按页面提示执行:填写策略参与人员与截止时间。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “创建紧急策略”入口可用且文案正确。\n2. 操作后结果为:生成紧急策略审批事项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的创建紧急策略动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=创建紧急策略 | 待执行\nTC-PROTO-0225 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:审批/确认 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:待系统管理员确认。 | 按钮=审批/确认;条件=待系统管理员确认 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:待系统管理员确认。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:确认联合策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:状态进入用户运营执行。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=审批/确认 | 待执行\nTC-PROTO-0226 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:生成计划 | P2 | 系统管理员已进入“计划中心”;存在业务条件:已确认需求存在且目标量明确。 | 按钮=生成计划;条件=已确认需求存在且目标量明确 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:已确认需求存在且目标量明确。\n3. 点击该行或页面上的“生成计划”按钮。\n4. 按页面提示执行:选择推新/回评/免评并拆分计划项。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成计划”入口可用且文案正确。\n2. 操作后结果为:生成计划ID和计划项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的生成计划动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=生成计划 | 待执行\nTC-PROTO-0227 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:批量审批 | P2 | 系统管理员已进入“计划中心”;存在业务条件:多条计划处于待审批。 | 按钮=批量审批;条件=多条计划处于待审批 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:多条计划处于待审批。\n3. 点击该行或页面上的“批量审批”按钮。\n4. 按页面提示执行:勾选多条计划并提交统一审批意见。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “批量审批”入口可用且文案正确。\n2. 操作后结果为:批量生成审批记录。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的批量审批动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=批量审批 | 待执行\nTC-PROTO-0228 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:流转 | P2 | 系统管理员已进入“计划中心”;存在业务条件:计划覆盖状态部分覆盖。 | 按钮=流转;条件=计划覆盖状态部分覆盖 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:计划覆盖状态部分覆盖。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看计划状态流转记录。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:展示创建、审批、执行节点。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=流转 | 待执行\nTC-PROTO-0229 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:计划与推送分配 | P2 | 系统管理员已进入“推送中心”;存在业务条件:周度推送计划待审。 | 按钮=计划与推送分配;条件=周度推送计划待审 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:周度推送计划待审。\n3. 点击该行或页面上的“计划与推送分配”按钮。\n4. 按页面提示执行:分配IM/EDM/TEL/App Push策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “计划与推送分配”入口可用且文案正确。\n2. 操作后结果为:生成推送任务。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的计划与推送分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=计划与推送分配 | 待执行\nTC-PROTO-0230 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:风险复核 | P2 | 系统管理员已进入“推送中心”;存在业务条件:退订率高于基线。 | 按钮=风险复核;条件=退订率高于基线 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线。\n3. 点击该行或页面上的“风险复核”按钮。\n4. 按页面提示执行:查看人群、素材、文案并选择暂停同策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “风险复核”入口可用且文案正确。\n2. 操作后结果为:推送状态变暂停待审。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的风险复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=风险复核 | 待执行\nTC-PROTO-0231 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:导出 | P2 | 系统管理员已进入“推送中心”;存在业务条件:当前筛选为推送风险。 | 按钮=导出;条件=当前筛选为推送风险 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:当前筛选为推送风险。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出推送ID、计划、渠道、发送点击回复退订。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:导出文件脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=导出 | 待执行\nTC-PROTO-0232 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:分配工单 | P2 | 系统管理员已进入“客服中心”;存在业务条件:差评跟进客服升级。 | 按钮=分配工单;条件=差评跟进客服升级 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:差评跟进客服升级。\n3. 点击该行或页面上的“分配工单”按钮。\n4. 按页面提示执行:选择客服A并填写分配原因。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配工单”入口可用且文案正确。\n2. 操作后结果为:工单状态变处理中。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的分配工单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=分配工单 | 待执行\nTC-PROTO-0233 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:流转 | P2 | 系统管理员已进入“客服中心”;存在业务条件:承诺配合用户待回访。 | 按钮=流转;条件=承诺配合用户待回访 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:承诺配合用户待回访。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看待回访和请假0.5天影响。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:生成回访待办。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=流转 | 待执行\nTC-PROTO-0234 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:导出 | P2 | 系统管理员已进入“客服中心”;存在业务条件:菲律宾团队管理。 | 按钮=导出;条件=菲律宾团队管理 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:菲律宾团队管理。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出工作时长、出勤、人均产出。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:仅主管可导出。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=导出 | 待执行\nTC-PROTO-0235 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:同步黑名单 | P2 | 系统管理员已进入“风险中心”;存在业务条件:接口超时失败待重试。 | 按钮=同步黑名单;条件=接口超时失败待重试 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:接口超时失败待重试。\n3. 点击该行或页面上的“同步黑名单”按钮。\n4. 按页面提示执行:点击同步黑名单。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “同步黑名单”入口可用且文案正确。\n2. 操作后结果为:失败保留并进入重试队列。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的同步黑名单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=同步黑名单 | 待执行\nTC-PROTO-0236 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:规则复核 | P2 | 系统管理员已进入“风险中心”;存在业务条件:退订率高于基线规则提醒。 | 按钮=规则复核;条件=退订率高于基线规则提醒 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线规则提醒。\n3. 点击该行或页面上的“规则复核”按钮。\n4. 按页面提示执行:查看规则依据并确认/误报。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “规则复核”入口可用且文案正确。\n2. 操作后结果为:复核结论写入风险事件。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的规则复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=规则复核 | 待执行\nTC-PROTO-0237 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“风险中心”;存在业务条件:Profile/邮箱/设备号脱敏。 | 按钮=查看完整信息;条件=Profile/邮箱/设备号脱敏 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:Profile/邮箱/设备号脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:授权角色查看完整主体摘要。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0238 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:生成/下载报表 | P2 | 系统管理员已进入“报表中心”;存在业务条件:Listing健康日报每日08:30自动生成。 | 按钮=生成/下载报表;条件=Listing健康日报每日08:30自动生成 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:Listing健康日报每日08:30自动生成。\n3. 点击该行或页面上的“生成/下载报表”按钮。\n4. 按页面提示执行:点击生成/下载报表。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成/下载报表”入口可用且文案正确。\n2. 操作后结果为:生成记录可下载且脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的生成/下载报表动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=生成/下载报表 | 待执行\nTC-PROTO-0239 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:上传记录 | P2 | 系统管理员已进入“报表中心”;存在业务条件:推送效果与风险复盘支持上传补充记录。 | 按钮=上传记录;条件=推送效果与风险复盘支持上传补充记录 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:推送效果与风险复盘支持上传补充记录。\n3. 点击该行或页面上的“上传记录”按钮。\n4. 按页面提示执行:上传人工复核附件。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “上传记录”入口可用且文案正确。\n2. 操作后结果为:附件关联报表ID。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的上传记录动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=上传记录 | 待执行\nTC-PROTO-0240 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:新建账号 | P2 | 系统管理员已进入“系统管理”;存在业务条件:按部门角色站点数据范围开通。 | 按钮=新建账号;条件=按部门角色站点数据范围开通 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:按部门角色站点数据范围开通。\n3. 点击该行或页面上的“新建账号”按钮。\n4. 按页面提示执行:录入账号、部门、角色、站点范围。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “新建账号”入口可用且文案正确。\n2. 操作后结果为:账号可登录且权限生效。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的新建账号动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=新建账号 | 待执行\nTC-PROTO-0241 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:离职管理 | P2 | 系统管理员已进入“系统管理”;存在业务条件:停用账号交接任务回收权限。 | 按钮=离职管理;条件=停用账号交接任务回收权限 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:停用账号交接任务回收权限。\n3. 点击该行或页面上的“离职管理”按钮。\n4. 按页面提示执行:选择离职员工并指定交接人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “离职管理”入口可用且文案正确。\n2. 操作后结果为:账号停用且敏感权限回收。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的离职管理动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=离职管理 | 待执行\nTC-PROTO-0285 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:重复点击处理卡点 | P2 | 已进入“工作台”;准备可执行场景:重复点击处理卡点。 | 动作=连续点击处理卡点按钮2次;预期=只打开一个详情/处理弹窗,不重复创建处理记录 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击处理卡点按钮2次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只打开一个详情/处理弹窗,不重复创建处理记录。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:重复点击处理卡点 | 待执行\nTC-PROTO-0286 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:刷新后保持时间范围 | P2 | 已进入“工作台”;准备可执行场景:刷新后保持时间范围。 | 动作=选择最近30天后刷新页面;预期=仍显示最近30天或按产品定义恢复默认并不报错 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:选择最近30天后刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:仍显示最近30天或按产品定义恢复默认并不报错。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:刷新后保持时间范围 | 待执行\nTC-PROTO-0287 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:黑名单同步重复提交 | P2 | 已进入“风险中心”;准备可执行场景:黑名单同步重复提交。 | 动作=同步黑名单按钮连续点击;预期=只生成一次同步任务,第二次提示处理中 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同步黑名单按钮连续点击。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只生成一次同步任务,第二次提示处理中。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:黑名单同步重复提交 | 待执行\nTC-PROTO-0288 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统稳定性与幂等 | 异常场景 | 报表中心稳定性校验:报表生成中重复下载 | P2 | 已进入“报表中心”;准备可执行场景:报表生成中重复下载。 | 动作=报表状态自动生成中点击下载;预期=提示生成中,不下载空文件 | 1. 打开原型页面“报表中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:报表状态自动生成中点击下载。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示生成中,不下载空文件。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:报表生成中重复下载 | 待执行\n# Sheet: HTML2-v10管理原型\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0034 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面用户管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“用户管理”。 | 现有页面=用户中心 / 用户;字段=JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍;查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;MVP用途=用户画像筛选、推送人群、客服定位、风险排查 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“用户管理”。\n3. 核对现有页面名称是否为“用户中心 / 用户”。\n4. 展开字段详情,逐项核对字段:JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍。\n5. 在查询条件区按“搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “用户管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:用户管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0035 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 异常场景 | 用户管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“用户管理”关系页面;用户有查询权限。 | 查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;输入值=不存在的标签/身份/国家组合 | 1. 在“用户管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0036 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面公域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-用户标签”。 | 现有页面=标签 / 公域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态;查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;MVP用途=公域人群圈选、覆盖人数评估、推送前过滤 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 公域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态。\n5. 在查询条件区按“搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0037 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 异常场景 | 公域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-用户标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0038 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 功能测试 | 现有ERP页面公域-产品标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-产品标签”。 | 现有页面=标签 / 公域产品;字段=标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人;查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;MVP用途=产品分层、Listing 健康策略、产品绑定率分析 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-产品标签”。\n3. 核对现有页面名称是否为“标签 / 公域产品”。\n4. 展开字段详情,逐项核对字段:标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人。\n5. 在查询条件区按“搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-产品标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-产品标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0039 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 异常场景 | 公域-产品标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-产品标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-产品标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0040 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面私域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“私域-用户标签”。 | 现有页面=标签 / 私域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态;查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;MVP用途=私域精细运营、客服分组、活动复盘、风险用户隔离 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“私域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 私域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态。\n5. 在查询条件区按“标签分类、打标方式、覆盖人数、状态、时间范围”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “私域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:私域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0041 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 异常场景 | 私域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“私域-用户标签”关系页面;用户有查询权限。 | 查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;输入值=不存在的标签/身份/国家组合 | 1. 在“私域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0042 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面身份管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“身份管理”。 | 现有页面=身份;字段=账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作;查询条件=身份名称、身份分组、状态、更新时间;MVP用途=识别官方、品牌、达人、风险、客服等用户身份 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“身份管理”。\n3. 核对现有页面名称是否为“身份”。\n4. 展开字段详情,逐项核对字段:账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作。\n5. 在查询条件区按“身份名称、身份分组、状态、更新时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “身份管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:身份管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0043 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 异常场景 | 身份管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“身份管理”关系页面;用户有查询权限。 | 查询条件=身份名称、身份分组、状态、更新时间;输入值=不存在的标签/身份/国家组合 | 1. 在“身份管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0044 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 用户标签关系用于人群圈选、用户画像、风险过滤 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 用户标签;关系类型=多对多;用途=人群圈选、用户画像、风险过滤 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 用户标签”。\n4. 查看关系类型是否显示为“多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“人群圈选、用户画像、风险过滤”。 | 1. 关系“用户 - 用户标签”存在。\n2. 类型展示为“多对多”。\n3. 详情能说明该关系服务于“人群圈选、用户画像、风险过滤”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 用户标签 | 待执行\nTC-PROTO-0045 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 身份关系用于官方、品牌、达人、客服、风险身份识别 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 身份;关系类型=待确认:一对多或多对多;用途=官方、品牌、达人、客服、风险身份识别 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 身份”。\n4. 查看关系类型是否显示为“待确认:一对多或多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“官方、品牌、达人、客服、风险身份识别”。 | 1. 关系“用户 - 身份”存在。\n2. 类型展示为“待确认:一对多或多对多”。\n3. 详情能说明该关系服务于“官方、品牌、达人、客服、风险身份识别”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 身份 | 待执行\nTC-PROTO-0046 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 产品关系用于测评用户池、客服定位 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 产品;关系类型=绑定/连接产品;用途=测评用户池、客服定位 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 产品”。\n4. 查看关系类型是否显示为“绑定/连接产品”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“测评用户池、客服定位”。 | 1. 关系“用户 - 产品”存在。\n2. 类型展示为“绑定/连接产品”。\n3. 详情能说明该关系服务于“测评用户池、客服定位”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 产品 | 待执行\nTC-PROTO-0047 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验产品 - 产品标签 - Listing关系用于Listing 健康策略、ASIN 归因 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=产品 - 产品标签 - Listing;关系类型=产品分层;用途=Listing 健康策略、ASIN 归因 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“产品 - 产品标签 - Listing”。\n4. 查看关系类型是否显示为“产品分层”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“Listing 健康策略、ASIN 归因”。 | 1. 关系“产品 - 产品标签 - Listing”存在。\n2. 类型展示为“产品分层”。\n3. 详情能说明该关系服务于“Listing 健康策略、ASIN 归因”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:产品 - 产品标签 - Listing | 待执行\nTC-PROTO-0048 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 活动关系用于KOC/KOL、私域运营沉淀 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 活动;关系类型=活动参与;用途=KOC/KOL、私域运营沉淀 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 活动”。\n4. 查看关系类型是否显示为“活动参与”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“KOC/KOL、私域运营沉淀”。 | 1. 关系“用户 - 活动”存在。\n2. 类型展示为“活动参与”。\n3. 详情能说明该关系服务于“KOC/KOL、私域运营沉淀”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 活动 | 待执行\nTC-PROTO-0049 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 推送关系用于IM/EDM/TEL/App Push 频控、点击、回复、退订 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 推送;关系类型=一对多;用途=IM/EDM/TEL/App Push 频控、点击、回复、退订 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 推送”。\n4. 查看关系类型是否显示为“一对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“IM/EDM/TEL/App Push 频控、点击、回复、退订”。 | 1. 关系“用户 - 推送”存在。\n2. 类型展示为“一对多”。\n3. 详情能说明该关系服务于“IM/EDM/TEL/App Push 频控、点击、回复、退订”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 推送 | 待执行\nTC-PROTO-0050 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 风险/黑名单关系用于诈骗同步、客服升级、风险用户隔离 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 风险/黑名单;关系类型=风险隔离;用途=诈骗同步、客服升级、风险用户隔离 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 风险/黑名单”。\n4. 查看关系类型是否显示为“风险隔离”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“诈骗同步、客服升级、风险用户隔离”。 | 1. 关系“用户 - 风险/黑名单”存在。\n2. 类型展示为“风险隔离”。\n3. 详情能说明该关系服务于“诈骗同步、客服升级、风险用户隔离”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 风险/黑名单 | 待执行\nTC-PROTO-0051 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行用户主档查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=用户主档查询;查询条件=JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间;预期输出=用户主档、标签、身份、产品关系、近期活跃;使用页面=用户中心 / 客服中心 / 风险中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“用户主档查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间。\n5. 点击查询。\n6. 查看结果区是否输出:用户主档、标签、身份、产品关系、近期活跃。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “用户主档查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“用户主档、标签、身份、产品关系、近期活跃”。\n4. 跳转到“用户中心 / 客服中心 / 风险中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:用户主档查询 | 待执行\nTC-PROTO-0052 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行推送前人群圈选并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=推送前人群圈选;查询条件=标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数;预期输出=候选用户数、预计触达、频控风险、可保存人群包;使用页面=计划中心 / 推送中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“推送前人群圈选”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数。\n5. 点击查询。\n6. 查看结果区是否输出:候选用户数、预计触达、频控风险、可保存人群包。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “推送前人群圈选”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“候选用户数、预计触达、频控风险、可保存人群包”。\n4. 跳转到“计划中心 / 推送中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:推送前人群圈选 | 待执行\nTC-PROTO-0053 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行测评与真实评价跟踪人群并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=测评与真实评价跟踪人群;查询条件=ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除;预期输出=推荐用户池、排除用户池、进入计划中心;使用页面=需求中心 / 计划中心 / 客服中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“测评与真实评价跟踪人群”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除。\n5. 点击查询。\n6. 查看结果区是否输出:推荐用户池、排除用户池、进入计划中心。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “测评与真实评价跟踪人群”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“推荐用户池、排除用户池、进入计划中心”。\n4. 跳转到“需求中心 / 计划中心 / 客服中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:测评与真实评价跟踪人群 | 待执行\nTC-PROTO-0054 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行标签覆盖查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=标签覆盖查询;查询条件=标签分类、打标方式、覆盖人数、最新打标时间、状态;预期输出=标签列表、覆盖趋势、异常覆盖提示;使用页面=现有ERP / 数据中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“标签覆盖查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签分类、打标方式、覆盖人数、最新打标时间、状态。\n5. 点击查询。\n6. 查看结果区是否输出:标签列表、覆盖趋势、异常覆盖提示。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “标签覆盖查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“标签列表、覆盖趋势、异常覆盖提示”。\n4. 跳转到“现有ERP / 数据中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:标签覆盖查询 | 待执行\nTC-PROTO-0055 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行身份风险查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=身份风险查询;查询条件=身份名称、身份分组、风险等级、状态;预期输出=风险用户池、客服/推送排除名单、黑名单同步候选;使用页面=风险中心 / 系统管理 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“身份风险查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:身份名称、身份分组、风险等级、状态。\n5. 点击查询。\n6. 查看结果区是否输出:风险用户池、客服/推送排除名单、黑名单同步候选。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “身份风险查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“风险用户池、客服/推送排除名单、黑名单同步候选”。\n4. 跳转到“风险中心 / 系统管理”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:身份风险查询 | 待执行\nTC-PROTO-0056 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行新建账号并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“新建账号”。 | 动作=新建账号;说明=按部门、角色、站点、数据范围开通账号;预期=账号创建后出现在系统管理列表 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“新建账号”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:按部门、角色、站点、数据范围开通账号。\n6. 提交后进入审计日志,按动作类型筛选“新建账号”。 | 1. 有权限角色可完成“新建账号”。\n2. 执行结果符合“账号创建后出现在系统管理列表”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 新建账号仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:新建账号;权限分配;审计日志 | 待执行\nTC-PROTO-0057 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行离职管理并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“离职管理”。 | 动作=离职管理;说明=停用账号、交接任务、回收敏感权限;预期=离职账号不可登录且任务已交接 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“离职管理”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:停用账号、交接任务、回收敏感权限。\n6. 提交后进入审计日志,按动作类型筛选“离职管理”。 | 1. 有权限角色可完成“离职管理”。\n2. 执行结果符合“离职账号不可登录且任务已交接”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 离职管理仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:离职管理;权限分配;审计日志 | 待执行\nTC-PROTO-0058 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行权限分配并校验权限与审计 | P1 | 用户以系统管理员/负责人身份登录;系统资产模块可访问;系统管理列表包含“权限分配”。 | 动作=权限分配;说明=导出、审批、查看敏感信息、黑名单同步独立授权;预期=权限粒度按按钮和数据范围生效 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“权限分配”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、审批、查看敏感信息、黑名单同步独立授权。\n6. 提交后进入审计日志,按动作类型筛选“权限分配”。 | 1. 有权限角色可完成“权限分配”。\n2. 执行结果符合“权限粒度按按钮和数据范围生效”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 权限分配仅对系统管理员/负责人开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:权限分配;权限分配;审计日志 | 待执行\nTC-PROTO-0059 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行审计日志并校验权限与审计 | P1 | 用户以系统管理员/审计角色身份登录;系统资产模块可访问;系统管理列表包含“审计日志”。 | 动作=审计日志;说明=导出、查看敏感信息、黑名单同步、审批动作;预期=每个敏感动作有日志ID和操作者 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“审计日志”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、查看敏感信息、黑名单同步、审批动作。\n6. 提交后进入审计日志,按动作类型筛选“审计日志”。 | 1. 有权限角色可完成“审计日志”。\n2. 执行结果符合“每个敏感动作有日志ID和操作者”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 审计日志仅对系统管理员/审计角色开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:审计日志;权限分配;审计日志 | 待执行\nTC-PROTO-0179 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段JOYHUB 用户ID按全员可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“JOYHUB 用户ID”存在。 | 字段=JOYHUB 用户ID;可见范围=全员可见;期望=完整ID可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“JOYHUB 用户ID”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“JOYHUB 用户ID”按照“全员可见”控制可见性。\n2. 符合预期:完整ID可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | JOYHUB 用户ID必须按全员可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:JOYHUB 用户ID/全员可见 | 待执行\nTC-PROTO-0180 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段用户名按授权可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“用户名”存在。 | 字段=用户名;可见范围=授权可见;期望=未授权显示脱敏 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“用户名”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“用户名”按照“授权可见”控制可见性。\n2. 符合预期:未授权显示脱敏。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 用户名必须按授权可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:用户名/授权可见 | 待执行\nTC-PROTO-0181 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段邮箱后缀按已脱敏权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“邮箱后缀”存在。 | 字段=邮箱后缀;可见范围=已脱敏;期望=只显示邮箱域名/后缀 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“邮箱后缀”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“邮箱后缀”按照“已脱敏”控制可见性。\n2. 符合预期:只显示邮箱域名/后缀。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 邮箱后缀必须按已脱敏控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:邮箱后缀/已脱敏 | 待执行\nTC-PROTO-0182 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段近7天EDM推送数按推送数据权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“近7天EDM推送数”存在。 | 字段=近7天EDM推送数;可见范围=推送数据;期望=仅推送/管理员可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“近7天EDM推送数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“近7天EDM推送数”按照“推送数据”控制可见性。\n2. 符合预期:仅推送/管理员可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 近7天EDM推送数必须按推送数据控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:近7天EDM推送数/推送数据 | 待执行\nTC-PROTO-0183 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段身份风险等级按系统管理员/风险负责人权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“身份风险等级”存在。 | 字段=身份风险等级;可见范围=系统管理员/风险负责人;期望=普通运营不可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“身份风险等级”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“身份风险等级”按照“系统管理员/风险负责人”控制可见性。\n2. 符合预期:普通运营不可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 身份风险等级必须按系统管理员/风险负责人控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:身份风险等级/系统管理员/风险负责人 | 待执行\nTC-PROTO-0184 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段标签覆盖人数按标签模块权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“标签覆盖人数”存在。 | 字段=标签覆盖人数;可见范围=标签模块;期望=负责人可见汇总,普通客服不可导出 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“标签覆盖人数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“标签覆盖人数”按照“标签模块”控制可见性。\n2. 符合预期:负责人可见汇总,普通客服不可导出。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 标签覆盖人数必须按标签模块控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:标签覆盖人数/标签模块 | 待执行\nTC-PROTO-0242 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:搜索JOYHUB用户ID | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=输入JOYHUB ID并查询;预期=返回用户主档、标签、身份、产品关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:输入JOYHUB ID并查询。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“搜索JOYHUB用户ID”。\n2. 输出结果为:返回用户主档、标签、身份、产品关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的搜索JOYHUB用户ID可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/搜索JOYHUB用户ID | 待执行\nTC-PROTO-0243 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:按国家和近7天EDM次数筛选 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=国家=US;EDM近7天>0;预期=返回可用于触达的人群 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:国家=US;EDM近7天>0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按国家和近7天EDM次数筛选”。\n2. 输出结果为:返回可用于触达的人群。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的按国家和近7天EDM次数筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/按国家和近7天EDM次数筛选 | 待执行\nTC-PROTO-0244 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:打开用户画像详情 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=点击用户行详情;预期=展示注册/活跃时间、标签、产品、活动 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:点击用户行详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“打开用户画像详情”。\n2. 输出结果为:展示注册/活跃时间、标签、产品、活动。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的打开用户画像详情可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/打开用户画像详情 | 待执行\nTC-PROTO-0245 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:新增标签覆盖查询 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=兴趣;打标方式=系统;预期=展示覆盖人数和最新打标时间 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签分类=兴趣;打标方式=系统。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“新增标签覆盖查询”。\n2. 输出结果为:展示覆盖人数和最新打标时间。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的新增标签覆盖查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/新增标签覆盖查询 | 待执行\nTC-PROTO-0246 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:覆盖人数异常提示 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签覆盖人数突增或为0;预期=展示异常覆盖提示 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签覆盖人数突增或为0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“覆盖人数异常提示”。\n2. 输出结果为:展示异常覆盖提示。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的覆盖人数异常提示可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/覆盖人数异常提示 | 待执行\nTC-PROTO-0247 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:产品标签关联Listing | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=选择产品标签并查看关联产品;预期=展示产品-品牌-Listing/ASIN关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:选择产品标签并查看关联产品。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“产品标签关联Listing”。\n2. 输出结果为:展示产品-品牌-Listing/ASIN关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的产品标签关联Listing可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/产品标签关联Listing | 待执行\nTC-PROTO-0248 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:按创建时间筛选标签 | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=开始/截止时间;预期=只返回时间范围内标签 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:开始/截止时间。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按创建时间筛选标签”。\n2. 输出结果为:只返回时间范围内标签。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的按创建时间筛选标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/按创建时间筛选标签 | 待执行\nTC-PROTO-0249 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:客服分组标签查询 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=客服分组;预期=输出私域用户标签与社群/活动关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签分类=客服分组。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“客服分组标签查询”。\n2. 输出结果为:输出私域用户标签与社群/活动关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的客服分组标签查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/客服分组标签查询 | 待执行\nTC-PROTO-0250 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:风险用户隔离标签 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签状态=启用;风险相关;预期=可作为客服/推送排除条件 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签状态=启用;风险相关。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“风险用户隔离标签”。\n2. 输出结果为:可作为客服/推送排除条件。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的风险用户隔离标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/风险用户隔离标签 | 待执行\nTC-PROTO-0251 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:查看多语言图标 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=打开身份详情;预期=展示英/德/日图标PNG | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:打开身份详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“查看多语言图标”。\n2. 输出结果为:展示英/德/日图标PNG。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的查看多语言图标可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/查看多语言图标 | 待执行\nTC-PROTO-0252 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份风险等级筛选 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=身份风险等级=高;预期=进入风险用户池候选 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:身份风险等级=高。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份风险等级筛选”。\n2. 输出结果为:进入风险用户池候选。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份风险等级筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份风险等级筛选 | 待执行\nTC-PROTO-0253 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份状态停用 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=将风险身份置为停用;预期=后续筛选不再作为可选身份 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:将风险身份置为停用。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份状态停用”。\n2. 输出结果为:后续筛选不再作为可选身份。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份状态停用可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份状态停用 | 待执行\nTC-PROTO-0289 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:生成字段表重复点击 | P2 | 已进入“现有ERP”;准备可执行场景:生成字段表重复点击。 | 动作=连续点击生成字段表;预期=字段表只生成一份或版本号递增可追踪 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击生成字段表。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:字段表只生成一份或版本号递增可追踪。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:生成字段表重复点击 | 待执行\nTC-PROTO-0290 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:导出现有关系无数据 | P2 | 已进入“现有ERP”;准备可执行场景:导出现有关系无数据。 | 动作=查询结果为空后导出;预期=提示暂无可导出数据 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:查询结果为空后导出。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示暂无可导出数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:导出现有关系无数据 | 待执行\nTC-PROTO-0291 | user_erp_mvp_admin_prototype_v10(1).html | 身份管理 | 系统稳定性与幂等 | 异常场景 | 身份管理稳定性校验:多语言图标缺失 | P2 | 已进入“身份管理”;准备可执行场景:多语言图标缺失。 | 动作=德语图标PNG为空;预期=页面显示占位并提示需补充 | 1. 打开原型页面“身份管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:德语图标PNG为空。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:页面显示占位并提示需补充。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多语言图标缺失 | 待执行\nTC-PROTO-0292 | user_erp_mvp_admin_prototype_v10(1).html | 用户管理 | 系统稳定性与幂等 | 异常场景 | 用户管理稳定性校验:分页切换保持筛选 | P2 | 已进入“用户管理”;准备可执行场景:分页切换保持筛选。 | 动作=筛选国家US后切换下一页;预期=筛选条件不丢失 | 1. 打开原型页面“用户管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选国家US后切换下一页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:筛选条件不丢失。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:分页切换保持筛选 | 待执行\n# Sheet: HTML3-客服执行\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0060 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看在线客服数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=在线客服数;原型变量=onlineAgents 人;动作=查看当前可用客服池 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“在线客服数”。\n3. 核对指标值单位是否符合“onlineAgents 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“在线客服数”显示在客服执行看板。\n2. 点击后进入与“查看当前可用客服池”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:onlineAgents 人;标题:客服执行看板 | 待执行\nTC-PROTO-0061 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看今日工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日工单;原型变量=todayTickets 单;动作=查看今日进入客服中心的工单总量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日工单”。\n3. 核对指标值单位是否符合“todayTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日工单”显示在客服执行看板。\n2. 点击后进入与“查看今日进入客服中心的工单总量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0062 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看待处理工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=待处理工单;原型变量=pendingTickets 单;动作=查看未关闭/待处理队列 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“待处理工单”。\n3. 核对指标值单位是否符合“pendingTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“待处理工单”显示在客服执行看板。\n2. 点击后进入与“查看未关闭/待处理队列”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:pendingTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0063 | 客服执行.html | 客服执行看板 | 多渠道触达引擎 | 功能测试 | 客服执行看板查看今日消息指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日消息;原型变量=todayMessages 条;动作=查看客服发送和接收消息量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日消息”。\n3. 核对指标值单位是否符合“todayMessages 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日消息”显示在客服执行看板。\n2. 点击后进入与“查看客服发送和接收消息量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayMessages 条;标题:客服执行看板 | 待执行\nTC-PROTO-0064 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看今日获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日获取评价;原型变量=todayReviews 条;动作=查看今日客服转化评价数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日获取评价”。\n3. 核对指标值单位是否符合“todayReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日获取评价”显示在客服执行看板。\n2. 点击后进入与“查看今日客服转化评价数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0065 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看本月获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=本月获取评价;原型变量=monthReviews 条;动作=查看月度评价产出 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“本月获取评价”。\n3. 核对指标值单位是否符合“monthReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“本月获取评价”显示在客服执行看板。\n2. 点击后进入与“查看月度评价产出”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:monthReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0066 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看排班到岗指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=排班到岗;原型变量=实际/应到 人;动作=查看排班、在线状态和出勤异常 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“排班到岗”。\n3. 核对指标值单位是否符合“实际/应到 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“排班到岗”显示在客服执行看板。\n2. 点击后进入与“查看排班、在线状态和出勤异常”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:实际/应到 人;标题:客服执行看板 | 待执行\nTC-PROTO-0067 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看迟到次数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=迟到次数;原型变量=lateDays 次;动作=查看迟到/早退/请假/缺勤 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“迟到次数”。\n3. 核对指标值单位是否符合“lateDays 次”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“迟到次数”显示在客服执行看板。\n2. 点击后进入与“查看迟到/早退/请假/缺勤”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:lateDays 次;标题:客服执行看板 | 待执行\nTC-PROTO-0068 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看回复用户数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=回复用户数;原型变量=repliedUsers 人;动作=查看回复覆盖用户数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“回复用户数”。\n3. 核对指标值单位是否符合“repliedUsers 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“回复用户数”显示在客服执行看板。\n2. 点击后进入与“查看回复覆盖用户数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:repliedUsers 人;标题:客服执行看板 | 待执行\nTC-PROTO-0069 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看处理工单数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=处理工单数;原型变量=ticketCount 单;动作=查看处理工单量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“处理工单数”。\n3. 核对指标值单位是否符合“ticketCount 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“处理工单数”显示在客服执行看板。\n2. 点击后进入与“查看处理工单量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:ticketCount 单;标题:客服执行看板 | 待执行\nTC-PROTO-0070 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看发送消息数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=发送消息数;原型变量=messageCount 条;动作=查看消息发送量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“发送消息数”。\n3. 核对指标值单位是否符合“messageCount 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“发送消息数”显示在客服执行看板。\n2. 点击后进入与“查看消息发送量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:messageCount 条;标题:客服执行看板 | 待执行\nTC-PROTO-0071 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看平均首次回复指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=平均首次回复;原型变量=averageFirstReply 分钟;动作=查看平均首次回复时长 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“平均首次回复”。\n3. 核对指标值单位是否符合“averageFirstReply 分钟”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“平均首次回复”显示在客服执行看板。\n2. 点击后进入与“查看平均首次回复时长”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:averageFirstReply 分钟;标题:客服执行看板 | 待执行\nTC-PROTO-0072 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看RSO/RDO评价数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=RSO/RDO评价数;原型变量=rsoReviews+rdoReviews 条;动作=查看回评/测评转化 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“RSO/RDO评价数”。\n3. 核对指标值单位是否符合“rsoReviews+rdoReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“RSO/RDO评价数”显示在客服执行看板。\n2. 点击后进入与“查看回评/测评转化”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:rsoReviews+rdoReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0073 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看目标完成率指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=目标完成率;原型变量=totalRate;动作=查看目标完成进度 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“目标完成率”。\n3. 核对指标值单位是否符合“totalRate”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“目标完成率”显示在客服执行看板。\n2. 点击后进入与“查看目标完成进度”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:totalRate;标题:客服执行看板 | 待执行\nTC-PROTO-0074 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户消息进入工单从待分配执行自动分配流转到已分配 | P1 | 客服系统存在来源为“用户消息进入”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=用户消息进入;起始状态=待分配;操作=自动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户消息进入”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“自动分配”:在线客服池按班次+在线状态+当前负载+最大工单数分配。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户消息进入”和当前状态“待分配”。\n2. 执行“自动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0075 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 推送转人工工单从待分配执行手动分配流转到已分配 | P1 | 客服系统存在来源为“推送转人工”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=推送转人工;起始状态=待分配;操作=手动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“推送转人工”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“手动分配”:组长选择在线且未满载客服。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“推送转人工”和当前状态“待分配”。\n2. 执行“手动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0076 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 售后触发工单从已分配执行首次回复流转到处理中 | P1 | 客服系统存在来源为“售后触发”的工单;当前状态为“已分配”;用户上下文卡可查询或降级展示。 | 来源=售后触发;起始状态=已分配;操作=首次回复;目标状态=处理中 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“售后触发”。\n3. 打开状态为“已分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“首次回复”:客服查看用户上下文后发送第一条回复。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“售后触发”和当前状态“已分配”。\n2. 执行“首次回复”后状态变为“处理中”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0077 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 风险触发工单从处理中执行标记疑似诈骗流转到疑似诈骗 | P1 | 客服系统存在来源为“风险触发”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=风险触发;起始状态=处理中;操作=标记疑似诈骗;目标状态=疑似诈骗 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“风险触发”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“标记疑似诈骗”:客服在工单处理结果中选择疑似诈骗。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“风险触发”和当前状态“处理中”。\n2. 执行“标记疑似诈骗”后状态变为“疑似诈骗”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0078 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 电话后续工单从处理中执行等待用户回复流转到等待用户 | P1 | 客服系统存在来源为“电话后续”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=电话后续;起始状态=处理中;操作=等待用户回复;目标状态=等待用户 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“电话后续”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“等待用户回复”:客服记录通话后等待用户补充订单号。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“电话后续”和当前状态“处理中”。\n2. 执行“等待用户回复”后状态变为“等待用户”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0079 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户答应配合工单从处理中执行创建跟进任务流转到待提醒 | P1 | 客服系统存在来源为“用户答应配合”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=用户答应配合;起始状态=处理中;操作=创建跟进任务;目标状态=待提醒 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户答应配合”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“创建跟进任务”:客服将答应配合状态置为已答应并设置负责人。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户答应配合”和当前状态“处理中”。\n2. 执行“创建跟进任务”后状态变为“待提醒”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0080 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 等待提交工单从待提醒执行提醒用户流转到等待提交 | P1 | 客服系统存在来源为“等待提交”的工单;当前状态为“待提醒”;用户上下文卡可查询或降级展示。 | 来源=等待提交;起始状态=待提醒;操作=提醒用户;目标状态=等待提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“等待提交”。\n3. 打开状态为“待提醒”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“提醒用户”:到期前客服发送提醒消息。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“等待提交”和当前状态“待提醒”。\n2. 执行“提醒用户”后状态变为“等待提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0081 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户提交评价工单从等待提交执行登记提交事实流转到已提交 | P1 | 客服系统存在来源为“用户提交评价”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=用户提交评价;起始状态=等待提交;操作=登记提交事实;目标状态=已提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户提交评价”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“登记提交事实”:客服上传截图/链接并关联计划和ASIN。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户提交评价”和当前状态“等待提交”。\n2. 执行“登记提交事实”后状态变为“已提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0082 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 评价核验完成工单从已提交执行关闭工单流转到已关闭 | P1 | 客服系统存在来源为“评价核验完成”的工单;当前状态为“已提交”;用户上下文卡可查询或降级展示。 | 来源=评价核验完成;起始状态=已提交;操作=关闭工单;目标状态=已关闭 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“评价核验完成”。\n3. 打开状态为“已提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“关闭工单”:评价展示确认后客服将工单置为已解决并关闭。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“评价核验完成”和当前状态“已提交”。\n2. 执行“关闭工单”后状态变为“已关闭”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0083 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 超时未提交工单从等待提交执行需再次联系流转到需再次联系 | P1 | 客服系统存在来源为“超时未提交”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=超时未提交;起始状态=等待提交;操作=需再次联系;目标状态=需再次联系 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“超时未提交”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“需再次联系”:超过答应配合期限后生成再次联系任务。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“超时未提交”和当前状态“等待提交”。\n2. 执行“需再次联系”后状态变为“需再次联系”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0084 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:自动分配无在线客服 | P2 | 客服执行看板可用;准备异常条件:所有排班客服均离线或满载。 | 异常=自动分配无在线客服;条件=所有排班客服均离线或满载 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:所有排班客服均离线或满载。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“自动分配无在线客服”。\n2. 处理结果为:工单留在公共池并提醒组长。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:自动分配无在线客服 | 待执行\nTC-PROTO-0085 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:分配给离线客服 | P2 | 客服执行看板可用;准备异常条件:客服在线状态在分配前变为离线。 | 异常=分配给离线客服;条件=客服在线状态在分配前变为离线 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服在线状态在分配前变为离线。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“分配给离线客服”。\n2. 处理结果为:阻止分配并要求重新选择。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:分配给离线客服 | 待执行\nTC-PROTO-0086 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:当前负载超过最大工单数 | P2 | 客服执行看板可用;准备异常条件:客服未关闭工单数达到上限。 | 异常=当前负载超过最大工单数;条件=客服未关闭工单数达到上限 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服未关闭工单数达到上限。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“当前负载超过最大工单数”。\n2. 处理结果为:自动跳过该客服。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:当前负载超过最大工单数 | 待执行\nTC-PROTO-0087 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:用户上下文卡查询失败 | P2 | 客服执行看板可用;准备异常条件:identity 服务超时。 | 异常=用户上下文卡查询失败;条件=identity 服务超时 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:identity 服务超时。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“用户上下文卡查询失败”。\n2. 处理结果为:工单可继续处理但显示上下文数据可能过期。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:用户上下文卡查询失败 | 待执行\nTC-PROTO-0088 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:首次回复为空 | P2 | 客服执行看板可用;准备异常条件:客服点击发送但消息内容为空。 | 异常=首次回复为空;条件=客服点击发送但消息内容为空 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服点击发送但消息内容为空。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“首次回复为空”。\n2. 处理结果为:阻止发送并提示请输入回复内容。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:首次回复为空 | 待执行\nTC-PROTO-0089 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:关闭工单未选择处理结果 | P2 | 客服执行看板可用;准备异常条件:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。 | 异常=关闭工单未选择处理结果;条件=点击关闭但未选择已解决/拒绝/疑似诈骗等结果 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“关闭工单未选择处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:关闭工单未选择处理结果 | 待执行\nTC-PROTO-0090 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:登记评价缺少证据 | P2 | 客服执行看板可用;准备异常条件:用户声称已评价但未上传截图或链接。 | 异常=登记评价缺少证据;条件=用户声称已评价但未上传截图或链接 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:用户声称已评价但未上传截图或链接。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“登记评价缺少证据”。\n2. 处理结果为:不允许进入已提交状态。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:登记评价缺少证据 | 待执行\nTC-PROTO-0091 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:答应配合任务超期 | P2 | 客服执行看板可用;准备异常条件:deadline_at 已过且无提交记录。 | 异常=答应配合任务超期;条件=deadline_at 已过且无提交记录 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:deadline_at 已过且无提交记录。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“答应配合任务超期”。\n2. 处理结果为:状态变为超时并生成需再次联系。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:答应配合任务超期 | 待执行\nTC-PROTO-0092 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:风险状态确认诈骗 | P2 | 客服执行看板可用;准备异常条件:risk 返回确认诈骗。 | 异常=风险状态确认诈骗;条件=risk 返回确认诈骗 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:risk 返回确认诈骗。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“风险状态确认诈骗”。\n2. 处理结果为:工单自动或人工确认后关闭并同步黑名单候选。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:风险状态确认诈骗 | 待执行\nTC-PROTO-0093 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:重复创建同用户打开工单 | P2 | 客服执行看板可用;准备异常条件:同 person_id 已存在 open 工单。 | 异常=重复创建同用户打开工单;条件=同 person_id 已存在 open 工单 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:同 person_id 已存在 open 工单。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“重复创建同用户打开工单”。\n2. 处理结果为:新工单关联已有工单或提示合并。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:重复创建同用户打开工单 | 待执行\nTC-PROTO-0185 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服本人在客服执行看板的可操作范围校验 | P1 | 准备客服本人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服本人;数据范围=我的工单;允许=回复用户、登记提交事实;限制=不能改派他人工单或查看团队绩效 | 1. 使用“客服本人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:回复用户、登记提交事实。\n4. 尝试执行限制动作:不能改派他人工单或查看团队绩效。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服本人只能看到“我的工单”。\n2. 允许动作“回复用户、登记提交事实”可正常提交。\n3. 限制动作“不能改派他人工单或查看团队绩效”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服本人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服本人 | 待执行\nTC-PROTO-0186 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服组长在客服执行看板的可操作范围校验 | P1 | 准备客服组长账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服组长;数据范围=组内工单池;允许=手动分配、转移、查看组员负载;限制=不能查看跨团队敏感字段 | 1. 使用“客服组长”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:手动分配、转移、查看组员负载。\n4. 尝试执行限制动作:不能查看跨团队敏感字段。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服组长只能看到“组内工单池”。\n2. 允许动作“手动分配、转移、查看组员负载”可正常提交。\n3. 限制动作“不能查看跨团队敏感字段”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服组长权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服组长 | 待执行\nTC-PROTO-0187 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服主管在客服执行看板的可操作范围校验 | P1 | 准备客服主管账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服主管;数据范围=团队看板;允许=查看排班、绩效、目标完成率;限制=不能同步黑名单除非额外授权 | 1. 使用“客服主管”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看排班、绩效、目标完成率。\n4. 尝试执行限制动作:不能同步黑名单除非额外授权。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服主管只能看到“团队看板”。\n2. 允许动作“查看排班、绩效、目标完成率”可正常提交。\n3. 限制动作“不能同步黑名单除非额外授权”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服主管权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服主管 | 待执行\nTC-PROTO-0188 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 风险负责人在客服执行看板的可操作范围校验 | P1 | 准备风险负责人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=风险负责人;数据范围=疑似诈骗工单;允许=确认诈骗、标记误报、同步黑名单候选;限制=不能修改客服排班 | 1. 使用“风险负责人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:确认诈骗、标记误报、同步黑名单候选。\n4. 尝试执行限制动作:不能修改客服排班。\n5. 打开审计日志查看敏感操作记录。 | 1. 风险负责人只能看到“疑似诈骗工单”。\n2. 允许动作“确认诈骗、标记误报、同步黑名单候选”可正常提交。\n3. 限制动作“不能修改客服排班”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 风险负责人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:风险负责人 | 待执行\nTC-PROTO-0189 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 系统管理员在客服执行看板的可操作范围校验 | P1 | 准备系统管理员账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=系统管理员;数据范围=全部客服数据;允许=查看审计、配置权限、导出绩效;限制=敏感查看仍需记录审计 | 1. 使用“系统管理员”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看审计、配置权限、导出绩效。\n4. 尝试执行限制动作:敏感查看仍需记录审计。\n5. 打开审计日志查看敏感操作记录。 | 1. 系统管理员只能看到“全部客服数据”。\n2. 允许动作“查看审计、配置权限、导出绩效”可正常提交。\n3. 限制动作“敏感查看仍需记录审计”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 系统管理员权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:系统管理员 | 待执行\nTC-PROTO-0254 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态待分配执行分配给客服A | P2 | 客服执行看板存在状态为“待分配”的工单;当前用户对该工单有处理权限。 | 当前状态=待分配;动作=分配给客服A | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“待分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“分配给客服A”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“待分配”的工单可执行“分配给客服A”。\n2. 执行结果:assigned_agent=客服A;写入assignment_logs。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=待分配;动作=分配给客服A | 待执行\nTC-PROTO-0255 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已分配执行客服首次回复 | P2 | 客服执行看板存在状态为“已分配”的工单;当前用户对该工单有处理权限。 | 当前状态=已分配;动作=客服首次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“客服首次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已分配”的工单可执行“客服首次回复”。\n2. 执行结果:记录首次回复时长并进入处理中。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已分配;动作=客服首次回复 | 待执行\nTC-PROTO-0256 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待用户回复 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待用户回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待用户回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待用户回复”。\n2. 执行结果:状态变等待用户,设置提醒时间。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待用户回复 | 待执行\nTC-PROTO-0257 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待内部协同 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待内部协同 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待内部协同”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待内部协同”。\n2. 执行结果:状态变等待内部,通知内部负责人。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待内部协同 | 待执行\nTC-PROTO-0258 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择答应配合 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择答应配合 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择答应配合”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择答应配合”。\n2. 执行结果:创建support_followups,状态PROMISED。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择答应配合 | 待执行\nTC-PROTO-0259 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择疑似诈骗 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择疑似诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择疑似诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择疑似诈骗”。\n2. 执行结果:生成风险案件并标记工单疑似诈骗。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择疑似诈骗 | 待执行\nTC-PROTO-0260 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待用户执行用户回复后继续处理 | P2 | 客服执行看板存在状态为“等待用户”的工单;当前用户对该工单有处理权限。 | 当前状态=等待用户;动作=用户回复后继续处理 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待用户”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“用户回复后继续处理”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待用户”的工单可执行“用户回复后继续处理”。\n2. 执行结果:状态回到处理中并记录用户消息。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待用户;动作=用户回复后继续处理 | 待执行\nTC-PROTO-0261 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待内部执行内部反馈完成 | P2 | 客服执行看板存在状态为“等待内部”的工单;当前用户对该工单有处理权限。 | 当前状态=等待内部;动作=内部反馈完成 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待内部”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“内部反馈完成”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待内部”的工单可执行“内部反馈完成”。\n2. 执行结果:状态回到处理中并追加内部协同记录。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待内部;动作=内部反馈完成 | 待执行\nTC-PROTO-0262 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已解决执行关闭工单 | P2 | 客服执行看板存在状态为“已解决”的工单;当前用户对该工单有处理权限。 | 当前状态=已解决;动作=关闭工单 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已解决”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“关闭工单”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已解决”的工单可执行“关闭工单”。\n2. 执行结果:resolved_at和closed状态写入。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已解决;动作=关闭工单 | 待执行\nTC-PROTO-0263 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认误报 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认误报 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认误报”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认误报”。\n2. 执行结果:工单可回到处理中或已解决。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认误报 | 待执行\nTC-PROTO-0264 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认诈骗 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认诈骗”。\n2. 执行结果:工单关闭并进入黑名单同步候选。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认诈骗 | 待执行\nTC-PROTO-0265 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已关闭执行尝试再次回复 | P2 | 客服执行看板存在状态为“已关闭”的工单;当前用户对该工单有处理权限。 | 当前状态=已关闭;动作=尝试再次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已关闭”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“尝试再次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已关闭”的工单可执行“尝试再次回复”。\n2. 执行结果:禁止直接回复,需重新打开或新建工单。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已关闭;动作=尝试再次回复 | 待执行\nTC-PROTO-0293 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:新工单到达实时刷新 | P2 | 已进入“客服执行看板”;准备可执行场景:新工单到达实时刷新。 | 动作=后台新增待分配工单;预期=看板待处理数增加并出现新工单 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:后台新增待分配工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:看板待处理数增加并出现新工单。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:新工单到达实时刷新 | 待执行\nTC-PROTO-0294 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:多人同时抢单 | P2 | 已进入“客服执行看板”;准备可执行场景:多人同时抢单。 | 动作=两个客服同时领取同一工单;预期=只有一个领取成功,另一个提示已被分配 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个客服同时领取同一工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只有一个领取成功,另一个提示已被分配。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多人同时抢单 | 待执行\nTC-PROTO-0295 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:首次回复重复发送 | P2 | 已进入“客服工单”;准备可执行场景:首次回复重复发送。 | 动作=客服双击发送回复;预期=只发送一条消息并记录一次首次回复时长 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:客服双击发送回复。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只发送一条消息并记录一次首次回复时长。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:首次回复重复发送 | 待执行\nTC-PROTO-0296 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:关闭工单后刷新 | P2 | 已进入“客服工单”;准备可执行场景:关闭工单后刷新。 | 动作=关闭工单后刷新详情页;预期=状态仍为已关闭且不可继续处理 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:关闭工单后刷新详情页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:状态仍为已关闭且不可继续处理。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:关闭工单后刷新 | 待执行\nTC-PROTO-0297 | 客服执行.html | 客服绩效 | 系统稳定性与幂等 | 异常场景 | 客服绩效稳定性校验:绩效周期切换 | P2 | 已进入“客服绩效”;准备可执行场景:绩效周期切换。 | 动作=日/周/月连续切换;预期=指标随周期变化且无串数据 | 1. 打开原型页面“客服绩效”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:日/周/月连续切换。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:指标随周期变化且无串数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:绩效周期切换 | 待执行\n# Sheet: HTML4-单文件系统\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0094 | 用户运营系统-单文件.html | 工作台 | 系统总览 | UI/交互测试 | 单文件系统路由#/dashboard进入Dashboard页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有Dashboard访问权限。 | 路由=#/dashboard;页面=Dashboard;用途=查看经营指标、待办、风险、评价进度 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/dashboard”或从侧边菜单点击“Dashboard”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“Dashboard”页面。 | 1. 页面成功进入“Dashboard”。\n2. 当前菜单高亮,页面内容与“查看经营指标、待办、风险、评价进度”一致。\n3. 刷新后 hash 路由不丢失,仍展示“Dashboard”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备Dashboard访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/dashboard | 待执行\nTC-PROTO-0095 | 用户运营系统-单文件.html | 需求管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/demand进入需求中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有需求中心访问权限。 | 路由=#/demand;页面=需求中心;用途=创建/评估/驳回/转计划 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/demand”或从侧边菜单点击“需求中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“需求中心”页面。 | 1. 页面成功进入“需求中心”。\n2. 当前菜单高亮,页面内容与“创建/评估/驳回/转计划”一致。\n3. 刷新后 hash 路由不丢失,仍展示“需求中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权 | 只有具备需求中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/demand | 待执行\nTC-PROTO-0096 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan/review进入计划审核页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划审核访问权限。 | 路由=#/plan/review;页面=计划审核;用途=提交审批、通过、驳回、待补充 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan/review”或从侧边菜单点击“计划审核”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划审核”页面。 | 1. 页面成功进入“计划审核”。\n2. 当前菜单高亮,页面内容与“提交审批、通过、驳回、待补充”一致。\n3. 刷新后 hash 路由不丢失,仍展示“计划审核”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划审核访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan/review | 待执行\nTC-PROTO-0097 | 用户运营系统-单文件.html | 计划管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan进入计划中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划中心访问权限。 | 路由=#/plan;页面=计划中心;用途=生成计划、拆分计划项、执行中/暂停/终止 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan”或从侧边菜单点击“计划中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划中心”页面。 | 1. 页面成功进入“计划中心”。\n2. 当前菜单高亮,页面内容 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan | 待执行\nTC-PROTO-0098 | 用户运营系统-单文件.html | Listing健康 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/asin进入ASIN/Listing页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有ASIN/Listing访问权限。 | 路由=#/asin;页面=ASIN/Listing;用途=查看评分、评价数、健康状态、紧急策略 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/asin”或从侧边菜单点击“ASIN/Listing”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“ASIN/Listing”页面。 | 1. 页面成功进入“ASIN/Listing”。\n2. 当前菜单高亮,页面内容与“查看评分、评价数、健康状态、紧急策略”一致。\n3. 刷新后 hash 路由不丢失,仍展示“ASIN/Listing”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备ASIN/Listing访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/asin | 待执行\nTC-PROTO-0099 | 用户运营系统-单文件.html | 用户上下文 | 用户身份与上下文 | UI/交互测试 | 单文件系统路由#/user进入用户中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有用户中心访问权限。 | 路由=#/user;页面=用户中心;用途=用户主档、标签、身份、产品、活动、触达历史 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/user”或从侧边菜单点击“用户中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“用户中心”页面。 | 1. 页面成功进入“用户中心”。\n2. 当前菜单高亮,页面内容与“用户主档、标签、身份、产品、活动、触达历史”一致。\n3. 刷新后 hash 路由不丢失,仍展示“用户中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备用户中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/user | 待执行\nTC-PROTO-0100 | 用户运营系统-单文件.html | 额度管理 | 额度与频控 | UI/交互测试 | 单文件系统路由#/quota进入额度频控页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有额度频控访问权限。 | 路由=#/quota;页面=额度频控;用途=额度查询、预占、确认、释放、终校 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/quota”或从侧边 | 1. 页面成功进入“额度频控”。\n2. 当前菜单高亮,页面内容与“额度查询、预占、确认、释放、终校”一致。\n3. 刷新后 hash 路由不丢失,仍展示“额度频控”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备额度频控访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/quota | 待执行\nTC-PROTO-0101 | 用户运营系统-单文件.html | 多渠道触达 | 多渠道触达引擎 | UI/交互测试 | 单文件系统路由#/outreach进入推送/触达页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有推送/触达访问权限。 | 路由=#/outreach;页面=推送/触达;用途=IM/EDM/APP/TEL 路由、去重、发送、追踪 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/outreach”或从侧边菜单点击“推送 | 1. 页面成功进入“推送/触达”。\n2. 当前菜单高亮,页面内容与“IM/EDM/APP/TEL 路由、去重、发送、追踪”一致。\n3. 刷新后 hash 路由不丢失,仍展示“推送/触达”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备推送/触达访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/outreach | 待执行\nTC-PROTO-0102 | 用户运营系统-单文件.html | 工单管理 | 客服工单与管理 | UI/交互测试 | 单文件系统路由#/support进入客服中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有客服中心访问权限。 | 路由=#/support;页面=客服中心;用途=工单创建、分配、处理、跟进 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/support”或从侧边菜单点击“客服中心”。\n3. 等待页面渲染完成。\n4. 检查 | 1. 页面成功进入“客服中心”。\n2. 当前菜单高亮,页面内容与“工单创建、分配、处理、跟进”一致。\n3. 刷新后 hash 路由不丢失,仍展示“客服中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备客服中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/support | 待执行\nTC-PROTO-0103 | 用户运营系统-单文件.html | 风险反欺诈 | 风险与反欺诈 | UI/交互测试 | 单文件系统路由#/risk进入风险中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有风险中心访问权限。 | 路由=#/risk;页面=风险中心;用途=风险信号、强弱关联、黑名单、复核 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/risk”或从侧边菜单点击“风险中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“风险中心”页面。 | 1. 页面成功进入“风险中心”。\n2. 当前菜单高亮,页面内容与“风险信号、强弱关联、黑名单、复核”一致。\n3. 刷新后 hash 路由不丢失,仍展示“风险中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备风险中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/risk | 待执行\nTC-PROTO-0104 | 用户运营系统-单文件.html | 评价结果 | 评价结果追踪 | UI/交互测试 | 单文件系统路由#/review进入评价追踪页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有评价追踪访问权限。 | 路由=#/review;页面=评价追踪;用途=提交记录、Amazon展示核验、异常观察 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/review”或从侧边菜单点击“评价追踪”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“评价追踪”页面。 | 1. 页面成功进入“评价追踪”。\n2. 当前菜单高亮,页面内容与“提交记录、Amazon展示核验、异常观察”一致。\n3. 刷新后 hash 路由不丢失,仍展示“评价追踪”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备评价追踪访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/review | 待执行\nTC-PROTO-0105 | 用户运营系统-单文件.html | 达人协作 | KOC/KOL协作 | UI/交互测试 | 单文件系统路由#/creator进入KOC/KOL页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有KOC/KOL访问权限。 | 路由=#/creator;页面=KOC/KOL;用途=免评计划、内容、CODE、JOYCOLLAB同步 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/creator”或从侧边菜单点击“KOC/KOL”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“KOC/KOL”页面。 | 1. 页面成功进入“KOC/KOL”。\n2. 当前菜单高亮,页面内容与“免评计划、内容、CODE、JOYCOLLAB同步”一致。\n3. 刷新后 hash 路由不丢失,仍展示“KOC/KOL”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备KOC/KOL访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/creator | 待执行\nTC-PROTO-0106 | 用户运营系统-单文件.html | 审计与通知 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/audit进入审计通知页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有审计通知访问权限。 | 路由=#/audit;页面=审计通知;用途=状态变更、敏感访问、通知告警 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/audit”或从侧边菜单点击“审计通知”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“审计通知”页面。 | 1. 页面成功进入“审计通知”。\n2. 当前菜单高亮,页面内容与“状态变更、敏感访问、通知告警”一致。\n3. 刷新后 hash 路由不丢失,仍展示“审计通知”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备审计通知访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/audit | 待执行\nTC-PROTO-0107 | 用户运营系统-单文件.html | 权限配置 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/system进入系统管理页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有系统管理访问权限。 | 路由=#/system;页面=系统管理;用途=用户角色、权限、数据范围、导出授权 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/system”或从侧边菜单点击“系统管理”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“系统管理”页面。 | 1. 页面成功进入“系统管理”。\n2. 当前菜单高亮,页面内容与“用户角色、权限、数据范围、导出授权”一致。\n3. 刷新后 hash 路由不丢失,仍展示“系统管理”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备系统管理访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/system | 待执行\nTC-PROTO-0108 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行创建测评需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“创建测评需求”的权限;相关基础数据已准备。 | ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“创建测评需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建测评需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“PENDING/EVALUATING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=PENDING/EVALUATING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建测评需求”;跨站点/跨部门数据需按权限范围过滤。 | 创建测评需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:创建测评需求 | 待执行\nTC-PROTO-0109 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行评估需求为待补充并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“评估需求为待补充”的权限;相关基础数据已准备。 | 需求ID=DEM-001;原因=ASIN目标数量缺少依据 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“评估需求为待补充”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-001;原因=ASIN目标数量缺少依据。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “评估需求为待补充”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“WAITING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=WAITING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“评估需求为待补充”;跨站点/跨部门数据需按权限范围过滤。 | 评估需求为待补充完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:评估需求为待补充 | 待执行\nTC-PROTO-0110 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行驳回不成立需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“驳回不成立需求”的权限;相关基础数据已准备。 | 需求ID=DEM-002;原因=ASIN评分已达标无需计划 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“驳回不成立需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-002;原因=ASIN评分已达标无需计划。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “驳回不成立需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“驳回不成立需求”;跨站点/跨部门数据需按权限范围过滤。 | 驳回不成立需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:驳回不成立需求 | 待执行\nTC-PROTO-0111 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行提交测评计划审批并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“提交测评计划审批”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“提交测评计划审批”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “提交测评计划审批”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REVIEW”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records;状态值=REVIEW;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“提交测评计划审批”;跨站点/跨部门数据需按权限范围过滤。 | 提交测评计划审批完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:提交测评计划审批 | 待执行\nTC-PROTO-0112 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批通过计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批通过计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;意见=同意执行;目标评价数=20 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批通过计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;意见=同意执行;目标评价数=20。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批通过计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“APPROVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=APPROVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批通过计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批通过计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批通过计划 | 待执行\nTC-PROTO-0113 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批驳回计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批驳回计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-002;意见=预算和风险说明不足 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批驳回计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-002;意见=预算和风险说明不足。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批驳回计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“DRAFT/REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=DRAFT/REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批驳回计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批驳回计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批驳回计划 | 待执行\nTC-PROTO-0114 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行生成候选用户池并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“生成候选用户池”的权限;相关基础数据已准备。 | 计划ID=PLAN-003;ASIN=B0TEST003;目标=50人 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“生成候选用户池”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-003;ASIN=B0TEST003;目标=50人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “生成候选用户池”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待触达”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plan_items/quota_reservations;状态值=待触达;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“生成候选用户池”;跨站点/跨部门数据需按权限范围过滤。 | 生成候选用户池完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:生成候选用户池 | 待执行\nTC-PROTO-0115 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行暂停执行中计划并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“暂停执行中计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-004;暂停原因=库存异常 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“暂停执行中计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-004;暂停原因=库存异常。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “暂停执行中计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已暂停”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plans;状态值=已暂停;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“暂停执行中计划”;跨站点/跨部门数据需按权限范围过滤。 | 暂停执行中计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:暂停执行中计划 | 待执行\nTC-PROTO-0116 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行批量预占额度并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“批量预占额度”的权限;相关基础数据已准备。 | person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“批量预占额度”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “批量预占额度”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RESERVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RESERVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“批量预占额度”;跨站点/跨部门数据需按权限范围过滤。 | 批量预占额度完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:批量预占额度 | 待执行\nTC-PROTO-0117 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行释放触达失败预占并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“释放触达失败预占”的权限;相关基础数据已准备。 | reservation_id=QR-001;释放原因=IM不可达 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“释放触达失败预占”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:reservation_id=QR-001;释放原因=IM不可达。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “释放触达失败预占”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RELEASED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RELEASED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“释放触达失败预占”;跨站点/跨部门数据需按权限范围过滤。 | 释放触达失败预占完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:释放触达失败预占 | 待执行\nTC-PROTO-0118 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行IM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行IM触达”的权限;相关基础数据已准备。 | plan_id=PLAN-006;channel=IM;content=回评卡片 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行IM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:plan_id=PLAN-006;channel=IM;content=回评卡片。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行IM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OUTBOUND/SENT”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:im_interaction_records;状态值=OUTBOUND/SENT;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行IM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行IM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行IM触达 | 待执行\nTC-PROTO-0119 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行EDM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行EDM触达”的权限;相关基础数据已准备。 | email=user@example.com;模板=回评邮件V1 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行EDM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:email=user@example.com;模板=回评邮件V1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行EDM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“SENT/DELIVERED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events;状态值=SENT/DELIVERED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行EDM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行EDM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行EDM触达 | 待执行\nTC-PROTO-0120 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行用户退订后停止EDM并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“用户退订后停止EDM”的权限;相关基础数据已准备。 | person_id=P100;event=UNSUBSCRIBED | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“用户退订后停止EDM”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P100;event=UNSUBSCRIBED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “用户退订后停止EDM”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“BLOCKED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events/channel_dedup_records;状态值=BLOCKED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“用户退订后停止EDM”;跨站点/跨部门数据需按权限范围过滤。 | 用户退订后停止EDM完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:用户退订后停止EDM | 待执行\nTC-PROTO-0121 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行创建客服工单并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“创建客服工单”的权限;相关基础数据已准备。 | person_id=P200;source=IM转人工;type=催评 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“创建客服工单”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P200;source=IM转人工;type=催评。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建客服工单”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_tickets;状态值=待分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建客服工单”;跨站点/跨部门数据需按权限范围过滤。 | 创建客服工单完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:创建客服工单 | 待执行\nTC-PROTO-0122 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行改派工单给组员并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“改派工单给组员”的权限;相关基础数据已准备。 | ticket_id=T001;from=组长;to=客服A;reason=当前负载低 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“改派工单给组员”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ticket_id=T001;from=组长;to=客服A;reason=当前负载低。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “改派工单给组员”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_assignment_logs;状态值=已分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“改派工单给组员”;跨站点/跨部门数据需按权限范围过滤。 | 改派工单给组员完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:改派工单给组员 | 待执行\nTC-PROTO-0123 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行强风险拦截并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“强风险拦截”的权限;相关基础数据已准备。 | person_id=P300;命中黑名单邮箱和设备 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“强风险拦截”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P300;命中黑名单邮箱和设备。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “强风险拦截”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“强风险拦截”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases/blacklist_entities;状态值=强风险拦截;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“强风险拦截”;跨站点/跨部门数据需按权限范围过滤。 | 强风险拦截完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:强风险拦截 | 待执行\nTC-PROTO-0124 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行弱风险人工放行并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“弱风险人工放行”的权限;相关基础数据已准备。 | risk_case=R001;原因=家庭共用设备;意见=放行 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“弱风险人工放行”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:risk_case=R001;原因=家庭共用设备;意见=放行。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “弱风险人工放行”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已放行”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases;状态值=已放行;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“弱风险人工放行”;跨站点/跨部门数据需按权限范围过滤。 | 弱风险人工放行完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:弱风险人工放行 | 待执行\nTC-PROTO-0125 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行登记评价提交并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“登记评价提交”的权限;相关基础数据已准备。 | person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接 | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“登记评价提交”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “登记评价提交”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已提交”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_submission_records;状态值=已提交;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“登记评价提交”;跨站点/跨部门数据需按权限范围过滤。 | 登记评价提交完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:登记评价提交 | 待执行\nTC-PROTO-0126 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon展示核验成功并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon展示核验成功”的权限;相关基础数据已准备。 | submission_id=SUB001;check_method=人工;result=DISPLAYED | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon展示核验成功”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB001;check_method=人工;result=DISPLAYED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon展示核验成功”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“CONFIRMED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks/review_results;状态值=CONFIRMED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon展示核验成功”;跨站点/跨部门数据需按权限范围过滤。 | Amazon展示核验成功完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon展示核验成功 | 待执行\nTC-PROTO-0127 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon暂不可核验进入观察并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon暂不可核验进入观察”的权限;相关基础数据已准备。 | submission_id=SUB002;result=UNVERIFIABLE | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon暂不可核验进入观察”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB002;result=UNVERIFIABLE。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon暂不可核验进入观察”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OBSERVING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks;状态值=OBSERVING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon暂不可核验进入观察”;跨站点/跨部门数据需按权限范围过滤。 | Amazon暂不可核验进入观察完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon暂不可核验进入观察 | 待执行\nTC-PROTO-0128 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL执行创建免评协作任务并校验业务结果 | P1 | 已进入“KOC/KOL”;当前用户具备执行“创建免评协作任务”的权限;相关基础数据已准备。 | creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频 | 1. 打开“用户运营系统-单文件.html”的“KOC/KOL”页面。\n2. 点击与“创建免评协作任务”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建免评协作任务”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“执行中”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:exemption_plan_tasks/code_records;状态值=执行中;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建免评协作任务”;跨站点/跨部门数据需按权限范围过滤。 | 创建免评协作任务完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:KOC/KOL;动作:创建免评协作任务 | 待执行\nTC-PROTO-0129 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知执行查看敏感信息审计并校验业务结果 | P1 | 已进入“审计通知”;当前用户具备执行“查看敏感信息审计”的权限;相关基础数据已准备。 | 对象=用户邮箱/设备号;动作=查看完整信息 | 1. 打开“用户运营系统-单文件.html”的“审计通知”页面。\n2. 点击与“查看敏感信息审计”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:对象=用户邮箱/设备号;动作=查看完整信息。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “查看敏感信息审计”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已记录”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:interaction_audit_logs;状态值=已记录;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“查看敏感信息审计”;跨站点/跨部门数据需按权限范围过滤。 | 查看敏感信息审计完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:审计通知;动作:查看敏感信息审计 | 待执行\nTC-PROTO-0130 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理执行分配导出权限并校验业务结果 | P1 | 已进入“系统管理”;当前用户具备执行“分配导出权限”的权限;相关基础数据已准备。 | role=用户运营组长;permission=导出计划数据;scope=US站点 | 1. 打开“用户运营系统-单文件.html”的“系统管理”页面。\n2. 点击与“分配导出权限”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:role=用户运营组长;permission=导出计划数据;scope=US站点。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “分配导出权限”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已授权”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:权限配置/审计日志;状态值=已授权;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“分配导出权限”;跨站点/跨部门数据需按权限范围过滤。 | 分配导出权限完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:系统管理;动作:分配导出权限 | 待执行\nTC-PROTO-0131 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:创建需求缺少ASIN | P2 | 已进入“需求中心”;准备异常数据或异常状态:ASIN为空,其他字段完整。 | 异常场景=创建需求缺少ASIN;异常数据=ASIN为空,其他字段完整 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:ASIN为空,其他字段完整。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“创建需求缺少ASIN”。\n2. 处理结果为:提示ASIN必填,需求不创建。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:创建需求缺少ASIN | 待执行\nTC-PROTO-0132 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:目标数量为0或负数 | P2 | 已进入“需求中心”;准备异常数据或异常状态:target_count=0/-1。 | 异常场景=目标数量为0或负数;异常数据=target_count=0/-1 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:target_count=0/-1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“目标数量为0或负数”。\n2. 处理结果为:提示目标数量必须大于0。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:目标数量为0或负数 | 待执行\nTC-PROTO-0133 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 异常场景 | 计划审核异常校验:审批意见为空驳回 | P2 | 已进入“计划审核”;准备异常数据或异常状态:decision=驳回;comment为空。 | 异常场景=审批意见为空驳回;异常数据=decision=驳回;comment为空 | 1. 打开“计划审核”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:decision=驳回;comment为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“审批意见为空驳回”。\n2. 处理结果为:阻止提交并提示填写驳回原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划审核;异常:审批意见为空驳回 | 待执行\nTC-PROTO-0134 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 异常场景 | 计划中心异常校验:已终止计划再次启动 | P2 | 已进入“计划中心”;准备异常数据或异常状态:plan.status=CANCELLED。 | 异常场景=已终止计划再次启动;异常数据=plan.status=CANCELLED | 1. 打开“计划中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:plan.status=CANCELLED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“已终止计划再次启动”。\n2. 处理结果为:启动按钮不可用或提示不可恢复。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划中心;异常:已终止计划再次启动 | 待执行\nTC-PROTO-0135 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:月度测评额度超过4 | P2 | 已进入“额度频控”;准备异常数据或异常状态:used=4,reserved=0,count=1。 | 异常场景=月度测评额度超过4;异常数据=used=4,reserved=0,count=1 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:used=4,reserved=0,count=1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“月度测评额度超过4”。\n2. 处理结果为:返回exceeded并阻止预占。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:月度测评额度超过4 | 待执行\nTC-PROTO-0136 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:累计评价超过12 | P2 | 已进入“额度频控”;准备异常数据或异常状态:lifetime_submission=12。 | 异常场景=累计评价超过12;异常数据=lifetime_submission=12 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:lifetime_submission=12。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“累计评价超过12”。\n2. 处理结果为:候选人进入排除池。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:累计评价超过12 | 待执行\nTC-PROTO-0137 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:发送前终校发现未关闭工单 | P2 | 已进入“额度频控”;准备异常数据或异常状态:support open ticket exists。 | 异常场景=发送前终校发现未关闭工单;异常数据=support open ticket exists | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:support open ticket exists。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“发送前终校发现未关闭工单”。\n2. 处理结果为:撤出本批次并记录原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:发送前终校发现未关闭工单 | 待执行\nTC-PROTO-0138 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:同计划同用户重复触达 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:person_id相同、plan_id相同、channel不同。 | 异常场景=同计划同用户重复触达;异常数据=person_id相同、plan_id相同、channel不同 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:person_id相同、plan_id相同、channel不同。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“同计划同用户重复触达”。\n2. 处理结果为:去重记录BLOCKED。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:同计划同用户重复触达 | 待执行\nTC-PROTO-0139 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:EDM硬退信 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:event=HARD_BOUNCED。 | 异常场景=EDM硬退信;异常数据=event=HARD_BOUNCED | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:event=HARD_BOUNCED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“EDM硬退信”。\n2. 处理结果为:邮箱状态标记不可触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:EDM硬退信 | 待执行\nTC-PROTO-0140 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:TEL三次未接通 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:retry_count=3。 | 异常场景=TEL三次未接通;异常数据=retry_count=3 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:retry_count=3。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“TEL三次未接通”。\n2. 处理结果为:降级EDM或关闭电话任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:TEL三次未接通 | 待执行\nTC-PROTO-0141 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:关闭工单缺少处理结果 | P2 | 已进入“客服中心”;准备异常数据或异常状态:result为空。 | 异常场景=关闭工单缺少处理结果;异常数据=result为空 | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:result为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“关闭工单缺少处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:关闭工单缺少处理结果 | 待执行\nTC-PROTO-0142 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:答应配合超时 | P2 | 已进入“客服中心”;准备异常数据或异常状态:deadline_at过期且无submission。 | 异常场景=答应配合超时;异常数据=deadline_at过期且无submission | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:deadline_at过期且无submission。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“答应配合超时”。\n2. 处理结果为:生成需再次联系任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:答应配合超时 | 待执行\nTC-PROTO-0143 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:黑名单同步接口超时 | P2 | 已进入“风险中心”;准备异常数据或异常状态:blacklist API timeout。 | 异常场景=黑名单同步接口超时;异常数据=blacklist API timeout | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:blacklist API timeout。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“黑名单同步接口超时”。\n2. 处理结果为:状态为失败待重试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:黑名单同步接口超时 | 待执行\nTC-PROTO-0144 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:弱风险复核被拒绝 | P2 | 已进入“风险中心”;准备异常数据或异常状态:人工意见=拒绝。 | 异常场景=弱风险复核被拒绝;异常数据=人工意见=拒绝 | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:人工意见=拒绝。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“弱风险复核被拒绝”。\n2. 处理结果为:用户不能进入触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:弱风险复核被拒绝 | 待执行\nTC-PROTO-0145 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:提交评价ASIN不匹配 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:登记ASIN=A,证据链接ASIN=B。 | 异常场景=提交评价ASIN不匹配;异常数据=登记ASIN=A,证据链接ASIN=B | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:登记ASIN=A,证据链接ASIN=B。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“提交评价ASIN不匹配”。\n2. 处理结果为:标记异常不计入完成。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:提交评价ASIN不匹配 | 待执行\nTC-PROTO-0146 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:Amazon未展示超过观察期 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:status=OBSERVING且retry超期。 | 异常场景=Amazon未展示超过观察期;异常数据=status=OBSERVING且retry超期 | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:status=OBSERVING且retry超期。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“Amazon未展示超过观察期”。\n2. 处理结果为:标记ABNORMAL并通知运营。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:Amazon未展示超过观察期 | 待执行\nTC-PROTO-0147 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 异常场景 | KOC/KOL异常校验:CODE缺失 | P2 | 已进入“KOC/KOL”;准备异常数据或异常状态:免评任务未填写CODE。 | 异常场景=CODE缺失;异常数据=免评任务未填写CODE | 1. 打开“KOC/KOL”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:免评任务未填写CODE。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“CODE缺失”。\n2. 处理结果为:阻止创建或标记价格/CODE待确认。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:KOC/KOL;异常:CODE缺失 | 待执行\nTC-PROTO-0148 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 异常场景 | 审计通知异常校验:普通客服查看完整邮箱 | P2 | 已进入“审计通知”;准备异常数据或异常状态:role=客服;action=查看完整信息。 | 异常场景=普通客服查看完整邮箱;异常数据=role=客服;action=查看完整信息 | 1. 打开“审计通知”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:role=客服;action=查看完整信息。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“普通客服查看完整邮箱”。\n2. 处理结果为:拒绝访问并记录越权尝试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:审计通知;异常:普通客服查看完整邮箱 | 待执行\nTC-PROTO-0149 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 异常场景 | 系统管理异常校验:离职账号仍有审批任务 | P2 | 已进入“系统管理”;准备异常数据或异常状态:account=disabled但任务未交接。 | 异常场景=离职账号仍有审批任务;异常数据=account=disabled但任务未交接 | 1. 打开“系统管理”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:account=disabled但任务未交接。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“离职账号仍有审批任务”。\n2. 处理结果为:阻止完成离职并提示先交接。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:系统管理;异常:离职账号仍有审批任务 | 待执行\nTC-PROTO-0150 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发按线索查真实人并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/person?type=email&value=xxx”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/person?type=email&value=xxx;预期输出=返回person_id、confidence、matched_clues | 1. 打开“用户中心”页面。\n2. 执行会触发“按线索查真实人”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/person?type=email&value=xxx”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/person?type=email&value=xxx”。\n2. 接口响应包含:返回person_id、confidence、matched_clues。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 按线索查真实人在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/person?type=email&value=xxx | 待执行\nTC-PROTO-0151 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发获取用户上下文卡并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/context/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/context/{person_id};预期输出=返回identity、transactions、services、risks、devices、outreach_history | 1. 打开“用户中心”页面。\n2. 执行会触发“获取用户上下文卡”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/context/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/context/{person_id}”。\n2. 接口响应包含:返回identity、transactions、services、risks、devices、outreach_history。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 获取用户上下文卡在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/context/{person_id} | 待执行\nTC-PROTO-0152 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发批量身份查询并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“POST /api/identity/batch-check”;测试用户拥有页面访问权限。 | 接口=POST /api/identity/batch-check;预期输出=返回每个线索对应person_id和confidence | 1. 打开“用户中心”页面。\n2. 执行会触发“批量身份查询”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/identity/batch-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/identity/batch-check”。\n2. 接口响应包含:返回每个线索对应person_id和confidence。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量身份查询在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:POST /api/identity/batch-check | 待执行\nTC-PROTO-0153 | 用户运营系统-单文件.html | 计划中心 | 需求与计划管理 | 数据校验 | 计划中心页面触发创建需求接口并校验接口数据 | P2 | 前端页面“计划中心”已打开;后端或 mock 服务提供接口“POST /api/demands”;测试用户拥有页面访问权限。 | 接口=POST /api/demands;预期输出=返回demand_id和status | 1. 打开“计划中心”页面。\n2. 执行会触发“创建需求接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/demands”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/demands”。\n2. 接口响应包含:返回demand_id和status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建需求接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划中心;接口:POST /api/demands | 待执行\nTC-PROTO-0154 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | 数据校验 | 计划审核页面触发提交审批接口并校验接口数据 | P2 | 前端页面“计划审核”已打开;后端或 mock 服务提供接口“POST /api/approvals/{plan_id}/submit”;测试用户拥有页面访问权限。 | 接口=POST /api/approvals/{plan_id}/submit;预期输出=生成approval_records | 1. 打开“计划审核”页面。\n2. 执行会触发“提交审批接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/approvals/{plan_id}/submit”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/approvals/{plan_id}/submit”。\n2. 接口响应包含:生成approval_records。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 提交审批接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划审核;接口:POST /api/approvals/{plan_id}/submit | 待执行\nTC-PROTO-0155 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发额度查询接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“GET /api/quota/check/{person_id}?type=REVIEW”;测试用户拥有页面访问权限。 | 接口=GET /api/quota/check/{person_id}?type=REVIEW;预期输出=返回used、in_progress、reserved、remaining、status | 1. 打开“额度频控”页面。\n2. 执行会触发“额度查询接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/quota/check/{person_id}?type=REVIEW”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/quota/check/{person_id}?type=REVIEW”。\n2. 接口响应包含:返回used、in_progress、reserved、remaining、status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 额度查询接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:GET /api/quota/check/{person_id}?type=REVIEW | 待执行\nTC-PROTO-0156 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发批量预占接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/reserve”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/reserve;预期输出=返回reservation_id并更新reserved | 1. 打开“额度频控”页面。\n2. 执行会触发“批量预占接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/reserve”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/reserve”。\n2. 接口响应包含:返回reservation_id并更新reserved。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量预占接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/reserve | 待执行\nTC-PROTO-0157 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发发送前终校接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/final-check”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/final-check;预期输出=返回APPROVED/WITHDRAWN和reasons | 1. 打开“额度频控”页面。\n2. 执行会触发“发送前终校接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/final-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/final-check”。\n2. 接口响应包含:返回APPROVED/WITHDRAWN和reasons。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 发送前终校接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/final-check | 待执行\nTC-PROTO-0158 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发渠道路由接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“POST /api/outreach/route”;测试用户拥有页面访问权限。 | 接口=POST /api/outreach/route;预期输出=返回recommended_channel和alternatives | 1. 打开“推送/触达”页面。\n2. 执行会触发“渠道路由接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/outreach/route”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/outreach/route”。\n2. 接口响应包含:返回recommended_channel和alternatives。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 渠道路由接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:POST /api/outreach/route | 待执行\nTC-PROTO-0159 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发触达历史接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“GET /api/outreach/history/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/outreach/history/{person_id};预期输出=返回im、edm、app、tel历史 | 1. 打开“推送/触达”页面。\n2. 执行会触发“触达历史接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/outreach/history/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/outreach/history/{person_id}”。\n2. 接口响应包含:返回im、edm、app、tel历史。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 触达历史接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:GET /api/outreach/history/{person_id} | 待执行\nTC-PROTO-0160 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发创建工单接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“POST /api/tickets”;测试用户拥有页面访问权限。 | 接口=POST /api/tickets;预期输出=返回ticket_id | 1. 打开“客服中心”页面。\n2. 执行会触发“创建工单接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/tickets”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/tickets”。\n2. 接口响应包含:返回ticket_id。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建工单接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:POST /api/tickets | 待执行\nTC-PROTO-0161 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发查询可用客服接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“GET /api/support/available-agents”;测试用户拥有页面访问权限。 | 接口=GET /api/support/available-agents;预期输出=返回agent_id和current_load | 1. 打开“客服中心”页面。\n2. 执行会触发“查询可用客服接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/support/available-agents”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/support/available-agents”。\n2. 接口响应包含:返回agent_id和current_load。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询可用客服接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:GET /api/support/available-agents | 待执行\nTC-PROTO-0162 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发记录评价提交接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“POST /api/reviews/submission”;测试用户拥有页面访问权限。 | 接口=POST /api/reviews/submission;预期输出=返回submission_id和quota_updated | 1. 打开“评价追踪”页面。\n2. 执行会触发“记录评价提交接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/reviews/submission”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/reviews/submission”。\n2. 接口响应包含:返回submission_id和quota_updated。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 记录评价提交接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:POST /api/reviews/submission | 待执行\nTC-PROTO-0163 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发查询计划评价进度接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“GET /api/reviews/status/{plan_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/reviews/status/{plan_id};预期输出=返回total_submissions、verified、pending、completion_rate | 1. 打开“评价追踪”页面。\n2. 执行会触发“查询计划评价进度接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/reviews/status/{plan_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/reviews/status/{plan_id}”。\n2. 接口响应包含:返回total_submissions、verified、pending、completion_rate。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询计划评价进度接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:GET /api/reviews/status/{plan_id} | 待执行\nTC-PROTO-0164 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:评价主闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流 | 1. 从 Dashboard 或对应入口启动“评价主闭环”。\n2. 按流程依次完成:ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “评价主闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 评价主闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:评价主闭环 | 待执行\nTC-PROTO-0165 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:紧急Listing闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升 | 1. 从 Dashboard 或对应入口启动“紧急Listing闭环”。\n2. 按流程依次完成:评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “紧急Listing闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 紧急Listing闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:紧急Listing闭环 | 待执行\nTC-PROTO-0166 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:推送风险复核闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录 | 1. 从 Dashboard 或对应入口启动“推送风险复核闭环”。\n2. 按流程依次完成:退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “推送风险复核闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 推送风险复核闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:推送风险复核闭环 | 待执行\nTC-PROTO-0167 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:黑名单同步闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查 | 1. 从 Dashboard 或对应入口启动“黑名单同步闭环”。\n2. 按流程依次完成:客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “黑名单同步闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 黑名单同步闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:黑名单同步闭环 | 待执行\nTC-PROTO-0168 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:客服转化闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新 | 1. 从 Dashboard 或对应入口启动“客服转化闭环”。\n2. 按流程依次完成:用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “客服转化闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 客服转化闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:客服转化闭环 | 待执行\nTC-PROTO-0169 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:免评协作闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新 | 1. 从 Dashboard 或对应入口启动“免评协作闭环”。\n2. 按流程依次完成:免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “免评协作闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 免评协作闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:免评协作闭环 | 待执行\nTC-PROTO-0190 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“需求中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2;详情字段=需求ID、ASIN、目标数量、周期、提交人、评估结果 | 1. 打开“需求中心”页面。\n2. 在筛选区按业务条件选择或输入:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:需求ID、ASIN、目标数量、周期、提交人、评估结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2”。\n2. 详情抽屉展示“需求ID、ASIN、目标数量、周期、提交人、评估结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 需求中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:需求中心;筛选:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2 | 待执行\nTC-PROTO-0191 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划审核”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回;详情字段=审批链、审批人、意见、step_order、decided_at | 1. 打开“计划审核”页面。\n2. 在筛选区按业务条件选择或输入:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:审批链、审批人、意见、step_order、decided_at。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回”。\n2. 详情抽屉展示“审批链、审批人、意见、step_order、decided_at”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划审核查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划审核;筛选:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回 | 待执行\nTC-PROTO-0192 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL;详情字段=计划项、目标量、候选人、资源分配、完成率 | 1. 打开“计划中心”页面。\n2. 在筛选区按业务条件选择或输入:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:计划项、目标量、候选人、资源分配、完成率。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL”。\n2. 详情抽屉展示“计划项、目标量、候选人、资源分配、完成率”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划中心;筛选:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL | 待执行\nTC-PROTO-0193 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 功能测试 | ASIN/Listing按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“ASIN/Listing”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间;详情字段=评分、评价数、差评数、健康状态、责任人 | 1. 打开“ASIN/Listing”页面。\n2. 在筛选区按业务条件选择或输入:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:评分、评价数、差评数、健康状态、责任人。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间”。\n2. 详情抽屉展示“评分、评价数、差评数、健康状态、责任人”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | ASIN/Listing查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:ASIN/Listing;筛选:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间 | 待执行\nTC-PROTO-0194 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 功能测试 | 用户中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“用户中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=国家、性别、标签、身份、产品数、活动数、近7天EDM次数;详情字段=用户主档、标签、身份、产品关系、近期活跃 | 1. 打开“用户中心”页面。\n2. 在筛选区按业务条件选择或输入:国家、性别、标签、身份、产品数、活动数、近7天EDM次数。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:用户主档、标签、身份、产品关系、近期活跃。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“国家、性别、标签、身份、产品数、活动数、近7天EDM次数”。\n2. 详情抽屉展示“用户主档、标签、身份、产品关系、近期活跃”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 用户中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:用户中心;筛选:国家、性别、标签、身份、产品数、活动数、近7天EDM次数 | 待执行\nTC-PROTO-0195 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“额度频控”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=额度类型=测评/免评/累计;状态=sufficient/warning/exceeded;详情字段=used、in_progress、reserved、remaining、limit_value | 1. 打开“额度频控”页面。\n2. 在筛选区按业务条件选择或输入:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:used、in_progress、reserved、remaining、limit_value。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“额度类型=测评/免评/累计;状态=sufficient/warning/exceeded”。\n2. 详情抽屉展示“used、in_progress、reserved、remaining、limit_value”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 额度频控查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:额度频控;筛选:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded | 待执行\nTC-PROTO-0196 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“推送/触达”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订;详情字段=发送、点击、回复、退订、route decision、dedup reason | 1. 打开“推送/触达”页面。\n2. 在筛选区按业务条件选择或输入:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:发送、点击、回复、退订、route decision、dedup reason。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订”。\n2. 详情抽屉展示“发送、点击、回复、退订、route decision、dedup reason”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 推送/触达查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:推送/触达;筛选:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订 | 待执行\nTC-PROTO-0197 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“客服中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭;详情字段=工单ID、assigned_agent、followup状态、首次回复时长 | 1. 打开“客服中心”页面。\n2. 在筛选区按业务条件选择或输入:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:工单ID、assigned_agent、followup状态、首次回复时长。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭”。\n2. 详情抽屉展示“工单ID、assigned_agent、followup状态、首次回复时长”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 客服中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:客服中心;筛选:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭 | 待执行\nTC-PROTO-0198 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“风险中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝;详情字段=risk_signal、risk_case、blacklist_entity | 1. 打开“风险中心”页面。\n2. 在筛选区按业务条件选择或输入:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:risk_signal、risk_case、blacklist_entity。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝”。\n2. 详情抽屉展示“risk_signal、risk_case、blacklist_entity”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 风险中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:风险中心;筛选:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝 | 待执行\nTC-PROTO-0199 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“评价追踪”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=核验状态=已提交/展示成功/暂不可核验/异常观察;详情字段=submission_id、display_check、retry_count、completion_rate | 1. 打开“评价追踪”页面。\n2. 在筛选区按业务条件选择或输入:核验状态=已提交/展示成功/暂不可核验/异常观察。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:submission_id、display_check、retry_count、completion_rate。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“核验状态=已提交/展示成功/暂不可核验/异常观察”。\n2. 详情抽屉展示“submission_id、display_check、retry_count、completion_rate”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 评价追踪查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:评价追踪;筛选:核验状态=已提交/展示成功/暂不可核验/异常观察 | 待执行\nTC-PROTO-0200 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“KOC/KOL”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置;详情字段=creator、Brief、CODE、返点、内容链接 | 1. 打开“KOC/KOL”页面。\n2. 在筛选区按业务条件选择或输入:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:creator、Brief、CODE、返点、内容链接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置”。\n2. 详情抽屉展示“creator、Brief、CODE、返点、内容链接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | KOC/KOL查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:KOC/KOL;筛选:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置 | 待执行\nTC-PROTO-0201 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“审计通知”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围;详情字段=日志ID、操作者、对象、动作、结果 | 1. 打开“审计通知”页面。\n2. 在筛选区按业务条件选择或输入:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:日志ID、操作者、对象、动作、结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围”。\n2. 详情抽屉展示“日志ID、操作者、对象、动作、结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 审计通知查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:审计通知;筛选:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围 | 待执行\nTC-PROTO-0202 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“系统管理”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=角色、部门、站点、权限点、账号状态;详情字段=账号、角色、数据范围、权限点、离职交接 | 1. 打开“系统管理”页面。\n2. 在筛选区按业务条件选择或输入:角色、部门、站点、权限点、账号状态。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:账号、角色、数据范围、权限点、离职交接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“角色、部门、站点、权限点、账号状态”。\n2. 详情抽屉展示“账号、角色、数据范围、权限点、离职交接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 系统管理查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:系统管理;筛选:角色、部门、站点、权限点、账号状态 | 待执行\nTC-PROTO-0203 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度剩余1次预警 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,in_progress=0,reserved=0,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,in_progress=0,reserved=0,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:允许预占但进入预警池。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度剩余1次预警 | 待执行\nTC-PROTO-0204 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度已用3且预占1再预占1 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,reserved=1,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,reserved=1,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:阻止预占,状态exceeded。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度已用3且预占1再预占1 | 待执行\nTC-PROTO-0205 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:免评额度独立预占 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=review used=4, exemption used=0,count=1 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:review used=4, exemption used=0,count=1。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:免评可预占,测评不可预占。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:免评额度独立预占 | 待执行\nTC-PROTO-0206 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:累计12提交后不因未展示回退 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=lifetime=11,提交评价后Amazon未展示 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:lifetime=11,提交评价后Amazon未展示。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:lifetime变12且不回退。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:累计12提交后不因未展示回退 | 待执行\nTC-PROTO-0207 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:预占超时自动释放 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=reservation.status=RESERVED且expires_at已过 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:reservation.status=RESERVED且expires_at已过。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:状态EXPIRED/RELEASED,remaining恢复。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:预占超时自动释放 | 待执行\nTC-PROTO-0208 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:跨计划重复入选 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=同person同时进入PLAN-A和PLAN-B | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:同person同时进入PLAN-A和PLAN-B。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:只保留高优先级或先预占计划,另一计划排除/预警。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:跨计划重复入选 | 待执行\nTC-PROTO-0209 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道IM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:APP活跃+已绑定用户;额度、风险、去重均通过。 | 渠道=IM;用户条件=APP活跃+已绑定用户;动作=推送回评卡片 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“APP活跃+已绑定用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“IM”。\n4. 执行“推送回评卡片”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“IM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“im_interaction_records”。\n3. 后续处理符合:用户回复后重新校验身份/额度/风险。\n4. Dashboard和用户上下文卡可查看触达历史。 | im_interaction_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | IM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:IM | 待执行\nTC-PROTO-0210 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道EDM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:未注册APP但邮箱可用用户;额度、风险、去重均通过。 | 渠道=EDM;用户条件=未注册APP但邮箱可用用户;动作=发送邮件并追踪送达/打开/点击/回复/退订 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“未注册APP但邮箱可用用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“EDM”。\n4. 执行“发送邮件并追踪送达/打开/点击/回复/退订”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“EDM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“edm_message_events”。\n3. 后续处理符合:回复邮件生成客服工单。\n4. Dashboard和用户上下文卡可查看触达历史。 | edm_message_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | EDM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:EDM | 待执行\nTC-PROTO-0211 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道APP Push按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;额度、风险、去重均通过。 | 渠道=APP Push;用户条件=绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;动作=发送Push并追踪点击打开/忽略/卸载 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“绑定新玩具/不活跃/计划到期/Listing紧急/活动触发”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“APP Push”。\n4. 执行“发送Push并追踪点击打开/忽略/卸载”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“APP Push”作为推荐渠道或可选渠道。\n2. 执行动作后写入“app_touch_events”。\n3. 后续处理符合:点击后分流到提交回评/联系客服/浏览。\n4. Dashboard和用户上下文卡可查看触达历史。 | app_touch_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | APP Push渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:APP Push | 待执行\nTC-PROTO-0212 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道TEL按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:高价值多次无响应或答应配合超时用户;额度、风险、去重均通过。 | 渠道=TEL;用户条件=高价值多次无响应或答应配合超时用户;动作=生成电话任务并记录通话结果 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“高价值多次无响应或答应配合超时用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“TEL”。\n4. 执行“生成电话任务并记录通话结果”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“TEL”作为推荐渠道或可选渠道。\n2. 执行动作后写入“tel_call_records”。\n3. 后续处理符合:未接通小于3次重拨,大于等于3次降级EDM或关闭。\n4. Dashboard和用户上下文卡可查看触达历史。 | tel_call_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | TEL渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:TEL | 待执行\nTC-PROTO-0213 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理截图证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=截图;包含ASIN和评论内容 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=截图;包含ASIN和评论内容。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并触发quota commit。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:截图证据登记 | 待执行\nTC-PROTO-0214 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理链接证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=Review Link;链接可打开 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=Review Link;链接可打开。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并进入展示核验。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:链接证据登记 | 待执行\nTC-PROTO-0215 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理人工核验展示成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=人工;check_result=DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=人工;check_result=DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:计入计划完成数并更新ASIN健康。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:人工核验展示成功 | 待执行\nTC-PROTO-0216 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理自动核验未展示 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=自动;check_result=NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=自动;check_result=NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:进入异常观察队列。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:自动核验未展示 | 待执行\nTC-PROTO-0217 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理暂不可核验 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_result=UNVERIFIABLE;原因=Amazon审核中 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_result=UNVERIFIABLE;原因=Amazon审核中。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:保留已提交事实并定期复查。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:暂不可核验 | 待执行\nTC-PROTO-0218 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察复查成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=OBSERVING重试后DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:OBSERVING重试后DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:转CONFIRMED并回流计划。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察复查成功 | 待执行\nTC-PROTO-0219 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察期满失败 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=retry_count超过阈值仍NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:retry_count超过阈值仍NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:标记ABNORMAL并通知运营。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察期满失败 | 待执行\nTC-PROTO-0266 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色Amazon运营模块访问与按钮权限 | P1 | 准备角色为“Amazon运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=Amazon运营;可见范围=需求中心/ASIN/计划审核查看;允许=创建需求、查看ASIN健康;限制=不能审批用户运营计划或查看完整用户敏感信息 | 1. 使用“Amazon运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:创建需求、查看ASIN健康。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批用户运营计划或查看完整用户敏感信息。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. Amazon运营只能访问“需求中心/ASIN/计划审核查看”。\n2. 允许动作“创建需求、查看ASIN健康”可正常执行。\n3. 限制动作“不能审批用户运营计划或查看完整用户敏感信息”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | Amazon运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=Amazon运营 | 待执行\nTC-PROTO-0267 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色用户运营模块访问与按钮权限 | P1 | 准备角色为“用户运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=用户运营;可见范围=需求中心/计划中心/推送中心/用户中心;允许=评估需求、生成计划、圈选人群、触达;限制=不能同步黑名单或配置系统权限 | 1. 使用“用户运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:评估需求、生成计划、圈选人群、触达。\n4. 尝试通过URL hash或按钮执行限制动作:不能同步黑名单或配置系统权限。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 用户运营只能访问“需求中心/计划中心/推送中心/用户中心”。\n2. 允许动作“评估需求、生成计划、圈选人群、触达”可正常执行。\n3. 限制动作“不能同步黑名单或配置系统权限”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 用户运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=用户运营 | 待执行\nTC-PROTO-0268 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服模块访问与按钮权限 | P1 | 准备角色为“客服”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服;可见范围=客服中心/用户上下文摘要/评价登记;允许=处理工单、登记评价提交;限制=不能查看跨团队绩效和完整设备号 | 1. 使用“客服”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:处理工单、登记评价提交。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看跨团队绩效和完整设备号。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服只能访问“客服中心/用户上下文摘要/评价登记”。\n2. 允许动作“处理工单、登记评价提交”可正常执行。\n3. 限制动作“不能查看跨团队绩效和完整设备号”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服 | 待执行\nTC-PROTO-0269 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服主管模块访问与按钮权限 | P1 | 准备角色为“客服主管”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服主管;可见范围=客服中心/客服执行看板/绩效;允许=分配工单、查看组内绩效、排班;限制=不能审批免评计划除非授权 | 1. 使用“客服主管”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:分配工单、查看组内绩效、排班。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批免评计划除非授权。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服主管只能访问“客服中心/客服执行看板/绩效”。\n2. 允许动作“分配工单、查看组内绩效、排班”可正常执行。\n3. 限制动作“不能审批免评计划除非授权”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服主管权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服主管 | 待执行\nTC-PROTO-0270 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色风险负责人模块访问与按钮权限 | P1 | 准备角色为“风险负责人”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=风险负责人;可见范围=风险中心/黑名单/审计;允许=复核风险、同步黑名单、标记误报;限制=不能修改计划目标量 | 1. 使用“风险负责人”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:复核风险、同步黑名单、标记误报。\n4. 尝试通过URL hash或按钮执行限制动作:不能修改计划目标量。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 风险负责人只能访问“风险中心/黑名单/审计”。\n2. 允许动作“复核风险、同步黑名单、标记误报”可正常执行。\n3. 限制动作“不能修改计划目标量”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 风险负责人权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=风险负责人 | 待执行\nTC-PROTO-0271 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色KOC运营模块访问与按钮权限 | P1 | 准备角色为“KOC运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=KOC运营;可见范围=KOC/KOL协作;允许=维护Brief、CODE、内容记录;限制=不能查看普通用户完整身份线索 | 1. 使用“KOC运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:维护Brief、CODE、内容记录。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看普通用户完整身份线索。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. KOC运营只能访问“KOC/KOL协作”。\n2. 允许动作“维护Brief、CODE、内容记录”可正常执行。\n3. 限制动作“不能查看普通用户完整身份线索”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | KOC运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=KOC运营 | 待执行\nTC-PROTO-0272 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色系统管理员模块访问与按钮权限 | P1 | 准备角色为“系统管理员”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=系统管理员;可见范围=全部模块;允许=账号权限、审计、配置、跨部门看板;限制=敏感访问仍需审计 | 1. 使用“系统管理员”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:账号权限、审计、配置、跨部门看板。\n4. 尝试通过URL hash或按钮执行限制动作:敏感访问仍需审计。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 系统管理员只能访问“全部模块”。\n2. 允许动作“账号权限、审计、配置、跨部门看板”可正常执行。\n3. 限制动作“敏感访问仍需审计”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 系统管理员权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=系统管理员 | 待执行\nTC-PROTO-0273 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 数据校验 | 需求中心执行导出待评估需求并校验导出脱敏与范围 | P2 | 已进入“需求中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=待评估;导出内容=导出demands当前筛选字段 | 1. 打开“需求中心”。\n2. 设置筛选条件:筛选状态=待评估。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出demands当前筛选字段。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=需求中心;导出=导出待评估需求 | 待执行\nTC-PROTO-0274 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 数据校验 | 计划审核执行导出审批记录并校验导出脱敏与范围 | P2 | 已进入“计划审核”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选审批状态=待审批;导出内容=导出approval_records | 1. 打开“计划审核”。\n2. 设置筛选条件:筛选审批状态=待审批。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出approval_records。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划审核;导出=导出审批记录 | 待执行\nTC-PROTO-0275 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 数据校验 | 计划中心执行导出计划执行进度并校验导出脱敏与范围 | P2 | 已进入“计划中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=执行中;导出内容=导出计划、计划项、完成率 | 1. 打开“计划中心”。\n2. 设置筛选条件:筛选状态=执行中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出计划、计划项、完成率。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划中心;导出=导出计划执行进度 | 待执行\nTC-PROTO-0276 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 数据校验 | ASIN/Listing执行导出健康风险ASIN并校验导出脱敏与范围 | P2 | 已进入“ASIN/Listing”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选健康状态=风险/严重风险;导出内容=导出评分、评价数、差评数 | 1. 打开“ASIN/Listing”。\n2. 设置筛选条件:筛选健康状态=风险/严重风险。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出评分、评价数、差评数。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=ASIN/Listing;导出=导出健康风险ASIN | 待执行\nTC-PROTO-0277 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 数据校验 | 用户中心执行导出人群包并校验导出脱敏与范围 | P2 | 已进入“用户中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选标签/国家/产品绑定;导出内容=导出脱敏用户ID和标签 | 1. 打开“用户中心”。\n2. 设置筛选条件:筛选标签/国家/产品绑定。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出脱敏用户ID和标签。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=用户中心;导出=导出人群包 | 待执行\nTC-PROTO-0278 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 数据校验 | 额度频控执行导出额度预警用户并校验导出脱敏与范围 | P2 | 已进入“额度频控”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=warning/exceeded;导出内容=导出额度台账摘要 | 1. 打开“额度频控”。\n2. 设置筛选条件:筛选status=warning/exceeded。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出额度台账摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=额度频控;导出=导出额度预警用户 | 待执行\nTC-PROTO-0279 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 数据校验 | 推送/触达执行导出退订用户并校验导出脱敏与范围 | P2 | 已进入“推送/触达”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选event=UNSUBSCRIBED;导出内容=导出退订事件和渠道 | 1. 打开“推送/触达”。\n2. 设置筛选条件:筛选event=UNSUBSCRIBED。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出退订事件和渠道。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=推送/触达;导出=导出退订用户 | 待执行\nTC-PROTO-0280 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 数据校验 | 客服中心执行导出超时工单并校验导出脱敏与范围 | P2 | 已进入“客服中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=等待用户且超时;导出内容=导出工单与负责人 | 1. 打开“客服中心”。\n2. 设置筛选条件:筛选状态=等待用户且超时。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出工单与负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=客服中心;导出=导出超时工单 | 待执行\nTC-PROTO-0281 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 数据校验 | 风险中心执行导出风险案件并校验导出脱敏与范围 | P2 | 已进入“风险中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=人工复核中;导出内容=导出风险摘要脱敏 | 1. 打开“风险中心”。\n2. 设置筛选条件:筛选状态=人工复核中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出风险摘要脱敏。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=风险中心;导出=导出风险案件 | 待执行\nTC-PROTO-0282 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 数据校验 | 评价追踪执行导出异常观察队列并校验导出脱敏与范围 | P2 | 已进入“评价追踪”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=OBSERVING/ABNORMAL;导出内容=导出提交和核验摘要 | 1. 打开“评价追踪”。\n2. 设置筛选条件:筛选status=OBSERVING/ABNORMAL。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出提交和核验摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=评价追踪;导出=导出异常观察队列 | 待执行\nTC-PROTO-0283 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 数据校验 | KOC/KOL执行导出逾期协作任务并校验导出脱敏与范围 | P2 | 已进入“KOC/KOL”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=逾期;导出内容=导出CODE/Brief/负责人 | 1. 打开“KOC/KOL”。\n2. 设置筛选条件:筛选状态=逾期。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出CODE/Brief/负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=KOC/KOL;导出=导出逾期协作任务 | 待执行\nTC-PROTO-0284 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 数据校验 | 审计通知执行导出敏感动作日志并校验导出脱敏与范围 | P2 | 已进入“审计通知”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选动作=查看完整信息/导出;导出内容=导出审计日志 | 1. 打开“审计通知”。\n2. 设置筛选条件:筛选动作=查看完整信息/导出。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出审计日志。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=审计通知;导出=导出敏感动作日志 | 待执行\nTC-PROTO-0298 | 用户运营系统-单文件.html | 需求中心 | 系统稳定性与幂等 | 异常场景 | 需求中心稳定性校验:创建需求重复提交 | P2 | 已进入“需求中心”;准备可执行场景:创建需求重复提交。 | 动作=提交按钮连续点击两次;预期=只创建一个demand_id | 1. 打开原型页面“需求中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:提交按钮连续点击两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只创建一个demand_id。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:创建需求重复提交 | 待执行\nTC-PROTO-0299 | 用户运营系统-单文件.html | 计划审核 | 系统稳定性与幂等 | 异常场景 | 计划审核稳定性校验:两名审批人同时审批 | P2 | 已进入“计划审核”;准备可执行场景:两名审批人同时审批。 | 动作=一个通过一个驳回并发提交;预期=按后端锁定规则只接受一个有效决策 | 1. 打开原型页面“计划审核”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:一个通过一个驳回并发提交。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:按后端锁定规则只接受一个有效决策。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:两名审批人同时审批 | 待执行\nTC-PROTO-0300 | 用户运营系统-单文件.html | 计划中心 | 系统稳定性与幂等 | 异常场景 | 计划中心稳定性校验:计划暂停后重复暂停 | P2 | 已进入“计划中心”;准备可执行场景:计划暂停后重复暂停。 | 动作=执行中计划点击暂停两次;预期=第二次提示计划已暂停 | 1. 打开原型页面“计划中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:执行中计划点击暂停两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:第二次提示计划已暂停。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:计划暂停后重复暂停 | 待执行\nTC-PROTO-0301 | 用户运营系统-单文件.html | 额度频控 | 系统稳定性与幂等 | 异常场景 | 额度频控稳定性校验:并发预占同一真实人最后额度 | P2 | 已进入“额度频控”;准备可执行场景:并发预占同一真实人最后额度。 | 动作=两个计划同时预占remaining=1;预期=只允许一个预占成功 | 1. 打开原型页面“额度频控”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个计划同时预占remaining=1。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只允许一个预占成功。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:并发预占同一真实人最后额度 | 待执行\nTC-PROTO-0302 | 用户运营系统-单文件.html | 推送/触达 | 系统稳定性与幂等 | 异常场景 | 推送/触达稳定性校验:发送任务队列中刷新 | P2 | 已进入“推送/触达”;准备可执行场景:发送任务队列中刷新。 | 动作=点击发送后立即刷新页面;预期=任务状态可从队列恢复查询 | 1. 打开原型页面“推送/触达”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:点击发送后立即刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:任务状态可从队列恢复查询。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:发送任务队列中刷新 | 待执行\nTC-PROTO-0303 | 用户运营系统-单文件.html | 客服中心 | 系统稳定性与幂等 | 异常场景 | 客服中心稳定性校验:同用户重复创建工单 | P2 | 已进入“客服中心”;准备可执行场景:同用户重复创建工单。 | 动作=同person_id已有open工单再次创建;预期=提示关联已有工单或合并 | 1. 打开原型页面“客服中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person_id已有open工单再次创建。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示关联已有工单或合并。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:同用户重复创建工单 | 待执行\nTC-PROTO-0304 | 用户运营系统-单文件.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:确认诈骗重复点击 | P2 | 已进入“风险中心”;准备可执行场景:确认诈骗重复点击。 | 动作=风险案件连续点击确认诈骗;预期=只同步一次黑名单候选 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:风险案件连续点击确认诈骗。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只同步一次黑名单候选。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:确认诈骗重复点击 | 待执行\nTC-PROTO-0305 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:评价提交重复登记 | P2 | 已进入“评价追踪”;准备可执行场景:评价提交重复登记。 | 动作=同person+asin+plan重复提交相同证据;预期=提示重复记录或合并,不重复扣额度 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person+asin+plan重复提交相同证据。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示重复记录或合并,不重复扣额度。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:评价提交重复登记 | 待执行\nTC-PROTO-0306 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:展示核验重复确认 | P2 | 已进入“评价追踪”;准备可执行场景:展示核验重复确认。 | 动作=已CONFIRMED记录再次确认展示;预期=计划完成数不重复增加 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:已CONFIRMED记录再次确认展示。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:计划完成数不重复增加。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:展示核验重复确认 | 待执行\nTC-PROTO-0307 | 用户运营系统-单文件.html | 系统管理 | 系统稳定性与幂等 | 异常场景 | 系统管理稳定性校验:权限变更后立即生效 | P2 | 已进入“系统管理”;准备可执行场景:权限变更后立即生效。 | 动作=撤销用户导出权限后刷新;预期=导出按钮不可用且接口拒绝 | 1. 打开原型页面“系统管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:撤销用户导出权限后刷新。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:导出按钮不可用且接口拒绝。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:权限变更后立即生效 | 待执行\nTC-PROTO-0308 | 用户运营系统-单文件.html | 审计通知 | 系统稳定性与幂等 | 异常场景 | 审计通知稳定性校验:审计列表空状态 | P2 | 已进入“审计通知”;准备可执行场景:审计列表空状态。 | 动作=筛选未来日期无日志;预期=显示暂无数据且可重置 | 1. 打开原型页面“审计通知”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选未来日期无日志。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:显示暂无数据且可重置。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:审计列表空状态 | 待执行\n# Sheet: 全量用例\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0001 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看测评需求审核卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在Amazon 运营提交的测评需求模拟数据。 | 卡片=测评需求审核;指标=申请 18 / 已批 8;状态=正常;目标页面=需求中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“测评需求审核”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入需求中心待审核入口”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“测评需求审核”和“申请 18 / 已批 8”,状态为“正常”。\n2. 点击后进入或打开与“需求中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对测评需求审核的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留需求中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:测评需求审核;状态:正常;操作:点击卡片进入需求中心待审核入口 | 待执行\nTC-PROTO-0002 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 多渠道触达引擎 | 功能测试 | 管理员工作台查看渠道推送风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在退订率高于基线的推送风险模拟数据。 | 卡片=渠道推送风险;指标=IM、EDM、TEL、App Push 日周月风险与反馈;状态=偏高;目标页面=推送中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“渠道推送风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入推送风险复核”。\n5. 返回首页后再次查看该卡片是否 | 1. 卡片展示“渠道推送风险”和“IM、EDM、TEL、App Push 日周月风险与反馈”,状态为“偏高”。\n2. 点击后进入或打开与“推送中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对渠道推送风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留推送中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:渠道推送风险;状态:偏高;操作:点击卡片进入推送风险复核 | 待执行\nTC-PROTO-0003 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看新增诈骗事件卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在诈骗同步与黑名单待同步事件模拟数据。 | 卡片=新增诈骗事件;指标=昨 5 / 周 18;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“新增诈骗事件”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入风险中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“新增诈骗事件”和“昨 5 / 周 18”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对新增诈骗事件的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:新增诈骗事件;状态:需复核;操作:点击卡片进入风险中心 | 待执行\nTC-PROTO-0004 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看紧急 Listing卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在评分接近 4.2 的 Listing模拟数据。 | 卡片=紧急 Listing;指标=新 3 / 未处理 7;状态=紧急;目标页面=Listing 管理 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“紧急 Listing”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入紧急 Listing 策略”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“紧急 Listing”和“新 3 / 未处理 7”,状态为“紧急”。\n2. 点击后进入或打开与“Listing 管理”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对紧急 Listing的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留Listing 管理语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:紧急 Listing;状态:紧急;操作:点击卡片进入紧急 Listing 策略 | 待执行\nTC-PROTO-0005 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看推广计划与紧急策略卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已确认需求生成的推广计划模拟数据。 | 卡片=推广计划与紧急策略;指标=日 12 / 周 38;状态=注意审核积压;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“推广计划与紧急策略”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入计划中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“推广计划与紧急策略”和“日 12 / 周 38”,状态为“注意审核积压”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对推广计划与紧急策略的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:推广计划与紧急策略;状态:注意审核积压;操作:点击卡片进入计划中心 | 待执行\nTC-PROTO-0006 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 评价结果追踪 | 功能测试 | 管理员工作台查看评价产出趋势卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在真实消费者回评完成趋势模拟数据。 | 卡片=评价产出趋势;指标=日 18 / 周 96;状态=稳定;目标页面=数据中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“评价产出趋势”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入评价产出趋势”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“评价产出趋势”和“日 18 / 周 96”,状态为“稳定”。\n2. 点击后进入或打开与“数据中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对评价产出趋势的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留数据中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:评价产出趋势;状态:稳定;操作:点击卡片进入评价产出趋势 | 待执行\nTC-PROTO-0007 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看黑名单同步严重度卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在黑名单系统接口超时模拟数据。 | 卡片=黑名单同步严重度;指标=失败 2 / 高危 1;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“黑名单同步严重度”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入黑名单同步”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“黑名单同步严重度”和“失败 2 / 高危 1”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对黑名单同步严重度的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:黑名单同步严重度;状态:需复核;操作:点击卡片进入黑名单同步 | 待执行\nTC-PROTO-0008 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | KOC/KOL协作 | 功能测试 | 管理员工作台查看KOC/KOL 对接卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在PR 对外联系、价格、CODE、返点和提款进度模拟数据。 | 卡片=KOC/KOL 对接;指标=2 个逾期;状态=逾期;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“KOC/KOL 对接”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入对外合作跟进”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“KOC/KOL 对接”和“2 个逾期”,状态为“逾期”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对KOC/KOL 对接的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:KOC/KOL 对接;状态:逾期;操作:点击卡片进入对外合作跟进 | 待执行\nTC-PROTO-0009 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 客服工单与管理 | 功能测试 | 管理员工作台查看菲律宾团队管理卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在菲律宾团队工作时长、请假、缺席、人均产出模拟数据。 | 卡片=菲律宾团队管理;指标=风险 2 / 缺口 1;状态=排班风险;目标页面=客服中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“菲律宾团队管理”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入客服中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“菲律宾团队管理”和“风险 2 / 缺口 1”,状态为“排班风险”。\n2. 点击后进入或打开与“客服中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对菲律宾团队管理的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留客服中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:菲律宾团队管理;状态:排班风险;操作:点击卡片进入客服中心 | 待执行\nTC-PROTO-0010 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 审计与通知中心 | 功能测试 | 管理员工作台查看审核积压与风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已发现问题汇总到总页面模拟数据。 | 卡片=审核积压与风险;指标=卡点 4;状态=影响进度;目标页面=工作台 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“审核积压与风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入处理卡点”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“审核积压与风险”和“卡点 4”,状态为“影响进度”。\n2. 点击后进入或打开与“工作台”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对审核积压与风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留工作台语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:审核积压与风险;状态:影响进度;操作:点击卡片进入处理卡点 | 待执行\nTC-PROTO-0011 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对测评需求执行接收动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“测评需求”;当前环节为“Amazon 已批准”,负责人为“用户运营负责人”。 | 事项=测评需求;来源=飞书需求表单 DEMO-001;截止=今日 18:00;处理动作=接收;描述=评分 4.46,低于 4.5,需要生成用户互动与真实评价跟踪计划 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“测评需求”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“接收”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:测评需求已核对”。\n7. 点击确认提交。 | 1. 队列行展示测评需求、Amazon 已批准、用户运营负责人、今日 18:00。\n2. 详情弹窗打开,展示来源“飞书需求表单 DEMO-001”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:测评需求;操作:接收 | 待执行\nTC-PROTO-0012 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 测评需求处理意见为空时阻止提交 | P2 | 已打开“测评需求”详情弹窗;当前用户有“接收”权限。 | 动作类型=通过/确认;处理意见=空;事项=测评需求 | 1. 在“测评需求”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0013 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对昨日推送风险执行复核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“昨日推送风险”;当前环节为“待复核”,负责人为“用户运营组长”。 | 事项=昨日推送风险;来源=推送风险自动单 DEMO-006;截止=今日 12:00;处理动作=复核;描述=昨日推送退订率高于基线,需复核人群、素材和文案 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“昨日推送风险”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“复核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:昨日推送风险已核对”。\n7. 点击确认提交。 | 1. 队列行展示昨日推送风险、待复核、用户运营组长、今日 12:00。\n2. 详情弹窗打开,展示来源“推送风险自动单 DEMO-006”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:昨日推送风险;操作:复核 | 待执行\nTC-PROTO-0014 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 昨日推送风险处理意见为空时阻止提交 | P2 | 已打开“昨日推送风险”详情弹窗;当前用户有“复核”权限。 | 动作类型=通过/确认;处理意见=空;事项=昨日推送风险 | 1. 在“昨日推送风险”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0015 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对待同步黑名单执行审核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“待同步黑名单”;当前环节为“待审核”,负责人为“风险负责人”。 | 事项=待同步黑名单;来源=客服升级表单 DEMO-003;截止=今日 14:00;处理动作=审核;描述=同一 JOYHUB ID 与多个 Profile ID 关联异常样品申请,邮箱和设备号已脱敏 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“待同步黑名单”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:待同步黑名单已核对”。\n7. 点击确认提交。 | 1. 队列行展示待同步黑名单、待审核、风险负责人、今日 14:00。\n2. 详情弹窗打开,展示来源“客服升级表单 DEMO-003”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:待同步黑名单;操作:审核 | 待执行\nTC-PROTO-0016 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 待同步黑名单处理意见为空时阻止提交 | P2 | 已打开“待同步黑名单”详情弹窗;当前用户有“审核”权限。 | 动作类型=通过/确认;处理意见=空;事项=待同步黑名单 | 1. 在“待同步黑名单”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0017 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对紧急策略审批执行审批动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“紧急策略审批”;当前环节为“待系统管理员确认”,负责人为“Amazon 运营总监”。 | 事项=紧急策略审批;来源=紧急 Listing 表单 DEMO-004;截止=今日 11:30;处理动作=审批;描述=当前评分 4.21,接近 4.2 紧急阈值,需要 Amazon 与用户运营联合策略 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“紧急策略审批”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审批”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:紧急策略审批已核对”。\n7. 点击确认提交。 | 1. 队列行展示紧急策略审批、待系统管理员确认、Amazon 运营总监、今日 11:30。\n2. 详情弹窗打开,展示来源“紧急 Listing 表单 DEMO-004”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:紧急策略审批;操作:审批 | 待执行\nTC-PROTO-0018 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 紧急策略审批处理意见为空时阻止提交 | P2 | 已打开“紧急策略审批”详情弹窗;当前用户有“审批”权限。 | 动作类型=通过/确认;处理意见=空;事项=紧急策略审批 | 1. 在“紧急策略审批”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0019 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对差评跟进执行分配动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“差评跟进”;当前环节为“客服升级”,负责人为“客服负责人”。 | 事项=差评跟进;来源=飞书客服需求 DEMO-005;截止=明日 10:00;处理动作=分配;描述=用户反馈产品说明理解偏差,需要客服跟进并回传产品改进建议 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“差评跟进”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“分配”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:差评跟进已核对”。\n7. 点击确认提交。 | 1. 队列行展示差评跟进、客服升级、客服负责人、明日 10:00。\n2. 详情弹窗打开,展示来源“飞书客服需求 DEMO-005”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:差评跟进;操作:分配 | 待执行\nTC-PROTO-0020 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 差评跟进处理意见为空时阻止提交 | P2 | 已打开“差评跟进”详情弹窗;当前用户有“分配”权限。 | 动作类型=通过/确认;处理意见=空;事项=差评跟进 | 1. 在“差评跟进”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0021 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入需求中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“需求中心”;模拟数据已加载。 | 页面=需求中心;主按钮=Amazon 提交测评需求;辅助按钮=待审核入口;字段=需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 1. 在管理员首页左侧导航点击“需求中心”。\n2. 观察页面标题是否切换为“需求中心列表”。\n3. 检查列表表头是否包含:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作。\n4. 点击页面主按钮“Amazon 提交测评需求”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“待审核入口”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“需求中心”。\n2. 列表字段与原型定义一致。\n3. “Amazon 提交测评需求”和“待审核入口”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问需求中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合需求中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:需求中心;按钮:Amazon 提交测评需求/待审核入口;字段:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 待执行\nTC-PROTO-0022 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入Listing 管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“Listing 管理”;模拟数据已加载。 | 页面=Listing 管理;主按钮=创建紧急策略;辅助按钮=更多;字段=站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 1. 在管理员首页左侧导航点击“Listing 管理”。\n2. 观察页面标题是否切换为“Listing 管理列表”。\n3. 检查列表表头是否包含:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度。\n4. 点击页面主按钮“创建紧急策略”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“更多”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“Listing 管理”。\n2. 列表字段与原型定义一致。\n3. “创建紧急策略”和“更多”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问Listing 管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合Listing 管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:Listing 管理;按钮:创建紧急策略/更多;字段:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 待执行\nTC-PROTO-0023 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入计划中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“计划中心”;模拟数据已加载。 | 页面=计划中心;主按钮=生成计划;辅助按钮=批量审批;字段=计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 1. 在管理员首页左侧导航点击“计划中心”。\n2. 观察页面标题是否切换为“计划中心列表”。\n3. 检查列表表头是否包含:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人。\n4. 点击页面主按钮“生成计划”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“批量审批”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“计划中心”。\n2. 列表字段与原型定义一致。\n3. “生成计划”和“批量审批”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问计划中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合计划中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:计划中心;按钮:生成计划/批量审批;字段:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 待执行\nTC-PROTO-0024 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入推送中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“推送中心”;模拟数据已加载。 | 页面=推送中心;主按钮=计划与推送分配;辅助按钮=风险复核;字段=推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 1. 在管理员首页左侧导航点击“推送中心”。\n2. 观察页面标题是否切换为“推送中心列表”。\n3. 检查列表表头是否包含:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订。\n4. 点击页面主按钮“计划与推送分配”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“风险复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“推送中心”。\n2. 列表字段与原型定义一致。\n3. “计划与推送分配”和“风险复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问推送中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合推送中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:推送中心;按钮:计划与推送分配/风险复核;字段:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 待执行\nTC-PROTO-0025 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入客服中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“客服中心”;模拟数据已加载。 | 页面=客服中心;主按钮=分配工单;辅助按钮=流转;字段=工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 1. 在管理员首页左侧导航点击“客服中心”。\n2. 观察页面标题是否切换为“客服中心列表”。\n3. 检查列表表头是否包含:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出。\n4. 点击页面主按钮“分配工单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“流转”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“客服中心”。\n2. 列表字段与原型定义一致。\n3. “分配工单”和“流转”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问客服中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合客服中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:客服中心;按钮:分配工单/流转;字段:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 待执行\nTC-PROTO-0026 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入风险中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“风险中心”;模拟数据已加载。 | 页面=风险中心;主按钮=同步黑名单;辅助按钮=规则复核;字段=事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“风险中心”。\n2. 观察页面标题是否切换为“风险中心列表”。\n3. 检查列表表头是否包含:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“同步黑名单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“规则复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“风险中心”。\n2. 列表字段与原型定义一致。\n3. “同步黑名单”和“规则复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问风险中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合风险中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:风险中心;按钮:同步黑名单/规则复核;字段:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0027 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 数据中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入数据中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“数据中心”;模拟数据已加载。 | 页面=数据中心;主按钮=立即同步;辅助按钮=导出;字段=来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“数据中心”。\n2. 观察页面标题是否切换为“数据中心列表”。\n3. 检查列表表头是否包含:来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“立即同步”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“导出”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“数据中心”。\n2. 列表字段与原型定义一致。\n3. “立即同步”和“导出”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问数据中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合数据中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:数据中心;按钮:立即同步/导出;字段:来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0028 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入报表中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“报表中心”;模拟数据已加载。 | 页面=报表中心;主按钮=生成/下载报表;辅助按钮=上传记录;字段=报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 1. 在管理员首页左侧导航点击“报表中心”。\n2. 观察页面标题是否切换为“报表中心列表”。\n3. 检查列表表头是否包含:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏。\n4. 点击页面主按钮“生成/下载报表”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“上传记录”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“报表中心”。\n2. 列表字段与原型定义一致。\n3. “生成/下载报表”和“上传记录”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问报表中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合报表中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:报表中心;按钮:生成/下载报表/上传记录;字段:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 待执行\nTC-PROTO-0029 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入系统管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“系统管理”;模拟数据已加载。 | 页面=系统管理;主按钮=新建账号;辅助按钮=离职管理;字段=配置ID、模块、说明、权限分配、审计日志 | 1. 在管理员首页左侧导航点击“系统管理”。\n2. 观察页面标题是否切换为“系统管理列表”。\n3. 检查列表表头是否包含:配置ID、模块、说明、权限分配、审计日志。\n4. 点击页面主按钮“新建账号”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“离职管理”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“系统管理”。\n2. 列表字段与原型定义一致。\n3. “新建账号”和“离职管理”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问系统管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合系统管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:系统管理;按钮:新建账号/离职管理;字段:配置ID、模块、说明、权限分配、审计日志 | 待执行\nTC-PROTO-0030 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 7 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 7 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 7 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 7 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0031 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 30 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 30 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 30 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 30 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0032 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换本月时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=本月;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“本月”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“本月”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0033 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换自定义时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=自定义;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“自定义”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“自定义”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0034 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面用户管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“用户管理”。 | 现有页面=用户中心 / 用户;字段=JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍;查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;MVP用途=用户画像筛选、推送人群、客服定位、风险排查 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“用户管理”。\n3. 核对现有页面名称是否为“用户中心 / 用户”。\n4. 展开字段详情,逐项核对字段:JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍。\n5. 在查询条件区按“搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “用户管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:用户管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0035 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 异常场景 | 用户管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“用户管理”关系页面;用户有查询权限。 | 查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;输入值=不存在的标签/身份/国家组合 | 1. 在“用户管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0036 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面公域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-用户标签”。 | 现有页面=标签 / 公域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态;查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;MVP用途=公域人群圈选、覆盖人数评估、推送前过滤 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 公域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态。\n5. 在查询条件区按“搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0037 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 异常场景 | 公域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-用户标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0038 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 功能测试 | 现有ERP页面公域-产品标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-产品标签”。 | 现有页面=标签 / 公域产品;字段=标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人;查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;MVP用途=产品分层、Listing 健康策略、产品绑定率分析 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-产品标签”。\n3. 核对现有页面名称是否为“标签 / 公域产品”。\n4. 展开字段详情,逐项核对字段:标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人。\n5. 在查询条件区按“搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-产品标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-产品标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0039 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 异常场景 | 公域-产品标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-产品标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-产品标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0040 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面私域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“私域-用户标签”。 | 现有页面=标签 / 私域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态;查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;MVP用途=私域精细运营、客服分组、活动复盘、风险用户隔离 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“私域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 私域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态。\n5. 在查询条件区按“标签分类、打标方式、覆盖人数、状态、时间范围”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “私域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:私域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0041 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 异常场景 | 私域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“私域-用户标签”关系页面;用户有查询权限。 | 查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;输入值=不存在的标签/身份/国家组合 | 1. 在“私域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0042 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面身份管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“身份管理”。 | 现有页面=身份;字段=账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作;查询条件=身份名称、身份分组、状态、更新时间;MVP用途=识别官方、品牌、达人、风险、客服等用户身份 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“身份管理”。\n3. 核对现有页面名称是否为“身份”。\n4. 展开字段详情,逐项核对字段:账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作。\n5. 在查询条件区按“身份名称、身份分组、状态、更新时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “身份管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:身份管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0043 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 异常场景 | 身份管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“身份管理”关系页面;用户有查询权限。 | 查询条件=身份名称、身份分组、状态、更新时间;输入值=不存在的标签/身份/国家组合 | 1. 在“身份管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0044 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 用户标签关系用于人群圈选、用户画像、风险过滤 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 用户标签;关系类型=多对多;用途=人群圈选、用户画像、风险过滤 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 用户标签”。\n4. 查看关系类型是否显示为“多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“人群圈选、用户画像、风险过滤”。 | 1. 关系“用户 - 用户标签”存在。\n2. 类型展示为“多对多”。\n3. 详情能说明该关系服务于“人群圈选、用户画像、风险过滤”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 用户标签 | 待执行\nTC-PROTO-0045 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 身份关系用于官方、品牌、达人、客服、风险身份识别 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 身份;关系类型=待确认:一对多或多对多;用途=官方、品牌、达人、客服、风险身份识别 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 身份”。\n4. 查看关系类型是否显示为“待确认:一对多或多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“官方、品牌、达人、客服、风险身份识别”。 | 1. 关系“用户 - 身份”存在。\n2. 类型展示为“待确认:一对多或多对多”。\n3. 详情能说明该关系服务于“官方、品牌、达人、客服、风险身份识别”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 身份 | 待执行\nTC-PROTO-0046 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 产品关系用于测评用户池、客服定位 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 产品;关系类型=绑定/连接产品;用途=测评用户池、客服定位 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 产品”。\n4. 查看关系类型是否显示为“绑定/连接产品”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“测评用户池、客服定位”。 | 1. 关系“用户 - 产品”存在。\n2. 类型展示为“绑定/连接产品”。\n3. 详情能说明该关系服务于“测评用户池、客服定位”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 产品 | 待执行\nTC-PROTO-0047 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验产品 - 产品标签 - Listing关系用于Listing 健康策略、ASIN 归因 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=产品 - 产品标签 - Listing;关系类型=产品分层;用途=Listing 健康策略、ASIN 归因 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“产品 - 产品标签 - Listing”。\n4. 查看关系类型是否显示为“产品分层”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“Listing 健康策略、ASIN 归因”。 | 1. 关系“产品 - 产品标签 - Listing”存在。\n2. 类型展示为“产品分层”。\n3. 详情能说明该关系服务于“Listing 健康策略、ASIN 归因”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:产品 - 产品标签 - Listing | 待执行\nTC-PROTO-0048 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 活动关系用于KOC/KOL、私域运营沉淀 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 活动;关系类型=活动参与;用途=KOC/KOL、私域运营沉淀 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 活动”。\n4. 查看关系类型是否显示为“活动参与”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“KOC/KOL、私域运营沉淀”。 | 1. 关系“用户 - 活动”存在。\n2. 类型展示为“活动参与”。\n3. 详情能说明该关系服务于“KOC/KOL、私域运营沉淀”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 活动 | 待执行\nTC-PROTO-0049 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 推送关系用于IM/EDM/TEL/App Push 频控、点击、回复、退订 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 推送;关系类型=一对多;用途=IM/EDM/TEL/App Push 频控、点击、回复、退订 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 推送”。\n4. 查看关系类型是否显示为“一对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“IM/EDM/TEL/App Push 频控、点击、回复、退订”。 | 1. 关系“用户 - 推送”存在。\n2. 类型展示为“一对多”。\n3. 详情能说明该关系服务于“IM/EDM/TEL/App Push 频控、点击、回复、退订”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 推送 | 待执行\nTC-PROTO-0050 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 风险/黑名单关系用于诈骗同步、客服升级、风险用户隔离 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 风险/黑名单;关系类型=风险隔离;用途=诈骗同步、客服升级、风险用户隔离 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 风险/黑名单”。\n4. 查看关系类型是否显示为“风险隔离”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“诈骗同步、客服升级、风险用户隔离”。 | 1. 关系“用户 - 风险/黑名单”存在。\n2. 类型展示为“风险隔离”。\n3. 详情能说明该关系服务于“诈骗同步、客服升级、风险用户隔离”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 风险/黑名单 | 待执行\nTC-PROTO-0051 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行用户主档查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=用户主档查询;查询条件=JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间;预期输出=用户主档、标签、身份、产品关系、近期活跃;使用页面=用户中心 / 客服中心 / 风险中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“用户主档查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间。\n5. 点击查询。\n6. 查看结果区是否输出:用户主档、标签、身份、产品关系、近期活跃。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “用户主档查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“用户主档、标签、身份、产品关系、近期活跃”。\n4. 跳转到“用户中心 / 客服中心 / 风险中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:用户主档查询 | 待执行\nTC-PROTO-0052 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行推送前人群圈选并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=推送前人群圈选;查询条件=标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数;预期输出=候选用户数、预计触达、频控风险、可保存人群包;使用页面=计划中心 / 推送中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“推送前人群圈选”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数。\n5. 点击查询。\n6. 查看结果区是否输出:候选用户数、预计触达、频控风险、可保存人群包。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “推送前人群圈选”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“候选用户数、预计触达、频控风险、可保存人群包”。\n4. 跳转到“计划中心 / 推送中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:推送前人群圈选 | 待执行\nTC-PROTO-0053 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行测评与真实评价跟踪人群并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=测评与真实评价跟踪人群;查询条件=ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除;预期输出=推荐用户池、排除用户池、进入计划中心;使用页面=需求中心 / 计划中心 / 客服中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“测评与真实评价跟踪人群”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除。\n5. 点击查询。\n6. 查看结果区是否输出:推荐用户池、排除用户池、进入计划中心。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “测评与真实评价跟踪人群”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“推荐用户池、排除用户池、进入计划中心”。\n4. 跳转到“需求中心 / 计划中心 / 客服中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:测评与真实评价跟踪人群 | 待执行\nTC-PROTO-0054 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行标签覆盖查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=标签覆盖查询;查询条件=标签分类、打标方式、覆盖人数、最新打标时间、状态;预期输出=标签列表、覆盖趋势、异常覆盖提示;使用页面=现有ERP / 数据中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“标签覆盖查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签分类、打标方式、覆盖人数、最新打标时间、状态。\n5. 点击查询。\n6. 查看结果区是否输出:标签列表、覆盖趋势、异常覆盖提示。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “标签覆盖查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“标签列表、覆盖趋势、异常覆盖提示”。\n4. 跳转到“现有ERP / 数据中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:标签覆盖查询 | 待执行\nTC-PROTO-0055 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行身份风险查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=身份风险查询;查询条件=身份名称、身份分组、风险等级、状态;预期输出=风险用户池、客服/推送排除名单、黑名单同步候选;使用页面=风险中心 / 系统管理 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“身份风险查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:身份名称、身份分组、风险等级、状态。\n5. 点击查询。\n6. 查看结果区是否输出:风险用户池、客服/推送排除名单、黑名单同步候选。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “身份风险查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“风险用户池、客服/推送排除名单、黑名单同步候选”。\n4. 跳转到“风险中心 / 系统管理”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:身份风险查询 | 待执行\nTC-PROTO-0056 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行新建账号并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“新建账号”。 | 动作=新建账号;说明=按部门、角色、站点、数据范围开通账号;预期=账号创建后出现在系统管理列表 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“新建账号”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:按部门、角色、站点、数据范围开通账号。\n6. 提交后进入审计日志,按动作类型筛选“新建账号”。 | 1. 有权限角色可完成“新建账号”。\n2. 执行结果符合“账号创建后出现在系统管理列表”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 新建账号仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:新建账号;权限分配;审计日志 | 待执行\nTC-PROTO-0057 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行离职管理并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“离职管理”。 | 动作=离职管理;说明=停用账号、交接任务、回收敏感权限;预期=离职账号不可登录且任务已交接 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“离职管理”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:停用账号、交接任务、回收敏感权限。\n6. 提交后进入审计日志,按动作类型筛选“离职管理”。 | 1. 有权限角色可完成“离职管理”。\n2. 执行结果符合“离职账号不可登录且任务已交接”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 离职管理仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:离职管理;权限分配;审计日志 | 待执行\nTC-PROTO-0058 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行权限分配并校验权限与审计 | P1 | 用户以系统管理员/负责人身份登录;系统资产模块可访问;系统管理列表包含“权限分配”。 | 动作=权限分配;说明=导出、审批、查看敏感信息、黑名单同步独立授权;预期=权限粒度按按钮和数据范围生效 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“权限分配”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、审批、查看敏感信息、黑名单同步独立授权。\n6. 提交后进入审计日志,按动作类型筛选“权限分配”。 | 1. 有权限角色可完成“权限分配”。\n2. 执行结果符合“权限粒度按按钮和数据范围生效”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 权限分配仅对系统管理员/负责人开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:权限分配;权限分配;审计日志 | 待执行\nTC-PROTO-0059 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行审计日志并校验权限与审计 | P1 | 用户以系统管理员/审计角色身份登录;系统资产模块可访问;系统管理列表包含“审计日志”。 | 动作=审计日志;说明=导出、查看敏感信息、黑名单同步、审批动作;预期=每个敏感动作有日志ID和操作者 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“审计日志”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、查看敏感信息、黑名单同步、审批动作。\n6. 提交后进入审计日志,按动作类型筛选“审计日志”。 | 1. 有权限角色可完成“审计日志”。\n2. 执行结果符合“每个敏感动作有日志ID和操作者”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 审计日志仅对系统管理员/审计角色开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:审计日志;权限分配;审计日志 | 待执行\nTC-PROTO-0060 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看在线客服数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=在线客服数;原型变量=onlineAgents 人;动作=查看当前可用客服池 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“在线客服数”。\n3. 核对指标值单位是否符合“onlineAgents 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“在线客服数”显示在客服执行看板。\n2. 点击后进入与“查看当前可用客服池”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:onlineAgents 人;标题:客服执行看板 | 待执行\nTC-PROTO-0061 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看今日工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日工单;原型变量=todayTickets 单;动作=查看今日进入客服中心的工单总量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日工单”。\n3. 核对指标值单位是否符合“todayTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日工单”显示在客服执行看板。\n2. 点击后进入与“查看今日进入客服中心的工单总量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0062 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看待处理工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=待处理工单;原型变量=pendingTickets 单;动作=查看未关闭/待处理队列 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“待处理工单”。\n3. 核对指标值单位是否符合“pendingTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“待处理工单”显示在客服执行看板。\n2. 点击后进入与“查看未关闭/待处理队列”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:pendingTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0063 | 客服执行.html | 客服执行看板 | 多渠道触达引擎 | 功能测试 | 客服执行看板查看今日消息指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日消息;原型变量=todayMessages 条;动作=查看客服发送和接收消息量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日消息”。\n3. 核对指标值单位是否符合“todayMessages 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日消息”显示在客服执行看板。\n2. 点击后进入与“查看客服发送和接收消息量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayMessages 条;标题:客服执行看板 | 待执行\nTC-PROTO-0064 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看今日获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日获取评价;原型变量=todayReviews 条;动作=查看今日客服转化评价数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日获取评价”。\n3. 核对指标值单位是否符合“todayReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日获取评价”显示在客服执行看板。\n2. 点击后进入与“查看今日客服转化评价数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0065 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看本月获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=本月获取评价;原型变量=monthReviews 条;动作=查看月度评价产出 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“本月获取评价”。\n3. 核对指标值单位是否符合“monthReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“本月获取评价”显示在客服执行看板。\n2. 点击后进入与“查看月度评价产出”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:monthReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0066 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看排班到岗指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=排班到岗;原型变量=实际/应到 人;动作=查看排班、在线状态和出勤异常 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“排班到岗”。\n3. 核对指标值单位是否符合“实际/应到 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“排班到岗”显示在客服执行看板。\n2. 点击后进入与“查看排班、在线状态和出勤异常”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:实际/应到 人;标题:客服执行看板 | 待执行\nTC-PROTO-0067 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看迟到次数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=迟到次数;原型变量=lateDays 次;动作=查看迟到/早退/请假/缺勤 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“迟到次数”。\n3. 核对指标值单位是否符合“lateDays 次”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“迟到次数”显示在客服执行看板。\n2. 点击后进入与“查看迟到/早退/请假/缺勤”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:lateDays 次;标题:客服执行看板 | 待执行\nTC-PROTO-0068 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看回复用户数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=回复用户数;原型变量=repliedUsers 人;动作=查看回复覆盖用户数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“回复用户数”。\n3. 核对指标值单位是否符合“repliedUsers 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“回复用户数”显示在客服执行看板。\n2. 点击后进入与“查看回复覆盖用户数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:repliedUsers 人;标题:客服执行看板 | 待执行\nTC-PROTO-0069 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看处理工单数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=处理工单数;原型变量=ticketCount 单;动作=查看处理工单量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“处理工单数”。\n3. 核对指标值单位是否符合“ticketCount 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“处理工单数”显示在客服执行看板。\n2. 点击后进入与“查看处理工单量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:ticketCount 单;标题:客服执行看板 | 待执行\nTC-PROTO-0070 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看发送消息数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=发送消息数;原型变量=messageCount 条;动作=查看消息发送量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“发送消息数”。\n3. 核对指标值单位是否符合“messageCount 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“发送消息数”显示在客服执行看板。\n2. 点击后进入与“查看消息发送量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:messageCount 条;标题:客服执行看板 | 待执行\nTC-PROTO-0071 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看平均首次回复指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=平均首次回复;原型变量=averageFirstReply 分钟;动作=查看平均首次回复时长 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“平均首次回复”。\n3. 核对指标值单位是否符合“averageFirstReply 分钟”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“平均首次回复”显示在客服执行看板。\n2. 点击后进入与“查看平均首次回复时长”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:averageFirstReply 分钟;标题:客服执行看板 | 待执行\nTC-PROTO-0072 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看RSO/RDO评价数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=RSO/RDO评价数;原型变量=rsoReviews+rdoReviews 条;动作=查看回评/测评转化 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“RSO/RDO评价数”。\n3. 核对指标值单位是否符合“rsoReviews+rdoReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“RSO/RDO评价数”显示在客服执行看板。\n2. 点击后进入与“查看回评/测评转化”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:rsoReviews+rdoReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0073 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看目标完成率指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=目标完成率;原型变量=totalRate;动作=查看目标完成进度 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“目标完成率”。\n3. 核对指标值单位是否符合“totalRate”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“目标完成率”显示在客服执行看板。\n2. 点击后进入与“查看目标完成进度”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:totalRate;标题:客服执行看板 | 待执行\nTC-PROTO-0074 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户消息进入工单从待分配执行自动分配流转到已分配 | P1 | 客服系统存在来源为“用户消息进入”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=用户消息进入;起始状态=待分配;操作=自动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户消息进入”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“自动分配”:在线客服池按班次+在线状态+当前负载+最大工单数分配。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户消息进入”和当前状态“待分配”。\n2. 执行“自动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0075 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 推送转人工工单从待分配执行手动分配流转到已分配 | P1 | 客服系统存在来源为“推送转人工”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=推送转人工;起始状态=待分配;操作=手动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“推送转人工”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“手动分配”:组长选择在线且未满载客服。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“推送转人工”和当前状态“待分配”。\n2. 执行“手动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0076 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 售后触发工单从已分配执行首次回复流转到处理中 | P1 | 客服系统存在来源为“售后触发”的工单;当前状态为“已分配”;用户上下文卡可查询或降级展示。 | 来源=售后触发;起始状态=已分配;操作=首次回复;目标状态=处理中 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“售后触发”。\n3. 打开状态为“已分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“首次回复”:客服查看用户上下文后发送第一条回复。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“售后触发”和当前状态“已分配”。\n2. 执行“首次回复”后状态变为“处理中”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0077 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 风险触发工单从处理中执行标记疑似诈骗流转到疑似诈骗 | P1 | 客服系统存在来源为“风险触发”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=风险触发;起始状态=处理中;操作=标记疑似诈骗;目标状态=疑似诈骗 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“风险触发”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“标记疑似诈骗”:客服在工单处理结果中选择疑似诈骗。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“风险触发”和当前状态“处理中”。\n2. 执行“标记疑似诈骗”后状态变为“疑似诈骗”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0078 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 电话后续工单从处理中执行等待用户回复流转到等待用户 | P1 | 客服系统存在来源为“电话后续”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=电话后续;起始状态=处理中;操作=等待用户回复;目标状态=等待用户 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“电话后续”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“等待用户回复”:客服记录通话后等待用户补充订单号。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“电话后续”和当前状态“处理中”。\n2. 执行“等待用户回复”后状态变为“等待用户”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0079 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户答应配合工单从处理中执行创建跟进任务流转到待提醒 | P1 | 客服系统存在来源为“用户答应配合”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=用户答应配合;起始状态=处理中;操作=创建跟进任务;目标状态=待提醒 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户答应配合”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“创建跟进任务”:客服将答应配合状态置为已答应并设置负责人。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户答应配合”和当前状态“处理中”。\n2. 执行“创建跟进任务”后状态变为“待提醒”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0080 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 等待提交工单从待提醒执行提醒用户流转到等待提交 | P1 | 客服系统存在来源为“等待提交”的工单;当前状态为“待提醒”;用户上下文卡可查询或降级展示。 | 来源=等待提交;起始状态=待提醒;操作=提醒用户;目标状态=等待提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“等待提交”。\n3. 打开状态为“待提醒”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“提醒用户”:到期前客服发送提醒消息。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“等待提交”和当前状态“待提醒”。\n2. 执行“提醒用户”后状态变为“等待提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0081 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户提交评价工单从等待提交执行登记提交事实流转到已提交 | P1 | 客服系统存在来源为“用户提交评价”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=用户提交评价;起始状态=等待提交;操作=登记提交事实;目标状态=已提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户提交评价”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“登记提交事实”:客服上传截图/链接并关联计划和ASIN。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户提交评价”和当前状态“等待提交”。\n2. 执行“登记提交事实”后状态变为“已提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0082 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 评价核验完成工单从已提交执行关闭工单流转到已关闭 | P1 | 客服系统存在来源为“评价核验完成”的工单;当前状态为“已提交”;用户上下文卡可查询或降级展示。 | 来源=评价核验完成;起始状态=已提交;操作=关闭工单;目标状态=已关闭 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“评价核验完成”。\n3. 打开状态为“已提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“关闭工单”:评价展示确认后客服将工单置为已解决并关闭。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“评价核验完成”和当前状态“已提交”。\n2. 执行“关闭工单”后状态变为“已关闭”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0083 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 超时未提交工单从等待提交执行需再次联系流转到需再次联系 | P1 | 客服系统存在来源为“超时未提交”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=超时未提交;起始状态=等待提交;操作=需再次联系;目标状态=需再次联系 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“超时未提交”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“需再次联系”:超过答应配合期限后生成再次联系任务。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“超时未提交”和当前状态“等待提交”。\n2. 执行“需再次联系”后状态变为“需再次联系”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0084 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:自动分配无在线客服 | P2 | 客服执行看板可用;准备异常条件:所有排班客服均离线或满载。 | 异常=自动分配无在线客服;条件=所有排班客服均离线或满载 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:所有排班客服均离线或满载。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“自动分配无在线客服”。\n2. 处理结果为:工单留在公共池并提醒组长。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:自动分配无在线客服 | 待执行\nTC-PROTO-0085 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:分配给离线客服 | P2 | 客服执行看板可用;准备异常条件:客服在线状态在分配前变为离线。 | 异常=分配给离线客服;条件=客服在线状态在分配前变为离线 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服在线状态在分配前变为离线。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“分配给离线客服”。\n2. 处理结果为:阻止分配并要求重新选择。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:分配给离线客服 | 待执行\nTC-PROTO-0086 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:当前负载超过最大工单数 | P2 | 客服执行看板可用;准备异常条件:客服未关闭工单数达到上限。 | 异常=当前负载超过最大工单数;条件=客服未关闭工单数达到上限 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服未关闭工单数达到上限。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“当前负载超过最大工单数”。\n2. 处理结果为:自动跳过该客服。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:当前负载超过最大工单数 | 待执行\nTC-PROTO-0087 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:用户上下文卡查询失败 | P2 | 客服执行看板可用;准备异常条件:identity 服务超时。 | 异常=用户上下文卡查询失败;条件=identity 服务超时 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:identity 服务超时。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“用户上下文卡查询失败”。\n2. 处理结果为:工单可继续处理但显示上下文数据可能过期。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:用户上下文卡查询失败 | 待执行\nTC-PROTO-0088 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:首次回复为空 | P2 | 客服执行看板可用;准备异常条件:客服点击发送但消息内容为空。 | 异常=首次回复为空;条件=客服点击发送但消息内容为空 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服点击发送但消息内容为空。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“首次回复为空”。\n2. 处理结果为:阻止发送并提示请输入回复内容。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:首次回复为空 | 待执行\nTC-PROTO-0089 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:关闭工单未选择处理结果 | P2 | 客服执行看板可用;准备异常条件:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。 | 异常=关闭工单未选择处理结果;条件=点击关闭但未选择已解决/拒绝/疑似诈骗等结果 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“关闭工单未选择处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:关闭工单未选择处理结果 | 待执行\nTC-PROTO-0090 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:登记评价缺少证据 | P2 | 客服执行看板可用;准备异常条件:用户声称已评价但未上传截图或链接。 | 异常=登记评价缺少证据;条件=用户声称已评价但未上传截图或链接 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:用户声称已评价但未上传截图或链接。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“登记评价缺少证据”。\n2. 处理结果为:不允许进入已提交状态。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:登记评价缺少证据 | 待执行\nTC-PROTO-0091 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:答应配合任务超期 | P2 | 客服执行看板可用;准备异常条件:deadline_at 已过且无提交记录。 | 异常=答应配合任务超期;条件=deadline_at 已过且无提交记录 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:deadline_at 已过且无提交记录。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“答应配合任务超期”。\n2. 处理结果为:状态变为超时并生成需再次联系。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:答应配合任务超期 | 待执行\nTC-PROTO-0092 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:风险状态确认诈骗 | P2 | 客服执行看板可用;准备异常条件:risk 返回确认诈骗。 | 异常=风险状态确认诈骗;条件=risk 返回确认诈骗 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:risk 返回确认诈骗。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“风险状态确认诈骗”。\n2. 处理结果为:工单自动或人工确认后关闭并同步黑名单候选。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:风险状态确认诈骗 | 待执行\nTC-PROTO-0093 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:重复创建同用户打开工单 | P2 | 客服执行看板可用;准备异常条件:同 person_id 已存在 open 工单。 | 异常=重复创建同用户打开工单;条件=同 person_id 已存在 open 工单 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:同 person_id 已存在 open 工单。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“重复创建同用户打开工单”。\n2. 处理结果为:新工单关联已有工单或提示合并。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:重复创建同用户打开工单 | 待执行\nTC-PROTO-0094 | 用户运营系统-单文件.html | 工作台 | 系统总览 | UI/交互测试 | 单文件系统路由#/dashboard进入Dashboard页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有Dashboard访问权限。 | 路由=#/dashboard;页面=Dashboard;用途=查看经营指标、待办、风险、评价进度 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/dashboard”或从侧边菜单点击“Dashboard”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“Dashboard”页面。 | 1. 页面成功进入“Dashboard”。\n2. 当前菜单高亮,页面内容与“查看经营指标、待办、风险、评价进度”一致。\n3. 刷新后 hash 路由不丢失,仍展示“Dashboard”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备Dashboard访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/dashboard | 待执行\nTC-PROTO-0095 | 用户运营系统-单文件.html | 需求管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/demand进入需求中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有需求中心访问权限。 | 路由=#/demand;页面=需求中心;用途=创建/评估/驳回/转计划 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/demand”或从侧边菜单点击“需求中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“需求中心”页面。 | 1. 页面成功进入“需求中心”。\n2. 当前菜单高亮,页面内容与“创建/评估/驳回/转计划”一致。\n3. 刷新后 hash 路由不丢失,仍展示“需求中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备需求中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/demand | 待执行\nTC-PROTO-0096 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan/review进入计划审核页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划审核访问权限。 | 路由=#/plan/review;页面=计划审核;用途=提交审批、通过、驳回、待补充 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan/review”或从侧边菜单点击“计划审核”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划审核”页面。 | 1. 页面成功进入“计划审核”。\n2. 当前菜单高亮,页面内容与“提交审批、通过、驳回、待补充”一致。\n3. 刷新后 hash 路由不丢失,仍展示“计划审核”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划审核访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan/review | 待执行\nTC-PROTO-0097 | 用户运营系统-单文件.html | 计划管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan进入计划中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划中心访问权限。 | 路由=#/plan;页面=计划中心;用途=生成计划、拆分计划项、执行中/暂停/终止 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan”或从侧边菜单点击“计划中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划中心”页面。 | 1. 页面成功进入“计划中心”。\n2. 当前菜单高亮,页面内容与“生成计划、拆分计划项、执行中/暂停/终止”一致。\n3. 刷新后 hash 路由不丢失,仍展示“计划中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan | 待执行\nTC-PROTO-0098 | 用户运营系统-单文件.html | Listing健康 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/asin进入ASIN/Listing页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有ASIN/Listing访问权限。 | 路由=#/asin;页面=ASIN/Listing;用途=查看评分、评价数、健康状态、紧急策略 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/asin”或从侧边菜单点击“ASIN/Listing”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“ASIN/Listing”页面。 | 1. 页面成功进入“ASIN/Listing”。\n2. 当前菜单高亮,页面内容与“查看评分、评价数、健康状态、紧急策略”一致。\n3. 刷新后 hash 路由不丢失,仍展示“ASIN/Listing”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备ASIN/Listing访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/asin | 待执行\nTC-PROTO-0099 | 用户运营系统-单文件.html | 用户上下文 | 用户身份与上下文 | UI/交互测试 | 单文件系统路由#/user进入用户中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有用户中心访问权限。 | 路由=#/user;页面=用户中心;用途=用户主档、标签、身份、产品、活动、触达历史 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/user”或从侧边菜单点击“用户中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“用户中心”页面。 | 1. 页面成功进入“用户中心”。\n2. 当前菜单高亮,页面内容与“用户主档、标签、身份、产品、活动、触达历史”一致。\n3. 刷新后 hash 路由不丢失,仍展示“用户中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备用户中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/user | 待执行\nTC-PROTO-0100 | 用户运营系统-单文件.html | 额度管理 | 额度与频控 | UI/交互测试 | 单文件系统路由#/quota进入额度频控页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有额度频控访问权限。 | 路由=#/quota;页面=额度频控;用途=额度查询、预占、确认、释放、终校 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/quota”或从侧边菜单点击“额度频控”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“额度频控”页面。 | 1. 页面成功进入“额度频控”。\n2. 当前菜单高亮,页面内容与“额度查询、预占、确认、释放、终校”一致。\n3. 刷新后 hash 路由不丢失,仍展示“额度频控”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备额度频控访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/quota | 待执行\nTC-PROTO-0101 | 用户运营系统-单文件.html | 多渠道触达 | 多渠道触达引擎 | UI/交互测试 | 单文件系统路由#/outreach进入推送/触达页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有推送/触达访问权限。 | 路由=#/outreach;页面=推送/触达;用途=IM/EDM/APP/TEL 路由、去重、发送、追踪 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/outreach”或从侧边菜单点击“推送/触达”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“推送/触达”页面。 | 1. 页面成功进入“推送/触达”。\n2. 当前菜单高亮,页面内容与“IM/EDM/APP/TEL 路由、去重、发送、追踪”一致。\n3. 刷新后 hash 路由不丢失,仍展示“推送/触达”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备推送/触达访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/outreach | 待执行\nTC-PROTO-0102 | 用户运营系统-单文件.html | 工单管理 | 客服工单与管理 | UI/交互测试 | 单文件系统路由#/support进入客服中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有客服中心访问权限。 | 路由=#/support;页面=客服中心;用途=工单创建、分配、处理、跟进 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/support”或从侧边菜单点击“客服中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“客服中心”页面。 | 1. 页面成功进入“客服中心”。\n2. 当前菜单高亮,页面内容与“工单创建、分配、处理、跟进”一致。\n3. 刷新后 hash 路由不丢失,仍展示“客服中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备客服中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/support | 待执行\nTC-PROTO-0103 | 用户运营系统-单文件.html | 风险反欺诈 | 风险与反欺诈 | UI/交互测试 | 单文件系统路由#/risk进入风险中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有风险中心访问权限。 | 路由=#/risk;页面=风险中心;用途=风险信号、强弱关联、黑名单、复核 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/risk”或从侧边菜单点击“风险中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“风险中心”页面。 | 1. 页面成功进入“风险中心”。\n2. 当前菜单高亮,页面内容与“风险信号、强弱关联、黑名单、复核”一致。\n3. 刷新后 hash 路由不丢失,仍展示“风险中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备风险中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/risk | 待执行\nTC-PROTO-0104 | 用户运营系统-单文件.html | 评价结果 | 评价结果追踪 | UI/交互测试 | 单文件系统路由#/review进入评价追踪页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有评价追踪访问权限。 | 路由=#/review;页面=评价追踪;用途=提交记录、Amazon展示核验、异常观察 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/review”或从侧边菜单点击“评价追踪”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“评价追踪”页面。 | 1. 页面成功进入“评价追踪”。\n2. 当前菜单高亮,页面内容与“提交记录、Amazon展示核验、异常观察”一致。\n3. 刷新后 hash 路由不丢失,仍展示“评价追踪”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备评价追踪访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/review | 待执行\nTC-PROTO-0105 | 用户运营系统-单文件.html | 达人协作 | KOC/KOL协作 | UI/交互测试 | 单文件系统路由#/creator进入KOC/KOL页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有KOC/KOL访问权限。 | 路由=#/creator;页面=KOC/KOL;用途=免评计划、内容、CODE、JOYCOLLAB同步 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/creator”或从侧边菜单点击“KOC/KOL”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“KOC/KOL”页面。 | 1. 页面成功进入“KOC/KOL”。\n2. 当前菜单高亮,页面内容与“免评计划、内容、CODE、JOYCOLLAB同步”一致。\n3. 刷新后 hash 路由不丢失,仍展示“KOC/KOL”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备KOC/KOL访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/creator | 待执行\nTC-PROTO-0106 | 用户运营系统-单文件.html | 审计与通知 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/audit进入审计通知页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有审计通知访问权限。 | 路由=#/audit;页面=审计通知;用途=状态变更、敏感访问、通知告警 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/audit”或从侧边菜单点击“审计通知”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“审计通知”页面。 | 1. 页面成功进入“审计通知”。\n2. 当前菜单高亮,页面内容与“状态变更、敏感访问、通知告警”一致。\n3. 刷新后 hash 路由不丢失,仍展示“审计通知”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备审计通知访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/audit | 待执行\nTC-PROTO-0107 | 用户运营系统-单文件.html | 权限配置 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/system进入系统管理页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有系统管理访问权限。 | 路由=#/system;页面=系统管理;用途=用户角色、权限、数据范围、导出授权 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/system”或从侧边菜单点击“系统管理”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“系统管理”页面。 | 1. 页面成功进入“系统管理”。\n2. 当前菜单高亮,页面内容与“用户角色、权限、数据范围、导出授权”一致。\n3. 刷新后 hash 路由不丢失,仍展示“系统管理”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备系统管理访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/system | 待执行\nTC-PROTO-0108 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行创建测评需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“创建测评需求”的权限;相关基础数据已准备。 | ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“创建测评需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建测评需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“PENDING/EVALUATING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=PENDING/EVALUATING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建测评需求”;跨站点/跨部门数据需按权限范围过滤。 | 创建测评需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:创建测评需求 | 待执行\nTC-PROTO-0109 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行评估需求为待补充并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“评估需求为待补充”的权限;相关基础数据已准备。 | 需求ID=DEM-001;原因=ASIN目标数量缺少依据 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“评估需求为待补充”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-001;原因=ASIN目标数量缺少依据。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “评估需求为待补充”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“WAITING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=WAITING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“评估需求为待补充”;跨站点/跨部门数据需按权限范围过滤。 | 评估需求为待补充完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:评估需求为待补充 | 待执行\nTC-PROTO-0110 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行驳回不成立需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“驳回不成立需求”的权限;相关基础数据已准备。 | 需求ID=DEM-002;原因=ASIN评分已达标无需计划 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“驳回不成立需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-002;原因=ASIN评分已达标无需计划。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “驳回不成立需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“驳回不成立需求”;跨站点/跨部门数据需按权限范围过滤。 | 驳回不成立需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:驳回不成立需求 | 待执行\nTC-PROTO-0111 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行提交测评计划审批并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“提交测评计划审批”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“提交测评计划审批”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “提交测评计划审批”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REVIEW”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records;状态值=REVIEW;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“提交测评计划审批”;跨站点/跨部门数据需按权限范围过滤。 | 提交测评计划审批完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:提交测评计划审批 | 待执行\nTC-PROTO-0112 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批通过计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批通过计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;意见=同意执行;目标评价数=20 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批通过计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;意见=同意执行;目标评价数=20。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批通过计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“APPROVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=APPROVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批通过计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批通过计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批通过计划 | 待执行\nTC-PROTO-0113 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批驳回计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批驳回计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-002;意见=预算和风险说明不足 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批驳回计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-002;意见=预算和风险说明不足。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批驳回计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“DRAFT/REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=DRAFT/REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批驳回计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批驳回计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批驳回计划 | 待执行\nTC-PROTO-0114 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行生成候选用户池并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“生成候选用户池”的权限;相关基础数据已准备。 | 计划ID=PLAN-003;ASIN=B0TEST003;目标=50人 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“生成候选用户池”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-003;ASIN=B0TEST003;目标=50人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “生成候选用户池”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待触达”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plan_items/quota_reservations;状态值=待触达;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“生成候选用户池”;跨站点/跨部门数据需按权限范围过滤。 | 生成候选用户池完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:生成候选用户池 | 待执行\nTC-PROTO-0115 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行暂停执行中计划并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“暂停执行中计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-004;暂停原因=库存异常 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“暂停执行中计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-004;暂停原因=库存异常。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “暂停执行中计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已暂停”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plans;状态值=已暂停;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“暂停执行中计划”;跨站点/跨部门数据需按权限范围过滤。 | 暂停执行中计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:暂停执行中计划 | 待执行\nTC-PROTO-0116 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行批量预占额度并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“批量预占额度”的权限;相关基础数据已准备。 | person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“批量预占额度”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “批量预占额度”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RESERVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RESERVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“批量预占额度”;跨站点/跨部门数据需按权限范围过滤。 | 批量预占额度完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:批量预占额度 | 待执行\nTC-PROTO-0117 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行释放触达失败预占并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“释放触达失败预占”的权限;相关基础数据已准备。 | reservation_id=QR-001;释放原因=IM不可达 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“释放触达失败预占”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:reservation_id=QR-001;释放原因=IM不可达。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “释放触达失败预占”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RELEASED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RELEASED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“释放触达失败预占”;跨站点/跨部门数据需按权限范围过滤。 | 释放触达失败预占完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:释放触达失败预占 | 待执行\nTC-PROTO-0118 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行IM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行IM触达”的权限;相关基础数据已准备。 | plan_id=PLAN-006;channel=IM;content=回评卡片 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行IM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:plan_id=PLAN-006;channel=IM;content=回评卡片。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行IM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OUTBOUND/SENT”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:im_interaction_records;状态值=OUTBOUND/SENT;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行IM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行IM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行IM触达 | 待执行\nTC-PROTO-0119 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行EDM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行EDM触达”的权限;相关基础数据已准备。 | email=user@example.com;模板=回评邮件V1 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行EDM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:email=user@example.com;模板=回评邮件V1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行EDM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“SENT/DELIVERED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events;状态值=SENT/DELIVERED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行EDM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行EDM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行EDM触达 | 待执行\nTC-PROTO-0120 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行用户退订后停止EDM并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“用户退订后停止EDM”的权限;相关基础数据已准备。 | person_id=P100;event=UNSUBSCRIBED | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“用户退订后停止EDM”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P100;event=UNSUBSCRIBED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “用户退订后停止EDM”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“BLOCKED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events/channel_dedup_records;状态值=BLOCKED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“用户退订后停止EDM”;跨站点/跨部门数据需按权限范围过滤。 | 用户退订后停止EDM完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:用户退订后停止EDM | 待执行\nTC-PROTO-0121 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行创建客服工单并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“创建客服工单”的权限;相关基础数据已准备。 | person_id=P200;source=IM转人工;type=催评 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“创建客服工单”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P200;source=IM转人工;type=催评。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建客服工单”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_tickets;状态值=待分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建客服工单”;跨站点/跨部门数据需按权限范围过滤。 | 创建客服工单完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:创建客服工单 | 待执行\nTC-PROTO-0122 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行改派工单给组员并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“改派工单给组员”的权限;相关基础数据已准备。 | ticket_id=T001;from=组长;to=客服A;reason=当前负载低 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“改派工单给组员”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ticket_id=T001;from=组长;to=客服A;reason=当前负载低。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “改派工单给组员”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_assignment_logs;状态值=已分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“改派工单给组员”;跨站点/跨部门数据需按权限范围过滤。 | 改派工单给组员完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:改派工单给组员 | 待执行\nTC-PROTO-0123 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行强风险拦截并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“强风险拦截”的权限;相关基础数据已准备。 | person_id=P300;命中黑名单邮箱和设备 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“强风险拦截”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P300;命中黑名单邮箱和设备。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “强风险拦截”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“强风险拦截”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases/blacklist_entities;状态值=强风险拦截;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“强风险拦截”;跨站点/跨部门数据需按权限范围过滤。 | 强风险拦截完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:强风险拦截 | 待执行\nTC-PROTO-0124 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行弱风险人工放行并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“弱风险人工放行”的权限;相关基础数据已准备。 | risk_case=R001;原因=家庭共用设备;意见=放行 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“弱风险人工放行”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:risk_case=R001;原因=家庭共用设备;意见=放行。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “弱风险人工放行”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已放行”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases;状态值=已放行;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“弱风险人工放行”;跨站点/跨部门数据需按权限范围过滤。 | 弱风险人工放行完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:弱风险人工放行 | 待执行\nTC-PROTO-0125 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行登记评价提交并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“登记评价提交”的权限;相关基础数据已准备。 | person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接 | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“登记评价提交”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “登记评价提交”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已提交”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_submission_records;状态值=已提交;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“登记评价提交”;跨站点/跨部门数据需按权限范围过滤。 | 登记评价提交完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:登记评价提交 | 待执行\nTC-PROTO-0126 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon展示核验成功并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon展示核验成功”的权限;相关基础数据已准备。 | submission_id=SUB001;check_method=人工;result=DISPLAYED | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon展示核验成功”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB001;check_method=人工;result=DISPLAYED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon展示核验成功”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“CONFIRMED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks/review_results;状态值=CONFIRMED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon展示核验成功”;跨站点/跨部门数据需按权限范围过滤。 | Amazon展示核验成功完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon展示核验成功 | 待执行\nTC-PROTO-0127 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon暂不可核验进入观察并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon暂不可核验进入观察”的权限;相关基础数据已准备。 | submission_id=SUB002;result=UNVERIFIABLE | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon暂不可核验进入观察”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB002;result=UNVERIFIABLE。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon暂不可核验进入观察”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OBSERVING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks;状态值=OBSERVING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon暂不可核验进入观察”;跨站点/跨部门数据需按权限范围过滤。 | Amazon暂不可核验进入观察完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon暂不可核验进入观察 | 待执行\nTC-PROTO-0128 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL执行创建免评协作任务并校验业务结果 | P1 | 已进入“KOC/KOL”;当前用户具备执行“创建免评协作任务”的权限;相关基础数据已准备。 | creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频 | 1. 打开“用户运营系统-单文件.html”的“KOC/KOL”页面。\n2. 点击与“创建免评协作任务”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建免评协作任务”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“执行中”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:exemption_plan_tasks/code_records;状态值=执行中;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建免评协作任务”;跨站点/跨部门数据需按权限范围过滤。 | 创建免评协作任务完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:KOC/KOL;动作:创建免评协作任务 | 待执行\nTC-PROTO-0129 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知执行查看敏感信息审计并校验业务结果 | P1 | 已进入“审计通知”;当前用户具备执行“查看敏感信息审计”的权限;相关基础数据已准备。 | 对象=用户邮箱/设备号;动作=查看完整信息 | 1. 打开“用户运营系统-单文件.html”的“审计通知”页面。\n2. 点击与“查看敏感信息审计”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:对象=用户邮箱/设备号;动作=查看完整信息。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “查看敏感信息审计”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已记录”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:interaction_audit_logs;状态值=已记录;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“查看敏感信息审计”;跨站点/跨部门数据需按权限范围过滤。 | 查看敏感信息审计完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:审计通知;动作:查看敏感信息审计 | 待执行\nTC-PROTO-0130 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理执行分配导出权限并校验业务结果 | P1 | 已进入“系统管理”;当前用户具备执行“分配导出权限”的权限;相关基础数据已准备。 | role=用户运营组长;permission=导出计划数据;scope=US站点 | 1. 打开“用户运营系统-单文件.html”的“系统管理”页面。\n2. 点击与“分配导出权限”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:role=用户运营组长;permission=导出计划数据;scope=US站点。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “分配导出权限”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已授权”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:权限配置/审计日志;状态值=已授权;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“分配导出权限”;跨站点/跨部门数据需按权限范围过滤。 | 分配导出权限完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:系统管理;动作:分配导出权限 | 待执行\nTC-PROTO-0131 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:创建需求缺少ASIN | P2 | 已进入“需求中心”;准备异常数据或异常状态:ASIN为空,其他字段完整。 | 异常场景=创建需求缺少ASIN;异常数据=ASIN为空,其他字段完整 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:ASIN为空,其他字段完整。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“创建需求缺少ASIN”。\n2. 处理结果为:提示ASIN必填,需求不创建。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:创建需求缺少ASIN | 待执行\nTC-PROTO-0132 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:目标数量为0或负数 | P2 | 已进入“需求中心”;准备异常数据或异常状态:target_count=0/-1。 | 异常场景=目标数量为0或负数;异常数据=target_count=0/-1 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:target_count=0/-1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“目标数量为0或负数”。\n2. 处理结果为:提示目标数量必须大于0。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:目标数量为0或负数 | 待执行\nTC-PROTO-0133 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 异常场景 | 计划审核异常校验:审批意见为空驳回 | P2 | 已进入“计划审核”;准备异常数据或异常状态:decision=驳回;comment为空。 | 异常场景=审批意见为空驳回;异常数据=decision=驳回;comment为空 | 1. 打开“计划审核”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:decision=驳回;comment为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“审批意见为空驳回”。\n2. 处理结果为:阻止提交并提示填写驳回原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划审核;异常:审批意见为空驳回 | 待执行\nTC-PROTO-0134 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 异常场景 | 计划中心异常校验:已终止计划再次启动 | P2 | 已进入“计划中心”;准备异常数据或异常状态:plan.status=CANCELLED。 | 异常场景=已终止计划再次启动;异常数据=plan.status=CANCELLED | 1. 打开“计划中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:plan.status=CANCELLED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“已终止计划再次启动”。\n2. 处理结果为:启动按钮不可用或提示不可恢复。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划中心;异常:已终止计划再次启动 | 待执行\nTC-PROTO-0135 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:月度测评额度超过4 | P2 | 已进入“额度频控”;准备异常数据或异常状态:used=4,reserved=0,count=1。 | 异常场景=月度测评额度超过4;异常数据=used=4,reserved=0,count=1 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:used=4,reserved=0,count=1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“月度测评额度超过4”。\n2. 处理结果为:返回exceeded并阻止预占。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:月度测评额度超过4 | 待执行\nTC-PROTO-0136 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:累计评价超过12 | P2 | 已进入“额度频控”;准备异常数据或异常状态:lifetime_submission=12。 | 异常场景=累计评价超过12;异常数据=lifetime_submission=12 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:lifetime_submission=12。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“累计评价超过12”。\n2. 处理结果为:候选人进入排除池。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:累计评价超过12 | 待执行\nTC-PROTO-0137 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:发送前终校发现未关闭工单 | P2 | 已进入“额度频控”;准备异常数据或异常状态:support open ticket exists。 | 异常场景=发送前终校发现未关闭工单;异常数据=support open ticket exists | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:support open ticket exists。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“发送前终校发现未关闭工单”。\n2. 处理结果为:撤出本批次并记录原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:发送前终校发现未关闭工单 | 待执行\nTC-PROTO-0138 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:同计划同用户重复触达 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:person_id相同、plan_id相同、channel不同。 | 异常场景=同计划同用户重复触达;异常数据=person_id相同、plan_id相同、channel不同 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:person_id相同、plan_id相同、channel不同。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“同计划同用户重复触达”。\n2. 处理结果为:去重记录BLOCKED。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:同计划同用户重复触达 | 待执行\nTC-PROTO-0139 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:EDM硬退信 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:event=HARD_BOUNCED。 | 异常场景=EDM硬退信;异常数据=event=HARD_BOUNCED | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:event=HARD_BOUNCED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“EDM硬退信”。\n2. 处理结果为:邮箱状态标记不可触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:EDM硬退信 | 待执行\nTC-PROTO-0140 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:TEL三次未接通 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:retry_count=3。 | 异常场景=TEL三次未接通;异常数据=retry_count=3 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:retry_count=3。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“TEL三次未接通”。\n2. 处理结果为:降级EDM或关闭电话任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:TEL三次未接通 | 待执行\nTC-PROTO-0141 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:关闭工单缺少处理结果 | P2 | 已进入“客服中心”;准备异常数据或异常状态:result为空。 | 异常场景=关闭工单缺少处理结果;异常数据=result为空 | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:result为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“关闭工单缺少处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:关闭工单缺少处理结果 | 待执行\nTC-PROTO-0142 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:答应配合超时 | P2 | 已进入“客服中心”;准备异常数据或异常状态:deadline_at过期且无submission。 | 异常场景=答应配合超时;异常数据=deadline_at过期且无submission | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:deadline_at过期且无submission。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“答应配合超时”。\n2. 处理结果为:生成需再次联系任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:答应配合超时 | 待执行\nTC-PROTO-0143 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:黑名单同步接口超时 | P2 | 已进入“风险中心”;准备异常数据或异常状态:blacklist API timeout。 | 异常场景=黑名单同步接口超时;异常数据=blacklist API timeout | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:blacklist API timeout。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“黑名单同步接口超时”。\n2. 处理结果为:状态为失败待重试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:黑名单同步接口超时 | 待执行\nTC-PROTO-0144 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:弱风险复核被拒绝 | P2 | 已进入“风险中心”;准备异常数据或异常状态:人工意见=拒绝。 | 异常场景=弱风险复核被拒绝;异常数据=人工意见=拒绝 | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:人工意见=拒绝。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“弱风险复核被拒绝”。\n2. 处理结果为:用户不能进入触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:弱风险复核被拒绝 | 待执行\nTC-PROTO-0145 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:提交评价ASIN不匹配 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:登记ASIN=A,证据链接ASIN=B。 | 异常场景=提交评价ASIN不匹配;异常数据=登记ASIN=A,证据链接ASIN=B | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:登记ASIN=A,证据链接ASIN=B。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“提交评价ASIN不匹配”。\n2. 处理结果为:标记异常不计入完成。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:提交评价ASIN不匹配 | 待执行\nTC-PROTO-0146 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:Amazon未展示超过观察期 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:status=OBSERVING且retry超期。 | 异常场景=Amazon未展示超过观察期;异常数据=status=OBSERVING且retry超期 | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:status=OBSERVING且retry超期。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“Amazon未展示超过观察期”。\n2. 处理结果为:标记ABNORMAL并通知运营。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:Amazon未展示超过观察期 | 待执行\nTC-PROTO-0147 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 异常场景 | KOC/KOL异常校验:CODE缺失 | P2 | 已进入“KOC/KOL”;准备异常数据或异常状态:免评任务未填写CODE。 | 异常场景=CODE缺失;异常数据=免评任务未填写CODE | 1. 打开“KOC/KOL”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:免评任务未填写CODE。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“CODE缺失”。\n2. 处理结果为:阻止创建或标记价格/CODE待确认。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:KOC/KOL;异常:CODE缺失 | 待执行\nTC-PROTO-0148 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 异常场景 | 审计通知异常校验:普通客服查看完整邮箱 | P2 | 已进入“审计通知”;准备异常数据或异常状态:role=客服;action=查看完整信息。 | 异常场景=普通客服查看完整邮箱;异常数据=role=客服;action=查看完整信息 | 1. 打开“审计通知”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:role=客服;action=查看完整信息。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“普通客服查看完整邮箱”。\n2. 处理结果为:拒绝访问并记录越权尝试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:审计通知;异常:普通客服查看完整邮箱 | 待执行\nTC-PROTO-0149 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 异常场景 | 系统管理异常校验:离职账号仍有审批任务 | P2 | 已进入“系统管理”;准备异常数据或异常状态:account=disabled但任务未交接。 | 异常场景=离职账号仍有审批任务;异常数据=account=disabled但任务未交接 | 1. 打开“系统管理”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:account=disabled但任务未交接。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“离职账号仍有审批任务”。\n2. 处理结果为:阻止完成离职并提示先交接。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:系统管理;异常:离职账号仍有审批任务 | 待执行\nTC-PROTO-0150 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发按线索查真实人并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/person?type=email&value=xxx”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/person?type=email&value=xxx;预期输出=返回person_id、confidence、matched_clues | 1. 打开“用户中心”页面。\n2. 执行会触发“按线索查真实人”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/person?type=email&value=xxx”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/person?type=email&value=xxx”。\n2. 接口响应包含:返回person_id、confidence、matched_clues。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 按线索查真实人在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/person?type=email&value=xxx | 待执行\nTC-PROTO-0151 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发获取用户上下文卡并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/context/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/context/{person_id};预期输出=返回identity、transactions、services、risks、devices、outreach_history | 1. 打开“用户中心”页面。\n2. 执行会触发“获取用户上下文卡”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/context/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/context/{person_id}”。\n2. 接口响应包含:返回identity、transactions、services、risks、devices、outreach_history。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 获取用户上下文卡在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/context/{person_id} | 待执行\nTC-PROTO-0152 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发批量身份查询并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“POST /api/identity/batch-check”;测试用户拥有页面访问权限。 | 接口=POST /api/identity/batch-check;预期输出=返回每个线索对应person_id和confidence | 1. 打开“用户中心”页面。\n2. 执行会触发“批量身份查询”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/identity/batch-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/identity/batch-check”。\n2. 接口响应包含:返回每个线索对应person_id和confidence。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量身份查询在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:POST /api/identity/batch-check | 待执行\nTC-PROTO-0153 | 用户运营系统-单文件.html | 计划中心 | 需求与计划管理 | 数据校验 | 计划中心页面触发创建需求接口并校验接口数据 | P2 | 前端页面“计划中心”已打开;后端或 mock 服务提供接口“POST /api/demands”;测试用户拥有页面访问权限。 | 接口=POST /api/demands;预期输出=返回demand_id和status | 1. 打开“计划中心”页面。\n2. 执行会触发“创建需求接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/demands”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/demands”。\n2. 接口响应包含:返回demand_id和status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建需求接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划中心;接口:POST /api/demands | 待执行\nTC-PROTO-0154 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | 数据校验 | 计划审核页面触发提交审批接口并校验接口数据 | P2 | 前端页面“计划审核”已打开;后端或 mock 服务提供接口“POST /api/approvals/{plan_id}/submit”;测试用户拥有页面访问权限。 | 接口=POST /api/approvals/{plan_id}/submit;预期输出=生成approval_records | 1. 打开“计划审核”页面。\n2. 执行会触发“提交审批接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/approvals/{plan_id}/submit”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/approvals/{plan_id}/submit”。\n2. 接口响应包含:生成approval_records。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 提交审批接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划审核;接口:POST /api/approvals/{plan_id}/submit | 待执行\nTC-PROTO-0155 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发额度查询接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“GET /api/quota/check/{person_id}?type=REVIEW”;测试用户拥有页面访问权限。 | 接口=GET /api/quota/check/{person_id}?type=REVIEW;预期输出=返回used、in_progress、reserved、remaining、status | 1. 打开“额度频控”页面。\n2. 执行会触发“额度查询接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/quota/check/{person_id}?type=REVIEW”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/quota/check/{person_id}?type=REVIEW”。\n2. 接口响应包含:返回used、in_progress、reserved、remaining、status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 额度查询接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:GET /api/quota/check/{person_id}?type=REVIEW | 待执行\nTC-PROTO-0156 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发批量预占接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/reserve”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/reserve;预期输出=返回reservation_id并更新reserved | 1. 打开“额度频控”页面。\n2. 执行会触发“批量预占接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/reserve”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/reserve”。\n2. 接口响应包含:返回reservation_id并更新reserved。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量预占接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/reserve | 待执行\nTC-PROTO-0157 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发发送前终校接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/final-check”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/final-check;预期输出=返回APPROVED/WITHDRAWN和reasons | 1. 打开“额度频控”页面。\n2. 执行会触发“发送前终校接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/final-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/final-check”。\n2. 接口响应包含:返回APPROVED/WITHDRAWN和reasons。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 发送前终校接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/final-check | 待执行\nTC-PROTO-0158 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发渠道路由接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“POST /api/outreach/route”;测试用户拥有页面访问权限。 | 接口=POST /api/outreach/route;预期输出=返回recommended_channel和alternatives | 1. 打开“推送/触达”页面。\n2. 执行会触发“渠道路由接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/outreach/route”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/outreach/route”。\n2. 接口响应包含:返回recommended_channel和alternatives。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 渠道路由接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:POST /api/outreach/route | 待执行\nTC-PROTO-0159 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发触达历史接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“GET /api/outreach/history/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/outreach/history/{person_id};预期输出=返回im、edm、app、tel历史 | 1. 打开“推送/触达”页面。\n2. 执行会触发“触达历史接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/outreach/history/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/outreach/history/{person_id}”。\n2. 接口响应包含:返回im、edm、app、tel历史。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 触达历史接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:GET /api/outreach/history/{person_id} | 待执行\nTC-PROTO-0160 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发创建工单接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“POST /api/tickets”;测试用户拥有页面访问权限。 | 接口=POST /api/tickets;预期输出=返回ticket_id | 1. 打开“客服中心”页面。\n2. 执行会触发“创建工单接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/tickets”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/tickets”。\n2. 接口响应包含:返回ticket_id。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建工单接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:POST /api/tickets | 待执行\nTC-PROTO-0161 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发查询可用客服接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“GET /api/support/available-agents”;测试用户拥有页面访问权限。 | 接口=GET /api/support/available-agents;预期输出=返回agent_id和current_load | 1. 打开“客服中心”页面。\n2. 执行会触发“查询可用客服接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/support/available-agents”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/support/available-agents”。\n2. 接口响应包含:返回agent_id和current_load。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询可用客服接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:GET /api/support/available-agents | 待执行\nTC-PROTO-0162 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发记录评价提交接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“POST /api/reviews/submission”;测试用户拥有页面访问权限。 | 接口=POST /api/reviews/submission;预期输出=返回submission_id和quota_updated | 1. 打开“评价追踪”页面。\n2. 执行会触发“记录评价提交接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/reviews/submission”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/reviews/submission”。\n2. 接口响应包含:返回submission_id和quota_updated。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 记录评价提交接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:POST /api/reviews/submission | 待执行\nTC-PROTO-0163 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发查询计划评价进度接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“GET /api/reviews/status/{plan_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/reviews/status/{plan_id};预期输出=返回total_submissions、verified、pending、completion_rate | 1. 打开“评价追踪”页面。\n2. 执行会触发“查询计划评价进度接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/reviews/status/{plan_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/reviews/status/{plan_id}”。\n2. 接口响应包含:返回total_submissions、verified、pending、completion_rate。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询计划评价进度接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:GET /api/reviews/status/{plan_id} | 待执行\nTC-PROTO-0164 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:评价主闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流 | 1. 从 Dashboard 或对应入口启动“评价主闭环”。\n2. 按流程依次完成:ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “评价主闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 评价主闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:评价主闭环 | 待执行\nTC-PROTO-0165 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:紧急Listing闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升 | 1. 从 Dashboard 或对应入口启动“紧急Listing闭环”。\n2. 按流程依次完成:评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “紧急Listing闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 紧急Listing闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:紧急Listing闭环 | 待执行\nTC-PROTO-0166 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:推送风险复核闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录 | 1. 从 Dashboard 或对应入口启动“推送风险复核闭环”。\n2. 按流程依次完成:退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “推送风险复核闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 推送风险复核闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:推送风险复核闭环 | 待执行\nTC-PROTO-0167 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:黑名单同步闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查 | 1. 从 Dashboard 或对应入口启动“黑名单同步闭环”。\n2. 按流程依次完成:客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “黑名单同步闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 黑名单同步闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:黑名单同步闭环 | 待执行\nTC-PROTO-0168 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:客服转化闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新 | 1. 从 Dashboard 或对应入口启动“客服转化闭环”。\n2. 按流程依次完成:用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “客服转化闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 客服转化闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:客服转化闭环 | 待执行\nTC-PROTO-0169 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:免评协作闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新 | 1. 从 Dashboard 或对应入口启动“免评协作闭环”。\n2. 按流程依次完成:免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “免评协作闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 免评协作闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:免评协作闭环 | 待执行\nTC-PROTO-0170 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换全部标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“全部”。 | 标签=全部;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“全部”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“全部”。 | 1. 队列只展示与“全部”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:全部 | 待执行\nTC-PROTO-0171 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换审核标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“审核”。 | 标签=审核;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“审核”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“审核”。 | 1. 队列只展示与“审核”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:审核 | 待执行\nTC-PROTO-0172 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换黑名单标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“黑名单”。 | 标签=黑名单;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“黑名单”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“黑名单”。 | 1. 队列只展示与“黑名单”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:黑名单 | 待执行\nTC-PROTO-0173 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换推送标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“推送”。 | 标签=推送;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“推送”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“推送”。 | 1. 队列只展示与“推送”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:推送 | 待执行\nTC-PROTO-0174 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门全部部门状态全部状态风险全部风险负责人全部负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=全部部门;状态=全部状态;风险=全部风险;负责人=全部负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“全部部门”。\n3. 选择状态“全部状态”、风险“全部风险”、负责人“全部负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合全部部门/全部状态/全部风险/全部负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0175 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门Amazon 运营状态待审批风险全部风险负责人Amazon 总监组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=Amazon 运营;状态=待审批;风险=全部风险;负责人=Amazon 总监 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“Amazon 运营”。\n3. 选择状态“待审批”、风险“全部风险”、负责人“Amazon 总监”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合Amazon 运营/待审批/全部风险/Amazon 总监的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0176 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门用户运营状态待复核风险偏高负责人用户运营组长组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=用户运营;状态=待复核;风险=偏高;负责人=用户运营组长 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“用户运营”。\n3. 选择状态“待复核”、风险“偏高”、负责人“用户运营组长”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合用户运营/待复核/偏高/用户运营组长的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0177 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门客服状态客服升级风险高风险负责人客服负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=客服;状态=客服升级;风险=高风险;负责人=客服负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“客服”。\n3. 选择状态“客服升级”、风险“高风险”、负责人“客服负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合客服/客服升级/高风险/客服负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0178 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门系统管理员状态待系统管理员确认风险紧急负责人系统管理员组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=系统管理员;状态=待系统管理员确认;风险=紧急;负责人=系统管理员 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“系统管理员”。\n3. 选择状态“待系统管理员确认”、风险“紧急”、负责人“系统管理员”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合系统管理员/待系统管理员确认/紧急/系统管理员的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0179 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段JOYHUB 用户ID按全员可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“JOYHUB 用户ID”存在。 | 字段=JOYHUB 用户ID;可见范围=全员可见;期望=完整ID可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“JOYHUB 用户ID”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“JOYHUB 用户ID”按照“全员可见”控制可见性。\n2. 符合预期:完整ID可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | JOYHUB 用户ID必须按全员可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:JOYHUB 用户ID/全员可见 | 待执行\nTC-PROTO-0180 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段用户名按授权可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“用户名”存在。 | 字段=用户名;可见范围=授权可见;期望=未授权显示脱敏 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“用户名”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“用户名”按照“授权可见”控制可见性。\n2. 符合预期:未授权显示脱敏。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 用户名必须按授权可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:用户名/授权可见 | 待执行\nTC-PROTO-0181 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段邮箱后缀按已脱敏权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“邮箱后缀”存在。 | 字段=邮箱后缀;可见范围=已脱敏;期望=只显示邮箱域名/后缀 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“邮箱后缀”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“邮箱后缀”按照“已脱敏”控制可见性。\n2. 符合预期:只显示邮箱域名/后缀。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 邮箱后缀必须按已脱敏控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:邮箱后缀/已脱敏 | 待执行\nTC-PROTO-0182 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段近7天EDM推送数按推送数据权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“近7天EDM推送数”存在。 | 字段=近7天EDM推送数;可见范围=推送数据;期望=仅推送/管理员可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“近7天EDM推送数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“近7天EDM推送数”按照“推送数据”控制可见性。\n2. 符合预期:仅推送/管理员可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 近7天EDM推送数必须按推送数据控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:近7天EDM推送数/推送数据 | 待执行\nTC-PROTO-0183 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段身份风险等级按系统管理员/风险负责人权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“身份风险等级”存在。 | 字段=身份风险等级;可见范围=系统管理员/风险负责人;期望=普通运营不可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“身份风险等级”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“身份风险等级”按照“系统管理员/风险负责人”控制可见性。\n2. 符合预期:普通运营不可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 身份风险等级必须按系统管理员/风险负责人控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:身份风险等级/系统管理员/风险负责人 | 待执行\nTC-PROTO-0184 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段标签覆盖人数按标签模块权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“标签覆盖人数”存在。 | 字段=标签覆盖人数;可见范围=标签模块;期望=负责人可见汇总,普通客服不可导出 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“标签覆盖人数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“标签覆盖人数”按照“标签模块”控制可见性。\n2. 符合预期:负责人可见汇总,普通客服不可导出。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 标签覆盖人数必须按标签模块控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:标签覆盖人数/标签模块 | 待执行\nTC-PROTO-0185 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服本人在客服执行看板的可操作范围校验 | P1 | 准备客服本人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服本人;数据范围=我的工单;允许=回复用户、登记提交事实;限制=不能改派他人工单或查看团队绩效 | 1. 使用“客服本人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:回复用户、登记提交事实。\n4. 尝试执行限制动作:不能改派他人工单或查看团队绩效。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服本人只能看到“我的工单”。\n2. 允许动作“回复用户、登记提交事实”可正常提交。\n3. 限制动作“不能改派他人工单或查看团队绩效”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服本人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服本人 | 待执行\nTC-PROTO-0186 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服组长在客服执行看板的可操作范围校验 | P1 | 准备客服组长账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服组长;数据范围=组内工单池;允许=手动分配、转移、查看组员负载;限制=不能查看跨团队敏感字段 | 1. 使用“客服组长”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:手动分配、转移、查看组员负载。\n4. 尝试执行限制动作:不能查看跨团队敏感字段。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服组长只能看到“组内工单池”。\n2. 允许动作“手动分配、转移、查看组员负载”可正常提交。\n3. 限制动作“不能查看跨团队敏感字段”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服组长权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服组长 | 待执行\nTC-PROTO-0187 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服主管在客服执行看板的可操作范围校验 | P1 | 准备客服主管账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服主管;数据范围=团队看板;允许=查看排班、绩效、目标完成率;限制=不能同步黑名单除非额外授权 | 1. 使用“客服主管”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看排班、绩效、目标完成率。\n4. 尝试执行限制动作:不能同步黑名单除非额外授权。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服主管只能看到“团队看板”。\n2. 允许动作“查看排班、绩效、目标完成率”可正常提交。\n3. 限制动作“不能同步黑名单除非额外授权”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服主管权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服主管 | 待执行\nTC-PROTO-0188 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 风险负责人在客服执行看板的可操作范围校验 | P1 | 准备风险负责人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=风险负责人;数据范围=疑似诈骗工单;允许=确认诈骗、标记误报、同步黑名单候选;限制=不能修改客服排班 | 1. 使用“风险负责人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:确认诈骗、标记误报、同步黑名单候选。\n4. 尝试执行限制动作:不能修改客服排班。\n5. 打开审计日志查看敏感操作记录。 | 1. 风险负责人只能看到“疑似诈骗工单”。\n2. 允许动作“确认诈骗、标记误报、同步黑名单候选”可正常提交。\n3. 限制动作“不能修改客服排班”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 风险负责人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:风险负责人 | 待执行\nTC-PROTO-0189 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 系统管理员在客服执行看板的可操作范围校验 | P1 | 准备系统管理员账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=系统管理员;数据范围=全部客服数据;允许=查看审计、配置权限、导出绩效;限制=敏感查看仍需记录审计 | 1. 使用“系统管理员”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看审计、配置权限、导出绩效。\n4. 尝试执行限制动作:敏感查看仍需记录审计。\n5. 打开审计日志查看敏感操作记录。 | 1. 系统管理员只能看到“全部客服数据”。\n2. 允许动作“查看审计、配置权限、导出绩效”可正常提交。\n3. 限制动作“敏感查看仍需记录审计”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 系统管理员权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:系统管理员 | 待执行\nTC-PROTO-0190 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“需求中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2;详情字段=需求ID、ASIN、目标数量、周期、提交人、评估结果 | 1. 打开“需求中心”页面。\n2. 在筛选区按业务条件选择或输入:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:需求ID、ASIN、目标数量、周期、提交人、评估结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2”。\n2. 详情抽屉展示“需求ID、ASIN、目标数量、周期、提交人、评估结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 需求中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:需求中心;筛选:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2 | 待执行\nTC-PROTO-0191 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划审核”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回;详情字段=审批链、审批人、意见、step_order、decided_at | 1. 打开“计划审核”页面。\n2. 在筛选区按业务条件选择或输入:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:审批链、审批人、意见、step_order、decided_at。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回”。\n2. 详情抽屉展示“审批链、审批人、意见、step_order、decided_at”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划审核查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划审核;筛选:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回 | 待执行\nTC-PROTO-0192 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL;详情字段=计划项、目标量、候选人、资源分配、完成率 | 1. 打开“计划中心”页面。\n2. 在筛选区按业务条件选择或输入:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:计划项、目标量、候选人、资源分配、完成率。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL”。\n2. 详情抽屉展示“计划项、目标量、候选人、资源分配、完成率”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划中心;筛选:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL | 待执行\nTC-PROTO-0193 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 功能测试 | ASIN/Listing按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“ASIN/Listing”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间;详情字段=评分、评价数、差评数、健康状态、责任人 | 1. 打开“ASIN/Listing”页面。\n2. 在筛选区按业务条件选择或输入:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:评分、评价数、差评数、健康状态、责任人。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间”。\n2. 详情抽屉展示“评分、评价数、差评数、健康状态、责任人”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | ASIN/Listing查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:ASIN/Listing;筛选:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间 | 待执行\nTC-PROTO-0194 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 功能测试 | 用户中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“用户中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=国家、性别、标签、身份、产品数、活动数、近7天EDM次数;详情字段=用户主档、标签、身份、产品关系、近期活跃 | 1. 打开“用户中心”页面。\n2. 在筛选区按业务条件选择或输入:国家、性别、标签、身份、产品数、活动数、近7天EDM次数。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:用户主档、标签、身份、产品关系、近期活跃。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“国家、性别、标签、身份、产品数、活动数、近7天EDM次数”。\n2. 详情抽屉展示“用户主档、标签、身份、产品关系、近期活跃”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 用户中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:用户中心;筛选:国家、性别、标签、身份、产品数、活动数、近7天EDM次数 | 待执行\nTC-PROTO-0195 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“额度频控”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=额度类型=测评/免评/累计;状态=sufficient/warning/exceeded;详情字段=used、in_progress、reserved、remaining、limit_value | 1. 打开“额度频控”页面。\n2. 在筛选区按业务条件选择或输入:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:used、in_progress、reserved、remaining、limit_value。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“额度类型=测评/免评/累计;状态=sufficient/warning/exceeded”。\n2. 详情抽屉展示“used、in_progress、reserved、remaining、limit_value”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 额度频控查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:额度频控;筛选:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded | 待执行\nTC-PROTO-0196 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“推送/触达”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订;详情字段=发送、点击、回复、退订、route decision、dedup reason | 1. 打开“推送/触达”页面。\n2. 在筛选区按业务条件选择或输入:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:发送、点击、回复、退订、route decision、dedup reason。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订”。\n2. 详情抽屉展示“发送、点击、回复、退订、route decision、dedup reason”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 推送/触达查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:推送/触达;筛选:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订 | 待执行\nTC-PROTO-0197 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“客服中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭;详情字段=工单ID、assigned_agent、followup状态、首次回复时长 | 1. 打开“客服中心”页面。\n2. 在筛选区按业务条件选择或输入:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:工单ID、assigned_agent、followup状态、首次回复时长。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭”。\n2. 详情抽屉展示“工单ID、assigned_agent、followup状态、首次回复时长”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 客服中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:客服中心;筛选:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭 | 待执行\nTC-PROTO-0198 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“风险中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝;详情字段=risk_signal、risk_case、blacklist_entity | 1. 打开“风险中心”页面。\n2. 在筛选区按业务条件选择或输入:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:risk_signal、risk_case、blacklist_entity。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝”。\n2. 详情抽屉展示“risk_signal、risk_case、blacklist_entity”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 风险中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:风险中心;筛选:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝 | 待执行\nTC-PROTO-0199 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“评价追踪”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=核验状态=已提交/展示成功/暂不可核验/异常观察;详情字段=submission_id、display_check、retry_count、completion_rate | 1. 打开“评价追踪”页面。\n2. 在筛选区按业务条件选择或输入:核验状态=已提交/展示成功/暂不可核验/异常观察。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:submission_id、display_check、retry_count、completion_rate。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“核验状态=已提交/展示成功/暂不可核验/异常观察”。\n2. 详情抽屉展示“submission_id、display_check、retry_count、completion_rate”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 评价追踪查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:评价追踪;筛选:核验状态=已提交/展示成功/暂不可核验/异常观察 | 待执行\nTC-PROTO-0200 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“KOC/KOL”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置;详情字段=creator、Brief、CODE、返点、内容链接 | 1. 打开“KOC/KOL”页面。\n2. 在筛选区按业务条件选择或输入:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:creator、Brief、CODE、返点、内容链接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置”。\n2. 详情抽屉展示“creator、Brief、CODE、返点、内容链接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | KOC/KOL查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:KOC/KOL;筛选:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置 | 待执行\nTC-PROTO-0201 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“审计通知”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围;详情字段=日志ID、操作者、对象、动作、结果 | 1. 打开“审计通知”页面。\n2. 在筛选区按业务条件选择或输入:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:日志ID、操作者、对象、动作、结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围”。\n2. 详情抽屉展示“日志ID、操作者、对象、动作、结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 审计通知查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:审计通知;筛选:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围 | 待执行\nTC-PROTO-0202 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“系统管理”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=角色、部门、站点、权限点、账号状态;详情字段=账号、角色、数据范围、权限点、离职交接 | 1. 打开“系统管理”页面。\n2. 在筛选区按业务条件选择或输入:角色、部门、站点、权限点、账号状态。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:账号、角色、数据范围、权限点、离职交接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“角色、部门、站点、权限点、账号状态”。\n2. 详情抽屉展示“账号、角色、数据范围、权限点、离职交接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 系统管理查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:系统管理;筛选:角色、部门、站点、权限点、账号状态 | 待执行\nTC-PROTO-0203 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度剩余1次预警 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,in_progress=0,reserved=0,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,in_progress=0,reserved=0,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:允许预占但进入预警池。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度剩余1次预警 | 待执行\nTC-PROTO-0204 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度已用3且预占1再预占1 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,reserved=1,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,reserved=1,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:阻止预占,状态exceeded。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度已用3且预占1再预占1 | 待执行\nTC-PROTO-0205 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:免评额度独立预占 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=review used=4, exemption used=0,count=1 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:review used=4, exemption used=0,count=1。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:免评可预占,测评不可预占。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:免评额度独立预占 | 待执行\nTC-PROTO-0206 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:累计12提交后不因未展示回退 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=lifetime=11,提交评价后Amazon未展示 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:lifetime=11,提交评价后Amazon未展示。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:lifetime变12且不回退。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:累计12提交后不因未展示回退 | 待执行\nTC-PROTO-0207 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:预占超时自动释放 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=reservation.status=RESERVED且expires_at已过 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:reservation.status=RESERVED且expires_at已过。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:状态EXPIRED/RELEASED,remaining恢复。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:预占超时自动释放 | 待执行\nTC-PROTO-0208 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:跨计划重复入选 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=同person同时进入PLAN-A和PLAN-B | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:同person同时进入PLAN-A和PLAN-B。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:只保留高优先级或先预占计划,另一计划排除/预警。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:跨计划重复入选 | 待执行\nTC-PROTO-0209 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道IM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:APP活跃+已绑定用户;额度、风险、去重均通过。 | 渠道=IM;用户条件=APP活跃+已绑定用户;动作=推送回评卡片 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“APP活跃+已绑定用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“IM”。\n4. 执行“推送回评卡片”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“IM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“im_interaction_records”。\n3. 后续处理符合:用户回复后重新校验身份/额度/风险。\n4. Dashboard和用户上下文卡可查看触达历史。 | im_interaction_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | IM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:IM | 待执行\nTC-PROTO-0210 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道EDM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:未注册APP但邮箱可用用户;额度、风险、去重均通过。 | 渠道=EDM;用户条件=未注册APP但邮箱可用用户;动作=发送邮件并追踪送达/打开/点击/回复/退订 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“未注册APP但邮箱可用用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“EDM”。\n4. 执行“发送邮件并追踪送达/打开/点击/回复/退订”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“EDM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“edm_message_events”。\n3. 后续处理符合:回复邮件生成客服工单。\n4. Dashboard和用户上下文卡可查看触达历史。 | edm_message_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | EDM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:EDM | 待执行\nTC-PROTO-0211 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道APP Push按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;额度、风险、去重均通过。 | 渠道=APP Push;用户条件=绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;动作=发送Push并追踪点击打开/忽略/卸载 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“绑定新玩具/不活跃/计划到期/Listing紧急/活动触发”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“APP Push”。\n4. 执行“发送Push并追踪点击打开/忽略/卸载”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“APP Push”作为推荐渠道或可选渠道。\n2. 执行动作后写入“app_touch_events”。\n3. 后续处理符合:点击后分流到提交回评/联系客服/浏览。\n4. Dashboard和用户上下文卡可查看触达历史。 | app_touch_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | APP Push渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:APP Push | 待执行\nTC-PROTO-0212 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道TEL按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:高价值多次无响应或答应配合超时用户;额度、风险、去重均通过。 | 渠道=TEL;用户条件=高价值多次无响应或答应配合超时用户;动作=生成电话任务并记录通话结果 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“高价值多次无响应或答应配合超时用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“TEL”。\n4. 执行“生成电话任务并记录通话结果”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“TEL”作为推荐渠道或可选渠道。\n2. 执行动作后写入“tel_call_records”。\n3. 后续处理符合:未接通小于3次重拨,大于等于3次降级EDM或关闭。\n4. Dashboard和用户上下文卡可查看触达历史。 | tel_call_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | TEL渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:TEL | 待执行\nTC-PROTO-0213 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理截图证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=截图;包含ASIN和评论内容 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=截图;包含ASIN和评论内容。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并触发quota commit。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:截图证据登记 | 待执行\nTC-PROTO-0214 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理链接证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=Review Link;链接可打开 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=Review Link;链接可打开。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并进入展示核验。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:链接证据登记 | 待执行\nTC-PROTO-0215 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理人工核验展示成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=人工;check_result=DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=人工;check_result=DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:计入计划完成数并更新ASIN健康。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:人工核验展示成功 | 待执行\nTC-PROTO-0216 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理自动核验未展示 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=自动;check_result=NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=自动;check_result=NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:进入异常观察队列。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:自动核验未展示 | 待执行\nTC-PROTO-0217 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理暂不可核验 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_result=UNVERIFIABLE;原因=Amazon审核中 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_result=UNVERIFIABLE;原因=Amazon审核中。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:保留已提交事实并定期复查。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:暂不可核验 | 待执行\nTC-PROTO-0218 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察复查成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=OBSERVING重试后DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:OBSERVING重试后DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:转CONFIRMED并回流计划。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察复查成功 | 待执行\nTC-PROTO-0219 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察期满失败 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=retry_count超过阈值仍NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:retry_count超过阈值仍NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:标记ABNORMAL并通知运营。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察期满失败 | 待执行\nTC-PROTO-0220 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“需求中心”;存在业务条件:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。 | 按钮=查看完整信息;条件=JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:打开详情后点击查看完整信息。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0221 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:分配 | P2 | 系统管理员已进入“需求中心”;存在业务条件:测评需求Amazon已批准待用户运营接收。 | 按钮=分配;条件=测评需求Amazon已批准待用户运营接收 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:测评需求Amazon已批准待用户运营接收。\n3. 点击该行或页面上的“分配”按钮。\n4. 按页面提示执行:选择下一负责人为用户运营负责人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配”入口可用且文案正确。\n2. 操作后结果为:负责人变更并通知。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=分配 | 待执行\nTC-PROTO-0222 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:审批/确认 | P2 | 系统管理员已进入“需求中心”;存在业务条件:需求评分4.46低于4.5。 | 按钮=审批/确认;条件=需求评分4.46低于4.5 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:需求评分4.46低于4.5。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:选择通过/确认并填写计划建议。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:需求进入待生成计划。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=审批/确认 | 待执行\nTC-PROTO-0223 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:查看完整信息 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:评分4.21接近4.2紧急阈值。 | 按钮=查看完整信息;条件=评分4.21接近4.2紧急阈值 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:评分4.21接近4.2紧急阈值。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:查看ASIN完整站点与评价数据。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:敏感数据按权限展示。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=查看完整信息 | 待执行\nTC-PROTO-0224 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:创建紧急策略 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:紧急Listing未处理7条。 | 按钮=创建紧急策略;条件=紧急Listing未处理7条 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:紧急Listing未处理7条。\n3. 点击该行或页面上的“创建紧急策略”按钮。\n4. 按页面提示执行:填写策略参与人员与截止时间。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “创建紧急策略”入口可用且文案正确。\n2. 操作后结果为:生成紧急策略审批事项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的创建紧急策略动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=创建紧急策略 | 待执行\nTC-PROTO-0225 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:审批/确认 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:待系统管理员确认。 | 按钮=审批/确认;条件=待系统管理员确认 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:待系统管理员确认。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:确认联合策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:状态进入用户运营执行。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=审批/确认 | 待执行\nTC-PROTO-0226 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:生成计划 | P2 | 系统管理员已进入“计划中心”;存在业务条件:已确认需求存在且目标量明确。 | 按钮=生成计划;条件=已确认需求存在且目标量明确 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:已确认需求存在且目标量明确。\n3. 点击该行或页面上的“生成计划”按钮。\n4. 按页面提示执行:选择推新/回评/免评并拆分计划项。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成计划”入口可用且文案正确。\n2. 操作后结果为:生成计划ID和计划项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的生成计划动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=生成计划 | 待执行\nTC-PROTO-0227 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:批量审批 | P2 | 系统管理员已进入“计划中心”;存在业务条件:多条计划处于待审批。 | 按钮=批量审批;条件=多条计划处于待审批 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:多条计划处于待审批。\n3. 点击该行或页面上的“批量审批”按钮。\n4. 按页面提示执行:勾选多条计划并提交统一审批意见。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “批量审批”入口可用且文案正确。\n2. 操作后结果为:批量生成审批记录。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的批量审批动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=批量审批 | 待执行\nTC-PROTO-0228 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:流转 | P2 | 系统管理员已进入“计划中心”;存在业务条件:计划覆盖状态部分覆盖。 | 按钮=流转;条件=计划覆盖状态部分覆盖 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:计划覆盖状态部分覆盖。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看计划状态流转记录。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:展示创建、审批、执行节点。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=流转 | 待执行\nTC-PROTO-0229 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:计划与推送分配 | P2 | 系统管理员已进入“推送中心”;存在业务条件:周度推送计划待审。 | 按钮=计划与推送分配;条件=周度推送计划待审 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:周度推送计划待审。\n3. 点击该行或页面上的“计划与推送分配”按钮。\n4. 按页面提示执行:分配IM/EDM/TEL/App Push策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “计划与推送分配”入口可用且文案正确。\n2. 操作后结果为:生成推送任务。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的计划与推送分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=计划与推送分配 | 待执行\nTC-PROTO-0230 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:风险复核 | P2 | 系统管理员已进入“推送中心”;存在业务条件:退订率高于基线。 | 按钮=风险复核;条件=退订率高于基线 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线。\n3. 点击该行或页面上的“风险复核”按钮。\n4. 按页面提示执行:查看人群、素材、文案并选择暂停同策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “风险复核”入口可用且文案正确。\n2. 操作后结果为:推送状态变暂停待审。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的风险复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=风险复核 | 待执行\nTC-PROTO-0231 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:导出 | P2 | 系统管理员已进入“推送中心”;存在业务条件:当前筛选为推送风险。 | 按钮=导出;条件=当前筛选为推送风险 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:当前筛选为推送风险。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出推送ID、计划、渠道、发送点击回复退订。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:导出文件脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=导出 | 待执行\nTC-PROTO-0232 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:分配工单 | P2 | 系统管理员已进入“客服中心”;存在业务条件:差评跟进客服升级。 | 按钮=分配工单;条件=差评跟进客服升级 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:差评跟进客服升级。\n3. 点击该行或页面上的“分配工单”按钮。\n4. 按页面提示执行:选择客服A并填写分配原因。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配工单”入口可用且文案正确。\n2. 操作后结果为:工单状态变处理中。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的分配工单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=分配工单 | 待执行\nTC-PROTO-0233 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:流转 | P2 | 系统管理员已进入“客服中心”;存在业务条件:承诺配合用户待回访。 | 按钮=流转;条件=承诺配合用户待回访 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:承诺配合用户待回访。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看待回访和请假0.5天影响。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:生成回访待办。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=流转 | 待执行\nTC-PROTO-0234 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:导出 | P2 | 系统管理员已进入“客服中心”;存在业务条件:菲律宾团队管理。 | 按钮=导出;条件=菲律宾团队管理 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:菲律宾团队管理。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出工作时长、出勤、人均产出。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:仅主管可导出。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=导出 | 待执行\nTC-PROTO-0235 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:同步黑名单 | P2 | 系统管理员已进入“风险中心”;存在业务条件:接口超时失败待重试。 | 按钮=同步黑名单;条件=接口超时失败待重试 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:接口超时失败待重试。\n3. 点击该行或页面上的“同步黑名单”按钮。\n4. 按页面提示执行:点击同步黑名单。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “同步黑名单”入口可用且文案正确。\n2. 操作后结果为:失败保留并进入重试队列。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的同步黑名单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=同步黑名单 | 待执行\nTC-PROTO-0236 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:规则复核 | P2 | 系统管理员已进入“风险中心”;存在业务条件:退订率高于基线规则提醒。 | 按钮=规则复核;条件=退订率高于基线规则提醒 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线规则提醒。\n3. 点击该行或页面上的“规则复核”按钮。\n4. 按页面提示执行:查看规则依据并确认/误报。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “规则复核”入口可用且文案正确。\n2. 操作后结果为:复核结论写入风险事件。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的规则复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=规则复核 | 待执行\nTC-PROTO-0237 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“风险中心”;存在业务条件:Profile/邮箱/设备号脱敏。 | 按钮=查看完整信息;条件=Profile/邮箱/设备号脱敏 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:Profile/邮箱/设备号脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:授权角色查看完整主体摘要。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0238 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:生成/下载报表 | P2 | 系统管理员已进入“报表中心”;存在业务条件:Listing健康日报每日08:30自动生成。 | 按钮=生成/下载报表;条件=Listing健康日报每日08:30自动生成 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:Listing健康日报每日08:30自动生成。\n3. 点击该行或页面上的“生成/下载报表”按钮。\n4. 按页面提示执行:点击生成/下载报表。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成/下载报表”入口可用且文案正确。\n2. 操作后结果为:生成记录可下载且脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的生成/下载报表动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=生成/下载报表 | 待执行\nTC-PROTO-0239 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:上传记录 | P2 | 系统管理员已进入“报表中心”;存在业务条件:推送效果与风险复盘支持上传补充记录。 | 按钮=上传记录;条件=推送效果与风险复盘支持上传补充记录 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:推送效果与风险复盘支持上传补充记录。\n3. 点击该行或页面上的“上传记录”按钮。\n4. 按页面提示执行:上传人工复核附件。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “上传记录”入口可用且文案正确。\n2. 操作后结果为:附件关联报表ID。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的上传记录动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=上传记录 | 待执行\nTC-PROTO-0240 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:新建账号 | P2 | 系统管理员已进入“系统管理”;存在业务条件:按部门角色站点数据范围开通。 | 按钮=新建账号;条件=按部门角色站点数据范围开通 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:按部门角色站点数据范围开通。\n3. 点击该行或页面上的“新建账号”按钮。\n4. 按页面提示执行:录入账号、部门、角色、站点范围。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “新建账号”入口可用且文案正确。\n2. 操作后结果为:账号可登录且权限生效。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的新建账号动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=新建账号 | 待执行\nTC-PROTO-0241 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:离职管理 | P2 | 系统管理员已进入“系统管理”;存在业务条件:停用账号交接任务回收权限。 | 按钮=离职管理;条件=停用账号交接任务回收权限 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:停用账号交接任务回收权限。\n3. 点击该行或页面上的“离职管理”按钮。\n4. 按页面提示执行:选择离职员工并指定交接人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “离职管理”入口可用且文案正确。\n2. 操作后结果为:账号停用且敏感权限回收。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的离职管理动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=离职管理 | 待执行\nTC-PROTO-0242 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:搜索JOYHUB用户ID | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=输入JOYHUB ID并查询;预期=返回用户主档、标签、身份、产品关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:输入JOYHUB ID并查询。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“搜索JOYHUB用户ID”。\n2. 输出结果为:返回用户主档、标签、身份、产品关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的搜索JOYHUB用户ID可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/搜索JOYHUB用户ID | 待执行\nTC-PROTO-0243 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:按国家和近7天EDM次数筛选 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=国家=US;EDM近7天>0;预期=返回可用于触达的人群 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:国家=US;EDM近7天>0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按国家和近7天EDM次数筛选”。\n2. 输出结果为:返回可用于触达的人群。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的按国家和近7天EDM次数筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/按国家和近7天EDM次数筛选 | 待执行\nTC-PROTO-0244 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:打开用户画像详情 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=点击用户行详情;预期=展示注册/活跃时间、标签、产品、活动 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:点击用户行详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“打开用户画像详情”。\n2. 输出结果为:展示注册/活跃时间、标签、产品、活动。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的打开用户画像详情可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/打开用户画像详情 | 待执行\nTC-PROTO-0245 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:新增标签覆盖查询 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=兴趣;打标方式=系统;预期=展示覆盖人数和最新打标时间 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签分类=兴趣;打标方式=系统。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“新增标签覆盖查询”。\n2. 输出结果为:展示覆盖人数和最新打标时间。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的新增标签覆盖查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/新增标签覆盖查询 | 待执行\nTC-PROTO-0246 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:覆盖人数异常提示 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签覆盖人数突增或为0;预期=展示异常覆盖提示 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签覆盖人数突增或为0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“覆盖人数异常提示”。\n2. 输出结果为:展示异常覆盖提示。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的覆盖人数异常提示可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/覆盖人数异常提示 | 待执行\nTC-PROTO-0247 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:产品标签关联Listing | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=选择产品标签并查看关联产品;预期=展示产品-品牌-Listing/ASIN关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:选择产品标签并查看关联产品。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“产品标签关联Listing”。\n2. 输出结果为:展示产品-品牌-Listing/ASIN关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的产品标签关联Listing可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/产品标签关联Listing | 待执行\nTC-PROTO-0248 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:按创建时间筛选标签 | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=开始/截止时间;预期=只返回时间范围内标签 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:开始/截止时间。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按创建时间筛选标签”。\n2. 输出结果为:只返回时间范围内标签。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的按创建时间筛选标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/按创建时间筛选标签 | 待执行\nTC-PROTO-0249 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:客服分组标签查询 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=客服分组;预期=输出私域用户标签与社群/活动关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签分类=客服分组。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“客服分组标签查询”。\n2. 输出结果为:输出私域用户标签与社群/活动关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的客服分组标签查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/客服分组标签查询 | 待执行\nTC-PROTO-0250 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:风险用户隔离标签 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签状态=启用;风险相关;预期=可作为客服/推送排除条件 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签状态=启用;风险相关。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“风险用户隔离标签”。\n2. 输出结果为:可作为客服/推送排除条件。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的风险用户隔离标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/风险用户隔离标签 | 待执行\nTC-PROTO-0251 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:查看多语言图标 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=打开身份详情;预期=展示英/德/日图标PNG | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:打开身份详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“查看多语言图标”。\n2. 输出结果为:展示英/德/日图标PNG。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的查看多语言图标可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/查看多语言图标 | 待执行\nTC-PROTO-0252 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份风险等级筛选 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=身份风险等级=高;预期=进入风险用户池候选 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:身份风险等级=高。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份风险等级筛选”。\n2. 输出结果为:进入风险用户池候选。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份风险等级筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份风险等级筛选 | 待执行\nTC-PROTO-0253 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份状态停用 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=将风险身份置为停用;预期=后续筛选不再作为可选身份 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:将风险身份置为停用。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份状态停用”。\n2. 输出结果为:后续筛选不再作为可选身份。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份状态停用可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份状态停用 | 待执行\nTC-PROTO-0254 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态待分配执行分配给客服A | P2 | 客服执行看板存在状态为“待分配”的工单;当前用户对该工单有处理权限。 | 当前状态=待分配;动作=分配给客服A | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“待分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“分配给客服A”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“待分配”的工单可执行“分配给客服A”。\n2. 执行结果:assigned_agent=客服A;写入assignment_logs。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=待分配;动作=分配给客服A | 待执行\nTC-PROTO-0255 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已分配执行客服首次回复 | P2 | 客服执行看板存在状态为“已分配”的工单;当前用户对该工单有处理权限。 | 当前状态=已分配;动作=客服首次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“客服首次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已分配”的工单可执行“客服首次回复”。\n2. 执行结果:记录首次回复时长并进入处理中。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已分配;动作=客服首次回复 | 待执行\nTC-PROTO-0256 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待用户回复 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待用户回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待用户回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待用户回复”。\n2. 执行结果:状态变等待用户,设置提醒时间。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待用户回复 | 待执行\nTC-PROTO-0257 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待内部协同 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待内部协同 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待内部协同”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待内部协同”。\n2. 执行结果:状态变等待内部,通知内部负责人。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待内部协同 | 待执行\nTC-PROTO-0258 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择答应配合 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择答应配合 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择答应配合”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择答应配合”。\n2. 执行结果:创建support_followups,状态PROMISED。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择答应配合 | 待执行\nTC-PROTO-0259 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择疑似诈骗 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择疑似诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择疑似诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择疑似诈骗”。\n2. 执行结果:生成风险案件并标记工单疑似诈骗。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择疑似诈骗 | 待执行\nTC-PROTO-0260 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待用户执行用户回复后继续处理 | P2 | 客服执行看板存在状态为“等待用户”的工单;当前用户对该工单有处理权限。 | 当前状态=等待用户;动作=用户回复后继续处理 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待用户”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“用户回复后继续处理”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待用户”的工单可执行“用户回复后继续处理”。\n2. 执行结果:状态回到处理中并记录用户消息。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待用户;动作=用户回复后继续处理 | 待执行\nTC-PROTO-0261 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待内部执行内部反馈完成 | P2 | 客服执行看板存在状态为“等待内部”的工单;当前用户对该工单有处理权限。 | 当前状态=等待内部;动作=内部反馈完成 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待内部”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“内部反馈完成”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待内部”的工单可执行“内部反馈完成”。\n2. 执行结果:状态回到处理中并追加内部协同记录。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待内部;动作=内部反馈完成 | 待执行\nTC-PROTO-0262 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已解决执行关闭工单 | P2 | 客服执行看板存在状态为“已解决”的工单;当前用户对该工单有处理权限。 | 当前状态=已解决;动作=关闭工单 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已解决”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“关闭工单”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已解决”的工单可执行“关闭工单”。\n2. 执行结果:resolved_at和closed状态写入。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已解决;动作=关闭工单 | 待执行\nTC-PROTO-0263 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认误报 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认误报 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认误报”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认误报”。\n2. 执行结果:工单可回到处理中或已解决。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认误报 | 待执行\nTC-PROTO-0264 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认诈骗 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认诈骗”。\n2. 执行结果:工单关闭并进入黑名单同步候选。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认诈骗 | 待执行\nTC-PROTO-0265 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已关闭执行尝试再次回复 | P2 | 客服执行看板存在状态为“已关闭”的工单;当前用户对该工单有处理权限。 | 当前状态=已关闭;动作=尝试再次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已关闭”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“尝试再次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已关闭”的工单可执行“尝试再次回复”。\n2. 执行结果:禁止直接回复,需重新打开或新建工单。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已关闭;动作=尝试再次回复 | 待执行\nTC-PROTO-0266 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色Amazon运营模块访问与按钮权限 | P1 | 准备角色为“Amazon运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=Amazon运营;可见范围=需求中心/ASIN/计划审核查看;允许=创建需求、查看ASIN健康;限制=不能审批用户运营计划或查看完整用户敏感信息 | 1. 使用“Amazon运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:创建需求、查看ASIN健康。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批用户运营计划或查看完整用户敏感信息。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. Amazon运营只能访问“需求中心/ASIN/计划审核查看”。\n2. 允许动作“创建需求、查看ASIN健康”可正常执行。\n3. 限制动作“不能审批用户运营计划或查看完整用户敏感信息”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | Amazon运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=Amazon运营 | 待执行\nTC-PROTO-0267 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色用户运营模块访问与按钮权限 | P1 | 准备角色为“用户运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=用户运营;可见范围=需求中心/计划中心/推送中心/用户中心;允许=评估需求、生成计划、圈选人群、触达;限制=不能同步黑名单或配置系统权限 | 1. 使用“用户运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:评估需求、生成计划、圈选人群、触达。\n4. 尝试通过URL hash或按钮执行限制动作:不能同步黑名单或配置系统权限。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 用户运营只能访问“需求中心/计划中心/推送中心/用户中心”。\n2. 允许动作“评估需求、生成计划、圈选人群、触达”可正常执行。\n3. 限制动作“不能同步黑名单或配置系统权限”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 用户运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=用户运营 | 待执行\nTC-PROTO-0268 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服模块访问与按钮权限 | P1 | 准备角色为“客服”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服;可见范围=客服中心/用户上下文摘要/评价登记;允许=处理工单、登记评价提交;限制=不能查看跨团队绩效和完整设备号 | 1. 使用“客服”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:处理工单、登记评价提交。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看跨团队绩效和完整设备号。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服只能访问“客服中心/用户上下文摘要/评价登记”。\n2. 允许动作“处理工单、登记评价提交”可正常执行。\n3. 限制动作“不能查看跨团队绩效和完整设备号”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服 | 待执行\nTC-PROTO-0269 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服主管模块访问与按钮权限 | P1 | 准备角色为“客服主管”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服主管;可见范围=客服中心/客服执行看板/绩效;允许=分配工单、查看组内绩效、排班;限制=不能审批免评计划除非授权 | 1. 使用“客服主管”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:分配工单、查看组内绩效、排班。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批免评计划除非授权。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服主管只能访问“客服中心/客服执行看板/绩效”。\n2. 允许动作“分配工单、查看组内绩效、排班”可正常执行。\n3. 限制动作“不能审批免评计划除非授权”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服主管权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服主管 | 待执行\nTC-PROTO-0270 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色风险负责人模块访问与按钮权限 | P1 | 准备角色为“风险负责人”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=风险负责人;可见范围=风险中心/黑名单/审计;允许=复核风险、同步黑名单、标记误报;限制=不能修改计划目标量 | 1. 使用“风险负责人”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:复核风险、同步黑名单、标记误报。\n4. 尝试通过URL hash或按钮执行限制动作:不能修改计划目标量。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 风险负责人只能访问“风险中心/黑名单/审计”。\n2. 允许动作“复核风险、同步黑名单、标记误报”可正常执行。\n3. 限制动作“不能修改计划目标量”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 风险负责人权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=风险负责人 | 待执行\nTC-PROTO-0271 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色KOC运营模块访问与按钮权限 | P1 | 准备角色为“KOC运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=KOC运营;可见范围=KOC/KOL协作;允许=维护Brief、CODE、内容记录;限制=不能查看普通用户完整身份线索 | 1. 使用“KOC运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:维护Brief、CODE、内容记录。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看普通用户完整身份线索。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. KOC运营只能访问“KOC/KOL协作”。\n2. 允许动作“维护Brief、CODE、内容记录”可正常执行。\n3. 限制动作“不能查看普通用户完整身份线索”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | KOC运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=KOC运营 | 待执行\nTC-PROTO-0272 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色系统管理员模块访问与按钮权限 | P1 | 准备角色为“系统管理员”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=系统管理员;可见范围=全部模块;允许=账号权限、审计、配置、跨部门看板;限制=敏感访问仍需审计 | 1. 使用“系统管理员”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:账号权限、审计、配置、跨部门看板。\n4. 尝试通过URL hash或按钮执行限制动作:敏感访问仍需审计。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 系统管理员只能访问“全部模块”。\n2. 允许动作“账号权限、审计、配置、跨部门看板”可正常执行。\n3. 限制动作“敏感访问仍需审计”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 系统管理员权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=系统管理员 | 待执行\nTC-PROTO-0273 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 数据校验 | 需求中心执行导出待评估需求并校验导出脱敏与范围 | P2 | 已进入“需求中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=待评估;导出内容=导出demands当前筛选字段 | 1. 打开“需求中心”。\n2. 设置筛选条件:筛选状态=待评估。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出demands当前筛选字段。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=需求中心;导出=导出待评估需求 | 待执行\nTC-PROTO-0274 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 数据校验 | 计划审核执行导出审批记录并校验导出脱敏与范围 | P2 | 已进入“计划审核”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选审批状态=待审批;导出内容=导出approval_records | 1. 打开“计划审核”。\n2. 设置筛选条件:筛选审批状态=待审批。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出approval_records。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划审核;导出=导出审批记录 | 待执行\nTC-PROTO-0275 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 数据校验 | 计划中心执行导出计划执行进度并校验导出脱敏与范围 | P2 | 已进入“计划中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=执行中;导出内容=导出计划、计划项、完成率 | 1. 打开“计划中心”。\n2. 设置筛选条件:筛选状态=执行中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出计划、计划项、完成率。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划中心;导出=导出计划执行进度 | 待执行\nTC-PROTO-0276 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 数据校验 | ASIN/Listing执行导出健康风险ASIN并校验导出脱敏与范围 | P2 | 已进入“ASIN/Listing”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选健康状态=风险/严重风险;导出内容=导出评分、评价数、差评数 | 1. 打开“ASIN/Listing”。\n2. 设置筛选条件:筛选健康状态=风险/严重风险。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出评分、评价数、差评数。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=ASIN/Listing;导出=导出健康风险ASIN | 待执行\nTC-PROTO-0277 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 数据校验 | 用户中心执行导出人群包并校验导出脱敏与范围 | P2 | 已进入“用户中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选标签/国家/产品绑定;导出内容=导出脱敏用户ID和标签 | 1. 打开“用户中心”。\n2. 设置筛选条件:筛选标签/国家/产品绑定。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出脱敏用户ID和标签。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=用户中心;导出=导出人群包 | 待执行\nTC-PROTO-0278 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 数据校验 | 额度频控执行导出额度预警用户并校验导出脱敏与范围 | P2 | 已进入“额度频控”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=warning/exceeded;导出内容=导出额度台账摘要 | 1. 打开“额度频控”。\n2. 设置筛选条件:筛选status=warning/exceeded。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出额度台账摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=额度频控;导出=导出额度预警用户 | 待执行\nTC-PROTO-0279 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 数据校验 | 推送/触达执行导出退订用户并校验导出脱敏与范围 | P2 | 已进入“推送/触达”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选event=UNSUBSCRIBED;导出内容=导出退订事件和渠道 | 1. 打开“推送/触达”。\n2. 设置筛选条件:筛选event=UNSUBSCRIBED。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出退订事件和渠道。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=推送/触达;导出=导出退订用户 | 待执行\nTC-PROTO-0280 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 数据校验 | 客服中心执行导出超时工单并校验导出脱敏与范围 | P2 | 已进入“客服中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=等待用户且超时;导出内容=导出工单与负责人 | 1. 打开“客服中心”。\n2. 设置筛选条件:筛选状态=等待用户且超时。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出工单与负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=客服中心;导出=导出超时工单 | 待执行\nTC-PROTO-0281 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 数据校验 | 风险中心执行导出风险案件并校验导出脱敏与范围 | P2 | 已进入“风险中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=人工复核中;导出内容=导出风险摘要脱敏 | 1. 打开“风险中心”。\n2. 设置筛选条件:筛选状态=人工复核中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出风险摘要脱敏。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=风险中心;导出=导出风险案件 | 待执行\nTC-PROTO-0282 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 数据校验 | 评价追踪执行导出异常观察队列并校验导出脱敏与范围 | P2 | 已进入“评价追踪”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=OBSERVING/ABNORMAL;导出内容=导出提交和核验摘要 | 1. 打开“评价追踪”。\n2. 设置筛选条件:筛选status=OBSERVING/ABNORMAL。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出提交和核验摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=评价追踪;导出=导出异常观察队列 | 待执行\nTC-PROTO-0283 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 数据校验 | KOC/KOL执行导出逾期协作任务并校验导出脱敏与范围 | P2 | 已进入“KOC/KOL”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=逾期;导出内容=导出CODE/Brief/负责人 | 1. 打开“KOC/KOL”。\n2. 设置筛选条件:筛选状态=逾期。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出CODE/Brief/负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=KOC/KOL;导出=导出逾期协作任务 | 待执行\nTC-PROTO-0284 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 数据校验 | 审计通知执行导出敏感动作日志并校验导出脱敏与范围 | P2 | 已进入“审计通知”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选动作=查看完整信息/导出;导出内容=导出审计日志 | 1. 打开“审计通知”。\n2. 设置筛选条件:筛选动作=查看完整信息/导出。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出审计日志。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=审计通知;导出=导出敏感动作日志 | 待执行\nTC-PROTO-0285 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:重复点击处理卡点 | P2 | 已进入“工作台”;准备可执行场景:重复点击处理卡点。 | 动作=连续点击处理卡点按钮2次;预期=只打开一个详情/处理弹窗,不重复创建处理记录 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击处理卡点按钮2次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只打开一个详情/处理弹窗,不重复创建处理记录。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:重复点击处理卡点 | 待执行\nTC-PROTO-0286 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:刷新后保持时间范围 | P2 | 已进入“工作台”;准备可执行场景:刷新后保持时间范围。 | 动作=选择最近30天后刷新页面;预期=仍显示最近30天或按产品定义恢复默认并不报错 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:选择最近30天后刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:仍显示最近30天或按产品定义恢复默认并不报错。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:刷新后保持时间范围 | 待执行\nTC-PROTO-0287 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:黑名单同步重复提交 | P2 | 已进入“风险中心”;准备可执行场景:黑名单同步重复提交。 | 动作=同步黑名单按钮连续点击;预期=只生成一次同步任务,第二次提示处理中 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同步黑名单按钮连续点击。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只生成一次同步任务,第二次提示处理中。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:黑名单同步重复提交 | 待执行\nTC-PROTO-0288 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统稳定性与幂等 | 异常场景 | 报表中心稳定性校验:报表生成中重复下载 | P2 | 已进入“报表中心”;准备可执行场景:报表生成中重复下载。 | 动作=报表状态自动生成中点击下载;预期=提示生成中,不下载空文件 | 1. 打开原型页面“报表中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:报表状态自动生成中点击下载。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示生成中,不下载空文件。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:报表生成中重复下载 | 待执行\nTC-PROTO-0289 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:生成字段表重复点击 | P2 | 已进入“现有ERP”;准备可执行场景:生成字段表重复点击。 | 动作=连续点击生成字段表;预期=字段表只生成一份或版本号递增可追踪 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击生成字段表。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:字段表只生成一份或版本号递增可追踪。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:生成字段表重复点击 | 待执行\nTC-PROTO-0290 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:导出现有关系无数据 | P2 | 已进入“现有ERP”;准备可执行场景:导出现有关系无数据。 | 动作=查询结果为空后导出;预期=提示暂无可导出数据 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:查询结果为空后导出。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示暂无可导出数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:导出现有关系无数据 | 待执行\nTC-PROTO-0291 | user_erp_mvp_admin_prototype_v10(1).html | 身份管理 | 系统稳定性与幂等 | 异常场景 | 身份管理稳定性校验:多语言图标缺失 | P2 | 已进入“身份管理”;准备可执行场景:多语言图标缺失。 | 动作=德语图标PNG为空;预期=页面显示占位并提示需补充 | 1. 打开原型页面“身份管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:德语图标PNG为空。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:页面显示占位并提示需补充。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多语言图标缺失 | 待执行\nTC-PROTO-0292 | user_erp_mvp_admin_prototype_v10(1).html | 用户管理 | 系统稳定性与幂等 | 异常场景 | 用户管理稳定性校验:分页切换保持筛选 | P2 | 已进入“用户管理”;准备可执行场景:分页切换保持筛选。 | 动作=筛选国家US后切换下一页;预期=筛选条件不丢失 | 1. 打开原型页面“用户管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选国家US后切换下一页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:筛选条件不丢失。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:分页切换保持筛选 | 待执行\nTC-PROTO-0293 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:新工单到达实时刷新 | P2 | 已进入“客服执行看板”;准备可执行场景:新工单到达实时刷新。 | 动作=后台新增待分配工单;预期=看板待处理数增加并出现新工单 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:后台新增待分配工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:看板待处理数增加并出现新工单。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:新工单到达实时刷新 | 待执行\nTC-PROTO-0294 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:多人同时抢单 | P2 | 已进入“客服执行看板”;准备可执行场景:多人同时抢单。 | 动作=两个客服同时领取同一工单;预期=只有一个领取成功,另一个提示已被分配 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个客服同时领取同一工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只有一个领取成功,另一个提示已被分配。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多人同时抢单 | 待执行\nTC-PROTO-0295 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:首次回复重复发送 | P2 | 已进入“客服工单”;准备可执行场景:首次回复重复发送。 | 动作=客服双击发送回复;预期=只发送一条消息并记录一次首次回复时长 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:客服双击发送回复。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只发送一条消息并记录一次首次回复时长。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:首次回复重复发送 | 待执行\nTC-PROTO-0296 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:关闭工单后刷新 | P2 | 已进入“客服工单”;准备可执行场景:关闭工单后刷新。 | 动作=关闭工单后刷新详情页;预期=状态仍为已关闭且不可继续处理 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:关闭工单后刷新详情页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:状态仍为已关闭且不可继续处理。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:关闭工单后刷新 | 待执行\nTC-PROTO-0297 | 客服执行.html | 客服绩效 | 系统稳定性与幂等 | 异常场景 | 客服绩效稳定性校验:绩效周期切换 | P2 | 已进入“客服绩效”;准备可执行场景:绩效周期切换。 | 动作=日/周/月连续切换;预期=指标随周期变化且无串数据 | 1. 打开原型页面“客服绩效”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:日/周/月连续切换。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:指标随周期变化且无串数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:绩效周期切换 | 待执行\nTC-PROTO-0298 | 用户运营系统-单文件.html | 需求中心 | 系统稳定性与幂等 | 异常场景 | 需求中心稳定性校验:创建需求重复提交 | P2 | 已进入“需求中心”;准备可执行场景:创建需求重复提交。 | 动作=提交按钮连续点击两次;预期=只创建一个demand_id | 1. 打开原型页面“需求中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:提交按钮连续点击两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只创建一个demand_id。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:创建需求重复提交 | 待执行\nTC-PROTO-0299 | 用户运营系统-单文件.html | 计划审核 | 系统稳定性与幂等 | 异常场景 | 计划审核稳定性校验:两名审批人同时审批 | P2 | 已进入“计划审核”;准备可执行场景:两名审批人同时审批。 | 动作=一个通过一个驳回并发提交;预期=按后端锁定规则只接受一个有效决策 | 1. 打开原型页面“计划审核”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:一个通过一个驳回并发提交。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:按后端锁定规则只接受一个有效决策。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:两名审批人同时审批 | 待执行\nTC-PROTO-0300 | 用户运营系统-单文件.html | 计划中心 | 系统稳定性与幂等 | 异常场景 | 计划中心稳定性校验:计划暂停后重复暂停 | P2 | 已进入“计划中心”;准备可执行场景:计划暂停后重复暂停。 | 动作=执行中计划点击暂停两次;预期=第二次提示计划已暂停 | 1. 打开原型页面“计划中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:执行中计划点击暂停两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:第二次提示计划已暂停。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:计划暂停后重复暂停 | 待执行\nTC-PROTO-0301 | 用户运营系统-单文件.html | 额度频控 | 系统稳定性与幂等 | 异常场景 | 额度频控稳定性校验:并发预占同一真实人最后额度 | P2 | 已进入“额度频控”;准备可执行场景:并发预占同一真实人最后额度。 | 动作=两个计划同时预占remaining=1;预期=只允许一个预占成功 | 1. 打开原型页面“额度频控”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个计划同时预占remaining=1。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只允许一个预占成功。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:并发预占同一真实人最后额度 | 待执行\nTC-PROTO-0302 | 用户运营系统-单文件.html | 推送/触达 | 系统稳定性与幂等 | 异常场景 | 推送/触达稳定性校验:发送任务队列中刷新 | P2 | 已进入“推送/触达”;准备可执行场景:发送任务队列中刷新。 | 动作=点击发送后立即刷新页面;预期=任务状态可从队列恢复查询 | 1. 打开原型页面“推送/触达”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:点击发送后立即刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:任务状态可从队列恢复查询。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:发送任务队列中刷新 | 待执行\nTC-PROTO-0303 | 用户运营系统-单文件.html | 客服中心 | 系统稳定性与幂等 | 异常场景 | 客服中心稳定性校验:同用户重复创建工单 | P2 | 已进入“客服中心”;准备可执行场景:同用户重复创建工单。 | 动作=同person_id已有open工单再次创建;预期=提示关联已有工单或合并 | 1. 打开原型页面“客服中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person_id已有open工单再次创建。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示关联已有工单或合并。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:同用户重复创建工单 | 待执行\nTC-PROTO-0304 | 用户运营系统-单文件.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:确认诈骗重复点击 | P2 | 已进入“风险中心”;准备可执行场景:确认诈骗重复点击。 | 动作=风险案件连续点击确认诈骗;预期=只同步一次黑名单候选 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:风险案件连续点击确认诈骗。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只同步一次黑名单候选。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:确认诈骗重复点击 | 待执行\nTC-PROTO-0305 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:评价提交重复登记 | P2 | 已进入“评价追踪”;准备可执行场景:评价提交重复登记。 | 动作=同person+asin+plan重复提交相同证据;预期=提示重复记录或合并,不重复扣额度 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person+asin+plan重复提交相同证据。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示重复记录或合并,不重复扣额度。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:评价提交重复登记 | 待执行\nTC-PROTO-0306 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:展示核验重复确认 | P2 | 已进入“评价追踪”;准备可执行场景:展示核验重复确认。 | 动作=已CONFIRMED记录再次确认展示;预期=计划完成数不重复增加 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:已CONFIRMED记录再次确认展示。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:计划完成数不重复增加。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:展示核验重复确认 | 待执行\nTC-PROTO-0307 | 用户运营系统-单文件.html | 系统管理 | 系统稳定性与幂等 | 异常场景 | 系统管理稳定性校验:权限变更后立即生效 | P2 | 已进入“系统管理”;准备可执行场景:权限变更后立即生效。 | 动作=撤销用户导出权限后刷新;预期=导出按钮不可用且接口拒绝 | 1. 打开原型页面“系统管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:撤销用户导出权限后刷新。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:导出按钮不可用且接口拒绝。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:权限变更后立即生效 | 待执行\nTC-PROTO-0308 | 用户运营系统-单文件.html | 审计通知 | 系统稳定性与幂等 | 异常场景 | 审计通知稳定性校验:审计列表空状态 | P2 | 已进入“审计通知”;准备可执行场景:审计列表空状态。 | 动作=筛选未来日期无日志;预期=显示暂无数据且可重置 | 1. 打开原型页面“审计通知”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选未来日期无日志。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:显示暂无数据且可重置。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:审计列表空状态 | 待执行\n# Sheet: 覆盖矩阵\nHTML原型 | 需求模块 | 功能页面 | 测试类型 | 用例数量\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | KOC/KOL协作 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 多渠道触达引擎 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-P0/P1处理队列 | 功能测试 | 4\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-P0/P1处理队列 | 异常场景 | 5\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-P0/P1处理队列 | 流程测试 | 5\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服工单与管理 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 数据中心 | 工作台-时间范围与周期切换 | 数据校验 | 4\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | Listing 管理 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 各模块列表-组合筛选 | 功能测试 | 5\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 客服中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 报表中心 | 功能测试 | 2\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 推送中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 系统管理 | 功能测试 | 2\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 计划中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 需求中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 风险中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统稳定性与幂等 | 工作台 | 异常场景 | 2\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统稳定性与幂等 | 报表中心 | 异常场景 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统稳定性与幂等 | 风险中心 | 异常场景 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | Listing 管理 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 客服中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 报表中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 推送中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 数据中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 系统管理 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 计划中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 需求中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 风险中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 评价结果追踪 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求与计划管理 | 工作台-核心KPI卡片 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险与反欺诈 | 工作台-核心KPI卡片 | 功能测试 | 2\nuser_erp_mvp_admin_prototype_v10(1).html | 审计与通知中心 | 系统资产-系统管理 | 权限校验 | 4\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-公域-产品标签 | 功能测试 | 2\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-公域-用户标签 | 功能测试 | 3\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-公域-用户标签 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-关系图谱 | 数据校验 | 7\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-字段权限与脱敏 | 权限校验 | 6\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-查询需求矩阵 | 流程测试 | 5\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-用户管理 | 功能测试 | 4\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-用户管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-私域-用户标签 | 功能测试 | 3\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-私域-用户标签 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-身份管理 | 功能测试 | 4\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-身份管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 系统稳定性与幂等 | 现有ERP | 异常场景 | 2\nuser_erp_mvp_admin_prototype_v10(1).html | 系统稳定性与幂等 | 用户管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 系统稳定性与幂等 | 身份管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 需求与计划管理 | 现有ERP-公域-产品标签 | 功能测试 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 需求与计划管理 | 现有ERP-公域-产品标签 | 异常场景 | 1\n客服执行.html | 多渠道触达引擎 | 客服执行看板 | 功能测试 | 1\n客服执行.html | 客服工单与管理 | 客服工单状态机 | 流程测试 | 12\n客服执行.html | 客服工单与管理 | 客服工单生命周期 | 流程测试 | 10\n客服执行.html | 客服工单与管理 | 客服异常处理 | 异常场景 | 10\n客服执行.html | 客服工单与管理 | 客服执行看板 | 功能测试 | 10\n客服执行.html | 客服工单与管理 | 客服执行看板-角色权限 | 权限校验 | 5\n客服执行.html | 系统稳定性与幂等 | 客服工单 | 异常场景 | 2\n客服执行.html | 系统稳定性与幂等 | 客服执行看板 | 异常场景 | 2\n客服执行.html | 系统稳定性与幂等 | 客服绩效 | 异常场景 | 1\n客服执行.html | 评价结果追踪 | 客服执行看板 | 功能测试 | 3\n用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 功能测试 | 1\n用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 数据校验 | 1\n用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | 2\n用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 异常场景 | 1\n用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 数据校验 | 1\n用户运营系统-单文件.html | KOC/KOL协作 | 达人协作 | UI/交互测试 | 1\n用户运营系统-单文件.html | 多渠道触达引擎 | 多渠道触达 | UI/交互测试 | 1\n用户运营系统-单文件.html | 多渠道触达引擎 | 推送/触达 | 数据校验 | 2\n用户运营系统-单文件.html | 多渠道触达引擎 | 推送/触达 | 流程测试 | 4\n用户运营系统-单文件.html | 审计与通知中心 | 审计与通知 | UI/交互测试 | 1\n用户运营系统-单文件.html | 审计与通知中心 | 权限配置 | UI/交互测试 | 1\n用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 2\n用户运营系统-单文件.html | 审计通知 | 审计通知 | 异常场景 | 1\n用户运营系统-单文件.html | 审计通知 | 审计通知 | 数据校验 | 1\n用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 3\n用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 2\n用户运营系统-单文件.html | 客服中心 | 客服中心 | 数据校验 | 1\n用户运营系统-单文件.html | 客服工单与管理 | 客服中心 | 数据校验 | 2\n用户运营系统-单文件.html | 客服工单与管理 | 工单管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 4\n用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 3\n用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 数据校验 | 1\n用户运营系统-单文件.html | 用户中心 | 用户中心 | 功能测试 | 1\n用户运营系统-单文件.html | 用户中心 | 用户中心 | 数据校验 | 1\n用户运营系统-单文件.html | 用户身份与上下文 | 用户上下文 | UI/交互测试 | 1\n用户运营系统-单文件.html | 用户身份与上下文 | 用户中心 | 数据校验 | 3\n用户运营系统-单文件.html | 系统总览 | 全局权限 | 权限校验 | 7\n用户运营系统-单文件.html | 系统总览 | 工作台 | UI/交互测试 | 1\n用户运营系统-单文件.html | 系统总览 | 端到端流程 | 验收测试 | 6\n用户运营系统-单文件.html | 系统稳定性与幂等 | 审计通知 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 客服中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 推送/触达 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 系统管理 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 计划中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 计划审核 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 评价追踪 | 异常场景 | 2\n用户运营系统-单文件.html | 系统稳定性与幂等 | 需求中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 额度频控 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 风险中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 2\n用户运营系统-单文件.html | 系统管理 | 系统管理 | 异常场景 | 1\n用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 3\n用户运营系统-单文件.html | 计划中心 | 计划中心 | 异常场景 | 1\n用户运营系统-单文件.html | 计划中心 | 计划中心 | 数据校验 | 1\n用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 4\n用户运营系统-单文件.html | 计划审核 | 计划审核 | 异常场景 | 1\n用户运营系统-单文件.html | 计划审核 | 计划审核 | 数据校验 | 1\n用户运营系统-单文件.html | 评价结果追踪 | 评价结果 | UI/交互测试 | 1\n用户运营系统-单文件.html | 评价结果追踪 | 评价追踪 | 数据校验 | 2\n用户运营系统-单文件.html | 评价结果追踪 | 评价追踪 | 流程测试 | 7\n用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 4\n用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 2\n用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 数据校验 | 1\n用户运营系统-单文件.html | 需求与计划管理 | Listing健康 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划中心 | 数据校验 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划审核 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划审核 | 数据校验 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 需求管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 4\n用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 2\n用户运营系统-单文件.html | 需求中心 | 需求中心 | 数据校验 | 1\n用户运营系统-单文件.html | 额度与频控 | 额度管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 额度与频控 | 额度频控 | 异常场景 | 6\n用户运营系统-单文件.html | 额度与频控 | 额度频控 | 数据校验 | 3\n用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 3\n用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 3\n用户运营系统-单文件.html | 额度频控 | 额度频控 | 数据校验 | 1\n用户运营系统-单文件.html | 风险与反欺诈 | 风险反欺诈 | UI/交互测试 | 1\n用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 3\n用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 2\n用户运营系统-单文件.html | 风险中心 | 风险中心 | 数据校验 | 1\n# Sheet: 待确认问题\n编号 | 问题 | 影响模块 | 建议验收前确认\nQ-01 | 月度额度按自然月还是30天滚动;跨月预占如何处理 | 额度与频控 | 确定周期规则后补充边界用例\nQ-02 | 审批驳回后修改再提交是否重走完整审批链 | 需求与计划管理 | 确定审批状态机\nQ-03 | Amazon展示核验方式、频率、观察期 | 评价结果追踪 | 确定自动/人工核验策略\nQ-04 | 工单等待用户/等待内部的超时时长与提醒方式 | 客服工单与管理 | 确定SLA与提醒频率\nQ-05 | IM/EDM/APP/TEL具体频控阈值 | 多渠道触达 | 确定频控配置后补边界值\nQ-06 | 黑名单同步失败重试次数和升级负责人 | 风险与反欺诈 | 确定失败重试策略\nQ-07 | KOC/KOL免评CODE缺失时是阻断还是待补充 | KOC/KOL协作 | 确定免评计划必填字段\nQ-08 | 现有ERP用户-身份是一对多还是多对多 | 用户身份与上下文 | 确定关系模型后补充数据一致性用例", + "wikilinks": [], + "category": "layer-testing" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 01 主流程说明 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md", + "summary": "USER ERP 0 1需求重构 01 主流程说明 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 01 主流程说明 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage 1 完整业务需求 负", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 01 主流程说明 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人\n- 核心参与:USER运营、客服、渠道运营、KOC/KOL运营、财务、风险、产品、前端观察员\n- 文件目的:把 USER ERP 的完整业务主流程从需求进入写到结果复盘,避免后续只围绕单页或单模块开发。\n\n## 主流程总览\n\nUSER ERP 的主流程不是从推送开始,也不是从测评单开始,而是从“需求能否变成可执行计划”开始。\n\n```mermaid\nflowchart TB\n A[\"需求进入\"] --> B[\"需求评估\"]\n B --> C[\"产品/ASIN/库存/评分校验\"]\n C --> D[\"生成或调整计划\"]\n D --> E[\"执行资源匹配\"]\n E --> F[\"渠道任务/客服任务/KOC-KOL任务生成\"]\n F --> G[\"用户或达人响应\"]\n G --> H[\"客服承接与信息补全\"]\n H --> I[\"订单/评价/内容/返款/佣金履约\"]\n I --> J[\"风险复检与异常处理\"]\n J --> K[\"结果回流到计划与需求\"]\n K --> L[\"复盘、关闭或二次计划\"]\n```\n\n## 主流程核心原则\n\n1. 需求完整性优先于 V1 实现边界。\n2. 每个需求必须能追到执行计划、执行对象、执行结果和复盘结论。\n3. 测评、回评、免评、KOC/KOL任务共用真实人、风险、额度、订单、客服上下文。\n4. 客服既是承接用户问题的执行团队,也是评价转化、订单登记、催评和异常闭环的核心执行资源。\n5. KOC/KOL不是纯外部接口,而是完整需求域:线索、分层、任务、内容、带货、佣金、风险都要在阶段1被定义。\n6. 每次有效互动都要复检身份、额度、风险、订单和未关闭承诺。\n7. 用户提交评价与 Amazon 展示确认必须拆开;计划完成口径不能混用。\n\n## 业务对象\n\n| 对象 | 含义 | 阶段 |\n| --- | --- | --- |\n| 需求 | OA/销售/运营/异常触发/KOC-KOL合作请求 | V1必做 |\n| 计划 | 测评、回评、免评、KOC/KOL任务、客服跟进计划 | V1必做 |\n| 执行资源 | 渠道、人群、客服、测评人、KOC/KOL、H5/卡片、返款能力 | V1必做 |\n| 真实人 | 跨账号、邮箱、电话、Profile、设备归并后的核心身份 | V1必做 |\n| 测评人 | 可参与测评/回评/免评的运营对象,是真实人的业务视图 | V1必做 |\n| KOC/KOL | 创作者/带货/内容合作对象,可与测评人身份重叠 | V1预留 |\n| 订单 | Amazon订单、测评订单、回评订单、免评订单、样品/带货订单 | V1必做 |\n| 评价 | 用户提交、评论链接/截图、Amazon展示确认、掉评/差评 | V1必做 |\n| 返款/佣金 | 用户返款、礼品卡、财务返款、KOC/KOL佣金 | 用户返款V1必做,佣金V1预留 |\n| 工单 | 客服消息、售后问题、催评、答应配合、异常跟进 | V1必做 |\n| 风险事件 | 黑名单、重复退款、额度超限、异常账号、内容/佣金风险 | V1必做 |\n| 复盘记录 | 需求关闭、计划表现、渠道表现、客服表现、KOC/KOL表现 | V1预留 |\n\n## 主流程分解\n\n### 1. 需求进入\n\n| 内容 | 说明 |\n| --- | --- |\n| 触发来源 | OA测评计划、销售/运营手动需求、ASIN评分异常、掉评/差评、库存/Listing状态变化、KOC/KOL合作需求、客服反馈 |\n| 必填信息 | 需求编号、需求类型、产品/ASIN、站点、店铺、目标、优先级、截止时间、需求人、负责人 |\n| 可选信息 | 关键词、关键词链接、目标受众、目标Review数、预算/追加金额、指定渠道、指定KOC/KOL |\n| 输出 | 进入需求池,状态为待评估 |\n| 阶段 | V1必做 |\n\n### 2. 需求评估\n\n运营需要判断:\n\n- 需求是测评、回评、免评、KOC/KOL任务、客服跟进,还是混合需求。\n- ASIN当前评分、Review数、差评、掉评、库存、Listing状态是否支持执行。\n- 需求目标是否超过可用人群、额度、渠道和客服容量。\n- 是否存在产品禁用、店铺异常、关键词失效、H5/卡片缺失。\n- 是否需要审批、拆分计划、延迟、驳回或转其他计划类型。\n\n输出状态:\n\n- 评估通过,进入计划生成。\n- 需补充信息,退回需求人。\n- 暂缓,等待产品/库存/Listing恢复。\n- 驳回,记录原因。\n\n阶段:V1必做。\n\n### 3. 产品/ASIN/库存/评分校验\n\n| 校验项 | 处理 |\n| --- | --- |\n| 产品是否启用 | 禁用则禁止生成渠道任务,允许进入待恢复池 |\n| ASIN是否正确 | 错误则退回补充或人工修正 |\n| 站点/店铺是否匹配 | 不匹配则阻断计划下发 |\n| 库存是否充足 | 库存紧张时限制测评/免评节奏 |\n| 评分/掉评/差评 | 触发回评或紧急催评策略 |\n| 关键词/H5/卡片 | 缺失则生成维护任务 |\n\n阶段:V1必做。\n\n### 4. 计划生成或调整\n\n计划类型必须保留:\n\n| 计划类型 | 说明 | 阶段 |\n| --- | --- | --- |\n| 测评计划 | 为产品增加评价、冲销量、拉排名、新品启动等 | V1必做 |\n| 回评计划 | 对已购/已测/待评价人群催评或稳定评分 | V1必做 |\n| 免评计划 | 面向长期测评人、KOC/KOL或补单需求,不计普通测评额度 | V1必做 |\n| KOC/KOL合作任务 | 样品、内容、带货、佣金、复盘 | V1预留/V2实现 |\n| 客服跟进计划 | 售后、催评、答应配合、异常用户跟进 | V1必做 |\n\n计划生成时必须写入:\n\n- 关联需求编号。\n- 产品/ASIN/站点/店铺。\n- 计划目标、周期、每日节奏、优先级。\n- 渠道策略。\n- 目标人群条件。\n- 额度预占策略。\n- 风险排除策略。\n- 客服承接要求。\n- H5/卡片/素材要求。\n- 关闭条件。\n\n### 5. 执行资源匹配\n\n计划不是创建后直接发出去,必须先匹配资源。\n\n| 资源 | 匹配规则 |\n| --- | --- |\n| 人群 | 用户层级、国家、品类偏好、活跃、历史订单、Review额度、风险 |\n| 测评人 | 可测评次数、可免评次数、可上评次数、合作状态、掉评率、退款取消记录 |\n| KOC/KOL | 分层、品类、内容能力、带货能力、合作状态、风险状态 |\n| 渠道 | IM/EDM/Phone/APP/KOC-KOL可用性、频控、退订、投诉、转化 |\n| 客服 | 在线状态、排班、工单量、国家/语言、转化目标、当前压力 |\n| 产品素材 | H5、卡片、关键词链接、首图、编码图、跳转链接 |\n| 财务 | 可返款方式、返款账号、礼品卡卡密、审核队列 |\n| 风险 | 黑名单、强弱关联、退款取消、重复订单/评论/返款 |\n\n阶段:V1必做半自动匹配,V2增强自动推荐。\n\n### 6. 渠道任务 / 客服任务 / KOC-KOL任务生成\n\n| 任务 | 生成条件 | 输出 |\n| --- | --- | --- |\n| IM推送任务 | 有可推人群、卡片/H5可用、频控通过 | 推送任务、渠道事件、标签 |\n| EDM任务 | 域名/邮箱/IP健康、UID人群正常、邮件素材/H5可用 | 邮件计划、AB Test、发送结果 |\n| Phone任务 | 用户有电话、适合电话沟通、客服容量可承接 | 电话名单、回拨任务、通话记录 |\n| 客服工单 | 用户回复、订单异常、信息缺失、售后问题、投诉 | 工单、处理人、状态 |\n| KOC/KOL任务 | 有合适达人、样品/免评/内容目标明确 | 合作任务、内容/带货跟踪 |\n| 财务返款任务 | 用户信息完整、订单/评价状态满足返款条件 | 请款/返款任务 |\n\n### 7. 用户或达人响应\n\n响应包括:\n\n- 用户点击、回复、提交订单号、提交返款账号、上传评论截图/链接。\n- 用户只提交部分信息。\n- 用户投诉、退订、不感兴趣。\n- KOC/KOL接受任务、拒绝任务、提交内容链接、提交带货链接。\n\n每次响应都要写入渠道事件,并触发有效互动复检。\n\n### 8. 客服承接与信息补全\n\n客服核心动作:\n\n- 查看用户上下文卡。\n- 回复用户。\n- 查询/核验订单号。\n- 补充返款账号、截图、评论链接。\n- 登记订单。\n- 催评。\n- 记录售后问题和解决方案。\n- 标记答应配合。\n- 升级风险、财务、运营或主管。\n- 关闭或重开工单。\n\n客服管理动作:\n\n- 分配/转移工单。\n- 调整排班。\n- 设置目标。\n- 统计回复、工单、转化、满意度。\n\n阶段:V1必做。\n\n### 9. 履约:订单 / 评价 / 返款 / 佣金\n\n| 履约对象 | 核心状态 |\n| --- | --- |\n| 订单 | 待登记、已登记、已发货、已取消、已退款、异常 |\n| 评价 | 待提交、已提交、待展示核验、已展示、未展示、掉评、差评 |\n| 返款 | 待请款、待审核、审核失败、待返款、返款成功、返款锁定 |\n| KOC/KOL内容 | 待接受、待寄样、待提交、待审核、已发布、链接异常 |\n| KOC/KOL佣金 | 待归因、待计算、待审核、待结算、已结算、争议 |\n\n### 10. 风险复检与异常处理\n\n复检时机:\n\n- 需求评估。\n- 计划生成。\n- 人群生成。\n- 渠道发送前。\n- 用户提交订单/评价/返款账号。\n- 客服登记订单。\n- 请款/返款。\n- KOC/KOL接受任务、提交内容、结算佣金。\n\n风险结果:\n\n- 正常,继续执行。\n- 弱风险,提醒人工确认。\n- 强风险,阻断并生成风险事件。\n- 确认风险,同步黑名单。\n\n### 11. 结果回流与复盘\n\n结果必须回流到:\n\n- 原需求:是否达成、是否关闭、是否需补量。\n- 计划:完成数、节奏、渠道表现、成本。\n- ASIN:评分、Review、差评、掉评。\n- 用户/测评人:额度、合作状态、标签、风险。\n- 客服:转化、回复、目标、绩效。\n- KOC/KOL:内容、带货、佣金、合作等级。\n- 渠道:素材、频控、人群、AB Test。\n\n阶段:V1预留复盘记录,核心指标 V1必做。\n\n## 状态总表\n\n| 状态域 | 必须拆开 |\n| --- | --- |\n| 需求状态 | 草稿、待评估、需补充、已通过、已驳回、已转计划、已关闭 |\n| 计划状态 | 草稿、待审批、进行中、暂停、已完成、已取消、需补量 |\n| 资源匹配状态 | 待匹配、匹配中、匹配不足、匹配完成、需人工确认 |\n| 渠道任务状态 | 待发送、发送中、已发送、失败、已下架、暂停 |\n| 用户响应状态 | 未响应、已点击、已回复、已提交部分信息、已提交完整信息、投诉/退订 |\n| 工单状态 | 待分配、待处理、处理中、等待用户、已解决、已关闭、已重开 |\n| 订单状态 | 待登记、已登记、已发货、已取消、已退款、异常 |\n| 评价状态 | 待提交、已提交、待核验、已展示、未展示、掉评、差评 |\n| 返款状态 | 待请款、待审核、审核失败、待返款、返款成功、锁定 |\n| KOC/KOL任务状态 | 待分配、待接受、进行中、待内容、待审核、已发布、已结算、异常 |\n| 风险状态 | 正常、弱风险、强风险、已拦截、复核中、已解除、已拉黑 |\n\n## Gate 1 - 主流程完成条件\n\n- 主流程从需求进入到结果复盘完整。\n- 需求与执行计划匹配被明确为核心流程。\n- 客服被写入主流程核心位置。\n- KOC/KOL被作为完整需求域纳入,而不是仅做接口预留。\n- 测评、回评、免评、客服、渠道、财务、风险、看板之间的流转关系清楚。\n- V1必做、V1预留、V2实现、待确认有明确标注。\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 02 日常操作页面结构 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md", + "summary": "USER ERP 0 1需求重构 02 日常操作页面结构 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 02 日常操作页面结构 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage 1 完整", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 02 日常操作页面结构 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人 / 产品负责人\n- 核心参与:USER运营、客服主管、渠道负责人、KOC/KOL负责人、风险、财务、前端观察员\n- 文件目的:定义每个岗位每天打开系统后先看什么、判断什么、处理什么,并据此组织页面结构。\n\n## 页面设计原则\n\n1. 第一屏必须是日常作战台,不是普通任务列表。\n2. 页面围绕“发现异常 -> 判断优先级 -> 分配动作 -> 执行 -> 跟进结果 -> 复盘沉淀”设计。\n3. 每个页面必须说明贡献哪个OKR:用户增长、评价转化、销售转化、活动转化、满意度、风险控制。\n4. 页面结构要服务需求与执行计划匹配,不能只展示静态数据。\n5. 客服执行和客服管理是一级核心入口。\n6. KOC/KOL在阶段1必须有页面结构,即使V1只预留部分入口。\n\n## 一级导航建议\n\n| 一级入口 | 定位 | 阶段 |\n| --- | --- | --- |\n| 今日作战台 | 所有角色的每日首页,显示异常、目标、今日必处理 | V1必做 |\n| 需求与计划调度中心 | 需求池、计划生成、执行资源匹配、计划调整 | V1必做 |\n| 评价计划与订单中心 | 测评、回评、免评计划和订单履约 | V1必做 |\n| 客服执行中心 | 工单、聊天、答应配合、催评、订单登记 | V1必做 |\n| 客服管理中心 | 出勤、排班、目标、绩效、服务质量 | V1必做 |\n| 渠道运营中心 | IM、EDM、Phone、APP/H5/卡片、推送任务 | V1必做 |\n| KOC/KOL协作中心 | 线索、达人、任务、内容、带货、佣金、风险 | V1预留/V2实现 |\n| 测评人/真实人中心 | 测评人档案、身份归并、额度、风险、历史 | V1必做 |\n| 风险与黑名单中心 | 风险事件、黑名单、退款比对、异常复检 | V1必做 |\n| 数据复盘看板 | 计划、ASIN、渠道、客服、KOC/KOL、财务复盘 | V1必做核心,V2增强 |\n| 系统配置 | 权限、渠道配置、字段、标签、通知、导入导出 | V1预留 |\n\n## 01 今日作战台\n\n### 目标\n\n让主管和各岗位每天开屏后立刻知道:昨天有什么异常、本周目标是否危险、今天必须推进什么、哪些任务没人处理、哪些结果需要复盘。\n\n### 页面区块\n\n| 区块 | 展示内容 | 操作 |\n| --- | --- | --- |\n| 昨日核心指标 | 需求新增、计划新增、完成、缺口、客服工单、风险、返款 | 查看详情 |\n| P0/P1异常 | Review低于计划、渠道下滑、客服超时、产品禁用、风险事件 | 指派处理、升级 |\n| 今日必跟进 | 待评估需求、待补量计划、待催评用户、待返款、待审核内容 | 进入处理 |\n| 目标进度 | 月度测评、回评、免评、客服转化、KOC/KOL内容/带货 | 调整计划 |\n| 资源压力 | 可用人群、额度、客服容量、KOC/KOL资源、返款队列 | 资源调度 |\n| 昨日复盘 | TOP/BOTTOM任务、异常原因、沉淀动作 | 创建复盘 |\n\n### 角色视图\n\n| 角色 | 首页重点 |\n| --- | --- |\n| 高级主管 | OKR、资源、P0异常、跨部门阻塞 |\n| USER运营 | 需求、计划、执行缺口、渠道/客服压力 |\n| 渠道运营 | 触达漏斗、素材、可推人群、退订/投诉 |\n| 客服主管 | 在线、排班、待处理工单、首次回复、转化目标 |\n| KOC/KOL运营 | 线索、任务、内容、带货、风险 |\n| 风险/财务 | 待审核、待返款、双重退款、敏感操作 |\n\n## 02 需求与计划调度中心\n\n### 子页面\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| 需求池 | OA/销售/运营/异常/KOC-KOL需求统一入口 | V1必做 |\n| 需求评估页 | 校验ASIN、产品、库存、评分、目标、优先级 | V1必做 |\n| 计划编排页 | 生成测评、回评、免评、客服、KOC/KOL计划 | V1必做 |\n| 执行匹配看板 | 人群、渠道、客服、KOC/KOL、风险、H5/卡片匹配 | V1必做基础 |\n| 计划调整页 | 补量、暂停、恢复、转免评、关闭、拆分/合并 | V1必做 |\n\n### 需求池字段\n\n| 字段 | 说明 |\n| --- | --- |\n| 需求编号 | OA/系统生成 |\n| 需求来源 | OA、销售、运营、ASIN异常、客服反馈、KOC/KOL |\n| 需求类型 | 测评、回评、免评、客服跟进、KOC/KOL内容/带货 |\n| 产品/ASIN/站点/店铺 | 执行基础 |\n| 目标 | 目标订单数、Review数、评分、内容数、带货数 |\n| 优先级 | S/A/B/C 或 P0/P1/P2 |\n| 截止时间 | 计划完成约束 |\n| 当前状态 | 待评估、需补充、已转计划、暂停、关闭 |\n| 负责人 | 运营负责人 |\n\n## 03 评价计划与订单中心\n\n### 页面结构\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| 测评计划 | 产品测评需求、计划周期、渠道、目标、进度、评分、库存 | V1必做 |\n| 回评计划 | 掉评/差评/维稳/冲刺等回评需求与每日目标 | V1必做 |\n| 免评计划 | 长期测评人/KOC-KOL/补单免评计划 | V1必做 |\n| 测评订单 | 订单登记、上传回评、请款、返款、状态跟踪 | V1必做 |\n| 回评订单 | 回评上传、确认、返款、售后来源、处理记录 | V1必做 |\n| 产品/ASIN详情 | 评分、Review、库存、关键词、渠道、H5/卡片 | V1预留 |\n\n## 04 客服执行中心\n\n客服执行中心是 V1 核心入口。\n\n| 页面 | 每天要解决的问题 | 核心操作 |\n| --- | --- | --- |\n| 工单池 | 哪些用户消息没人处理,哪些工单快超时 | 分配、领取、转移、标记解决、关闭 |\n| 我的工单 | 当前客服今天处理什么 | 回复、登记订单、催评、上传结果、升级 |\n| 聊天/消息 | 用户具体说了什么 | 快捷回复、补充信息、创建跟进 |\n| 服务聊天记录 | 复查历史沟通 | 查询、查看上下文 |\n| 答应配合 | 用户答应评价/反馈后是否完成 | 确认、拒绝、提醒、过期 |\n| 催评池 | 哪些测评/回评待催 | 发送提醒、转人工、关闭 |\n| 售后详情 | 用户问题、解决方案、订单、返款 | 记录方案、回访、升级 |\n\n## 05 客服管理中心\n\n| 页面 | 展示 | 操作 |\n| --- | --- | --- |\n| 客服Dashboard | 在线客服、今日工单、待处理、今日转化、本月目标 | 查看详情 |\n| 出勤管理 | 应出勤、实际出勤、出勤率、迟到/请假/缺勤 | 导入/调整 |\n| 排班管理 | 早班、午班、晚班、渠道、最大工单数 | 设置、批量排班、调整 |\n| 工单分配管理 | 分配规则、当前工单量、超时工单 | 自动分配、手动调整 |\n| 回复统计 | 回复用户数、处理工单、消息数、首次回复 | 导出 |\n| 转化绩效 | RSO/RDO登记订单、获取评价、完成率 | 查看排行 |\n| 目标管理 | 月目标、历史完成、客服个人趋势 | 设置目标、调整 |\n\n## 06 渠道运营中心\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| IM推送 | 用户分层、卡片、推送任务、回复、转化 | V1必做 |\n| IM卡片/H5管理 | 卡片、图片、链接、跳转、产品禁用联动 | V1必做 |\n| EDM每日检查 | 域名/邮箱/IP信誉、UID、H5、AB Test、转化 | V1必做基础 |\n| Phone工作台 | 未接/待回拨、国家/时段、问题类型、客服容量 | V1预留 |\n| 推送配置 | 渠道账号、日限额、状态、负责人 | V1预留 |\n| 渠道漏斗看板 | 推送、曝光、点击、回复、订单、评价 | V1必做 |\n\n## 07 KOC/KOL协作中心\n\n阶段1必须完整定义,V1可部分预留。\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| KOC/KOL线索池 | 新增线索、来源、标签、风险、分层 | V1预留 |\n| 达人档案 | 国家、品类、内容能力、带货能力、合作状态 | V1预留 |\n| 合作任务 | 样品、免评、内容、带货、佣金任务 | V1预留/V2 |\n| 内容记录 | 内容链接、审核、发布时间、表现 | V2实现 |\n| 带货归因 | Code、链接、订单、销售额 | V2实现 |\n| 佣金结算 | 佣金规则、结算、争议 | V2实现 |\n| KOC/KOL风险 | 标签混乱、重复触达、违规内容、佣金争议 | V1预留 |\n\n## 08 测评人/真实人中心\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| 测评人列表 | 编号、Joyhub、邮箱、电话、Profile、国家、标签、合作状态 | V1必做 |\n| 测评人详情 | 额度、订单、评价、返款、风险、沟通记录 | V1必做 |\n| 真实人归并 | 标准化姓名+地址、设备、邮箱、电话、Profile、收款信息等自动归并;人工确认/拆分作为修正入口 | 自动归并 V1必做,人工确认/拆分 V1预留 |\n| 额度台账 | 月度测评、月度免评、累计Review、预占/释放 | V1必做 |\n| 风险记录 | 黑名单、退款取消、掉评、弱/强关联 | V1必做 |\n\n说明:`4/4/12`额度控制依赖真实人维度,V1必须先具备自动归并能力,否则 `person_quota_ledgers` 无法跨 JOYHUB ID、Amazon 账号和 Profile 合并计算。\n\n## 09 风险与黑名单中心\n\n| 页面 | 说明 |\n| --- | --- |\n| 风险事件 | 所有风险信号与人工复核案件 |\n| 黑名单 | 生效中/已移除、风险等级、来源、原因 |\n| 退款比对 | Amazon退款与OA返款双重比对 |\n| 关联风险 | 邮箱、电话、设备、IP、Profile、地址、返款账号 |\n| 内容/佣金风险 | KOC/KOL内容不合规、归因失败、佣金争议 |\n\n## 10 数据复盘看板\n\n| 看板 | 指标 |\n| --- | --- |\n| 计划看板 | 总计划、进行中、完成率、缺口、补量 |\n| ASIN看板 | 评分、Review、差评、掉评、库存、风险ASIN |\n| 渠道看板 | 推送、曝光、点击、回复、转化、退订/投诉 |\n| 客服看板 | 工单、响应、解决、满意度、RSO/RDO转化 |\n| 测评人看板 | 额度、完成率、掉评率、风险、合作状态 |\n| KOC/KOL看板 | 线索、任务、内容、带货、佣金、风险 |\n| 财务看板 | 待请款、待审核、返款成功、异常返款 |\n\n## Gate 1 - 页面结构完成条件\n\n- 今日作战台和岗位每日工作方式已定义。\n- 需求与计划调度中心作为一级核心入口。\n- 客服执行中心和客服管理中心作为一级核心入口。\n- KOC/KOL协作中心已有完整需求结构,即使V1不全量实现。\n- 各页面能对应主流程中的对象、状态、动作和复盘指标。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 03 功能页面按钮盘点表 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md", + "summary": "USER ERP 0 1需求重构 03 功能页面按钮盘点表 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 03 功能页面按钮盘点表 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage 1 ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 03 功能页面按钮盘点表 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:产品负责人 / 前端观察员\n- 核心参与:业务用户、客服主管、渠道运营、KOC/KOL运营、财务、风险、后端、测试\n- 文件目的:盘点阶段1涉及的页面按钮、业务含义、读写对象、状态变化、权限和审计要求,作为 Stage 2 按钮行为矩阵的输入。\n\n## 盘点规则\n\n| 字段 | 说明 |\n| --- | --- |\n| 页面 | 按一级/二级页面归类 |\n| 按钮/动作 | 用户可点击或系统触发的动作 |\n| 业务含义 | 为什么需要这个按钮 |\n| 读取对象 | 点击前必须读的数据 |\n| 写入对象 | 点击后必须落盘的数据 |\n| 状态变化 | 影响哪些状态 |\n| 权限 | 哪些角色可操作 |\n| 审计 | 是否必须记录操作日志 |\n| 阶段 | V1必做、V1预留、V2实现、待确认 |\n\n## 对象命名对照\n\n按钮盘点表中的“读取对象/写入对象”保留页面语境简称;进入 Stage 2 接口和数据设计时,应优先映射到数据对象 v3 的正式对象名。\n\n| 按钮盘点简称 | v3正式对象或聚合口径 |\n| --- | --- |\n| person | `person_profiles` |\n| identity | `person_identity_links` |\n| reviewer | `person_identity_links` + `person_quota_ledgers` 聚合视图 |\n| quota / quota_ledger | `person_quota_ledgers` |\n| quota_reservation | `quota_reservations` |\n| audience / audience_snapshot | `audience_snapshots` |\n| exclusion | `audience_exclusions` |\n| risk_event | `risk_signals` / `risk_cases` |\n| risk | `risk_signals` / `risk_cases` / `blacklist_entities` 聚合摘要 |\n| order / review_order | `amazon_orders`、`review_submission_records`、`review_display_checks` |\n| push_task / channel_event | `channel_route_decisions`、`channel_dedup_records`、各渠道事件表 |\n| interaction_recheck | `interaction_recheck_records` |\n| koc_kol / creator | JOYCOLLAB 的 `creator_id`,以及 `exemption_plan_tasks`、`creator_content_records`、`exemption_result_snapshots` |\n\n## 01 今日作战台\n\n| 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| 查看异常 | 进入昨日/今日异常详情 | alert、plan、channel_event、ticket、risk_event | - | - | 主管、运营 | 否 | V1必做 |\n| 指派处理人 | 把异常转成可执行任务 | alert、staff、shift | task、notification | 异常:待处理 -> 已指派 | 主管、运营 | 是 | V1必做 |\n| 升级异常 | P0/P1问题升级主管或跨部门 | alert、risk_event、ticket | escalation、notification | 异常:待处理 -> 已升级 | 主管、风险 | 是 | V1必做 |\n| 调整计划 | 从作战台直接进入计划调整 | demand、plan、progress | plan_change_log | 计划:进行中 -> 调整中/暂停/补量 | 主管、运营 | 是 | V1必做 |\n| 创建复盘 | 对异常或任务形成复盘记录 | plan、channel_event、ticket、order | retrospective | 异常:已处理 -> 待复盘 | 主管、运营 | 是 | V1预留 |\n\n## 02 需求与计划调度中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 需求池 | 新增需求 | 手动录入销售/运营/KOC-KOL需求 | product、asin、staff | demand | 需求:草稿 | 运营、主管 | 是 | V1必做 |\n| 需求池 | 导入OA需求 | 从OA或表格批量导入需求 | import_file | demand、import_log | 需求:待评估 | 运营、管理员 | 是 | V1必做 |\n| 需求池 | 需求评估 | 判断需求是否可执行 | demand、product、asin、inventory、risk | demand_review | 待评估 -> 已通过/需补充/驳回 | 运营、主管 | 是 | V1必做 |\n| 需求池 | 退回补充 | 信息不足时退回需求人 | demand | demand、notification | 待评估 -> 需补充 | 运营 | 是 | V1必做 |\n| 需求池 | 生成计划 | 将需求转成测评/回评/免评/KOC-KOL/客服计划 | demand、product、audience、quota、risk | plan、plan_log | 已通过 -> 已转计划 | 运营、主管 | 是 | V1必做 |\n| 计划编排 | 拆分计划 | 一个需求拆成多个站点/渠道/周期计划 | demand、plan | plan、plan_relation | 计划:草稿/进行中 | 运营、主管 | 是 | V1预留 |\n| 计划编排 | 合并计划 | 合并同产品/同周期/同目标计划 | plan | plan_relation、plan_log | 计划:合并/关闭旧计划 | 主管 | 是 | V1预留 |\n| 执行匹配 | 匹配人群 | 生成候选用户/测评人/KOC-KOL名单 | person、reviewer、koc_kol、quota、risk | audience_snapshot、exclusion | 匹配:待匹配 -> 完成/不足 | 运营 | 是 | V1必做 |\n| 执行匹配 | 预占额度 | 锁定测评/免评/Review额度 | audience_snapshot、quota_ledger | quota_reservation | 额度:可用 -> 预占 | 运营 | 是 | V1必做 |\n| 执行匹配 | 手动释放预占 | 预占超时、计划取消或人群撤出后释放额度 | quota_reservation、quota_ledger、plan | quota_reservation_log、quota_ledger | 额度:已预占 -> 已释放 | 主管、管理员 | 是 | V1必做 |\n| 执行匹配 | 分配客服 | 按容量、语言、工单量分配承接客服 | plan、ticket、shift、agent_capacity | task、ticket_assignment | 待分配 -> 已分配 | 主管、客服主管 | 是 | V1必做 |\n| 执行匹配 | 生成推送任务 | 从计划生成人群和渠道任务 | plan、audience、card、h5、channel_config | push_task、channel_event | 待发送 | 渠道运营 | 是 | V1必做 |\n| 执行匹配 | 匹配KOC/KOL | 将需求匹配给合适创作者 | koc_kol_profile、task、risk | koc_kol_task | 待分配 -> 待接受 | KOC/KOL运营 | 是 | V1预留 |\n| 计划调整 | 调整名额 | 增减计划数量 | plan、progress、resource | plan_change_log | 计划目标变化 | 运营、主管 | 是 | V1必做 |\n| 计划调整 | 暂停计划 | 禁止继续执行但保留历史 | plan、risk、product | plan_log、notification | 进行中 -> 暂停 | 主管 | 是 | V1必做 |\n| 计划调整 | 恢复计划 | 恢复已暂停计划 | plan、product、risk | plan_log | 暂停 -> 进行中 | 主管 | 是 | V1必做 |\n| 计划调整 | 转免评 | 普通测评/回评因店铺/订单/用户问题转免评 | plan、order、reviewer | plan、order_log | 测评/回评 -> 免评 | 运营、主管 | 是 | V1必做 |\n| 计划调整 | 关闭需求 | 需求完成或取消 | demand、plan、progress | demand_close_log | 需求:已关闭 | 主管 | 是 | V1必做 |\n\n## 03 评价计划与订单中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 测评计划 | 新建测评计划 | 从需求或人工创建计划 | demand、product、asin | review_plan | 草稿/未开始 | 运营 | 是 | V1必做 |\n| 测评计划 | 编辑计划 | 修改目标、渠道、周期、负责人 | review_plan | plan_log | 计划字段变化 | 运营、主管 | 是 | V1必做 |\n| 测评计划 | 复制关键词链接 | 运营执行用 | review_plan | copy_event | - | 运营 | 否 | V1必做 |\n| 测评计划 | 查看关联推送 | 查看计划下发的渠道任务 | plan、push_task | - | - | 运营、渠道 | 否 | V1必做 |\n| 测评计划 | 查看关联订单 | 查看计划履约订单 | plan、order | - | - | 运营 | 否 | V1必做 |\n| 回评计划 | 新建回评计划 | 因掉评/差评/维稳生成回评计划 | asin、review_stats | reply_plan | 待开始 | 运营 | 是 | V1必做 |\n| 回评计划 | 调整今日目标 | 根据评分/掉评变化调整当天目标 | reply_plan、asin_stats | plan_log | 今日目标变化 | 运营、主管 | 是 | V1必做 |\n| 免评计划 | 新建免评计划 | 给长期测评人/KOC-KOL/补单池创建免评计划 | demand、reviewer、koc_kol | free_plan | 待审批/进行中 | 运营 | 是 | V1必做 |\n| 测评订单 | 新增订单 | 记录测评执行单 | plan、person、product | review_order | 待上传回评/待登记 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 上传订单 | 绑定Amazon订单号 | review_order、amazon_order | review_order、audit_log | 订单:待登记 -> 已登记 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 上传回评 | 绑定评论ID/链接/截图 | review_order、comment | review_submission | 待回评 -> 待确认/已提交 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 提交排队 | 评论暂不能绑定,进入排队 | review_order、comment_query | review_queue | 待确认 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 请款 | 发起用户返款 | review_order、refund_account | refund_request | 待请款 -> 待审核 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 更换订单 | 用新订单替代原订单 | review_order、amazon_order | order_change_log | 订单号变化 | 运营、主管 | 是 | V1必做 |\n| 测评订单 | 更改订单 | 修正订单字段 | review_order | order_change_log | 订单字段变化 | 运营、主管 | 是 | V1必做 |\n| 测评订单 | 撤销 | 撤销错误/风险订单 | review_order、risk | order_log、quota_release | 进行中 -> 撤销 | 风险、主管 | 是 | V1必做 |\n| 测评订单 | 批量导出 | 导出当前筛选结果 | order、permission | export_task | - | 有导出权限 | 是 | V1必做 |\n| 回评订单 | 回评确认 | 核验回评是否有效 | review_submission、comment | review_display_check | 待确认 -> 已回评/未通过 | 运营、审核 | 是 | V1必做 |\n\n## 04 客服执行中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 工单池 | 自动分配 | 按在线、工单量、排班分配客服 | ticket、agent、shift | ticket_assignment | 待分配 -> 待处理 | 客服主管 | 是 | V1必做 |\n| 工单池 | 手动分配 | 指定处理人 | ticket、agent | ticket_assignment | 待分配 -> 待处理 | 客服主管 | 是 | V1必做 |\n| 工单池 | 转移工单 | 换客服处理 | ticket、agent | ticket_transfer_log | 处理人变化 | 客服、主管 | 是 | V1必做 |\n| 我的工单 | 回复用户 | 处理用户消息 | ticket、chat、context_snapshot | chat_message、ticket_log | 待处理 -> 处理中/等待用户 | 客服 | 是 | V1必做 |\n| 我的工单 | 登记订单 | 客服拿到订单号后登记 | ticket、amazon_order、person | review_order、ticket_log | 工单推进 | 客服 | 是 | V1必做 |\n| 我的工单 | 催评 | 提醒用户提交评价 | ticket、order、followup | reminder_event、followup | 催评次数+1 | 客服、运营 | 是 | V1必做 |\n| 我的工单 | 标记解决 | 用户问题解决 | ticket | ticket_log | 处理中 -> 已解决 | 客服 | 是 | V1必做 |\n| 我的工单 | 关闭工单 | 完成或无需继续 | ticket | ticket_log | 已解决/等待用户 -> 已关闭 | 客服、主管 | 是 | V1必做 |\n| 我的工单 | 重开工单 | 用户再次反馈 | ticket | ticket_log | 已关闭 -> 已重开/处理中 | 客服、主管 | 是 | V1必做 |\n| 答应配合 | 确认答应 | 用户答应评价/反馈 | ticket、person | support_followup | 待确认 -> 已确认 | 客服 | 是 | V1必做 |\n| 答应配合 | 拒绝 | 用户拒绝配合 | ticket | support_followup | 待确认 -> 已拒绝 | 客服 | 是 | V1必做 |\n| 答应配合 | 标记过期 | 超时未提交 | followup | support_followup | 已确认 -> 已过期 | 客服、系统 | 是 | V1必做 |\n\n## 05 客服管理中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 出勤管理 | 导入出勤 | 导入当天/本月出勤 | attendance_file | attendance_record | 出勤记录更新 | 客服主管、人事 | 是 | V1预留 |\n| 出勤管理 | 调整出勤状态 | 修正迟到/请假/缺勤 | attendance_record | attendance_log | 状态变化 | 客服主管 | 是 | V1必做 |\n| 排班管理 | 设置班次 | 给客服设置早/午/晚班 | agent、date | shift_schedule | 排班变化 | 客服主管 | 是 | V1必做 |\n| 排班管理 | 批量排班 | 一次生成多天排班 | agent、date_range | shift_schedule | 排班批量变化 | 客服主管 | 是 | V1必做 |\n| 目标管理 | 设置月目标 | 设置RSO/RDO登记和上评目标 | agent、period | support_target | 目标变化 | 客服主管 | 是 | V1必做 |\n| 绩效看板 | 导出绩效 | 导出回复/转化/目标完成 | performance_snapshot | export_task | - | 客服主管 | 是 | V1必做 |\n\n## 06 渠道运营中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| IM推送 | 新增推送 | 创建IM触达任务 | plan、audience、card | im_push_task | 草稿/待发送 | 渠道运营 | 是 | V1必做 |\n| IM推送 | 上架/下架 | 控制任务是否可执行 | im_push_task | channel_log | 已上架/已下架 | 渠道运营 | 是 | V1必做 |\n| IM推送 | 批量分配 | 将任务分配到账号/渠道 | push_task、im_account | assignment | 待分配 -> 已分配 | 渠道运营 | 是 | V1预留 |\n| IM卡片 | 新增卡片 | 创建卡片素材 | product、h5 | im_card | 已上架/草稿 | 渠道运营 | 是 | V1必做 |\n| IM卡片 | 下架卡片 | 产品禁用/素材失效时下架 | im_card、product | card_log | 已上架 -> 已下架 | 渠道运营 | 是 | V1必做 |\n| EDM | 检查基础设施 | 确认域名/邮箱/IP信誉 | edm_config、deliverability | daily_check | 健康/异常 | EDM运营 | 否 | V1必做 |\n| EDM | 创建AB Test | 测试标题/素材/按钮 | edm_campaign | ab_test | 运行中 | EDM运营 | 是 | V1预留 |\n| Phone | 创建回拨任务 | 对未接/待跟进用户安排电话 | tel_pool、agent_shift | tel_task | 待拨打 | Phone/客服 | 是 | V1预留 |\n| 渠道配置 | 配置去重规则 | 定义跨IM/EDM/APP/Phone/KOC触达的去重、例外和频控条件 | channel_config、person、risk、quota | channel_dedup_rule、channel_log | 规则生效/停用 | 渠道主管、管理员 | 是 | V1预留 |\n\n## 07 KOC/KOL协作中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 线索池 | 新增线索 | 新增KOC/KOL候选人 | source、contact | creator_profile | 待审核 | KOC/KOL运营 | 是 | V1预留 |\n| 线索池 | 打标分层 | 区分新手KOC、稳定测评者、垂直专家、带货型KOL | creator_profile、metrics | creator_tag、tier_log | 分层变化 | KOC/KOL运营 | 是 | V1预留 |\n| 任务管理 | 创建合作任务 | 样品、免评、内容、带货任务 | demand、product、creator | creator_task | 待接受 | KOC/KOL运营 | 是 | V1预留 |\n| 任务管理 | 分配样品/免评名额 | 执行合作资源分配 | creator_task、inventory、quota | sample_order/free_order | 待寄样/待执行 | KOC/KOL运营 | 是 | V1预留 |\n| 内容记录 | 上传内容链接 | 记录发布内容 | creator_task | content_record | 待审核/已发布 | KOC/KOL运营 | 是 | V2实现 |\n| 内容记录 | 审核内容 | 判断内容合规和是否可分发 | content_record、content_policy | content_audit | 通过/驳回 | 审核、运营 | 是 | V2实现 |\n| 带货归因 | 同步订单 | 记录Code/链接带来的订单 | attribution、order | creator_order | 待结算 | KOC/KOL运营 | 是 | V2实现 |\n| 佣金结算 | 计算佣金 | 根据规则生成佣金 | creator_order、commission_rule | commission_record | 待审核 | 财务、运营 | 是 | V2实现 |\n| 风险处理 | 暂停合作 | 风险/投诉/作弊时暂停 | creator_profile、risk | creator_status_log | 可合作 -> 暂停 | 风险、主管 | 是 | V1预留 |\n\n## 08 测评人/真实人中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 真实人归并 | 手动合并 | 修正自动归并不足,把多个账号/身份线索合并到同一真实人 | person、identity、quota_ledger、risk | person_identity_link、merge_log、quota_ledger | 多个身份 -> 同一真实人 | 主管、风险、管理员 | 是 | V1预留 |\n| 真实人归并 | 手动拆分 | 纠正误合并,拆开不同真实人 | person、identity、quota_ledger、risk | person_identity_link、split_log、quota_ledger | 同一真实人 -> 多个真实人 | 主管、风险、管理员 | 是 | V1预留 |\n| 额度台账 | 查看额度明细 | 查看4/4/12额度、预占、释放、提交计数来源 | person、quota_ledger、quota_reservation、review_submission | - | - | 运营、客服主管、风险 | 否 | V1必做 |\n| 互动复检 | 查看审计记录 | 追溯每次有效互动为什么继续、拦截或转人工 | interaction_recheck、person、quota_ledger、risk | - | - | 运营、风险、客服主管 | 否 | V1必做 |\n\n## 09 风险与财务\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 风险事件 | 创建风险事件 | 人工或系统发现风险 | person、order、refund、creator | risk_case | 待复核 | 风险、系统 | 是 | V1必做 |\n| 风险事件 | 复核通过 | 确认风险 | risk_case | risk_case、blacklist | 复核中 -> 确认风险 | 风险 | 是 | V1必做 |\n| 风险事件 | 排除风险 | 解除误报 | risk_case | risk_case | 复核中 -> 排除 | 风险 | 是 | V1必做 |\n| 黑名单 | 新增黑名单 | 阻断后续触达/返款/合作 | person、creator、identity | blacklist_item | 生效中 | 风险 | 是 | V1必做 |\n| 退款比对 | 标记双重退款 | Amazon退款+OA返款命中 | refund_records | risk_signal | 疑似/确认双重退款 | 风险、财务 | 是 | V1必做 |\n| 返款 | 审核请款 | 付款前审核 | refund_request、order、risk | refund_record | 待审核 -> 待返款/失败 | 财务 | 是 | V1必做 |\n| 返款 | 确认返款 | 完成付款/卡密发送 | refund_record | refund_record、notification | 待返款 -> 成功 | 财务 | 是 | V1必做 |\n\n## Gate 1 - 按钮盘点完成条件\n\n- 核心页面按钮均有业务含义,不只是UI动作。\n- 每个关键按钮都有读写对象、状态变化、权限和审计要求。\n- 客服执行与客服管理按钮覆盖完整。\n- KOC/KOL按钮覆盖需求完整性,并标注实现阶段。\n- 需求与计划匹配相关按钮覆盖生成、匹配、分配、调整、暂停、补量、关闭。\n- 额度预占释放、真实人合并/拆分、互动复检审计、渠道去重规则已有按钮入口。\n- Stage 2 可按对象命名对照表把页面简称映射到数据对象 v3 的正式对象。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 04 分支流程_完整需求域 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md", + "summary": "USER ERP 0 1需求重构 04 分支流程 完整需求域 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 04 分支流程 完整需求域 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 04 分支流程_完整需求域 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人\n- 核心参与:USER运营、渠道运营、客服、KOC/KOL运营、财务、风险、产品\n- 文件目的:整理主流程下所有核心分支,确保阶段1需求完整,不因V1实现边界遗漏KOC/KOL、客服或计划匹配。\n\n## 分支一:需求与计划匹配\n\n### 触发条件\n\n- OA测评计划新增或变化。\n- 销售/运营提交产品推广需求。\n- ASIN评分、差评、掉评触发回评/紧急催评。\n- 新品、库存、Listing、关键词状态变化。\n- KOC/KOL合作或带货需求进入。\n- 客服反馈某类用户或产品问题集中出现。\n\n### 流程\n\n```text\n需求进入\n-> 校验产品/ASIN/站点/店铺/库存/评分/关键词\n-> 判断需求类型\n-> 匹配计划类型\n-> 匹配执行资源\n-> 生成计划、渠道任务、客服任务或KOC/KOL任务\n-> 执行结果回流需求\n```\n\n### 读取\n\n- demand、product、asin、inventory、rating、review_stats、keyword、h5/card。\n- person_feature、reviewer、creator、quota、risk。\n- channel_config、agent_shift、agent_capacity、refund_capacity。\n\n### 写入\n\n- plan、plan_change_log、audience_snapshot、quota_reservation。\n- push_task、ticket、creator_task、risk_exclusion。\n\n### 关闭条件\n\n- 需求已转计划并完成。\n- 需求被驳回并有原因。\n- 需求被拆分或合并,并保留引用关系。\n- 需求进入待补充或待恢复,不再误触发执行。\n\n### 阶段\n\nV1必做。\n\n## 分支二:IM推送\n\n### 用户分层\n\n| 层级 | 定义 | 策略 |\n| --- | --- | --- |\n| 未参与过用户 | 注册App并绑定玩具,未参与回评/测评 | 优先推绑定产品回评卡片 |\n| 参与过用户 | 已参与回评/测评,真实人累计提交评价 < 12 | 优先催评,再推新测评 |\n| 长期测评人 | 真实人累计提交评价 >= 12 | 不优先普通测评,优先免评补单 |\n| 回评待完成 | 订单已登记但未上传评价/截图 | 催评 |\n| 测评待返款 | 已登记但缺返款信息或待返款 | 补信息/财务提醒 |\n\n### 流程\n\n```text\n人群生成\n-> 频控与风险复检\n-> 选择卡片/H5/文字/图片\n-> 推送\n-> 用户点击/回复/提交信息\n-> 系统核验订单号/返款账号/评论截图\n-> 完整则进入订单/返款/评价流程\n-> 不完整则生成客服工单或待补信息标签\n```\n\n### 必查\n\n- 用户是否黑名单。\n- 读取 `person_quota_ledgers`,按真实人判断累计提交评价、月度测评和月度免评额度。\n- 是否有未完成测评/回评。\n- 是否退款/取消订单。\n- 产品是否禁用。\n- H5/卡片是否有效。\n- 同一用户今日是否已触达。\n\n### 阶段\n\nV1必做。\n\n## 分支三:EDM每日运营\n\n### 每日先看\n\n- 域名、邮箱、IP信誉是否健康。\n- OA测评计划是否变化。\n- KV/UID人群是否正常。\n- 产品选择、H5制作和H5数据维护是否受产品禁用影响。\n- 邮件发送、打开、点击、回复、退订、投诉。\n- 当天推送计划、AB Test、审核状态。\n- 菲律宾问题、订单、客服跟进清单。\n- 邮件转化数据。\n\n### 流程\n\n```text\n基础设施检查\n-> 同步OA计划\n-> 检查UID人群\n-> 检查产品/H5/链接\n-> 生成或调整EDM任务\n-> 发送与AB Test\n-> 回收打开/点击/回复/退订/投诉\n-> 转化到订单/客服/风险/复盘\n```\n\n### 阶段\n\nV1必做基础;深度AB自动优化 V2实现。\n\n## 分支四:Phone工作流\n\n### 每日先看\n\n- 未接电话和待回拨。\n- 问题类型分布。\n- 国家/语言/时段。\n- 哪个国家电话量最高。\n- 哪个国家未接率最高。\n- 高峰时段客服是否不足。\n- 是否需要调整排班或回拨时间。\n- 客服效率和转化。\n\n### 流程\n\n```text\n生成电话名单\n-> 匹配国家/语言/时段/客服排班\n-> 拨打或回拨\n-> 记录通话结果\n-> 如有订单/评价意向则进入客服转化\n-> 如有投诉/售后则进入工单\n-> 如有KOC/KOL意向则转线索池\n```\n\n### 阶段\n\nV1预留,客服和Phone结合较强时进入V1必做。\n\n## 分支五:客服承接\n\n### 入口\n\n- 用户通过App/Email/IM/Phone发送消息。\n- 用户提交信息不完整。\n- 渠道推送带来咨询、投诉或售后问题。\n- 测评/回评/返款异常。\n- 用户答应配合后需要跟进。\n\n### 流程\n\n```text\n用户消息进入\n-> 系统生成待处理工单\n-> 自动或手动分配客服\n-> 客服查看上下文卡\n-> 客服回复和补全信息\n-> 登记订单/催评/上传结果/升级异常\n-> 工单解决或关闭\n-> 回复、转化、满意度、目标数据回流\n```\n\n### 客服转化流程\n\n```text\n客服沟通用户\n-> 用户下单或提供订单号\n-> 客服登记订单\n-> 后续拿到用户评价\n-> 上传评价结果\n-> 返款/核验\n-> 完成转化\n```\n\n### 管理流程\n\n```text\n排班\n-> 出勤\n-> 工单分配\n-> 回复统计\n-> RSO/RDO转化统计\n-> 月目标管理\n-> 绩效复盘\n```\n\n### 阶段\n\nV1必做。\n\n## 分支六:评价型订单与免评执行\n\n### 6A 评价型订单:测评/回评\n\n```text\n计划分配人群\n-> 生成订单或等待用户提交订单\n-> 核验订单号\n-> 绑定产品/ASIN/店铺/站点\n-> 上传回评/评论链接/截图\n-> 展示核验\n-> 返款/请款\n-> 完成或异常关闭\n```\n\n#### 关键规则\n\n- 非公司产品阻断。\n- 订单撤销或退款阻断。\n- 用户提交评价不等于Amazon展示确认。\n- 提交评价立即计入真实人累计提交额度;展示确认才计入评价型计划完成。\n- 更换订单、更改订单、转免评、撤销必须有日志。\n\n### 6B 免评执行:KOC/KOL为主,IM/EDM/APP协同\n\n```text\n免评计划批准\n-> 拆解KOC/KOL任务、免评Code、内容协同任务\n-> 匹配KOC/KOL、长期测评人或站外流量资源\n-> 发布内容、分发Code、同步IM/EDM/APP协同触达\n-> 回收点击、Code使用、订单、内容互动、权重结果\n-> 形成免评结果快照\n-> 达标关闭或调整KOC/素材/Code/渠道策略\n```\n\n#### 关键规则\n\n- 免评执行不以“用户提交评价”为终点,也不以 Amazon 展示评价作为完成口径。\n- 免评结果应写入 `exemption_plan_tasks`、`creator_content_records`、`exemption_result_snapshots` 等对象。\n- KOC/KOL任务、长期测评人免评补单、IM/EDM/APP协同触达可以服务同一个免评计划,但必须分别记录任务来源和结果口径。\n- 免评Code、点击、订单、内容链接、带货归因和权重结果需要单独复盘,不混入测评/回评订单完成数。\n\n### 阶段\n\n6A V1必做;6B 的免评补单与协同入口 V1必做,完整内容/归因/佣金闭环 V2实现。\n\n## 分支七:财务返款\n\n### 用户返款\n\n```text\n返款条件满足\n-> 发起请款\n-> 财务/审核确认\n-> 付款或礼品卡\n-> 凭证/卡密通知用户\n-> 返款状态回写订单和工单\n```\n\n### KOC/KOL佣金\n\n```text\n带货订单归因\n-> 计算佣金\n-> 审核\n-> 结算\n-> 争议处理\n-> 复盘表现\n```\n\n### 阶段\n\n用户返款 V1必做;KOC/KOL佣金 V1预留,V2实现。\n\n## 分支八:KOC/KOL协作\n\n### 入口\n\n- 新增KOC/KOL线索。\n- KOC每日工作流发现可合作用户。\n- 免评计划需要长期测评人或达人补单。\n- 商家/产品需要内容或带货合作。\n\n### 流程\n\n```text\n线索进入\n-> 账号/标签/风险状态检查\n-> 自动打标与人工修正\n-> 分层:新手KOC/稳定测评者/垂直专家/带货型KOL/品牌合作型\n-> 匹配产品/样品/免评/内容/带货任务\n-> 达人接受或拒绝\n-> 样品/订单/内容执行\n-> 上传内容链接或带货链接\n-> 审核、发布、归因\n-> 佣金/返款\n-> 风险与复盘\n```\n\n### 必须区分\n\n- KOC/KOL身份和普通测评人身份可能重叠,但业务对象不同。\n- KOC/KOL标签、带货、测评、免评标签不能混。\n- KOC/KOL内容任务和普通回评/测评订单不能混用完成口径。\n\n### 阶段\n\n需求完整性必须覆盖;V1预留核心入口与字段,免评补单相关 V1必做,完整内容/佣金 V2实现。\n\n## 分支九:风险与黑名单\n\n### 流程\n\n```text\n风险信号产生\n-> 判断弱风险/强风险\n-> 弱风险提醒人工确认\n-> 强风险阻断执行\n-> 人工复核\n-> 解除/确认风险\n-> 同步黑名单或恢复执行\n```\n\n### 风险来源\n\n- 黑名单命中。\n- 邮箱/电话/设备/IP/Profile/地址强弱关联。\n- 退款/取消订单。\n- 双重退款。\n- 多账号/多Profile异常。\n- 重复订单、重复评论、重复返款。\n- 差评/掉评异常。\n- KOC/KOL内容违规、佣金争议、带货归因异常。\n\n### 阶段\n\nV1必做。\n\n## Gate 1 - 分支流程完成条件\n\n- 所有核心分支都有触发、执行、写入、关闭条件。\n- 客服与KOC/KOL均作为完整需求域出现。\n- 分支结果能回流主流程和复盘。\n- 各分支有 V1必做、V1预留、V2实现边界。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 05 异常流程_完整需求域 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md", + "summary": "USER ERP 0 1需求重构 05 异常流程 完整需求域 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 05 异常流程 完整需求域 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 05 异常流程_完整需求域 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人 / 风险负责人 / 客服主管\n- 核心参与:USER运营、渠道运营、客服、KOC/KOL运营、财务、风险、测试\n- 文件目的:定义完整需求域中的异常、拦截、提醒、升级、人工复核和恢复机制,为 Stage 2 测试用例和状态机做输入。\n\n## 异常分级\n\n| 等级 | 含义 | 处理要求 |\n| --- | --- | --- |\n| P0 | 直接影响今日核心目标、资金、合规或账号安全 | 当天处理,主管可见 |\n| P1 | 影响本周目标或大量用户体验 | 24小时内处理 |\n| P2 | 局部异常,可进入跟进列表 | 按责任人处理 |\n| P3 | 观察项,暂不阻断 | 持续监控 |\n\n## 01 需求与执行计划不匹配\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 需求目标高于可用人群/额度 | 计划生成时匹配不足 | 提醒运营降低目标、拆分周期、转免评或补充渠道 | 目标调整或资源补足 | V1必做 |\n| 产品禁用但计划仍在执行 | 产品状态变化 | 自动暂停计划和下架H5/卡片,通知负责人 | 产品恢复或计划关闭 | V1必做 |\n| 库存不足但继续推测评/免评 | 库存低于安全线 | 限制节奏或暂停,通知运营 | 库存恢复或调整计划 | V1必做 |\n| ASIN/店铺/关键词变化未同步 | ASIN信息与计划不一致 | 阻断新推送,生成H5/卡片维护任务 | 链接和卡片确认有效 | V1必做 |\n| 计划有名额但渠道无可推人群 | 人群生成结果为空或不足 | 提醒补充人群、换渠道、延长周期 | 匹配成功或关闭计划 | V1必做 |\n| 渠道有响应但客服容量不足 | 工单/回复超时、在线人数不足 | 暂停或降频推送,调整排班 | 客服容量恢复 | V1必做 |\n| 需求变更后旧计划未暂停 | 同需求存在多个冲突计划 | 提醒主管确认,自动标记冲突 | 旧计划暂停/合并/关闭 | V1预留 |\n| 完成数口径不一致 | 计划完成、订单完成、评价展示不一致 | 进入口径复核 | 口径修正并记录 | V1必做 |\n\n## 02 评价/订单异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 订单号格式错误 | 用户提交时校验 | 提示用户重填,必要时转客服 | 正确订单号提交 | V1必做 |\n| 非公司产品 | 订单核验 | 阻断登记,转客服说明 | 工单关闭或人工改判 | V1必做 |\n| 店铺下错 | 订单店铺与计划不一致 | 转人工处理,可转免评 | 订单状态修正 | V1必做 |\n| 订单已取消 | Amazon状态 | 阻断回评和返款 | 用户换单或关闭 | V1必做 |\n| 订单已退款 | Amazon退款命中 | 进入退款比对和风险 | 风险结论完成 | V1必做 |\n| 评论重复绑定 | 评论ID/链接已绑定 | 阻断或申请例外审核 | 绑定唯一或审核通过 | V1必做 |\n| 订单重复绑定 | 订单号已绑定其他单 | 阻断,进入人工复核 | 确认归属 | V1必做 |\n| 用户只提交部分信息 | 缺返款账号/截图/链接 | 自动打标,生成客服跟进 | 信息补齐或关闭 | V1必做 |\n| 评论未展示 | Amazon展示核验失败 | 标记未展示,计划不计完成 | 后续展示或关闭 | V1必做 |\n| 掉评/差评 | Review状态变化 | 触发回评/紧急催评/风险 | 计划调整或风险关闭 | V1必做 |\n\n## 03 返款/佣金异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 缺返款账号 | 用户提交不完整 | 自动打标待返款,客服跟进 | 账号补齐 | V1必做 |\n| 返款账号格式异常 | 表单校验/人工审核 | 退回补充 | 格式正确 | V1必做 |\n| 重复请款 | 同订单/同返款ID重复 | 阻断并提醒财务 | 去重完成 | V1必做 |\n| 双重退款 | Amazon退款+OA返款命中 | 生成风险事件 | 风险确认或解除 | V1必做 |\n| 非APP用户邮件退款 | 邮件退款链路缺设备/注册邮箱维度 | 标记风险盲区,补充邮箱、订单、地址、收款信息等识别线索 | 识别维度补齐或人工复核完成 | V1必做 |\n| 返款超额 | 金额超过规则 | 进入超额审核 | 审核通过/拒绝 | V1必做 |\n| 卡密/凭证缺失 | 返款成功但无凭证 | 生成财务待补任务 | 凭证补齐 | V1预留 |\n| KOC/KOL佣金争议 | 达人反馈或金额不一致 | 进入佣金复核 | 结算修正或驳回 | V2实现 |\n\n## 04 客服异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 无客服在线 | 在线人数为0但有待处理工单 | 提醒主管,暂停部分推送 | 有客服承接 | V1必做 |\n| 工单未分配 | 待分配超时 | 自动分配或主管手动分配 | 工单有处理人 | V1必做 |\n| 首次回复超时 | 首次回复时长超过阈值 | 提醒客服/主管 | 已回复或转移 | V1必做 |\n| 等待用户超时 | 用户长时间未回复 | 自动提醒或关闭 | 用户回复/关闭 | V1必做 |\n| 客服转移失败 | 目标客服离线或超负荷 | 重新选择处理人 | 转移成功 | V1必做 |\n| 排班不足 | 高峰工单大于客服容量 | 主管调整排班或降频推送 | 容量恢复 | V1必做 |\n| 目标异常 | 月目标明显低于进度 | 提醒主管调度 | 目标追平或调整 | V1必做 |\n| 客服误登记订单 | 人工发现或订单核验失败 | 更改订单并留痕 | 订单修正 | V1必做 |\n| 用户投诉客服 | 投诉/负反馈 | 升级主管复核 | 处理结论 | V1预留 |\n\n## 05 渠道异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| IM点击低 | 漏斗低于阈值 | 换素材/卡片/人群 | 指标恢复或复盘 | V1必做 |\n| IM回复低于3% | 日常监控 | 更换图片/话术 | 回复率恢复 | V1必做 |\n| 退订或不感兴趣升高 | 退订/投诉异常 | 降频、暂停素材、调整人群 | 指标恢复 | V1必做 |\n| EDM域名/IP信誉异常 | 每日检查 | 暂停发送、切换账号、通知负责人 | 信誉恢复 | V1必做 |\n| KV/UID人群导出失败 | 人群数量异常 | 暂停推送,重跑导出 | 人群正常 | V1必做 |\n| H5链接失效 | 链接检测或用户反馈 | 下架任务,修复链接 | 链接有效 | V1必做 |\n| 卡片产品禁用未同步 | 产品状态变更 | 自动下架卡片 | 卡片状态正确 | V1必做 |\n| Phone未接率过高 | 电话数据 | 调整时段/排班/回拨 | 未接率下降 | V1预留 |\n\n## 06 KOC/KOL异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| KOC标签混乱 | 同人存在KOC/测评/带货冲突 | 人工复核标签 | 标签修正 | V1预留 |\n| KOC/KOL与C类测评人身份重叠 | 同一真实人同时命中免评推送和KOC任务 | 阻断重复触达,进入身份/任务冲突复核 | 明确走免评协同或KOC任务,不重复计完成 | V1必做 |\n| 达人重复触达 | 同一KOC/KOL多任务冲突 | 合并或暂停任务 | 冲突解除 | V1预留 |\n| 样品发出未产出内容 | 任务超时 | 催办、暂停合作、风险记录 | 内容提交或任务关闭 | V2实现 |\n| 内容链接无效 | 链接检测失败 | 要求重提 | 链接有效 | V2实现 |\n| 内容不合规 | 内容审核 | 驳回、下架、降权 | 内容修正或关闭 | V2实现 |\n| 带货订单归因失败 | Code/链接无法匹配 | 进入人工归因 | 归因完成或驳回 | V2实现 |\n| 佣金争议 | 金额或订单争议 | 财务/运营复核 | 结算修正或驳回 | V2实现 |\n| 高风险KOC继续推送 | 风险状态命中 | 阻断任务 | 风险解除或拉黑 | V1预留 |\n\n## 07 风险异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 黑名单命中 | 身份线索命中 | 阻断触达/返款/合作 | 人工解除或保持黑名单 | V1必做 |\n| 强关联命中 | 邮箱/电话/设备/Profile/地址 | 阻断并复核 | 风险结论 | V1必做 |\n| 弱关联命中 | IP/相似地址等 | 提醒人工确认 | 确认/排除 | V1必做 |\n| 额度超限 | 月度/累计额度命中 | 阻断普通测评,转免评池 | 额度恢复或转免评 | V1必做 |\n| 多账号归并冲突 | 同一真实人下多个 JOYHUB/Profile 额度不一致 | 按 `person_quota_ledgers` 重新汇总,进入人工复核 | 额度统一并记录合并/拆分结论 | V1必做 |\n| 频控超限 | 当日/周期触达超限 | 暂停触达 | 到期恢复 | V1必做 |\n| 敏感字段导出风险 | 导出包含敏感字段 | 强制脱敏/审批 | 导出完成或拒绝 | V1必做 |\n\n## 异常处理通用状态\n\n```text\n待发现 -> 已发现 -> 待处理 -> 处理中 -> 待复核 -> 已关闭\n```\n\n可选状态:\n\n- 已升级。\n- 已驳回。\n- 已阻断。\n- 已恢复。\n- 需补充信息。\n- 需跨部门协同。\n\n## Gate 1 - 异常流程完成条件\n\n- 需求计划、评价订单、客服、渠道、KOC/KOL、返款、风险异常均覆盖。\n- 每类异常有发现方式、处理动作、关闭条件。\n- P0/P1/P2/P3等级可用于今日作战台。\n- 异常能回流计划调整、客服任务、风险事件和复盘记录。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md", + "summary": "USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:产品负责人 / 前端观察员\n- 核心参与:业务负责人、USER运营、客服主管、渠道负责人、KOC/KOL负责人、技术负责人\n- 文件目的:把现有 Vibe Coding / AI 原型作为需求验证材料,记录已覆盖能力、暴露缺口和 Stage 2 高保真模型输入。\n\n## 验证原则\n\n1. 原型不是生产代码,不作为正式系统底座。\n2. 原型用于发现页面、字段、按钮、状态和流程缺口。\n3. 验证重点不是“页面能不能打开”,而是“需求是否完整、业务对象是否统一、每日操作是否可执行”。\n4. 阶段1必须记录原型未覆盖的需求域,特别是 KOC/KOL、客服管理、需求计划匹配和复盘口径。\n\n## 验证材料\n\n| 材料 | 用途 | 结论 |\n| --- | --- | --- |\n| `C:\\XCODE\\USER\\input\\user-review-system` | React/Vite原型,含计划、测评人、订单、客服、渠道、风险、看板;入口参考 `src\\App.tsx`、`src\\config\\routes.tsx`、`src\\config\\menu.tsx` | 覆盖面较广,可作为页面/字段参考 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-PRD.md` | 回评/测评订单详细PRD | 订单页面细,但只是局部,不足以代表ERP主流程 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-architecture.md` | 订单页面技术架构 | 可参考组件拆分和待确认问题 |\n| `C:\\XCODE\\USER\\src\\user_erp_mvp_admin_prototype_v10.html` | 既有 v10 HTML 原型 | 可参考早期管理端页面,但不直接复用为新系统底座 |\n| `C:\\XCODE\\USER\\input\\用户运营系统-单文件.html` | 大型单文件运营系统原型 | 可参考全局菜单和字段,但不直接复用 |\n| `C:\\XCODE\\USER\\input\\amazon_operator_test_entry.html` | 亚马逊提评入口原型 | 可参考需求提报、IM账号、推送策略 |\n| `C:\\XCODE\\USER\\input\\客服执行.html` | 客服执行/管理原型 | 客服核心需求的重要参考 |\n| `C:\\XCODE\\USER\\input\\IM 推送业务流.mm` | IM业务流脑图 | IM分层和流转最完整 |\n\n## user-review-system 验证\n\n### 已覆盖能力\n\n| 模块 | 已覆盖 |\n| --- | --- |\n| 菜单/路由 | 工作台、评价计划、评价人管理、测评订单、回评订单、客服中心、渠道推送、风险中心、数据看板 |\n| 计划 | 测评计划、回评计划、免评计划,含列表、表单、详情、关联推送/订单 |\n| 测评人 | 测评人列表、详情、表单、额度、订单、风险、联系记录 |\n| 订单 | 测评订单、回评订单、上传订单、上传回评、请款、返款、详情抽屉 |\n| 客服 | 工单池、聊天、聊天记录、答应配合、客服绩效看板 |\n| 渠道 | 推送任务、IM推送、IM卡片、IM/EDM配置 |\n| 风险 | 风险事件、黑名单、退款比对 |\n| 看板 | 计划、ASIN、客服绩效 |\n\n### 暴露的缺口\n\n| 缺口 | 影响 | 后续处理 |\n| --- | --- | --- |\n| 需求池与计划生成链路弱 | 无法解释需求如何变成计划 | Stage 2 需新增需求与计划调度中心 |\n| 计划匹配资源视图不足 | 人群、客服、渠道、H5/卡片、风险没有统一调度 | 需新增执行匹配看板 |\n| 客服管理不完整 | 排班、出勤、目标、首次回复、转化绩效不足 | 结合客服执行原型补齐 |\n| KOC/KOL需求域不足 | 线索、任务、内容、带货、佣金缺失 | 阶段1必须补出完整需求 |\n| 订单PRD局部过细 | 容易用订单页面替代ERP主流程 | 保留为订单模块参考 |\n| 状态枚举不统一 | 测评、回评、返款、计划、客服状态口径混乱 | Stage 2需统一状态机 |\n| Mock 数据含临时品类 | 部分示例与真实业务不一致 | 只取字段结构,不取业务事实 |\n\n## 用户运营系统单文件验证\n\n### 已覆盖\n\n- USER评价业务闭环系统的全局菜单雏形。\n- 产品、计划、订单、客服、风险、真实人、用户信息等多类字段。\n- 较多运营筛选、状态、详情、操作入口。\n\n### 缺口\n\n- 文件过大,页面和数据逻辑混杂,不适合做正式底座。\n- 未清晰区分需求完整范围和V1实现范围。\n- KOC/KOL相关需求不完整。\n- 客服核心执行与管理需要与 `客服执行.html` 合并分析。\n- 需求到计划再到执行匹配的调度视图不足。\n\n## amazon_operator_test_entry 验证\n\n### 已覆盖\n\n- 亚马逊提评入口。\n- 提评单列表。\n- IM推送。\n- IM账号管理。\n- 新建提评单。\n- 推送策略。\n- 卡片内容管理。\n- 业务类型:测评、免评、回评。\n\n### 可纳入需求\n\n- 提评需求提报字段:站点、ASIN、商品、关键词、关键词链接、品牌、新品状态、要求数量、店铺、原因、备注。\n- IM账号和身份管理:官方客服、品牌客服、达人、内部/外部品牌。\n- 推送策略:用户标签、去除标签、优先级、间隔时间、内容形式、跳转链接。\n\n### 缺口\n\n- 未打通完整需求池和计划对象。\n- 提评入口与USER运营调度中心的关系需重构。\n- IM账号管理需与渠道配置、权限和风险联动。\n\n## 客服执行原型验证\n\n### 已覆盖\n\n- 客服执行看板。\n- 首页Dashboard。\n- 出勤管理。\n- 排班管理。\n- 工单管理。\n- 回复统计。\n- 转化绩效。\n- 目标管理。\n- 角色权限:客服与主管/管理员视角。\n\n### 可纳入需求\n\n| 能力 | 说明 |\n| --- | --- |\n| 工单自动分配 | 在线优先、按工单量平均、最大工单数 |\n| 客服视角 | 只看自己工单、绩效和目标 |\n| 主管视角 | 查看团队数据、调整工单、排班和目标 |\n| 绩效统计 | RSO/RDO登记订单、上评、完成率 |\n| 出勤/排班 | 早班、午班、晚班、迟到、早退、请假、缺勤 |\n\n### 缺口\n\n- 与评价计划、渠道推送和订单履约的联动需要补强。\n- 客服容量尚未进入需求与计划匹配。\n- 客服误登记、投诉、转移失败、排班不足等异常需要补全。\n\n## IM推送业务流验证\n\n### 已覆盖\n\n- 用户分层:未参与、参与过、长期测评人。\n- 推送前风控校验。\n- 回评卡片、测评卡片、免评产品、催评消息。\n- 用户提交订单号、返款账号、评论截图/链接。\n- 订单号核实和客服补全。\n- 财务返款提醒。\n- 自动打标体系。\n- 店铺紧急催评。\n\n### 可直接作为Stage 2输入\n\n- IM用户分层。\n- 核心标签体系。\n- 每次互动复检。\n- 未完成流程的标签与自动通知。\n- 长期测评人转免评策略。\n\n### 缺口\n\n- IM流程需要与统一计划、额度台账、客服工单和风险事件对象对齐。\n- 文档中提到的“当天测评计划需要刷的名额”需要接入计划匹配。\n\n## KOC/KOL需求验证\n\n### 已有线索\n\n- `KOC 每日工作流.md` 提到账号、标签、风险、新增线索、用户分层、自动打标、任务去重、回复、订单、返款、Review、带货、风险。\n- `商家KOL商业服务系统-USER项目知识迁移清单.md` 提供服务包、KOC/KOL分层、佣金原则、内容分级和推荐分发接口意识。\n- 现有原型仅零散覆盖KOC/KOL,未形成完整中心。\n\n### 阶段1必须补出的需求\n\n- KOC/KOL线索池。\n- KOC/KOL档案和分层。\n- KOC/KOL与测评人/真实人的身份关系。\n- 内容任务、样品/免评、带货任务。\n- 内容审核、链接、Code、订单归因。\n- 佣金计算与结算接口。\n- KOC/KOL风险和异常。\n\n### 阶段建议\n\n- V1必做:免评补单相关的KOC/KOL入口、标签、风险和任务关联。\n- V1预留:KOC/KOL档案、线索池、合作状态。\n- V2实现:内容审核、带货归因、佣金结算、商家服务包完整后台。\n\n## 进入Stage 2的输入清单\n\n| 输入 | 说明 |\n| --- | --- |\n| 需求与计划调度中心 | 需要新设计,不应从现有订单页面改 |\n| 今日作战台 | 需要按OKR和岗位每日工作方式设计 |\n| 客服执行/管理 | 可综合 `客服相关模块.md` 和 `客服执行.html` |\n| IM推送 | 可基于 `IM 推送业务流.mm` 和 user-review-system IM页面 |\n| 订单页面 | 可参考 review-order PRD,但需合并测评/回评/免评口径 |\n| 测评人/真实人 | 可参考 `测评信息字段管理.xlsx` 和数据对象v3 |\n| KOC/KOL中心 | 现有原型不足,需要新建需求模型 |\n| 风险中心 | 可参考现有风险页面,但需扩展KOC/KOL与计划匹配异常 |\n\n## 不能直接继承的内容\n\n- 不能直接继承 Vibe Coding 的文件结构作为正式工程结构。\n- 不能把 mock 数据当业务事实。\n- 不能把回评/测评订单PRD当成整个 ERP PRD。\n- 不能因为 V1不实现完整KOC/KOL平台,就在阶段1删除KOC/KOL需求。\n- 不能把客服降级为“工单页面”,客服是核心执行和转化资源。\n\n## Gate 1 - Vibe Coding验证完成条件\n\n- 已记录所有原型的可用需求点。\n- 已记录原型不能支撑的业务缺口。\n- 已确认正式系统需要重新做 Stage 2 高保真模型。\n- 已确认KOC/KOL、客服、需求计划匹配是现有原型的主要补强方向。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1)", + "type": "document", + "name": "USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1).md", + "summary": "USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:产品负责人 / 前端观察员\n- 核心参与:业务负责人、USER运营、客服主管、渠道负责人、KOC/KOL负责人、技术负责人\n- 文件目的:把现有 Vibe Coding / AI 原型作为需求验证材料,记录已覆盖能力、暴露缺口和 Stage 2 高保真模型输入。\n\n## 验证原则\n\n1. 原型不是生产代码,不作为正式系统底座。\n2. 原型用于发现页面、字段、按钮、状态和流程缺口。\n3. 验证重点不是“页面能不能打开”,而是“需求是否完整、业务对象是否统一、每日操作是否可执行”。\n4. 阶段1必须记录原型未覆盖的需求域,特别是 KOC/KOL、客服管理、需求计划匹配和复盘口径。\n\n## 验证材料\n\n| 材料 | 用途 | 结论 |\n| --- | --- | --- |\n| `C:\\XCODE\\USER\\input\\user-review-system` | React/Vite原型,含计划、测评人、订单、客服、渠道、风险、看板;入口参考 `src\\App.tsx`、`src\\config\\routes.tsx`、`src\\config\\menu.tsx` | 覆盖面较广,可作为页面/字段参考 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-PRD.md` | 回评/测评订单详细PRD | 订单页面细,但只是局部,不足以代表ERP主流程 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-architecture.md` | 订单页面技术架构 | 可参考组件拆分和待确认问题 |\n| `C:\\XCODE\\USER\\src\\user_erp_mvp_admin_prototype_v10.html` | 既有 v10 HTML 原型 | 可参考早期管理端页面,但不直接复用为新系统底座 |\n| `C:\\XCODE\\USER\\input\\用户运营系统-单文件.html` | 大型单文件运营系统原型 | 可参考全局菜单和字段,但不直接复用 |\n| `C:\\XCODE\\USER\\input\\amazon_operator_test_entry.html` | 亚马逊提评入口原型 | 可参考需求提报、IM账号、推送策略 |\n| `C:\\XCODE\\USER\\input\\客服执行.html` | 客服执行/管理原型 | 客服核心需求的重要参考 |\n| `C:\\XCODE\\USER\\input\\IM 推送业务流.mm` | IM业务流脑图 | IM分层和流转最完整 |\n\n## user-review-system 验证\n\n### 已覆盖能力\n\n| 模块 | 已覆盖 |\n| --- | --- |\n| 菜单/路由 | 工作台、评价计划、评价人管理、测评订单、回评订单、客服中心、渠道推送、风险中心、数据看板 |\n| 计划 | 测评计划、回评计划、免评计划,含列表、表单、详情、关联推送/订单 |\n| 测评人 | 测评人列表、详情、表单、额度、订单、风险、联系记录 |\n| 订单 | 测评订单、回评订单、上传订单、上传回评、请款、返款、详情抽屉 |\n| 客服 | 工单池、聊天、聊天记录、答应配合、客服绩效看板 |\n| 渠道 | 推送任务、IM推送、IM卡片、IM/EDM配置 |\n| 风险 | 风险事件、黑名单、退款比对 |\n| 看板 | 计划、ASIN、客服绩效 |\n\n### 暴露的缺口\n\n| 缺口 | 影响 | 后续处理 |\n| --- | --- | --- |\n| 需求池与计划生成链路弱 | 无法解释需求如何变成计划 | Stage 2 需新增需求与计划调度中心 |\n| 计划匹配资源视图不足 | 人群、客服、渠道、H5/卡片、风险没有统一调度 | 需新增执行匹配看板 |\n| 客服管理不完整 | 排班、出勤、目标、首次回复、转化绩效不足 | 结合客服执行原型补齐 |\n| KOC/KOL需求域不足 | 线索、任务、内容、带货、佣金缺失 | 阶段1必须补出完整需求 |\n| 订单PRD局部过细 | 容易用订单页面替代ERP主流程 | 保留为订单模块参考 |\n| 状态枚举不统一 | 测评、回评、返款、计划、客服状态口径混乱 | Stage 2需统一状态机 |\n| Mock 数据含临时品类 | 部分示例与真实业务不一致 | 只取字段结构,不取业务事实 |\n\n## 用户运营系统单文件验证\n\n### 已覆盖\n\n- USER评价业务闭环系统的全局菜单雏形。\n- 产品、计划、订单、客服、风险、真实人、用户信息等多类字段。\n- 较多运营筛选、状态、详情、操作入口。\n\n### 缺口\n\n- 文件过大,页面和数据逻辑混杂,不适合做正式底座。\n- 未清晰区分需求完整范围和V1实现范围。\n- KOC/KOL相关需求不完整。\n- 客服核心执行与管理需要与 `客服执行.html` 合并分析。\n- 需求到计划再到执行匹配的调度视图不足。\n\n## amazon_operator_test_entry 验证\n\n### 已覆盖\n\n- 亚马逊提评入口。\n- 提评单列表。\n- IM推送。\n- IM账号管理。\n- 新建提评单。\n- 推送策略。\n- 卡片内容管理。\n- 业务类型:测评、免评、回评。\n\n### 可纳入需求\n\n- 提评需求提报字段:站点、ASIN、商品、关键词、关键词链接、品牌、新品状态、要求数量、店铺、原因、备注。\n- IM账号和身份管理:官方客服、品牌客服、达人、内部/外部品牌。\n- 推送策略:用户标签、去除标签、优先级、间隔时间、内容形式、跳转链接。\n\n### 缺口\n\n- 未打通完整需求池和计划对象。\n- 提评入口与USER运营调度中心的关系需重构。\n- IM账号管理需与渠道配置、权限和风险联动。\n\n## 客服执行原型验证\n\n### 已覆盖\n\n- 客服执行看板。\n- 首页Dashboard。\n- 出勤管理。\n- 排班管理。\n- 工单管理。\n- 回复统计。\n- 转化绩效。\n- 目标管理。\n- 角色权限:客服与主管/管理员视角。\n\n### 可纳入需求\n\n| 能力 | 说明 |\n| --- | --- |\n| 工单自动分配 | 在线优先、按工单量平均、最大工单数 |\n| 客服视角 | 只看自己工单、绩效和目标 |\n| 主管视角 | 查看团队数据、调整工单、排班和目标 |\n| 绩效统计 | RSO/RDO登记订单、上评、完成率 |\n| 出勤/排班 | 早班、午班、晚班、迟到、早退、请假、缺勤 |\n\n### 缺口\n\n- 与评价计划、渠道推送和订单履约的联动需要补强。\n- 客服容量尚未进入需求与计划匹配。\n- 客服误登记、投诉、转移失败、排班不足等异常需要补全。\n\n## IM推送业务流验证\n\n### 已覆盖\n\n- 用户分层:未参与、参与过、长期测评人。\n- 推送前风控校验。\n- 回评卡片、测评卡片、免评产品、催评消息。\n- 用户提交订单号、返款账号、评论截图/链接。\n- 订单号核实和客服补全。\n- 财务返款提醒。\n- 自动打标体系。\n- 店铺紧急催评。\n\n### 可直接作为Stage 2输入\n\n- IM用户分层。\n- 核心标签体系。\n- 每次互动复检。\n- 未完成流程的标签与自动通知。\n- 长期测评人转免评策略。\n\n### 缺口\n\n- IM流程需要与统一计划、额度台账、客服工单和风险事件对象对齐。\n- 文档中提到的“当天测评计划需要刷的名额”需要接入计划匹配。\n\n## KOC/KOL需求验证\n\n### 已有线索\n\n- `KOC 每日工作流.md` 提到账号、标签、风险、新增线索、用户分层、自动打标、任务去重、回复、订单、返款、Review、带货、风险。\n- `商家KOL商业服务系统-USER项目知识迁移清单.md` 提供服务包、KOC/KOL分层、佣金原则、内容分级和推荐分发接口意识。\n- 现有原型仅零散覆盖KOC/KOL,未形成完整中心。\n\n### 阶段1必须补出的需求\n\n- KOC/KOL线索池。\n- KOC/KOL档案和分层。\n- KOC/KOL与测评人/真实人的身份关系。\n- 内容任务、样品/免评、带货任务。\n- 内容审核、链接、Code、订单归因。\n- 佣金计算与结算接口。\n- KOC/KOL风险和异常。\n\n### 阶段建议\n\n- V1必做:免评补单相关的KOC/KOL入口、标签、风险和任务关联。\n- V1预留:KOC/KOL档案、线索池、合作状态。\n- V2实现:内容审核、带货归因、佣金结算、商家服务包完整后台。\n\n## 进入Stage 2的输入清单\n\n| 输入 | 说明 |\n| --- | --- |\n| 需求与计划调度中心 | 需要新设计,不应从现有订单页面改 |\n| 今日作战台 | 需要按OKR和岗位每日工作方式设计 |\n| 客服执行/管理 | 可综合 `客服相关模块.md` 和 `客服执行.html` |\n| IM推送 | 可基于 `IM 推送业务流.mm` 和 user-review-system IM页面 |\n| 订单页面 | 可参考 review-order PRD,但需合并测评/回评/免评口径 |\n| 测评人/真实人 | 可参考 `测评信息字段管理.xlsx` 和数据对象v3 |\n| KOC/KOL中心 | 现有原型不足,需要新建需求模型 |\n| 风险中心 | 可参考现有风险页面,但需扩展KOC/KOL与计划匹配异常 |\n\n## 不能直接继承的内容\n\n- 不能直接继承 Vibe Coding 的文件结构作为正式工程结构。\n- 不能把 mock 数据当业务事实。\n- 不能把回评/测评订单PRD当成整个 ERP PRD。\n- 不能因为 V1不实现完整KOC/KOL平台,就在阶段1删除KOC/KOL需求。\n- 不能把客服降级为“工单页面”,客服是核心执行和转化资源。\n\n## Gate 1 - Vibe Coding验证完成条件\n\n- 已记录所有原型的可用需求点。\n- 已记录原型不能支撑的业务缺口。\n- 已确认正式系统需要重新做 Stage 2 高保真模型。\n- 已确认KOC/KOL、客服、需求计划匹配是现有原型的主要补强方向。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 · 可点击参考原型 v1", + "filePath": "05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1.html", + "summary": "USER ERP 0-1需求重构 · 可点击参考原型 v1 USER ERP Stage 1 Prototype 模拟数据 仅用于流程验证 当前模块 今日作战 从异常、资源和今日动作进入业务闭环。 高频跳转 6 P0/P1异常 37 需求待评估 12 客服超时 9 免评缺口 4 额度预警 18 复盘待写 7 今日作战台 高级主管 / USER运营 / 客服 ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "complex", + "knowledgeMeta": { + "content": "\n\n\n \n \n USER ERP 0-1需求重构 · 可点击参考原型 v1\n \n\n\n
\n \n\n
\n
\n
\n 今日作战台\n 高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险\n
\n
\n \n
\n \n \n \n
\n \n \n \n
\n
\n
\n
\n
\n\n
\n \n
\n\n \n\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260528_USER_ERP_完整参考原型_v2", + "type": "document", + "name": "USER ERP 完整参考原型 v2 · 2026-05-28", + "filePath": "05_需求文档/20260528_USER_ERP_完整参考原型_v2.html", + "summary": "USER ERP 完整参考原型 v2 · 2026-05-28 USER ERP Stage 1 · v2 模拟数据 仅用于流程验证 当前模块 今日作战 从异常、资源和今日动作进入业务闭环。 高频跳转 6 P0/P1异常 37 需求待评估 8 客服超时工单 3 免评缺口 4 额度预警 18 复盘待写 7 今日作战台 高级主管 / USER运营 / 客服 / ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "complex", + "knowledgeMeta": { + "content": "\n\n\n\n\nUSER ERP 完整参考原型 v2 · 2026-05-28\n\n\n\n
\n\n\n\n\n
\n
\n
今日作战台高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险
\n
\n\n
\n\n\n\n
\n
\n
\n
\n
\n\n\n
\n\n
\n\n\n
\n ⚠ 本原型所有数据均为模拟数据,仅用于 Stage 1 需求验证 · v2\n
\n\n", + "wikilinks": [], + "category": "layer-requirements" + } } ], "edges": [ @@ -2325,6 +2600,110 @@ "direction": "forward", "description": "本层文档", "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/通用EDM业务流程说明", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/通用IM业务流程与接口频率限制说明", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-technical", + "target": "doc:07_技术文档/01-子系统-identity-数据库表关系", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-testing", + "target": "doc:08_测试相关/USER用户运营系统_原型逐页详细测试用例集", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1)", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260528_USER_ERP_完整参考原型_v2", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 } ], "layers": [ @@ -2367,7 +2746,18 @@ "doc:05_需求文档/用户运营系统-单文件", "doc:05_需求文档/evaluation-business-architecture", "doc:05_需求文档/user_erp_mvp_admin_prototype_v10", - "doc:05_需求文档/user_erp_mvp_admin_prototype_v10(1)" + "doc:05_需求文档/user_erp_mvp_admin_prototype_v10(1)", + "doc:05_需求文档/通用EDM业务流程说明", + "doc:05_需求文档/通用IM业务流程与接口频率限制说明", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1)", + "doc:05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1", + "doc:05_需求文档/20260528_USER_ERP_完整参考原型_v2" ] }, { @@ -2397,20 +2787,21 @@ { "id": "layer-technical", "name": "技术文档", - "description": "系统架构、数据模型、接口说明、技术方案和技术决策。", + "description": "系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。", "nodeIds": [ "flow:layer-technical", "doc:07_技术文档/README", "doc:07_技术文档/技术决策记录", "doc:07_技术文档/技术文档索引", "doc:07_技术文档/接口说明模板", - "doc:07_技术文档/系统架构说明模板" + "doc:07_技术文档/系统架构说明模板", + "doc:07_技术文档/01-子系统-identity-数据库表关系" ] }, { "id": "layer-testing", "name": "测试相关", - "description": "测试计划、测试用例、缺陷记录、验收记录和上线检查。", + "description": "测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。", "nodeIds": [ "flow:layer-testing", "doc:08_测试相关/README", @@ -2419,7 +2810,8 @@ "doc:08_测试相关/测试用例索引", "doc:08_测试相关/测试计划模板", "doc:08_测试相关/缺陷记录模板", - "doc:08_测试相关/验收记录模板" + "doc:08_测试相关/验收记录模板", + "doc:08_测试相关/USER用户运营系统_原型逐页详细测试用例集" ] }, { diff --git a/wishfulfilled-dashboard/meta.json b/wishfulfilled-dashboard/meta.json index f9e650f..0bff34a 100644 --- a/wishfulfilled-dashboard/meta.json +++ b/wishfulfilled-dashboard/meta.json @@ -1,8 +1,8 @@ { - "lastAnalyzedAt": "2026-05-27T07:14:45.072Z", + "lastAnalyzedAt": "2026-05-29T06:03:33.594Z", "gitCommitHash": "", "version": "1.0.0", - "analyzedFiles": 60, + "analyzedFiles": 73, "theme": { "presetId": "dark", "accentId": "cyan" diff --git a/wishfulfilled-wiki/.understand-anything/knowledge-graph.json b/wishfulfilled-wiki/.understand-anything/knowledge-graph.json index 3851b1a..dc9d755 100644 --- a/wishfulfilled-wiki/.understand-anything/knowledge-graph.json +++ b/wishfulfilled-wiki/.understand-anything/knowledge-graph.json @@ -11,7 +11,7 @@ "Obsidian" ], "description": "按需求文档、里程碑、技术文档、测试相关、Agent检索组织的流程式知识库。", - "analyzedAt": "2026-05-27T07:14:44.893Z", + "analyzedAt": "2026-05-29T06:03:32.374Z", "gitCommitHash": "" }, "nodes": [ @@ -490,15 +490,26 @@ "type": "document", "name": "技术文档", "filePath": "07_技术文档/README.md", - "summary": "本目录用于存放系统架构、数据模型、接口说明、实现方案、部署说明和技术决策记录。", + "summary": "type: technical docs home tags: 技术文档, 架构, 开发, 知识库 aliases: 技术文档入口, 技术资料 source: manual status: active owner: 技术负责人 updated: 2026 05 技术文档 本目录用于存放系统架构、数据模型、接口说明、实现方案、部署说明和技术决策记录。 二级入", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "架构", + "开发", + "知识库" ], "complexity": "simple", "knowledgeMeta": { "content": "---\ntype: technical_docs_home\ntags: [技术文档, 架构, 开发, 知识库]\naliases: [技术文档入口, 技术资料]\nsource: manual\nstatus: active\nowner: 技术负责人\nupdated: 2026-05\n---\n\n# 技术文档\n\n本目录用于存放系统架构、数据模型、接口说明、实现方案、部署说明和技术决策记录。\n\n## 二级入口\n\n- [[技术文档索引]]\n- [[系统架构说明模板]]\n- [[接口说明模板]]\n- [[技术决策记录]]\n\n## 存放内容\n\n- 系统架构说明\n- 模块设计说明\n- 数据表/业务对象设计\n- API 接口说明\n- 权限与安全设计\n- 部署与配置说明\n- 技术决策记录\n\n## 命名建议\n\n```text\n系统或模块_技术方案_YYYYMMDD.md\n系统或模块_接口说明_YYYYMMDD.md\n系统或模块_数据模型_YYYYMMDD.md\n```\n\n## 关联目录\n\n- 需求依据:[[../05_需求文档/README|需求文档]]\n- 测试依据:[[../08_测试相关/README|测试相关]]\n- 里程碑:[[../06_里程碑/README|里程碑]]\n", - "wikilinks": [], + "wikilinks": [ + "技术文档索引", + "系统架构说明模板", + "接口说明模板", + "技术决策记录", + "../05_需求文档/README|需求文档", + "../08_测试相关/README|测试相关", + "../06_里程碑/README|里程碑" + ], "category": "layer-technical" } }, @@ -507,10 +518,12 @@ "type": "document", "name": "技术决策记录", "filePath": "07_技术文档/技术决策记录.md", - "summary": "知识库文档。", + "summary": "type: adr log tags: 技术文档, 技术决策, ADR aliases: 技术决策, ADR source: manual status: active owner: 技术负责人 updated: 2026 05 技术决策记录 日期 决策主题 背景 决策结论 影响范围 关联需求/技术文档", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "技术决策", + "ADR" ], "complexity": "simple", "knowledgeMeta": { @@ -524,10 +537,12 @@ "type": "document", "name": "技术文档索引", "filePath": "07_技术文档/技术文档索引.md", - "summary": "- 新增技术方案、接口说明、数据模型后,在本索引登记。", + "summary": "type: technical docs index tags: 技术文档, 索引, Agent检索 aliases: 技术索引, 技术资料清单 source: manual status: active owner: 技术负责人 updated: 2026 05 技术文档索引 技术文档清单 模块/系统 文档类型 文件 关联需求 负责人 更新时间 状态 Ag", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "索引", + "Agent检索" ], "complexity": "simple", "knowledgeMeta": { @@ -541,10 +556,12 @@ "type": "document", "name": "接口说明模板", "filePath": "07_技术文档/接口说明模板.md", - "summary": "- 关键词:", + "summary": "type: api template tags: 技术文档, 接口, 模板 aliases: 接口模板, API说明模板 source: manual status: active owner: 技术负责人 updated: 2026 05 接口说明模板 基本信息 项目 内容 接口名称 所属模块 关联需求 负责人 状态 draft 接口用途 请求说明 字段 ", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "接口", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -558,10 +575,12 @@ "type": "document", "name": "系统架构说明模板", "filePath": "07_技术文档/系统架构说明模板.md", - "summary": "- 关键词:", + "summary": "type: architecture template tags: 技术文档, 架构, 模板 aliases: 架构说明模板 source: manual status: active owner: 技术负责人 updated: 2026 05 系统架构说明模板 基本信息 项目 内容 系统/模块 关联需求 负责人 状态 draft 背景与目标 架构说明 模块", "tags": [ "07_技术文档", - "技术文档" + "技术文档", + "架构", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -575,15 +594,31 @@ "type": "document", "name": "测试相关", "filePath": "08_测试相关/README.md", - "summary": "本目录用于存放测试计划、测试用例、测试报告、缺陷记录、验收记录和上线检查材料。", + "summary": "type: testing home tags: 测试, 测试用例, 验收, 知识库 aliases: 测试相关入口, 测试文档 source: manual status: active owner: 测试负责人 updated: 2026 05 测试相关 本目录用于存放测试计划、测试用例、测试报告、缺陷记录、验收记录和上线检查材料。 二级入口 测试用例索", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试", + "测试用例", + "验收", + "知识库" ], "complexity": "simple", "knowledgeMeta": { "content": "---\ntype: testing_home\ntags: [测试, 测试用例, 验收, 知识库]\naliases: [测试相关入口, 测试文档]\nsource: manual\nstatus: active\nowner: 测试负责人\nupdated: 2026-05\n---\n\n# 测试相关\n\n本目录用于存放测试计划、测试用例、测试报告、缺陷记录、验收记录和上线检查材料。\n\n## 二级入口\n\n- [[测试用例索引]]\n- [[测试用例模板]]\n- [[测试计划模板]]\n- [[缺陷记录模板]]\n- [[验收记录模板]]\n- [[上线检查模板]]\n\n## 存放内容\n\n- 测试计划\n- 测试用例\n- 测试执行记录\n- 缺陷记录\n- 验收记录\n- 上线检查记录\n- 回归测试说明\n\n## 命名建议\n\n```text\n项目或模块_测试用例_YYYYMMDD.md\n项目或模块_测试计划_YYYYMMDD.md\n项目或模块_缺陷记录_YYYYMMDD.md\n项目或模块_验收记录_YYYYMMDD.md\n```\n\n## 关联目录\n\n- 需求依据:[[../05_需求文档/README|需求文档]]\n- 技术依据:[[../07_技术文档/README|技术文档]]\n- 里程碑依据:[[../06_里程碑/README|里程碑]]\n- 流程依据:[[../02_项目管理流程/阶段2.5_测试提前补漏|阶段2.5 测试提前补漏]]、[[../02_项目管理流程/阶段4_测试培训上线回流|阶段4 测试培训上线回流]]\n", - "wikilinks": [], + "wikilinks": [ + "测试用例索引", + "测试用例模板", + "测试计划模板", + "缺陷记录模板", + "验收记录模板", + "上线检查模板", + "../05_需求文档/README|需求文档", + "../07_技术文档/README|技术文档", + "../06_里程碑/README|里程碑", + "../02_项目管理流程/阶段2.5_测试提前补漏|阶段2.5 测试提前补漏", + "../02_项目管理流程/阶段4_测试培训上线回流|阶段4 测试培训上线回流" + ], "category": "layer-testing" } }, @@ -592,10 +627,13 @@ "type": "document", "name": "上线检查模板", "filePath": "08_测试相关/上线检查模板.md", - "summary": "- 关键词:", + "summary": "type: go live checklist template tags: 上线检查, 测试, 模板 aliases: 上线检查, 发布检查 source: manual status: active owner: 测试负责人 / 项目经理 updated: 2026 05 上线检查模板 基本信息 项目 内容 项目/模块 关联需求 关联里程碑 负责人 检查", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "上线检查", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -609,10 +647,13 @@ "type": "document", "name": "测试用例模板", "filePath": "08_测试相关/测试用例模板.md", - "summary": "- 关键词:", + "summary": "type: test case template tags: 测试用例, 测试, 模板 aliases: 用例模板, 测试用例 source: manual status: active owner: 测试负责人 updated: 2026 05 测试用例模板 基本信息 项目 内容 项目/模块 关联需求 关联技术文档 测试负责人 状态 draft 测试范围 ", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试用例", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -626,10 +667,14 @@ "type": "document", "name": "测试用例索引", "filePath": "08_测试相关/测试用例索引.md", - "summary": "- 新增测试用例后,必须在本索引登记。", + "summary": "type: test case index tags: 测试用例, 测试, 索引, Agent检索 aliases: 测试用例清单, 用例索引 source: manual status: active owner: 测试负责人 updated: 2026 05 测试用例索引 测试用例清单 编号 项目/模块 用例集名称 文件 关联需求 关联技术文档 负责人 ", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试用例", + "测试", + "索引", + "Agent检索" ], "complexity": "simple", "knowledgeMeta": { @@ -643,10 +688,13 @@ "type": "document", "name": "测试计划模板", "filePath": "08_测试相关/测试计划模板.md", - "summary": "- 关键词:", + "summary": "type: test plan template tags: 测试计划, 测试, 模板 aliases: 测试计划模板 source: manual status: active owner: 测试负责人 updated: 2026 05 测试计划模板 基本信息 项目 内容 项目/模块 关联需求 关联里程碑 测试负责人 计划周期 测试目标 测试范围 不在范围", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "测试计划", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -660,10 +708,13 @@ "type": "document", "name": "缺陷记录模板", "filePath": "08_测试相关/缺陷记录模板.md", - "summary": "1.", + "summary": "type: defect template tags: 缺陷, 测试, 模板 aliases: Bug记录模板, 缺陷记录 source: manual status: active owner: 测试负责人 updated: 2026 05 缺陷记录模板 基本信息 项目 内容 缺陷编号 BUG 项目/模块 关联需求 关联用例 严重级别 当前状态 open ", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "缺陷", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -677,10 +728,13 @@ "type": "document", "name": "验收记录模板", "filePath": "08_测试相关/验收记录模板.md", - "summary": "- 关键词:", + "summary": "type: acceptance template tags: 验收, 测试, 模板 aliases: 验收记录, UAT模板 source: manual status: active owner: 测试负责人 / 业务负责人 updated: 2026 05 验收记录模板 基本信息 项目 内容 项目/模块 关联需求 关联测试用例 验收负责人 验收日期 验", "tags": [ "08_测试相关", - "测试相关" + "测试相关", + "验收", + "测试", + "模板" ], "complexity": "simple", "knowledgeMeta": { @@ -748,7 +802,7 @@ ], "complexity": "simple", "knowledgeMeta": { - "content": "# 需求文档\n\n所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。\n\n本层包含 21 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", + "content": "# 需求文档\n\n所有正式需求、业务规则、需求变更和需求索引。点击本层可查看全部需求文档并检索。\n\n本层包含 32 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", "wikilinks": [], "category": "layer-requirements" } @@ -773,14 +827,14 @@ "id": "flow:layer-technical", "type": "document", "name": "4. 技术文档", - "summary": "系统架构、数据模型、接口说明、技术方案和技术决策。", + "summary": "系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。", "tags": [ "流程入口", "技术文档" ], "complexity": "simple", "knowledgeMeta": { - "content": "# 技术文档\n\n系统架构、数据模型、接口说明、技术方案和技术决策。\n\n本层包含 5 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", + "content": "# 技术文档\n\n系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。\n\n本层包含 6 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", "wikilinks": [], "category": "layer-technical" } @@ -789,14 +843,14 @@ "id": "flow:layer-testing", "type": "document", "name": "5. 测试相关", - "summary": "测试计划、测试用例、缺陷记录、验收记录和上线检查。", + "summary": "测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。", "tags": [ "流程入口", "测试相关" ], "complexity": "simple", "knowledgeMeta": { - "content": "# 测试相关\n\n测试计划、测试用例、缺陷记录、验收记录和上线检查。\n\n本层包含 7 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", + "content": "# 测试相关\n\n测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。\n\n本层包含 8 个文档。点击右侧 Files 或在本层详情中选择具体文档查看内容。", "wikilinks": [], "category": "layer-testing" } @@ -1139,6 +1193,227 @@ ], "category": "layer-requirements" } + }, + { + "id": "doc:05_需求文档/通用EDM业务流程说明", + "type": "document", + "name": "通用 EDM 业务流程说明", + "filePath": "05_需求文档/通用EDM业务流程说明.md", + "summary": "通用 EDM 业务流程说明 更新时间:2026 05 26 1. 文档目标 本文用于新的 EDM 子系统设计或重构,目标是在功能保持一致的前提下,将现有 EDM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、接入邮件发送通道和处理邮件客服工单。 2. 业务范围 通用 EDM 子系统建议分为三条业务线: 业务线 说明 批量营销", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# 通用 EDM 业务流程说明\n\n更新时间:2026-05-26\n\n## 1. 文档目标\n\n本文用于新的 EDM 子系统设计或重构,目标是在功能保持一致的前提下,将现有 EDM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、接入邮件发送通道和处理邮件客服工单。\n\n## 2. 业务范围\n\n通用 EDM 子系统建议分为三条业务线:\n\n| 业务线 | 说明 |\n| --- | --- |\n| 批量营销邮件 | 管理后台创建邮件任务,按标签、站点、产品、用户状态筛选目标用户,生成待发送邮件记录,通过队列异步发送 |\n| 自动 / 实时策略邮件 | 根据用户注册、访问、在线、站点、产品、行为、无消息等规则自动筛选用户,并生成策略邮件 |\n| 邮件工单 | 用户来信、表单提交或外部收信服务进入后台后,生成或更新邮件工单,由客服处理、回复、转发、关闭 |\n\n如果新系统还需要普通邮箱功能,可以作为独立模块处理。普通邮箱收发不一定进入 EDM 工单链路,是否合并需要单独确认。\n\n## 3. 总体架构\n\n```mermaid\nflowchart TD\n A[\"管理后台\"] --> B[\"EDM 任务服务\"]\n B --> C[\"目标用户筛选服务\"]\n C --> D[\"邮件记录生成服务\"]\n D --> E[\"Kafka / 队列\"]\n E --> F[\"邮件发送消费者\"]\n F --> G[\"外部发送通道\"]\n G --> H[\"AWS SES / SMTP / Gmail / Microsoft\"]\n H --> I[\"事件回调\"]\n I --> J[\"事件处理服务\"]\n J --> K[\"统计与黑名单\"]\n\n L[\"用户来信 / 表单提交\"] --> M[\"入站接收服务\"]\n M --> N[\"Kafka / 入站队列\"]\n N --> O[\"EDM 工单服务\"]\n O --> P[\"客服工作台\"]\n P --> E\n```\n\n核心组件职责:\n\n| 组件 | 职责 |\n| --- | --- |\n| 管理后台 | 创建邮件任务、审核任务、查看统计、处理邮件工单 |\n| 任务服务 | 保存任务配置、正文、模板、发送时间、审核状态 |\n| 用户筛选服务 | 根据标签、站点、产品、黑名单、订阅状态、发送频率等规则筛选目标用户 |\n| 邮件记录服务 | 按用户生成单封待发送邮件记录和正文快照 |\n| Kafka / 队列 | 解耦任务生成、邮件发送、入站消息、事件统计 |\n| 发送消费者 | 消费待发送邮件,调用外部发送通道,并保存发送结果 |\n| 入站接收服务 | 接收表单、用户来信或外部邮件服务回调,写入入站队列 |\n| 工单服务 | 根据来信生成或更新邮件工单,维护状态、负责人、未读数和处理记录 |\n| 事件处理服务 | 处理送达、打开、点击、退信、投诉、拒信等邮件事件 |\n| Redis / 缓存 | 保存并发锁、游标、限流计数、近期任务统计、临时筛选集合 |\n\n## 4. 核心数据模型\n\n新子系统建议至少抽象以下对象:\n\n| 对象 | 说明 |\n| --- | --- |\n| 邮件任务 EmailTask | 批量营销或策略邮件任务,保存任务名称、类型、发送时间、审核状态、目标条件 |\n| 邮件内容 EmailContent | 任务级正文、标题、模板、发件人、回复地址、附件配置 |\n| 目标用户 TaskRecipient | 任务命中的用户关系,便于统计和去重 |\n| 单封邮件 EmailMessage | 最终发送或接收的一封邮件记录,包含方向、收件人、发件人、状态、message_id、工单 ID |\n| 邮件正文 EmailBody | 单封邮件正文快照,避免模板后续变化影响历史邮件 |\n| 工单 EmailTicket | 用户来信或客服主动发起的一次处理过程 |\n| 分配记录 Assignment | 工单分配、移交、释放、代班等操作记录 |\n| 节点日志 NodeLog | 创建、分配、首次回复、关闭、未解决、转化中等关键节点 |\n| 发送事件 EmailEvent | 送达、打开、点击、退信、投诉、拒信、渲染失败等事件 |\n| 黑名单 / 退订名单 Suppression | 退信、投诉、退订、风险用户等不可发送或需谨慎发送的人群 |\n\n## 5. 批量营销邮件流程\n\n```mermaid\nflowchart TD\n A[\"后台创建邮件任务\"] --> B[\"校验任务配置\"]\n B --> C[\"写入任务、内容、标签条件\"]\n C --> D[\"进入待审核\"]\n D --> E{\"审核结果\"}\n E -->|通过| F[\"进入待执行\"]\n E -->|驳回| G[\"记录驳回原因并结束\"]\n F --> H[\"到达发送时间\"]\n H --> I[\"筛选目标用户\"]\n I --> J[\"生成单封待发送邮件记录\"]\n J --> K[\"投递 Kafka\"]\n K --> L[\"发送消费者调用外部邮件通道\"]\n L --> M[\"更新发送状态和 message_id\"]\n```\n\n创建任务时建议校验:\n\n1. 任务名称不能重复。\n2. 邮件模板或正文必须存在。\n3. 发件邮箱必须存在并可用。\n4. 发件域名必须在允许范围内。\n5. 必须选择目标人群或策略条件。\n6. 发送时间必须符合业务规则。\n7. 如果绑定活动,发送时间需要满足活动时间约束。\n8. 目标人数需要预估,避免误发全量用户。\n\n目标用户筛选建议包含:\n\n1. 标签包含和标签排除。\n2. 站点、产品、品牌、语言、地区。\n3. 订阅状态、退订状态、黑名单、投诉用户、永久退信用户。\n4. 近期发送频率限制,避免短时间重复触达。\n5. 任务级去重,避免同一用户重复生成同一任务邮件。\n\n## 6. 自动 / 实时策略邮件流程\n\n```mermaid\nflowchart TD\n A[\"策略配置\"] --> B[\"定时任务生成当日策略任务\"]\n B --> C[\"实时策略扫描\"]\n C --> D[\"按用户行为和条件筛选\"]\n D --> E[\"应用黑名单、退订、频率控制\"]\n E --> F[\"生成待发送邮件\"]\n F --> G[\"投递发送队列\"]\n G --> H[\"发送消费者调用邮件通道\"]\n H --> I[\"事件回调更新统计\"]\n```\n\n策略邮件与批量邮件的区别:\n\n1. 批量邮件通常由运营手动创建,发送时间明确。\n2. 策略邮件通常由系统按规则自动生成,可能按分钟或按天扫描。\n3. 策略邮件更依赖幂等和频率控制,避免同一用户在同一策略下反复触发。\n4. 策略邮件应记录策略 ID、触发原因、触发时间,便于归因。\n\n建议策略执行时做并发锁,避免多个任务实例重复生成邮件。\n\n## 7. 邮件发送链路\n\n通用发送链路:\n\n```mermaid\nflowchart TD\n A[\"待发送邮件记录\"] --> B[\"写入 Kafka\"]\n B --> C[\"发送消费者\"]\n C --> D[\"读取发件通道配置\"]\n D --> E{\"发送通道\"}\n E -->|批量营销| F[\"AWS SES 或批量发送通道\"]\n E -->|客服回复| G[\"SMTP / Gmail / Microsoft\"]\n F --> H[\"保存发送结果\"]\n G --> H\n H --> I[\"通知前端或更新统计\"]\n```\n\n发送消费者需要处理:\n\n1. 队列消息反序列化。\n2. 邮件正文、标题、收件人、发件人、回复地址、附件组装。\n3. 发送通道选择。\n4. 调用外部服务。\n5. 成功后保存 `message_id`、发送时间和成功状态。\n6. 失败后保存错误信息、失败状态和重试次数。\n\n发送通道建议按场景区分:\n\n| 场景 | 推荐处理 |\n| --- | --- |\n| 批量营销邮件 | 走支持批量和事件回调的邮件服务,例如 AWS SES |\n| 策略邮件 | 可复用批量发送通道,但必须做频率和幂等控制 |\n| 工单客服回复 | 按发件邮箱配置选择 SMTP、Gmail API 或 Microsoft Graph |\n| 普通邮箱回复 | 可独立于工单链路,同步或异步发送均可 |\n\n## 8. 邮件事件回调与统计\n\n邮件发送后,外部服务会产生事件。通用事件包括:\n\n| 事件 | 处理建议 |\n| --- | --- |\n| Delivery / 送达 | 标记邮件已送达,记录送达时间和发送 IP |\n| Bounce / 退信 | 区分永久退信和临时退信,更新任务统计;永久退信可加入黑名单 |\n| Open / 打开 | 标记打开时间,更新任务打开统计 |\n| Click / 点击 | 记录点击链接和点击时间,更新点击统计 |\n| Complaint / 投诉 | 记录投诉,加入抑制名单或黑名单 |\n| Subscription / 订阅变更 | 更新订阅或退订状态 |\n| Reject / 拒信 | 记录拒信原因,更新失败统计 |\n| Rendering Failure / 渲染失败 | 记录模板或内容渲染失败 |\n| DeliveryDelay / 延迟 | 可记录延迟事件,是否统计需业务确认 |\n\n事件处理要点:\n\n1. 事件必须通过 `message_id` 或自定义追踪 ID 关联到本地邮件记录。\n2. 同一事件可能重复回调,需要幂等处理。\n3. 打开和点击事件存在图片加载、隐私保护、客户端屏蔽等不确定性,统计只能作为参考指标。\n4. 投诉、退订、永久退信应优先进入发送抑制规则。\n\n## 9. 入站邮件 / 表单进入工单流程\n\n入站来源可以有多种:\n\n1. 网站表单提交。\n2. 用户真实邮件来信。\n3. 外部收信服务回调。\n4. IM 或其他渠道转入邮件客服。\n\n通用流程:\n\n```mermaid\nflowchart TD\n A[\"用户来信或表单提交\"] --> B[\"入站接收服务\"]\n B --> C[\"写入 Kafka 入站队列\"]\n C --> D[\"EDM 工单消费者\"]\n D --> E[\"保存入站邮件和正文\"]\n E --> F{\"是否存在未关闭工单\"}\n F -->|否| G[\"创建新工单\"]\n F -->|是| H[\"绑定到原工单并更新未读数\"]\n G --> I[\"写入节点日志\"]\n H --> I\n I --> J[\"通知客服工作台\"]\n```\n\n创建或更新工单时建议:\n\n1. 以发件邮箱、收件邮箱、业务用户 ID、会话标识等组合判断是否复用未关闭工单。\n2. 新工单记录来源、用户邮箱、发件邮箱、团队、状态、未读数、最后来信时间。\n3. 已有工单更新最后来信时间、未读数、用户来信数。\n4. 如果当前客服离线,可以释放负责人,让工单重新进入分配池。\n5. 入站正文应保存原始内容和清洗后的展示内容。\n\n## 10. 工单客服处理流程\n\n### 10.1 工单状态\n\n通用状态建议:\n\n| 状态 | 说明 |\n| --- | --- |\n| 待处理 | 新入站邮件或表单生成工单,等待客服处理 |\n| 服务中 | 客服已接手并正在处理 |\n| 未解决 | 客服标记暂未解决,需要后续跟进 |\n| 转化中 | 进入销售或转化跟进阶段 |\n| 已关闭 | 本次邮件工单处理结束 |\n\n状态值可以由新系统自行定义,但需要保证列表筛选、统计、自动关闭和权限校验口径统一。\n\n### 10.2 自动分配\n\n自动分配建议流程:\n\n1. 找到待处理且未分配的工单。\n2. 根据收件邮箱、团队、站点、语言或业务线确定可服务团队。\n3. 获取在线客服。\n4. 按接单上限、当前处理数、最近分配时间选择客服。\n5. 更新工单负责人。\n6. 写入分配记录和节点日志。\n7. 通知客服工作台。\n\n### 10.3 客服回复\n\n```mermaid\nflowchart TD\n A[\"客服点击发送\"] --> B[\"校验客服在线、权限、工单状态\"]\n B --> C[\"写入待发送邮件记录和正文\"]\n C --> D[\"更新工单未读数、首次响应、回复耗时\"]\n D --> E[\"投递客服回复队列\"]\n E --> F[\"发送消费者选择 SMTP / Gmail / Microsoft\"]\n F --> G[\"保存发送成功或失败结果\"]\n G --> H[\"通知客服工作台\"]\n```\n\n发送前建议校验:\n\n1. 客服必须在线。\n2. 工单必须存在且未关闭。\n3. 当前客服必须是工单处理人,或具备接手权限。\n4. 工单必须属于当前客服可处理团队。\n5. 主题、正文、收件人、回复地址必须合法。\n6. 附件大小、类型、数量需要符合业务规则和发送通道限制。\n\n### 10.4 转发和主动开工单\n\n转发:\n\n1. 需要填写新的收件人。\n2. 转发邮件可以不绑定到原工单作为普通回复。\n3. 原邮件和转发邮件需要建立关联,方便追溯。\n4. 发送链路仍可复用客服回复队列。\n\n主动开工单:\n\n1. 客服选择发件邮箱和目标用户邮箱。\n2. 系统校验发件邮箱归属团队。\n3. 如果同一发件邮箱和用户邮箱已有未关闭工单,应拒绝重复创建或要求接手原工单。\n4. 创建服务中工单,负责人为当前客服。\n5. 写入节点日志和分配记录。\n6. 发送第一封邮件。\n\n## 11. 工单辅助任务\n\n新子系统可按需要保留以下后台任务:\n\n| 任务 | 说明 |\n| --- | --- |\n| 自动分配 | 将未分配待处理工单分配给在线客服 |\n| 自动移交 | 当前负责人离线且有新来信时,按代班或团队规则重新分配 |\n| DDL 释放 | 工单分配后超过配置时间未处理,释放为未分配 |\n| 未回复提醒 | 用户新来信超过配置时间未回复,提醒负责人 |\n| 自动关闭 | 服务中工单超过配置时间无新用户来信时自动关闭 |\n| 未分配告警 | 未分配工单数量超过阈值时通知团队管理员 |\n| 统计同步 | 定时刷新任务发送数、回复数、打开数、点击数等统计 |\n\n具体调度频率和启用范围需要按新系统 SLA 确认。\n\n## 12. 通用限制与风控点\n\n### 12.1 任务和发送限制\n\n建议配置化管理:\n\n1. 单任务最大目标人数。\n2. 单轮投递队列数量。\n3. 单发件邮箱每分钟、每小时、每天发送上限。\n4. 单用户每天或一段时间内最大触达次数。\n5. 单域名发送上限。\n6. 批量邮件和客服回复是否共享额度。\n\n### 12.2 内容限制\n\n建议校验:\n\n1. 邮件主题最大长度。\n2. 正文最小和最大长度。\n3. 附件大小、类型、数量。\n4. 发件邮箱和回复邮箱格式。\n5. 链接合法性和追踪参数。\n6. 必要的退订入口和合规声明。\n\n### 12.3 人群抑制\n\n发送前应排除:\n\n1. 退订用户。\n2. 投诉用户。\n3. 永久退信用户。\n4. 风险用户。\n5. 明确不允许触达的用户。\n6. 已达到频率上限的用户。\n\n### 12.4 幂等和重试\n\n需要幂等的场景:\n\n1. 任务生成邮件记录。\n2. 邮件记录投递 Kafka。\n3. Kafka 消费发送。\n4. 外部事件回调。\n5. 入站邮件生成工单。\n\n失败重试建议区分:\n\n1. 可重试:网络超时、临时服务不可用、临时退信、限流。\n2. 不可重试:邮箱格式错误、发件权限错误、账号不存在、永久退信、投诉抑制。\n\n## 13. 可观测性\n\n建议至少记录以下指标:\n\n| 指标 | 说明 |\n| --- | --- |\n| 任务创建数 | 按类型、状态统计 |\n| 目标用户数 | 预估人数、实际生成邮件数、过滤人数 |\n| 队列积压 | 批量发送队列、客服回复队列、入站队列 |\n| 发送成功率 | 按通道、发件邮箱、任务统计 |\n| 失败原因分布 | 发送失败、退信、拒信、限流 |\n| 送达率、打开率、点击率 | 以事件回调统计,注意打开和点击有误差 |\n| 投诉率、退订率 | 作为发送风控核心指标 |\n| 工单响应时长 | 首次响应、最近响应、关闭时长 |\n| 未分配工单数 | 用于团队容量和告警 |\n\n\n## 14. 参考代码位置\n\n以下为现有项目中可参考的代码位置,重构时可按新架构重新命名和拆分:\n\n| 模块 | 现有参考 |\n| --- | --- |\n| 邮件任务后台入口 | `app/admin/controller/MailTaskController.php` |\n| 邮件任务服务 | `app/service/MailTaskService.php` |\n| 批量邮件生成与投递参考 | `app/service/UserService.php` |\n| 批量邮件发送消费者 | `app/command/kafkaConsumer/BatchMailCommand.php` |\n| 工单后台入口 | `app/admin/controller/EdmChatController.php` |\n| 工单服务 | `app/service/EdmChatService.php` |\n| 客服回复消费者 | `app/command/kafkaConsumer/MailSendCommand.php` |\n| 表单数据消费者 | `app/command/kafkaConsumer/QaForm.php` |\n| 表单创建工单 | `app/service/WebFormDataService.php` |\n| 邮件事件回调 | `app/controller/AmazonEmailController.php` |\n| 普通邮箱服务 | `app/admin/controller/MailboxController.php`、`app/service/MailboxService.php` |\n| 邮件发送封装 | `app/service/Mail.php` |\n| Microsoft 邮件服务 | `app/service/MicrosoftService.php` |\n| 策略邮件命令 | `app/command/EdmDoRealTimeStrategy.php`、`app/command/EdmSendRealTimeStrategy.php`、`app/command/EdmDayCurrentTask.php` |\n| 工单辅助命令 | `app/command/EdmAllocate.php`、`app/command/EdmMove.php`、`app/command/EdmDealLine.php`、`app/command/EdmTimeout.php`、`app/command/EdmWorkOrderAutoClose.php` |\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/通用IM业务流程与接口频率限制说明", + "type": "document", + "name": "通用 IM 业务流程与接口频率限制说明", + "filePath": "05_需求文档/通用IM业务流程与接口频率限制说明.md", + "summary": "通用 IM 业务流程与接口频率限制说明 更新时间:2026 05 26 1. 文档目标 本文用于新的 IM 子系统重构设计,目标是在功能保持一致的前提下,将现有 IM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、控制腾讯云 IM REST API 调用频率。 2. 总体架构 通用 IM 链路由以下组件组成: 组件 职责 A", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# 通用 IM 业务流程与接口频率限制说明\n\n更新时间:2026-05-26\n\n## 1. 文档目标\n\n本文用于新的 IM 子系统重构设计,目标是在功能保持一致的前提下,将现有 IM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、控制腾讯云 IM REST API 调用频率。\n\n## 2. 总体架构\n\n通用 IM 链路由以下组件组成:\n\n| 组件 | 职责 |\n| --- | --- |\n| App 客户端 | 用户通过 App 使用腾讯云 IM SDK 发送和接收消息 |\n| 腾讯云 IM | 负责即时消息投递、账号体系、消息格式、离线推送、服务端 REST API、回调触发 |\n| App 后台 | 接收腾讯云 IM 回调,识别需要管理后台处理的消息,并写入 Kafka |\n| Kafka | 解耦 App 后台与管理后台 IM 子系统,承接入站消息、推送消息、异步任务 |\n| IM 子系统 / 管理后台 | 消费 Kafka 消息,维护本地会话、消息、工单、分配、状态流转和客服操作 |\n| 本地数据库 | 作为管理后台 IM 业务的长期数据源,保存消息流水、会话状态、工单状态、操作记录 |\n| Redis / 缓存 | 保存在线状态、分配触发标记、限流计数、幂等键、临时游标 |\n| WebSocket / 站内通知 | 将新消息、分配变化、发送结果、状态变化推送给管理后台前端 |\n\n## 3. 总体消息流\n\n```mermaid\nflowchart TD\n A[\"用户在 App 发送 IM 消息\"] --> B[\"腾讯云 IM SDK 投递消息\"]\n B --> C[\"腾讯云 IM 回调 App 后台\"]\n C --> D[\"App 后台校验回调并识别管理后台相关消息\"]\n D --> E[\"App 后台写入 Kafka\"]\n E --> F[\"IM 子系统消费 Kafka\"]\n F --> G[\"幂等校验与消息解析\"]\n G --> H[\"写入本地消息表\"]\n H --> I[\"更新会话、工单、未读数、最后消息时间\"]\n I --> J[\"触发分配、提醒或状态流转\"]\n J --> K[\"管理后台客服前端展示\"]\n```\n\n核心原则:\n\n1. App 客户端消息先进入腾讯云 IM。\n2. 腾讯云 IM 将消息回调给 App 后台。\n3. App 后台不直接写管理后台业务库,而是将管理后台关心的消息写入 Kafka。\n4. 管理后台 IM 子系统消费 Kafka 后入库,并维护本地会话、工单和客服状态。\n5. 管理后台后续展示、检索、统计、客服处理,应以本地数据库为主数据源。\n\n## 4. 核心数据模型\n\n新子系统建议至少抽象以下数据对象:\n\n| 对象 | 说明 |\n| --- | --- |\n| IM 账号 | 业务用户、品牌账号、客服账号在腾讯云 IM 中的账号映射 |\n| 消息 Message | 本地保存的消息流水,包含方向、发送方、接收方、消息类型、消息体、腾讯消息 ID、业务扩展字段 |\n| 会话 Conversation | 用户与品牌/客服之间的当前会话指针,包含未读数、最后消息时间、当前工单 ID |\n| 工单 Ticket / ChatRecord | 一次客服处理过程,包含状态、负责人、团队、首次响应时间、关闭时间 |\n| 分配记录 Assignment | 自动分配、手动接手、转接、释放、代班等操作记录 |\n| 节点日志 NodeLog | 工单生成、分配、首次回复、转接、未解决、转化中、关闭等关键节点 |\n| 推送任务 PushTask | 运营或策略创建的 IM 推送任务 |\n| 推送记录 PushRecord | 单个用户、单条内容的实际待发送记录和发送结果 |\n\n## 5. 入站消息流程\n\n### 5.1 用户发送消息\n\n1. 用户在 App 中通过腾讯云 IM SDK 发送文本、图片、视频、自定义卡片或其他消息。\n2. 腾讯云 IM 完成消息投递,并根据配置触发服务端回调。\n3. App 后台接收腾讯云 IM 回调,完成签名、来源、事件类型和消息结构校验。\n4. App 后台判断该消息是否需要管理后台处理。\n5. 需要管理后台处理的消息,由 App 后台写入 Kafka。\n6. IM 子系统消费 Kafka,执行幂等校验,解析消息体。\n7. IM 子系统写入本地消息流水,并更新会话和工单状态。\n8. 若消息产生新的待处理工单,则进入分配流程。\n9. 管理后台前端通过列表查询、WebSocket 或站内通知看到新消息。\n\n### 5.2 幂等与顺序\n\n腾讯云回调、App 后台写 Kafka、Kafka 消费都可能出现重试,因此 IM 子系统必须做幂等处理。\n\n建议幂等键优先级:\n\n1. 腾讯云 IM 消息唯一标识。\n2. 发送方、接收方、消息随机数、消息序列号、消息时间组合。\n3. App 后台写入 Kafka 时生成的业务事件 ID。\n\n同一会话内需要尽量按消息时间和腾讯云消息顺序展示。若存在乱序到达,应允许入库后按消息时间、序列号或腾讯消息 ID 排序展示。\n\n## 6. 客服处理流程\n\n### 6.1 会话和工单状态\n\n通用状态建议如下:\n\n| 状态 | 说明 |\n| --- | --- |\n| 待接入 | 用户有新消息,尚未分配或尚未被客服处理 |\n| 服务中 | 客服已接入并正在处理 |\n| 已转接 | 工单被转给其他客服或团队,等待新处理人接手 |\n| 未解决 | 客服标记暂未解决,需要后续跟进 |\n| 转化中 | 进入转化或商机跟进阶段 |\n| 已关闭 | 本次客服处理结束 |\n\n状态值可以由新子系统自行定义,但需要保证列表筛选、统计口径、自动关闭、转接和重新打开逻辑一致。\n\n### 6.2 自动分配\n\n自动分配通常在新消息入库后触发:\n\n1. 找到待接入工单。\n2. 根据品牌、站点、团队、语言、业务线等条件确定可服务团队。\n3. 获取在线客服列表。\n4. 按客服接单上限、当前处理数、最近分配时间等规则筛选。\n5. 选择目标客服并更新工单负责人。\n6. 写入分配记录和节点日志。\n7. 通知管理后台前端。\n\n建议将分配能力独立成可重试任务,避免 Kafka 入站消费被复杂分配逻辑拖慢。\n\n### 6.3 客服打开会话\n\n客服打开会话时,系统通常需要:\n\n1. 校验客服是否在线、是否有处理权限。\n2. 查询会话和工单详情。\n3. 拉取本地消息列表。\n4. 清理当前客服视角下的未读数。\n5. 必要时将工单从待接入或已转接推进到服务中。\n6. 记录读取、接手或首次处理节点。\n\n### 6.4 客服发送消息\n\n客服发送消息的通用链路:\n\n```mermaid\nflowchart TD\n A[\"客服在管理后台发送消息\"] --> B[\"IM 子系统校验权限、会话、工单状态\"]\n B --> C[\"组装腾讯云 IM 消息体\"]\n C --> D[\"调用腾讯云 REST API\"]\n D --> E{\"腾讯云返回结果\"}\n E -->|成功| F[\"写入本地消息流水并更新会话\"]\n E -->|失败| G[\"记录失败原因并进入重试或人工处理\"]\n F --> H[\"通知管理后台前端发送成功\"]\n G --> I[\"通知管理后台前端发送失败\"]\n```\n\n发送前建议校验:\n\n1. 当前客服必须具备客服身份。\n2. 当前客服必须有该会话或工单处理权限。\n3. 工单不能处于已关闭状态,除非业务允许重新打开。\n4. 消息体大小、消息类型、媒体文件大小必须符合业务和腾讯云限制。\n5. 对运营推送和客服消息应区分业务来源,方便统计和风控。\n\n### 6.5 转接、释放和自动关闭\n\n通用处理规则:\n\n1. 手动转接给个人:校验当前处理人权限,更新负责人,记录转接节点。\n2. 手动转接给团队:记录目标团队,等待团队内客服接手或自动分配。\n3. 客服离线释放:如果负责人离线且有未读用户消息,可释放为待分配。\n4. 超时未处理释放:待接入或已转接超过配置时间后,重新进入分配池。\n5. 自动关闭:服务中会话在配置时间内无新用户消息或无新客服动作时,自动关闭。\n\n## 7. 运营 IM 推送流程\n\n运营推送与客服消息都可能调用腾讯云 IM REST API,但应在业务上区分:\n\n```mermaid\nflowchart TD\n A[\"后台创建推送任务\"] --> B[\"审核或直接进入待发送\"]\n B --> C[\"按标签、站点、产品、用户状态筛选目标人群\"]\n C --> D[\"生成 PushRecord\"]\n D --> E[\"写入 Kafka 推送队列\"]\n E --> F[\"推送消费者限速发送\"]\n F --> G[\"调用 sendmsg 或 batchsendmsg\"]\n G --> H[\"保存腾讯云返回结果\"]\n H --> I[\"汇总任务完成状态\"]\n```\n\n建议:\n\n1. 大批量运营推送优先评估 `batchsendmsg`,减少单发接口压力。\n2. 推送消费者必须按 SDKAppID 和接口维度做限速。\n3. 推送记录需要保存发送状态、错误码、重试次数、腾讯云返回结果。\n4. 发送失败应区分可重试错误和不可重试错误。\n5. 推送消息应写入业务扩展字段,便于后续归因和统计。\n\n## 8. 本地存储与历史消息\n\n管理后台 IM 子系统建议以本地数据库作为长期主数据源。\n\n腾讯云 IM 漫游消息适合用于消息补偿、短期核对或异常排查,不建议作为客服记录、统计报表和审计的唯一长期来源。原因是历史消息存储时长与套餐、增值服务和控制台配置有关,且可能产生额外费用。\n\n建议本地保存:\n\n1. 入站用户消息。\n2. 客服发送消息。\n3. 系统提示消息。\n4. 运营推送消息及发送结果。\n5. 转接、关闭、分配、未解决、转化等节点日志。\n\n## 9. 腾讯云 IM 接口频率限制说明\n\n以下为腾讯云官方文档口径,实际限制可能随套餐、数据中心、控制台配置和商务协议变化,最终以腾讯云控制台和合同为准。\n\n### 9.1 通用限制\n\n| 项目 | 限制 |\n| --- | --- |\n| 单聊、群聊单条消息长度 | 最大 12KB |\n| UserID | 长度不超过 32 字节,不支持特殊字符,建议使用英文字母或数字 |\n| REST API 通用调用频率 | 多数 REST API 默认最高 200 次/秒 |\n| 导入多个账号、删除账号、查询账号 | 默认最高 100 次/秒 |\n| 查询在线状态 | 单次请求最多查询 500 个用户 |\n| 批量发单聊消息 | 单次最多给 500 个用户发送 |\n| 导入多个账号 | 单次最多导入 100 个用户名 |\n| 单个用户好友数 | 默认支持 3000 个好友 |\n| 单个用户黑名单数 | 最多 1000 人 |\n\n### 9.2 常用 REST API 频率\n\n| 接口 | 默认频率限制 | 叠加包增量 | 说明 |\n| --- | --- | --- | --- |\n| `v4/openim/sendmsg` 单发单聊消息 | 200 次/秒 | 每个叠加包 +100 次/秒 | 体验版和开发版每日累计发送量限制为 1000 条/天 |\n| `v4/openim/batchsendmsg` 批量发单聊消息 | 200 次/秒,同时 12000 条/分钟 | 每个叠加包 +6000 条/分钟 | 条数按接收方数量计算,一次发给 500 人计 500 条 |\n| `v4/profile/portrait_set` 设置资料 | 200 次/秒 | 每个叠加包 +100 次/秒 | 用于更新头像、昵称等资料 |\n| `v4/profile/portrait_get` 拉取资料 | 200 次/秒 | 每个叠加包 +100 次/秒 | 用于查询资料 |\n| `v4/openim/query_online_status` 查询在线状态 | 200 次/秒 | 每个叠加包 +100 次/秒 | 单次最多查询 500 个用户 |\n| `v4/openim/get_c2c_unread_msg_num` 查询单聊未读数 | 200 次/秒 | 每个叠加包 +100 次/秒 | 如新系统需要同步未读数需注意限频 |\n| `v4/group_open_http_svc/send_group_msg` 群内发普通消息 | 200 次/秒 | 每个叠加包 +100 次/秒 | 单个群发送频率上限为 40 条/秒 |\n| `v4/sns/black_list_get` 拉取黑名单 | 200 次/秒 | 每个叠加包 +100 次/秒 | 关系链相关能力需控制调用频率 |\n\n### 9.3 调频收费说明\n\n腾讯云 IM 服务端 API 调频属于增值服务。官方公告口径为:\n\n| 数据中心 | 服务端 API 调用频率叠加包价格 |\n| --- | --- |\n| 国内数据中心 | 10 元/个/日,日结后付费 |\n| 境外数据中心 | 20 元/个/日,日结后付费 |\n\n注意事项:\n\n1. 调频对单个 SDKAppID 生效,多个 SDKAppID 需要分别配置。\n2. 每个调频项按当日配置的最高数值计费,次日出账。\n3. 体验版不支持调整服务端 API 调用频率上限。\n4. 如果新子系统存在大规模运营推送,必须提前评估 `sendmsg` 与 `batchsendmsg` 的峰值。\n\n## 10. 新子系统设计建议\n\n### 10.1 按接口维度做限流\n\n建议在 IM 子系统服务端增加统一腾讯云 IM Client,并在 Client 内按以下维度做限流:\n\n1. SDKAppID。\n2. API 路径,例如 `openim/sendmsg`、`openim/batchsendmsg`。\n3. 业务来源,例如客服消息、运营推送、系统通知。\n\nKafka 消费者不能只依赖消费并发控制,否则高峰期可能瞬间打满腾讯云 API 限频。\n\n### 10.2 单发和批量发送分流\n\n建议策略:\n\n| 场景 | 推荐接口 |\n| --- | --- |\n| 客服一对一即时回复 | `openim/sendmsg` |\n| 系统给单个用户发送提示 | `openim/sendmsg` |\n| 运营批量推送相同或相似内容 | 优先评估 `openim/batchsendmsg` |\n| 需要强个性化卡片、每人内容不同 | 可用 `sendmsg`,但必须限速 |\n\n### 10.3 错误处理和重试\n\n建议将发送结果分为:\n\n1. 成功:腾讯云返回 `ErrorCode=0` 且业务认为发送完成。\n2. 部分成功:批量发送时部分账号失败,需要保存失败账号列表。\n3. 可重试失败:网络异常、超时、腾讯云内部错误、限频。\n4. 不可重试失败:账号不存在、消息体非法、权限错误、消息包超长。\n\n重试建议采用延迟重试,并设置最大重试次数。超过次数后进入失败表或死信队列,供人工排查。\n\n### 10.4 回调链路可观测性\n\n入站链路建议记录以下指标:\n\n1. 腾讯云回调接收量。\n2. App 后台写 Kafka 成功量和失败量。\n3. Kafka 积压量。\n4. IM 子系统消费延迟。\n5. 入库成功量和幂等重复量。\n6. 会话创建量、工单创建量、分配成功量。\n\n发送链路建议记录:\n\n1. 每个 API 的 QPS。\n2. 限流等待时间。\n3. 腾讯云错误码分布。\n4. 发送成功率。\n5. 可重试失败量和最终失败量。\n\n## 11. 参考来源\n\n- 腾讯云 IM 使用限制:https://cloud.tencent.com/document/product/269/32429\n- 腾讯云 IM 单发单聊消息:https://cloud.tencent.com/document/product/269/2282\n- 腾讯云 IM 批量发单聊消息:https://cloud.tencent.com/document/product/269/1612\n- 腾讯云 IM 服务端 API 调频收费公告:https://cloud.tencent.com/document/product/269/93324\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:07_技术文档/01-子系统-identity-数据库表关系", + "type": "document", + "name": "identity 子系统 — doris数据库相关表与关联关系(供参考)", + "filePath": "07_技术文档/01-子系统-identity-数据库表关系.md", + "summary": "identity 子系统 — doris数据库相关表与关联关系(供参考) 1. 数据库全景 数据库 说明 与 identity 的关系 ods app base data APP基础数据(用户、设备、好友、产品) 核心 — 用户身份主数据 ods app app community 社区数据(帖子、评论、关注) 行为数据,辅助归并 ods app jh da", + "tags": [ + "07_技术文档", + "技术文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# identity 子系统 — doris数据库相关表与关联关系(供参考)\n\n\n---\n\n## 1. 数据库全景\n\n| 数据库 | 说明 | 与 identity 的关系 |\n|--------|------|-------------------|\n| `ods_app_base_data` | APP基础数据(用户、设备、好友、产品) | **核心** — 用户身份主数据 |\n| `ods_app_app_community` | 社区数据(帖子、评论、关注) | 行为数据,辅助归并 |\n| `ods_app_jh_data` | JOYHUB事件数据 | 行为数据,辅助归并 |\n| `ods_oa_oaaftersales` | OA售后系统(客户、订单、测评) | **核心** — 非APP用户身份线索 |\n| `app_tag_data` | 标签数据 | **关键** — 已有 OneID 归并表 |\n\n---\n\n## 2. 核心发现:已存在的 OneID 归并表\n\n### `app_tag_data.user_oneid` — 用户唯一标识归并表(6 字段,已有数据)\n\n> **这是 identity 子系统 M2(归并引擎)的核心参考**,已实现 uuid → one_id 的归并逻辑\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `uuid` | VARCHAR(64) | UNI, NOT NULL | 原始客户唯一标识符 |\n| `one_id` | VARCHAR(64) | NOT NULL | 用户唯一标识(归并后的ID) |\n| `bridge_uuid` | STRING | | 当前 uuid 对应的非当前桥接 uuid |\n| `association_fields` | STRING | | 关联字段 |\n| `detail` | STRING | | uuid 指向 one_id 的证据说明(JSON) |\n| `update_time` | DATETIME(3) | | 同步更新时间 |\n\n**关键设计点**:\n- `uuid` → `one_id` 是多对一关系(多个 uuid 可归并到同一个 one_id)\n- `bridge_uuid` 记录桥接关联,用于跨系统身份串联\n- `detail` 字段存储归并证据(JSON),与设计文档中 `person_profiles.merge_evidence` 概念一致\n- `association_fields` 记录关联字段,对应设计文档中的线索类型\n\n---\n\n## 3. 核心表状态\n\n设计文档定义的 4 张核心表 **尚未在数据库中创建**:\n\n| 表名 | 设计文档定义 | 数据库状态 | 与现有表的关系 |\n|------|-------------|-----------|--------------|\n| `person_profiles` | 真实人主表 | **不存在** | 可参考 `app_tag_data.user_oneid`(one_id) |\n| `person_identity_links` | 身份线索关联表 | **不存在** | 可参考 `ods_oa_oaaftersales.customer_platform_info`(type映射线索类型) |\n| `contact_context_snapshots` | 上下文快照 | **不存在** | 需聚合多表新建 |\n| `device_records` | 设备变化记录 | **不存在** | 可参考 `user_device_token_log`(已有252万条记录) |\n\n---\n\n## 4. 已存在的数据源表\n\n### 4.1 用户与身份核心表\n\n#### `ods_app_base_data.users` — 用户主表(36 字段)\n\n> identity 子系统的核心用户数据源,提供 JOYHUB ID、邮箱、设备号等身份线索\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 用户ID(JOYHUB ID) |\n| `userName` | STRING | | 用户名 |\n| `email` | STRING | | 邮箱 |\n| `deviceToken` | VARCHAR(300) | | 设备推送令牌 |\n| `IMEI` | STRING | | 设备IMEI |\n| `sysType` | VARCHAR(765) | | 系统类型(安卓/IOS/Windows Phone) |\n| `deviceId` | STRING | | 设备ID |\n| `appVersion` | VARCHAR(90) | | APP版本 |\n| `contact_information` | VARCHAR(765) | | 联系方式(电话号码) |\n| `mobile` | STRING | | 手机号 |\n| `area_code` | BIGINT | | 区域代码(美国1,中国86) |\n| `status` | TINYINT | | 1活跃/2封禁/3注销 |\n| `sysTime` | DATETIME | | 系统时间(注册时间) |\n| `created_at` | DATETIME | | 创建时间 |\n\n#### `ods_app_base_data.user_login_last` — 最近登录信息(21 字段)\n\n> 提供设备型号、系统版本、APP版本、国家等信息,是设备变化识别(M4)的重要数据源\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `UserId` | BIGINT | UNI | 用户ID → 关联 `users.id` |\n| `deviceId` | STRING | | 设备ID |\n| `deviceModel` | VARCHAR(150) | | 手机型号 |\n| `device` | VARCHAR(150) | | 手机系统 |\n| `sysType` | VARCHAR(150) | | 系统设备信息与版本 |\n| `appVersion` | VARCHAR(45) | | APP版本号 |\n| `appChannel` | INT | | 渠道 |\n| `countryName` | VARCHAR(600) | | 国家名称 |\n| `countryCode` | VARCHAR(30) | | 国家缩写 |\n| `Time` | DATETIME | | 登录时间 |\n| `Ip` | STRING | | IP地址 |\n\n#### `ods_app_base_data.user_device_token_log` — 设备令牌变更日志(7 字段,252万行)\n\n> 记录设备令牌的添加和更新,可用于追踪设备变化\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | INT | UNI | 主键 |\n| `user_id` | INT | | 用户ID → 关联 `users.id` |\n| `type` | TINYINT | | 0添加/1更新 |\n| `device_id` | STRING | | 设备ID |\n| `new_device_token` | VARCHAR(300) | | 新设备令牌 |\n| `created_at` | DATETIME | | 创建时间 |\n| `client_time` | DATETIME | | 客户端时间 |\n\n#### `ods_app_base_data.user_contact_information_history` — 联系方式变更历史(11 字段,20万行)\n\n> 记录用户手机号等联系方式的变更历史,可用于身份线索追踪\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `user_id` | BIGINT | | 用户ID → 关联 `users.id` |\n| `user_type` | VARCHAR(150) | | 用户角色 |\n| `area_code` | BIGINT | | 区域代码 |\n| `mobile` | STRING | | 手机号 |\n| `area_id` | INT | | 区域ID |\n| `marketing_phone` | TINYINT | | 营销电话开关 |\n| `marketing_sms` | TINYINT | | 个性化广告开关 |\n| `status` | SMALLINT | | 1生效中/2已过期 |\n| `verify_status` | SMALLINT | | 短信验证状态:1通过/2未通过 |\n| `created_at` | BIGINT | | 创建时间 |\n\n#### `ods_app_base_data.banned_device_id` — 设备封禁表(3 字段,6831行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | INT | UNI | 主键 |\n| `device_id` | VARCHAR(765) | | 封禁设备ID |\n| `created_at` | INT | | 创建时间 |\n\n#### `ods_app_base_data.blacklist_users_aggregate` — 用户黑名单汇总(8 字段)\n\n> 按 uid、设备、IP 维度的黑名单,用于风险判断\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | INT | UNI | 主键 |\n| `target_id` | INT | | 1=uid, 2=设备, 3=IP |\n| `target_value` | VARCHAR(1500) | | 字段值 |\n| `category_id` | INT | | 黑名单类别ID |\n| `describe` | VARCHAR(1500) | | 加入原因 |\n\n\n### 4.2 OA 售后系统 — 客户身份数据\n\n#### `ods_oa_oaaftersales.customer` — 客户主表(22 字段,23万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 客户ID |\n| `name` | STRING | | 客户名 |\n| `country` | VARCHAR(60) | | 国家 |\n| `is_black` | TINYINT | | 是否黑名单 |\n| `high_risk` | TINYINT | | 是否高风险 |\n| `erp_contact` | STRING | | ERP联系方式 |\n| `erp_pay_account` | VARCHAR(1500) | | ERP付款账号 |\n\n#### `ods_oa_oaaftersales.customer_platform_info` — 客户平台信息(8 字段,26万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `type` | TINYINT | | **1电话/2邮箱/3joyhub_id/4邮箱编码/5twitter/6facebook** |\n| `customer_id` | INT | | 客户ID → 关联 `customer.id` |\n| `account` | STRING | | 账号值 |\n| `is_delete` | TINYINT | | 是否删除 |\n\n#### `ods_oa_oaaftersales.customer_address` — 客户地址(18 字段,5631行)\n\n> 提供姓名+地址组合,可用于 ORDER_NAME_ADDRESS 线索归并\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `customer_id` | INT | | 客户ID → 关联 `customer.id` |\n| `recipient_name` | STRING | | 收件人姓名 |\n| `phone` | STRING | | 电话 |\n| `zip_code` | STRING | | 邮编 |\n| `country` | VARCHAR(300) | | 国家 |\n| `city` | STRING | | 城市 |\n| `state` | STRING | | 州/省 |\n| `detail` | VARCHAR(1500) | | 详细地址 |\n\n#### `ods_oa_oaaftersales.customer_payment_account` — 客户付款账号(12 字段,10万行)\n\n> 提供收款信息(银行卡、PayPal等),可作为身份归并线索\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `ct_id` | INT | | 客户ID → 关联 `customer.id` |\n| `pay_name` | VARCHAR(150) | | 支付方式 |\n| `account_number` | STRING | | 账号 |\n| `account_name` | STRING | | 账户名 |\n| `card_no` | VARCHAR(300) | | 卡号 |\n\n#### `ods_oa_oaaftersales.customer_bind` — 客户绑定关系(6 字段,4980行)\n\n> 客户间的绑定关系,已有归并概念\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `customer_ids` | STRING | | 绑定的客户ID集合 |\n| `unbind_time` | DATETIME | | 解绑时间 |\n| `is_deleted` | TINYINT | | 是否删除 |\n\n#### `ods_oa_oaaftersales.customer_bind_log` — 客户绑定日志(6 字段,1.2万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `user_id` | INT | | 操作人ID |\n| `bind_customer_ids` | STRING | | 绑定的客户ID |\n| `type` | VARCHAR(765) | | 操作类型 |\n\n#### `ods_oa_oaaftersales.evaluation_order` — 测评订单(55+ 字段,45万行)\n\n> 包含丰富的身份线索:邮箱、电话、JOYHUB ID、社交媒体账号\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 订单ID |\n| `ct_id` | INT | | 客户ID → 关联 `customer.id` |\n| `amazon_order_id` | STRING | | 亚马逊订单号 |\n| `email` | STRING | | 邮箱 |\n| `phone` | STRING | | 电话 |\n| `joyhub_id` | VARCHAR(150) | | JOYHUB ID |\n| `twitter` | STRING | | Twitter账号 |\n| `facebook` | STRING | | Facebook账号 |\n\n#### `ods_oa_oaaftersales.lingxing_order` — 亚马逊订单(30+ 字段,2142万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 订单ID |\n| `amazon_order_id` | VARCHAR(150) | | 亚马逊订单号 |\n| `buyer_name` | VARCHAR(765) | | 买家姓名 |\n| `buyer_email` | VARCHAR(765) | | 买家邮箱 |\n| `phone` | VARCHAR(90) | | 电话 |\n| `postal_code` | VARCHAR(765) | | 邮编 |\n| `address` | VARCHAR(765) | | 地址 |\n\n#### `ods_oa_oaaftersales.phone_records` — 电话记录(30+ 字段,8万行)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 主键 |\n| `ct_id` | INT | | 客户ID → 关联 `customer.id` |\n| `phone` | STRING | | 电话 |\n| `email` | STRING | | 邮箱 |\n| `joyhub_id` | VARCHAR(150) | | JOYHUB ID |\n\n### 4.3 社区行为表\n\n#### `ods_app_app_community.posts` — 帖子表(35 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 帖子ID |\n| `user_id` | INT | | 用户ID → 关联 `users.id` |\n| `status` | SMALLINT | | 10待审核/20拒绝/30通过 |\n| `deleted_at` | INT | | 删除时间(0=未删除) |\n\n#### `ods_app_app_community.post_likes` — 点赞表(5 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `post_id` | BIGINT | | 帖子ID → 关联 `posts.id` |\n| `user_id` | INT | | 用户ID → 关联 `users.id` |\n\n#### `ods_app_app_community.comments` — 评论表(14 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 评论ID |\n| `post_id` | DECIMAL(20,0) | | 帖子ID → 关联 `posts.id` |\n| `user_id` | DECIMAL(20,0) | | 用户ID → 关联 `users.id` |\n\n#### `ods_app_app_community.follows` — 关注关系表(7 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `user_id` | INT | | 关注者ID → 关联 `users.id` |\n| `following_user_id` | INT | | 被关注者ID → 关联 `users.id` |\n\n#### `ods_app_base_data.friends` — 好友关系表(6 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | DECIMAL(20,0) | UNI | 主键 |\n| `user_id` | DECIMAL(20,0) | | 用户ID → 关联 `users.id` |\n| `friend_id` | DECIMAL(20,0) | | 好友ID → 关联 `users.id` |\n\n### 4.4 事件行为表\n\n#### `ods_app_jh_data.events` — APP事件表(18 字段)\n\n> event_type: 13=home, 8=玩具连接, 5=视频等\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 事件ID |\n| `add_date` | DATE | UNI | 记录日期 |\n| `uid` | BIGINT | | 用户ID → 关联 `users.id` |\n| `event_type` | INT | | 事件类型 |\n| `pid` | BIGINT | | 产品ID → 关联 `def_product_list.id` |\n\n#### `ods_app_jh_data.remote_events` — 远程连接事件表(15 字段)\n\n| 字段 | 类型 | 键 | 说明 |\n|------|------|---|------|\n| `id` | BIGINT | UNI | 事件ID |\n| `uid` | BIGINT | | 用户ID → 关联 `users.id` |\n| `call_sn` | VARCHAR(600) | | 远程序列号(格式:uid1_uid2_uuid) |\n| `mode` | INT | | 1文字/2语音/3视频 |\n\n\n---\n\n## 5. 表关联关系图\n\n```\n┌─────────────────────────────────────────────────────────────────────────┐\n│ identity 子系统数据关系 │\n└─────────────────────────────────────────────────────────────────────────┘\n\n ┌───────────────────────┐\n │ app_tag_data │\n │ .user_oneid │\n │ uuid ──► one_id │\n │ (已有归并逻辑) │\n └───────────┬───────────┘\n │ uuid 可能是 email/phone/device\n ▼\n┌──────────────────────────────────────────────────────────────────────┐\n│ ods_app_base_data(APP用户体系) │\n│ │\n│ users ◄── user_profiles (user_id) │\n│ │ user_login_last (UserId) ── deviceId, deviceModel │\n│ │ user_device_token_log (user_id) ── 252万条设备变更记录 │\n│ │ user_contact_information_history (user_id) ── 20万条变更 │\n│ │ friends (user_id / friend_id) │\n│ │ banned_device_id (device_id) ── 设备封禁 │\n│ │ blacklist_users_aggregate (target_id+target_value) │\n│ │ │\n│ └── id = JOYHUB_ID │\n└──────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ ods_oa_oaaftersales(OA售后体系) │\n│ │\n│ customer ◄── customer_platform_info (customer_id) │\n│ │ type: 1电话/2邮箱/3joyhub_id/5twitter/6facebook │\n│ │ customer_address (customer_id) ── 姓名+地址+电话 │\n│ │ customer_payment_account (ct_id) ── 收款账号 │\n│ │ customer_bind (customer_ids) ── 客户绑定关系 │\n│ │ customer_bind_log ── 绑定操作日志 │\n│ │ evaluation_order (ct_id) ── 邮箱/电话/joyhub_id/社媒 │\n│ │ phone_records (ct_id) ── 电话/邮箱/joyhub_id │\n│ │ order_refund (ct_id) ── 返款详情 │\n│ │ │\n│ lingxing_order ── buyer_name + buyer_email + phone + address │\n│ └── lingxing_order_item (amazon_order_id) │\n└──────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ 社区行为(ods_app_app_community) │\n│ │\n│ posts ◄── post_likes (post_id, user_id) │\n│ │ comments (post_id, user_id) │\n│ └── follows (user_id, following_user_id) │\n└──────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ 事件行为(ods_app_jh_data) |\n│ │\n│ events (uid, event_type, pid) │\n│ communities (uid, event_type) │\n│ remote_events (uid, call_sn ── 含对端uid) │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## 6. identity 设计表与现有表的字段映射\n\n### 6.1 person_identity_links(身份线索关联表)— 待建\n\n| clue_type | 设计文档定义 | 数据来源表 | 来源字段 | 数据量级 |\n|-----------|-------------|-----------|---------|---------|\n| JOYHUB_ID | JOYHUB用户ID | `users.id` | id | - |\n| EMAIL | 邮箱 | `users.email` / `evaluation_order.email` / `lingxing_order.buyer_email` / `edm_contact_user.email` / `customer_platform_info`(type=2) | email | - |\n| PHONE | 电话 | `users.contact_information` / `users.mobile` / `evaluation_order.phone` / `lingxing_order.phone` / `customer_platform_info`(type=1) | phone | - |\n| DEVICE | 设备号 | `users.deviceId` / `user_login_last.deviceId` / `user_device_token_log.device_id` | deviceId | 252万条日志 |\n| ORDER_NAME_ADDRESS | 订单姓名+地址 | `lingxing_order.buyer_name` + `lingxing_order.address` / `customer_address.recipient_name` + `customer_address.detail` | name+address | 5631条地址 |\n| SOCIAL_ACCOUNT | 社交媒体(扩展) | `evaluation_order.twitter` / `evaluation_order.facebook` / `customer_platform_info`(type=5,6) | twitter/facebook | - |\n| PAYMENT_ACCOUNT | 收款账号(扩展) | `customer_payment_account.account_number` / `customer.erp_pay_account` | account | 10万条 |\n\n### 6.2 device_records(设备变化记录)— 待建\n\n| 设计字段 | 数据来源表 | 来源字段 |\n|---------|-----------|---------|\n| `joyhub_id` | `users.id` | id |\n| `device_id` | `users.deviceId` / `user_login_last.deviceId` / `user_device_token_log.device_id` | deviceId |\n| `device_model` | `user_login_last.deviceModel` | deviceModel |\n| `os_version` | `user_login_last.device` / `user_login_last.sysType` | device/sysType |\n| `app_version` | `user_login_last.appVersion` / `users.appVersion` | appVersion |\n| `change_type` | `user_device_token_log.type` | 0=NEW, 1=UPDATE |\n\n### 6.3 contact_context_snapshots(上下文快照)— 待建\n\n| 设计字段 | 数据来源子系统 | 来源表 |\n|---------|--------------|--------|\n| `identity_snapshot` | identity | person_profiles + person_identity_links |\n| `transaction_snapshot` | planning | lingxing_order, lingxing_order_item |\n| `service_snapshot` | support | order_refund, evaluation_order, phone_records |\n| `risk_snapshot` | risk | customer.is_black, customer.high_risk, banned_device_id, blacklist_users_aggregate |\n| `device_snapshot` | identity(M4) | device_records, user_login_last |\n| `outreach_snapshot` | outreach | (待确认) |\n\n\n", + "wikilinks": [], + "category": "layer-technical" + } + }, + { + "id": "doc:08_测试相关/USER用户运营系统_原型逐页详细测试用例集", + "type": "document", + "name": "Sheet: 总览", + "filePath": "08_测试相关/USER用户运营系统_原型逐页详细测试用例集.xlsx", + "summary": "Sheet: 总览 项目 内容 生成方式 按4个HTML原型逐页/逐交互拆解,结合全部需求文档与流程图 覆盖HTML 20260504 USER后台ERP MVP管理员首页高保真原型 v7.html;user erp mvp admin prototype v10 1 .html;客服执行.html;用户运营系统 单文件.html 用例粒度 页面、按钮、列表", + "tags": [ + "08_测试相关", + "测试相关" + ], + "complexity": "complex", + "knowledgeMeta": { + "content": "# Sheet: 总览\n项目 | 内容\n生成方式 | 按4个HTML原型逐页/逐交互拆解,结合全部需求文档与流程图\n覆盖HTML | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html;user_erp_mvp_admin_prototype_v10(1).html;客服执行.html;用户运营系统-单文件.html\n用例粒度 | 页面、按钮、列表字段、筛选、详情弹窗、状态流转、异常、权限、数据、端到端验收\n用例总数 | 308\n说明 | 每条用例均单独编写前置条件、操作步骤、预期结果、数据校验、权限校验和验收标准;不使用统一占位模板。\n# Sheet: HTML1-v7管理员首页\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0001 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看测评需求审核卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在Amazon 运营提交的测评需求模拟数据。 | 卡片=测评需求审核;指标=申请 18 / 已批 8;状态=正常;目标页面=需求中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“测评需求审核”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入需求中心待审核入口”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“测评需求审核”和“申请 18 / 已批 8”,状态为“正常”。\n2. 点击后进入或打开与“需求中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对测评需求审核的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留需求中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:测评需求审核;状态:正常;操作:点击卡片进入需求中心待审核入口 | 待执行\nTC-PROTO-0002 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 多渠道触达引擎 | 功能测试 | 管理员工作台查看渠道推送风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在退订率高于基线的推送风险模拟数据。 | 卡片=渠道推送风险;指标=IM、EDM、TEL、App Push 日周月风险与反馈;状态=偏高;目标页面=推送中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“渠道推送风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入推送风险复核”。\n5. 返回首页后再次查看该卡片是否 | 1. 卡片展示“渠道推送风险”和“IM、EDM、TEL、App Push 日周月风险与反馈”,状态为“偏高”。\n2. 点击后进入或打开与“推送中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对渠道推送风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留推送中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:渠道推送风险;状态:偏高;操作:点击卡片进入推送风险复核 | 待执行\nTC-PROTO-0003 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看新增诈骗事件卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在诈骗同步与黑名单待同步事件模拟数据。 | 卡片=新增诈骗事件;指标=昨 5 / 周 18;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“新增诈骗事件”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入风险中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“新增诈骗事件”和“昨 5 / 周 18”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对新增诈骗事件的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:新增诈骗事件;状态:需复核;操作:点击卡片进入风险中心 | 待执行\nTC-PROTO-0004 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看紧急 Listing卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在评分接近 4.2 的 Listing模拟数据。 | 卡片=紧急 Listing;指标=新 3 / 未处理 7;状态=紧急;目标页面=Listing 管理 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“紧急 Listing”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入紧急 Listing 策略”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“紧急 Listing”和“新 3 / 未处理 7”,状态为“紧急”。\n2. 点击后进入或打开与“Listing 管理”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对紧急 Listing的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留Listing 管理语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:紧急 Listing;状态:紧急;操作:点击卡片进入紧急 Listing 策略 | 待执行\nTC-PROTO-0005 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看推广计划与紧急策略卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已确认需求生成的推广计划模拟数据。 | 卡片=推广计划与紧急策略;指标=日 12 / 周 38;状态=注意审核积压;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“推广计划与紧急策略”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入计划中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“推广计划与紧急策略”和“日 12 / 周 38”,状态为“注意审核积压”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对推广计划与紧急策略的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:推广计划与紧急策略;状态:注意审核积压;操作:点击卡片进入计划中心 | 待执行\nTC-PROTO-0006 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 评价结果追踪 | 功能测试 | 管理员工作台查看评价产出趋势卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在真实消费者回评完成趋势模拟数据。 | 卡片=评价产出趋势;指标=日 18 / 周 96;状态=稳定;目标页面=数据中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“评价产出趋势”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入评价产出趋势”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“评价产出趋势”和“日 18 / 周 96”,状态为“稳定”。\n2. 点击后进入或打开与“数据中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对评价产出趋势的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留数据中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:评价产出趋势;状态:稳定;操作:点击卡片进入评价产出趋势 | 待执行\nTC-PROTO-0007 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看黑名单同步严重度卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在黑名单系统接口超时模拟数据。 | 卡片=黑名单同步严重度;指标=失败 2 / 高危 1;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“黑名单同步严重度”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入黑名单同步”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“黑名单同步严重度”和“失败 2 / 高危 1”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对黑名单同步严重度的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:黑名单同步严重度;状态:需复核;操作:点击卡片进入黑名单同步 | 待执行\nTC-PROTO-0008 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | KOC/KOL协作 | 功能测试 | 管理员工作台查看KOC/KOL 对接卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在PR 对外联系、价格、CODE、返点和提款进度模拟数据。 | 卡片=KOC/KOL 对接;指标=2 个逾期;状态=逾期;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“KOC/KOL 对接”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入对外合作跟进”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“KOC/KOL 对接”和“2 个逾期”,状态为“逾期”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对KOC/KOL 对接的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:KOC/KOL 对接;状态:逾期;操作:点击卡片进入对外合作跟进 | 待执行\nTC-PROTO-0009 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 客服工单与管理 | 功能测试 | 管理员工作台查看菲律宾团队管理卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在菲律宾团队工作时长、请假、缺席、人均产出模拟数据。 | 卡片=菲律宾团队管理;指标=风险 2 / 缺口 1;状态=排班风险;目标页面=客服中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“菲律宾团队管理”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入客服中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“菲律宾团队管理”和“风险 2 / 缺口 1”,状态为“排班风险”。\n2. 点击后进入或打开与“客服中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对菲律宾团队管理的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留客服中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:菲律宾团队管理;状态:排班风险;操作:点击卡片进入客服中心 | 待执行\nTC-PROTO-0010 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 审计与通知中心 | 功能测试 | 管理员工作台查看审核积压与风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已发现问题汇总到总页面模拟数据。 | 卡片=审核积压与风险;指标=卡点 4;状态=影响进度;目标页面=工作台 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“审核积压与风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入处理卡点”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“审核积压与风险”和“卡点 4”,状态为“影响进度”。\n2. 点击后进入或打开与“工作台”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对审核积压与风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留工作台语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:审核积压与风险;状态:影响进度;操作:点击卡片进入处理卡点 | 待执行\nTC-PROTO-0011 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对测评需求执行接收动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“测评需求”;当前环节为“Amazon 已批准”,负责人为“用户运营负责人”。 | 事项=测评需求;来源=飞书需求表单 DEMO-001;截止=今日 18:00;处理动作=接收;描述=评分 4.46,低于 4.5,需要生成用户互动与真实评价跟踪计划 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“测评需求”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“接收”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:测评需求已核对”。\n7. 点击确认提交。 | 1. 队列行展示测评需求、Amazon 已批准、用户运营负责人、今日 18:00。\n2. 详情弹窗打开,展示来源“飞书需求表单 DEMO-001”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:测评需求;操作:接收 | 待执行\nTC-PROTO-0012 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 测评需求处理意见为空时阻止提交 | P2 | 已打开“测评需求”详情弹窗;当前用户有“接收”权限。 | 动作类型=通过/确认;处理意见=空;事项=测评需求 | 1. 在“测评需求”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0013 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对昨日推送风险执行复核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“昨日推送风险”;当前环节为“待复核”,负责人为“用户运营组长”。 | 事项=昨日推送风险;来源=推送风险自动单 DEMO-006;截止=今日 12:00;处理动作=复核;描述=昨日推送退订率高于基线,需复核人群、素材和文案 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“昨日推送风险”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“复核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:昨日推送风险已核对”。\n7. 点击确认提交。 | 1. 队列行展示昨日推送风险、待复核、用户运营组长、今日 12:00。\n2. 详情弹窗打开,展示来源“推送风险自动单 DEMO-006”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:昨日推送风险;操作:复核 | 待执行\nTC-PROTO-0014 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 昨日推送风险处理意见为空时阻止提交 | P2 | 已打开“昨日推送风险”详情弹窗;当前用户有“复核”权限。 | 动作类型=通过/确认;处理意见=空;事项=昨日推送风险 | 1. 在“昨日推送风险”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0015 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对待同步黑名单执行审核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“待同步黑名单”;当前环节为“待审核”,负责人为“风险负责人”。 | 事项=待同步黑名单;来源=客服升级表单 DEMO-003;截止=今日 14:00;处理动作=审核;描述=同一 JOYHUB ID 与多个 Profile ID 关联异常样品申请,邮箱和设备号已脱敏 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“待同步黑名单”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:待同步黑名单已核对”。\n7. 点击确认提交。 | 1. 队列行展示待同步黑名单、待审核、风险负责人、今日 14:00。\n2. 详情弹窗打开,展示来源“客服升级表单 DEMO-003”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:待同步黑名单;操作:审核 | 待执行\nTC-PROTO-0016 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 待同步黑名单处理意见为空时阻止提交 | P2 | 已打开“待同步黑名单”详情弹窗;当前用户有“审核”权限。 | 动作类型=通过/确认;处理意见=空;事项=待同步黑名单 | 1. 在“待同步黑名单”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0017 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对紧急策略审批执行审批动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“紧急策略审批”;当前环节为“待系统管理员确认”,负责人为“Amazon 运营总监”。 | 事项=紧急策略审批;来源=紧急 Listing 表单 DEMO-004;截止=今日 11:30;处理动作=审批;描述=当前评分 4.21,接近 4.2 紧急阈值,需要 Amazon 与用户运营联合策略 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“紧急策略审批”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审批”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:紧急策略审批已核对”。\n7. 点击确认提交。 | 1. 队列行展示紧急策略审批、待系统管理员确认、Amazon 运营总监、今日 11:30。\n2. 详情弹窗打开,展示来源“紧急 Listing 表单 DEMO-004”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:紧急策略审批;操作:审批 | 待执行\nTC-PROTO-0018 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 紧急策略审批处理意见为空时阻止提交 | P2 | 已打开“紧急策略审批”详情弹窗;当前用户有“审批”权限。 | 动作类型=通过/确认;处理意见=空;事项=紧急策略审批 | 1. 在“紧急策略审批”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0019 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对差评跟进执行分配动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“差评跟进”;当前环节为“客服升级”,负责人为“客服负责人”。 | 事项=差评跟进;来源=飞书客服需求 DEMO-005;截止=明日 10:00;处理动作=分配;描述=用户反馈产品说明理解偏差,需要客服跟进并回传产品改进建议 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“差评跟进”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“分配”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:差评跟进已核对”。\n7. 点击确认提交。 | 1. 队列行展示差评跟进、客服升级、客服负责人、明日 10:00。\n2. 详情弹窗打开,展示来源“飞书客服需求 DEMO-005”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:差评跟进;操作:分配 | 待执行\nTC-PROTO-0020 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 差评跟进处理意见为空时阻止提交 | P2 | 已打开“差评跟进”详情弹窗;当前用户有“分配”权限。 | 动作类型=通过/确认;处理意见=空;事项=差评跟进 | 1. 在“差评跟进”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0021 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入需求中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“需求中心”;模拟数据已加载。 | 页面=需求中心;主按钮=Amazon 提交测评需求;辅助按钮=待审核入口;字段=需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 1. 在管理员首页左侧导航点击“需求中心”。\n2. 观察页面标题是否切换为“需求中心列表”。\n3. 检查列表表头是否包含:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作。\n4. 点击页面主按钮“Amazon 提交测评需求”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“待审核入口”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“需求中心”。\n2. 列表字段与原型定义一致。\n3. “Amazon 提交测评需求”和“待审核入口”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问需求中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合需求中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:需求中心;按钮:Amazon 提交测评需求/待审核入口;字段:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 待执行\nTC-PROTO-0022 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入Listing 管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“Listing 管理”;模拟数据已加载。 | 页面=Listing 管理;主按钮=创建紧急策略;辅助按钮=更多;字段=站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 1. 在管理员首页左侧导航点击“Listing 管理”。\n2. 观察页面标题是否切换为“Listing 管理列表”。\n3. 检查列表表头是否包含:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度。\n4. 点击页面主按钮“创建紧急策略”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“更多”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“Listing 管理”。\n2. 列表字段与原型定义一致。\n3. “创建紧急策略”和“更多”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问Listing 管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合Listing 管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:Listing 管理;按钮:创建紧急策略/更多;字段:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 待执行\nTC-PROTO-0023 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入计划中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“计划中心”;模拟数据已加载。 | 页面=计划中心;主按钮=生成计划;辅助按钮=批量审批;字段=计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 1. 在管理员首页左侧导航点击“计划中心”。\n2. 观察页面标题是否切换为“计划中心列表”。\n3. 检查列表表头是否包含:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人。\n4. 点击页面主按钮“生成计划”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“批量审批”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“计划中心”。\n2. 列表字段与原型定义一致。\n3. “生成计划”和“批量审批”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问计划中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合计划中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:计划中心;按钮:生成计划/批量审批;字段:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 待执行\nTC-PROTO-0024 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入推送中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“推送中心”;模拟数据已加载。 | 页面=推送中心;主按钮=计划与推送分配;辅助按钮=风险复核;字段=推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 1. 在管理员首页左侧导航点击“推送中心”。\n2. 观察页面标题是否切换为“推送中心列表”。\n3. 检查列表表头是否包含:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订。\n4. 点击页面主按钮“计划与推送分配”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“风险复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“推送中心”。\n2. 列表字段与原型定义一致。\n3. “计划与推送分配”和“风险复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问推送中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合推送中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:推送中心;按钮:计划与推送分配/风险复核;字段:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 待执行\nTC-PROTO-0025 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入客服中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“客服中心”;模拟数据已加载。 | 页面=客服中心;主按钮=分配工单;辅助按钮=流转;字段=工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 1. 在管理员首页左侧导航点击“客服中心”。\n2. 观察页面标题是否切换为“客服中心列表”。\n3. 检查列表表头是否包含:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出。\n4. 点击页面主按钮“分配工单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“流转”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“客服中心”。\n2. 列表字段与原型定义一致。\n3. “分配工单”和“流转”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问客服中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合客服中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:客服中心;按钮:分配工单/流转;字段:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 待执行\nTC-PROTO-0026 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入风险中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“风险中心”;模拟数据已加载。 | 页面=风险中心;主按钮=同步黑名单;辅助按钮=规则复核;字段=事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“风险中心”。\n2. 观察页面标题是否切换为“风险中心列表”。\n3. 检查列表表头是否包含:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“同步黑名单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“规则复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“风险中心”。\n2. 列表字段与原型定义一致。\n3. “同步黑名单”和“规则复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问风险中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合风险中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:风险中心;按钮:同步黑名单/规则复核;字段:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0027 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 数据中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入数据中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“数据中心”;模拟数据已加载。 | 页面=数据中心;主按钮=立即同步;辅助按钮=导出;字段=来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“数据中心”。\n2. 观察页面标题是否切换为“数据中心列表”。\n3. 检查列表表头是否包含:来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“立即同步”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“导出”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“数据中心”。\n2. 列表字段与原型定义一致。\n3. “立即同步”和“导出”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问数据中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合数据中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:数据中心;按钮:立即同步/导出;字段:来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0028 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入报表中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“报表中心”;模拟数据已加载。 | 页面=报表中心;主按钮=生成/下载报表;辅助按钮=上传记录;字段=报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 1. 在管理员首页左侧导航点击“报表中心”。\n2. 观察页面标题是否切换为“报表中心列表”。\n3. 检查列表表头是否包含:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏。\n4. 点击页面主按钮“生成/下载报表”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“上传记录”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“报表中心”。\n2. 列表字段与原型定义一致。\n3. “生成/下载报表”和“上传记录”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问报表中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合报表中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:报表中心;按钮:生成/下载报表/上传记录;字段:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 待执行\nTC-PROTO-0029 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入系统管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“系统管理”;模拟数据已加载。 | 页面=系统管理;主按钮=新建账号;辅助按钮=离职管理;字段=配置ID、模块、说明、权限分配、审计日志 | 1. 在管理员首页左侧导航点击“系统管理”。\n2. 观察页面标题是否切换为“系统管理列表”。\n3. 检查列表表头是否包含:配置ID、模块、说明、权限分配、审计日志。\n4. 点击页面主按钮“新建账号”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“离职管理”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“系统管理”。\n2. 列表字段与原型定义一致。\n3. “新建账号”和“离职管理”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问系统管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合系统管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:系统管理;按钮:新建账号/离职管理;字段:配置ID、模块、说明、权限分配、审计日志 | 待执行\nTC-PROTO-0030 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 7 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 7 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 7 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 7 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0031 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 30 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 30 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 30 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 30 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0032 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换本月时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=本月;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“本月”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“本月”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0033 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换自定义时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=自定义;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“自定义”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“自定义”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0170 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换全部标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“全部”。 | 标签=全部;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“全部”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“全部”。 | 1. 队列只展示与“全部”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:全部 | 待执行\nTC-PROTO-0171 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换审核标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“审核”。 | 标签=审核;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“审核”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“审核”。 | 1. 队列只展示与“审核”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:审核 | 待执行\nTC-PROTO-0172 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换黑名单标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“黑名单”。 | 标签=黑名单;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“黑名单”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“黑名单”。 | 1. 队列只展示与“黑名单”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:黑名单 | 待执行\nTC-PROTO-0173 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换推送标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“推送”。 | 标签=推送;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“推送”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“推送”。 | 1. 队列只展示与“推送”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:推送 | 待执行\nTC-PROTO-0174 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门全部部门状态全部状态风险全部风险负责人全部负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=全部部门;状态=全部状态;风险=全部风险;负责人=全部负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“全部部门”。\n3. 选择状态“全部状态”、风险“全部风险”、负责人“全部负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合全部部门/全部状态/全部风险/全部负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0175 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门Amazon 运营状态待审批风险全部风险负责人Amazon 总监组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=Amazon 运营;状态=待审批;风险=全部风险;负责人=Amazon 总监 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“Amazon 运营”。\n3. 选择状态“待审批”、风险“全部风险”、负责人“Amazon 总监”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合Amazon 运营/待审批/全部风险/Amazon 总监的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0176 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门用户运营状态待复核风险偏高负责人用户运营组长组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=用户运营;状态=待复核;风险=偏高;负责人=用户运营组长 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“用户运营”。\n3. 选择状态“待复核”、风险“偏高”、负责人“用户运营组长”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合用户运营/待复核/偏高/用户运营组长的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0177 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门客服状态客服升级风险高风险负责人客服负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=客服;状态=客服升级;风险=高风险;负责人=客服负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“客服”。\n3. 选择状态“客服升级”、风险“高风险”、负责人“客服负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合客服/客服升级/高风险/客服负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0178 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门系统管理员状态待系统管理员确认风险紧急负责人系统管理员组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=系统管理员;状态=待系统管理员确认;风险=紧急;负责人=系统管理员 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“系统管理员”。\n3. 选择状态“待系统管理员确认”、风险“紧急”、负责人“系统管理员”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合系统管理员/待系统管理员确认/紧急/系统管理员的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0220 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“需求中心”;存在业务条件:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。 | 按钮=查看完整信息;条件=JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:打开详情后点击查看完整信息。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0221 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:分配 | P2 | 系统管理员已进入“需求中心”;存在业务条件:测评需求Amazon已批准待用户运营接收。 | 按钮=分配;条件=测评需求Amazon已批准待用户运营接收 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:测评需求Amazon已批准待用户运营接收。\n3. 点击该行或页面上的“分配”按钮。\n4. 按页面提示执行:选择下一负责人为用户运营负责人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配”入口可用且文案正确。\n2. 操作后结果为:负责人变更并通知。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=分配 | 待执行\nTC-PROTO-0222 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:审批/确认 | P2 | 系统管理员已进入“需求中心”;存在业务条件:需求评分4.46低于4.5。 | 按钮=审批/确认;条件=需求评分4.46低于4.5 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:需求评分4.46低于4.5。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:选择通过/确认并填写计划建议。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:需求进入待生成计划。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=审批/确认 | 待执行\nTC-PROTO-0223 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:查看完整信息 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:评分4.21接近4.2紧急阈值。 | 按钮=查看完整信息;条件=评分4.21接近4.2紧急阈值 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:评分4.21接近4.2紧急阈值。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:查看ASIN完整站点与评价数据。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:敏感数据按权限展示。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=查看完整信息 | 待执行\nTC-PROTO-0224 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:创建紧急策略 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:紧急Listing未处理7条。 | 按钮=创建紧急策略;条件=紧急Listing未处理7条 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:紧急Listing未处理7条。\n3. 点击该行或页面上的“创建紧急策略”按钮。\n4. 按页面提示执行:填写策略参与人员与截止时间。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “创建紧急策略”入口可用且文案正确。\n2. 操作后结果为:生成紧急策略审批事项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的创建紧急策略动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=创建紧急策略 | 待执行\nTC-PROTO-0225 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:审批/确认 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:待系统管理员确认。 | 按钮=审批/确认;条件=待系统管理员确认 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:待系统管理员确认。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:确认联合策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:状态进入用户运营执行。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=审批/确认 | 待执行\nTC-PROTO-0226 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:生成计划 | P2 | 系统管理员已进入“计划中心”;存在业务条件:已确认需求存在且目标量明确。 | 按钮=生成计划;条件=已确认需求存在且目标量明确 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:已确认需求存在且目标量明确。\n3. 点击该行或页面上的“生成计划”按钮。\n4. 按页面提示执行:选择推新/回评/免评并拆分计划项。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成计划”入口可用且文案正确。\n2. 操作后结果为:生成计划ID和计划项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的生成计划动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=生成计划 | 待执行\nTC-PROTO-0227 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:批量审批 | P2 | 系统管理员已进入“计划中心”;存在业务条件:多条计划处于待审批。 | 按钮=批量审批;条件=多条计划处于待审批 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:多条计划处于待审批。\n3. 点击该行或页面上的“批量审批”按钮。\n4. 按页面提示执行:勾选多条计划并提交统一审批意见。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “批量审批”入口可用且文案正确。\n2. 操作后结果为:批量生成审批记录。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的批量审批动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=批量审批 | 待执行\nTC-PROTO-0228 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:流转 | P2 | 系统管理员已进入“计划中心”;存在业务条件:计划覆盖状态部分覆盖。 | 按钮=流转;条件=计划覆盖状态部分覆盖 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:计划覆盖状态部分覆盖。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看计划状态流转记录。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:展示创建、审批、执行节点。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=流转 | 待执行\nTC-PROTO-0229 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:计划与推送分配 | P2 | 系统管理员已进入“推送中心”;存在业务条件:周度推送计划待审。 | 按钮=计划与推送分配;条件=周度推送计划待审 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:周度推送计划待审。\n3. 点击该行或页面上的“计划与推送分配”按钮。\n4. 按页面提示执行:分配IM/EDM/TEL/App Push策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “计划与推送分配”入口可用且文案正确。\n2. 操作后结果为:生成推送任务。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的计划与推送分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=计划与推送分配 | 待执行\nTC-PROTO-0230 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:风险复核 | P2 | 系统管理员已进入“推送中心”;存在业务条件:退订率高于基线。 | 按钮=风险复核;条件=退订率高于基线 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线。\n3. 点击该行或页面上的“风险复核”按钮。\n4. 按页面提示执行:查看人群、素材、文案并选择暂停同策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “风险复核”入口可用且文案正确。\n2. 操作后结果为:推送状态变暂停待审。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的风险复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=风险复核 | 待执行\nTC-PROTO-0231 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:导出 | P2 | 系统管理员已进入“推送中心”;存在业务条件:当前筛选为推送风险。 | 按钮=导出;条件=当前筛选为推送风险 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:当前筛选为推送风险。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出推送ID、计划、渠道、发送点击回复退订。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:导出文件脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=导出 | 待执行\nTC-PROTO-0232 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:分配工单 | P2 | 系统管理员已进入“客服中心”;存在业务条件:差评跟进客服升级。 | 按钮=分配工单;条件=差评跟进客服升级 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:差评跟进客服升级。\n3. 点击该行或页面上的“分配工单”按钮。\n4. 按页面提示执行:选择客服A并填写分配原因。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配工单”入口可用且文案正确。\n2. 操作后结果为:工单状态变处理中。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的分配工单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=分配工单 | 待执行\nTC-PROTO-0233 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:流转 | P2 | 系统管理员已进入“客服中心”;存在业务条件:承诺配合用户待回访。 | 按钮=流转;条件=承诺配合用户待回访 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:承诺配合用户待回访。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看待回访和请假0.5天影响。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:生成回访待办。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=流转 | 待执行\nTC-PROTO-0234 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:导出 | P2 | 系统管理员已进入“客服中心”;存在业务条件:菲律宾团队管理。 | 按钮=导出;条件=菲律宾团队管理 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:菲律宾团队管理。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出工作时长、出勤、人均产出。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:仅主管可导出。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=导出 | 待执行\nTC-PROTO-0235 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:同步黑名单 | P2 | 系统管理员已进入“风险中心”;存在业务条件:接口超时失败待重试。 | 按钮=同步黑名单;条件=接口超时失败待重试 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:接口超时失败待重试。\n3. 点击该行或页面上的“同步黑名单”按钮。\n4. 按页面提示执行:点击同步黑名单。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “同步黑名单”入口可用且文案正确。\n2. 操作后结果为:失败保留并进入重试队列。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的同步黑名单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=同步黑名单 | 待执行\nTC-PROTO-0236 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:规则复核 | P2 | 系统管理员已进入“风险中心”;存在业务条件:退订率高于基线规则提醒。 | 按钮=规则复核;条件=退订率高于基线规则提醒 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线规则提醒。\n3. 点击该行或页面上的“规则复核”按钮。\n4. 按页面提示执行:查看规则依据并确认/误报。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “规则复核”入口可用且文案正确。\n2. 操作后结果为:复核结论写入风险事件。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的规则复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=规则复核 | 待执行\nTC-PROTO-0237 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“风险中心”;存在业务条件:Profile/邮箱/设备号脱敏。 | 按钮=查看完整信息;条件=Profile/邮箱/设备号脱敏 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:Profile/邮箱/设备号脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:授权角色查看完整主体摘要。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0238 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:生成/下载报表 | P2 | 系统管理员已进入“报表中心”;存在业务条件:Listing健康日报每日08:30自动生成。 | 按钮=生成/下载报表;条件=Listing健康日报每日08:30自动生成 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:Listing健康日报每日08:30自动生成。\n3. 点击该行或页面上的“生成/下载报表”按钮。\n4. 按页面提示执行:点击生成/下载报表。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成/下载报表”入口可用且文案正确。\n2. 操作后结果为:生成记录可下载且脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的生成/下载报表动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=生成/下载报表 | 待执行\nTC-PROTO-0239 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:上传记录 | P2 | 系统管理员已进入“报表中心”;存在业务条件:推送效果与风险复盘支持上传补充记录。 | 按钮=上传记录;条件=推送效果与风险复盘支持上传补充记录 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:推送效果与风险复盘支持上传补充记录。\n3. 点击该行或页面上的“上传记录”按钮。\n4. 按页面提示执行:上传人工复核附件。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “上传记录”入口可用且文案正确。\n2. 操作后结果为:附件关联报表ID。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的上传记录动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=上传记录 | 待执行\nTC-PROTO-0240 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:新建账号 | P2 | 系统管理员已进入“系统管理”;存在业务条件:按部门角色站点数据范围开通。 | 按钮=新建账号;条件=按部门角色站点数据范围开通 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:按部门角色站点数据范围开通。\n3. 点击该行或页面上的“新建账号”按钮。\n4. 按页面提示执行:录入账号、部门、角色、站点范围。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “新建账号”入口可用且文案正确。\n2. 操作后结果为:账号可登录且权限生效。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的新建账号动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=新建账号 | 待执行\nTC-PROTO-0241 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:离职管理 | P2 | 系统管理员已进入“系统管理”;存在业务条件:停用账号交接任务回收权限。 | 按钮=离职管理;条件=停用账号交接任务回收权限 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:停用账号交接任务回收权限。\n3. 点击该行或页面上的“离职管理”按钮。\n4. 按页面提示执行:选择离职员工并指定交接人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “离职管理”入口可用且文案正确。\n2. 操作后结果为:账号停用且敏感权限回收。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的离职管理动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=离职管理 | 待执行\nTC-PROTO-0285 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:重复点击处理卡点 | P2 | 已进入“工作台”;准备可执行场景:重复点击处理卡点。 | 动作=连续点击处理卡点按钮2次;预期=只打开一个详情/处理弹窗,不重复创建处理记录 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击处理卡点按钮2次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只打开一个详情/处理弹窗,不重复创建处理记录。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:重复点击处理卡点 | 待执行\nTC-PROTO-0286 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:刷新后保持时间范围 | P2 | 已进入“工作台”;准备可执行场景:刷新后保持时间范围。 | 动作=选择最近30天后刷新页面;预期=仍显示最近30天或按产品定义恢复默认并不报错 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:选择最近30天后刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:仍显示最近30天或按产品定义恢复默认并不报错。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:刷新后保持时间范围 | 待执行\nTC-PROTO-0287 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:黑名单同步重复提交 | P2 | 已进入“风险中心”;准备可执行场景:黑名单同步重复提交。 | 动作=同步黑名单按钮连续点击;预期=只生成一次同步任务,第二次提示处理中 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同步黑名单按钮连续点击。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只生成一次同步任务,第二次提示处理中。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:黑名单同步重复提交 | 待执行\nTC-PROTO-0288 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统稳定性与幂等 | 异常场景 | 报表中心稳定性校验:报表生成中重复下载 | P2 | 已进入“报表中心”;准备可执行场景:报表生成中重复下载。 | 动作=报表状态自动生成中点击下载;预期=提示生成中,不下载空文件 | 1. 打开原型页面“报表中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:报表状态自动生成中点击下载。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示生成中,不下载空文件。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:报表生成中重复下载 | 待执行\n# Sheet: HTML2-v10管理原型\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0034 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面用户管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“用户管理”。 | 现有页面=用户中心 / 用户;字段=JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍;查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;MVP用途=用户画像筛选、推送人群、客服定位、风险排查 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“用户管理”。\n3. 核对现有页面名称是否为“用户中心 / 用户”。\n4. 展开字段详情,逐项核对字段:JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍。\n5. 在查询条件区按“搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “用户管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:用户管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0035 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 异常场景 | 用户管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“用户管理”关系页面;用户有查询权限。 | 查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;输入值=不存在的标签/身份/国家组合 | 1. 在“用户管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0036 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面公域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-用户标签”。 | 现有页面=标签 / 公域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态;查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;MVP用途=公域人群圈选、覆盖人数评估、推送前过滤 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 公域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态。\n5. 在查询条件区按“搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0037 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 异常场景 | 公域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-用户标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0038 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 功能测试 | 现有ERP页面公域-产品标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-产品标签”。 | 现有页面=标签 / 公域产品;字段=标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人;查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;MVP用途=产品分层、Listing 健康策略、产品绑定率分析 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-产品标签”。\n3. 核对现有页面名称是否为“标签 / 公域产品”。\n4. 展开字段详情,逐项核对字段:标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人。\n5. 在查询条件区按“搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-产品标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-产品标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0039 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 异常场景 | 公域-产品标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-产品标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-产品标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0040 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面私域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“私域-用户标签”。 | 现有页面=标签 / 私域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态;查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;MVP用途=私域精细运营、客服分组、活动复盘、风险用户隔离 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“私域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 私域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态。\n5. 在查询条件区按“标签分类、打标方式、覆盖人数、状态、时间范围”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “私域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:私域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0041 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 异常场景 | 私域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“私域-用户标签”关系页面;用户有查询权限。 | 查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;输入值=不存在的标签/身份/国家组合 | 1. 在“私域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0042 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面身份管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“身份管理”。 | 现有页面=身份;字段=账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作;查询条件=身份名称、身份分组、状态、更新时间;MVP用途=识别官方、品牌、达人、风险、客服等用户身份 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“身份管理”。\n3. 核对现有页面名称是否为“身份”。\n4. 展开字段详情,逐项核对字段:账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作。\n5. 在查询条件区按“身份名称、身份分组、状态、更新时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “身份管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:身份管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0043 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 异常场景 | 身份管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“身份管理”关系页面;用户有查询权限。 | 查询条件=身份名称、身份分组、状态、更新时间;输入值=不存在的标签/身份/国家组合 | 1. 在“身份管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0044 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 用户标签关系用于人群圈选、用户画像、风险过滤 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 用户标签;关系类型=多对多;用途=人群圈选、用户画像、风险过滤 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 用户标签”。\n4. 查看关系类型是否显示为“多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“人群圈选、用户画像、风险过滤”。 | 1. 关系“用户 - 用户标签”存在。\n2. 类型展示为“多对多”。\n3. 详情能说明该关系服务于“人群圈选、用户画像、风险过滤”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 用户标签 | 待执行\nTC-PROTO-0045 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 身份关系用于官方、品牌、达人、客服、风险身份识别 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 身份;关系类型=待确认:一对多或多对多;用途=官方、品牌、达人、客服、风险身份识别 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 身份”。\n4. 查看关系类型是否显示为“待确认:一对多或多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“官方、品牌、达人、客服、风险身份识别”。 | 1. 关系“用户 - 身份”存在。\n2. 类型展示为“待确认:一对多或多对多”。\n3. 详情能说明该关系服务于“官方、品牌、达人、客服、风险身份识别”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 身份 | 待执行\nTC-PROTO-0046 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 产品关系用于测评用户池、客服定位 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 产品;关系类型=绑定/连接产品;用途=测评用户池、客服定位 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 产品”。\n4. 查看关系类型是否显示为“绑定/连接产品”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“测评用户池、客服定位”。 | 1. 关系“用户 - 产品”存在。\n2. 类型展示为“绑定/连接产品”。\n3. 详情能说明该关系服务于“测评用户池、客服定位”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 产品 | 待执行\nTC-PROTO-0047 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验产品 - 产品标签 - Listing关系用于Listing 健康策略、ASIN 归因 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=产品 - 产品标签 - Listing;关系类型=产品分层;用途=Listing 健康策略、ASIN 归因 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“产品 - 产品标签 - Listing”。\n4. 查看关系类型是否显示为“产品分层”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“Listing 健康策略、ASIN 归因”。 | 1. 关系“产品 - 产品标签 - Listing”存在。\n2. 类型展示为“产品分层”。\n3. 详情能说明该关系服务于“Listing 健康策略、ASIN 归因”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:产品 - 产品标签 - Listing | 待执行\nTC-PROTO-0048 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 活动关系用于KOC/KOL、私域运营沉淀 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 活动;关系类型=活动参与;用途=KOC/KOL、私域运营沉淀 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 活动”。\n4. 查看关系类型是否显示为“活动参与”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“KOC/KOL、私域运营沉淀”。 | 1. 关系“用户 - 活动”存在。\n2. 类型展示为“活动参与”。\n3. 详情能说明该关系服务于“KOC/KOL、私域运营沉淀”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 活动 | 待执行\nTC-PROTO-0049 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 推送关系用于IM/EDM/TEL/App Push 频控、点击、回复、退订 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 推送;关系类型=一对多;用途=IM/EDM/TEL/App Push 频控、点击、回复、退订 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 推送”。\n4. 查看关系类型是否显示为“一对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“IM/EDM/TEL/App Push 频控、点击、回复、退订”。 | 1. 关系“用户 - 推送”存在。\n2. 类型展示为“一对多”。\n3. 详情能说明该关系服务于“IM/EDM/TEL/App Push 频控、点击、回复、退订”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 推送 | 待执行\nTC-PROTO-0050 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 风险/黑名单关系用于诈骗同步、客服升级、风险用户隔离 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 风险/黑名单;关系类型=风险隔离;用途=诈骗同步、客服升级、风险用户隔离 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 风险/黑名单”。\n4. 查看关系类型是否显示为“风险隔离”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“诈骗同步、客服升级、风险用户隔离”。 | 1. 关系“用户 - 风险/黑名单”存在。\n2. 类型展示为“风险隔离”。\n3. 详情能说明该关系服务于“诈骗同步、客服升级、风险用户隔离”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 风险/黑名单 | 待执行\nTC-PROTO-0051 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行用户主档查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=用户主档查询;查询条件=JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间;预期输出=用户主档、标签、身份、产品关系、近期活跃;使用页面=用户中心 / 客服中心 / 风险中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“用户主档查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间。\n5. 点击查询。\n6. 查看结果区是否输出:用户主档、标签、身份、产品关系、近期活跃。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “用户主档查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“用户主档、标签、身份、产品关系、近期活跃”。\n4. 跳转到“用户中心 / 客服中心 / 风险中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:用户主档查询 | 待执行\nTC-PROTO-0052 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行推送前人群圈选并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=推送前人群圈选;查询条件=标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数;预期输出=候选用户数、预计触达、频控风险、可保存人群包;使用页面=计划中心 / 推送中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“推送前人群圈选”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数。\n5. 点击查询。\n6. 查看结果区是否输出:候选用户数、预计触达、频控风险、可保存人群包。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “推送前人群圈选”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“候选用户数、预计触达、频控风险、可保存人群包”。\n4. 跳转到“计划中心 / 推送中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:推送前人群圈选 | 待执行\nTC-PROTO-0053 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行测评与真实评价跟踪人群并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=测评与真实评价跟踪人群;查询条件=ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除;预期输出=推荐用户池、排除用户池、进入计划中心;使用页面=需求中心 / 计划中心 / 客服中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“测评与真实评价跟踪人群”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除。\n5. 点击查询。\n6. 查看结果区是否输出:推荐用户池、排除用户池、进入计划中心。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “测评与真实评价跟踪人群”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“推荐用户池、排除用户池、进入计划中心”。\n4. 跳转到“需求中心 / 计划中心 / 客服中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:测评与真实评价跟踪人群 | 待执行\nTC-PROTO-0054 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行标签覆盖查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=标签覆盖查询;查询条件=标签分类、打标方式、覆盖人数、最新打标时间、状态;预期输出=标签列表、覆盖趋势、异常覆盖提示;使用页面=现有ERP / 数据中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“标签覆盖查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签分类、打标方式、覆盖人数、最新打标时间、状态。\n5. 点击查询。\n6. 查看结果区是否输出:标签列表、覆盖趋势、异常覆盖提示。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “标签覆盖查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“标签列表、覆盖趋势、异常覆盖提示”。\n4. 跳转到“现有ERP / 数据中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:标签覆盖查询 | 待执行\nTC-PROTO-0055 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行身份风险查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=身份风险查询;查询条件=身份名称、身份分组、风险等级、状态;预期输出=风险用户池、客服/推送排除名单、黑名单同步候选;使用页面=风险中心 / 系统管理 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“身份风险查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:身份名称、身份分组、风险等级、状态。\n5. 点击查询。\n6. 查看结果区是否输出:风险用户池、客服/推送排除名单、黑名单同步候选。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “身份风险查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“风险用户池、客服/推送排除名单、黑名单同步候选”。\n4. 跳转到“风险中心 / 系统管理”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:身份风险查询 | 待执行\nTC-PROTO-0056 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行新建账号并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“新建账号”。 | 动作=新建账号;说明=按部门、角色、站点、数据范围开通账号;预期=账号创建后出现在系统管理列表 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“新建账号”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:按部门、角色、站点、数据范围开通账号。\n6. 提交后进入审计日志,按动作类型筛选“新建账号”。 | 1. 有权限角色可完成“新建账号”。\n2. 执行结果符合“账号创建后出现在系统管理列表”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 新建账号仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:新建账号;权限分配;审计日志 | 待执行\nTC-PROTO-0057 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行离职管理并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“离职管理”。 | 动作=离职管理;说明=停用账号、交接任务、回收敏感权限;预期=离职账号不可登录且任务已交接 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“离职管理”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:停用账号、交接任务、回收敏感权限。\n6. 提交后进入审计日志,按动作类型筛选“离职管理”。 | 1. 有权限角色可完成“离职管理”。\n2. 执行结果符合“离职账号不可登录且任务已交接”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 离职管理仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:离职管理;权限分配;审计日志 | 待执行\nTC-PROTO-0058 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行权限分配并校验权限与审计 | P1 | 用户以系统管理员/负责人身份登录;系统资产模块可访问;系统管理列表包含“权限分配”。 | 动作=权限分配;说明=导出、审批、查看敏感信息、黑名单同步独立授权;预期=权限粒度按按钮和数据范围生效 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“权限分配”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、审批、查看敏感信息、黑名单同步独立授权。\n6. 提交后进入审计日志,按动作类型筛选“权限分配”。 | 1. 有权限角色可完成“权限分配”。\n2. 执行结果符合“权限粒度按按钮和数据范围生效”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 权限分配仅对系统管理员/负责人开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:权限分配;权限分配;审计日志 | 待执行\nTC-PROTO-0059 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行审计日志并校验权限与审计 | P1 | 用户以系统管理员/审计角色身份登录;系统资产模块可访问;系统管理列表包含“审计日志”。 | 动作=审计日志;说明=导出、查看敏感信息、黑名单同步、审批动作;预期=每个敏感动作有日志ID和操作者 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“审计日志”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、查看敏感信息、黑名单同步、审批动作。\n6. 提交后进入审计日志,按动作类型筛选“审计日志”。 | 1. 有权限角色可完成“审计日志”。\n2. 执行结果符合“每个敏感动作有日志ID和操作者”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 审计日志仅对系统管理员/审计角色开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:审计日志;权限分配;审计日志 | 待执行\nTC-PROTO-0179 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段JOYHUB 用户ID按全员可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“JOYHUB 用户ID”存在。 | 字段=JOYHUB 用户ID;可见范围=全员可见;期望=完整ID可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“JOYHUB 用户ID”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“JOYHUB 用户ID”按照“全员可见”控制可见性。\n2. 符合预期:完整ID可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | JOYHUB 用户ID必须按全员可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:JOYHUB 用户ID/全员可见 | 待执行\nTC-PROTO-0180 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段用户名按授权可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“用户名”存在。 | 字段=用户名;可见范围=授权可见;期望=未授权显示脱敏 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“用户名”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“用户名”按照“授权可见”控制可见性。\n2. 符合预期:未授权显示脱敏。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 用户名必须按授权可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:用户名/授权可见 | 待执行\nTC-PROTO-0181 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段邮箱后缀按已脱敏权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“邮箱后缀”存在。 | 字段=邮箱后缀;可见范围=已脱敏;期望=只显示邮箱域名/后缀 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“邮箱后缀”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“邮箱后缀”按照“已脱敏”控制可见性。\n2. 符合预期:只显示邮箱域名/后缀。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 邮箱后缀必须按已脱敏控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:邮箱后缀/已脱敏 | 待执行\nTC-PROTO-0182 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段近7天EDM推送数按推送数据权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“近7天EDM推送数”存在。 | 字段=近7天EDM推送数;可见范围=推送数据;期望=仅推送/管理员可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“近7天EDM推送数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“近7天EDM推送数”按照“推送数据”控制可见性。\n2. 符合预期:仅推送/管理员可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 近7天EDM推送数必须按推送数据控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:近7天EDM推送数/推送数据 | 待执行\nTC-PROTO-0183 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段身份风险等级按系统管理员/风险负责人权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“身份风险等级”存在。 | 字段=身份风险等级;可见范围=系统管理员/风险负责人;期望=普通运营不可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“身份风险等级”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“身份风险等级”按照“系统管理员/风险负责人”控制可见性。\n2. 符合预期:普通运营不可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 身份风险等级必须按系统管理员/风险负责人控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:身份风险等级/系统管理员/风险负责人 | 待执行\nTC-PROTO-0184 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段标签覆盖人数按标签模块权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“标签覆盖人数”存在。 | 字段=标签覆盖人数;可见范围=标签模块;期望=负责人可见汇总,普通客服不可导出 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“标签覆盖人数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“标签覆盖人数”按照“标签模块”控制可见性。\n2. 符合预期:负责人可见汇总,普通客服不可导出。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 标签覆盖人数必须按标签模块控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:标签覆盖人数/标签模块 | 待执行\nTC-PROTO-0242 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:搜索JOYHUB用户ID | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=输入JOYHUB ID并查询;预期=返回用户主档、标签、身份、产品关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:输入JOYHUB ID并查询。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“搜索JOYHUB用户ID”。\n2. 输出结果为:返回用户主档、标签、身份、产品关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的搜索JOYHUB用户ID可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/搜索JOYHUB用户ID | 待执行\nTC-PROTO-0243 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:按国家和近7天EDM次数筛选 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=国家=US;EDM近7天>0;预期=返回可用于触达的人群 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:国家=US;EDM近7天>0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按国家和近7天EDM次数筛选”。\n2. 输出结果为:返回可用于触达的人群。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的按国家和近7天EDM次数筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/按国家和近7天EDM次数筛选 | 待执行\nTC-PROTO-0244 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:打开用户画像详情 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=点击用户行详情;预期=展示注册/活跃时间、标签、产品、活动 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:点击用户行详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“打开用户画像详情”。\n2. 输出结果为:展示注册/活跃时间、标签、产品、活动。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的打开用户画像详情可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/打开用户画像详情 | 待执行\nTC-PROTO-0245 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:新增标签覆盖查询 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=兴趣;打标方式=系统;预期=展示覆盖人数和最新打标时间 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签分类=兴趣;打标方式=系统。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“新增标签覆盖查询”。\n2. 输出结果为:展示覆盖人数和最新打标时间。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的新增标签覆盖查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/新增标签覆盖查询 | 待执行\nTC-PROTO-0246 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:覆盖人数异常提示 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签覆盖人数突增或为0;预期=展示异常覆盖提示 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签覆盖人数突增或为0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“覆盖人数异常提示”。\n2. 输出结果为:展示异常覆盖提示。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的覆盖人数异常提示可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/覆盖人数异常提示 | 待执行\nTC-PROTO-0247 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:产品标签关联Listing | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=选择产品标签并查看关联产品;预期=展示产品-品牌-Listing/ASIN关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:选择产品标签并查看关联产品。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“产品标签关联Listing”。\n2. 输出结果为:展示产品-品牌-Listing/ASIN关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的产品标签关联Listing可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/产品标签关联Listing | 待执行\nTC-PROTO-0248 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:按创建时间筛选标签 | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=开始/截止时间;预期=只返回时间范围内标签 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:开始/截止时间。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按创建时间筛选标签”。\n2. 输出结果为:只返回时间范围内标签。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的按创建时间筛选标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/按创建时间筛选标签 | 待执行\nTC-PROTO-0249 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:客服分组标签查询 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=客服分组;预期=输出私域用户标签与社群/活动关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签分类=客服分组。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“客服分组标签查询”。\n2. 输出结果为:输出私域用户标签与社群/活动关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的客服分组标签查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/客服分组标签查询 | 待执行\nTC-PROTO-0250 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:风险用户隔离标签 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签状态=启用;风险相关;预期=可作为客服/推送排除条件 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签状态=启用;风险相关。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“风险用户隔离标签”。\n2. 输出结果为:可作为客服/推送排除条件。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的风险用户隔离标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/风险用户隔离标签 | 待执行\nTC-PROTO-0251 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:查看多语言图标 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=打开身份详情;预期=展示英/德/日图标PNG | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:打开身份详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“查看多语言图标”。\n2. 输出结果为:展示英/德/日图标PNG。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的查看多语言图标可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/查看多语言图标 | 待执行\nTC-PROTO-0252 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份风险等级筛选 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=身份风险等级=高;预期=进入风险用户池候选 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:身份风险等级=高。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份风险等级筛选”。\n2. 输出结果为:进入风险用户池候选。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份风险等级筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份风险等级筛选 | 待执行\nTC-PROTO-0253 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份状态停用 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=将风险身份置为停用;预期=后续筛选不再作为可选身份 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:将风险身份置为停用。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份状态停用”。\n2. 输出结果为:后续筛选不再作为可选身份。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份状态停用可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份状态停用 | 待执行\nTC-PROTO-0289 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:生成字段表重复点击 | P2 | 已进入“现有ERP”;准备可执行场景:生成字段表重复点击。 | 动作=连续点击生成字段表;预期=字段表只生成一份或版本号递增可追踪 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击生成字段表。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:字段表只生成一份或版本号递增可追踪。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:生成字段表重复点击 | 待执行\nTC-PROTO-0290 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:导出现有关系无数据 | P2 | 已进入“现有ERP”;准备可执行场景:导出现有关系无数据。 | 动作=查询结果为空后导出;预期=提示暂无可导出数据 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:查询结果为空后导出。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示暂无可导出数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:导出现有关系无数据 | 待执行\nTC-PROTO-0291 | user_erp_mvp_admin_prototype_v10(1).html | 身份管理 | 系统稳定性与幂等 | 异常场景 | 身份管理稳定性校验:多语言图标缺失 | P2 | 已进入“身份管理”;准备可执行场景:多语言图标缺失。 | 动作=德语图标PNG为空;预期=页面显示占位并提示需补充 | 1. 打开原型页面“身份管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:德语图标PNG为空。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:页面显示占位并提示需补充。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多语言图标缺失 | 待执行\nTC-PROTO-0292 | user_erp_mvp_admin_prototype_v10(1).html | 用户管理 | 系统稳定性与幂等 | 异常场景 | 用户管理稳定性校验:分页切换保持筛选 | P2 | 已进入“用户管理”;准备可执行场景:分页切换保持筛选。 | 动作=筛选国家US后切换下一页;预期=筛选条件不丢失 | 1. 打开原型页面“用户管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选国家US后切换下一页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:筛选条件不丢失。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:分页切换保持筛选 | 待执行\n# Sheet: HTML3-客服执行\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0060 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看在线客服数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=在线客服数;原型变量=onlineAgents 人;动作=查看当前可用客服池 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“在线客服数”。\n3. 核对指标值单位是否符合“onlineAgents 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“在线客服数”显示在客服执行看板。\n2. 点击后进入与“查看当前可用客服池”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:onlineAgents 人;标题:客服执行看板 | 待执行\nTC-PROTO-0061 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看今日工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日工单;原型变量=todayTickets 单;动作=查看今日进入客服中心的工单总量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日工单”。\n3. 核对指标值单位是否符合“todayTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日工单”显示在客服执行看板。\n2. 点击后进入与“查看今日进入客服中心的工单总量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0062 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看待处理工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=待处理工单;原型变量=pendingTickets 单;动作=查看未关闭/待处理队列 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“待处理工单”。\n3. 核对指标值单位是否符合“pendingTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“待处理工单”显示在客服执行看板。\n2. 点击后进入与“查看未关闭/待处理队列”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:pendingTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0063 | 客服执行.html | 客服执行看板 | 多渠道触达引擎 | 功能测试 | 客服执行看板查看今日消息指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日消息;原型变量=todayMessages 条;动作=查看客服发送和接收消息量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日消息”。\n3. 核对指标值单位是否符合“todayMessages 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日消息”显示在客服执行看板。\n2. 点击后进入与“查看客服发送和接收消息量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayMessages 条;标题:客服执行看板 | 待执行\nTC-PROTO-0064 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看今日获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日获取评价;原型变量=todayReviews 条;动作=查看今日客服转化评价数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日获取评价”。\n3. 核对指标值单位是否符合“todayReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日获取评价”显示在客服执行看板。\n2. 点击后进入与“查看今日客服转化评价数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0065 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看本月获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=本月获取评价;原型变量=monthReviews 条;动作=查看月度评价产出 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“本月获取评价”。\n3. 核对指标值单位是否符合“monthReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“本月获取评价”显示在客服执行看板。\n2. 点击后进入与“查看月度评价产出”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:monthReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0066 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看排班到岗指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=排班到岗;原型变量=实际/应到 人;动作=查看排班、在线状态和出勤异常 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“排班到岗”。\n3. 核对指标值单位是否符合“实际/应到 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“排班到岗”显示在客服执行看板。\n2. 点击后进入与“查看排班、在线状态和出勤异常”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:实际/应到 人;标题:客服执行看板 | 待执行\nTC-PROTO-0067 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看迟到次数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=迟到次数;原型变量=lateDays 次;动作=查看迟到/早退/请假/缺勤 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“迟到次数”。\n3. 核对指标值单位是否符合“lateDays 次”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“迟到次数”显示在客服执行看板。\n2. 点击后进入与“查看迟到/早退/请假/缺勤”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:lateDays 次;标题:客服执行看板 | 待执行\nTC-PROTO-0068 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看回复用户数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=回复用户数;原型变量=repliedUsers 人;动作=查看回复覆盖用户数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“回复用户数”。\n3. 核对指标值单位是否符合“repliedUsers 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“回复用户数”显示在客服执行看板。\n2. 点击后进入与“查看回复覆盖用户数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:repliedUsers 人;标题:客服执行看板 | 待执行\nTC-PROTO-0069 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看处理工单数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=处理工单数;原型变量=ticketCount 单;动作=查看处理工单量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“处理工单数”。\n3. 核对指标值单位是否符合“ticketCount 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“处理工单数”显示在客服执行看板。\n2. 点击后进入与“查看处理工单量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:ticketCount 单;标题:客服执行看板 | 待执行\nTC-PROTO-0070 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看发送消息数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=发送消息数;原型变量=messageCount 条;动作=查看消息发送量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“发送消息数”。\n3. 核对指标值单位是否符合“messageCount 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“发送消息数”显示在客服执行看板。\n2. 点击后进入与“查看消息发送量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:messageCount 条;标题:客服执行看板 | 待执行\nTC-PROTO-0071 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看平均首次回复指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=平均首次回复;原型变量=averageFirstReply 分钟;动作=查看平均首次回复时长 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“平均首次回复”。\n3. 核对指标值单位是否符合“averageFirstReply 分钟”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“平均首次回复”显示在客服执行看板。\n2. 点击后进入与“查看平均首次回复时长”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:averageFirstReply 分钟;标题:客服执行看板 | 待执行\nTC-PROTO-0072 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看RSO/RDO评价数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=RSO/RDO评价数;原型变量=rsoReviews+rdoReviews 条;动作=查看回评/测评转化 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“RSO/RDO评价数”。\n3. 核对指标值单位是否符合“rsoReviews+rdoReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“RSO/RDO评价数”显示在客服执行看板。\n2. 点击后进入与“查看回评/测评转化”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:rsoReviews+rdoReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0073 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看目标完成率指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=目标完成率;原型变量=totalRate;动作=查看目标完成进度 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“目标完成率”。\n3. 核对指标值单位是否符合“totalRate”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“目标完成率”显示在客服执行看板。\n2. 点击后进入与“查看目标完成进度”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:totalRate;标题:客服执行看板 | 待执行\nTC-PROTO-0074 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户消息进入工单从待分配执行自动分配流转到已分配 | P1 | 客服系统存在来源为“用户消息进入”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=用户消息进入;起始状态=待分配;操作=自动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户消息进入”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“自动分配”:在线客服池按班次+在线状态+当前负载+最大工单数分配。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户消息进入”和当前状态“待分配”。\n2. 执行“自动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0075 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 推送转人工工单从待分配执行手动分配流转到已分配 | P1 | 客服系统存在来源为“推送转人工”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=推送转人工;起始状态=待分配;操作=手动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“推送转人工”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“手动分配”:组长选择在线且未满载客服。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“推送转人工”和当前状态“待分配”。\n2. 执行“手动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0076 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 售后触发工单从已分配执行首次回复流转到处理中 | P1 | 客服系统存在来源为“售后触发”的工单;当前状态为“已分配”;用户上下文卡可查询或降级展示。 | 来源=售后触发;起始状态=已分配;操作=首次回复;目标状态=处理中 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“售后触发”。\n3. 打开状态为“已分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“首次回复”:客服查看用户上下文后发送第一条回复。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“售后触发”和当前状态“已分配”。\n2. 执行“首次回复”后状态变为“处理中”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0077 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 风险触发工单从处理中执行标记疑似诈骗流转到疑似诈骗 | P1 | 客服系统存在来源为“风险触发”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=风险触发;起始状态=处理中;操作=标记疑似诈骗;目标状态=疑似诈骗 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“风险触发”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“标记疑似诈骗”:客服在工单处理结果中选择疑似诈骗。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“风险触发”和当前状态“处理中”。\n2. 执行“标记疑似诈骗”后状态变为“疑似诈骗”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0078 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 电话后续工单从处理中执行等待用户回复流转到等待用户 | P1 | 客服系统存在来源为“电话后续”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=电话后续;起始状态=处理中;操作=等待用户回复;目标状态=等待用户 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“电话后续”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“等待用户回复”:客服记录通话后等待用户补充订单号。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“电话后续”和当前状态“处理中”。\n2. 执行“等待用户回复”后状态变为“等待用户”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0079 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户答应配合工单从处理中执行创建跟进任务流转到待提醒 | P1 | 客服系统存在来源为“用户答应配合”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=用户答应配合;起始状态=处理中;操作=创建跟进任务;目标状态=待提醒 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户答应配合”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“创建跟进任务”:客服将答应配合状态置为已答应并设置负责人。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户答应配合”和当前状态“处理中”。\n2. 执行“创建跟进任务”后状态变为“待提醒”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0080 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 等待提交工单从待提醒执行提醒用户流转到等待提交 | P1 | 客服系统存在来源为“等待提交”的工单;当前状态为“待提醒”;用户上下文卡可查询或降级展示。 | 来源=等待提交;起始状态=待提醒;操作=提醒用户;目标状态=等待提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“等待提交”。\n3. 打开状态为“待提醒”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“提醒用户”:到期前客服发送提醒消息。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“等待提交”和当前状态“待提醒”。\n2. 执行“提醒用户”后状态变为“等待提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0081 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户提交评价工单从等待提交执行登记提交事实流转到已提交 | P1 | 客服系统存在来源为“用户提交评价”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=用户提交评价;起始状态=等待提交;操作=登记提交事实;目标状态=已提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户提交评价”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“登记提交事实”:客服上传截图/链接并关联计划和ASIN。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户提交评价”和当前状态“等待提交”。\n2. 执行“登记提交事实”后状态变为“已提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0082 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 评价核验完成工单从已提交执行关闭工单流转到已关闭 | P1 | 客服系统存在来源为“评价核验完成”的工单;当前状态为“已提交”;用户上下文卡可查询或降级展示。 | 来源=评价核验完成;起始状态=已提交;操作=关闭工单;目标状态=已关闭 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“评价核验完成”。\n3. 打开状态为“已提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“关闭工单”:评价展示确认后客服将工单置为已解决并关闭。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“评价核验完成”和当前状态“已提交”。\n2. 执行“关闭工单”后状态变为“已关闭”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0083 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 超时未提交工单从等待提交执行需再次联系流转到需再次联系 | P1 | 客服系统存在来源为“超时未提交”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=超时未提交;起始状态=等待提交;操作=需再次联系;目标状态=需再次联系 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“超时未提交”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“需再次联系”:超过答应配合期限后生成再次联系任务。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“超时未提交”和当前状态“等待提交”。\n2. 执行“需再次联系”后状态变为“需再次联系”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0084 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:自动分配无在线客服 | P2 | 客服执行看板可用;准备异常条件:所有排班客服均离线或满载。 | 异常=自动分配无在线客服;条件=所有排班客服均离线或满载 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:所有排班客服均离线或满载。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“自动分配无在线客服”。\n2. 处理结果为:工单留在公共池并提醒组长。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:自动分配无在线客服 | 待执行\nTC-PROTO-0085 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:分配给离线客服 | P2 | 客服执行看板可用;准备异常条件:客服在线状态在分配前变为离线。 | 异常=分配给离线客服;条件=客服在线状态在分配前变为离线 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服在线状态在分配前变为离线。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“分配给离线客服”。\n2. 处理结果为:阻止分配并要求重新选择。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:分配给离线客服 | 待执行\nTC-PROTO-0086 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:当前负载超过最大工单数 | P2 | 客服执行看板可用;准备异常条件:客服未关闭工单数达到上限。 | 异常=当前负载超过最大工单数;条件=客服未关闭工单数达到上限 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服未关闭工单数达到上限。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“当前负载超过最大工单数”。\n2. 处理结果为:自动跳过该客服。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:当前负载超过最大工单数 | 待执行\nTC-PROTO-0087 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:用户上下文卡查询失败 | P2 | 客服执行看板可用;准备异常条件:identity 服务超时。 | 异常=用户上下文卡查询失败;条件=identity 服务超时 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:identity 服务超时。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“用户上下文卡查询失败”。\n2. 处理结果为:工单可继续处理但显示上下文数据可能过期。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:用户上下文卡查询失败 | 待执行\nTC-PROTO-0088 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:首次回复为空 | P2 | 客服执行看板可用;准备异常条件:客服点击发送但消息内容为空。 | 异常=首次回复为空;条件=客服点击发送但消息内容为空 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服点击发送但消息内容为空。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“首次回复为空”。\n2. 处理结果为:阻止发送并提示请输入回复内容。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:首次回复为空 | 待执行\nTC-PROTO-0089 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:关闭工单未选择处理结果 | P2 | 客服执行看板可用;准备异常条件:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。 | 异常=关闭工单未选择处理结果;条件=点击关闭但未选择已解决/拒绝/疑似诈骗等结果 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“关闭工单未选择处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:关闭工单未选择处理结果 | 待执行\nTC-PROTO-0090 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:登记评价缺少证据 | P2 | 客服执行看板可用;准备异常条件:用户声称已评价但未上传截图或链接。 | 异常=登记评价缺少证据;条件=用户声称已评价但未上传截图或链接 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:用户声称已评价但未上传截图或链接。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“登记评价缺少证据”。\n2. 处理结果为:不允许进入已提交状态。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:登记评价缺少证据 | 待执行\nTC-PROTO-0091 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:答应配合任务超期 | P2 | 客服执行看板可用;准备异常条件:deadline_at 已过且无提交记录。 | 异常=答应配合任务超期;条件=deadline_at 已过且无提交记录 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:deadline_at 已过且无提交记录。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“答应配合任务超期”。\n2. 处理结果为:状态变为超时并生成需再次联系。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:答应配合任务超期 | 待执行\nTC-PROTO-0092 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:风险状态确认诈骗 | P2 | 客服执行看板可用;准备异常条件:risk 返回确认诈骗。 | 异常=风险状态确认诈骗;条件=risk 返回确认诈骗 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:risk 返回确认诈骗。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“风险状态确认诈骗”。\n2. 处理结果为:工单自动或人工确认后关闭并同步黑名单候选。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:风险状态确认诈骗 | 待执行\nTC-PROTO-0093 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:重复创建同用户打开工单 | P2 | 客服执行看板可用;准备异常条件:同 person_id 已存在 open 工单。 | 异常=重复创建同用户打开工单;条件=同 person_id 已存在 open 工单 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:同 person_id 已存在 open 工单。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“重复创建同用户打开工单”。\n2. 处理结果为:新工单关联已有工单或提示合并。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:重复创建同用户打开工单 | 待执行\nTC-PROTO-0185 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服本人在客服执行看板的可操作范围校验 | P1 | 准备客服本人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服本人;数据范围=我的工单;允许=回复用户、登记提交事实;限制=不能改派他人工单或查看团队绩效 | 1. 使用“客服本人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:回复用户、登记提交事实。\n4. 尝试执行限制动作:不能改派他人工单或查看团队绩效。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服本人只能看到“我的工单”。\n2. 允许动作“回复用户、登记提交事实”可正常提交。\n3. 限制动作“不能改派他人工单或查看团队绩效”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服本人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服本人 | 待执行\nTC-PROTO-0186 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服组长在客服执行看板的可操作范围校验 | P1 | 准备客服组长账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服组长;数据范围=组内工单池;允许=手动分配、转移、查看组员负载;限制=不能查看跨团队敏感字段 | 1. 使用“客服组长”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:手动分配、转移、查看组员负载。\n4. 尝试执行限制动作:不能查看跨团队敏感字段。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服组长只能看到“组内工单池”。\n2. 允许动作“手动分配、转移、查看组员负载”可正常提交。\n3. 限制动作“不能查看跨团队敏感字段”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服组长权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服组长 | 待执行\nTC-PROTO-0187 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服主管在客服执行看板的可操作范围校验 | P1 | 准备客服主管账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服主管;数据范围=团队看板;允许=查看排班、绩效、目标完成率;限制=不能同步黑名单除非额外授权 | 1. 使用“客服主管”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看排班、绩效、目标完成率。\n4. 尝试执行限制动作:不能同步黑名单除非额外授权。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服主管只能看到“团队看板”。\n2. 允许动作“查看排班、绩效、目标完成率”可正常提交。\n3. 限制动作“不能同步黑名单除非额外授权”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服主管权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服主管 | 待执行\nTC-PROTO-0188 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 风险负责人在客服执行看板的可操作范围校验 | P1 | 准备风险负责人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=风险负责人;数据范围=疑似诈骗工单;允许=确认诈骗、标记误报、同步黑名单候选;限制=不能修改客服排班 | 1. 使用“风险负责人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:确认诈骗、标记误报、同步黑名单候选。\n4. 尝试执行限制动作:不能修改客服排班。\n5. 打开审计日志查看敏感操作记录。 | 1. 风险负责人只能看到“疑似诈骗工单”。\n2. 允许动作“确认诈骗、标记误报、同步黑名单候选”可正常提交。\n3. 限制动作“不能修改客服排班”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 风险负责人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:风险负责人 | 待执行\nTC-PROTO-0189 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 系统管理员在客服执行看板的可操作范围校验 | P1 | 准备系统管理员账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=系统管理员;数据范围=全部客服数据;允许=查看审计、配置权限、导出绩效;限制=敏感查看仍需记录审计 | 1. 使用“系统管理员”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看审计、配置权限、导出绩效。\n4. 尝试执行限制动作:敏感查看仍需记录审计。\n5. 打开审计日志查看敏感操作记录。 | 1. 系统管理员只能看到“全部客服数据”。\n2. 允许动作“查看审计、配置权限、导出绩效”可正常提交。\n3. 限制动作“敏感查看仍需记录审计”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 系统管理员权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:系统管理员 | 待执行\nTC-PROTO-0254 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态待分配执行分配给客服A | P2 | 客服执行看板存在状态为“待分配”的工单;当前用户对该工单有处理权限。 | 当前状态=待分配;动作=分配给客服A | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“待分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“分配给客服A”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“待分配”的工单可执行“分配给客服A”。\n2. 执行结果:assigned_agent=客服A;写入assignment_logs。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=待分配;动作=分配给客服A | 待执行\nTC-PROTO-0255 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已分配执行客服首次回复 | P2 | 客服执行看板存在状态为“已分配”的工单;当前用户对该工单有处理权限。 | 当前状态=已分配;动作=客服首次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“客服首次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已分配”的工单可执行“客服首次回复”。\n2. 执行结果:记录首次回复时长并进入处理中。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已分配;动作=客服首次回复 | 待执行\nTC-PROTO-0256 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待用户回复 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待用户回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待用户回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待用户回复”。\n2. 执行结果:状态变等待用户,设置提醒时间。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待用户回复 | 待执行\nTC-PROTO-0257 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待内部协同 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待内部协同 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待内部协同”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待内部协同”。\n2. 执行结果:状态变等待内部,通知内部负责人。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待内部协同 | 待执行\nTC-PROTO-0258 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择答应配合 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择答应配合 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择答应配合”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择答应配合”。\n2. 执行结果:创建support_followups,状态PROMISED。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择答应配合 | 待执行\nTC-PROTO-0259 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择疑似诈骗 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择疑似诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择疑似诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择疑似诈骗”。\n2. 执行结果:生成风险案件并标记工单疑似诈骗。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择疑似诈骗 | 待执行\nTC-PROTO-0260 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待用户执行用户回复后继续处理 | P2 | 客服执行看板存在状态为“等待用户”的工单;当前用户对该工单有处理权限。 | 当前状态=等待用户;动作=用户回复后继续处理 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待用户”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“用户回复后继续处理”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待用户”的工单可执行“用户回复后继续处理”。\n2. 执行结果:状态回到处理中并记录用户消息。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待用户;动作=用户回复后继续处理 | 待执行\nTC-PROTO-0261 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待内部执行内部反馈完成 | P2 | 客服执行看板存在状态为“等待内部”的工单;当前用户对该工单有处理权限。 | 当前状态=等待内部;动作=内部反馈完成 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待内部”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“内部反馈完成”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待内部”的工单可执行“内部反馈完成”。\n2. 执行结果:状态回到处理中并追加内部协同记录。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待内部;动作=内部反馈完成 | 待执行\nTC-PROTO-0262 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已解决执行关闭工单 | P2 | 客服执行看板存在状态为“已解决”的工单;当前用户对该工单有处理权限。 | 当前状态=已解决;动作=关闭工单 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已解决”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“关闭工单”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已解决”的工单可执行“关闭工单”。\n2. 执行结果:resolved_at和closed状态写入。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已解决;动作=关闭工单 | 待执行\nTC-PROTO-0263 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认误报 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认误报 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认误报”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认误报”。\n2. 执行结果:工单可回到处理中或已解决。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认误报 | 待执行\nTC-PROTO-0264 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认诈骗 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认诈骗”。\n2. 执行结果:工单关闭并进入黑名单同步候选。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认诈骗 | 待执行\nTC-PROTO-0265 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已关闭执行尝试再次回复 | P2 | 客服执行看板存在状态为“已关闭”的工单;当前用户对该工单有处理权限。 | 当前状态=已关闭;动作=尝试再次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已关闭”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“尝试再次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已关闭”的工单可执行“尝试再次回复”。\n2. 执行结果:禁止直接回复,需重新打开或新建工单。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已关闭;动作=尝试再次回复 | 待执行\nTC-PROTO-0293 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:新工单到达实时刷新 | P2 | 已进入“客服执行看板”;准备可执行场景:新工单到达实时刷新。 | 动作=后台新增待分配工单;预期=看板待处理数增加并出现新工单 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:后台新增待分配工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:看板待处理数增加并出现新工单。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:新工单到达实时刷新 | 待执行\nTC-PROTO-0294 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:多人同时抢单 | P2 | 已进入“客服执行看板”;准备可执行场景:多人同时抢单。 | 动作=两个客服同时领取同一工单;预期=只有一个领取成功,另一个提示已被分配 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个客服同时领取同一工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只有一个领取成功,另一个提示已被分配。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多人同时抢单 | 待执行\nTC-PROTO-0295 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:首次回复重复发送 | P2 | 已进入“客服工单”;准备可执行场景:首次回复重复发送。 | 动作=客服双击发送回复;预期=只发送一条消息并记录一次首次回复时长 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:客服双击发送回复。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只发送一条消息并记录一次首次回复时长。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:首次回复重复发送 | 待执行\nTC-PROTO-0296 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:关闭工单后刷新 | P2 | 已进入“客服工单”;准备可执行场景:关闭工单后刷新。 | 动作=关闭工单后刷新详情页;预期=状态仍为已关闭且不可继续处理 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:关闭工单后刷新详情页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:状态仍为已关闭且不可继续处理。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:关闭工单后刷新 | 待执行\nTC-PROTO-0297 | 客服执行.html | 客服绩效 | 系统稳定性与幂等 | 异常场景 | 客服绩效稳定性校验:绩效周期切换 | P2 | 已进入“客服绩效”;准备可执行场景:绩效周期切换。 | 动作=日/周/月连续切换;预期=指标随周期变化且无串数据 | 1. 打开原型页面“客服绩效”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:日/周/月连续切换。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:指标随周期变化且无串数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:绩效周期切换 | 待执行\n# Sheet: HTML4-单文件系统\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0094 | 用户运营系统-单文件.html | 工作台 | 系统总览 | UI/交互测试 | 单文件系统路由#/dashboard进入Dashboard页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有Dashboard访问权限。 | 路由=#/dashboard;页面=Dashboard;用途=查看经营指标、待办、风险、评价进度 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/dashboard”或从侧边菜单点击“Dashboard”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“Dashboard”页面。 | 1. 页面成功进入“Dashboard”。\n2. 当前菜单高亮,页面内容与“查看经营指标、待办、风险、评价进度”一致。\n3. 刷新后 hash 路由不丢失,仍展示“Dashboard”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备Dashboard访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/dashboard | 待执行\nTC-PROTO-0095 | 用户运营系统-单文件.html | 需求管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/demand进入需求中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有需求中心访问权限。 | 路由=#/demand;页面=需求中心;用途=创建/评估/驳回/转计划 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/demand”或从侧边菜单点击“需求中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“需求中心”页面。 | 1. 页面成功进入“需求中心”。\n2. 当前菜单高亮,页面内容与“创建/评估/驳回/转计划”一致。\n3. 刷新后 hash 路由不丢失,仍展示“需求中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权 | 只有具备需求中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/demand | 待执行\nTC-PROTO-0096 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan/review进入计划审核页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划审核访问权限。 | 路由=#/plan/review;页面=计划审核;用途=提交审批、通过、驳回、待补充 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan/review”或从侧边菜单点击“计划审核”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划审核”页面。 | 1. 页面成功进入“计划审核”。\n2. 当前菜单高亮,页面内容与“提交审批、通过、驳回、待补充”一致。\n3. 刷新后 hash 路由不丢失,仍展示“计划审核”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划审核访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan/review | 待执行\nTC-PROTO-0097 | 用户运营系统-单文件.html | 计划管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan进入计划中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划中心访问权限。 | 路由=#/plan;页面=计划中心;用途=生成计划、拆分计划项、执行中/暂停/终止 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan”或从侧边菜单点击“计划中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划中心”页面。 | 1. 页面成功进入“计划中心”。\n2. 当前菜单高亮,页面内容 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan | 待执行\nTC-PROTO-0098 | 用户运营系统-单文件.html | Listing健康 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/asin进入ASIN/Listing页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有ASIN/Listing访问权限。 | 路由=#/asin;页面=ASIN/Listing;用途=查看评分、评价数、健康状态、紧急策略 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/asin”或从侧边菜单点击“ASIN/Listing”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“ASIN/Listing”页面。 | 1. 页面成功进入“ASIN/Listing”。\n2. 当前菜单高亮,页面内容与“查看评分、评价数、健康状态、紧急策略”一致。\n3. 刷新后 hash 路由不丢失,仍展示“ASIN/Listing”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备ASIN/Listing访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/asin | 待执行\nTC-PROTO-0099 | 用户运营系统-单文件.html | 用户上下文 | 用户身份与上下文 | UI/交互测试 | 单文件系统路由#/user进入用户中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有用户中心访问权限。 | 路由=#/user;页面=用户中心;用途=用户主档、标签、身份、产品、活动、触达历史 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/user”或从侧边菜单点击“用户中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“用户中心”页面。 | 1. 页面成功进入“用户中心”。\n2. 当前菜单高亮,页面内容与“用户主档、标签、身份、产品、活动、触达历史”一致。\n3. 刷新后 hash 路由不丢失,仍展示“用户中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备用户中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/user | 待执行\nTC-PROTO-0100 | 用户运营系统-单文件.html | 额度管理 | 额度与频控 | UI/交互测试 | 单文件系统路由#/quota进入额度频控页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有额度频控访问权限。 | 路由=#/quota;页面=额度频控;用途=额度查询、预占、确认、释放、终校 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/quota”或从侧边 | 1. 页面成功进入“额度频控”。\n2. 当前菜单高亮,页面内容与“额度查询、预占、确认、释放、终校”一致。\n3. 刷新后 hash 路由不丢失,仍展示“额度频控”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备额度频控访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/quota | 待执行\nTC-PROTO-0101 | 用户运营系统-单文件.html | 多渠道触达 | 多渠道触达引擎 | UI/交互测试 | 单文件系统路由#/outreach进入推送/触达页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有推送/触达访问权限。 | 路由=#/outreach;页面=推送/触达;用途=IM/EDM/APP/TEL 路由、去重、发送、追踪 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/outreach”或从侧边菜单点击“推送 | 1. 页面成功进入“推送/触达”。\n2. 当前菜单高亮,页面内容与“IM/EDM/APP/TEL 路由、去重、发送、追踪”一致。\n3. 刷新后 hash 路由不丢失,仍展示“推送/触达”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备推送/触达访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/outreach | 待执行\nTC-PROTO-0102 | 用户运营系统-单文件.html | 工单管理 | 客服工单与管理 | UI/交互测试 | 单文件系统路由#/support进入客服中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有客服中心访问权限。 | 路由=#/support;页面=客服中心;用途=工单创建、分配、处理、跟进 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/support”或从侧边菜单点击“客服中心”。\n3. 等待页面渲染完成。\n4. 检查 | 1. 页面成功进入“客服中心”。\n2. 当前菜单高亮,页面内容与“工单创建、分配、处理、跟进”一致。\n3. 刷新后 hash 路由不丢失,仍展示“客服中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备客服中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/support | 待执行\nTC-PROTO-0103 | 用户运营系统-单文件.html | 风险反欺诈 | 风险与反欺诈 | UI/交互测试 | 单文件系统路由#/risk进入风险中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有风险中心访问权限。 | 路由=#/risk;页面=风险中心;用途=风险信号、强弱关联、黑名单、复核 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/risk”或从侧边菜单点击“风险中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“风险中心”页面。 | 1. 页面成功进入“风险中心”。\n2. 当前菜单高亮,页面内容与“风险信号、强弱关联、黑名单、复核”一致。\n3. 刷新后 hash 路由不丢失,仍展示“风险中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备风险中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/risk | 待执行\nTC-PROTO-0104 | 用户运营系统-单文件.html | 评价结果 | 评价结果追踪 | UI/交互测试 | 单文件系统路由#/review进入评价追踪页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有评价追踪访问权限。 | 路由=#/review;页面=评价追踪;用途=提交记录、Amazon展示核验、异常观察 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/review”或从侧边菜单点击“评价追踪”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“评价追踪”页面。 | 1. 页面成功进入“评价追踪”。\n2. 当前菜单高亮,页面内容与“提交记录、Amazon展示核验、异常观察”一致。\n3. 刷新后 hash 路由不丢失,仍展示“评价追踪”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备评价追踪访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/review | 待执行\nTC-PROTO-0105 | 用户运营系统-单文件.html | 达人协作 | KOC/KOL协作 | UI/交互测试 | 单文件系统路由#/creator进入KOC/KOL页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有KOC/KOL访问权限。 | 路由=#/creator;页面=KOC/KOL;用途=免评计划、内容、CODE、JOYCOLLAB同步 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/creator”或从侧边菜单点击“KOC/KOL”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“KOC/KOL”页面。 | 1. 页面成功进入“KOC/KOL”。\n2. 当前菜单高亮,页面内容与“免评计划、内容、CODE、JOYCOLLAB同步”一致。\n3. 刷新后 hash 路由不丢失,仍展示“KOC/KOL”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备KOC/KOL访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/creator | 待执行\nTC-PROTO-0106 | 用户运营系统-单文件.html | 审计与通知 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/audit进入审计通知页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有审计通知访问权限。 | 路由=#/audit;页面=审计通知;用途=状态变更、敏感访问、通知告警 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/audit”或从侧边菜单点击“审计通知”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“审计通知”页面。 | 1. 页面成功进入“审计通知”。\n2. 当前菜单高亮,页面内容与“状态变更、敏感访问、通知告警”一致。\n3. 刷新后 hash 路由不丢失,仍展示“审计通知”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备审计通知访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/audit | 待执行\nTC-PROTO-0107 | 用户运营系统-单文件.html | 权限配置 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/system进入系统管理页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有系统管理访问权限。 | 路由=#/system;页面=系统管理;用途=用户角色、权限、数据范围、导出授权 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/system”或从侧边菜单点击“系统管理”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“系统管理”页面。 | 1. 页面成功进入“系统管理”。\n2. 当前菜单高亮,页面内容与“用户角色、权限、数据范围、导出授权”一致。\n3. 刷新后 hash 路由不丢失,仍展示“系统管理”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备系统管理访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/system | 待执行\nTC-PROTO-0108 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行创建测评需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“创建测评需求”的权限;相关基础数据已准备。 | ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“创建测评需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建测评需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“PENDING/EVALUATING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=PENDING/EVALUATING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建测评需求”;跨站点/跨部门数据需按权限范围过滤。 | 创建测评需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:创建测评需求 | 待执行\nTC-PROTO-0109 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行评估需求为待补充并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“评估需求为待补充”的权限;相关基础数据已准备。 | 需求ID=DEM-001;原因=ASIN目标数量缺少依据 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“评估需求为待补充”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-001;原因=ASIN目标数量缺少依据。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “评估需求为待补充”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“WAITING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=WAITING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“评估需求为待补充”;跨站点/跨部门数据需按权限范围过滤。 | 评估需求为待补充完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:评估需求为待补充 | 待执行\nTC-PROTO-0110 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行驳回不成立需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“驳回不成立需求”的权限;相关基础数据已准备。 | 需求ID=DEM-002;原因=ASIN评分已达标无需计划 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“驳回不成立需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-002;原因=ASIN评分已达标无需计划。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “驳回不成立需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“驳回不成立需求”;跨站点/跨部门数据需按权限范围过滤。 | 驳回不成立需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:驳回不成立需求 | 待执行\nTC-PROTO-0111 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行提交测评计划审批并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“提交测评计划审批”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“提交测评计划审批”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “提交测评计划审批”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REVIEW”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records;状态值=REVIEW;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“提交测评计划审批”;跨站点/跨部门数据需按权限范围过滤。 | 提交测评计划审批完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:提交测评计划审批 | 待执行\nTC-PROTO-0112 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批通过计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批通过计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;意见=同意执行;目标评价数=20 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批通过计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;意见=同意执行;目标评价数=20。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批通过计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“APPROVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=APPROVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批通过计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批通过计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批通过计划 | 待执行\nTC-PROTO-0113 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批驳回计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批驳回计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-002;意见=预算和风险说明不足 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批驳回计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-002;意见=预算和风险说明不足。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批驳回计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“DRAFT/REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=DRAFT/REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批驳回计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批驳回计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批驳回计划 | 待执行\nTC-PROTO-0114 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行生成候选用户池并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“生成候选用户池”的权限;相关基础数据已准备。 | 计划ID=PLAN-003;ASIN=B0TEST003;目标=50人 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“生成候选用户池”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-003;ASIN=B0TEST003;目标=50人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “生成候选用户池”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待触达”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plan_items/quota_reservations;状态值=待触达;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“生成候选用户池”;跨站点/跨部门数据需按权限范围过滤。 | 生成候选用户池完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:生成候选用户池 | 待执行\nTC-PROTO-0115 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行暂停执行中计划并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“暂停执行中计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-004;暂停原因=库存异常 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“暂停执行中计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-004;暂停原因=库存异常。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “暂停执行中计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已暂停”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plans;状态值=已暂停;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“暂停执行中计划”;跨站点/跨部门数据需按权限范围过滤。 | 暂停执行中计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:暂停执行中计划 | 待执行\nTC-PROTO-0116 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行批量预占额度并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“批量预占额度”的权限;相关基础数据已准备。 | person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“批量预占额度”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “批量预占额度”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RESERVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RESERVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“批量预占额度”;跨站点/跨部门数据需按权限范围过滤。 | 批量预占额度完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:批量预占额度 | 待执行\nTC-PROTO-0117 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行释放触达失败预占并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“释放触达失败预占”的权限;相关基础数据已准备。 | reservation_id=QR-001;释放原因=IM不可达 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“释放触达失败预占”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:reservation_id=QR-001;释放原因=IM不可达。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “释放触达失败预占”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RELEASED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RELEASED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“释放触达失败预占”;跨站点/跨部门数据需按权限范围过滤。 | 释放触达失败预占完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:释放触达失败预占 | 待执行\nTC-PROTO-0118 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行IM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行IM触达”的权限;相关基础数据已准备。 | plan_id=PLAN-006;channel=IM;content=回评卡片 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行IM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:plan_id=PLAN-006;channel=IM;content=回评卡片。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行IM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OUTBOUND/SENT”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:im_interaction_records;状态值=OUTBOUND/SENT;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行IM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行IM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行IM触达 | 待执行\nTC-PROTO-0119 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行EDM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行EDM触达”的权限;相关基础数据已准备。 | email=user@example.com;模板=回评邮件V1 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行EDM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:email=user@example.com;模板=回评邮件V1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行EDM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“SENT/DELIVERED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events;状态值=SENT/DELIVERED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行EDM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行EDM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行EDM触达 | 待执行\nTC-PROTO-0120 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行用户退订后停止EDM并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“用户退订后停止EDM”的权限;相关基础数据已准备。 | person_id=P100;event=UNSUBSCRIBED | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“用户退订后停止EDM”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P100;event=UNSUBSCRIBED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “用户退订后停止EDM”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“BLOCKED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events/channel_dedup_records;状态值=BLOCKED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“用户退订后停止EDM”;跨站点/跨部门数据需按权限范围过滤。 | 用户退订后停止EDM完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:用户退订后停止EDM | 待执行\nTC-PROTO-0121 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行创建客服工单并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“创建客服工单”的权限;相关基础数据已准备。 | person_id=P200;source=IM转人工;type=催评 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“创建客服工单”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P200;source=IM转人工;type=催评。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建客服工单”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_tickets;状态值=待分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建客服工单”;跨站点/跨部门数据需按权限范围过滤。 | 创建客服工单完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:创建客服工单 | 待执行\nTC-PROTO-0122 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行改派工单给组员并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“改派工单给组员”的权限;相关基础数据已准备。 | ticket_id=T001;from=组长;to=客服A;reason=当前负载低 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“改派工单给组员”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ticket_id=T001;from=组长;to=客服A;reason=当前负载低。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “改派工单给组员”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_assignment_logs;状态值=已分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“改派工单给组员”;跨站点/跨部门数据需按权限范围过滤。 | 改派工单给组员完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:改派工单给组员 | 待执行\nTC-PROTO-0123 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行强风险拦截并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“强风险拦截”的权限;相关基础数据已准备。 | person_id=P300;命中黑名单邮箱和设备 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“强风险拦截”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P300;命中黑名单邮箱和设备。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “强风险拦截”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“强风险拦截”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases/blacklist_entities;状态值=强风险拦截;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“强风险拦截”;跨站点/跨部门数据需按权限范围过滤。 | 强风险拦截完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:强风险拦截 | 待执行\nTC-PROTO-0124 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行弱风险人工放行并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“弱风险人工放行”的权限;相关基础数据已准备。 | risk_case=R001;原因=家庭共用设备;意见=放行 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“弱风险人工放行”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:risk_case=R001;原因=家庭共用设备;意见=放行。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “弱风险人工放行”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已放行”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases;状态值=已放行;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“弱风险人工放行”;跨站点/跨部门数据需按权限范围过滤。 | 弱风险人工放行完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:弱风险人工放行 | 待执行\nTC-PROTO-0125 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行登记评价提交并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“登记评价提交”的权限;相关基础数据已准备。 | person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接 | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“登记评价提交”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “登记评价提交”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已提交”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_submission_records;状态值=已提交;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“登记评价提交”;跨站点/跨部门数据需按权限范围过滤。 | 登记评价提交完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:登记评价提交 | 待执行\nTC-PROTO-0126 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon展示核验成功并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon展示核验成功”的权限;相关基础数据已准备。 | submission_id=SUB001;check_method=人工;result=DISPLAYED | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon展示核验成功”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB001;check_method=人工;result=DISPLAYED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon展示核验成功”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“CONFIRMED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks/review_results;状态值=CONFIRMED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon展示核验成功”;跨站点/跨部门数据需按权限范围过滤。 | Amazon展示核验成功完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon展示核验成功 | 待执行\nTC-PROTO-0127 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon暂不可核验进入观察并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon暂不可核验进入观察”的权限;相关基础数据已准备。 | submission_id=SUB002;result=UNVERIFIABLE | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon暂不可核验进入观察”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB002;result=UNVERIFIABLE。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon暂不可核验进入观察”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OBSERVING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks;状态值=OBSERVING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon暂不可核验进入观察”;跨站点/跨部门数据需按权限范围过滤。 | Amazon暂不可核验进入观察完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon暂不可核验进入观察 | 待执行\nTC-PROTO-0128 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL执行创建免评协作任务并校验业务结果 | P1 | 已进入“KOC/KOL”;当前用户具备执行“创建免评协作任务”的权限;相关基础数据已准备。 | creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频 | 1. 打开“用户运营系统-单文件.html”的“KOC/KOL”页面。\n2. 点击与“创建免评协作任务”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建免评协作任务”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“执行中”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:exemption_plan_tasks/code_records;状态值=执行中;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建免评协作任务”;跨站点/跨部门数据需按权限范围过滤。 | 创建免评协作任务完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:KOC/KOL;动作:创建免评协作任务 | 待执行\nTC-PROTO-0129 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知执行查看敏感信息审计并校验业务结果 | P1 | 已进入“审计通知”;当前用户具备执行“查看敏感信息审计”的权限;相关基础数据已准备。 | 对象=用户邮箱/设备号;动作=查看完整信息 | 1. 打开“用户运营系统-单文件.html”的“审计通知”页面。\n2. 点击与“查看敏感信息审计”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:对象=用户邮箱/设备号;动作=查看完整信息。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “查看敏感信息审计”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已记录”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:interaction_audit_logs;状态值=已记录;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“查看敏感信息审计”;跨站点/跨部门数据需按权限范围过滤。 | 查看敏感信息审计完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:审计通知;动作:查看敏感信息审计 | 待执行\nTC-PROTO-0130 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理执行分配导出权限并校验业务结果 | P1 | 已进入“系统管理”;当前用户具备执行“分配导出权限”的权限;相关基础数据已准备。 | role=用户运营组长;permission=导出计划数据;scope=US站点 | 1. 打开“用户运营系统-单文件.html”的“系统管理”页面。\n2. 点击与“分配导出权限”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:role=用户运营组长;permission=导出计划数据;scope=US站点。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “分配导出权限”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已授权”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:权限配置/审计日志;状态值=已授权;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“分配导出权限”;跨站点/跨部门数据需按权限范围过滤。 | 分配导出权限完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:系统管理;动作:分配导出权限 | 待执行\nTC-PROTO-0131 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:创建需求缺少ASIN | P2 | 已进入“需求中心”;准备异常数据或异常状态:ASIN为空,其他字段完整。 | 异常场景=创建需求缺少ASIN;异常数据=ASIN为空,其他字段完整 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:ASIN为空,其他字段完整。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“创建需求缺少ASIN”。\n2. 处理结果为:提示ASIN必填,需求不创建。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:创建需求缺少ASIN | 待执行\nTC-PROTO-0132 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:目标数量为0或负数 | P2 | 已进入“需求中心”;准备异常数据或异常状态:target_count=0/-1。 | 异常场景=目标数量为0或负数;异常数据=target_count=0/-1 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:target_count=0/-1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“目标数量为0或负数”。\n2. 处理结果为:提示目标数量必须大于0。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:目标数量为0或负数 | 待执行\nTC-PROTO-0133 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 异常场景 | 计划审核异常校验:审批意见为空驳回 | P2 | 已进入“计划审核”;准备异常数据或异常状态:decision=驳回;comment为空。 | 异常场景=审批意见为空驳回;异常数据=decision=驳回;comment为空 | 1. 打开“计划审核”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:decision=驳回;comment为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“审批意见为空驳回”。\n2. 处理结果为:阻止提交并提示填写驳回原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划审核;异常:审批意见为空驳回 | 待执行\nTC-PROTO-0134 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 异常场景 | 计划中心异常校验:已终止计划再次启动 | P2 | 已进入“计划中心”;准备异常数据或异常状态:plan.status=CANCELLED。 | 异常场景=已终止计划再次启动;异常数据=plan.status=CANCELLED | 1. 打开“计划中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:plan.status=CANCELLED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“已终止计划再次启动”。\n2. 处理结果为:启动按钮不可用或提示不可恢复。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划中心;异常:已终止计划再次启动 | 待执行\nTC-PROTO-0135 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:月度测评额度超过4 | P2 | 已进入“额度频控”;准备异常数据或异常状态:used=4,reserved=0,count=1。 | 异常场景=月度测评额度超过4;异常数据=used=4,reserved=0,count=1 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:used=4,reserved=0,count=1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“月度测评额度超过4”。\n2. 处理结果为:返回exceeded并阻止预占。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:月度测评额度超过4 | 待执行\nTC-PROTO-0136 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:累计评价超过12 | P2 | 已进入“额度频控”;准备异常数据或异常状态:lifetime_submission=12。 | 异常场景=累计评价超过12;异常数据=lifetime_submission=12 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:lifetime_submission=12。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“累计评价超过12”。\n2. 处理结果为:候选人进入排除池。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:累计评价超过12 | 待执行\nTC-PROTO-0137 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:发送前终校发现未关闭工单 | P2 | 已进入“额度频控”;准备异常数据或异常状态:support open ticket exists。 | 异常场景=发送前终校发现未关闭工单;异常数据=support open ticket exists | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:support open ticket exists。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“发送前终校发现未关闭工单”。\n2. 处理结果为:撤出本批次并记录原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:发送前终校发现未关闭工单 | 待执行\nTC-PROTO-0138 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:同计划同用户重复触达 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:person_id相同、plan_id相同、channel不同。 | 异常场景=同计划同用户重复触达;异常数据=person_id相同、plan_id相同、channel不同 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:person_id相同、plan_id相同、channel不同。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“同计划同用户重复触达”。\n2. 处理结果为:去重记录BLOCKED。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:同计划同用户重复触达 | 待执行\nTC-PROTO-0139 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:EDM硬退信 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:event=HARD_BOUNCED。 | 异常场景=EDM硬退信;异常数据=event=HARD_BOUNCED | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:event=HARD_BOUNCED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“EDM硬退信”。\n2. 处理结果为:邮箱状态标记不可触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:EDM硬退信 | 待执行\nTC-PROTO-0140 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:TEL三次未接通 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:retry_count=3。 | 异常场景=TEL三次未接通;异常数据=retry_count=3 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:retry_count=3。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“TEL三次未接通”。\n2. 处理结果为:降级EDM或关闭电话任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:TEL三次未接通 | 待执行\nTC-PROTO-0141 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:关闭工单缺少处理结果 | P2 | 已进入“客服中心”;准备异常数据或异常状态:result为空。 | 异常场景=关闭工单缺少处理结果;异常数据=result为空 | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:result为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“关闭工单缺少处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:关闭工单缺少处理结果 | 待执行\nTC-PROTO-0142 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:答应配合超时 | P2 | 已进入“客服中心”;准备异常数据或异常状态:deadline_at过期且无submission。 | 异常场景=答应配合超时;异常数据=deadline_at过期且无submission | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:deadline_at过期且无submission。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“答应配合超时”。\n2. 处理结果为:生成需再次联系任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:答应配合超时 | 待执行\nTC-PROTO-0143 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:黑名单同步接口超时 | P2 | 已进入“风险中心”;准备异常数据或异常状态:blacklist API timeout。 | 异常场景=黑名单同步接口超时;异常数据=blacklist API timeout | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:blacklist API timeout。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“黑名单同步接口超时”。\n2. 处理结果为:状态为失败待重试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:黑名单同步接口超时 | 待执行\nTC-PROTO-0144 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:弱风险复核被拒绝 | P2 | 已进入“风险中心”;准备异常数据或异常状态:人工意见=拒绝。 | 异常场景=弱风险复核被拒绝;异常数据=人工意见=拒绝 | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:人工意见=拒绝。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“弱风险复核被拒绝”。\n2. 处理结果为:用户不能进入触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:弱风险复核被拒绝 | 待执行\nTC-PROTO-0145 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:提交评价ASIN不匹配 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:登记ASIN=A,证据链接ASIN=B。 | 异常场景=提交评价ASIN不匹配;异常数据=登记ASIN=A,证据链接ASIN=B | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:登记ASIN=A,证据链接ASIN=B。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“提交评价ASIN不匹配”。\n2. 处理结果为:标记异常不计入完成。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:提交评价ASIN不匹配 | 待执行\nTC-PROTO-0146 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:Amazon未展示超过观察期 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:status=OBSERVING且retry超期。 | 异常场景=Amazon未展示超过观察期;异常数据=status=OBSERVING且retry超期 | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:status=OBSERVING且retry超期。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“Amazon未展示超过观察期”。\n2. 处理结果为:标记ABNORMAL并通知运营。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:Amazon未展示超过观察期 | 待执行\nTC-PROTO-0147 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 异常场景 | KOC/KOL异常校验:CODE缺失 | P2 | 已进入“KOC/KOL”;准备异常数据或异常状态:免评任务未填写CODE。 | 异常场景=CODE缺失;异常数据=免评任务未填写CODE | 1. 打开“KOC/KOL”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:免评任务未填写CODE。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“CODE缺失”。\n2. 处理结果为:阻止创建或标记价格/CODE待确认。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:KOC/KOL;异常:CODE缺失 | 待执行\nTC-PROTO-0148 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 异常场景 | 审计通知异常校验:普通客服查看完整邮箱 | P2 | 已进入“审计通知”;准备异常数据或异常状态:role=客服;action=查看完整信息。 | 异常场景=普通客服查看完整邮箱;异常数据=role=客服;action=查看完整信息 | 1. 打开“审计通知”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:role=客服;action=查看完整信息。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“普通客服查看完整邮箱”。\n2. 处理结果为:拒绝访问并记录越权尝试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:审计通知;异常:普通客服查看完整邮箱 | 待执行\nTC-PROTO-0149 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 异常场景 | 系统管理异常校验:离职账号仍有审批任务 | P2 | 已进入“系统管理”;准备异常数据或异常状态:account=disabled但任务未交接。 | 异常场景=离职账号仍有审批任务;异常数据=account=disabled但任务未交接 | 1. 打开“系统管理”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:account=disabled但任务未交接。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“离职账号仍有审批任务”。\n2. 处理结果为:阻止完成离职并提示先交接。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:系统管理;异常:离职账号仍有审批任务 | 待执行\nTC-PROTO-0150 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发按线索查真实人并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/person?type=email&value=xxx”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/person?type=email&value=xxx;预期输出=返回person_id、confidence、matched_clues | 1. 打开“用户中心”页面。\n2. 执行会触发“按线索查真实人”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/person?type=email&value=xxx”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/person?type=email&value=xxx”。\n2. 接口响应包含:返回person_id、confidence、matched_clues。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 按线索查真实人在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/person?type=email&value=xxx | 待执行\nTC-PROTO-0151 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发获取用户上下文卡并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/context/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/context/{person_id};预期输出=返回identity、transactions、services、risks、devices、outreach_history | 1. 打开“用户中心”页面。\n2. 执行会触发“获取用户上下文卡”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/context/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/context/{person_id}”。\n2. 接口响应包含:返回identity、transactions、services、risks、devices、outreach_history。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 获取用户上下文卡在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/context/{person_id} | 待执行\nTC-PROTO-0152 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发批量身份查询并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“POST /api/identity/batch-check”;测试用户拥有页面访问权限。 | 接口=POST /api/identity/batch-check;预期输出=返回每个线索对应person_id和confidence | 1. 打开“用户中心”页面。\n2. 执行会触发“批量身份查询”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/identity/batch-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/identity/batch-check”。\n2. 接口响应包含:返回每个线索对应person_id和confidence。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量身份查询在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:POST /api/identity/batch-check | 待执行\nTC-PROTO-0153 | 用户运营系统-单文件.html | 计划中心 | 需求与计划管理 | 数据校验 | 计划中心页面触发创建需求接口并校验接口数据 | P2 | 前端页面“计划中心”已打开;后端或 mock 服务提供接口“POST /api/demands”;测试用户拥有页面访问权限。 | 接口=POST /api/demands;预期输出=返回demand_id和status | 1. 打开“计划中心”页面。\n2. 执行会触发“创建需求接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/demands”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/demands”。\n2. 接口响应包含:返回demand_id和status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建需求接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划中心;接口:POST /api/demands | 待执行\nTC-PROTO-0154 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | 数据校验 | 计划审核页面触发提交审批接口并校验接口数据 | P2 | 前端页面“计划审核”已打开;后端或 mock 服务提供接口“POST /api/approvals/{plan_id}/submit”;测试用户拥有页面访问权限。 | 接口=POST /api/approvals/{plan_id}/submit;预期输出=生成approval_records | 1. 打开“计划审核”页面。\n2. 执行会触发“提交审批接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/approvals/{plan_id}/submit”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/approvals/{plan_id}/submit”。\n2. 接口响应包含:生成approval_records。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 提交审批接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划审核;接口:POST /api/approvals/{plan_id}/submit | 待执行\nTC-PROTO-0155 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发额度查询接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“GET /api/quota/check/{person_id}?type=REVIEW”;测试用户拥有页面访问权限。 | 接口=GET /api/quota/check/{person_id}?type=REVIEW;预期输出=返回used、in_progress、reserved、remaining、status | 1. 打开“额度频控”页面。\n2. 执行会触发“额度查询接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/quota/check/{person_id}?type=REVIEW”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/quota/check/{person_id}?type=REVIEW”。\n2. 接口响应包含:返回used、in_progress、reserved、remaining、status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 额度查询接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:GET /api/quota/check/{person_id}?type=REVIEW | 待执行\nTC-PROTO-0156 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发批量预占接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/reserve”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/reserve;预期输出=返回reservation_id并更新reserved | 1. 打开“额度频控”页面。\n2. 执行会触发“批量预占接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/reserve”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/reserve”。\n2. 接口响应包含:返回reservation_id并更新reserved。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量预占接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/reserve | 待执行\nTC-PROTO-0157 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发发送前终校接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/final-check”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/final-check;预期输出=返回APPROVED/WITHDRAWN和reasons | 1. 打开“额度频控”页面。\n2. 执行会触发“发送前终校接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/final-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/final-check”。\n2. 接口响应包含:返回APPROVED/WITHDRAWN和reasons。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 发送前终校接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/final-check | 待执行\nTC-PROTO-0158 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发渠道路由接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“POST /api/outreach/route”;测试用户拥有页面访问权限。 | 接口=POST /api/outreach/route;预期输出=返回recommended_channel和alternatives | 1. 打开“推送/触达”页面。\n2. 执行会触发“渠道路由接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/outreach/route”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/outreach/route”。\n2. 接口响应包含:返回recommended_channel和alternatives。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 渠道路由接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:POST /api/outreach/route | 待执行\nTC-PROTO-0159 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发触达历史接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“GET /api/outreach/history/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/outreach/history/{person_id};预期输出=返回im、edm、app、tel历史 | 1. 打开“推送/触达”页面。\n2. 执行会触发“触达历史接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/outreach/history/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/outreach/history/{person_id}”。\n2. 接口响应包含:返回im、edm、app、tel历史。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 触达历史接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:GET /api/outreach/history/{person_id} | 待执行\nTC-PROTO-0160 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发创建工单接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“POST /api/tickets”;测试用户拥有页面访问权限。 | 接口=POST /api/tickets;预期输出=返回ticket_id | 1. 打开“客服中心”页面。\n2. 执行会触发“创建工单接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/tickets”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/tickets”。\n2. 接口响应包含:返回ticket_id。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建工单接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:POST /api/tickets | 待执行\nTC-PROTO-0161 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发查询可用客服接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“GET /api/support/available-agents”;测试用户拥有页面访问权限。 | 接口=GET /api/support/available-agents;预期输出=返回agent_id和current_load | 1. 打开“客服中心”页面。\n2. 执行会触发“查询可用客服接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/support/available-agents”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/support/available-agents”。\n2. 接口响应包含:返回agent_id和current_load。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询可用客服接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:GET /api/support/available-agents | 待执行\nTC-PROTO-0162 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发记录评价提交接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“POST /api/reviews/submission”;测试用户拥有页面访问权限。 | 接口=POST /api/reviews/submission;预期输出=返回submission_id和quota_updated | 1. 打开“评价追踪”页面。\n2. 执行会触发“记录评价提交接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/reviews/submission”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/reviews/submission”。\n2. 接口响应包含:返回submission_id和quota_updated。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 记录评价提交接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:POST /api/reviews/submission | 待执行\nTC-PROTO-0163 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发查询计划评价进度接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“GET /api/reviews/status/{plan_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/reviews/status/{plan_id};预期输出=返回total_submissions、verified、pending、completion_rate | 1. 打开“评价追踪”页面。\n2. 执行会触发“查询计划评价进度接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/reviews/status/{plan_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/reviews/status/{plan_id}”。\n2. 接口响应包含:返回total_submissions、verified、pending、completion_rate。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询计划评价进度接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:GET /api/reviews/status/{plan_id} | 待执行\nTC-PROTO-0164 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:评价主闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流 | 1. 从 Dashboard 或对应入口启动“评价主闭环”。\n2. 按流程依次完成:ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “评价主闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 评价主闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:评价主闭环 | 待执行\nTC-PROTO-0165 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:紧急Listing闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升 | 1. 从 Dashboard 或对应入口启动“紧急Listing闭环”。\n2. 按流程依次完成:评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “紧急Listing闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 紧急Listing闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:紧急Listing闭环 | 待执行\nTC-PROTO-0166 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:推送风险复核闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录 | 1. 从 Dashboard 或对应入口启动“推送风险复核闭环”。\n2. 按流程依次完成:退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “推送风险复核闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 推送风险复核闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:推送风险复核闭环 | 待执行\nTC-PROTO-0167 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:黑名单同步闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查 | 1. 从 Dashboard 或对应入口启动“黑名单同步闭环”。\n2. 按流程依次完成:客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “黑名单同步闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 黑名单同步闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:黑名单同步闭环 | 待执行\nTC-PROTO-0168 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:客服转化闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新 | 1. 从 Dashboard 或对应入口启动“客服转化闭环”。\n2. 按流程依次完成:用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “客服转化闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 客服转化闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:客服转化闭环 | 待执行\nTC-PROTO-0169 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:免评协作闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新 | 1. 从 Dashboard 或对应入口启动“免评协作闭环”。\n2. 按流程依次完成:免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “免评协作闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 免评协作闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:免评协作闭环 | 待执行\nTC-PROTO-0190 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“需求中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2;详情字段=需求ID、ASIN、目标数量、周期、提交人、评估结果 | 1. 打开“需求中心”页面。\n2. 在筛选区按业务条件选择或输入:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:需求ID、ASIN、目标数量、周期、提交人、评估结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2”。\n2. 详情抽屉展示“需求ID、ASIN、目标数量、周期、提交人、评估结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 需求中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:需求中心;筛选:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2 | 待执行\nTC-PROTO-0191 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划审核”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回;详情字段=审批链、审批人、意见、step_order、decided_at | 1. 打开“计划审核”页面。\n2. 在筛选区按业务条件选择或输入:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:审批链、审批人、意见、step_order、decided_at。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回”。\n2. 详情抽屉展示“审批链、审批人、意见、step_order、decided_at”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划审核查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划审核;筛选:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回 | 待执行\nTC-PROTO-0192 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL;详情字段=计划项、目标量、候选人、资源分配、完成率 | 1. 打开“计划中心”页面。\n2. 在筛选区按业务条件选择或输入:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:计划项、目标量、候选人、资源分配、完成率。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL”。\n2. 详情抽屉展示“计划项、目标量、候选人、资源分配、完成率”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划中心;筛选:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL | 待执行\nTC-PROTO-0193 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 功能测试 | ASIN/Listing按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“ASIN/Listing”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间;详情字段=评分、评价数、差评数、健康状态、责任人 | 1. 打开“ASIN/Listing”页面。\n2. 在筛选区按业务条件选择或输入:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:评分、评价数、差评数、健康状态、责任人。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间”。\n2. 详情抽屉展示“评分、评价数、差评数、健康状态、责任人”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | ASIN/Listing查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:ASIN/Listing;筛选:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间 | 待执行\nTC-PROTO-0194 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 功能测试 | 用户中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“用户中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=国家、性别、标签、身份、产品数、活动数、近7天EDM次数;详情字段=用户主档、标签、身份、产品关系、近期活跃 | 1. 打开“用户中心”页面。\n2. 在筛选区按业务条件选择或输入:国家、性别、标签、身份、产品数、活动数、近7天EDM次数。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:用户主档、标签、身份、产品关系、近期活跃。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“国家、性别、标签、身份、产品数、活动数、近7天EDM次数”。\n2. 详情抽屉展示“用户主档、标签、身份、产品关系、近期活跃”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 用户中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:用户中心;筛选:国家、性别、标签、身份、产品数、活动数、近7天EDM次数 | 待执行\nTC-PROTO-0195 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“额度频控”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=额度类型=测评/免评/累计;状态=sufficient/warning/exceeded;详情字段=used、in_progress、reserved、remaining、limit_value | 1. 打开“额度频控”页面。\n2. 在筛选区按业务条件选择或输入:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:used、in_progress、reserved、remaining、limit_value。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“额度类型=测评/免评/累计;状态=sufficient/warning/exceeded”。\n2. 详情抽屉展示“used、in_progress、reserved、remaining、limit_value”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 额度频控查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:额度频控;筛选:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded | 待执行\nTC-PROTO-0196 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“推送/触达”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订;详情字段=发送、点击、回复、退订、route decision、dedup reason | 1. 打开“推送/触达”页面。\n2. 在筛选区按业务条件选择或输入:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:发送、点击、回复、退订、route decision、dedup reason。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订”。\n2. 详情抽屉展示“发送、点击、回复、退订、route decision、dedup reason”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 推送/触达查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:推送/触达;筛选:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订 | 待执行\nTC-PROTO-0197 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“客服中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭;详情字段=工单ID、assigned_agent、followup状态、首次回复时长 | 1. 打开“客服中心”页面。\n2. 在筛选区按业务条件选择或输入:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:工单ID、assigned_agent、followup状态、首次回复时长。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭”。\n2. 详情抽屉展示“工单ID、assigned_agent、followup状态、首次回复时长”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 客服中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:客服中心;筛选:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭 | 待执行\nTC-PROTO-0198 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“风险中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝;详情字段=risk_signal、risk_case、blacklist_entity | 1. 打开“风险中心”页面。\n2. 在筛选区按业务条件选择或输入:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:risk_signal、risk_case、blacklist_entity。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝”。\n2. 详情抽屉展示“risk_signal、risk_case、blacklist_entity”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 风险中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:风险中心;筛选:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝 | 待执行\nTC-PROTO-0199 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“评价追踪”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=核验状态=已提交/展示成功/暂不可核验/异常观察;详情字段=submission_id、display_check、retry_count、completion_rate | 1. 打开“评价追踪”页面。\n2. 在筛选区按业务条件选择或输入:核验状态=已提交/展示成功/暂不可核验/异常观察。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:submission_id、display_check、retry_count、completion_rate。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“核验状态=已提交/展示成功/暂不可核验/异常观察”。\n2. 详情抽屉展示“submission_id、display_check、retry_count、completion_rate”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 评价追踪查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:评价追踪;筛选:核验状态=已提交/展示成功/暂不可核验/异常观察 | 待执行\nTC-PROTO-0200 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“KOC/KOL”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置;详情字段=creator、Brief、CODE、返点、内容链接 | 1. 打开“KOC/KOL”页面。\n2. 在筛选区按业务条件选择或输入:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:creator、Brief、CODE、返点、内容链接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置”。\n2. 详情抽屉展示“creator、Brief、CODE、返点、内容链接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | KOC/KOL查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:KOC/KOL;筛选:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置 | 待执行\nTC-PROTO-0201 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“审计通知”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围;详情字段=日志ID、操作者、对象、动作、结果 | 1. 打开“审计通知”页面。\n2. 在筛选区按业务条件选择或输入:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:日志ID、操作者、对象、动作、结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围”。\n2. 详情抽屉展示“日志ID、操作者、对象、动作、结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 审计通知查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:审计通知;筛选:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围 | 待执行\nTC-PROTO-0202 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“系统管理”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=角色、部门、站点、权限点、账号状态;详情字段=账号、角色、数据范围、权限点、离职交接 | 1. 打开“系统管理”页面。\n2. 在筛选区按业务条件选择或输入:角色、部门、站点、权限点、账号状态。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:账号、角色、数据范围、权限点、离职交接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“角色、部门、站点、权限点、账号状态”。\n2. 详情抽屉展示“账号、角色、数据范围、权限点、离职交接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 系统管理查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:系统管理;筛选:角色、部门、站点、权限点、账号状态 | 待执行\nTC-PROTO-0203 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度剩余1次预警 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,in_progress=0,reserved=0,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,in_progress=0,reserved=0,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:允许预占但进入预警池。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度剩余1次预警 | 待执行\nTC-PROTO-0204 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度已用3且预占1再预占1 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,reserved=1,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,reserved=1,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:阻止预占,状态exceeded。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度已用3且预占1再预占1 | 待执行\nTC-PROTO-0205 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:免评额度独立预占 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=review used=4, exemption used=0,count=1 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:review used=4, exemption used=0,count=1。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:免评可预占,测评不可预占。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:免评额度独立预占 | 待执行\nTC-PROTO-0206 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:累计12提交后不因未展示回退 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=lifetime=11,提交评价后Amazon未展示 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:lifetime=11,提交评价后Amazon未展示。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:lifetime变12且不回退。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:累计12提交后不因未展示回退 | 待执行\nTC-PROTO-0207 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:预占超时自动释放 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=reservation.status=RESERVED且expires_at已过 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:reservation.status=RESERVED且expires_at已过。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:状态EXPIRED/RELEASED,remaining恢复。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:预占超时自动释放 | 待执行\nTC-PROTO-0208 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:跨计划重复入选 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=同person同时进入PLAN-A和PLAN-B | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:同person同时进入PLAN-A和PLAN-B。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:只保留高优先级或先预占计划,另一计划排除/预警。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:跨计划重复入选 | 待执行\nTC-PROTO-0209 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道IM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:APP活跃+已绑定用户;额度、风险、去重均通过。 | 渠道=IM;用户条件=APP活跃+已绑定用户;动作=推送回评卡片 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“APP活跃+已绑定用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“IM”。\n4. 执行“推送回评卡片”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“IM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“im_interaction_records”。\n3. 后续处理符合:用户回复后重新校验身份/额度/风险。\n4. Dashboard和用户上下文卡可查看触达历史。 | im_interaction_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | IM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:IM | 待执行\nTC-PROTO-0210 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道EDM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:未注册APP但邮箱可用用户;额度、风险、去重均通过。 | 渠道=EDM;用户条件=未注册APP但邮箱可用用户;动作=发送邮件并追踪送达/打开/点击/回复/退订 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“未注册APP但邮箱可用用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“EDM”。\n4. 执行“发送邮件并追踪送达/打开/点击/回复/退订”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“EDM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“edm_message_events”。\n3. 后续处理符合:回复邮件生成客服工单。\n4. Dashboard和用户上下文卡可查看触达历史。 | edm_message_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | EDM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:EDM | 待执行\nTC-PROTO-0211 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道APP Push按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;额度、风险、去重均通过。 | 渠道=APP Push;用户条件=绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;动作=发送Push并追踪点击打开/忽略/卸载 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“绑定新玩具/不活跃/计划到期/Listing紧急/活动触发”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“APP Push”。\n4. 执行“发送Push并追踪点击打开/忽略/卸载”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“APP Push”作为推荐渠道或可选渠道。\n2. 执行动作后写入“app_touch_events”。\n3. 后续处理符合:点击后分流到提交回评/联系客服/浏览。\n4. Dashboard和用户上下文卡可查看触达历史。 | app_touch_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | APP Push渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:APP Push | 待执行\nTC-PROTO-0212 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道TEL按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:高价值多次无响应或答应配合超时用户;额度、风险、去重均通过。 | 渠道=TEL;用户条件=高价值多次无响应或答应配合超时用户;动作=生成电话任务并记录通话结果 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“高价值多次无响应或答应配合超时用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“TEL”。\n4. 执行“生成电话任务并记录通话结果”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“TEL”作为推荐渠道或可选渠道。\n2. 执行动作后写入“tel_call_records”。\n3. 后续处理符合:未接通小于3次重拨,大于等于3次降级EDM或关闭。\n4. Dashboard和用户上下文卡可查看触达历史。 | tel_call_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | TEL渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:TEL | 待执行\nTC-PROTO-0213 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理截图证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=截图;包含ASIN和评论内容 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=截图;包含ASIN和评论内容。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并触发quota commit。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:截图证据登记 | 待执行\nTC-PROTO-0214 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理链接证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=Review Link;链接可打开 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=Review Link;链接可打开。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并进入展示核验。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:链接证据登记 | 待执行\nTC-PROTO-0215 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理人工核验展示成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=人工;check_result=DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=人工;check_result=DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:计入计划完成数并更新ASIN健康。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:人工核验展示成功 | 待执行\nTC-PROTO-0216 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理自动核验未展示 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=自动;check_result=NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=自动;check_result=NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:进入异常观察队列。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:自动核验未展示 | 待执行\nTC-PROTO-0217 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理暂不可核验 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_result=UNVERIFIABLE;原因=Amazon审核中 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_result=UNVERIFIABLE;原因=Amazon审核中。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:保留已提交事实并定期复查。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:暂不可核验 | 待执行\nTC-PROTO-0218 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察复查成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=OBSERVING重试后DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:OBSERVING重试后DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:转CONFIRMED并回流计划。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察复查成功 | 待执行\nTC-PROTO-0219 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察期满失败 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=retry_count超过阈值仍NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:retry_count超过阈值仍NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:标记ABNORMAL并通知运营。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察期满失败 | 待执行\nTC-PROTO-0266 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色Amazon运营模块访问与按钮权限 | P1 | 准备角色为“Amazon运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=Amazon运营;可见范围=需求中心/ASIN/计划审核查看;允许=创建需求、查看ASIN健康;限制=不能审批用户运营计划或查看完整用户敏感信息 | 1. 使用“Amazon运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:创建需求、查看ASIN健康。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批用户运营计划或查看完整用户敏感信息。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. Amazon运营只能访问“需求中心/ASIN/计划审核查看”。\n2. 允许动作“创建需求、查看ASIN健康”可正常执行。\n3. 限制动作“不能审批用户运营计划或查看完整用户敏感信息”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | Amazon运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=Amazon运营 | 待执行\nTC-PROTO-0267 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色用户运营模块访问与按钮权限 | P1 | 准备角色为“用户运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=用户运营;可见范围=需求中心/计划中心/推送中心/用户中心;允许=评估需求、生成计划、圈选人群、触达;限制=不能同步黑名单或配置系统权限 | 1. 使用“用户运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:评估需求、生成计划、圈选人群、触达。\n4. 尝试通过URL hash或按钮执行限制动作:不能同步黑名单或配置系统权限。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 用户运营只能访问“需求中心/计划中心/推送中心/用户中心”。\n2. 允许动作“评估需求、生成计划、圈选人群、触达”可正常执行。\n3. 限制动作“不能同步黑名单或配置系统权限”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 用户运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=用户运营 | 待执行\nTC-PROTO-0268 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服模块访问与按钮权限 | P1 | 准备角色为“客服”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服;可见范围=客服中心/用户上下文摘要/评价登记;允许=处理工单、登记评价提交;限制=不能查看跨团队绩效和完整设备号 | 1. 使用“客服”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:处理工单、登记评价提交。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看跨团队绩效和完整设备号。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服只能访问“客服中心/用户上下文摘要/评价登记”。\n2. 允许动作“处理工单、登记评价提交”可正常执行。\n3. 限制动作“不能查看跨团队绩效和完整设备号”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服 | 待执行\nTC-PROTO-0269 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服主管模块访问与按钮权限 | P1 | 准备角色为“客服主管”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服主管;可见范围=客服中心/客服执行看板/绩效;允许=分配工单、查看组内绩效、排班;限制=不能审批免评计划除非授权 | 1. 使用“客服主管”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:分配工单、查看组内绩效、排班。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批免评计划除非授权。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服主管只能访问“客服中心/客服执行看板/绩效”。\n2. 允许动作“分配工单、查看组内绩效、排班”可正常执行。\n3. 限制动作“不能审批免评计划除非授权”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服主管权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服主管 | 待执行\nTC-PROTO-0270 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色风险负责人模块访问与按钮权限 | P1 | 准备角色为“风险负责人”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=风险负责人;可见范围=风险中心/黑名单/审计;允许=复核风险、同步黑名单、标记误报;限制=不能修改计划目标量 | 1. 使用“风险负责人”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:复核风险、同步黑名单、标记误报。\n4. 尝试通过URL hash或按钮执行限制动作:不能修改计划目标量。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 风险负责人只能访问“风险中心/黑名单/审计”。\n2. 允许动作“复核风险、同步黑名单、标记误报”可正常执行。\n3. 限制动作“不能修改计划目标量”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 风险负责人权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=风险负责人 | 待执行\nTC-PROTO-0271 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色KOC运营模块访问与按钮权限 | P1 | 准备角色为“KOC运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=KOC运营;可见范围=KOC/KOL协作;允许=维护Brief、CODE、内容记录;限制=不能查看普通用户完整身份线索 | 1. 使用“KOC运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:维护Brief、CODE、内容记录。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看普通用户完整身份线索。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. KOC运营只能访问“KOC/KOL协作”。\n2. 允许动作“维护Brief、CODE、内容记录”可正常执行。\n3. 限制动作“不能查看普通用户完整身份线索”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | KOC运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=KOC运营 | 待执行\nTC-PROTO-0272 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色系统管理员模块访问与按钮权限 | P1 | 准备角色为“系统管理员”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=系统管理员;可见范围=全部模块;允许=账号权限、审计、配置、跨部门看板;限制=敏感访问仍需审计 | 1. 使用“系统管理员”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:账号权限、审计、配置、跨部门看板。\n4. 尝试通过URL hash或按钮执行限制动作:敏感访问仍需审计。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 系统管理员只能访问“全部模块”。\n2. 允许动作“账号权限、审计、配置、跨部门看板”可正常执行。\n3. 限制动作“敏感访问仍需审计”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 系统管理员权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=系统管理员 | 待执行\nTC-PROTO-0273 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 数据校验 | 需求中心执行导出待评估需求并校验导出脱敏与范围 | P2 | 已进入“需求中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=待评估;导出内容=导出demands当前筛选字段 | 1. 打开“需求中心”。\n2. 设置筛选条件:筛选状态=待评估。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出demands当前筛选字段。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=需求中心;导出=导出待评估需求 | 待执行\nTC-PROTO-0274 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 数据校验 | 计划审核执行导出审批记录并校验导出脱敏与范围 | P2 | 已进入“计划审核”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选审批状态=待审批;导出内容=导出approval_records | 1. 打开“计划审核”。\n2. 设置筛选条件:筛选审批状态=待审批。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出approval_records。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划审核;导出=导出审批记录 | 待执行\nTC-PROTO-0275 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 数据校验 | 计划中心执行导出计划执行进度并校验导出脱敏与范围 | P2 | 已进入“计划中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=执行中;导出内容=导出计划、计划项、完成率 | 1. 打开“计划中心”。\n2. 设置筛选条件:筛选状态=执行中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出计划、计划项、完成率。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划中心;导出=导出计划执行进度 | 待执行\nTC-PROTO-0276 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 数据校验 | ASIN/Listing执行导出健康风险ASIN并校验导出脱敏与范围 | P2 | 已进入“ASIN/Listing”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选健康状态=风险/严重风险;导出内容=导出评分、评价数、差评数 | 1. 打开“ASIN/Listing”。\n2. 设置筛选条件:筛选健康状态=风险/严重风险。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出评分、评价数、差评数。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=ASIN/Listing;导出=导出健康风险ASIN | 待执行\nTC-PROTO-0277 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 数据校验 | 用户中心执行导出人群包并校验导出脱敏与范围 | P2 | 已进入“用户中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选标签/国家/产品绑定;导出内容=导出脱敏用户ID和标签 | 1. 打开“用户中心”。\n2. 设置筛选条件:筛选标签/国家/产品绑定。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出脱敏用户ID和标签。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=用户中心;导出=导出人群包 | 待执行\nTC-PROTO-0278 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 数据校验 | 额度频控执行导出额度预警用户并校验导出脱敏与范围 | P2 | 已进入“额度频控”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=warning/exceeded;导出内容=导出额度台账摘要 | 1. 打开“额度频控”。\n2. 设置筛选条件:筛选status=warning/exceeded。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出额度台账摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=额度频控;导出=导出额度预警用户 | 待执行\nTC-PROTO-0279 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 数据校验 | 推送/触达执行导出退订用户并校验导出脱敏与范围 | P2 | 已进入“推送/触达”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选event=UNSUBSCRIBED;导出内容=导出退订事件和渠道 | 1. 打开“推送/触达”。\n2. 设置筛选条件:筛选event=UNSUBSCRIBED。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出退订事件和渠道。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=推送/触达;导出=导出退订用户 | 待执行\nTC-PROTO-0280 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 数据校验 | 客服中心执行导出超时工单并校验导出脱敏与范围 | P2 | 已进入“客服中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=等待用户且超时;导出内容=导出工单与负责人 | 1. 打开“客服中心”。\n2. 设置筛选条件:筛选状态=等待用户且超时。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出工单与负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=客服中心;导出=导出超时工单 | 待执行\nTC-PROTO-0281 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 数据校验 | 风险中心执行导出风险案件并校验导出脱敏与范围 | P2 | 已进入“风险中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=人工复核中;导出内容=导出风险摘要脱敏 | 1. 打开“风险中心”。\n2. 设置筛选条件:筛选状态=人工复核中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出风险摘要脱敏。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=风险中心;导出=导出风险案件 | 待执行\nTC-PROTO-0282 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 数据校验 | 评价追踪执行导出异常观察队列并校验导出脱敏与范围 | P2 | 已进入“评价追踪”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=OBSERVING/ABNORMAL;导出内容=导出提交和核验摘要 | 1. 打开“评价追踪”。\n2. 设置筛选条件:筛选status=OBSERVING/ABNORMAL。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出提交和核验摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=评价追踪;导出=导出异常观察队列 | 待执行\nTC-PROTO-0283 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 数据校验 | KOC/KOL执行导出逾期协作任务并校验导出脱敏与范围 | P2 | 已进入“KOC/KOL”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=逾期;导出内容=导出CODE/Brief/负责人 | 1. 打开“KOC/KOL”。\n2. 设置筛选条件:筛选状态=逾期。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出CODE/Brief/负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=KOC/KOL;导出=导出逾期协作任务 | 待执行\nTC-PROTO-0284 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 数据校验 | 审计通知执行导出敏感动作日志并校验导出脱敏与范围 | P2 | 已进入“审计通知”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选动作=查看完整信息/导出;导出内容=导出审计日志 | 1. 打开“审计通知”。\n2. 设置筛选条件:筛选动作=查看完整信息/导出。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出审计日志。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=审计通知;导出=导出敏感动作日志 | 待执行\nTC-PROTO-0298 | 用户运营系统-单文件.html | 需求中心 | 系统稳定性与幂等 | 异常场景 | 需求中心稳定性校验:创建需求重复提交 | P2 | 已进入“需求中心”;准备可执行场景:创建需求重复提交。 | 动作=提交按钮连续点击两次;预期=只创建一个demand_id | 1. 打开原型页面“需求中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:提交按钮连续点击两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只创建一个demand_id。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:创建需求重复提交 | 待执行\nTC-PROTO-0299 | 用户运营系统-单文件.html | 计划审核 | 系统稳定性与幂等 | 异常场景 | 计划审核稳定性校验:两名审批人同时审批 | P2 | 已进入“计划审核”;准备可执行场景:两名审批人同时审批。 | 动作=一个通过一个驳回并发提交;预期=按后端锁定规则只接受一个有效决策 | 1. 打开原型页面“计划审核”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:一个通过一个驳回并发提交。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:按后端锁定规则只接受一个有效决策。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:两名审批人同时审批 | 待执行\nTC-PROTO-0300 | 用户运营系统-单文件.html | 计划中心 | 系统稳定性与幂等 | 异常场景 | 计划中心稳定性校验:计划暂停后重复暂停 | P2 | 已进入“计划中心”;准备可执行场景:计划暂停后重复暂停。 | 动作=执行中计划点击暂停两次;预期=第二次提示计划已暂停 | 1. 打开原型页面“计划中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:执行中计划点击暂停两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:第二次提示计划已暂停。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:计划暂停后重复暂停 | 待执行\nTC-PROTO-0301 | 用户运营系统-单文件.html | 额度频控 | 系统稳定性与幂等 | 异常场景 | 额度频控稳定性校验:并发预占同一真实人最后额度 | P2 | 已进入“额度频控”;准备可执行场景:并发预占同一真实人最后额度。 | 动作=两个计划同时预占remaining=1;预期=只允许一个预占成功 | 1. 打开原型页面“额度频控”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个计划同时预占remaining=1。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只允许一个预占成功。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:并发预占同一真实人最后额度 | 待执行\nTC-PROTO-0302 | 用户运营系统-单文件.html | 推送/触达 | 系统稳定性与幂等 | 异常场景 | 推送/触达稳定性校验:发送任务队列中刷新 | P2 | 已进入“推送/触达”;准备可执行场景:发送任务队列中刷新。 | 动作=点击发送后立即刷新页面;预期=任务状态可从队列恢复查询 | 1. 打开原型页面“推送/触达”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:点击发送后立即刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:任务状态可从队列恢复查询。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:发送任务队列中刷新 | 待执行\nTC-PROTO-0303 | 用户运营系统-单文件.html | 客服中心 | 系统稳定性与幂等 | 异常场景 | 客服中心稳定性校验:同用户重复创建工单 | P2 | 已进入“客服中心”;准备可执行场景:同用户重复创建工单。 | 动作=同person_id已有open工单再次创建;预期=提示关联已有工单或合并 | 1. 打开原型页面“客服中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person_id已有open工单再次创建。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示关联已有工单或合并。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:同用户重复创建工单 | 待执行\nTC-PROTO-0304 | 用户运营系统-单文件.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:确认诈骗重复点击 | P2 | 已进入“风险中心”;准备可执行场景:确认诈骗重复点击。 | 动作=风险案件连续点击确认诈骗;预期=只同步一次黑名单候选 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:风险案件连续点击确认诈骗。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只同步一次黑名单候选。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:确认诈骗重复点击 | 待执行\nTC-PROTO-0305 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:评价提交重复登记 | P2 | 已进入“评价追踪”;准备可执行场景:评价提交重复登记。 | 动作=同person+asin+plan重复提交相同证据;预期=提示重复记录或合并,不重复扣额度 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person+asin+plan重复提交相同证据。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示重复记录或合并,不重复扣额度。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:评价提交重复登记 | 待执行\nTC-PROTO-0306 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:展示核验重复确认 | P2 | 已进入“评价追踪”;准备可执行场景:展示核验重复确认。 | 动作=已CONFIRMED记录再次确认展示;预期=计划完成数不重复增加 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:已CONFIRMED记录再次确认展示。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:计划完成数不重复增加。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:展示核验重复确认 | 待执行\nTC-PROTO-0307 | 用户运营系统-单文件.html | 系统管理 | 系统稳定性与幂等 | 异常场景 | 系统管理稳定性校验:权限变更后立即生效 | P2 | 已进入“系统管理”;准备可执行场景:权限变更后立即生效。 | 动作=撤销用户导出权限后刷新;预期=导出按钮不可用且接口拒绝 | 1. 打开原型页面“系统管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:撤销用户导出权限后刷新。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:导出按钮不可用且接口拒绝。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:权限变更后立即生效 | 待执行\nTC-PROTO-0308 | 用户运营系统-单文件.html | 审计通知 | 系统稳定性与幂等 | 异常场景 | 审计通知稳定性校验:审计列表空状态 | P2 | 已进入“审计通知”;准备可执行场景:审计列表空状态。 | 动作=筛选未来日期无日志;预期=显示暂无数据且可重置 | 1. 打开原型页面“审计通知”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选未来日期无日志。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:显示暂无数据且可重置。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:审计列表空状态 | 待执行\n# Sheet: 全量用例\n用例编号 | HTML原型 | 功能页面 | 需求模块 | 测试类型 | 用例名称 | 优先级 | 前置条件 | 测试数据 | 操作步骤 | 预期结果 | 数据校验 | 权限校验 | 验收标准 | 需求依据 | 原型依据 | 用例状态\nTC-PROTO-0001 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看测评需求审核卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在Amazon 运营提交的测评需求模拟数据。 | 卡片=测评需求审核;指标=申请 18 / 已批 8;状态=正常;目标页面=需求中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“测评需求审核”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入需求中心待审核入口”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“测评需求审核”和“申请 18 / 已批 8”,状态为“正常”。\n2. 点击后进入或打开与“需求中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对测评需求审核的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留需求中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:测评需求审核;状态:正常;操作:点击卡片进入需求中心待审核入口 | 待执行\nTC-PROTO-0002 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 多渠道触达引擎 | 功能测试 | 管理员工作台查看渠道推送风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在退订率高于基线的推送风险模拟数据。 | 卡片=渠道推送风险;指标=IM、EDM、TEL、App Push 日周月风险与反馈;状态=偏高;目标页面=推送中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“渠道推送风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入推送风险复核”。\n5. 返回首页后再次查看该卡片是否 | 1. 卡片展示“渠道推送风险”和“IM、EDM、TEL、App Push 日周月风险与反馈”,状态为“偏高”。\n2. 点击后进入或打开与“推送中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对渠道推送风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留推送中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:渠道推送风险;状态:偏高;操作:点击卡片进入推送风险复核 | 待执行\nTC-PROTO-0003 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看新增诈骗事件卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在诈骗同步与黑名单待同步事件模拟数据。 | 卡片=新增诈骗事件;指标=昨 5 / 周 18;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“新增诈骗事件”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入风险中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“新增诈骗事件”和“昨 5 / 周 18”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对新增诈骗事件的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:新增诈骗事件;状态:需复核;操作:点击卡片进入风险中心 | 待执行\nTC-PROTO-0004 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看紧急 Listing卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在评分接近 4.2 的 Listing模拟数据。 | 卡片=紧急 Listing;指标=新 3 / 未处理 7;状态=紧急;目标页面=Listing 管理 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“紧急 Listing”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入紧急 Listing 策略”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“紧急 Listing”和“新 3 / 未处理 7”,状态为“紧急”。\n2. 点击后进入或打开与“Listing 管理”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对紧急 Listing的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留Listing 管理语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:紧急 Listing;状态:紧急;操作:点击卡片进入紧急 Listing 策略 | 待执行\nTC-PROTO-0005 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 需求与计划管理 | 功能测试 | 管理员工作台查看推广计划与紧急策略卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已确认需求生成的推广计划模拟数据。 | 卡片=推广计划与紧急策略;指标=日 12 / 周 38;状态=注意审核积压;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“推广计划与紧急策略”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入计划中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“推广计划与紧急策略”和“日 12 / 周 38”,状态为“注意审核积压”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对推广计划与紧急策略的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:推广计划与紧急策略;状态:注意审核积压;操作:点击卡片进入计划中心 | 待执行\nTC-PROTO-0006 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 评价结果追踪 | 功能测试 | 管理员工作台查看评价产出趋势卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在真实消费者回评完成趋势模拟数据。 | 卡片=评价产出趋势;指标=日 18 / 周 96;状态=稳定;目标页面=数据中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“评价产出趋势”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入评价产出趋势”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“评价产出趋势”和“日 18 / 周 96”,状态为“稳定”。\n2. 点击后进入或打开与“数据中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对评价产出趋势的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留数据中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:评价产出趋势;状态:稳定;操作:点击卡片进入评价产出趋势 | 待执行\nTC-PROTO-0007 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 风险与反欺诈 | 功能测试 | 管理员工作台查看黑名单同步严重度卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在黑名单系统接口超时模拟数据。 | 卡片=黑名单同步严重度;指标=失败 2 / 高危 1;状态=需复核;目标页面=风险中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“黑名单同步严重度”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入黑名单同步”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“黑名单同步严重度”和“失败 2 / 高危 1”,状态为“需复核”。\n2. 点击后进入或打开与“风险中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对黑名单同步严重度的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留风险中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:黑名单同步严重度;状态:需复核;操作:点击卡片进入黑名单同步 | 待执行\nTC-PROTO-0008 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | KOC/KOL协作 | 功能测试 | 管理员工作台查看KOC/KOL 对接卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在PR 对外联系、价格、CODE、返点和提款进度模拟数据。 | 卡片=KOC/KOL 对接;指标=2 个逾期;状态=逾期;目标页面=计划中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“KOC/KOL 对接”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入对外合作跟进”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“KOC/KOL 对接”和“2 个逾期”,状态为“逾期”。\n2. 点击后进入或打开与“计划中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对KOC/KOL 对接的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留计划中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:KOC/KOL 对接;状态:逾期;操作:点击卡片进入对外合作跟进 | 待执行\nTC-PROTO-0009 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 客服工单与管理 | 功能测试 | 管理员工作台查看菲律宾团队管理卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在菲律宾团队工作时长、请假、缺席、人均产出模拟数据。 | 卡片=菲律宾团队管理;指标=风险 2 / 缺口 1;状态=排班风险;目标页面=客服中心 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“菲律宾团队管理”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入客服中心”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“菲律宾团队管理”和“风险 2 / 缺口 1”,状态为“排班风险”。\n2. 点击后进入或打开与“客服中心”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对菲律宾团队管理的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留客服中心语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:菲律宾团队管理;状态:排班风险;操作:点击卡片进入客服中心 | 待执行\nTC-PROTO-0010 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-核心KPI卡片 | 审计与通知中心 | 功能测试 | 管理员工作台查看审核积压与风险卡片并跳转处理页 | P1 | 系统管理员账号已登录;首页默认展示固定待办提醒和核心看板;存在已发现问题汇总到总页面模拟数据。 | 卡片=审核积压与风险;指标=卡点 4;状态=影响进度;目标页面=工作台 | 1. 打开管理员首页原型。\n2. 在核心看板区定位“审核积压与风险”卡片,核对卡片标题、指标和状态标签。\n3. 将鼠标移入卡片,观察是否有可点击反馈。\n4. 执行“点击卡片进入处理卡点”。\n5. 返回首页后再次查看该卡片是否保持原指标展示。 | 1. 卡片展示“审核积压与风险”和“卡点 4”,状态为“影响进度”。\n2. 点击后进入或打开与“工作台”相关的列表/处理区域。\n3. 返回首页后核心指标未丢失,仍可继续查看固定待办。 | 核对审核积压与风险的日/周/月指标、风险状态与页面内模拟数据一致;跳转后筛选上下文应保留工作台语义。 | 仅系统管理员、负责人/总监可查看全部部门指标;普通客服不得看到跨部门风险汇总和黑名单严重度。 | 卡片内容准确、可点击、跳转目标正确;敏感指标对无权限角色不可见。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 核心看板:审核积压与风险;状态:影响进度;操作:点击卡片进入处理卡点 | 待执行\nTC-PROTO-0011 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对测评需求执行接收动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“测评需求”;当前环节为“Amazon 已批准”,负责人为“用户运营负责人”。 | 事项=测评需求;来源=飞书需求表单 DEMO-001;截止=今日 18:00;处理动作=接收;描述=评分 4.46,低于 4.5,需要生成用户互动与真实评价跟踪计划 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“测评需求”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“接收”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:测评需求已核对”。\n7. 点击确认提交。 | 1. 队列行展示测评需求、Amazon 已批准、用户运营负责人、今日 18:00。\n2. 详情弹窗打开,展示来源“飞书需求表单 DEMO-001”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:测评需求;操作:接收 | 待执行\nTC-PROTO-0012 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 测评需求处理意见为空时阻止提交 | P2 | 已打开“测评需求”详情弹窗;当前用户有“接收”权限。 | 动作类型=通过/确认;处理意见=空;事项=测评需求 | 1. 在“测评需求”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0013 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对昨日推送风险执行复核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“昨日推送风险”;当前环节为“待复核”,负责人为“用户运营组长”。 | 事项=昨日推送风险;来源=推送风险自动单 DEMO-006;截止=今日 12:00;处理动作=复核;描述=昨日推送退订率高于基线,需复核人群、素材和文案 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“昨日推送风险”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“复核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:昨日推送风险已核对”。\n7. 点击确认提交。 | 1. 队列行展示昨日推送风险、待复核、用户运营组长、今日 12:00。\n2. 详情弹窗打开,展示来源“推送风险自动单 DEMO-006”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:昨日推送风险;操作:复核 | 待执行\nTC-PROTO-0014 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 昨日推送风险处理意见为空时阻止提交 | P2 | 已打开“昨日推送风险”详情弹窗;当前用户有“复核”权限。 | 动作类型=通过/确认;处理意见=空;事项=昨日推送风险 | 1. 在“昨日推送风险”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0015 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对待同步黑名单执行审核动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“待同步黑名单”;当前环节为“待审核”,负责人为“风险负责人”。 | 事项=待同步黑名单;来源=客服升级表单 DEMO-003;截止=今日 14:00;处理动作=审核;描述=同一 JOYHUB ID 与多个 Profile ID 关联异常样品申请,邮箱和设备号已脱敏 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“待同步黑名单”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审核”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:待同步黑名单已核对”。\n7. 点击确认提交。 | 1. 队列行展示待同步黑名单、待审核、风险负责人、今日 14:00。\n2. 详情弹窗打开,展示来源“客服升级表单 DEMO-003”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:待同步黑名单;操作:审核 | 待执行\nTC-PROTO-0016 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 待同步黑名单处理意见为空时阻止提交 | P2 | 已打开“待同步黑名单”详情弹窗;当前用户有“审核”权限。 | 动作类型=通过/确认;处理意见=空;事项=待同步黑名单 | 1. 在“待同步黑名单”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0017 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对紧急策略审批执行审批动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“紧急策略审批”;当前环节为“待系统管理员确认”,负责人为“Amazon 运营总监”。 | 事项=紧急策略审批;来源=紧急 Listing 表单 DEMO-004;截止=今日 11:30;处理动作=审批;描述=当前评分 4.21,接近 4.2 紧急阈值,需要 Amazon 与用户运营联合策略 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“紧急策略审批”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“审批”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:紧急策略审批已核对”。\n7. 点击确认提交。 | 1. 队列行展示紧急策略审批、待系统管理员确认、Amazon 运营总监、今日 11:30。\n2. 详情弹窗打开,展示来源“紧急 Listing 表单 DEMO-004”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:紧急策略审批;操作:审批 | 待执行\nTC-PROTO-0018 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 紧急策略审批处理意见为空时阻止提交 | P2 | 已打开“紧急策略审批”详情弹窗;当前用户有“审批”权限。 | 动作类型=通过/确认;处理意见=空;事项=紧急策略审批 | 1. 在“紧急策略审批”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0019 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 流程测试 | 处理队列中对差评跟进执行分配动作 | P1 | 系统管理员登录;P0/P1 处理队列存在事项“差评跟进”;当前环节为“客服升级”,负责人为“客服负责人”。 | 事项=差评跟进;来源=飞书客服需求 DEMO-005;截止=明日 10:00;处理动作=分配;描述=用户反馈产品说明理解偏差,需要客服跟进并回传产品改进建议 | 1. 进入工作台的“P0/P1 处理队列”。\n2. 在队列中按事项名称查找“差评跟进”。\n3. 核对对象说明、当前环节、负责人、截止时间和风险描述。\n4. 点击该行右侧“分配”。\n5. 在详情弹窗查看状态流转记录和脱敏与审计说明。\n6. 在操作确认区选择“通过 / 确认”,填写处理意见“测试通过:差评跟进已核对”。\n7. 点击确认提交。 | 1. 队列行展示差评跟进、客服升级、客服负责人、明日 10:00。\n2. 详情弹窗打开,展示来源“飞书客服需求 DEMO-005”及状态流转记录。\n3. 提交后该事项从待处理状态更新为已确认或流转到下一负责人。\n4. 页面出现成功反馈,通知/审计记录新增一条处理日志。 | 校验事项ID、来源表单、负责人、截止时间、处理意见、动作类型均写入状态流转记录;处理前后队列统计同步变化。 | 只有系统管理员或当前负责人可提交确认;非负责人只能查看,不能操作审批/分配。 | 队列事项可定位、详情可追溯、操作后状态变化清晰,且动作留痕。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列:差评跟进;操作:分配 | 待执行\nTC-PROTO-0020 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 异常场景 | 差评跟进处理意见为空时阻止提交 | P2 | 已打开“差评跟进”详情弹窗;当前用户有“分配”权限。 | 动作类型=通过/确认;处理意见=空;事项=差评跟进 | 1. 在“差评跟进”详情弹窗点击“审批/确认”。\n2. 选择动作类型“通过 / 确认”。\n3. 清空处理意见文本框。\n4. 点击确认按钮提交。 | 1. 系统阻止提交。\n2. 处理意见输入框出现必填提示。\n3. 事项状态不改变,状态流转记录不新增确认日志。\n4. 弹窗保持打开,用户可补充意见后重新提交。 | 确认数据库或前端状态中该事项仍保持原当前环节;无空意见审计记录。 | 有权限用户也必须填写处理意见;无权限用户不显示确认按钮。 | 必填校验生效,不产生错误状态流转。 | 09-审计与通知中心;README 权限要求 | 操作确认弹窗:处理意见 textarea;动作类型:通过/确认 | 待执行\nTC-PROTO-0021 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入需求中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“需求中心”;模拟数据已加载。 | 页面=需求中心;主按钮=Amazon 提交测评需求;辅助按钮=待审核入口;字段=需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 1. 在管理员首页左侧导航点击“需求中心”。\n2. 观察页面标题是否切换为“需求中心列表”。\n3. 检查列表表头是否包含:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作。\n4. 点击页面主按钮“Amazon 提交测评需求”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“待审核入口”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“需求中心”。\n2. 列表字段与原型定义一致。\n3. “Amazon 提交测评需求”和“待审核入口”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问需求中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合需求中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:需求中心;按钮:Amazon 提交测评需求/待审核入口;字段:需求ID、类型、提交人、审核人、审核结果、来源表单、ASIN/站点、当前环节、负责人、风险、截止、操作 | 待执行\nTC-PROTO-0022 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入Listing 管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“Listing 管理”;模拟数据已加载。 | 页面=Listing 管理;主按钮=创建紧急策略;辅助按钮=更多;字段=站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 1. 在管理员首页左侧导航点击“Listing 管理”。\n2. 观察页面标题是否切换为“Listing 管理列表”。\n3. 检查列表表头是否包含:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度。\n4. 点击页面主按钮“创建紧急策略”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“更多”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“Listing 管理”。\n2. 列表字段与原型定义一致。\n3. “创建紧急策略”和“更多”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问Listing 管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合Listing 管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:Listing 管理;按钮:创建紧急策略/更多;字段:站点组合、评分、等级、评价数、差评数、健康状态、责任人、问题所在、参与人员/进度 | 待执行\nTC-PROTO-0023 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入计划中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“计划中心”;模拟数据已加载。 | 页面=计划中心;主按钮=生成计划;辅助按钮=批量审批;字段=计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 1. 在管理员首页左侧导航点击“计划中心”。\n2. 观察页面标题是否切换为“计划中心列表”。\n3. 检查列表表头是否包含:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人。\n4. 点击页面主按钮“生成计划”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“批量审批”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“计划中心”。\n2. 列表字段与原型定义一致。\n3. “生成计划”和“批量审批”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问计划中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合计划中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:计划中心;按钮:生成计划/批量审批;字段:计划ID、关联需求、覆盖状态、资源分配、目标量、状态、审批人 | 待执行\nTC-PROTO-0024 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入推送中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“推送中心”;模拟数据已加载。 | 页面=推送中心;主按钮=计划与推送分配;辅助按钮=风险复核;字段=推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 1. 在管理员首页左侧导航点击“推送中心”。\n2. 观察页面标题是否切换为“推送中心列表”。\n3. 检查列表表头是否包含:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订。\n4. 点击页面主按钮“计划与推送分配”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“风险复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“推送中心”。\n2. 列表字段与原型定义一致。\n3. “计划与推送分配”和“风险复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问推送中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合推送中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:推送中心;按钮:计划与推送分配/风险复核;字段:推送ID、计划、渠道、策略、H5/素材、人群、发送、点击、回复、退订 | 待执行\nTC-PROTO-0025 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入客服中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“客服中心”;模拟数据已加载。 | 页面=客服中心;主按钮=分配工单;辅助按钮=流转;字段=工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 1. 在管理员首页左侧导航点击“客服中心”。\n2. 观察页面标题是否切换为“客服中心列表”。\n3. 检查列表表头是否包含:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出。\n4. 点击页面主按钮“分配工单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“流转”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“客服中心”。\n2. 列表字段与原型定义一致。\n3. “分配工单”和“流转”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问客服中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合客服中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:客服中心;按钮:分配工单/流转;字段:工单ID、用户摘要、平均响应、工作时长、出勤、人均产出 | 待执行\nTC-PROTO-0026 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入风险中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“风险中心”;模拟数据已加载。 | 页面=风险中心;主按钮=同步黑名单;辅助按钮=规则复核;字段=事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“风险中心”。\n2. 观察页面标题是否切换为“风险中心列表”。\n3. 检查列表表头是否包含:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“同步黑名单”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“规则复核”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“风险中心”。\n2. 列表字段与原型定义一致。\n3. “同步黑名单”和“规则复核”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问风险中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合风险中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:风险中心;按钮:同步黑名单/规则复核;字段:事件ID、主体摘要、关联字段、来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0027 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 数据中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入数据中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“数据中心”;模拟数据已加载。 | 页面=数据中心;主按钮=立即同步;辅助按钮=导出;字段=来源、同步频率、最近同步、记录数 | 1. 在管理员首页左侧导航点击“数据中心”。\n2. 观察页面标题是否切换为“数据中心列表”。\n3. 检查列表表头是否包含:来源、同步频率、最近同步、记录数。\n4. 点击页面主按钮“立即同步”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“导出”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“数据中心”。\n2. 列表字段与原型定义一致。\n3. “立即同步”和“导出”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问数据中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合数据中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:数据中心;按钮:立即同步/导出;字段:来源、同步频率、最近同步、记录数 | 待执行\nTC-PROTO-0028 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入报表中心并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“报表中心”;模拟数据已加载。 | 页面=报表中心;主按钮=生成/下载报表;辅助按钮=上传记录;字段=报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 1. 在管理员首页左侧导航点击“报表中心”。\n2. 观察页面标题是否切换为“报表中心列表”。\n3. 检查列表表头是否包含:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏。\n4. 点击页面主按钮“生成/下载报表”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“上传记录”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“报表中心”。\n2. 列表字段与原型定义一致。\n3. “生成/下载报表”和“上传记录”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问报表中心全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合报表中心页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:报表中心;按钮:生成/下载报表/上传记录;字段:报表ID、报表名称、可见角色、周期、生成计划、上传/记录、可导出、脱敏 | 待执行\nTC-PROTO-0029 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统管理/页面导航 | 功能测试 | 从左侧导航进入系统管理并校验列表字段 | P1 | 系统管理员已登录;左侧导航包含“系统管理”;模拟数据已加载。 | 页面=系统管理;主按钮=新建账号;辅助按钮=离职管理;字段=配置ID、模块、说明、权限分配、审计日志 | 1. 在管理员首页左侧导航点击“系统管理”。\n2. 观察页面标题是否切换为“系统管理列表”。\n3. 检查列表表头是否包含:配置ID、模块、说明、权限分配、审计日志。\n4. 点击页面主按钮“新建账号”,观察是否打开对应创建/处理入口。\n5. 关闭入口后点击“离职管理”,观察是否进入辅助操作。\n6. 点击“导出”或“流转”按钮,确认对应操作入口可用。 | 1. 当前模块高亮切换到“系统管理”。\n2. 列表字段与原型定义一致。\n3. “新建账号”和“离职管理”按钮可点击,打开的弹窗/区域与页面业务一致。\n4. 返回列表后筛选条件不丢失。 | 列表数据字段顺序、状态标签、负责人、风险和操作列与模拟数据一致;导出时仅导出当前筛选结果。 | 系统管理员可访问系统管理全部数据;部门负责人仅可访问本部门/站点范围;普通角色无权限进入系统级配置。 | 导航、字段、按钮、筛选和导出符合系统管理页面定位。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | 左侧导航:系统管理;按钮:新建账号/离职管理;字段:配置ID、模块、说明、权限分配、审计日志 | 待执行\nTC-PROTO-0030 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 7 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 7 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 7 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 7 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0031 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换最近 30 天时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=最近 30 天;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“最近 30 天”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“最近 30 天”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0032 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换本月时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=本月;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“本月”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“本月”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0033 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-时间范围与周期切换 | 数据中心 | 数据校验 | 管理员首页切换自定义时间范围后刷新趋势指标 | P2 | 系统管理员停留在工作台;核心看板、经营主题矩阵、业务复盘趋势均已展示。 | 时间范围=自定义;涉及指标=审核卡点、未处理紧急、评价产出趋势、黑名单同步严重度 | 1. 在首页顶部时间范围控件选择“自定义”。\n2. 如果选择自定义,则输入开始日期 2026-05-01、结束日期 2026-05-07。\n3. 点击查询或等待页面自动刷新。\n4. 对比核心看板、经营主题矩阵、业务复盘趋势中的日/周/月数值。\n5. 切换到其他模块再返回首页。 | 1. 页面按“自定义”刷新相关趋势指标。\n2. 周/月预生成提示仍显示,不影响实时入口。\n3. 切换模块再返回后,时间范围保持用户最后一次选择。 | 日/周/月聚合口径一致;自定义范围不能出现结束日期早于开始日期的数据。 | 普通客服仅能查看与本人相关指标;系统管理员可查看全部部门趋势。 | 时间筛选可用,趋势数据与范围一致,筛选状态可保持。 | 00-系统总览;evaluation-business-architecture 数据看板 | 时间范围;周期切换;周/月预生成 | 待执行\nTC-PROTO-0034 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面用户管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“用户管理”。 | 现有页面=用户中心 / 用户;字段=JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍;查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;MVP用途=用户画像筛选、推送人群、客服定位、风险排查 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“用户管理”。\n3. 核对现有页面名称是否为“用户中心 / 用户”。\n4. 展开字段详情,逐项核对字段:JOYHUB 用户ID、用户名、头像、注册时间、最近活跃时间、用户身份、标签、邮箱后缀、主页背景图、自我介绍。\n5. 在查询条件区按“搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “用户管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:用户管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0035 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 异常场景 | 用户管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“用户管理”关系页面;用户有查询权限。 | 查询条件=搜索字段、时间类型、标签、性别、国家、产品数、活动数、EDM近7天、渠道、身份;输入值=不存在的标签/身份/国家组合 | 1. 在“用户管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0036 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面公域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-用户标签”。 | 现有页面=标签 / 公域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态;查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;MVP用途=公域人群圈选、覆盖人数评估、推送前过滤 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 公域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、备注、状态。\n5. 在查询条件区按“搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0037 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 异常场景 | 公域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-用户标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、标签分类、覆盖用户数量、打标方式、时间类型、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0038 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 功能测试 | 现有ERP页面公域-产品标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“公域-产品标签”。 | 现有页面=标签 / 公域产品;字段=标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人;查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;MVP用途=产品分层、Listing 健康策略、产品绑定率分析 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“公域-产品标签”。\n3. 核对现有页面名称是否为“标签 / 公域产品”。\n4. 展开字段详情,逐项核对字段:标签ID、标签名称、产品、标签覆盖产品数量、备注、创建时间、创建人。\n5. 在查询条件区按“搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “公域-产品标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:公域-产品标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0039 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 需求与计划管理 | 异常场景 | 公域-产品标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“公域-产品标签”关系页面;用户有查询权限。 | 查询条件=搜索字段、搜索关键词、覆盖产品数量、创建标签时间、开始/截止时间;输入值=不存在的标签/身份/国家组合 | 1. 在“公域-产品标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0040 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP页面私域-用户标签字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“私域-用户标签”。 | 现有页面=标签 / 私域用户;字段=ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态;查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;MVP用途=私域精细运营、客服分组、活动复盘、风险用户隔离 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“私域-用户标签”。\n3. 核对现有页面名称是否为“标签 / 私域用户”。\n4. 展开字段详情,逐项核对字段:ID、标签编号、标签名称、标签分类、打标方式、标签覆盖人数、最新打标时间、状态。\n5. 在查询条件区按“标签分类、打标方式、覆盖人数、状态、时间范围”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “私域-用户标签”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:私域-用户标签;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0041 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 异常场景 | 私域-用户标签查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“私域-用户标签”关系页面;用户有查询权限。 | 查询条件=标签分类、打标方式、覆盖人数、状态、时间范围;输入值=不存在的标签/身份/国家组合 | 1. 在“私域-用户标签”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0042 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP页面身份管理字段展示与MVP纳入方式校验 | P1 | 系统管理员打开 v10 原型;当前模块切换到“现有ERP”;存在现有页面“身份管理”。 | 现有页面=身份;字段=账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作;查询条件=身份名称、身份分组、状态、更新时间;MVP用途=识别官方、品牌、达人、风险、客服等用户身份 | 1. 点击一级模块“现有ERP”。\n2. 在现有页面与当前字段区域定位“身份管理”。\n3. 核对现有页面名称是否为“身份”。\n4. 展开字段详情,逐项核对字段:账号身份、图标PNG(英)、图标PNG(德)、图标PNG(日)、操作。\n5. 在查询条件区按“身份名称、身份分组、状态、更新时间”组合输入筛选值。\n6. 点击“生成字段表”。\n7. 点击“导出现有关系”。 | 1. “身份管理”显示在现有ERP字段关系区域。\n2. 字段、查询条件、关系对象、MVP纳入方式均能展示。\n3. 生成字段表后可看到新增字段明细。\n4. 导出关系时文件只包含当前模块关系,不混入无关模块。 | 字段表包含页面ID、现有页面、模块、现有表格字段、现有查询条件、关系对象、MVP纳入方式;导出记录写入审计。 | 只有系统管理员/负责人可生成字段表和导出现有关系;普通客服不可导出现有ERP字段。 | 现有ERP字段能作为MVP建模依据,字段关系可查看、可导出、可审计。 | 01-用户身份与上下文;00-系统总览 数据所有权 | 现有ERP字段关系:身份管理;按钮:生成字段表、导出现有关系 | 待执行\nTC-PROTO-0043 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 异常场景 | 身份管理查询条件组合无结果时显示空状态 | P3 | 已进入现有ERP“身份管理”关系页面;用户有查询权限。 | 查询条件=身份名称、身份分组、状态、更新时间;输入值=不存在的标签/身份/国家组合 | 1. 在“身份管理”查询区域输入一个不存在的关键词,例如 ZZZ-NOT-FOUND。\n2. 选择一个与页面不匹配的状态或标签分类。\n3. 点击查询。\n4. 查看字段表、关系对象和MVP纳入方式区域。\n5. 点击重置。 | 1. 查询结果为空时页面显示暂无数据或空状态提示。\n2. 不应出现脚本错误、字段错位或沿用上一次结果。\n3. 点击重置后恢复默认数据。 | 空结果不生成脏数据;重置后查询条件和结果恢复默认。 | 用户只能查询有权限的数据范围;无权限字段应脱敏或不可见。 | 无结果场景可理解、可恢复,不污染已有字段关系。 | README 权限要求;00-系统总览 单一数据源 | 查询需求矩阵;筛选;重置 | 待执行\nTC-PROTO-0044 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 用户标签关系用于人群圈选、用户画像、风险过滤 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 用户标签;关系类型=多对多;用途=人群圈选、用户画像、风险过滤 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 用户标签”。\n4. 查看关系类型是否显示为“多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“人群圈选、用户画像、风险过滤”。 | 1. 关系“用户 - 用户标签”存在。\n2. 类型展示为“多对多”。\n3. 详情能说明该关系服务于“人群圈选、用户画像、风险过滤”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 用户标签 | 待执行\nTC-PROTO-0045 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 身份关系用于官方、品牌、达人、客服、风险身份识别 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 身份;关系类型=待确认:一对多或多对多;用途=官方、品牌、达人、客服、风险身份识别 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 身份”。\n4. 查看关系类型是否显示为“待确认:一对多或多对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“官方、品牌、达人、客服、风险身份识别”。 | 1. 关系“用户 - 身份”存在。\n2. 类型展示为“待确认:一对多或多对多”。\n3. 详情能说明该关系服务于“官方、品牌、达人、客服、风险身份识别”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 身份 | 待执行\nTC-PROTO-0046 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 产品关系用于测评用户池、客服定位 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 产品;关系类型=绑定/连接产品;用途=测评用户池、客服定位 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 产品”。\n4. 查看关系类型是否显示为“绑定/连接产品”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“测评用户池、客服定位”。 | 1. 关系“用户 - 产品”存在。\n2. 类型展示为“绑定/连接产品”。\n3. 详情能说明该关系服务于“测评用户池、客服定位”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 产品 | 待执行\nTC-PROTO-0047 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验产品 - 产品标签 - Listing关系用于Listing 健康策略、ASIN 归因 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=产品 - 产品标签 - Listing;关系类型=产品分层;用途=Listing 健康策略、ASIN 归因 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“产品 - 产品标签 - Listing”。\n4. 查看关系类型是否显示为“产品分层”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“Listing 健康策略、ASIN 归因”。 | 1. 关系“产品 - 产品标签 - Listing”存在。\n2. 类型展示为“产品分层”。\n3. 详情能说明该关系服务于“Listing 健康策略、ASIN 归因”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:产品 - 产品标签 - Listing | 待执行\nTC-PROTO-0048 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 活动关系用于KOC/KOL、私域运营沉淀 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 活动;关系类型=活动参与;用途=KOC/KOL、私域运营沉淀 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 活动”。\n4. 查看关系类型是否显示为“活动参与”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“KOC/KOL、私域运营沉淀”。 | 1. 关系“用户 - 活动”存在。\n2. 类型展示为“活动参与”。\n3. 详情能说明该关系服务于“KOC/KOL、私域运营沉淀”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 活动 | 待执行\nTC-PROTO-0049 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 推送关系用于IM/EDM/TEL/App Push 频控、点击、回复、退订 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 推送;关系类型=一对多;用途=IM/EDM/TEL/App Push 频控、点击、回复、退订 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 推送”。\n4. 查看关系类型是否显示为“一对多”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“IM/EDM/TEL/App Push 频控、点击、回复、退订”。 | 1. 关系“用户 - 推送”存在。\n2. 类型展示为“一对多”。\n3. 详情能说明该关系服务于“IM/EDM/TEL/App Push 频控、点击、回复、退订”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 推送 | 待执行\nTC-PROTO-0050 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-关系图谱 | 用户身份与上下文 | 数据校验 | 关系图谱校验用户 - 风险/黑名单关系用于诈骗同步、客服升级、风险用户隔离 | P2 | 系统管理员进入 v10 原型的现有ERP模块;关系图谱区域已展示。 | 关系=用户 - 风险/黑名单;关系类型=风险隔离;用途=诈骗同步、客服升级、风险用户隔离 | 1. 进入“现有ERP”。\n2. 滚动到“关系图谱”区域。\n3. 定位关系“用户 - 风险/黑名单”。\n4. 查看关系类型是否显示为“风险隔离”。\n5. 点击该关系行的查看/展开操作。\n6. 检查弹出的关系详情中是否说明“诈骗同步、客服升级、风险用户隔离”。 | 1. 关系“用户 - 风险/黑名单”存在。\n2. 类型展示为“风险隔离”。\n3. 详情能说明该关系服务于“诈骗同步、客服升级、风险用户隔离”。\n4. 关闭详情后返回关系图谱不丢失当前位置。 | 关系对象、类型、用途和来源页面字段一致;待确认关系必须标注待确认,不能当作已定规则。 | 只有系统管理员/负责人能查看完整关系;普通运营只看业务可用摘要。 | 关系图谱可支撑用户画像、人群圈选、风险过滤和触达频控建模。 | 00-系统总览 子系统数据所有权;01-用户身份与上下文 | 关系图谱:用户 - 风险/黑名单 | 待执行\nTC-PROTO-0051 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行用户主档查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=用户主档查询;查询条件=JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间;预期输出=用户主档、标签、身份、产品关系、近期活跃;使用页面=用户中心 / 客服中心 / 风险中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“用户主档查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:JOYHUB ID、用户名、邮箱后缀、国家、性别、注册/活跃时间。\n5. 点击查询。\n6. 查看结果区是否输出:用户主档、标签、身份、产品关系、近期活跃。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “用户主档查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“用户主档、标签、身份、产品关系、近期活跃”。\n4. 跳转到“用户中心 / 客服中心 / 风险中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:用户主档查询 | 待执行\nTC-PROTO-0052 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行推送前人群圈选并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=推送前人群圈选;查询条件=标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数;预期输出=候选用户数、预计触达、频控风险、可保存人群包;使用页面=计划中心 / 推送中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“推送前人群圈选”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签、身份、国家、渠道、产品绑定/连接、活动、EDM近7天次数。\n5. 点击查询。\n6. 查看结果区是否输出:候选用户数、预计触达、频控风险、可保存人群包。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “推送前人群圈选”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“候选用户数、预计触达、频控风险、可保存人群包”。\n4. 跳转到“计划中心 / 推送中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:推送前人群圈选 | 待执行\nTC-PROTO-0053 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行测评与真实评价跟踪人群并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=测评与真实评价跟踪人群;查询条件=ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除;预期输出=推荐用户池、排除用户池、进入计划中心;使用页面=需求中心 / 计划中心 / 客服中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“测评与真实评价跟踪人群”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:ASIN/Listing、产品绑定、连接产品、最近活跃、国家/站点、风险身份排除。\n5. 点击查询。\n6. 查看结果区是否输出:推荐用户池、排除用户池、进入计划中心。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “测评与真实评价跟踪人群”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“推荐用户池、排除用户池、进入计划中心”。\n4. 跳转到“需求中心 / 计划中心 / 客服中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:测评与真实评价跟踪人群 | 待执行\nTC-PROTO-0054 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行标签覆盖查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=标签覆盖查询;查询条件=标签分类、打标方式、覆盖人数、最新打标时间、状态;预期输出=标签列表、覆盖趋势、异常覆盖提示;使用页面=现有ERP / 数据中心 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“标签覆盖查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:标签分类、打标方式、覆盖人数、最新打标时间、状态。\n5. 点击查询。\n6. 查看结果区是否输出:标签列表、覆盖趋势、异常覆盖提示。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “标签覆盖查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“标签列表、覆盖趋势、异常覆盖提示”。\n4. 跳转到“现有ERP / 数据中心”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:标签覆盖查询 | 待执行\nTC-PROTO-0055 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-查询需求矩阵 | 用户身份与上下文 | 流程测试 | 查询需求矩阵执行身份风险查询并校验输出用途 | P1 | 系统管理员或用户运营负责人打开 v10 原型;现有ERP模块的查询需求矩阵已展示。 | 查询场景=身份风险查询;查询条件=身份名称、身份分组、风险等级、状态;预期输出=风险用户池、客服/推送排除名单、黑名单同步候选;使用页面=风险中心 / 系统管理 | 1. 进入“现有ERP”模块。\n2. 在“查询需求矩阵”中定位“身份风险查询”。\n3. 点击该查询场景的查看/进入处理按钮。\n4. 按原型列出的查询条件输入:身份名称、身份分组、风险等级、状态。\n5. 点击查询。\n6. 查看结果区是否输出:风险用户池、客服/推送排除名单、黑名单同步候选。\n7. 点击“保存人群包”或“进入计划中心/风险中心”相关入口。 | 1. “身份风险查询”可从矩阵进入。\n2. 查询条件与原型字段一致。\n3. 结果输出符合“风险用户池、客服/推送排除名单、黑名单同步候选”。\n4. 跳转到“风险中心 / 系统管理”时携带当前查询上下文。 | 候选用户数、排除用户池、风险身份、EDM近7天次数等统计与筛选条件一致;保存人群包生成唯一ID。 | 客服只能查看客服定位所需字段;风险负责人可查看风险身份;系统管理员可查看完整字段,邮箱/设备仍默认脱敏。 | 查询矩阵能从现有ERP字段转化为MVP业务查询能力。 | 01-用户身份与上下文;03-额度与频控;04-多渠道触达 | 查询需求矩阵:身份风险查询 | 待执行\nTC-PROTO-0056 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行新建账号并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“新建账号”。 | 动作=新建账号;说明=按部门、角色、站点、数据范围开通账号;预期=账号创建后出现在系统管理列表 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“新建账号”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:按部门、角色、站点、数据范围开通账号。\n6. 提交后进入审计日志,按动作类型筛选“新建账号”。 | 1. 有权限角色可完成“新建账号”。\n2. 执行结果符合“账号创建后出现在系统管理列表”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 新建账号仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:新建账号;权限分配;审计日志 | 待执行\nTC-PROTO-0057 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行离职管理并校验权限与审计 | P1 | 用户以系统管理员身份登录;系统资产模块可访问;系统管理列表包含“离职管理”。 | 动作=离职管理;说明=停用账号、交接任务、回收敏感权限;预期=离职账号不可登录且任务已交接 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“离职管理”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:停用账号、交接任务、回收敏感权限。\n6. 提交后进入审计日志,按动作类型筛选“离职管理”。 | 1. 有权限角色可完成“离职管理”。\n2. 执行结果符合“离职账号不可登录且任务已交接”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 离职管理仅对系统管理员开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:离职管理;权限分配;审计日志 | 待执行\nTC-PROTO-0058 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行权限分配并校验权限与审计 | P1 | 用户以系统管理员/负责人身份登录;系统资产模块可访问;系统管理列表包含“权限分配”。 | 动作=权限分配;说明=导出、审批、查看敏感信息、黑名单同步独立授权;预期=权限粒度按按钮和数据范围生效 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“权限分配”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、审批、查看敏感信息、黑名单同步独立授权。\n6. 提交后进入审计日志,按动作类型筛选“权限分配”。 | 1. 有权限角色可完成“权限分配”。\n2. 执行结果符合“权限粒度按按钮和数据范围生效”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 权限分配仅对系统管理员/负责人开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:权限分配;权限分配;审计日志 | 待执行\nTC-PROTO-0059 | user_erp_mvp_admin_prototype_v10(1).html | 系统资产-系统管理 | 审计与通知中心 | 权限校验 | 系统管理执行审计日志并校验权限与审计 | P1 | 用户以系统管理员/审计角色身份登录;系统资产模块可访问;系统管理列表包含“审计日志”。 | 动作=审计日志;说明=导出、查看敏感信息、黑名单同步、审批动作;预期=每个敏感动作有日志ID和操作者 | 1. 点击一级模块“系统资产”。\n2. 进入“系统管理”。\n3. 定位配置项“审计日志”。\n4. 点击对应操作按钮。\n5. 按页面说明执行:导出、查看敏感信息、黑名单同步、审批动作。\n6. 提交后进入审计日志,按动作类型筛选“审计日志”。 | 1. 有权限角色可完成“审计日志”。\n2. 执行结果符合“每个敏感动作有日志ID和操作者”。\n3. 审计日志新增记录,包含动作类型、操作者、时间、影响对象和处理意见。\n4. 无权限角色访问时按钮隐藏或提交被拒绝。 | 账号状态、权限点、任务交接关系和审计日志一致;敏感权限回收后立即生效。 | 审计日志仅对系统管理员/审计角色开放;查看敏感信息、导出、黑名单同步、审批动作需独立授权。 | 权限配置可执行、可追溯、可撤销;离职管理无遗留敏感权限。 | 09-审计与通知中心;00-系统总览 角色前端映射 | 系统资产:审计日志;权限分配;审计日志 | 待执行\nTC-PROTO-0060 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看在线客服数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=在线客服数;原型变量=onlineAgents 人;动作=查看当前可用客服池 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“在线客服数”。\n3. 核对指标值单位是否符合“onlineAgents 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“在线客服数”显示在客服执行看板。\n2. 点击后进入与“查看当前可用客服池”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:onlineAgents 人;标题:客服执行看板 | 待执行\nTC-PROTO-0061 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看今日工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日工单;原型变量=todayTickets 单;动作=查看今日进入客服中心的工单总量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日工单”。\n3. 核对指标值单位是否符合“todayTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日工单”显示在客服执行看板。\n2. 点击后进入与“查看今日进入客服中心的工单总量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0062 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看待处理工单指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=待处理工单;原型变量=pendingTickets 单;动作=查看未关闭/待处理队列 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“待处理工单”。\n3. 核对指标值单位是否符合“pendingTickets 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“待处理工单”显示在客服执行看板。\n2. 点击后进入与“查看未关闭/待处理队列”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:pendingTickets 单;标题:客服执行看板 | 待执行\nTC-PROTO-0063 | 客服执行.html | 客服执行看板 | 多渠道触达引擎 | 功能测试 | 客服执行看板查看今日消息指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日消息;原型变量=todayMessages 条;动作=查看客服发送和接收消息量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日消息”。\n3. 核对指标值单位是否符合“todayMessages 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日消息”显示在客服执行看板。\n2. 点击后进入与“查看客服发送和接收消息量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayMessages 条;标题:客服执行看板 | 待执行\nTC-PROTO-0064 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看今日获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=今日获取评价;原型变量=todayReviews 条;动作=查看今日客服转化评价数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“今日获取评价”。\n3. 核对指标值单位是否符合“todayReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“今日获取评价”显示在客服执行看板。\n2. 点击后进入与“查看今日客服转化评价数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:todayReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0065 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看本月获取评价指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=本月获取评价;原型变量=monthReviews 条;动作=查看月度评价产出 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“本月获取评价”。\n3. 核对指标值单位是否符合“monthReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“本月获取评价”显示在客服执行看板。\n2. 点击后进入与“查看月度评价产出”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:monthReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0066 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看排班到岗指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=排班到岗;原型变量=实际/应到 人;动作=查看排班、在线状态和出勤异常 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“排班到岗”。\n3. 核对指标值单位是否符合“实际/应到 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“排班到岗”显示在客服执行看板。\n2. 点击后进入与“查看排班、在线状态和出勤异常”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:实际/应到 人;标题:客服执行看板 | 待执行\nTC-PROTO-0067 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看迟到次数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=迟到次数;原型变量=lateDays 次;动作=查看迟到/早退/请假/缺勤 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“迟到次数”。\n3. 核对指标值单位是否符合“lateDays 次”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“迟到次数”显示在客服执行看板。\n2. 点击后进入与“查看迟到/早退/请假/缺勤”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:lateDays 次;标题:客服执行看板 | 待执行\nTC-PROTO-0068 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看回复用户数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=回复用户数;原型变量=repliedUsers 人;动作=查看回复覆盖用户数 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“回复用户数”。\n3. 核对指标值单位是否符合“repliedUsers 人”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“回复用户数”显示在客服执行看板。\n2. 点击后进入与“查看回复覆盖用户数”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:repliedUsers 人;标题:客服执行看板 | 待执行\nTC-PROTO-0069 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看处理工单数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=处理工单数;原型变量=ticketCount 单;动作=查看处理工单量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“处理工单数”。\n3. 核对指标值单位是否符合“ticketCount 单”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“处理工单数”显示在客服执行看板。\n2. 点击后进入与“查看处理工单量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:ticketCount 单;标题:客服执行看板 | 待执行\nTC-PROTO-0070 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看发送消息数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=发送消息数;原型变量=messageCount 条;动作=查看消息发送量 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“发送消息数”。\n3. 核对指标值单位是否符合“messageCount 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“发送消息数”显示在客服执行看板。\n2. 点击后进入与“查看消息发送量”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:messageCount 条;标题:客服执行看板 | 待执行\nTC-PROTO-0071 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看平均首次回复指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=平均首次回复;原型变量=averageFirstReply 分钟;动作=查看平均首次回复时长 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“平均首次回复”。\n3. 核对指标值单位是否符合“averageFirstReply 分钟”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“平均首次回复”显示在客服执行看板。\n2. 点击后进入与“查看平均首次回复时长”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:averageFirstReply 分钟;标题:客服执行看板 | 待执行\nTC-PROTO-0072 | 客服执行.html | 客服执行看板 | 评价结果追踪 | 功能测试 | 客服执行看板查看RSO/RDO评价数指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=RSO/RDO评价数;原型变量=rsoReviews+rdoReviews 条;动作=查看回评/测评转化 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“RSO/RDO评价数”。\n3. 核对指标值单位是否符合“rsoReviews+rdoReviews 条”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“RSO/RDO评价数”显示在客服执行看板。\n2. 点击后进入与“查看回评/测评转化”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:rsoReviews+rdoReviews 条;标题:客服执行看板 | 待执行\nTC-PROTO-0073 | 客服执行.html | 客服执行看板 | 客服工单与管理 | 功能测试 | 客服执行看板查看目标完成率指标并钻取明细 | P1 | 客服主管账号已登录客服执行看板;存在当天排班、工单、消息、评价和绩效数据。 | 指标=目标完成率;原型变量=totalRate;动作=查看目标完成进度 | 1. 打开“客服执行看板”。\n2. 在顶部指标区定位“目标完成率”。\n3. 核对指标值单位是否符合“totalRate”。\n4. 点击该指标卡片。\n5. 查看弹出的明细列表或右侧详情。\n6. 将时间范围切换到本月,再回到今日。 | 1. 指标“目标完成率”显示在客服执行看板。\n2. 点击后进入与“查看目标完成进度”一致的明细。\n3. 今日/本月切换后指标重新计算。\n4. 返回看板后指标卡片仍展示最新状态。 | 指标明细总数与卡片汇总一致;首次回复时长单位为分钟,评价数区分 RSO/RDO。 | 客服本人只能看本人指标;组长可看组内;主管/管理员可看团队汇总。 | 客服执行指标可见、可钻取、按周期统计准确。 | 05-客服工单与管理 绩效统计;07-评价结果追踪 | React 原型变量:totalRate;标题:客服执行看板 | 待执行\nTC-PROTO-0074 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户消息进入工单从待分配执行自动分配流转到已分配 | P1 | 客服系统存在来源为“用户消息进入”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=用户消息进入;起始状态=待分配;操作=自动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户消息进入”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“自动分配”:在线客服池按班次+在线状态+当前负载+最大工单数分配。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户消息进入”和当前状态“待分配”。\n2. 执行“自动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0075 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 推送转人工工单从待分配执行手动分配流转到已分配 | P1 | 客服系统存在来源为“推送转人工”的工单;当前状态为“待分配”;用户上下文卡可查询或降级展示。 | 来源=推送转人工;起始状态=待分配;操作=手动分配;目标状态=已分配 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“推送转人工”。\n3. 打开状态为“待分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“手动分配”:组长选择在线且未满载客服。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“推送转人工”和当前状态“待分配”。\n2. 执行“手动分配”后状态变为“已分配”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0076 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 售后触发工单从已分配执行首次回复流转到处理中 | P1 | 客服系统存在来源为“售后触发”的工单;当前状态为“已分配”;用户上下文卡可查询或降级展示。 | 来源=售后触发;起始状态=已分配;操作=首次回复;目标状态=处理中 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“售后触发”。\n3. 打开状态为“已分配”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“首次回复”:客服查看用户上下文后发送第一条回复。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“售后触发”和当前状态“已分配”。\n2. 执行“首次回复”后状态变为“处理中”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0077 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 风险触发工单从处理中执行标记疑似诈骗流转到疑似诈骗 | P1 | 客服系统存在来源为“风险触发”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=风险触发;起始状态=处理中;操作=标记疑似诈骗;目标状态=疑似诈骗 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“风险触发”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“标记疑似诈骗”:客服在工单处理结果中选择疑似诈骗。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“风险触发”和当前状态“处理中”。\n2. 执行“标记疑似诈骗”后状态变为“疑似诈骗”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0078 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 电话后续工单从处理中执行等待用户回复流转到等待用户 | P1 | 客服系统存在来源为“电话后续”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=电话后续;起始状态=处理中;操作=等待用户回复;目标状态=等待用户 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“电话后续”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“等待用户回复”:客服记录通话后等待用户补充订单号。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“电话后续”和当前状态“处理中”。\n2. 执行“等待用户回复”后状态变为“等待用户”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0079 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户答应配合工单从处理中执行创建跟进任务流转到待提醒 | P1 | 客服系统存在来源为“用户答应配合”的工单;当前状态为“处理中”;用户上下文卡可查询或降级展示。 | 来源=用户答应配合;起始状态=处理中;操作=创建跟进任务;目标状态=待提醒 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户答应配合”。\n3. 打开状态为“处理中”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“创建跟进任务”:客服将答应配合状态置为已答应并设置负责人。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户答应配合”和当前状态“处理中”。\n2. 执行“创建跟进任务”后状态变为“待提醒”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0080 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 等待提交工单从待提醒执行提醒用户流转到等待提交 | P1 | 客服系统存在来源为“等待提交”的工单;当前状态为“待提醒”;用户上下文卡可查询或降级展示。 | 来源=等待提交;起始状态=待提醒;操作=提醒用户;目标状态=等待提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“等待提交”。\n3. 打开状态为“待提醒”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“提醒用户”:到期前客服发送提醒消息。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“等待提交”和当前状态“待提醒”。\n2. 执行“提醒用户”后状态变为“等待提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0081 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 用户提交评价工单从等待提交执行登记提交事实流转到已提交 | P1 | 客服系统存在来源为“用户提交评价”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=用户提交评价;起始状态=等待提交;操作=登记提交事实;目标状态=已提交 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“用户提交评价”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“登记提交事实”:客服上传截图/链接并关联计划和ASIN。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“用户提交评价”和当前状态“等待提交”。\n2. 执行“登记提交事实”后状态变为“已提交”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0082 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 评价核验完成工单从已提交执行关闭工单流转到已关闭 | P1 | 客服系统存在来源为“评价核验完成”的工单;当前状态为“已提交”;用户上下文卡可查询或降级展示。 | 来源=评价核验完成;起始状态=已提交;操作=关闭工单;目标状态=已关闭 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“评价核验完成”。\n3. 打开状态为“已提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“关闭工单”:评价展示确认后客服将工单置为已解决并关闭。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“评价核验完成”和当前状态“已提交”。\n2. 执行“关闭工单”后状态变为“已关闭”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0083 | 客服执行.html | 客服工单生命周期 | 客服工单与管理 | 流程测试 | 超时未提交工单从等待提交执行需再次联系流转到需再次联系 | P1 | 客服系统存在来源为“超时未提交”的工单;当前状态为“等待提交”;用户上下文卡可查询或降级展示。 | 来源=超时未提交;起始状态=等待提交;操作=需再次联系;目标状态=需再次联系 | 1. 打开客服执行看板。\n2. 在工单列表按来源筛选“超时未提交”。\n3. 打开状态为“等待提交”的工单详情。\n4. 查看用户上下文卡、历史聊天、历史评价和风险提示。\n5. 执行业务动作“需再次联系”:超过答应配合期限后生成再次联系任务。\n6. 保存处理结果并返回工单列表。\n7. 再次筛选该工单,查看当前状态。 | 1. 工单详情展示来源“超时未提交”和当前状态“等待提交”。\n2. 执行“需再次联系”后状态变为“需再次联系”。\n3. 处理记录写入 support_followups 或 support_tickets。\n4. 工单列表、看板待处理数和绩效统计同步更新。 | support_tickets.status、support_followups.status、support_assignment_logs 或绩效快照按动作更新;状态不得跳过必要节点。 | 客服只能处理分配给自己的工单;组长可改派;风险相关动作需组长或风险负责人确认。 | 工单状态流转符合待分配→已分配→处理中→等待用户/等待内部→已解决/疑似诈骗→已关闭规则。 | 05-客服工单与管理 M1/M2/M3 | 客服执行.html:客服执行看板;工单/回复/评价/目标指标 | 待执行\nTC-PROTO-0084 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:自动分配无在线客服 | P2 | 客服执行看板可用;准备异常条件:所有排班客服均离线或满载。 | 异常=自动分配无在线客服;条件=所有排班客服均离线或满载 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:所有排班客服均离线或满载。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“自动分配无在线客服”。\n2. 处理结果为:工单留在公共池并提醒组长。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:自动分配无在线客服 | 待执行\nTC-PROTO-0085 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:分配给离线客服 | P2 | 客服执行看板可用;准备异常条件:客服在线状态在分配前变为离线。 | 异常=分配给离线客服;条件=客服在线状态在分配前变为离线 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服在线状态在分配前变为离线。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“分配给离线客服”。\n2. 处理结果为:阻止分配并要求重新选择。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:分配给离线客服 | 待执行\nTC-PROTO-0086 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:当前负载超过最大工单数 | P2 | 客服执行看板可用;准备异常条件:客服未关闭工单数达到上限。 | 异常=当前负载超过最大工单数;条件=客服未关闭工单数达到上限 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服未关闭工单数达到上限。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“当前负载超过最大工单数”。\n2. 处理结果为:自动跳过该客服。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:当前负载超过最大工单数 | 待执行\nTC-PROTO-0087 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:用户上下文卡查询失败 | P2 | 客服执行看板可用;准备异常条件:identity 服务超时。 | 异常=用户上下文卡查询失败;条件=identity 服务超时 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:identity 服务超时。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“用户上下文卡查询失败”。\n2. 处理结果为:工单可继续处理但显示上下文数据可能过期。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:用户上下文卡查询失败 | 待执行\nTC-PROTO-0088 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:首次回复为空 | P2 | 客服执行看板可用;准备异常条件:客服点击发送但消息内容为空。 | 异常=首次回复为空;条件=客服点击发送但消息内容为空 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:客服点击发送但消息内容为空。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“首次回复为空”。\n2. 处理结果为:阻止发送并提示请输入回复内容。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:首次回复为空 | 待执行\nTC-PROTO-0089 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:关闭工单未选择处理结果 | P2 | 客服执行看板可用;准备异常条件:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。 | 异常=关闭工单未选择处理结果;条件=点击关闭但未选择已解决/拒绝/疑似诈骗等结果 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:点击关闭但未选择已解决/拒绝/疑似诈骗等结果。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“关闭工单未选择处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:关闭工单未选择处理结果 | 待执行\nTC-PROTO-0090 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:登记评价缺少证据 | P2 | 客服执行看板可用;准备异常条件:用户声称已评价但未上传截图或链接。 | 异常=登记评价缺少证据;条件=用户声称已评价但未上传截图或链接 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:用户声称已评价但未上传截图或链接。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“登记评价缺少证据”。\n2. 处理结果为:不允许进入已提交状态。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:登记评价缺少证据 | 待执行\nTC-PROTO-0091 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:答应配合任务超期 | P2 | 客服执行看板可用;准备异常条件:deadline_at 已过且无提交记录。 | 异常=答应配合任务超期;条件=deadline_at 已过且无提交记录 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:deadline_at 已过且无提交记录。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“答应配合任务超期”。\n2. 处理结果为:状态变为超时并生成需再次联系。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:答应配合任务超期 | 待执行\nTC-PROTO-0092 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:风险状态确认诈骗 | P2 | 客服执行看板可用;准备异常条件:risk 返回确认诈骗。 | 异常=风险状态确认诈骗;条件=risk 返回确认诈骗 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:risk 返回确认诈骗。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“风险状态确认诈骗”。\n2. 处理结果为:工单自动或人工确认后关闭并同步黑名单候选。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:风险状态确认诈骗 | 待执行\nTC-PROTO-0093 | 客服执行.html | 客服异常处理 | 客服工单与管理 | 异常场景 | 客服执行异常场景:重复创建同用户打开工单 | P2 | 客服执行看板可用;准备异常条件:同 person_id 已存在 open 工单。 | 异常=重复创建同用户打开工单;条件=同 person_id 已存在 open 工单 | 1. 进入客服执行看板。\n2. 构造或选择满足条件的工单:同 person_id 已存在 open 工单。\n3. 按正常处理路径执行对应动作。\n4. 观察页面提示、工单状态和待办数量。\n5. 打开工单详情的处理记录。 | 1. 系统识别异常“重复创建同用户打开工单”。\n2. 处理结果为:新工单关联已有工单或提示合并。\n3. 不产生错误状态或重复工单。\n4. 异常处理记录可在工单详情中查看。 | 异常前后 support_tickets、support_followups、assignment_logs 数据一致;不应出现状态倒退或重复计数。 | 无权限客服不得绕过异常限制;组长/主管可进行改派、关闭、风险升级等授权动作。 | 异常被明确提示并进入可追踪处理路径,不影响其他工单。 | 05-客服工单与管理 业务澄清与状态规则 | 客服执行看板异常:重复创建同用户打开工单 | 待执行\nTC-PROTO-0094 | 用户运营系统-单文件.html | 工作台 | 系统总览 | UI/交互测试 | 单文件系统路由#/dashboard进入Dashboard页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有Dashboard访问权限。 | 路由=#/dashboard;页面=Dashboard;用途=查看经营指标、待办、风险、评价进度 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/dashboard”或从侧边菜单点击“Dashboard”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“Dashboard”页面。 | 1. 页面成功进入“Dashboard”。\n2. 当前菜单高亮,页面内容与“查看经营指标、待办、风险、评价进度”一致。\n3. 刷新后 hash 路由不丢失,仍展示“Dashboard”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备Dashboard访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/dashboard | 待执行\nTC-PROTO-0095 | 用户运营系统-单文件.html | 需求管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/demand进入需求中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有需求中心访问权限。 | 路由=#/demand;页面=需求中心;用途=创建/评估/驳回/转计划 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/demand”或从侧边菜单点击“需求中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“需求中心”页面。 | 1. 页面成功进入“需求中心”。\n2. 当前菜单高亮,页面内容与“创建/评估/驳回/转计划”一致。\n3. 刷新后 hash 路由不丢失,仍展示“需求中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备需求中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/demand | 待执行\nTC-PROTO-0096 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan/review进入计划审核页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划审核访问权限。 | 路由=#/plan/review;页面=计划审核;用途=提交审批、通过、驳回、待补充 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan/review”或从侧边菜单点击“计划审核”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划审核”页面。 | 1. 页面成功进入“计划审核”。\n2. 当前菜单高亮,页面内容与“提交审批、通过、驳回、待补充”一致。\n3. 刷新后 hash 路由不丢失,仍展示“计划审核”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划审核访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan/review | 待执行\nTC-PROTO-0097 | 用户运营系统-单文件.html | 计划管理 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/plan进入计划中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有计划中心访问权限。 | 路由=#/plan;页面=计划中心;用途=生成计划、拆分计划项、执行中/暂停/终止 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/plan”或从侧边菜单点击“计划中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“计划中心”页面。 | 1. 页面成功进入“计划中心”。\n2. 当前菜单高亮,页面内容与“生成计划、拆分计划项、执行中/暂停/终止”一致。\n3. 刷新后 hash 路由不丢失,仍展示“计划中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备计划中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/plan | 待执行\nTC-PROTO-0098 | 用户运营系统-单文件.html | Listing健康 | 需求与计划管理 | UI/交互测试 | 单文件系统路由#/asin进入ASIN/Listing页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有ASIN/Listing访问权限。 | 路由=#/asin;页面=ASIN/Listing;用途=查看评分、评价数、健康状态、紧急策略 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/asin”或从侧边菜单点击“ASIN/Listing”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“ASIN/Listing”页面。 | 1. 页面成功进入“ASIN/Listing”。\n2. 当前菜单高亮,页面内容与“查看评分、评价数、健康状态、紧急策略”一致。\n3. 刷新后 hash 路由不丢失,仍展示“ASIN/Listing”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备ASIN/Listing访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/asin | 待执行\nTC-PROTO-0099 | 用户运营系统-单文件.html | 用户上下文 | 用户身份与上下文 | UI/交互测试 | 单文件系统路由#/user进入用户中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有用户中心访问权限。 | 路由=#/user;页面=用户中心;用途=用户主档、标签、身份、产品、活动、触达历史 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/user”或从侧边菜单点击“用户中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“用户中心”页面。 | 1. 页面成功进入“用户中心”。\n2. 当前菜单高亮,页面内容与“用户主档、标签、身份、产品、活动、触达历史”一致。\n3. 刷新后 hash 路由不丢失,仍展示“用户中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备用户中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/user | 待执行\nTC-PROTO-0100 | 用户运营系统-单文件.html | 额度管理 | 额度与频控 | UI/交互测试 | 单文件系统路由#/quota进入额度频控页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有额度频控访问权限。 | 路由=#/quota;页面=额度频控;用途=额度查询、预占、确认、释放、终校 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/quota”或从侧边菜单点击“额度频控”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“额度频控”页面。 | 1. 页面成功进入“额度频控”。\n2. 当前菜单高亮,页面内容与“额度查询、预占、确认、释放、终校”一致。\n3. 刷新后 hash 路由不丢失,仍展示“额度频控”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备额度频控访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/quota | 待执行\nTC-PROTO-0101 | 用户运营系统-单文件.html | 多渠道触达 | 多渠道触达引擎 | UI/交互测试 | 单文件系统路由#/outreach进入推送/触达页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有推送/触达访问权限。 | 路由=#/outreach;页面=推送/触达;用途=IM/EDM/APP/TEL 路由、去重、发送、追踪 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/outreach”或从侧边菜单点击“推送/触达”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“推送/触达”页面。 | 1. 页面成功进入“推送/触达”。\n2. 当前菜单高亮,页面内容与“IM/EDM/APP/TEL 路由、去重、发送、追踪”一致。\n3. 刷新后 hash 路由不丢失,仍展示“推送/触达”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备推送/触达访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/outreach | 待执行\nTC-PROTO-0102 | 用户运营系统-单文件.html | 工单管理 | 客服工单与管理 | UI/交互测试 | 单文件系统路由#/support进入客服中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有客服中心访问权限。 | 路由=#/support;页面=客服中心;用途=工单创建、分配、处理、跟进 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/support”或从侧边菜单点击“客服中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“客服中心”页面。 | 1. 页面成功进入“客服中心”。\n2. 当前菜单高亮,页面内容与“工单创建、分配、处理、跟进”一致。\n3. 刷新后 hash 路由不丢失,仍展示“客服中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备客服中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/support | 待执行\nTC-PROTO-0103 | 用户运营系统-单文件.html | 风险反欺诈 | 风险与反欺诈 | UI/交互测试 | 单文件系统路由#/risk进入风险中心页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有风险中心访问权限。 | 路由=#/risk;页面=风险中心;用途=风险信号、强弱关联、黑名单、复核 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/risk”或从侧边菜单点击“风险中心”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“风险中心”页面。 | 1. 页面成功进入“风险中心”。\n2. 当前菜单高亮,页面内容与“风险信号、强弱关联、黑名单、复核”一致。\n3. 刷新后 hash 路由不丢失,仍展示“风险中心”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备风险中心访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/risk | 待执行\nTC-PROTO-0104 | 用户运营系统-单文件.html | 评价结果 | 评价结果追踪 | UI/交互测试 | 单文件系统路由#/review进入评价追踪页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有评价追踪访问权限。 | 路由=#/review;页面=评价追踪;用途=提交记录、Amazon展示核验、异常观察 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/review”或从侧边菜单点击“评价追踪”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“评价追踪”页面。 | 1. 页面成功进入“评价追踪”。\n2. 当前菜单高亮,页面内容与“提交记录、Amazon展示核验、异常观察”一致。\n3. 刷新后 hash 路由不丢失,仍展示“评价追踪”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备评价追踪访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/review | 待执行\nTC-PROTO-0105 | 用户运营系统-单文件.html | 达人协作 | KOC/KOL协作 | UI/交互测试 | 单文件系统路由#/creator进入KOC/KOL页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有KOC/KOL访问权限。 | 路由=#/creator;页面=KOC/KOL;用途=免评计划、内容、CODE、JOYCOLLAB同步 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/creator”或从侧边菜单点击“KOC/KOL”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“KOC/KOL”页面。 | 1. 页面成功进入“KOC/KOL”。\n2. 当前菜单高亮,页面内容与“免评计划、内容、CODE、JOYCOLLAB同步”一致。\n3. 刷新后 hash 路由不丢失,仍展示“KOC/KOL”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备KOC/KOL访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/creator | 待执行\nTC-PROTO-0106 | 用户运营系统-单文件.html | 审计与通知 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/audit进入审计通知页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有审计通知访问权限。 | 路由=#/audit;页面=审计通知;用途=状态变更、敏感访问、通知告警 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/audit”或从侧边菜单点击“审计通知”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“审计通知”页面。 | 1. 页面成功进入“审计通知”。\n2. 当前菜单高亮,页面内容与“状态变更、敏感访问、通知告警”一致。\n3. 刷新后 hash 路由不丢失,仍展示“审计通知”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备审计通知访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/audit | 待执行\nTC-PROTO-0107 | 用户运营系统-单文件.html | 权限配置 | 审计与通知中心 | UI/交互测试 | 单文件系统路由#/system进入系统管理页面 | P1 | 用户已登录 USER评价业务闭环系统;浏览器地址支持 hash 路由;当前账号拥有系统管理访问权限。 | 路由=#/system;页面=系统管理;用途=用户角色、权限、数据范围、导出授权 | 1. 在浏览器打开用户运营系统单文件原型。\n2. 将地址 hash 修改为“#/system”或从侧边菜单点击“系统管理”。\n3. 等待页面渲染完成。\n4. 检查页面标题、面包屑、主按钮、筛选区和列表区域。\n5. 刷新浏览器页面。\n6. 再次确认仍停留在“系统管理”页面。 | 1. 页面成功进入“系统管理”。\n2. 当前菜单高亮,页面内容与“用户角色、权限、数据范围、导出授权”一致。\n3. 刷新后 hash 路由不丢失,仍展示“系统管理”。\n4. 无权限时应显示无权限或返回默认工作台。 | 路由、菜单高亮、页面状态和当前用户权限一致;刷新后筛选默认值正确。 | 只有具备系统管理访问权限的角色能进入该页面;无权限角色不能通过直接输入 hash 绕过。 | Hash 路由可访问、可刷新、权限拦截有效。 | 00-系统总览 角色独立前端;对应子系统文档 | 用户运营系统-单文件.html 路由:#/system | 待执行\nTC-PROTO-0108 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行创建测评需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“创建测评需求”的权限;相关基础数据已准备。 | ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“创建测评需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ASIN=B0TEST001;类型=测评;目标数量=20;周期=2026-05-01至2026-05-31;优先级=P0。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建测评需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“PENDING/EVALUATING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=PENDING/EVALUATING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建测评需求”;跨站点/跨部门数据需按权限范围过滤。 | 创建测评需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:创建测评需求 | 待执行\nTC-PROTO-0109 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行评估需求为待补充并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“评估需求为待补充”的权限;相关基础数据已准备。 | 需求ID=DEM-001;原因=ASIN目标数量缺少依据 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“评估需求为待补充”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-001;原因=ASIN目标数量缺少依据。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “评估需求为待补充”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“WAITING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=WAITING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“评估需求为待补充”;跨站点/跨部门数据需按权限范围过滤。 | 评估需求为待补充完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:评估需求为待补充 | 待执行\nTC-PROTO-0110 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心执行驳回不成立需求并校验业务结果 | P1 | 已进入“需求中心”;当前用户具备执行“驳回不成立需求”的权限;相关基础数据已准备。 | 需求ID=DEM-002;原因=ASIN评分已达标无需计划 | 1. 打开“用户运营系统-单文件.html”的“需求中心”页面。\n2. 点击与“驳回不成立需求”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:需求ID=DEM-002;原因=ASIN评分已达标无需计划。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “驳回不成立需求”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:demands;状态值=REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“驳回不成立需求”;跨站点/跨部门数据需按权限范围过滤。 | 驳回不成立需求完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:需求中心;动作:驳回不成立需求 | 待执行\nTC-PROTO-0111 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行提交测评计划审批并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“提交测评计划审批”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“提交测评计划审批”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;审批链=Amazon运营总监→用户负责人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “提交测评计划审批”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“REVIEW”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records;状态值=REVIEW;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“提交测评计划审批”;跨站点/跨部门数据需按权限范围过滤。 | 提交测评计划审批完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:提交测评计划审批 | 待执行\nTC-PROTO-0112 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批通过计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批通过计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-001;意见=同意执行;目标评价数=20 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批通过计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-001;意见=同意执行;目标评价数=20。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批通过计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“APPROVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=APPROVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批通过计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批通过计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批通过计划 | 待执行\nTC-PROTO-0113 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核执行审批驳回计划并校验业务结果 | P1 | 已进入“计划审核”;当前用户具备执行“审批驳回计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-002;意见=预算和风险说明不足 | 1. 打开“用户运营系统-单文件.html”的“计划审核”页面。\n2. 点击与“审批驳回计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-002;意见=预算和风险说明不足。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “审批驳回计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“DRAFT/REJECTED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:approval_records/plans;状态值=DRAFT/REJECTED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“审批驳回计划”;跨站点/跨部门数据需按权限范围过滤。 | 审批驳回计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划审核;动作:审批驳回计划 | 待执行\nTC-PROTO-0114 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行生成候选用户池并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“生成候选用户池”的权限;相关基础数据已准备。 | 计划ID=PLAN-003;ASIN=B0TEST003;目标=50人 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“生成候选用户池”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-003;ASIN=B0TEST003;目标=50人。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “生成候选用户池”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待触达”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plan_items/quota_reservations;状态值=待触达;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“生成候选用户池”;跨站点/跨部门数据需按权限范围过滤。 | 生成候选用户池完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:生成候选用户池 | 待执行\nTC-PROTO-0115 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心执行暂停执行中计划并校验业务结果 | P1 | 已进入“计划中心”;当前用户具备执行“暂停执行中计划”的权限;相关基础数据已准备。 | 计划ID=PLAN-004;暂停原因=库存异常 | 1. 打开“用户运营系统-单文件.html”的“计划中心”页面。\n2. 点击与“暂停执行中计划”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:计划ID=PLAN-004;暂停原因=库存异常。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “暂停执行中计划”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已暂停”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:plans;状态值=已暂停;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“暂停执行中计划”;跨站点/跨部门数据需按权限范围过滤。 | 暂停执行中计划完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:计划中心;动作:暂停执行中计划 | 待执行\nTC-PROTO-0116 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行批量预占额度并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“批量预占额度”的权限;相关基础数据已准备。 | person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“批量预占额度”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_ids=10个;type=REVIEW;plan_id=PLAN-005;count=1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “批量预占额度”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RESERVED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RESERVED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“批量预占额度”;跨站点/跨部门数据需按权限范围过滤。 | 批量预占额度完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:批量预占额度 | 待执行\nTC-PROTO-0117 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控执行释放触达失败预占并校验业务结果 | P1 | 已进入“额度频控”;当前用户具备执行“释放触达失败预占”的权限;相关基础数据已准备。 | reservation_id=QR-001;释放原因=IM不可达 | 1. 打开“用户运营系统-单文件.html”的“额度频控”页面。\n2. 点击与“释放触达失败预占”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:reservation_id=QR-001;释放原因=IM不可达。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “释放触达失败预占”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“RELEASED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:quota_reservations;状态值=RELEASED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“释放触达失败预占”;跨站点/跨部门数据需按权限范围过滤。 | 释放触达失败预占完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:额度频控;动作:释放触达失败预占 | 待执行\nTC-PROTO-0118 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行IM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行IM触达”的权限;相关基础数据已准备。 | plan_id=PLAN-006;channel=IM;content=回评卡片 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行IM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:plan_id=PLAN-006;channel=IM;content=回评卡片。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行IM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OUTBOUND/SENT”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:im_interaction_records;状态值=OUTBOUND/SENT;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行IM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行IM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行IM触达 | 待执行\nTC-PROTO-0119 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行执行EDM触达并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“执行EDM触达”的权限;相关基础数据已准备。 | email=user@example.com;模板=回评邮件V1 | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“执行EDM触达”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:email=user@example.com;模板=回评邮件V1。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “执行EDM触达”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“SENT/DELIVERED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events;状态值=SENT/DELIVERED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“执行EDM触达”;跨站点/跨部门数据需按权限范围过滤。 | 执行EDM触达完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:执行EDM触达 | 待执行\nTC-PROTO-0120 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达执行用户退订后停止EDM并校验业务结果 | P1 | 已进入“推送/触达”;当前用户具备执行“用户退订后停止EDM”的权限;相关基础数据已准备。 | person_id=P100;event=UNSUBSCRIBED | 1. 打开“用户运营系统-单文件.html”的“推送/触达”页面。\n2. 点击与“用户退订后停止EDM”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P100;event=UNSUBSCRIBED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “用户退订后停止EDM”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“BLOCKED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:edm_message_events/channel_dedup_records;状态值=BLOCKED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“用户退订后停止EDM”;跨站点/跨部门数据需按权限范围过滤。 | 用户退订后停止EDM完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:推送/触达;动作:用户退订后停止EDM | 待执行\nTC-PROTO-0121 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行创建客服工单并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“创建客服工单”的权限;相关基础数据已准备。 | person_id=P200;source=IM转人工;type=催评 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“创建客服工单”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P200;source=IM转人工;type=催评。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建客服工单”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“待分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_tickets;状态值=待分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建客服工单”;跨站点/跨部门数据需按权限范围过滤。 | 创建客服工单完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:创建客服工单 | 待执行\nTC-PROTO-0122 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心执行改派工单给组员并校验业务结果 | P1 | 已进入“客服中心”;当前用户具备执行“改派工单给组员”的权限;相关基础数据已准备。 | ticket_id=T001;from=组长;to=客服A;reason=当前负载低 | 1. 打开“用户运营系统-单文件.html”的“客服中心”页面。\n2. 点击与“改派工单给组员”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:ticket_id=T001;from=组长;to=客服A;reason=当前负载低。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “改派工单给组员”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已分配”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:support_assignment_logs;状态值=已分配;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“改派工单给组员”;跨站点/跨部门数据需按权限范围过滤。 | 改派工单给组员完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:客服中心;动作:改派工单给组员 | 待执行\nTC-PROTO-0123 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行强风险拦截并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“强风险拦截”的权限;相关基础数据已准备。 | person_id=P300;命中黑名单邮箱和设备 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“强风险拦截”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P300;命中黑名单邮箱和设备。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “强风险拦截”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“强风险拦截”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases/blacklist_entities;状态值=强风险拦截;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“强风险拦截”;跨站点/跨部门数据需按权限范围过滤。 | 强风险拦截完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:强风险拦截 | 待执行\nTC-PROTO-0124 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心执行弱风险人工放行并校验业务结果 | P1 | 已进入“风险中心”;当前用户具备执行“弱风险人工放行”的权限;相关基础数据已准备。 | risk_case=R001;原因=家庭共用设备;意见=放行 | 1. 打开“用户运营系统-单文件.html”的“风险中心”页面。\n2. 点击与“弱风险人工放行”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:risk_case=R001;原因=家庭共用设备;意见=放行。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “弱风险人工放行”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已放行”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:risk_cases;状态值=已放行;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“弱风险人工放行”;跨站点/跨部门数据需按权限范围过滤。 | 弱风险人工放行完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:风险中心;动作:弱风险人工放行 | 待执行\nTC-PROTO-0125 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行登记评价提交并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“登记评价提交”的权限;相关基础数据已准备。 | person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接 | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“登记评价提交”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:person_id=P400;asin=B0TEST004;plan_id=PLAN-007;evidence=截图+链接。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “登记评价提交”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已提交”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_submission_records;状态值=已提交;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“登记评价提交”;跨站点/跨部门数据需按权限范围过滤。 | 登记评价提交完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:登记评价提交 | 待执行\nTC-PROTO-0126 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon展示核验成功并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon展示核验成功”的权限;相关基础数据已准备。 | submission_id=SUB001;check_method=人工;result=DISPLAYED | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon展示核验成功”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB001;check_method=人工;result=DISPLAYED。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon展示核验成功”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“CONFIRMED”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks/review_results;状态值=CONFIRMED;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon展示核验成功”;跨站点/跨部门数据需按权限范围过滤。 | Amazon展示核验成功完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon展示核验成功 | 待执行\nTC-PROTO-0127 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪执行Amazon暂不可核验进入观察并校验业务结果 | P1 | 已进入“评价追踪”;当前用户具备执行“Amazon暂不可核验进入观察”的权限;相关基础数据已准备。 | submission_id=SUB002;result=UNVERIFIABLE | 1. 打开“用户运营系统-单文件.html”的“评价追踪”页面。\n2. 点击与“Amazon暂不可核验进入观察”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:submission_id=SUB002;result=UNVERIFIABLE。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “Amazon暂不可核验进入观察”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“OBSERVING”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:review_display_checks;状态值=OBSERVING;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“Amazon暂不可核验进入观察”;跨站点/跨部门数据需按权限范围过滤。 | Amazon暂不可核验进入观察完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:评价追踪;动作:Amazon暂不可核验进入观察 | 待执行\nTC-PROTO-0128 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL执行创建免评协作任务并校验业务结果 | P1 | 已进入“KOC/KOL”;当前用户具备执行“创建免评协作任务”的权限;相关基础数据已准备。 | creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频 | 1. 打开“用户运营系统-单文件.html”的“KOC/KOL”页面。\n2. 点击与“创建免评协作任务”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:creator_id=C001;ASIN=B0TEST005;CODE=KOC20;Brief=短视频。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “创建免评协作任务”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“执行中”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:exemption_plan_tasks/code_records;状态值=执行中;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“创建免评协作任务”;跨站点/跨部门数据需按权限范围过滤。 | 创建免评协作任务完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:KOC/KOL;动作:创建免评协作任务 | 待执行\nTC-PROTO-0129 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知执行查看敏感信息审计并校验业务结果 | P1 | 已进入“审计通知”;当前用户具备执行“查看敏感信息审计”的权限;相关基础数据已准备。 | 对象=用户邮箱/设备号;动作=查看完整信息 | 1. 打开“用户运营系统-单文件.html”的“审计通知”页面。\n2. 点击与“查看敏感信息审计”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:对象=用户邮箱/设备号;动作=查看完整信息。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “查看敏感信息审计”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已记录”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:interaction_audit_logs;状态值=已记录;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“查看敏感信息审计”;跨站点/跨部门数据需按权限范围过滤。 | 查看敏感信息审计完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:审计通知;动作:查看敏感信息审计 | 待执行\nTC-PROTO-0130 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理执行分配导出权限并校验业务结果 | P1 | 已进入“系统管理”;当前用户具备执行“分配导出权限”的权限;相关基础数据已准备。 | role=用户运营组长;permission=导出计划数据;scope=US站点 | 1. 打开“用户运营系统-单文件.html”的“系统管理”页面。\n2. 点击与“分配导出权限”对应的主按钮或列表行操作。\n3. 在表单/弹窗中录入测试数据:role=用户运营组长;permission=导出计划数据;scope=US站点。\n4. 根据页面业务选择确认、提交、保存或审批动作。\n5. 返回列表,使用关键词或ID搜索刚才处理的数据。\n6. 打开详情页查看状态流转、关联对象和审计记录。 | 1. “分配导出权限”提交成功。\n2. 列表中可搜索到对应记录。\n3. 记录状态变为“已授权”。\n4. 详情页展示关联计划/用户/ASIN/风险/工单等上下文。\n5. 关键动作写入审计或通知。 | 校验数据写入/更新对象:权限配置/审计日志;状态值=已授权;关联ID、创建人、更新时间、处理意见完整。 | 无对应权限时不能看到或不能提交“分配导出权限”;跨站点/跨部门数据需按权限范围过滤。 | 分配导出权限完整落库、状态正确、可追溯、可在相关模块回查。 | 对应子系统需求文档与数据对象章节 | 单文件系统页面:系统管理;动作:分配导出权限 | 待执行\nTC-PROTO-0131 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:创建需求缺少ASIN | P2 | 已进入“需求中心”;准备异常数据或异常状态:ASIN为空,其他字段完整。 | 异常场景=创建需求缺少ASIN;异常数据=ASIN为空,其他字段完整 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:ASIN为空,其他字段完整。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“创建需求缺少ASIN”。\n2. 处理结果为:提示ASIN必填,需求不创建。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:创建需求缺少ASIN | 待执行\nTC-PROTO-0132 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 需求中心异常校验:目标数量为0或负数 | P2 | 已进入“需求中心”;准备异常数据或异常状态:target_count=0/-1。 | 异常场景=目标数量为0或负数;异常数据=target_count=0/-1 | 1. 打开“需求中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:target_count=0/-1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“目标数量为0或负数”。\n2. 处理结果为:提示目标数量必须大于0。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:需求中心;异常:目标数量为0或负数 | 待执行\nTC-PROTO-0133 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 异常场景 | 计划审核异常校验:审批意见为空驳回 | P2 | 已进入“计划审核”;准备异常数据或异常状态:decision=驳回;comment为空。 | 异常场景=审批意见为空驳回;异常数据=decision=驳回;comment为空 | 1. 打开“计划审核”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:decision=驳回;comment为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“审批意见为空驳回”。\n2. 处理结果为:阻止提交并提示填写驳回原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划审核;异常:审批意见为空驳回 | 待执行\nTC-PROTO-0134 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 异常场景 | 计划中心异常校验:已终止计划再次启动 | P2 | 已进入“计划中心”;准备异常数据或异常状态:plan.status=CANCELLED。 | 异常场景=已终止计划再次启动;异常数据=plan.status=CANCELLED | 1. 打开“计划中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:plan.status=CANCELLED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“已终止计划再次启动”。\n2. 处理结果为:启动按钮不可用或提示不可恢复。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:计划中心;异常:已终止计划再次启动 | 待执行\nTC-PROTO-0135 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:月度测评额度超过4 | P2 | 已进入“额度频控”;准备异常数据或异常状态:used=4,reserved=0,count=1。 | 异常场景=月度测评额度超过4;异常数据=used=4,reserved=0,count=1 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:used=4,reserved=0,count=1。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“月度测评额度超过4”。\n2. 处理结果为:返回exceeded并阻止预占。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:月度测评额度超过4 | 待执行\nTC-PROTO-0136 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:累计评价超过12 | P2 | 已进入“额度频控”;准备异常数据或异常状态:lifetime_submission=12。 | 异常场景=累计评价超过12;异常数据=lifetime_submission=12 | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:lifetime_submission=12。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“累计评价超过12”。\n2. 处理结果为:候选人进入排除池。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:累计评价超过12 | 待执行\nTC-PROTO-0137 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 额度频控异常校验:发送前终校发现未关闭工单 | P2 | 已进入“额度频控”;准备异常数据或异常状态:support open ticket exists。 | 异常场景=发送前终校发现未关闭工单;异常数据=support open ticket exists | 1. 打开“额度频控”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:support open ticket exists。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“发送前终校发现未关闭工单”。\n2. 处理结果为:撤出本批次并记录原因。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:额度频控;异常:发送前终校发现未关闭工单 | 待执行\nTC-PROTO-0138 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:同计划同用户重复触达 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:person_id相同、plan_id相同、channel不同。 | 异常场景=同计划同用户重复触达;异常数据=person_id相同、plan_id相同、channel不同 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:person_id相同、plan_id相同、channel不同。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“同计划同用户重复触达”。\n2. 处理结果为:去重记录BLOCKED。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:同计划同用户重复触达 | 待执行\nTC-PROTO-0139 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:EDM硬退信 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:event=HARD_BOUNCED。 | 异常场景=EDM硬退信;异常数据=event=HARD_BOUNCED | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:event=HARD_BOUNCED。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“EDM硬退信”。\n2. 处理结果为:邮箱状态标记不可触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:EDM硬退信 | 待执行\nTC-PROTO-0140 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 推送/触达异常校验:TEL三次未接通 | P2 | 已进入“推送/触达”;准备异常数据或异常状态:retry_count=3。 | 异常场景=TEL三次未接通;异常数据=retry_count=3 | 1. 打开“推送/触达”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:retry_count=3。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“TEL三次未接通”。\n2. 处理结果为:降级EDM或关闭电话任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:推送/触达;异常:TEL三次未接通 | 待执行\nTC-PROTO-0141 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:关闭工单缺少处理结果 | P2 | 已进入“客服中心”;准备异常数据或异常状态:result为空。 | 异常场景=关闭工单缺少处理结果;异常数据=result为空 | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:result为空。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“关闭工单缺少处理结果”。\n2. 处理结果为:阻止关闭。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:关闭工单缺少处理结果 | 待执行\nTC-PROTO-0142 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 客服中心异常校验:答应配合超时 | P2 | 已进入“客服中心”;准备异常数据或异常状态:deadline_at过期且无submission。 | 异常场景=答应配合超时;异常数据=deadline_at过期且无submission | 1. 打开“客服中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:deadline_at过期且无submission。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“答应配合超时”。\n2. 处理结果为:生成需再次联系任务。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:客服中心;异常:答应配合超时 | 待执行\nTC-PROTO-0143 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:黑名单同步接口超时 | P2 | 已进入“风险中心”;准备异常数据或异常状态:blacklist API timeout。 | 异常场景=黑名单同步接口超时;异常数据=blacklist API timeout | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:blacklist API timeout。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“黑名单同步接口超时”。\n2. 处理结果为:状态为失败待重试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:黑名单同步接口超时 | 待执行\nTC-PROTO-0144 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 风险中心异常校验:弱风险复核被拒绝 | P2 | 已进入“风险中心”;准备异常数据或异常状态:人工意见=拒绝。 | 异常场景=弱风险复核被拒绝;异常数据=人工意见=拒绝 | 1. 打开“风险中心”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:人工意见=拒绝。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“弱风险复核被拒绝”。\n2. 处理结果为:用户不能进入触达。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:风险中心;异常:弱风险复核被拒绝 | 待执行\nTC-PROTO-0145 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:提交评价ASIN不匹配 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:登记ASIN=A,证据链接ASIN=B。 | 异常场景=提交评价ASIN不匹配;异常数据=登记ASIN=A,证据链接ASIN=B | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:登记ASIN=A,证据链接ASIN=B。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“提交评价ASIN不匹配”。\n2. 处理结果为:标记异常不计入完成。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:提交评价ASIN不匹配 | 待执行\nTC-PROTO-0146 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 评价追踪异常校验:Amazon未展示超过观察期 | P2 | 已进入“评价追踪”;准备异常数据或异常状态:status=OBSERVING且retry超期。 | 异常场景=Amazon未展示超过观察期;异常数据=status=OBSERVING且retry超期 | 1. 打开“评价追踪”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:status=OBSERVING且retry超期。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“Amazon未展示超过观察期”。\n2. 处理结果为:标记ABNORMAL并通知运营。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:评价追踪;异常:Amazon未展示超过观察期 | 待执行\nTC-PROTO-0147 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 异常场景 | KOC/KOL异常校验:CODE缺失 | P2 | 已进入“KOC/KOL”;准备异常数据或异常状态:免评任务未填写CODE。 | 异常场景=CODE缺失;异常数据=免评任务未填写CODE | 1. 打开“KOC/KOL”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:免评任务未填写CODE。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“CODE缺失”。\n2. 处理结果为:阻止创建或标记价格/CODE待确认。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:KOC/KOL;异常:CODE缺失 | 待执行\nTC-PROTO-0148 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 异常场景 | 审计通知异常校验:普通客服查看完整邮箱 | P2 | 已进入“审计通知”;准备异常数据或异常状态:role=客服;action=查看完整信息。 | 异常场景=普通客服查看完整邮箱;异常数据=role=客服;action=查看完整信息 | 1. 打开“审计通知”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:role=客服;action=查看完整信息。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“普通客服查看完整邮箱”。\n2. 处理结果为:拒绝访问并记录越权尝试。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:审计通知;异常:普通客服查看完整邮箱 | 待执行\nTC-PROTO-0149 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 异常场景 | 系统管理异常校验:离职账号仍有审批任务 | P2 | 已进入“系统管理”;准备异常数据或异常状态:account=disabled但任务未交接。 | 异常场景=离职账号仍有审批任务;异常数据=account=disabled但任务未交接 | 1. 打开“系统管理”。\n2. 按正常业务入口开始操作。\n3. 输入或选择异常数据:account=disabled但任务未交接。\n4. 点击提交/保存/审批/发送。\n5. 查看页面校验提示、状态变化和日志记录。\n6. 刷新页面后再次查询该对象。 | 1. 系统识别异常“离职账号仍有审批任务”。\n2. 处理结果为:阻止完成离职并提示先交接。\n3. 不应产生错误落库、重复扣减额度或错误计划完成数。\n4. 刷新后异常状态仍可追溯。 | 异常场景下相关业务表不产生不一致数据;需要记录失败原因、操作者和时间。 | 无权限角色不能通过异常路径绕过审批、额度、风险或敏感字段控制。 | 异常被明确拦截或进入规定处理队列,业务数据保持一致。 | 对应子系统异常/待确认规则;09-审计与通知中心 | 单文件系统页面:系统管理;异常:离职账号仍有审批任务 | 待执行\nTC-PROTO-0150 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发按线索查真实人并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/person?type=email&value=xxx”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/person?type=email&value=xxx;预期输出=返回person_id、confidence、matched_clues | 1. 打开“用户中心”页面。\n2. 执行会触发“按线索查真实人”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/person?type=email&value=xxx”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/person?type=email&value=xxx”。\n2. 接口响应包含:返回person_id、confidence、matched_clues。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 按线索查真实人在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/person?type=email&value=xxx | 待执行\nTC-PROTO-0151 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发获取用户上下文卡并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“GET /api/identity/context/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/identity/context/{person_id};预期输出=返回identity、transactions、services、risks、devices、outreach_history | 1. 打开“用户中心”页面。\n2. 执行会触发“获取用户上下文卡”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/identity/context/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/identity/context/{person_id}”。\n2. 接口响应包含:返回identity、transactions、services、risks、devices、outreach_history。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 获取用户上下文卡在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:GET /api/identity/context/{person_id} | 待执行\nTC-PROTO-0152 | 用户运营系统-单文件.html | 用户中心 | 用户身份与上下文 | 数据校验 | 用户中心页面触发批量身份查询并校验接口数据 | P2 | 前端页面“用户中心”已打开;后端或 mock 服务提供接口“POST /api/identity/batch-check”;测试用户拥有页面访问权限。 | 接口=POST /api/identity/batch-check;预期输出=返回每个线索对应person_id和confidence | 1. 打开“用户中心”页面。\n2. 执行会触发“批量身份查询”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/identity/batch-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/identity/batch-check”。\n2. 接口响应包含:返回每个线索对应person_id和confidence。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量身份查询在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:用户中心;接口:POST /api/identity/batch-check | 待执行\nTC-PROTO-0153 | 用户运营系统-单文件.html | 计划中心 | 需求与计划管理 | 数据校验 | 计划中心页面触发创建需求接口并校验接口数据 | P2 | 前端页面“计划中心”已打开;后端或 mock 服务提供接口“POST /api/demands”;测试用户拥有页面访问权限。 | 接口=POST /api/demands;预期输出=返回demand_id和status | 1. 打开“计划中心”页面。\n2. 执行会触发“创建需求接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/demands”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/demands”。\n2. 接口响应包含:返回demand_id和status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建需求接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划中心;接口:POST /api/demands | 待执行\nTC-PROTO-0154 | 用户运营系统-单文件.html | 计划审核 | 需求与计划管理 | 数据校验 | 计划审核页面触发提交审批接口并校验接口数据 | P2 | 前端页面“计划审核”已打开;后端或 mock 服务提供接口“POST /api/approvals/{plan_id}/submit”;测试用户拥有页面访问权限。 | 接口=POST /api/approvals/{plan_id}/submit;预期输出=生成approval_records | 1. 打开“计划审核”页面。\n2. 执行会触发“提交审批接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/approvals/{plan_id}/submit”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/approvals/{plan_id}/submit”。\n2. 接口响应包含:生成approval_records。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 提交审批接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:计划审核;接口:POST /api/approvals/{plan_id}/submit | 待执行\nTC-PROTO-0155 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发额度查询接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“GET /api/quota/check/{person_id}?type=REVIEW”;测试用户拥有页面访问权限。 | 接口=GET /api/quota/check/{person_id}?type=REVIEW;预期输出=返回used、in_progress、reserved、remaining、status | 1. 打开“额度频控”页面。\n2. 执行会触发“额度查询接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/quota/check/{person_id}?type=REVIEW”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/quota/check/{person_id}?type=REVIEW”。\n2. 接口响应包含:返回used、in_progress、reserved、remaining、status。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 额度查询接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:GET /api/quota/check/{person_id}?type=REVIEW | 待执行\nTC-PROTO-0156 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发批量预占接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/reserve”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/reserve;预期输出=返回reservation_id并更新reserved | 1. 打开“额度频控”页面。\n2. 执行会触发“批量预占接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/reserve”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/reserve”。\n2. 接口响应包含:返回reservation_id并更新reserved。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 批量预占接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/reserve | 待执行\nTC-PROTO-0157 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 数据校验 | 额度频控页面触发发送前终校接口并校验接口数据 | P2 | 前端页面“额度频控”已打开;后端或 mock 服务提供接口“POST /api/quota/final-check”;测试用户拥有页面访问权限。 | 接口=POST /api/quota/final-check;预期输出=返回APPROVED/WITHDRAWN和reasons | 1. 打开“额度频控”页面。\n2. 执行会触发“发送前终校接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/quota/final-check”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/quota/final-check”。\n2. 接口响应包含:返回APPROVED/WITHDRAWN和reasons。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 发送前终校接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:额度频控;接口:POST /api/quota/final-check | 待执行\nTC-PROTO-0158 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发渠道路由接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“POST /api/outreach/route”;测试用户拥有页面访问权限。 | 接口=POST /api/outreach/route;预期输出=返回recommended_channel和alternatives | 1. 打开“推送/触达”页面。\n2. 执行会触发“渠道路由接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/outreach/route”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/outreach/route”。\n2. 接口响应包含:返回recommended_channel和alternatives。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 渠道路由接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:POST /api/outreach/route | 待执行\nTC-PROTO-0159 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 数据校验 | 推送/触达页面触发触达历史接口并校验接口数据 | P2 | 前端页面“推送/触达”已打开;后端或 mock 服务提供接口“GET /api/outreach/history/{person_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/outreach/history/{person_id};预期输出=返回im、edm、app、tel历史 | 1. 打开“推送/触达”页面。\n2. 执行会触发“触达历史接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/outreach/history/{person_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/outreach/history/{person_id}”。\n2. 接口响应包含:返回im、edm、app、tel历史。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 触达历史接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:推送/触达;接口:GET /api/outreach/history/{person_id} | 待执行\nTC-PROTO-0160 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发创建工单接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“POST /api/tickets”;测试用户拥有页面访问权限。 | 接口=POST /api/tickets;预期输出=返回ticket_id | 1. 打开“客服中心”页面。\n2. 执行会触发“创建工单接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/tickets”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/tickets”。\n2. 接口响应包含:返回ticket_id。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 创建工单接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:POST /api/tickets | 待执行\nTC-PROTO-0161 | 用户运营系统-单文件.html | 客服中心 | 客服工单与管理 | 数据校验 | 客服中心页面触发查询可用客服接口并校验接口数据 | P2 | 前端页面“客服中心”已打开;后端或 mock 服务提供接口“GET /api/support/available-agents”;测试用户拥有页面访问权限。 | 接口=GET /api/support/available-agents;预期输出=返回agent_id和current_load | 1. 打开“客服中心”页面。\n2. 执行会触发“查询可用客服接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/support/available-agents”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/support/available-agents”。\n2. 接口响应包含:返回agent_id和current_load。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询可用客服接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:客服中心;接口:GET /api/support/available-agents | 待执行\nTC-PROTO-0162 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发记录评价提交接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“POST /api/reviews/submission”;测试用户拥有页面访问权限。 | 接口=POST /api/reviews/submission;预期输出=返回submission_id和quota_updated | 1. 打开“评价追踪”页面。\n2. 执行会触发“记录评价提交接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“POST /api/reviews/submission”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“POST /api/reviews/submission”。\n2. 接口响应包含:返回submission_id和quota_updated。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 记录评价提交接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:POST /api/reviews/submission | 待执行\nTC-PROTO-0163 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 数据校验 | 评价追踪页面触发查询计划评价进度接口并校验接口数据 | P2 | 前端页面“评价追踪”已打开;后端或 mock 服务提供接口“GET /api/reviews/status/{plan_id}”;测试用户拥有页面访问权限。 | 接口=GET /api/reviews/status/{plan_id};预期输出=返回total_submissions、verified、pending、completion_rate | 1. 打开“评价追踪”页面。\n2. 执行会触发“查询计划评价进度接口”的页面操作。\n3. 在浏览器网络面板或接口日志中定位请求“GET /api/reviews/status/{plan_id}”。\n4. 校验请求参数来自页面当前选择的数据。\n5. 查看接口响应并回到页面查看展示结果。 | 1. 页面操作正确触发“GET /api/reviews/status/{plan_id}”。\n2. 接口响应包含:返回total_submissions、verified、pending、completion_rate。\n3. 页面展示与接口响应一致。\n4. 接口失败时页面给出可理解的错误提示,不出现空白页。 | 请求参数、响应字段、页面展示、数据库对象四者一致;失败响应不写入成功状态。 | 接口必须校验登录态和角色权限;前端隐藏按钮不能替代后端鉴权。 | 查询计划评价进度接口在页面、接口、数据层三端一致。 | 对应子系统 API 契约章节 | 单文件页面:评价追踪;接口:GET /api/reviews/status/{plan_id} | 待执行\nTC-PROTO-0164 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:评价主闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流 | 1. 从 Dashboard 或对应入口启动“评价主闭环”。\n2. 按流程依次完成:ASIN评分4.46触发需求→生成计划→审批→候选筛选→额度预占→风险放行→IM触达→客服跟进→用户提交→Amazon展示→计划完成度回流。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “评价主闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 评价主闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:评价主闭环 | 待执行\nTC-PROTO-0165 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:紧急Listing闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升 | 1. 从 Dashboard 或对应入口启动“紧急Listing闭环”。\n2. 按流程依次完成:评分4.21接近4.2→创建紧急策略→系统管理员审批→用户运营执行→风险雷达监控→评价健康回升。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “紧急Listing闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 紧急Listing闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:紧急Listing闭环 | 待执行\nTC-PROTO-0166 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:推送风险复核闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录 | 1. 从 Dashboard 或对应入口启动“推送风险复核闭环”。\n2. 按流程依次完成:退订率高于基线→进入推送风险→复核人群和素材→暂停同策略→输出复盘记录。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “推送风险复核闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 推送风险复核闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:推送风险复核闭环 | 待执行\nTC-PROTO-0167 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:黑名单同步闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查 | 1. 从 Dashboard 或对应入口启动“黑名单同步闭环”。\n2. 按流程依次完成:客服升级疑似诈骗→风险复核→确认诈骗→同步黑名单→失败待重试→审计可查。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “黑名单同步闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 黑名单同步闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:黑名单同步闭环 | 待执行\nTC-PROTO-0168 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:客服转化闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新 | 1. 从 Dashboard 或对应入口启动“客服转化闭环”。\n2. 按流程依次完成:用户消息进入→自动分配→客服回复→用户答应配合→提醒→提交评价→工单关闭→绩效更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “客服转化闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 客服转化闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:客服转化闭环 | 待执行\nTC-PROTO-0169 | 用户运营系统-单文件.html | 端到端流程 | 系统总览 | 验收测试 | 端到端验收:免评协作闭环 | P1 | 准备完整链路数据;管理员、Amazon运营、用户运营、客服、风险负责人、KOC/KOL运营账号均可登录;相关外部系统可使用mock。 | 免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新 | 1. 从 Dashboard 或对应入口启动“免评协作闭环”。\n2. 按流程依次完成:免评需求→免评计划审批→KOC/KOL匹配→CODE配置→内容发布→结果回流→ASIN健康更新。\n3. 每到一个状态节点,记录页面状态、负责人、时间和关联ID。\n4. 在 Dashboard、计划中心、客服中心、风险中心、评价追踪中分别回查结果。\n5. 查看审计日志和通知记录。 | 1. “免评协作闭环”可完整跑通。\n2. 每个模块状态与上游动作一致。\n3. Dashboard 指标、计划完成度、工单绩效、风险记录和评价结果均同步回流。\n4. 审计日志可按关联ID串起全链路。 | 需求、计划、候选人、额度、风险、触达、工单、评价、审计对象均有一致关联ID;提交评价与展示核验分开计数。 | 不同角色只能执行自己职责内动作;审批、黑名单、敏感信息、导出需独立权限。 | 免评协作闭环闭环无断点、状态无冲突、数据可追溯、异常可恢复。 | 全部需求文档;业务闭环流程图 | 单文件系统 Dashboard 与各业务路由;流程:免评协作闭环 | 待执行\nTC-PROTO-0170 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换全部标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“全部”。 | 标签=全部;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“全部”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“全部”。 | 1. 队列只展示与“全部”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:全部 | 待执行\nTC-PROTO-0171 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换审核标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“审核”。 | 标签=审核;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“审核”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“审核”。 | 1. 队列只展示与“审核”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:审核 | 待执行\nTC-PROTO-0172 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换黑名单标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“黑名单”。 | 标签=黑名单;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“黑名单”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“黑名单”。 | 1. 队列只展示与“黑名单”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:黑名单 | 待执行\nTC-PROTO-0173 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台-P0/P1处理队列 | 审计与通知中心 | 功能测试 | P0/P1处理队列切换推送标签后只展示对应事项 | P2 | 系统管理员在工作台;P0/P1处理队列包含审核、黑名单、推送三类事项;当前标签可切换到“推送”。 | 标签=推送;队列事项=测评需求、推送风险、待同步黑名单、紧急策略审批、差评跟进 | 1. 打开管理员首页。\n2. 在P0/P1处理队列点击“推送”标签。\n3. 逐行检查事项类型、负责人、时限和操作按钮。\n4. 点击任意一条事项的“处理/审核/复核/分配”按钮进入详情。\n5. 关闭详情后再次查看当前标签是否仍为“推送”。 | 1. 队列只展示与“推送”匹配的事项;如果为全部则展示所有事项。\n2. 当前标签高亮。\n3. 打开并关闭详情后筛选标签不丢失。 | 筛选后的事项数量与队列分类统计一致;关闭详情不重置筛选条件。 | 普通角色只能看到本人相关事项;系统管理员可以切换全部标签。 | 队列标签筛选准确、状态保持、权限范围正确。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | P0/P1处理队列标签:推送 | 待执行\nTC-PROTO-0174 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门全部部门状态全部状态风险全部风险负责人全部负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=全部部门;状态=全部状态;风险=全部风险;负责人=全部负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“全部部门”。\n3. 选择状态“全部状态”、风险“全部风险”、负责人“全部负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合全部部门/全部状态/全部风险/全部负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0175 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门Amazon 运营状态待审批风险全部风险负责人Amazon 总监组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=Amazon 运营;状态=待审批;风险=全部风险;负责人=Amazon 总监 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“Amazon 运营”。\n3. 选择状态“待审批”、风险“全部风险”、负责人“Amazon 总监”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合Amazon 运营/待审批/全部风险/Amazon 总监的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0176 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门用户运营状态待复核风险偏高负责人用户运营组长组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=用户运营;状态=待复核;风险=偏高;负责人=用户运营组长 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“用户运营”。\n3. 选择状态“待复核”、风险“偏高”、负责人“用户运营组长”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合用户运营/待复核/偏高/用户运营组长的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0177 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门客服状态客服升级风险高风险负责人客服负责人组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=客服;状态=客服升级;风险=高风险;负责人=客服负责人 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“客服”。\n3. 选择状态“客服升级”、风险“高风险”、负责人“客服负责人”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合客服/客服升级/高风险/客服负责人的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0178 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 各模块列表-组合筛选 | 系统总览 | 功能测试 | 列表按部门系统管理员状态待系统管理员确认风险紧急负责人系统管理员组合查询 | P2 | 系统管理员进入任一业务列表页;列表顶部存在部门、状态、风险、负责人筛选项和查询按钮。 | 部门=系统管理员;状态=待系统管理员确认;风险=紧急;负责人=系统管理员 | 1. 从左侧导航进入需求中心或风险中心。\n2. 在筛选区选择部门“系统管理员”。\n3. 选择状态“待系统管理员确认”、风险“紧急”、负责人“系统管理员”。\n4. 点击“查询”。\n5. 检查列表每一行的部门、当前环节、风险和负责人。\n6. 点击“导出”。 | 1. 列表只返回符合系统管理员/待系统管理员确认/紧急/系统管理员的记录。\n2. 统计数量与当前筛选条件一致。\n3. 导出文件只包含当前筛选结果。\n4. 导出动作写入审计日志。 | 筛选条件、列表结果、导出结果、审计日志中的查询条件一致。 | 无导出权限时导出按钮隐藏或提示无权限;不能导出其他部门数据。 | 组合筛选准确、导出范围正确、审计完整。 | 00-系统总览;09-审计与通知中心 | 筛选项:全部部门/全部状态/全部风险/全部负责人;按钮:查询/导出 | 待执行\nTC-PROTO-0179 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段JOYHUB 用户ID按全员可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“JOYHUB 用户ID”存在。 | 字段=JOYHUB 用户ID;可见范围=全员可见;期望=完整ID可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“JOYHUB 用户ID”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“JOYHUB 用户ID”按照“全员可见”控制可见性。\n2. 符合预期:完整ID可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | JOYHUB 用户ID必须按全员可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:JOYHUB 用户ID/全员可见 | 待执行\nTC-PROTO-0180 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段用户名按授权可见权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“用户名”存在。 | 字段=用户名;可见范围=授权可见;期望=未授权显示脱敏 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“用户名”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“用户名”按照“授权可见”控制可见性。\n2. 符合预期:未授权显示脱敏。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 用户名必须按授权可见控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:用户名/授权可见 | 待执行\nTC-PROTO-0181 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段邮箱后缀按已脱敏权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“邮箱后缀”存在。 | 字段=邮箱后缀;可见范围=已脱敏;期望=只显示邮箱域名/后缀 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“邮箱后缀”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“邮箱后缀”按照“已脱敏”控制可见性。\n2. 符合预期:只显示邮箱域名/后缀。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 邮箱后缀必须按已脱敏控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:邮箱后缀/已脱敏 | 待执行\nTC-PROTO-0182 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段近7天EDM推送数按推送数据权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“近7天EDM推送数”存在。 | 字段=近7天EDM推送数;可见范围=推送数据;期望=仅推送/管理员可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“近7天EDM推送数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“近7天EDM推送数”按照“推送数据”控制可见性。\n2. 符合预期:仅推送/管理员可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 近7天EDM推送数必须按推送数据控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:近7天EDM推送数/推送数据 | 待执行\nTC-PROTO-0183 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段身份风险等级按系统管理员/风险负责人权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“身份风险等级”存在。 | 字段=身份风险等级;可见范围=系统管理员/风险负责人;期望=普通运营不可见 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“身份风险等级”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“身份风险等级”按照“系统管理员/风险负责人”控制可见性。\n2. 符合预期:普通运营不可见。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 身份风险等级必须按系统管理员/风险负责人控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:身份风险等级/系统管理员/风险负责人 | 待执行\nTC-PROTO-0184 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-字段权限与脱敏 | 用户身份与上下文 | 权限校验 | 现有ERP字段标签覆盖人数按标签模块权限展示 | P2 | v10原型进入现有ERP;测试账号分别准备系统管理员、用户运营、客服、风险负责人;字段“标签覆盖人数”存在。 | 字段=标签覆盖人数;可见范围=标签模块;期望=负责人可见汇总,普通客服不可导出 | 1. 使用系统管理员账号进入现有ERP用户管理字段表,查看“标签覆盖人数”。\n2. 退出后使用普通客服账号进入同一页面。\n3. 再使用风险负责人或用户运营账号进入同一页面。\n4. 分别点击“查看完整信息”和“导出现有关系”。\n5. 对比三个角色看到的字段内容。 | 1. 字段“标签覆盖人数”按照“标签模块”控制可见性。\n2. 符合预期:负责人可见汇总,普通客服不可导出。\n3. 未授权角色点击查看完整信息被拒绝并记录审计。\n4. 导出文件不包含未授权字段明文。 | 字段展示、导出内容、审计记录中的角色和权限点一致;脱敏字段不得在前端源码/导出中泄露明文。 | 标签覆盖人数必须按标签模块控制;查看完整信息和导出是独立权限。 | 字段级权限和脱敏在页面展示、详情、导出三个场景均生效。 | 00-系统总览;09-审计与通知中心;01-用户身份与上下文 | 字段权限清单:标签覆盖人数/标签模块 | 待执行\nTC-PROTO-0185 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服本人在客服执行看板的可操作范围校验 | P1 | 准备客服本人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服本人;数据范围=我的工单;允许=回复用户、登记提交事实;限制=不能改派他人工单或查看团队绩效 | 1. 使用“客服本人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:回复用户、登记提交事实。\n4. 尝试执行限制动作:不能改派他人工单或查看团队绩效。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服本人只能看到“我的工单”。\n2. 允许动作“回复用户、登记提交事实”可正常提交。\n3. 限制动作“不能改派他人工单或查看团队绩效”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服本人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服本人 | 待执行\nTC-PROTO-0186 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服组长在客服执行看板的可操作范围校验 | P1 | 准备客服组长账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服组长;数据范围=组内工单池;允许=手动分配、转移、查看组员负载;限制=不能查看跨团队敏感字段 | 1. 使用“客服组长”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:手动分配、转移、查看组员负载。\n4. 尝试执行限制动作:不能查看跨团队敏感字段。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服组长只能看到“组内工单池”。\n2. 允许动作“手动分配、转移、查看组员负载”可正常提交。\n3. 限制动作“不能查看跨团队敏感字段”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服组长权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服组长 | 待执行\nTC-PROTO-0187 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 客服主管在客服执行看板的可操作范围校验 | P1 | 准备客服主管账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=客服主管;数据范围=团队看板;允许=查看排班、绩效、目标完成率;限制=不能同步黑名单除非额外授权 | 1. 使用“客服主管”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看排班、绩效、目标完成率。\n4. 尝试执行限制动作:不能同步黑名单除非额外授权。\n5. 打开审计日志查看敏感操作记录。 | 1. 客服主管只能看到“团队看板”。\n2. 允许动作“查看排班、绩效、目标完成率”可正常提交。\n3. 限制动作“不能同步黑名单除非额外授权”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 客服主管权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:客服主管 | 待执行\nTC-PROTO-0188 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 风险负责人在客服执行看板的可操作范围校验 | P1 | 准备风险负责人账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=风险负责人;数据范围=疑似诈骗工单;允许=确认诈骗、标记误报、同步黑名单候选;限制=不能修改客服排班 | 1. 使用“风险负责人”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:确认诈骗、标记误报、同步黑名单候选。\n4. 尝试执行限制动作:不能修改客服排班。\n5. 打开审计日志查看敏感操作记录。 | 1. 风险负责人只能看到“疑似诈骗工单”。\n2. 允许动作“确认诈骗、标记误报、同步黑名单候选”可正常提交。\n3. 限制动作“不能修改客服排班”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 风险负责人权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:风险负责人 | 待执行\nTC-PROTO-0189 | 客服执行.html | 客服执行看板-角色权限 | 客服工单与管理 | 权限校验 | 系统管理员在客服执行看板的可操作范围校验 | P1 | 准备系统管理员账号;客服执行看板存在待分配、处理中、等待提交、疑似诈骗等工单。 | 角色=系统管理员;数据范围=全部客服数据;允许=查看审计、配置权限、导出绩效;限制=敏感查看仍需记录审计 | 1. 使用“系统管理员”账号登录客服执行看板。\n2. 查看顶部指标、工单列表、绩效区域和排班区域。\n3. 尝试执行允许动作:查看审计、配置权限、导出绩效。\n4. 尝试执行限制动作:敏感查看仍需记录审计。\n5. 打开审计日志查看敏感操作记录。 | 1. 系统管理员只能看到“全部客服数据”。\n2. 允许动作“查看审计、配置权限、导出绩效”可正常提交。\n3. 限制动作“敏感查看仍需记录审计”按钮隐藏或提交失败。\n4. 敏感查看、导出、风险处置均记录审计。 | support_tickets、assignment_logs、performance_snapshots按角色范围返回;越权请求后端拒绝。 | 系统管理员权限模型正确,前后端均不可越权。 | 角色数据范围、按钮权限、审计记录一致。 | 05-客服工单与管理;09-审计与通知中心 | 客服执行角色:系统管理员 | 待执行\nTC-PROTO-0190 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 需求中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“需求中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2;详情字段=需求ID、ASIN、目标数量、周期、提交人、评估结果 | 1. 打开“需求中心”页面。\n2. 在筛选区按业务条件选择或输入:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:需求ID、ASIN、目标数量、周期、提交人、评估结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2”。\n2. 详情抽屉展示“需求ID、ASIN、目标数量、周期、提交人、评估结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 需求中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:需求中心;筛选:类型=测评/回评/免评;状态=待评估/待补充/已通过/已拒绝;优先级=P0/P1/P2 | 待执行\nTC-PROTO-0191 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 计划审核按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划审核”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回;详情字段=审批链、审批人、意见、step_order、decided_at | 1. 打开“计划审核”页面。\n2. 在筛选区按业务条件选择或输入:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:审批链、审批人、意见、step_order、decided_at。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回”。\n2. 详情抽屉展示“审批链、审批人、意见、step_order、decided_at”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划审核查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划审核;筛选:计划类型=推新/回评/免评/紧急;审批状态=待审批/已通过/已驳回 | 待执行\nTC-PROTO-0192 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 计划中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“计划中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL;详情字段=计划项、目标量、候选人、资源分配、完成率 | 1. 打开“计划中心”页面。\n2. 在筛选区按业务条件选择或输入:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:计划项、目标量、候选人、资源分配、完成率。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL”。\n2. 详情抽屉展示“计划项、目标量、候选人、资源分配、完成率”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 计划中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:计划中心;筛选:状态=草稿/执行中/待核验/已完成/已终止;渠道=IM/EDM/APP/TEL | 待执行\nTC-PROTO-0193 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 功能测试 | ASIN/Listing按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“ASIN/Listing”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间;详情字段=评分、评价数、差评数、健康状态、责任人 | 1. 打开“ASIN/Listing”页面。\n2. 在筛选区按业务条件选择或输入:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:评分、评价数、差评数、健康状态、责任人。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间”。\n2. 详情抽屉展示“评分、评价数、差评数、健康状态、责任人”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | ASIN/Listing查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:ASIN/Listing;筛选:站点=US/CA/UK;健康状态=健康/关注/风险/严重风险;评分区间 | 待执行\nTC-PROTO-0194 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 功能测试 | 用户中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“用户中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=国家、性别、标签、身份、产品数、活动数、近7天EDM次数;详情字段=用户主档、标签、身份、产品关系、近期活跃 | 1. 打开“用户中心”页面。\n2. 在筛选区按业务条件选择或输入:国家、性别、标签、身份、产品数、活动数、近7天EDM次数。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:用户主档、标签、身份、产品关系、近期活跃。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“国家、性别、标签、身份、产品数、活动数、近7天EDM次数”。\n2. 详情抽屉展示“用户主档、标签、身份、产品关系、近期活跃”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 用户中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:用户中心;筛选:国家、性别、标签、身份、产品数、活动数、近7天EDM次数 | 待执行\nTC-PROTO-0195 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 额度频控按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“额度频控”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=额度类型=测评/免评/累计;状态=sufficient/warning/exceeded;详情字段=used、in_progress、reserved、remaining、limit_value | 1. 打开“额度频控”页面。\n2. 在筛选区按业务条件选择或输入:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:used、in_progress、reserved、remaining、limit_value。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“额度类型=测评/免评/累计;状态=sufficient/warning/exceeded”。\n2. 详情抽屉展示“used、in_progress、reserved、remaining、limit_value”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 额度频控查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:额度频控;筛选:额度类型=测评/免评/累计;状态=sufficient/warning/exceeded | 待执行\nTC-PROTO-0196 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 推送/触达按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“推送/触达”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订;详情字段=发送、点击、回复、退订、route decision、dedup reason | 1. 打开“推送/触达”页面。\n2. 在筛选区按业务条件选择或输入:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:发送、点击、回复、退订、route decision、dedup reason。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订”。\n2. 详情抽屉展示“发送、点击、回复、退订、route decision、dedup reason”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 推送/触达查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:推送/触达;筛选:渠道=IM/EDM/APP/TEL;状态=待发送/已发送/失败/退订 | 待执行\nTC-PROTO-0197 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 客服中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“客服中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭;详情字段=工单ID、assigned_agent、followup状态、首次回复时长 | 1. 打开“客服中心”页面。\n2. 在筛选区按业务条件选择或输入:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:工单ID、assigned_agent、followup状态、首次回复时长。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭”。\n2. 详情抽屉展示“工单ID、assigned_agent、followup状态、首次回复时长”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 客服中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:客服中心;筛选:来源=IM转人工/售后/风险/电话;状态=待分配/处理中/等待用户/已关闭 | 待执行\nTC-PROTO-0198 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 风险中心按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“风险中心”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝;详情字段=risk_signal、risk_case、blacklist_entity | 1. 打开“风险中心”页面。\n2. 在筛选区按业务条件选择或输入:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:risk_signal、risk_case、blacklist_entity。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝”。\n2. 详情抽屉展示“risk_signal、risk_case、blacklist_entity”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 风险中心查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:风险中心;筛选:风险类型=强关联/弱关联/黑名单/双重退款;状态=复核中/已放行/已拒绝 | 待执行\nTC-PROTO-0199 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 评价追踪按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“评价追踪”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=核验状态=已提交/展示成功/暂不可核验/异常观察;详情字段=submission_id、display_check、retry_count、completion_rate | 1. 打开“评价追踪”页面。\n2. 在筛选区按业务条件选择或输入:核验状态=已提交/展示成功/暂不可核验/异常观察。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:submission_id、display_check、retry_count、completion_rate。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“核验状态=已提交/展示成功/暂不可核验/异常观察”。\n2. 详情抽屉展示“submission_id、display_check、retry_count、completion_rate”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 评价追踪查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:评价追踪;筛选:核验状态=已提交/展示成功/暂不可核验/异常观察 | 待执行\nTC-PROTO-0200 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | KOC/KOL按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“KOC/KOL”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置;详情字段=creator、Brief、CODE、返点、内容链接 | 1. 打开“KOC/KOL”页面。\n2. 在筛选区按业务条件选择或输入:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:creator、Brief、CODE、返点、内容链接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置”。\n2. 详情抽屉展示“creator、Brief、CODE、返点、内容链接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | KOC/KOL查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:KOC/KOL;筛选:任务状态=待确认/执行中/逾期/已完成;CODE状态=待确认/已配置 | 待执行\nTC-PROTO-0201 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 审计通知按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“审计通知”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围;详情字段=日志ID、操作者、对象、动作、结果 | 1. 打开“审计通知”页面。\n2. 在筛选区按业务条件选择或输入:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:日志ID、操作者、对象、动作、结果。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围”。\n2. 详情抽屉展示“日志ID、操作者、对象、动作、结果”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 审计通知查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:审计通知;筛选:动作类型=导出/查看敏感信息/审批/黑名单同步;时间范围 | 待执行\nTC-PROTO-0202 | 用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 系统管理按业务筛选条件查询并打开详情抽屉 | P2 | 用户已进入“系统管理”;页面存在筛选区、查询按钮、列表和详情入口;当前用户有该页面查询权限。 | 筛选条件=角色、部门、站点、权限点、账号状态;详情字段=账号、角色、数据范围、权限点、离职交接 | 1. 打开“系统管理”页面。\n2. 在筛选区按业务条件选择或输入:角色、部门、站点、权限点、账号状态。\n3. 点击“筛选/查询”。\n4. 在结果列表选择第一条记录,点击“详情/查看”。\n5. 在详情抽屉中核对字段:账号、角色、数据范围、权限点、离职交接。\n6. 关闭详情抽屉并点击“重置”。 | 1. 查询结果均符合“角色、部门、站点、权限点、账号状态”。\n2. 详情抽屉展示“账号、角色、数据范围、权限点、离职交接”。\n3. 关闭详情不清空列表;点击重置后恢复默认查询。\n4. 无数据时显示暂无数据,不沿用旧详情。 | 列表字段、详情字段、筛选条件和后端查询参数一致;重置后不残留旧条件。 | 用户只能查询授权站点、部门、角色范围内数据;详情敏感字段脱敏。 | 系统管理查询、详情、重置、空状态均可用。 | 对应子系统页面与数据对象章节 | 单文件页面:系统管理;筛选:角色、部门、站点、权限点、账号状态 | 待执行\nTC-PROTO-0203 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度剩余1次预警 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,in_progress=0,reserved=0,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,in_progress=0,reserved=0,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:允许预占但进入预警池。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度剩余1次预警 | 待执行\nTC-PROTO-0204 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:测评额度已用3且预占1再预占1 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=used=3,reserved=1,count=1,limit=4 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:used=3,reserved=1,count=1,limit=4。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:阻止预占,状态exceeded。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:测评额度已用3且预占1再预占1 | 待执行\nTC-PROTO-0205 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:免评额度独立预占 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=review used=4, exemption used=0,count=1 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:review used=4, exemption used=0,count=1。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:免评可预占,测评不可预占。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:免评额度独立预占 | 待执行\nTC-PROTO-0206 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:累计12提交后不因未展示回退 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=lifetime=11,提交评价后Amazon未展示 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:lifetime=11,提交评价后Amazon未展示。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:lifetime变12且不回退。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:累计12提交后不因未展示回退 | 待执行\nTC-PROTO-0207 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:预占超时自动释放 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=reservation.status=RESERVED且expires_at已过 | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:reservation.status=RESERVED且expires_at已过。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:状态EXPIRED/RELEASED,remaining恢复。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:预占超时自动释放 | 待执行\nTC-PROTO-0208 | 用户运营系统-单文件.html | 额度频控 | 额度与频控 | 异常场景 | 额度边界:跨计划重复入选 | P1 | 已进入额度频控页面;准备真实人额度台账、预占记录和关联计划。 | 边界数据=同person同时进入PLAN-A和PLAN-B | 1. 打开额度频控页面。\n2. 查询目标真实人的额度台账。\n3. 按边界条件设置或选择记录:同person同时进入PLAN-A和PLAN-B。\n4. 点击额度检查或批量预占。\n5. 打开发送前终校结果和额度审计记录。 | 1. 系统按边界规则处理:只保留高优先级或先预占计划,另一计划排除/预警。\n2. 台账 used/in_progress/reserved/remaining 计算正确。\n3. 额度审计记录包含计划ID、真实人ID、操作类型和原因。 | person_quota_ledgers与quota_reservations合计一致;提交评价立即影响累计12,Amazon未展示不回退。 | 额度手动调整、重置、放宽必须要求管理员或授权负责人。 | 额度边界不超发、不重复占用、不漏释放。 | 03-额度与频控 M1/M2/M4 | 额度页面边界:跨计划重复入选 | 待执行\nTC-PROTO-0209 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道IM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:APP活跃+已绑定用户;额度、风险、去重均通过。 | 渠道=IM;用户条件=APP活跃+已绑定用户;动作=推送回评卡片 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“APP活跃+已绑定用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“IM”。\n4. 执行“推送回评卡片”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“IM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“im_interaction_records”。\n3. 后续处理符合:用户回复后重新校验身份/额度/风险。\n4. Dashboard和用户上下文卡可查看触达历史。 | im_interaction_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | IM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:IM | 待执行\nTC-PROTO-0210 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道EDM按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:未注册APP但邮箱可用用户;额度、风险、去重均通过。 | 渠道=EDM;用户条件=未注册APP但邮箱可用用户;动作=发送邮件并追踪送达/打开/点击/回复/退订 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“未注册APP但邮箱可用用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“EDM”。\n4. 执行“发送邮件并追踪送达/打开/点击/回复/退订”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“EDM”作为推荐渠道或可选渠道。\n2. 执行动作后写入“edm_message_events”。\n3. 后续处理符合:回复邮件生成客服工单。\n4. Dashboard和用户上下文卡可查看触达历史。 | edm_message_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | EDM渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:EDM | 待执行\nTC-PROTO-0211 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道APP Push按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;额度、风险、去重均通过。 | 渠道=APP Push;用户条件=绑定新玩具/不活跃/计划到期/Listing紧急/活动触发;动作=发送Push并追踪点击打开/忽略/卸载 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“绑定新玩具/不活跃/计划到期/Listing紧急/活动触发”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“APP Push”。\n4. 执行“发送Push并追踪点击打开/忽略/卸载”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“APP Push”作为推荐渠道或可选渠道。\n2. 执行动作后写入“app_touch_events”。\n3. 后续处理符合:点击后分流到提交回评/联系客服/浏览。\n4. Dashboard和用户上下文卡可查看触达历史。 | app_touch_events记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | APP Push渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:APP Push | 待执行\nTC-PROTO-0212 | 用户运营系统-单文件.html | 推送/触达 | 多渠道触达引擎 | 流程测试 | 触达渠道TEL按用户状态执行并产生后续流转 | P1 | 计划已审批;候选用户满足条件:高价值多次无响应或答应配合超时用户;额度、风险、去重均通过。 | 渠道=TEL;用户条件=高价值多次无响应或答应配合超时用户;动作=生成电话任务并记录通话结果 | 1. 进入推送/触达页面。\n2. 选择已审批计划和满足“高价值多次无响应或答应配合超时用户”的候选用户。\n3. 点击渠道路由,确认推荐渠道为“TEL”。\n4. 执行“生成电话任务并记录通话结果”。\n5. 查看触达历史和后续流转。\n6. 模拟用户响应或失败事件。 | 1. 系统选择“TEL”作为推荐渠道或可选渠道。\n2. 执行动作后写入“tel_call_records”。\n3. 后续处理符合:未接通小于3次重拨,大于等于3次降级EDM或关闭。\n4. Dashboard和用户上下文卡可查看触达历史。 | tel_call_records记录person_id、plan_id、channel/status、发生时间;channel_dedup_records记录允许或阻断原因。 | 触达发送需通过终校;退订、强风险、未关闭工单用户不得发送。 | TEL渠道触达、事件追踪、后续流转完整。 | 04-多渠道触达引擎 M1-M7 | 推送/触达页面渠道:TEL | 待执行\nTC-PROTO-0213 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理截图证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=截图;包含ASIN和评论内容 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=截图;包含ASIN和评论内容。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并触发quota commit。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:截图证据登记 | 待执行\nTC-PROTO-0214 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理链接证据登记 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=evidence_type=Review Link;链接可打开 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:evidence_type=Review Link;链接可打开。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:记录提交事实并进入展示核验。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:链接证据登记 | 待执行\nTC-PROTO-0215 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理人工核验展示成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=人工;check_result=DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=人工;check_result=DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:计入计划完成数并更新ASIN健康。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:人工核验展示成功 | 待执行\nTC-PROTO-0216 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理自动核验未展示 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_method=自动;check_result=NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_method=自动;check_result=NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:进入异常观察队列。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:自动核验未展示 | 待执行\nTC-PROTO-0217 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理暂不可核验 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=check_result=UNVERIFIABLE;原因=Amazon审核中 | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:check_result=UNVERIFIABLE;原因=Amazon审核中。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:保留已提交事实并定期复查。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:暂不可核验 | 待执行\nTC-PROTO-0218 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察复查成功 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=OBSERVING重试后DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:OBSERVING重试后DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:转CONFIRMED并回流计划。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察复查成功 | 待执行\nTC-PROTO-0219 | 用户运营系统-单文件.html | 评价追踪 | 评价结果追踪 | 流程测试 | 评价追踪处理异常观察期满失败 | P1 | 已存在执行中计划、真实人、客服工单或IM互动记录;用户声称已提交评价。 | 评价数据=retry_count超过阈值仍NOT_DISPLAYED | 1. 进入评价追踪页面。\n2. 点击“登记评价提交”或打开待核验记录。\n3. 录入/选择数据:retry_count超过阈值仍NOT_DISPLAYED。\n4. 提交后查看提交记录。\n5. 执行展示核验或等待复查。\n6. 回到计划详情查看完成度。 | 1. 系统处理结果:标记ABNORMAL并通知运营。\n2. 用户真实提交评价和Amazon展示核验被拆分记录。\n3. 额度按提交事实计数,计划完成按展示确认计数。\n4. 异常观察有复查记录和通知。 | review_submission_records、review_display_checks、review_results三类数据一致;quota_updated状态正确。 | 只有客服/运营可登记提交;展示核验人工确认需运营负责人或授权角色。 | 提交事实、展示事实、额度、计划完成度四者口径清晰且可追溯。 | 07-评价结果追踪 M1-M4 | 评价追踪场景:异常观察期满失败 | 待执行\nTC-PROTO-0220 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“需求中心”;存在业务条件:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。 | 按钮=查看完整信息;条件=JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:JOYHUB ID、邮箱、电话、设备号、订单号默认脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:打开详情后点击查看完整信息。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0221 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:分配 | P2 | 系统管理员已进入“需求中心”;存在业务条件:测评需求Amazon已批准待用户运营接收。 | 按钮=分配;条件=测评需求Amazon已批准待用户运营接收 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:测评需求Amazon已批准待用户运营接收。\n3. 点击该行或页面上的“分配”按钮。\n4. 按页面提示执行:选择下一负责人为用户运营负责人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配”入口可用且文案正确。\n2. 操作后结果为:负责人变更并通知。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=分配 | 待执行\nTC-PROTO-0222 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求中心 | 系统总览 | 功能测试 | 需求中心按钮级操作:审批/确认 | P2 | 系统管理员已进入“需求中心”;存在业务条件:需求评分4.46低于4.5。 | 按钮=审批/确认;条件=需求评分4.46低于4.5 | 1. 从管理员首页左侧导航进入“需求中心”。\n2. 在列表中找到满足条件的记录:需求评分4.46低于4.5。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:选择通过/确认并填写计划建议。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:需求进入待生成计划。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 需求中心的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=需求中心;按钮=审批/确认 | 待执行\nTC-PROTO-0223 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:查看完整信息 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:评分4.21接近4.2紧急阈值。 | 按钮=查看完整信息;条件=评分4.21接近4.2紧急阈值 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:评分4.21接近4.2紧急阈值。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:查看ASIN完整站点与评价数据。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:敏感数据按权限展示。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=查看完整信息 | 待执行\nTC-PROTO-0224 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:创建紧急策略 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:紧急Listing未处理7条。 | 按钮=创建紧急策略;条件=紧急Listing未处理7条 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:紧急Listing未处理7条。\n3. 点击该行或页面上的“创建紧急策略”按钮。\n4. 按页面提示执行:填写策略参与人员与截止时间。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “创建紧急策略”入口可用且文案正确。\n2. 操作后结果为:生成紧急策略审批事项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的创建紧急策略动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=创建紧急策略 | 待执行\nTC-PROTO-0225 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | Listing 管理 | 系统总览 | 功能测试 | Listing 管理按钮级操作:审批/确认 | P2 | 系统管理员已进入“Listing 管理”;存在业务条件:待系统管理员确认。 | 按钮=审批/确认;条件=待系统管理员确认 | 1. 从管理员首页左侧导航进入“Listing 管理”。\n2. 在列表中找到满足条件的记录:待系统管理员确认。\n3. 点击该行或页面上的“审批/确认”按钮。\n4. 按页面提示执行:确认联合策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “审批/确认”入口可用且文案正确。\n2. 操作后结果为:状态进入用户运营执行。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | Listing 管理的审批/确认动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=Listing 管理;按钮=审批/确认 | 待执行\nTC-PROTO-0226 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:生成计划 | P2 | 系统管理员已进入“计划中心”;存在业务条件:已确认需求存在且目标量明确。 | 按钮=生成计划;条件=已确认需求存在且目标量明确 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:已确认需求存在且目标量明确。\n3. 点击该行或页面上的“生成计划”按钮。\n4. 按页面提示执行:选择推新/回评/免评并拆分计划项。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成计划”入口可用且文案正确。\n2. 操作后结果为:生成计划ID和计划项。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的生成计划动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=生成计划 | 待执行\nTC-PROTO-0227 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:批量审批 | P2 | 系统管理员已进入“计划中心”;存在业务条件:多条计划处于待审批。 | 按钮=批量审批;条件=多条计划处于待审批 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:多条计划处于待审批。\n3. 点击该行或页面上的“批量审批”按钮。\n4. 按页面提示执行:勾选多条计划并提交统一审批意见。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “批量审批”入口可用且文案正确。\n2. 操作后结果为:批量生成审批记录。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的批量审批动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=批量审批 | 待执行\nTC-PROTO-0228 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 计划中心 | 系统总览 | 功能测试 | 计划中心按钮级操作:流转 | P2 | 系统管理员已进入“计划中心”;存在业务条件:计划覆盖状态部分覆盖。 | 按钮=流转;条件=计划覆盖状态部分覆盖 | 1. 从管理员首页左侧导航进入“计划中心”。\n2. 在列表中找到满足条件的记录:计划覆盖状态部分覆盖。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看计划状态流转记录。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:展示创建、审批、执行节点。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 计划中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=计划中心;按钮=流转 | 待执行\nTC-PROTO-0229 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:计划与推送分配 | P2 | 系统管理员已进入“推送中心”;存在业务条件:周度推送计划待审。 | 按钮=计划与推送分配;条件=周度推送计划待审 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:周度推送计划待审。\n3. 点击该行或页面上的“计划与推送分配”按钮。\n4. 按页面提示执行:分配IM/EDM/TEL/App Push策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “计划与推送分配”入口可用且文案正确。\n2. 操作后结果为:生成推送任务。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的计划与推送分配动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=计划与推送分配 | 待执行\nTC-PROTO-0230 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:风险复核 | P2 | 系统管理员已进入“推送中心”;存在业务条件:退订率高于基线。 | 按钮=风险复核;条件=退订率高于基线 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线。\n3. 点击该行或页面上的“风险复核”按钮。\n4. 按页面提示执行:查看人群、素材、文案并选择暂停同策略。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “风险复核”入口可用且文案正确。\n2. 操作后结果为:推送状态变暂停待审。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的风险复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=风险复核 | 待执行\nTC-PROTO-0231 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 推送中心 | 系统总览 | 功能测试 | 推送中心按钮级操作:导出 | P2 | 系统管理员已进入“推送中心”;存在业务条件:当前筛选为推送风险。 | 按钮=导出;条件=当前筛选为推送风险 | 1. 从管理员首页左侧导航进入“推送中心”。\n2. 在列表中找到满足条件的记录:当前筛选为推送风险。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出推送ID、计划、渠道、发送点击回复退订。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:导出文件脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 推送中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=推送中心;按钮=导出 | 待执行\nTC-PROTO-0232 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:分配工单 | P2 | 系统管理员已进入“客服中心”;存在业务条件:差评跟进客服升级。 | 按钮=分配工单;条件=差评跟进客服升级 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:差评跟进客服升级。\n3. 点击该行或页面上的“分配工单”按钮。\n4. 按页面提示执行:选择客服A并填写分配原因。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “分配工单”入口可用且文案正确。\n2. 操作后结果为:工单状态变处理中。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的分配工单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=分配工单 | 待执行\nTC-PROTO-0233 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:流转 | P2 | 系统管理员已进入“客服中心”;存在业务条件:承诺配合用户待回访。 | 按钮=流转;条件=承诺配合用户待回访 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:承诺配合用户待回访。\n3. 点击该行或页面上的“流转”按钮。\n4. 按页面提示执行:查看待回访和请假0.5天影响。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “流转”入口可用且文案正确。\n2. 操作后结果为:生成回访待办。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的流转动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=流转 | 待执行\nTC-PROTO-0234 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服中心 | 系统总览 | 功能测试 | 客服中心按钮级操作:导出 | P2 | 系统管理员已进入“客服中心”;存在业务条件:菲律宾团队管理。 | 按钮=导出;条件=菲律宾团队管理 | 1. 从管理员首页左侧导航进入“客服中心”。\n2. 在列表中找到满足条件的记录:菲律宾团队管理。\n3. 点击该行或页面上的“导出”按钮。\n4. 按页面提示执行:导出工作时长、出勤、人均产出。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “导出”入口可用且文案正确。\n2. 操作后结果为:仅主管可导出。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 客服中心的导出动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=客服中心;按钮=导出 | 待执行\nTC-PROTO-0235 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:同步黑名单 | P2 | 系统管理员已进入“风险中心”;存在业务条件:接口超时失败待重试。 | 按钮=同步黑名单;条件=接口超时失败待重试 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:接口超时失败待重试。\n3. 点击该行或页面上的“同步黑名单”按钮。\n4. 按页面提示执行:点击同步黑名单。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “同步黑名单”入口可用且文案正确。\n2. 操作后结果为:失败保留并进入重试队列。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的同步黑名单动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=同步黑名单 | 待执行\nTC-PROTO-0236 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:规则复核 | P2 | 系统管理员已进入“风险中心”;存在业务条件:退订率高于基线规则提醒。 | 按钮=规则复核;条件=退订率高于基线规则提醒 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:退订率高于基线规则提醒。\n3. 点击该行或页面上的“规则复核”按钮。\n4. 按页面提示执行:查看规则依据并确认/误报。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “规则复核”入口可用且文案正确。\n2. 操作后结果为:复核结论写入风险事件。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的规则复核动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=规则复核 | 待执行\nTC-PROTO-0237 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统总览 | 功能测试 | 风险中心按钮级操作:查看完整信息 | P2 | 系统管理员已进入“风险中心”;存在业务条件:Profile/邮箱/设备号脱敏。 | 按钮=查看完整信息;条件=Profile/邮箱/设备号脱敏 | 1. 从管理员首页左侧导航进入“风险中心”。\n2. 在列表中找到满足条件的记录:Profile/邮箱/设备号脱敏。\n3. 点击该行或页面上的“查看完整信息”按钮。\n4. 按页面提示执行:授权角色查看完整主体摘要。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “查看完整信息”入口可用且文案正确。\n2. 操作后结果为:记录敏感访问审计。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 风险中心的查看完整信息动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=风险中心;按钮=查看完整信息 | 待执行\nTC-PROTO-0238 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:生成/下载报表 | P2 | 系统管理员已进入“报表中心”;存在业务条件:Listing健康日报每日08:30自动生成。 | 按钮=生成/下载报表;条件=Listing健康日报每日08:30自动生成 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:Listing健康日报每日08:30自动生成。\n3. 点击该行或页面上的“生成/下载报表”按钮。\n4. 按页面提示执行:点击生成/下载报表。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “生成/下载报表”入口可用且文案正确。\n2. 操作后结果为:生成记录可下载且脱敏。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的生成/下载报表动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=生成/下载报表 | 待执行\nTC-PROTO-0239 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统总览 | 功能测试 | 报表中心按钮级操作:上传记录 | P2 | 系统管理员已进入“报表中心”;存在业务条件:推送效果与风险复盘支持上传补充记录。 | 按钮=上传记录;条件=推送效果与风险复盘支持上传补充记录 | 1. 从管理员首页左侧导航进入“报表中心”。\n2. 在列表中找到满足条件的记录:推送效果与风险复盘支持上传补充记录。\n3. 点击该行或页面上的“上传记录”按钮。\n4. 按页面提示执行:上传人工复核附件。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “上传记录”入口可用且文案正确。\n2. 操作后结果为:附件关联报表ID。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 报表中心的上传记录动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=报表中心;按钮=上传记录 | 待执行\nTC-PROTO-0240 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:新建账号 | P2 | 系统管理员已进入“系统管理”;存在业务条件:按部门角色站点数据范围开通。 | 按钮=新建账号;条件=按部门角色站点数据范围开通 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:按部门角色站点数据范围开通。\n3. 点击该行或页面上的“新建账号”按钮。\n4. 按页面提示执行:录入账号、部门、角色、站点范围。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “新建账号”入口可用且文案正确。\n2. 操作后结果为:账号可登录且权限生效。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的新建账号动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=新建账号 | 待执行\nTC-PROTO-0241 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理 | 系统总览 | 功能测试 | 系统管理按钮级操作:离职管理 | P2 | 系统管理员已进入“系统管理”;存在业务条件:停用账号交接任务回收权限。 | 按钮=离职管理;条件=停用账号交接任务回收权限 | 1. 从管理员首页左侧导航进入“系统管理”。\n2. 在列表中找到满足条件的记录:停用账号交接任务回收权限。\n3. 点击该行或页面上的“离职管理”按钮。\n4. 按页面提示执行:选择离职员工并指定交接人。\n5. 提交后回到列表并重新打开该记录详情。 | 1. “离职管理”入口可用且文案正确。\n2. 操作后结果为:账号停用且敏感权限回收。\n3. 列表状态、详情状态流转记录和通知/审计同步更新。 | 操作前后记录ID不变;状态、负责人、处理意见、附件或导出记录按动作写入。 | 仅授权角色可执行该按钮动作;敏感查看、导出、审批、黑名单同步需独立授权。 | 系统管理的离职管理动作可执行、可追踪、无越权。 | 00-系统总览;02-需求与计划管理;04-多渠道触达;05-客服工单;06-风险与反欺诈;07-评价结果追踪;09-审计与通知 | v7页面=系统管理;按钮=离职管理 | 待执行\nTC-PROTO-0242 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:搜索JOYHUB用户ID | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=输入JOYHUB ID并查询;预期=返回用户主档、标签、身份、产品关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:输入JOYHUB ID并查询。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“搜索JOYHUB用户ID”。\n2. 输出结果为:返回用户主档、标签、身份、产品关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的搜索JOYHUB用户ID可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/搜索JOYHUB用户ID | 待执行\nTC-PROTO-0243 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:按国家和近7天EDM次数筛选 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=国家=US;EDM近7天>0;预期=返回可用于触达的人群 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:国家=US;EDM近7天>0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按国家和近7天EDM次数筛选”。\n2. 输出结果为:返回可用于触达的人群。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的按国家和近7天EDM次数筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/按国家和近7天EDM次数筛选 | 待执行\nTC-PROTO-0244 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-用户管理 | 用户身份与上下文 | 功能测试 | 现有ERP 用户管理:打开用户画像详情 | P2 | v10原型进入现有ERP模块;当前页为“用户管理”;用户具备字段查询权限。 | 操作=点击用户行详情;预期=展示注册/活跃时间、标签、产品、活动 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“用户管理”。\n3. 执行业务操作:点击用户行详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“打开用户画像详情”。\n2. 输出结果为:展示注册/活跃时间、标签、产品、活动。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 用户管理的打开用户画像详情可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:用户管理/打开用户画像详情 | 待执行\nTC-PROTO-0245 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:新增标签覆盖查询 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=兴趣;打标方式=系统;预期=展示覆盖人数和最新打标时间 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签分类=兴趣;打标方式=系统。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“新增标签覆盖查询”。\n2. 输出结果为:展示覆盖人数和最新打标时间。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的新增标签覆盖查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/新增标签覆盖查询 | 待执行\nTC-PROTO-0246 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-用户标签:覆盖人数异常提示 | P2 | v10原型进入现有ERP模块;当前页为“公域-用户标签”;用户具备字段查询权限。 | 操作=标签覆盖人数突增或为0;预期=展示异常覆盖提示 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-用户标签”。\n3. 执行业务操作:标签覆盖人数突增或为0。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“覆盖人数异常提示”。\n2. 输出结果为:展示异常覆盖提示。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-用户标签的覆盖人数异常提示可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-用户标签/覆盖人数异常提示 | 待执行\nTC-PROTO-0247 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:产品标签关联Listing | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=选择产品标签并查看关联产品;预期=展示产品-品牌-Listing/ASIN关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:选择产品标签并查看关联产品。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“产品标签关联Listing”。\n2. 输出结果为:展示产品-品牌-Listing/ASIN关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的产品标签关联Listing可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/产品标签关联Listing | 待执行\nTC-PROTO-0248 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-公域-产品标签 | 用户身份与上下文 | 功能测试 | 现有ERP 公域-产品标签:按创建时间筛选标签 | P2 | v10原型进入现有ERP模块;当前页为“公域-产品标签”;用户具备字段查询权限。 | 操作=开始/截止时间;预期=只返回时间范围内标签 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“公域-产品标签”。\n3. 执行业务操作:开始/截止时间。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“按创建时间筛选标签”。\n2. 输出结果为:只返回时间范围内标签。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 公域-产品标签的按创建时间筛选标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:公域-产品标签/按创建时间筛选标签 | 待执行\nTC-PROTO-0249 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:客服分组标签查询 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签分类=客服分组;预期=输出私域用户标签与社群/活动关系 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签分类=客服分组。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“客服分组标签查询”。\n2. 输出结果为:输出私域用户标签与社群/活动关系。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的客服分组标签查询可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/客服分组标签查询 | 待执行\nTC-PROTO-0250 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-私域-用户标签 | 用户身份与上下文 | 功能测试 | 现有ERP 私域-用户标签:风险用户隔离标签 | P2 | v10原型进入现有ERP模块;当前页为“私域-用户标签”;用户具备字段查询权限。 | 操作=标签状态=启用;风险相关;预期=可作为客服/推送排除条件 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“私域-用户标签”。\n3. 执行业务操作:标签状态=启用;风险相关。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“风险用户隔离标签”。\n2. 输出结果为:可作为客服/推送排除条件。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 私域-用户标签的风险用户隔离标签可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:私域-用户标签/风险用户隔离标签 | 待执行\nTC-PROTO-0251 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:查看多语言图标 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=打开身份详情;预期=展示英/德/日图标PNG | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:打开身份详情。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“查看多语言图标”。\n2. 输出结果为:展示英/德/日图标PNG。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的查看多语言图标可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/查看多语言图标 | 待执行\nTC-PROTO-0252 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份风险等级筛选 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=身份风险等级=高;预期=进入风险用户池候选 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:身份风险等级=高。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份风险等级筛选”。\n2. 输出结果为:进入风险用户池候选。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份风险等级筛选可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份风险等级筛选 | 待执行\nTC-PROTO-0253 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP-身份管理 | 用户身份与上下文 | 功能测试 | 现有ERP 身份管理:身份状态停用 | P2 | v10原型进入现有ERP模块;当前页为“身份管理”;用户具备字段查询权限。 | 操作=将风险身份置为停用;预期=后续筛选不再作为可选身份 | 1. 打开v10管理员原型。\n2. 点击“现有ERP”并进入“身份管理”。\n3. 执行业务操作:将风险身份置为停用。\n4. 查看列表、字段表、关系对象和MVP纳入方式。\n5. 点击导出现有关系或查看详情。 | 1. 页面完成操作“身份状态停用”。\n2. 输出结果为:后续筛选不再作为可选身份。\n3. 字段来源和关系对象展示清晰。\n4. 导出/详情遵循字段脱敏。 | 现有字段、查询条件、关系对象、MVP纳入方式保持一致;导出数据不超范围。 | 无字段权限角色不能查看完整身份、邮箱、风险等级等敏感字段。 | 身份管理的身份状态停用可用于MVP建模和测试追踪。 | 01-用户身份与上下文;v10现有ERP字段说明 | v10现有ERP:身份管理/身份状态停用 | 待执行\nTC-PROTO-0254 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态待分配执行分配给客服A | P2 | 客服执行看板存在状态为“待分配”的工单;当前用户对该工单有处理权限。 | 当前状态=待分配;动作=分配给客服A | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“待分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“分配给客服A”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“待分配”的工单可执行“分配给客服A”。\n2. 执行结果:assigned_agent=客服A;写入assignment_logs。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=待分配;动作=分配给客服A | 待执行\nTC-PROTO-0255 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已分配执行客服首次回复 | P2 | 客服执行看板存在状态为“已分配”的工单;当前用户对该工单有处理权限。 | 当前状态=已分配;动作=客服首次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已分配”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“客服首次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已分配”的工单可执行“客服首次回复”。\n2. 执行结果:记录首次回复时长并进入处理中。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已分配;动作=客服首次回复 | 待执行\nTC-PROTO-0256 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待用户回复 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待用户回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待用户回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待用户回复”。\n2. 执行结果:状态变等待用户,设置提醒时间。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待用户回复 | 待执行\nTC-PROTO-0257 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择等待内部协同 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择等待内部协同 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择等待内部协同”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择等待内部协同”。\n2. 执行结果:状态变等待内部,通知内部负责人。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择等待内部协同 | 待执行\nTC-PROTO-0258 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择答应配合 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择答应配合 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择答应配合”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择答应配合”。\n2. 执行结果:创建support_followups,状态PROMISED。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择答应配合 | 待执行\nTC-PROTO-0259 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态处理中执行选择疑似诈骗 | P2 | 客服执行看板存在状态为“处理中”的工单;当前用户对该工单有处理权限。 | 当前状态=处理中;动作=选择疑似诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“处理中”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“选择疑似诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“处理中”的工单可执行“选择疑似诈骗”。\n2. 执行结果:生成风险案件并标记工单疑似诈骗。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=处理中;动作=选择疑似诈骗 | 待执行\nTC-PROTO-0260 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待用户执行用户回复后继续处理 | P2 | 客服执行看板存在状态为“等待用户”的工单;当前用户对该工单有处理权限。 | 当前状态=等待用户;动作=用户回复后继续处理 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待用户”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“用户回复后继续处理”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待用户”的工单可执行“用户回复后继续处理”。\n2. 执行结果:状态回到处理中并记录用户消息。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待用户;动作=用户回复后继续处理 | 待执行\nTC-PROTO-0261 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态等待内部执行内部反馈完成 | P2 | 客服执行看板存在状态为“等待内部”的工单;当前用户对该工单有处理权限。 | 当前状态=等待内部;动作=内部反馈完成 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“等待内部”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“内部反馈完成”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“等待内部”的工单可执行“内部反馈完成”。\n2. 执行结果:状态回到处理中并追加内部协同记录。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=等待内部;动作=内部反馈完成 | 待执行\nTC-PROTO-0262 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已解决执行关闭工单 | P2 | 客服执行看板存在状态为“已解决”的工单;当前用户对该工单有处理权限。 | 当前状态=已解决;动作=关闭工单 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已解决”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“关闭工单”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已解决”的工单可执行“关闭工单”。\n2. 执行结果:resolved_at和closed状态写入。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已解决;动作=关闭工单 | 待执行\nTC-PROTO-0263 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认误报 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认误报 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认误报”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认误报”。\n2. 执行结果:工单可回到处理中或已解决。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认误报 | 待执行\nTC-PROTO-0264 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态疑似诈骗执行风险确认诈骗 | P2 | 客服执行看板存在状态为“疑似诈骗”的工单;当前用户对该工单有处理权限。 | 当前状态=疑似诈骗;动作=风险确认诈骗 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“疑似诈骗”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“风险确认诈骗”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“疑似诈骗”的工单可执行“风险确认诈骗”。\n2. 执行结果:工单关闭并进入黑名单同步候选。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=疑似诈骗;动作=风险确认诈骗 | 待执行\nTC-PROTO-0265 | 客服执行.html | 客服工单状态机 | 客服工单与管理 | 流程测试 | 客服工单状态已关闭执行尝试再次回复 | P2 | 客服执行看板存在状态为“已关闭”的工单;当前用户对该工单有处理权限。 | 当前状态=已关闭;动作=尝试再次回复 | 1. 登录客服执行看板。\n2. 在工单列表筛选状态“已关闭”。\n3. 打开一条工单详情,查看用户上下文、聊天记录和处理历史。\n4. 点击处理动作“尝试再次回复”。\n5. 按要求填写处理说明并提交。\n6. 返回列表确认状态与数量变化。 | 1. 状态为“已关闭”的工单可执行“尝试再次回复”。\n2. 执行结果:禁止直接回复,需重新打开或新建工单。\n3. 工单处理历史追加记录,绩效指标按规则更新。 | support_tickets.status、resolved_at、assigned_agent、followup状态与操作结果一致。 | 客服仅处理本人分配工单;组长/主管可处理组内改派和复核。 | 客服状态机动作合法,不能出现非法跳转。 | 05-客服工单与管理 状态流转 | 客服状态=已关闭;动作=尝试再次回复 | 待执行\nTC-PROTO-0266 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色Amazon运营模块访问与按钮权限 | P1 | 准备角色为“Amazon运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=Amazon运营;可见范围=需求中心/ASIN/计划审核查看;允许=创建需求、查看ASIN健康;限制=不能审批用户运营计划或查看完整用户敏感信息 | 1. 使用“Amazon运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:创建需求、查看ASIN健康。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批用户运营计划或查看完整用户敏感信息。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. Amazon运营只能访问“需求中心/ASIN/计划审核查看”。\n2. 允许动作“创建需求、查看ASIN健康”可正常执行。\n3. 限制动作“不能审批用户运营计划或查看完整用户敏感信息”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | Amazon运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=Amazon运营 | 待执行\nTC-PROTO-0267 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色用户运营模块访问与按钮权限 | P1 | 准备角色为“用户运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=用户运营;可见范围=需求中心/计划中心/推送中心/用户中心;允许=评估需求、生成计划、圈选人群、触达;限制=不能同步黑名单或配置系统权限 | 1. 使用“用户运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:评估需求、生成计划、圈选人群、触达。\n4. 尝试通过URL hash或按钮执行限制动作:不能同步黑名单或配置系统权限。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 用户运营只能访问“需求中心/计划中心/推送中心/用户中心”。\n2. 允许动作“评估需求、生成计划、圈选人群、触达”可正常执行。\n3. 限制动作“不能同步黑名单或配置系统权限”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 用户运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=用户运营 | 待执行\nTC-PROTO-0268 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服模块访问与按钮权限 | P1 | 准备角色为“客服”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服;可见范围=客服中心/用户上下文摘要/评价登记;允许=处理工单、登记评价提交;限制=不能查看跨团队绩效和完整设备号 | 1. 使用“客服”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:处理工单、登记评价提交。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看跨团队绩效和完整设备号。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服只能访问“客服中心/用户上下文摘要/评价登记”。\n2. 允许动作“处理工单、登记评价提交”可正常执行。\n3. 限制动作“不能查看跨团队绩效和完整设备号”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服 | 待执行\nTC-PROTO-0269 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色客服主管模块访问与按钮权限 | P1 | 准备角色为“客服主管”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=客服主管;可见范围=客服中心/客服执行看板/绩效;允许=分配工单、查看组内绩效、排班;限制=不能审批免评计划除非授权 | 1. 使用“客服主管”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:分配工单、查看组内绩效、排班。\n4. 尝试通过URL hash或按钮执行限制动作:不能审批免评计划除非授权。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 客服主管只能访问“客服中心/客服执行看板/绩效”。\n2. 允许动作“分配工单、查看组内绩效、排班”可正常执行。\n3. 限制动作“不能审批免评计划除非授权”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 客服主管权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=客服主管 | 待执行\nTC-PROTO-0270 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色风险负责人模块访问与按钮权限 | P1 | 准备角色为“风险负责人”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=风险负责人;可见范围=风险中心/黑名单/审计;允许=复核风险、同步黑名单、标记误报;限制=不能修改计划目标量 | 1. 使用“风险负责人”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:复核风险、同步黑名单、标记误报。\n4. 尝试通过URL hash或按钮执行限制动作:不能修改计划目标量。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 风险负责人只能访问“风险中心/黑名单/审计”。\n2. 允许动作“复核风险、同步黑名单、标记误报”可正常执行。\n3. 限制动作“不能修改计划目标量”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 风险负责人权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=风险负责人 | 待执行\nTC-PROTO-0271 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色KOC运营模块访问与按钮权限 | P1 | 准备角色为“KOC运营”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=KOC运营;可见范围=KOC/KOL协作;允许=维护Brief、CODE、内容记录;限制=不能查看普通用户完整身份线索 | 1. 使用“KOC运营”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:维护Brief、CODE、内容记录。\n4. 尝试通过URL hash或按钮执行限制动作:不能查看普通用户完整身份线索。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. KOC运营只能访问“KOC/KOL协作”。\n2. 允许动作“维护Brief、CODE、内容记录”可正常执行。\n3. 限制动作“不能查看普通用户完整身份线索”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | KOC运营权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=KOC运营 | 待执行\nTC-PROTO-0272 | 用户运营系统-单文件.html | 全局权限 | 系统总览 | 权限校验 | 单文件系统角色系统管理员模块访问与按钮权限 | P1 | 准备角色为“系统管理员”的账号;系统存在需求、计划、用户、工单、风险、评价等数据。 | 角色=系统管理员;可见范围=全部模块;允许=账号权限、审计、配置、跨部门看板;限制=敏感访问仍需审计 | 1. 使用“系统管理员”账号登录单文件系统。\n2. 逐个访问导航模块,记录可见页面。\n3. 在可见页面尝试执行允许动作:账号权限、审计、配置、跨部门看板。\n4. 尝试通过URL hash或按钮执行限制动作:敏感访问仍需审计。\n5. 查看审计日志中是否记录越权或敏感访问。 | 1. 系统管理员只能访问“全部模块”。\n2. 允许动作“账号权限、审计、配置、跨部门看板”可正常执行。\n3. 限制动作“敏感访问仍需审计”不可执行,直接URL也无法绕过。\n4. 敏感访问和越权尝试有审计记录。 | 前端菜单、按钮、后端接口权限一致;角色切换后缓存权限刷新。 | 系统管理员权限边界符合角色职责。 | 模块访问、按钮权限、数据范围、审计均正确。 | 00-系统总览 角色前端映射;09-审计与通知中心 | 单文件系统角色=系统管理员 | 待执行\nTC-PROTO-0273 | 用户运营系统-单文件.html | 需求中心 | 需求中心 | 数据校验 | 需求中心执行导出待评估需求并校验导出脱敏与范围 | P2 | 已进入“需求中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=待评估;导出内容=导出demands当前筛选字段 | 1. 打开“需求中心”。\n2. 设置筛选条件:筛选状态=待评估。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出demands当前筛选字段。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=需求中心;导出=导出待评估需求 | 待执行\nTC-PROTO-0274 | 用户运营系统-单文件.html | 计划审核 | 计划审核 | 数据校验 | 计划审核执行导出审批记录并校验导出脱敏与范围 | P2 | 已进入“计划审核”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选审批状态=待审批;导出内容=导出approval_records | 1. 打开“计划审核”。\n2. 设置筛选条件:筛选审批状态=待审批。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出approval_records。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划审核;导出=导出审批记录 | 待执行\nTC-PROTO-0275 | 用户运营系统-单文件.html | 计划中心 | 计划中心 | 数据校验 | 计划中心执行导出计划执行进度并校验导出脱敏与范围 | P2 | 已进入“计划中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=执行中;导出内容=导出计划、计划项、完成率 | 1. 打开“计划中心”。\n2. 设置筛选条件:筛选状态=执行中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出计划、计划项、完成率。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=计划中心;导出=导出计划执行进度 | 待执行\nTC-PROTO-0276 | 用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 数据校验 | ASIN/Listing执行导出健康风险ASIN并校验导出脱敏与范围 | P2 | 已进入“ASIN/Listing”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选健康状态=风险/严重风险;导出内容=导出评分、评价数、差评数 | 1. 打开“ASIN/Listing”。\n2. 设置筛选条件:筛选健康状态=风险/严重风险。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出评分、评价数、差评数。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=ASIN/Listing;导出=导出健康风险ASIN | 待执行\nTC-PROTO-0277 | 用户运营系统-单文件.html | 用户中心 | 用户中心 | 数据校验 | 用户中心执行导出人群包并校验导出脱敏与范围 | P2 | 已进入“用户中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选标签/国家/产品绑定;导出内容=导出脱敏用户ID和标签 | 1. 打开“用户中心”。\n2. 设置筛选条件:筛选标签/国家/产品绑定。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出脱敏用户ID和标签。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=用户中心;导出=导出人群包 | 待执行\nTC-PROTO-0278 | 用户运营系统-单文件.html | 额度频控 | 额度频控 | 数据校验 | 额度频控执行导出额度预警用户并校验导出脱敏与范围 | P2 | 已进入“额度频控”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=warning/exceeded;导出内容=导出额度台账摘要 | 1. 打开“额度频控”。\n2. 设置筛选条件:筛选status=warning/exceeded。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出额度台账摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=额度频控;导出=导出额度预警用户 | 待执行\nTC-PROTO-0279 | 用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 数据校验 | 推送/触达执行导出退订用户并校验导出脱敏与范围 | P2 | 已进入“推送/触达”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选event=UNSUBSCRIBED;导出内容=导出退订事件和渠道 | 1. 打开“推送/触达”。\n2. 设置筛选条件:筛选event=UNSUBSCRIBED。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出退订事件和渠道。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=推送/触达;导出=导出退订用户 | 待执行\nTC-PROTO-0280 | 用户运营系统-单文件.html | 客服中心 | 客服中心 | 数据校验 | 客服中心执行导出超时工单并校验导出脱敏与范围 | P2 | 已进入“客服中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=等待用户且超时;导出内容=导出工单与负责人 | 1. 打开“客服中心”。\n2. 设置筛选条件:筛选状态=等待用户且超时。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出工单与负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=客服中心;导出=导出超时工单 | 待执行\nTC-PROTO-0281 | 用户运营系统-单文件.html | 风险中心 | 风险中心 | 数据校验 | 风险中心执行导出风险案件并校验导出脱敏与范围 | P2 | 已进入“风险中心”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=人工复核中;导出内容=导出风险摘要脱敏 | 1. 打开“风险中心”。\n2. 设置筛选条件:筛选状态=人工复核中。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出风险摘要脱敏。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=风险中心;导出=导出风险案件 | 待执行\nTC-PROTO-0282 | 用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 数据校验 | 评价追踪执行导出异常观察队列并校验导出脱敏与范围 | P2 | 已进入“评价追踪”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选status=OBSERVING/ABNORMAL;导出内容=导出提交和核验摘要 | 1. 打开“评价追踪”。\n2. 设置筛选条件:筛选status=OBSERVING/ABNORMAL。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出提交和核验摘要。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=评价追踪;导出=导出异常观察队列 | 待执行\nTC-PROTO-0283 | 用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 数据校验 | KOC/KOL执行导出逾期协作任务并校验导出脱敏与范围 | P2 | 已进入“KOC/KOL”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选状态=逾期;导出内容=导出CODE/Brief/负责人 | 1. 打开“KOC/KOL”。\n2. 设置筛选条件:筛选状态=逾期。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出CODE/Brief/负责人。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=KOC/KOL;导出=导出逾期协作任务 | 待执行\nTC-PROTO-0284 | 用户运营系统-单文件.html | 审计通知 | 审计通知 | 数据校验 | 审计通知执行导出敏感动作日志并校验导出脱敏与范围 | P2 | 已进入“审计通知”;当前角色具备导出权限;列表支持筛选和导出。 | 筛选=筛选动作=查看完整信息/导出;导出内容=导出审计日志 | 1. 打开“审计通知”。\n2. 设置筛选条件:筛选动作=查看完整信息/导出。\n3. 点击查询并确认列表有数据。\n4. 点击导出。\n5. 打开导出文件,检查字段、数据范围和脱敏内容。\n6. 回到审计通知页面查询导出日志。 | 1. 导出内容为:导出审计日志。\n2. 导出文件仅包含当前筛选范围。\n3. 敏感字段脱敏。\n4. 审计日志记录导出人、时间、筛选条件和导出对象。 | 导出总数与列表筛选总数一致;脱敏规则与页面展示一致。 | 无导出权限时按钮隐藏或后端拒绝;导出权限与查看完整信息权限分离。 | 导出范围准确、脱敏有效、审计可查。 | 09-审计与通知中心;各子系统数据对象 | 单文件页面=审计通知;导出=导出敏感动作日志 | 待执行\nTC-PROTO-0285 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:重复点击处理卡点 | P2 | 已进入“工作台”;准备可执行场景:重复点击处理卡点。 | 动作=连续点击处理卡点按钮2次;预期=只打开一个详情/处理弹窗,不重复创建处理记录 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击处理卡点按钮2次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只打开一个详情/处理弹窗,不重复创建处理记录。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:重复点击处理卡点 | 待执行\nTC-PROTO-0286 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 工作台 | 系统稳定性与幂等 | 异常场景 | 工作台稳定性校验:刷新后保持时间范围 | P2 | 已进入“工作台”;准备可执行场景:刷新后保持时间范围。 | 动作=选择最近30天后刷新页面;预期=仍显示最近30天或按产品定义恢复默认并不报错 | 1. 打开原型页面“工作台”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:选择最近30天后刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:仍显示最近30天或按产品定义恢复默认并不报错。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:刷新后保持时间范围 | 待执行\nTC-PROTO-0287 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:黑名单同步重复提交 | P2 | 已进入“风险中心”;准备可执行场景:黑名单同步重复提交。 | 动作=同步黑名单按钮连续点击;预期=只生成一次同步任务,第二次提示处理中 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同步黑名单按钮连续点击。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只生成一次同步任务,第二次提示处理中。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:黑名单同步重复提交 | 待执行\nTC-PROTO-0288 | 20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 报表中心 | 系统稳定性与幂等 | 异常场景 | 报表中心稳定性校验:报表生成中重复下载 | P2 | 已进入“报表中心”;准备可执行场景:报表生成中重复下载。 | 动作=报表状态自动生成中点击下载;预期=提示生成中,不下载空文件 | 1. 打开原型页面“报表中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:报表状态自动生成中点击下载。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示生成中,不下载空文件。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:报表生成中重复下载 | 待执行\nTC-PROTO-0289 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:生成字段表重复点击 | P2 | 已进入“现有ERP”;准备可执行场景:生成字段表重复点击。 | 动作=连续点击生成字段表;预期=字段表只生成一份或版本号递增可追踪 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:连续点击生成字段表。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:字段表只生成一份或版本号递增可追踪。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:生成字段表重复点击 | 待执行\nTC-PROTO-0290 | user_erp_mvp_admin_prototype_v10(1).html | 现有ERP | 系统稳定性与幂等 | 异常场景 | 现有ERP稳定性校验:导出现有关系无数据 | P2 | 已进入“现有ERP”;准备可执行场景:导出现有关系无数据。 | 动作=查询结果为空后导出;预期=提示暂无可导出数据 | 1. 打开原型页面“现有ERP”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:查询结果为空后导出。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示暂无可导出数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:导出现有关系无数据 | 待执行\nTC-PROTO-0291 | user_erp_mvp_admin_prototype_v10(1).html | 身份管理 | 系统稳定性与幂等 | 异常场景 | 身份管理稳定性校验:多语言图标缺失 | P2 | 已进入“身份管理”;准备可执行场景:多语言图标缺失。 | 动作=德语图标PNG为空;预期=页面显示占位并提示需补充 | 1. 打开原型页面“身份管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:德语图标PNG为空。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:页面显示占位并提示需补充。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多语言图标缺失 | 待执行\nTC-PROTO-0292 | user_erp_mvp_admin_prototype_v10(1).html | 用户管理 | 系统稳定性与幂等 | 异常场景 | 用户管理稳定性校验:分页切换保持筛选 | P2 | 已进入“用户管理”;准备可执行场景:分页切换保持筛选。 | 动作=筛选国家US后切换下一页;预期=筛选条件不丢失 | 1. 打开原型页面“用户管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选国家US后切换下一页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:筛选条件不丢失。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:分页切换保持筛选 | 待执行\nTC-PROTO-0293 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:新工单到达实时刷新 | P2 | 已进入“客服执行看板”;准备可执行场景:新工单到达实时刷新。 | 动作=后台新增待分配工单;预期=看板待处理数增加并出现新工单 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:后台新增待分配工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:看板待处理数增加并出现新工单。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:新工单到达实时刷新 | 待执行\nTC-PROTO-0294 | 客服执行.html | 客服执行看板 | 系统稳定性与幂等 | 异常场景 | 客服执行看板稳定性校验:多人同时抢单 | P2 | 已进入“客服执行看板”;准备可执行场景:多人同时抢单。 | 动作=两个客服同时领取同一工单;预期=只有一个领取成功,另一个提示已被分配 | 1. 打开原型页面“客服执行看板”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个客服同时领取同一工单。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只有一个领取成功,另一个提示已被分配。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:多人同时抢单 | 待执行\nTC-PROTO-0295 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:首次回复重复发送 | P2 | 已进入“客服工单”;准备可执行场景:首次回复重复发送。 | 动作=客服双击发送回复;预期=只发送一条消息并记录一次首次回复时长 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:客服双击发送回复。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只发送一条消息并记录一次首次回复时长。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:首次回复重复发送 | 待执行\nTC-PROTO-0296 | 客服执行.html | 客服工单 | 系统稳定性与幂等 | 异常场景 | 客服工单稳定性校验:关闭工单后刷新 | P2 | 已进入“客服工单”;准备可执行场景:关闭工单后刷新。 | 动作=关闭工单后刷新详情页;预期=状态仍为已关闭且不可继续处理 | 1. 打开原型页面“客服工单”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:关闭工单后刷新详情页。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:状态仍为已关闭且不可继续处理。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:关闭工单后刷新 | 待执行\nTC-PROTO-0297 | 客服执行.html | 客服绩效 | 系统稳定性与幂等 | 异常场景 | 客服绩效稳定性校验:绩效周期切换 | P2 | 已进入“客服绩效”;准备可执行场景:绩效周期切换。 | 动作=日/周/月连续切换;预期=指标随周期变化且无串数据 | 1. 打开原型页面“客服绩效”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:日/周/月连续切换。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:指标随周期变化且无串数据。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:绩效周期切换 | 待执行\nTC-PROTO-0298 | 用户运营系统-单文件.html | 需求中心 | 系统稳定性与幂等 | 异常场景 | 需求中心稳定性校验:创建需求重复提交 | P2 | 已进入“需求中心”;准备可执行场景:创建需求重复提交。 | 动作=提交按钮连续点击两次;预期=只创建一个demand_id | 1. 打开原型页面“需求中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:提交按钮连续点击两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只创建一个demand_id。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:创建需求重复提交 | 待执行\nTC-PROTO-0299 | 用户运营系统-单文件.html | 计划审核 | 系统稳定性与幂等 | 异常场景 | 计划审核稳定性校验:两名审批人同时审批 | P2 | 已进入“计划审核”;准备可执行场景:两名审批人同时审批。 | 动作=一个通过一个驳回并发提交;预期=按后端锁定规则只接受一个有效决策 | 1. 打开原型页面“计划审核”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:一个通过一个驳回并发提交。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:按后端锁定规则只接受一个有效决策。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:两名审批人同时审批 | 待执行\nTC-PROTO-0300 | 用户运营系统-单文件.html | 计划中心 | 系统稳定性与幂等 | 异常场景 | 计划中心稳定性校验:计划暂停后重复暂停 | P2 | 已进入“计划中心”;准备可执行场景:计划暂停后重复暂停。 | 动作=执行中计划点击暂停两次;预期=第二次提示计划已暂停 | 1. 打开原型页面“计划中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:执行中计划点击暂停两次。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:第二次提示计划已暂停。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:计划暂停后重复暂停 | 待执行\nTC-PROTO-0301 | 用户运营系统-单文件.html | 额度频控 | 系统稳定性与幂等 | 异常场景 | 额度频控稳定性校验:并发预占同一真实人最后额度 | P2 | 已进入“额度频控”;准备可执行场景:并发预占同一真实人最后额度。 | 动作=两个计划同时预占remaining=1;预期=只允许一个预占成功 | 1. 打开原型页面“额度频控”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:两个计划同时预占remaining=1。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只允许一个预占成功。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:并发预占同一真实人最后额度 | 待执行\nTC-PROTO-0302 | 用户运营系统-单文件.html | 推送/触达 | 系统稳定性与幂等 | 异常场景 | 推送/触达稳定性校验:发送任务队列中刷新 | P2 | 已进入“推送/触达”;准备可执行场景:发送任务队列中刷新。 | 动作=点击发送后立即刷新页面;预期=任务状态可从队列恢复查询 | 1. 打开原型页面“推送/触达”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:点击发送后立即刷新页面。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:任务状态可从队列恢复查询。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:发送任务队列中刷新 | 待执行\nTC-PROTO-0303 | 用户运营系统-单文件.html | 客服中心 | 系统稳定性与幂等 | 异常场景 | 客服中心稳定性校验:同用户重复创建工单 | P2 | 已进入“客服中心”;准备可执行场景:同用户重复创建工单。 | 动作=同person_id已有open工单再次创建;预期=提示关联已有工单或合并 | 1. 打开原型页面“客服中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person_id已有open工单再次创建。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示关联已有工单或合并。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:同用户重复创建工单 | 待执行\nTC-PROTO-0304 | 用户运营系统-单文件.html | 风险中心 | 系统稳定性与幂等 | 异常场景 | 风险中心稳定性校验:确认诈骗重复点击 | P2 | 已进入“风险中心”;准备可执行场景:确认诈骗重复点击。 | 动作=风险案件连续点击确认诈骗;预期=只同步一次黑名单候选 | 1. 打开原型页面“风险中心”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:风险案件连续点击确认诈骗。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:只同步一次黑名单候选。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:确认诈骗重复点击 | 待执行\nTC-PROTO-0305 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:评价提交重复登记 | P2 | 已进入“评价追踪”;准备可执行场景:评价提交重复登记。 | 动作=同person+asin+plan重复提交相同证据;预期=提示重复记录或合并,不重复扣额度 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:同person+asin+plan重复提交相同证据。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:提示重复记录或合并,不重复扣额度。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:评价提交重复登记 | 待执行\nTC-PROTO-0306 | 用户运营系统-单文件.html | 评价追踪 | 系统稳定性与幂等 | 异常场景 | 评价追踪稳定性校验:展示核验重复确认 | P2 | 已进入“评价追踪”;准备可执行场景:展示核验重复确认。 | 动作=已CONFIRMED记录再次确认展示;预期=计划完成数不重复增加 | 1. 打开原型页面“评价追踪”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:已CONFIRMED记录再次确认展示。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:计划完成数不重复增加。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:展示核验重复确认 | 待执行\nTC-PROTO-0307 | 用户运营系统-单文件.html | 系统管理 | 系统稳定性与幂等 | 异常场景 | 系统管理稳定性校验:权限变更后立即生效 | P2 | 已进入“系统管理”;准备可执行场景:权限变更后立即生效。 | 动作=撤销用户导出权限后刷新;预期=导出按钮不可用且接口拒绝 | 1. 打开原型页面“系统管理”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:撤销用户导出权限后刷新。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:导出按钮不可用且接口拒绝。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:权限变更后立即生效 | 待执行\nTC-PROTO-0308 | 用户运营系统-单文件.html | 审计通知 | 系统稳定性与幂等 | 异常场景 | 审计通知稳定性校验:审计列表空状态 | P2 | 已进入“审计通知”;准备可执行场景:审计列表空状态。 | 动作=筛选未来日期无日志;预期=显示暂无数据且可重置 | 1. 打开原型页面“审计通知”。\n2. 准备或选择满足场景的数据。\n3. 执行操作:筛选未来日期无日志。\n4. 观察页面提示、按钮状态、列表变化和详情状态。\n5. 刷新页面或重新查询该记录。\n6. 如涉及日志,进入审计通知页面按对象ID查询。 | 1. 系统按幂等/空状态/刷新规则处理。\n2. 结果为:显示暂无数据且可重置。\n3. 不产生重复记录、重复扣减、重复完成数或错误状态。\n4. 刷新后状态可恢复查询。 | 校验唯一ID、状态、计数、日志数量;重复操作不得造成多条业务成功记录。 | 重复/并发操作仍必须校验后端权限,不能因前端状态异常绕过权限。 | 页面在重复点击、刷新、并发、空状态下保持数据一致且用户可理解。 | 全局幂等与审计要求;各子系统状态规则 | 稳定性场景:审计列表空状态 | 待执行\n# Sheet: 覆盖矩阵\nHTML原型 | 需求模块 | 功能页面 | 测试类型 | 用例数量\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | KOC/KOL协作 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 多渠道触达引擎 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-P0/P1处理队列 | 功能测试 | 4\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-P0/P1处理队列 | 异常场景 | 5\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-P0/P1处理队列 | 流程测试 | 5\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 审计与通知中心 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 客服工单与管理 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 数据中心 | 工作台-时间范围与周期切换 | 数据校验 | 4\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | Listing 管理 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 各模块列表-组合筛选 | 功能测试 | 5\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 客服中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 报表中心 | 功能测试 | 2\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 推送中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 系统管理 | 功能测试 | 2\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 计划中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 需求中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统总览 | 风险中心 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统稳定性与幂等 | 工作台 | 异常场景 | 2\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统稳定性与幂等 | 报表中心 | 异常场景 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统稳定性与幂等 | 风险中心 | 异常场景 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | Listing 管理 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 客服中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 报表中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 推送中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 数据中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 系统管理 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 计划中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 需求中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 系统管理/页面导航 | 风险中心 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 评价结果追踪 | 工作台-核心KPI卡片 | 功能测试 | 1\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 需求与计划管理 | 工作台-核心KPI卡片 | 功能测试 | 3\n20260504_USER后台ERP_MVP管理员首页高保真原型_v7.html | 风险与反欺诈 | 工作台-核心KPI卡片 | 功能测试 | 2\nuser_erp_mvp_admin_prototype_v10(1).html | 审计与通知中心 | 系统资产-系统管理 | 权限校验 | 4\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-公域-产品标签 | 功能测试 | 2\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-公域-用户标签 | 功能测试 | 3\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-公域-用户标签 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-关系图谱 | 数据校验 | 7\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-字段权限与脱敏 | 权限校验 | 6\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-查询需求矩阵 | 流程测试 | 5\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-用户管理 | 功能测试 | 4\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-用户管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-私域-用户标签 | 功能测试 | 3\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-私域-用户标签 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-身份管理 | 功能测试 | 4\nuser_erp_mvp_admin_prototype_v10(1).html | 用户身份与上下文 | 现有ERP-身份管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 系统稳定性与幂等 | 现有ERP | 异常场景 | 2\nuser_erp_mvp_admin_prototype_v10(1).html | 系统稳定性与幂等 | 用户管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 系统稳定性与幂等 | 身份管理 | 异常场景 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 需求与计划管理 | 现有ERP-公域-产品标签 | 功能测试 | 1\nuser_erp_mvp_admin_prototype_v10(1).html | 需求与计划管理 | 现有ERP-公域-产品标签 | 异常场景 | 1\n客服执行.html | 多渠道触达引擎 | 客服执行看板 | 功能测试 | 1\n客服执行.html | 客服工单与管理 | 客服工单状态机 | 流程测试 | 12\n客服执行.html | 客服工单与管理 | 客服工单生命周期 | 流程测试 | 10\n客服执行.html | 客服工单与管理 | 客服异常处理 | 异常场景 | 10\n客服执行.html | 客服工单与管理 | 客服执行看板 | 功能测试 | 10\n客服执行.html | 客服工单与管理 | 客服执行看板-角色权限 | 权限校验 | 5\n客服执行.html | 系统稳定性与幂等 | 客服工单 | 异常场景 | 2\n客服执行.html | 系统稳定性与幂等 | 客服执行看板 | 异常场景 | 2\n客服执行.html | 系统稳定性与幂等 | 客服绩效 | 异常场景 | 1\n客服执行.html | 评价结果追踪 | 客服执行看板 | 功能测试 | 3\n用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 功能测试 | 1\n用户运营系统-单文件.html | ASIN/Listing | ASIN/Listing | 数据校验 | 1\n用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 功能测试 | 2\n用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 异常场景 | 1\n用户运营系统-单文件.html | KOC/KOL | KOC/KOL | 数据校验 | 1\n用户运营系统-单文件.html | KOC/KOL协作 | 达人协作 | UI/交互测试 | 1\n用户运营系统-单文件.html | 多渠道触达引擎 | 多渠道触达 | UI/交互测试 | 1\n用户运营系统-单文件.html | 多渠道触达引擎 | 推送/触达 | 数据校验 | 2\n用户运营系统-单文件.html | 多渠道触达引擎 | 推送/触达 | 流程测试 | 4\n用户运营系统-单文件.html | 审计与通知中心 | 审计与通知 | UI/交互测试 | 1\n用户运营系统-单文件.html | 审计与通知中心 | 权限配置 | UI/交互测试 | 1\n用户运营系统-单文件.html | 审计通知 | 审计通知 | 功能测试 | 2\n用户运营系统-单文件.html | 审计通知 | 审计通知 | 异常场景 | 1\n用户运营系统-单文件.html | 审计通知 | 审计通知 | 数据校验 | 1\n用户运营系统-单文件.html | 客服中心 | 客服中心 | 功能测试 | 3\n用户运营系统-单文件.html | 客服中心 | 客服中心 | 异常场景 | 2\n用户运营系统-单文件.html | 客服中心 | 客服中心 | 数据校验 | 1\n用户运营系统-单文件.html | 客服工单与管理 | 客服中心 | 数据校验 | 2\n用户运营系统-单文件.html | 客服工单与管理 | 工单管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 功能测试 | 4\n用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 异常场景 | 3\n用户运营系统-单文件.html | 推送/触达 | 推送/触达 | 数据校验 | 1\n用户运营系统-单文件.html | 用户中心 | 用户中心 | 功能测试 | 1\n用户运营系统-单文件.html | 用户中心 | 用户中心 | 数据校验 | 1\n用户运营系统-单文件.html | 用户身份与上下文 | 用户上下文 | UI/交互测试 | 1\n用户运营系统-单文件.html | 用户身份与上下文 | 用户中心 | 数据校验 | 3\n用户运营系统-单文件.html | 系统总览 | 全局权限 | 权限校验 | 7\n用户运营系统-单文件.html | 系统总览 | 工作台 | UI/交互测试 | 1\n用户运营系统-单文件.html | 系统总览 | 端到端流程 | 验收测试 | 6\n用户运营系统-单文件.html | 系统稳定性与幂等 | 审计通知 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 客服中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 推送/触达 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 系统管理 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 计划中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 计划审核 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 评价追踪 | 异常场景 | 2\n用户运营系统-单文件.html | 系统稳定性与幂等 | 需求中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 额度频控 | 异常场景 | 1\n用户运营系统-单文件.html | 系统稳定性与幂等 | 风险中心 | 异常场景 | 1\n用户运营系统-单文件.html | 系统管理 | 系统管理 | 功能测试 | 2\n用户运营系统-单文件.html | 系统管理 | 系统管理 | 异常场景 | 1\n用户运营系统-单文件.html | 计划中心 | 计划中心 | 功能测试 | 3\n用户运营系统-单文件.html | 计划中心 | 计划中心 | 异常场景 | 1\n用户运营系统-单文件.html | 计划中心 | 计划中心 | 数据校验 | 1\n用户运营系统-单文件.html | 计划审核 | 计划审核 | 功能测试 | 4\n用户运营系统-单文件.html | 计划审核 | 计划审核 | 异常场景 | 1\n用户运营系统-单文件.html | 计划审核 | 计划审核 | 数据校验 | 1\n用户运营系统-单文件.html | 评价结果追踪 | 评价结果 | UI/交互测试 | 1\n用户运营系统-单文件.html | 评价结果追踪 | 评价追踪 | 数据校验 | 2\n用户运营系统-单文件.html | 评价结果追踪 | 评价追踪 | 流程测试 | 7\n用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 功能测试 | 4\n用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 异常场景 | 2\n用户运营系统-单文件.html | 评价追踪 | 评价追踪 | 数据校验 | 1\n用户运营系统-单文件.html | 需求与计划管理 | Listing健康 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划中心 | 数据校验 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划审核 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划审核 | 数据校验 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 计划管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求与计划管理 | 需求管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 需求中心 | 需求中心 | 功能测试 | 4\n用户运营系统-单文件.html | 需求中心 | 需求中心 | 异常场景 | 2\n用户运营系统-单文件.html | 需求中心 | 需求中心 | 数据校验 | 1\n用户运营系统-单文件.html | 额度与频控 | 额度管理 | UI/交互测试 | 1\n用户运营系统-单文件.html | 额度与频控 | 额度频控 | 异常场景 | 6\n用户运营系统-单文件.html | 额度与频控 | 额度频控 | 数据校验 | 3\n用户运营系统-单文件.html | 额度频控 | 额度频控 | 功能测试 | 3\n用户运营系统-单文件.html | 额度频控 | 额度频控 | 异常场景 | 3\n用户运营系统-单文件.html | 额度频控 | 额度频控 | 数据校验 | 1\n用户运营系统-单文件.html | 风险与反欺诈 | 风险反欺诈 | UI/交互测试 | 1\n用户运营系统-单文件.html | 风险中心 | 风险中心 | 功能测试 | 3\n用户运营系统-单文件.html | 风险中心 | 风险中心 | 异常场景 | 2\n用户运营系统-单文件.html | 风险中心 | 风险中心 | 数据校验 | 1\n# Sheet: 待确认问题\n编号 | 问题 | 影响模块 | 建议验收前确认\nQ-01 | 月度额度按自然月还是30天滚动;跨月预占如何处理 | 额度与频控 | 确定周期规则后补充边界用例\nQ-02 | 审批驳回后修改再提交是否重走完整审批链 | 需求与计划管理 | 确定审批状态机\nQ-03 | Amazon展示核验方式、频率、观察期 | 评价结果追踪 | 确定自动/人工核验策略\nQ-04 | 工单等待用户/等待内部的超时时长与提醒方式 | 客服工单与管理 | 确定SLA与提醒频率\nQ-05 | IM/EDM/APP/TEL具体频控阈值 | 多渠道触达 | 确定频控配置后补边界值\nQ-06 | 黑名单同步失败重试次数和升级负责人 | 风险与反欺诈 | 确定失败重试策略\nQ-07 | KOC/KOL免评CODE缺失时是阻断还是待补充 | KOC/KOL协作 | 确定免评计划必填字段\nQ-08 | 现有ERP用户-身份是一对多还是多对多 | 用户身份与上下文 | 确定关系模型后补充数据一致性用例", + "wikilinks": [], + "category": "layer-testing" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 01 主流程说明 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md", + "summary": "USER ERP 0 1需求重构 01 主流程说明 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 01 主流程说明 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage 1 完整业务需求 负", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 01 主流程说明 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人\n- 核心参与:USER运营、客服、渠道运营、KOC/KOL运营、财务、风险、产品、前端观察员\n- 文件目的:把 USER ERP 的完整业务主流程从需求进入写到结果复盘,避免后续只围绕单页或单模块开发。\n\n## 主流程总览\n\nUSER ERP 的主流程不是从推送开始,也不是从测评单开始,而是从“需求能否变成可执行计划”开始。\n\n```mermaid\nflowchart TB\n A[\"需求进入\"] --> B[\"需求评估\"]\n B --> C[\"产品/ASIN/库存/评分校验\"]\n C --> D[\"生成或调整计划\"]\n D --> E[\"执行资源匹配\"]\n E --> F[\"渠道任务/客服任务/KOC-KOL任务生成\"]\n F --> G[\"用户或达人响应\"]\n G --> H[\"客服承接与信息补全\"]\n H --> I[\"订单/评价/内容/返款/佣金履约\"]\n I --> J[\"风险复检与异常处理\"]\n J --> K[\"结果回流到计划与需求\"]\n K --> L[\"复盘、关闭或二次计划\"]\n```\n\n## 主流程核心原则\n\n1. 需求完整性优先于 V1 实现边界。\n2. 每个需求必须能追到执行计划、执行对象、执行结果和复盘结论。\n3. 测评、回评、免评、KOC/KOL任务共用真实人、风险、额度、订单、客服上下文。\n4. 客服既是承接用户问题的执行团队,也是评价转化、订单登记、催评和异常闭环的核心执行资源。\n5. KOC/KOL不是纯外部接口,而是完整需求域:线索、分层、任务、内容、带货、佣金、风险都要在阶段1被定义。\n6. 每次有效互动都要复检身份、额度、风险、订单和未关闭承诺。\n7. 用户提交评价与 Amazon 展示确认必须拆开;计划完成口径不能混用。\n\n## 业务对象\n\n| 对象 | 含义 | 阶段 |\n| --- | --- | --- |\n| 需求 | OA/销售/运营/异常触发/KOC-KOL合作请求 | V1必做 |\n| 计划 | 测评、回评、免评、KOC/KOL任务、客服跟进计划 | V1必做 |\n| 执行资源 | 渠道、人群、客服、测评人、KOC/KOL、H5/卡片、返款能力 | V1必做 |\n| 真实人 | 跨账号、邮箱、电话、Profile、设备归并后的核心身份 | V1必做 |\n| 测评人 | 可参与测评/回评/免评的运营对象,是真实人的业务视图 | V1必做 |\n| KOC/KOL | 创作者/带货/内容合作对象,可与测评人身份重叠 | V1预留 |\n| 订单 | Amazon订单、测评订单、回评订单、免评订单、样品/带货订单 | V1必做 |\n| 评价 | 用户提交、评论链接/截图、Amazon展示确认、掉评/差评 | V1必做 |\n| 返款/佣金 | 用户返款、礼品卡、财务返款、KOC/KOL佣金 | 用户返款V1必做,佣金V1预留 |\n| 工单 | 客服消息、售后问题、催评、答应配合、异常跟进 | V1必做 |\n| 风险事件 | 黑名单、重复退款、额度超限、异常账号、内容/佣金风险 | V1必做 |\n| 复盘记录 | 需求关闭、计划表现、渠道表现、客服表现、KOC/KOL表现 | V1预留 |\n\n## 主流程分解\n\n### 1. 需求进入\n\n| 内容 | 说明 |\n| --- | --- |\n| 触发来源 | OA测评计划、销售/运营手动需求、ASIN评分异常、掉评/差评、库存/Listing状态变化、KOC/KOL合作需求、客服反馈 |\n| 必填信息 | 需求编号、需求类型、产品/ASIN、站点、店铺、目标、优先级、截止时间、需求人、负责人 |\n| 可选信息 | 关键词、关键词链接、目标受众、目标Review数、预算/追加金额、指定渠道、指定KOC/KOL |\n| 输出 | 进入需求池,状态为待评估 |\n| 阶段 | V1必做 |\n\n### 2. 需求评估\n\n运营需要判断:\n\n- 需求是测评、回评、免评、KOC/KOL任务、客服跟进,还是混合需求。\n- ASIN当前评分、Review数、差评、掉评、库存、Listing状态是否支持执行。\n- 需求目标是否超过可用人群、额度、渠道和客服容量。\n- 是否存在产品禁用、店铺异常、关键词失效、H5/卡片缺失。\n- 是否需要审批、拆分计划、延迟、驳回或转其他计划类型。\n\n输出状态:\n\n- 评估通过,进入计划生成。\n- 需补充信息,退回需求人。\n- 暂缓,等待产品/库存/Listing恢复。\n- 驳回,记录原因。\n\n阶段:V1必做。\n\n### 3. 产品/ASIN/库存/评分校验\n\n| 校验项 | 处理 |\n| --- | --- |\n| 产品是否启用 | 禁用则禁止生成渠道任务,允许进入待恢复池 |\n| ASIN是否正确 | 错误则退回补充或人工修正 |\n| 站点/店铺是否匹配 | 不匹配则阻断计划下发 |\n| 库存是否充足 | 库存紧张时限制测评/免评节奏 |\n| 评分/掉评/差评 | 触发回评或紧急催评策略 |\n| 关键词/H5/卡片 | 缺失则生成维护任务 |\n\n阶段:V1必做。\n\n### 4. 计划生成或调整\n\n计划类型必须保留:\n\n| 计划类型 | 说明 | 阶段 |\n| --- | --- | --- |\n| 测评计划 | 为产品增加评价、冲销量、拉排名、新品启动等 | V1必做 |\n| 回评计划 | 对已购/已测/待评价人群催评或稳定评分 | V1必做 |\n| 免评计划 | 面向长期测评人、KOC/KOL或补单需求,不计普通测评额度 | V1必做 |\n| KOC/KOL合作任务 | 样品、内容、带货、佣金、复盘 | V1预留/V2实现 |\n| 客服跟进计划 | 售后、催评、答应配合、异常用户跟进 | V1必做 |\n\n计划生成时必须写入:\n\n- 关联需求编号。\n- 产品/ASIN/站点/店铺。\n- 计划目标、周期、每日节奏、优先级。\n- 渠道策略。\n- 目标人群条件。\n- 额度预占策略。\n- 风险排除策略。\n- 客服承接要求。\n- H5/卡片/素材要求。\n- 关闭条件。\n\n### 5. 执行资源匹配\n\n计划不是创建后直接发出去,必须先匹配资源。\n\n| 资源 | 匹配规则 |\n| --- | --- |\n| 人群 | 用户层级、国家、品类偏好、活跃、历史订单、Review额度、风险 |\n| 测评人 | 可测评次数、可免评次数、可上评次数、合作状态、掉评率、退款取消记录 |\n| KOC/KOL | 分层、品类、内容能力、带货能力、合作状态、风险状态 |\n| 渠道 | IM/EDM/Phone/APP/KOC-KOL可用性、频控、退订、投诉、转化 |\n| 客服 | 在线状态、排班、工单量、国家/语言、转化目标、当前压力 |\n| 产品素材 | H5、卡片、关键词链接、首图、编码图、跳转链接 |\n| 财务 | 可返款方式、返款账号、礼品卡卡密、审核队列 |\n| 风险 | 黑名单、强弱关联、退款取消、重复订单/评论/返款 |\n\n阶段:V1必做半自动匹配,V2增强自动推荐。\n\n### 6. 渠道任务 / 客服任务 / KOC-KOL任务生成\n\n| 任务 | 生成条件 | 输出 |\n| --- | --- | --- |\n| IM推送任务 | 有可推人群、卡片/H5可用、频控通过 | 推送任务、渠道事件、标签 |\n| EDM任务 | 域名/邮箱/IP健康、UID人群正常、邮件素材/H5可用 | 邮件计划、AB Test、发送结果 |\n| Phone任务 | 用户有电话、适合电话沟通、客服容量可承接 | 电话名单、回拨任务、通话记录 |\n| 客服工单 | 用户回复、订单异常、信息缺失、售后问题、投诉 | 工单、处理人、状态 |\n| KOC/KOL任务 | 有合适达人、样品/免评/内容目标明确 | 合作任务、内容/带货跟踪 |\n| 财务返款任务 | 用户信息完整、订单/评价状态满足返款条件 | 请款/返款任务 |\n\n### 7. 用户或达人响应\n\n响应包括:\n\n- 用户点击、回复、提交订单号、提交返款账号、上传评论截图/链接。\n- 用户只提交部分信息。\n- 用户投诉、退订、不感兴趣。\n- KOC/KOL接受任务、拒绝任务、提交内容链接、提交带货链接。\n\n每次响应都要写入渠道事件,并触发有效互动复检。\n\n### 8. 客服承接与信息补全\n\n客服核心动作:\n\n- 查看用户上下文卡。\n- 回复用户。\n- 查询/核验订单号。\n- 补充返款账号、截图、评论链接。\n- 登记订单。\n- 催评。\n- 记录售后问题和解决方案。\n- 标记答应配合。\n- 升级风险、财务、运营或主管。\n- 关闭或重开工单。\n\n客服管理动作:\n\n- 分配/转移工单。\n- 调整排班。\n- 设置目标。\n- 统计回复、工单、转化、满意度。\n\n阶段:V1必做。\n\n### 9. 履约:订单 / 评价 / 返款 / 佣金\n\n| 履约对象 | 核心状态 |\n| --- | --- |\n| 订单 | 待登记、已登记、已发货、已取消、已退款、异常 |\n| 评价 | 待提交、已提交、待展示核验、已展示、未展示、掉评、差评 |\n| 返款 | 待请款、待审核、审核失败、待返款、返款成功、返款锁定 |\n| KOC/KOL内容 | 待接受、待寄样、待提交、待审核、已发布、链接异常 |\n| KOC/KOL佣金 | 待归因、待计算、待审核、待结算、已结算、争议 |\n\n### 10. 风险复检与异常处理\n\n复检时机:\n\n- 需求评估。\n- 计划生成。\n- 人群生成。\n- 渠道发送前。\n- 用户提交订单/评价/返款账号。\n- 客服登记订单。\n- 请款/返款。\n- KOC/KOL接受任务、提交内容、结算佣金。\n\n风险结果:\n\n- 正常,继续执行。\n- 弱风险,提醒人工确认。\n- 强风险,阻断并生成风险事件。\n- 确认风险,同步黑名单。\n\n### 11. 结果回流与复盘\n\n结果必须回流到:\n\n- 原需求:是否达成、是否关闭、是否需补量。\n- 计划:完成数、节奏、渠道表现、成本。\n- ASIN:评分、Review、差评、掉评。\n- 用户/测评人:额度、合作状态、标签、风险。\n- 客服:转化、回复、目标、绩效。\n- KOC/KOL:内容、带货、佣金、合作等级。\n- 渠道:素材、频控、人群、AB Test。\n\n阶段:V1预留复盘记录,核心指标 V1必做。\n\n## 状态总表\n\n| 状态域 | 必须拆开 |\n| --- | --- |\n| 需求状态 | 草稿、待评估、需补充、已通过、已驳回、已转计划、已关闭 |\n| 计划状态 | 草稿、待审批、进行中、暂停、已完成、已取消、需补量 |\n| 资源匹配状态 | 待匹配、匹配中、匹配不足、匹配完成、需人工确认 |\n| 渠道任务状态 | 待发送、发送中、已发送、失败、已下架、暂停 |\n| 用户响应状态 | 未响应、已点击、已回复、已提交部分信息、已提交完整信息、投诉/退订 |\n| 工单状态 | 待分配、待处理、处理中、等待用户、已解决、已关闭、已重开 |\n| 订单状态 | 待登记、已登记、已发货、已取消、已退款、异常 |\n| 评价状态 | 待提交、已提交、待核验、已展示、未展示、掉评、差评 |\n| 返款状态 | 待请款、待审核、审核失败、待返款、返款成功、锁定 |\n| KOC/KOL任务状态 | 待分配、待接受、进行中、待内容、待审核、已发布、已结算、异常 |\n| 风险状态 | 正常、弱风险、强风险、已拦截、复核中、已解除、已拉黑 |\n\n## Gate 1 - 主流程完成条件\n\n- 主流程从需求进入到结果复盘完整。\n- 需求与执行计划匹配被明确为核心流程。\n- 客服被写入主流程核心位置。\n- KOC/KOL被作为完整需求域纳入,而不是仅做接口预留。\n- 测评、回评、免评、客服、渠道、财务、风险、看板之间的流转关系清楚。\n- V1必做、V1预留、V2实现、待确认有明确标注。\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 02 日常操作页面结构 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md", + "summary": "USER ERP 0 1需求重构 02 日常操作页面结构 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 02 日常操作页面结构 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage 1 完整", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 02 日常操作页面结构 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人 / 产品负责人\n- 核心参与:USER运营、客服主管、渠道负责人、KOC/KOL负责人、风险、财务、前端观察员\n- 文件目的:定义每个岗位每天打开系统后先看什么、判断什么、处理什么,并据此组织页面结构。\n\n## 页面设计原则\n\n1. 第一屏必须是日常作战台,不是普通任务列表。\n2. 页面围绕“发现异常 -> 判断优先级 -> 分配动作 -> 执行 -> 跟进结果 -> 复盘沉淀”设计。\n3. 每个页面必须说明贡献哪个OKR:用户增长、评价转化、销售转化、活动转化、满意度、风险控制。\n4. 页面结构要服务需求与执行计划匹配,不能只展示静态数据。\n5. 客服执行和客服管理是一级核心入口。\n6. KOC/KOL在阶段1必须有页面结构,即使V1只预留部分入口。\n\n## 一级导航建议\n\n| 一级入口 | 定位 | 阶段 |\n| --- | --- | --- |\n| 今日作战台 | 所有角色的每日首页,显示异常、目标、今日必处理 | V1必做 |\n| 需求与计划调度中心 | 需求池、计划生成、执行资源匹配、计划调整 | V1必做 |\n| 评价计划与订单中心 | 测评、回评、免评计划和订单履约 | V1必做 |\n| 客服执行中心 | 工单、聊天、答应配合、催评、订单登记 | V1必做 |\n| 客服管理中心 | 出勤、排班、目标、绩效、服务质量 | V1必做 |\n| 渠道运营中心 | IM、EDM、Phone、APP/H5/卡片、推送任务 | V1必做 |\n| KOC/KOL协作中心 | 线索、达人、任务、内容、带货、佣金、风险 | V1预留/V2实现 |\n| 测评人/真实人中心 | 测评人档案、身份归并、额度、风险、历史 | V1必做 |\n| 风险与黑名单中心 | 风险事件、黑名单、退款比对、异常复检 | V1必做 |\n| 数据复盘看板 | 计划、ASIN、渠道、客服、KOC/KOL、财务复盘 | V1必做核心,V2增强 |\n| 系统配置 | 权限、渠道配置、字段、标签、通知、导入导出 | V1预留 |\n\n## 01 今日作战台\n\n### 目标\n\n让主管和各岗位每天开屏后立刻知道:昨天有什么异常、本周目标是否危险、今天必须推进什么、哪些任务没人处理、哪些结果需要复盘。\n\n### 页面区块\n\n| 区块 | 展示内容 | 操作 |\n| --- | --- | --- |\n| 昨日核心指标 | 需求新增、计划新增、完成、缺口、客服工单、风险、返款 | 查看详情 |\n| P0/P1异常 | Review低于计划、渠道下滑、客服超时、产品禁用、风险事件 | 指派处理、升级 |\n| 今日必跟进 | 待评估需求、待补量计划、待催评用户、待返款、待审核内容 | 进入处理 |\n| 目标进度 | 月度测评、回评、免评、客服转化、KOC/KOL内容/带货 | 调整计划 |\n| 资源压力 | 可用人群、额度、客服容量、KOC/KOL资源、返款队列 | 资源调度 |\n| 昨日复盘 | TOP/BOTTOM任务、异常原因、沉淀动作 | 创建复盘 |\n\n### 角色视图\n\n| 角色 | 首页重点 |\n| --- | --- |\n| 高级主管 | OKR、资源、P0异常、跨部门阻塞 |\n| USER运营 | 需求、计划、执行缺口、渠道/客服压力 |\n| 渠道运营 | 触达漏斗、素材、可推人群、退订/投诉 |\n| 客服主管 | 在线、排班、待处理工单、首次回复、转化目标 |\n| KOC/KOL运营 | 线索、任务、内容、带货、风险 |\n| 风险/财务 | 待审核、待返款、双重退款、敏感操作 |\n\n## 02 需求与计划调度中心\n\n### 子页面\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| 需求池 | OA/销售/运营/异常/KOC-KOL需求统一入口 | V1必做 |\n| 需求评估页 | 校验ASIN、产品、库存、评分、目标、优先级 | V1必做 |\n| 计划编排页 | 生成测评、回评、免评、客服、KOC/KOL计划 | V1必做 |\n| 执行匹配看板 | 人群、渠道、客服、KOC/KOL、风险、H5/卡片匹配 | V1必做基础 |\n| 计划调整页 | 补量、暂停、恢复、转免评、关闭、拆分/合并 | V1必做 |\n\n### 需求池字段\n\n| 字段 | 说明 |\n| --- | --- |\n| 需求编号 | OA/系统生成 |\n| 需求来源 | OA、销售、运营、ASIN异常、客服反馈、KOC/KOL |\n| 需求类型 | 测评、回评、免评、客服跟进、KOC/KOL内容/带货 |\n| 产品/ASIN/站点/店铺 | 执行基础 |\n| 目标 | 目标订单数、Review数、评分、内容数、带货数 |\n| 优先级 | S/A/B/C 或 P0/P1/P2 |\n| 截止时间 | 计划完成约束 |\n| 当前状态 | 待评估、需补充、已转计划、暂停、关闭 |\n| 负责人 | 运营负责人 |\n\n## 03 评价计划与订单中心\n\n### 页面结构\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| 测评计划 | 产品测评需求、计划周期、渠道、目标、进度、评分、库存 | V1必做 |\n| 回评计划 | 掉评/差评/维稳/冲刺等回评需求与每日目标 | V1必做 |\n| 免评计划 | 长期测评人/KOC-KOL/补单免评计划 | V1必做 |\n| 测评订单 | 订单登记、上传回评、请款、返款、状态跟踪 | V1必做 |\n| 回评订单 | 回评上传、确认、返款、售后来源、处理记录 | V1必做 |\n| 产品/ASIN详情 | 评分、Review、库存、关键词、渠道、H5/卡片 | V1预留 |\n\n## 04 客服执行中心\n\n客服执行中心是 V1 核心入口。\n\n| 页面 | 每天要解决的问题 | 核心操作 |\n| --- | --- | --- |\n| 工单池 | 哪些用户消息没人处理,哪些工单快超时 | 分配、领取、转移、标记解决、关闭 |\n| 我的工单 | 当前客服今天处理什么 | 回复、登记订单、催评、上传结果、升级 |\n| 聊天/消息 | 用户具体说了什么 | 快捷回复、补充信息、创建跟进 |\n| 服务聊天记录 | 复查历史沟通 | 查询、查看上下文 |\n| 答应配合 | 用户答应评价/反馈后是否完成 | 确认、拒绝、提醒、过期 |\n| 催评池 | 哪些测评/回评待催 | 发送提醒、转人工、关闭 |\n| 售后详情 | 用户问题、解决方案、订单、返款 | 记录方案、回访、升级 |\n\n## 05 客服管理中心\n\n| 页面 | 展示 | 操作 |\n| --- | --- | --- |\n| 客服Dashboard | 在线客服、今日工单、待处理、今日转化、本月目标 | 查看详情 |\n| 出勤管理 | 应出勤、实际出勤、出勤率、迟到/请假/缺勤 | 导入/调整 |\n| 排班管理 | 早班、午班、晚班、渠道、最大工单数 | 设置、批量排班、调整 |\n| 工单分配管理 | 分配规则、当前工单量、超时工单 | 自动分配、手动调整 |\n| 回复统计 | 回复用户数、处理工单、消息数、首次回复 | 导出 |\n| 转化绩效 | RSO/RDO登记订单、获取评价、完成率 | 查看排行 |\n| 目标管理 | 月目标、历史完成、客服个人趋势 | 设置目标、调整 |\n\n## 06 渠道运营中心\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| IM推送 | 用户分层、卡片、推送任务、回复、转化 | V1必做 |\n| IM卡片/H5管理 | 卡片、图片、链接、跳转、产品禁用联动 | V1必做 |\n| EDM每日检查 | 域名/邮箱/IP信誉、UID、H5、AB Test、转化 | V1必做基础 |\n| Phone工作台 | 未接/待回拨、国家/时段、问题类型、客服容量 | V1预留 |\n| 推送配置 | 渠道账号、日限额、状态、负责人 | V1预留 |\n| 渠道漏斗看板 | 推送、曝光、点击、回复、订单、评价 | V1必做 |\n\n## 07 KOC/KOL协作中心\n\n阶段1必须完整定义,V1可部分预留。\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| KOC/KOL线索池 | 新增线索、来源、标签、风险、分层 | V1预留 |\n| 达人档案 | 国家、品类、内容能力、带货能力、合作状态 | V1预留 |\n| 合作任务 | 样品、免评、内容、带货、佣金任务 | V1预留/V2 |\n| 内容记录 | 内容链接、审核、发布时间、表现 | V2实现 |\n| 带货归因 | Code、链接、订单、销售额 | V2实现 |\n| 佣金结算 | 佣金规则、结算、争议 | V2实现 |\n| KOC/KOL风险 | 标签混乱、重复触达、违规内容、佣金争议 | V1预留 |\n\n## 08 测评人/真实人中心\n\n| 页面 | 说明 | 阶段 |\n| --- | --- | --- |\n| 测评人列表 | 编号、Joyhub、邮箱、电话、Profile、国家、标签、合作状态 | V1必做 |\n| 测评人详情 | 额度、订单、评价、返款、风险、沟通记录 | V1必做 |\n| 真实人归并 | 标准化姓名+地址、设备、邮箱、电话、Profile、收款信息等自动归并;人工确认/拆分作为修正入口 | 自动归并 V1必做,人工确认/拆分 V1预留 |\n| 额度台账 | 月度测评、月度免评、累计Review、预占/释放 | V1必做 |\n| 风险记录 | 黑名单、退款取消、掉评、弱/强关联 | V1必做 |\n\n说明:`4/4/12`额度控制依赖真实人维度,V1必须先具备自动归并能力,否则 `person_quota_ledgers` 无法跨 JOYHUB ID、Amazon 账号和 Profile 合并计算。\n\n## 09 风险与黑名单中心\n\n| 页面 | 说明 |\n| --- | --- |\n| 风险事件 | 所有风险信号与人工复核案件 |\n| 黑名单 | 生效中/已移除、风险等级、来源、原因 |\n| 退款比对 | Amazon退款与OA返款双重比对 |\n| 关联风险 | 邮箱、电话、设备、IP、Profile、地址、返款账号 |\n| 内容/佣金风险 | KOC/KOL内容不合规、归因失败、佣金争议 |\n\n## 10 数据复盘看板\n\n| 看板 | 指标 |\n| --- | --- |\n| 计划看板 | 总计划、进行中、完成率、缺口、补量 |\n| ASIN看板 | 评分、Review、差评、掉评、库存、风险ASIN |\n| 渠道看板 | 推送、曝光、点击、回复、转化、退订/投诉 |\n| 客服看板 | 工单、响应、解决、满意度、RSO/RDO转化 |\n| 测评人看板 | 额度、完成率、掉评率、风险、合作状态 |\n| KOC/KOL看板 | 线索、任务、内容、带货、佣金、风险 |\n| 财务看板 | 待请款、待审核、返款成功、异常返款 |\n\n## Gate 1 - 页面结构完成条件\n\n- 今日作战台和岗位每日工作方式已定义。\n- 需求与计划调度中心作为一级核心入口。\n- 客服执行中心和客服管理中心作为一级核心入口。\n- KOC/KOL协作中心已有完整需求结构,即使V1不全量实现。\n- 各页面能对应主流程中的对象、状态、动作和复盘指标。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 03 功能页面按钮盘点表 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md", + "summary": "USER ERP 0 1需求重构 03 功能页面按钮盘点表 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 03 功能页面按钮盘点表 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage 1 ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "moderate", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 03 功能页面按钮盘点表 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:产品负责人 / 前端观察员\n- 核心参与:业务用户、客服主管、渠道运营、KOC/KOL运营、财务、风险、后端、测试\n- 文件目的:盘点阶段1涉及的页面按钮、业务含义、读写对象、状态变化、权限和审计要求,作为 Stage 2 按钮行为矩阵的输入。\n\n## 盘点规则\n\n| 字段 | 说明 |\n| --- | --- |\n| 页面 | 按一级/二级页面归类 |\n| 按钮/动作 | 用户可点击或系统触发的动作 |\n| 业务含义 | 为什么需要这个按钮 |\n| 读取对象 | 点击前必须读的数据 |\n| 写入对象 | 点击后必须落盘的数据 |\n| 状态变化 | 影响哪些状态 |\n| 权限 | 哪些角色可操作 |\n| 审计 | 是否必须记录操作日志 |\n| 阶段 | V1必做、V1预留、V2实现、待确认 |\n\n## 对象命名对照\n\n按钮盘点表中的“读取对象/写入对象”保留页面语境简称;进入 Stage 2 接口和数据设计时,应优先映射到数据对象 v3 的正式对象名。\n\n| 按钮盘点简称 | v3正式对象或聚合口径 |\n| --- | --- |\n| person | `person_profiles` |\n| identity | `person_identity_links` |\n| reviewer | `person_identity_links` + `person_quota_ledgers` 聚合视图 |\n| quota / quota_ledger | `person_quota_ledgers` |\n| quota_reservation | `quota_reservations` |\n| audience / audience_snapshot | `audience_snapshots` |\n| exclusion | `audience_exclusions` |\n| risk_event | `risk_signals` / `risk_cases` |\n| risk | `risk_signals` / `risk_cases` / `blacklist_entities` 聚合摘要 |\n| order / review_order | `amazon_orders`、`review_submission_records`、`review_display_checks` |\n| push_task / channel_event | `channel_route_decisions`、`channel_dedup_records`、各渠道事件表 |\n| interaction_recheck | `interaction_recheck_records` |\n| koc_kol / creator | JOYCOLLAB 的 `creator_id`,以及 `exemption_plan_tasks`、`creator_content_records`、`exemption_result_snapshots` |\n\n## 01 今日作战台\n\n| 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| 查看异常 | 进入昨日/今日异常详情 | alert、plan、channel_event、ticket、risk_event | - | - | 主管、运营 | 否 | V1必做 |\n| 指派处理人 | 把异常转成可执行任务 | alert、staff、shift | task、notification | 异常:待处理 -> 已指派 | 主管、运营 | 是 | V1必做 |\n| 升级异常 | P0/P1问题升级主管或跨部门 | alert、risk_event、ticket | escalation、notification | 异常:待处理 -> 已升级 | 主管、风险 | 是 | V1必做 |\n| 调整计划 | 从作战台直接进入计划调整 | demand、plan、progress | plan_change_log | 计划:进行中 -> 调整中/暂停/补量 | 主管、运营 | 是 | V1必做 |\n| 创建复盘 | 对异常或任务形成复盘记录 | plan、channel_event、ticket、order | retrospective | 异常:已处理 -> 待复盘 | 主管、运营 | 是 | V1预留 |\n\n## 02 需求与计划调度中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 需求池 | 新增需求 | 手动录入销售/运营/KOC-KOL需求 | product、asin、staff | demand | 需求:草稿 | 运营、主管 | 是 | V1必做 |\n| 需求池 | 导入OA需求 | 从OA或表格批量导入需求 | import_file | demand、import_log | 需求:待评估 | 运营、管理员 | 是 | V1必做 |\n| 需求池 | 需求评估 | 判断需求是否可执行 | demand、product、asin、inventory、risk | demand_review | 待评估 -> 已通过/需补充/驳回 | 运营、主管 | 是 | V1必做 |\n| 需求池 | 退回补充 | 信息不足时退回需求人 | demand | demand、notification | 待评估 -> 需补充 | 运营 | 是 | V1必做 |\n| 需求池 | 生成计划 | 将需求转成测评/回评/免评/KOC-KOL/客服计划 | demand、product、audience、quota、risk | plan、plan_log | 已通过 -> 已转计划 | 运营、主管 | 是 | V1必做 |\n| 计划编排 | 拆分计划 | 一个需求拆成多个站点/渠道/周期计划 | demand、plan | plan、plan_relation | 计划:草稿/进行中 | 运营、主管 | 是 | V1预留 |\n| 计划编排 | 合并计划 | 合并同产品/同周期/同目标计划 | plan | plan_relation、plan_log | 计划:合并/关闭旧计划 | 主管 | 是 | V1预留 |\n| 执行匹配 | 匹配人群 | 生成候选用户/测评人/KOC-KOL名单 | person、reviewer、koc_kol、quota、risk | audience_snapshot、exclusion | 匹配:待匹配 -> 完成/不足 | 运营 | 是 | V1必做 |\n| 执行匹配 | 预占额度 | 锁定测评/免评/Review额度 | audience_snapshot、quota_ledger | quota_reservation | 额度:可用 -> 预占 | 运营 | 是 | V1必做 |\n| 执行匹配 | 手动释放预占 | 预占超时、计划取消或人群撤出后释放额度 | quota_reservation、quota_ledger、plan | quota_reservation_log、quota_ledger | 额度:已预占 -> 已释放 | 主管、管理员 | 是 | V1必做 |\n| 执行匹配 | 分配客服 | 按容量、语言、工单量分配承接客服 | plan、ticket、shift、agent_capacity | task、ticket_assignment | 待分配 -> 已分配 | 主管、客服主管 | 是 | V1必做 |\n| 执行匹配 | 生成推送任务 | 从计划生成人群和渠道任务 | plan、audience、card、h5、channel_config | push_task、channel_event | 待发送 | 渠道运营 | 是 | V1必做 |\n| 执行匹配 | 匹配KOC/KOL | 将需求匹配给合适创作者 | koc_kol_profile、task、risk | koc_kol_task | 待分配 -> 待接受 | KOC/KOL运营 | 是 | V1预留 |\n| 计划调整 | 调整名额 | 增减计划数量 | plan、progress、resource | plan_change_log | 计划目标变化 | 运营、主管 | 是 | V1必做 |\n| 计划调整 | 暂停计划 | 禁止继续执行但保留历史 | plan、risk、product | plan_log、notification | 进行中 -> 暂停 | 主管 | 是 | V1必做 |\n| 计划调整 | 恢复计划 | 恢复已暂停计划 | plan、product、risk | plan_log | 暂停 -> 进行中 | 主管 | 是 | V1必做 |\n| 计划调整 | 转免评 | 普通测评/回评因店铺/订单/用户问题转免评 | plan、order、reviewer | plan、order_log | 测评/回评 -> 免评 | 运营、主管 | 是 | V1必做 |\n| 计划调整 | 关闭需求 | 需求完成或取消 | demand、plan、progress | demand_close_log | 需求:已关闭 | 主管 | 是 | V1必做 |\n\n## 03 评价计划与订单中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 测评计划 | 新建测评计划 | 从需求或人工创建计划 | demand、product、asin | review_plan | 草稿/未开始 | 运营 | 是 | V1必做 |\n| 测评计划 | 编辑计划 | 修改目标、渠道、周期、负责人 | review_plan | plan_log | 计划字段变化 | 运营、主管 | 是 | V1必做 |\n| 测评计划 | 复制关键词链接 | 运营执行用 | review_plan | copy_event | - | 运营 | 否 | V1必做 |\n| 测评计划 | 查看关联推送 | 查看计划下发的渠道任务 | plan、push_task | - | - | 运营、渠道 | 否 | V1必做 |\n| 测评计划 | 查看关联订单 | 查看计划履约订单 | plan、order | - | - | 运营 | 否 | V1必做 |\n| 回评计划 | 新建回评计划 | 因掉评/差评/维稳生成回评计划 | asin、review_stats | reply_plan | 待开始 | 运营 | 是 | V1必做 |\n| 回评计划 | 调整今日目标 | 根据评分/掉评变化调整当天目标 | reply_plan、asin_stats | plan_log | 今日目标变化 | 运营、主管 | 是 | V1必做 |\n| 免评计划 | 新建免评计划 | 给长期测评人/KOC-KOL/补单池创建免评计划 | demand、reviewer、koc_kol | free_plan | 待审批/进行中 | 运营 | 是 | V1必做 |\n| 测评订单 | 新增订单 | 记录测评执行单 | plan、person、product | review_order | 待上传回评/待登记 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 上传订单 | 绑定Amazon订单号 | review_order、amazon_order | review_order、audit_log | 订单:待登记 -> 已登记 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 上传回评 | 绑定评论ID/链接/截图 | review_order、comment | review_submission | 待回评 -> 待确认/已提交 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 提交排队 | 评论暂不能绑定,进入排队 | review_order、comment_query | review_queue | 待确认 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 请款 | 发起用户返款 | review_order、refund_account | refund_request | 待请款 -> 待审核 | 运营、客服 | 是 | V1必做 |\n| 测评订单 | 更换订单 | 用新订单替代原订单 | review_order、amazon_order | order_change_log | 订单号变化 | 运营、主管 | 是 | V1必做 |\n| 测评订单 | 更改订单 | 修正订单字段 | review_order | order_change_log | 订单字段变化 | 运营、主管 | 是 | V1必做 |\n| 测评订单 | 撤销 | 撤销错误/风险订单 | review_order、risk | order_log、quota_release | 进行中 -> 撤销 | 风险、主管 | 是 | V1必做 |\n| 测评订单 | 批量导出 | 导出当前筛选结果 | order、permission | export_task | - | 有导出权限 | 是 | V1必做 |\n| 回评订单 | 回评确认 | 核验回评是否有效 | review_submission、comment | review_display_check | 待确认 -> 已回评/未通过 | 运营、审核 | 是 | V1必做 |\n\n## 04 客服执行中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 工单池 | 自动分配 | 按在线、工单量、排班分配客服 | ticket、agent、shift | ticket_assignment | 待分配 -> 待处理 | 客服主管 | 是 | V1必做 |\n| 工单池 | 手动分配 | 指定处理人 | ticket、agent | ticket_assignment | 待分配 -> 待处理 | 客服主管 | 是 | V1必做 |\n| 工单池 | 转移工单 | 换客服处理 | ticket、agent | ticket_transfer_log | 处理人变化 | 客服、主管 | 是 | V1必做 |\n| 我的工单 | 回复用户 | 处理用户消息 | ticket、chat、context_snapshot | chat_message、ticket_log | 待处理 -> 处理中/等待用户 | 客服 | 是 | V1必做 |\n| 我的工单 | 登记订单 | 客服拿到订单号后登记 | ticket、amazon_order、person | review_order、ticket_log | 工单推进 | 客服 | 是 | V1必做 |\n| 我的工单 | 催评 | 提醒用户提交评价 | ticket、order、followup | reminder_event、followup | 催评次数+1 | 客服、运营 | 是 | V1必做 |\n| 我的工单 | 标记解决 | 用户问题解决 | ticket | ticket_log | 处理中 -> 已解决 | 客服 | 是 | V1必做 |\n| 我的工单 | 关闭工单 | 完成或无需继续 | ticket | ticket_log | 已解决/等待用户 -> 已关闭 | 客服、主管 | 是 | V1必做 |\n| 我的工单 | 重开工单 | 用户再次反馈 | ticket | ticket_log | 已关闭 -> 已重开/处理中 | 客服、主管 | 是 | V1必做 |\n| 答应配合 | 确认答应 | 用户答应评价/反馈 | ticket、person | support_followup | 待确认 -> 已确认 | 客服 | 是 | V1必做 |\n| 答应配合 | 拒绝 | 用户拒绝配合 | ticket | support_followup | 待确认 -> 已拒绝 | 客服 | 是 | V1必做 |\n| 答应配合 | 标记过期 | 超时未提交 | followup | support_followup | 已确认 -> 已过期 | 客服、系统 | 是 | V1必做 |\n\n## 05 客服管理中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 出勤管理 | 导入出勤 | 导入当天/本月出勤 | attendance_file | attendance_record | 出勤记录更新 | 客服主管、人事 | 是 | V1预留 |\n| 出勤管理 | 调整出勤状态 | 修正迟到/请假/缺勤 | attendance_record | attendance_log | 状态变化 | 客服主管 | 是 | V1必做 |\n| 排班管理 | 设置班次 | 给客服设置早/午/晚班 | agent、date | shift_schedule | 排班变化 | 客服主管 | 是 | V1必做 |\n| 排班管理 | 批量排班 | 一次生成多天排班 | agent、date_range | shift_schedule | 排班批量变化 | 客服主管 | 是 | V1必做 |\n| 目标管理 | 设置月目标 | 设置RSO/RDO登记和上评目标 | agent、period | support_target | 目标变化 | 客服主管 | 是 | V1必做 |\n| 绩效看板 | 导出绩效 | 导出回复/转化/目标完成 | performance_snapshot | export_task | - | 客服主管 | 是 | V1必做 |\n\n## 06 渠道运营中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| IM推送 | 新增推送 | 创建IM触达任务 | plan、audience、card | im_push_task | 草稿/待发送 | 渠道运营 | 是 | V1必做 |\n| IM推送 | 上架/下架 | 控制任务是否可执行 | im_push_task | channel_log | 已上架/已下架 | 渠道运营 | 是 | V1必做 |\n| IM推送 | 批量分配 | 将任务分配到账号/渠道 | push_task、im_account | assignment | 待分配 -> 已分配 | 渠道运营 | 是 | V1预留 |\n| IM卡片 | 新增卡片 | 创建卡片素材 | product、h5 | im_card | 已上架/草稿 | 渠道运营 | 是 | V1必做 |\n| IM卡片 | 下架卡片 | 产品禁用/素材失效时下架 | im_card、product | card_log | 已上架 -> 已下架 | 渠道运营 | 是 | V1必做 |\n| EDM | 检查基础设施 | 确认域名/邮箱/IP信誉 | edm_config、deliverability | daily_check | 健康/异常 | EDM运营 | 否 | V1必做 |\n| EDM | 创建AB Test | 测试标题/素材/按钮 | edm_campaign | ab_test | 运行中 | EDM运营 | 是 | V1预留 |\n| Phone | 创建回拨任务 | 对未接/待跟进用户安排电话 | tel_pool、agent_shift | tel_task | 待拨打 | Phone/客服 | 是 | V1预留 |\n| 渠道配置 | 配置去重规则 | 定义跨IM/EDM/APP/Phone/KOC触达的去重、例外和频控条件 | channel_config、person、risk、quota | channel_dedup_rule、channel_log | 规则生效/停用 | 渠道主管、管理员 | 是 | V1预留 |\n\n## 07 KOC/KOL协作中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 线索池 | 新增线索 | 新增KOC/KOL候选人 | source、contact | creator_profile | 待审核 | KOC/KOL运营 | 是 | V1预留 |\n| 线索池 | 打标分层 | 区分新手KOC、稳定测评者、垂直专家、带货型KOL | creator_profile、metrics | creator_tag、tier_log | 分层变化 | KOC/KOL运营 | 是 | V1预留 |\n| 任务管理 | 创建合作任务 | 样品、免评、内容、带货任务 | demand、product、creator | creator_task | 待接受 | KOC/KOL运营 | 是 | V1预留 |\n| 任务管理 | 分配样品/免评名额 | 执行合作资源分配 | creator_task、inventory、quota | sample_order/free_order | 待寄样/待执行 | KOC/KOL运营 | 是 | V1预留 |\n| 内容记录 | 上传内容链接 | 记录发布内容 | creator_task | content_record | 待审核/已发布 | KOC/KOL运营 | 是 | V2实现 |\n| 内容记录 | 审核内容 | 判断内容合规和是否可分发 | content_record、content_policy | content_audit | 通过/驳回 | 审核、运营 | 是 | V2实现 |\n| 带货归因 | 同步订单 | 记录Code/链接带来的订单 | attribution、order | creator_order | 待结算 | KOC/KOL运营 | 是 | V2实现 |\n| 佣金结算 | 计算佣金 | 根据规则生成佣金 | creator_order、commission_rule | commission_record | 待审核 | 财务、运营 | 是 | V2实现 |\n| 风险处理 | 暂停合作 | 风险/投诉/作弊时暂停 | creator_profile、risk | creator_status_log | 可合作 -> 暂停 | 风险、主管 | 是 | V1预留 |\n\n## 08 测评人/真实人中心\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 真实人归并 | 手动合并 | 修正自动归并不足,把多个账号/身份线索合并到同一真实人 | person、identity、quota_ledger、risk | person_identity_link、merge_log、quota_ledger | 多个身份 -> 同一真实人 | 主管、风险、管理员 | 是 | V1预留 |\n| 真实人归并 | 手动拆分 | 纠正误合并,拆开不同真实人 | person、identity、quota_ledger、risk | person_identity_link、split_log、quota_ledger | 同一真实人 -> 多个真实人 | 主管、风险、管理员 | 是 | V1预留 |\n| 额度台账 | 查看额度明细 | 查看4/4/12额度、预占、释放、提交计数来源 | person、quota_ledger、quota_reservation、review_submission | - | - | 运营、客服主管、风险 | 否 | V1必做 |\n| 互动复检 | 查看审计记录 | 追溯每次有效互动为什么继续、拦截或转人工 | interaction_recheck、person、quota_ledger、risk | - | - | 运营、风险、客服主管 | 否 | V1必做 |\n\n## 09 风险与财务\n\n| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 风险事件 | 创建风险事件 | 人工或系统发现风险 | person、order、refund、creator | risk_case | 待复核 | 风险、系统 | 是 | V1必做 |\n| 风险事件 | 复核通过 | 确认风险 | risk_case | risk_case、blacklist | 复核中 -> 确认风险 | 风险 | 是 | V1必做 |\n| 风险事件 | 排除风险 | 解除误报 | risk_case | risk_case | 复核中 -> 排除 | 风险 | 是 | V1必做 |\n| 黑名单 | 新增黑名单 | 阻断后续触达/返款/合作 | person、creator、identity | blacklist_item | 生效中 | 风险 | 是 | V1必做 |\n| 退款比对 | 标记双重退款 | Amazon退款+OA返款命中 | refund_records | risk_signal | 疑似/确认双重退款 | 风险、财务 | 是 | V1必做 |\n| 返款 | 审核请款 | 付款前审核 | refund_request、order、risk | refund_record | 待审核 -> 待返款/失败 | 财务 | 是 | V1必做 |\n| 返款 | 确认返款 | 完成付款/卡密发送 | refund_record | refund_record、notification | 待返款 -> 成功 | 财务 | 是 | V1必做 |\n\n## Gate 1 - 按钮盘点完成条件\n\n- 核心页面按钮均有业务含义,不只是UI动作。\n- 每个关键按钮都有读写对象、状态变化、权限和审计要求。\n- 客服执行与客服管理按钮覆盖完整。\n- KOC/KOL按钮覆盖需求完整性,并标注实现阶段。\n- 需求与计划匹配相关按钮覆盖生成、匹配、分配、调整、暂停、补量、关闭。\n- 额度预占释放、真实人合并/拆分、互动复检审计、渠道去重规则已有按钮入口。\n- Stage 2 可按对象命名对照表把页面简称映射到数据对象 v3 的正式对象。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 04 分支流程_完整需求域 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md", + "summary": "USER ERP 0 1需求重构 04 分支流程 完整需求域 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 04 分支流程 完整需求域 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 04 分支流程_完整需求域 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人\n- 核心参与:USER运营、渠道运营、客服、KOC/KOL运营、财务、风险、产品\n- 文件目的:整理主流程下所有核心分支,确保阶段1需求完整,不因V1实现边界遗漏KOC/KOL、客服或计划匹配。\n\n## 分支一:需求与计划匹配\n\n### 触发条件\n\n- OA测评计划新增或变化。\n- 销售/运营提交产品推广需求。\n- ASIN评分、差评、掉评触发回评/紧急催评。\n- 新品、库存、Listing、关键词状态变化。\n- KOC/KOL合作或带货需求进入。\n- 客服反馈某类用户或产品问题集中出现。\n\n### 流程\n\n```text\n需求进入\n-> 校验产品/ASIN/站点/店铺/库存/评分/关键词\n-> 判断需求类型\n-> 匹配计划类型\n-> 匹配执行资源\n-> 生成计划、渠道任务、客服任务或KOC/KOL任务\n-> 执行结果回流需求\n```\n\n### 读取\n\n- demand、product、asin、inventory、rating、review_stats、keyword、h5/card。\n- person_feature、reviewer、creator、quota、risk。\n- channel_config、agent_shift、agent_capacity、refund_capacity。\n\n### 写入\n\n- plan、plan_change_log、audience_snapshot、quota_reservation。\n- push_task、ticket、creator_task、risk_exclusion。\n\n### 关闭条件\n\n- 需求已转计划并完成。\n- 需求被驳回并有原因。\n- 需求被拆分或合并,并保留引用关系。\n- 需求进入待补充或待恢复,不再误触发执行。\n\n### 阶段\n\nV1必做。\n\n## 分支二:IM推送\n\n### 用户分层\n\n| 层级 | 定义 | 策略 |\n| --- | --- | --- |\n| 未参与过用户 | 注册App并绑定玩具,未参与回评/测评 | 优先推绑定产品回评卡片 |\n| 参与过用户 | 已参与回评/测评,真实人累计提交评价 < 12 | 优先催评,再推新测评 |\n| 长期测评人 | 真实人累计提交评价 >= 12 | 不优先普通测评,优先免评补单 |\n| 回评待完成 | 订单已登记但未上传评价/截图 | 催评 |\n| 测评待返款 | 已登记但缺返款信息或待返款 | 补信息/财务提醒 |\n\n### 流程\n\n```text\n人群生成\n-> 频控与风险复检\n-> 选择卡片/H5/文字/图片\n-> 推送\n-> 用户点击/回复/提交信息\n-> 系统核验订单号/返款账号/评论截图\n-> 完整则进入订单/返款/评价流程\n-> 不完整则生成客服工单或待补信息标签\n```\n\n### 必查\n\n- 用户是否黑名单。\n- 读取 `person_quota_ledgers`,按真实人判断累计提交评价、月度测评和月度免评额度。\n- 是否有未完成测评/回评。\n- 是否退款/取消订单。\n- 产品是否禁用。\n- H5/卡片是否有效。\n- 同一用户今日是否已触达。\n\n### 阶段\n\nV1必做。\n\n## 分支三:EDM每日运营\n\n### 每日先看\n\n- 域名、邮箱、IP信誉是否健康。\n- OA测评计划是否变化。\n- KV/UID人群是否正常。\n- 产品选择、H5制作和H5数据维护是否受产品禁用影响。\n- 邮件发送、打开、点击、回复、退订、投诉。\n- 当天推送计划、AB Test、审核状态。\n- 菲律宾问题、订单、客服跟进清单。\n- 邮件转化数据。\n\n### 流程\n\n```text\n基础设施检查\n-> 同步OA计划\n-> 检查UID人群\n-> 检查产品/H5/链接\n-> 生成或调整EDM任务\n-> 发送与AB Test\n-> 回收打开/点击/回复/退订/投诉\n-> 转化到订单/客服/风险/复盘\n```\n\n### 阶段\n\nV1必做基础;深度AB自动优化 V2实现。\n\n## 分支四:Phone工作流\n\n### 每日先看\n\n- 未接电话和待回拨。\n- 问题类型分布。\n- 国家/语言/时段。\n- 哪个国家电话量最高。\n- 哪个国家未接率最高。\n- 高峰时段客服是否不足。\n- 是否需要调整排班或回拨时间。\n- 客服效率和转化。\n\n### 流程\n\n```text\n生成电话名单\n-> 匹配国家/语言/时段/客服排班\n-> 拨打或回拨\n-> 记录通话结果\n-> 如有订单/评价意向则进入客服转化\n-> 如有投诉/售后则进入工单\n-> 如有KOC/KOL意向则转线索池\n```\n\n### 阶段\n\nV1预留,客服和Phone结合较强时进入V1必做。\n\n## 分支五:客服承接\n\n### 入口\n\n- 用户通过App/Email/IM/Phone发送消息。\n- 用户提交信息不完整。\n- 渠道推送带来咨询、投诉或售后问题。\n- 测评/回评/返款异常。\n- 用户答应配合后需要跟进。\n\n### 流程\n\n```text\n用户消息进入\n-> 系统生成待处理工单\n-> 自动或手动分配客服\n-> 客服查看上下文卡\n-> 客服回复和补全信息\n-> 登记订单/催评/上传结果/升级异常\n-> 工单解决或关闭\n-> 回复、转化、满意度、目标数据回流\n```\n\n### 客服转化流程\n\n```text\n客服沟通用户\n-> 用户下单或提供订单号\n-> 客服登记订单\n-> 后续拿到用户评价\n-> 上传评价结果\n-> 返款/核验\n-> 完成转化\n```\n\n### 管理流程\n\n```text\n排班\n-> 出勤\n-> 工单分配\n-> 回复统计\n-> RSO/RDO转化统计\n-> 月目标管理\n-> 绩效复盘\n```\n\n### 阶段\n\nV1必做。\n\n## 分支六:评价型订单与免评执行\n\n### 6A 评价型订单:测评/回评\n\n```text\n计划分配人群\n-> 生成订单或等待用户提交订单\n-> 核验订单号\n-> 绑定产品/ASIN/店铺/站点\n-> 上传回评/评论链接/截图\n-> 展示核验\n-> 返款/请款\n-> 完成或异常关闭\n```\n\n#### 关键规则\n\n- 非公司产品阻断。\n- 订单撤销或退款阻断。\n- 用户提交评价不等于Amazon展示确认。\n- 提交评价立即计入真实人累计提交额度;展示确认才计入评价型计划完成。\n- 更换订单、更改订单、转免评、撤销必须有日志。\n\n### 6B 免评执行:KOC/KOL为主,IM/EDM/APP协同\n\n```text\n免评计划批准\n-> 拆解KOC/KOL任务、免评Code、内容协同任务\n-> 匹配KOC/KOL、长期测评人或站外流量资源\n-> 发布内容、分发Code、同步IM/EDM/APP协同触达\n-> 回收点击、Code使用、订单、内容互动、权重结果\n-> 形成免评结果快照\n-> 达标关闭或调整KOC/素材/Code/渠道策略\n```\n\n#### 关键规则\n\n- 免评执行不以“用户提交评价”为终点,也不以 Amazon 展示评价作为完成口径。\n- 免评结果应写入 `exemption_plan_tasks`、`creator_content_records`、`exemption_result_snapshots` 等对象。\n- KOC/KOL任务、长期测评人免评补单、IM/EDM/APP协同触达可以服务同一个免评计划,但必须分别记录任务来源和结果口径。\n- 免评Code、点击、订单、内容链接、带货归因和权重结果需要单独复盘,不混入测评/回评订单完成数。\n\n### 阶段\n\n6A V1必做;6B 的免评补单与协同入口 V1必做,完整内容/归因/佣金闭环 V2实现。\n\n## 分支七:财务返款\n\n### 用户返款\n\n```text\n返款条件满足\n-> 发起请款\n-> 财务/审核确认\n-> 付款或礼品卡\n-> 凭证/卡密通知用户\n-> 返款状态回写订单和工单\n```\n\n### KOC/KOL佣金\n\n```text\n带货订单归因\n-> 计算佣金\n-> 审核\n-> 结算\n-> 争议处理\n-> 复盘表现\n```\n\n### 阶段\n\n用户返款 V1必做;KOC/KOL佣金 V1预留,V2实现。\n\n## 分支八:KOC/KOL协作\n\n### 入口\n\n- 新增KOC/KOL线索。\n- KOC每日工作流发现可合作用户。\n- 免评计划需要长期测评人或达人补单。\n- 商家/产品需要内容或带货合作。\n\n### 流程\n\n```text\n线索进入\n-> 账号/标签/风险状态检查\n-> 自动打标与人工修正\n-> 分层:新手KOC/稳定测评者/垂直专家/带货型KOL/品牌合作型\n-> 匹配产品/样品/免评/内容/带货任务\n-> 达人接受或拒绝\n-> 样品/订单/内容执行\n-> 上传内容链接或带货链接\n-> 审核、发布、归因\n-> 佣金/返款\n-> 风险与复盘\n```\n\n### 必须区分\n\n- KOC/KOL身份和普通测评人身份可能重叠,但业务对象不同。\n- KOC/KOL标签、带货、测评、免评标签不能混。\n- KOC/KOL内容任务和普通回评/测评订单不能混用完成口径。\n\n### 阶段\n\n需求完整性必须覆盖;V1预留核心入口与字段,免评补单相关 V1必做,完整内容/佣金 V2实现。\n\n## 分支九:风险与黑名单\n\n### 流程\n\n```text\n风险信号产生\n-> 判断弱风险/强风险\n-> 弱风险提醒人工确认\n-> 强风险阻断执行\n-> 人工复核\n-> 解除/确认风险\n-> 同步黑名单或恢复执行\n```\n\n### 风险来源\n\n- 黑名单命中。\n- 邮箱/电话/设备/IP/Profile/地址强弱关联。\n- 退款/取消订单。\n- 双重退款。\n- 多账号/多Profile异常。\n- 重复订单、重复评论、重复返款。\n- 差评/掉评异常。\n- KOC/KOL内容违规、佣金争议、带货归因异常。\n\n### 阶段\n\nV1必做。\n\n## Gate 1 - 分支流程完成条件\n\n- 所有核心分支都有触发、执行、写入、关闭条件。\n- 客服与KOC/KOL均作为完整需求域出现。\n- 分支结果能回流主流程和复盘。\n- 各分支有 V1必做、V1预留、V2实现边界。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 05 异常流程_完整需求域 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md", + "summary": "USER ERP 0 1需求重构 05 异常流程 完整需求域 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 05 异常流程 完整需求域 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27 所属阶段:Stage ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 05 异常流程_完整需求域 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:业务负责人 / 风险负责人 / 客服主管\n- 核心参与:USER运营、渠道运营、客服、KOC/KOL运营、财务、风险、测试\n- 文件目的:定义完整需求域中的异常、拦截、提醒、升级、人工复核和恢复机制,为 Stage 2 测试用例和状态机做输入。\n\n## 异常分级\n\n| 等级 | 含义 | 处理要求 |\n| --- | --- | --- |\n| P0 | 直接影响今日核心目标、资金、合规或账号安全 | 当天处理,主管可见 |\n| P1 | 影响本周目标或大量用户体验 | 24小时内处理 |\n| P2 | 局部异常,可进入跟进列表 | 按责任人处理 |\n| P3 | 观察项,暂不阻断 | 持续监控 |\n\n## 01 需求与执行计划不匹配\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 需求目标高于可用人群/额度 | 计划生成时匹配不足 | 提醒运营降低目标、拆分周期、转免评或补充渠道 | 目标调整或资源补足 | V1必做 |\n| 产品禁用但计划仍在执行 | 产品状态变化 | 自动暂停计划和下架H5/卡片,通知负责人 | 产品恢复或计划关闭 | V1必做 |\n| 库存不足但继续推测评/免评 | 库存低于安全线 | 限制节奏或暂停,通知运营 | 库存恢复或调整计划 | V1必做 |\n| ASIN/店铺/关键词变化未同步 | ASIN信息与计划不一致 | 阻断新推送,生成H5/卡片维护任务 | 链接和卡片确认有效 | V1必做 |\n| 计划有名额但渠道无可推人群 | 人群生成结果为空或不足 | 提醒补充人群、换渠道、延长周期 | 匹配成功或关闭计划 | V1必做 |\n| 渠道有响应但客服容量不足 | 工单/回复超时、在线人数不足 | 暂停或降频推送,调整排班 | 客服容量恢复 | V1必做 |\n| 需求变更后旧计划未暂停 | 同需求存在多个冲突计划 | 提醒主管确认,自动标记冲突 | 旧计划暂停/合并/关闭 | V1预留 |\n| 完成数口径不一致 | 计划完成、订单完成、评价展示不一致 | 进入口径复核 | 口径修正并记录 | V1必做 |\n\n## 02 评价/订单异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 订单号格式错误 | 用户提交时校验 | 提示用户重填,必要时转客服 | 正确订单号提交 | V1必做 |\n| 非公司产品 | 订单核验 | 阻断登记,转客服说明 | 工单关闭或人工改判 | V1必做 |\n| 店铺下错 | 订单店铺与计划不一致 | 转人工处理,可转免评 | 订单状态修正 | V1必做 |\n| 订单已取消 | Amazon状态 | 阻断回评和返款 | 用户换单或关闭 | V1必做 |\n| 订单已退款 | Amazon退款命中 | 进入退款比对和风险 | 风险结论完成 | V1必做 |\n| 评论重复绑定 | 评论ID/链接已绑定 | 阻断或申请例外审核 | 绑定唯一或审核通过 | V1必做 |\n| 订单重复绑定 | 订单号已绑定其他单 | 阻断,进入人工复核 | 确认归属 | V1必做 |\n| 用户只提交部分信息 | 缺返款账号/截图/链接 | 自动打标,生成客服跟进 | 信息补齐或关闭 | V1必做 |\n| 评论未展示 | Amazon展示核验失败 | 标记未展示,计划不计完成 | 后续展示或关闭 | V1必做 |\n| 掉评/差评 | Review状态变化 | 触发回评/紧急催评/风险 | 计划调整或风险关闭 | V1必做 |\n\n## 03 返款/佣金异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 缺返款账号 | 用户提交不完整 | 自动打标待返款,客服跟进 | 账号补齐 | V1必做 |\n| 返款账号格式异常 | 表单校验/人工审核 | 退回补充 | 格式正确 | V1必做 |\n| 重复请款 | 同订单/同返款ID重复 | 阻断并提醒财务 | 去重完成 | V1必做 |\n| 双重退款 | Amazon退款+OA返款命中 | 生成风险事件 | 风险确认或解除 | V1必做 |\n| 非APP用户邮件退款 | 邮件退款链路缺设备/注册邮箱维度 | 标记风险盲区,补充邮箱、订单、地址、收款信息等识别线索 | 识别维度补齐或人工复核完成 | V1必做 |\n| 返款超额 | 金额超过规则 | 进入超额审核 | 审核通过/拒绝 | V1必做 |\n| 卡密/凭证缺失 | 返款成功但无凭证 | 生成财务待补任务 | 凭证补齐 | V1预留 |\n| KOC/KOL佣金争议 | 达人反馈或金额不一致 | 进入佣金复核 | 结算修正或驳回 | V2实现 |\n\n## 04 客服异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 无客服在线 | 在线人数为0但有待处理工单 | 提醒主管,暂停部分推送 | 有客服承接 | V1必做 |\n| 工单未分配 | 待分配超时 | 自动分配或主管手动分配 | 工单有处理人 | V1必做 |\n| 首次回复超时 | 首次回复时长超过阈值 | 提醒客服/主管 | 已回复或转移 | V1必做 |\n| 等待用户超时 | 用户长时间未回复 | 自动提醒或关闭 | 用户回复/关闭 | V1必做 |\n| 客服转移失败 | 目标客服离线或超负荷 | 重新选择处理人 | 转移成功 | V1必做 |\n| 排班不足 | 高峰工单大于客服容量 | 主管调整排班或降频推送 | 容量恢复 | V1必做 |\n| 目标异常 | 月目标明显低于进度 | 提醒主管调度 | 目标追平或调整 | V1必做 |\n| 客服误登记订单 | 人工发现或订单核验失败 | 更改订单并留痕 | 订单修正 | V1必做 |\n| 用户投诉客服 | 投诉/负反馈 | 升级主管复核 | 处理结论 | V1预留 |\n\n## 05 渠道异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| IM点击低 | 漏斗低于阈值 | 换素材/卡片/人群 | 指标恢复或复盘 | V1必做 |\n| IM回复低于3% | 日常监控 | 更换图片/话术 | 回复率恢复 | V1必做 |\n| 退订或不感兴趣升高 | 退订/投诉异常 | 降频、暂停素材、调整人群 | 指标恢复 | V1必做 |\n| EDM域名/IP信誉异常 | 每日检查 | 暂停发送、切换账号、通知负责人 | 信誉恢复 | V1必做 |\n| KV/UID人群导出失败 | 人群数量异常 | 暂停推送,重跑导出 | 人群正常 | V1必做 |\n| H5链接失效 | 链接检测或用户反馈 | 下架任务,修复链接 | 链接有效 | V1必做 |\n| 卡片产品禁用未同步 | 产品状态变更 | 自动下架卡片 | 卡片状态正确 | V1必做 |\n| Phone未接率过高 | 电话数据 | 调整时段/排班/回拨 | 未接率下降 | V1预留 |\n\n## 06 KOC/KOL异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| KOC标签混乱 | 同人存在KOC/测评/带货冲突 | 人工复核标签 | 标签修正 | V1预留 |\n| KOC/KOL与C类测评人身份重叠 | 同一真实人同时命中免评推送和KOC任务 | 阻断重复触达,进入身份/任务冲突复核 | 明确走免评协同或KOC任务,不重复计完成 | V1必做 |\n| 达人重复触达 | 同一KOC/KOL多任务冲突 | 合并或暂停任务 | 冲突解除 | V1预留 |\n| 样品发出未产出内容 | 任务超时 | 催办、暂停合作、风险记录 | 内容提交或任务关闭 | V2实现 |\n| 内容链接无效 | 链接检测失败 | 要求重提 | 链接有效 | V2实现 |\n| 内容不合规 | 内容审核 | 驳回、下架、降权 | 内容修正或关闭 | V2实现 |\n| 带货订单归因失败 | Code/链接无法匹配 | 进入人工归因 | 归因完成或驳回 | V2实现 |\n| 佣金争议 | 金额或订单争议 | 财务/运营复核 | 结算修正或驳回 | V2实现 |\n| 高风险KOC继续推送 | 风险状态命中 | 阻断任务 | 风险解除或拉黑 | V1预留 |\n\n## 07 风险异常\n\n| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 |\n| --- | --- | --- | --- | --- |\n| 黑名单命中 | 身份线索命中 | 阻断触达/返款/合作 | 人工解除或保持黑名单 | V1必做 |\n| 强关联命中 | 邮箱/电话/设备/Profile/地址 | 阻断并复核 | 风险结论 | V1必做 |\n| 弱关联命中 | IP/相似地址等 | 提醒人工确认 | 确认/排除 | V1必做 |\n| 额度超限 | 月度/累计额度命中 | 阻断普通测评,转免评池 | 额度恢复或转免评 | V1必做 |\n| 多账号归并冲突 | 同一真实人下多个 JOYHUB/Profile 额度不一致 | 按 `person_quota_ledgers` 重新汇总,进入人工复核 | 额度统一并记录合并/拆分结论 | V1必做 |\n| 频控超限 | 当日/周期触达超限 | 暂停触达 | 到期恢复 | V1必做 |\n| 敏感字段导出风险 | 导出包含敏感字段 | 强制脱敏/审批 | 导出完成或拒绝 | V1必做 |\n\n## 异常处理通用状态\n\n```text\n待发现 -> 已发现 -> 待处理 -> 处理中 -> 待复核 -> 已关闭\n```\n\n可选状态:\n\n- 已升级。\n- 已驳回。\n- 已阻断。\n- 已恢复。\n- 需补充信息。\n- 需跨部门协同。\n\n## Gate 1 - 异常流程完成条件\n\n- 需求计划、评价订单、客服、渠道、KOC/KOL、返款、风险异常均覆盖。\n- 每类异常有发现方式、处理动作、关闭条件。\n- P0/P1/P2/P3等级可用于今日作战台。\n- 异常能回流计划调整、客服任务、风险事件和复盘记录。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md", + "summary": "USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:产品负责人 / 前端观察员\n- 核心参与:业务负责人、USER运营、客服主管、渠道负责人、KOC/KOL负责人、技术负责人\n- 文件目的:把现有 Vibe Coding / AI 原型作为需求验证材料,记录已覆盖能力、暴露缺口和 Stage 2 高保真模型输入。\n\n## 验证原则\n\n1. 原型不是生产代码,不作为正式系统底座。\n2. 原型用于发现页面、字段、按钮、状态和流程缺口。\n3. 验证重点不是“页面能不能打开”,而是“需求是否完整、业务对象是否统一、每日操作是否可执行”。\n4. 阶段1必须记录原型未覆盖的需求域,特别是 KOC/KOL、客服管理、需求计划匹配和复盘口径。\n\n## 验证材料\n\n| 材料 | 用途 | 结论 |\n| --- | --- | --- |\n| `C:\\XCODE\\USER\\input\\user-review-system` | React/Vite原型,含计划、测评人、订单、客服、渠道、风险、看板;入口参考 `src\\App.tsx`、`src\\config\\routes.tsx`、`src\\config\\menu.tsx` | 覆盖面较广,可作为页面/字段参考 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-PRD.md` | 回评/测评订单详细PRD | 订单页面细,但只是局部,不足以代表ERP主流程 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-architecture.md` | 订单页面技术架构 | 可参考组件拆分和待确认问题 |\n| `C:\\XCODE\\USER\\src\\user_erp_mvp_admin_prototype_v10.html` | 既有 v10 HTML 原型 | 可参考早期管理端页面,但不直接复用为新系统底座 |\n| `C:\\XCODE\\USER\\input\\用户运营系统-单文件.html` | 大型单文件运营系统原型 | 可参考全局菜单和字段,但不直接复用 |\n| `C:\\XCODE\\USER\\input\\amazon_operator_test_entry.html` | 亚马逊提评入口原型 | 可参考需求提报、IM账号、推送策略 |\n| `C:\\XCODE\\USER\\input\\客服执行.html` | 客服执行/管理原型 | 客服核心需求的重要参考 |\n| `C:\\XCODE\\USER\\input\\IM 推送业务流.mm` | IM业务流脑图 | IM分层和流转最完整 |\n\n## user-review-system 验证\n\n### 已覆盖能力\n\n| 模块 | 已覆盖 |\n| --- | --- |\n| 菜单/路由 | 工作台、评价计划、评价人管理、测评订单、回评订单、客服中心、渠道推送、风险中心、数据看板 |\n| 计划 | 测评计划、回评计划、免评计划,含列表、表单、详情、关联推送/订单 |\n| 测评人 | 测评人列表、详情、表单、额度、订单、风险、联系记录 |\n| 订单 | 测评订单、回评订单、上传订单、上传回评、请款、返款、详情抽屉 |\n| 客服 | 工单池、聊天、聊天记录、答应配合、客服绩效看板 |\n| 渠道 | 推送任务、IM推送、IM卡片、IM/EDM配置 |\n| 风险 | 风险事件、黑名单、退款比对 |\n| 看板 | 计划、ASIN、客服绩效 |\n\n### 暴露的缺口\n\n| 缺口 | 影响 | 后续处理 |\n| --- | --- | --- |\n| 需求池与计划生成链路弱 | 无法解释需求如何变成计划 | Stage 2 需新增需求与计划调度中心 |\n| 计划匹配资源视图不足 | 人群、客服、渠道、H5/卡片、风险没有统一调度 | 需新增执行匹配看板 |\n| 客服管理不完整 | 排班、出勤、目标、首次回复、转化绩效不足 | 结合客服执行原型补齐 |\n| KOC/KOL需求域不足 | 线索、任务、内容、带货、佣金缺失 | 阶段1必须补出完整需求 |\n| 订单PRD局部过细 | 容易用订单页面替代ERP主流程 | 保留为订单模块参考 |\n| 状态枚举不统一 | 测评、回评、返款、计划、客服状态口径混乱 | Stage 2需统一状态机 |\n| Mock 数据含临时品类 | 部分示例与真实业务不一致 | 只取字段结构,不取业务事实 |\n\n## 用户运营系统单文件验证\n\n### 已覆盖\n\n- USER评价业务闭环系统的全局菜单雏形。\n- 产品、计划、订单、客服、风险、真实人、用户信息等多类字段。\n- 较多运营筛选、状态、详情、操作入口。\n\n### 缺口\n\n- 文件过大,页面和数据逻辑混杂,不适合做正式底座。\n- 未清晰区分需求完整范围和V1实现范围。\n- KOC/KOL相关需求不完整。\n- 客服核心执行与管理需要与 `客服执行.html` 合并分析。\n- 需求到计划再到执行匹配的调度视图不足。\n\n## amazon_operator_test_entry 验证\n\n### 已覆盖\n\n- 亚马逊提评入口。\n- 提评单列表。\n- IM推送。\n- IM账号管理。\n- 新建提评单。\n- 推送策略。\n- 卡片内容管理。\n- 业务类型:测评、免评、回评。\n\n### 可纳入需求\n\n- 提评需求提报字段:站点、ASIN、商品、关键词、关键词链接、品牌、新品状态、要求数量、店铺、原因、备注。\n- IM账号和身份管理:官方客服、品牌客服、达人、内部/外部品牌。\n- 推送策略:用户标签、去除标签、优先级、间隔时间、内容形式、跳转链接。\n\n### 缺口\n\n- 未打通完整需求池和计划对象。\n- 提评入口与USER运营调度中心的关系需重构。\n- IM账号管理需与渠道配置、权限和风险联动。\n\n## 客服执行原型验证\n\n### 已覆盖\n\n- 客服执行看板。\n- 首页Dashboard。\n- 出勤管理。\n- 排班管理。\n- 工单管理。\n- 回复统计。\n- 转化绩效。\n- 目标管理。\n- 角色权限:客服与主管/管理员视角。\n\n### 可纳入需求\n\n| 能力 | 说明 |\n| --- | --- |\n| 工单自动分配 | 在线优先、按工单量平均、最大工单数 |\n| 客服视角 | 只看自己工单、绩效和目标 |\n| 主管视角 | 查看团队数据、调整工单、排班和目标 |\n| 绩效统计 | RSO/RDO登记订单、上评、完成率 |\n| 出勤/排班 | 早班、午班、晚班、迟到、早退、请假、缺勤 |\n\n### 缺口\n\n- 与评价计划、渠道推送和订单履约的联动需要补强。\n- 客服容量尚未进入需求与计划匹配。\n- 客服误登记、投诉、转移失败、排班不足等异常需要补全。\n\n## IM推送业务流验证\n\n### 已覆盖\n\n- 用户分层:未参与、参与过、长期测评人。\n- 推送前风控校验。\n- 回评卡片、测评卡片、免评产品、催评消息。\n- 用户提交订单号、返款账号、评论截图/链接。\n- 订单号核实和客服补全。\n- 财务返款提醒。\n- 自动打标体系。\n- 店铺紧急催评。\n\n### 可直接作为Stage 2输入\n\n- IM用户分层。\n- 核心标签体系。\n- 每次互动复检。\n- 未完成流程的标签与自动通知。\n- 长期测评人转免评策略。\n\n### 缺口\n\n- IM流程需要与统一计划、额度台账、客服工单和风险事件对象对齐。\n- 文档中提到的“当天测评计划需要刷的名额”需要接入计划匹配。\n\n## KOC/KOL需求验证\n\n### 已有线索\n\n- `KOC 每日工作流.md` 提到账号、标签、风险、新增线索、用户分层、自动打标、任务去重、回复、订单、返款、Review、带货、风险。\n- `商家KOL商业服务系统-USER项目知识迁移清单.md` 提供服务包、KOC/KOL分层、佣金原则、内容分级和推荐分发接口意识。\n- 现有原型仅零散覆盖KOC/KOL,未形成完整中心。\n\n### 阶段1必须补出的需求\n\n- KOC/KOL线索池。\n- KOC/KOL档案和分层。\n- KOC/KOL与测评人/真实人的身份关系。\n- 内容任务、样品/免评、带货任务。\n- 内容审核、链接、Code、订单归因。\n- 佣金计算与结算接口。\n- KOC/KOL风险和异常。\n\n### 阶段建议\n\n- V1必做:免评补单相关的KOC/KOL入口、标签、风险和任务关联。\n- V1预留:KOC/KOL档案、线索池、合作状态。\n- V2实现:内容审核、带货归因、佣金结算、商家服务包完整后台。\n\n## 进入Stage 2的输入清单\n\n| 输入 | 说明 |\n| --- | --- |\n| 需求与计划调度中心 | 需要新设计,不应从现有订单页面改 |\n| 今日作战台 | 需要按OKR和岗位每日工作方式设计 |\n| 客服执行/管理 | 可综合 `客服相关模块.md` 和 `客服执行.html` |\n| IM推送 | 可基于 `IM 推送业务流.mm` 和 user-review-system IM页面 |\n| 订单页面 | 可参考 review-order PRD,但需合并测评/回评/免评口径 |\n| 测评人/真实人 | 可参考 `测评信息字段管理.xlsx` 和数据对象v3 |\n| KOC/KOL中心 | 现有原型不足,需要新建需求模型 |\n| 风险中心 | 可参考现有风险页面,但需扩展KOC/KOL与计划匹配异常 |\n\n## 不能直接继承的内容\n\n- 不能直接继承 Vibe Coding 的文件结构作为正式工程结构。\n- 不能把 mock 数据当业务事实。\n- 不能把回评/测评订单PRD当成整个 ERP PRD。\n- 不能因为 V1不实现完整KOC/KOL平台,就在阶段1删除KOC/KOL需求。\n- 不能把客服降级为“工单页面”,客服是核心执行和转化资源。\n\n## Gate 1 - Vibe Coding验证完成条件\n\n- 已记录所有原型的可用需求点。\n- 已记录原型不能支撑的业务缺口。\n- 已确认正式系统需要重新做 Stage 2 高保真模型。\n- 已确认KOC/KOL、客服、需求计划匹配是现有原型的主要补强方向。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1)", + "type": "document", + "name": "USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1", + "filePath": "05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1).md", + "summary": "USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1 文件信息 文件名称: 20260527 USER ERP 0 1需求重构 06 VibeCoding页面验证记录 v1.md 项目路径: C:\\XCODE\\USER 输出位置: C:\\XCODE\\USER\\output\\docs 当前版本: v1 最近更新: 2026 05 27", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "simple", + "knowledgeMeta": { + "content": "# USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1\n\n## 文件信息\n\n- 文件名称:`20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md`\n- 项目路径:`C:\\XCODE\\USER`\n- 输出位置:`C:\\XCODE\\USER\\output\\docs`\n- 当前版本:`v1`\n- 最近更新:`2026-05-27`\n- 所属阶段:Stage 1 完整业务需求\n- 负责人:产品负责人 / 前端观察员\n- 核心参与:业务负责人、USER运营、客服主管、渠道负责人、KOC/KOL负责人、技术负责人\n- 文件目的:把现有 Vibe Coding / AI 原型作为需求验证材料,记录已覆盖能力、暴露缺口和 Stage 2 高保真模型输入。\n\n## 验证原则\n\n1. 原型不是生产代码,不作为正式系统底座。\n2. 原型用于发现页面、字段、按钮、状态和流程缺口。\n3. 验证重点不是“页面能不能打开”,而是“需求是否完整、业务对象是否统一、每日操作是否可执行”。\n4. 阶段1必须记录原型未覆盖的需求域,特别是 KOC/KOL、客服管理、需求计划匹配和复盘口径。\n\n## 验证材料\n\n| 材料 | 用途 | 结论 |\n| --- | --- | --- |\n| `C:\\XCODE\\USER\\input\\user-review-system` | React/Vite原型,含计划、测评人、订单、客服、渠道、风险、看板;入口参考 `src\\App.tsx`、`src\\config\\routes.tsx`、`src\\config\\menu.tsx` | 覆盖面较广,可作为页面/字段参考 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-PRD.md` | 回评/测评订单详细PRD | 订单页面细,但只是局部,不足以代表ERP主流程 |\n| `C:\\XCODE\\USER\\input\\user-review-system\\docs\\review-order-architecture.md` | 订单页面技术架构 | 可参考组件拆分和待确认问题 |\n| `C:\\XCODE\\USER\\src\\user_erp_mvp_admin_prototype_v10.html` | 既有 v10 HTML 原型 | 可参考早期管理端页面,但不直接复用为新系统底座 |\n| `C:\\XCODE\\USER\\input\\用户运营系统-单文件.html` | 大型单文件运营系统原型 | 可参考全局菜单和字段,但不直接复用 |\n| `C:\\XCODE\\USER\\input\\amazon_operator_test_entry.html` | 亚马逊提评入口原型 | 可参考需求提报、IM账号、推送策略 |\n| `C:\\XCODE\\USER\\input\\客服执行.html` | 客服执行/管理原型 | 客服核心需求的重要参考 |\n| `C:\\XCODE\\USER\\input\\IM 推送业务流.mm` | IM业务流脑图 | IM分层和流转最完整 |\n\n## user-review-system 验证\n\n### 已覆盖能力\n\n| 模块 | 已覆盖 |\n| --- | --- |\n| 菜单/路由 | 工作台、评价计划、评价人管理、测评订单、回评订单、客服中心、渠道推送、风险中心、数据看板 |\n| 计划 | 测评计划、回评计划、免评计划,含列表、表单、详情、关联推送/订单 |\n| 测评人 | 测评人列表、详情、表单、额度、订单、风险、联系记录 |\n| 订单 | 测评订单、回评订单、上传订单、上传回评、请款、返款、详情抽屉 |\n| 客服 | 工单池、聊天、聊天记录、答应配合、客服绩效看板 |\n| 渠道 | 推送任务、IM推送、IM卡片、IM/EDM配置 |\n| 风险 | 风险事件、黑名单、退款比对 |\n| 看板 | 计划、ASIN、客服绩效 |\n\n### 暴露的缺口\n\n| 缺口 | 影响 | 后续处理 |\n| --- | --- | --- |\n| 需求池与计划生成链路弱 | 无法解释需求如何变成计划 | Stage 2 需新增需求与计划调度中心 |\n| 计划匹配资源视图不足 | 人群、客服、渠道、H5/卡片、风险没有统一调度 | 需新增执行匹配看板 |\n| 客服管理不完整 | 排班、出勤、目标、首次回复、转化绩效不足 | 结合客服执行原型补齐 |\n| KOC/KOL需求域不足 | 线索、任务、内容、带货、佣金缺失 | 阶段1必须补出完整需求 |\n| 订单PRD局部过细 | 容易用订单页面替代ERP主流程 | 保留为订单模块参考 |\n| 状态枚举不统一 | 测评、回评、返款、计划、客服状态口径混乱 | Stage 2需统一状态机 |\n| Mock 数据含临时品类 | 部分示例与真实业务不一致 | 只取字段结构,不取业务事实 |\n\n## 用户运营系统单文件验证\n\n### 已覆盖\n\n- USER评价业务闭环系统的全局菜单雏形。\n- 产品、计划、订单、客服、风险、真实人、用户信息等多类字段。\n- 较多运营筛选、状态、详情、操作入口。\n\n### 缺口\n\n- 文件过大,页面和数据逻辑混杂,不适合做正式底座。\n- 未清晰区分需求完整范围和V1实现范围。\n- KOC/KOL相关需求不完整。\n- 客服核心执行与管理需要与 `客服执行.html` 合并分析。\n- 需求到计划再到执行匹配的调度视图不足。\n\n## amazon_operator_test_entry 验证\n\n### 已覆盖\n\n- 亚马逊提评入口。\n- 提评单列表。\n- IM推送。\n- IM账号管理。\n- 新建提评单。\n- 推送策略。\n- 卡片内容管理。\n- 业务类型:测评、免评、回评。\n\n### 可纳入需求\n\n- 提评需求提报字段:站点、ASIN、商品、关键词、关键词链接、品牌、新品状态、要求数量、店铺、原因、备注。\n- IM账号和身份管理:官方客服、品牌客服、达人、内部/外部品牌。\n- 推送策略:用户标签、去除标签、优先级、间隔时间、内容形式、跳转链接。\n\n### 缺口\n\n- 未打通完整需求池和计划对象。\n- 提评入口与USER运营调度中心的关系需重构。\n- IM账号管理需与渠道配置、权限和风险联动。\n\n## 客服执行原型验证\n\n### 已覆盖\n\n- 客服执行看板。\n- 首页Dashboard。\n- 出勤管理。\n- 排班管理。\n- 工单管理。\n- 回复统计。\n- 转化绩效。\n- 目标管理。\n- 角色权限:客服与主管/管理员视角。\n\n### 可纳入需求\n\n| 能力 | 说明 |\n| --- | --- |\n| 工单自动分配 | 在线优先、按工单量平均、最大工单数 |\n| 客服视角 | 只看自己工单、绩效和目标 |\n| 主管视角 | 查看团队数据、调整工单、排班和目标 |\n| 绩效统计 | RSO/RDO登记订单、上评、完成率 |\n| 出勤/排班 | 早班、午班、晚班、迟到、早退、请假、缺勤 |\n\n### 缺口\n\n- 与评价计划、渠道推送和订单履约的联动需要补强。\n- 客服容量尚未进入需求与计划匹配。\n- 客服误登记、投诉、转移失败、排班不足等异常需要补全。\n\n## IM推送业务流验证\n\n### 已覆盖\n\n- 用户分层:未参与、参与过、长期测评人。\n- 推送前风控校验。\n- 回评卡片、测评卡片、免评产品、催评消息。\n- 用户提交订单号、返款账号、评论截图/链接。\n- 订单号核实和客服补全。\n- 财务返款提醒。\n- 自动打标体系。\n- 店铺紧急催评。\n\n### 可直接作为Stage 2输入\n\n- IM用户分层。\n- 核心标签体系。\n- 每次互动复检。\n- 未完成流程的标签与自动通知。\n- 长期测评人转免评策略。\n\n### 缺口\n\n- IM流程需要与统一计划、额度台账、客服工单和风险事件对象对齐。\n- 文档中提到的“当天测评计划需要刷的名额”需要接入计划匹配。\n\n## KOC/KOL需求验证\n\n### 已有线索\n\n- `KOC 每日工作流.md` 提到账号、标签、风险、新增线索、用户分层、自动打标、任务去重、回复、订单、返款、Review、带货、风险。\n- `商家KOL商业服务系统-USER项目知识迁移清单.md` 提供服务包、KOC/KOL分层、佣金原则、内容分级和推荐分发接口意识。\n- 现有原型仅零散覆盖KOC/KOL,未形成完整中心。\n\n### 阶段1必须补出的需求\n\n- KOC/KOL线索池。\n- KOC/KOL档案和分层。\n- KOC/KOL与测评人/真实人的身份关系。\n- 内容任务、样品/免评、带货任务。\n- 内容审核、链接、Code、订单归因。\n- 佣金计算与结算接口。\n- KOC/KOL风险和异常。\n\n### 阶段建议\n\n- V1必做:免评补单相关的KOC/KOL入口、标签、风险和任务关联。\n- V1预留:KOC/KOL档案、线索池、合作状态。\n- V2实现:内容审核、带货归因、佣金结算、商家服务包完整后台。\n\n## 进入Stage 2的输入清单\n\n| 输入 | 说明 |\n| --- | --- |\n| 需求与计划调度中心 | 需要新设计,不应从现有订单页面改 |\n| 今日作战台 | 需要按OKR和岗位每日工作方式设计 |\n| 客服执行/管理 | 可综合 `客服相关模块.md` 和 `客服执行.html` |\n| IM推送 | 可基于 `IM 推送业务流.mm` 和 user-review-system IM页面 |\n| 订单页面 | 可参考 review-order PRD,但需合并测评/回评/免评口径 |\n| 测评人/真实人 | 可参考 `测评信息字段管理.xlsx` 和数据对象v3 |\n| KOC/KOL中心 | 现有原型不足,需要新建需求模型 |\n| 风险中心 | 可参考现有风险页面,但需扩展KOC/KOL与计划匹配异常 |\n\n## 不能直接继承的内容\n\n- 不能直接继承 Vibe Coding 的文件结构作为正式工程结构。\n- 不能把 mock 数据当业务事实。\n- 不能把回评/测评订单PRD当成整个 ERP PRD。\n- 不能因为 V1不实现完整KOC/KOL平台,就在阶段1删除KOC/KOL需求。\n- 不能把客服降级为“工单页面”,客服是核心执行和转化资源。\n\n## Gate 1 - Vibe Coding验证完成条件\n\n- 已记录所有原型的可用需求点。\n- 已记录原型不能支撑的业务缺口。\n- 已确认正式系统需要重新做 Stage 2 高保真模型。\n- 已确认KOC/KOL、客服、需求计划匹配是现有原型的主要补强方向。\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1", + "type": "document", + "name": "USER ERP 0-1需求重构 · 可点击参考原型 v1", + "filePath": "05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1.html", + "summary": "USER ERP 0-1需求重构 · 可点击参考原型 v1 USER ERP Stage 1 Prototype 模拟数据 仅用于流程验证 当前模块 今日作战 从异常、资源和今日动作进入业务闭环。 高频跳转 6 P0/P1异常 37 需求待评估 12 客服超时 9 免评缺口 4 额度预警 18 复盘待写 7 今日作战台 高级主管 / USER运营 / 客服 ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "complex", + "knowledgeMeta": { + "content": "\n\n\n \n \n USER ERP 0-1需求重构 · 可点击参考原型 v1\n \n\n\n
\n \n\n
\n
\n
\n 今日作战台\n 高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险\n
\n
\n \n
\n \n \n \n
\n \n \n \n
\n
\n
\n
\n
\n\n
\n \n
\n\n \n\n\n", + "wikilinks": [], + "category": "layer-requirements" + } + }, + { + "id": "doc:05_需求文档/20260528_USER_ERP_完整参考原型_v2", + "type": "document", + "name": "USER ERP 完整参考原型 v2 · 2026-05-28", + "filePath": "05_需求文档/20260528_USER_ERP_完整参考原型_v2.html", + "summary": "USER ERP 完整参考原型 v2 · 2026-05-28 USER ERP Stage 1 · v2 模拟数据 仅用于流程验证 当前模块 今日作战 从异常、资源和今日动作进入业务闭环。 高频跳转 6 P0/P1异常 37 需求待评估 8 客服超时工单 3 免评缺口 4 额度预警 18 复盘待写 7 今日作战台 高级主管 / USER运营 / 客服 / ", + "tags": [ + "05_需求文档", + "需求文档" + ], + "complexity": "complex", + "knowledgeMeta": { + "content": "\n\n\n\n\nUSER ERP 完整参考原型 v2 · 2026-05-28\n\n\n\n
\n\n\n\n\n
\n
\n
今日作战台高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险
\n
\n\n
\n\n\n\n
\n
\n
\n
\n
\n\n\n
\n\n
\n\n\n
\n ⚠ 本原型所有数据均为模拟数据,仅用于 Stage 1 需求验证 · v2\n
\n\n", + "wikilinks": [], + "category": "layer-requirements" + } } ], "edges": [ @@ -2325,6 +2600,110 @@ "direction": "forward", "description": "本层文档", "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/通用EDM业务流程说明", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/通用IM业务流程与接口频率限制说明", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-technical", + "target": "doc:07_技术文档/01-子系统-identity-数据库表关系", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-testing", + "target": "doc:08_测试相关/USER用户运营系统_原型逐页详细测试用例集", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1)", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 + }, + { + "source": "flow:layer-requirements", + "target": "doc:05_需求文档/20260528_USER_ERP_完整参考原型_v2", + "type": "documents", + "direction": "forward", + "description": "本层文档", + "weight": 0.65 } ], "layers": [ @@ -2367,7 +2746,18 @@ "doc:05_需求文档/user_erp_mvp_admin_prototype_v10(1)", "doc:05_需求文档/user_erp_mvp_admin_prototype_v10", "doc:05_需求文档/客服执行", - "doc:05_需求文档/用户运营系统-单文件" + "doc:05_需求文档/用户运营系统-单文件", + "doc:05_需求文档/通用EDM业务流程说明", + "doc:05_需求文档/通用IM业务流程与接口频率限制说明", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1", + "doc:05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1)", + "doc:05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1", + "doc:05_需求文档/20260528_USER_ERP_完整参考原型_v2" ] }, { @@ -2397,20 +2787,21 @@ { "id": "layer-technical", "name": "技术文档", - "description": "系统架构、数据模型、接口说明、技术方案和技术决策。", + "description": "系统架构、数据模型、接口说明、技术方案和技术决策。点击本层可查看全部技术文档并检索。", "nodeIds": [ "flow:layer-technical", "doc:07_技术文档/README", "doc:07_技术文档/技术决策记录", "doc:07_技术文档/技术文档索引", "doc:07_技术文档/接口说明模板", - "doc:07_技术文档/系统架构说明模板" + "doc:07_技术文档/系统架构说明模板", + "doc:07_技术文档/01-子系统-identity-数据库表关系" ] }, { "id": "layer-testing", "name": "测试相关", - "description": "测试计划、测试用例、缺陷记录、验收记录和上线检查。", + "description": "测试计划、测试用例、缺陷记录、验收记录、上线检查和测试资产。点击本层可查看全部测试相关文档并检索。", "nodeIds": [ "flow:layer-testing", "doc:08_测试相关/README", @@ -2419,7 +2810,8 @@ "doc:08_测试相关/测试用例索引", "doc:08_测试相关/测试计划模板", "doc:08_测试相关/缺陷记录模板", - "doc:08_测试相关/验收记录模板" + "doc:08_测试相关/验收记录模板", + "doc:08_测试相关/USER用户运营系统_原型逐页详细测试用例集" ] }, { diff --git a/wishfulfilled-wiki/.understand-anything/meta.json b/wishfulfilled-wiki/.understand-anything/meta.json index 5fef6c4..1668cb9 100644 --- a/wishfulfilled-wiki/.understand-anything/meta.json +++ b/wishfulfilled-wiki/.understand-anything/meta.json @@ -1,8 +1,8 @@ { - "lastAnalyzedAt": "2026-05-27T07:14:45.036Z", + "lastAnalyzedAt": "2026-05-29T06:03:33.528Z", "gitCommitHash": "", "version": "1.0.0", - "analyzedFiles": 60, + "analyzedFiles": 73, "theme": { "presetId": "dark", "accentId": "cyan" diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md new file mode 100644 index 0000000..1debe31 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md @@ -0,0 +1,261 @@ +# USER ERP 0-1需求重构 - 01 主流程说明 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_01_主流程说明_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:业务负责人 +- 核心参与:USER运营、客服、渠道运营、KOC/KOL运营、财务、风险、产品、前端观察员 +- 文件目的:把 USER ERP 的完整业务主流程从需求进入写到结果复盘,避免后续只围绕单页或单模块开发。 + +## 主流程总览 + +USER ERP 的主流程不是从推送开始,也不是从测评单开始,而是从“需求能否变成可执行计划”开始。 + +```mermaid +flowchart TB + A["需求进入"] --> B["需求评估"] + B --> C["产品/ASIN/库存/评分校验"] + C --> D["生成或调整计划"] + D --> E["执行资源匹配"] + E --> F["渠道任务/客服任务/KOC-KOL任务生成"] + F --> G["用户或达人响应"] + G --> H["客服承接与信息补全"] + H --> I["订单/评价/内容/返款/佣金履约"] + I --> J["风险复检与异常处理"] + J --> K["结果回流到计划与需求"] + K --> L["复盘、关闭或二次计划"] +``` + +## 主流程核心原则 + +1. 需求完整性优先于 V1 实现边界。 +2. 每个需求必须能追到执行计划、执行对象、执行结果和复盘结论。 +3. 测评、回评、免评、KOC/KOL任务共用真实人、风险、额度、订单、客服上下文。 +4. 客服既是承接用户问题的执行团队,也是评价转化、订单登记、催评和异常闭环的核心执行资源。 +5. KOC/KOL不是纯外部接口,而是完整需求域:线索、分层、任务、内容、带货、佣金、风险都要在阶段1被定义。 +6. 每次有效互动都要复检身份、额度、风险、订单和未关闭承诺。 +7. 用户提交评价与 Amazon 展示确认必须拆开;计划完成口径不能混用。 + +## 业务对象 + +| 对象 | 含义 | 阶段 | +| --- | --- | --- | +| 需求 | OA/销售/运营/异常触发/KOC-KOL合作请求 | V1必做 | +| 计划 | 测评、回评、免评、KOC/KOL任务、客服跟进计划 | V1必做 | +| 执行资源 | 渠道、人群、客服、测评人、KOC/KOL、H5/卡片、返款能力 | V1必做 | +| 真实人 | 跨账号、邮箱、电话、Profile、设备归并后的核心身份 | V1必做 | +| 测评人 | 可参与测评/回评/免评的运营对象,是真实人的业务视图 | V1必做 | +| KOC/KOL | 创作者/带货/内容合作对象,可与测评人身份重叠 | V1预留 | +| 订单 | Amazon订单、测评订单、回评订单、免评订单、样品/带货订单 | V1必做 | +| 评价 | 用户提交、评论链接/截图、Amazon展示确认、掉评/差评 | V1必做 | +| 返款/佣金 | 用户返款、礼品卡、财务返款、KOC/KOL佣金 | 用户返款V1必做,佣金V1预留 | +| 工单 | 客服消息、售后问题、催评、答应配合、异常跟进 | V1必做 | +| 风险事件 | 黑名单、重复退款、额度超限、异常账号、内容/佣金风险 | V1必做 | +| 复盘记录 | 需求关闭、计划表现、渠道表现、客服表现、KOC/KOL表现 | V1预留 | + +## 主流程分解 + +### 1. 需求进入 + +| 内容 | 说明 | +| --- | --- | +| 触发来源 | OA测评计划、销售/运营手动需求、ASIN评分异常、掉评/差评、库存/Listing状态变化、KOC/KOL合作需求、客服反馈 | +| 必填信息 | 需求编号、需求类型、产品/ASIN、站点、店铺、目标、优先级、截止时间、需求人、负责人 | +| 可选信息 | 关键词、关键词链接、目标受众、目标Review数、预算/追加金额、指定渠道、指定KOC/KOL | +| 输出 | 进入需求池,状态为待评估 | +| 阶段 | V1必做 | + +### 2. 需求评估 + +运营需要判断: + +- 需求是测评、回评、免评、KOC/KOL任务、客服跟进,还是混合需求。 +- ASIN当前评分、Review数、差评、掉评、库存、Listing状态是否支持执行。 +- 需求目标是否超过可用人群、额度、渠道和客服容量。 +- 是否存在产品禁用、店铺异常、关键词失效、H5/卡片缺失。 +- 是否需要审批、拆分计划、延迟、驳回或转其他计划类型。 + +输出状态: + +- 评估通过,进入计划生成。 +- 需补充信息,退回需求人。 +- 暂缓,等待产品/库存/Listing恢复。 +- 驳回,记录原因。 + +阶段:V1必做。 + +### 3. 产品/ASIN/库存/评分校验 + +| 校验项 | 处理 | +| --- | --- | +| 产品是否启用 | 禁用则禁止生成渠道任务,允许进入待恢复池 | +| ASIN是否正确 | 错误则退回补充或人工修正 | +| 站点/店铺是否匹配 | 不匹配则阻断计划下发 | +| 库存是否充足 | 库存紧张时限制测评/免评节奏 | +| 评分/掉评/差评 | 触发回评或紧急催评策略 | +| 关键词/H5/卡片 | 缺失则生成维护任务 | + +阶段:V1必做。 + +### 4. 计划生成或调整 + +计划类型必须保留: + +| 计划类型 | 说明 | 阶段 | +| --- | --- | --- | +| 测评计划 | 为产品增加评价、冲销量、拉排名、新品启动等 | V1必做 | +| 回评计划 | 对已购/已测/待评价人群催评或稳定评分 | V1必做 | +| 免评计划 | 面向长期测评人、KOC/KOL或补单需求,不计普通测评额度 | V1必做 | +| KOC/KOL合作任务 | 样品、内容、带货、佣金、复盘 | V1预留/V2实现 | +| 客服跟进计划 | 售后、催评、答应配合、异常用户跟进 | V1必做 | + +计划生成时必须写入: + +- 关联需求编号。 +- 产品/ASIN/站点/店铺。 +- 计划目标、周期、每日节奏、优先级。 +- 渠道策略。 +- 目标人群条件。 +- 额度预占策略。 +- 风险排除策略。 +- 客服承接要求。 +- H5/卡片/素材要求。 +- 关闭条件。 + +### 5. 执行资源匹配 + +计划不是创建后直接发出去,必须先匹配资源。 + +| 资源 | 匹配规则 | +| --- | --- | +| 人群 | 用户层级、国家、品类偏好、活跃、历史订单、Review额度、风险 | +| 测评人 | 可测评次数、可免评次数、可上评次数、合作状态、掉评率、退款取消记录 | +| KOC/KOL | 分层、品类、内容能力、带货能力、合作状态、风险状态 | +| 渠道 | IM/EDM/Phone/APP/KOC-KOL可用性、频控、退订、投诉、转化 | +| 客服 | 在线状态、排班、工单量、国家/语言、转化目标、当前压力 | +| 产品素材 | H5、卡片、关键词链接、首图、编码图、跳转链接 | +| 财务 | 可返款方式、返款账号、礼品卡卡密、审核队列 | +| 风险 | 黑名单、强弱关联、退款取消、重复订单/评论/返款 | + +阶段:V1必做半自动匹配,V2增强自动推荐。 + +### 6. 渠道任务 / 客服任务 / KOC-KOL任务生成 + +| 任务 | 生成条件 | 输出 | +| --- | --- | --- | +| IM推送任务 | 有可推人群、卡片/H5可用、频控通过 | 推送任务、渠道事件、标签 | +| EDM任务 | 域名/邮箱/IP健康、UID人群正常、邮件素材/H5可用 | 邮件计划、AB Test、发送结果 | +| Phone任务 | 用户有电话、适合电话沟通、客服容量可承接 | 电话名单、回拨任务、通话记录 | +| 客服工单 | 用户回复、订单异常、信息缺失、售后问题、投诉 | 工单、处理人、状态 | +| KOC/KOL任务 | 有合适达人、样品/免评/内容目标明确 | 合作任务、内容/带货跟踪 | +| 财务返款任务 | 用户信息完整、订单/评价状态满足返款条件 | 请款/返款任务 | + +### 7. 用户或达人响应 + +响应包括: + +- 用户点击、回复、提交订单号、提交返款账号、上传评论截图/链接。 +- 用户只提交部分信息。 +- 用户投诉、退订、不感兴趣。 +- KOC/KOL接受任务、拒绝任务、提交内容链接、提交带货链接。 + +每次响应都要写入渠道事件,并触发有效互动复检。 + +### 8. 客服承接与信息补全 + +客服核心动作: + +- 查看用户上下文卡。 +- 回复用户。 +- 查询/核验订单号。 +- 补充返款账号、截图、评论链接。 +- 登记订单。 +- 催评。 +- 记录售后问题和解决方案。 +- 标记答应配合。 +- 升级风险、财务、运营或主管。 +- 关闭或重开工单。 + +客服管理动作: + +- 分配/转移工单。 +- 调整排班。 +- 设置目标。 +- 统计回复、工单、转化、满意度。 + +阶段:V1必做。 + +### 9. 履约:订单 / 评价 / 返款 / 佣金 + +| 履约对象 | 核心状态 | +| --- | --- | +| 订单 | 待登记、已登记、已发货、已取消、已退款、异常 | +| 评价 | 待提交、已提交、待展示核验、已展示、未展示、掉评、差评 | +| 返款 | 待请款、待审核、审核失败、待返款、返款成功、返款锁定 | +| KOC/KOL内容 | 待接受、待寄样、待提交、待审核、已发布、链接异常 | +| KOC/KOL佣金 | 待归因、待计算、待审核、待结算、已结算、争议 | + +### 10. 风险复检与异常处理 + +复检时机: + +- 需求评估。 +- 计划生成。 +- 人群生成。 +- 渠道发送前。 +- 用户提交订单/评价/返款账号。 +- 客服登记订单。 +- 请款/返款。 +- KOC/KOL接受任务、提交内容、结算佣金。 + +风险结果: + +- 正常,继续执行。 +- 弱风险,提醒人工确认。 +- 强风险,阻断并生成风险事件。 +- 确认风险,同步黑名单。 + +### 11. 结果回流与复盘 + +结果必须回流到: + +- 原需求:是否达成、是否关闭、是否需补量。 +- 计划:完成数、节奏、渠道表现、成本。 +- ASIN:评分、Review、差评、掉评。 +- 用户/测评人:额度、合作状态、标签、风险。 +- 客服:转化、回复、目标、绩效。 +- KOC/KOL:内容、带货、佣金、合作等级。 +- 渠道:素材、频控、人群、AB Test。 + +阶段:V1预留复盘记录,核心指标 V1必做。 + +## 状态总表 + +| 状态域 | 必须拆开 | +| --- | --- | +| 需求状态 | 草稿、待评估、需补充、已通过、已驳回、已转计划、已关闭 | +| 计划状态 | 草稿、待审批、进行中、暂停、已完成、已取消、需补量 | +| 资源匹配状态 | 待匹配、匹配中、匹配不足、匹配完成、需人工确认 | +| 渠道任务状态 | 待发送、发送中、已发送、失败、已下架、暂停 | +| 用户响应状态 | 未响应、已点击、已回复、已提交部分信息、已提交完整信息、投诉/退订 | +| 工单状态 | 待分配、待处理、处理中、等待用户、已解决、已关闭、已重开 | +| 订单状态 | 待登记、已登记、已发货、已取消、已退款、异常 | +| 评价状态 | 待提交、已提交、待核验、已展示、未展示、掉评、差评 | +| 返款状态 | 待请款、待审核、审核失败、待返款、返款成功、锁定 | +| KOC/KOL任务状态 | 待分配、待接受、进行中、待内容、待审核、已发布、已结算、异常 | +| 风险状态 | 正常、弱风险、强风险、已拦截、复核中、已解除、已拉黑 | + +## Gate 1 - 主流程完成条件 + +- 主流程从需求进入到结果复盘完整。 +- 需求与执行计划匹配被明确为核心流程。 +- 客服被写入主流程核心位置。 +- KOC/KOL被作为完整需求域纳入,而不是仅做接口预留。 +- 测评、回评、免评、客服、渠道、财务、风险、看板之间的流转关系清楚。 +- V1必做、V1预留、V2实现、待确认有明确标注。 + diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md new file mode 100644 index 0000000..0c6854f --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md @@ -0,0 +1,198 @@ +# USER ERP 0-1需求重构 - 02 日常操作页面结构 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_02_日常操作页面结构_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:业务负责人 / 产品负责人 +- 核心参与:USER运营、客服主管、渠道负责人、KOC/KOL负责人、风险、财务、前端观察员 +- 文件目的:定义每个岗位每天打开系统后先看什么、判断什么、处理什么,并据此组织页面结构。 + +## 页面设计原则 + +1. 第一屏必须是日常作战台,不是普通任务列表。 +2. 页面围绕“发现异常 -> 判断优先级 -> 分配动作 -> 执行 -> 跟进结果 -> 复盘沉淀”设计。 +3. 每个页面必须说明贡献哪个OKR:用户增长、评价转化、销售转化、活动转化、满意度、风险控制。 +4. 页面结构要服务需求与执行计划匹配,不能只展示静态数据。 +5. 客服执行和客服管理是一级核心入口。 +6. KOC/KOL在阶段1必须有页面结构,即使V1只预留部分入口。 + +## 一级导航建议 + +| 一级入口 | 定位 | 阶段 | +| --- | --- | --- | +| 今日作战台 | 所有角色的每日首页,显示异常、目标、今日必处理 | V1必做 | +| 需求与计划调度中心 | 需求池、计划生成、执行资源匹配、计划调整 | V1必做 | +| 评价计划与订单中心 | 测评、回评、免评计划和订单履约 | V1必做 | +| 客服执行中心 | 工单、聊天、答应配合、催评、订单登记 | V1必做 | +| 客服管理中心 | 出勤、排班、目标、绩效、服务质量 | V1必做 | +| 渠道运营中心 | IM、EDM、Phone、APP/H5/卡片、推送任务 | V1必做 | +| KOC/KOL协作中心 | 线索、达人、任务、内容、带货、佣金、风险 | V1预留/V2实现 | +| 测评人/真实人中心 | 测评人档案、身份归并、额度、风险、历史 | V1必做 | +| 风险与黑名单中心 | 风险事件、黑名单、退款比对、异常复检 | V1必做 | +| 数据复盘看板 | 计划、ASIN、渠道、客服、KOC/KOL、财务复盘 | V1必做核心,V2增强 | +| 系统配置 | 权限、渠道配置、字段、标签、通知、导入导出 | V1预留 | + +## 01 今日作战台 + +### 目标 + +让主管和各岗位每天开屏后立刻知道:昨天有什么异常、本周目标是否危险、今天必须推进什么、哪些任务没人处理、哪些结果需要复盘。 + +### 页面区块 + +| 区块 | 展示内容 | 操作 | +| --- | --- | --- | +| 昨日核心指标 | 需求新增、计划新增、完成、缺口、客服工单、风险、返款 | 查看详情 | +| P0/P1异常 | Review低于计划、渠道下滑、客服超时、产品禁用、风险事件 | 指派处理、升级 | +| 今日必跟进 | 待评估需求、待补量计划、待催评用户、待返款、待审核内容 | 进入处理 | +| 目标进度 | 月度测评、回评、免评、客服转化、KOC/KOL内容/带货 | 调整计划 | +| 资源压力 | 可用人群、额度、客服容量、KOC/KOL资源、返款队列 | 资源调度 | +| 昨日复盘 | TOP/BOTTOM任务、异常原因、沉淀动作 | 创建复盘 | + +### 角色视图 + +| 角色 | 首页重点 | +| --- | --- | +| 高级主管 | OKR、资源、P0异常、跨部门阻塞 | +| USER运营 | 需求、计划、执行缺口、渠道/客服压力 | +| 渠道运营 | 触达漏斗、素材、可推人群、退订/投诉 | +| 客服主管 | 在线、排班、待处理工单、首次回复、转化目标 | +| KOC/KOL运营 | 线索、任务、内容、带货、风险 | +| 风险/财务 | 待审核、待返款、双重退款、敏感操作 | + +## 02 需求与计划调度中心 + +### 子页面 + +| 页面 | 说明 | 阶段 | +| --- | --- | --- | +| 需求池 | OA/销售/运营/异常/KOC-KOL需求统一入口 | V1必做 | +| 需求评估页 | 校验ASIN、产品、库存、评分、目标、优先级 | V1必做 | +| 计划编排页 | 生成测评、回评、免评、客服、KOC/KOL计划 | V1必做 | +| 执行匹配看板 | 人群、渠道、客服、KOC/KOL、风险、H5/卡片匹配 | V1必做基础 | +| 计划调整页 | 补量、暂停、恢复、转免评、关闭、拆分/合并 | V1必做 | + +### 需求池字段 + +| 字段 | 说明 | +| --- | --- | +| 需求编号 | OA/系统生成 | +| 需求来源 | OA、销售、运营、ASIN异常、客服反馈、KOC/KOL | +| 需求类型 | 测评、回评、免评、客服跟进、KOC/KOL内容/带货 | +| 产品/ASIN/站点/店铺 | 执行基础 | +| 目标 | 目标订单数、Review数、评分、内容数、带货数 | +| 优先级 | S/A/B/C 或 P0/P1/P2 | +| 截止时间 | 计划完成约束 | +| 当前状态 | 待评估、需补充、已转计划、暂停、关闭 | +| 负责人 | 运营负责人 | + +## 03 评价计划与订单中心 + +### 页面结构 + +| 页面 | 说明 | 阶段 | +| --- | --- | --- | +| 测评计划 | 产品测评需求、计划周期、渠道、目标、进度、评分、库存 | V1必做 | +| 回评计划 | 掉评/差评/维稳/冲刺等回评需求与每日目标 | V1必做 | +| 免评计划 | 长期测评人/KOC-KOL/补单免评计划 | V1必做 | +| 测评订单 | 订单登记、上传回评、请款、返款、状态跟踪 | V1必做 | +| 回评订单 | 回评上传、确认、返款、售后来源、处理记录 | V1必做 | +| 产品/ASIN详情 | 评分、Review、库存、关键词、渠道、H5/卡片 | V1预留 | + +## 04 客服执行中心 + +客服执行中心是 V1 核心入口。 + +| 页面 | 每天要解决的问题 | 核心操作 | +| --- | --- | --- | +| 工单池 | 哪些用户消息没人处理,哪些工单快超时 | 分配、领取、转移、标记解决、关闭 | +| 我的工单 | 当前客服今天处理什么 | 回复、登记订单、催评、上传结果、升级 | +| 聊天/消息 | 用户具体说了什么 | 快捷回复、补充信息、创建跟进 | +| 服务聊天记录 | 复查历史沟通 | 查询、查看上下文 | +| 答应配合 | 用户答应评价/反馈后是否完成 | 确认、拒绝、提醒、过期 | +| 催评池 | 哪些测评/回评待催 | 发送提醒、转人工、关闭 | +| 售后详情 | 用户问题、解决方案、订单、返款 | 记录方案、回访、升级 | + +## 05 客服管理中心 + +| 页面 | 展示 | 操作 | +| --- | --- | --- | +| 客服Dashboard | 在线客服、今日工单、待处理、今日转化、本月目标 | 查看详情 | +| 出勤管理 | 应出勤、实际出勤、出勤率、迟到/请假/缺勤 | 导入/调整 | +| 排班管理 | 早班、午班、晚班、渠道、最大工单数 | 设置、批量排班、调整 | +| 工单分配管理 | 分配规则、当前工单量、超时工单 | 自动分配、手动调整 | +| 回复统计 | 回复用户数、处理工单、消息数、首次回复 | 导出 | +| 转化绩效 | RSO/RDO登记订单、获取评价、完成率 | 查看排行 | +| 目标管理 | 月目标、历史完成、客服个人趋势 | 设置目标、调整 | + +## 06 渠道运营中心 + +| 页面 | 说明 | 阶段 | +| --- | --- | --- | +| IM推送 | 用户分层、卡片、推送任务、回复、转化 | V1必做 | +| IM卡片/H5管理 | 卡片、图片、链接、跳转、产品禁用联动 | V1必做 | +| EDM每日检查 | 域名/邮箱/IP信誉、UID、H5、AB Test、转化 | V1必做基础 | +| Phone工作台 | 未接/待回拨、国家/时段、问题类型、客服容量 | V1预留 | +| 推送配置 | 渠道账号、日限额、状态、负责人 | V1预留 | +| 渠道漏斗看板 | 推送、曝光、点击、回复、订单、评价 | V1必做 | + +## 07 KOC/KOL协作中心 + +阶段1必须完整定义,V1可部分预留。 + +| 页面 | 说明 | 阶段 | +| --- | --- | --- | +| KOC/KOL线索池 | 新增线索、来源、标签、风险、分层 | V1预留 | +| 达人档案 | 国家、品类、内容能力、带货能力、合作状态 | V1预留 | +| 合作任务 | 样品、免评、内容、带货、佣金任务 | V1预留/V2 | +| 内容记录 | 内容链接、审核、发布时间、表现 | V2实现 | +| 带货归因 | Code、链接、订单、销售额 | V2实现 | +| 佣金结算 | 佣金规则、结算、争议 | V2实现 | +| KOC/KOL风险 | 标签混乱、重复触达、违规内容、佣金争议 | V1预留 | + +## 08 测评人/真实人中心 + +| 页面 | 说明 | 阶段 | +| --- | --- | --- | +| 测评人列表 | 编号、Joyhub、邮箱、电话、Profile、国家、标签、合作状态 | V1必做 | +| 测评人详情 | 额度、订单、评价、返款、风险、沟通记录 | V1必做 | +| 真实人归并 | 标准化姓名+地址、设备、邮箱、电话、Profile、收款信息等自动归并;人工确认/拆分作为修正入口 | 自动归并 V1必做,人工确认/拆分 V1预留 | +| 额度台账 | 月度测评、月度免评、累计Review、预占/释放 | V1必做 | +| 风险记录 | 黑名单、退款取消、掉评、弱/强关联 | V1必做 | + +说明:`4/4/12`额度控制依赖真实人维度,V1必须先具备自动归并能力,否则 `person_quota_ledgers` 无法跨 JOYHUB ID、Amazon 账号和 Profile 合并计算。 + +## 09 风险与黑名单中心 + +| 页面 | 说明 | +| --- | --- | +| 风险事件 | 所有风险信号与人工复核案件 | +| 黑名单 | 生效中/已移除、风险等级、来源、原因 | +| 退款比对 | Amazon退款与OA返款双重比对 | +| 关联风险 | 邮箱、电话、设备、IP、Profile、地址、返款账号 | +| 内容/佣金风险 | KOC/KOL内容不合规、归因失败、佣金争议 | + +## 10 数据复盘看板 + +| 看板 | 指标 | +| --- | --- | +| 计划看板 | 总计划、进行中、完成率、缺口、补量 | +| ASIN看板 | 评分、Review、差评、掉评、库存、风险ASIN | +| 渠道看板 | 推送、曝光、点击、回复、转化、退订/投诉 | +| 客服看板 | 工单、响应、解决、满意度、RSO/RDO转化 | +| 测评人看板 | 额度、完成率、掉评率、风险、合作状态 | +| KOC/KOL看板 | 线索、任务、内容、带货、佣金、风险 | +| 财务看板 | 待请款、待审核、返款成功、异常返款 | + +## Gate 1 - 页面结构完成条件 + +- 今日作战台和岗位每日工作方式已定义。 +- 需求与计划调度中心作为一级核心入口。 +- 客服执行中心和客服管理中心作为一级核心入口。 +- KOC/KOL协作中心已有完整需求结构,即使V1不全量实现。 +- 各页面能对应主流程中的对象、状态、动作和复盘指标。 diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md new file mode 100644 index 0000000..f5c5563 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md @@ -0,0 +1,190 @@ +# USER ERP 0-1需求重构 - 03 功能页面按钮盘点表 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_03_功能页面按钮盘点表_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:产品负责人 / 前端观察员 +- 核心参与:业务用户、客服主管、渠道运营、KOC/KOL运营、财务、风险、后端、测试 +- 文件目的:盘点阶段1涉及的页面按钮、业务含义、读写对象、状态变化、权限和审计要求,作为 Stage 2 按钮行为矩阵的输入。 + +## 盘点规则 + +| 字段 | 说明 | +| --- | --- | +| 页面 | 按一级/二级页面归类 | +| 按钮/动作 | 用户可点击或系统触发的动作 | +| 业务含义 | 为什么需要这个按钮 | +| 读取对象 | 点击前必须读的数据 | +| 写入对象 | 点击后必须落盘的数据 | +| 状态变化 | 影响哪些状态 | +| 权限 | 哪些角色可操作 | +| 审计 | 是否必须记录操作日志 | +| 阶段 | V1必做、V1预留、V2实现、待确认 | + +## 对象命名对照 + +按钮盘点表中的“读取对象/写入对象”保留页面语境简称;进入 Stage 2 接口和数据设计时,应优先映射到数据对象 v3 的正式对象名。 + +| 按钮盘点简称 | v3正式对象或聚合口径 | +| --- | --- | +| person | `person_profiles` | +| identity | `person_identity_links` | +| reviewer | `person_identity_links` + `person_quota_ledgers` 聚合视图 | +| quota / quota_ledger | `person_quota_ledgers` | +| quota_reservation | `quota_reservations` | +| audience / audience_snapshot | `audience_snapshots` | +| exclusion | `audience_exclusions` | +| risk_event | `risk_signals` / `risk_cases` | +| risk | `risk_signals` / `risk_cases` / `blacklist_entities` 聚合摘要 | +| order / review_order | `amazon_orders`、`review_submission_records`、`review_display_checks` | +| push_task / channel_event | `channel_route_decisions`、`channel_dedup_records`、各渠道事件表 | +| interaction_recheck | `interaction_recheck_records` | +| koc_kol / creator | JOYCOLLAB 的 `creator_id`,以及 `exemption_plan_tasks`、`creator_content_records`、`exemption_result_snapshots` | + +## 01 今日作战台 + +| 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 查看异常 | 进入昨日/今日异常详情 | alert、plan、channel_event、ticket、risk_event | - | - | 主管、运营 | 否 | V1必做 | +| 指派处理人 | 把异常转成可执行任务 | alert、staff、shift | task、notification | 异常:待处理 -> 已指派 | 主管、运营 | 是 | V1必做 | +| 升级异常 | P0/P1问题升级主管或跨部门 | alert、risk_event、ticket | escalation、notification | 异常:待处理 -> 已升级 | 主管、风险 | 是 | V1必做 | +| 调整计划 | 从作战台直接进入计划调整 | demand、plan、progress | plan_change_log | 计划:进行中 -> 调整中/暂停/补量 | 主管、运营 | 是 | V1必做 | +| 创建复盘 | 对异常或任务形成复盘记录 | plan、channel_event、ticket、order | retrospective | 异常:已处理 -> 待复盘 | 主管、运营 | 是 | V1预留 | + +## 02 需求与计划调度中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 需求池 | 新增需求 | 手动录入销售/运营/KOC-KOL需求 | product、asin、staff | demand | 需求:草稿 | 运营、主管 | 是 | V1必做 | +| 需求池 | 导入OA需求 | 从OA或表格批量导入需求 | import_file | demand、import_log | 需求:待评估 | 运营、管理员 | 是 | V1必做 | +| 需求池 | 需求评估 | 判断需求是否可执行 | demand、product、asin、inventory、risk | demand_review | 待评估 -> 已通过/需补充/驳回 | 运营、主管 | 是 | V1必做 | +| 需求池 | 退回补充 | 信息不足时退回需求人 | demand | demand、notification | 待评估 -> 需补充 | 运营 | 是 | V1必做 | +| 需求池 | 生成计划 | 将需求转成测评/回评/免评/KOC-KOL/客服计划 | demand、product、audience、quota、risk | plan、plan_log | 已通过 -> 已转计划 | 运营、主管 | 是 | V1必做 | +| 计划编排 | 拆分计划 | 一个需求拆成多个站点/渠道/周期计划 | demand、plan | plan、plan_relation | 计划:草稿/进行中 | 运营、主管 | 是 | V1预留 | +| 计划编排 | 合并计划 | 合并同产品/同周期/同目标计划 | plan | plan_relation、plan_log | 计划:合并/关闭旧计划 | 主管 | 是 | V1预留 | +| 执行匹配 | 匹配人群 | 生成候选用户/测评人/KOC-KOL名单 | person、reviewer、koc_kol、quota、risk | audience_snapshot、exclusion | 匹配:待匹配 -> 完成/不足 | 运营 | 是 | V1必做 | +| 执行匹配 | 预占额度 | 锁定测评/免评/Review额度 | audience_snapshot、quota_ledger | quota_reservation | 额度:可用 -> 预占 | 运营 | 是 | V1必做 | +| 执行匹配 | 手动释放预占 | 预占超时、计划取消或人群撤出后释放额度 | quota_reservation、quota_ledger、plan | quota_reservation_log、quota_ledger | 额度:已预占 -> 已释放 | 主管、管理员 | 是 | V1必做 | +| 执行匹配 | 分配客服 | 按容量、语言、工单量分配承接客服 | plan、ticket、shift、agent_capacity | task、ticket_assignment | 待分配 -> 已分配 | 主管、客服主管 | 是 | V1必做 | +| 执行匹配 | 生成推送任务 | 从计划生成人群和渠道任务 | plan、audience、card、h5、channel_config | push_task、channel_event | 待发送 | 渠道运营 | 是 | V1必做 | +| 执行匹配 | 匹配KOC/KOL | 将需求匹配给合适创作者 | koc_kol_profile、task、risk | koc_kol_task | 待分配 -> 待接受 | KOC/KOL运营 | 是 | V1预留 | +| 计划调整 | 调整名额 | 增减计划数量 | plan、progress、resource | plan_change_log | 计划目标变化 | 运营、主管 | 是 | V1必做 | +| 计划调整 | 暂停计划 | 禁止继续执行但保留历史 | plan、risk、product | plan_log、notification | 进行中 -> 暂停 | 主管 | 是 | V1必做 | +| 计划调整 | 恢复计划 | 恢复已暂停计划 | plan、product、risk | plan_log | 暂停 -> 进行中 | 主管 | 是 | V1必做 | +| 计划调整 | 转免评 | 普通测评/回评因店铺/订单/用户问题转免评 | plan、order、reviewer | plan、order_log | 测评/回评 -> 免评 | 运营、主管 | 是 | V1必做 | +| 计划调整 | 关闭需求 | 需求完成或取消 | demand、plan、progress | demand_close_log | 需求:已关闭 | 主管 | 是 | V1必做 | + +## 03 评价计划与订单中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 测评计划 | 新建测评计划 | 从需求或人工创建计划 | demand、product、asin | review_plan | 草稿/未开始 | 运营 | 是 | V1必做 | +| 测评计划 | 编辑计划 | 修改目标、渠道、周期、负责人 | review_plan | plan_log | 计划字段变化 | 运营、主管 | 是 | V1必做 | +| 测评计划 | 复制关键词链接 | 运营执行用 | review_plan | copy_event | - | 运营 | 否 | V1必做 | +| 测评计划 | 查看关联推送 | 查看计划下发的渠道任务 | plan、push_task | - | - | 运营、渠道 | 否 | V1必做 | +| 测评计划 | 查看关联订单 | 查看计划履约订单 | plan、order | - | - | 运营 | 否 | V1必做 | +| 回评计划 | 新建回评计划 | 因掉评/差评/维稳生成回评计划 | asin、review_stats | reply_plan | 待开始 | 运营 | 是 | V1必做 | +| 回评计划 | 调整今日目标 | 根据评分/掉评变化调整当天目标 | reply_plan、asin_stats | plan_log | 今日目标变化 | 运营、主管 | 是 | V1必做 | +| 免评计划 | 新建免评计划 | 给长期测评人/KOC-KOL/补单池创建免评计划 | demand、reviewer、koc_kol | free_plan | 待审批/进行中 | 运营 | 是 | V1必做 | +| 测评订单 | 新增订单 | 记录测评执行单 | plan、person、product | review_order | 待上传回评/待登记 | 运营、客服 | 是 | V1必做 | +| 测评订单 | 上传订单 | 绑定Amazon订单号 | review_order、amazon_order | review_order、audit_log | 订单:待登记 -> 已登记 | 运营、客服 | 是 | V1必做 | +| 测评订单 | 上传回评 | 绑定评论ID/链接/截图 | review_order、comment | review_submission | 待回评 -> 待确认/已提交 | 运营、客服 | 是 | V1必做 | +| 测评订单 | 提交排队 | 评论暂不能绑定,进入排队 | review_order、comment_query | review_queue | 待确认 | 运营、客服 | 是 | V1必做 | +| 测评订单 | 请款 | 发起用户返款 | review_order、refund_account | refund_request | 待请款 -> 待审核 | 运营、客服 | 是 | V1必做 | +| 测评订单 | 更换订单 | 用新订单替代原订单 | review_order、amazon_order | order_change_log | 订单号变化 | 运营、主管 | 是 | V1必做 | +| 测评订单 | 更改订单 | 修正订单字段 | review_order | order_change_log | 订单字段变化 | 运营、主管 | 是 | V1必做 | +| 测评订单 | 撤销 | 撤销错误/风险订单 | review_order、risk | order_log、quota_release | 进行中 -> 撤销 | 风险、主管 | 是 | V1必做 | +| 测评订单 | 批量导出 | 导出当前筛选结果 | order、permission | export_task | - | 有导出权限 | 是 | V1必做 | +| 回评订单 | 回评确认 | 核验回评是否有效 | review_submission、comment | review_display_check | 待确认 -> 已回评/未通过 | 运营、审核 | 是 | V1必做 | + +## 04 客服执行中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 工单池 | 自动分配 | 按在线、工单量、排班分配客服 | ticket、agent、shift | ticket_assignment | 待分配 -> 待处理 | 客服主管 | 是 | V1必做 | +| 工单池 | 手动分配 | 指定处理人 | ticket、agent | ticket_assignment | 待分配 -> 待处理 | 客服主管 | 是 | V1必做 | +| 工单池 | 转移工单 | 换客服处理 | ticket、agent | ticket_transfer_log | 处理人变化 | 客服、主管 | 是 | V1必做 | +| 我的工单 | 回复用户 | 处理用户消息 | ticket、chat、context_snapshot | chat_message、ticket_log | 待处理 -> 处理中/等待用户 | 客服 | 是 | V1必做 | +| 我的工单 | 登记订单 | 客服拿到订单号后登记 | ticket、amazon_order、person | review_order、ticket_log | 工单推进 | 客服 | 是 | V1必做 | +| 我的工单 | 催评 | 提醒用户提交评价 | ticket、order、followup | reminder_event、followup | 催评次数+1 | 客服、运营 | 是 | V1必做 | +| 我的工单 | 标记解决 | 用户问题解决 | ticket | ticket_log | 处理中 -> 已解决 | 客服 | 是 | V1必做 | +| 我的工单 | 关闭工单 | 完成或无需继续 | ticket | ticket_log | 已解决/等待用户 -> 已关闭 | 客服、主管 | 是 | V1必做 | +| 我的工单 | 重开工单 | 用户再次反馈 | ticket | ticket_log | 已关闭 -> 已重开/处理中 | 客服、主管 | 是 | V1必做 | +| 答应配合 | 确认答应 | 用户答应评价/反馈 | ticket、person | support_followup | 待确认 -> 已确认 | 客服 | 是 | V1必做 | +| 答应配合 | 拒绝 | 用户拒绝配合 | ticket | support_followup | 待确认 -> 已拒绝 | 客服 | 是 | V1必做 | +| 答应配合 | 标记过期 | 超时未提交 | followup | support_followup | 已确认 -> 已过期 | 客服、系统 | 是 | V1必做 | + +## 05 客服管理中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 出勤管理 | 导入出勤 | 导入当天/本月出勤 | attendance_file | attendance_record | 出勤记录更新 | 客服主管、人事 | 是 | V1预留 | +| 出勤管理 | 调整出勤状态 | 修正迟到/请假/缺勤 | attendance_record | attendance_log | 状态变化 | 客服主管 | 是 | V1必做 | +| 排班管理 | 设置班次 | 给客服设置早/午/晚班 | agent、date | shift_schedule | 排班变化 | 客服主管 | 是 | V1必做 | +| 排班管理 | 批量排班 | 一次生成多天排班 | agent、date_range | shift_schedule | 排班批量变化 | 客服主管 | 是 | V1必做 | +| 目标管理 | 设置月目标 | 设置RSO/RDO登记和上评目标 | agent、period | support_target | 目标变化 | 客服主管 | 是 | V1必做 | +| 绩效看板 | 导出绩效 | 导出回复/转化/目标完成 | performance_snapshot | export_task | - | 客服主管 | 是 | V1必做 | + +## 06 渠道运营中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| IM推送 | 新增推送 | 创建IM触达任务 | plan、audience、card | im_push_task | 草稿/待发送 | 渠道运营 | 是 | V1必做 | +| IM推送 | 上架/下架 | 控制任务是否可执行 | im_push_task | channel_log | 已上架/已下架 | 渠道运营 | 是 | V1必做 | +| IM推送 | 批量分配 | 将任务分配到账号/渠道 | push_task、im_account | assignment | 待分配 -> 已分配 | 渠道运营 | 是 | V1预留 | +| IM卡片 | 新增卡片 | 创建卡片素材 | product、h5 | im_card | 已上架/草稿 | 渠道运营 | 是 | V1必做 | +| IM卡片 | 下架卡片 | 产品禁用/素材失效时下架 | im_card、product | card_log | 已上架 -> 已下架 | 渠道运营 | 是 | V1必做 | +| EDM | 检查基础设施 | 确认域名/邮箱/IP信誉 | edm_config、deliverability | daily_check | 健康/异常 | EDM运营 | 否 | V1必做 | +| EDM | 创建AB Test | 测试标题/素材/按钮 | edm_campaign | ab_test | 运行中 | EDM运营 | 是 | V1预留 | +| Phone | 创建回拨任务 | 对未接/待跟进用户安排电话 | tel_pool、agent_shift | tel_task | 待拨打 | Phone/客服 | 是 | V1预留 | +| 渠道配置 | 配置去重规则 | 定义跨IM/EDM/APP/Phone/KOC触达的去重、例外和频控条件 | channel_config、person、risk、quota | channel_dedup_rule、channel_log | 规则生效/停用 | 渠道主管、管理员 | 是 | V1预留 | + +## 07 KOC/KOL协作中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 线索池 | 新增线索 | 新增KOC/KOL候选人 | source、contact | creator_profile | 待审核 | KOC/KOL运营 | 是 | V1预留 | +| 线索池 | 打标分层 | 区分新手KOC、稳定测评者、垂直专家、带货型KOL | creator_profile、metrics | creator_tag、tier_log | 分层变化 | KOC/KOL运营 | 是 | V1预留 | +| 任务管理 | 创建合作任务 | 样品、免评、内容、带货任务 | demand、product、creator | creator_task | 待接受 | KOC/KOL运营 | 是 | V1预留 | +| 任务管理 | 分配样品/免评名额 | 执行合作资源分配 | creator_task、inventory、quota | sample_order/free_order | 待寄样/待执行 | KOC/KOL运营 | 是 | V1预留 | +| 内容记录 | 上传内容链接 | 记录发布内容 | creator_task | content_record | 待审核/已发布 | KOC/KOL运营 | 是 | V2实现 | +| 内容记录 | 审核内容 | 判断内容合规和是否可分发 | content_record、content_policy | content_audit | 通过/驳回 | 审核、运营 | 是 | V2实现 | +| 带货归因 | 同步订单 | 记录Code/链接带来的订单 | attribution、order | creator_order | 待结算 | KOC/KOL运营 | 是 | V2实现 | +| 佣金结算 | 计算佣金 | 根据规则生成佣金 | creator_order、commission_rule | commission_record | 待审核 | 财务、运营 | 是 | V2实现 | +| 风险处理 | 暂停合作 | 风险/投诉/作弊时暂停 | creator_profile、risk | creator_status_log | 可合作 -> 暂停 | 风险、主管 | 是 | V1预留 | + +## 08 测评人/真实人中心 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 真实人归并 | 手动合并 | 修正自动归并不足,把多个账号/身份线索合并到同一真实人 | person、identity、quota_ledger、risk | person_identity_link、merge_log、quota_ledger | 多个身份 -> 同一真实人 | 主管、风险、管理员 | 是 | V1预留 | +| 真实人归并 | 手动拆分 | 纠正误合并,拆开不同真实人 | person、identity、quota_ledger、risk | person_identity_link、split_log、quota_ledger | 同一真实人 -> 多个真实人 | 主管、风险、管理员 | 是 | V1预留 | +| 额度台账 | 查看额度明细 | 查看4/4/12额度、预占、释放、提交计数来源 | person、quota_ledger、quota_reservation、review_submission | - | - | 运营、客服主管、风险 | 否 | V1必做 | +| 互动复检 | 查看审计记录 | 追溯每次有效互动为什么继续、拦截或转人工 | interaction_recheck、person、quota_ledger、risk | - | - | 运营、风险、客服主管 | 否 | V1必做 | + +## 09 风险与财务 + +| 页面 | 按钮/动作 | 业务含义 | 读取对象 | 写入对象 | 状态变化 | 权限 | 审计 | 阶段 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 风险事件 | 创建风险事件 | 人工或系统发现风险 | person、order、refund、creator | risk_case | 待复核 | 风险、系统 | 是 | V1必做 | +| 风险事件 | 复核通过 | 确认风险 | risk_case | risk_case、blacklist | 复核中 -> 确认风险 | 风险 | 是 | V1必做 | +| 风险事件 | 排除风险 | 解除误报 | risk_case | risk_case | 复核中 -> 排除 | 风险 | 是 | V1必做 | +| 黑名单 | 新增黑名单 | 阻断后续触达/返款/合作 | person、creator、identity | blacklist_item | 生效中 | 风险 | 是 | V1必做 | +| 退款比对 | 标记双重退款 | Amazon退款+OA返款命中 | refund_records | risk_signal | 疑似/确认双重退款 | 风险、财务 | 是 | V1必做 | +| 返款 | 审核请款 | 付款前审核 | refund_request、order、risk | refund_record | 待审核 -> 待返款/失败 | 财务 | 是 | V1必做 | +| 返款 | 确认返款 | 完成付款/卡密发送 | refund_record | refund_record、notification | 待返款 -> 成功 | 财务 | 是 | V1必做 | + +## Gate 1 - 按钮盘点完成条件 + +- 核心页面按钮均有业务含义,不只是UI动作。 +- 每个关键按钮都有读写对象、状态变化、权限和审计要求。 +- 客服执行与客服管理按钮覆盖完整。 +- KOC/KOL按钮覆盖需求完整性,并标注实现阶段。 +- 需求与计划匹配相关按钮覆盖生成、匹配、分配、调整、暂停、补量、关闭。 +- 额度预占释放、真实人合并/拆分、互动复检审计、渠道去重规则已有按钮入口。 +- Stage 2 可按对象命名对照表把页面简称映射到数据对象 v3 的正式对象。 diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md new file mode 100644 index 0000000..b0949c3 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md @@ -0,0 +1,352 @@ +# USER ERP 0-1需求重构 - 04 分支流程_完整需求域 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_04_分支流程_完整需求域_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:业务负责人 +- 核心参与:USER运营、渠道运营、客服、KOC/KOL运营、财务、风险、产品 +- 文件目的:整理主流程下所有核心分支,确保阶段1需求完整,不因V1实现边界遗漏KOC/KOL、客服或计划匹配。 + +## 分支一:需求与计划匹配 + +### 触发条件 + +- OA测评计划新增或变化。 +- 销售/运营提交产品推广需求。 +- ASIN评分、差评、掉评触发回评/紧急催评。 +- 新品、库存、Listing、关键词状态变化。 +- KOC/KOL合作或带货需求进入。 +- 客服反馈某类用户或产品问题集中出现。 + +### 流程 + +```text +需求进入 +-> 校验产品/ASIN/站点/店铺/库存/评分/关键词 +-> 判断需求类型 +-> 匹配计划类型 +-> 匹配执行资源 +-> 生成计划、渠道任务、客服任务或KOC/KOL任务 +-> 执行结果回流需求 +``` + +### 读取 + +- demand、product、asin、inventory、rating、review_stats、keyword、h5/card。 +- person_feature、reviewer、creator、quota、risk。 +- channel_config、agent_shift、agent_capacity、refund_capacity。 + +### 写入 + +- plan、plan_change_log、audience_snapshot、quota_reservation。 +- push_task、ticket、creator_task、risk_exclusion。 + +### 关闭条件 + +- 需求已转计划并完成。 +- 需求被驳回并有原因。 +- 需求被拆分或合并,并保留引用关系。 +- 需求进入待补充或待恢复,不再误触发执行。 + +### 阶段 + +V1必做。 + +## 分支二:IM推送 + +### 用户分层 + +| 层级 | 定义 | 策略 | +| --- | --- | --- | +| 未参与过用户 | 注册App并绑定玩具,未参与回评/测评 | 优先推绑定产品回评卡片 | +| 参与过用户 | 已参与回评/测评,真实人累计提交评价 < 12 | 优先催评,再推新测评 | +| 长期测评人 | 真实人累计提交评价 >= 12 | 不优先普通测评,优先免评补单 | +| 回评待完成 | 订单已登记但未上传评价/截图 | 催评 | +| 测评待返款 | 已登记但缺返款信息或待返款 | 补信息/财务提醒 | + +### 流程 + +```text +人群生成 +-> 频控与风险复检 +-> 选择卡片/H5/文字/图片 +-> 推送 +-> 用户点击/回复/提交信息 +-> 系统核验订单号/返款账号/评论截图 +-> 完整则进入订单/返款/评价流程 +-> 不完整则生成客服工单或待补信息标签 +``` + +### 必查 + +- 用户是否黑名单。 +- 读取 `person_quota_ledgers`,按真实人判断累计提交评价、月度测评和月度免评额度。 +- 是否有未完成测评/回评。 +- 是否退款/取消订单。 +- 产品是否禁用。 +- H5/卡片是否有效。 +- 同一用户今日是否已触达。 + +### 阶段 + +V1必做。 + +## 分支三:EDM每日运营 + +### 每日先看 + +- 域名、邮箱、IP信誉是否健康。 +- OA测评计划是否变化。 +- KV/UID人群是否正常。 +- 产品选择、H5制作和H5数据维护是否受产品禁用影响。 +- 邮件发送、打开、点击、回复、退订、投诉。 +- 当天推送计划、AB Test、审核状态。 +- 菲律宾问题、订单、客服跟进清单。 +- 邮件转化数据。 + +### 流程 + +```text +基础设施检查 +-> 同步OA计划 +-> 检查UID人群 +-> 检查产品/H5/链接 +-> 生成或调整EDM任务 +-> 发送与AB Test +-> 回收打开/点击/回复/退订/投诉 +-> 转化到订单/客服/风险/复盘 +``` + +### 阶段 + +V1必做基础;深度AB自动优化 V2实现。 + +## 分支四:Phone工作流 + +### 每日先看 + +- 未接电话和待回拨。 +- 问题类型分布。 +- 国家/语言/时段。 +- 哪个国家电话量最高。 +- 哪个国家未接率最高。 +- 高峰时段客服是否不足。 +- 是否需要调整排班或回拨时间。 +- 客服效率和转化。 + +### 流程 + +```text +生成电话名单 +-> 匹配国家/语言/时段/客服排班 +-> 拨打或回拨 +-> 记录通话结果 +-> 如有订单/评价意向则进入客服转化 +-> 如有投诉/售后则进入工单 +-> 如有KOC/KOL意向则转线索池 +``` + +### 阶段 + +V1预留,客服和Phone结合较强时进入V1必做。 + +## 分支五:客服承接 + +### 入口 + +- 用户通过App/Email/IM/Phone发送消息。 +- 用户提交信息不完整。 +- 渠道推送带来咨询、投诉或售后问题。 +- 测评/回评/返款异常。 +- 用户答应配合后需要跟进。 + +### 流程 + +```text +用户消息进入 +-> 系统生成待处理工单 +-> 自动或手动分配客服 +-> 客服查看上下文卡 +-> 客服回复和补全信息 +-> 登记订单/催评/上传结果/升级异常 +-> 工单解决或关闭 +-> 回复、转化、满意度、目标数据回流 +``` + +### 客服转化流程 + +```text +客服沟通用户 +-> 用户下单或提供订单号 +-> 客服登记订单 +-> 后续拿到用户评价 +-> 上传评价结果 +-> 返款/核验 +-> 完成转化 +``` + +### 管理流程 + +```text +排班 +-> 出勤 +-> 工单分配 +-> 回复统计 +-> RSO/RDO转化统计 +-> 月目标管理 +-> 绩效复盘 +``` + +### 阶段 + +V1必做。 + +## 分支六:评价型订单与免评执行 + +### 6A 评价型订单:测评/回评 + +```text +计划分配人群 +-> 生成订单或等待用户提交订单 +-> 核验订单号 +-> 绑定产品/ASIN/店铺/站点 +-> 上传回评/评论链接/截图 +-> 展示核验 +-> 返款/请款 +-> 完成或异常关闭 +``` + +#### 关键规则 + +- 非公司产品阻断。 +- 订单撤销或退款阻断。 +- 用户提交评价不等于Amazon展示确认。 +- 提交评价立即计入真实人累计提交额度;展示确认才计入评价型计划完成。 +- 更换订单、更改订单、转免评、撤销必须有日志。 + +### 6B 免评执行:KOC/KOL为主,IM/EDM/APP协同 + +```text +免评计划批准 +-> 拆解KOC/KOL任务、免评Code、内容协同任务 +-> 匹配KOC/KOL、长期测评人或站外流量资源 +-> 发布内容、分发Code、同步IM/EDM/APP协同触达 +-> 回收点击、Code使用、订单、内容互动、权重结果 +-> 形成免评结果快照 +-> 达标关闭或调整KOC/素材/Code/渠道策略 +``` + +#### 关键规则 + +- 免评执行不以“用户提交评价”为终点,也不以 Amazon 展示评价作为完成口径。 +- 免评结果应写入 `exemption_plan_tasks`、`creator_content_records`、`exemption_result_snapshots` 等对象。 +- KOC/KOL任务、长期测评人免评补单、IM/EDM/APP协同触达可以服务同一个免评计划,但必须分别记录任务来源和结果口径。 +- 免评Code、点击、订单、内容链接、带货归因和权重结果需要单独复盘,不混入测评/回评订单完成数。 + +### 阶段 + +6A V1必做;6B 的免评补单与协同入口 V1必做,完整内容/归因/佣金闭环 V2实现。 + +## 分支七:财务返款 + +### 用户返款 + +```text +返款条件满足 +-> 发起请款 +-> 财务/审核确认 +-> 付款或礼品卡 +-> 凭证/卡密通知用户 +-> 返款状态回写订单和工单 +``` + +### KOC/KOL佣金 + +```text +带货订单归因 +-> 计算佣金 +-> 审核 +-> 结算 +-> 争议处理 +-> 复盘表现 +``` + +### 阶段 + +用户返款 V1必做;KOC/KOL佣金 V1预留,V2实现。 + +## 分支八:KOC/KOL协作 + +### 入口 + +- 新增KOC/KOL线索。 +- KOC每日工作流发现可合作用户。 +- 免评计划需要长期测评人或达人补单。 +- 商家/产品需要内容或带货合作。 + +### 流程 + +```text +线索进入 +-> 账号/标签/风险状态检查 +-> 自动打标与人工修正 +-> 分层:新手KOC/稳定测评者/垂直专家/带货型KOL/品牌合作型 +-> 匹配产品/样品/免评/内容/带货任务 +-> 达人接受或拒绝 +-> 样品/订单/内容执行 +-> 上传内容链接或带货链接 +-> 审核、发布、归因 +-> 佣金/返款 +-> 风险与复盘 +``` + +### 必须区分 + +- KOC/KOL身份和普通测评人身份可能重叠,但业务对象不同。 +- KOC/KOL标签、带货、测评、免评标签不能混。 +- KOC/KOL内容任务和普通回评/测评订单不能混用完成口径。 + +### 阶段 + +需求完整性必须覆盖;V1预留核心入口与字段,免评补单相关 V1必做,完整内容/佣金 V2实现。 + +## 分支九:风险与黑名单 + +### 流程 + +```text +风险信号产生 +-> 判断弱风险/强风险 +-> 弱风险提醒人工确认 +-> 强风险阻断执行 +-> 人工复核 +-> 解除/确认风险 +-> 同步黑名单或恢复执行 +``` + +### 风险来源 + +- 黑名单命中。 +- 邮箱/电话/设备/IP/Profile/地址强弱关联。 +- 退款/取消订单。 +- 双重退款。 +- 多账号/多Profile异常。 +- 重复订单、重复评论、重复返款。 +- 差评/掉评异常。 +- KOC/KOL内容违规、佣金争议、带货归因异常。 + +### 阶段 + +V1必做。 + +## Gate 1 - 分支流程完成条件 + +- 所有核心分支都有触发、执行、写入、关闭条件。 +- 客服与KOC/KOL均作为完整需求域出现。 +- 分支结果能回流主流程和复盘。 +- 各分支有 V1必做、V1预留、V2实现边界。 diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md new file mode 100644 index 0000000..82d5a48 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md @@ -0,0 +1,138 @@ +# USER ERP 0-1需求重构 - 05 异常流程_完整需求域 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_05_异常流程_完整需求域_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:业务负责人 / 风险负责人 / 客服主管 +- 核心参与:USER运营、渠道运营、客服、KOC/KOL运营、财务、风险、测试 +- 文件目的:定义完整需求域中的异常、拦截、提醒、升级、人工复核和恢复机制,为 Stage 2 测试用例和状态机做输入。 + +## 异常分级 + +| 等级 | 含义 | 处理要求 | +| --- | --- | --- | +| P0 | 直接影响今日核心目标、资金、合规或账号安全 | 当天处理,主管可见 | +| P1 | 影响本周目标或大量用户体验 | 24小时内处理 | +| P2 | 局部异常,可进入跟进列表 | 按责任人处理 | +| P3 | 观察项,暂不阻断 | 持续监控 | + +## 01 需求与执行计划不匹配 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| 需求目标高于可用人群/额度 | 计划生成时匹配不足 | 提醒运营降低目标、拆分周期、转免评或补充渠道 | 目标调整或资源补足 | V1必做 | +| 产品禁用但计划仍在执行 | 产品状态变化 | 自动暂停计划和下架H5/卡片,通知负责人 | 产品恢复或计划关闭 | V1必做 | +| 库存不足但继续推测评/免评 | 库存低于安全线 | 限制节奏或暂停,通知运营 | 库存恢复或调整计划 | V1必做 | +| ASIN/店铺/关键词变化未同步 | ASIN信息与计划不一致 | 阻断新推送,生成H5/卡片维护任务 | 链接和卡片确认有效 | V1必做 | +| 计划有名额但渠道无可推人群 | 人群生成结果为空或不足 | 提醒补充人群、换渠道、延长周期 | 匹配成功或关闭计划 | V1必做 | +| 渠道有响应但客服容量不足 | 工单/回复超时、在线人数不足 | 暂停或降频推送,调整排班 | 客服容量恢复 | V1必做 | +| 需求变更后旧计划未暂停 | 同需求存在多个冲突计划 | 提醒主管确认,自动标记冲突 | 旧计划暂停/合并/关闭 | V1预留 | +| 完成数口径不一致 | 计划完成、订单完成、评价展示不一致 | 进入口径复核 | 口径修正并记录 | V1必做 | + +## 02 评价/订单异常 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| 订单号格式错误 | 用户提交时校验 | 提示用户重填,必要时转客服 | 正确订单号提交 | V1必做 | +| 非公司产品 | 订单核验 | 阻断登记,转客服说明 | 工单关闭或人工改判 | V1必做 | +| 店铺下错 | 订单店铺与计划不一致 | 转人工处理,可转免评 | 订单状态修正 | V1必做 | +| 订单已取消 | Amazon状态 | 阻断回评和返款 | 用户换单或关闭 | V1必做 | +| 订单已退款 | Amazon退款命中 | 进入退款比对和风险 | 风险结论完成 | V1必做 | +| 评论重复绑定 | 评论ID/链接已绑定 | 阻断或申请例外审核 | 绑定唯一或审核通过 | V1必做 | +| 订单重复绑定 | 订单号已绑定其他单 | 阻断,进入人工复核 | 确认归属 | V1必做 | +| 用户只提交部分信息 | 缺返款账号/截图/链接 | 自动打标,生成客服跟进 | 信息补齐或关闭 | V1必做 | +| 评论未展示 | Amazon展示核验失败 | 标记未展示,计划不计完成 | 后续展示或关闭 | V1必做 | +| 掉评/差评 | Review状态变化 | 触发回评/紧急催评/风险 | 计划调整或风险关闭 | V1必做 | + +## 03 返款/佣金异常 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| 缺返款账号 | 用户提交不完整 | 自动打标待返款,客服跟进 | 账号补齐 | V1必做 | +| 返款账号格式异常 | 表单校验/人工审核 | 退回补充 | 格式正确 | V1必做 | +| 重复请款 | 同订单/同返款ID重复 | 阻断并提醒财务 | 去重完成 | V1必做 | +| 双重退款 | Amazon退款+OA返款命中 | 生成风险事件 | 风险确认或解除 | V1必做 | +| 非APP用户邮件退款 | 邮件退款链路缺设备/注册邮箱维度 | 标记风险盲区,补充邮箱、订单、地址、收款信息等识别线索 | 识别维度补齐或人工复核完成 | V1必做 | +| 返款超额 | 金额超过规则 | 进入超额审核 | 审核通过/拒绝 | V1必做 | +| 卡密/凭证缺失 | 返款成功但无凭证 | 生成财务待补任务 | 凭证补齐 | V1预留 | +| KOC/KOL佣金争议 | 达人反馈或金额不一致 | 进入佣金复核 | 结算修正或驳回 | V2实现 | + +## 04 客服异常 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| 无客服在线 | 在线人数为0但有待处理工单 | 提醒主管,暂停部分推送 | 有客服承接 | V1必做 | +| 工单未分配 | 待分配超时 | 自动分配或主管手动分配 | 工单有处理人 | V1必做 | +| 首次回复超时 | 首次回复时长超过阈值 | 提醒客服/主管 | 已回复或转移 | V1必做 | +| 等待用户超时 | 用户长时间未回复 | 自动提醒或关闭 | 用户回复/关闭 | V1必做 | +| 客服转移失败 | 目标客服离线或超负荷 | 重新选择处理人 | 转移成功 | V1必做 | +| 排班不足 | 高峰工单大于客服容量 | 主管调整排班或降频推送 | 容量恢复 | V1必做 | +| 目标异常 | 月目标明显低于进度 | 提醒主管调度 | 目标追平或调整 | V1必做 | +| 客服误登记订单 | 人工发现或订单核验失败 | 更改订单并留痕 | 订单修正 | V1必做 | +| 用户投诉客服 | 投诉/负反馈 | 升级主管复核 | 处理结论 | V1预留 | + +## 05 渠道异常 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| IM点击低 | 漏斗低于阈值 | 换素材/卡片/人群 | 指标恢复或复盘 | V1必做 | +| IM回复低于3% | 日常监控 | 更换图片/话术 | 回复率恢复 | V1必做 | +| 退订或不感兴趣升高 | 退订/投诉异常 | 降频、暂停素材、调整人群 | 指标恢复 | V1必做 | +| EDM域名/IP信誉异常 | 每日检查 | 暂停发送、切换账号、通知负责人 | 信誉恢复 | V1必做 | +| KV/UID人群导出失败 | 人群数量异常 | 暂停推送,重跑导出 | 人群正常 | V1必做 | +| H5链接失效 | 链接检测或用户反馈 | 下架任务,修复链接 | 链接有效 | V1必做 | +| 卡片产品禁用未同步 | 产品状态变更 | 自动下架卡片 | 卡片状态正确 | V1必做 | +| Phone未接率过高 | 电话数据 | 调整时段/排班/回拨 | 未接率下降 | V1预留 | + +## 06 KOC/KOL异常 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| KOC标签混乱 | 同人存在KOC/测评/带货冲突 | 人工复核标签 | 标签修正 | V1预留 | +| KOC/KOL与C类测评人身份重叠 | 同一真实人同时命中免评推送和KOC任务 | 阻断重复触达,进入身份/任务冲突复核 | 明确走免评协同或KOC任务,不重复计完成 | V1必做 | +| 达人重复触达 | 同一KOC/KOL多任务冲突 | 合并或暂停任务 | 冲突解除 | V1预留 | +| 样品发出未产出内容 | 任务超时 | 催办、暂停合作、风险记录 | 内容提交或任务关闭 | V2实现 | +| 内容链接无效 | 链接检测失败 | 要求重提 | 链接有效 | V2实现 | +| 内容不合规 | 内容审核 | 驳回、下架、降权 | 内容修正或关闭 | V2实现 | +| 带货订单归因失败 | Code/链接无法匹配 | 进入人工归因 | 归因完成或驳回 | V2实现 | +| 佣金争议 | 金额或订单争议 | 财务/运营复核 | 结算修正或驳回 | V2实现 | +| 高风险KOC继续推送 | 风险状态命中 | 阻断任务 | 风险解除或拉黑 | V1预留 | + +## 07 风险异常 + +| 异常 | 自动发现 | 处理 | 关闭条件 | 阶段 | +| --- | --- | --- | --- | --- | +| 黑名单命中 | 身份线索命中 | 阻断触达/返款/合作 | 人工解除或保持黑名单 | V1必做 | +| 强关联命中 | 邮箱/电话/设备/Profile/地址 | 阻断并复核 | 风险结论 | V1必做 | +| 弱关联命中 | IP/相似地址等 | 提醒人工确认 | 确认/排除 | V1必做 | +| 额度超限 | 月度/累计额度命中 | 阻断普通测评,转免评池 | 额度恢复或转免评 | V1必做 | +| 多账号归并冲突 | 同一真实人下多个 JOYHUB/Profile 额度不一致 | 按 `person_quota_ledgers` 重新汇总,进入人工复核 | 额度统一并记录合并/拆分结论 | V1必做 | +| 频控超限 | 当日/周期触达超限 | 暂停触达 | 到期恢复 | V1必做 | +| 敏感字段导出风险 | 导出包含敏感字段 | 强制脱敏/审批 | 导出完成或拒绝 | V1必做 | + +## 异常处理通用状态 + +```text +待发现 -> 已发现 -> 待处理 -> 处理中 -> 待复核 -> 已关闭 +``` + +可选状态: + +- 已升级。 +- 已驳回。 +- 已阻断。 +- 已恢复。 +- 需补充信息。 +- 需跨部门协同。 + +## Gate 1 - 异常流程完成条件 + +- 需求计划、评价订单、客服、渠道、KOC/KOL、返款、风险异常均覆盖。 +- 每类异常有发现方式、处理动作、关闭条件。 +- P0/P1/P2/P3等级可用于今日作战台。 +- 异常能回流计划调整、客服任务、风险事件和复盘记录。 diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1).md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1).md new file mode 100644 index 0000000..fdb7ff2 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1(1).md @@ -0,0 +1,209 @@ +# USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:产品负责人 / 前端观察员 +- 核心参与:业务负责人、USER运营、客服主管、渠道负责人、KOC/KOL负责人、技术负责人 +- 文件目的:把现有 Vibe Coding / AI 原型作为需求验证材料,记录已覆盖能力、暴露缺口和 Stage 2 高保真模型输入。 + +## 验证原则 + +1. 原型不是生产代码,不作为正式系统底座。 +2. 原型用于发现页面、字段、按钮、状态和流程缺口。 +3. 验证重点不是“页面能不能打开”,而是“需求是否完整、业务对象是否统一、每日操作是否可执行”。 +4. 阶段1必须记录原型未覆盖的需求域,特别是 KOC/KOL、客服管理、需求计划匹配和复盘口径。 + +## 验证材料 + +| 材料 | 用途 | 结论 | +| --- | --- | --- | +| `C:\XCODE\USER\input\user-review-system` | React/Vite原型,含计划、测评人、订单、客服、渠道、风险、看板;入口参考 `src\App.tsx`、`src\config\routes.tsx`、`src\config\menu.tsx` | 覆盖面较广,可作为页面/字段参考 | +| `C:\XCODE\USER\input\user-review-system\docs\review-order-PRD.md` | 回评/测评订单详细PRD | 订单页面细,但只是局部,不足以代表ERP主流程 | +| `C:\XCODE\USER\input\user-review-system\docs\review-order-architecture.md` | 订单页面技术架构 | 可参考组件拆分和待确认问题 | +| `C:\XCODE\USER\src\user_erp_mvp_admin_prototype_v10.html` | 既有 v10 HTML 原型 | 可参考早期管理端页面,但不直接复用为新系统底座 | +| `C:\XCODE\USER\input\用户运营系统-单文件.html` | 大型单文件运营系统原型 | 可参考全局菜单和字段,但不直接复用 | +| `C:\XCODE\USER\input\amazon_operator_test_entry.html` | 亚马逊提评入口原型 | 可参考需求提报、IM账号、推送策略 | +| `C:\XCODE\USER\input\客服执行.html` | 客服执行/管理原型 | 客服核心需求的重要参考 | +| `C:\XCODE\USER\input\IM 推送业务流.mm` | IM业务流脑图 | IM分层和流转最完整 | + +## user-review-system 验证 + +### 已覆盖能力 + +| 模块 | 已覆盖 | +| --- | --- | +| 菜单/路由 | 工作台、评价计划、评价人管理、测评订单、回评订单、客服中心、渠道推送、风险中心、数据看板 | +| 计划 | 测评计划、回评计划、免评计划,含列表、表单、详情、关联推送/订单 | +| 测评人 | 测评人列表、详情、表单、额度、订单、风险、联系记录 | +| 订单 | 测评订单、回评订单、上传订单、上传回评、请款、返款、详情抽屉 | +| 客服 | 工单池、聊天、聊天记录、答应配合、客服绩效看板 | +| 渠道 | 推送任务、IM推送、IM卡片、IM/EDM配置 | +| 风险 | 风险事件、黑名单、退款比对 | +| 看板 | 计划、ASIN、客服绩效 | + +### 暴露的缺口 + +| 缺口 | 影响 | 后续处理 | +| --- | --- | --- | +| 需求池与计划生成链路弱 | 无法解释需求如何变成计划 | Stage 2 需新增需求与计划调度中心 | +| 计划匹配资源视图不足 | 人群、客服、渠道、H5/卡片、风险没有统一调度 | 需新增执行匹配看板 | +| 客服管理不完整 | 排班、出勤、目标、首次回复、转化绩效不足 | 结合客服执行原型补齐 | +| KOC/KOL需求域不足 | 线索、任务、内容、带货、佣金缺失 | 阶段1必须补出完整需求 | +| 订单PRD局部过细 | 容易用订单页面替代ERP主流程 | 保留为订单模块参考 | +| 状态枚举不统一 | 测评、回评、返款、计划、客服状态口径混乱 | Stage 2需统一状态机 | +| Mock 数据含临时品类 | 部分示例与真实业务不一致 | 只取字段结构,不取业务事实 | + +## 用户运营系统单文件验证 + +### 已覆盖 + +- USER评价业务闭环系统的全局菜单雏形。 +- 产品、计划、订单、客服、风险、真实人、用户信息等多类字段。 +- 较多运营筛选、状态、详情、操作入口。 + +### 缺口 + +- 文件过大,页面和数据逻辑混杂,不适合做正式底座。 +- 未清晰区分需求完整范围和V1实现范围。 +- KOC/KOL相关需求不完整。 +- 客服核心执行与管理需要与 `客服执行.html` 合并分析。 +- 需求到计划再到执行匹配的调度视图不足。 + +## amazon_operator_test_entry 验证 + +### 已覆盖 + +- 亚马逊提评入口。 +- 提评单列表。 +- IM推送。 +- IM账号管理。 +- 新建提评单。 +- 推送策略。 +- 卡片内容管理。 +- 业务类型:测评、免评、回评。 + +### 可纳入需求 + +- 提评需求提报字段:站点、ASIN、商品、关键词、关键词链接、品牌、新品状态、要求数量、店铺、原因、备注。 +- IM账号和身份管理:官方客服、品牌客服、达人、内部/外部品牌。 +- 推送策略:用户标签、去除标签、优先级、间隔时间、内容形式、跳转链接。 + +### 缺口 + +- 未打通完整需求池和计划对象。 +- 提评入口与USER运营调度中心的关系需重构。 +- IM账号管理需与渠道配置、权限和风险联动。 + +## 客服执行原型验证 + +### 已覆盖 + +- 客服执行看板。 +- 首页Dashboard。 +- 出勤管理。 +- 排班管理。 +- 工单管理。 +- 回复统计。 +- 转化绩效。 +- 目标管理。 +- 角色权限:客服与主管/管理员视角。 + +### 可纳入需求 + +| 能力 | 说明 | +| --- | --- | +| 工单自动分配 | 在线优先、按工单量平均、最大工单数 | +| 客服视角 | 只看自己工单、绩效和目标 | +| 主管视角 | 查看团队数据、调整工单、排班和目标 | +| 绩效统计 | RSO/RDO登记订单、上评、完成率 | +| 出勤/排班 | 早班、午班、晚班、迟到、早退、请假、缺勤 | + +### 缺口 + +- 与评价计划、渠道推送和订单履约的联动需要补强。 +- 客服容量尚未进入需求与计划匹配。 +- 客服误登记、投诉、转移失败、排班不足等异常需要补全。 + +## IM推送业务流验证 + +### 已覆盖 + +- 用户分层:未参与、参与过、长期测评人。 +- 推送前风控校验。 +- 回评卡片、测评卡片、免评产品、催评消息。 +- 用户提交订单号、返款账号、评论截图/链接。 +- 订单号核实和客服补全。 +- 财务返款提醒。 +- 自动打标体系。 +- 店铺紧急催评。 + +### 可直接作为Stage 2输入 + +- IM用户分层。 +- 核心标签体系。 +- 每次互动复检。 +- 未完成流程的标签与自动通知。 +- 长期测评人转免评策略。 + +### 缺口 + +- IM流程需要与统一计划、额度台账、客服工单和风险事件对象对齐。 +- 文档中提到的“当天测评计划需要刷的名额”需要接入计划匹配。 + +## KOC/KOL需求验证 + +### 已有线索 + +- `KOC 每日工作流.md` 提到账号、标签、风险、新增线索、用户分层、自动打标、任务去重、回复、订单、返款、Review、带货、风险。 +- `商家KOL商业服务系统-USER项目知识迁移清单.md` 提供服务包、KOC/KOL分层、佣金原则、内容分级和推荐分发接口意识。 +- 现有原型仅零散覆盖KOC/KOL,未形成完整中心。 + +### 阶段1必须补出的需求 + +- KOC/KOL线索池。 +- KOC/KOL档案和分层。 +- KOC/KOL与测评人/真实人的身份关系。 +- 内容任务、样品/免评、带货任务。 +- 内容审核、链接、Code、订单归因。 +- 佣金计算与结算接口。 +- KOC/KOL风险和异常。 + +### 阶段建议 + +- V1必做:免评补单相关的KOC/KOL入口、标签、风险和任务关联。 +- V1预留:KOC/KOL档案、线索池、合作状态。 +- V2实现:内容审核、带货归因、佣金结算、商家服务包完整后台。 + +## 进入Stage 2的输入清单 + +| 输入 | 说明 | +| --- | --- | +| 需求与计划调度中心 | 需要新设计,不应从现有订单页面改 | +| 今日作战台 | 需要按OKR和岗位每日工作方式设计 | +| 客服执行/管理 | 可综合 `客服相关模块.md` 和 `客服执行.html` | +| IM推送 | 可基于 `IM 推送业务流.mm` 和 user-review-system IM页面 | +| 订单页面 | 可参考 review-order PRD,但需合并测评/回评/免评口径 | +| 测评人/真实人 | 可参考 `测评信息字段管理.xlsx` 和数据对象v3 | +| KOC/KOL中心 | 现有原型不足,需要新建需求模型 | +| 风险中心 | 可参考现有风险页面,但需扩展KOC/KOL与计划匹配异常 | + +## 不能直接继承的内容 + +- 不能直接继承 Vibe Coding 的文件结构作为正式工程结构。 +- 不能把 mock 数据当业务事实。 +- 不能把回评/测评订单PRD当成整个 ERP PRD。 +- 不能因为 V1不实现完整KOC/KOL平台,就在阶段1删除KOC/KOL需求。 +- 不能把客服降级为“工单页面”,客服是核心执行和转化资源。 + +## Gate 1 - Vibe Coding验证完成条件 + +- 已记录所有原型的可用需求点。 +- 已记录原型不能支撑的业务缺口。 +- 已确认正式系统需要重新做 Stage 2 高保真模型。 +- 已确认KOC/KOL、客服、需求计划匹配是现有原型的主要补强方向。 diff --git a/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md new file mode 100644 index 0000000..fdb7ff2 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md @@ -0,0 +1,209 @@ +# USER ERP 0-1需求重构 - 06 VibeCoding页面验证记录 v1 + +## 文件信息 + +- 文件名称:`20260527_USER_ERP_0-1需求重构_06_VibeCoding页面验证记录_v1.md` +- 项目路径:`C:\XCODE\USER` +- 输出位置:`C:\XCODE\USER\output\docs` +- 当前版本:`v1` +- 最近更新:`2026-05-27` +- 所属阶段:Stage 1 完整业务需求 +- 负责人:产品负责人 / 前端观察员 +- 核心参与:业务负责人、USER运营、客服主管、渠道负责人、KOC/KOL负责人、技术负责人 +- 文件目的:把现有 Vibe Coding / AI 原型作为需求验证材料,记录已覆盖能力、暴露缺口和 Stage 2 高保真模型输入。 + +## 验证原则 + +1. 原型不是生产代码,不作为正式系统底座。 +2. 原型用于发现页面、字段、按钮、状态和流程缺口。 +3. 验证重点不是“页面能不能打开”,而是“需求是否完整、业务对象是否统一、每日操作是否可执行”。 +4. 阶段1必须记录原型未覆盖的需求域,特别是 KOC/KOL、客服管理、需求计划匹配和复盘口径。 + +## 验证材料 + +| 材料 | 用途 | 结论 | +| --- | --- | --- | +| `C:\XCODE\USER\input\user-review-system` | React/Vite原型,含计划、测评人、订单、客服、渠道、风险、看板;入口参考 `src\App.tsx`、`src\config\routes.tsx`、`src\config\menu.tsx` | 覆盖面较广,可作为页面/字段参考 | +| `C:\XCODE\USER\input\user-review-system\docs\review-order-PRD.md` | 回评/测评订单详细PRD | 订单页面细,但只是局部,不足以代表ERP主流程 | +| `C:\XCODE\USER\input\user-review-system\docs\review-order-architecture.md` | 订单页面技术架构 | 可参考组件拆分和待确认问题 | +| `C:\XCODE\USER\src\user_erp_mvp_admin_prototype_v10.html` | 既有 v10 HTML 原型 | 可参考早期管理端页面,但不直接复用为新系统底座 | +| `C:\XCODE\USER\input\用户运营系统-单文件.html` | 大型单文件运营系统原型 | 可参考全局菜单和字段,但不直接复用 | +| `C:\XCODE\USER\input\amazon_operator_test_entry.html` | 亚马逊提评入口原型 | 可参考需求提报、IM账号、推送策略 | +| `C:\XCODE\USER\input\客服执行.html` | 客服执行/管理原型 | 客服核心需求的重要参考 | +| `C:\XCODE\USER\input\IM 推送业务流.mm` | IM业务流脑图 | IM分层和流转最完整 | + +## user-review-system 验证 + +### 已覆盖能力 + +| 模块 | 已覆盖 | +| --- | --- | +| 菜单/路由 | 工作台、评价计划、评价人管理、测评订单、回评订单、客服中心、渠道推送、风险中心、数据看板 | +| 计划 | 测评计划、回评计划、免评计划,含列表、表单、详情、关联推送/订单 | +| 测评人 | 测评人列表、详情、表单、额度、订单、风险、联系记录 | +| 订单 | 测评订单、回评订单、上传订单、上传回评、请款、返款、详情抽屉 | +| 客服 | 工单池、聊天、聊天记录、答应配合、客服绩效看板 | +| 渠道 | 推送任务、IM推送、IM卡片、IM/EDM配置 | +| 风险 | 风险事件、黑名单、退款比对 | +| 看板 | 计划、ASIN、客服绩效 | + +### 暴露的缺口 + +| 缺口 | 影响 | 后续处理 | +| --- | --- | --- | +| 需求池与计划生成链路弱 | 无法解释需求如何变成计划 | Stage 2 需新增需求与计划调度中心 | +| 计划匹配资源视图不足 | 人群、客服、渠道、H5/卡片、风险没有统一调度 | 需新增执行匹配看板 | +| 客服管理不完整 | 排班、出勤、目标、首次回复、转化绩效不足 | 结合客服执行原型补齐 | +| KOC/KOL需求域不足 | 线索、任务、内容、带货、佣金缺失 | 阶段1必须补出完整需求 | +| 订单PRD局部过细 | 容易用订单页面替代ERP主流程 | 保留为订单模块参考 | +| 状态枚举不统一 | 测评、回评、返款、计划、客服状态口径混乱 | Stage 2需统一状态机 | +| Mock 数据含临时品类 | 部分示例与真实业务不一致 | 只取字段结构,不取业务事实 | + +## 用户运营系统单文件验证 + +### 已覆盖 + +- USER评价业务闭环系统的全局菜单雏形。 +- 产品、计划、订单、客服、风险、真实人、用户信息等多类字段。 +- 较多运营筛选、状态、详情、操作入口。 + +### 缺口 + +- 文件过大,页面和数据逻辑混杂,不适合做正式底座。 +- 未清晰区分需求完整范围和V1实现范围。 +- KOC/KOL相关需求不完整。 +- 客服核心执行与管理需要与 `客服执行.html` 合并分析。 +- 需求到计划再到执行匹配的调度视图不足。 + +## amazon_operator_test_entry 验证 + +### 已覆盖 + +- 亚马逊提评入口。 +- 提评单列表。 +- IM推送。 +- IM账号管理。 +- 新建提评单。 +- 推送策略。 +- 卡片内容管理。 +- 业务类型:测评、免评、回评。 + +### 可纳入需求 + +- 提评需求提报字段:站点、ASIN、商品、关键词、关键词链接、品牌、新品状态、要求数量、店铺、原因、备注。 +- IM账号和身份管理:官方客服、品牌客服、达人、内部/外部品牌。 +- 推送策略:用户标签、去除标签、优先级、间隔时间、内容形式、跳转链接。 + +### 缺口 + +- 未打通完整需求池和计划对象。 +- 提评入口与USER运营调度中心的关系需重构。 +- IM账号管理需与渠道配置、权限和风险联动。 + +## 客服执行原型验证 + +### 已覆盖 + +- 客服执行看板。 +- 首页Dashboard。 +- 出勤管理。 +- 排班管理。 +- 工单管理。 +- 回复统计。 +- 转化绩效。 +- 目标管理。 +- 角色权限:客服与主管/管理员视角。 + +### 可纳入需求 + +| 能力 | 说明 | +| --- | --- | +| 工单自动分配 | 在线优先、按工单量平均、最大工单数 | +| 客服视角 | 只看自己工单、绩效和目标 | +| 主管视角 | 查看团队数据、调整工单、排班和目标 | +| 绩效统计 | RSO/RDO登记订单、上评、完成率 | +| 出勤/排班 | 早班、午班、晚班、迟到、早退、请假、缺勤 | + +### 缺口 + +- 与评价计划、渠道推送和订单履约的联动需要补强。 +- 客服容量尚未进入需求与计划匹配。 +- 客服误登记、投诉、转移失败、排班不足等异常需要补全。 + +## IM推送业务流验证 + +### 已覆盖 + +- 用户分层:未参与、参与过、长期测评人。 +- 推送前风控校验。 +- 回评卡片、测评卡片、免评产品、催评消息。 +- 用户提交订单号、返款账号、评论截图/链接。 +- 订单号核实和客服补全。 +- 财务返款提醒。 +- 自动打标体系。 +- 店铺紧急催评。 + +### 可直接作为Stage 2输入 + +- IM用户分层。 +- 核心标签体系。 +- 每次互动复检。 +- 未完成流程的标签与自动通知。 +- 长期测评人转免评策略。 + +### 缺口 + +- IM流程需要与统一计划、额度台账、客服工单和风险事件对象对齐。 +- 文档中提到的“当天测评计划需要刷的名额”需要接入计划匹配。 + +## KOC/KOL需求验证 + +### 已有线索 + +- `KOC 每日工作流.md` 提到账号、标签、风险、新增线索、用户分层、自动打标、任务去重、回复、订单、返款、Review、带货、风险。 +- `商家KOL商业服务系统-USER项目知识迁移清单.md` 提供服务包、KOC/KOL分层、佣金原则、内容分级和推荐分发接口意识。 +- 现有原型仅零散覆盖KOC/KOL,未形成完整中心。 + +### 阶段1必须补出的需求 + +- KOC/KOL线索池。 +- KOC/KOL档案和分层。 +- KOC/KOL与测评人/真实人的身份关系。 +- 内容任务、样品/免评、带货任务。 +- 内容审核、链接、Code、订单归因。 +- 佣金计算与结算接口。 +- KOC/KOL风险和异常。 + +### 阶段建议 + +- V1必做:免评补单相关的KOC/KOL入口、标签、风险和任务关联。 +- V1预留:KOC/KOL档案、线索池、合作状态。 +- V2实现:内容审核、带货归因、佣金结算、商家服务包完整后台。 + +## 进入Stage 2的输入清单 + +| 输入 | 说明 | +| --- | --- | +| 需求与计划调度中心 | 需要新设计,不应从现有订单页面改 | +| 今日作战台 | 需要按OKR和岗位每日工作方式设计 | +| 客服执行/管理 | 可综合 `客服相关模块.md` 和 `客服执行.html` | +| IM推送 | 可基于 `IM 推送业务流.mm` 和 user-review-system IM页面 | +| 订单页面 | 可参考 review-order PRD,但需合并测评/回评/免评口径 | +| 测评人/真实人 | 可参考 `测评信息字段管理.xlsx` 和数据对象v3 | +| KOC/KOL中心 | 现有原型不足,需要新建需求模型 | +| 风险中心 | 可参考现有风险页面,但需扩展KOC/KOL与计划匹配异常 | + +## 不能直接继承的内容 + +- 不能直接继承 Vibe Coding 的文件结构作为正式工程结构。 +- 不能把 mock 数据当业务事实。 +- 不能把回评/测评订单PRD当成整个 ERP PRD。 +- 不能因为 V1不实现完整KOC/KOL平台,就在阶段1删除KOC/KOL需求。 +- 不能把客服降级为“工单页面”,客服是核心执行和转化资源。 + +## Gate 1 - Vibe Coding验证完成条件 + +- 已记录所有原型的可用需求点。 +- 已记录原型不能支撑的业务缺口。 +- 已确认正式系统需要重新做 Stage 2 高保真模型。 +- 已确认KOC/KOL、客服、需求计划匹配是现有原型的主要补强方向。 diff --git a/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1.html b/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1.html new file mode 100644 index 0000000..47a7722 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_0-1需求重构_可点击参考原型_v1.html @@ -0,0 +1,1568 @@ + + + + + + USER ERP 0-1需求重构 · 可点击参考原型 v1 + + + +
+ + +
+
+
+ 今日作战台 + 高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险 +
+
+ +
+ + + +
+ + + +
+
+
+
+
+ +
+ +
+ + + + diff --git a/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_完整参考原型_v2.html b/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_完整参考原型_v2.html new file mode 100644 index 0000000..490394b --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_完整参考原型_v2.html @@ -0,0 +1,1107 @@ + + + + + +USER ERP 完整参考原型 v2 · 2026-05-28 + + + +
+ + + + +
+
+
今日作战台高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险
+
+ +
+ + + +
+
+
+
+
+ + +
+ +
+ + +
+ ⚠ 本原型所有数据均为模拟数据,仅用于 Stage 1 需求验证 · v2 +
+ + \ No newline at end of file diff --git a/wishfulfilled-wiki/05_需求文档/通用EDM业务流程说明.md b/wishfulfilled-wiki/05_需求文档/通用EDM业务流程说明.md new file mode 100644 index 0000000..ea572f8 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/通用EDM业务流程说明.md @@ -0,0 +1,398 @@ +# 通用 EDM 业务流程说明 + +更新时间:2026-05-26 + +## 1. 文档目标 + +本文用于新的 EDM 子系统设计或重构,目标是在功能保持一致的前提下,将现有 EDM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、接入邮件发送通道和处理邮件客服工单。 + +## 2. 业务范围 + +通用 EDM 子系统建议分为三条业务线: + +| 业务线 | 说明 | +| --- | --- | +| 批量营销邮件 | 管理后台创建邮件任务,按标签、站点、产品、用户状态筛选目标用户,生成待发送邮件记录,通过队列异步发送 | +| 自动 / 实时策略邮件 | 根据用户注册、访问、在线、站点、产品、行为、无消息等规则自动筛选用户,并生成策略邮件 | +| 邮件工单 | 用户来信、表单提交或外部收信服务进入后台后,生成或更新邮件工单,由客服处理、回复、转发、关闭 | + +如果新系统还需要普通邮箱功能,可以作为独立模块处理。普通邮箱收发不一定进入 EDM 工单链路,是否合并需要单独确认。 + +## 3. 总体架构 + +```mermaid +flowchart TD + A["管理后台"] --> B["EDM 任务服务"] + B --> C["目标用户筛选服务"] + C --> D["邮件记录生成服务"] + D --> E["Kafka / 队列"] + E --> F["邮件发送消费者"] + F --> G["外部发送通道"] + G --> H["AWS SES / SMTP / Gmail / Microsoft"] + H --> I["事件回调"] + I --> J["事件处理服务"] + J --> K["统计与黑名单"] + + L["用户来信 / 表单提交"] --> M["入站接收服务"] + M --> N["Kafka / 入站队列"] + N --> O["EDM 工单服务"] + O --> P["客服工作台"] + P --> E +``` + +核心组件职责: + +| 组件 | 职责 | +| --- | --- | +| 管理后台 | 创建邮件任务、审核任务、查看统计、处理邮件工单 | +| 任务服务 | 保存任务配置、正文、模板、发送时间、审核状态 | +| 用户筛选服务 | 根据标签、站点、产品、黑名单、订阅状态、发送频率等规则筛选目标用户 | +| 邮件记录服务 | 按用户生成单封待发送邮件记录和正文快照 | +| Kafka / 队列 | 解耦任务生成、邮件发送、入站消息、事件统计 | +| 发送消费者 | 消费待发送邮件,调用外部发送通道,并保存发送结果 | +| 入站接收服务 | 接收表单、用户来信或外部邮件服务回调,写入入站队列 | +| 工单服务 | 根据来信生成或更新邮件工单,维护状态、负责人、未读数和处理记录 | +| 事件处理服务 | 处理送达、打开、点击、退信、投诉、拒信等邮件事件 | +| Redis / 缓存 | 保存并发锁、游标、限流计数、近期任务统计、临时筛选集合 | + +## 4. 核心数据模型 + +新子系统建议至少抽象以下对象: + +| 对象 | 说明 | +| --- | --- | +| 邮件任务 EmailTask | 批量营销或策略邮件任务,保存任务名称、类型、发送时间、审核状态、目标条件 | +| 邮件内容 EmailContent | 任务级正文、标题、模板、发件人、回复地址、附件配置 | +| 目标用户 TaskRecipient | 任务命中的用户关系,便于统计和去重 | +| 单封邮件 EmailMessage | 最终发送或接收的一封邮件记录,包含方向、收件人、发件人、状态、message_id、工单 ID | +| 邮件正文 EmailBody | 单封邮件正文快照,避免模板后续变化影响历史邮件 | +| 工单 EmailTicket | 用户来信或客服主动发起的一次处理过程 | +| 分配记录 Assignment | 工单分配、移交、释放、代班等操作记录 | +| 节点日志 NodeLog | 创建、分配、首次回复、关闭、未解决、转化中等关键节点 | +| 发送事件 EmailEvent | 送达、打开、点击、退信、投诉、拒信、渲染失败等事件 | +| 黑名单 / 退订名单 Suppression | 退信、投诉、退订、风险用户等不可发送或需谨慎发送的人群 | + +## 5. 批量营销邮件流程 + +```mermaid +flowchart TD + A["后台创建邮件任务"] --> B["校验任务配置"] + B --> C["写入任务、内容、标签条件"] + C --> D["进入待审核"] + D --> E{"审核结果"} + E -->|通过| F["进入待执行"] + E -->|驳回| G["记录驳回原因并结束"] + F --> H["到达发送时间"] + H --> I["筛选目标用户"] + I --> J["生成单封待发送邮件记录"] + J --> K["投递 Kafka"] + K --> L["发送消费者调用外部邮件通道"] + L --> M["更新发送状态和 message_id"] +``` + +创建任务时建议校验: + +1. 任务名称不能重复。 +2. 邮件模板或正文必须存在。 +3. 发件邮箱必须存在并可用。 +4. 发件域名必须在允许范围内。 +5. 必须选择目标人群或策略条件。 +6. 发送时间必须符合业务规则。 +7. 如果绑定活动,发送时间需要满足活动时间约束。 +8. 目标人数需要预估,避免误发全量用户。 + +目标用户筛选建议包含: + +1. 标签包含和标签排除。 +2. 站点、产品、品牌、语言、地区。 +3. 订阅状态、退订状态、黑名单、投诉用户、永久退信用户。 +4. 近期发送频率限制,避免短时间重复触达。 +5. 任务级去重,避免同一用户重复生成同一任务邮件。 + +## 6. 自动 / 实时策略邮件流程 + +```mermaid +flowchart TD + A["策略配置"] --> B["定时任务生成当日策略任务"] + B --> C["实时策略扫描"] + C --> D["按用户行为和条件筛选"] + D --> E["应用黑名单、退订、频率控制"] + E --> F["生成待发送邮件"] + F --> G["投递发送队列"] + G --> H["发送消费者调用邮件通道"] + H --> I["事件回调更新统计"] +``` + +策略邮件与批量邮件的区别: + +1. 批量邮件通常由运营手动创建,发送时间明确。 +2. 策略邮件通常由系统按规则自动生成,可能按分钟或按天扫描。 +3. 策略邮件更依赖幂等和频率控制,避免同一用户在同一策略下反复触发。 +4. 策略邮件应记录策略 ID、触发原因、触发时间,便于归因。 + +建议策略执行时做并发锁,避免多个任务实例重复生成邮件。 + +## 7. 邮件发送链路 + +通用发送链路: + +```mermaid +flowchart TD + A["待发送邮件记录"] --> B["写入 Kafka"] + B --> C["发送消费者"] + C --> D["读取发件通道配置"] + D --> E{"发送通道"} + E -->|批量营销| F["AWS SES 或批量发送通道"] + E -->|客服回复| G["SMTP / Gmail / Microsoft"] + F --> H["保存发送结果"] + G --> H + H --> I["通知前端或更新统计"] +``` + +发送消费者需要处理: + +1. 队列消息反序列化。 +2. 邮件正文、标题、收件人、发件人、回复地址、附件组装。 +3. 发送通道选择。 +4. 调用外部服务。 +5. 成功后保存 `message_id`、发送时间和成功状态。 +6. 失败后保存错误信息、失败状态和重试次数。 + +发送通道建议按场景区分: + +| 场景 | 推荐处理 | +| --- | --- | +| 批量营销邮件 | 走支持批量和事件回调的邮件服务,例如 AWS SES | +| 策略邮件 | 可复用批量发送通道,但必须做频率和幂等控制 | +| 工单客服回复 | 按发件邮箱配置选择 SMTP、Gmail API 或 Microsoft Graph | +| 普通邮箱回复 | 可独立于工单链路,同步或异步发送均可 | + +## 8. 邮件事件回调与统计 + +邮件发送后,外部服务会产生事件。通用事件包括: + +| 事件 | 处理建议 | +| --- | --- | +| Delivery / 送达 | 标记邮件已送达,记录送达时间和发送 IP | +| Bounce / 退信 | 区分永久退信和临时退信,更新任务统计;永久退信可加入黑名单 | +| Open / 打开 | 标记打开时间,更新任务打开统计 | +| Click / 点击 | 记录点击链接和点击时间,更新点击统计 | +| Complaint / 投诉 | 记录投诉,加入抑制名单或黑名单 | +| Subscription / 订阅变更 | 更新订阅或退订状态 | +| Reject / 拒信 | 记录拒信原因,更新失败统计 | +| Rendering Failure / 渲染失败 | 记录模板或内容渲染失败 | +| DeliveryDelay / 延迟 | 可记录延迟事件,是否统计需业务确认 | + +事件处理要点: + +1. 事件必须通过 `message_id` 或自定义追踪 ID 关联到本地邮件记录。 +2. 同一事件可能重复回调,需要幂等处理。 +3. 打开和点击事件存在图片加载、隐私保护、客户端屏蔽等不确定性,统计只能作为参考指标。 +4. 投诉、退订、永久退信应优先进入发送抑制规则。 + +## 9. 入站邮件 / 表单进入工单流程 + +入站来源可以有多种: + +1. 网站表单提交。 +2. 用户真实邮件来信。 +3. 外部收信服务回调。 +4. IM 或其他渠道转入邮件客服。 + +通用流程: + +```mermaid +flowchart TD + A["用户来信或表单提交"] --> B["入站接收服务"] + B --> C["写入 Kafka 入站队列"] + C --> D["EDM 工单消费者"] + D --> E["保存入站邮件和正文"] + E --> F{"是否存在未关闭工单"} + F -->|否| G["创建新工单"] + F -->|是| H["绑定到原工单并更新未读数"] + G --> I["写入节点日志"] + H --> I + I --> J["通知客服工作台"] +``` + +创建或更新工单时建议: + +1. 以发件邮箱、收件邮箱、业务用户 ID、会话标识等组合判断是否复用未关闭工单。 +2. 新工单记录来源、用户邮箱、发件邮箱、团队、状态、未读数、最后来信时间。 +3. 已有工单更新最后来信时间、未读数、用户来信数。 +4. 如果当前客服离线,可以释放负责人,让工单重新进入分配池。 +5. 入站正文应保存原始内容和清洗后的展示内容。 + +## 10. 工单客服处理流程 + +### 10.1 工单状态 + +通用状态建议: + +| 状态 | 说明 | +| --- | --- | +| 待处理 | 新入站邮件或表单生成工单,等待客服处理 | +| 服务中 | 客服已接手并正在处理 | +| 未解决 | 客服标记暂未解决,需要后续跟进 | +| 转化中 | 进入销售或转化跟进阶段 | +| 已关闭 | 本次邮件工单处理结束 | + +状态值可以由新系统自行定义,但需要保证列表筛选、统计、自动关闭和权限校验口径统一。 + +### 10.2 自动分配 + +自动分配建议流程: + +1. 找到待处理且未分配的工单。 +2. 根据收件邮箱、团队、站点、语言或业务线确定可服务团队。 +3. 获取在线客服。 +4. 按接单上限、当前处理数、最近分配时间选择客服。 +5. 更新工单负责人。 +6. 写入分配记录和节点日志。 +7. 通知客服工作台。 + +### 10.3 客服回复 + +```mermaid +flowchart TD + A["客服点击发送"] --> B["校验客服在线、权限、工单状态"] + B --> C["写入待发送邮件记录和正文"] + C --> D["更新工单未读数、首次响应、回复耗时"] + D --> E["投递客服回复队列"] + E --> F["发送消费者选择 SMTP / Gmail / Microsoft"] + F --> G["保存发送成功或失败结果"] + G --> H["通知客服工作台"] +``` + +发送前建议校验: + +1. 客服必须在线。 +2. 工单必须存在且未关闭。 +3. 当前客服必须是工单处理人,或具备接手权限。 +4. 工单必须属于当前客服可处理团队。 +5. 主题、正文、收件人、回复地址必须合法。 +6. 附件大小、类型、数量需要符合业务规则和发送通道限制。 + +### 10.4 转发和主动开工单 + +转发: + +1. 需要填写新的收件人。 +2. 转发邮件可以不绑定到原工单作为普通回复。 +3. 原邮件和转发邮件需要建立关联,方便追溯。 +4. 发送链路仍可复用客服回复队列。 + +主动开工单: + +1. 客服选择发件邮箱和目标用户邮箱。 +2. 系统校验发件邮箱归属团队。 +3. 如果同一发件邮箱和用户邮箱已有未关闭工单,应拒绝重复创建或要求接手原工单。 +4. 创建服务中工单,负责人为当前客服。 +5. 写入节点日志和分配记录。 +6. 发送第一封邮件。 + +## 11. 工单辅助任务 + +新子系统可按需要保留以下后台任务: + +| 任务 | 说明 | +| --- | --- | +| 自动分配 | 将未分配待处理工单分配给在线客服 | +| 自动移交 | 当前负责人离线且有新来信时,按代班或团队规则重新分配 | +| DDL 释放 | 工单分配后超过配置时间未处理,释放为未分配 | +| 未回复提醒 | 用户新来信超过配置时间未回复,提醒负责人 | +| 自动关闭 | 服务中工单超过配置时间无新用户来信时自动关闭 | +| 未分配告警 | 未分配工单数量超过阈值时通知团队管理员 | +| 统计同步 | 定时刷新任务发送数、回复数、打开数、点击数等统计 | + +具体调度频率和启用范围需要按新系统 SLA 确认。 + +## 12. 通用限制与风控点 + +### 12.1 任务和发送限制 + +建议配置化管理: + +1. 单任务最大目标人数。 +2. 单轮投递队列数量。 +3. 单发件邮箱每分钟、每小时、每天发送上限。 +4. 单用户每天或一段时间内最大触达次数。 +5. 单域名发送上限。 +6. 批量邮件和客服回复是否共享额度。 + +### 12.2 内容限制 + +建议校验: + +1. 邮件主题最大长度。 +2. 正文最小和最大长度。 +3. 附件大小、类型、数量。 +4. 发件邮箱和回复邮箱格式。 +5. 链接合法性和追踪参数。 +6. 必要的退订入口和合规声明。 + +### 12.3 人群抑制 + +发送前应排除: + +1. 退订用户。 +2. 投诉用户。 +3. 永久退信用户。 +4. 风险用户。 +5. 明确不允许触达的用户。 +6. 已达到频率上限的用户。 + +### 12.4 幂等和重试 + +需要幂等的场景: + +1. 任务生成邮件记录。 +2. 邮件记录投递 Kafka。 +3. Kafka 消费发送。 +4. 外部事件回调。 +5. 入站邮件生成工单。 + +失败重试建议区分: + +1. 可重试:网络超时、临时服务不可用、临时退信、限流。 +2. 不可重试:邮箱格式错误、发件权限错误、账号不存在、永久退信、投诉抑制。 + +## 13. 可观测性 + +建议至少记录以下指标: + +| 指标 | 说明 | +| --- | --- | +| 任务创建数 | 按类型、状态统计 | +| 目标用户数 | 预估人数、实际生成邮件数、过滤人数 | +| 队列积压 | 批量发送队列、客服回复队列、入站队列 | +| 发送成功率 | 按通道、发件邮箱、任务统计 | +| 失败原因分布 | 发送失败、退信、拒信、限流 | +| 送达率、打开率、点击率 | 以事件回调统计,注意打开和点击有误差 | +| 投诉率、退订率 | 作为发送风控核心指标 | +| 工单响应时长 | 首次响应、最近响应、关闭时长 | +| 未分配工单数 | 用于团队容量和告警 | + + +## 14. 参考代码位置 + +以下为现有项目中可参考的代码位置,重构时可按新架构重新命名和拆分: + +| 模块 | 现有参考 | +| --- | --- | +| 邮件任务后台入口 | `app/admin/controller/MailTaskController.php` | +| 邮件任务服务 | `app/service/MailTaskService.php` | +| 批量邮件生成与投递参考 | `app/service/UserService.php` | +| 批量邮件发送消费者 | `app/command/kafkaConsumer/BatchMailCommand.php` | +| 工单后台入口 | `app/admin/controller/EdmChatController.php` | +| 工单服务 | `app/service/EdmChatService.php` | +| 客服回复消费者 | `app/command/kafkaConsumer/MailSendCommand.php` | +| 表单数据消费者 | `app/command/kafkaConsumer/QaForm.php` | +| 表单创建工单 | `app/service/WebFormDataService.php` | +| 邮件事件回调 | `app/controller/AmazonEmailController.php` | +| 普通邮箱服务 | `app/admin/controller/MailboxController.php`、`app/service/MailboxService.php` | +| 邮件发送封装 | `app/service/Mail.php` | +| Microsoft 邮件服务 | `app/service/MicrosoftService.php` | +| 策略邮件命令 | `app/command/EdmDoRealTimeStrategy.php`、`app/command/EdmSendRealTimeStrategy.php`、`app/command/EdmDayCurrentTask.php` | +| 工单辅助命令 | `app/command/EdmAllocate.php`、`app/command/EdmMove.php`、`app/command/EdmDealLine.php`、`app/command/EdmTimeout.php`、`app/command/EdmWorkOrderAutoClose.php` | + diff --git a/wishfulfilled-wiki/05_需求文档/通用IM业务流程与接口频率限制说明.md b/wishfulfilled-wiki/05_需求文档/通用IM业务流程与接口频率限制说明.md new file mode 100644 index 0000000..82a9899 --- /dev/null +++ b/wishfulfilled-wiki/05_需求文档/通用IM业务流程与接口频率限制说明.md @@ -0,0 +1,309 @@ +# 通用 IM 业务流程与接口频率限制说明 + +更新时间:2026-05-26 + +## 1. 文档目标 + +本文用于新的 IM 子系统重构设计,目标是在功能保持一致的前提下,将现有 IM 业务抽象成通用流程,便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、控制腾讯云 IM REST API 调用频率。 + +## 2. 总体架构 + +通用 IM 链路由以下组件组成: + +| 组件 | 职责 | +| --- | --- | +| App 客户端 | 用户通过 App 使用腾讯云 IM SDK 发送和接收消息 | +| 腾讯云 IM | 负责即时消息投递、账号体系、消息格式、离线推送、服务端 REST API、回调触发 | +| App 后台 | 接收腾讯云 IM 回调,识别需要管理后台处理的消息,并写入 Kafka | +| Kafka | 解耦 App 后台与管理后台 IM 子系统,承接入站消息、推送消息、异步任务 | +| IM 子系统 / 管理后台 | 消费 Kafka 消息,维护本地会话、消息、工单、分配、状态流转和客服操作 | +| 本地数据库 | 作为管理后台 IM 业务的长期数据源,保存消息流水、会话状态、工单状态、操作记录 | +| Redis / 缓存 | 保存在线状态、分配触发标记、限流计数、幂等键、临时游标 | +| WebSocket / 站内通知 | 将新消息、分配变化、发送结果、状态变化推送给管理后台前端 | + +## 3. 总体消息流 + +```mermaid +flowchart TD + A["用户在 App 发送 IM 消息"] --> B["腾讯云 IM SDK 投递消息"] + B --> C["腾讯云 IM 回调 App 后台"] + C --> D["App 后台校验回调并识别管理后台相关消息"] + D --> E["App 后台写入 Kafka"] + E --> F["IM 子系统消费 Kafka"] + F --> G["幂等校验与消息解析"] + G --> H["写入本地消息表"] + H --> I["更新会话、工单、未读数、最后消息时间"] + I --> J["触发分配、提醒或状态流转"] + J --> K["管理后台客服前端展示"] +``` + +核心原则: + +1. App 客户端消息先进入腾讯云 IM。 +2. 腾讯云 IM 将消息回调给 App 后台。 +3. App 后台不直接写管理后台业务库,而是将管理后台关心的消息写入 Kafka。 +4. 管理后台 IM 子系统消费 Kafka 后入库,并维护本地会话、工单和客服状态。 +5. 管理后台后续展示、检索、统计、客服处理,应以本地数据库为主数据源。 + +## 4. 核心数据模型 + +新子系统建议至少抽象以下数据对象: + +| 对象 | 说明 | +| --- | --- | +| IM 账号 | 业务用户、品牌账号、客服账号在腾讯云 IM 中的账号映射 | +| 消息 Message | 本地保存的消息流水,包含方向、发送方、接收方、消息类型、消息体、腾讯消息 ID、业务扩展字段 | +| 会话 Conversation | 用户与品牌/客服之间的当前会话指针,包含未读数、最后消息时间、当前工单 ID | +| 工单 Ticket / ChatRecord | 一次客服处理过程,包含状态、负责人、团队、首次响应时间、关闭时间 | +| 分配记录 Assignment | 自动分配、手动接手、转接、释放、代班等操作记录 | +| 节点日志 NodeLog | 工单生成、分配、首次回复、转接、未解决、转化中、关闭等关键节点 | +| 推送任务 PushTask | 运营或策略创建的 IM 推送任务 | +| 推送记录 PushRecord | 单个用户、单条内容的实际待发送记录和发送结果 | + +## 5. 入站消息流程 + +### 5.1 用户发送消息 + +1. 用户在 App 中通过腾讯云 IM SDK 发送文本、图片、视频、自定义卡片或其他消息。 +2. 腾讯云 IM 完成消息投递,并根据配置触发服务端回调。 +3. App 后台接收腾讯云 IM 回调,完成签名、来源、事件类型和消息结构校验。 +4. App 后台判断该消息是否需要管理后台处理。 +5. 需要管理后台处理的消息,由 App 后台写入 Kafka。 +6. IM 子系统消费 Kafka,执行幂等校验,解析消息体。 +7. IM 子系统写入本地消息流水,并更新会话和工单状态。 +8. 若消息产生新的待处理工单,则进入分配流程。 +9. 管理后台前端通过列表查询、WebSocket 或站内通知看到新消息。 + +### 5.2 幂等与顺序 + +腾讯云回调、App 后台写 Kafka、Kafka 消费都可能出现重试,因此 IM 子系统必须做幂等处理。 + +建议幂等键优先级: + +1. 腾讯云 IM 消息唯一标识。 +2. 发送方、接收方、消息随机数、消息序列号、消息时间组合。 +3. App 后台写入 Kafka 时生成的业务事件 ID。 + +同一会话内需要尽量按消息时间和腾讯云消息顺序展示。若存在乱序到达,应允许入库后按消息时间、序列号或腾讯消息 ID 排序展示。 + +## 6. 客服处理流程 + +### 6.1 会话和工单状态 + +通用状态建议如下: + +| 状态 | 说明 | +| --- | --- | +| 待接入 | 用户有新消息,尚未分配或尚未被客服处理 | +| 服务中 | 客服已接入并正在处理 | +| 已转接 | 工单被转给其他客服或团队,等待新处理人接手 | +| 未解决 | 客服标记暂未解决,需要后续跟进 | +| 转化中 | 进入转化或商机跟进阶段 | +| 已关闭 | 本次客服处理结束 | + +状态值可以由新子系统自行定义,但需要保证列表筛选、统计口径、自动关闭、转接和重新打开逻辑一致。 + +### 6.2 自动分配 + +自动分配通常在新消息入库后触发: + +1. 找到待接入工单。 +2. 根据品牌、站点、团队、语言、业务线等条件确定可服务团队。 +3. 获取在线客服列表。 +4. 按客服接单上限、当前处理数、最近分配时间等规则筛选。 +5. 选择目标客服并更新工单负责人。 +6. 写入分配记录和节点日志。 +7. 通知管理后台前端。 + +建议将分配能力独立成可重试任务,避免 Kafka 入站消费被复杂分配逻辑拖慢。 + +### 6.3 客服打开会话 + +客服打开会话时,系统通常需要: + +1. 校验客服是否在线、是否有处理权限。 +2. 查询会话和工单详情。 +3. 拉取本地消息列表。 +4. 清理当前客服视角下的未读数。 +5. 必要时将工单从待接入或已转接推进到服务中。 +6. 记录读取、接手或首次处理节点。 + +### 6.4 客服发送消息 + +客服发送消息的通用链路: + +```mermaid +flowchart TD + A["客服在管理后台发送消息"] --> B["IM 子系统校验权限、会话、工单状态"] + B --> C["组装腾讯云 IM 消息体"] + C --> D["调用腾讯云 REST API"] + D --> E{"腾讯云返回结果"} + E -->|成功| F["写入本地消息流水并更新会话"] + E -->|失败| G["记录失败原因并进入重试或人工处理"] + F --> H["通知管理后台前端发送成功"] + G --> I["通知管理后台前端发送失败"] +``` + +发送前建议校验: + +1. 当前客服必须具备客服身份。 +2. 当前客服必须有该会话或工单处理权限。 +3. 工单不能处于已关闭状态,除非业务允许重新打开。 +4. 消息体大小、消息类型、媒体文件大小必须符合业务和腾讯云限制。 +5. 对运营推送和客服消息应区分业务来源,方便统计和风控。 + +### 6.5 转接、释放和自动关闭 + +通用处理规则: + +1. 手动转接给个人:校验当前处理人权限,更新负责人,记录转接节点。 +2. 手动转接给团队:记录目标团队,等待团队内客服接手或自动分配。 +3. 客服离线释放:如果负责人离线且有未读用户消息,可释放为待分配。 +4. 超时未处理释放:待接入或已转接超过配置时间后,重新进入分配池。 +5. 自动关闭:服务中会话在配置时间内无新用户消息或无新客服动作时,自动关闭。 + +## 7. 运营 IM 推送流程 + +运营推送与客服消息都可能调用腾讯云 IM REST API,但应在业务上区分: + +```mermaid +flowchart TD + A["后台创建推送任务"] --> B["审核或直接进入待发送"] + B --> C["按标签、站点、产品、用户状态筛选目标人群"] + C --> D["生成 PushRecord"] + D --> E["写入 Kafka 推送队列"] + E --> F["推送消费者限速发送"] + F --> G["调用 sendmsg 或 batchsendmsg"] + G --> H["保存腾讯云返回结果"] + H --> I["汇总任务完成状态"] +``` + +建议: + +1. 大批量运营推送优先评估 `batchsendmsg`,减少单发接口压力。 +2. 推送消费者必须按 SDKAppID 和接口维度做限速。 +3. 推送记录需要保存发送状态、错误码、重试次数、腾讯云返回结果。 +4. 发送失败应区分可重试错误和不可重试错误。 +5. 推送消息应写入业务扩展字段,便于后续归因和统计。 + +## 8. 本地存储与历史消息 + +管理后台 IM 子系统建议以本地数据库作为长期主数据源。 + +腾讯云 IM 漫游消息适合用于消息补偿、短期核对或异常排查,不建议作为客服记录、统计报表和审计的唯一长期来源。原因是历史消息存储时长与套餐、增值服务和控制台配置有关,且可能产生额外费用。 + +建议本地保存: + +1. 入站用户消息。 +2. 客服发送消息。 +3. 系统提示消息。 +4. 运营推送消息及发送结果。 +5. 转接、关闭、分配、未解决、转化等节点日志。 + +## 9. 腾讯云 IM 接口频率限制说明 + +以下为腾讯云官方文档口径,实际限制可能随套餐、数据中心、控制台配置和商务协议变化,最终以腾讯云控制台和合同为准。 + +### 9.1 通用限制 + +| 项目 | 限制 | +| --- | --- | +| 单聊、群聊单条消息长度 | 最大 12KB | +| UserID | 长度不超过 32 字节,不支持特殊字符,建议使用英文字母或数字 | +| REST API 通用调用频率 | 多数 REST API 默认最高 200 次/秒 | +| 导入多个账号、删除账号、查询账号 | 默认最高 100 次/秒 | +| 查询在线状态 | 单次请求最多查询 500 个用户 | +| 批量发单聊消息 | 单次最多给 500 个用户发送 | +| 导入多个账号 | 单次最多导入 100 个用户名 | +| 单个用户好友数 | 默认支持 3000 个好友 | +| 单个用户黑名单数 | 最多 1000 人 | + +### 9.2 常用 REST API 频率 + +| 接口 | 默认频率限制 | 叠加包增量 | 说明 | +| --- | --- | --- | --- | +| `v4/openim/sendmsg` 单发单聊消息 | 200 次/秒 | 每个叠加包 +100 次/秒 | 体验版和开发版每日累计发送量限制为 1000 条/天 | +| `v4/openim/batchsendmsg` 批量发单聊消息 | 200 次/秒,同时 12000 条/分钟 | 每个叠加包 +6000 条/分钟 | 条数按接收方数量计算,一次发给 500 人计 500 条 | +| `v4/profile/portrait_set` 设置资料 | 200 次/秒 | 每个叠加包 +100 次/秒 | 用于更新头像、昵称等资料 | +| `v4/profile/portrait_get` 拉取资料 | 200 次/秒 | 每个叠加包 +100 次/秒 | 用于查询资料 | +| `v4/openim/query_online_status` 查询在线状态 | 200 次/秒 | 每个叠加包 +100 次/秒 | 单次最多查询 500 个用户 | +| `v4/openim/get_c2c_unread_msg_num` 查询单聊未读数 | 200 次/秒 | 每个叠加包 +100 次/秒 | 如新系统需要同步未读数需注意限频 | +| `v4/group_open_http_svc/send_group_msg` 群内发普通消息 | 200 次/秒 | 每个叠加包 +100 次/秒 | 单个群发送频率上限为 40 条/秒 | +| `v4/sns/black_list_get` 拉取黑名单 | 200 次/秒 | 每个叠加包 +100 次/秒 | 关系链相关能力需控制调用频率 | + +### 9.3 调频收费说明 + +腾讯云 IM 服务端 API 调频属于增值服务。官方公告口径为: + +| 数据中心 | 服务端 API 调用频率叠加包价格 | +| --- | --- | +| 国内数据中心 | 10 元/个/日,日结后付费 | +| 境外数据中心 | 20 元/个/日,日结后付费 | + +注意事项: + +1. 调频对单个 SDKAppID 生效,多个 SDKAppID 需要分别配置。 +2. 每个调频项按当日配置的最高数值计费,次日出账。 +3. 体验版不支持调整服务端 API 调用频率上限。 +4. 如果新子系统存在大规模运营推送,必须提前评估 `sendmsg` 与 `batchsendmsg` 的峰值。 + +## 10. 新子系统设计建议 + +### 10.1 按接口维度做限流 + +建议在 IM 子系统服务端增加统一腾讯云 IM Client,并在 Client 内按以下维度做限流: + +1. SDKAppID。 +2. API 路径,例如 `openim/sendmsg`、`openim/batchsendmsg`。 +3. 业务来源,例如客服消息、运营推送、系统通知。 + +Kafka 消费者不能只依赖消费并发控制,否则高峰期可能瞬间打满腾讯云 API 限频。 + +### 10.2 单发和批量发送分流 + +建议策略: + +| 场景 | 推荐接口 | +| --- | --- | +| 客服一对一即时回复 | `openim/sendmsg` | +| 系统给单个用户发送提示 | `openim/sendmsg` | +| 运营批量推送相同或相似内容 | 优先评估 `openim/batchsendmsg` | +| 需要强个性化卡片、每人内容不同 | 可用 `sendmsg`,但必须限速 | + +### 10.3 错误处理和重试 + +建议将发送结果分为: + +1. 成功:腾讯云返回 `ErrorCode=0` 且业务认为发送完成。 +2. 部分成功:批量发送时部分账号失败,需要保存失败账号列表。 +3. 可重试失败:网络异常、超时、腾讯云内部错误、限频。 +4. 不可重试失败:账号不存在、消息体非法、权限错误、消息包超长。 + +重试建议采用延迟重试,并设置最大重试次数。超过次数后进入失败表或死信队列,供人工排查。 + +### 10.4 回调链路可观测性 + +入站链路建议记录以下指标: + +1. 腾讯云回调接收量。 +2. App 后台写 Kafka 成功量和失败量。 +3. Kafka 积压量。 +4. IM 子系统消费延迟。 +5. 入库成功量和幂等重复量。 +6. 会话创建量、工单创建量、分配成功量。 + +发送链路建议记录: + +1. 每个 API 的 QPS。 +2. 限流等待时间。 +3. 腾讯云错误码分布。 +4. 发送成功率。 +5. 可重试失败量和最终失败量。 + +## 11. 参考来源 + +- 腾讯云 IM 使用限制:https://cloud.tencent.com/document/product/269/32429 +- 腾讯云 IM 单发单聊消息:https://cloud.tencent.com/document/product/269/2282 +- 腾讯云 IM 批量发单聊消息:https://cloud.tencent.com/document/product/269/1612 +- 腾讯云 IM 服务端 API 调频收费公告:https://cloud.tencent.com/document/product/269/93324 + diff --git a/wishfulfilled-wiki/07_技术文档/01-子系统-identity-数据库表关系.md b/wishfulfilled-wiki/07_技术文档/01-子系统-identity-数据库表关系.md new file mode 100644 index 0000000..2354af5 --- /dev/null +++ b/wishfulfilled-wiki/07_技术文档/01-子系统-identity-数据库表关系.md @@ -0,0 +1,430 @@ +# identity 子系统 — doris数据库相关表与关联关系(供参考) + + +--- + +## 1. 数据库全景 + +| 数据库 | 说明 | 与 identity 的关系 | +|--------|------|-------------------| +| `ods_app_base_data` | APP基础数据(用户、设备、好友、产品) | **核心** — 用户身份主数据 | +| `ods_app_app_community` | 社区数据(帖子、评论、关注) | 行为数据,辅助归并 | +| `ods_app_jh_data` | JOYHUB事件数据 | 行为数据,辅助归并 | +| `ods_oa_oaaftersales` | OA售后系统(客户、订单、测评) | **核心** — 非APP用户身份线索 | +| `app_tag_data` | 标签数据 | **关键** — 已有 OneID 归并表 | + +--- + +## 2. 核心发现:已存在的 OneID 归并表 + +### `app_tag_data.user_oneid` — 用户唯一标识归并表(6 字段,已有数据) + +> **这是 identity 子系统 M2(归并引擎)的核心参考**,已实现 uuid → one_id 的归并逻辑 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `uuid` | VARCHAR(64) | UNI, NOT NULL | 原始客户唯一标识符 | +| `one_id` | VARCHAR(64) | NOT NULL | 用户唯一标识(归并后的ID) | +| `bridge_uuid` | STRING | | 当前 uuid 对应的非当前桥接 uuid | +| `association_fields` | STRING | | 关联字段 | +| `detail` | STRING | | uuid 指向 one_id 的证据说明(JSON) | +| `update_time` | DATETIME(3) | | 同步更新时间 | + +**关键设计点**: +- `uuid` → `one_id` 是多对一关系(多个 uuid 可归并到同一个 one_id) +- `bridge_uuid` 记录桥接关联,用于跨系统身份串联 +- `detail` 字段存储归并证据(JSON),与设计文档中 `person_profiles.merge_evidence` 概念一致 +- `association_fields` 记录关联字段,对应设计文档中的线索类型 + +--- + +## 3. 核心表状态 + +设计文档定义的 4 张核心表 **尚未在数据库中创建**: + +| 表名 | 设计文档定义 | 数据库状态 | 与现有表的关系 | +|------|-------------|-----------|--------------| +| `person_profiles` | 真实人主表 | **不存在** | 可参考 `app_tag_data.user_oneid`(one_id) | +| `person_identity_links` | 身份线索关联表 | **不存在** | 可参考 `ods_oa_oaaftersales.customer_platform_info`(type映射线索类型) | +| `contact_context_snapshots` | 上下文快照 | **不存在** | 需聚合多表新建 | +| `device_records` | 设备变化记录 | **不存在** | 可参考 `user_device_token_log`(已有252万条记录) | + +--- + +## 4. 已存在的数据源表 + +### 4.1 用户与身份核心表 + +#### `ods_app_base_data.users` — 用户主表(36 字段) + +> identity 子系统的核心用户数据源,提供 JOYHUB ID、邮箱、设备号等身份线索 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 用户ID(JOYHUB ID) | +| `userName` | STRING | | 用户名 | +| `email` | STRING | | 邮箱 | +| `deviceToken` | VARCHAR(300) | | 设备推送令牌 | +| `IMEI` | STRING | | 设备IMEI | +| `sysType` | VARCHAR(765) | | 系统类型(安卓/IOS/Windows Phone) | +| `deviceId` | STRING | | 设备ID | +| `appVersion` | VARCHAR(90) | | APP版本 | +| `contact_information` | VARCHAR(765) | | 联系方式(电话号码) | +| `mobile` | STRING | | 手机号 | +| `area_code` | BIGINT | | 区域代码(美国1,中国86) | +| `status` | TINYINT | | 1活跃/2封禁/3注销 | +| `sysTime` | DATETIME | | 系统时间(注册时间) | +| `created_at` | DATETIME | | 创建时间 | + +#### `ods_app_base_data.user_login_last` — 最近登录信息(21 字段) + +> 提供设备型号、系统版本、APP版本、国家等信息,是设备变化识别(M4)的重要数据源 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `UserId` | BIGINT | UNI | 用户ID → 关联 `users.id` | +| `deviceId` | STRING | | 设备ID | +| `deviceModel` | VARCHAR(150) | | 手机型号 | +| `device` | VARCHAR(150) | | 手机系统 | +| `sysType` | VARCHAR(150) | | 系统设备信息与版本 | +| `appVersion` | VARCHAR(45) | | APP版本号 | +| `appChannel` | INT | | 渠道 | +| `countryName` | VARCHAR(600) | | 国家名称 | +| `countryCode` | VARCHAR(30) | | 国家缩写 | +| `Time` | DATETIME | | 登录时间 | +| `Ip` | STRING | | IP地址 | + +#### `ods_app_base_data.user_device_token_log` — 设备令牌变更日志(7 字段,252万行) + +> 记录设备令牌的添加和更新,可用于追踪设备变化 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | INT | UNI | 主键 | +| `user_id` | INT | | 用户ID → 关联 `users.id` | +| `type` | TINYINT | | 0添加/1更新 | +| `device_id` | STRING | | 设备ID | +| `new_device_token` | VARCHAR(300) | | 新设备令牌 | +| `created_at` | DATETIME | | 创建时间 | +| `client_time` | DATETIME | | 客户端时间 | + +#### `ods_app_base_data.user_contact_information_history` — 联系方式变更历史(11 字段,20万行) + +> 记录用户手机号等联系方式的变更历史,可用于身份线索追踪 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 主键 | +| `user_id` | BIGINT | | 用户ID → 关联 `users.id` | +| `user_type` | VARCHAR(150) | | 用户角色 | +| `area_code` | BIGINT | | 区域代码 | +| `mobile` | STRING | | 手机号 | +| `area_id` | INT | | 区域ID | +| `marketing_phone` | TINYINT | | 营销电话开关 | +| `marketing_sms` | TINYINT | | 个性化广告开关 | +| `status` | SMALLINT | | 1生效中/2已过期 | +| `verify_status` | SMALLINT | | 短信验证状态:1通过/2未通过 | +| `created_at` | BIGINT | | 创建时间 | + +#### `ods_app_base_data.banned_device_id` — 设备封禁表(3 字段,6831行) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | INT | UNI | 主键 | +| `device_id` | VARCHAR(765) | | 封禁设备ID | +| `created_at` | INT | | 创建时间 | + +#### `ods_app_base_data.blacklist_users_aggregate` — 用户黑名单汇总(8 字段) + +> 按 uid、设备、IP 维度的黑名单,用于风险判断 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | INT | UNI | 主键 | +| `target_id` | INT | | 1=uid, 2=设备, 3=IP | +| `target_value` | VARCHAR(1500) | | 字段值 | +| `category_id` | INT | | 黑名单类别ID | +| `describe` | VARCHAR(1500) | | 加入原因 | + + +### 4.2 OA 售后系统 — 客户身份数据 + +#### `ods_oa_oaaftersales.customer` — 客户主表(22 字段,23万行) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 客户ID | +| `name` | STRING | | 客户名 | +| `country` | VARCHAR(60) | | 国家 | +| `is_black` | TINYINT | | 是否黑名单 | +| `high_risk` | TINYINT | | 是否高风险 | +| `erp_contact` | STRING | | ERP联系方式 | +| `erp_pay_account` | VARCHAR(1500) | | ERP付款账号 | + +#### `ods_oa_oaaftersales.customer_platform_info` — 客户平台信息(8 字段,26万行) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 主键 | +| `type` | TINYINT | | **1电话/2邮箱/3joyhub_id/4邮箱编码/5twitter/6facebook** | +| `customer_id` | INT | | 客户ID → 关联 `customer.id` | +| `account` | STRING | | 账号值 | +| `is_delete` | TINYINT | | 是否删除 | + +#### `ods_oa_oaaftersales.customer_address` — 客户地址(18 字段,5631行) + +> 提供姓名+地址组合,可用于 ORDER_NAME_ADDRESS 线索归并 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 主键 | +| `customer_id` | INT | | 客户ID → 关联 `customer.id` | +| `recipient_name` | STRING | | 收件人姓名 | +| `phone` | STRING | | 电话 | +| `zip_code` | STRING | | 邮编 | +| `country` | VARCHAR(300) | | 国家 | +| `city` | STRING | | 城市 | +| `state` | STRING | | 州/省 | +| `detail` | VARCHAR(1500) | | 详细地址 | + +#### `ods_oa_oaaftersales.customer_payment_account` — 客户付款账号(12 字段,10万行) + +> 提供收款信息(银行卡、PayPal等),可作为身份归并线索 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 主键 | +| `ct_id` | INT | | 客户ID → 关联 `customer.id` | +| `pay_name` | VARCHAR(150) | | 支付方式 | +| `account_number` | STRING | | 账号 | +| `account_name` | STRING | | 账户名 | +| `card_no` | VARCHAR(300) | | 卡号 | + +#### `ods_oa_oaaftersales.customer_bind` — 客户绑定关系(6 字段,4980行) + +> 客户间的绑定关系,已有归并概念 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 主键 | +| `customer_ids` | STRING | | 绑定的客户ID集合 | +| `unbind_time` | DATETIME | | 解绑时间 | +| `is_deleted` | TINYINT | | 是否删除 | + +#### `ods_oa_oaaftersales.customer_bind_log` — 客户绑定日志(6 字段,1.2万行) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 主键 | +| `user_id` | INT | | 操作人ID | +| `bind_customer_ids` | STRING | | 绑定的客户ID | +| `type` | VARCHAR(765) | | 操作类型 | + +#### `ods_oa_oaaftersales.evaluation_order` — 测评订单(55+ 字段,45万行) + +> 包含丰富的身份线索:邮箱、电话、JOYHUB ID、社交媒体账号 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 订单ID | +| `ct_id` | INT | | 客户ID → 关联 `customer.id` | +| `amazon_order_id` | STRING | | 亚马逊订单号 | +| `email` | STRING | | 邮箱 | +| `phone` | STRING | | 电话 | +| `joyhub_id` | VARCHAR(150) | | JOYHUB ID | +| `twitter` | STRING | | Twitter账号 | +| `facebook` | STRING | | Facebook账号 | + +#### `ods_oa_oaaftersales.lingxing_order` — 亚马逊订单(30+ 字段,2142万行) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 订单ID | +| `amazon_order_id` | VARCHAR(150) | | 亚马逊订单号 | +| `buyer_name` | VARCHAR(765) | | 买家姓名 | +| `buyer_email` | VARCHAR(765) | | 买家邮箱 | +| `phone` | VARCHAR(90) | | 电话 | +| `postal_code` | VARCHAR(765) | | 邮编 | +| `address` | VARCHAR(765) | | 地址 | + +#### `ods_oa_oaaftersales.phone_records` — 电话记录(30+ 字段,8万行) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 主键 | +| `ct_id` | INT | | 客户ID → 关联 `customer.id` | +| `phone` | STRING | | 电话 | +| `email` | STRING | | 邮箱 | +| `joyhub_id` | VARCHAR(150) | | JOYHUB ID | + +### 4.3 社区行为表 + +#### `ods_app_app_community.posts` — 帖子表(35 字段) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 帖子ID | +| `user_id` | INT | | 用户ID → 关联 `users.id` | +| `status` | SMALLINT | | 10待审核/20拒绝/30通过 | +| `deleted_at` | INT | | 删除时间(0=未删除) | + +#### `ods_app_app_community.post_likes` — 点赞表(5 字段) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 主键 | +| `post_id` | BIGINT | | 帖子ID → 关联 `posts.id` | +| `user_id` | INT | | 用户ID → 关联 `users.id` | + +#### `ods_app_app_community.comments` — 评论表(14 字段) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 评论ID | +| `post_id` | DECIMAL(20,0) | | 帖子ID → 关联 `posts.id` | +| `user_id` | DECIMAL(20,0) | | 用户ID → 关联 `users.id` | + +#### `ods_app_app_community.follows` — 关注关系表(7 字段) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 主键 | +| `user_id` | INT | | 关注者ID → 关联 `users.id` | +| `following_user_id` | INT | | 被关注者ID → 关联 `users.id` | + +#### `ods_app_base_data.friends` — 好友关系表(6 字段) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | DECIMAL(20,0) | UNI | 主键 | +| `user_id` | DECIMAL(20,0) | | 用户ID → 关联 `users.id` | +| `friend_id` | DECIMAL(20,0) | | 好友ID → 关联 `users.id` | + +### 4.4 事件行为表 + +#### `ods_app_jh_data.events` — APP事件表(18 字段) + +> event_type: 13=home, 8=玩具连接, 5=视频等 + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 事件ID | +| `add_date` | DATE | UNI | 记录日期 | +| `uid` | BIGINT | | 用户ID → 关联 `users.id` | +| `event_type` | INT | | 事件类型 | +| `pid` | BIGINT | | 产品ID → 关联 `def_product_list.id` | + +#### `ods_app_jh_data.remote_events` — 远程连接事件表(15 字段) + +| 字段 | 类型 | 键 | 说明 | +|------|------|---|------| +| `id` | BIGINT | UNI | 事件ID | +| `uid` | BIGINT | | 用户ID → 关联 `users.id` | +| `call_sn` | VARCHAR(600) | | 远程序列号(格式:uid1_uid2_uuid) | +| `mode` | INT | | 1文字/2语音/3视频 | + + +--- + +## 5. 表关联关系图 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ identity 子系统数据关系 │ +└─────────────────────────────────────────────────────────────────────────┘ + + ┌───────────────────────┐ + │ app_tag_data │ + │ .user_oneid │ + │ uuid ──► one_id │ + │ (已有归并逻辑) │ + └───────────┬───────────┘ + │ uuid 可能是 email/phone/device + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ ods_app_base_data(APP用户体系) │ +│ │ +│ users ◄── user_profiles (user_id) │ +│ │ user_login_last (UserId) ── deviceId, deviceModel │ +│ │ user_device_token_log (user_id) ── 252万条设备变更记录 │ +│ │ user_contact_information_history (user_id) ── 20万条变更 │ +│ │ friends (user_id / friend_id) │ +│ │ banned_device_id (device_id) ── 设备封禁 │ +│ │ blacklist_users_aggregate (target_id+target_value) │ +│ │ │ +│ └── id = JOYHUB_ID │ +└──────────────────────────────────────────────────────────────────────┘ + +┌──────────────────────────────────────────────────────────────────────┐ +│ ods_oa_oaaftersales(OA售后体系) │ +│ │ +│ customer ◄── customer_platform_info (customer_id) │ +│ │ type: 1电话/2邮箱/3joyhub_id/5twitter/6facebook │ +│ │ customer_address (customer_id) ── 姓名+地址+电话 │ +│ │ customer_payment_account (ct_id) ── 收款账号 │ +│ │ customer_bind (customer_ids) ── 客户绑定关系 │ +│ │ customer_bind_log ── 绑定操作日志 │ +│ │ evaluation_order (ct_id) ── 邮箱/电话/joyhub_id/社媒 │ +│ │ phone_records (ct_id) ── 电话/邮箱/joyhub_id │ +│ │ order_refund (ct_id) ── 返款详情 │ +│ │ │ +│ lingxing_order ── buyer_name + buyer_email + phone + address │ +│ └── lingxing_order_item (amazon_order_id) │ +└──────────────────────────────────────────────────────────────────────┘ + +┌──────────────────────────────────────────────────────────────────────┐ +│ 社区行为(ods_app_app_community) │ +│ │ +│ posts ◄── post_likes (post_id, user_id) │ +│ │ comments (post_id, user_id) │ +│ └── follows (user_id, following_user_id) │ +└──────────────────────────────────────────────────────────────────────┘ + +┌──────────────────────────────────────────────────────────────────────┐ +│ 事件行为(ods_app_jh_data) | +│ │ +│ events (uid, event_type, pid) │ +│ communities (uid, event_type) │ +│ remote_events (uid, call_sn ── 含对端uid) │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 6. identity 设计表与现有表的字段映射 + +### 6.1 person_identity_links(身份线索关联表)— 待建 + +| clue_type | 设计文档定义 | 数据来源表 | 来源字段 | 数据量级 | +|-----------|-------------|-----------|---------|---------| +| JOYHUB_ID | JOYHUB用户ID | `users.id` | id | - | +| EMAIL | 邮箱 | `users.email` / `evaluation_order.email` / `lingxing_order.buyer_email` / `edm_contact_user.email` / `customer_platform_info`(type=2) | email | - | +| PHONE | 电话 | `users.contact_information` / `users.mobile` / `evaluation_order.phone` / `lingxing_order.phone` / `customer_platform_info`(type=1) | phone | - | +| DEVICE | 设备号 | `users.deviceId` / `user_login_last.deviceId` / `user_device_token_log.device_id` | deviceId | 252万条日志 | +| ORDER_NAME_ADDRESS | 订单姓名+地址 | `lingxing_order.buyer_name` + `lingxing_order.address` / `customer_address.recipient_name` + `customer_address.detail` | name+address | 5631条地址 | +| SOCIAL_ACCOUNT | 社交媒体(扩展) | `evaluation_order.twitter` / `evaluation_order.facebook` / `customer_platform_info`(type=5,6) | twitter/facebook | - | +| PAYMENT_ACCOUNT | 收款账号(扩展) | `customer_payment_account.account_number` / `customer.erp_pay_account` | account | 10万条 | + +### 6.2 device_records(设备变化记录)— 待建 + +| 设计字段 | 数据来源表 | 来源字段 | +|---------|-----------|---------| +| `joyhub_id` | `users.id` | id | +| `device_id` | `users.deviceId` / `user_login_last.deviceId` / `user_device_token_log.device_id` | deviceId | +| `device_model` | `user_login_last.deviceModel` | deviceModel | +| `os_version` | `user_login_last.device` / `user_login_last.sysType` | device/sysType | +| `app_version` | `user_login_last.appVersion` / `users.appVersion` | appVersion | +| `change_type` | `user_device_token_log.type` | 0=NEW, 1=UPDATE | + +### 6.3 contact_context_snapshots(上下文快照)— 待建 + +| 设计字段 | 数据来源子系统 | 来源表 | +|---------|--------------|--------| +| `identity_snapshot` | identity | person_profiles + person_identity_links | +| `transaction_snapshot` | planning | lingxing_order, lingxing_order_item | +| `service_snapshot` | support | order_refund, evaluation_order, phone_records | +| `risk_snapshot` | risk | customer.is_black, customer.high_risk, banned_device_id, blacklist_users_aggregate | +| `device_snapshot` | identity(M4) | device_records, user_login_last | +| `outreach_snapshot` | outreach | (待确认) | + + diff --git a/wishfulfilled-wiki/08_测试相关/USER用户运营系统_原型逐页详细测试用例集.xlsx b/wishfulfilled-wiki/08_测试相关/USER用户运营系统_原型逐页详细测试用例集.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..71e4bb506ae025c7ccbbe9be90bc051d9a57214f GIT binary patch literal 182957 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%0Dk`u0O9`t%=`y{ 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

(y7B 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

c@v!)Z0BcwGlOG8MekkT5+b%Ln>dk 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Ð#2$ga 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 lp<~ literal 0 HcmV?d00001