213 lines
5.9 KiB
JavaScript
213 lines
5.9 KiB
JavaScript
/**
|
||
* API 签名生成工具 - 用于 192.168.101.213:9090 接口
|
||
*/
|
||
|
||
import md5 from 'blueimp-md5'
|
||
|
||
// 配置参数
|
||
const APP_KEY = 'psi'
|
||
const CLIENT_ID = 'psi'
|
||
const APP_SECRET = 'psi_api_sign_secret'
|
||
const SIGN_METHOD = 'md5'
|
||
|
||
// API基础地址
|
||
const BASE_URL = 'https://psi.api.buzhiyushu.cn'
|
||
|
||
/**
|
||
* 对参数键进行排序(与服务器Go代码 sortKeysWithIndex 完全一致)
|
||
* Go代码使用 < 进行字符串比较(按字节字典序)
|
||
*/
|
||
function sortKeys(keys) {
|
||
const indexPattern = /^(.+)\[(\d+)\](.*)$/
|
||
keys.sort((keyI, keyJ) => {
|
||
const ma = indexPattern.exec(keyI)
|
||
const mb = indexPattern.exec(keyJ)
|
||
if (ma && mb) {
|
||
const [, preA, idxA, sufA] = ma
|
||
const [, preB, idxB, sufB] = mb
|
||
if (preA !== preB) return preA < preB ? -1 : 1
|
||
const numA = parseInt(idxA, 10)
|
||
const numB = parseInt(idxB, 10)
|
||
if (numA !== numB) return numA - numB
|
||
return sufA < sufB ? -1 : 1
|
||
}
|
||
// 至少有一个不是带索引的键,使用 < 比较(与Go一致)
|
||
return keyI < keyJ ? -1 : (keyI > keyJ ? 1 : 0)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 计算签名(与服务器Go代码 calculateSign 一致)
|
||
* 1. 收集所有参数(跳过sign)
|
||
* 2. 按key排序
|
||
* 3. 拼接 key1=value1&key2=value2...
|
||
* 4. 前后加 secret
|
||
* 5. MD5并转大写
|
||
*/
|
||
function calculateSign(params) {
|
||
// 收集参数,跳过sign
|
||
const entries = []
|
||
for (const key in params) {
|
||
if (key === 'sign') continue
|
||
const value = params[key]
|
||
if (value === undefined || value === null) continue
|
||
entries.push({ key, value: String(value) })
|
||
}
|
||
|
||
// 排序entries数组(按key)
|
||
entries.sort((a, b) => {
|
||
const indexPattern = /^(.+)\[(\d+)\](.*)$/
|
||
const ma = indexPattern.exec(a.key)
|
||
const mb = indexPattern.exec(b.key)
|
||
if (ma && mb) {
|
||
const [, preA, idxA, sufA] = ma
|
||
const [, preB, idxB, sufB] = mb
|
||
if (preA !== preB) return preA < preB ? -1 : 1
|
||
const numA = parseInt(idxA, 10)
|
||
const numB = parseInt(idxB, 10)
|
||
if (numA !== numB) return numA - numB
|
||
return sufA < sufB ? -1 : 1
|
||
}
|
||
return a.key < b.key ? -1 : (a.key > b.key ? 1 : 0)
|
||
})
|
||
|
||
// 拼接签名字符串
|
||
const signStr = entries.map(e => `${e.key}=${e.value}`).join('&')
|
||
console.log('calculateSign 签名字符串:', signStr)
|
||
|
||
// 前后加secret
|
||
const signWithSecret = APP_SECRET + signStr + APP_SECRET
|
||
console.log('calculateSign 加secret后:', signWithSecret)
|
||
|
||
const sign = md5(signWithSecret).toUpperCase()
|
||
console.log('calculateSign 生成的sign:', sign)
|
||
|
||
return sign
|
||
}
|
||
|
||
/**
|
||
* 生成带签名的URL(带系统参数:app_key, client_id, timestamp, sign_method)
|
||
* 用于仓库列表等接口
|
||
*/
|
||
function generateSignedUrl(baseUrl, params = {}) {
|
||
const timestamp = Math.floor(Date.now() / 1000).toString()
|
||
|
||
// 构建完整参数(业务参数 + 系统参数)
|
||
const allParams = { ...params }
|
||
delete allParams.sign
|
||
allParams.app_key = APP_KEY
|
||
allParams.client_id = CLIENT_ID
|
||
allParams.timestamp = timestamp
|
||
allParams.sign_method = SIGN_METHOD
|
||
|
||
// 计算签名
|
||
const sign = calculateSign(allParams)
|
||
|
||
// 构建URL查询字符串(所有参数按key排序)
|
||
const keys = Object.keys(allParams)
|
||
sortKeys(keys)
|
||
const queryParts = keys.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(allParams[key])}`)
|
||
queryParts.push(`sign=${sign}`)
|
||
|
||
const separator = baseUrl.includes('?') ? '&' : '?'
|
||
return `${baseUrl}${separator}${queryParts.join('&')}`
|
||
}
|
||
|
||
/**
|
||
* 生成简单签名的URL(不带系统参数,只有业务参数签名)
|
||
* 用于货位列表等接口
|
||
*/
|
||
function generateSimpleSignedUrl(baseUrl, params = {}) {
|
||
// 只使用业务参数
|
||
const allParams = { ...params }
|
||
delete allParams.sign
|
||
|
||
// 计算签名(不带系统参数)
|
||
const sign = calculateSign(allParams)
|
||
|
||
// 构建URL查询字符串(所有参数按key排序)
|
||
const keys = Object.keys(allParams)
|
||
sortKeys(keys)
|
||
const queryParts = keys.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(allParams[key])}`)
|
||
queryParts.push(`sign=${sign}`)
|
||
|
||
const separator = baseUrl.includes('?') ? '&' : '?'
|
||
return `${baseUrl}${separator}${queryParts.join('&')}`
|
||
}
|
||
|
||
/**
|
||
* 获取token(从本地存储或登录接口)
|
||
*/
|
||
function getAuthToken() {
|
||
// 优先从本地存储获取
|
||
const token = uni.getStorageSync('psi_token')
|
||
if (token) return token
|
||
|
||
// 使用curl中的固定token(临时方案)
|
||
return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6MjU1LCJ1c2VybmFtZSI6IjE4OTA0MDU2ODAwIiwiYWJvdXRfaWQiOjE5NjUyNTQ3NzQzMjc1MzM1NzAsImlzcyI6InBzaS1zeXN0ZW0iLCJleHAiOjE3ODA0NjkxNzMsIm5iZiI6MTc4MDM4Mjc3MywiaWF0IjoxNzgwMzgyNzczfQ.FQkDiDzNj0tN3ct0RJCnL7s03vstf7uMmeXqqek8sME'
|
||
}
|
||
|
||
/**
|
||
* 获取仓库列表
|
||
*/
|
||
export function getWarehouseList(params = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
const url = generateSignedUrl(`${BASE_URL}/api/warehouse/list`, params)
|
||
console.log('请求仓库列表URL:', url)
|
||
uni.request({
|
||
url: url,
|
||
method: 'GET',
|
||
header: {
|
||
'Authorization': 'Bearer ' + getAuthToken()
|
||
},
|
||
success: (res) => {
|
||
console.log('仓库列表请求成功:', res.statusCode, res.data)
|
||
if (res.statusCode === 200) {
|
||
resolve(res.data)
|
||
} else {
|
||
reject(new Error(`请求失败: ${res.statusCode}`))
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('仓库列表请求失败:', err)
|
||
reject(err)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取货位列表
|
||
*/
|
||
export function getLocationList(params = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
const url = generateSignedUrl(`${BASE_URL}/api/location/list`, params)
|
||
console.log('请求货位列表URL:', url)
|
||
uni.request({
|
||
url: url,
|
||
method: 'GET',
|
||
header: {
|
||
'Authorization': 'Bearer ' + getAuthToken()
|
||
},
|
||
success: (res) => {
|
||
console.log('货位列表请求成功:', res.statusCode, res.data)
|
||
if (res.statusCode === 200) {
|
||
resolve(res.data)
|
||
} else {
|
||
reject(new Error(`请求失败: ${res.statusCode}`))
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('货位列表请求失败:', err)
|
||
reject(err)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
export default {
|
||
getWarehouseList,
|
||
getLocationList,
|
||
generateSignedUrl
|
||
}
|