feature/limeng-work-normal-product-price-changes #155
@ -1808,7 +1808,7 @@ class Pages extends Controller
|
||||
fputcsv($csv, [
|
||||
'ID', '款式编码', '商品编码', '商品名称', '商品简称', '规格 / 颜色',
|
||||
'品牌', '类目', '供应商', '销售价', '成本价', '普通商品采购价',
|
||||
'工作平台价格', '工作平台价格生效日期', '1688代发单价',
|
||||
'工作平台价格', '工作平台价格生效日期', '定价状态',
|
||||
'变价前价格', '变价后价格', '差额', '变价时间',
|
||||
'数据来源', '市场价', '商品状态', '库存同步', '业务更新时间',
|
||||
]);
|
||||
@ -1839,7 +1839,7 @@ class Pages extends Controller
|
||||
$row->purchase_price,
|
||||
$row->work_platform_price ?? null,
|
||||
$this->priceChangeDate($row->work_platform_price_effect_time ?? null),
|
||||
$row->purchase_order_unit_price,
|
||||
$row->pricing_status ?? '未定价',
|
||||
$row->price_change_before_price ?? null,
|
||||
$row->price_change_after_price ?? null,
|
||||
$row->price_change_diff_amount ?? null,
|
||||
|
||||
@ -127,8 +127,8 @@ return [
|
||||
// JianSun 旧业务库的商品价格记录表。页面按商品编码取最近一条有效记录,
|
||||
// 不改动普通商品自身的采购价与成本价口径。
|
||||
'product_price_changes' => [
|
||||
'dataset_key' => 'jiansun.dbo.op_priceRecord',
|
||||
'target_table' => 'js_op_price_record',
|
||||
'dataset_key' => 'jiansun.dbo.pm_priceAdjustment_log',
|
||||
'target_table' => 'js_pm_price_adjustment_log',
|
||||
],
|
||||
|
||||
// 工作平台采购价格按 SKU 补充到普通商品资料;页面只读取共享镜像。
|
||||
@ -469,11 +469,28 @@ return [
|
||||
'watermark' => null,
|
||||
'columns' => ['Id', 'skuCode', 'purchasePrice', 'effectTime'],
|
||||
'indexes' => [
|
||||
['skuCode(64)', 'effectTime', 'Id'],
|
||||
['skuCode', 'effectTime', 'Id'],
|
||||
['effectTime', 'Id'],
|
||||
],
|
||||
],
|
||||
|
||||
// 变价明细(采购价变价历史):skucode 对应 ops_jst_sku.sku_id(商品编码)。
|
||||
// 页面按商品编码取最近一条 priceType=1(变价)记录,填变价前/后/差额/时间。
|
||||
// 无可靠修改时间(createTime 多为空),按 Id 追加,周期全量校准修正历史。
|
||||
'jiansun.dbo.pm_priceAdjustment_log' => [
|
||||
'source_key' => 'jiansun',
|
||||
'schema' => 'dbo',
|
||||
'object' => 'pm_priceAdjustment_log',
|
||||
'target' => 'js_pm_price_adjustment_log',
|
||||
'keys' => ['Id'],
|
||||
'watermark' => null,
|
||||
'columns' => ['Id', 'skucode', 'priceType', 'originalPrice', 'afterPrice', 'diffPrice', 'effectStartTime', 'effectEndTime', 'createTime'],
|
||||
'indexes' => [
|
||||
['skucode', 'effectStartTime', 'Id'],
|
||||
['Id'],
|
||||
],
|
||||
],
|
||||
|
||||
// 工作平台 2.0:快递资料及其网点、打款账户。页面只读取共享镜像,
|
||||
// 首次使用前需由刷新节点显式执行对应数据集的 --full 同步。
|
||||
'jiansun.dbo.wp_express' => [
|
||||
|
||||
@ -23,10 +23,23 @@ class ProductCatalogMirrorRepository
|
||||
|
||||
public const WORK_PLATFORM_SOURCE_LABEL = '工作平台价格';
|
||||
|
||||
/**
|
||||
* 工作平台采购价来自 JianSun 旧业务库的价格调整表,按 SKU 编码
|
||||
* 关联普通商品资料;同一 SKU 取当前已生效的最新价格记录。
|
||||
*/
|
||||
public const WORK_PLATFORM_PRICE_DATASET_KEY = 'jiansun.dbo.pm_priceAdjustment';
|
||||
|
||||
private const WORK_PLATFORM_PRICE_TABLE = 'js_pm_price_adjustment';
|
||||
|
||||
/**
|
||||
* 普通商品资料的“变价前价格 / 变价后价格 / 差额 / 变价时间”来自
|
||||
* JianSun 旧业务库的商品价格记录表。它与普通商品采购价、1688 代发单价
|
||||
* 分属不同来源,只按商品编码取最近一条有效记录,不参与成本价口径。
|
||||
*/
|
||||
public const PRICE_CHANGE_DATASET_KEY = 'jiansun.dbo.pm_priceAdjustment_log';
|
||||
|
||||
private const PRICE_CHANGE_TABLE = 'js_pm_price_adjustment_log';
|
||||
|
||||
private readonly ConnectionInterface $connection;
|
||||
|
||||
public function __construct(
|
||||
@ -95,6 +108,7 @@ class ProductCatalogMirrorRepository
|
||||
* data:list<object>,
|
||||
* total:int,
|
||||
* purchase_price_sync_status:array{code:string,initialized:bool,last_succeeded_at:?string},
|
||||
* price_change_sync_status:array{code:string,initialized:bool,last_succeeded_at:?string},
|
||||
* work_platform_price_sync_status:array{code:string,initialized:bool,last_succeeded_at:?string}
|
||||
* }
|
||||
*/
|
||||
@ -116,6 +130,8 @@ class ProductCatalogMirrorRepository
|
||||
|
||||
$purchasePriceSyncStatus = $this->purchasePriceSyncStatus();
|
||||
$this->attachPurchasePrices($rows, $purchasePriceSyncStatus['initialized']);
|
||||
$priceChangeSyncStatus = $this->priceChangeSyncStatus();
|
||||
$this->attachPriceChanges($rows, $priceChangeSyncStatus['initialized']);
|
||||
$workPlatformPriceSyncStatus = $this->workPlatformPriceSyncStatus();
|
||||
$this->attachWorkPlatformPrices($rows, $workPlatformPriceSyncStatus['initialized']);
|
||||
|
||||
@ -123,6 +139,7 @@ class ProductCatalogMirrorRepository
|
||||
'data' => $rows,
|
||||
'total' => $total,
|
||||
'purchase_price_sync_status' => $purchasePriceSyncStatus,
|
||||
'price_change_sync_status' => $priceChangeSyncStatus,
|
||||
'work_platform_price_sync_status' => $workPlatformPriceSyncStatus,
|
||||
];
|
||||
}
|
||||
@ -139,6 +156,7 @@ class ProductCatalogMirrorRepository
|
||||
|
||||
$batchSize = min(max($batchSize, 1), 5000);
|
||||
$purchasePriceSyncStatus = $this->purchasePriceSyncStatus();
|
||||
$priceChangeSyncStatus = $this->priceChangeSyncStatus();
|
||||
$workPlatformPriceSyncStatus = $this->workPlatformPriceSyncStatus();
|
||||
$lastAutoid = null;
|
||||
|
||||
@ -157,6 +175,7 @@ class ProductCatalogMirrorRepository
|
||||
}
|
||||
|
||||
$this->attachPurchasePrices($rows, $purchasePriceSyncStatus['initialized']);
|
||||
$this->attachPriceChanges($rows, $priceChangeSyncStatus['initialized']);
|
||||
$this->attachWorkPlatformPrices($rows, $workPlatformPriceSyncStatus['initialized']);
|
||||
foreach ($rows as $row) {
|
||||
yield $row;
|
||||
@ -233,7 +252,7 @@ class ProductCatalogMirrorRepository
|
||||
|
||||
/**
|
||||
* 工作平台采购价是普通商品资料的补充价格来源,不覆盖商品自身采购价。
|
||||
* 只取已生效记录;同一 SKU 按生效时间和稳定键取最新一条。
|
||||
* effectTime 为空的历史记录仍可作为兜底,但未来生效记录不能提前覆盖当前价。
|
||||
*
|
||||
* @param list<object> $rows
|
||||
*/
|
||||
@ -242,6 +261,7 @@ class ProductCatalogMirrorRepository
|
||||
foreach ($rows as $row) {
|
||||
$row->work_platform_price = null;
|
||||
$row->work_platform_price_effect_time = null;
|
||||
$row->pricing_status = '未定价';
|
||||
}
|
||||
|
||||
if ($rows === [] || ! $initialized) {
|
||||
@ -281,10 +301,105 @@ class ProductCatalogMirrorRepository
|
||||
|
||||
$row->work_platform_price = $price->purchasePrice;
|
||||
$row->work_platform_price_effect_time = $price->effectTime;
|
||||
$row->pricing_status = '已定价';
|
||||
$row->data_sources[] = self::WORK_PLATFORM_SOURCE_LABEL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 变价记录与采购单价一样是补充来源:即使镜像尚未就绪,普通商品行仍必须
|
||||
* 完整显示,四个变价字段保持为空,由页面统一说明同步状态。
|
||||
*
|
||||
* @param list<object> $rows
|
||||
*/
|
||||
private function attachPriceChanges(array $rows, bool $initialized): void
|
||||
{
|
||||
foreach ($rows as $row) {
|
||||
$row->price_change_before_price = null;
|
||||
$row->price_change_after_price = null;
|
||||
$row->price_change_diff_amount = null;
|
||||
$row->price_change_effect_time = null;
|
||||
}
|
||||
|
||||
if ($rows === [] || ! $initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$skuIds = collect($rows)
|
||||
->pluck('sku_id')
|
||||
->filter(static fn (mixed $value): bool => is_string($value) && trim($value) !== '')
|
||||
->map(static fn (string $value): string => trim($value))
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
if ($skuIds === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$candidates = $this->connection->table($this->priceChangeTable())
|
||||
->whereIn('skucode', $skuIds)
|
||||
->where('priceType', '1')
|
||||
->orderByDesc('effectStartTime')
|
||||
->orderByDesc('Id')
|
||||
->get([
|
||||
'skucode',
|
||||
'originalPrice',
|
||||
'afterPrice',
|
||||
'diffPrice',
|
||||
'effectStartTime',
|
||||
])
|
||||
->unique('skucode')
|
||||
->keyBy('skucode');
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$change = $candidates->get(trim((string) ($row->sku_id ?? '')));
|
||||
if ($change === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row->price_change_before_price = $change->originalPrice;
|
||||
$row->price_change_after_price = $change->afterPrice;
|
||||
$row->price_change_diff_amount = $change->diffPrice;
|
||||
$row->price_change_effect_time = $change->effectStartTime;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array{code:string,initialized:bool,last_succeeded_at:?string} */
|
||||
public function priceChangeSyncStatus(): array
|
||||
{
|
||||
$schema = Schema::connection($this->connection->getName());
|
||||
if (! $schema->hasTable('business_mirror_table_states')
|
||||
|| ! $schema->hasTable($this->priceChangeTable())) {
|
||||
return ['code' => 'unavailable', 'initialized' => false, 'last_succeeded_at' => null];
|
||||
}
|
||||
|
||||
$state = $this->connection->table('business_mirror_table_states')
|
||||
->where('dataset_key', self::PRICE_CHANGE_DATASET_KEY)
|
||||
->first();
|
||||
|
||||
if ($state === null || $state->initial_completed_at === null) {
|
||||
$code = match ((string) ($state->status ?? 'pending')) {
|
||||
'running' => 'initializing',
|
||||
'failed' => 'initial_failed',
|
||||
default => 'pending',
|
||||
};
|
||||
|
||||
return [
|
||||
'code' => $code,
|
||||
'initialized' => false,
|
||||
'last_succeeded_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 'ready',
|
||||
'initialized' => true,
|
||||
'last_succeeded_at' => ($state->last_succeeded_at ?? null) === null
|
||||
? null
|
||||
: (string) $state->last_succeeded_at,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array{code:string,initialized:bool,last_succeeded_at:?string} */
|
||||
public function workPlatformPriceSyncStatus(): array
|
||||
{
|
||||
@ -305,7 +420,11 @@ class ProductCatalogMirrorRepository
|
||||
default => 'pending',
|
||||
};
|
||||
|
||||
return ['code' => $code, 'initialized' => false, 'last_succeeded_at' => null];
|
||||
return [
|
||||
'code' => $code,
|
||||
'initialized' => false,
|
||||
'last_succeeded_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
@ -317,6 +436,14 @@ class ProductCatalogMirrorRepository
|
||||
];
|
||||
}
|
||||
|
||||
private function priceChangeTable(): string
|
||||
{
|
||||
return (string) config(
|
||||
'upstream_mirrors.product_price_changes.target_table',
|
||||
self::PRICE_CHANGE_TABLE,
|
||||
);
|
||||
}
|
||||
|
||||
private function workPlatformPriceTable(): string
|
||||
{
|
||||
return (string) config(
|
||||
|
||||
@ -9,6 +9,11 @@
|
||||
'initialized' => false,
|
||||
'last_succeeded_at' => null,
|
||||
];
|
||||
$productCatalogPriceChangeSyncStatus = $content['productCatalogPriceChangeSyncStatus'] ?? [
|
||||
'code' => 'unavailable',
|
||||
'initialized' => false,
|
||||
'last_succeeded_at' => null,
|
||||
];
|
||||
$productCatalogWorkPlatformPriceSyncStatus = $content['productCatalogWorkPlatformPriceSyncStatus'] ?? [
|
||||
'code' => 'unavailable',
|
||||
'initialized' => false,
|
||||
@ -225,7 +230,7 @@
|
||||
|
||||
.product-catalog-table {
|
||||
width: 100%;
|
||||
min-width: 2320px;
|
||||
min-width: 2620px;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.78125rem;
|
||||
}
|
||||
@ -501,25 +506,26 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (!($productCatalogPurchasePriceSyncStatus['initialized'] ?? false) && !$productCatalogError)
|
||||
@if (!($productCatalogPriceChangeSyncStatus['initialized'] ?? false) && !$productCatalogError)
|
||||
<div
|
||||
class="product-catalog-notice"
|
||||
role="status"
|
||||
data-purchase-price-sync-state="{{ $productCatalogPurchasePriceSyncStatus['code'] ?? 'unavailable' }}"
|
||||
data-price-change-sync-state="{{ $productCatalogPriceChangeSyncStatus['code'] ?? 'unavailable' }}"
|
||||
>
|
||||
<span class="material-icons-outlined text-base" aria-hidden="true">schedule</span>
|
||||
<span>
|
||||
@if (($productCatalogPurchasePriceSyncStatus['code'] ?? '') === 'initializing')
|
||||
1688代发单价正在执行首次同步,完成前当前仅显示普通商品采购价。
|
||||
@elseif (($productCatalogPurchasePriceSyncStatus['code'] ?? '') === 'initial_failed')
|
||||
1688代发单价首次同步失败,当前仅显示普通商品采购价。
|
||||
@if (($productCatalogPriceChangeSyncStatus['code'] ?? '') === 'initializing')
|
||||
商品价格变价记录正在执行首次同步,完成前变价前价格、变价后价格、差额与变价时间显示为空。
|
||||
@elseif (($productCatalogPriceChangeSyncStatus['code'] ?? '') === 'initial_failed')
|
||||
商品价格变价记录首次同步失败,当前不显示变价前价格、变价后价格、差额与变价时间。
|
||||
@else
|
||||
1688代发单价尚未完成首次同步,当前仅显示普通商品采购价。
|
||||
商品价格变价记录尚未完成首次同步,当前不显示变价前价格、变价后价格、差额与变价时间。
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
@if (!($productCatalogWorkPlatformPriceSyncStatus['initialized'] ?? false) && !$productCatalogError)
|
||||
<div
|
||||
class="product-catalog-notice"
|
||||
@ -557,7 +563,11 @@
|
||||
<th class="is-numeric">普通商品采购价</th>
|
||||
<th class="is-numeric">工作平台价格</th>
|
||||
<th>工作平台价格生效日期</th>
|
||||
<th class="is-numeric">1688代发单价</th>
|
||||
<th>定价状态</th>
|
||||
<th class="is-numeric">变价前价格</th>
|
||||
<th class="is-numeric">变价后价格</th>
|
||||
<th class="is-numeric">差额</th>
|
||||
<th>变价时间</th>
|
||||
<th>数据来源</th>
|
||||
<th class="is-numeric">市场价</th>
|
||||
<th>商品状态</th>
|
||||
@ -594,7 +604,14 @@
|
||||
<td class="is-numeric">{{ isset($row->purchase_price) && $row->purchase_price !== null ? number_format((float) $row->purchase_price, 2) : '-' }}</td>
|
||||
<td class="is-numeric" title="{{ $row->work_platform_price_effect_time ?? '' }}">{{ isset($row->work_platform_price) && $row->work_platform_price !== null ? number_format((float) $row->work_platform_price, 2) : '-' }}</td>
|
||||
<td class="is-code">{{ !empty($row->work_platform_price_effect_time) ? \Illuminate\Support\Carbon::parse($row->work_platform_price_effect_time)->format('Y-m-d') : '-' }}</td>
|
||||
<td class="is-numeric" title="{{ $row->purchase_order_finished_at ?? '' }}">{{ isset($row->purchase_order_unit_price) && $row->purchase_order_unit_price !== null ? number_format((float) $row->purchase_order_unit_price, 2) : '-' }}</td>
|
||||
<td>{!! (($row->pricing_status ?? '未定价') === '已定价')
|
||||
? '<span style="color:#16a34a;font-weight:600;">已定价</span>'
|
||||
: '<span style="color:#9ca3af;">未定价</span>' !!}</td>
|
||||
@foreach (['price_change_before_price', 'price_change_after_price'] as $priceChangeField)
|
||||
<td class="is-numeric">{{ isset($row->{$priceChangeField}) && $row->{$priceChangeField} !== null ? number_format((float) $row->{$priceChangeField}, 2) : '-' }}</td>
|
||||
@endforeach
|
||||
<td class="is-numeric">{{ isset($row->price_change_diff_amount) && $row->price_change_diff_amount !== null ? number_format((float) $row->price_change_diff_amount, 2) : '-' }}</td>
|
||||
<td class="is-code">{{ !empty($row->price_change_effect_time) ? \Illuminate\Support\Carbon::parse($row->price_change_effect_time)->format('Y-m-d') : '-' }}</td>
|
||||
<td class="product-catalog-source-list">
|
||||
@forelse ($row->data_sources ?? [] as $source)
|
||||
<span class="product-catalog-source-item">{{ $source }}</span>
|
||||
@ -617,7 +634,7 @@
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="20" class="product-catalog-empty">
|
||||
<td colspan="24" class="product-catalog-empty">
|
||||
<span class="material-icons-outlined text-3xl block mb-2" aria-hidden="true">inventory_2</span>
|
||||
@if ($productCatalogError)
|
||||
数据暂时不可用。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user