From 566f59f0e459a0ddf37b070bc29327f68fb688b4 Mon Sep 17 00:00:00 2001 From: TradeMate Dev Date: Thu, 14 May 2026 19:23:45 +0800 Subject: [PATCH] fix: onboarding passes product_info dict; marketing service template fallback when no AI; frontend style-switching tabs --- backend/app/services/marketing.py | 55 +++++++++++++++- backend/app/services/onboarding.py | 7 +- uni-app/src/pages/marketing/marketing.vue | 78 +++++++++++++++++++++-- 3 files changed, 128 insertions(+), 12 deletions(-) diff --git a/backend/app/services/marketing.py b/backend/app/services/marketing.py index 591122a..6238301 100644 --- a/backend/app/services/marketing.py +++ b/backend/app/services/marketing.py @@ -7,7 +7,9 @@ logger = logging.getLogger(__name__) class MarketingService: def __init__(self): - self.ai = get_ai_router() + ai_router = get_ai_router() + self.ai = ai_router + self._ai_available = len(ai_router.providers) > 0 async def generate( self, @@ -18,6 +20,12 @@ class MarketingService: count: int = 3, preference_context: Optional[str] = None, ) -> List[Dict[str, Any]]: + name = product_info.get("name", "") + desc = product_info.get("description", "") + + if not self._ai_available: + return self._template_fallback(name, desc, target, style, count, language) + results = [] styles = self._get_style_variants(style, count) @@ -35,9 +43,44 @@ class MarketingService: return results + def _template_fallback(self, name: str, desc: str, target: str, style: str, count: int, language: str) -> List[Dict[str, Any]]: + styles = self._get_style_variants(style, count) + results = [] + for s in styles: + if language == "zh": + results.append({ + "content": f"【{name}】产品介绍\n\n{desc}\n\n我们诚挚向您推荐{name},产品品质优良,价格具有竞争力,欢迎联系我们获取更多信息。", + "style": s, + "provider": "template", + }) + else: + results.append({ + "content": f"Subject: Introduction of {name}\n\nDear Customer,\n\nWe are pleased to introduce our {name}. {desc}\n\n{self._get_closing(s)}", + "style": s, + "provider": "template", + }) + return results + + def _get_closing(self, style: str) -> str: + closings = { + "professional": "Looking forward to your favorable reply. Best regards.", + "friendly": "Hope to hear from you soon! Warm regards.", + "urgent": "Please contact us at your earliest convenience. Best regards.", + "benefit_focused": "Don't miss this opportunity to boost your business. Contact us today!", + "storytelling": "Let us help you tell your brand story. Get in touch!", + } + return closings.get(style, closings["professional"]) + async def generate_keywords( self, product_info: Dict[str, Any], language: str = "en", count: int = 10 ) -> List[str]: + name = product_info.get("name", "") + desc = product_info.get("description", "") + + if not self._ai_available: + words = name.split() + [w for w in desc.split() if len(w) > 4] + return list(dict.fromkeys(words))[:count] + try: schema = { "type": "object", @@ -48,7 +91,7 @@ class MarketingService: } }, } - text = f"Product: {product_info.get('name', '')}. {product_info.get('description', '')}" + text = f"Product: {name}. {desc}" result = await self.ai.extract(text, schema) keywords = result.get("data", {}).get("keywords", []) return keywords[:count] @@ -66,6 +109,14 @@ class MarketingService: async def analyze_competitors( self, product_info: Dict[str, Any], market: str = "US" ) -> Dict[str, Any]: + if not self._ai_available: + return { + "price_range": "Contact us for pricing", + "key_selling_points": [product_info.get("name", "")], + "common_keywords": [], + "market_trends": "AI analysis unavailable. Please configure an AI provider in settings.", + "suggestions": ["Set up an AI provider for competitor insights"], + } try: text = f"Product: {product_info.get('name', '')} in {market} market. Category: {product_info.get('category', '')}. Description: {product_info.get('description', '')}" schema = { diff --git a/backend/app/services/onboarding.py b/backend/app/services/onboarding.py index a97f0c8..f25fd37 100644 --- a/backend/app/services/onboarding.py +++ b/backend/app/services/onboarding.py @@ -35,11 +35,10 @@ class OnboardingService: await self.db.flush() mkt = MarketingService() + product_info = {"name": name, "description": description, "category": category or "general"} try: content = await mkt.generate( - product_name=name, - description=description, - category=category or "general", + product_info=product_info, target=target, style="professional", count=3, @@ -51,7 +50,7 @@ class OnboardingService: try: keywords_result = await mkt.generate_keywords( - product_name=name, description=description, category=category or "general" + product_info=product_info ) keywords = keywords_result if isinstance(keywords_result, list) else [] except Exception as e: diff --git a/uni-app/src/pages/marketing/marketing.vue b/uni-app/src/pages/marketing/marketing.vue index fcea8a8..ee822c1 100644 --- a/uni-app/src/pages/marketing/marketing.vue +++ b/uni-app/src/pages/marketing/marketing.vue @@ -100,12 +100,23 @@ 导出CSV + + + {{ styleLabels[s] || s }} + + - - {{ item }} + + {{ item.content || item }} - 复制 - 发送 + 复制 + 发送 竞品分析 @@ -134,7 +145,7 @@