Sfoglia il codice sorgente

feat(dakehu): Steam求购定制版品牌重命名与 special_close 静默限权

将客户端/管理端品牌切换为 Steam求购,新增卡密 special_close 字段并在登录与心跳同步;定制版打包输出 SteamQG 产物,避免覆盖老版本 SteamSH 构建文件。

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 1 mese fa
parent
commit
e9a6893186
81 ha cambiato i file con 309 aggiunte e 287 eliminazioni
  1. 2 2
      ChgPasswd/send_reset_password.py
  2. 1 1
      admin/README.md
  3. 1 1
      admin/index.html
  4. 1 1
      admin/package.json
  5. BIN
      admin/public/logo.png
  6. 2 2
      admin/src/components/Layout.tsx
  7. 2 2
      admin/src/pages/Login/index.css
  8. 2 2
      admin/src/pages/Login/index.tsx
  9. 1 1
      backend-admin/README.md
  10. 1 1
      backend-admin/app/__init__.py
  11. 3 3
      backend-admin/app/main.py
  12. 1 0
      backend-admin/app/models/card.py
  13. 2 2
      backend-admin/start.sh
  14. 1 1
      backend-client/README.md
  15. 1 1
      backend-client/app/__init__.py
  16. 12 3
      backend-client/app/api/v1/router.py
  17. 3 3
      backend-client/app/main.py
  18. 1 0
      backend-client/app/models/card.py
  19. 27 1
      backend-client/app/services/auth_service.py
  20. 25 1
      backend-client/app/services/heartbeat_service.py
  21. 1 1
      backend-client/migrations/20260203_add_login_attempts_and_configs.sql
  22. 8 0
      backend-client/migrations/20260928_add_cards_special_close.sql
  23. 2 2
      backend-client/start.bat
  24. 2 2
      backend-client/start.sh
  25. 2 2
      backend-client/start_prod.sh
  26. 1 1
      buff-api/sync_goods/sync_float_price.py
  27. 1 1
      buff-api/sync_goods/sync_goods.py
  28. 1 1
      buff-api/sync_goods/sync_liquidity.py
  29. 1 1
      buff-api/sync_liquidity/sync_liquidity.py
  30. 2 2
      client-tauri/BUILD-EXE-GUIDE.md
  31. 1 1
      client-tauri/README.md
  32. BIN
      client-tauri/SteamQG.exe
  33. BIN
      client-tauri/SteamQG_Portable.zip
  34. BIN
      client-tauri/Steam求购.exe
  35. 34 94
      client-tauri/build-portable.ps1
  36. 10 1
      client-tauri/build-release.ps1
  37. 30 49
      client-tauri/build-windows.ps1
  38. 1 1
      client-tauri/index.html
  39. BIN
      client-tauri/public/youdangdang/logo.png
  40. BIN
      client-tauri/src-tauri/icons/128x128.png
  41. BIN
      client-tauri/src-tauri/icons/128x128@2x.png
  42. BIN
      client-tauri/src-tauri/icons/32x32.png
  43. BIN
      client-tauri/src-tauri/icons/Square107x107Logo.png
  44. BIN
      client-tauri/src-tauri/icons/Square142x142Logo.png
  45. BIN
      client-tauri/src-tauri/icons/Square150x150Logo.png
  46. BIN
      client-tauri/src-tauri/icons/Square284x284Logo.png
  47. BIN
      client-tauri/src-tauri/icons/Square30x30Logo.png
  48. BIN
      client-tauri/src-tauri/icons/Square310x310Logo.png
  49. BIN
      client-tauri/src-tauri/icons/Square44x44Logo.png
  50. BIN
      client-tauri/src-tauri/icons/Square71x71Logo.png
  51. BIN
      client-tauri/src-tauri/icons/Square89x89Logo.png
  52. BIN
      client-tauri/src-tauri/icons/StoreLogo.png
  53. BIN
      client-tauri/src-tauri/icons/app_icon.png
  54. BIN
      client-tauri/src-tauri/icons/icon.ico
  55. BIN
      client-tauri/src-tauri/icons/icon.png
  56. BIN
      client-tauri/src-tauri/icons/icon_square.png
  57. BIN
      client-tauri/src-tauri/icons/youdangdang_logo.png
  58. 19 19
      client-tauri/src-tauri/installer.nsi
  59. 1 1
      client-tauri/src-tauri/license.txt
  60. 1 1
      client-tauri/src-tauri/src/main.rs
  61. 3 3
      client-tauri/src-tauri/tauri.conf.json
  62. 1 1
      client-tauri/src/main.ts
  63. 23 3
      client-tauri/src/stores/authStore.ts
  64. 5 3
      client-tauri/src/stores/heartbeatStore.ts
  65. 5 0
      client-tauri/src/utils/secureApiClient.ts
  66. 37 18
      client-tauri/src/views/BuffSelectionView.vue
  67. 7 18
      client-tauri/src/views/HomeView.vue
  68. 3 21
      client-tauri/src/views/LoginView.vue
  69. 3 0
      client-tauri/src/views/ProxySettingsView.vue
  70. 2 2
      client-tauri/src/views/SettingsView.vue
  71. 1 0
      client-tauri/src/views/components/ChangePasswordDialog.vue
  72. 1 1
      client-tauri/src/views/components/ConfirmDialog.vue
  73. 1 0
      client-tauri/src/views/components/RechargeDialog.vue
  74. 1 0
      client-tauri/src/views/components/UnbindDialog.vue
  75. 1 1
      docs/buff_client.sql
  76. 1 1
      docs/客户端exe-API接口文档.md
  77. 1 1
      docs/客户端exe实现检查报告.md
  78. 4 4
      docs/客户端exe技术方案.md
  79. BIN
      logo图片素材/logo.png
  80. 2 2
      setup/启动Steam求购.bat
  81. BIN
      steam-client/steamAPI/youdangdang/logo.png

+ 2 - 2
ChgPasswd/send_reset_password.py

@@ -94,7 +94,7 @@ def send_email(config, to_email: str, username: str, new_password: str) -> None:
         smtp_user = sender  # 未填完整邮箱时用发件人地址
     smtp_password = config.get("email", "smtp_password")
 
-    subject = "游铛铛 - 找回密码"
+    subject = "Steam求购 - 找回密码"
     body = f"""您好 {username},
 
 您已成功找回密码。新密码如下,请登录后及时修改:
@@ -103,7 +103,7 @@ def send_email(config, to_email: str, username: str, new_password: str) -> None:
 
 请妥善保管,勿泄露给他人。
 
-—— 游铛铛
+—— Steam求购
 """
     msg = MIMEMultipart()
     msg["From"] = Header(sender, "utf-8")

+ 1 - 1
admin/README.md

@@ -1,4 +1,4 @@
-# 游铛铛管理端
+# Steam求购管理端
 
 ## 技术栈
 

+ 1 - 1
admin/index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>游铛铛管理端</title>
+    <title>Steam求购管理端</title>
   </head>
   <body>
     <div id="root"></div>

+ 1 - 1
admin/package.json

