$v !== '')); } /** * 按用户可见性与倍率格式化 routine * * @param bool $showPrice 是否可见价格 * @param int|float|string $multiplier 价格倍率 * @return array */ 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; } }