Web 端数据交互与 design2 布局修复

统一门户 JSON 契约:新增 GET /templates/{id},模板/云文件/保存/打开走虚拟 key;
layer 保存弹层替代 prompt,本地 .soon 已登录后台 POST 登记;修复保存静默失败与 design2 全宽布局。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
24kycj
2026-06-09 01:20:34 +08:00
parent ebe191b06d
commit 877fd278c2
27 changed files with 1232 additions and 326 deletions
+24 -5
View File
@@ -16,7 +16,7 @@ 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 AND code = "member_lifetime" AND price_cents > 0 '
. 'WHERE is_active = 1 AND code = \'member_lifetime\' AND price_cents > 0 '
. 'ORDER BY sort_order ASC, price_cents ASC'
);
$items = [];
@@ -34,7 +34,7 @@ final class MembershipService
$stmt = $pdo->prepare(
'SELECT p.*, s.id AS subscription_id, s.expires_at AS subscription_expires_at, s.started_at AS subscription_started_at '
. 'FROM subscriptions s JOIN plans p ON p.id = s.plan_id '
. 'WHERE s.user_id = :u AND s.status = "active" AND s.expires_at > NOW() '
. 'WHERE s.user_id = :u AND s.status = \'active\' AND s.expires_at > NOW() '
. 'ORDER BY s.expires_at DESC LIMIT 1'
);
$stmt->execute(['u' => $userId]);
@@ -70,7 +70,7 @@ final class MembershipService
$memberQuota = (int)Config::get('limits.member_quota_mb', 2048);
$memberFiles = (int)Config::get('limits.member_max_files', 200);
$freeQuota = (int)Config::get('limits.free_quota_mb', $memberQuota);
$freeStmt = $pdo->prepare('SELECT * FROM plans WHERE code = "free" AND is_active = 1 LIMIT 1');
$freeStmt = $pdo->prepare('SELECT * FROM plans WHERE code = \'free\' AND is_active = 1 LIMIT 1');
$freeStmt->execute();
$freeRow = $freeStmt->fetch();
if ($freeRow) {
@@ -108,13 +108,32 @@ final class MembershipService
{
$stmt = Db::pdo()->prepare(
'SELECT 1 FROM subscriptions s JOIN plans p ON p.id = s.plan_id '
. 'WHERE s.user_id = :u AND s.status = "active" AND s.expires_at > NOW() '
. 'AND p.code <> "free" AND p.price_cents > 0 LIMIT 1'
. 'WHERE s.user_id = :u AND s.status = \'active\' AND s.expires_at > NOW() '
. 'AND p.code <> \'free\' AND p.price_cents > 0 LIMIT 1'
);
$stmt->execute(['u' => $userId]);
return (bool)$stmt->fetchColumn();
}
/** @return array{is_member:bool,tier:string,name:string,subscription:array<string,mixed>} */
public static function membershipSummary(int $userId): array
{
if (self::isActiveMember($userId)) {
return [
'is_member' => true,
'tier' => 'member',
'name' => '会员',
'subscription' => ['status' => 'active'],
];
}
return [
'is_member' => false,
'tier' => 'free',
'name' => '普通用户',
'subscription' => ['status' => 'free'],
];
}
/** @return array<int, array<string, mixed>> */
public static function recentOrders(int $userId, int $limit = 8): array
{