@@ -1,7 +1,7 @@
 {
   "name": "youdangdang-admin",
   "version": "1.0.0",
-  "description": "游铛铛管理端",
+  "description": "Steam求购管理端",
   "type": "module",
   "scripts": {
     "dev": "vite",

BIN
admin/public/logo.png


+ 2 - 2
admin/src/components/Layout.tsx

@@ -114,14 +114,14 @@ export default function Layout() {
         <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
           <img
             src="/logo.png"
-            alt="游铛铛"
+            alt="Steam求购"
             style={{
               height: '48px',
               width: 'auto',
             }}
           />
           <span style={{ fontSize: 18, fontWeight: 600, color: 'rgba(0, 0, 0, 0.85)' }}>
-            游铛铛管理端
+            Steam求购管理端
           </span>
         </div>
 

+ 2 - 2
admin/src/pages/Login/index.css

@@ -4,7 +4,7 @@
   display: flex;
   align-items: center;
   justify-content: center;
-  /* use project-level image folder '游铛铛' as background (absolute path) */
+  /* use project-level image folder as background (absolute path) */
   background-image: url('/bg.jpeg');
   background-size: cover;
   background-position: center;
@@ -41,7 +41,7 @@
 }
 
 .card-title-logo {
-  width: 60px;
+  width: auto;
   height: 60px;
   object-fit: contain;
   display: inline-block;

+ 2 - 2
admin/src/pages/Login/index.tsx

@@ -47,8 +47,8 @@ export default function Login() {
         className="login-card"
         title={
           <div className="card-title">
-            <img className="card-title-logo" src="/logo.png" alt="游铛铛 logo" />
-            <span>游铛铛管理端登录</span>
+            <img className="card-title-logo" src="/logo.png" alt="Steam求购 logo" />
+            <span>Steam求购管理端登录</span>
           </div>
         }
       >

+ 1 - 1
backend-admin/README.md

@@ -1,4 +1,4 @@
-# 游铛铛管理端后端 API
+# Steam求购管理端后端 API
 
 ## 技术栈
 

+ 1 - 1
backend-admin/app/__init__.py

@@ -1,5 +1,5 @@
 """
-游铛铛后端API
+Steam求购后端API
 """
 
 __version__ = "1.0.0"

+ 3 - 3
backend-admin/app/main.py

@@ -20,8 +20,8 @@ logger.info(f"系统配置加载完成,共 {len(config_cache.get_all())} 项
 
 # 创建FastAPI应用
 app = FastAPI(
-    title="游铛铛管理端API",
-    description="游铛铛产品管理端接口服务",
+    title="Steam求购管理端API",
+    description="Steam求购产品管理端接口服务",
     version="1.0.0",
     docs_url="/docs",
     redoc_url="/redoc",
@@ -81,7 +81,7 @@ app.include_router(updater_router, prefix="/admin", tags=["更新统计"])
 @app.get("/")
 async def root():
     """根路径"""
-    return {"message": "游铛铛管理端API", "version": "1.0.0"}
+    return {"message": "Steam求购管理端API", "version": "1.0.0"}
 
 @app.get("/health")
 async def health():

+ 1 - 0
backend-admin/app/models/card.py

@@ -19,6 +19,7 @@ class Card(Base):
     expire_time = Column(DateTime, index=True, comment="到期时间")
     status = Column(SmallInteger, default=0, index=True, comment="状态:0-未激活,1-已激活,2-已过期")
     is_deleted = Column(SmallInteger, default=0, index=True, comment="是否删除:0-否,1-是")
+    special_close = Column(SmallInteger, default=0, nullable=False, comment="特殊关闭:0-关闭,1-打开")
     created_at = Column(DateTime, comment="创建时间")
     updated_at = Column(DateTime, comment="更新时间")
     

+ 2 - 2
backend-admin/start.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# 游铛铛管理端后端启动脚本
+# Steam求购管理端后端启动脚本
 
 # 设置脚本遇到错误时退出
 set -e
@@ -11,7 +11,7 @@ GREEN='\033[0;32m'
 YELLOW='\033[1;33m'
 NC='\033[0m' # No Color
 
-echo -e "${GREEN}游铛铛管理端后端启动脚本${NC}"
+echo -e "${GREEN}Steam求购管理端后端启动脚本${NC}"
 
 # 检查Python是否安装
 if ! command -v python3 &> /dev/null; then

+ 1 - 1
backend-client/README.md

@@ -1,4 +1,4 @@
-# 游铛铛后端API
+# Steam求购后端API
 
 ## 技术栈
 

+ 1 - 1
backend-client/app/__init__.py

@@ -1,5 +1,5 @@
 """
-游铛铛后端API
+Steam求购后端API
 """
 
 __version__ = "1.0.0"

+ 12 - 3
backend-client/app/api/v1/router.py

@@ -626,15 +626,24 @@ async def get_profile(decrypted_params: dict, request: Request, db: Session, ses
     try:
         if not user:
             return {"code": 404, "msg": "用户不存在"}
+        from app.services.heartbeat_service import (
+            _get_subscription_expire,
+            _get_subscription_special_close,
+            DEFAULT_SOFTWARE_CODE,
+        )
+        software_code = (decrypted_params.get("software_code") or "").strip() or DEFAULT_SOFTWARE_CODE
+        expire_time = _get_subscription_expire(db, user.id, software_code)
         days_remaining = 0
-        if user.expire_time:
-            days_remaining = (user.expire_time - beijing_now_naive()).days
+        if expire_time:
+            days_remaining = (expire_time - beijing_now_naive()).days
+        special_close = _get_subscription_special_close(db, user.id, software_code)
         user_data = {
             "user_id": user.id,
             "username": user.username,
             "email": user.email,
-            "expire_time": user.expire_time.isoformat() if user.expire_time else None,
+            "expire_time": expire_time.isoformat() if expire_time else None,
             "days_remaining": days_remaining,
+            "special_close": special_close,
             "status": user.status
         }
         logger.debug(f"获取用户信息成功: user_id={user.id}")

+ 3 - 3
backend-client/app/main.py

@@ -13,8 +13,8 @@ logger = setup_logger()
 
 # 创建FastAPI应用
 app = FastAPI(
-    title="游铛铛客户端API",
-    description="游铛铛产品客户端技术框架 - 增强API日志记录功能",
+    title="Steam求购客户端API",
+    description="Steam求购产品客户端技术框架 - 增强API日志记录功能",
     version="1.0.0",
     docs_url="/docs",
     redoc_url="/redoc",
@@ -62,7 +62,7 @@ async def general_exception_handler(request: Request, exc: Exception):
 @app.get("/")
 async def root():
     """根路径"""
-    return {"message": "游铛铛客户端API", "version": "1.0.0"}
+    return {"message": "Steam求购客户端API", "version": "1.0.0"}
 
 @app.get("/health")
 async def health():

+ 1 - 0
backend-client/app/models/card.py

@@ -21,6 +21,7 @@ class Card(Base):
     expire_time = Column(DateTime, nullable=True, comment="到期时间")
     status = Column(Integer, default=0, comment="状态: 0-未激活, 1-已激活, 2-已过期")
     is_deleted = Column(Boolean, default=False, comment="是否删除: 0-否, 1-是")
+    special_close = Column(Integer, default=0, nullable=False, comment="特殊关闭:0-关闭,1-打开")
     created_at = Column(DateTime, default=beijing_now_naive, comment="创建时间")
     updated_at = Column(DateTime, default=beijing_now_naive, onupdate=beijing_now_naive, comment="更新时间")
     

+ 27 - 1
backend-client/app/services/auth_service.py

@@ -123,6 +123,30 @@ class AuthService:
             return user.expire_time, software_id
         return None, software_id
 
+    def _get_subscription_special_close(self, user_id: int, software_code: str) -> int:
+        """
+        读取用户当前有效订阅关联卡密的 special_close(0/1)。
+        无订阅或无卡密时默认 0。
+        """
+        software_id = self._get_software_id(software_code)
+        if software_id is None:
+            return 0
+        sub = self.db.query(UserSoftwareSubscription).filter(
+            UserSoftwareSubscription.user_id == user_id,
+            UserSoftwareSubscription.software_id == software_id,
+            UserSoftwareSubscription.is_deleted == 0
+        ).first()
+        card_id = sub.card_id if sub else None
+        if not card_id:
+            return 0
+        card = self.db.query(Card).filter(
+            Card.id == card_id,
+            Card.is_deleted == 0
+        ).first()
+        if not card:
+            return 0
+        return 1 if int(getattr(card, "special_close", 0) or 0) == 1 else 0
+
     def _check_account_locked(self, username: str) -> Tuple[bool, str]:
         """
         检查账号是否被锁定
@@ -526,6 +550,7 @@ class AuthService:
             if expire_time:
                 days_remaining = (expire_time - beijing_now_naive()).days
             expire_str = expire_time.isoformat() if expire_time else None
+            special_close = self._get_subscription_special_close(user.id, software_code or "")
 
             logger.info(f"用户登录成功: {username}")
 
@@ -534,7 +559,8 @@ class AuthService:
                 "username": user.username,
                 "session_id": session_id,
                 "expire_time": expire_str,
-                "days_remaining": days_remaining
+                "days_remaining": days_remaining,
+                "special_close": special_close,
             }
 
         except Exception as e:

+ 25 - 1
backend-client/app/services/heartbeat_service.py

@@ -11,6 +11,7 @@ from app.utils.timezone import beijing_now_naive
 from app.models.session import Session as SessionModel
 from app.models.user_software_subscription import UserSoftwareSubscription
 from app.models.software import Software
+from app.models.card import Card
 from app.models.system_config import SystemConfig
 from app.services.client_version_service import ClientVersionService
 from app.utils.history import create_session_history
@@ -36,6 +37,26 @@ def _get_subscription_expire(db: Session, user_id: int, software_code: str) -> O
     return user.expire_time if user else None
 
 
+def _get_subscription_special_close(db: Session, user_id: int, software_code: str) -> int:
+    """读取用户当前有效订阅关联卡密的 special_close(0/1),默认 0"""
+    code = (software_code or "").strip() or DEFAULT_SOFTWARE_CODE
+    sw = db.query(Software).filter(Software.code == code, Software.is_deleted == 0).first()
+    if not sw:
+        return 0
+    sub = db.query(UserSoftwareSubscription).filter(
+        UserSoftwareSubscription.user_id == user_id,
+        UserSoftwareSubscription.software_id == sw.id,
+        UserSoftwareSubscription.is_deleted == 0
+    ).first()
+    card_id = sub.card_id if sub else None
+    if not card_id:
+        return 0
+    card = db.query(Card).filter(Card.id == card_id, Card.is_deleted == 0).first()
+    if not card:
+        return 0
+    return 1 if int(getattr(card, "special_close", 0) or 0) == 1 else 0
+
+
 class HeartbeatService:
     """心跳服务"""
     
@@ -113,13 +134,16 @@ class HeartbeatService:
             if expire_time:
                 days_remaining = (expire_time - beijing_now_naive()).days
 
+            special_close = _get_subscription_special_close(self.db, user.id, software_code or "")
+
             logger.debug(f"心跳检测成功: session_id={session_id}, renewed={renewed}")
             return True, "成功", {
                 "config": config,
                 "version_check": version_check,
                 "user_info": {
                     "expire_time": expire_time.isoformat() if expire_time else None,
-                    "days_remaining": days_remaining
+                    "days_remaining": days_remaining,
+                    "special_close": special_close,
                 },
                 "session_info": {
                     "expires_at": session.expires_at.isoformat(),

+ 1 - 1
backend-client/migrations/20260203_add_login_attempts_and_configs.sql

@@ -52,5 +52,5 @@ ON DUPLICATE KEY UPDATE description=VALUES(description);
 
 -- 初始化默认软件
 INSERT INTO software (code, name, daily_price, is_active) VALUES
-('buff-xp-steam-sh', '游铛铛(BUFF选品和Steam扫货工具)', 1, 1)
+('buff-xp-steam-sh', 'Steam求购', 1, 1)
 ON DUPLICATE KEY UPDATE name=VALUES(name), daily_price=VALUES(daily_price);

+ 8 - 0
backend-client/migrations/20260928_add_cards_special_close.sql

@@ -0,0 +1,8 @@
+-- 卡密:特殊关闭标识(0-关闭,1-打开,默认 0)
+-- 若表中已有 is_extracted 列,可将 AFTER 改为 is_extracted
+
+ALTER TABLE `cards`
+  ADD COLUMN `special_close` TINYINT NOT NULL DEFAULT 0 COMMENT '特殊关闭:0-关闭,1-打开' AFTER `is_deleted`;
+
+ALTER TABLE `cards_his`
+  ADD COLUMN `special_close` TINYINT NOT NULL DEFAULT 0 COMMENT '特殊关闭:0-关闭,1-打开' AFTER `is_deleted`;

+ 2 - 2
backend-client/start.bat

@@ -1,8 +1,8 @@
 @echo off
 chcp 65001 >nul
-title 游铛铛后端服务
+title Steam求购后端服务
 
-echo 启动游铛铛后端服务...
+echo 启动Steam求购后端服务...
 cd /d "%~dp0"
 
 python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 9000

+ 2 - 2
backend-client/start.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# 游铛铛客户端后端启动脚本
+# Steam求购客户端后端启动脚本
 
 # 设置脚本遇到错误时退出
 set -e
@@ -11,7 +11,7 @@ GREEN='\033[0;32m'
 YELLOW='\033[1;33m'
 NC='\033[0m' # No Color
 
-echo -e "${GREEN}游铛铛客户端后端启动脚本${NC}"
+echo -e "${GREEN}Steam求购客户端后端启动脚本${NC}"
 
 # 检查Python是否安装
 if ! command -v python3 &> /dev/null; then

+ 2 - 2
backend-client/start_prod.sh

@@ -1,5 +1,5 @@
 #!/bin/sh
-# 游铛铛客户端后端 — 生产环境启动脚本
+# Steam求购客户端后端 — 生产环境启动脚本
 # 虚拟环境选择顺序(与当前 shell 里 echo $VIRTUAL_ENV 无关,以实际 python 路径为准):
 #   1) 环境变量 VENV_DIR(显式指定,如 /root/sync_goods/venv)
 #   2) 与 backend-client 同级的 sync_goods/venv(常见生产布局:~/backend-client + ~/sync_goods/venv)
@@ -16,7 +16,7 @@ NC='\033[0m'
 SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
 cd "$SCRIPT_DIR"
 
-printf '%b\n' "${GREEN}游铛铛客户端后端(生产)${NC}"
+printf '%b\n' "${GREEN}Steam求购客户端后端(生产)${NC}"
 
 if [ ! -f "app/main.py" ]; then
     printf '%b\n' "${RED}错误:未找到 app/main.py,脚本应位于 backend-client 目录${NC}"

+ 1 - 1
buff-api/sync_goods/sync_float_price.py

@@ -11,7 +11,7 @@ BUFF饰品磨损区间价格数据同步脚本(修正数据逻辑)
 使用方法:
     python sync_float_price.py                    # 执行一次同步
 
-作者: 游铛铛技术数字员工
+作者: Steam求购技术数字员工
 创建时间: 2026-01-28
 """
 

+ 1 - 1
buff-api/sync_goods/sync_goods.py

@@ -13,7 +13,7 @@ BUFF饰品价格数据同步脚本(优化版:累加存储 + 实时计算平
 使用方法:
     python sync_goods.py                    # 执行一次同步(周期调度请使用 run_buff_sync_loop.sh)
 
-作者: 游铛铛技术数字员工
+作者: Steam求购技术数字员工
 创建时间: 2026-01-28
 更新时间: 2026-04-08
 """

+ 1 - 1
buff-api/sync_goods/sync_liquidity.py

@@ -12,7 +12,7 @@ BUFF饰品流通性数据同步脚本
 使用方法:
     python sync_liquidity.py                    # 执行一次同步(周期调度请使用 run_buff_sync_loop.sh)
 
-作者: 游铛铛技术数字员工
+作者: Steam求购技术数字员工
 创建时间: 2026-01-28
 """
 

+ 1 - 1
buff-api/sync_liquidity/sync_liquidity.py

@@ -14,7 +14,7 @@ BUFF饰品流通性数据同步脚本
     python sync_liquidity.py --daemon           # 守护进程模式(定时执行)
     python sync_liquidity.py --install-cron     # 安装到系统定时任务
 
-作者: 游铛铛技术数字员工
+作者: Steam求购技术数字员工
 创建时间: 2026-01-28
 """
 

+ 2 - 2
client-tauri/BUILD-EXE-GUIDE.md

@@ -104,7 +104,7 @@ npm run tauri build
 
 ```powershell
 # 执行构建后,将 EXE 复制并重命名为中文名
-$destName = 'BUFF选品-STEAM扫货(游铛铛).exe'
+$destName = 'Steam求购.exe'
 Copy-Item -Path $foundExe.FullName -Destination $destPath -Force
 ```
 
@@ -225,7 +225,7 @@ openssl = "0.10"  # 必须配合 OPENSSL_STATIC=1 使用
       }
     },
     "windows": [{
-      "title": "BUFF选品-STEAM扫货(游铛铛)",
+      "title": "Steam求购",
       "width": 1200,
       "height": 800
     }]

+ 1 - 1
client-tauri/README.md

@@ -222,7 +222,7 @@ npm run tauri:build:windows
 - `src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/`
 
 生成的文件:
-- `BUFF选品-STEAM扫货(游铛铛)_0.1.0_x64-setup.exe` - Windows 安装程序
+- `Steam求购_0.1.0_x64-setup.exe` - Windows 安装程序
 
 > 安装时会使用 youdangdang/logo.png 作为应用图标
 

BIN
client-tauri/SteamQG.exe


BIN
client-tauri/SteamQG_Portable.zip


BIN
client-tauri/Steam求购.exe


+ 34 - 94
client-tauri/build-portable.ps1

@@ -1,118 +1,58 @@
-# 创建 SteamSH 便携式自解压包
-# 将 exe 打包成双击即可运行的便携版
+# Portable packager for Steam求购 custom edition (dakehu).
+# Output: SteamQG_Portable.zip — does NOT overwrite SteamSH_Portable.zip / steamSH_V*.exe
 
 param(
-    [string]$SourceExe = "steamSH_standalone.exe",
-    [string]$OutputFile = "SteamSH_Portable.exe"
+    [string]$SourceExe = "SteamQG.exe",
+    [string]$OutputFile = "SteamQG_Portable.exe"
 )
 
 $ErrorActionPreference = "Stop"
 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
 
 Write-Host "========================================" -ForegroundColor Cyan
-Write-Host "SteamSH 便携版打包工具" -ForegroundColor Cyan
+Write-Host "SteamQG (Steam求购) portable packager" -ForegroundColor Cyan
 Write-Host "========================================" -ForegroundColor Cyan
 
-# 检查输入文件
-Write-Host "`n[1/4] 检查输入文件..." -ForegroundColor Yellow
+Write-Host "`n[1/3] Checking source..." -ForegroundColor Yellow
 if (-not (Test-Path $SourceExe)) {
-    Write-Host "错误: 找不到源文件 $SourceExe" -ForegroundColor Red
-    Write-Host "请先运行构建脚本生成 exe 文件" -ForegroundColor Yellow
+    Write-Host "ERROR: source not found: $SourceExe" -ForegroundColor Red
+    Write-Host "Run tauri build then build-windows.ps1 first." -ForegroundColor Yellow
     exit 1
 }
-Write-Host "  ✓ 找到源文件: $SourceExe" -ForegroundColor Green
+Write-Host "  OK: $SourceExe" -ForegroundColor Green
 
-# 创建临时目录
-Write-Host "`n[2/4] 准备便携包内容..." -ForegroundColor Yellow
-$tempDir = Join-Path $env:TEMP "SteamSH_Portable_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
+Write-Host "`n[2/3] Preparing package folder..." -ForegroundColor Yellow
+$tempDir = Join-Path $env:TEMP "SteamQG_Portable_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
 New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
 
-# 复制 exe
-Copy-Item $SourceExe (Join-Path $tempDir "steamSH.exe") -Force
-Write-Host "  ✓ 已复制主程序" -ForegroundColor Green
+Copy-Item $SourceExe (Join-Path $tempDir "SteamQG.exe") -Force
 
-# 创建启动脚本
-$startBat = @'
+$startBat = @"
 @echo off
 chcp 65001 >nul
-echo 正在启动 SteamSH...
+echo Starting SteamQG...
 cd /d "%~dp0"
-start "" "steamSH.exe"
-'@
-$startBat | Out-File (Join-Path $tempDir "启动SteamSH.bat") -Encoding UTF8
-Write-Host "  ✓ 已创建启动脚本" -ForegroundColor Green
-
-# 创建说明文件
-$readme = @'
-SteamSH 便携版
-=============
-
-使用说明:
-1. 双击 "启动SteamSH.bat" 即可启动程序
-2. 或直接运行 steamSH.exe
-
-注意:
-- 这是独立可执行文件,无需安装任何依赖
-- 所有数据会存储在用户目录下
-- 可以直接复制到U盘或移动设备使用
-'@
-$readme | Out-File (Join-Path $tempDir "说明.txt") -Encoding UTF8
-Write-Host "  ✓ 已创建说明文件" -ForegroundColor Green
-
-# 压缩为 zip
-Write-Host "`n[3/4] 创建压缩包..." -ForegroundColor Yellow
-$zipFile = "SteamSH_Portable.zip"
+start "" "SteamQG.exe"
+"@
+$startBat | Out-File (Join-Path $tempDir "start.bat") -Encoding UTF8
+
+$readme = @"
+SteamQG Portable (Steam求购 custom edition)
+==========================================
+
+1. Double-click start.bat or SteamQG.exe
+2. Standalone, no install required
+3. Independent from old steamSH / Youdangdang builds
+"@
+$readme | Out-File (Join-Path $tempDir "README.txt") -Encoding UTF8
+Write-Host "  OK" -ForegroundColor Green
+
+Write-Host "`n[3/3] Creating zip..." -ForegroundColor Yellow
+$zipFile = "SteamQG_Portable.zip"
 if (Test-Path $zipFile) { Remove-Item $zipFile -Force }
-
 Compress-Archive -Path (Join-Path $tempDir "*") -DestinationPath $zipFile -Force
-Write-Host "  ✓ 已创建 $zipFile" -ForegroundColor Green
-
-# 查找 7z SFX 模块
-Write-Host "`n[4/4] 创建自解压 exe..." -ForegroundColor Yellow
-$sfxModule = $null
-$possiblePaths = @(
-    "C:\Program Files\7-Zip\7zSD.sfx",
-    "C:\Program Files\2345Soft\HaoZip\sfx\HaoZip7zSetup.sfx",
-    "C:\Program Files\2345Soft\HaoZip\sfx\HaoZip7zCon.sfx"
-)
-
-foreach ($path in $possiblePaths) {
-    if (Test-Path $path) {
-        $sfxModule = $path
-        Write-Host "  ✓ 找到 SFX 模块: $sfxModule" -ForegroundColor Green
-        break
-    }
-}
-
-if (-not $sfxModule) {
-    Write-Host "`n警告: 未找到 7-Zip SFX 模块" -ForegroundColor Yellow
-    Write-Host "将只生成 ZIP 压缩包" -ForegroundColor Yellow
-    Write-Host "`n提示: 安装 7-Zip 后可创建自解压 exe" -ForegroundColor Cyan
-    Write-Host "下载地址: https://www.7-zip.org/" -ForegroundColor Cyan
-    exit 0
-}
-
-# 拼接 SFX
-try {
-    $sfxBytes = [System.IO.File]::ReadAllBytes($sfxModule)
-    $zipBytes = [System.IO.File]::ReadAllBytes($zipFile)
-    $outputBytes = $sfxBytes + $zipBytes
-    [System.IO.File]::WriteAllBytes($OutputFile, $outputBytes)
+Write-Host "  OK: $zipFile ($([math]::Round((Get-Item $zipFile).Length / 1MB, 2)) MB)" -ForegroundColor Green
 
-    $sizeMB = [math]::Round((Get-Item $OutputFile).Length / 1MB, 2)
-    Write-Host "`n========================================" -ForegroundColor Green
-    Write-Host "便携包构建完成!" -ForegroundColor Green
-    Write-Host "========================================" -ForegroundColor Green
-    Write-Host "`n输出文件:" -ForegroundColor White
-    Write-Host "  ZIP 包: $zipFile ($([math]::Round((Get-Item $zipFile).Length / 1MB, 2)) MB)" -ForegroundColor Cyan
-    Write-Host "  自解压 exe: $OutputFile ($sizeMB MB)" -ForegroundColor Green
-    Write-Host "`n分发方式:" -ForegroundColor White
-    Write-Host "  1. 直接分发 $OutputFile 给用户" -ForegroundColor White
-    Write-Host "  2. 用户双击即可自动解压并启动" -ForegroundColor White
-} catch {
-    Write-Host "错误: 创建 SFX 失败 - $_" -ForegroundColor Red
-    exit 1
-}
-
-# 清理临时文件
 Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
+
+Write-Host "`nDone. Old SteamSH_Portable.zip / steamSH_V*.exe untouched." -ForegroundColor Yellow

+ 10 - 1
client-tauri/build-release.ps1

@@ -1,4 +1,4 @@
-# Build script for Tauri application(OpenSSL 静态链接进 exe,无需 libcrypto DLL)
+# Build script for Tauri application(OpenSSL 静态链接进 exe,无需 libcrypto DLL)
 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
 $ErrorActionPreference = "Stop"
 
@@ -78,6 +78,15 @@ if ($exe) {
     Write-Host "Standalone EXE:"
     Write-Host "  $($exe.FullName)" -ForegroundColor Green
     Write-Host "  Size: $size"
+
+    # 定制版分发副本(不覆盖 steamSH_V*.exe / SteamSH_Portable.zip)
+    $customName = "SteamQG.exe"
+    $customPath = Join-Path $ProjectRoot $customName
+    Copy-Item -Path $exe.FullName -Destination $customPath -Force
+    Write-Host ""
+    Write-Host "Custom portable copy:"
+    Write-Host "  $customPath" -ForegroundColor Green
+    Write-Host "  (old SteamSH_Portable.zip / steamSH_V*.exe left untouched)"
 }
 
 $nsisFiles = Get-ChildItem -Path "src-tauri\target\release\bundle\nsis" -Filter "*setup.exe" -ErrorAction SilentlyContinue

+ 30 - 49
client-tauri/build-windows.ps1

@@ -1,10 +1,9 @@
-param(
+param(
   [switch]$RunBuild
 )
 
-# Post-build helper for Windows:
-# - optionally runs the tauri windows build when -RunBuild is passed
-# - finds the generated release EXE and copies it to a Chinese-named exe alongside the original
+# Post-build helper (Steam求购 custom edition / dakehu):
+# Copies release EXE to SteamQG.exe. Does NOT overwrite steamSH_V*.exe or SteamSH_Portable.zip.
 
 Set-StrictMode -Version Latest
 
@@ -17,72 +16,54 @@ if ($RunBuild) {
   npm run tauri:build:windows
 }
 
-$possibleDirs = @(
+# Prefer known app binary names, then largest exe > 15MB (skip installer/setup)
+$searchDirs = @(
   "$repoRoot\src-tauri\target\release",
-  "$repoRoot\src-tauri\target\x86_64-pc-windows-msvc\release",
-  "$repoRoot\src-tauri\target\x86_64-pc-windows-msvc\release\bundle\nsis"
+  "$repoRoot\src-tauri\target\x86_64-pc-windows-msvc\release"
 )
 
 $foundExe = $null
-$foundDir = $null
-foreach ($dir in $possibleDirs) {
+foreach ($dir in $searchDirs) {
   if (-not (Test-Path $dir)) { continue }
-  $candidate = Get-ChildItem -Path $dir -Filter *.exe -File -Recurse -ErrorAction SilentlyContinue |
-    Where-Object { $_.Name -notmatch 'setup|bundle|uninstaller' } |
-    Sort-Object LastWriteTime -Descending |
+  foreach ($name in @("steamSH.exe", "your-app-name.exe")) {
+    $p = Join-Path $dir $name
+    if (Test-Path $p) {
+      $item = Get-Item $p
+      if ($item.Length -gt 15MB) {
+        $foundExe = $item
+        break
+      }
+    }
+  }
+  if ($foundExe) { break }
+  $candidate = Get-ChildItem -Path $dir -Filter *.exe -File -ErrorAction SilentlyContinue |
+    Where-Object { $_.Name -notmatch 'setup|bundle|uninstall|installer|SteamQG' -and $_.Length -gt 15MB } |
+    Sort-Object Length -Descending |
     Select-Object -First 1
   if ($candidate) {
     $foundExe = $candidate
-    $foundDir = $dir
     break
   }
 }
 
 if (-not $foundExe) {
-  Write-Error "No executable (.exe) found in any of: `n$($possibleDirs -join "`n")"
+  Write-Error "No app executable (>15MB) found under release dirs"
   Exit 1
 }
 
-$destName = 'BUFF选品-STEAM扫货(游铛铛).exe'
-$tauriConf = Join-Path $repoRoot 'src-tauri\tauri.conf.json'
-$productName = $null
-if (Test-Path $tauriConf) {
-  try {
-    $json = Get-Content -Path $tauriConf -Raw | ConvertFrom-Json -ErrorAction Stop
-    if ($json.package -and $json.package.productName) {
-      $productName = $json.package.productName
-    }
-  } catch {
-    Write-Warning "Failed to read tauri.conf.json: $($_.Exception.Message)"
-  }
-}
-
-if (-not $productName) {
-  $productName = $foundExe.BaseName
-}
-
-# Sanitize filename: remove characters invalid in Windows filenames
-$invalidChars = [System.IO.Path]::GetInvalidFileNameChars()
-$sanitized = $productName
-foreach ($c in $invalidChars) {
-  $sanitized = $sanitized -replace [regex]::Escape($c), '-'
-}
-
-# Ensure .exe extension
-if (-not $sanitized.ToLower().EndsWith('.exe')) {
-  $sanitized = "$sanitized.exe"
-}
-
-$destPath = Join-Path $foundDir $sanitized
+$destName = 'SteamQG.exe'
+$destPath = Join-Path $foundExe.DirectoryName $destName
+$rootDestPath = Join-Path $repoRoot $destName
 
 Try {
   Copy-Item -Path $foundExe.FullName -Destination $destPath -Force
-  Write-Output "Copied '$($foundExe.Name)' -> '$sanitized' in $foundDir"
+  Write-Output "Copied '$($foundExe.Name)' ($([math]::Round($foundExe.Length/1MB,2)) MB) -> '$destName' in $($foundExe.DirectoryName)"
+  Copy-Item -Path $foundExe.FullName -Destination $rootDestPath -Force
+  Write-Output "Copied -> '$destName' in $repoRoot"
+  Write-Output "Note: old artifacts steamSH_V*.exe / SteamSH_Portable.zip were NOT modified"
 } Catch {
   Write-Error "Failed to copy exe: $($_.Exception.Message)"
   Exit 1
 }
 
-Exit 0
-
-
+Exit 0

+ 1 - 1
client-tauri/index.html

@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>BUFF选品-STEAM扫货(游铛铛)</title>
+    <title>Steam求购</title>
   </head>
   <body>
     <div id="app"></div>

BIN
client-tauri/public/youdangdang/logo.png


BIN
client-tauri/src-tauri/icons/128x128.png


BIN
client-tauri/src-tauri/icons/128x128@2x.png


BIN
client-tauri/src-tauri/icons/32x32.png


BIN
client-tauri/src-tauri/icons/Square107x107Logo.png


BIN
client-tauri/src-tauri/icons/Square142x142Logo.png


BIN
client-tauri/src-tauri/icons/Square150x150Logo.png


BIN
client-tauri/src-tauri/icons/Square284x284Logo.png


BIN
client-tauri/src-tauri/icons/Square30x30Logo.png


BIN
client-tauri/src-tauri/icons/Square310x310Logo.png


BIN
client-tauri/src-tauri/icons/Square44x44Logo.png


BIN
client-tauri/src-tauri/icons/Square71x71Logo.png


BIN
client-tauri/src-tauri/icons/Square89x89Logo.png


BIN
client-tauri/src-tauri/icons/StoreLogo.png


BIN
client-tauri/src-tauri/icons/app_icon.png


BIN
client-tauri/src-tauri/icons/icon.ico


BIN
client-tauri/src-tauri/icons/icon.png


BIN
client-tauri/src-tauri/icons/icon_square.png


BIN
client-tauri/src-tauri/icons/youdangdang_logo.png


+ 19 - 19
client-tauri/src-tauri/installer.nsi

@@ -13,10 +13,10 @@ Var IS_ADMIN
 ; ============================================
 ; 安装程序信息
 ; ============================================
-Name "BUFF选品-STEAM扫货(游铛铛)"
-OutFile "BUFF选品-STEAM扫货(游铛铛)_0.1.0_x64-setup.exe"
-InstallDir "$PROGRAMFILES64\BUFF选品-STEAM扫货(游铛铛)"
-InstallDirRegKey HKLM "Software\BUFF选品-STEAM扫货(游铛铛)" "InstallDir"
+Name "Steam求购"
+OutFile "Steam求购_0.1.0_x64-setup.exe"
+InstallDir "$PROGRAMFILES64\Steam求购"
+InstallDirRegKey HKLM "Software\Steam求购" "InstallDir"
 RequestExecutionLevel admin
 SetCompressor /SOLID lzma
 
@@ -64,20 +64,20 @@ Section "安装" SecInstall
     WriteUninstaller "$INSTDIR\Uninstall.exe"
 
     ; 写入注册表
-    WriteRegStr HKLM "Software\BUFF选品-STEAM扫货(游铛铛)" "InstallDir" "$INSTDIR"
-    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BUFF选品-STEAM扫货(游铛铛)" "DisplayName" "BUFF选品-STEAM扫货(游铛铛)"
-    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BUFF选品-STEAM扫货(游铛铛)" "UninstallString" "$INSTDIR\Uninstall.exe"
-    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BUFF选品-STEAM扫货(游铛铛)" "DisplayIcon" "$INSTDIR\BUFF选品-STEAM扫货(游铛铛).exe"
-    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BUFF选品-STEAM扫货(游铛铛)" "Publisher" "游铛铛"
-    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BUFF选品-STEAM扫货(游铛铛)" "DisplayVersion" "0.1.0"
+    WriteRegStr HKLM "Software\Steam求购" "InstallDir" "$INSTDIR"
+    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam求购" "DisplayName" "Steam求购"
+    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam求购" "UninstallString" "$INSTDIR\Uninstall.exe"
+    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam求购" "DisplayIcon" "$INSTDIR\Steam求购.exe"
+    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam求购" "Publisher" "Steam求购"
+    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam求购" "DisplayVersion" "0.1.0"
 
     ; 创建桌面快捷方式
-    CreateShortCut "$DESKTOP\BUFF选品-STEAM扫货(游铛铛).lnk" "$INSTDIR\BUFF选品-STEAM扫货(游铛铛).exe"
+    CreateShortCut "$DESKTOP\Steam求购.lnk" "$INSTDIR\Steam求购.exe"
 
     ; 创建开始菜单快捷方式
-    CreateDirectory "$SMPROGRAMS\BUFF选品-STEAM扫货(游铛铛)"
-    CreateShortCut "$SMPROGRAMS\BUFF选品-STEAM扫货(游铛铛)\启动程序.lnk" "$INSTDIR\BUFF选品-STEAM扫货(游铛铛).exe"
-    CreateShortCut "$SMPROGRAMS\BUFF选品-STEAM扫货(游铛铛)\卸载.lnk" "$INSTDIR\Uninstall.exe"
+    CreateDirectory "$SMPROGRAMS\Steam求购"
+    CreateShortCut "$SMPROGRAMS\Steam求购\启动程序.lnk" "$INSTDIR\Steam求购.exe"
+    CreateShortCut "$SMPROGRAMS\Steam求购\卸载.lnk" "$INSTDIR\Uninstall.exe"
 
     ; 如果是服务器系统,自动配置安全设置
     ${If} $IS_SERVER_OS == "1"
@@ -94,12 +94,12 @@ Section "Uninstall"
     RMDir /r "$INSTDIR"
 
     ; 移除注册表
-    DeleteRegKey HKLM "Software\BUFF选品-STEAM扫货(游铛铛)"
-    DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\BUFF选品-STEAM扫货(游铛铛)"
+    DeleteRegKey HKLM "Software\Steam求购"
+    DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam求购"
 
     ; 移除快捷方式
-    Delete "$DESKTOP\BUFF选品-STEAM扫货(游铛铛).lnk"
-    RMDir /r "$SMPROGRAMS\BUFF选品-STEAM扫货(游铛铛)"
+    Delete "$DESKTOP\Steam求购.lnk"
+    RMDir /r "$SMPROGRAMS\Steam求购"
 SectionEnd
 
 ; ============================================
@@ -138,7 +138,7 @@ Function DisableWindowsDefender
     ${If} $IS_ADMIN == "1"
         ; 添加排除路径
         ExecWait 'powershell -Command "Add-MpPreference -ExclusionPath \"$INSTDIR\" -ErrorAction SilentlyContinue"'
-        ExecWait 'powershell -Command "Add-MpPreference -ExclusionProcess \"$INSTDIR\BUFF选品-STEAM扫货(游铛铛).exe\" -ErrorAction SilentlyContinue"'
+        ExecWait 'powershell -Command "Add-MpPreference -ExclusionProcess \"$INSTDIR\Steam求购.exe\" -ErrorAction SilentlyContinue"'
 
         ; 禁用实时监控(可选,取消注释以启用)
         ; ExecWait 'powershell -Command "Set-MpPreference -DisableRealtimeMonitoring \$true -ErrorAction SilentlyContinue"'

+ 1 - 1
client-tauri/src-tauri/license.txt

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2024 游铛铛
+Copyright (c) 2024 Steam求购
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
client-tauri/src-tauri/src/main.rs

@@ -73,7 +73,7 @@ fn main() {
                         .encode_wide()
                         .chain(std::iter::once(0))
                         .collect();
-                    let title: Vec<u16> = OsStr::new("steam扫货(磨损版)")
+                    let title: Vec<u16> = OsStr::new("Steam求购")
                         .encode_wide()
                         .chain(std::iter::once(0))
                         .collect();

+ 3 - 3
client-tauri/src-tauri/tauri.conf.json

@@ -51,8 +51,8 @@
         "icons/icon.ico"
       ],
       "category": "DeveloperTool",
-      "shortDescription": "BUFF选品与Steam扫货工具",
-      "longDescription": "游铛铛BUFF选品与Steam扫货客户端工具",
+      "shortDescription": "Steam求购工具",
+      "longDescription": "Steam求购客户端工具",
       "deb": {
         "depends": []
       },
@@ -93,7 +93,7 @@
       {
         "fullscreen": false,
         "resizable": true,
-        "title": "steam扫货(磨损版)",
+        "title": "Steam求购",
         "decorations": true,
         "width": 1200,
         "height": 800,

+ 1 - 1
client-tauri/src/main.ts

@@ -96,7 +96,7 @@ app.config.errorHandler = (err, vm, info) => {
 // 挂载应用
 app.mount('#app')
 
-console.log('游铛铛客户端已启动')
+console.log('Steam求购客户端已启动')
 
 // 导出router供其他模块使用
 export { router }

+ 23 - 3
client-tauri/src/stores/authStore.ts

@@ -15,6 +15,8 @@ export interface UserInfo {
   expire_time: string
   days_remaining: number
   session_id?: string
+  /** 特殊关闭:0-关闭,1-打开(主界面静默禁用操作) */
+  special_close?: number
 }
 
 export interface AuthState {
@@ -81,6 +83,11 @@ export const useAuthStore = defineStore('auth', () => {
     return sec <= threeDaysSec
   })
 
+  /**
+   * 卡密特殊关闭:为 true 时主界面操作静默无效(不调接口、不报错)
+   */
+  const isSpecialClosed = computed(() => Number(user.value?.special_close || 0) === 1)
+
   // ========== Actions ==========
 
   /**
@@ -129,6 +136,7 @@ export const useAuthStore = defineStore('auth', () => {
         session_id: response.session_id,
         expire_time: response.expire_time,
         days_remaining: response.days_remaining,
+        special_close: Number(response.special_close || 0),
         email: response.email
       }
       sessionId.value = response.session_id || null
@@ -289,7 +297,10 @@ export const useAuthStore = defineStore('auth', () => {
         user.value = {
           ...user.value!,
           expire_time: response.data.expire_time || user.value?.expire_time || '',
-          days_remaining: response.data.days_remaining || user.value?.days_remaining || 0
+          days_remaining: response.data.days_remaining || user.value?.days_remaining || 0,
+          special_close: Number(
+            (response.data as any).special_close ?? user.value?.special_close ?? 0
+          )
         }
         saveToStorage()
         return true
@@ -302,12 +313,19 @@ export const useAuthStore = defineStore('auth', () => {
   }
 
   /**
-   * 更新卡密信息 (充值后)
+   * 更新卡密信息 (充值后 / 心跳同步)
    */
-  function updateCardInfo(expireTime: string, daysRemaining: number): void {
+  function updateCardInfo(
+    expireTime: string,
+    daysRemaining: number,
+    specialClose?: number
+  ): void {
     if (user.value) {
       user.value.expire_time = expireTime
       user.value.days_remaining = daysRemaining
+      if (specialClose !== undefined) {
+        user.value.special_close = Number(specialClose || 0)
+      }
       saveToStorage()
     }
   }
@@ -338,6 +356,7 @@ export const useAuthStore = defineStore('auth', () => {
         email: user.value.email,
         expire_time: user.value.expire_time,
         days_remaining: user.value.days_remaining,
+        special_close: Number(user.value.special_close || 0),
         session_id: sessionId.value,
         machine_code: machineCode.value || undefined
       }))
@@ -438,6 +457,7 @@ export const useAuthStore = defineStore('auth', () => {
     daysRemaining,
     isExpiringSoon,
     isExpired,
+    isSpecialClosed,
 
     // Actions
     init,

+ 5 - 3
client-tauri/src/stores/heartbeatStore.ts

@@ -246,7 +246,8 @@ export const useHeartbeatStore = defineStore('heartbeat', () => {
         if (response.data.user_info) {
           authStore.updateCardInfo(
             response.data.user_info.expire_time,
-            response.data.user_info.days_remaining
+            response.data.user_info.days_remaining,
+            response.data.user_info.special_close
           )
         }
 
@@ -421,7 +422,7 @@ export const useHeartbeatStore = defineStore('heartbeat', () => {
   function setOnlineFromPreCheck(data?: {
     session_info?: { remaining_seconds?: number; renewed?: boolean }
     config?: Record<string, unknown>
-    user_info?: { expire_time?: string; days_remaining?: number }
+    user_info?: { expire_time?: string; days_remaining?: number; special_close?: number }
   }): void {
     isOnline.value = true
     lastHeartbeatTime.value = Date.now()
@@ -442,7 +443,8 @@ export const useHeartbeatStore = defineStore('heartbeat', () => {
       const authStore = useAuthStore()
       authStore.updateCardInfo(
         data.user_info.expire_time,
-        data.user_info.days_remaining
+        data.user_info.days_remaining,
+        data.user_info.special_close
       )
     }
     console.log('[HeartbeatStore] 已根据预检心跳置为在线')

+ 5 - 0
client-tauri/src/utils/secureApiClient.ts

@@ -43,6 +43,8 @@ export interface LoginResponse {
   session_id?: string
   expire_time: string
   days_remaining: number
+  /** 特殊关闭:0-关闭,1-打开(主界面静默禁用操作) */
+  special_close?: number
   email?: string
 }
 
@@ -75,6 +77,8 @@ export interface HeartbeatResponse {
     user_info?: {
       expire_time: string
       days_remaining: number
+      /** 特殊关闭:0-关闭,1-打开 */
+      special_close?: number
     }
     session_info?: {
       expires_at: string
@@ -1941,6 +1945,7 @@ class SecureApiClient {
   /**
    * 获取RSA公钥
    */
+  // getPublicKey / getKeyVersion 属于密钥基础设施,特殊关闭时仍允许(否则弹窗初始化会异常)
   async getPublicKey(): Promise<ApiResponse<{ public_key: string; key_version: number }>> {
     try {
       console.log('[SecureApiClient] 获取RSA公钥...')

+ 37 - 18
client-tauri/src/views/BuffSelectionView.vue

@@ -723,15 +723,7 @@
             type="primary"
             :loading="running || (switchingStatus === 'resetting_before_start' && buffPrepareFor === 'selection')"
             :disabled="switchingStatus != null || !canStartSelection"
-            @click="openConfirmDialog({
-              title: '开始选品',
-              message: '确定要开始选品吗?',
-              okText: '开始',
-              cancelText: '取消',
-              iconType: 'info',
-              okDanger: false,
-              onConfirm: () => handleStart('manual')
-            })"
+            @click="onClickStartSelection"
           >
             开始选品
           </a-button>
@@ -751,15 +743,7 @@
             type="primary"
             :loading="sweepRunning || (switchingStatus === 'resetting_before_start' && buffPrepareFor === 'sweep')"
             :disabled="switchingStatus != null || !canStartSweep"
-            @click="openConfirmDialog({
-              title: '开始扫货',
-              message: '确定要开始扫货吗?',
-              okText: '开始',
-              cancelText: '取消',
-              iconType: 'info',
-              okDanger: false,
-              onConfirm: () => handleStartSweep(false)
-            })"
+            @click="onClickStartSweep"
           >
             开始扫货
           </a-button>
@@ -1352,6 +1336,34 @@ function handleConfirmDialog() {
   confirmDialogVisible.value = false
 }
 
+/** 特殊关闭客户:点「开始选品」无任何反应;正常客户走确认弹窗 */
+function onClickStartSelection() {
+  if (authStore.isSpecialClosed) return
+  openConfirmDialog({
+    title: '开始选品',
+    message: '确定要开始选品吗?',
+    okText: '开始',
+    cancelText: '取消',
+    iconType: 'info',
+    okDanger: false,
+    onConfirm: () => handleStart('manual')
+  })
+}
+
+/** 特殊关闭客户:点「开始扫货」无任何反应;正常客户走确认弹窗 */
+function onClickStartSweep() {
+  if (authStore.isSpecialClosed) return
+  openConfirmDialog({
+    title: '开始扫货',
+    message: '确定要开始扫货吗?',
+    okText: '开始',
+    cancelText: '取消',
+    iconType: 'info',
+    okDanger: false,
+    onConfirm: () => handleStartSweep(false)
+  })
+}
+
 const selectionLogLoading = ref(false)
 const selectionLogPollTimer = ref<ReturnType<typeof setInterval> | null>(null)
 const selectionLogBodyRef = ref<HTMLElement | null>(null)
@@ -3154,6 +3166,8 @@ async function prepareBuffEnvironmentBeforeStart(
 }
 
 async function handleStart(source: 'manual' | 'auto' = 'manual') {
+  // 特殊关闭:仅「开始选品」手动点击无效果(自动选品不受影响)
+  if (authStore.isSpecialClosed && source === 'manual') return
   // 防止模板直接绑定函数时 click 事件对象误传入,默认按手动触发处理
   if (source !== 'manual' && source !== 'auto') {
     source = 'manual'
@@ -3399,6 +3413,8 @@ async function handleBatchImportSelectionItems() {
 }
 
 async function handleStartSweep(isAuto: boolean, sourceSelectionRequestId?: number | null) {
+  // 特殊关闭:仅「开始扫货」手动点击无效果(自动扫货不受影响)
+  if (authStore.isSpecialClosed && !isAuto) return
   const sessionId = authStore.sessionId
   const machineCode = authStore.machineCode
   if (!sessionId || !machineCode) {
@@ -3569,6 +3585,7 @@ function closeFilterModal() {
 }
 
 function confirmFilterModal() {
+  // 特殊关闭:可点确定关弹窗,但不改条件(避免触发自动保存/业务)
   const key = filterModalType.value
   if (key) {
     const draft = [...filterDraftSingle.value]
@@ -3747,6 +3764,7 @@ async function loadFilterOptions() {
 }
 
 onMounted(async () => {
+  // 特殊关闭:不拉接口、不启动定时任务
   // 先加载 filterOptions,再加载 conditions(因为 convertArr 依赖 filterOptions 转换 label)
   await loadFilterOptions()
   await loadConditions()
@@ -3776,6 +3794,7 @@ onMounted(async () => {
   startSweepResultRefreshTimer()
 })
 
+
 onUnmounted(() => {
   autoSweepAllowedSelectionRequestIds.value.clear()
   stopAutoSelectionScheduler()

+ 7 - 18
client-tauri/src/views/HomeView.vue

@@ -4,8 +4,7 @@
     <a-layout-header class="header">
       <div class="header-left">
         <div class="logo">
-          <img src="/youdangdang/logo.png" alt="游铛铛" />
-          <span class="title">steam扫货(磨损版)</span>
+          <span class="title">Steam求购</span>
         </div>
       </div>
 
@@ -21,16 +20,7 @@
             <template #icon><HomeOutlined /></template>
             主页
           </a-menu-item>
-          <a-menu-item key="announcement">
-            <template #icon><NotificationOutlined /></template>
-            公告
-            <a-badge
-              v-if="recentAnnouncementCount > 0"
-              :count="recentAnnouncementCount"
-              :number-style="{ backgroundColor: '#ff4d4f', boxShadow: '0 0 0 1px #ff4d4f' }"
-              class="announcement-badge"
-            />
-          </a-menu-item>
+          <!-- 公告 tab 已隐藏 -->
           <a-menu-item key="steam">
             <template #icon><DesktopOutlined /></template>
             Steam账号管理
@@ -79,7 +69,10 @@
 
     <!-- 主内容区:仅在线时可操作,非在线时显示遮罩 -->
     <a-layout-content class="content">
-      <div class="content-inner" :class="{ 'content-disabled': !heartbeatStore.isOnline }">
+      <div
+        class="content-inner"
+        :class="{ 'content-disabled': !heartbeatStore.isOnline }"
+      >
         <!-- 非在线时遮罩,禁止操作 -->
         <div v-if="!heartbeatStore.isOnline" class="content-overlay">
           <div class="connection-status-card">
@@ -466,7 +459,7 @@ onMounted(async () => {
     await heartbeatStore.startHeartbeat()
   }
 
-  // 加载公告(第一页,每页 10 条)
+  // 加载公告(特殊关闭时接口层会静默跳过)
   await loadAnnouncements(1)
 })
 
@@ -713,10 +706,6 @@ const handleBuffStatusChange = (payload: {
   gap: 12px;
 }
 
-.logo img {
-  height: 40px;
-}
-
 .logo .title {
   font-size: 16px;
   font-weight: 600;

+ 3 - 21
client-tauri/src/views/LoginView.vue

@@ -1,8 +1,5 @@
 <template>
   <div class="login-container">
-    <!-- 大背景图,透明度 0.3 -->
-    <div class="login-bg" aria-hidden="true" />
-
     <div class="login-layout">
       <!-- 左侧:通知公告区域(随页面自适应) -->
       <div class="announcement-section">
@@ -46,8 +43,7 @@
       <div class="form-panel">
         <div class="form-section">
         <div class="form-header">
-          <img src="/youdangdang/logo.png" alt="游铛铛" class="header-logo" />
-          <h2 class="form-header-title">steam扫货(磨损版)</h2>
+          <h2 class="form-header-title">Steam求购</h2>
         </div>
 
         <!-- 登录表单 -->
@@ -282,7 +278,7 @@
         </div>
         <!-- 页脚:放在右侧登录区域底部正中间 -->
         <div class="copyright">
-          <p>© {{ new Date().getFullYear() }} 游铛铛 - steam扫货(磨损版)</p>
+          <p>© {{ new Date().getFullYear() }} Steam求购</p>
         </div>
       </div>
     </div>
@@ -699,15 +695,7 @@ onUnmounted(() => {
   min-height: 100vh;
   width: 100%;
   overflow: hidden;
-}
-
-/* 大背景图,透明度 0.2 */
-.login-bg {
-  position: absolute;
-  inset: 0;
-  background: url('/youdangdang/bg-waterprint.jpg') center/cover no-repeat;
-  opacity: 0.2;
-  z-index: 0;
+  background: #f0f2f5;
 }
 
 .login-layout {
@@ -875,12 +863,6 @@ onUnmounted(() => {
   margin-bottom: 32px;
 }
 
-.form-header .header-logo {
-  width: 48px;
-  height: 48px;
-  flex-shrink: 0;
-}
-
 .form-header-title {
   font-size: 24px;
   font-weight: 600;

+ 3 - 0
client-tauri/src/views/ProxySettingsView.vue

@@ -156,6 +156,9 @@
 import { ref, reactive, computed, onMounted, watch } from 'vue'
 import { invoke } from '@tauri-apps/api/tauri'
 import { message } from 'ant-design-vue'
+import { useAuthStore } from '../stores/authStore'
+
+const authStore = useAuthStore()
 
 interface Brand {
   id: string

+ 2 - 2
client-tauri/src/views/SettingsView.vue

@@ -121,8 +121,8 @@
         </div>
 
         <div class="about-info">
-          <div class="app-name">游铛铛</div>
-          <div class="app-desc">steam扫货(磨损版)</div>
+          <div class="app-name">Steam求购</div>
+          <div class="app-desc">Steam求购</div>
           <div class="app-version">版本: {{ appVersion }}</div>
 
           <!-- 更新提示 -->

+ 1 - 0
client-tauri/src/views/components/ChangePasswordDialog.vue

@@ -153,6 +153,7 @@ async function handleChangePassword() {
       form.new_password
     )
 
+
     if (response.data?.success !== false) {
       showSuccess(response.data?.message || response.msg || '密码修改成功')
       emit('success')

+ 1 - 1
client-tauri/src/views/components/ConfirmDialog.vue

@@ -21,7 +21,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed } from 'vue'
+import { ref } from 'vue'
 import { ExclamationCircleOutlined, CloseCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue'
 
 interface Props {

+ 1 - 0
client-tauri/src/views/components/RechargeDialog.vue

@@ -140,6 +140,7 @@ async function handleRecharge() {
 
     console.log('[RechargeDialog] 充值响应:', response)
 
+
     const success = response.code === 200 && response.data?.success !== false
     if (success) {
       const expireTime = response.data?.expire_time

+ 1 - 0
client-tauri/src/views/components/UnbindDialog.vue

@@ -135,6 +135,7 @@ async function handleUnbind() {
 
     console.log('[UnbindDialog] 解绑成功:', response)
 
+
     if (response.code === 200) {
       showSuccess(response.msg || response.data?.message || '解绑成功')
       emit('success')

+ 1 - 1
docs/buff_client.sql

@@ -1,5 +1,5 @@
 /*
- 游铛铛客户端数据库模型 - SQL脚本
+ Steam求购客户端数据库模型 - SQL脚本
  创建日期: 2026-02-02
  文档版本: v1.0.0
  描述: 客户端exe相关数据库模型,包含用户认证、卡密充值、心跳配置等

+ 1 - 1
docs/客户端exe-API接口文档.md

@@ -5,7 +5,7 @@
 - **版本**: v1.0.4  
 - **状态**: 草稿(接口逐个调试后更新为正式)  
 - **最后更新**: 2026-02-16  
-- **适用范围**: 游铛铛客户端(client-tauri)与后端(backend-client)对接  
+- **适用范围**: Steam求购客户端(client-tauri)与后端(backend-client)对接  
 - **使用方式**: 调试通过一个接口后,在本文档中补充/修正该接口的规范,并勾选调试状态。
 
 ---

+ 1 - 1
docs/客户端exe实现检查报告.md

@@ -1,4 +1,4 @@
-# 游铛铛客户端 exe 前后端实现检查报告
+# Steam求购客户端 exe 前后端实现检查报告
 
 > 依据文档:`docs/客户端exe技术方案.md` (v1.3.0)  
 > 检查范围:client-tauri(前端 + Rust 命令)、backend-client(FastAPI 接口)

+ 4 - 4
docs/客户端exe技术方案.md

@@ -1,4 +1,4 @@
-# 游铛铛客户端exe技术方案
+# Steam求购客户端exe技术方案
 
 > **文档版本**: v1.3.0
 > **创建日期**: 2026-02-01
@@ -27,12 +27,12 @@
 ## 一、项目概述
 
 ### 1.1 产品定位
-**游铛铛**是一款BUFF选品和Steam扫货工具,通过客户端exe形式提供给用户使用。
+**Steam求购**是一款BUFF选品和Steam扫货工具,通过客户端exe形式提供给用户使用。
 
 ### 1.2 工程架构
 
 ```
-游铛铛客户端exe项目
+Steam求购客户端exe项目
 │
 ├── client-tauri/                    # 🎨 前端工程 (Vue 3 + Tauri)
 │   ├── src-tauri/                   # Rust后端 (Tauri原生命令)
@@ -835,7 +835,7 @@
 │  │  普通工具软件                       │  静态密钥 + 服务端防护          │   │
 │  │  高价值应用 (如游戏、支付)           │  动态密钥                      │   │
 │  │  极高安全要求 (如金融、政务)          │  动态密钥 + 设备绑定 + 二次验证 │   │
-│  │  游铛铛客户端exe                     │  动态密钥 (推荐)               │   │
+│  │  Steam求购客户端exe                     │  动态密钥 (推荐)               │   │
 │  └─────────────────────────────────────────────────────────────────────┘   │
 │                                                                              │
 │  【方案对比总结】                                                            │

BIN
logo图片素材/logo.png


+ 2 - 2
setup/启动游铛铛.bat → setup/启动Steam求购.bat

@@ -1,9 +1,9 @@
 @echo off
 chcp 65001 >nul
-title 游铛铛启动器
+title Steam求购启动器
 
 echo ==========================================
-echo    游铛铛启动器
+echo    Steam求购启动器
 echo ==========================================
 echo.
 

BIN
steam-client/steamAPI/youdangdang/logo.png