Kaynağa Gözat

初始化项目

初始化项目
dangyunlong 3 hafta önce
ebeveyn
işleme
68d8c19028
46 değiştirilmiş dosya ile 3593 ekleme ve 0 silme
  1. 37 0
      .dockerignore
  2. 2 0
      .gitignore
  3. 82 0
      Dockerfile
  4. BIN
      __pycache__/config.cpython-313.pyc
  5. 0 0
      api/__init__.py
  6. BIN
      api/__pycache__/__init__.cpython-313.pyc
  7. BIN
      api/__pycache__/routes.cpython-313.pyc
  8. 6 0
      api/routes.py
  9. 54 0
      app.py
  10. 0 0
      models/__init__.py
  11. BIN
      models/__pycache__/__init__.cpython-313.pyc
  12. BIN
      models/__pycache__/model_loader.cpython-313.pyc
  13. 37 0
      models/downloadMT5Model.py
  14. 20 0
      models/downloadV2Model.py
  15. 24 0
      models/model_loader.py
  16. 17 0
      requirements.txt
  17. 0 0
      services/__init__.py
  18. BIN
      services/__pycache__/__init__.cpython-313.pyc
  19. BIN
      services/__pycache__/ai_service.cpython-313.pyc
  20. BIN
      services/__pycache__/templateData.cpython-313.pyc
  21. BIN
      services/__pycache__/templateFun.cpython-313.pyc
  22. BIN
      services/__pycache__/work.cpython-313.pyc
  23. 462 0
      services/ai_service.py
  24. 52 0
      services/demo.py
  25. BIN
      services/style/__pycache__/style1to4data.cpython-313.pyc
  26. BIN
      services/style/__pycache__/style5data.cpython-313.pyc
  27. 472 0
      services/style/style1to4data.py
  28. 587 0
      services/style/style5data.py
  29. 692 0
      services/templateData.py
  30. 230 0
      services/templateFun.py
  31. 311 0
      services/work.py
  32. 0 0
      utils/__init__.py
  33. BIN
      utils/__pycache__/__init__.cpython-313.pyc
  34. BIN
      utils/__pycache__/command.cpython-313.pyc
  35. BIN
      utils/__pycache__/inputType.cpython-313.pyc
  36. BIN
      utils/__pycache__/matching.cpython-313.pyc
  37. BIN
      utils/__pycache__/offFetch.cpython-313.pyc
  38. BIN
      utils/__pycache__/request.cpython-313.pyc
  39. BIN
      utils/__pycache__/verifyInput.cpython-313.pyc
  40. 16 0
      utils/command.py
  41. 164 0
      utils/inputType.py
  42. 170 0
      utils/matching.py
  43. 24 0
      utils/offFetch.py
  44. 41 0
      utils/request.py
  45. 76 0
      utils/verifyInput.py
  46. 17 0
      utils/version.py

+ 37 - 0
.dockerignore

@@ -0,0 +1,37 @@
+# .dockerignore 哪些文件不需要复制到镜像中
+
+# 忽略版本控制
+.git/
+.gitignore
+
+# 忽略虚拟环境和本地配置
+venv/
+env/
+.venv/
+.env
+.env.local
+.env.*.local
+
+# 忽略系统文件
+.DS_Store
+Thumbs.db
+
+# 忽略IDE文件
+.vscode/
+.idea/
+*.swp
+*.swo
+
+# 忽略Python缓存
+__pycache__/
+*.py[cod]
+*$py.class
+
+# 忽略日志和临时文件
+*.log
+*.tmp
+*.temp
+
+# 忽略Docker自身文件
+Dockerfile
+docker-compose.yml

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+models/bin/
+

+ 82 - 0
Dockerfile

