import { moduleResourceName, normalizeMarketContext } from '../market/context-contract.js' import { getMarketModule } from '../market/module-registry.js' function assertContext(context) { return normalizeMarketContext(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) { const normalizedContext = assertContext(context) const base = `/mini/market/${normalizedContext.marketRole}/${moduleResourceName(normalizedContext.module)}` if (arguments.length < 2) return base const normalizedId = normalizeMarketEntityId(id, { rejectPathSeparators: normalizedContext.module === 'presign' }) return `${base}/${encodeURIComponent(normalizedId)}` } export function buildMarketFormOptionsPath(context) { const normalizedContext = assertContext(context) return `/mini/market/${normalizedContext.marketRole}/${moduleResourceName(normalizedContext.module)}/form-options` } export function buildMarketAttachmentPath(context) { const normalizedContext = assertContext(context) return `/mini/market/${normalizedContext.marketRole}/${normalizedContext.module}/attachments` } export function buildMarketAttachmentDownloadPath(context) { return `${buildMarketAttachmentPath(context)}/download-url` } export function buildMarketActionPath(context, action, id, data = {}) { const normalizedContext = assertContext(context) const roleBase = `/mini/market/${normalizedContext.marketRole}` const encodeId = () => encodeURIComponent(normalizeMarketEntityId(id, { rejectPathSeparators: normalizedContext.module === 'presign' })) if (normalizedContext.module === 'presign') { const encodedId = encodeId() if (action === 'delete') return buildMarketResourcePath(context, id) if (action === 'convert') return `${buildMarketResourcePath(context, id)}/convert` if (action === 'create-transfer') { return `${buildMarketResourcePath(context, id)}/transfers` } if (['edit-presign-transfer', 'delete-transfer'].includes(action)) { return `${roleBase}/presign-transfers/${encodedId}` } if (['submit-transfer', 'withdraw-transfer'].includes(action)) { const suffix = action === 'submit-transfer' ? 'submit' : 'withdraw' return `${roleBase}/presign-transfers/${encodedId}/${suffix}` } } if (normalizedContext.module === 'payment') { if (action === 'register-transfer') return `${roleBase}/payment-transfers` if (['edit-transfer', 'delete-payment-transfer', 'submit', 'withdraw'].includes(action)) { const encodedId = encodeId() const suffix = ['edit-transfer', 'delete-payment-transfer'].includes(action) ? '' : `/${action}` return `${roleBase}/payment-transfers/${encodedId}${suffix}` } } if (normalizedContext.module === 'protocol' && action === 'delete') { encodeId() const source = String(data?.source || '').trim() if (!['cell_storage', 'research_donation'].includes(source)) { throw new Error('协议业务来源无效') } return `${buildMarketResourcePath(context, id)}?source=${encodeURIComponent(source)}` } if (normalizedContext.module === 'freight') { if (action === 'delete') return buildMarketResourcePath(context, id) if (['link', 'unlink'].includes(action)) { encodeId() return `${buildMarketResourcePath(context, id)}/${action}` } } throw new Error('不支持的市场业务操作') } export async function fetchMarketList(context, params, { signal } = {}) { const { request } = await import('../utils/http.js') return request({ url: buildMarketResourcePath(context), method: 'GET', data: params, signal }) } export async function fetchMarketDetail(context, id, { signal, params } = {}) { const { request } = await import('../utils/http.js') return request({ url: buildMarketResourcePath(context, id), method: 'GET', data: params, signal }) } export async function fetchMarketFormOptions(context, params, { signal } = {}) { const { request } = await import('../utils/http.js') return request({ url: buildMarketFormOptionsPath(context), method: 'GET', data: params, signal }) } export async function fetchMarketProtocolCustomers(context, params, { signal } = {}) { const { request } = await import('../utils/http.js') const normalizedContext = assertContext(context) if (normalizedContext.module !== 'protocol') { throw new Error('仅协议模块支持客户档案检索') } return request({ url: `${buildMarketResourcePath(normalizedContext)}/customers`, method: 'GET', data: params, signal }) } export async function saveMarketRecord(context, { id, data }, { signal } = {}) { const { request } = await import('../utils/http.js') const normalizedContext = assertContext(context) const normalizedId = id == null || id === '' ? '' : normalizeMarketEntityId(id, { rejectPathSeparators: normalizedContext.module === 'presign' }) return request({ url: normalizedId ? buildMarketResourcePath(context, normalizedId) : buildMarketResourcePath(context), method: normalizedId ? 'PUT' : 'POST', data, signal }) } export function marketActionMethod(action) { if (['delete', 'delete-transfer', 'delete-payment-transfer'].includes(action)) return 'DELETE' return ['edit-transfer', 'edit-presign-transfer'].includes(action) ? 'PUT' : 'POST' } export async function performMarketAction(context, action, { id, data } = {}, { signal } = {}) { const { request } = await import('../utils/http.js') const requestData = context.module === 'protocol' && action === 'delete' ? Object.fromEntries(Object.entries(data || {}).filter(([key]) => key !== 'source')) : data const omitBody = action === 'delete' && ['protocol', 'freight', 'presign'].includes(context.module) return request({ url: buildMarketActionPath(context, action, id, data), method: marketActionMethod(action), data: omitBody ? undefined : requestData, signal }) } export const deletePresignOrder = (context, id, options) => performMarketAction(context, 'delete', { id }, options) export const createPresignTransfer = (context, orderId, data, options) => performMarketAction(context, 'create-transfer', { id: orderId, data }, options) export const updatePresignTransfer = (context, transferId, data, options) => performMarketAction(context, 'edit-presign-transfer', { id: transferId, data }, options) export const deletePresignTransfer = (context, transferId, options) => performMarketAction(context, 'delete-transfer', { id: transferId }, options) export const submitPresignTransfer = (context, transferId, options) => performMarketAction(context, 'submit-transfer', { id: transferId }, options) export const withdrawPresignTransfer = (context, transferId, options) => performMarketAction(context, 'withdraw-transfer', { id: transferId }, options) export async function uploadMarketAttachment(context, file, { signal } = {}) { const { request } = await import('../utils/http.js') const data = new FormData() data.append('file', file) return request({ url: buildMarketAttachmentPath(context), method: 'POST', data, signal }) } export async function fetchMarketAttachmentDownloadUrl(context, data, { signal } = {}) { const { request } = await import('../utils/http.js') return request({ url: buildMarketAttachmentDownloadPath(context), method: 'POST', data, signal }) } export async function fetchFreightSampleOptions(context, params, { signal } = {}) { const { request } = await import('../utils/http.js') return request({ url: `${buildMarketResourcePath(context)}/sample-options`, method: 'GET', data: params, signal }) } export function marketModuleContract(context) { const normalizedContext = assertContext(context) return getMarketModule(normalizedContext.module) } export async function fetchProjectOptions(bizType, { signal, filter = false, context } = {}) { const { request } = await import('../utils/http.js') const url = filter ? `/mini/market/${assertContext(context).marketRole}/project-options` : '/project-management/options' return request({ url, method: 'GET', data: { bizType }, signal }) }