7b62c2f8b4
## H5 底部导航修复 (Bug #10) - 精简 App.vue,移除重复 tabbar,仅保留全局样式 - uni-page 设置 height: calc(100% - 50px) + overflow-y: auto - 内容区域精确停在底部导航上方,独立滚动不再叠加 - 恢复 custom-tab-bar 组件 ## 项目进度文档 - PROGRESS.md 更新至 10 个 Bug 修复 - 新增 H5 底部导航修复记录 - 新增历史变更条目
128 lines
4.1 KiB
Python
128 lines
4.1 KiB
Python
from typing import Dict, Any, Optional, List
|
|
from datetime import datetime, timedelta
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, func, and_
|
|
from app.models.preference import MarketingEffect
|
|
import hashlib
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MarketingEffectService:
|
|
def __init__(self, db: AsyncSession):
|
|
self.db = db
|
|
|
|
async def track_event(
|
|
self,
|
|
user_id: str,
|
|
content: str,
|
|
product_id: Optional[str] = None,
|
|
product_name: Optional[str] = None,
|
|
channel: str = "copy",
|
|
event_type: str = "copy",
|
|
target_audience: str = "",
|
|
metadata: Optional[Dict] = None,
|
|
) -> Dict[str, Any]:
|
|
content_hash = hashlib.sha256(content.encode()).hexdigest()
|
|
|
|
event = MarketingEffect(
|
|
user_id=user_id,
|
|
content_hash=content_hash,
|
|
product_id=product_id,
|
|
product_name=product_name,
|
|
channel=channel,
|
|
event_type=event_type,
|
|
target_audience=target_audience,
|
|
metadata=metadata or {},
|
|
)
|
|
self.db.add(event)
|
|
await self.db.flush()
|
|
|
|
return {
|
|
"id": str(event.id),
|
|
"event_type": event_type,
|
|
"content_hash": content_hash,
|
|
}
|
|
|
|
async def get_effects(
|
|
self, user_id: str, page: int = 1, size: int = 20
|
|
) -> Dict[str, Any]:
|
|
query = (
|
|
select(MarketingEffect)
|
|
.where(MarketingEffect.user_id == user_id)
|
|
.order_by(MarketingEffect.created_at.desc())
|
|
.offset((page - 1) * size)
|
|
.limit(size)
|
|
)
|
|
count_query = select(func.count(MarketingEffect.id)).where(
|
|
MarketingEffect.user_id == user_id
|
|
)
|
|
|
|
total = await self.db.execute(count_query)
|
|
result = await self.db.execute(query)
|
|
events = result.scalars().all()
|
|
|
|
return {
|
|
"items": [
|
|
{
|
|
"id": str(e.id),
|
|
"product_name": e.product_name,
|
|
"channel": e.channel,
|
|
"event_type": e.event_type,
|
|
"target_audience": e.target_audience,
|
|
"created_at": e.created_at.isoformat() if e.created_at else None,
|
|
}
|
|
for e in events
|
|
],
|
|
"total": total.scalar() or 0,
|
|
"page": page,
|
|
"size": size,
|
|
}
|
|
|
|
async def get_stats(self, user_id: str) -> Dict[str, Any]:
|
|
today = datetime.utcnow().date()
|
|
week_ago = today - timedelta(days=7)
|
|
|
|
total_query = select(func.count(MarketingEffect.id)).where(
|
|
MarketingEffect.user_id == user_id
|
|
)
|
|
today_query = select(func.count(MarketingEffect.id)).where(
|
|
and_(
|
|
MarketingEffect.user_id == user_id,
|
|
func.date(MarketingEffect.created_at) == today,
|
|
)
|
|
)
|
|
week_query = select(func.count(MarketingEffect.id)).where(
|
|
and_(
|
|
MarketingEffect.user_id == user_id,
|
|
func.date(MarketingEffect.created_at) >= week_ago,
|
|
)
|
|
)
|
|
copy_query = select(func.count(MarketingEffect.id)).where(
|
|
and_(
|
|
MarketingEffect.user_id == user_id,
|
|
MarketingEffect.event_type == "copy",
|
|
)
|
|
)
|
|
send_query = select(func.count(MarketingEffect.id)).where(
|
|
and_(
|
|
MarketingEffect.user_id == user_id,
|
|
MarketingEffect.event_type == "send",
|
|
)
|
|
)
|
|
|
|
totals = await self.db.execute(total_query)
|
|
todays = await self.db.execute(today_query)
|
|
weeks = await self.db.execute(week_query)
|
|
copies = await self.db.execute(copy_query)
|
|
sends = await self.db.execute(send_query)
|
|
|
|
return {
|
|
"total_events": totals.scalar() or 0,
|
|
"today": todays.scalar() or 0,
|
|
"this_week": weeks.scalar() or 0,
|
|
"copy_count": copies.scalar() or 0,
|
|
"send_count": sends.scalar() or 0,
|
|
}
|