@@ -0,0 +1,82 @@
+# Dockerfile for AIServ
+# 使用 Python 3.11(稳定兼容)
+FROM python:3.11-slim
+
+# 设置环境变量
+ENV PYTHONDONTWRITEBYTECODE=1 \
+    PYTHONUNBUFFERED=1 \
+    PIP_NO_CACHE_DIR=1 \
+    PYTHONPATH=/app  
+#添加项目根目录到Python路径
+
+WORKDIR /app
+
+# 1. 安装系统依赖
+# PyTorch和sentence-transformers需要编译工具
+RUN apt-get update && apt-get install -y \
+    gcc \
+    g++ \
+    git \
+    curl \
+    && rm -rf /var/lib/apt/lists/*
+
+# 2. 复制依赖文件并安装
+COPY requirements.txt .
+
+# 分开安装以避免依赖冲突
+# 2.1 先安装PyTorch(指定CPU版本)
+RUN pip install --no-cache-dir \
+    torch==2.8.0 \
+    --index-url https://download.pytorch.org/whl/cpu
+
+# 2.2 安装NumPy
+RUN pip install --no-cache-dir numpy==2.2.6
+
+# 2.3 安装其他依赖
+RUN pip install --no-cache-dir \
+    flask==3.1.2 \
+    flask-cors==6.0.1 \
+    scikit-learn==1.7.2 \
+    sentence-transformers==5.1.1 \
+    joblib==1.5.2 \
+    cachetools==6.2.1
+
+# 3. 验证关键包安装成功
+RUN python -c "import torch; print(f'✓ PyTorch {torch.__version__} installed')" && \
+    python -c "import numpy; print(f'✓ NumPy {numpy.__version__} installed')" && \
+    python -c "import flask; print(f'✓ Flask {flask.__version__} installed')" && \
+    python -c "from sentence_transformers import SentenceTransformer; print('✓ sentence-transformers installed')"
+
+# 4. 复制应用代码
+# 注意:logs/目录不需要复制,运行时生成
+COPY app.py .
+COPY api/ ./api/
+COPY models/ ./models/
+COPY services/ ./services/
+COPY utils/ ./utils/
+
+# 5. 验证模型文件是否存在
+RUN if [ -d "models/bin/all-MiniLM-L6-v2" ]; then \
+        echo "✓ Model files found: models/bin/all-MiniLM-L6-v2"; \
+        ls -la models/bin/all-MiniLM-L6-v2/ | head -5; \
+    else \
+        echo "⚠ Warning: Model directory not found"; \
+    fi
+
+# 6. 创建非root用户(安全最佳实践)
+RUN useradd -m -u 1000 appuser && \
+    chown -R appuser:appuser /app
+USER appuser
+
+# 7. 创建日志目录(确保有写入权限)
+RUN mkdir -p /app/logs && chmod 755 /app/logs
+
+# 8. 暴露端口(Flask默认5000)
+EXPOSE 5000
+
+# 9. 健康检查(可选)
+HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
+    CMD python -c "import requests; requests.get('http://localhost:5000/health', timeout=5)" 2>/dev/null || exit 1
+
+# 10. 启动应用
+CMD ["python", "app.py"]

BIN
__pycache__/config.cpython-313.pyc


+ 0 - 0
api/__init__.py


BIN
api/__pycache__/__init__.cpython-313.pyc


BIN
api/__pycache__/routes.cpython-313.pyc


+ 6 - 0
api/routes.py

@@ -0,0 +1,6 @@
+from services.work import stream_response
+
+def register_routes(app):
+    @app.route('/ai/stream', methods=['POST'])
+    def ai_stream_route():
+        return stream_response()

+ 54 - 0
app.py

@@ -0,0 +1,54 @@
+from flask import Flask, jsonify
+from flask_cors import CORS  # 导入Flask-CORS
+from api.routes import register_routes
+
+def create_app():
+    app = Flask(__name__)
+    
+    # 配置CORS - 核心修改部分
+    CORS(app,
+        origins=[
+            'http://localhost:9527',
+            'https://adminpre.bjzxtw.org.cn',
+            'http://adminpre.bjzxtw.org.cn',
+            'https://admin.bjzxtw.org.cn',
+            'http://admin.bjzxtw.org.cn'
+        ],  # 明确允许你的前端域名
+        supports_credentials=True,                   # 允许携带Cookie等凭证
+        methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],  # 允许的HTTP方法
+        allow_headers=['Content-Type', 'Authorization'],      # 允许的请求头
+        expose_headers=None,                         # 允许浏览器访问的响应头
+        max_age=600)                                 # 预检请求缓存时间(秒)
+    
+    # 路由检查
+    @app.route('/health')
+    def health():
+        return jsonify({'status': 'healthy', 'service': 'aiserv'})
+    
+    # 添加根路由
+    @app.route('/')
+    def index():
+        return jsonify({
+            'service': 'AIServ',
+            'version': '1.2',
+            'status': 'running'
+        })
+    
+    # 注册其他路由
+    register_routes(app)
+    return app
+
+if __name__ == '__main__':
+    app = create_app()
+    print("🚀 小龙包服务启动...")
+    print("📡 监听地址: 0.0.0.0:5000")
+    print("🌐 外部访问: http://localhost:5000")
+    print("🔓 CORS已启用,允许跨域访问")
+    print(f"✅ 允许的源: https://apipre1.bjzxtw.org.cn")
+    
+    app.run(
+        host='0.0.0.0',
+        port=5000,
+        debug=False,
+        threaded=True
+    )

+ 0 - 0
models/__init__.py


BIN
models/__pycache__/__init__.cpython-313.pyc


BIN
models/__pycache__/model_loader.cpython-313.pyc


+ 37 - 0
models/downloadMT5Model.py

@@ -0,0 +1,37 @@
+# 注意:请把该文件放到文件根目录运行
+# 下载或更新本地的模型
+import os
+
+def download_mT5_model_only():
+    """只下载 mT5 摘要模型"""
+    model_name = "csebuetnlp/mT5_multilingual_XLSum"
+    save_path = "./models/bin/mT5_multilingual_XLSum"
+    
+    print(f"下载摘要模型: {model_name}")
+    print(f"保存到: {save_path}")
+    
+    # 确保目录存在
+    os.makedirs(save_path, exist_ok=True)
+    
+    # 下载模型
+    from transformers import MT5ForConditionalGeneration, MT5Tokenizer
+    
+    print("下载 mT5 模型中...")
+    model = MT5ForConditionalGeneration.from_pretrained(model_name)
+    print("下载 mT5 分词器中...")
+    tokenizer = MT5Tokenizer.from_pretrained(model_name)
+    
+    print("保存到本地...")
+    model.save_pretrained(save_path)
+    tokenizer.save_pretrained(save_path)
+    
+    print(f"✅ mT5 模型已保存到: {save_path}")
+    return model, tokenizer
+
+# 在 __main__ 中可以选择调用哪个
+if __name__ == "__main__":
+    # 只下载摘要模型
+    download_mT5_model_only()
+    
+    # 或者下载所有模型
+    # download_all_models()

+ 20 - 0
models/downloadV2Model.py

@@ -0,0 +1,20 @@
+# 注意:请把该文件放到文件根目录运行
+# 下载或更新本地的模型
+import os
+from sentence_transformers import SentenceTransformer
+
+def download_and_save_model():
+    # 1.创建项目目录
+    model_path = "./models/bin/all-MiniLM-L6-v2"
+    os.makedirs(os.path.dirname(model_path), exist_ok=True)
+    print("正在下载模型...")
+    # 2.从Hugging Face下载模型
+    model = SentenceTransformer('all-MiniLM-L6-v2')
+    print("正在保存模型到本地...")
+    # 3.保存到指定路径
+    model.save(model_path)
+    print(f"模型已保存到: {model_path}")
+    return model
+
+if __name__ == "__main__":
+    download_and_save_model()

+ 24 - 0
models/model_loader.py

@@ -0,0 +1,24 @@
+import torch
+from sentence_transformers import SentenceTransformer
+import joblib
+import os
+
+class ModelLoader:
+    def __init__(self):
+        self.text_model = None
+        self.classification_model = None
+        self.summarization_model = None
+        self.summarization_tokenizer = None
+        
+    def load_text_model(self, use_local=True, local_model_path="./models/bin/all-MiniLM-L6-v2"):
+        if self.text_model is None:
+            if use_local and os.path.exists(local_model_path):
+                print("🔧 从本地加载sentence-transformers模型...ok")
+                self.text_model = SentenceTransformer(local_model_path)
+            else:
+                print("🔧 从网络加载sentence-transformers模型...ok")
+                self.text_model = SentenceTransformer('all-MiniLM-L6-v2')
+        return self.text_model
+
+# 全局模型加载器
+model_loader = ModelLoader()

+ 17 - 0
requirements.txt

@@ -0,0 +1,17 @@
+# Web框架
+flask == 3.1.2
+
+# 深度学习核心
+torch == 2.8.0
+scikit-learn == 1.7.2
+sentence-transformers == 5.1.1
+joblib == 1.5.2
+
+# 数据处理
+numpy == 2.2.6
+
+# 缓存
+cachetools == 6.2.1
+
+# transformers
+# sentencepiece

+ 0 - 0
services/__init__.py


BIN
services/__pycache__/__init__.cpython-313.pyc


BIN
services/__pycache__/ai_service.cpython-313.pyc


BIN
services/__pycache__/templateData.cpython-313.pyc


BIN
services/__pycache__/templateFun.cpython-313.pyc


BIN
services/__pycache__/work.cpython-313.pyc


+ 462 - 0
services/ai_service.py

@@ -0,0 +1,462 @@
+#1.引入numpy
+import numpy as np
+#2.引入模型
+from models.model_loader import model_loader
+#3.引入scikit-learn的余弦相似度函数
+from sklearn.metrics.pairwise import cosine_similarity
+#4.引入json
+import json
+#5.引入缓存机制
+from cachetools import TTLCache
+#6.引入哈希值
+import hashlib
+#7.引入标题提取
+import utils.matching as TitleMatcher
+#8.引入模板数据
+from services.templateFun import templateData, SectorScheduler
+# 引入样式数据
+from services.style.style1to4data import sectorStyle1to4Data
+from services.style.style5data import sectorStyle5Data
+#9.引入随机数
+import random
+#10.引入请求
+from utils.request import post_request,generate_error
+#11.引入时间
+import time
+#12.引入输入类型
+import utils.inputType as inputType
+
+# 创建AI处理类
+class AIService:
+    """第一步:初始化"""
+    def __init__(self):
+        # 1.1加载文本处理模型
+        self.text_model = model_loader.load_text_model()
+        # 1.2使用TTLCache,最大缓存50个模板,1小时过期,缓存模板嵌入向量
+        self.template_embeddings_cache = TTLCache(maxsize=50, ttl=3600)
+        # 1.3用户输入文本
+        self.user_input = ""
+        # 1.4组件样式数据
+        self.component_style_data = []
+        # 1.5推理过程
+        self.reasoning = []
+        # 1.6 当前样式数据对象
+        self.current_style_data = None
+
+    """第二步:调用模型能力"""
+    #2.1 文字转换成向量
+    def text_to_embedding(self, text):
+        # 2.1.1如果text是字符串,则转换成列表
+        if isinstance(text, str):
+            text = [text]
+        
+        embeddings = self.text_model.encode(text)
+        # 2.2.2返回numpy数组,不要转成list
+        return embeddings
+
+    #2.2生成文字摘要
+    def userMessage_to_title(self, userMessage):
+        return TitleMatcher.title_matcher.generate_title(userMessage)
+
+    #2.3筛选通栏
+    def filter_sectors(self, userMessage,matched_template_id):
+        return inputType.inputType_matcher.generate_inputType(userMessage,matched_template_id)
+ 
+    """第三步:查找最匹配的皮肤模板"""
+    def find_best_matching_template(self, user_text, skin_data):
+        # 3.1如果皮肤数据为空,则返回None
+        if not skin_data:
+            return None
+        
+        # 3.2提取用户输入中的颜色关键词
+        color_keywords = self.extract_color_keywords(user_text)
+        print(f"提取到用户颜色关键词: {color_keywords}")
+        # 更新推理过程
+        self.reasoning.append(f"我提取到用户需求中的颜色关键词是: {color_keywords}")
+        
+        # 3.3根据颜色关键词过滤模板
+        filtered_skin_data = skin_data
+        if color_keywords:
+            filtered_skin_data = self.filter_templates_by_color(skin_data, color_keywords)
+            print(f"根据颜色过滤后模板数量: {len(filtered_skin_data)}")
+            # 更新推理过程
+            self.reasoning.append(f"根据颜色过滤数据以后有: {len(filtered_skin_data)}个符合该需求的模板")
+        
+        # 3.4获取模板信息(使用缓存)
+        template_infos, template_embeddings = self.get_template_embeddings(filtered_skin_data)
+        
+        # 3.5如果模板信息为空,则返回None
+        if not template_infos:
+            return None
+        
+        # 3.6用户输入的文本转化为向量
+        user_embedding = self.text_to_embedding(user_text)
+        
+        # 3.7如果用户输入的文本的维度是1,则转换成2维
+        if len(user_embedding.shape) == 1:
+            user_embedding = user_embedding.reshape(1, -1)
+        if len(template_embeddings.shape) == 1:
+            template_embeddings = template_embeddings.reshape(1, -1)
+        
+        # 3.8通过余弦函数批量计算相似度
+        similarities = cosine_similarity(user_embedding, template_embeddings)[0]
+        best_index = np.argmax(similarities)
+        best_similarity = similarities[best_index]
+        
+        return {
+            'template_info': template_infos[best_index],
+            'similarity_score': best_similarity
+        }
+
+    # 3.9提取用户输入中的颜色关键词
+    def extract_color_keywords(self, text):
+        color_map = {
+            "红": "红色",
+            "绿": "绿色",
+            "蓝": "蓝色",
+            "黄": "黄色",
+            "橙": "橙色",
+            "紫": "紫色",
+            "黑": "黑色",
+            "白": "白色",
+            "灰": "灰色"
+        }
+        
+        found_colors = []
+        for char, color in color_map.items():
+            if char in text:
+                found_colors.append(color)
+        
+        return found_colors
+    
+    # 3.10根据颜色关键词过滤模板
+    def filter_templates_by_color(self, skin_data, color_keywords):
+        filtered = []
+        for template in skin_data:
+            try:
+                keywords = json.loads(template.get('template_keyword', '[]'))
+                keyword_text = ' '.join(keywords)
+                
+                # 检查模板关键词是否包含任何颜色关键词
+                for color in color_keywords:
+                    if color in keyword_text:
+                        filtered.append(template)
+                        break
+            except:
+                continue
+        
+        return filtered
+
+    """第四步:缓存"""
+    # 4.1读取或者新建缓存
+    def get_template_embeddings(self, skin_data):
+        
+        if not skin_data:
+            return None, []
+        
+        # 4.1.2准备模板关键词文本
+        template_texts = []
+        template_infos = []
+        
+        for template in skin_data:
+            try:
+                keywords = json.loads(template.get('template_keyword', '[]'))
+                keyword_text = ' '.join(keywords)
+                template_texts.append(keyword_text)
+                template_infos.append(template)
+            except json.JSONDecodeError:
+                continue
+        
+        if not template_texts:
+            return None, []
+        
+        # 4.1.3检查缓存
+        cache_key = self._get_template_cache_key(skin_data)
+        if cache_key in self.template_embeddings_cache:
+            print("📦 从缓存加载模板嵌入向量")
+            template_embeddings = self.template_embeddings_cache[cache_key]
+            return template_infos, template_embeddings
+        
+        print("🔄 计算新的模板嵌入向量")
+        template_embeddings = self.text_model.encode(template_texts)
+        
+        # 4.1.4存储到缓存(cachetools自动处理过期)
+        self.template_embeddings_cache[cache_key] = template_embeddings
+        return template_infos, template_embeddings
+
+    # 4.2 生成模板数据的缓存键
+    def _get_template_cache_key(self, skin_data):
+        template_keys = []
+        for template in skin_data:
+            template_keys.append(f"{template.get('id')}_{template.get('template_keyword')}")
+        combined_key = '|||'.join(sorted(template_keys))
+        return hashlib.md5(combined_key.encode('utf-8')).hexdigest()
+
+    # 4.3 清空模板缓存
+    def clear_cache(self):
+        self.template_embeddings_cache.clear()
+        print("🗑️ 模板嵌入向量缓存已清空")
+    
+     # 4.4 获取模板缓存
+    def get_cache_info(self):
+        return {
+            'template_embeddings_cache_size': len(self.template_embeddings_cache),
+            'cached_templates_count': len(self.template_embeddings_cache),
+            'cache_maxsize': self.template_embeddings_cache.maxsize,
+            'cache_ttl': self.template_embeddings_cache.ttl
+        }
+
+
+    """第五步:生成通栏数据"""
+    # 5.1 随机生成通栏数据
+    def get_sectors(self,website_id,matched_template_id,token):
+        print("开始生成通栏数据!")
+        print(matched_template_id)
+        # 根据matched_template_id选择样式数据
+        if int(matched_template_id) == 5:
+            print("正在使用第5套皮肤的数据!")
+            self.current_style_data = sectorStyle5Data
+        else:
+            print("正在使用前4套皮肤的数据!")
+            self.current_style_data = sectorStyle1to4Data
+
+        sectors_config = self.current_style_data.sectors_config
+        scheduler = SectorScheduler(sectors_config)
+        result = scheduler.generate_sequence(10)
+        
+        # 处理强制位置(倒数位置)
+        forced_map = {}
+        for sector in sectors_config:
+            if 'force_pos' in sector:
+                # force_pos: 1(倒数第一), 2(倒数第二), 3(倒数第三)
+                forced_map[sector['force_pos']] = sector['name']
+        
+        # 替换结果中的对应位置
+        if forced_map and result:
+            for pos, name in forced_map.items():
+                idx = -1 * pos
+                if abs(idx) <= len(result):
+                    print(f"强制调整位置: 将 {name} 放入索引 {idx}")
+                    result[idx] = name
+
+        # print("结果:", result)
+        sectorNames = []
+
+        # 第一步:获取到筛选结果
+        for sector_name in result:
+            for sector in sectors_config:
+                if sector['name'] == sector_name:
+                    sectorNames.append(sector['name'])
+                    break
+
+        # 第二步:使用筛选结果把sectors_webSiteData重组为模板数据
+        for name in sectorNames:
+            # 从字典中直接获取对应的sector数据
+            if name in self.current_style_data.sectors_data:
+                matched_sector = self.current_style_data.sectors_data[name]
+                # 添加到templateData.sectors_webSiteData.index
+                # templateData.sectors_webSiteData["template"]["index"].append(matched_sector)
+                # print(matched_sector)
+                # print(templateData.sectors_webSiteData["template"]["index"])
+                templateData.sectors_webSiteData["template"]["index"].append(matched_sector)
+
+        #print("模板数据:" + json.dumps(templateData.sectors_webSiteData["template"]["index"], ensure_ascii=False))
+        #获得通栏名称
+        ai_service.test_normal_case_cnames()
+
+        # 第三步:选择组件样式
+        ai_service.updata_component_style(website_id,matched_template_id,token)
+
+        # 第四步:依照用户需求修改样式
+        ai_service.updata_component_style_to_user(website_id,matched_template_id,token)
+
+        # 第五步:生成画布数据
+        # 循环templateData.sectors_webSiteData["template"]["index"],生成画布数据
+        # templateData.canvas_data["template"]["index"].append()
+        all_y = 0  # 初始化累计高度
+        for index,sectorData in enumerate(templateData.sectors_webSiteData["template"]["index"]):
+            # 获取当前通栏的高度
+            sector_height = sectorData["sectorCanvasHeight"]
+            # 生成画布数据并添加到canvas_data
+            canvas_item = ai_service.get_canvans_data(sector_height, all_y, sectorData,index)
+            templateData.canvas_data["template"]["index"].append(canvas_item)
+            # 更新累计高度
+            all_y += sector_height
+
+    # 5.2 根据通栏数据生成画布数据
+    def get_canvans_data(self,sector_height,all_y,sectorData,sort):
+        data = {
+            "i": int(time.time() * 1000) * 1000 + random.randint(0, 9999),  # 毫秒级时间戳后加随机4位数
+            "x": 0,#通栏的x坐标恒定为0
+            "y": all_y,#通栏的y坐标为当前所有通栏的高度之和
+            "w": 12,#通栏的宽度恒定为12
+            "h": sector_height,#通栏的高度为sectorData["sectorCanvasHeight"]
+            "type": sectorData["sectorName"],
+            "content":sectorData,
+            "dataSort": sort,
+            "moved": False
+        }
+
+        return data
+
+    # 5.3 生成随机的样式数据
+    def updata_component_style(self,website_id,matched_template_id,token):
+        getComponentStyle = post_request("public/getAllSectorComponentStyle", {
+            "website_id":website_id,
+            "template_id": matched_template_id,
+        },token)
+        # 请补全此方法
+        # 检查响应是否成功
+        if getComponentStyle.get('code') != 200:
+            print(f"获取组件样式失败: {getComponentStyle.get('message')}")
+            return
+        
+        # 获取组件样式数据
+        style_data = getComponentStyle.get('data', [])
+        self.component_style_data = style_data
+        
+        # 需要跳过的通栏类型
+        skip_sectors = ["adSector", "linkSector", "linkCxfwSector"]
+
+        # 更新推理过程
+        self.reasoning.append(f"通栏创建完毕")
+        
+        # 遍历模板中的每个通栏
+        for sector in templateData.sectors_webSiteData["template"]["index"]:
+            sector_name = sector["sectorName"]
+            
+            # 跳过不需要修改样式的通栏
+            if sector_name in skip_sectors:
+                print(f"跳过 {sector_name} 的组件样式修改")
+                continue
+            
+            # 在样式数据中查找匹配的通栏
+            matched_style = next((s for s in style_data if s["sector_id"] == sector_name), None)
+            if not matched_style:
+                print(f"警告: 未找到 {sector_name} 的组件样式配置")
+                continue
+            
+            # 获取该通栏的组件列表配置
+            component_list_config = matched_style.get("component_list", [])
+            
+            # 遍历通栏中的每个组件
+            for idx, component in enumerate(sector["componentList"]):
+                if idx < len(component_list_config):
+                    comp_config = component_list_config[idx]
+                    if comp_config and isinstance(comp_config, list) and len(comp_config) > 0:
+                        comp_style_data = comp_config[0].get("component_style_data", [])
+                        
+                        # 如果有可用的样式数据,随机选择一个样式
+                        if comp_style_data:
+                            style_count = len(comp_style_data)
+                            random_style = random.randint(1, style_count)
+                            component["component_style"] = random_style
+                            print(f"初始化 {sector_name} 的第{idx+1}个组件样式为: {random_style}")
+                            # 更新推理过程
+                            #self.reasoning.append(f"初始化 {sector_name} 的第{idx+1}个组件样式为: {random_style}号样式")
+                        else:
+                            print(f"警告: {sector_name} 的第{idx+1}个组件没有可用的样式数据")
+                    else:
+                        print(f"警告: {sector_name} 的第{idx+1}个组件配置无效")
+                else:
+                    print(f"警告: {sector_name} 的组件数量({len(sector['componentList'])})超过配置数量({len(component_list_config)})")
+    
+    # 5.4 根据用户需求修改样式数据
+    def updata_component_style_to_user(self, website_id, matched_template_id, token):
+        # 需要跳过的通栏类型
+        skip_sectors = ["adSector", "linkSector", "linkCxfwSector"]
+        
+        # 1. 将用户输入转换为向量
+        user_embedding = self.text_to_embedding(self.user_input)
+        if len(user_embedding.shape) == 1:
+            user_embedding = user_embedding.reshape(1, -1)
+
+        # 更新推理过程
+        self.reasoning.append(f"最后让我挑选一下符合用户需求的组件样式")
+        
+        # 2. 遍历每个通栏
+        for sector in templateData.sectors_webSiteData["template"]["index"]:
+            sector_name = sector["sectorName"]
+            
+            # 跳过不需要修改样式的通栏
+            if sector_name in skip_sectors:
+                print(f"跳过 {sector_name} 的组件样式优化")
+                # 更新推理过程
+                #self.reasoning.append(f"跳过 {sector_name} 的组件样式优化")
+                continue
+            
+            # 3. 在样式数据中查找匹配的通栏
+            matched_style = next((s for s in self.component_style_data if s["sector_id"] == sector_name), None)
+            if not matched_style:
+                print(f"警告: 未找到 {sector_name} 的组件样式配置")
+                continue
+            
+            # 4. 遍历通栏中的每个组件
+            for idx, component in enumerate(sector["componentList"]):
+                comp_config_list = matched_style.get("component_list", [])
+                if idx >= len(comp_config_list) or not comp_config_list[idx]:
+                    continue
+                
+                comp_config = comp_config_list[idx][0]  # 取第一个配置
+                comp_style_data = comp_config.get("component_style_data", [])
+                
+                if not comp_style_data:
+                    continue
+                
+                # 5. 提取所有样式的描述文本
+                style_descriptions = [style["img_name"] for style in comp_style_data]
+                
+                # 6. 将样式描述转换为向量
+                style_embeddings = self.text_to_embedding(style_descriptions)
+                
+                # 7. 计算用户输入与每个样式的相似度
+                similarities = cosine_similarity(user_embedding, style_embeddings)[0]
+                best_index = np.argmax(similarities)
+                best_style = comp_style_data[best_index]
+                
+                # 8. 更新组件样式
+                component["component_style"] = best_style["img_id"]
+                print(f"优化 {sector_name} 的第{idx+1}个组件样式为: {best_style['img_name']} (ID: {best_style['img_id']})")
+                # 更新推理过程
+                self.reasoning.append(f"选择 {sector_name} 的第{idx+1}个组件样式为: {best_style['img_name']} (ID: {best_style['img_id']})")
+    
+    
+    """其他功能 模拟推理过程"""
+    # 获得选择通栏的中文名称
+    def test_normal_case_cnames(self):
+        #获得生成的模板数据
+        result = templateData.sectors_webSiteData["template"]["index"]
+        #更新推理过程
+        self.reasoning.append(f"接着我要创建一套符合用户需求的通栏组合")
+        #遍历result获得所有通栏的Cname
+        cnnames = []
+        
+        # 遍历每个通栏,使用enumerate获取索引
+        for i, sector in enumerate(result):
+            sector_name = sector["sectorName"]
+            # 在sectors_config中查找对应的中文名称
+            if self.current_style_data:
+                for config in self.current_style_data.sectors_config:
+                    if config["name"] == sector_name:
+                        cnnames.append(config["CNname"])
+                        # 使用通栏在结果列表中的索引+1作为序号
+                        self.reasoning.append(f"第{i+1}个通栏选择: {config['CNname']}")
+                        break  # 找到后跳出内层循环
+         
+    # 清理数据
+    def clear_data(self):
+        #清理模板数据
+        templateData.canvas_data["template"]["index"] = []
+        templateData.sectors_webSiteData["template"]["index"] = []
+        #清理推理数据
+        self.reasoning = []
+        self.current_style_data = None
+        #初始化权重
+        sectorStyle1to4Data.reset_sectors_config()
+        sectorStyle5Data.reset_sectors_config()
+
+    #添加推理过程
+    def add_reasoning(self, reasoning):
+        self.reasoning.append(reasoning)
+
+ai_service = AIService()

+ 52 - 0
services/demo.py

@@ -0,0 +1,52 @@
+import numpy as np
+from models.model_loader import model_loader
+
+class AIService:
+    # 创建AI处理类
+    def __init__(self):
+        self.text_model = model_loader.load_text_model()
+
+    # 常用AI方法
+    def text_to_embedding(self, text):
+        # 1.文本转向量
+        if isinstance(text, str):
+            text = [text]
+        
+        embeddings = self.text_model.encode(text)
+        # 返回numpy数组,不要转成list
+        return embeddings
+    
+    def calculate_similarity(self, text1, text2):
+        # 2.计算文本相似度
+        emb1 = self.text_to_embedding(text1)
+        emb2 = self.text_to_embedding(text2)
+        
+        # 确保是1D数组
+        if len(emb1.shape) > 1:
+            emb1 = emb1[0]
+        if len(emb2.shape) > 1:
+            emb2 = emb2[0]
+            
+        similarity = np.dot(emb1, emb2) / (
+            np.linalg.norm(emb1) * np.linalg.norm(emb2)
+        )
+        return float(similarity)
+    
+    def batch_text_processing(self, texts):
+        # 3.批量文本处理
+        embeddings = self.text_to_embedding(texts)
+        
+        # 简单的文本分析(可扩展)
+        results = []
+        for i, text in enumerate(texts):
+            results.append({
+                'text': text,
+                'embedding_size': embeddings.shape[1],  # 使用shape获取维度
+                'text_length': len(text),
+                'embedding': embeddings[i].tolist()  # 只在返回给前端时转成list
+            })
+        
+        return results
+
+# 全局AI服务实例
+ai_service = AIService()

BIN
services/style/__pycache__/style1to4data.cpython-313.pyc


BIN
services/style/__pycache__/style5data.cpython-313.pyc


+ 472 - 0
services/style/style1to4data.py

@@ -0,0 +1,472 @@
+'''
+    注意,复制数据的时候不要忘了把sectorCanvasHeight加上,因为我的数据跟前端的不太一样。
+    我生成画布数据需要sectorCanvasHeight画布高度。
+'''
+#1-4套皮肤 首页数据
+class sectorStyle1to4Data:
+    def __init__(self):
+
+        # 通栏权重设置
+        self.sectors_config = [
+            {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条" , "random": False},
+            {"name": "bannerSector", "total": 1, "weight": 9 ,"CNname":"焦点图" , "random": False},
+            {"name": "linkSector", "total": 1, "weight": 8 ,"CNname":"外链面板" , "random": False},
+            {"name": "manyPictureSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合1" , "random": True},
+            {"name": "commentSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合2" , "random": True},
+            {"name": "listSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合3" , "random": True},
+            {"name": "onlyImgSector", "total": 2, "weight": 7 ,"CNname":"带广告新闻组合" , "random": True},
+            {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告" , "random": False}
+        ]
+
+        # 通栏数据
+        self.sectors_data = {
+            #广告通栏
+            "adSector": {
+                "sectorName": "adSector",
+                "sectorCanvasHeight": 12, #占据画布高度
+                "componentList": [
+                    {
+                        "component_type": 2,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {}
+                    }
+                ],
+                "ad": {
+                    "width": 1200,
+                    "height": 90,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250530/1748588901281358.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                }
+            },
+            #头条通栏
+            "headLineSector": {
+                "sectorName": "headLineSector",
+                "sectorCanvasHeight": 17,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "level": 1,
+                            "imgSize": 0,
+                            "textSize": 4,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #轮播图通栏
+            "bannerSector": {
+                "sectorName": "bannerSector",
+                "sectorCanvasHeight": 44,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "level": 2,
+                            "imgSize": 5,
+                            "textSize": 0,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "level": 6,
+                            "imgSize": 0,
+                            "textSize": 10,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #新闻组合通栏1
+            "manyPictureSector": {
+                "sectorName": "manyPictureSector",
+                "sectorCanvasHeight": 47,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 3,
+                            "textSize": 9,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 3,
+                            "textSize": 6,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #新闻组合通栏2
+            "commentSector": {
+                "sectorName": "commentSector",
+                "sectorCanvasHeight": 47,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 2,
+                            "textSize": 12,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 1,
+                            "textSize": 3,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #新闻组合通栏3
+            "listSector": {
+                "sectorName": "listSector",
+                "sectorCanvasHeight": 98,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 2,
+                            "textSize": 6,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 2,
+                            "textSize": 14,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 3,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 7,
+                            "textSize": 0,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #广告新闻混合通栏
+            "onlyImgSector": {
+                "sectorName": "onlyImgSector",
+                "sectorCanvasHeight": 51,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 3,
+                            "textSize": 4,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                ],
+                "ad": {
+                    "width": 450,
+                    "height": 290,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250609/174945725555092.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                }
+            },
+            #外链通栏
+            "linkSector": {
+                "sectorName": "linkSector",
+                "sectorCanvasHeight": 26,
+                "componentList": [
+                    {
+                        "component_type": 3,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {}
+                    }
+                ]
+            },
+        } 
+
+    #初始化通栏权重
+    def reset_sectors_config(self):
+        self.sectors_config = [
+            {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条"},
+            {"name": "bannerSector", "total": 1, "weight": 9 ,"CNname":"焦点图"},
+            {"name": "linkSector", "total": 1, "weight": 8 ,"CNname":"外链面板"},
+            {"name": "manyPictureSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合1"},
+            {"name": "commentSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合2"},
+            {"name": "listSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合3"},
+            {"name": "onlyImgSector", "total": 2, "weight": 7 ,"CNname":"带广告新闻组合"},
+            {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告"}
+        ]
+
+#创建一个templateData实例
+sectorStyle1to4Data = sectorStyle1to4Data()

+ 587 - 0
services/style/style5data.py

@@ -0,0 +1,587 @@
+'''
+    注意,复制数据的时候不要忘了把sectorCanvasHeight加上,因为我的数据跟前端的不太一样。
+    我生成画布数据需要sectorCanvasHeight画布高度。
+'''
+#第5套皮肤 首页数据
+class sectorStyle5Data:
+    def __init__(self):
+
+        # 通栏权重设置
+        self.sectors_config = [
+            # 皮肤5-首页
+            {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条" , "random": False},
+            {"name": "bannerSectorMerge", "total": 1, "weight": 9 ,"CNname":"焦点图" , "random": False},
+            {"name": "linkCxfwSector", "total": 1, "weight": 8 ,"CNname":"外链面板-带查询服务" , "random": False},
+            {"name": "NewsHyjjSector", "total": 2, "weight": 7 ,"CNname":"行业聚焦新闻通栏" , "random": True},
+            {"name": "newsTabsSector", "total": 2, "weight": 7 ,"CNname":"热点新闻选项卡" , "random": True},
+            {"name": "newsTextSector", "total": 2, "weight": 7 ,"CNname":"文字新闻通栏1" , "random": True},
+            {"name": "newsTabsTextSector", "total": 2, "weight": 7 ,"CNname":"选项卡文字新闻通栏" , "random": True},
+            {"name": "newsAllPictureSector", "total": 2, "weight": 7 ,"CNname":"图片新闻通栏1" , "random": True},
+            {"name": "newsTabsAdSector", "total": 2, "weight": 7 ,"CNname":"选项卡资讯带两个广告" , "random": True},
+            {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告" , "random": False}
+        ]
+
+        # 通栏数据
+        self.sectors_data = {
+            #广告通栏
+            "adSector": {
+                "sectorName": "adSector",
+                "sectorCanvasHeight": 12, #占据画布高度
+                "componentList": [
+                    {
+                        "component_type": 2,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {}
+                    }
+                ],
+                "ad": {
+                    "width": 1200,
+                    "height": 90,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250530/1748588901281358.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                }
+            },
+            #皮肤5-新增轮播图通栏
+            "bannerSectorMerge": {
+                "sectorName": "bannerSectorMerge",
+                "sectorCanvasHeight": 44,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "level": 2,
+                            "imgSize": 5,
+                            "textSize": 7,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #皮肤5-新增外链面板-带查询服务
+            "linkCxfwSector": {
+                "sectorName": "linkCxfwSector",
+                "sectorCanvasHeight": 28,
+                "componentList": [
+                    {
+                        "component_type": 3,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {}
+                    }
+                ]
+            },
+            #行业聚焦新闻通栏
+            "NewsHyjjSector": {
+                "sectorName": "NewsHyjjSector",
+                "sectorCanvasHeight": 56,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id": "",
+                            "category_arr": "",
+                            "name": "请选择导航..",
+                            "level": "",
+                            "imgSize": 1,
+                            "textSize": 9,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        },
+                    },
+                ],
+            },
+            #热点新闻选项卡
+            "newsTabsSector": {
+                "sectorName": "newsTabsSector",
+                "sectorCanvasHeight": 38,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id": "",
+                            "category_arr": "",
+                            "name": "请选择导航..",
+                            "level": "",
+                            "imgSize": 0,
+                            "textSize": 5,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        },
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": [
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 5,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            },
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 5,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            }
+                        ]
+                    },
+                ]
+            },
+            #文字新闻通栏1
+            "newsTextSector": {
+                "sectorName": "newsTextSector",
+                "sectorCanvasHeight": 43,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id": "",
+                            "category_arr": "",
+                            "name": "请选择导航..",
+                            "level": "",
+                            "imgSize": 0,
+                            "textSize": 6,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id": "",
+                            "category_arr": "",
+                            "name": "请选择导航..",
+                            "level": "",
+                            "imgSize": 0,
+                            "textSize": 6,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #选项卡文字新闻通栏
+            "newsTabsTextSector": {
+                "sectorName": "newsTabsTextSector",
+                "sectorCanvasHeight": 28,
+                "componentList": [
+                    {
+                        "component_type": 4,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": [
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 5,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            },
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 5,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            }
+                        ]
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": [
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 5,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            },
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 5,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            }
+                        ]
+                    },
+                ]
+            },
+            #图片新闻通栏1
+            "newsAllPictureSector": {
+                "sectorName": "newsAllPictureSector",
+                "sectorCanvasHeight": 41,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id": "",
+                            "category_arr": "",
+                            "name": "请选择导航..",
+                            "level": "",
+                            "imgSize": 7,
+                            "textSize": 0,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        },
+                    },
+                ],
+            },
+            #选项卡资讯带两个广告
+            "newsTabsAdSector": {
+                "sectorName": "newsTabsAdSector",
+                "sectorCanvasHeight": 40,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": [
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 6,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            },
+                            {
+                                "category_id": "",
+                                "category_arr": "",
+                                "name": "请选择导航..",
+                                "level": "",
+                                "imgSize": 0,
+                                "textSize": 6,
+                                "child": {
+                                    "id": "",
+                                    "imgSize": "",
+                                    "textSize": ""
+                                },
+                                "listType": [
+                                    "id",
+                                    "title",
+                                    "imgurl",
+                                    "author",
+                                    "updated_at",
+                                    "introduce",
+                                    "islink",
+                                    "linkurl",
+                                    "copyfrom",
+                                    "cat_arr_id",
+                                    "catid",
+                                    "pinyin"
+                                ],
+                            }
+                        ]
+                    },
+                ],
+                "ad": [
+                    {
+                        "width": 380,
+                        "height": 148,
+                        "name": "",
+                        "price": 0,
+                        "introduce": "",
+                        "website_id": "",
+                        "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20251106/1762408575165380.png",
+                        "typeid": 2,
+                        "ad_tag": ""
+                    },
+                    {
+                        "width": 380, 
+                        "height": 148, 
+                        "name": "",
+                        "price": 0,
+                        "introduce": "",
+                        "website_id": "",
+                        "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20251106/1762408575165380.png",
+                        "typeid": 2,
+                        "ad_tag": ""
+                    }
+                ]
+            }
+        }
+
+    #初始化通栏权重
+    def reset_sectors_config(self):
+        self.sectors_config = [
+            {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条"},
+            {"name": "bannerSectorMerge", "total": 1, "weight": 9 ,"CNname":"焦点图"},
+            {"name": "linkCxfwSector", "total": 1, "weight": 8 ,"CNname":"外链面板-带查询服务"},
+            {"name": "NewsHyjjSector", "total": 2, "weight": 7 ,"CNname":"行业聚焦新闻通栏"},
+            {"name": "newsTabsSector", "total": 2, "weight": 7 ,"CNname":"热点新闻选项卡"},
+            {"name": "newsTextSector", "total": 2, "weight": 7 ,"CNname":"文字新闻通栏1"},
+            {"name": "newsTabsTextSector", "total": 2, "weight": 7 ,"CNname":"选项卡文字新闻通栏"},
+            {"name": "newsAllPictureSector", "total": 2, "weight": 7 ,"CNname":"图片新闻通栏1"},
+            {"name": "newsTabsAdSector", "total": 2, "weight": 7 ,"CNname":"选项卡资讯带两个广告"},
+            {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告"}
+        ]
+
+#创建一个templateData实例
+sectorStyle5Data = sectorStyle5Data()

+ 692 - 0
services/templateData.py

@@ -0,0 +1,692 @@
+import math
+import random
+from typing import List, Dict, Optional
+
+#首页数据
+class indexData:
+    def __init__(self):
+
+        # 通栏权重设置
+        self.sectors_config = [
+            {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条" , "random": False},
+            {"name": "bannerSector", "total": 1, "weight": 9 ,"CNname":"焦点图" , "random": False},
+            {"name": "linkSector", "total": 1, "weight": 8 ,"CNname":"外链面板" , "random": False},
+            {"name": "manyPictureSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合1" , "random": True},
+            {"name": "commentSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合2" , "random": True},
+            {"name": "listSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合3" , "random": True},
+            {"name": "onlyImgSector", "total": 2, "weight": 7 ,"CNname":"带广告新闻组合" , "random": True},
+            {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告" , "random": False}
+        ]
+
+        # 通栏数据
+        self.sectors_data = {
+            #广告通栏
+            "adSector": {
+                "sectorName": "adSector",
+                "sectorCanvasHeight": 12, #占据画布高度
+                "componentList": [
+                    {
+                        "component_type": 2,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {}
+                    }
+                ],
+                "ad": {
+                    "width": 1200,
+                    "height": 90,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250530/1748588901281358.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                }
+            },
+            #头条通栏
+            "headLineSector": {
+                "sectorName": "headLineSector",
+                "sectorCanvasHeight": 17,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "level": 1,
+                            "imgSize": 0,
+                            "textSize": 4,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #轮播图通栏
+            "bannerSector": {
+                "sectorName": "bannerSector",
+                "sectorCanvasHeight": 44,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "level": 2,
+                            "imgSize": 5,
+                            "textSize": 0,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "level": 6,
+                            "imgSize": 0,
+                            "textSize": 10,
+                            "child": {
+                                "id": "",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #新闻组合通栏1
+            "manyPictureSector": {
+                "sectorName": "manyPictureSector",
+                "sectorCanvasHeight": 47,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 3,
+                            "textSize": 9,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 3,
+                            "textSize": 6,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #新闻组合通栏2
+            "commentSector": {
+                "sectorName": "commentSector",
+                "sectorCanvasHeight": 47,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 2,
+                            "textSize": 12,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 1,
+                            "textSize": 3,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #新闻组合通栏3
+            "listSector": {
+                "sectorName": "listSector",
+                "sectorCanvasHeight": 98,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 2,
+                            "textSize": 6,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 2,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 2,
+                            "textSize": 14,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 3,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 7,
+                            "textSize": 0,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    }
+                ]
+            },
+            #广告新闻混合通栏
+            "onlyImgSector": {
+                "sectorName": "onlyImgSector",
+                "sectorCanvasHeight": 51,
+                "componentList": [
+                    {
+                        "component_type": 1,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {
+                            "category_id":"",
+                            "category_arr":"",
+                            "name":"请选择导航..",
+                            "level":"",
+                            "imgSize": 3,
+                            "textSize": 4,
+                            "child":{
+                                "id":"",
+                                "imgSize": "",
+                                "textSize": ""
+                            },
+                            "listType": [
+                                "id",
+                                "title",
+                                "imgurl",
+                                "author",
+                                "updated_at",
+                                "introduce",
+                                "islink",
+                                "linkurl",
+                                "copyfrom",
+                                "cat_arr_id",
+                                "catid",
+                                "pinyin"
+                            ],
+                        }
+                    },
+                ],
+                "ad": {
+                    "width": 450,
+                    "height": 290,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250609/174945725555092.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                }
+            },
+            #外链通栏
+            "linkSector": {
+                "sectorName": "linkSector",
+                "sectorCanvasHeight": 26,
+                "componentList": [
+                    {
+                        "component_type": 3,
+                        "component_style": 1,
+                        "sort": 1,
+                        "componentData": {}
+                    }
+                ]
+            },
+        }
+        #模板数据
+        self.sectors_webSiteData = {
+            "base": {
+                "websiteId": ""
+            },
+            "style": {
+                "styleId": ""
+            },
+            "template": {
+                "index": [],
+                "class": [],
+                "list": [],
+                "article": [],
+                "search": [],
+                "aloneList": [],
+                "aloneArticle": []
+            },
+            "ad": {
+                "top":{
+                    "width": 830,
+                    "height": 110,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250527/1748332370111555.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                },
+                "index": [],
+                "class": [],
+                "list": [],
+                "article": [],
+                "search": [],
+                "aloneList": [],
+                "aloneArticle": []
+            },
+            "isSearch": True
+        }
+
+        #画布数据
+        self.canvas_data = {
+            "topAd":{
+                "width": 830,
+                "height": 110,
+                "name": "",
+                "price": "",
+                "introduce": "",
+                "website_id": "",
+                "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250928/1759038686452927.png",
+                "typeid": 2,
+                "ad_tag": "",
+                "ad_url": ""
+            },
+            "template":{
+                "index":[],
+                "class": [],
+                "list": [],
+                "article": [],
+                "search": [],
+                "aloneList": [],
+                "aloneArticle": []
+            }
+        }
+
+    #初始化通栏权重
+    def reset_sectors_config(self):
+        self.sectors_config = [
+            {"name": "headLineSector", "total": 1, "weight": 10 ,"CNname":"网站头条"},
+            {"name": "bannerSector", "total": 1, "weight": 9 ,"CNname":"焦点图"},
+            {"name": "linkSector", "total": 1, "weight": 8 ,"CNname":"外链面板"},
+            {"name": "manyPictureSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合1"},
+            {"name": "commentSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合2"},
+            {"name": "listSector", "total": 2, "weight": 7 ,"CNname":"新闻图文组合3"},
+            {"name": "onlyImgSector", "total": 2, "weight": 7 ,"CNname":"带广告新闻组合"},
+            {"name": "adSector", "total": 4, "weight": 6 ,"CNname":"通栏广告"}
+        ]
+
+#创建一个templateData实例   
+templateData = indexData()
+
+# 动态权重计算类
+class Sector:
+    def __init__(self, name: str, total: int, weight: int, CNname: str):
+        self.name = name # 名称
+        self.total = total # 总数
+        self.weight = weight # 权重
+        self.remaining = total # 剩余数量 后面我们要对比 剩余数据/总数 来计算权重
+        self.used_count = 0  # 记录已使用次数
+        self.CNname = CNname # 中文名称
+
+"""
+    思路:
+    1.按照权限的高低选择通栏
+    2.如果权限一样,随机选择
+    3.每选择两次新闻通栏后,adSector权重提升
+"""
+class SectorScheduler:
+    def __init__(self, sectors_config: List[Dict]):
+        # 过滤掉total为0的sector(即不存在的sector)
+        self.sectors = [
+            Sector(sector['name'], sector['total'], sector['weight'], sector['CNname']) 
+            for sector in sectors_config 
+            if sector['total'] > 0
+        ]
+        self.history = []  # 记录选择历史
+        self.consecutive_count = 0  # 连续选择相同权重sector的次数
+        self.last_weight = None  # 上一次选择的权重
+        
+        # 定义需要随机选择的sector组
+        self.random_sector_group = {sector['name'] for sector in sectors_config if sector.get('random', False)}
+    
+    def calculate_dynamic_weight(self, sector: Sector) -> float:
+        """计算动态权重 - 更复杂的权重计算"""
+        if sector.remaining <= 0:
+            return 0
+        
+        base_weight = sector.weight
+        
+        # 1. 基于剩余数量的权重调整
+        usage_ratio = sector.remaining / sector.total
+        quantity_factor = math.pow(usage_ratio, 0.4)  # 剩余数量越多,权重相对越高
+        
+        # 2. 基于使用频率的权重调整(避免过度使用)
+        if sector.used_count > 0:
+            usage_frequency = 1.0 / (1 + math.log(1 + sector.used_count))
+        else:
+            usage_frequency = 1.0
+        
+        # 3. 对于adSector的特殊处理
+        if sector.name == "adSector":
+            # 检查是否需要插入adSector(每两个相同权重后)
+            if self.consecutive_count >= 2 and self.last_weight == 7:
+                ad_boost = 2.0  # 大幅提升adSector权重
+            else:
+                # 根据历史中adSector的出现频率调整
+                recent_ad_count = self.history[-3:].count("adSector") if len(self.history) >= 3 else 0
+                if recent_ad_count == 0:
+                    ad_boost = 1.5  # 一段时间没出现,适当提升
+                else:
+                    ad_boost = 0.8  # 最近出现较多,适当降低
+        else:
+            ad_boost = 1.0
+        
+        # 4. 对于高权重sector的保护(确保它们优先被选择)
+        if base_weight >= 8:
+            priority_boost = 1.5
+        else:
+            priority_boost = 1.0
+        
+        dynamic_weight = base_weight * quantity_factor * usage_frequency * ad_boost * priority_boost
+        
+        return dynamic_weight
+    
+    def get_random_sector_from_group(self, target_weight: int) -> Optional[Sector]:
+        """从随机组中随机选择一个指定权重的sector"""
+        available_sectors = [
+            sector for sector in self.sectors 
+            if sector.name in self.random_sector_group 
+            and sector.weight == target_weight 
+            and sector.remaining > 0
+        ]
+        
+        if available_sectors:
+            return random.choice(available_sectors)
+        return None
+    
+    def select_next(self) -> Optional[str]:
+        """选择下一个sector"""
+        best_sector = None
+        best_weight = -1
+        
+        current_weight = 7  # 随机选择组的权重
+        
+        # 首先检查是否需要强制插入adSector
+        if (self.consecutive_count >= 2 and self.last_weight == current_weight and
+            any(s.name == "adSector" and s.remaining > 0 for s in self.sectors)):
+            
+            ad_sector = next(s for s in self.sectors if s.name == "adSector" and s.remaining > 0)
+            ad_sector.remaining -= 1
+            ad_sector.used_count += 1
+            
+            # 更新历史记录
+            self.history.append("adSector")
+            self.consecutive_count = 0
+            self.last_weight = ad_sector.weight
+            
+            return "adSector"
+        
+        # 正常选择逻辑
+        for sector in self.sectors:
+            if sector.remaining > 0:
+                # 对于随机选择组的sector,使用统一的权重计算
+                if sector.name in self.random_sector_group and sector.weight == current_weight:
+                    # 使用组内统一的权重值进行比较
+                    dynamic_weight = current_weight
+                else:
+                    dynamic_weight = self.calculate_dynamic_weight(sector)
+                
+                if dynamic_weight > best_weight:
+                    best_weight = dynamic_weight
+                    best_sector = sector
+        
+        if best_sector:
+            # 如果最佳sector属于随机选择组,则随机选择该组中的一个
+            if best_sector.name in self.random_sector_group and best_sector.weight == current_weight:
+                random_sector = self.get_random_sector_from_group(current_weight)
+                if random_sector:
+                    best_sector = random_sector
+            
+            best_sector.remaining -= 1
+            best_sector.used_count += 1
+            
+            # 更新连续选择计数
+            if self.last_weight == best_sector.weight and best_sector.name != "adSector":
+                self.consecutive_count += 1
+            else:
+                self.consecutive_count = 1
+            
+            self.last_weight = best_sector.weight
+            self.history.append(best_sector.name)
+            
+            return best_sector.name
+        
+        return None
+    
+    def generate_sequence(self, count: int) -> List[str]:
+        """生成指定长度的序列"""
+        result = []
+        for i in range(count):
+            next_sector = self.select_next()
+            if next_sector:
+                result.append(next_sector)
+            else:
+                break  # 没有可选的sector了
+        return result

+ 230 - 0
services/templateFun.py

@@ -0,0 +1,230 @@
+import math
+import random
+from typing import List, Dict, Optional
+
+#首页数据
+class indexData:
+    def __init__(self):
+        #提交的模板数据
+        self.sectors_webSiteData = {
+            "base": {
+                "websiteId": ""
+            },
+            "style": {
+                "styleId": ""
+            },
+            "template": {
+                "index": [],
+                "class": [],
+                "list": [],
+                "article": [],
+                "search": [],
+                "aloneList": [],
+                "aloneArticle": []
+            },
+            "ad": {
+                "top":{
+                    "width": 830,
+                    "height": 110,
+                    "name": "",
+                    "price": 0,
+                    "introduce":"",
+                    "website_id": "",
+                    "thumb": "http://img.bjzxtw.org.cn/pre/image/png/20250527/1748332370111555.png",
+                    "typeid": 2,
+                    "ad_tag": ""
+                },
+                "index": [],
+                "class": [],
+                "list": [],
+                "article": [],
+                "search": [],
+                "aloneList": [],
+                "aloneArticle": []
+            },
+            "isSearch": True
+        }
+
+        #提交的画布数据
+        self.canvas_data = {
+            "topAd":{
+                "width": 830,
+                "height": 110,
+                "name": "",
+                "price": "",
+                "introduce": "",
+                "website_id": "",
+                "thumb": "https://img.bjzxtw.org.cn/pre/image/png/20250928/1759038686452927.png",
+                "typeid": 2,
+                "ad_tag": "",
+                "ad_url": ""
+            },
+            "template":{
+                "index":[],
+                "class": [],
+                "list": [],
+                "article": [],
+                "search": [],
+                "aloneList": [],
+                "aloneArticle": []
+            }
+        }
+
+#创建一个templateData实例   
+templateData = indexData()
+
+# 动态权重计算类
+class Sector:
+    def __init__(self, name: str, total: int, weight: int, CNname: str):
+        self.name = name # 名称
+        self.total = total # 总数
+        self.weight = weight # 权重
+        self.remaining = total # 剩余数量 后面我们要对比 剩余数据/总数 来计算权重
+        self.used_count = 0  # 记录已使用次数
+        self.CNname = CNname # 中文名称
+
+"""
+    思路:
+    1.按照权限的高低选择通栏
+    2.如果权限一样,随机选择
+    3.每选择两次新闻通栏后,adSector权重提升
+"""
+class SectorScheduler:
+    def __init__(self, sectors_config: List[Dict]):
+        # 过滤掉total为0的sector(即不存在的sector)
+        self.sectors = [
+            Sector(sector['name'], sector['total'], sector['weight'], sector['CNname']) 
+            for sector in sectors_config 
+            if sector['total'] > 0
+        ]
+        self.history = []  # 记录选择历史
+        self.consecutive_count = 0  # 连续选择相同权重sector的次数
+        self.last_weight = None  # 上一次选择的权重
+        
+        # 定义需要随机选择的sector组
+        self.random_sector_group = {sector['name'] for sector in sectors_config if sector.get('random', False)}
+    
+    def calculate_dynamic_weight(self, sector: Sector) -> float:
+        """计算动态权重 - 更复杂的权重计算"""
+        if sector.remaining <= 0:
+            return 0
+        
+        base_weight = sector.weight
+        
+        # 1. 基于剩余数量的权重调整
+        usage_ratio = sector.remaining / sector.total
+        quantity_factor = math.pow(usage_ratio, 0.4)  # 剩余数量越多,权重相对越高
+        
+        # 2. 基于使用频率的权重调整(避免过度使用)
+        if sector.used_count > 0:
+            usage_frequency = 1.0 / (1 + math.log(1 + sector.used_count))
+        else:
+            usage_frequency = 1.0
+        
+        # 3. 对于adSector的特殊处理
+        if sector.name == "adSector":
+            # 检查是否需要插入adSector(每两个相同权重后)
+            if self.consecutive_count >= 2 and self.last_weight == 7:
+                ad_boost = 2.0  # 大幅提升adSector权重
+            else:
+                # 根据历史中adSector的出现频率调整
+                recent_ad_count = self.history[-3:].count("adSector") if len(self.history) >= 3 else 0
+                if recent_ad_count == 0:
+                    ad_boost = 1.5  # 一段时间没出现,适当提升
+                else:
+                    ad_boost = 0.8  # 最近出现较多,适当降低
+        else:
+            ad_boost = 1.0
+        
+        # 4. 对于高权重sector的保护(确保它们优先被选择)
+        if base_weight >= 8:
+            priority_boost = 1.5
+        else:
+            priority_boost = 1.0
+        
+        dynamic_weight = base_weight * quantity_factor * usage_frequency * ad_boost * priority_boost
+        
+        return dynamic_weight
+    
+    def get_random_sector_from_group(self, target_weight: int) -> Optional[Sector]:
+        """从随机组中随机选择一个指定权重的sector"""
+        available_sectors = [
+            sector for sector in self.sectors 
+            if sector.name in self.random_sector_group 
+            and sector.weight == target_weight 
+            and sector.remaining > 0
+        ]
+        
+        if available_sectors:
+            return random.choice(available_sectors)
+        return None
+    
+    def select_next(self) -> Optional[str]:
+        """选择下一个sector"""
+        best_sector = None
+        best_weight = -1
+        
+        current_weight = 7  # 随机选择组的权重
+        
+        # 首先检查是否需要强制插入adSector
+        if (self.consecutive_count >= 2 and self.last_weight == current_weight and
+            any(s.name == "adSector" and s.remaining > 0 for s in self.sectors)):
+            
+            ad_sector = next(s for s in self.sectors if s.name == "adSector" and s.remaining > 0)
+            ad_sector.remaining -= 1
+            ad_sector.used_count += 1
+            
+            # 更新历史记录
+            self.history.append("adSector")
+            self.consecutive_count = 0
+            self.last_weight = ad_sector.weight
+            
+            return "adSector"
+        
+        # 正常选择逻辑
+        for sector in self.sectors:
+            if sector.remaining > 0:
+                # 对于随机选择组的sector,使用统一的权重计算
+                if sector.name in self.random_sector_group and sector.weight == current_weight:
+                    # 使用组内统一的权重值进行比较
+                    dynamic_weight = current_weight
+                else:
+                    dynamic_weight = self.calculate_dynamic_weight(sector)
+                
+                if dynamic_weight > best_weight:
+                    best_weight = dynamic_weight
+                    best_sector = sector
+        
+        if best_sector:
+            # 如果最佳sector属于随机选择组,则随机选择该组中的一个
+            if best_sector.name in self.random_sector_group and best_sector.weight == current_weight:
+                random_sector = self.get_random_sector_from_group(current_weight)
+                if random_sector:
+                    best_sector = random_sector
+            
+            best_sector.remaining -= 1
+            best_sector.used_count += 1
+            
+            # 更新连续选择计数
+            if self.last_weight == best_sector.weight and best_sector.name != "adSector":
+                self.consecutive_count += 1
+            else:
+                self.consecutive_count = 1
+            
+            self.last_weight = best_sector.weight
+            self.history.append(best_sector.name)
+            
+            return best_sector.name
+        
+        return None
+    
+    def generate_sequence(self, count: int) -> List[str]:
+        """生成指定长度的序列"""
+        result = []
+        for i in range(count):
+            next_sector = self.select_next()
+            if next_sector:
+                result.append(next_sector)
+            else:
+                break  # 没有可选的sector了
+        return result

+ 311 - 0
services/work.py

@@ -0,0 +1,311 @@
+from flask import request,Response,stream_with_context 
+from utils.request import post_request,generate_error
+import json
+import time
+from services.ai_service import ai_service
+from services.templateFun import templateData
+#防止fetch请求中断
+from utils.offFetch import RequestInterruptHandler
+#判断用户需求
+from utils.verifyInput import messageValidator
+#判断是否含有特殊指令
+from utils.command import commandObj
+
+def stream_response():
+    
+    # 0.在重组数据之前,应该先清空原来的数据
+    ai_service.clear_data()
+
+    """第一步:获得必备参数"""
+    # 1.1获得header中的website_id
+    website_id = request.get_json().get('website_id')
+    # 1.2获得header中的Token
+    token = request.headers.get('Token')
+    # 1.3获得header中的会话id
+    session_id = request.get_json().get('session_id')
+    # 1.4获得用户输入userMessage
+    userMessage = request.get_json().get('userMessage')
+    print("接收到前端用户输入:" + userMessage)
+    # 1.5当前消息是否需要生成标题(摘要)
+    ifNewMessage = request.get_json().get('new_msg')
+    
+
+    """第二步:验证网站是否可编辑"""
+    checkEdit = post_request("website/checkWebsiteEdit", {
+        "website_id": website_id
+    },token)
+
+    if checkEdit.get('code') != 200:
+        # 2.1如果网站不可编辑或者用户未登录,则返回异常信息
+        return Response(generate_error(checkEdit.get('message')), mimetype='text/plain')
+
+    else:
+        # 2.2开始请求网站皮肤
+        getAllTemplate = post_request("public/getAITemplate", {
+            "website_id": website_id
+        },token)
+
+        # 2.3获得皮肤数据
+        skinData = getAllTemplate.get('data').get('template')
+
+        # 判断用户输入是否符合要求
+        if messageValidator.validate(userMessage):
+            print("用户输入符合要求,允许进修后续操作")
+
+            # 储存用户输入
+            ai_service.user_input = userMessage
+
+            # 1=是新会话,需要修改标题摘要
+            if(ifNewMessage==1):
+                # 生成标题
+                title = ai_service.userMessage_to_title(userMessage)
+                print("该会话需要生成标题:" + title)
+
+                getAllTemplate = post_request("/public/upAiSession", {
+                    "session_id": session_id,
+                    "session_title": title
+                },token)
+
+                if getAllTemplate.get('code') != 200:
+                    # 如果创建会话失败,返回错误
+                    print("修改会话标题失败!" + getAllTemplate.get('message'))
+                else:
+                    print("修改会话标题成功!" + getAllTemplate.get('message'))
+            else:
+                print("该消息不需要生成标题!")
+                    
+            # 2.4使用AI服务找到最匹配的模板
+            best_match = ai_service.find_best_matching_template(userMessage, skinData)
+            matched_template_name = "默认模板"
+            matched_template_id = 0
+
+            if best_match:
+                matched_template_name = best_match['template_info']['template_name']
+                matched_template_id = best_match['template_info']['template_id']
+                similarity_score = best_match['similarity_score']
+                print(f"找到最匹配的模板: {matched_template_name}, 相似度: {similarity_score:.4f}")
+            else:
+                print("未找到匹配的模板,使用默认模板")
+
+            # 2.5开始执行数据重组操作
+            templateData.sectors_webSiteData["base"]["websiteId"] = website_id
+            templateData.sectors_webSiteData["style"]["styleId"] = matched_template_id
+
+            """demo : 当前缺少的逻辑:通栏应该是从后端获取的,而不是存在这里的!"""
+            # 2.6依照用户需求筛选通栏
+            ai_service.filter_sectors(userMessage,matched_template_id)
+
+            # 2.7生成通栏数据
+            ai_service.get_sectors(website_id,matched_template_id,token)
+            """demo : 当前缺少的逻辑:通栏应该是从后端获取的,而不是存在这里的!"""
+            
+            #组合待提交的数据
+            submit_data = {
+                "website_id": website_id,
+                "session_id": session_id,
+                "template_id": matched_template_id,
+                "template_data": json.dumps(templateData.sectors_webSiteData, ensure_ascii=False),
+                "canvas_data": json.dumps(templateData.canvas_data, ensure_ascii=False)
+            }
+
+            """demo:会话编号应该通过创建会话来取得,目前先使用固定的编号进行测试 自助建站标准化测试平台website_id:109 会话id:09504811488333"""
+            template_ref = post_request("public/addTemplateDraftbox", submit_data ,token)
+
+            if template_ref.get('code') != 200:
+                print("临时模板保存失败!" + template_ref.get('message'))
+            else:
+                print("临时模板保存成功!" + template_ref.get('message'))
+
+
+            #2.7判断用户是否需要填充导航
+            command_number = commandObj.get_command(userMessage)
+            if command_number == 1:
+                print("用户需要填充导航")
+
+                templateCates = post_request("/public/addTemplateCates", {
+                    "session_id": session_id,
+                },token)
+                if templateCates.get('code') != 200:
+                    ai_service.add_reasoning(f"模板导航因远程服务器故障未能完成添加")
+                    print("填充导航失败!" + templateCates.get('message'))
+                else:
+                    ai_service.add_reasoning(f"我已经按照用户的要求给每个组件都选择了导航")
+                    print("填充导航成功!" + templateCates.get('message'))
+
+            else:
+                print("用户不需要填充导航")
+            
+            # 2.8获得推理过程
+            reasoning = ai_service.reasoning
+            reasoning_str = "。".join(reasoning)
+
+            """第三步:返回内容生成器"""
+            def generate():
+                with RequestInterruptHandler(request) as handler:
+                    texts = [
+                        {"content": reasoning_str, "class": "computerLi"},
+                        {"content": "👌久等了!您的需求已完成:", "class": "computertextBold"},
+                        {"content": "我使用了皮肤库中名为:"+ matched_template_name +"的皮肤,网站通栏与组件的构建已经完成。", "class": "computertext"},
+                        {"content": best_match['template_info'].get('template_img')[0].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[0].get('name')},
+                        {"content": best_match['template_info'].get('template_img')[1].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[1].get('name')},
+                        {"content": best_match['template_info'].get('template_img')[2].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[2].get('name')},
+                        {"content": best_match['template_info'].get('template_img')[3].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[3].get('name')},
+                        {"content": best_match['template_info'].get('template_img')[4].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[4].get('name')},
+                        {"content": best_match['template_info'].get('template_img')[5].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[5].get('name')},
+                        {"content": best_match['template_info'].get('template_img')[6].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[6].get('name')},
+                        {"content": "点击下面👇的链接可以查看该模板的设计效果,如果需要修改请告诉我。", "class": "computertext"},
+                        {"content": "/#/templateCreat?website_id=" + str(website_id) + "&style=" + str(matched_template_id) + "&mode=ai&session_id=" + session_id, "class": "computertextLink"}
+                    ]
+                
+                    for text_item in texts:
+                        # 检查客户端是否断开
+                        if not handler.is_client_connected():
+                            print("客户端已断开连接,停止发送")
+                            handler.client_closed = True
+                            break
+                        
+                        text = text_item["content"]
+                        text_class = text_item["class"]
+                        pagename = text_item.get('pagename')
+
+                        if text_class in ["computerImgBox", "computertextLink"]:
+                            data = {
+                                'code': 200,
+                                'type': text_class,
+                                'pagename': pagename,
+                                'data': text,
+                                'finished': False,
+                                'isComplete': True
+                            }
+                            yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
+                        else:
+                            for char in text:
+                                data = {
+                                    'code': 200,
+                                    'type': text_class,
+                                    'data': char,
+                                    'finished': False,
+                                    'isComplete': False
+                                }
+                                yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
+                                time.sleep(0.02)
+                            
+                            # 发送完成信号
+                            complete_data = {
+                                'code': 200,
+                                'type': text_class,
+                                'data': '',
+                                'finished': False,
+                                'isComplete': True
+                            }
+                            yield f"data: {json.dumps(complete_data, ensure_ascii=False)}\n\n"
+                        
+                        # 发送换行符
+                        newline_data = {
+                            'code': 200,
+                            'type': 'newline',
+                            'data': '\n\n',
+                            'finished': False,
+                            'isComplete': True
+                        }
+                        yield f"data: {json.dumps(newline_data, ensure_ascii=False)}\n\n"
+                    
+                    # 如果连接还在,发送结束信号
+                    if not handler.client_closed:
+                        end_data = {
+                            'code': 200,
+                            'data': '',
+                            'finished': True,
+                            'full_text': "处理结果已发送完毕!"
+                        }
+                        yield f"data: {json.dumps(end_data, ensure_ascii=False)}\n\n"
+            
+            return Response(stream_with_context(generate()), mimetype='text/event-stream')
+
+        else:
+            """第四步:处理非模板生成类请求"""
+
+            # 如果用户一上来输入非模板问题,这里需要生成一个标题
+            # 1=是新会话,需要修改标题摘要
+            if(ifNewMessage==1):
+                # 生成标题
+                title = "非模板需求的会话"
+                print("该会话需要生成标题:" + title)
+
+                getAllTemplate = post_request("/public/upAiSession", {
+                    "session_id": session_id,
+                    "session_title": title
+                },token)
+
+                if getAllTemplate.get('code') != 200:
+                    # 如果创建会话失败,返回错误
+                    print("修改会话标题失败!" + getAllTemplate.get('message'))
+                else:
+                    print("修改会话标题成功!" + getAllTemplate.get('message'))
+            else:
+                print("该消息不需要生成标题!")
+
+            # 返回错误信息
+            def generate_error_response():
+                texts = [
+                    {"content": "对不起,您的指令暂不支持!", "class": "computertext"},
+                    {"content": "小龙包暂时无法处理非网站模板生成类的问题😅,我们将会在未来的版本中增加对更多指令的支持,敬请期待!", "class": "computertext"},
+                    {"content": "推荐的提示词语句:", "class": "computertextBoldLine"},
+                    {"content": "1️⃣ 帮我推荐一套中国传统红色皮肤。", "class": "computertext"},
+                    {"content": "2️⃣ 帮我推荐一套中国传统红色皮肤,把焦点图放到第一个,去掉所有广告。", "class": "computertext"},
+                    {"content": "3️⃣ 帮我推荐一套中国传统红色皮肤,把焦点图放到第一个,去掉所有广告,帮我填好导航。", "class": "computertext"},
+                ]
+
+                for text_item in texts:
+                    text = text_item["content"]
+                    text_class = text_item["class"]
+                    
+                    for char in text:
+                        data = {
+                            'code': 200,
+                            'type': text_class,
+                            'data': char,
+                            'finished': False,
+                            'isComplete': False
+                        }
+                        yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
+                        time.sleep(0.02)
+                    
+                    # 发送完成信号
+                    complete_data = {
+                        'code': 200,
+                        'type': text_class,
+                        'data': '',
+                        'finished': False,
+                        'isComplete': True
+                    }
+                    yield f"data: {json.dumps(complete_data, ensure_ascii=False)}\n\n"
+                    
+                    # 发送换行符
+                    newline_data = {
+                        'code': 200,
+                        'type': 'newline',
+                        'data': '\n\n',
+                        'finished': False,
+                        'isComplete': True
+                    }
+                    yield f"data: {json.dumps(newline_data, ensure_ascii=False)}\n\n"
+
+                end_data = {
+                    'code': 200,
+                    'data': '',
+                    'finished': True,
+                    'full_text': "处理结果已发送完毕!"
+                }
+                
+                yield f"data: {json.dumps(end_data, ensure_ascii=False)}\n\n"
+
+            return Response(stream_with_context(generate_error_response()), mimetype='text/event-stream')
+
+
+
+
+
+
+
+        

+ 0 - 0
utils/__init__.py


BIN
utils/__pycache__/__init__.cpython-313.pyc


BIN
utils/__pycache__/command.cpython-313.pyc


BIN
utils/__pycache__/inputType.cpython-313.pyc


BIN
utils/__pycache__/matching.cpython-313.pyc


BIN
utils/__pycache__/offFetch.cpython-313.pyc


BIN
utils/__pycache__/request.cpython-313.pyc


BIN
utils/__pycache__/verifyInput.cpython-313.pyc


+ 16 - 0
utils/command.py

@@ -0,0 +1,16 @@
+
+class command:
+    def __init__(self):
+        self.command_list = [
+            {"number": 1, "text": "帮我填好导航"},
+        ]
+
+    # 根据用户输入返回对应的命令编号
+    def get_command(self, userMsg):
+        for cmd in self.command_list:
+            if cmd["text"] in userMsg:
+                return cmd["number"]
+        return 0
+
+commandObj = command()
+        

+ 164 - 0
utils/inputType.py

@@ -0,0 +1,164 @@
+# inputType.py
+# from services.templateData import templateData
+# 引入1-4套皮肤数据
+from services.style.style1to4data import sectorStyle1to4Data 
+# 引入5套皮肤数据
+from services.style.style5data import sectorStyle5Data
+
+class inputType:
+    def __init__(self):
+        self.del_map = [
+            "删除", "去掉", "移除", "去除", "不要", "删掉", "不需要", "没有"
+        ]
+
+        self.del_group_map = [
+            "不要显示广告", "去掉所有广告", "纯净版" , "只要新闻"
+        ]
+
+        self.sectors_name = {
+            # 公用通栏
+            "adSector": "通栏广告",
+
+            # 1-4套皮肤
+            "headLineSector": "网站头条",
+            "bannerSector": "焦点图",
+            "linkSector": "外链面板",
+            "manyPictureSector": "新闻图文组合1",
+            "commentSector": "新闻图文组合2",
+            "listSector": "新闻图文组合3",
+            "onlyImgSector": "带广告新闻组合",
+            
+            # 第5套皮肤
+            "headLineSector": "网站头条",
+            "bannerSectorMerge": "焦点图",
+            "linkCxfwSector": "外链面板-带查询服务",
+            "NewsHyjjSector": "行业聚焦新闻通栏",
+            "newsTabsSector": "热点新闻选项卡",
+            "newsTextSector": "文字新闻通栏1",
+            "newsTabsTextSector": "选项卡文字新闻通栏",
+            "newsAllPictureSector": "图片新闻通栏1",
+            "newsTabsAdSector": "选项卡资讯带两个广告",
+        }
+        
+        self.pos_first = ["第一个", "第一", "首位", "最前面", "开头", "第一位", "顶部"]
+        self.pos_second = ["第二个", "第二", "第二位"]
+        self.pos_third = ["第三个", "第三", "第三位"]
+        self.pos_last = ["最后一个", "最后", "末尾", "垫底", "最后一位", "底部"]
+        self.pos_last_second = ["倒数第二个", "倒数第二", "倒数第二位"]
+        self.pos_last_third = ["倒数第三个", "倒数第三", "倒数第三位"]
+       
+    def generate_inputType(self, user_text,matched_template_id): 
+        # 导入推理方法
+        from services.ai_service import ai_service    
+        import re
+
+        # 按标点符号分句处理,支持多指令
+        segments = re.split(r'[,,。;;|]+', user_text)
+        segments = [s.strip() for s in segments if s.strip()]
+
+        templateData = None
+
+        # 根据matched_template_id选择样式数据
+        if int(matched_template_id) == 5:
+            print("正在使用第5套皮肤的数据!")
+            templateData = sectorStyle5Data
+        else:
+            print("正在使用前4套皮肤的数据!")
+            templateData = sectorStyle1to4Data
+
+        if not segments:
+            segments = [user_text]
+
+        for segment in segments:
+            # 1. 检查用户输入中是否包含组删除词
+            if any(word in segment for word in self.del_group_map):
+                # 删除所有广告相关通栏
+                # ad_sectors = ["通栏广告", "带广告新闻组合"]
+                # 找到所有名字中含有广告的通栏
+                ad_sectors = [sector for sector in templateData.sectors_config if "广告" in sector['CNname']]
+                
+                templateData.sectors_config = [
+                    sector for sector in templateData.sectors_config 
+                    if sector not in ad_sectors
+                ]
+                # 添加推理过程
+                ai_service.add_reasoning(f"另外,用户不想看到广告,因此我修改了通栏列表,只保留了新闻相关通栏")
+                print(f"已删除所有广告通栏: {ad_sectors}")
+            
+            # 2. 检查用户输入中是否包含普通删除词
+            elif any(word in segment for word in self.del_map):
+                # 查找用户输入中提到的通栏名称
+                sectors_to_delete = []
+                for cnname in self.sectors_name.values():
+                    if cnname in segment:
+                        sectors_to_delete.append(cnname)
+            
+                # 删除匹配的通栏配置
+                if sectors_to_delete:
+                    templateData.sectors_config = [
+                        sector for sector in templateData.sectors_config 
+                        if sector['CNname'] not in sectors_to_delete
+                    ]
+                    # 添加推理过程
+                    ai_service.add_reasoning(f"另外,用户想要删除{sectors_to_delete}通栏,我来把它隐藏掉")
+                    print(f"已删除通栏: {sectors_to_delete}")
+            
+            # 3. 检查用户输入中是否包含位置调整
+            # 遍历所有通栏名称
+            for name, cnname in self.sectors_name.items():
+                if cnname in segment:
+                    new_weight = None
+                    reasoning_msg = ""
+                    
+                    # 获取当前最大和最小权重
+                    current_weights = [s['weight'] for s in templateData.sectors_config]
+                    if not current_weights:
+                        break
+                    max_weight = max(current_weights)
+                    min_weight = min(current_weights)
+                    
+                    # 检查位置关键词
+                    force_pos_val = None
+                    
+                    if any(word in segment for word in self.pos_first):
+                        new_weight = max_weight + 2
+                        reasoning_msg = f"用户想要把{cnname}放在第一位,我提升了它的权重"
+                    elif any(word in segment for word in self.pos_second):
+                        new_weight = max_weight - 1
+                        reasoning_msg = f"用户想要把{cnname}放在第二位,我调整了它的权重"
+                    elif any(word in segment for word in self.pos_third):
+                        new_weight = max_weight - 2
+                        reasoning_msg = f"用户想要把{cnname}放在第三位,我调整了它的权重"
+                    elif any(word in segment for word in self.pos_last):
+                        new_weight = min_weight - 3
+                        force_pos_val = 1
+                        reasoning_msg = f"用户想要把{cnname}放在最后一位,我降低了它的权重"
+                    elif any(word in segment for word in self.pos_last_second):
+                        if min_weight < 6:
+                            new_weight = min_weight + 1
+                        else:
+                            new_weight = min_weight - 2
+                        force_pos_val = 2
+                        reasoning_msg = f"用户想要把{cnname}放在倒数第二位,我调整了它的权重"
+                    elif any(word in segment for word in self.pos_last_third):
+                        if min_weight < 6:
+                            new_weight = min_weight + 2
+                        else:
+                            new_weight = min_weight - 1
+                        force_pos_val = 3
+                        reasoning_msg = f"用户想要把{cnname}放在倒数第三位,我调整了它的权重"
+                    
+                    # 如果确定了新权重,进行修改
+                    if new_weight is not None:
+                        for sector in templateData.sectors_config:
+                            if sector['name'] == name:
+                                sector['weight'] = new_weight
+                                if force_pos_val is not None:
+                                    sector['force_pos'] = force_pos_val
+                                # 添加推理过程
+                                ai_service.add_reasoning(reasoning_msg)
+                                print(f"已调整通栏 {cnname} 权重为: {new_weight}")
+                                break
+        
+# 全局匹配器实例
+inputType_matcher = inputType()

+ 170 - 0
utils/matching.py

@@ -0,0 +1,170 @@
+# matching.py
+import datetime
+import re
+
+class TitleMatcher:
+    def __init__(self):
+        # 完整的颜色映射表
+        self.color_map = {
+            # 红色系
+            "红色": "红色", "红": "红色", "大红": "红色", "深红": "红色", "浅红": "浅红色",
+            "粉红": "粉红色", "粉色": "粉红色", "粉": "粉红色", "玫红": "玫红色",
+            # 橙色系
+            "橙色": "橙色", "橙": "橙色", "橘色": "橙色", "橘黄": "橙黄色", "桔色": "橙色",
+            # 黄色系
+            "黄色": "黄色", "黄": "黄色", "金黄": "金黄色", "浅黄": "浅黄色", "深黄": "深黄色",
+            "橘黄": "橙黄色",
+            # 绿色系
+            "绿色": "绿色", "绿": "绿色", "墨绿": "墨绿色", "深绿": "深绿色", "浅绿": "浅绿色",
+            "翠绿": "翠绿色", "草绿": "草绿色", "青绿": "青绿色", "军绿": "军绿色",
+            # 青色系
+            "青色": "青色", "青": "青色", "蓝绿": "青绿色", "青蓝": "青蓝色", "淡青": "淡青色",
+            # 蓝色系
+            "蓝色": "蓝色", "蓝": "蓝色", "深蓝": "深蓝色", "浅蓝": "浅蓝色", "天蓝": "天蓝色",
+            "宝蓝": "宝蓝色", "湖蓝": "湖蓝色", "藏青": "藏青色",
+            # 紫色系
+            "紫色": "紫色", "紫": "紫色", "深紫": "深紫色", "浅紫": "浅紫色", "紫罗": "紫罗兰",
+            "紫红": "紫红色",
+            # 黑白灰系
+            "黑色": "黑色", "黑": "黑色", "白色": "白色", "白": "白色", "灰色": "灰色", "灰": "灰色",
+            "深灰": "深灰色", "浅灰": "浅灰色", "银灰": "银灰色", "银": "银色",
+            # 色系描述
+            "浅色": "浅色", "深色": "深色", "彩色": "多彩", "暖色": "暖色", "冷色": "冷色",
+            "单色": "单色", "多色": "多彩"
+        }
+        
+        # 风格映射表
+        self.style_map = {
+            # 人群定位
+            "年轻人": "青春", "青年": "青春", "学生": "青春", "00后": "青春", "90后": "青春",
+            "老年": "稳重", "老人": "稳重", "中年": "成熟", "商务人士": "商务", "男性": "硬朗",
+            "女性": "柔美", "儿童": "可爱", "小朋友": "可爱", "女孩": "柔美", "男孩": "活力",
+            # 风格描述
+            "简约": "简约", "简单": "简约", "简洁": "简约", "清爽": "简约", "干净": "简约",
+            "现代": "现代", "现代化": "现代", "当代": "现代", "时尚": "时尚", "时髦": "时尚",
+            "潮流": "时尚", "流行": "时尚", "商务": "商务", "商业": "商务", "正式": "商务",
+            "专业": "专业", "活力": "活力", "活泼": "活力", "动感": "活力", "温馨": "温馨",
+            "温暖": "温馨", "柔和": "温馨", "科技": "科技", "科技感": "科技", "未来": "科技",
+            "自然": "自然", "清新": "自然", "环保": "自然", "极简": "极简", "经典": "经典",
+            "传统": "经典", "复古": "复古", "欧式": "欧式", "中式": "中式", "日式": "日式",
+            "卡通": "卡通", "可爱": "可爱", "甜美": "甜美", "酷炫": "酷炫", "奢华": "奢华"
+        }
+        
+        # 类型映射表
+        self.type_map = {
+            "模板": "模板", "皮肤": "皮肤", "主题": "主题", "风格": "风格",
+            "设计": "设计", "界面": "界面", "网站": "网站", "样式": "样式",
+            "一套": "模板", "个": "模板", "款": "模板", "张": "模板"
+        }
+        
+        # 情感词(用于优化标题)
+        self.positive_words = ["好看", "漂亮", "美观", "美丽", "优雅", "舒服", "舒适", 
+                              "喜欢", "不错", "好", "棒", "赞", "精美", "优质"]
+        
+        self.intensity_words = ["最好", "首选", "特别喜欢", "非常", "很", "特别", "超", "超级"]
+
+    def generate_title(self, user_text):
+        """生成标题的主方法"""
+        print(f"📝 用户输入: {user_text}")
+        
+        # 1. 提取关键词信息
+        extracted_info = self._extract_keywords(user_text)
+        
+        # 2. 生成标题
+        title = self._compose_title(extracted_info)
+        
+        print(f"✅ 生成标题: {title}")
+        return title
+
+    def _extract_keywords(self, user_text):
+        """提取关键词信息"""
+        # 清理文本
+        cleaned_text = self._clean_text(user_text)
+        
+        # 提取各类关键词
+        found_color = self._extract_color(cleaned_text)
+        found_style = self._extract_style(cleaned_text)
+        found_type = self._extract_type(cleaned_text)
+        has_positive = self._has_positive_words(cleaned_text)
+        
+        return {
+            'color': found_color,
+            'style': found_style,
+            'type': found_type,
+            'has_positive': has_positive,
+            'has_any_keyword': bool(found_color or found_style)
+        }
+
+    def _extract_color(self, text):
+        """提取颜色关键词"""
+        for color_keyword in self.color_map:
+            if color_keyword in text:
+                return self.color_map[color_keyword]
+        return None
+
+    def _extract_style(self, text):
+        """提取风格关键词"""
+        for style_keyword in self.style_map:
+            if style_keyword in text:
+                return self.style_map[style_keyword]
+        return None
+
+    def _extract_type(self, text):
+        """提取类型关键词"""
+        for type_keyword in self.type_map:
+            if type_keyword in text:
+                return self.type_map[type_keyword]
+        return "模板"  # 默认返回模板
+
+    def _has_positive_words(self, text):
+        """检查是否有积极词汇"""
+        return any(word in text for word in self.positive_words)
+
+    def _clean_text(self, text):
+        """清理文本"""
+        # 移除标点符号,用空格替换
+        text = re.sub(r'[,。!?、;:"“”‘\'’()【】《》<>{}【】]', ' ', text)
+        # 合并多个空格
+        text = re.sub(r'\s+', ' ', text)
+        return text.strip()
+
+    def _compose_title(self, info):
+        """组合生成标题"""
+        # 如果没有任何关键词,返回日期标题
+        if not info['has_any_keyword']:
+            return self._generate_date_title()
+        
+        color = info['color']
+        style = info['style']
+        template_type = info['type']
+        has_positive = info['has_positive']
+        
+        # 根据提取到的信息组合标题
+        if color and style:
+            base_title = f"{color}{style}{template_type}"
+        elif color and not style:
+            base_title = f"{color}主题{template_type}"
+        elif style and not color:
+            base_title = f"{style}风格{template_type}"
+        else:
+            base_title = f"精选{template_type}"
+        
+        # 添加修饰词(如果有积极词汇)
+        if has_positive and len(base_title) < 10:
+            base_title = f"优质{base_title}"
+        
+        # 确保长度合理
+        if len(base_title) > 15:
+            base_title = base_title[:13] + "..."
+        
+        return base_title
+
+    def _generate_date_title(self):
+        """生成带日期的默认标题"""
+        current_date = datetime.datetime.now()
+        # 格式:月日推荐模板,如:1224推荐模板
+        date_str = current_date.strftime("%m月%d日")
+        return f"{date_str}推荐模板"
+
+# 全局匹配器实例
+title_matcher = TitleMatcher()

+ 24 - 0
utils/offFetch.py

@@ -0,0 +1,24 @@
+class RequestInterruptHandler:
+    """处理请求中断的上下文管理器"""
+    
+    def __init__(self, request):
+        self.request = request
+        self.client_closed = False
+    
+    def __enter__(self):
+        return self
+    
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        # 这里可以添加资源清理逻辑
+        if exc_type is GeneratorExit:
+            print("客户端中断了请求")
+            return True  # 抑制异常
+    
+    def is_client_connected(self):
+        """检查客户端是否仍然连接"""
+        try:
+            # 检查 socket 是否关闭
+            return not self.request.environ.get('werkzeug.socket').closed
+        except:
+            # 如果检查失败,假设连接仍然存在
+            return True

+ 41 - 0
utils/request.py

@@ -0,0 +1,41 @@
+import requests
+import json
+import time
+
+# 基础配置 start ---------------------------------------->
+# pre环境
+URL = "https://apipre1.bjzxtw.org.cn/"
+# master环境
+# URL = "https://flzxw.bjzxtw.org.cn/"
+# 服务器本地
+# URL = "http://127.0.0.1:9501/"
+# 基础配置 end ---------------------------------------->
+
+def post_request(url, data, token):
+    url = URL + url
+    response = requests.post(url, json=data, headers={'Token': token})
+    return response.json()
+
+# 异常响应 start ---------------------------------------->
+# 当php服务端出现异常的时候,或者python端出现异常的时候,汇报前端
+def generate_error(message):
+    for char in message:
+        data = {
+            'code': 0,
+            'type': 'error',
+            'data': char,
+            'finished': False,
+            'isComplete': False
+        }
+        yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
+        time.sleep(0.05)
+
+    # 发送结束信号
+    end_data = {
+        'code': 200,
+        'data': '',
+        'finished': True,
+        'full_text': "处理结果已发送完毕!"
+    }
+    yield f"data: {json.dumps(end_data, ensure_ascii=False)}\n\n"
+# 异常响应 end ---------------------------------------->

+ 76 - 0
utils/verifyInput.py

@@ -0,0 +1,76 @@
+import re
+from services.ai_service import ai_service
+from sklearn.metrics.pairwise import cosine_similarity
+
+class HybridRequestValidator:
+    def __init__(self):
+        
+        # 规则1:检查是否包含模板设计相关词汇
+        self.template_patterns = [
+            r'.*模板.*',
+            r'.*设计.*',
+            r'.*推荐.*(样式|风格|主题)',
+            r'.*(颜色|色彩|配色).*',
+            r'.*(去掉|移除|删除|隐藏).*',
+            r'.*(组件|元素|部件).*',
+            r'.*(采用|使用|设置为).*'
+        ]
+        
+        # 规则2:明显的非模板请求模式
+        self.reject_patterns = [
+            r'^告诉我.*',
+            r'.*天气.*',
+            r'.*时间.*',
+            r'.*播放.*',
+            r'.*新闻.*',
+            r'^查询.*',
+            r'^搜索.*'
+        ]
+        
+        # 语义检查的参考语句
+        self.reference_phrases = [
+            "模板设计需求,包含样式和组件要求",
+            "视觉设计请求,涉及颜色和布局调整",
+            "UI模板定制,需要修改特定元素"
+        ]
+        self.ref_embeddings = ai_service.text_to_embedding(self.reference_phrases)
+    
+    def validate(self, user_input):
+        # 1. 快速拒绝明显无关的请求
+        for pattern in self.reject_patterns:
+            if re.search(pattern, user_input, re.IGNORECASE):
+                return False
+        
+        # 2. 模式匹配检查
+        match_count = 0
+        for pattern in self.template_patterns:
+            if re.search(pattern, user_input, re.IGNORECASE):
+                match_count += 1
+        
+        # 修改:只要包含关键词即可,不再强制要求多个关键词或语义验证
+        # >=1 只要包含1个关键词就通过验证 >=2 必须包含2个关键词 以此类推
+        if match_count >= 1:
+            return True
+            
+        # 3. 语义验证(仅作为补充,用于没有任何关键词匹配但语义相关的情况)
+        try:
+            user_embedding = ai_service.text_to_embedding(user_input)
+            
+            # 确保维度匹配
+            if len(user_embedding.shape) == 1:
+                user_embedding = user_embedding.reshape(1, -1)
+                
+            similarity = cosine_similarity(
+                user_embedding, 
+                self.ref_embeddings
+            ).max()
+            
+            if similarity > 0.4:  #稍微提高阈值,因为没有关键词支撑
+                return True
+        except Exception as e:
+            print(f"语义验证出错: {e}")
+            pass
+        
+        return False
+
+messageValidator = HybridRequestValidator()

+ 17 - 0
utils/version.py

@@ -0,0 +1,17 @@
+
+# import torch
+# print(torch.__version__)
+# import sklearn
+# print(sklearn.__version__)
+# import numpy
+# print(numpy.__version__)
+# import cachetools
+# print(cachetools.__version__)
+# import sentence_transformers
+# print(sentence_transformers.__version__)
+# import joblib
+# print(joblib.__version__)
+
+# 如果显示 3.13.x,就需要降级
+import sys
+print(sys.version)