Merge branch 'release/test' of http://8.163.30.128:10082/html/pda-overseas-uniapp into release/test

This commit is contained in:
2026-09-29 20:52:46 +08:00
14 changed files with 135 additions and 152 deletions
+3 -3
View File
@@ -45,12 +45,12 @@ function overseasUrl(path) {
const nativeRuntime = typeof plus !== 'undefined'
|| (typeof location !== 'undefined' && location.protocol === 'file:')
if (nativeRuntime && !/^https?:\/\//i.test(base)) {
const error = new Error('海外服务地址未配置:App 必须设置绝对 HTTP(S) 地址 VITE_OVERSEAS_API_BASE_URL')
error.code = 'PDA_OVERSEAS_API_BASE_REQUIRED'
const error = new Error('海外服务地址未配置:App 必须设置绝对 HTTP(S) 地址 VITE_API_BASE_URL')
error.code = 'PDA_API_BASE_REQUIRED'
throw error
}
const value = `/${String(path || '').replace(/^\/+/, '')}`
const normalized = /\/(?:api|overseas-api)$/.test(base)
const normalized = /\/api$/.test(base)
? value.replace(/^\/api(?=\/)/, '')
: value
return `${base}${normalized}`
+7 -24
View File
@@ -1,31 +1,14 @@
const runtimeEnvironment = import.meta.env || {}
const configuredAuthApiRoot = runtimeEnvironment.VITE_AUTH_API_BASE_URL || ''
const configuredOverseasApiRoot = runtimeEnvironment.VITE_OVERSEAS_API_BASE_URL || ''
const configuredApiRoot = runtimeEnvironment.VITE_API_BASE_URL || ''
const development = Boolean(runtimeEnvironment.DEV)
let selectedAuthApiRoot = configuredAuthApiRoot || '/api'
let selectedOverseasApiRoot = configuredOverseasApiRoot || '/overseas-api'
let selectedApiRoot = configuredApiRoot || '/api'
// #ifdef H5
selectedApiRoot = runtimeEnvironment.VITE_H5_API_BASE_URL || '/api'
// #endif
const nativeRuntime = typeof plus !== 'undefined'
|| (typeof location !== 'undefined' && location.protocol === 'file:')
// H5 must stay same-origin and use Vite's CORS-safe proxy; App builds keep the absolute API roots above.
// #ifdef H5
selectedAuthApiRoot = runtimeEnvironment.VITE_H5_AUTH_API_BASE_URL || '/api'
selectedOverseasApiRoot = runtimeEnvironment.VITE_H5_OVERSEAS_API_BASE_URL || '/overseas-api'
// #endif
// HBuilderX/App 基座 can expose a file:// location; never allow a relative API
// root there, even if a stale H5 bundle or compiler flag was reused.
if (nativeRuntime) {
selectedAuthApiRoot = configuredAuthApiRoot || selectedAuthApiRoot
// Native file:// builds cannot use the H5-only /overseas-api Vite proxy.
// Keep the missing value explicit so the request layer can report a useful
// configuration error instead of surfacing an opaque uploadFile:fail.
selectedOverseasApiRoot = configuredOverseasApiRoot || ''
}
export const API_ROOT = String(selectedAuthApiRoot).replace(/\/$/, '')
export const OVERSEAS_API_ROOT = String(selectedOverseasApiRoot).replace(/\/$/, '')
if (nativeRuntime) selectedApiRoot = configuredApiRoot || ''
export const API_ROOT = String(selectedApiRoot).replace(/\/$/, '')
export const DEV_LOGIN = Object.freeze({
username: development ? String(runtimeEnvironment.VITE_PDA_DEV_USERNAME || '') : '',
password: development ? String(runtimeEnvironment.VITE_PDA_DEV_PASSWORD || '') : ''
+33 -34
View File
@@ -1,5 +1,5 @@
import { reactive } from 'vue'
import { OVERSEAS_API_ROOT } from '../config/runtime.js'
import { API_ROOT } from '../config/runtime.js'
import { resetReceivingSession } from './receiving.js'
import { resetFulfillmentSession } from './fulfillment.js'
import { resetWarehouseWork } from './warehouse-work.js'
@@ -9,7 +9,7 @@ export const PDA_ROLE = 'OVERSEAS_WAREHOUSE_PDA_OPERATOR'
export const PDA_AUTHORITY = 'lastleg:pda:operate'
export const session = reactive({
currentUser: null, token: '', domesticToken: '', refreshToken: '', tenantId: '', apiBase: OVERSEAS_API_ROOT,
currentUser: null, token: '', refreshToken: '', tenantId: '', apiBase: API_ROOT,
authorization: { roles: [], authorities: [] }
})
@@ -47,7 +47,7 @@ function includesClaim(values, expected) {
}
function requireActiveClaims(claims, label) {
if (claims.exp && Number(claims.exp) * 1000 <= Date.now()) throw new Error(`${label}已过期`)
if (!Number.isFinite(Number(claims.exp)) || Number(claims.exp) * 1000 <= Date.now()) throw new Error(`${label}已过期`)
if (claims.app_code !== 'PLATFORM' || claimId(claims, 'tenant_id') !== '0') {
throw new Error('当前账号不属于平台仓库作业端')
}
@@ -67,25 +67,20 @@ function requireAuthorizationSnapshot(authorization) {
return { roles, authorities }
}
export function validatePdaTokenPair(accessToken, overseasAccessToken, authorization) {
const domestic = decodeAccessToken(accessToken)
const overseas = decodeAccessToken(overseasAccessToken)
requireActiveClaims(domestic, '登录状态')
requireActiveClaims(overseas, '海外作业令牌')
for (const claim of ['user_id', 'membership_id', 'tenant_id', 'app_code', 'overseas_warehouse_id']) {
if (claimId(domestic, claim) !== claimId(overseas, claim)) throw new Error('国内与海外令牌作业主体不一致')
}
const warehouseId = claimId(overseas, 'overseas_warehouse_id')
export function validatePdaToken(accessToken, authorization) {
const claims = decodeAccessToken(accessToken)
requireActiveClaims(claims, '登录状态')
const warehouseId = claimId(claims, 'overseas_warehouse_id')
if (!/^\d+$/.test(warehouseId) || warehouseId === '0') throw new Error('海外PDA账号未绑定有效作业仓库')
return { domestic, overseas, warehouseId, authorization: requireAuthorizationSnapshot(authorization) }
return { claims, warehouseId, authorization: requireAuthorizationSnapshot(authorization) }
}
function userFromClaims(pair, saved = {}) {
const savedWarehouseId = String(saved?.warehouseId || '')
const sameWarehouse = savedWarehouseId === pair.warehouseId
return {
id: claimId(pair.domestic, 'user_id'),
name: pair.domestic.username || '仓库作业员',
id: claimId(pair.claims, 'user_id'),
name: pair.claims.username || '仓库作业员',
role: PDA_ROLE,
roles: [...pair.authorization.roles],
warehouse: sameWarehouse && saved.warehouseName ? saved.warehouseName : '',
@@ -102,9 +97,9 @@ function storageAvailable() {
function persistSession() {
if (!storageAvailable()) return
uni.setStorageSync(SESSION_KEY, {
version: 2,
currentUser: session.currentUser,
token: session.token,
domesticToken: session.domesticToken,
refreshToken: session.refreshToken,
tenantId: session.tenantId,
apiBase: session.apiBase,
@@ -116,10 +111,9 @@ export function signIn(account, warehouse, options = {}) {
session.currentUser = { id: String(account.id), name: account.name, role: account.role, roles: account.roles || [], warehouse,
warehouseId: account.warehouseId || null, warehouseCode: account.warehouseCode || '', warehouseName: account.warehouseName || '' }
session.token = options.token || account.token || ''
session.domesticToken = options.domesticToken || account.domesticToken || ''
session.refreshToken = options.refreshToken || account.refreshToken || ''
session.tenantId = options.tenantId || account.tenantId || ''
session.apiBase = options.apiBase || account.apiBase || OVERSEAS_API_ROOT
session.apiBase = options.apiBase || account.apiBase || API_ROOT
session.authorization = {
roles: [...(options.authorization?.roles || [])],
authorities: [...(options.authorization?.authorities || [])]
@@ -130,28 +124,33 @@ export function signIn(account, warehouse, options = {}) {
export function signInWithTokens(tokens) {
if (!tokens?.accessToken) throw new Error('登录响应缺少 accessToken')
if (!tokens?.overseasAccessToken) throw new Error('登录响应缺少 overseasAccessToken')
const pair = validatePdaTokenPair(tokens.accessToken, tokens.overseasAccessToken, tokens)
const pair = validatePdaToken(tokens.accessToken, tokens)
const user = userFromClaims(pair)
return signIn(user, user.warehouse, {
token: tokens.overseasAccessToken,
domesticToken: tokens.accessToken,
token: tokens.accessToken,
refreshToken: tokens.refreshToken || '',
tenantId: claimId(pair.domestic, 'tenant_id'),
tenantId: claimId(pair.claims, 'tenant_id'),
authorization: pair.authorization
})
}
export function replaceTokens(tokens) {
if (!tokens?.accessToken || !tokens?.overseasAccessToken) throw new Error('刷新响应缺少双区域访问令牌')
const pair = validatePdaTokenPair(tokens.accessToken, tokens.overseasAccessToken, tokens)
if (!tokens?.accessToken) throw new Error('刷新响应缺少访问令牌')
const pair = validatePdaToken(tokens.accessToken, tokens)
if (session.currentUser) {
const previousClaims = decodeAccessToken(session.token)
const identityChanged = ['user_id', 'membership_id', 'tenant_id', 'app_code', 'overseas_warehouse_id']
.some((name) => claimId(previousClaims, name) !== claimId(pair.claims, name))
if (identityChanged || String(session.currentUser.id) !== claimId(pair.claims, 'user_id') || String(session.currentUser.warehouseId) !== pair.warehouseId) {
throw new Error('作业主体或绑定仓库已变更,请重新登录')
}
}
const user = userFromClaims(pair, session.currentUser)
const nextRefreshToken = tokens.refreshToken || session.refreshToken
// Validate the complete replacement before mutating reactive or persisted session state.
session.domesticToken = tokens.accessToken
session.token = tokens.overseasAccessToken
session.token = tokens.accessToken
session.refreshToken = nextRefreshToken
session.tenantId = claimId(pair.domestic, 'tenant_id')
session.tenantId = claimId(pair.claims, 'tenant_id')
session.currentUser = user
session.authorization = pair.authorization
persistSession()
@@ -174,15 +173,16 @@ export function applyWarehouseProfile(profile) {
export function restoreSession() {
if (!storageAvailable()) return false
const saved = uni.getStorageSync(SESSION_KEY)
if (!saved?.token || !saved?.domesticToken || !saved?.refreshToken || !saved?.currentUser) {
if (!saved?.token || !saved?.refreshToken || !saved?.currentUser || saved.version !== 2) {
signOut()
return false
}
try {
const pair = validatePdaTokenPair(saved.domesticToken, saved.token, saved.authorization)
const pair = validatePdaToken(saved.token, saved.authorization)
if (String(saved.currentUser.id) !== claimId(pair.claims, 'user_id') || String(saved.currentUser.warehouseId) !== pair.warehouseId) throw new Error('缓存作业主体不一致')
Object.assign(session, saved, {
currentUser: userFromClaims(pair, saved.currentUser), tenantId: claimId(pair.domestic, 'tenant_id'),
apiBase: OVERSEAS_API_ROOT, authorization: pair.authorization
currentUser: userFromClaims(pair, saved.currentUser), tenantId: claimId(pair.claims, 'tenant_id'),
apiBase: API_ROOT, authorization: pair.authorization
})
return true
} catch (_) {
@@ -194,10 +194,9 @@ export function restoreSession() {
export function signOut() {
session.currentUser = null
session.token = ''
session.domesticToken = ''
session.refreshToken = ''
session.tenantId = ''
session.apiBase = OVERSEAS_API_ROOT
session.apiBase = API_ROOT
session.authorization = { roles: [], authorities: [] }
if (typeof uni !== 'undefined' && typeof uni.removeStorageSync === 'function') uni.removeStorageSync(SESSION_KEY)
resetReceivingSession()