|
|
@@ -19,11 +19,30 @@ function assertContext(context) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+export function normalizeMarketEntityId(id, { rejectPathSeparators = false } = {}) {
|
|
|
+ if (id == null) throw new Error('业务实体 ID 无效')
|
|
|
+ const normalized = String(id).trim()
|
|
|
+ if (
|
|
|
+ !normalized ||
|
|
|
+ normalized.length > 64 ||
|
|
|
+ normalized === '.' ||
|
|
|
+ normalized === '..' ||
|
|
|
+ /[\u0000-\u001f\u007f-\u009f]/.test(normalized) ||
|
|
|
+ (rejectPathSeparators && /[\\/]/.test(normalized))
|
|
|
+ ) {
|
|
|
+ throw new Error('业务实体 ID 无效')
|
|
|
+ }
|
|
|
+ return normalized
|
|
|
+}
|
|
|
+
|
|
|
export function buildMarketResourcePath(context, id) {
|
|
|
assertContext(context)
|
|
|
const base = `/mini/market/${context.marketRole}/${RESOURCE_BY_MODULE[context.module]}`
|
|
|
- const normalizedId = String(id ?? '').trim()
|
|
|
- return normalizedId ? `${base}/${encodeURIComponent(normalizedId)}` : base
|
|
|
+ if (arguments.length < 2) return base
|
|
|
+ const normalizedId = normalizeMarketEntityId(id, {
|
|
|
+ rejectPathSeparators: context.module === 'presign'
|
|
|
+ })
|
|
|
+ return `${base}/${encodeURIComponent(normalizedId)}`
|
|
|
}
|
|
|
|
|
|
export function buildMarketFormOptionsPath(context) {
|
|
|
@@ -36,11 +55,14 @@ export function buildMarketAttachmentPath(context) {
|
|
|
return `/mini/market/${context.marketRole}/${context.module}/attachments`
|
|
|
}
|
|
|
|
|
|
-export function buildMarketActionPath(context, action, id) {
|
|
|
+export function buildMarketActionPath(context, action, id, data = {}) {
|
|
|
assertContext(context)
|
|
|
const roleBase = `/mini/market/${context.marketRole}`
|
|
|
- const encodedId = encodeURIComponent(String(id ?? '').trim())
|
|
|
- if (context.module === 'presign' && encodedId) {
|
|
|
+ const encodeId = () => encodeURIComponent(normalizeMarketEntityId(id, {
|
|
|
+ rejectPathSeparators: context.module === 'presign'
|
|
|
+ }))
|
|
|
+ if (context.module === 'presign') {
|
|
|
+ const encodedId = encodeId()
|
|
|
if (action === 'delete') return buildMarketResourcePath(context, id)
|
|
|
if (action === 'convert') return `${buildMarketResourcePath(context, id)}/convert`
|
|
|
if (action === 'create-transfer') {
|
|
|
@@ -56,15 +78,27 @@ export function buildMarketActionPath(context, action, id) {
|
|
|
}
|
|
|
if (context.module === 'payment') {
|
|
|
if (action === 'register-transfer') return `${roleBase}/payment-transfers`
|
|
|
- if (['edit-transfer', 'submit', 'withdraw'].includes(action) && encodedId) {
|
|
|
+ if (['edit-transfer', 'submit', 'withdraw'].includes(action)) {
|
|
|
+ const encodedId = encodeId()
|
|
|
const suffix = action === 'edit-transfer' ? '' : `/${action}`
|
|
|
return `${roleBase}/payment-transfers/${encodedId}${suffix}`
|
|
|
}
|
|
|
}
|
|
|
- if (context.module === 'sample' && ['storage-cert', 'insurance'].includes(action) && encodedId) {
|
|
|
+ if (context.module === 'protocol' && ['audit', 'delete'].includes(action)) {
|
|
|
+ encodeId()
|
|
|
+ const source = String(data?.source || '').trim()
|
|
|
+ if (!['cell_storage', 'research_donation'].includes(source)) {
|
|
|
+ throw new Error('协议业务来源无效')
|
|
|
+ }
|
|
|
+ const suffix = action === 'audit' ? '/audit' : ''
|
|
|
+ return `${buildMarketResourcePath(context, id)}${suffix}?source=${encodeURIComponent(source)}`
|
|
|
+ }
|
|
|
+ if (context.module === 'sample' && ['storage-cert', 'insurance'].includes(action)) {
|
|
|
+ encodeId()
|
|
|
return `${buildMarketResourcePath(context, id)}/${action}`
|
|
|
}
|
|
|
- if (context.module === 'freight' && ['link', 'unlink'].includes(action) && encodedId) {
|
|
|
+ if (context.module === 'freight' && ['link', 'unlink'].includes(action)) {
|
|
|
+ encodeId()
|
|
|
return `${buildMarketResourcePath(context, id)}/${action}`
|
|
|
}
|
|
|
throw new Error('不支持的市场业务操作')
|
|
|
@@ -97,9 +131,14 @@ export async function fetchMarketFormOptions(context, params, { signal } = {}) {
|
|
|
|
|
|
export async function saveMarketRecord(context, { id, data }, { signal } = {}) {
|
|
|
const { request } = await import('../utils/http.js')
|
|
|
+ const normalizedId = id == null || id === ''
|
|
|
+ ? ''
|
|
|
+ : normalizeMarketEntityId(id, { rejectPathSeparators: context?.module === 'presign' })
|
|
|
return request({
|
|
|
- url: buildMarketResourcePath(context, id),
|
|
|
- method: id ? 'PUT' : 'POST',
|
|
|
+ url: normalizedId
|
|
|
+ ? buildMarketResourcePath(context, normalizedId)
|
|
|
+ : buildMarketResourcePath(context),
|
|
|
+ method: normalizedId ? 'PUT' : 'POST',
|
|
|
data,
|
|
|
signal
|
|
|
})
|
|
|
@@ -114,10 +153,13 @@ export function marketActionMethod(action) {
|
|
|
|
|
|
export async function performMarketAction(context, action, { id, data } = {}, { signal } = {}) {
|
|
|
const { request } = await import('../utils/http.js')
|
|
|
+ const requestData = context.module === 'protocol' && ['audit', 'delete'].includes(action)
|
|
|
+ ? Object.fromEntries(Object.entries(data || {}).filter(([key]) => key !== 'source'))
|
|
|
+ : data
|
|
|
return request({
|
|
|
- url: buildMarketActionPath(context, action, id),
|
|
|
+ url: buildMarketActionPath(context, action, id, data),
|
|
|
method: marketActionMethod(action),
|
|
|
- data,
|
|
|
+ data: action === 'delete' && context.module === 'protocol' ? undefined : requestData,
|
|
|
signal
|
|
|
})
|
|
|
}
|