181 lines
5.7 KiB
PHP
181 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace app\common\service\ai;
|
|
|
|
use app\common\model\ai\AiQuotaUnlock;
|
|
use app\common\model\user\User;
|
|
use think\facade\Db;
|
|
|
|
class AiQuotaService
|
|
{
|
|
public const DAILY_FREE_COUNT = 3;
|
|
public const VIP_REMAIN_COUNT = 999;
|
|
|
|
public const TYPE_MATCH = 'match';
|
|
public const TYPE_ARTICLE = 'article';
|
|
public const TYPE_POST = 'post';
|
|
public const TYPE_LOTTERY = 'lottery';
|
|
public const TYPE_CREDIBILITY = 'credibility';
|
|
|
|
public static function isVip(User $user): bool
|
|
{
|
|
return (int) $user->getData('is_vip') === 1 && (int) $user->getData('vip_expire_time') > time();
|
|
}
|
|
|
|
public static function resetDailyIfNeeded(User $user): int
|
|
{
|
|
$today = date('Y-m-d');
|
|
if ($user->getData('ai_free_date') !== $today) {
|
|
$user->save([
|
|
'ai_free_count' => self::DAILY_FREE_COUNT,
|
|
'ai_free_date' => $today,
|
|
]);
|
|
return self::DAILY_FREE_COUNT;
|
|
}
|
|
|
|
return max(0, (int) $user->getData('ai_free_count'));
|
|
}
|
|
|
|
public static function getRemainCount(int $userId): int
|
|
{
|
|
$user = User::findOrEmpty($userId);
|
|
if ($user->isEmpty()) {
|
|
return 0;
|
|
}
|
|
|
|
if (self::isVip($user)) {
|
|
return self::VIP_REMAIN_COUNT;
|
|
}
|
|
|
|
return self::resetDailyIfNeeded($user);
|
|
}
|
|
|
|
public static function makeKey(string $analysisType, $refId = 0, array $parts = []): string
|
|
{
|
|
$segments = array_merge([$analysisType, (string) $refId], array_map(static function ($part) {
|
|
return trim((string) $part);
|
|
}, $parts));
|
|
$segments = array_values(array_filter($segments, static fn($part) => $part !== ''));
|
|
return self::normalizeKey(implode(':', $segments), $analysisType);
|
|
}
|
|
|
|
public static function hasUnlocked(int $userId, string $analysisKey): bool
|
|
{
|
|
if ($userId <= 0 || $analysisKey === '') {
|
|
return false;
|
|
}
|
|
|
|
return AiQuotaUnlock::where('user_id', $userId)
|
|
->where('analysis_key', self::normalizeKey($analysisKey))
|
|
->count() > 0;
|
|
}
|
|
|
|
public static function unlock(int $userId, string $analysisType, string $analysisKey, int $refId = 0): array
|
|
{
|
|
if ($userId <= 0) {
|
|
return [
|
|
'ok' => false,
|
|
'msg' => '请先登录',
|
|
'code' => 401,
|
|
'remain' => 0,
|
|
'unlocked' => false,
|
|
'charged' => false,
|
|
];
|
|
}
|
|
|
|
$analysisType = trim($analysisType);
|
|
$analysisKey = self::normalizeKey($analysisKey, $analysisType);
|
|
if ($analysisType === '' || $analysisKey === '') {
|
|
return ['ok' => false, 'msg' => 'AI分析类型缺失', 'remain' => self::getRemainCount($userId)];
|
|
}
|
|
|
|
return Db::transaction(function () use ($userId, $analysisType, $analysisKey, $refId) {
|
|
$user = User::where('id', $userId)->lock(true)->findOrEmpty();
|
|
if ($user->isEmpty()) {
|
|
return ['ok' => false, 'msg' => '用户不存在', 'remain' => 0];
|
|
}
|
|
|
|
$existing = AiQuotaUnlock::where('user_id', $userId)
|
|
->where('analysis_key', $analysisKey)
|
|
->findOrEmpty();
|
|
if (!$existing->isEmpty()) {
|
|
return [
|
|
'ok' => true,
|
|
'msg' => '已解锁',
|
|
'remain' => self::getRemainCountForUser($user),
|
|
'unlocked' => true,
|
|
'charged' => false,
|
|
];
|
|
}
|
|
|
|
if (self::isVip($user)) {
|
|
self::createUnlockRecord($userId, $analysisType, $analysisKey, $refId);
|
|
return [
|
|
'ok' => true,
|
|
'msg' => 'VIP解锁',
|
|
'remain' => self::VIP_REMAIN_COUNT,
|
|
'unlocked' => true,
|
|
'charged' => false,
|
|
];
|
|
}
|
|
|
|
$freeCount = self::resetDailyIfNeeded($user);
|
|
if ($freeCount <= 0) {
|
|
return [
|
|
'ok' => false,
|
|
'msg' => '今日免费次数已用完,升级VIP可无限使用',
|
|
'remain' => 0,
|
|
'unlocked' => false,
|
|
'charged' => false,
|
|
];
|
|
}
|
|
|
|
$remain = $freeCount - 1;
|
|
$user->save(['ai_free_count' => $remain]);
|
|
self::createUnlockRecord($userId, $analysisType, $analysisKey, $refId);
|
|
|
|
return [
|
|
'ok' => true,
|
|
'msg' => '解锁成功',
|
|
'remain' => $remain,
|
|
'unlocked' => true,
|
|
'charged' => true,
|
|
];
|
|
});
|
|
}
|
|
|
|
private static function createUnlockRecord(int $userId, string $analysisType, string $analysisKey, int $refId): void
|
|
{
|
|
AiQuotaUnlock::create([
|
|
'user_id' => $userId,
|
|
'analysis_type' => $analysisType,
|
|
'analysis_key' => $analysisKey,
|
|
'ref_id' => $refId,
|
|
]);
|
|
}
|
|
|
|
private static function getRemainCountForUser(User $user): int
|
|
{
|
|
if (self::isVip($user)) {
|
|
return self::VIP_REMAIN_COUNT;
|
|
}
|
|
|
|
return self::resetDailyIfNeeded($user);
|
|
}
|
|
|
|
private static function normalizeKey(string $analysisKey, string $analysisType = 'ai'): string
|
|
{
|
|
$analysisKey = trim(preg_replace('/\s+/', '', $analysisKey) ?? '');
|
|
if ($analysisKey === '') {
|
|
return '';
|
|
}
|
|
|
|
if (strlen($analysisKey) <= 190) {
|
|
return $analysisKey;
|
|
}
|
|
|
|
$prefix = trim($analysisType) ?: 'ai';
|
|
return $prefix . ':' . sha1($analysisKey);
|
|
}
|
|
}
|