Files
BMS/bms-front/src/api/price/createPriceSetApi.js
T
2026-11-05 12:57:13 +08:00

485 lines
13 KiB
JavaScript

import request from '@/utils/request'
/** Build price-set style API against a controller base path (e.g. /price/set, /price/exclusive). */
export function createPriceSetApi(basePath) {
const base = String(basePath || '/price/set').replace(/\/$/, '')
function listPriceSet(query) {
return request({
url: base + '/list',
method: 'get',
params: query
})
}
function getPriceSet(priceSetId) {
return request({
url: base + '/' + priceSetId,
method: 'get'
})
}
function savePriceSet(data) {
return request({
url: base + '',
method: 'post',
data: data
})
}
function getPriceSetSaveTask(taskId) {
return request({
url: base + '/save/task/' + taskId,
method: 'get',
timeout: 10 * 60 * 1000
})
}
/**
* Submit save async and poll Redis progress until SUCCESS/FAIL.
* @returns {Promise<{ priceSetId: number|string, task: object }>}
*/
async function savePriceSetAsync(data, onProgress) {
const submitRes = await savePriceSet(data)
const taskId = pickSaveTaskId(submitRes)
if (!taskId) {
throw new Error('missing save taskId')
}
const started = Date.now()
const maxMs = 30 * 60 * 1000
while (Date.now() - started < maxMs) {
const res = await getPriceSetSaveTask(taskId)
const task = res.data || {}
if (typeof onProgress === 'function') {
onProgress(task)
}
if (task.status === 'SUCCESS') {
return { priceSetId: task.priceSetId, task }
}
if (task.status === 'FAIL') {
const err = new Error(task.error || task.message || 'save failed')
err.task = task
throw err
}
await new Promise(resolve => setTimeout(resolve, 400))
}
throw new Error('save timeout')
}
/** Accept data.taskId / data string / legacy msg UUID (Bms String overload). */
function pickSaveTaskId(res) {
if (!res) return null
const d = res.data
if (d && typeof d === 'object' && d.taskId) return String(d.taskId)
if (typeof d === 'string' && d.trim()) return d.trim()
if (typeof res.msg === 'string') {
const m = res.msg.trim()
if (/^[a-f0-9]{32}$/i.test(m)) return m
if (/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(m)) {
return m.replace(/-/g, '')
}
}
return null
}
function updatePriceSet(data) {
return request({
url: base + '',
method: 'put',
data: data
})
}
/** Update package name and note only. */
function updatePriceSetBasic(data) {
return request({
url: base + '/basic',
method: 'put',
data: data
})
}
function delPriceSet(priceSetIds) {
return request({
url: base + '/' + priceSetIds,
method: 'delete'
})
}
function submitPriceSet(priceSetId) {
return request({
url: base + '/submit/' + priceSetId,
method: 'put'
})
}
function approvePriceSet(priceSetId) {
return request({
url: base + '/approve/' + priceSetId,
method: 'put'
})
}
function rejectPriceSet(priceSetId) {
return request({
url: base + '/reject/' + priceSetId,
method: 'put'
})
}
function activatePriceSetVersion(psvId, data) {
return request({
url: base + '/version/activate/' + psvId,
method: 'put',
data: data || {}
})
}
function copyPriceSetVersion(psvId) {
return request({
url: base + '/version/copy/' + psvId,
method: 'post'
})
}
/** Create empty draft version under package */
function createPriceSetVersion(priceSetId) {
return request({
url: base + '/version/create/' + priceSetId,
method: 'post'
})
}
/** Import excel attachments for a price-set version (via prc_price_set_product.psv_id). */
function listPriceSetVersionExcelFiles(psvId) {
return request({
url: base + '/version/' + psvId + '/excelFiles',
method: 'get'
})
}
function deletePriceSetVersion(psvId) {
return request({
url: base + '/version/' + psvId,
method: 'delete'
})
}
function disablePriceSet(priceSetId) {
return request({
url: base + '/disable/' + priceSetId,
method: 'put'
})
}
function enablePriceSet(priceSetId) {
return request({
url: base + '/enable/' + priceSetId,
method: 'put'
})
}
function copyPriceSet(priceSetId) {
return request({
url: base + '/copy/' + priceSetId,
method: 'post'
})
}
function validatePriceSet(data) {
return request({
url: base + '/validate',
method: 'post',
data: data
})
}
function listSetDetail(query) {
return request({
url: base + '/detail/list',
method: 'get',
params: query
})
}
/** All detail rows for one product under a version (no pagination). */
function listSetDetailByProduct(psvId, productCode) {
return request({
url: base + '/detail/byProduct',
method: 'get',
params: { psvId, productCode }
})
}
/** All detail rows for a version (no pagination); one call instead of N byProduct. */
function listSetDetailByVersion(psvId) {
return request({
url: base + '/detail/byVersion',
method: 'get',
params: { psvId }
})
}
/** 版本明细搜索下拉选项:按版本去重后的 产品/国家/分区 */
function listSetDetailOptions(psvId) {
return request({
url: base + '/detail/options',
method: 'get',
params: { psvId }
})
}
function addSetDetail(data) {
return request({
url: base + '/detail',
method: 'post',
data: data
})
}
/**
* Async import quote Excel into FIXED package detail prices; poll until done.
* @param {File|Blob} file
* @param {{ priceSetId: string|number, psvId?: string|number, updateOnly?: boolean, replaceByProductCountry?: boolean, countryCode?: string }} query
* @param {(task: object) => void} [onProgress]
* @returns {Promise<{ task: object }>}
*/
function appendUploadFiles(formData, file) {
const list = Array.isArray(file) ? file : [file]
for (const item of list) {
const raw = item && (item.raw || item)
if (raw) formData.append('files', raw)
}
}
async function importSetDetailPricesAsync(file, query, onProgress) {
const formData = new FormData()
appendUploadFiles(formData, file)
const params = new URLSearchParams()
if (query && query.priceSetId != null) params.set('priceSetId', query.priceSetId)
if (query && query.psvId != null && query.psvId !== '') params.set('psvId', query.psvId)
if (query && query.updateOnly) params.set('updateOnly', 'true')
// Package Excel build-price only (product+country replace scope)
if (query && query.replaceByProductCountry) params.set('replaceByProductCountry', 'true')
if (query && query.countryCode) params.set('countryCode', query.countryCode)
const submitRes = await request({
url: base + '/detail/importPrices?' + params.toString(),
method: 'post',
data: formData,
headers: { 'Content-Type': 'multipart/form-data', repeatSubmit: false }
})
const taskId = pickSaveTaskId(submitRes)
if (!taskId) {
throw new Error('missing import taskId')
}
return pollPriceSetSaveTask(taskId, onProgress)
}
/**
* Preview pull product library (no DB write). Returns { products, details, message }.
*/
function previewPullFromLibrary() {
return request({
url: base + '/detail/previewPullFromLibrary',
method: 'post'
})
}
/**
* Preview Excel import (no DB write).
* @param {File|Blob} file
* @param {{ updateOnly?: boolean, replaceByProductCountry?: boolean, countryCode?: string, details?: Array }} [opts]
*/
function previewImportDetailPrices(file, opts = {}) {
const formData = new FormData()
appendUploadFiles(formData, file)
if (opts.updateOnly) formData.append('updateOnly', 'true')
if (opts.replaceByProductCountry) formData.append('replaceByProductCountry', 'true')
if (opts.countryCode) formData.append('countryCode', opts.countryCode)
// updateOnly / Excel build-price merge need current draft details
if ((opts.updateOnly || opts.replaceByProductCountry || opts.countryCode) && Array.isArray(opts.details)) {
formData.append('detailsJson', JSON.stringify(opts.details))
}
return request({
url: base + '/detail/previewImportPrices',
method: 'post',
data: formData,
// FormData must not go through repeat-submit JSON.stringify (collapses to "{}")
headers: { 'Content-Type': 'multipart/form-data', repeatSubmit: false },
timeout: 10 * 60 * 1000
})
}
/**
* Async pull product library into package version details; poll until done.
* @param {{ priceSetId: string|number, psvId?: string|number, force?: boolean }} query
* @param {(task: object) => void} [onProgress]
*/
async function pullSetDetailsFromLibraryAsync(query, onProgress) {
const params = new URLSearchParams()
if (query && query.priceSetId != null) params.set('priceSetId', query.priceSetId)
if (query && query.psvId != null && query.psvId !== '') params.set('psvId', query.psvId)
params.set('force', query && query.force ? 'true' : 'false')
const submitRes = await request({
url: base + '/detail/pullFromLibrary?' + params.toString(),
method: 'post'
})
const taskId = pickSaveTaskId(submitRes)
if (!taskId) {
throw new Error('missing pull taskId')
}
return pollPriceSetSaveTask(taskId, onProgress)
}
async function pollPriceSetSaveTask(taskId, onProgress) {
const started = Date.now()
const maxMs = 30 * 60 * 1000
while (Date.now() - started < maxMs) {
const res = await getPriceSetSaveTask(taskId)
const task = res.data || {}
if (typeof onProgress === 'function') {
onProgress(task)
}
if (task.status === 'SUCCESS') {
return { task }
}
if (task.status === 'FAIL') {
const err = new Error(task.error || task.message || 'task failed')
err.task = task
throw err
}
await new Promise(resolve => setTimeout(resolve, 400))
}
throw new Error('task timeout')
}
function updateSetDetail(data) {
return request({
url: base + '/detail',
method: 'put',
data: data
})
}
/**
* Submit async batch write of detail surcharge snapshots and poll until done.
* @returns {Promise<{ task: object }>}
*/
async function updateSetDetailSurchargeAsync(details, onProgress) {
const submitRes = await request({
url: base + '/detail/surcharge/async',
method: 'post',
data: details || []
})
const taskId = pickSaveTaskId(submitRes)
if (!taskId) {
throw new Error('missing surcharge taskId')
}
const started = Date.now()
const maxMs = 30 * 60 * 1000
while (Date.now() - started < maxMs) {
const res = await getPriceSetSaveTask(taskId)
const task = res.data || {}
if (typeof onProgress === 'function') {
onProgress(task)
}
if (task.status === 'SUCCESS') {
return { task }
}
if (task.status === 'FAIL') {
const err = new Error(task.error || task.message || 'surcharge save failed')
err.task = task
throw err
}
await new Promise(resolve => setTimeout(resolve, 400))
}
throw new Error('surcharge save timeout')
}
function delSetDetail(psdIds) {
return request({
url: base + '/detail/' + psdIds,
method: 'delete'
})
}
function listSetCustomer(query) {
return request({
url: base + '/customer/list',
method: 'get',
params: query
})
}
function addSetCustomer(data) {
return request({
url: base + '/customer',
method: 'post',
data: data
})
}
function batchAddSetCustomer(data) {
return request({
url: base + '/customer/batch',
method: 'post',
data: data
})
}
function updateSetCustomer(data) {
return request({
url: base + '/customer',
method: 'put',
data: data
})
}
function delSetCustomer(ids) {
return request({
url: base + '/customer/' + ids,
method: 'delete'
})
}
return {
listPriceSet,
getPriceSet,
savePriceSet,
getPriceSetSaveTask,
savePriceSetAsync,
updatePriceSet,
updatePriceSetBasic,
delPriceSet,
submitPriceSet,
approvePriceSet,
rejectPriceSet,
activatePriceSetVersion,
copyPriceSetVersion,
createPriceSetVersion,
listPriceSetVersionExcelFiles,
deletePriceSetVersion,
disablePriceSet,
enablePriceSet,
copyPriceSet,
validatePriceSet,
listSetDetail,
listSetDetailByProduct,
listSetDetailByVersion,
listSetDetailOptions,
addSetDetail,
importSetDetailPricesAsync,
previewPullFromLibrary,
previewImportDetailPrices,
pullSetDetailsFromLibraryAsync,
updateSetDetail,
updateSetDetailSurchargeAsync,
delSetDetail,
listSetCustomer,
addSetCustomer,
batchAddSetCustomer,
updateSetCustomer,
delSetCustomer
}
}