From f00388b06614ccdbbbe2afb26d89a960046c497a Mon Sep 17 00:00:00 2001 From: "DESKTOP-TKLFCPR\\ython" Date: Tue, 2 Jun 2026 20:21:07 +0900 Subject: [PATCH] =?UTF-8?q?fix(enhance-v4):=20APK=20QR=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95=20+=20=EC=9B=B9=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models.py | 3 ++- routers/app_deploy.py | 31 +++++++++++++------------------ routers/smart_notify.py | 2 +- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/models.py b/models.py index 8649121..844bea4 100644 --- a/models.py +++ b/models.py @@ -5457,7 +5457,8 @@ class AppVersion(Base): android_url = Column(String(1000), nullable=True) ios_url = Column(String(1000), nullable=True) qr_image_path = Column(String(500), nullable=True) - landing_token = Column(String(36), nullable=True, unique=True) + qr_data = Column(Text, nullable=True) # base64 PNG + landing_token = Column(String(64), nullable=True, unique=True) download_count = Column(Integer, default=0) is_latest = Column(Boolean, default=False) release_notes = Column(Text, nullable=True) diff --git a/routers/app_deploy.py b/routers/app_deploy.py index 40589a6..5dac926 100644 --- a/routers/app_deploy.py +++ b/routers/app_deploy.py @@ -97,9 +97,10 @@ async def upload_apk( file_path.write_bytes(file_bytes) # 기존 latest 해제 + from sqlalchemy import update as sa_update await db.execute( - __import__('sqlalchemy', fromlist=['update']).update(AppVersion) - .where(AppVersion.tenant_id == user.tenant_id, AppVersion.is_latest == True) + sa_update(AppVersion) + .where(AppVersion.is_latest == True) .values(is_latest=False) ) @@ -110,11 +111,10 @@ async def upload_apk( qr_b64 = base64.b64encode(qr_bytes).decode() app_ver = AppVersion( - tenant_id=user.tenant_id, version=version, platform="ANDROID", file_path=str(file_path), - file_size=len(file_bytes), + file_size_mb=round(len(file_bytes) / 1024 / 1024, 2), android_url=f"{BASE_URL}/api/app/download?token={token}", ios_url=ios_url or None, landing_token=token, @@ -147,9 +147,10 @@ async def set_app_url( user: User = Depends(require_admin_role), ): """외부 URL(EAS 빌드 등)로 QR 코드 생성.""" + from sqlalchemy import update as sa_update await db.execute( - __import__('sqlalchemy', fromlist=['update']).update(AppVersion) - .where(AppVersion.tenant_id == user.tenant_id, AppVersion.is_latest == True) + sa_update(AppVersion) + .where(AppVersion.is_latest == True) .values(is_latest=False) ) @@ -158,7 +159,6 @@ async def set_app_url( qr_bytes = _generate_qr(landing_url) app_ver = AppVersion( - tenant_id=user.tenant_id, version=req.version, platform="BOTH" if req.ios_url else "ANDROID", android_url=req.android_url, @@ -190,10 +190,7 @@ async def get_latest( ): """최신 버전 정보 조회.""" row = await db.execute( - select(AppVersion).where( - AppVersion.tenant_id == user.tenant_id, - AppVersion.is_latest == True, - ) + select(AppVersion).where(AppVersion.is_latest == True) ) ver = row.scalar_one_or_none() if not ver: @@ -303,8 +300,7 @@ async def app_landing( version_id=ver.id, platform="IOS" if is_ios else "ANDROID", user_agent=request.headers.get("User-Agent", "")[:200], - ip_addr=request.client.host if request.client else "", - accessed_at=datetime.utcnow(), + downloaded_at=datetime.utcnow(), ) db.add(log) await db.commit() @@ -343,8 +339,7 @@ async def list_versions( user: User = Depends(get_current_user), ): rows = await db.execute( - select(AppVersion).where(AppVersion.tenant_id == user.tenant_id) - .order_by(desc(AppVersion.created_at)).limit(20) + select(AppVersion).order_by(desc(AppVersion.created_at)).limit(20) ) versions = rows.scalars().all() return [ @@ -353,7 +348,7 @@ async def list_versions( "download_count": v.download_count, "is_latest": v.is_latest, "qr_url": f"{BASE_URL}/api/app/qr?token={v.landing_token}", "landing_url": f"{BASE_URL}/api/app/landing?token={v.landing_token}", - "file_size_mb": round((v.file_size or 0) / 1024 / 1024, 1), + "file_size_mb": round((v.file_size_mb or 0), 1), "release_notes": v.release_notes, "created_at": v.created_at, } @@ -368,7 +363,7 @@ async def delete_version( user: User = Depends(require_admin_role), ): row = await db.execute( - select(AppVersion).where(AppVersion.id == version_id, AppVersion.tenant_id == user.tenant_id) + select(AppVersion).where(AppVersion.id == version_id) ) ver = row.scalar_one_or_none() if not ver: @@ -388,7 +383,7 @@ async def app_stats( user: User = Depends(get_current_user), ): total = (await db.execute( - select(func.sum(AppVersion.download_count)).where(AppVersion.tenant_id == user.tenant_id) + select(func.sum(AppVersion.download_count)) )).scalar() or 0 android = (await db.execute( select(func.count(AppDownloadLog.id)).where(AppDownloadLog.platform == "ANDROID") diff --git a/routers/smart_notify.py b/routers/smart_notify.py index 98b7cfd..6a22d46 100644 --- a/routers/smart_notify.py +++ b/routers/smart_notify.py @@ -42,7 +42,7 @@ class NotifyRuleCreate(BaseModel): name: str = Field(..., max_length=200) trigger_type: str = Field(..., description="SR_CREATED|SR_UPDATED|INCIDENT|DRIFT|KPI_BREACH|CUSTOM") conditions: dict = Field(default_factory=dict) - channels: List[str] = Field(default_factory=list, description=["messenger","email","slack","kakao"]) + channels: List[str] = Field(default_factory=list, description="messenger|email|slack|kakao") priority_filter: str = Field("ALL", description="HIGH|MEDIUM|ALL") silence_hours: Optional[List[int]] = Field(None, description="무음 시간 목록 [22,23,0,1,...,7]") digest_mode: bool = Field(False, description="묶음 발송 모드")