积分与余额账务系统硬切重构

This commit is contained in:
kate522 committed 2026-08-24 15:59:11 +08:00
1 parent ca12226b39
commit 5cae5b5c93
159 files changed
+7275 -4718

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Ledger;
defined('ABSPATH') || exit;
final class AssetAmount {
private function __construct(
private readonly AssetType $asset,
private readonly int $minor
) {}
public static function points(int $points): self {
if ($points < 0) {
throw new \InvalidArgumentException('points_must_be_non_negative');
}
return new self(AssetType::POINTS, $points);
}
public static function balance(string $decimal): self {
$decimal = trim($decimal);
if (!preg_match('/^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/', $decimal, $matches)) {
throw new \InvalidArgumentException('balance_decimal_invalid');
}
$whole = (int) $matches[1];
$fraction = str_pad((string) ($matches[2] ?? ''), 2, '0');
if ($whole > intdiv(PHP_INT_MAX - (int) $fraction, 100)) {
throw new \InvalidArgumentException('balance_decimal_overflow');
}
return new self(AssetType::BALANCE, ($whole * 100) + (int) $fraction);
}
public static function balance_minor(string $decimal): int {
return self::balance($decimal)->minor();
}
public static function decimal_from_minor(int $minor): string {
return self::from_minor(AssetType::BALANCE, $minor)->decimal();
}
/**
* Apply an integer percentage using half-up rounding without floats.
*/
public static function percentage(int $minor, int $percent): int {
if ($minor < 0) {
throw new \InvalidArgumentException('asset_minor_must_be_non_negative');
}
if ($percent < 0 || $percent > 100) {
throw new \InvalidArgumentException('asset_percentage_invalid');
}
$whole = intdiv($minor, 100) * $percent;
$remainder = ($minor % 100) * $percent;
return $whole + intdiv($remainder + 50, 100);
}
public static function from_minor(AssetType|string $asset, int $minor): self {
if ($minor < 0) {
throw new \InvalidArgumentException('asset_minor_must_be_non_negative');
}
return new self(AssetType::from_value($asset), $minor);
}
public function asset(): AssetType {
return $this->asset;
}
public function minor(): int {
return $this->minor;
}
public function decimal(): string {
if ($this->asset === AssetType::POINTS) {
return (string) $this->minor;
}
return sprintf('%d.%02d', intdiv($this->minor, 100), $this->minor % 100);
}
public function formatted(): string {
if ($this->asset === AssetType::POINTS) {
return number_format_i18n($this->minor);
}
return $this->decimal();
}
}