初始化
缺陷:主题配色需要优化整体的同风格
This commit is contained in:
152
app/Service/business/PriceService.php
Normal file
152
app/Service/business/PriceService.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\business;
|
||||
|
||||
/**
|
||||
* 价格可见性与倍率
|
||||
*
|
||||
* 报价单的 routine 列是一串用 @ 分隔的价格,每段要么是纯数字,要么是「名称:价格」,
|
||||
* 乘倍率时只能乘价格段,把整段当数字乘会把名称吃掉。
|
||||
* show_price 为假时整个价格数组换成 ['****'],并回 is_show_price=false 给前端。
|
||||
*
|
||||
* 这段逻辑原先只存在于 lgp-wx-api 的 ProductService,后台完全没有,
|
||||
* 于是后台看到的是原价、小程序看到的是倍率价,对账时无从复现。收在这里两边共用。
|
||||
*/
|
||||
class PriceService
|
||||
{
|
||||
private static mixed $_instance;
|
||||
|
||||
public const MASK = '****';
|
||||
|
||||
public static function getInstance(): null|static
|
||||
{
|
||||
$name = get_called_class();
|
||||
if (!isset(self::$_instance[$name])) {
|
||||
self::$_instance[$name] = new static();
|
||||
}
|
||||
return self::$_instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 拆分 routine 列
|
||||
*/
|
||||
public function split(?string $routine): array
|
||||
{
|
||||
$routine = trim((string) $routine);
|
||||
if ($routine === '') {
|
||||
return [];
|
||||
}
|
||||
return array_values(array_filter(array_map('trim', explode('@', $routine)), fn ($v) => $v !== ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按用户可见性与倍率格式化 routine
|
||||
*
|
||||
* @param bool $showPrice 是否可见价格
|
||||
* @param int|float|string $multiplier 价格倍率
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function formatRoutine(?string $routine, bool $showPrice, mixed $multiplier = 1): array
|
||||
{
|
||||
if (!$showPrice) {
|
||||
return [self::MASK];
|
||||
}
|
||||
$items = $this->split($routine);
|
||||
foreach ($items as &$item) {
|
||||
$item = $this->formatPrice($item, $multiplier);
|
||||
}
|
||||
unset($item);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单段价格乘倍率。「名称:价格」只乘价格部分,兼容半角冒号与空格
|
||||
*/
|
||||
public function formatPrice(string $value, mixed $multiplier = 1): string
|
||||
{
|
||||
$multiplier = $this->normalizeMultiplier($multiplier);
|
||||
try {
|
||||
if (preg_match('/^[0-9.]+$/', $value)) {
|
||||
return bcmul($value, $multiplier, 0);
|
||||
}
|
||||
$normalized = str_replace([':', ' '], [':', ''], $value);
|
||||
$parts = explode(':', $normalized);
|
||||
if (array_key_exists(1, $parts) && preg_match('/^[0-9.]+$/', $parts[1])) {
|
||||
$parts[1] = bcmul($parts[1], $multiplier, 0);
|
||||
}
|
||||
return implode(':', $parts);
|
||||
} catch (\Throwable) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给一组报价单行套上价格规则,返回值里 routine 变成数组
|
||||
*
|
||||
* @param array $rows price_sheet 行
|
||||
* @return array{rows: array, is_show_price: bool}
|
||||
*/
|
||||
public function applyToRows(array $rows, bool $showPrice, mixed $multiplier = 1): array
|
||||
{
|
||||
foreach ($rows as &$row) {
|
||||
$row['routine'] = $this->formatRoutine($row['routine'] ?? '', $showPrice, $multiplier);
|
||||
}
|
||||
unset($row);
|
||||
return ['rows' => $rows, 'is_show_price' => $showPrice];
|
||||
}
|
||||
|
||||
/**
|
||||
* 取某个材质的单价,返回「分」
|
||||
*
|
||||
* 下单要的是一个确定的数字,而 routine 是给人看的字符串(可能是 "1200",
|
||||
* 也可能是 "布艺:1200@皮艺:1800")。这里按 materialKey 找对应段,
|
||||
* 找不到就退回第一个能解析出数字的段;一个都没有返回 0,由调用方决定报错还是放过。
|
||||
*
|
||||
* 金额一律整数分:老库价格是整数元,乘完倍率再 ×100,不引入浮点。
|
||||
*/
|
||||
public function resolveUnitPrice(?string $routine, string $materialKey = '', mixed $multiplier = 1): int
|
||||
{
|
||||
$items = $this->split($routine);
|
||||
if (empty($items)) {
|
||||
return 0;
|
||||
}
|
||||
$materialKey = trim($materialKey);
|
||||
$fallback = 0;
|
||||
foreach ($items as $item) {
|
||||
$normalized = str_replace([':', ' '], [':', ''], $item);
|
||||
$parts = explode(':', $normalized);
|
||||
$name = count($parts) > 1 ? $parts[0] : '';
|
||||
$value = count($parts) > 1 ? $parts[1] : $parts[0];
|
||||
if (!preg_match('/^[0-9.]+$/', $value)) {
|
||||
continue;
|
||||
}
|
||||
$yuan = (int) bcmul($value, $this->normalizeMultiplier($multiplier), 0);
|
||||
if ($materialKey !== '' && $materialKey !== 'routine' && $name === $materialKey) {
|
||||
return $yuan * 100;
|
||||
}
|
||||
if ($fallback === 0) {
|
||||
$fallback = $yuan * 100;
|
||||
}
|
||||
}
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分转元字符串,仅用于展示与导出
|
||||
*/
|
||||
public function centsToYuan(int $cents): string
|
||||
{
|
||||
return number_format($cents / 100, 2, '.', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 倍率兜底:库里可能是空串、0 或负数,直接拿去 bcmul 会把价格清零
|
||||
*/
|
||||
private function normalizeMultiplier(mixed $multiplier): string
|
||||
{
|
||||
if (!is_numeric($multiplier) || (float) $multiplier <= 0) {
|
||||
return '1';
|
||||
}
|
||||
return (string) $multiplier;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user