Files
cursornew2026/backend/app/models/models.py
ccdojox-crypto 73a71f198f 蜂鸟Pro v2.0.1 - 基础框架版本 (待完善)
## 当前状态
- 插件界面已完成重命名 (cursorpro → hummingbird)
- 双账号池 UI 已实现 (Auto/Pro 卡片)
- 后端已切换到 MySQL 数据库
- 添加了 Cursor 官方用量 API 文档

## 已知问题 (待修复)
1. 激活时检查账号导致无账号时激活失败
2. 未启用无感换号时不应获取账号
3. 账号用量模块不显示 (seamless 未启用时应隐藏)
4. 积分显示为 0 (后端未正确返回)
5. Auto/Pro 双密钥逻辑混乱,状态不同步
6. 账号添加后无自动分析功能

## 下一版本计划
- 重构数据模型,优化账号状态管理
- 实现 Cursor API 自动分析账号
- 修复激活流程,不依赖账号
- 启用无感时才分配账号
- 完善账号用量实时显示

## 文件说明
- docs/系统设计文档.md - 完整架构设计
- cursor 官方用量接口.md - Cursor API 文档
- 参考计费/ - Vibeviewer 开源项目参考

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:21:52 +08:00

149 lines
6.2 KiB
Python

from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, ForeignKey, Enum
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.database import Base
import enum
class MembershipType(str, enum.Enum):
FREE = "free"
PRO = "pro"
class AccountStatus(str, enum.Enum):
ACTIVE = "active" # 可用
IN_USE = "in_use" # 使用中
DISABLED = "disabled" # 禁用
EXPIRED = "expired" # 过期
class KeyStatus(str, enum.Enum):
UNUSED = "unused" # 未使用
ACTIVE = "active" # 已激活(主密钥)
MERGED = "merged" # 已合并到主密钥
REVOKED = "revoked" # 已撤销
DISABLED = "disabled" # 禁用
EXPIRED = "expired" # 过期
class CursorAccount(Base):
"""Cursor 账号池"""
__tablename__ = "cursor_accounts"
id = Column(Integer, primary_key=True, autoincrement=True)
email = Column(String(255), unique=True, nullable=False, comment="邮箱")
access_token = Column(Text, nullable=False, comment="访问令牌")
refresh_token = Column(Text, nullable=True, comment="刷新令牌")
workos_session_token = Column(Text, nullable=True, comment="WorkOS会话令牌")
membership_type = Column(Enum(MembershipType), default=MembershipType.PRO, comment="会员类型")
status = Column(Enum(AccountStatus), default=AccountStatus.ACTIVE, comment="状态")
# 使用统计
usage_count = Column(Integer, default=0, comment="使用次数")
last_used_at = Column(DateTime, nullable=True, comment="最后使用时间")
current_key_id = Column(Integer, ForeignKey("activation_keys.id"), nullable=True, comment="当前使用的激活码")
# 备注
remark = Column(String(500), nullable=True, comment="备注")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class ActivationKey(Base):
"""激活码"""
__tablename__ = "activation_keys"
id = Column(Integer, primary_key=True, autoincrement=True)
key = Column(String(64), unique=True, nullable=False, index=True, comment="激活码")
status = Column(Enum(KeyStatus), default=KeyStatus.UNUSED, comment="状态")
# 套餐类型
membership_type = Column(Enum(MembershipType), default=MembershipType.PRO, comment="套餐类型: free=Auto池, pro=Pro池")
# 密钥合并关系
master_key_id = Column(Integer, ForeignKey("activation_keys.id"), nullable=True, comment="主密钥ID(如果已合并)")
device_id = Column(String(255), nullable=True, index=True, comment="绑定的设备ID")
# 该密钥贡献的资源 (创建时设置,不变)
duration_days = Column(Integer, default=30, comment="Auto: 该密钥贡献的天数")
quota_contribution = Column(Integer, default=500, comment="Pro: 该密钥贡献的积分")
# 额度系统 (仅主密钥使用,累计值)
quota = Column(Integer, default=500, comment="Pro主密钥: 总额度(累加)")
quota_used = Column(Integer, default=0, comment="Pro主密钥: 已用额度")
# 有效期 (仅主密钥使用)
expire_at = Column(DateTime, nullable=True, comment="Auto主密钥: 到期时间(累加)")
# 激活信息
first_activated_at = Column(DateTime, nullable=True, comment="首次激活时间")
merged_at = Column(DateTime, nullable=True, comment="合并时间")
# 设备限制 (可换设备,此字段保留但不强制)
max_devices = Column(Integer, default=3, comment="最大设备数(可换设备)")
# 当前绑定的账号 (仅主密钥使用)
current_account_id = Column(Integer, ForeignKey("cursor_accounts.id"), nullable=True)
current_account = relationship("CursorAccount", foreign_keys=[current_account_id])
# 统计 (仅主密钥使用)
switch_count = Column(Integer, default=0, comment="总换号次数")
last_switch_at = Column(DateTime, nullable=True, comment="最后换号时间")
merged_count = Column(Integer, default=0, comment="已合并的密钥数量")
# 备注
remark = Column(String(500), nullable=True, comment="备注")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
# 关系
master_key = relationship("ActivationKey", remote_side=[id], foreign_keys=[master_key_id])
@property
def valid_days(self):
"""兼容旧API: duration_days的别名"""
return self.duration_days or 0
class KeyDevice(Base):
"""激活码绑定的设备"""
__tablename__ = "key_devices"
id = Column(Integer, primary_key=True, autoincrement=True)
key_id = Column(Integer, ForeignKey("activation_keys.id"), nullable=False)
device_id = Column(String(255), nullable=False, comment="设备标识")
device_name = Column(String(255), nullable=True, comment="设备名称")
last_active_at = Column(DateTime, nullable=True, comment="最后活跃时间")
created_at = Column(DateTime, server_default=func.now())
key = relationship("ActivationKey")
class GlobalSettings(Base):
"""全局设置"""
__tablename__ = "global_settings"
id = Column(Integer, primary_key=True, autoincrement=True)
key = Column(String(100), unique=True, nullable=False, comment="设置键")
value = Column(String(500), nullable=False, comment="设置值")
description = Column(String(500), nullable=True, comment="描述")
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class UsageLog(Base):
"""使用日志"""
__tablename__ = "usage_logs"
id = Column(Integer, primary_key=True, autoincrement=True)
key_id = Column(Integer, ForeignKey("activation_keys.id"), nullable=False)
account_id = Column(Integer, ForeignKey("cursor_accounts.id"), nullable=True)
action = Column(String(50), nullable=False, comment="操作类型: verify/switch/seamless")
ip_address = Column(String(50), nullable=True)
user_agent = Column(String(500), nullable=True)
success = Column(Boolean, default=True)
message = Column(String(500), nullable=True)
created_at = Column(DateTime, server_default=func.now())
key = relationship("ActivationKey")
account = relationship("CursorAccount")