积分与余额账务系统硬切重构
This commit is contained in:
1 parent
ca12226b39
commit
5cae5b5c93
159 files changed
+7275
-4718
No files matched your search
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user