永久会员与模板库后台化:预览门控、轻量首页与 Admin CRUD
- 会员改为永久激活方案;云端文件不再拦截非会员;预览弹窗内导出/打印才校验 - 首页最近文件支持本地记录,登录后与云端合并;移除独立会员页与订阅页 - 模板库入库管理:soon_templates 表、Admin 上传 CRUD、/templates 轻量列表与按需下载 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,7 +5,6 @@ namespace Soon\Api\Services;
|
||||
|
||||
use Soon\Api\Core\Config;
|
||||
use Soon\Api\Core\Db;
|
||||
use Soon\Api\Core\Json;
|
||||
|
||||
/**
|
||||
* 会员与配额服务。
|
||||
@@ -16,7 +15,8 @@ final class MembershipService
|
||||
{
|
||||
$stmt = Db::pdo()->query(
|
||||
'SELECT id, code, name, description, price_cents, quota_mb, max_files, duration_days, '
|
||||
. 'features, sort_order, is_recommended, is_active FROM plans WHERE is_active = 1 '
|
||||
. 'features, sort_order, is_recommended, is_active FROM plans '
|
||||
. 'WHERE is_active = 1 AND code = "member_lifetime" AND price_cents > 0 '
|
||||
. 'ORDER BY sort_order ASC, price_cents ASC'
|
||||
);
|
||||
$items = [];
|
||||
@@ -42,15 +42,25 @@ final class MembershipService
|
||||
|
||||
if ($row) {
|
||||
$plan = self::enrichPlan($row);
|
||||
$expiresAt = (string)($row['subscription_expires_at'] ?? '');
|
||||
$daysRemaining = self::daysUntil($expiresAt);
|
||||
$plan['subscription'] = [
|
||||
'id' => (int)$row['subscription_id'],
|
||||
'started_at' => $row['subscription_started_at'] ?? null,
|
||||
'expires_at' => $expiresAt,
|
||||
'days_remaining' => $daysRemaining,
|
||||
'status' => $daysRemaining <= 7 ? 'expiring' : 'active',
|
||||
];
|
||||
$durationDays = (int)($row['duration_days'] ?? 0);
|
||||
$startedAt = $row['subscription_started_at'] ?? null;
|
||||
if ($durationDays <= 0 || (string)($row['code'] ?? '') === 'member_lifetime') {
|
||||
$plan['subscription'] = [
|
||||
'type' => 'lifetime',
|
||||
'activated_at' => $startedAt,
|
||||
'status' => 'active',
|
||||
];
|
||||
} else {
|
||||
$expiresAt = (string)($row['subscription_expires_at'] ?? '');
|
||||
$daysRemaining = self::daysUntil($expiresAt);
|
||||
$plan['subscription'] = [
|
||||
'id' => (int)$row['subscription_id'],
|
||||
'started_at' => $startedAt,
|
||||
'expires_at' => $expiresAt,
|
||||
'days_remaining' => $daysRemaining,
|
||||
'status' => $daysRemaining <= 7 ? 'expiring' : 'active',
|
||||
];
|
||||
}
|
||||
$plan['usage'] = $usage;
|
||||
$plan['tier'] = 'member';
|
||||
$plan['is_member'] = true;
|
||||
@@ -69,7 +79,7 @@ final class MembershipService
|
||||
$plan = self::enrichPlan([
|
||||
'id' => 0,
|
||||
'code' => 'free',
|
||||
'name' => '免费版',
|
||||
'name' => '普通用户',
|
||||
'description' => '适合个人体验与轻量设计',
|
||||
'price_cents' => 0,
|
||||
'quota_mb' => $freeQuota,
|
||||
@@ -105,13 +115,6 @@ final class MembershipService
|
||||
return (bool)$stmt->fetchColumn();
|
||||
}
|
||||
|
||||
public static function requireActiveMember(int $userId): void
|
||||
{
|
||||
if (!self::isActiveMember($userId)) {
|
||||
Json::fail('membership_required', '此功能需要订阅后使用', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<int, array<string, mixed>> */
|
||||
public static function recentOrders(int $userId, int $limit = 8): array
|
||||
{
|
||||
@@ -158,9 +161,15 @@ final class MembershipService
|
||||
return ['items' => $items, 'total' => $total, 'page' => $page, 'size' => $size];
|
||||
}
|
||||
|
||||
public static function previewAllowed(int $userId): bool
|
||||
private const LIFETIME_EXPIRES = '9999-12-31 23:59:59';
|
||||
|
||||
public static function subscriptionExpiresAt(int $durationDays, ?int $baseTs = null): string
|
||||
{
|
||||
return self::isActiveMember($userId);
|
||||
if ($durationDays <= 0) {
|
||||
return self::LIFETIME_EXPIRES;
|
||||
}
|
||||
$baseTs = $baseTs ?? time();
|
||||
return date('Y-m-d H:i:s', $baseTs + $durationDays * 86400);
|
||||
}
|
||||
|
||||
public static function settings(): array
|
||||
@@ -255,6 +264,8 @@ final class MembershipService
|
||||
}
|
||||
if ($durationDays > 0) {
|
||||
$core[] = '订阅周期:' . self::periodLabel($durationDays);
|
||||
} elseif ($code === 'member_lifetime' || $durationDays <= 0) {
|
||||
$core[] = '永久有效';
|
||||
}
|
||||
return $core;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user