fix(audit): 修复「退出登录」无日志(PC + 移动端)

现象:审计日志里 logout 记录数恒为 0,退出动作完全不可见。

根因有两处,缺一不可:

1. 移动端(App)根本没有发起过上报
   settings.vue 的 handleLogout 只做 removeStorageSync + reLaunch,
   一个请求都没发。而 uni.reLaunch 会销毁页面上下文、直接掐断未完成的
   uni.request —— 所以必须「先 await 上报、再清 token 与跳转」。

2. PC 端存在时序竞态
   axios 的请求拦截器在微任务里执行、现读 localStorage 取 token;
   而原实现同步清空 localStorage 并立刻 navigate,拦截器跑到时 token
   已经没了 → 请求不带 Authorization → 后端只能记成「未认证」。
   注:只改 AuthContext 不够,调用方 AdminLayout / ProfilePage 原来是
   `logout(); navigate(...)`,不等就跳转照样会掐断请求 —— 三处都得改。

改动:
- 移动端 handleLogout 改 async,await post("/auth/logout") 后再清 token
- AuthContext.logout 改 async(类型同步为 () => Promise<void>),
  先 await 上报再清 token;失败静默,绝不阻断退出
- AdminLayout / ProfilePage 两个调用点补 await
- 全部用 try-catch 兜住:断网/超时也照退不误(JWT 无状态,
  服务端本就不需要它成功)
This commit is contained in:
2026-09-21 13:47:31 +08:00
parent c3667fe00d
commit df3f914eb1
5 changed files with 46 additions and 16 deletions

View File

@ -38,6 +38,7 @@
<script setup>
import { ref, computed, onMounted } from "vue";
import { checkAppUpdate } from "../../utils/ota";
import { post } from "../../utils/request";
// ============================================================
// 缓存清理策略 —— 黑名单式:只删「明确登记过的业务缓存」
@ -167,15 +168,31 @@ async function handleCheckUpdate() {
await checkAppUpdate({ manual: true });
}
function handleLogout() {
async function handleLogout() {
// 退出是不可逆的(要重新输账号密码),按车间使用场景加一道确认防误触
uni.showModal({
title: "退出登录",
content: "退出后需要重新输入账号密码,确定退出吗?",
confirmText: "退出",
cancelText: "取消",
success: (res) => {
success: async (res) => {
if (!res.confirm) return;
// 🔴 必须【先 await 上报、再清 token】——两者顺序反了或不等,退出就留不下痕:
// 1) uni.reLaunch 会销毁页面上下文,直接掐断尚未发出的 uni.request;
// 2) 而 request.js 是在发送时才从 storage 读 access_token,
// 先清 storage 的话请求会不带 Authorization,后端只能记成「未认证」。
// 这里刻意 try/catch 兜住:上报失败(断网/超时)也绝不能挡住用户退出。
uni.showLoading({ title: "退出中...", mask: true });
try {
await post("/auth/logout");
} catch (e) {
// 静默:JWT 无状态,服务端本就不需要它成功
console.warn("[logout] 上报失败(不影响退出)", e);
} finally {
uni.hideLoading();
}
try {
uni.removeStorageSync("token");
uni.removeStorageSync("access_token");