fix(miniapp): 提现/财务接口不再用 1001 演示身份,修复 403

- getUserProfile/getUserWithdrawals/getUserFinanceRecords 去掉 userId=1001 默认值,改由 normalizeUserId 解析登录身份

- withdraw.vue / withdraw-records.vue / workspace.vue 调用改为 normalizeUserId(),不再硬编码 1001

- normalizeUserId 将 1001 视为演示占位符:登录态下传 0 由 token 识别,未登录才兜底 1001

- request.js 遇 403 身份校验失败时清理错配登录态并提示重新登录(其它 403 权限拒绝不受影响)

- 登录态闸门补齐用例(108 套件/714 用例全绿)
这个提交包含在:
xcbyy
2026-10-21 11:38:26 +08:00
父节点 9217dfcf11
当前提交 87cb6a6374
共有 6 个文件被更改,包括 45 次插入 和 12 次删除

查看文件

@@ -558,13 +558,16 @@ export async function viewPost(id) {
*/ */
export function normalizeUserId(uid) { export function normalizeUserId(uid) {
let target = uid; let target = uid;
if (!target && target !== 0) { // 1001 是历史演示账号默认值,不代表真实登录用户:一律回到本地存储解析
const isPlaceholder = target === undefined || target === null || target === '' || Number(target) === 1001;
if (isPlaceholder) {
try { try {
target = uni.getStorageSync('userId'); target = uni.getStorageSync('userId');
} catch (_) {} } catch (_) {}
} }
const n = Number(target); const n = Number(target);
const valid = !isNaN(n) && n > 0; // 1001 仅作为"未登录演示"的兜底,登录态下不作为真实身份
const valid = !isNaN(n) && n > 0 && n !== 1001;
// 已登录(存在会话 token)时身份以服务端 token 为准:若本地 userId 缺失则传 0, // 已登录(存在会话 token)时身份以服务端 token 为准:若本地 userId 缺失则传 0,
// 避免"旧 token 属主 + 声明的 userId"不一致被服务端判定为以他人身份操作(403)。 // 避免"旧 token 属主 + 声明的 userId"不一致被服务端判定为以他人身份操作(403)。
try { try {
@@ -577,7 +580,7 @@ export function normalizeUserId(uid) {
/** /**
* 获取用户个人资料与钱包资产 (真实读取 MySQL users 表) * 获取用户个人资料与钱包资产 (真实读取 MySQL users 表)
*/ */
export async function getUserProfile(userId = 1001) { export async function getUserProfile(userId) {
const safeId = normalizeUserId(userId); const safeId = normalizeUserId(userId);
try { try {
const res = await http.get(`/users/${safeId}`); const res = await http.get(`/users/${safeId}`);
@@ -653,7 +656,7 @@ export async function withdrawCommission(arg1, arg2, arg3 = {}) {
/** /**
* 查询用户提现记录与审批状态 (待审核/处理中/已打款/已驳回) * 查询用户提现记录与审批状态 (待审核/处理中/已打款/已驳回)
*/ */
export async function getUserWithdrawals(userId = 1001) { export async function getUserWithdrawals(userId) {
const safeId = normalizeUserId(userId); const safeId = normalizeUserId(userId);
try { try {
const res = await http.get(`/users/${safeId}/withdrawals`); const res = await http.get(`/users/${safeId}/withdrawals`);
@@ -667,7 +670,7 @@ export async function getUserWithdrawals(userId = 1001) {
/** /**
* 查询用户财务收支流水 * 查询用户财务收支流水
*/ */
export async function getUserFinanceRecords(userId = 1001) { export async function getUserFinanceRecords(userId) {
const safeId = normalizeUserId(userId); const safeId = normalizeUserId(userId);
try { try {
const res = await http.get(`/users/${safeId}/finance-records`); const res = await http.get(`/users/${safeId}/finance-records`);

查看文件

@@ -75,6 +75,7 @@ export function request(options = {}) {
bizErr.code = res.data.code; bizErr.code = res.data.code;
bizErr.statusCode = res.statusCode; bizErr.statusCode = res.statusCode;
if (res.data.code === 40100) handleAuthExpired(); if (res.data.code === 40100) handleAuthExpired();
else if (res.data.code === 40300 && /身份校验失败|无权以他人身份/.test(res.data.message || '')) handleAuthExpired();
reject(bizErr); reject(bizErr);
} }
} else { } else {
@@ -86,6 +87,7 @@ export function request(options = {}) {
httpErr.statusCode = res.statusCode; httpErr.statusCode = res.statusCode;
httpErr.code = (res.data && res.data.code) || res.statusCode; httpErr.code = (res.data && res.data.code) || res.statusCode;
if (res.statusCode === 401) handleAuthExpired(); if (res.statusCode === 401) handleAuthExpired();
else if (res.statusCode === 403 && /身份校验失败|无权以他人身份/.test(errMsg)) handleAuthExpired();
reject(httpErr); reject(httpErr);
} }
}, },

查看文件

@@ -490,7 +490,8 @@ import {
updateExpertService, updateExpertService,
deleteExpertService, deleteExpertService,
uploadVoiceIntro, uploadVoiceIntro,
getUserWithdrawals getUserWithdrawals,
normalizeUserId
} from '@/common/api.js'; } from '@/common/api.js';
const currentThemeKey = ref(getCurrentThemeKey()); const currentThemeKey = ref(getCurrentThemeKey());
@@ -801,7 +802,7 @@ const goToWithdrawPage = () => {
const openWithdrawRecords = async () => { const openWithdrawRecords = async () => {
try { try {
const list = await getUserWithdrawals(1001); const list = await getUserWithdrawals(normalizeUserId());
withdrawRecords.value = list || []; withdrawRecords.value = list || [];
} catch (_) {} } catch (_) {}
showWithdrawRecordsModal.value = true; showWithdrawRecordsModal.value = true;

查看文件

@@ -180,7 +180,7 @@ import { onShow } from '@dcloudio/uni-app';
import { ICONS } from '@/common/icons.js'; import { ICONS } from '@/common/icons.js';
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js'; import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
import { getNavBarInfo } from '@/common/nav.js'; import { getNavBarInfo } from '@/common/nav.js';
import { getUserWithdrawals } from '@/common/api.js'; import { getUserWithdrawals, normalizeUserId } from '@/common/api.js';
const currentThemeKey = ref(getCurrentThemeKey()); const currentThemeKey = ref(getCurrentThemeKey());
const navInfo = ref(getNavBarInfo()); const navInfo = ref(getNavBarInfo());
@@ -272,7 +272,7 @@ const handleCopyOrderNo = (orderNo) => {
const loadWithdrawals = async () => { const loadWithdrawals = async () => {
try { try {
const list = await getUserWithdrawals(1001); const list = await getUserWithdrawals(normalizeUserId());
if (Array.isArray(list)) { if (Array.isArray(list)) {
allRecords.value = list; allRecords.value = list;
let successSum = 0; let successSum = 0;

查看文件

@@ -189,7 +189,8 @@ import {
getUserProfile, getUserProfile,
withdrawCommission, withdrawCommission,
getUserWithdrawals, getUserWithdrawals,
getPlatformConfigs getPlatformConfigs,
normalizeUserId
} from '@/common/api.js'; } from '@/common/api.js';
const currentThemeKey = ref(getCurrentThemeKey()); const currentThemeKey = ref(getCurrentThemeKey());
@@ -281,7 +282,7 @@ const handleFillAllCommission = () => {
const loadPageData = async () => { const loadPageData = async () => {
try { try {
const profile = await getUserProfile(1001); const profile = await getUserProfile(normalizeUserId());
if (profile && profile.commission !== undefined) { if (profile && profile.commission !== undefined) {
userCommission.value = String(profile.commission); userCommission.value = String(profile.commission);
if (profile.nickname) { if (profile.nickname) {
@@ -298,7 +299,7 @@ const loadPageData = async () => {
} catch (_) {} } catch (_) {}
try { try {
const records = await getUserWithdrawals(1001); const records = await getUserWithdrawals(normalizeUserId());
if (Array.isArray(records)) { if (Array.isArray(records)) {
withdrawalsList.value = records; withdrawalsList.value = records;
let successSum = 0; let successSum = 0;

查看文件

@@ -26,4 +26,30 @@ describe('第九十批登录态一致性闸门', () => {
expect(api).not.toContain('if (n === 101) return 2006;'); expect(api).not.toContain('if (n === 101) return 2006;');
expect(api).toContain('if (token) return valid ? n : 0;'); expect(api).toContain('if (token) return valid ? n : 0;');
}); });
it('1001 演示默认值不作为真实身份(登录态下回退 token 识别)', () => {
expect(api).toContain('Number(target) === 1001');
expect(api).toContain('n !== 1001');
expect(api).not.toContain('getUserWithdrawals(userId = 1001)');
expect(api).not.toContain('getUserFinanceRecords(userId = 1001)');
expect(api).not.toContain('getUserProfile(userId = 1001)');
});
it('提现/财务页面不再硬编码 1001 查询本人数据', () => {
for (const rel of [
'pwuniapp/pages/user/withdraw.vue',
'pwuniapp/pages/user/withdraw-records.vue',
'pwuniapp/pages/expert/workspace.vue',
]) {
const src = fs.readFileSync(path.join(root, rel), 'utf-8');
expect(src).not.toContain('getUserWithdrawals(1001)');
expect(src).not.toContain('getUserProfile(1001)');
}
});
it('请求层遇身份校验失败 403 时清理错配登录态并提示重新登录', () => {
const request = fs.readFileSync(path.join(root, 'pwuniapp/common/request.js'), 'utf-8');
expect(request).toContain('40300 && /身份校验失败|无权以他人身份/');
expect(request).toContain('res.statusCode === 403 && /身份校验失败|无权以他人身份/');
});
}); });