fix: 自动恢复失效令牌并保留身份入口

This commit is contained in:
NathanAlex7
2026-09-15 11:24:52 +08:00
parent 5f49f94ca4
commit e44716b3ec
5 changed files with 32 additions and 5 deletions
+4 -1
View File
@@ -149,9 +149,12 @@ class OperationsCenterTests(unittest.TestCase):
def test_navigation_is_role_aware_and_keeps_project_context(self):
root = Path(__file__).resolve().parents[1]
nav = (root / "src/components/xingbang/AgentNav.tsx").read_text(encoding="utf-8")
selector = (root / "src/components/xingbang/IdentitySelector.tsx").read_text(encoding="utf-8")
self.assertNotIn("四 Agent 工作区",nav)
for label in ("业务助手","项目工作台","业务时效","配置治理","当前身份"):
for label in ("业务助手","项目工作台","业务时效","配置治理"):
self.assertIn(label,nav)
self.assertIn("当前身份",selector)
self.assertIn("选择身份",selector)
for label in ("运行中心","检查依据","演示与验收","兼容入口"):
self.assertNotIn(label,nav)
self.assertIn("ROLE_LABEL",nav)
+15
View File
@@ -0,0 +1,15 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const root = path.resolve(__dirname, "..");
const nav = fs.readFileSync(path.join(root, "src/components/xingbang/AgentNav.tsx"), "utf8");
const selector = fs.readFileSync(path.join(root, "src/components/xingbang/IdentitySelector.tsx"), "utf8");
assert.match(nav, /response\.status===401/, "导航应识别无效访问令牌");
assert.match(nav, /removeItem\("xingbang-access-token"\)/, "无效旧令牌应自动清理");
assert.match(nav, /setItem\("xingbang-demo-role","manager"\)/, "清理后应恢复到客户经理演示身份");
assert.match(nav, /pending=\{!identity\}/, "身份连接失败时仍应显示身份选择入口");
assert.match(selector, /pending&&<option value="" disabled>选择身份<\/option>/, "待连接状态应给出明确的身份选择提示");
console.log("Identity recovery behavior checks passed");
+1
View File
@@ -209,6 +209,7 @@ def main() -> int:
("repository-diff", [git, "diff", "--check"], ROOT),
("backend-unit", [project_python(), "-m", "unittest", "discover", "-s", ".", "-p", "test_*.py", "-v"], BACKEND),
("reviewer-workspace-ui", [executable("node"), "scripts/test_reviewer_workspace.cjs"], ROOT),
("identity-recovery-ui", [executable("node"), "scripts/test_identity_recovery.cjs"], ROOT),
("frontend-build", [npm, "run", "build"], ROOT),
("compose-local-config", [docker, "compose", "-f", "docker-compose.yml", "config", "--quiet"], ROOT),
("compose-aliyun-config", [docker, "compose", "--env-file", "deploy/aliyun.env.example", "-f", "deploy/docker-compose.aliyun.yml", "config", "--quiet"], ROOT),
+9 -2
View File
@@ -24,7 +24,14 @@ export default function AgentNav({active,project,identity:provided}:{active:stri
useEffect(()=>{
setRoleHint(sessionStorage.getItem("xingbang-demo-role")||"");
if(provided)return;
fetch("/api/risk-agent/access/context",{headers:identityHeaders()}).then(async r=>r.ok?setLoaded(await r.json()):null).catch(()=>null);
fetch("/api/risk-agent/access/context",{headers:identityHeaders()}).then(async response=>{
if(response.ok){setLoaded(await response.json());return;}
if(response.status===401&&sessionStorage.getItem("xingbang-access-token")&&!sessionStorage.getItem("xingbang-demo-role")){
sessionStorage.removeItem("xingbang-access-token");
sessionStorage.setItem("xingbang-demo-role","manager");
location.reload();
}
}).catch(()=>null);
},[provided]);
const identity=provided||loaded;
const role=identity?.role||roleHint;
@@ -42,6 +49,6 @@ export default function AgentNav({active,project,identity:provided}:{active:stri
{canConfigure&&<a href="/data-access" className={active==="risk"?"selected":""}><Settings2 size={16}/>配置治理</a>}
<span className="xb-nav-spacer"/>
{project&&<span className="xb-project-context">项目 · {project}</span>}
{identity?<IdentitySelector identity={identity}/>:<span className="xb-identity"><i/><span>当前身份</span><b>待连接</b></span>}
<IdentitySelector identity={identity||{role:roleHint}} pending={!identity}/>
</nav>;
}
+3 -2
View File
@@ -4,8 +4,8 @@ import {ShieldCheck} from "lucide-react";
export const ROLE_LABEL:Record<string,string>={manager:"客户经理",reviewer:"评审员",admin:"配置管理员"};
export default function IdentitySelector({identity,dark=false}:{identity:any;dark?:boolean}){
const role=identity?.role||"manager";
export default function IdentitySelector({identity,dark=false,pending=false}:{identity:any;dark?:boolean;pending?:boolean}){
const role=identity?.role||(pending?"":"manager");
function switchRole(next:string){
sessionStorage.removeItem("xingbang-access-token");
sessionStorage.setItem("xingbang-demo-role",next);
@@ -14,6 +14,7 @@ export default function IdentitySelector({identity,dark=false}:{identity:any;dar
return <label className={`xb-role-switch role-${role}${dark?" dark":""}`}>
<span>当前身份</span><ShieldCheck size={16}/>
<select aria-label="切换演示身份" value={role} onChange={event=>switchRole(event.target.value)}>
{pending&&<option value="" disabled>选择身份</option>}
<option value="manager">客户经理</option><option value="reviewer">评审员</option><option value="admin">配置管理员</option>
</select>
</label>;