Files
lgp-admin-plus-api/app/Service/business/PriceService.php
LQ bcf54c2727 初始化
缺陷:主题配色需要优化整体的同风格
2026-08-14 23:21:21 +08:00

153 lines
5.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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;
}
}