| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- <?php
- namespace App\Module\ThirdParty\Models;
- use UCore\ModelCore;
- use App\Module\ThirdParty\Enums\QUOTA_TYPE;
- use Illuminate\Database\Eloquent\Relations\BelongsTo;
- /**
- * 第三方服务配额管理模型
- *
- * field start
- * @property int $id 主键ID
- * @property int $service_id 服务ID
- * @property string $type 配额类型
- * @property int $limit_value 限制值
- * @property int $used_value 已使用值
- * @property \Carbon\Carbon $reset_at 重置时间
- * @property \Carbon\Carbon $window_start 时间窗口开始
- * @property \Carbon\Carbon $window_end 时间窗口结束
- * @property bool $is_active 是否激活
- * @property float $alert_threshold 告警阈值(百分比)
- * @property bool $is_exceeded 是否已超限
- * @property \Carbon\Carbon $exceeded_at 超限时间
- * @property \Carbon\Carbon $created_at 创建时间
- * @property \Carbon\Carbon $updated_at 更新时间
- * field end
- */
- class ThirdPartyQuota extends ModelCore
- {
- /**
- * 数据表名
- *
- * @var string
- */
- protected $table = 'thirdparty_quotas';
- /**
- * 属性类型转换
- *
- * @var array
- */
- protected $casts = [
- 'service_id' => 'integer',
- 'limit_value' => 'integer',
- 'used_value' => 'integer',
- 'reset_at' => 'datetime',
- 'window_start' => 'datetime',
- 'window_end' => 'datetime',
- 'is_active' => 'boolean',
- 'alert_threshold' => 'decimal:2',
- 'is_exceeded' => 'boolean',
- 'exceeded_at' => 'datetime',
- 'created_at' => 'datetime',
- 'updated_at' => 'datetime',
- ];
- /**
- * 获取配额类型枚举
- *
- * @return QUOTA_TYPE
- */
- public function getQuotaTypeEnum(): QUOTA_TYPE
- {
- return QUOTA_TYPE::from($this->type);
- }
- /**
- * 获取配额类型标签
- *
- * @return string
- */
- public function getTypeLabel(): string
- {
- return $this->getQuotaTypeEnum()->getLabel();
- }
- /**
- * 获取使用百分比
- *
- * @return float
- */
- public function getUsagePercentage(): float
- {
- if ($this->limit_value <= 0) {
- return 0;
- }
- return min(100, ($this->used_value / $this->limit_value) * 100);
- }
- /**
- * 获取剩余配额
- *
- * @return int
- */
- public function getRemainingQuota(): int
- {
- return max(0, $this->limit_value - $this->used_value);
- }
- /**
- * 检查是否已超限
- *
- * @return bool
- */
- public function isExceeded(): bool
- {
- return $this->is_exceeded || $this->used_value >= $this->limit_value;
- }
- /**
- * 检查是否接近告警阈值
- *
- * @return bool
- */
- public function isNearThreshold(): bool
- {
- return $this->getUsagePercentage() >= $this->alert_threshold;
- }
- /**
- * 检查是否需要重置
- *
- * @return bool
- */
- public function needsReset(): bool
- {
- $quotaType = $this->getQuotaTypeEnum();
- // 总计类型不需要重置
- if ($quotaType === QUOTA_TYPE::TOTAL) {
- return false;
- }
- // 检查是否超过了当前时间窗口
- if ($this->window_end && now()->gt($this->window_end)) {
- return true;
- }
- // 检查重置时间
- if ($this->reset_at && now()->gte($this->reset_at)) {
- return true;
- }
- return false;
- }
- /**
- * 重置配额
- *
- * @return bool
- */
- public function resetQuota(): bool
- {
- $quotaType = $this->getQuotaTypeEnum();
- // 计算新的时间窗口
- $windowStart = $quotaType->getCurrentWindowStart();
- $windowEnd = $quotaType->getCurrentWindowEnd();
- // 计算下次重置时间
- $resetAt = $quotaType === QUOTA_TYPE::TOTAL ? null : $windowEnd;
- return $this->update([
- 'used_value' => 0,
- 'window_start' => $windowStart,
- 'window_end' => $windowEnd,
- 'reset_at' => $resetAt,
- 'is_exceeded' => false,
- 'exceeded_at' => null,
- ]);
- }
- /**
- * 增加使用量
- *
- * @param int $amount
- * @return bool
- */
- public function incrementUsage(int $amount = 1): bool
- {
- // 检查是否需要重置
- if ($this->needsReset()) {
- $this->resetQuota();
- $this->refresh();
- }
- $newUsedValue = $this->used_value + $amount;
- $updateData = ['used_value' => $newUsedValue];
- // 检查是否超限
- if ($newUsedValue >= $this->limit_value && !$this->is_exceeded) {
- $updateData['is_exceeded'] = true;
- $updateData['exceeded_at'] = now();
- }
- return $this->update($updateData);
- }
- /**
- * 检查是否可以使用指定数量的配额
- *
- * @param int $amount
- * @return bool
- */
- public function canUse(int $amount = 1): bool
- {
- if (!$this->is_active) {
- return false;
- }
- // 检查是否需要重置
- if ($this->needsReset()) {
- return $amount <= $this->limit_value;
- }
- return ($this->used_value + $amount) <= $this->limit_value;
- }
- /**
- * 获取配额状态
- *
- * @return string
- */
- public function getStatus(): string
- {
- if (!$this->is_active) {
- return 'inactive';
- }
- if ($this->isExceeded()) {
- return 'exceeded';
- }
- if ($this->isNearThreshold()) {
- return 'warning';
- }
- return 'normal';
- }
- /**
- * 获取配额状态颜色
- *
- * @return string
- */
- public function getStatusColor(): string
- {
- return match ($this->getStatus()) {
- 'exceeded' => 'danger',
- 'warning' => 'warning',
- 'normal' => 'success',
- 'inactive' => 'secondary',
- };
- }
- /**
- * 获取配额状态标签
- *
- * @return string
- */
- public function getStatusLabel(): string
- {
- return match ($this->getStatus()) {
- 'exceeded' => '已超限',
- 'warning' => '接近限制',
- 'normal' => '正常',
- 'inactive' => '未激活',
- };
- }
- /**
- * 获取格式化的配额信息
- *
- * @return string
- */
- public function getFormattedQuota(): string
- {
- return number_format($this->used_value) . ' / ' . number_format($this->limit_value);
- }
- /**
- * 关联第三方服务
- *
- * @return BelongsTo
- */
- public function service(): BelongsTo
- {
- return $this->belongsTo(ThirdPartyService::class, 'service_id');
- }
- /**
- * 创建配额记录
- *
- * @param array $data
- * @return static
- */
- public static function createQuota(array $data): static
- {
- $quotaType = QUOTA_TYPE::from($data['type']);
- // 设置时间窗口
- if ($quotaType->isTimeWindow()) {
- $data['window_start'] = $quotaType->getCurrentWindowStart();
- $data['window_end'] = $quotaType->getCurrentWindowEnd();
- $data['reset_at'] = $data['window_end'];
- }
- // 设置默认值
- $data['used_value'] = $data['used_value'] ?? 0;
- $data['is_active'] = $data['is_active'] ?? true;
- $data['alert_threshold'] = $data['alert_threshold'] ?? 80.0;
- return static::create($data);
- }
- /**
- * 按服务查询
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param int $serviceId
- * @return \Illuminate\Database\Eloquent\Builder
- */
- public function scopeByService($query, int $serviceId)
- {
- return $query->where('service_id', $serviceId);
- }
- /**
- * 查询激活的配额
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @return \Illuminate\Database\Eloquent\Builder
- */
- public function scopeActive($query)
- {
- return $query->where('is_active', true);
- }
- /**
- * 查询已超限的配额
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @return \Illuminate\Database\Eloquent\Builder
- */
- public function scopeExceeded($query)
- {
- return $query->where('is_exceeded', true);
- }
- /**
- * 查询接近阈值的配额
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @return \Illuminate\Database\Eloquent\Builder
- */
- public function scopeNearThreshold($query)
- {
- return $query->whereRaw('(used_value / limit_value * 100) >= alert_threshold');
- }
- }
|