sun-pc-linux 1 month ago
commit
884175d30d
100 changed files with 17464 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 184 0
      imgsearchimg/api/app.py
  3. 14 0
      imgsearchimg/api/docker-compose.yml
  4. 52 0
      imgsearchimg/api/generate.py
  5. 674 0
      imgsearchimg/api/image_search.py
  6. 9 0
      imgsearchimg/api/requirements.txt
  7. 35 0
      imgsearchimg/func/Dockerfile
  8. 73 0
      imgsearchimg/func/func_test.py
  9. 283 0
      imgsearchimg/func/image_search.py
  10. 8 0
      imgsearchimg/func/requirements.txt
  11. 0 0
      imgsearchimg/readme.md
  12. 38 0
      pack-queentree/config-service.yaml
  13. 82 0
      pack-queentree/docker-compose.yaml
  14. 64 0
      pack-queentree/nginx/conf.d/default.conf
  15. 1 0
      pack-queentree/nginx/www/index.html
  16. 21 0
      pack-queentree/readme.md
  17. 4 0
      pack-queentree/start.sh
  18. 43 0
      pack-sku3d/config-service.yaml
  19. 101 0
      pack-sku3d/docker-compose.yaml
  20. 77 0
      pack-sku3d/nginx/conf.d/default.conf
  21. 1 0
      pack-sku3d/nginx/www/index.html
  22. 19 0
      pack-sku3d/readme.md
  23. 183 0
      sku3d/.drone.yml
  24. 16 0
      sku3d/.gitignore
  25. 3 0
      sku3d/.gitmodules
  26. 70 0
      sku3d/comm/app-quitsocket.go
  27. 251 0
      sku3d/comm/app-root.go
  28. 8 0
      sku3d/comm/asset.go
  29. 41 0
      sku3d/comm/category.go
  30. 173 0
      sku3d/comm/commEntity.go
  31. 1380 0
      sku3d/comm/database.go
  32. 136 0
      sku3d/comm/design.go
  33. 137 0
      sku3d/comm/fabric.go
  34. 59 0
      sku3d/comm/go.mod
  35. 1928 0
      sku3d/comm/go.sum
  36. 118 0
      sku3d/comm/lancher-client.go
  37. 186 0
      sku3d/comm/lancher-server.go
  38. 28 0
      sku3d/comm/license.go
  39. 193 0
      sku3d/comm/liscense-deviceId.go
  40. 34 0
      sku3d/comm/liscense-enity.go
  41. 298 0
      sku3d/comm/liscense-main.go
  42. 48 0
      sku3d/comm/liscense-main_test.go
  43. 251 0
      sku3d/comm/liscense-utils.go
  44. 61 0
      sku3d/comm/liscense-utils_test.go
  45. 226 0
      sku3d/comm/mat.go
  46. 40 0
      sku3d/comm/mesh.go
  47. 50 0
      sku3d/comm/migration.go
  48. 76 0
      sku3d/comm/nats-config-mongo.go
  49. 68 0
      sku3d/comm/nats-config-redis.go
  50. 84 0
      sku3d/comm/nats-config.go
  51. 30 0
      sku3d/comm/nats-const-pay.go
  52. 12 0
      sku3d/comm/nats-const.go
  53. 149 0
      sku3d/comm/nats-proxy.go
  54. 207 0
      sku3d/comm/nats-stream.go
  55. 704 0
      sku3d/comm/nats.go
  56. 377 0
      sku3d/comm/package.go
  57. 66 0
      sku3d/comm/pay/const.go
  58. 51 0
      sku3d/comm/pay/order.go
  59. 48 0
      sku3d/comm/pay/point.go
  60. 51 0
      sku3d/comm/pay/refund.go
  61. 215 0
      sku3d/comm/render.go
  62. 204 0
      sku3d/comm/task-background.go
  63. 334 0
      sku3d/comm/task-cmd-background.go
  64. 283 0
      sku3d/comm/task-cmd.go
  65. 230 0
      sku3d/comm/task-dispatcher-background.go
  66. 47 0
      sku3d/comm/task.go
  67. 92 0
      sku3d/comm/utils-other.go
  68. 19 0
      sku3d/sku3d/Dockerfile
  69. 30 0
      sku3d/sku3d/api/api-admin.go
  70. 137 0
      sku3d/sku3d/api/api.go
  71. 389 0
      sku3d/sku3d/api/controller.go
  72. 60 0
      sku3d/sku3d/api/imCategory.go
  73. 98 0
      sku3d/sku3d/api/imageMaterial.go
  74. 169 0
      sku3d/sku3d/api/jwt.go
  75. 170 0
      sku3d/sku3d/api/router.go
  76. 80 0
      sku3d/sku3d/api/search-image.go
  77. 15 0
      sku3d/sku3d/api/servcie-test.go
  78. 193 0
      sku3d/sku3d/api/service-array.go
  79. 219 0
      sku3d/sku3d/api/service-asset-remove.go
  80. 393 0
      sku3d/sku3d/api/service-asset-updator.go
  81. 317 0
      sku3d/sku3d/api/service-asset.go
  82. 36 0
      sku3d/sku3d/api/service-background.go
  83. 179 0
      sku3d/sku3d/api/service-cat.go
  84. 49 0
      sku3d/sku3d/api/service-collocate.go
  85. 330 0
      sku3d/sku3d/api/service-conv.go
  86. 667 0
      sku3d/sku3d/api/service-design.go
  87. 169 0
      sku3d/sku3d/api/service-hdr.go
  88. 95 0
      sku3d/sku3d/api/service-imgmat.go
  89. 79 0
      sku3d/sku3d/api/service-lib.go
  90. 118 0
      sku3d/sku3d/api/service-login.go
  91. 659 0
      sku3d/sku3d/api/service-mat.go
  92. 270 0
      sku3d/sku3d/api/service-member-user.go
  93. 111 0
      sku3d/sku3d/api/service-member.go
  94. 412 0
      sku3d/sku3d/api/service-mesh.go
  95. 124 0
      sku3d/sku3d/api/service-minio.go
  96. 374 0
      sku3d/sku3d/api/service-obs.go
  97. 290 0
      sku3d/sku3d/api/service-order.go
  98. 5 0
      sku3d/sku3d/api/service-patch.go
  99. 118 0
      sku3d/sku3d/api/service-perm.go
  100. 59 0
      sku3d/sku3d/api/service-project.go

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*static
+*__pycache__

+ 184 - 0
imgsearchimg/api/app.py

@@ -0,0 +1,184 @@
+from flask import Flask, request, jsonify, render_template, send_from_directory
+import os
+from image_search import ImageSearchEngine
+import magic
+from urllib.request import urlretrieve
+import time
+
+app = Flask(__name__)
+
+# 配置常量
+UPLOAD_FOLDER = 'static/images'
+ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}
+
+# 搜索参数默认值
+TOP_K = 5  # 默认返回的最大结果数
+MIN_SCORE = 0.0  # 默认最小相似度分数
+MAX_SCORE = 100.0  # 默认最大相似度分数
+
+os.makedirs(UPLOAD_FOLDER, exist_ok=True)
+
+# 初始化图像搜索引擎
+search_engine = ImageSearchEngine()
+
+def allowed_file(filename):
+    """检查文件是否允许上传"""
+    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
+
+def is_valid_image(file_path):
+    """检查文件是否为有效的图片文件"""
+    try:
+        mime = magic.Magic(mime=True)
+        file_type = mime.from_file(file_path)
+        return file_type.startswith('image/')
+    except Exception:
+        return False
+
+@app.route('/')
+def index():
+    """渲染主页"""
+    return render_template('index.html')
+
+@app.route('/upload', methods=['POST'])
+def upload_file():
+    """处理图片上传请求"""
+    try:
+        # 获取并验证参数
+        data = request.get_json()
+        if not data:
+            return jsonify({'error': '请求必须包含JSON数据'}), 400
+        
+        product_id = data.get('product_id')
+        image_url = data.get('url')
+        
+        if not product_id:
+            return jsonify({'error': '缺少product_id参数'}), 400
+        if not image_url:
+            return jsonify({'error': '缺少url参数'}), 400
+        if not isinstance(image_url, str) or not (image_url.startswith('http://') or image_url.startswith('https://')):
+            return jsonify({'error': '无效的图片URL'}), 400
+        
+        time_base = str(time.time() * 1000)
+        image_path = os.path.join(UPLOAD_FOLDER, time_base + os.path.basename(image_url))
+        urlretrieve(image_url, image_path)
+
+        if allowed_file(image_path) and is_valid_image(image_path):
+            # 添加图片到搜索引擎
+            if search_engine.add_image_from_url(image_path, product_id):
+                # 删除临时图片文件
+                os.remove(image_path)
+                return jsonify({'message': '上传成功', 'product_id': product_id})
+            else:
+                return jsonify({'error': '处理图片失败'}), 500
+        else:
+            return jsonify({'error': '图片错误'}), 500
+            
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@app.route('/search', methods=['POST'])
+def search():
+    """处理图片搜索请求"""
+    try:
+        # 获取并验证参数
+        data = request.get_json()
+        if not data:
+            return jsonify({'error': '请求必须包含JSON数据'}), 400
+        
+        image_url = data.get('url')
+        if not image_url:
+            return jsonify({'error': '缺少url参数'}), 400
+        if not isinstance(image_url, str) or not (image_url.startswith('http://') or image_url.startswith('https://')):
+            return jsonify({'error': '无效的图片URL'}), 400
+            
+        # 获取可选参数
+        limit = data.get('limit', TOP_K)
+        min_score = data.get('min_score', MIN_SCORE)
+        max_score = data.get('max_score', MAX_SCORE)
+        
+        # 验证参数类型和范围
+        try:
+            limit = int(limit)
+            min_score = float(min_score)
+            max_score = float(max_score)
+            
+            if limit <= 0:
+                return jsonify({'error': 'limit必须大于0'}), 400
+            if min_score < 0 or min_score > 100:
+                return jsonify({'error': 'min_score必须在0到100之间'}), 400
+            if max_score < 0 or max_score > 100:
+                return jsonify({'error': 'max_score必须在0到100之间'}), 400
+            if min_score > max_score:
+                return jsonify({'error': 'min_score不能大于max_score'}), 400
+        except ValueError:
+            return jsonify({'error': '参数类型错误'}), 400
+        
+        start_download_time = time.time()
+        time_base = str(time.time() * 1000)
+        image_path = os.path.join(UPLOAD_FOLDER, time_base + os.path.basename(image_url))
+        urlretrieve(image_url, image_path)
+        end_download_time = time.time()
+        print(f"下载耗时: { end_download_time - start_download_time } s",)
+
+        if allowed_file(image_path) and is_valid_image(image_path):
+            # 搜索相似图片,使用用户指定的 limit
+            start_search_time = time.time()
+            results = search_engine.search(image_path, top_k=limit)
+            os.remove(image_path)
+            # 格式化结果并过滤不在分数范围内的结果
+            formatted_results = []
+            for product_id, score in results:
+                similarity = round(score * 100, 2)  # 转换为百分比
+                if min_score <= similarity <= max_score:
+                    formatted_results.append({
+                        'product_id': product_id,
+                        'similarity': similarity
+                    })
+            end_search_time = time.time()
+            print(f"搜索耗时: { end_search_time - start_search_time } s",)
+            return jsonify({'results': formatted_results})
+        else:
+            return jsonify({'error': '图片错误'})
+
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+# @app.route('/images')
+# def list_images():
+#     """列出所有已索引的图片"""
+#     try:
+#         images = []
+#         for path in search_engine.image_paths:
+#             filename = os.path.basename(path)
+#             images.append({
+#                 'filename': filename,
+#                 'path': '/static/images/' + filename
+#             })
+#         return jsonify({'images': images})
+#     except Exception as e:
+#         return jsonify({'error': str(e)}), 500
+
+@app.route('/remove/<product_id>', methods=['POST'])
+def remove_image(product_id):
+    """移除指定商品ID的图片特征"""
+    try:
+        # 从索引中移除
+        if search_engine.remove_by_product_id(product_id):
+            return jsonify({'message': '删除成功', 'product_id': product_id})
+        else:
+            return jsonify({'error': '商品ID不存在或删除失败'}), 404
+            
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@app.route('/clear/<password>', methods=['POST'])
+def clear_index(password):
+    """清空索引"""
+    if password == "infish@2025":
+        try:
+            search_engine.clear()
+        except Exception as e:
+            return jsonify({'error': str(e)}), 500
+
+if __name__ == '__main__':
+    app.run(host='0.0.0.0', port=5000, debug=False)

+ 14 - 0
imgsearchimg/api/docker-compose.yml

@@ -0,0 +1,14 @@
+  services:
+    # mongo数据库
+    faiss-mongo:
+      image: mongo:4.4.1
+      restart: always
+      environment:
+        MONGO_INITDB_ROOT_USERNAME: root
+        MONGO_INITDB_ROOT_PASSWORD: faiss_image_search
+
+      volumes:
+        - ~/data/faiss-mongo/mongo/db:/data/db
+        - ~/data/faiss-mongo/mongo/log:/var/log/mongodb
+      ports:
+        - 27017:27017

+ 52 - 0
imgsearchimg/api/generate.py

@@ -0,0 +1,52 @@
+import pymongo
+import numpy as np
+import faiss
+import time
+from bson.objectid import ObjectId
+
+# MongoDB连接配置
+client = pymongo.MongoClient("mongodb://root:faiss_image_search@localhost:27017/")
+db = client["faiss_index"]
+collection = db["mat_vectors"]
+
+collection.create_index([("product_id", 1)], unique=True)
+collection.create_index([("faiss_id", 1)], unique=True)
+
+# FAISS配置
+dimension = 2048
+base_index = faiss.IndexFlatL2(dimension)
+index = faiss.IndexIDMap(base_index)
+
+# 生成随机向量
+def generate_random_vector(dimension):
+    return np.random.random(dimension).astype('float32')
+
+# 插入100万条数据
+def insert_million_records():
+    batch_size = 10000  # 每批插入的数据量
+    total_records = 200000
+    start_time = time.time()
+
+    for i in range(0, total_records, batch_size):
+        batch = []
+        for j in range(batch_size):
+            faiss_id = i + j
+            vector = generate_random_vector(dimension)
+            index.add_with_ids(np.array([vector]), np.array([faiss_id]))
+
+            record = {
+                "_id": ObjectId(),
+                "product_id": ObjectId(),
+                "faiss_id": faiss_id,
+                "vector": vector.tolist()
+            }
+            batch.append(record)
+
+        collection.insert_many(batch)
+        print(f"Inserted {i + batch_size} records")
+
+    end_time = time.time()
+    print(f"Total time taken: {end_time - start_time} seconds")
+
+if __name__ == "__main__":
+    insert_million_records()

+ 674 - 0
imgsearchimg/api/image_search.py

@@ -0,0 +1,674 @@
+import faiss
+import numpy as np
+from PIL import Image
+import io
+import os
+from typing import List, Tuple, Optional, Union
+import torch
+import torchvision.transforms as transforms
+import torchvision.models as models
+from torchvision.models import ResNet50_Weights
+from scipy import ndimage
+
+import torch.nn.functional as F
+from pymongo import MongoClient
+import datetime
+import time
+
+class ImageSearchEngine:
+    def __init__(self):
+
+        # 添加mongodb
+        self.mongo_client = MongoClient("mongodb://root:faiss_image_search@localhost:27017/")  # MongoDB 连接字符串
+        self.mongo_db = self.mongo_client["faiss_index"]  # 数据库名称
+        self.mongo_collection = self.mongo_db["mat_vectors"]  # 集合名称
+        self.mongo_collection.create_index([("product_id", 1)], unique=True)
+        self.mongo_collection.create_index([("faiss_id", 1)], unique=True)
+        # 初始化一个id生成计数
+        self.faiss_id_max = 0
+     
+        # 强制使用CPU设备
+        self.device = torch.device("cpu")
+        print(f"使用设备: {self.device}")
+        
+        # 定义基础预处理转换
+        self.base_transform = transforms.Compose([
+            transforms.Grayscale(num_output_channels=3),  # 转换为灰度图但保持3通道
+            transforms.ToTensor(),
+            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+        ])
+        
+    
+        # 加载预训练的ResNet模型并保持全精度
+        self.model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
+        # 移除最后的全连接层
+        self.model = torch.nn.Sequential(*list(self.model.children())[:-1])
+        self.model = self.model.float().to(self.device).eval()
+        
+        # 初始化FAISS索引(2048是ResNet50的特征维度)
+        self.dimension = 2048
+        # self.index = faiss.IndexFlatL2(self.dimension)
+
+        # 改为支持删除的索引
+        base_index = faiss.IndexFlatL2(self.dimension)
+        self.index = faiss.IndexIDMap(base_index) 
+        
+        
+        # 尝试加载现有索引,如果不存在则创建新索引
+        if self._load_index():
+            print("成功加载现有索引")
+
+    def _batch_generator(self, cursor, batch_size):
+        """从MongoDB游标中分批生成数据"""
+        batch = []
+        for doc in cursor:
+            batch.append(doc)
+            if len(batch) == batch_size:
+                yield batch
+                batch = []
+        if batch:
+            yield batch
+
+
+    def _process_image(self, image_path: str) -> Optional[torch.Tensor]:
+        """处理单张图片并提取特征。
+        
+        Args:
+            image_path: 图片路径
+            
+        Returns:
+            处理后的特征向量,如果处理失败返回None
+        """
+        try:
+            # 读取图片
+            image = Image.open(image_path)
+            
+            # 确保图片是RGB模式
+            if image.mode != 'RGB':
+                image = image.convert('RGB')
+            
+
+            start_ms_time = time.time()
+            # 提取多尺度特征
+            multi_scale_features = self._extract_multi_scale_features(image)
+            end_ms_time = time.time()
+            print(f"提取多尺度特征耗时: { end_ms_time - start_ms_time } s",)
+            if multi_scale_features is None:
+                return None
+            
+            start_sw_time = time.time()
+            # 提取滑动窗口特征
+            sliding_window_features = self._extract_sliding_window_features(image)
+            end_sw_time = time.time()
+            print(f"提取滑动窗口耗时: { end_sw_time - start_sw_time } s",)
+            if sliding_window_features is None:
+                return None
+            
+            # 组合特征(加权平均)
+            combined_feature = multi_scale_features * 0.6 + sliding_window_features * 0.4
+            
+            # 标准化特征
+            combined_feature = F.normalize(combined_feature, p=2, dim=0)
+            
+            return combined_feature
+            
+        except Exception as e:
+            print(f"处理图片时出错: {e}")
+            return None
+
+    def _extract_multi_scale_features(self, image: Image.Image) -> Optional[torch.Tensor]:
+        """基于原图分辨率的多尺度特征提取(智能动态调整版)
+        
+        Args:
+            image: PIL图片对象
+            
+        Returns:
+            多尺度特征向量,处理失败返回None
+        """
+        try:
+            # 三重精度保障
+            self.model = self.model.float()
+            
+            # 获取原图信息
+            orig_w, orig_h = image.size
+            max_edge = max(orig_w, orig_h)
+            aspect_ratio = orig_w / orig_h
+
+            # 动态调整策略 -------------------------------------------
+            # 策略1:根据最大边长确定基准尺寸
+            base_size = min(max_edge, 2048)  # 最大尺寸限制
+            
+            # 策略2:自动生成窗口尺寸(等比数列)
+            min_size = 224  # 最小特征尺寸
+            num_scales = 3  # 采样点数
+            scale_factors = np.logspace(0, 1, num_scales, base=2)
+            window_sizes = [int(base_size * f) for f in scale_factors]
+            window_sizes = sorted({min(max(s, min_size), 2048) for s in window_sizes})
+            
+            # 策略3:根据长宽比调整尺寸组合
+            if aspect_ratio > 1.5:  # 宽幅图像
+                window_sizes = [int(s*aspect_ratio) for s in window_sizes]
+            elif aspect_ratio < 0.67:  # 竖幅图像
+                window_sizes = [int(s/aspect_ratio) for s in window_sizes]
+
+            # 预处理优化 --------------------------------------------
+            # 选择最优基准尺寸(最接近原图尺寸的2的幂次)
+            base_size = 2 ** int(np.log2(base_size))
+            base_transform = transforms.Compose([
+                transforms.Resize((base_size, base_size),
+                                interpolation=transforms.InterpolationMode.LANCZOS),
+                self.base_transform
+            ])
+            
+            # 
+            img_base = base_transform(image).unsqueeze(0).to(torch.float32).to(self.device)
+
+            # 动态特征提取 ------------------------------------------
+            features = []
+            for size in window_sizes:
+                # 保持长宽比的重采样
+                target_size = (int(size*aspect_ratio), size) if aspect_ratio > 1 else (size, int(size/aspect_ratio))
+                
+                # CPU兼容的插值
+                img_tensor = torch.nn.functional.interpolate(
+                    img_base, 
+                    size=target_size,
+                    mode='bilinear',
+                    align_corners=False
+                ).to(torch.float32)
+
+                # 自适应归一化(保持原图统计特性)
+                if hasattr(self, 'adaptive_normalize'):
+                    img_tensor = self.adaptive_normalize(img_tensor)
+
+                # 混合精度推理
+                with torch.no_grad():
+                    feature = self.model(img_tensor).to(torch.float32)
+                
+                features.append(feature.squeeze().float())
+
+            # 动态权重分配 ------------------------------------------
+            # 基于尺寸差异的权重(尺寸越接近原图权重越高)
+            size_diffs = [abs(size - base_size) for size in window_sizes]
+            weights = 1 / (torch.tensor(size_diffs, device=self.device) + 1e-6)
+            weights = weights / weights.sum()
+
+            # 加权融合
+            final_feature = torch.stack([f * w for f, w in zip(features, weights)]).sum(dim=0)
+            
+            return final_feature
+
+        except Exception as e:
+            print(f"智能特征提取失败: {e}")
+            return None
+
+
+
+
+    def _extract_sliding_window_features(self, image: Image.Image) -> Optional[torch.Tensor]:
+        """优化版滑动窗口特征提取(动态调整+批量处理)
+        
+        Args:
+            image: PIL图片对象
+            
+        Returns:
+            滑动窗口特征向量,处理失败返回None
+        """
+        try:
+            # 三重精度保障
+            self.model = self.model.float()
+            
+            # 获取原图信息
+            orig_w, orig_h = image.size
+            aspect_ratio = orig_w / orig_h
+            
+            # 动态窗口配置 -------------------------------------------
+            # 根据原图尺寸自动选择关键窗口尺寸(示例逻辑,需根据实际调整)
+            max_dim = max(orig_w, orig_h)
+            window_sizes = sorted({
+                int(2 ** np.round(np.log2(max_dim * 0.1))),  # 约10%尺寸
+                int(2 ** np.floor(np.log2(max_dim * 0.5))),  # 约50%尺寸
+                int(2 ** np.ceil(np.log2(max_dim)))          # 接近原图尺寸
+            } & {256, 512, 1024, 2048})  # 与预设尺寸取交集
+            
+            # 智能步长调整(窗口尺寸越大步长越大)
+            stride_ratios = {256:0.5, 512:0.4, 1024:0.3, 2048:0.2}
+            
+            # 预处理优化 --------------------------------------------
+            # 生成基准图像(最大窗口尺寸)
+            max_win_size = max(window_sizes)
+            base_size = (int(max_win_size * aspect_ratio), max_win_size) if aspect_ratio > 1 else \
+                        (max_win_size, int(max_win_size / aspect_ratio))
+            
+            transform = transforms.Compose([
+                transforms.Resize(base_size[::-1], interpolation=transforms.InterpolationMode.BILINEAR),
+                self.base_transform
+            ])
+            base_img = transform(image).to(torch.float32).to(self.device)
+            
+            # 批量特征提取 ------------------------------------------
+            all_features = []
+            for win_size in window_sizes:
+                # 动态步长选择
+                stride = int(win_size * stride_ratios.get(win_size, 0.3))
+                
+                # 生成窗口坐标(考虑边缘填充)
+                h, w = base_img.shape[1:]
+                num_h = (h - win_size) // stride + 1
+                num_w = (w - win_size) // stride + 1
+                
+                # 调整窗口数量上限(防止显存溢出)
+                MAX_WINDOWS = 16  # 最大窗口数
+                if num_h * num_w > MAX_WINDOWS:
+                    stride = int(np.sqrt(h * w * win_size**2 / MAX_WINDOWS))
+                    num_h = (h - win_size) // stride + 1
+                    num_w = (w - win_size) // stride + 1
+
+                # 批量裁剪窗口
+                windows = []
+                for i in range(num_h):
+                    for j in range(num_w):
+                        top = i * stride
+                        left = j * stride
+                        window = base_img[:, top:top+win_size, left:left+win_size]
+                        windows.append(window)
+                
+                if not windows:
+                    continue
+
+                # 批量处理(自动分块防止OOM)
+                BATCH_SIZE = 4  # 批处理大小
+                with torch.no_grad():
+                    for i in range(0, len(windows), BATCH_SIZE):
+                        batch = torch.stack(windows[i:i+BATCH_SIZE]).to(torch.float32)
+                        features = self.model(batch).to(torch.float32)
+                        all_features.append(features.cpu().float())  # 转移至CPU释放显存
+
+            # 特征融合 ---------------------------------------------
+            if not all_features:
+                return None
+            
+            final_feature = torch.cat([f.view(-1, f.shape[-1]) for f in all_features], dim=0)
+            final_feature = final_feature.mean(dim=0).to(self.device)
+
+            return final_feature.float()
+
+        except Exception as e:
+            print(f"滑动窗口特征提取失败: {e}")
+            return None
+
+
+    def extract_features(self, img: Image.Image) -> np.ndarray:
+        """结合多尺度和滑动窗口提取特征。
+        
+        Args:
+            img: PIL图像对象
+            
+        Returns:
+            特征向量
+        """
+        try:
+            # 提取多尺度特征
+            multi_scale_features = self._extract_multi_scale_features(img)
+            if multi_scale_features is None:
+                raise ValueError("无法提取多尺度特征")
+            
+            # 提取滑动窗口特征
+            sliding_window_features = self._extract_sliding_window_features(img)
+            if sliding_window_features is None:
+                raise ValueError("无法提取滑动窗口特征")
+            
+            # 组合特征
+            combined_feature = multi_scale_features * 0.6 + sliding_window_features * 0.4
+            
+            # 标准化特征
+            combined_feature = F.normalize(combined_feature, p=2, dim=0)
+            
+            # 转换为numpy数组
+            return combined_feature.cpu().numpy()
+            
+        except Exception as e:
+            print(f"特征提取失败: {e}")
+            raise
+
+    def add_image_from_url(self, image_path: str, product_id: str) -> bool:
+        """从URL添加图片到索引。
+        
+        Args:
+            url: 图片URL
+            product_id: 图片对应的商品ID
+            
+        Returns:
+            添加成功返回True,失败返回False
+        """
+        try:
+            # 使用原有的特征提取逻辑
+            feature = self._process_image(image_path)
+            if feature is None:
+                print("无法提取特征")
+                return False
+            
+            # 转换为numpy数组并添加到索引
+            feature_np = feature.cpu().numpy().reshape(1, -1).astype('float32')
+
+            idx = self.faiss_id_max + 1 
+            print(f"当前: idx { idx }")
+            if not isinstance(idx, int) or idx <= 0:
+                print("ID生成失败")
+                return False
+            self.faiss_id_max = idx
+            
+            # 向数据库写入记录
+            record = {
+                "faiss_id": idx,
+                "product_id": product_id,
+                "vector": feature_np.flatten().tolist(),  # 将numpy数组转为列表
+                "created_at": datetime.datetime.utcnow()  # 记录创建时间
+            }
+            self.mongo_collection.insert_one(record)
+
+            # 为向量设置ID并添加到Faiss索引
+            self.index.add_with_ids(feature_np, np.array([idx], dtype='int64'))
+            
+            print(f"已添加图片:  product_id: {product_id}, faiss_id: {idx}")
+            return True
+
+        except Exception as e:
+            print(f"添加图片时出错: {e}")
+            return False
+
+    def get_product_id_by_faiss_id(self, faiss_id: int) -> Optional[str]:
+        """根据 faiss_id 查找 MongoDB 中的 product_id。
+        
+        Args:
+            faiss_id: Faiss 索引中的 ID
+            
+        Returns:
+            对应的 product_id,如果未找到则返回 None
+        """
+        try:
+            faiss_id = int(faiss_id)
+            # 检查 faiss_id 是否有效
+            if  faiss_id < 0:
+                print(f"无效的 faiss_id: {faiss_id}")
+                return None
+
+            # 查询 MongoDB
+            query = {"faiss_id": faiss_id}
+            record = self.mongo_collection.find_one(query)
+
+            # 检查是否找到记录
+            if record is None:
+                print(f"未找到 faiss_id 为 {faiss_id} 的记录")
+                return None
+
+            # 返回 product_id
+            product_id = record.get("product_id")
+            if product_id is None:
+                print(f"记录中缺少 product_id 字段: {record}")
+                return None
+
+            return str(product_id)  # 确保返回字符串类型
+
+        except Exception as e:
+            print(f"查询 faiss_id 为 {faiss_id} 的记录时出错: {e}")
+            return None
+
+    def search(self, image_path: str = None, top_k: int = 5) -> List[Tuple[str, float]]:
+        try:
+            if image_path is None:
+                print("搜索图片下载失败!")
+                return []
+            feature = self._process_image(image_path)
+            if feature is None:
+                print("无法提取查询图片的特征")
+                return []
+            
+            # 将特征转换为numpy数组
+            feature_np = feature.cpu().numpy().reshape(1, -1).astype('float32')
+            
+            start_vector_time = time.time()
+            # 搜索最相似的图片
+            distances, indices = self.index.search(feature_np, min(top_k, self.index.ntotal))
+            end_vector_time = time.time()
+            print(f"搜索vector耗时: {end_vector_time - start_vector_time}")
+
+            start_other_time = time.time()
+            # 返回结果
+            results = []
+            for faiss_id, dist in zip(indices[0], distances[0]):
+                if faiss_id == -1:  # Faiss返回-1表示无效结果
+                    continue
+                
+                # 将距离转换为相似度分数(0-1之间,1表示完全相似)
+                similarity = 1.0 / (1.0 + dist)
+                
+                # 根据faiss_id获取product_id
+                print(f"搜索结果->faiss_id: { faiss_id }")
+                product_id = self.get_product_id_by_faiss_id(faiss_id)
+                if product_id:
+                    results.append((product_id, similarity))
+            end_other_time = time.time()
+            print(f"查询结果耗时: {end_other_time - start_other_time}")
+            return results
+
+        except Exception as e:
+            print(f"搜索图片时出错: {e}")
+            return []
+
+
+
+
+    def _load_index(self) -> bool:
+        """从数据库分批加载数据并初始化faiss_id_max"""
+        try:
+            # 配置参数
+            BATCH_SIZE = 10000
+
+            # 获取文档总数
+            total_docs = self.mongo_collection.count_documents({})
+            if total_docs == 0:
+                print("数据库为空,跳过索引加载")
+                return True  # 空数据库不算错误
+
+            # 用于跟踪最大ID(兼容空数据情况)
+            max_faiss_id = -1
+            
+            # 分批加载数据
+            cursor = self.mongo_collection.find({}).batch_size(BATCH_SIZE)
+            for batch in self._batch_generator(cursor, BATCH_SIZE):
+                # 处理批次数据
+                batch_vectors = []
+                batch_ids = []
+                current_max = -1
+                
+                for doc in batch:
+                    try:
+                        # 数据校验
+                        if len(doc['vector']) != self.dimension:
+                            continue
+                        if not isinstance(doc['faiss_id'], int):
+                            continue
+                        
+                        # 提取数据
+                        faiss_id = int(doc['faiss_id'])
+                        vector = doc['vector']
+                        print(f"load faiss_id :{ faiss_id }")
+                        
+                        # 更新最大值
+                        if faiss_id > current_max:
+                            current_max = faiss_id
+                        
+                        # 收集数据
+                        batch_vectors.append(vector)
+                        batch_ids.append(faiss_id)
+
+                    except Exception as e:
+                        print(f"文档处理异常: {str(e)}")
+                        continue
+
+                # 批量添加到索引
+                if batch_vectors:
+                    vectors_np = np.array(batch_vectors, dtype='float32')
+                    ids_np = np.array(batch_ids, dtype='int64')
+                    self.index.add_with_ids(vectors_np, ids_np)
+                    
+                    # 更新全局最大值
+                    if current_max > max_faiss_id:
+                        max_faiss_id = current_max
+
+            print(f"向量总数: {self.index.ntotal}")
+         
+            # 设置初始值(如果已有更大值则保留)
+            if max_faiss_id != -1:
+                new_id = max_faiss_id
+                self.faiss_id_max = new_id
+                print(f"ID计数器初始化完成,当前值: {new_id}")
+         
+            return True
+
+        except Exception as e:
+            print(f"索引加载失败: {str(e)}")
+            return False
+ 
+    def clear(self) -> bool:
+        """清除所有索引和 MongoDB 中的记录。
+        
+        Returns:
+            清除成功返回 True,失败返回 False
+        """
+        try:
+            # 检查索引是否支持重置操作
+            if not hasattr(self.index, "reset"):
+                print("当前索引不支持重置操作")
+                return False
+
+            # 重置 Faiss 索引
+            self.index.reset()
+            print("已清除 Faiss 索引中的所有向量")
+
+            # 删除 MongoDB 中的所有记录
+            result = self.mongo_collection.delete_many({})
+            print(f"已从 MongoDB 中删除 {result.deleted_count} 条记录")
+
+            self.faiss_id_max = 0
+
+            return True
+
+        except Exception as e:
+            print(f"清除索引时出错: {e}")
+            return False
+
+    def remove_image(self, image_path: str) -> bool:
+        """从索引中移除指定图片。
+        
+        Args:
+            image_path: 要移除的图片路径
+            
+        Returns:
+            是否成功移除
+        """
+        try:
+            if image_path in self.image_paths:
+                idx = self.image_paths.index(image_path)
+                
+                # 创建新的索引
+                new_index = faiss.IndexFlatL2(self.dimension)
+                
+                # 获取所有特征
+                all_features = faiss.vector_to_array(self.index.get_xb()).reshape(-1, self.dimension)
+                
+                # 移除指定图片的特征
+                mask = np.ones(len(self.image_paths), dtype=bool)
+                mask[idx] = False
+                filtered_features = all_features[mask]
+                
+                # 更新索引
+                if len(filtered_features) > 0:
+                    new_index.add(filtered_features)
+                
+                # 更新图片路径列表
+                self.image_paths.pop(idx)
+                self.product_ids.pop(idx)
+                
+                # 更新索引
+                self.index = new_index
+                
+                # 保存更改
+                self._save_index()
+                
+                print(f"已移除图片: {image_path}")
+                return True
+            else:
+                print(f"图片不存在: {image_path}")
+                return False
+                
+        except Exception as e:
+            print(f"移除图片时出错: {e}")
+            return False
+
+    def remove_by_product_id(self, product_id: str) -> bool:
+        """通过 product_id 删除向量索引和数据库记录。
+        
+        Args:
+            product_id: 要删除的商品 ID
+            
+        Returns:
+            删除成功返回 True,失败返回 False
+        """
+        try:
+            # 检查 product_id 是否有效
+            if not product_id or not isinstance(product_id, str):
+                print(f"无效的 product_id: {product_id}")
+                return False
+
+            # 查询 MongoDB 获取 faiss_id
+            query = {"product_id": product_id}
+            record = self.mongo_collection.find_one(query)
+
+            # 检查是否找到记录
+            if record is None:
+                print(f"未找到 product_id 为 {product_id} 的记录")
+                return False
+
+            # 提取 faiss_id
+            faiss_id = record.get("faiss_id")
+            if faiss_id is None:
+                print(f"记录中缺少 faiss_id 字段: {record}")
+                return False
+
+            # 删除 Faiss 索引中的向量
+            if isinstance(self.index, faiss.IndexIDMap):
+                # 检查 faiss_id 是否在索引中
+                # ids = self.index.id_map.at(1)  # 获取所有 ID
+                # if faiss_id not in ids:
+                #     print(f"faiss_id {faiss_id} 不在索引中")
+                #     return False
+
+                # 删除向量
+                self.index.remove_ids(np.array([faiss_id], dtype='int64'))
+                print(f"已从 Faiss 索引中删除 faiss_id: {faiss_id}")
+            else:
+                print("当前索引不支持删除操作")
+                return False
+
+            # 删除 MongoDB 中的记录
+            result = self.mongo_collection.delete_one({"faiss_id": faiss_id})
+            if result.deleted_count == 1:
+                print(f"已从 MongoDB 中删除 faiss_id: {faiss_id}")
+                return True
+            else:
+                print(f"未找到 faiss_id 为 {faiss_id} 的记录")
+                return False
+
+        except Exception as e:
+            print(f"删除 product_id 为 {product_id} 的记录时出错: {e}")
+            return False
+
+    def get_index_size(self) -> int:
+        """获取索引中的图片数量。
+        
+        Returns:
+            索引中的图片数量
+        """
+        return len(self.image_paths)

+ 9 - 0
imgsearchimg/api/requirements.txt

@@ -0,0 +1,9 @@
+torch==2.0.1
+torchvision==0.15.2
+faiss-cpu==1.7.4
+Flask==2.3.3
+Pillow==10.0.0
+numpy==1.24.3
+scipy==1.11.2
+python-magic==0.4.27
+pymongo

+ 35 - 0
imgsearchimg/func/Dockerfile

@@ -0,0 +1,35 @@
+# 使用Python 3.11.11作为基础镜像
+# FROM python:3.11.11-slim
+# FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/wallies/python-cuda:3.11-cuda11.8-runtime
+FROM registry.cn-hangzhou.aliyuncs.com/serverless_devs/pytorch:22.12-py3
+
+RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
+RUN sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list && \
+    sed -i 's|http://security.ubuntu.com/ubuntu|http://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list
+
+# 设置工作目录
+WORKDIR /app
+
+# 设置环境变量
+ENV PYTHONUNBUFFERED=1 \
+    PYTHONDONTWRITEBYTECODE=1 \
+    PIP_NO_CACHE_DIR=1
+
+# 安装系统依赖
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    build-essential \
+    libmagic1 \
+    && rm -rf /var/lib/apt/lists/*
+
+# 复制项目文件
+COPY . .
+
+
+# 安装Python依赖
+RUN pip install --no-cache-dir -r requirements.txt
+
+
+EXPOSE 5001
+
+# 设置默认命令
+CMD ["python", "func_test.py"]

+ 73 - 0
imgsearchimg/func/func_test.py

@@ -0,0 +1,73 @@
+from flask import Flask, request, jsonify
+import os
+from urllib.request import urlretrieve
+import time
+from image_search import ImageSearchEngine
+
+app = Flask(__name__)
+
+# 配置常量
+UPLOAD_FOLDER = 'static/images'
+ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}
+
+# 搜索参数默认值
+TOP_K = 5  # 默认返回的最大结果数
+MIN_SCORE = 0.0  # 默认最小相似度分数
+MAX_SCORE = 100.0  # 默认最大相似度分数
+
+os.makedirs(UPLOAD_FOLDER, exist_ok=True)
+
+# 初始化图像搜索引擎
+search_engine = ImageSearchEngine()
+
+
+# @app.route("/initialize", methods=["POST"])
+# def initialize():
+#     # See FC docs for all the HTTP headers: https://www.alibabacloud.com/help/doc-detail/132044.htm#common-headers
+#     request_id = request.headers.get("x-fc-request-id", "")
+#     print("FC Initialize Start RequestId: " + request_id)
+
+#     # do your things
+#     # Use the following code to get temporary credentials
+#     # access_key_id = request.headers['x-fc-access-key-id']
+#     # access_key_secret = request.headers['x-fc-access-key-secret']
+#     # access_security_token = request.headers['x-fc-security-token']
+
+#     print("FC Initialize End RequestId: " + request_id)
+#     return "Function is initialized, request_id: " + request_id + "\n"
+
+
+@app.route("/", methods=["POST"])
+def invoke():
+    """处理图片上传请求"""
+    try:
+        # 获取并验证参数
+        data = request.get_json()
+        if not data:
+            return jsonify({'error': '请求必须包含JSON数据'}), 400
+        
+        print(f"Received data: {data}")
+        image_url = data.get('url')
+        
+        time_base = str(time.time() * 1000)
+        image_path = os.path.join(UPLOAD_FOLDER, time_base + os.path.basename(image_url))
+        urlretrieve(image_url, image_path)
+        
+        start_time = time.time()
+        search_engine._process_image(image_path)
+        os.remove(image_path)
+        return jsonify({"vector_spend_time": time.time() - start_time}),200
+            
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+
+
+
+if __name__ == '__main__':
+    app.run(host='0.0.0.0', port=5001, debug=False)
+
+
+
+
+

+ 283 - 0
imgsearchimg/func/image_search.py

@@ -0,0 +1,283 @@
+import faiss
+import numpy as np
+from PIL import Image
+import io
+import os
+from typing import List, Tuple, Optional, Union
+import torch
+import torchvision.transforms as transforms
+import torchvision.models as models
+from torchvision.models import ResNet50_Weights
+from scipy import ndimage
+
+import torch.nn.functional as F
+from pymongo import MongoClient
+import datetime
+import time
+
+class ImageSearchEngine:
+    def __init__(self):
+     
+        # 强制使用CPU设备
+        self.device = torch.device("cpu")
+        print(f"使用设备: {self.device}")
+        
+        # 定义基础预处理转换
+        self.base_transform = transforms.Compose([
+            transforms.Grayscale(num_output_channels=3),  # 转换为灰度图但保持3通道
+            transforms.ToTensor(),
+            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
+        ])
+        
+    
+        # 加载预训练的ResNet模型并保持全精度
+        self.model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
+        # 移除最后的全连接层
+        self.model = torch.nn.Sequential(*list(self.model.children())[:-1])
+        self.model = self.model.float().to(self.device).eval()
+        
+        # 初始化FAISS索引(2048是ResNet50的特征维度)
+        self.dimension = 2048
+        # self.index = faiss.IndexFlatL2(self.dimension)
+
+
+    def _batch_generator(self, cursor, batch_size):
+        """从MongoDB游标中分批生成数据"""
+        batch = []
+        for doc in cursor:
+            batch.append(doc)
+            if len(batch) == batch_size:
+                yield batch
+                batch = []
+        if batch:
+            yield batch
+
+
+    def _process_image(self, image_path: str) -> Optional[torch.Tensor]:
+        """处理单张图片并提取特征。
+        
+        Args:
+            image_path: 图片路径
+            
+        Returns:
+            处理后的特征向量,如果处理失败返回None
+        """
+        try:
+            # 读取图片
+            image = Image.open(image_path)
+            
+            # 确保图片是RGB模式
+            if image.mode != 'RGB':
+                image = image.convert('RGB')
+            
+
+            start_ms_time = time.time()
+            # 提取多尺度特征
+            multi_scale_features = self._extract_multi_scale_features(image)
+            end_ms_time = time.time()
+            print(f"提取多尺度特征耗时: { end_ms_time - start_ms_time } s",)
+            if multi_scale_features is None:
+                return None
+            
+            start_sw_time = time.time()
+            # 提取滑动窗口特征
+            sliding_window_features = self._extract_sliding_window_features(image)
+            end_sw_time = time.time()
+            print(f"提取滑动窗口耗时: { end_sw_time - start_sw_time } s",)
+            if sliding_window_features is None:
+                return None
+            
+            # 组合特征(加权平均)
+            combined_feature = multi_scale_features * 0.6 + sliding_window_features * 0.4
+            
+            # 标准化特征
+            combined_feature = F.normalize(combined_feature, p=2, dim=0)
+            
+            return combined_feature
+            
+        except Exception as e:
+            print(f"处理图片时出错: {e}")
+            return None
+
+    def _extract_multi_scale_features(self, image: Image.Image) -> Optional[torch.Tensor]:
+        """基于原图分辨率的多尺度特征提取(智能动态调整版)
+        
+        Args:
+            image: PIL图片对象
+            
+        Returns:
+            多尺度特征向量,处理失败返回None
+        """
+        try:
+            # 三重精度保障
+            self.model = self.model.float()
+            
+            # 获取原图信息
+            orig_w, orig_h = image.size
+            max_edge = max(orig_w, orig_h)
+            aspect_ratio = orig_w / orig_h
+
+            # 动态调整策略 -------------------------------------------
+            # 策略1:根据最大边长确定基准尺寸
+            base_size = min(max_edge, 2048)  # 最大尺寸限制
+            
+            # 策略2:自动生成窗口尺寸(等比数列)
+            min_size = 224  # 最小特征尺寸
+            num_scales = 3  # 采样点数
+            scale_factors = np.logspace(0, 1, num_scales, base=2)
+            window_sizes = [int(base_size * f) for f in scale_factors]
+            window_sizes = sorted({min(max(s, min_size), 2048) for s in window_sizes})
+            
+            # 策略3:根据长宽比调整尺寸组合
+            if aspect_ratio > 1.5:  # 宽幅图像
+                window_sizes = [int(s*aspect_ratio) for s in window_sizes]
+            elif aspect_ratio < 0.67:  # 竖幅图像
+                window_sizes = [int(s/aspect_ratio) for s in window_sizes]
+
+            # 预处理优化 --------------------------------------------
+            # 选择最优基准尺寸(最接近原图尺寸的2的幂次)
+            base_size = 2 ** int(np.log2(base_size))
+            base_transform = transforms.Compose([
+                transforms.Resize((base_size, base_size),
+                                interpolation=transforms.InterpolationMode.LANCZOS),
+                self.base_transform
+            ])
+            
+            # 
+            img_base = base_transform(image).unsqueeze(0).to(torch.float32).to(self.device)
+
+            # 动态特征提取 ------------------------------------------
+            features = []
+            for size in window_sizes:
+                # 保持长宽比的重采样
+                target_size = (int(size*aspect_ratio), size) if aspect_ratio > 1 else (size, int(size/aspect_ratio))
+                
+                # CPU兼容的插值
+                img_tensor = torch.nn.functional.interpolate(
+                    img_base, 
+                    size=target_size,
+                    mode='bilinear',
+                    align_corners=False
+                ).to(torch.float32)
+
+                # 自适应归一化(保持原图统计特性)
+                if hasattr(self, 'adaptive_normalize'):
+                    img_tensor = self.adaptive_normalize(img_tensor)
+
+                # 混合精度推理
+                with torch.no_grad():
+                    feature = self.model(img_tensor).to(torch.float32)
+                
+                features.append(feature.squeeze().float())
+
+            # 动态权重分配 ------------------------------------------
+            # 基于尺寸差异的权重(尺寸越接近原图权重越高)
+            size_diffs = [abs(size - base_size) for size in window_sizes]
+            weights = 1 / (torch.tensor(size_diffs, device=self.device) + 1e-6)
+            weights = weights / weights.sum()
+
+            # 加权融合
+            final_feature = torch.stack([f * w for f, w in zip(features, weights)]).sum(dim=0)
+            
+            return final_feature
+
+        except Exception as e:
+            print(f"智能特征提取失败: {e}")
+            return None
+
+
+
+
+    def _extract_sliding_window_features(self, image: Image.Image) -> Optional[torch.Tensor]:
+        """优化版滑动窗口特征提取(动态调整+批量处理)
+        
+        Args:
+            image: PIL图片对象
+            
+        Returns:
+            滑动窗口特征向量,处理失败返回None
+        """
+        try:
+            # 三重精度保障
+            self.model = self.model.float()
+            
+            # 获取原图信息
+            orig_w, orig_h = image.size
+            aspect_ratio = orig_w / orig_h
+            
+            # 动态窗口配置 -------------------------------------------
+            # 根据原图尺寸自动选择关键窗口尺寸(示例逻辑,需根据实际调整)
+            max_dim = max(orig_w, orig_h)
+            window_sizes = sorted({
+                int(2 ** np.round(np.log2(max_dim * 0.1))),  # 约10%尺寸
+                int(2 ** np.floor(np.log2(max_dim * 0.5))),  # 约50%尺寸
+                int(2 ** np.ceil(np.log2(max_dim)))          # 接近原图尺寸
+            } & {256, 512, 1024, 2048})  # 与预设尺寸取交集
+            
+            # 智能步长调整(窗口尺寸越大步长越大)
+            stride_ratios = {256:0.5, 512:0.4, 1024:0.3, 2048:0.2}
+            
+            # 预处理优化 --------------------------------------------
+            # 生成基准图像(最大窗口尺寸)
+            max_win_size = max(window_sizes)
+            base_size = (int(max_win_size * aspect_ratio), max_win_size) if aspect_ratio > 1 else \
+                        (max_win_size, int(max_win_size / aspect_ratio))
+            
+            transform = transforms.Compose([
+                transforms.Resize(base_size[::-1], interpolation=transforms.InterpolationMode.BILINEAR),
+                self.base_transform
+            ])
+            base_img = transform(image).to(torch.float32).to(self.device)
+            
+            # 批量特征提取 ------------------------------------------
+            all_features = []
+            for win_size in window_sizes:
+                # 动态步长选择
+                stride = int(win_size * stride_ratios.get(win_size, 0.3))
+                
+                # 生成窗口坐标(考虑边缘填充)
+                h, w = base_img.shape[1:]
+                num_h = (h - win_size) // stride + 1
+                num_w = (w - win_size) // stride + 1
+                
+                # 调整窗口数量上限(防止显存溢出)
+                MAX_WINDOWS = 16  # 最大窗口数
+                if num_h * num_w > MAX_WINDOWS:
+                    stride = int(np.sqrt(h * w * win_size**2 / MAX_WINDOWS))
+                    num_h = (h - win_size) // stride + 1
+                    num_w = (w - win_size) // stride + 1
+
+                # 批量裁剪窗口
+                windows = []
+                for i in range(num_h):
+                    for j in range(num_w):
+                        top = i * stride
+                        left = j * stride
+                        window = base_img[:, top:top+win_size, left:left+win_size]
+                        windows.append(window)
+                
+                if not windows:
+                    continue
+
+                # 批量处理(自动分块防止OOM)
+                BATCH_SIZE = 4  # 批处理大小
+                with torch.no_grad():
+                    for i in range(0, len(windows), BATCH_SIZE):
+                        batch = torch.stack(windows[i:i+BATCH_SIZE]).to(torch.float32)
+                        features = self.model(batch).to(torch.float32)
+                        all_features.append(features.cpu().float())  # 转移至CPU释放显存
+
+            # 特征融合 ---------------------------------------------
+            if not all_features:
+                return None
+            
+            final_feature = torch.cat([f.view(-1, f.shape[-1]) for f in all_features], dim=0)
+            final_feature = final_feature.mean(dim=0).to(self.device)
+
+            return final_feature.float()
+
+        except Exception as e:
+            print(f"滑动窗口特征提取失败: {e}")
+            return None
+
+

+ 8 - 0
imgsearchimg/func/requirements.txt

@@ -0,0 +1,8 @@
+# torch==2.0.1
+# torchvision==0.15.2
+# faiss-cpu==1.7.4
+Flask==2.3.3
+Pillow==10.0.0
+numpy==1.24.3
+# scipy==1.11.2
+python-magic==0.4.27

+ 0 - 0
imgsearchimg/readme.md


+ 38 - 0
pack-queentree/config-service.yaml

@@ -0,0 +1,38 @@
+log:
+  fileName: comm-bus.log
+  level: 1
+  serviceName: bus
+  
+configer:
+  -
+    name: tree-mongo
+    value: mongodb://root:tree@tree-mongo:27017/queentree?authSource=admin
+    devValue: mongodb://root:tree@124.71.139.24:37019/queentree?authSource=admin
+    # devValue: mongodb://root:tree@127.0.0.1:37019/queentree?authSource=admin
+
+  -
+    name: redis
+    value: redis:6379#0#root#packtree
+
+  - name: bus-network #外网bus地址
+    # value: nats://124.71.139.24:14220
+    value: nats://192.168.0.162:14220
+
+# adapter:
+#   -
+#     busName:
+#       service2
+#     nats:
+#       nats://pack2-nats-1:4222
+#     apis:
+#       - service2.licence.check
+#     streams:
+#       - service2-vip-pay#service2.pay.succ
+startLocalNats: true
+startNatsShellParams: "-p 4222 -m 8222 -js -c server.conf"
+startNatsPort: 4222
+
+nats:
+  url: nats://bus:4222
+  maxReconnect: 10000
+  reconnDelaySecond: 5

+ 82 - 0
pack-queentree/docker-compose.yaml

@@ -0,0 +1,82 @@
+version: '3.8'
+
+# 网络
+networks:
+  default:
+    external:
+      name: default-network
+
+services:
+  #nginx反向代理
+  tree-nginx:
+    image: "registry.cn-chengdu.aliyuncs.com/infish/pack-comm-nginx:1.23.1"
+    restart: always
+    ports:
+      - 18082:80
+    volumes:
+      - ./nginx/conf.d:/etc/nginx/conf.d
+      - ./nginx/www:/usr/share/nginx/html
+    depends_on:
+      - tree-main
+      - tree-task
+      - tree-count
+
+  #bus消息中间件
+  tree-bus:
+    restart: always
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-comm-bus:v1.0.1
+    volumes:
+      - ./config-service.yaml:/root/bus/app.yaml
+
+    depends_on:
+      - tree-mongo
+      - tree-redis    
+    ports:
+      - 14220:4222
+      - 14225:4333
+      
+  #redis缓存
+  tree-redis:
+    image: "redis:alpine"
+    restart: always
+    command:
+      --requirepass "packtree" #这一行是设置密码
+    privileged: true
+
+  #mongo -alpha数据库
+  tree-mongo:
+    image: mongo:4.4.1
+    restart: always
+    environment:
+      MONGO_INITDB_ROOT_USERNAME: root
+      MONGO_INITDB_ROOT_PASSWORD: tree
+    volumes:
+      - ~/data/packs-queentree/mongo/db:/data/db
+      - ~/data/packs-queentree/mongo/log:/var/log/mongodb
+    ports: 
+      - 37019:27017
+
+  #tree
+  tree-main:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-queentree-tree:v1.0.1
+    restart: always
+    environment:
+      NATS: nats://tree-bus:4222
+    depends_on:
+      - tree-bus
+      
+  tree-task:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-tree-taskcenter:v1.0.1
+    restart: always
+    environment:
+      NATS: nats://tree-bus:4222
+    depends_on:
+      - tree-bus
+
+  tree-count:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-queentree-count:v1.0.0
+    restart: always
+    environment:
+      NATS: nats://tree-bus:4222
+    depends_on:
+      - tree-bus

+ 64 - 0
pack-queentree/nginx/conf.d/default.conf

@@ -0,0 +1,64 @@
+server {
+    listen       80;
+    listen  [::]:80;
+    server_name  localhost;
+    #access_log  /var/log/nginx/host.access.log  main;
+    location / {
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+    }
+    #error_page  404              /404.html;
+
+    # redirect server error pages to the static page /50x.html
+    #
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   /usr/share/nginx/html;
+    }
+
+    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+    #
+    #location ~ \.php$ {
+    #    proxy_pass   http://127.0.0.1;
+    #}
+
+    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+    #
+    #location ~ \.php$ {
+    #    root           html;
+    #    fastcgi_pass   127.0.0.1:9000;
+    #    fastcgi_index  index.php;
+    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
+    #    include        fastcgi_params;
+    #}
+
+    # deny access to .htaccess files, if Apache's document root
+    # concurs with nginx's one
+    #
+    #location ~ /\.ht {
+    #    deny  all;
+    #}
+    #资产中心接口
+    location /assetcenter/ {
+       proxy_pass  http://tree-main:8902/assetcenter/;
+    }
+
+    location /taskcenter/ws {
+      proxy_pass http://tree-task:9502/taskcenter/ws;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection "Upgrade";
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_read_timeout 500s;
+      proxy_send_timeout 500s;
+    }
+
+    location /taskcenter/ {
+       proxy_pass  http://tree-task:9502/taskcenter/;
+    }
+
+    # 统计
+    location /queencount/ {
+       proxy_pass  http://tree-count:8902/queencount/;
+    }
+}

+ 1 - 0
pack-queentree/nginx/www/index.html

@@ -0,0 +1 @@
+hello world~!!!

+ 21 - 0
pack-queentree/readme.md

@@ -0,0 +1,21 @@
+# 包含的微服务说明
+1 pay 模块 registry.cn-chengdu.aliyuncs.com/infish/sku3dpaytest:latest
+    environment:
+      REDIS: redis:6379/0
+      NATS: nats://192.168.0.162:24222
+
+    ports:
+      - 18888:8888
+
+## 流程
+
+1. 构建每个服务的镜像
+2. 配置到对应service的image下
+3. docker compose up
+
+
+## task 1.0.1 tree 1.0.1
+
+> 大文件source无法存储到数据库,添加config.source存储source内容的文件,obs路径
+
+- 适配后台glb等等模型转换逻辑,添加tree和task服务对应接口逻辑

+ 4 - 0
pack-queentree/start.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+docker-compose up -d --remove-orphans
+docker-compose ps

+ 43 - 0
pack-sku3d/config-service.yaml

@@ -0,0 +1,43 @@
+log:
+  fileName: pack-sku3d.log
+  level: 1
+  serviceName: pack-sku3d
+  
+configer:
+  -
+    name: mall-mongo
+    value: mongodb://root:root@sku3d-mongo:27017/custom-mall?authSource=admin
+    devValue: mongodb://root:root@124.71.139.24:37018/custom-mall?authSource=admin
+  -
+    name: user-mongo
+    value: mongodb://root:root@sku3d-mongo:27017/usercenter?authSource=admin
+    devValue: mongodb://root:root@124.71.139.24:37018/usercenter?authSource=admin
+  -
+    name: malluser-mongo
+    value: mongodb://root:root@sku3d-mongo:27017/mall-users?authSource=admin
+    devValue: mongodb://root:root@124.71.139.24:37018/mall-users?authSource=admin
+  -
+    name: website-mongo
+    value: mongodb://root:root@sku3d-mongo:27017/sku3d?authSource=admin
+    devValue: mongodb://root:root@124.71.139.24:37018/sku3d?authSource=admin
+  -
+    name: redis
+    value: sku3d-redis:6379#0#default#packsku3d
+    devValue: 124.71.139.24:16379#0#default#packsku3d
+
+adapter:
+  -
+    busName: comm
+    nats: nats://comm-bus:4222
+  -
+    busName: queentree
+    nats: nats://tree-bus:4222
+
+startLocalNats: true
+startNatsShellParams: "-p 4222 -m 8222 -js -c server.conf"
+startNatsPort: 4222
+
+nats:
+  url: nats://sku3d-nats:4222
+  maxReconnect: 100000
+  reconnDelaySecond: 5

+ 101 - 0
pack-sku3d/docker-compose.yaml

@@ -0,0 +1,101 @@
+version: '3.8'
+# 网络
+networks:
+  default:
+    name: default-network
+    external: true
+# 服务
+services:
+  #nginx反向代理
+  sku3d-nginx:
+    image: "registry.cn-chengdu.aliyuncs.com/infish/pack-comm-nginx:1.23.1"
+    restart: always
+    ports:
+      - 18081:80
+    volumes:
+      - ./nginx/conf.d:/etc/nginx/conf.d
+      - ./nginx/www:/usr/share/nginx/html
+
+    depends_on:
+      - sku3d-mall
+      - sku3d-usercenter
+      - sku3d-website
+      - sku3d-malluser
+
+  # nats消息中间件
+  sku3d-bus:
+    restart: always
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-comm-bus:v1.0.1
+    volumes:
+      - ./config-service.yaml:/root/bus/app.yaml
+
+    depends_on:
+      - sku3d-mongo
+      - sku3d-redis
+    ports:
+      - 14223:4222
+
+  # redis缓存
+  sku3d-redis:
+    image: "redis:alpine"
+    restart: always
+    command:
+      --requirepass "packsku3d" #这一行是设置密码
+    privileged: true
+    ports:
+      - 16379:6379
+
+  # mongo数据库
+  sku3d-mongo:
+    image: mongo:4.4.1
+    restart: always
+    environment:
+      MONGO_INITDB_ROOT_USERNAME: root
+      MONGO_INITDB_ROOT_PASSWORD: root
+
+    volumes:
+      - ~/data/packs-sku3d/mongo/db:/data/db
+      - ~/data/packs-sku3d/mongo/log:/var/log/mongodb
+    ports: 
+      - 37018:27017
+
+  # 商城服务
+  sku3d-mall:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-sku3d-mall:v1.0.2
+    restart: always
+    depends_on:
+      - sku3d-bus
+    environment: 
+      NATS: nats://sku3d-bus:4222
+
+  #user服务
+  sku3d-usercenter:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-comm-usercenter:v1.0.0
+    restart: always
+    depends_on:
+      - sku3d-bus
+    environment: 
+      NATS: nats://sku3d-bus:4222
+  
+  #商城c端用户
+  sku3d-malluser:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-sku3d-malluser:v1.0.0
+    restart: always
+    depends_on:
+      - sku3d-bus
+    environment: 
+      NATS: nats://sku3d-bus:4222
+
+  #sku3d-webite官网服务
+  sku3d-website:
+    image: registry.cn-chengdu.aliyuncs.com/infish/pack-sku3d-website:v1.0.0
+    restart: always
+    depends_on:
+      - sku3d-bus
+    environment: 
+      NATS: nats://sku3d-bus:4222
+
+  #商城c端用户
+  eps2svg-srv:
+    image: registry.cn-chengdu.aliyuncs.com/infish/eps2svg:v1.0.0
+    restart: always

+ 77 - 0
pack-sku3d/nginx/conf.d/default.conf

@@ -0,0 +1,77 @@
+server {
+    listen       80;
+    listen  [::]:80;
+    server_name  localhost;
+    #access_log  /var/log/nginx/host.access.log  main;
+    client_max_body_size 200M;
+    client_body_buffer_size 30M;
+    location / {
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+    }
+    #error_page  404              /404.html;
+
+    # redirect server error pages to the static page /50x.html
+    #
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   /usr/share/nginx/html;
+    }
+
+    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+    #
+    #location ~ \.php$ {
+    #    proxy_pass   http://127.0.0.1;
+    #}
+
+    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+    #
+    #location ~ \.php$ {
+    #    root           html;
+    #    fastcgi_pass   127.0.0.1:9000;
+    #    fastcgi_index  index.php;
+    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
+    #    include        fastcgi_params;
+    #}
+
+    # deny access to .htaccess files, if Apache's document root
+    # concurs with nginx's one
+    #
+    #location ~ /\.ht {
+    #    deny  all;
+    #}
+
+
+    # 商城接口
+    location /mall/ {
+       proxy_pass  http://sku3d-mall:7904/mall/;
+    }
+
+    # 商城接口
+    location /website/ {
+       proxy_pass  http://sku3d-website:7902/sku3dsite/;
+    }
+
+    # 网站用户接口
+    location /usercenter/ {
+        proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_set_header X-Forwarded-Host $server_name;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_pass  http://sku3d-usercenter:8908/usercenter/;
+    }
+
+    #商城用户接口
+    location /malluser/ {
+       proxy_pass  http://sku3d-malluser:8908/usercenter/;
+    }
+
+    location /eps2svg/ {
+       proxy_pass  http://eps2svg-srv:8000/;
+    }
+
+    #nats-exporter
+    # location /natsexporter/ {
+    #    proxy_pass  http://nats-exporter:7777/;
+    # }
+}

+ 1 - 0
pack-sku3d/nginx/www/index.html

@@ -0,0 +1 @@
+hello world~!!!

+ 19 - 0
pack-sku3d/readme.md

@@ -0,0 +1,19 @@
+# 包含的微服务说明
+
+- config-service
+  
+  configer: 配置,可通过bus获取  
+  adapter: 适配,本bus上的requsetApi可代理到该对应配置的bus上
+
+- mall-service
+
+## 流程
+
+1. 构建每个服务的镜像,每个服务root目录下`./build`
+2. 配置当前`docker-compose.yaml`
+3. docker compose up
+
+## 说明
+
+在Newbus中注册了replayer/streams并用到了mongo/redis,需要不依赖bus的方式拿取mongoclient/redisClient。  
+需要在配置文件中配置此时用到的mongo/redis。

+ 183 - 0
sku3d/.drone.yml

@@ -0,0 +1,183 @@
+---
+kind: pipeline
+type: docker
+name: default
+steps:
+- name: 1.submodules
+  image: alpine/git
+  commands:
+  - git submodule update --init --recursive
+
+- name: 2.building
+  image: golang:1.17
+  environment:
+    GOOS: linux
+    GOARCH: amd64
+    GO111MODULE: on
+    CGO_ENABLED: 0
+    GOPROXY: https://proxy.golang.com.cn,direct
+  
+  commands:
+  - cd sku3d
+  - go version
+  - go env
+  - go build
+
+- name: 3.dockerbuild(test)  
+  image: plugins/docker
+  settings:
+    username: infish2018
+    password: infish@2022
+    repo: registry.cn-chengdu.aliyuncs.com/sku3d/maintest
+    registry: registry.cn-chengdu.aliyuncs.com
+    mirror: https://17itejgx.mirror.aliyuncs.com
+
+    tags:
+      - latest
+      - '1.0.13'
+    context: sku3d/
+    dockerfile: sku3d/Dockerfile
+  when:
+    branch:
+    - test
+
+- name: 4.ssh-deploy(test)
+  image: appleboy/drone-ssh
+  settings:
+    host:
+      - 124.71.139.24
+    username: root
+    password:
+      from_secret: serverpassword
+    port: 22
+    command_timeout: 3m
+    script:
+      - cd /root/services/compose-sku3d-test
+      - docker pull registry.cn-chengdu.aliyuncs.com/sku3d/maintest:latest
+      - docker-compose up -d
+      - docker-compose ps
+  when:
+    branch:
+    - test
+
+- name: 3.dockerbuild(release)  
+  image: plugins/docker
+  settings:
+    username: infish2018
+    password: infish@2022
+    repo: registry.cn-chengdu.aliyuncs.com/sku3d/main
+    registry: registry.cn-chengdu.aliyuncs.com
+    mirror: https://17itejgx.mirror.aliyuncs.com
+
+    tags:
+      - latest
+      - '1.0.13'
+    context: sku3d/
+    dockerfile: sku3d/Dockerfile
+  when:
+    branch:
+    - release
+
+- name: 4.ssh-deploy(release)
+  image: appleboy/drone-ssh
+  settings:
+    host:
+      - 124.71.139.24
+    username: root
+    password:
+      from_secret: serverpassword
+    port: 22
+    command_timeout: 3m
+    script:
+      - cd /root/services/platform-release
+      - docker pull registry.cn-chengdu.aliyuncs.com/sku3d/main:latest
+      - docker-compose up -d
+      - docker-compose ps
+  when:
+    branch:
+    - release
+
+- name: 3.dockerbuild(dadongtest)  
+  image: plugins/docker
+  settings:
+    username: infish2018
+    password: infish@2022
+    repo: registry.cn-chengdu.aliyuncs.com/waibao/dadongmaintest
+    registry: registry.cn-chengdu.aliyuncs.com
+    mirror: https://17itejgx.mirror.aliyuncs.com
+
+    tags:
+      - latest
+      - '0.0.1'
+    context: sku3d/
+    dockerfile: sku3d/Dockerfile
+  when:
+    branch:
+    - dadongtest
+
+- name: 4.ssh-deploy(dadongtest)
+  image: appleboy/drone-ssh
+  settings:
+    host:
+      - 124.71.170.116
+    username: root
+    password:
+      from_secret: dadongpassword
+    port: 22
+    command_timeout: 3m
+    script:
+      - cd /root/sku3d-test
+      - docker pull registry.cn-chengdu.aliyuncs.com/waibao/dadongmaintest:latest
+      - docker-compose up -d
+      - docker-compose ps
+  when:
+    branch:
+    - dadongtest
+
+- name: 3.dockerbuild(dadongrelease)  
+  image: plugins/docker
+  settings:
+    username: infish2018
+    password: infish@2022
+    repo: registry.cn-chengdu.aliyuncs.com/waibao/dadongmain
+    registry: registry.cn-chengdu.aliyuncs.com
+    mirror: https://17itejgx.mirror.aliyuncs.com
+
+    tags:
+      - latest
+      - '0.0.1'
+    context: sku3d/
+    dockerfile: sku3d/Dockerfile
+  when:
+    branch:
+    - dadongrelease
+
+- name: 4.ssh-deploy(dadongrelease)
+  image: appleboy/drone-ssh
+  settings:
+    host:
+      - 124.71.170.116
+    username: root
+    password:
+      from_secret: dadongpassword
+    port: 22
+    command_timeout: 3m
+    script:
+      - cd /root/sku3d
+      - docker pull registry.cn-chengdu.aliyuncs.com/waibao/dadongmain:latest
+      - docker-compose up -d
+      - docker-compose ps
+  when:
+    branch:
+    - dadongrelease
+
+trigger:
+  branch:
+  - test
+  - release
+  - dadongrelease
+  - dadongtest
+  
+  event:
+  - push
+  

+ 16 - 0
sku3d/.gitignore

@@ -0,0 +1,16 @@
+sku3dweb
+sku3dweb.exe
+*.log
+worker-shadow/bin/shadow/_baked_lightmaps/
+worker-sku3drender/bin/cache/
+worker-sku3drender-local/bin/cache/
+worker-treeRender/bin/cache/
+local-sku3d/bin/render/bin/cache/
+.idea/
+sku3d/uploads/
+usercenter/__debug_bin.exe
+usercenter/sku3dweb
+usercenter/sku3dweb.exe
+*.exe
+
+./custom-mall/custom-mall

+ 3 - 0
sku3d/.gitmodules

@@ -0,0 +1,3 @@
+[submodule "comm"]
+	path = comm
+	url = http://124.70.149.18:10880/service/comm.git

+ 70 - 0
sku3d/comm/app-quitsocket.go

@@ -0,0 +1,70 @@
+package comm
+
+import (
+	"fmt"
+	"net"
+)
+
+type QuitCtx map[string]interface{}
+
+type QuiteHandle func(ctx QuitCtx) bool
+
+var _quitCtx = make(map[string]interface{})
+var _listeners = []QuiteHandle{}
+
+func InitQuitCtx(ctx QuitCtx) {
+	_quitCtx = ctx
+}
+
+func PushQuitHandle(handle QuiteHandle) {
+	_listeners = append(_listeners, handle)
+}
+
+func onQuite() {
+	t := len(_listeners)
+	for i, h := range _listeners {
+		isBreak := h(_quitCtx)
+		fmt.Println("[quit] ", i, "/", t, "breaking next =>", isBreak)
+		if isBreak {
+			break
+		}
+	}
+}
+
+var QuitChan = make(chan int)
+
+func InitQuitSocket(port int) error {
+	if port <= 0 {
+		p, e := GetFreePort()
+		if e != nil {
+			return e
+		}
+		port = p
+	}
+
+	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
+	if err != nil {
+		fmt.Println("listen tcp err", err)
+		return err
+	}
+	fmt.Println("quitsokect=>", port)
+
+	go func() {
+		// defer ln.Close()
+		conn, err := ln.Accept()
+		if err != nil {
+			fmt.Println("Accept error=>", err)
+			ln.Close()
+			return
+		}
+		defer conn.Close() //接收
+		onQuite()
+		message := "pong"
+		_, err = conn.Write([]byte(message))
+		if err != nil {
+			fmt.Println("Error sending message:", err)
+		}
+		QuitChan <- 1
+	}()
+	return nil
+}

+ 251 - 0
sku3d/comm/app-root.go

@@ -0,0 +1,251 @@
+package comm
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"github.com/jessevdk/go-flags"
+	"github.com/kardianos/osext"
+	"github.com/nats-io/nats.go"
+	"github.com/spf13/viper"
+)
+
+type AppRootOption struct {
+	Version  string `short:"v" long:"version" description:"conf version"`
+	NatsPort int    `short:"p" long:"port" description:"conf nats port"`
+	Data     string `short:"d" long:"data" description:"conf data dir"`
+	Socket   int    `short:"s" long:"socket" description:"config quit tcp socket"`
+}
+
+type SysBusProfile struct {
+	NatsWsPort int
+	NatsPort   int
+	MiniPort   int
+	DbPort     int
+}
+
+func (o *AppRootOption) Parse() {
+	flags.NewParser(o, flags.Default|flags.IgnoreUnknown).Parse()
+}
+
+var GAppOption = &AppRootOption{Data: "./data", Version: "0.0.0"}
+
+type AppRoot struct {
+	ExeDir      string
+	DataDir     string
+	Name        string
+	ExitCode    int
+	RunExe      func(func()) error
+	OnQuitExe   func()
+	Quit        chan struct{}
+	HttpPort    int
+	NatsWsPort  int
+	NatsPort    int
+	ConfYaml    string
+	ParseConfig func(*viper.Viper) error
+	Open        func() error
+	SysBus      SysBusProfile
+
+	Bus *NatsBus
+}
+
+func (a *AppRoot) InitBus(bus *NatsBus) error {
+
+	fpath := path.Join(filepath.Dir(a.ExeDir), fmt.Sprintf("%s.exe", a.Name))
+	appId := GetAppPathId(fpath)
+	fmt.Println("app id==========", appId, fpath)
+
+	p, e := bus.RequestConfig("sysbus.profile")
+	if e != nil {
+		fmt.Println("获取配置失败!", e)
+		return e
+	}
+	e = json.Unmarshal([]byte(p), &a.SysBus)
+	if e != nil {
+		fmt.Println("解析配置失败!", e)
+		return e
+	}
+	a.NatsWsPort = a.SysBus.NatsWsPort
+
+	_, e = bus.QueueSubscribe(&SubOption{
+		Sub: "app.quit." + appId,
+		Call: func(obj interface{}, msg *nats.Msg) interface{} {
+			a.QuitApp(0)
+			return "ok"
+		},
+	})
+
+	if e != nil {
+		return e
+	}
+	_, e = bus.QueueSubscribe(&SubOption{
+		Sub: "app.open." + appId,
+		Call: func(obj interface{}, msg *nats.Msg) interface{} {
+			d := NatsResponse{ErrorNo: 400}
+			if a.Open == nil {
+				d.ErrorDesc = "当前应用不能打开"
+			} else {
+				e := a.Open()
+				if e != nil {
+					d.ErrorDesc = e.Error()
+				} else {
+					d.ErrorNo = 200
+					d.ErrorDesc = ""
+				}
+			}
+			return d
+		},
+	})
+	if e != nil {
+		return e
+	}
+
+	e = LancherClientRun(bus, fpath, path.Join(a.ExeDir, "app.yaml"), "")
+	if e != nil {
+		return e
+	}
+
+	a.Bus = bus
+
+	return nil
+}
+
+func (a *AppRoot) QuitApp(exit int) {
+	if a.OnQuitExe != nil {
+		a.OnQuitExe()
+		a.OnQuitExe = nil
+	}
+	a.ExitCode = exit
+	a.Quit <- struct{}{}
+}
+func (a *AppRoot) GetNatsUrl() string {
+	return fmt.Sprintf("nats://localhost:%d", a.NatsPort)
+}
+func (a *AppRoot) GetNatsWebUrl() string {
+	return fmt.Sprintf("ws://localhost:%d", a.NatsWsPort)
+}
+
+func (a *AppRoot) GenHttpPort(d int) int {
+	portFile := filepath.Join(a.DataDir, fmt.Sprintf("%s.port", a.Name))
+	portStr, err := ioutil.ReadFile(portFile)
+	if err == nil && len(portStr) > 0 {
+		port, _ := strconv.Atoi(string(portStr))
+		if port > 0 {
+			return port
+		}
+	}
+	port, _ := GetFreePort()
+	if port > 0 {
+		d = port
+	}
+	os.MkdirAll(GAppOption.Data, os.ModePerm)
+	ioutil.WriteFile(portFile, []byte(fmt.Sprintf("%d", d)), os.ModePerm)
+	return d
+}
+
+func (a *AppRoot) Run() {
+	GAppOption.Parse()
+
+	e := InitQuitSocket(GAppOption.Socket)
+	if e != nil {
+		fmt.Println(e)
+		return
+	}
+
+	//go run . 执行
+	f, _ := osext.ExecutableFolder()
+	exeFoloder := ""
+	if strings.LastIndex(f, "\\go-build") == -1 {
+		exeFoloder = f
+	}
+
+	a.ExeDir = exeFoloder
+	a.HttpPort = a.GenHttpPort(20000)
+	a.DataDir = GAppOption.Data
+
+	a.NatsPort = GAppOption.NatsPort
+
+	cfile := filepath.Join(exeFoloder, "app.yaml")
+
+	a.ConfYaml = cfile
+
+	if isExist(cfile) && a.ParseConfig != nil {
+		file, err := os.Open(cfile)
+		if err != nil {
+			fmt.Println("open app.yaml error=>", cfile, err)
+			return
+		}
+		v := viper.New()
+		v.SetConfigType("yaml")
+		err = v.ReadConfig(file)
+		if err != nil {
+			fmt.Println("read app.yaml error=>", cfile, err)
+			return
+		}
+		err = a.ParseConfig(v)
+		if err != nil {
+			fmt.Println("parse config error=>", err)
+			return
+		}
+	}
+
+	a.Quit = make(chan struct{})
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("系统异常", r)
+			a.QuitApp(500)
+		}
+	}()
+
+	//开启一个线程跑主要流畅
+	go func() {
+		err := a.RunExe(func() {
+			fmt.Printf("%s started\n", a.Name)
+			fmt.Printf("%s is running\n", a.Name)
+		})
+
+		if err != nil {
+			fmt.Println(a.Name, " running error=>", err)
+			a.ExitCode = 1
+			a.Quit <- struct{}{}
+		} else {
+			<-QuitChan
+			a.QuitApp(0)
+		}
+	}()
+	<-a.Quit
+
+	if a.OnQuitExe != nil {
+		a.OnQuitExe()
+	}
+
+	fmt.Println(a.Name, " System Quited")
+	if a.ExitCode > 0 {
+		os.Exit(a.ExitCode)
+	}
+	if a.Bus != nil {
+		LancherClientQuitApp(a.Bus)
+	}
+}
+
+func isExist(path string) bool {
+	_, err := os.Stat(path)
+	if err != nil {
+		if os.IsExist(err) {
+			return true
+		}
+		if os.IsNotExist(err) {
+			return false
+		}
+		return false
+	}
+	return true
+}
+
+var GApp = &AppRoot{}

+ 8 - 0
sku3d/comm/asset.go

@@ -0,0 +1,8 @@
+package comm
+
+type PublicAssetReq struct {
+	DbId     string
+	DefineId string
+	Id       string
+	Enable   bool
+}

+ 41 - 0
sku3d/comm/category.go

@@ -0,0 +1,41 @@
+package comm
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type CategoryNode struct {
+	Id       string          `bson:"id,omitempty" json:"id"` //分类Id
+	Type     string          `bson:"type,omitempty" json:"type"`
+	Name     string          `bson:"name,omitempty" json:"name"`
+	Value    string          `bson:"value,omitempty" json:"value"`
+	IsEdit   *bool           `bson:"isEdit,omitempty" json:"isEdit"`
+	Cover    string          `bson:"cover,omitempty" json:"cover"`
+	Children []*CategoryNode `bson:"children,omitempty" json:"children"` //是否是叶子节点
+}
+
+type DbCategory struct {
+	Id         primitive.ObjectID `bson:"_id,omitempty" json:"_id"`       //Id
+	UserId     primitive.ObjectID `bson:"userId,omitempty" json:"userId"` //用户Id
+	Scope      string             `bson:"scope,omitempty" json:"scope"`   //用户标签
+	CreateTime time.Time          `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime time.Time          `bson:"updateTime,omitempty" json:"updateTime"`
+	Categories []*CategoryNode    `bson:"categories,omitempty" json:"categories" complex:"true"` //所有树形分类定义
+}
+
+type DbAssetCategory struct {
+	DbAssetId   primitive.ObjectID `bson:"dbAssetConfId,omitempty" json:"dbAssetConfId"` //资产定义Id
+	CategoryIds []string           `bson:"categoryIds,omitempty" json:"categoryIds"`
+}
+
+type DbAssetUserCategory struct {
+	Id     primitive.ObjectID `bson:"_id,omitempty" json:"_id"`       //用户的资产分类定义
+	UserId primitive.ObjectID `bson:"userId,omitempty" json:"userId"` //用户Id
+	Scope  string             `bson:"scope,omitempty" json:"scope"`   //企业账号还是个人账号
+
+	CreateTime    time.Time          `bson:"createTime,omitempty" json:"createTime"`                      //创建时间
+	UpdateTime    time.Time          `bson:"updateTime,omitempty" json:"updateTime"`                      //更新时间
+	CategoryConfs []*DbAssetCategory `bson:"categoryConfs,omitempty" json:"categoryConfs" complex:"true"` //更新时间
+}

+ 173 - 0
sku3d/comm/commEntity.go

@@ -0,0 +1,173 @@
+package comm
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type OssType struct {
+	Url  string `bson:"url" json:"url"`
+	Size int64  `bson:"size" json:"size"`
+}
+
+func (o *OssType) UpdateSourceUrl(handler UpdateUrlHandler) {
+	o.Url = handler(o.Url)
+}
+
+func NewOssTypeWithMap(data map[string]interface{}) *OssType {
+	url := data["url"].(string)
+	size := data["size"].(int64)
+
+	return &OssType{Url: url, Size: size}
+}
+
+type MaterialConfig struct {
+	Id   string
+	Data interface{}
+}
+
+type Transform struct {
+}
+
+type Vect2 [2]float64
+type Vect3 [3]float64
+type Vect4 [4]float64
+
+type VectXYWH struct {
+	Y float64 `bson:"y" json:"y"`
+	X float64 `bson:"x" json:"x"`
+	W float64 `bson:"w" json:"w"`
+	H float64 `bson:"h" json:"h"`
+}
+
+type Env3dOption struct {
+	Rotation float64 `bson:"rotation" json:"rotation"`
+	Exposure float64 `bson:"exposure" json:"exposure"`
+}
+
+type ToneMap struct {
+	Method     *int32   `bson:"method,omitempty" json:"method"`         //0 "linear" 值0 "reinhard" 值 1 "filmic”:值2
+	Exposure   *float64 `bson:"exposure,omitempty" json:"exposure"`     //默认值1 范围0到2
+	Brightness *float64 `bson:"brightness,omitempty" json:"brightness"` //默认值0 范围-1到 1
+	Contrast   *float64 `bson:"contrast,omitempty" json:"contrast"`     //默认值0 范围-1到1
+	Saturation *float64 `bson:"saturation,omitempty" json:"saturation"` //默认值1 范围0-2
+}
+
+type Env3d struct {
+	Id         primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	Name       string             `bson:"name,omitempty" json:"name"`
+	CusNum     string             `bson:"cusNum,omitempty" json:"cusNum"` //自定义编号
+	Thumbnail  *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	CreateTime time.Time          `bson:"createTime,omitempty" json:"createTime"`
+	HDR        *OssType           `bson:"hdr,omitempty" json:"hdr"`
+	Config     *Evn3dHdrConf      `bson:"config,omitempty" json:"config"`
+	Options    *Env3dOption       `bson:"options,omitempty" json:"options"`
+	ToneMap    *ToneMap           `bson:"toneMap,omitempty" json:"toneMap"`
+	Background *Evn3dBackground   `bson:"background,omitempty" json:"background"`
+
+	UserData interface{} `bson:"userData,omitempty" json:"userData"` //用户数据
+}
+
+type Evn3dTextureImage struct {
+	File             string  `bson:"file,omitempty" json:"file"`
+	SizeUncompressed int32   `bson:"sizeUncompressed,omitempty" json:"sizeUncompressed"`
+	Samples          uint32  `bson:"samples,omitempty" json:"samples"`
+	Height           float32 `bson:"height,omitempty" json:"height"`
+	Width            float32 `bson:"width,omitempty" json:"width"`
+	SizeCompressed   int32   `bson:"sizeCompressed,omitempty" json:"sizeCompressed"`
+	Blur             float32 `bson:"blur,omitempty,truncate" json:"blur"`
+}
+
+type Evn3dTexture struct {
+	Images    []*Evn3dTextureImage `bson:"images,omitempty" json:"images"`
+	LimitSize int32                `bson:"limitSize,omitempty" json:"limitSize"`
+	Encoding  string               `bson:"encoding,omitempty" json:"encoding"`
+	Type      string               `bson:"type,omitempty" json:"type"`
+	Format    string               `bson:"format,omitempty" json:"format"`
+}
+
+type Evn3dLight struct {
+	Direction  Vect3    `bson:"direction,omitempty" json:"direction"`
+	Lum_ratio  float64  `bson:"lum_ratio,omitempty" json:"lum_ratio"`
+	Color      Vect3    `bson:"color,omitempty" json:"color"`
+	Luminosity float64  `bson:"luminosity,omitempty" json:"luminosity"`
+	Sum        float64  `bson:"sum,omitempty" json:"sum"`
+	Area       VectXYWH `bson:"area,omitempty" json:"area"`
+	Error      int32    `bson:"error,omitempty" json:"error"`
+	Variance   float64  `bson:"variance,omitempty" json:"variance"`
+}
+
+type Evn3dHdrConf struct {
+	Textures       []*Evn3dTexture `bson:"textures,omitempty" json:"textures"`
+	WriteByChannel bool            `bson:"writeByChannel,omitempty" json:"writeByChannel"`
+	Lights         []*Evn3dLight   `bson:"lights,omitempty" json:"lights"`
+	DiffuseSPH     []float64       `bson:"diffuseSPH,omitempty" json:"diffuseSPH"`
+}
+
+func (c *Evn3dHdrConf) UpdateSourceUrl(handler UpdateUrlHandler) {
+	for _, t := range c.Textures {
+		if t.Images == nil {
+			continue
+		}
+		for _, img := range t.Images {
+			img.File = handler(img.File)
+		}
+	}
+}
+
+type CanvasBackground struct {
+	Id         primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	Color      *Vect3             `bson:"color,omitempty" json:"color"`
+	Image      *OssType           `bson:"image,omitempty" json:"image"`
+	Type       int32              `bson:"type,omitempty" json:"type"`
+	Order      int32              `bson:"order,omitempty" json:"order"`
+	CreateTime time.Time          `bson:"createTime,omitempty" json:"createTime"`
+}
+
+func NewEnv3dToneMapWithMap(tone map[string]interface{}) *ToneMap {
+
+	method := int32(0)
+	Exposure := float64(1.0)
+	Saturation := float64(1.0)
+	Brightness := float64(0.0)
+	Contrast := float64(0.0)
+
+	if tone == nil {
+		return &ToneMap{
+			Method:     &method,
+			Exposure:   &Exposure,
+			Saturation: &Saturation,
+			Brightness: &Brightness,
+			Contrast:   &Contrast,
+		}
+	}
+
+	if v, ok := tone["method"].(int32); ok {
+		method = v
+	}
+
+	if v, ok := tone["exposure"].(float64); ok {
+		Exposure = v
+	}
+
+	if v, ok := tone["saturation"].(float64); ok {
+		Saturation = v
+	}
+
+	if v, ok := tone["brightness"].(float64); ok {
+		Brightness = v
+	}
+
+	if v, ok := tone["contrast"].(float64); ok {
+		Contrast = v
+	}
+
+	return &ToneMap{Method: &method, Exposure: &Exposure, Saturation: &Saturation, Brightness: &Brightness, Contrast: &Contrast}
+}
+
+type Evn3dBackground struct {
+	Color *Vect3   `bson:"color,omitempty" json:"color"`
+	Image *OssType `bson:"image,omitempty" json:"image"`
+	Type  int32    `bson:"type,omitempty" json:"type"`
+}

+ 1380 - 0
sku3d/comm/database.go

@@ -0,0 +1,1380 @@
+package comm
+
+import (
+	"encoding/json"
+	"fmt"
+	"math"
+	"net/http"
+	"strings"
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+const (
+	AssetTypeMesh          = 10 //"mesh"     //模型
+	AssetTypeImage         = 20 //"image"    //图片
+	AssetTypeMaterial      = 30 //"material" //材质球
+	AssetTypeMaterialGroup = 31 //"material" //材质球组
+	AssetTypeEnv3d         = 40 //"hdr "      //环境球
+	AssetTypeScene         = 50 //"scene "    //场景
+
+	AssetState_Empty    = 0
+	AssetState_Waiting  = 100 //等待处理
+	AssetState_Proccing = 101 //正在处理
+	AssetState_Failed   = 102 //处理失败!
+	AssetState_Succ     = 200 //处理成功
+)
+
+type DbAsset struct {
+	Id     string `bson:"id,omitempty" json:"id"` //资产id
+	UserId string `bson:"userId,omitempty" json:"userId"`
+	Label  string `bson:"label,omitempty" json:"label"`
+	Type   int    `bson:"type,omitempty" json:"type"` //AssetTypeMesh  AssetTypeImage ...
+
+	Collection  string    `bson:"collection,omitempty" json:"collection"` //数据库存储集合
+	CreateTime  time.Time `bson:"createTime,omitempty" json:"createTime"`
+	CategoryIds []string  `bson:"categoryIds,omitempty" json:"categoryIds"`
+}
+
+type AssetDbConf struct {
+	Db        *Database
+	AssetConf *DbAsset
+}
+
+type DbCategoryOld struct {
+	Id         string    `bson:"id,omitempty" json:"id"`
+	Type       string    `bson:"type,omitempty" json:"type"`
+	Parent     string    `bson:"parent,omitempty" json:"parent"`
+	Name       string    `bson:"name,omitempty" json:"name"`
+	Value      string    `bson:"value,omitempty" json:"value"`
+	Level      *int32    `bson:"level,omitempty" json:"level"`
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+	OrderId    int64     `bson:"orderId,omitempty" json:"orderId"`
+}
+
+type DatabaseOld struct {
+	Id     primitive.ObjectID `bson:"_id,omitempty" json:"_id"`       //数据库Id
+	UserId string             `bson:"userId,omitempty" json:"userId"` //数据库Id
+
+	Name       string    `bson:"name,omitempty" json:"name"`   //数据库名字
+	Label      string    `bson:"label,omitempty" json:"label"` //前端展示标签
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+
+	Categories []*DbCategoryOld `bson:"categories,omitempty" json:"categories"` //数据的分类
+	Assets     []*DbAsset       `bson:"assets,omitempty" json:"assets"`         //资产定义
+}
+
+type Database struct {
+	Id     primitive.ObjectID `bson:"_id,omitempty" json:"_id"`       //数据库Id
+	UserId string             `bson:"userId,omitempty" json:"userId"` //数据库Id
+
+	Name       string    `bson:"name,omitempty" json:"name"`   //数据库名字
+	Label      string    `bson:"label,omitempty" json:"label"` //前端展示标签
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+
+	Categories *DbCategory `bson:"categories,omitempty" json:"categories" complex:"true"` //数据的分类
+	Assets     []*DbAsset  `bson:"assets,omitempty" json:"assets"  complex:"true"`        //资产定义
+}
+
+type UserDatabase struct {
+	Id       primitive.ObjectID `bson:"_id,omitempty" json:"_id"`           //userId
+	UserName string             `bson:"userName,omitempty" json:"userName"` //用户名字
+	Limits   int                `bson:"limits,omitempty" json:"limits"`     //数据库限制数据
+}
+
+func CreateDefaultDbAssets() []*DbAsset {
+	return []*DbAsset{
+		{
+			Id:         primitive.NewObjectID().Hex(),
+			Label:      "模型",
+			Type:       AssetTypeMesh,
+			Collection: "mesh",
+			CreateTime: time.Now(),
+		},
+		{
+			Id:         primitive.NewObjectID().Hex(),
+			Label:      "图片",
+			Type:       AssetTypeImage,
+			Collection: "image",
+			CreateTime: time.Now(),
+		},
+		{
+			Id:         primitive.NewObjectID().Hex(),
+			Label:      "材质球",
+			Type:       AssetTypeMaterial,
+			Collection: "material",
+			CreateTime: time.Now(),
+		},
+		{
+			Id:         primitive.NewObjectID().Hex(),
+			Label:      "环境球",
+			Type:       AssetTypeEnv3d,
+			Collection: "env3d",
+			CreateTime: time.Now(),
+		},
+	}
+}
+
+func CreateDefaultDatabase() *Database {
+	return &Database{
+		Name:       "qdb-default",
+		Label:      "默认资产库",
+		CreateTime: time.Now(),
+
+		Assets:     CreateDefaultDbAssets(),
+		Categories: &DbCategory{},
+	}
+}
+
+type Obb struct {
+	Min Vec3Obj `bson:"min,omitempty" json:"min"`
+	Max Vec3Obj `bson:"max,omitempty" json:"max"`
+}
+
+type AssetBase struct {
+	OwnerId    primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`
+	Name       string             `bson:"name,omitempty" json:"name"`
+	CreateTime time.Time          `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime time.Time          `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail  *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Categories []string           `bson:"categories,omitempty" json:"categories"` //所属分类Id
+	TaskId     string             `bson:"taskId,omitempty" json:"taskId"`         //资产处理id
+	AssetState int                `bson:"assetState,omitempty" json:"assetState"`
+	Enable     *bool              `bson:"enable,omitempty" json:"enable"` //是否有效!
+}
+
+type UvSize struct {
+	Width  int `bson:"width,omitempty" json:"width,omitempty"`
+	Height int `bson:"height,omitempty" json:"height,omitempty"`
+}
+
+type MatTextureColor struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Color      *Vect3   `bson:"color" json:"color"`
+}
+
+func (m *MatTextureColor) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if m.Texture != nil {
+		m.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatDiamond struct {
+	ScaleY     *float32 `bson:"scaleY" json:"scaleY"`
+	ScaleX     *float32 `bson:"scaleX" json:"scaleX"`
+	Brightness *float32 `bson:"brightness" json:"brightness"`
+	Color      *Vect3   `bson:"color" json:"color"`
+}
+
+type MatTextureFactor struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+}
+
+func (m *MatTextureFactor) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if m.Texture != nil {
+		m.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatNormal struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+	FlipY      *bool    `bson:"flipY" json:"flipY"`
+}
+
+func (n *MatNormal) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Texture != nil {
+		n.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatTextureFactorWithEnable struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+	Enable     bool     `bson:"enable" json:"enable"`
+}
+
+func (n *MatTextureFactorWithEnable) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Texture != nil {
+		n.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatAlbedo struct {
+	Color      *Vect3   `bson:"color" json:"color"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+}
+
+func (n *MatAlbedo) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Texture != nil {
+		n.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatRoughness struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+}
+
+func (n *MatRoughness) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Texture != nil {
+		n.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatMetalness struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+}
+
+func (n *MatMetalness) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Texture != nil {
+		n.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type MatDisplace struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+}
+
+type MatDiffuse struct {
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+}
+
+type MatOpacity struct {
+	Enable     bool     `bson:"enable" json:"enable"`
+	Factor     *float32 `bson:"factor" json:"factor"`
+	Texture    *OssType `bson:"texture" json:"texture"`
+	UseTexture *bool    `bson:"useTexture" json:"useTexture"`
+}
+
+func (n *MatOpacity) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Texture != nil {
+		n.Texture.UpdateSourceUrl(handler)
+	}
+}
+
+type TechMatConfig struct {
+	Version     int           `bson:"version,omitempty" json:"version"`
+	Type        string        `bson:"type,omitempty" json:"type"`
+	Uvtransform *MaterailUv   `bson:"uvtransform,omitempty" json:"uvtransform"`
+	CullFace    string        `bson:"cullFace" json:"cullFace"`
+	Albedo      *MatAlbedo    `bson:"albedo" json:"albedo"`
+	Roughness   *MatRoughness `bson:"roughness" json:"roughness"`
+	Metalness   *MatMetalness `bson:"metalness" json:"metalness"`
+	Specular    *MatAlbedo    `bson:"specular" json:"specular"`
+	Normal      *MatNormal    `bson:"normal" json:"normal"`
+	Opacity     *MatOpacity   `bson:"opacity" json:"opacity"`
+}
+
+func (n *TechMatConfig) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if n.Albedo != nil {
+		n.Albedo.UpdateSourceUrl(handler)
+	}
+	if n.Roughness != nil {
+		n.Roughness.UpdateSourceUrl(handler)
+	}
+	if n.Metalness != nil {
+		n.Metalness.UpdateSourceUrl(handler)
+	}
+	if n.Specular != nil {
+		n.Specular.UpdateSourceUrl(handler)
+	}
+	if n.Normal != nil {
+		n.Normal.UpdateSourceUrl(handler)
+	}
+	if n.Opacity != nil {
+		n.Opacity.UpdateSourceUrl(handler)
+	}
+}
+
+type ComponentImage struct {
+	Id     string   `bson:"id" json:"id"`
+	Image  *OssType `bson:"image" json:"image"`
+	Width  int      `bson:"width" json:"width"`
+	Height int      `bson:"height" json:"height"`
+
+	Position         Vect2   `bson:"position" json:"position"`
+	Scale            Vect2   `bson:"scale" json:"scale"`
+	Rotation         float64 `bson:"rotation" json:"rotation"`
+	FillType         string  `bson:"fillType" json:"fillType"`
+	Technology       string  `bson:"technology" json:"technology"`
+	BasicColor       Vect3   `bson:"basicColor" json:"basicColor"`
+	AotuFactor       float32 `bson:"aotuFactor" json:"aotuFactor"`
+	RoughFactor      float32 `bson:"roughFactor" json:"roughFactor"`
+	ReflectionFactor float32 `bson:"reflectionFactor" json:"reflectionFactor"`
+	MetalFactor      float32 `bson:"metalFactor" json:"metalFactor"`
+
+	IsAotu  *bool `bson:"isAotu" json:"isAotu"`
+	TjColor Vect3 `bson:"tjColor" json:"tjColor"` //烫金颜色
+}
+
+type MeshMatConf struct {
+	UvMap        *OssType          `bson:"uvmap" json:"uvmap"`
+	UvSize       *UvSize           `bson:"uvsize" json:"uvsize"`
+	Name         string            `bson:"name" json:"name"`
+	Material     *MaterialConf     `bson:"material" json:"material"`
+	TechMaterial *TechMatConfig    `bson:"techMaterial" json:"techMaterial"`
+	Images       []*ComponentImage `bson:"images" json:"images"`
+	Index        int               `bson:"index" json:"index"`
+	Visible      *bool             `bson:"visible" json:"visible"`
+	UserData     interface{}       `bson:"userData,omitempty" json:"userData,omitempty"` //自定义数据
+}
+
+type StaticMeshSource struct {
+	Components  []*MeshMatConf `bson:"components,omitempty" json:"components"`
+	Osgjs       *OssType       `bson:"osgjs,omitempty" json:"osgjs"`
+	File        *OssType       `bson:"file,omitempty" json:"file"` //hdr or fbx
+	Glb         *OssType       `bson:"glb,omitempty" json:"glb"`   //hdr or fbx
+	Shadow      *OssType       `bson:"shadow,omitempty" json:"shadow,omitempty"`
+	BoundingBox *Obb           `bson:"boundingBox,omitempty" json:"boundingBox,omitempty"`
+}
+
+type HdrSource struct {
+	Config  *Evn3dHdrConf `bson:"config,omitempty" json:"config"`
+	File    *OssType      `bson:"file,omitempty" json:"file"` //hdr or fbx
+	Options *Env3dOption  `bson:"options,omitempty" json:"options"`
+	ToneMap *ToneMap      `bson:"toneMap,omitempty" json:"toneMap"`
+}
+
+type ImageSource struct {
+	File *OssType `bson:"file,omitempty" json:"file"` //hdr or fbx
+}
+
+type TransTask struct {
+	File  *TransFile `bson:"file,omitempty" json:"file"`
+	Osgjs *OssType   `bson:"osgjs,omitempty" json:"osgjs"`
+}
+
+type ShadowTask struct {
+	File   *TransFile `bson:"file,omitempty" json:"file"`
+	Shadow *OssType   `bson:"osgjs,omitempty" json:"osgjs"`
+}
+
+func FetchAndParseJSON(url string, data interface{}) error {
+	fmt.Println("parse config url: ", url)
+	if !strings.Contains(url, "https://") {
+		url = fmt.Sprintf("https:%s", url)
+	}
+	// 创建HTTP请求
+	response, err := http.Get(url)
+	if err != nil {
+		return fmt.Errorf("HTTP请求失败: %v", err)
+	}
+	defer response.Body.Close()
+
+	// 检查响应状态码
+	if response.StatusCode != http.StatusOK {
+		return fmt.Errorf("非200响应状态码: %v", response.StatusCode)
+	}
+
+	// 解析JSON响应
+	err = json.NewDecoder(response.Body).Decode(data)
+	if err != nil {
+		return fmt.Errorf("解析JSON失败: %v", err)
+	}
+
+	return nil
+}
+
+func TransTaskIsNotEmpty(transTask *TransTask) bool {
+	if transTask != nil {
+		if transTask.File != nil {
+			if len(transTask.File.Url) > 0 {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func ShadowIsNotEmpty(shadowTask *ShadowTask) bool {
+	if shadowTask != nil {
+		if shadowTask.File != nil {
+			if len(shadowTask.File.Url) > 0 {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+type TransFile struct {
+	Id  string `bson:"id,omitempty" json:"id"`
+	Url string `bson:"url,omitempty" json:"url"`
+}
+
+type AssetStaticMesh struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId    primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`
+	OwnerId   primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`     //userId teamId companyId
+	OwnerType string             `bson:"ownerType,omitempty" json:"ownerType,omitempty"` //user team company
+
+	Name   string `bson:"name,omitempty" json:"name"`
+	CusNum string `bson:"cusNum,omitempty" json:"cusNum,omitempty"` //型号
+
+	CreateTime    time.Time `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime    time.Time `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail     *OssType  `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Categories    []string  `bson:"categories,omitempty" json:"categories"`       //所属分类Id
+	CusCategories []string  `bson:"cusCategories,omitempty" json:"cusCategories"` //所属分类Id
+
+	TaskId     string `bson:"taskId,omitempty" json:"taskId"` //资产处理id
+	AssetState int    `bson:"assetState,omitempty" json:"assetState"`
+	Enable     *bool  `bson:"enable,omitempty" json:"enable"` //是否有效!
+
+	DotQueen   string            `bson:"dotQueen,omitempty" json:"dotQueen,omitempty"`
+	ViewMode   string            `bson:"viewMode,omitempty" json:"viewMode,omitempty"`
+	Source     *StaticMeshSource `bson:"source,omitempty" json:"source,omitempty"`
+	TransTask  *TransTask        `bson:"transTask,omitempty" json:"transTask"`
+	ShadowTask *ShadowTask       `bson:"shadowTask,omitempty" json:"shadowTask"`
+	Config     string            `bson:"config,omitempty" json:"config,omitempty"`
+
+	UserData interface{} `bson:"userData,omitempty" json:"userData,omitempty"` //自定义数据
+}
+
+type AssetEnv3dHdr struct {
+	Id            primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId        primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`                    //用户Id
+	UserInfo      *AssetUserInfo     `bson:"userInfo,omitempty" json:"userInfo,omitempty" complex:"true"` //用户Id
+	OwnerId       primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`
+	OwnerType     string             `bson:"ownerType,omitempty" json:"ownerType,omitempty"`
+	AssetType     string             `bson:"assetType,omitempty" json:"assetType,omitempty"`
+	Name          string             `bson:"name,omitempty" json:"name"`
+	CusNum        string             `bson:"cusNum,omitempty" json:"cusNum,omitempty"` //型号
+	CreateTime    time.Time          `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime    time.Time          `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail     *OssType           `bson:"thumbnail,omitempty" json:"thumbnail" complex:"true"`
+	Categories    []string           `bson:"categories,omitempty" json:"categories" complex:"true"`       //所属分类Id
+	CusCategories []string           `bson:"cusCategories,omitempty" json:"cusCategories" complex:"true"` //所属分类Id
+	TaskId        string             `bson:"taskId,omitempty" json:"taskId"`                              //资产处理id
+	AssetState    int                `bson:"assetState,omitempty" json:"assetState"`
+	Enable        *bool              `bson:"enable,omitempty" json:"enable"` //是否有效!
+
+	DotQueen   string      `bson:"dotQueen,omitempty" json:"dotQueen,omitempty"`
+	ViewMode   string      `bson:"viewMode,omitempty" json:"viewMode,omitempty"`
+	Source     *HdrSource  `bson:"source,omitempty" json:"source,omitempty" complex:"true"`
+	TransTask  *TransTask  `bson:"transTask,omitempty" json:"transTask" complex:"true"`
+	ShadowTask *ShadowTask `bson:"shadowTask,omitempty" json:"shadowTask"`
+	Config     string      `bson:"config,omitempty" json:"config,omitempty"`
+	UserData   interface{} `bson:"userData,omitempty" json:"userData,omitempty" complex:"true"` //用户数据
+}
+
+func (s *AssetEnv3dHdr) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if s.Thumbnail != nil {
+		s.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.UserInfo != nil && s.UserInfo.Thumbnail != nil {
+		s.UserInfo.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.Source == nil {
+		return
+	}
+
+	if s.Source.Config != nil {
+		s.Source.Config.UpdateSourceUrl(handler)
+	}
+	if s.Source.File != nil {
+		s.Source.File.UpdateSourceUrl(handler)
+	}
+}
+
+func (s *AssetEnv3dHdr) SetIdEmpty() {
+	s.Id = primitive.NilObjectID
+}
+
+func (s *AssetEnv3dHdr) ResetCreateTime() {
+	s.CreateTime = time.Now()
+	s.UpdateTime = time.Now()
+}
+func (s *AssetEnv3dHdr) SetOwner(id string, otype string) {
+	s.OwnerId, _ = primitive.ObjectIDFromHex(id)
+	s.OwnerType = otype
+}
+func (s *AssetEnv3dHdr) SetAssetType(otype string) {
+	s.AssetType = otype
+}
+
+func (s *AssetEnv3dHdr) SetUserInfo(id string, info *AssetUserInfo) {
+	s.UserId, _ = primitive.ObjectIDFromHex(id)
+	s.UserInfo = info
+}
+
+type CusColorParams struct {
+	BaseAlbedoId         string   `bson:"baseAlbedoId,omitempty" json:"baseAlbedoId,omitempty"` //基础图片
+	BaseAlbedo           *OssType `bson:"baseAlbedo,omitempty" json:"baseAlbedo,omitempty"`     //基础图片
+	SourceMode           *int     `bson:"sourceMode,omitempty" json:"sourceMode,omitempty"`
+	SourceColor          *Vect3   `bson:"sourceColor,omitempty" json:"sourceColor,omitempty"`
+	TargetColor          *Vect3   `bson:"targetColor,omitempty" json:"targetColor,omitempty"`
+	ColorVariationHue    *int     `bson:"color_variation_hue,omitempty" json:"color_variation_hue,omitempty"`
+	ColorVariationChroma *float32 `bson:"color_variation_chroma,omitempty" json:"color_variation_chroma,omitempty"`
+	ColorVariationLuma   *float32 `bson:"color_variation_luma,omitempty" json:"color_variation_luma,omitempty"`
+	UseMask              bool     `bson:"useMask,omitempty" json:"useMask,omitempty"`
+	MaskHue              *int     `bson:"mask_hue,omitempty" json:"mask_hue,omitempty"`
+	MaskChroma           *float32 `bson:"mask_chroma,omitempty" json:"mask_chroma,omitempty"`
+	MaskLuma             *float32 `bson:"mask_luma,omitempty" json:"mask_luma,omitempty"`
+	MaskBlur             *float32 `bson:"mask_blur,omitempty" json:"mask_blur,omitempty"`
+	MaskSmoothness       *float32 `bson:"mask_smoothness,omitempty" json:"mask_smoothness,omitempty"`
+}
+
+const (
+	MatGenerateCusColor       = "cuscolor" //色卡换色 算法生成
+	MatGenerateTech           = "tech"     //按工艺生成
+	CusColorSourceModeAverage = 0
+	CusColorSourceModeColor   = 1
+	CusColorSourceModeTexure  = 2
+)
+
+type MatConfigSource struct {
+	Id        string   `bson:"id,omitempty" json:"id,omitempty"`
+	Name      string   `bson:"name,omitempty" json:"name,omitempty"`
+	CusNum    string   `bson:"cusNum,omitempty" json:"cusNum,omitempty"`
+	Thumbnail *OssType `bson:"thumbnail,omitempty" json:"thumbnail,omitempty"`
+
+	UvTransform    *MaterailUv `bson:"uv,omitempty" json:"uv,omitempty"`
+	CusUvTransform *MaterailUv `bson:"cusUv,omitempty" json:"cusUv,omitempty"`
+	CullFace       string      `bson:"cullFace" json:"cullFace,omitempty"`
+
+	Diamond   *MatDiamond                 `bson:"diamond" json:"diamond,omitempty"`
+	Albedo    *MatTextureColor            `bson:"albedo" json:"albedo,omitempty"`
+	Roughness *MatTextureFactor           `bson:"roughness" json:"roughness,omitempty"`
+	Metalness *MatTextureFactor           `bson:"metalness" json:"metalness,omitempty"`
+	Normal    *MatNormal                  `bson:"normal" json:"normal,omitempty"`
+	Opacity   *MatTextureFactorWithEnable `bson:"opacity" json:"opacity,omitempty"`
+
+	Displace *MatTextureFactorWithEnable `bson:"displace,omitempty" json:"displace,omitempty"` //置换
+	Diffuse  *MatTextureColor            `bson:"diffuse,omitempty" json:"diffuse,omitempty"`
+	Specular *MatTextureColor            `bson:"specular" json:"specular,omitempty"`
+	Gloss    *MatTextureFactor           `bson:"gloss,omitempty" json:"gloss,omitempty"` //光泽贴图
+
+	Version   int    `bson:"version,omitempty" json:"version,omitempty"`
+	Type      string `bson:"type,omitempty" json:"type,omitempty"`           //高光流 金属流(默认) meta spec
+	UvMap     string `bson:"uvMap,omitempty" json:"uvMap,omitempty"`         //uv包裹 box、uv
+	ClassType string `bson:"classType,omitempty" json:"classType,omitempty"` //pbr(默认) / diamond
+
+	//工艺相关材质球
+	TechMaterial *TechMatConfig    `bson:"techMaterial" json:"techMaterial,omitempty"`
+	Images       []*ComponentImage `bson:"images" json:"images,omitempty"`
+
+	//自定义换色材质
+	ColorMatch   *CusColorParams `bson:"colorMatch" json:"colorMatch,omitempty"`     //自定义颜色参数
+	GenerateType string          `bson:"generateType" json:"generateType,omitempty"` //面临生成类型 cuscolor(换色) / tech(工艺)
+
+	//其他数据
+
+	Anisotropic           float32 `bson:"anisotropic" json:"anisotropic"`
+	AnisotropicRotation   float32 `bson:"anisotropicRotation" json:"anisotropicRotation"`
+	Clearcoat             float32 `bson:"clearcoat" json:"clearcoat"`
+	ClearcoatRoughness    float32 `bson:"clearcoatRoughness" json:"clearcoatRoughness"`
+	Emission              Vect3   `bson:"emission" json:"emission"`
+	EmissionStrength      float32 `bson:"emissionStrength" json:"emissionStrength"`
+	Ior                   float32 `bson:"ior" json:"ior"`
+	Sheen                 float32 `bson:"sheen" json:"sheen"`
+	SheenTint             float32 `bson:"sheenTint" json:"sheenTint"`
+	SpecularTint          float32 `bson:"specularTint" json:"specularTint"`
+	Subsurface            float32 `bson:"subsurface" json:"subsurface"`
+	SubsurfaceColor       Vect3   `bson:"subsurfaceColor" json:"subsurfaceColor"`
+	SubsurfaceRadius      Vect3   `bson:"subsurfaceRadius" json:"subsurfaceRadius"`
+	Transmission          float32 `bson:"transmission" json:"transmission"`
+	TransmissionRoughness float32 `bson:"transmissionRoughness" json:"transmissionRoughness"`
+	Reflectivity          float32 `bson:"reflectivity" json:"reflectivity"`
+
+	Queenter interface{} `bson:"queenter,omitempty" json:"queenter,omitempty"`
+	UserData interface{} `bson:"userData,omitempty" json:"userData,omitempty"` //指定要数据放置的字段
+}
+
+func (m *MatConfigSource) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if m.Thumbnail != nil {
+		m.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if m.Albedo != nil {
+		m.Albedo.UpdateSourceUrl(handler)
+	}
+	if m.Roughness != nil {
+		m.Roughness.UpdateSourceUrl(handler)
+	}
+	if m.Metalness != nil {
+		m.Metalness.UpdateSourceUrl(handler)
+	}
+	if m.Normal != nil {
+		m.Normal.UpdateSourceUrl(handler)
+	}
+	if m.Opacity != nil {
+		m.Opacity.UpdateSourceUrl(handler)
+	}
+
+	if m.Displace != nil {
+		m.Displace.UpdateSourceUrl(handler)
+	}
+	if m.Diffuse != nil {
+		m.Diffuse.UpdateSourceUrl(handler)
+	}
+
+	if m.Specular != nil {
+		m.Specular.UpdateSourceUrl(handler)
+	}
+	if m.Gloss != nil {
+		m.Gloss.UpdateSourceUrl(handler)
+	}
+	if m.TechMaterial != nil {
+		m.TechMaterial.UpdateSourceUrl(handler)
+	}
+
+	for _, im := range m.Images {
+		if im.Image != nil {
+			im.Image.UpdateSourceUrl(handler)
+		}
+	}
+	if m.ColorMatch != nil && m.ColorMatch.BaseAlbedo != nil {
+		m.ColorMatch.BaseAlbedo.UpdateSourceUrl(handler)
+	}
+}
+
+type AssetMatGroupSource struct {
+	Version    string             `bson:"version,omitempty" json:"version"`
+	ColorCards []*MatConfigSource `bson:"colorCards,omitempty" json:"colorCards"`
+}
+
+type AssetMatGroup struct {
+	Id       primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId   primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`
+	OwnerId  primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`
+	UserInfo *AssetUserInfo     `bson:"userInfo,omitempty" json:"userInfo,omitempty" complex:"true"` //用户信息
+
+	OwnerType string `bson:"ownerType,omitempty" json:"ownerType,omitempty"`
+	AssetType string `bson:"assetType,omitempty" json:"assetType,omitempty"`
+
+	Name   string `bson:"name,omitempty" json:"name"`     //名字
+	CusNum string `bson:"cusNum,omitempty" json:"cusNum"` //编号
+
+	CreateTime    time.Time `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime    time.Time `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail     *OssType  `bson:"thumbnail,omitempty" json:"thumbnail" complex:"true"`
+	Categories    []string  `bson:"categories,omitempty" json:"categories" complex:"true"`       //所属分类Id
+	CusCategories []string  `bson:"cusCategories,omitempty" json:"cusCategories" complex:"true"` //所属分类Id
+	TaskId        string    `bson:"taskId,omitempty" json:"taskId"`                              //资产处理id
+	AssetState    int       `bson:"assetState,omitempty" json:"assetState"`
+	Enable        *bool     `bson:"enable,omitempty" json:"enable"` //是否有效!
+
+	DotQueen   string               `bson:"dotQueen,omitempty" json:"dotQueen,omitempty"`
+	ViewMode   string               `bson:"viewMode,omitempty" json:"viewMode,omitempty"`
+	Source     *AssetMatGroupSource `bson:"source,omitempty" json:"source,omitempty" complex:"true"`
+	TransTask  *TransTask           `bson:"transTask,omitempty" json:"transTask" complex:"true"`
+	ShadowTask *ShadowTask          `bson:"shadowTask,omitempty" json:"shadowTask"`
+	Config     string               `bson:"config,omitempty" json:"config,omitempty"`
+	Queenter   interface{}          `bson:"queenter,omitempty" json:"queenter,omitempty" complex:"true"` //自定义数据
+	UserData   interface{}          `bson:"userData,omitempty" json:"userData,omitempty" complex:"true"` //自定义数据
+
+	QiYeInfo *QiYeInfo `bson:"qiYeInfo,omitempty" json:"qiYeInfo"`
+}
+
+func (s *AssetMatGroup) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if s.UserInfo != nil && s.UserInfo.Thumbnail != nil {
+		s.UserInfo.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.Thumbnail != nil {
+		s.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.Source != nil && s.Source.ColorCards != nil {
+		for _, c := range s.Source.ColorCards {
+			c.UpdateSourceUrl(handler)
+		}
+	}
+}
+
+func (s *AssetMatGroup) SetIdEmpty() {
+	s.Id = primitive.NilObjectID
+}
+func (s *AssetMatGroup) ResetCreateTime() {
+	s.CreateTime = time.Now()
+	s.UpdateTime = time.Now()
+}
+func (s *AssetMatGroup) SetOwner(id string, otype string) {
+	s.OwnerId, _ = primitive.ObjectIDFromHex(id)
+	s.OwnerType = otype
+}
+
+func (s *AssetMatGroup) SetAssetType(otype string) {
+	s.AssetType = otype
+}
+
+func (s *AssetMatGroup) SetUserInfo(id string, info *AssetUserInfo) {
+	s.UserId, _ = primitive.ObjectIDFromHex(id)
+	s.UserInfo = info
+}
+
+type AssetMat struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId    primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`
+	OwnerId   primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`
+	OwnerType string             `bson:"ownerType,omitempty" json:"ownerType,omitempty"`
+	AssetType string             `bson:"assetType,omitempty" json:"assetType,omitempty"`
+
+	UserInfo *AssetUserInfo `bson:"userInfo,omitempty" json:"userInfo,omitempty" complex:"true"` //用户信息
+
+	Name   string `bson:"name,omitempty" json:"name"`     //名字
+	CusNum string `bson:"cusNum,omitempty" json:"cusNum"` //编号
+
+	CreateTime    time.Time `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime    time.Time `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail     *OssType  `bson:"thumbnail,omitempty" json:"thumbnail" complex:"true"`
+	Categories    []string  `bson:"categories,omitempty" json:"categories" complex:"true"`       //所属分类Id
+	CusCategories []string  `bson:"cusCategories,omitempty" json:"cusCategories" complex:"true"` //所属分类Id
+	TaskId        string    `bson:"taskId,omitempty" json:"taskId"`                              //资产处理id
+	AssetState    int       `bson:"assetState,omitempty" json:"assetState"`
+	Enable        *bool     `bson:"enable,omitempty" json:"enable"` //是否有效!
+
+	DotQueen   string           `bson:"dotQueen,omitempty" json:"dotQueen,omitempty"`
+	ViewMode   string           `bson:"viewMode,omitempty" json:"viewMode,omitempty"`
+	Source     *MatConfigSource `bson:"source,omitempty" json:"source,omitempty" complex:"true"`
+	TransTask  *TransTask       `bson:"transTask,omitempty" json:"transTask" complex:"true"`
+	ShadowTask *ShadowTask      `bson:"shadowTask,omitempty" json:"shadowTask"`
+	Config     string           `bson:"config,omitempty" json:"config,omitempty"`
+
+	UserData interface{} `bson:"userData,omitempty" json:"userData,omitempty" complex:"true"` //自定义数据
+}
+
+func (s *AssetMat) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if s.UserInfo != nil && s.UserInfo.Thumbnail != nil {
+		s.UserInfo.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.Thumbnail != nil {
+		s.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.Source != nil {
+		s.Source.UpdateSourceUrl(handler)
+	}
+}
+
+func (s *AssetMat) SetIdEmpty() {
+	s.Id = primitive.NilObjectID
+}
+func (s *AssetMat) ResetCreateTime() {
+	s.CreateTime = time.Now()
+	s.UpdateTime = time.Now()
+}
+func (s *AssetMat) SetOwner(id string, otype string) {
+	s.OwnerId, _ = primitive.ObjectIDFromHex(id)
+	s.OwnerType = otype
+}
+
+func (s *AssetMat) SetAssetType(otype string) {
+	s.AssetType = otype
+}
+
+func (s *AssetMat) SetUserInfo(id string, info *AssetUserInfo) {
+	s.UserId, _ = primitive.ObjectIDFromHex(id)
+	s.UserInfo = info
+}
+
+func (m *AssetMat) CopyFromSpecMat(sm *SpecialMat) {
+	m.Name = sm.Name
+	m.CreateTime = sm.CreateTime
+	m.UpdateTime = sm.CreateTime
+	m.Thumbnail = sm.Thumbnail
+	m.UserData = map[string]string{"category": sm.Category}
+	m.AssetState = AssetState_Succ
+	trueV := true
+	m.Enable = &trueV
+	m.Source = &MatConfigSource{
+		Version:   1,
+		Id:        sm.Id.Hex(),
+		Name:      sm.Name,
+		Thumbnail: sm.Thumbnail,
+
+		Type:        sm.Type,
+		UvTransform: sm.Uvtransform,
+		Albedo:      &MatTextureColor{},
+		Roughness:   &MatTextureFactor{},
+		Metalness:   &MatTextureFactor{},
+		Opacity:     &MatTextureFactorWithEnable{Enable: false},
+		Displace:    &MatTextureFactorWithEnable{Enable: false},
+		Diffuse:     &MatTextureColor{},
+		Specular:    &MatTextureColor{},
+		Gloss:       &MatTextureFactor{},
+	}
+
+	if len(m.Source.Type) < 1 {
+		m.Source.Type = "meta"
+	}
+
+	falseV := false
+
+	if sm.BaseMap != nil {
+		if len(sm.BaseMap.Url) > 0 {
+			m.Source.Albedo.UseTexture = &trueV
+			m.Source.Albedo.Texture = sm.BaseMap
+		} else {
+			m.Source.Albedo.UseTexture = &falseV
+			m.Source.Albedo.Color = sm.BaseColor
+		}
+	}
+
+	if sm.NormalMap != nil {
+		m.Source.Normal = &MatNormal{
+			UseTexture: &trueV,
+			Texture:    sm.NormalMap,
+			FlipY:      &falseV,
+		}
+	}
+	if sm.RoughMap != nil {
+		if len(sm.BaseMap.Url) > 0 {
+			m.Source.Roughness.UseTexture = &trueV
+			m.Source.Roughness.Texture = sm.RoughMap
+		} else {
+			m.Source.Roughness.UseTexture = &falseV
+			if sm.RoughFactor != nil {
+				v := float32(*sm.RoughFactor)
+				m.Source.Roughness.Factor = &v
+			} else {
+				v := float32(0.5)
+				m.Source.Roughness.Factor = &v
+			}
+
+		}
+	}
+	if sm.MetalMap != nil {
+		if len(sm.MetalMap.Url) > 0 {
+			m.Source.Metalness.UseTexture = &trueV
+			m.Source.Metalness.Texture = sm.MetalMap
+		} else {
+			m.Source.Metalness.UseTexture = &falseV
+			if sm.MetalFactor != nil {
+				v := float32(*sm.MetalFactor)
+				m.Source.Metalness.Factor = &v
+			} else {
+				v := float32(0.5)
+				m.Source.Metalness.Factor = &v
+			}
+		}
+	}
+	if sm.OpacMap != nil {
+		if len(sm.OpacMap.Url) > 0 {
+			m.Source.Opacity.UseTexture = &trueV
+			m.Source.Opacity.Texture = sm.OpacMap
+			m.Source.Opacity.Enable = true
+			if sm.OpacFactor != nil {
+				v := float32(*sm.OpacFactor)
+				m.Source.Opacity.Factor = &v
+			} else {
+				v := float32(1.0)
+				m.Source.Opacity.Factor = &v
+			}
+
+		} else if sm.OpacFactor != nil {
+			if math.Abs(*sm.OpacFactor-1.0) < 0.001 {
+				m.Source.Opacity.UseTexture = &falseV
+				m.Source.Opacity.Enable = true
+				v := float32(*sm.OpacFactor)
+				m.Source.Opacity.Factor = &v
+			}
+		}
+	}
+
+	if sm.DisplaceMap != nil {
+		if len(sm.DisplaceMap.Url) > 0 {
+			m.Source.Displace.UseTexture = &trueV
+			m.Source.Displace.Texture = sm.DisplaceMap
+			m.Source.Displace.Enable = true
+		} else if sm.DisplaceFactor != nil {
+			v := float32(*sm.DisplaceFactor)
+			m.Source.Displace.Factor = &v
+		}
+	}
+
+	if sm.DiffuseMap != nil {
+		if len(sm.DiffuseMap.Url) > 0 {
+			m.Source.Diffuse.UseTexture = &trueV
+			m.Source.Diffuse.Texture = sm.DiffuseMap
+		} else {
+			m.Source.Diffuse.UseTexture = &falseV
+			m.Source.Diffuse.Color = sm.DiffuseColor
+		}
+	}
+	if sm.SpecMap != nil {
+		if len(sm.SpecMap.Url) > 0 {
+			m.Source.Specular.UseTexture = &trueV
+			m.Source.Specular.Texture = sm.SpecMap
+		} else {
+			m.Source.Specular.UseTexture = &falseV
+			m.Source.Specular.Color = sm.SpecColor
+		}
+	}
+	if sm.GlossMap != nil {
+		if len(sm.GlossMap.Url) > 0 {
+			m.Source.Gloss.UseTexture = &trueV
+			m.Source.Gloss.Texture = sm.GlossMap
+		} else {
+			m.Source.Gloss.UseTexture = &falseV
+			if sm.GlossFactor != nil {
+				v := float32(*sm.GlossFactor)
+				m.Source.Gloss.Factor = &v
+			}
+		}
+	}
+	m.Source.UvMap = "box"
+}
+
+func (m *AssetMat) CopyFromLineMat(sm *LineMat) {
+	m.Name = sm.Name
+	m.CreateTime = sm.CreateTime
+	m.UpdateTime = sm.CreateTime
+	m.Thumbnail = sm.Thumbnail
+	m.UserData = map[string]string{"category": sm.Category}
+	m.AssetState = AssetState_Succ
+	trueV := true
+	m.Enable = &trueV
+	m.Source = &MatConfigSource{
+		Version:   1,
+		Id:        sm.Id.Hex(),
+		Name:      sm.Name,
+		Thumbnail: sm.Thumbnail,
+
+		Type:        sm.Type,
+		UvTransform: sm.Uvtransform,
+		Albedo:      &MatTextureColor{},
+		Roughness:   &MatTextureFactor{},
+		Metalness:   &MatTextureFactor{},
+		Opacity:     &MatTextureFactorWithEnable{Enable: false},
+		Displace:    &MatTextureFactorWithEnable{Enable: false},
+		Diffuse:     &MatTextureColor{},
+		Specular:    &MatTextureColor{},
+		Gloss:       &MatTextureFactor{},
+	}
+
+	if len(m.Source.Type) < 1 {
+		m.Source.Type = "meta"
+	}
+
+	falseV := false
+
+	if sm.BaseMap != nil {
+		if len(sm.BaseMap.Url) > 0 {
+			m.Source.Albedo.UseTexture = &trueV
+			m.Source.Albedo.Texture = sm.BaseMap
+		} else {
+			m.Source.Albedo.UseTexture = &falseV
+			m.Source.Albedo.Color = sm.BaseColor
+		}
+	}
+
+	if sm.NormalMap != nil {
+		m.Source.Normal = &MatNormal{
+			UseTexture: &trueV,
+			Texture:    sm.NormalMap,
+			FlipY:      &falseV,
+		}
+	}
+	if sm.RoughMap != nil {
+		if len(sm.BaseMap.Url) > 0 {
+			m.Source.Roughness.UseTexture = &trueV
+			m.Source.Roughness.Texture = sm.RoughMap
+		} else {
+			m.Source.Roughness.UseTexture = &falseV
+			if sm.RoughFactor != nil {
+				v := float32(*sm.RoughFactor)
+				m.Source.Roughness.Factor = &v
+			} else {
+				v := float32(0.5)
+				m.Source.Roughness.Factor = &v
+			}
+
+		}
+	}
+	if sm.MetalMap != nil {
+		if len(sm.MetalMap.Url) > 0 {
+			m.Source.Metalness.UseTexture = &trueV
+			m.Source.Metalness.Texture = sm.MetalMap
+		} else {
+			m.Source.Metalness.UseTexture = &falseV
+			if sm.MetalFactor != nil {
+				v := float32(*sm.MetalFactor)
+				m.Source.Metalness.Factor = &v
+			} else {
+				v := float32(0.5)
+				m.Source.Metalness.Factor = &v
+			}
+		}
+	}
+	if sm.OpacMap != nil {
+		if len(sm.OpacMap.Url) > 0 {
+			m.Source.Opacity.UseTexture = &trueV
+			m.Source.Opacity.Texture = sm.OpacMap
+			m.Source.Opacity.Enable = true
+			if sm.OpacFactor != nil {
+				v := float32(*sm.OpacFactor)
+				m.Source.Opacity.Factor = &v
+			} else {
+				v := float32(1.0)
+				m.Source.Opacity.Factor = &v
+			}
+
+		} else if sm.OpacFactor != nil {
+			if math.Abs(*sm.OpacFactor-1.0) < 0.001 {
+				m.Source.Opacity.UseTexture = &falseV
+				m.Source.Opacity.Enable = true
+				v := float32(*sm.OpacFactor)
+				m.Source.Opacity.Factor = &v
+			}
+		}
+	}
+
+	if sm.DisplaceMap != nil {
+		if len(sm.DisplaceMap.Url) > 0 {
+			m.Source.Displace.UseTexture = &trueV
+			m.Source.Displace.Texture = sm.DisplaceMap
+			m.Source.Displace.Enable = true
+		} else if sm.DisplaceFactor != nil {
+			v := float32(*sm.DisplaceFactor)
+			m.Source.Displace.Factor = &v
+		}
+	}
+
+	if sm.DiffuseMap != nil {
+		if len(sm.DiffuseMap.Url) > 0 {
+			m.Source.Diffuse.UseTexture = &trueV
+			m.Source.Diffuse.Texture = sm.DiffuseMap
+		} else {
+			m.Source.Diffuse.UseTexture = &falseV
+			m.Source.Diffuse.Color = sm.DiffuseColor
+		}
+	}
+	if sm.SpecMap != nil {
+		if len(sm.SpecMap.Url) > 0 {
+			m.Source.Specular.UseTexture = &trueV
+			m.Source.Specular.Texture = sm.SpecMap
+		} else {
+			m.Source.Specular.UseTexture = &falseV
+			m.Source.Specular.Color = sm.SpecColor
+		}
+	}
+	if sm.GlossMap != nil {
+		if len(sm.GlossMap.Url) > 0 {
+			m.Source.Gloss.UseTexture = &trueV
+			m.Source.Gloss.Texture = sm.GlossMap
+		} else {
+			m.Source.Gloss.UseTexture = &falseV
+			if sm.GlossFactor != nil {
+				v := float32(*sm.GlossFactor)
+				m.Source.Gloss.Factor = &v
+			}
+		}
+	}
+	m.Source.UvMap = "box"
+}
+
+func (m *AssetMat) CopyFromMatConf(sm *MaterialConf) {
+	m.Name = sm.Name
+	m.CreateTime = time.Now()
+	m.UpdateTime = time.Now()
+	m.Thumbnail = sm.Thumbnail
+	m.CusNum = sm.CusNum
+	m.AssetState = AssetState_Succ
+	trueV := true
+	m.Enable = &trueV
+	m.Source = &MatConfigSource{
+		Version:        1,
+		Id:             sm.MatId.Hex(),
+		CusNum:         sm.CusNum,
+		Name:           sm.Name,
+		Thumbnail:      sm.Thumbnail,
+		UserData:       sm.UserData,
+		Type:           sm.Type,
+		UvTransform:    sm.Uvtransform,
+		CusUvTransform: sm.CusUvTransform,
+		Albedo:         &MatTextureColor{},
+		Roughness:      &MatTextureFactor{},
+		Metalness:      &MatTextureFactor{},
+		Opacity:        &MatTextureFactorWithEnable{Enable: false},
+		Displace:       &MatTextureFactorWithEnable{Enable: false},
+		Diffuse:        &MatTextureColor{},
+		Specular:       &MatTextureColor{},
+		Gloss:          &MatTextureFactor{},
+	}
+
+	if len(m.Source.Type) < 1 {
+		m.Source.Type = "meta"
+	}
+
+	falseV := false
+
+	if sm.BaseMap != nil {
+		if len(sm.BaseMap.Url) > 0 {
+			m.Source.Albedo.UseTexture = &trueV
+			m.Source.Albedo.Texture = sm.BaseMap
+		} else {
+			m.Source.Albedo.UseTexture = &falseV
+			m.Source.Albedo.Color = sm.BaseColor
+		}
+	}
+
+	if sm.NormalMap != nil {
+		m.Source.Normal = &MatNormal{
+			UseTexture: &trueV,
+			Texture:    sm.NormalMap,
+			FlipY:      &falseV,
+		}
+	}
+	if sm.RoughMap != nil {
+		if len(sm.BaseMap.Url) > 0 {
+			m.Source.Roughness.UseTexture = &trueV
+			m.Source.Roughness.Texture = sm.RoughMap
+		} else {
+			m.Source.Roughness.UseTexture = &falseV
+			if sm.RoughFactor != nil {
+				v := float32(*sm.RoughFactor)
+				m.Source.Roughness.Factor = &v
+			} else {
+				v := float32(0.5)
+				m.Source.Roughness.Factor = &v
+			}
+
+		}
+	}
+	if sm.MetalMap != nil {
+		if len(sm.MetalMap.Url) > 0 {
+			m.Source.Metalness.UseTexture = &trueV
+			m.Source.Metalness.Texture = sm.MetalMap
+		} else {
+			m.Source.Metalness.UseTexture = &falseV
+			if sm.MetalFactor != nil {
+				v := float32(*sm.MetalFactor)
+				m.Source.Metalness.Factor = &v
+			} else {
+				v := float32(0.5)
+				m.Source.Metalness.Factor = &v
+			}
+		}
+	}
+	if sm.OpacMap != nil {
+		if len(sm.OpacMap.Url) > 0 {
+			m.Source.Opacity.UseTexture = &trueV
+			m.Source.Opacity.Texture = sm.OpacMap
+			m.Source.Opacity.Enable = true
+			if sm.OpacFactor != nil {
+				v := float32(*sm.OpacFactor)
+				m.Source.Opacity.Factor = &v
+			} else {
+				v := float32(1.0)
+				m.Source.Opacity.Factor = &v
+			}
+
+		} else if sm.OpacFactor != nil {
+			if math.Abs(*sm.OpacFactor-1.0) < 0.001 {
+				m.Source.Opacity.UseTexture = &falseV
+				m.Source.Opacity.Enable = true
+				v := float32(*sm.OpacFactor)
+				m.Source.Opacity.Factor = &v
+			}
+		}
+	}
+
+	if sm.DisplaceMap != nil {
+		if len(sm.DisplaceMap.Url) > 0 {
+			m.Source.Displace.UseTexture = &trueV
+			m.Source.Displace.Texture = sm.DisplaceMap
+			m.Source.Displace.Enable = true
+		} else if sm.DisplaceFactor != nil {
+			v := float32(*sm.DisplaceFactor)
+			m.Source.Displace.Factor = &v
+		}
+	}
+
+	if sm.DiffuseMap != nil {
+		if len(sm.DiffuseMap.Url) > 0 {
+			m.Source.Diffuse.UseTexture = &trueV
+			m.Source.Diffuse.Texture = sm.DiffuseMap
+		} else {
+			m.Source.Diffuse.UseTexture = &falseV
+			m.Source.Diffuse.Color = sm.DiffuseColor
+		}
+	}
+	if sm.SpecMap != nil {
+		if len(sm.SpecMap.Url) > 0 {
+			m.Source.Specular.UseTexture = &trueV
+			m.Source.Specular.Texture = sm.SpecMap
+		} else {
+			m.Source.Specular.UseTexture = &falseV
+			m.Source.Specular.Color = sm.SpecColor
+		}
+	}
+	if sm.GlossMap != nil {
+		if len(sm.GlossMap.Url) > 0 {
+			m.Source.Gloss.UseTexture = &trueV
+			m.Source.Gloss.Texture = sm.GlossMap
+		} else {
+			m.Source.Gloss.UseTexture = &falseV
+			if sm.GlossFactor != nil {
+				v := float32(*sm.GlossFactor)
+				m.Source.Gloss.Factor = &v
+			}
+		}
+	}
+	m.Source.UvMap = "box"
+}
+
+type AssetImage struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId    primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`                     //用户Id
+	UserInfo  *AssetUserInfo     `bson:"userInfo,omitempty" json:"userInfo,omitempty"  complex:"true"` //用户Id
+	OwnerId   primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`
+	OwnerType string             `bson:"ownerType,omitempty" json:"ownerType,omitempty"` //user team company
+	AssetType string             `bson:"assetType,omitempty" json:"assetType,omitempty"` //user team company
+
+	Name   string `bson:"name,omitempty" json:"name"`
+	CusNum string `bson:"cusNum,omitempty" json:"cusNum,omitempty"` //型号
+
+	CreateTime    time.Time `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime    time.Time `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail     *OssType  `bson:"thumbnail,omitempty" json:"thumbnail"  complex:"true"`
+	Categories    []string  `bson:"categories,omitempty" json:"categories"  complex:"true"`       //所属分类Id
+	CusCategories []string  `bson:"cusCategories,omitempty" json:"cusCategories"  complex:"true"` //所属分类Id
+
+	TaskId     string `bson:"taskId,omitempty" json:"taskId"` //资产处理id
+	AssetState int    `bson:"assetState,omitempty" json:"assetState"`
+	Enable     *bool  `bson:"enable,omitempty" json:"enable"` //是否有效!
+
+	DotQueen   string       `bson:"dotQueen,omitempty" json:"dotQueen,omitempty"`
+	ViewMode   string       `bson:"viewMode,omitempty" json:"viewMode,omitempty"`
+	Source     *ImageSource `bson:"source,omitempty" json:"source,omitempty" complex:"true"`
+	TransTask  *TransTask   `bson:"transTask,omitempty" json:"transTask" complex:"true"`
+	ShadowTask *ShadowTask  `bson:"shadowTask,omitempty" json:"shadowTask"`
+	Config     string       `bson:"config,omitempty" json:"config,omitempty"`
+	UserData   interface{}  `bson:"userData,omitempty" json:"userData,omitempty" complex:"true"` //用户数据
+}
+
+func (s *AssetImage) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if s.Thumbnail != nil {
+		s.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if s.Source != nil && s.Source.File != nil {
+		s.Source.File.UpdateSourceUrl(handler)
+	}
+}
+
+func (s *AssetImage) SetIdEmpty() {
+	s.Id = primitive.NilObjectID
+}
+func (s *AssetImage) ResetCreateTime() {
+	s.CreateTime = time.Now()
+	s.UpdateTime = time.Now()
+}
+func (s *AssetImage) SetOwner(id string, otype string) {
+	s.OwnerId, _ = primitive.ObjectIDFromHex(id)
+	s.OwnerType = otype
+}
+
+func (s *AssetImage) SetAssetType(otype string) {
+	s.AssetType = otype
+}
+func (s *AssetImage) SetUserInfo(id string, info *AssetUserInfo) {
+	s.UserId, _ = primitive.ObjectIDFromHex(id)
+	s.UserInfo = info
+}
+
+type AssetUserInfo struct {
+	Name      string   `bson:"name,omitempty" json:"name"`
+	Thumbnail *OssType `bson:"thumbnail,omitempty" json:"thumbnail"`
+}
+
+type AssetPackage struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	OwnerId   primitive.ObjectID `bson:"ownerId,omitempty" json:"ownerId,omitempty"`                  //userId teamId companyId
+	OwnerType string             `bson:"ownerType,omitempty" json:"ownerType,omitempty"`              //user team company
+	UserId    primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`                    //用户Id
+	UserInfo  *AssetUserInfo     `bson:"userInfo,omitempty" json:"userInfo,omitempty" complex:"true"` //用户Id
+
+	AssetType string `bson:"assetType,omitempty" json:"assetType,omitempty"` //业务类型 shoe sole heel decorate
+	Name      string `bson:"name,omitempty" json:"name"`
+	CusNum    string `bson:"cusNum,omitempty" json:"cusNum,omitempty"` //型号
+
+	CreateTime    time.Time `bson:"createTime,omitempty" json:"createTime"`
+	UpdateTime    time.Time `bson:"updateTime,omitempty" json:"updateTime"`
+	Thumbnail     *OssType  `bson:"thumbnail,omitempty" json:"thumbnail" complex:"true"`
+	Categories    []string  `bson:"categories,omitempty" json:"categories" complex:"true"`       //所属分类Id
+	CusCategories []string  `bson:"cusCategories,omitempty" json:"cusCategories" complex:"true"` //所属分类Id
+
+	TaskId string `bson:"taskId,omitempty" json:"taskId"` //资产处理id
+
+	AssetState int   `bson:"assetState,omitempty" json:"assetState"`
+	Enable     *bool `bson:"enable,omitempty" json:"enable"` //是否有效!
+
+	DotQueen   string                `bson:"dotQueen,omitempty" json:"dotQueen,omitempty"`
+	ViewMode   string                `bson:"viewMode,omitempty" json:"viewMode,omitempty"`
+	Source     *Queen3dPackageSource `bson:"source,omitempty" json:"source,omitempty"`
+	TransTask  *TransTask            `bson:"transTask,omitempty" json:"transTask" complex:"true"`
+	ShadowTask *ShadowTask           `bson:"shadowTask,omitempty" json:"shadowTask"`
+	Config     string                `bson:"config,omitempty" json:"config,omitempty"`
+	UserData   interface{}           `bson:"userData,omitempty" json:"userData,omitempty" complex:"true"` //用户数据
+
+	// !11.06添加企业信息
+	QiYeInfo *QiYeInfo `bson:"qiYeInfo,omitempty" json:"qiYeInfo"`
+}
+
+type QiYeInfo struct {
+	Id      primitive.ObjectID `bson:"id,omitempty" json:"id"`
+	ChildId primitive.ObjectID `bson:"childId,omitempty" json:"childId"`
+	Name    string             `bson:"name,omitempty" json:"name"`
+}
+
+func (s *AssetPackage) SetIdEmpty() {
+	s.Id = primitive.NilObjectID
+}
+func (s *AssetPackage) ResetCreateTime() {
+	s.CreateTime = time.Now()
+	s.UpdateTime = time.Now()
+}
+func (s *AssetPackage) SetOwner(id string, otype string) {
+	s.OwnerId, _ = primitive.ObjectIDFromHex(id)
+	s.OwnerType = otype
+}
+
+func (s *AssetPackage) SetAssetType(otype string) {
+	s.AssetType = otype
+}
+
+func (s *AssetPackage) SetUserInfo(id string, info *AssetUserInfo) {
+	s.UserId, _ = primitive.ObjectIDFromHex(id)
+	s.UserInfo = info
+}
+
+type UpdateUrlHandler func(string) string
+
+func (s *AssetPackage) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if s.Thumbnail != nil {
+		s.Thumbnail.Url = handler(s.Thumbnail.Url)
+	}
+	if s.Source == nil {
+		return
+	}
+
+	source := s.Source
+	for _, g := range source.Geoms {
+		g.UpdateSourceUrl(handler)
+	}
+	for _, e := range source.Env3ds {
+		e.UpdateSourceUrl(handler)
+	}
+	for _, p := range source.Products {
+		p.UpdateSourceUrl(handler)
+	}
+	for _, s := range source.Scenes {
+		s.UpdateSourceUrl(handler)
+	}
+	for _, m := range source.Mats {
+		m.UpdateSourceUrl(handler)
+	}
+}

+ 136 - 0
sku3d/comm/design.go

@@ -0,0 +1,136 @@
+package comm
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+const (
+	MeshState_SetFile         = 0  //create
+	MeshState_SetOsgjs        = 1  //模型转换到osgjs了,可以用于webgl显示了
+	MeshState_SetOsgjsFailded = -1 //模型转换到osgjs了,可以用于webgl显示了
+	MeshState_SetShadow       = 2  //阴影已生成
+)
+
+type Evn3dOption struct {
+	Rotation float64 `bson:"rotation" json:"rotation"`
+	Exposure float64 `bson:"exposure" json:"exposure"`
+}
+
+type DesingSceneEnv3d struct {
+	Id        primitive.ObjectID `bson:"id,omitempty" json:"id"`
+	Name      string             `bson:"name,omitempty" json:"name"`
+	Thumbnail *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	HDR       *OssType           `bson:"hdr,omitempty" json:"hdr"`
+	Config    *Evn3dHdrConf      `bson:"config,omitempty" json:"config"`
+
+	Options *Evn3dOption `bson:"options,omitempty" json:"options"`
+	ToneMap *ToneMap     `bson:"toneMap,omitempty" json:"toneMap"`
+}
+
+type Uvtransform struct {
+	Scale      float32 `bson:"scale" json:"scale"`
+	Rotate     float32 `bson:"rotate" json:"rotate"`
+	OffsetX    float32 `bson:"offsetX" json:"offsetX"`
+	OffsetY    float32 `bson:"offsetY" json:"offsetY"`
+	FinalScale float32 `bson:"finalScale" json:"finalScale"`
+}
+
+type ImageMatConf struct {
+	Image *OssType           `bson:"image,omitempty" json:"image"`
+	Id    primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+}
+
+type SceneComponent struct {
+	Name        string              `bson:"name,omitempty" json:"name"`
+	FabricScope string              `bson:"fabricScope,omitempty" json:"fabricScope,omitempty"` //面料使用的库
+	FabricId    *primitive.ObjectID `bson:"fabricId,omitempty" json:"fabricId,omitempty"`
+	CardId      *primitive.ObjectID `bson:"cardId,omitempty" json:"cardId,omitempty"`
+	MatType     uint8               `bson:"matType,omitempty" json:"matType"`
+	Imagemat    *ImageMatConf       `bson:"imageMat,omitempty" json:"imageMat,omitempty"`
+	Uvtransform *Uvtransform        `bson:"transform,omitempty" json:"transform"`
+	Color       *Vect3              `bson:"color,omitempty" json:"color"`
+	GroupId     uint64              `bson:"groupId,omitempty" json:"groupId"`
+	Visible     *bool               `bson:"visible,omitempty" json:"visible"`
+}
+
+type SceneBackground struct {
+	Id    string   `bson:"_id,omitempty" json:"_id"`
+	Image *OssType `bson:"image,omitempty" json:"image"`
+	Type  int32    `bson:"type,omitempty" json:"type"`
+	Color *Vect3   `bson:"color,omitempty" json:"color"`
+}
+
+type ModelTransform struct {
+	Pos      Vect3 `bson:"pos,omitempty" json:"pos"`
+	Scale    Vect3 `bson:"scale,omitempty" json:"scale"`
+	Rotation Vect4 `bson:"rotation,omitempty" json:"rotation"`
+}
+
+type SceneModels struct {
+	Id         string              `bson:"id,omitempty" json:"id"`
+	MeshId     *primitive.ObjectID `bson:"meshId,omitempty" json:"meshId,omitempty"`
+	Mesh       *DesignMesh         `bson:"mesh,omitempty" json:"mesh,omitempty"`
+	Type       string              `bson:"type,omitempty" json:"type"`
+	TypeId     *primitive.ObjectID `bson:"typeId,omitempty" json:"typeId,omitempty"`
+	Transform  ModelTransform      `bson:"transform,omitempty" json:"transform,omitempty"`
+	Components []*SceneComponent   `bson:"components,omitempty" json:"components,omitempty"`
+}
+
+type SceneCamera struct {
+	Id       string `bson:"id,omitempty" json:"id"`
+	Position *Vect3 `bson:"position,omitempty" json:"position"`
+	Target   *Vect3 `bson:"target,omitempty" json:"target"`
+	Enable   *bool  `bson:"enable,omitempty" json:"enable"`
+}
+
+type Matrix [16]float64
+type Light struct {
+	Id        string  `bson:"id,omitempty" json:"id"`
+	Type      string  `bson:"type,omitempty" json:"type"`
+	Enable    bool    `bson:"enable,omitempty" json:"enable"`
+	Matrix    Matrix  `bson:"matrix,omitempty" json:"matrix"`
+	Color     Vect3   `bson:"color,omitempty" json:"color"`
+	Intensity float64 `bson:"intensity,omitempty" json:"intensity"`
+}
+
+type SceneLight struct {
+	Enable bool     `bson:"enable,omitempty" json:"enable"`
+	List   []*Light `bson:"list,omitempty" json:"list"`
+}
+
+type DesignScene struct {
+	Id         uint64            `bson:"id,omitempty" json:"id,omitempty"`
+	Name       string            `bson:"name,omitempty" json:"name,omitempty"`
+	Thumbnail  *OssType          `bson:"thumbnail,omitempty" json:"thumbnail,omitempty"`
+	Images     []*OssType        `bson:"images,omitempty" json:"images,omitempty"`
+	Videos     []*OssType        `bson:"videos,omitempty" json:"videos,omitempty"`
+	Locks      []string          `bson:"locks,omitempty" json:"locks,omitempty"`
+	Background *SceneBackground  `bson:"background,omitempty" json:"background,omitempty"`
+	Env        *DesingSceneEnv3d `bson:"env,omitempty" json:"env,omitempty"`
+	Components []*SceneComponent `bson:"components,omitempty" json:"components,omitempty"`
+	Models     []*SceneModels    `bson:"models,omitempty" json:"models,omitempty"`
+	Stickers   []*Sticker        `bson:"stickers,omitempty" json:"stickers,omitempty"`
+	Lights     *SceneLight       `bson:"lights,omitempty" json:"lights,omitempty"`
+}
+
+type Design struct {
+	Id         primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	Mesh       *DesignMesh        `bson:"mesh,omitempty" json:"mesh"`
+	Name       string             `bson:"name,omitempty" json:"name"`
+	Thumbnail  *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	CreateTime time.Time          `bson:"createTime,omitempty" json:"createTime"`
+	Scenes     []*DesignScene     `bson:"scenes,omitempty" json:"scenes"`
+}
+
+type Sticker struct {
+	Id       string                 `bson:"id,omitempty" json:"id,omitempty"`
+	Img      *OssType               `bson:"img,omitempty" json:"img,omitempty"`
+	Scale    *Vect2                 `bson:"scale,omitempty" json:"scale,omitempty"`
+	Visible  *bool                  `bson:"visible,omitempty" json:"visible,omitempty"`
+	Rotation float32                `bson:"rotation" json:"rotation"`
+	Mirror   *Vect2                 `bson:"mirror,omitempty" json:"mirror,omitempty"`
+	Anchors  []*Vect2               `bson:"anchors,omitempty" json:"anchors,omitempty"`
+	Config   map[string]interface{} `bson:"config,omitempty" json:"config,omitempty"`
+}

+ 137 - 0
sku3d/comm/fabric.go

@@ -0,0 +1,137 @@
+package comm
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type Fabric struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
+	UserId    primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`
+	TeamId    string             `bson:"teamId,omitempty" json:"teamId,omitempty"`       //所属团队Id
+	CompanyId string             `bson:"companyId,omitempty" json:"companyId,omitempty"` //所属团队Id
+
+	Name       string    `bson:"name,omitempty" json:"name,omitempty"`
+	CusNum     string    `bson:"cusNum,omitempty" json:"cusNum,omitempty"`
+	Thumbnail  *OssType  `bson:"thumbnail,omitempty" json:"thumbnail,omitempty"`
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime,omitempty"`
+
+	Price      float32         `bson:"price,omitempty" json:"price,omitempty"`
+	ColorCards []*MaterialConf `bson:"colorCards,omitempty" json:"colorCards,omitempty"`
+
+	Categories []string `bson:"categories,omitempty" json:"categories,omitempty"`
+	State      int32    `bson:"state,omitempty" json:"state,omitempty"`
+}
+
+func (s *AssetMatGroup) CopyFromFabric(fab *Fabric) {
+	s.Id = fab.Id
+	s.AssetState = AssetState_Succ
+	s.Categories = fab.Categories
+	s.CreateTime = fab.CreateTime
+	s.UpdateTime = fab.CreateTime
+	s.UserId = fab.UserId
+	s.OwnerId = fab.UserId
+	s.OwnerType = "user"
+	s.CusCategories = fab.Categories
+	s.CusNum = fab.CusNum
+	s.Name = fab.Name
+	enable := true
+	s.Enable = &enable
+	s.Thumbnail = fab.Thumbnail
+	s.Source = &AssetMatGroupSource{
+		Version:    "1.0",
+		ColorCards: []*MatConfigSource{},
+	}
+
+	var toFloat32 = func(f *float64) *float32 {
+		var out float32 = 0
+
+		if f != nil {
+			out = float32(*f)
+		}
+		return &out
+	}
+
+	for _, c := range fab.ColorCards {
+		conf := &MatConfigSource{
+			Version:        1,
+			Type:           c.Type,
+			Name:           c.Name,
+			CusNum:         c.CusNum,
+			Thumbnail:      c.Thumbnail,
+			UvTransform:    c.Uvtransform,
+			CusUvTransform: c.CusUvTransform,
+			Albedo: &MatTextureColor{
+				Color:   c.BaseColor,
+				Texture: c.BaseMap,
+			},
+			Roughness: &MatTextureFactor{
+				Texture: c.RoughMap,
+				Factor:  toFloat32(c.RoughFactor),
+			},
+			Metalness: &MatTextureFactor{
+				Factor:  toFloat32(c.MetalFactor),
+				Texture: c.MetalMap,
+			},
+			Normal: &MatNormal{
+				UseTexture: &enable,
+				Texture:    c.NormalMap,
+			},
+			Opacity: &MatTextureFactorWithEnable{
+				Texture: c.OpacMap,
+				Factor:  toFloat32(c.OpacFactor),
+			},
+			Displace: &MatTextureFactorWithEnable{
+				Texture: c.DisplaceMap,
+				Factor:  toFloat32(c.DisplaceFactor),
+			},
+			Diffuse: &MatTextureColor{
+				Color:   c.DiffuseColor,
+				Texture: c.DiffuseMap,
+			},
+			Gloss: &MatTextureFactor{
+				Factor:  toFloat32(c.GlossFactor),
+				Texture: c.GlossMap,
+			},
+			Specular: &MatTextureColor{
+				Color:   c.SpecColor,
+				Texture: c.SpecMap,
+			},
+			UvMap: "box",
+		}
+
+		if c.BaseMap != nil && len(c.BaseMap.Url) > 0 {
+			conf.Albedo.UseTexture = &enable
+		}
+		if c.RoughMap != nil && len(c.RoughMap.Url) > 0 {
+			conf.Roughness.UseTexture = &enable
+		}
+		if c.MetalMap != nil && len(c.MetalMap.Url) > 0 {
+			conf.Metalness.UseTexture = &enable
+		}
+		if c.OpacFactor != nil && *c.OpacFactor < 0.99 {
+			conf.Opacity.Enable = true
+		}
+		if c.OpacMap != nil && len(c.OpacMap.Url) > 0 {
+			conf.Opacity.Enable = true
+			conf.Opacity.UseTexture = &enable
+		}
+
+		if c.DisplaceMap != nil && len(c.DisplaceMap.Url) > 0 {
+			conf.Displace.Enable = true
+			conf.Displace.UseTexture = &enable
+		}
+		if c.DiffuseMap != nil && len(c.DiffuseMap.Url) > 0 {
+			conf.Diffuse.UseTexture = &enable
+		}
+		if c.GlossMap != nil && len(c.GlossMap.Url) > 0 {
+			conf.Gloss.UseTexture = &enable
+		}
+		if c.SpecMap != nil && len(c.SpecMap.Url) > 0 {
+			conf.Specular.UseTexture = &enable
+		}
+
+		s.Source.ColorCards = append(s.Source.ColorCards, conf)
+	}
+}

+ 59 - 0
sku3d/comm/go.mod

@@ -0,0 +1,59 @@
+module comm
+
+go 1.17
+
+require (
+	github.com/StackExchange/wmi v1.2.1
+	github.com/jessevdk/go-flags v1.5.0
+	github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
+	github.com/nats-io/nats.go v1.22.1
+	github.com/spf13/viper v1.7.0
+	go.mongodb.org/mongo-driver v1.11.1
+	golang.org/x/sys v0.3.0
+)
+
+require (
+	github.com/cenkalti/backoff/v4 v4.2.0 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/fsnotify/fsnotify v1.4.9 // indirect
+	github.com/go-ole/go-ole v1.2.5 // indirect
+	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-multierror v1.1.1 // indirect
+	github.com/hashicorp/hcl v1.0.0 // indirect
+	github.com/magiconair/properties v1.8.1 // indirect
+	github.com/mitchellh/mapstructure v1.1.2 // indirect
+	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/pelletier/go-toml v1.9.3 // indirect
+	github.com/spf13/afero v1.6.0 // indirect
+	github.com/spf13/cast v1.3.0 // indirect
+	github.com/spf13/jwalterweatherman v1.0.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/stretchr/testify v1.8.1 // indirect
+	github.com/subosito/gotenv v1.2.0 // indirect
+	go.uber.org/atomic v1.10.0 // indirect
+	golang.org/x/net v0.4.0 // indirect
+	golang.org/x/time v0.3.0 // indirect
+	gopkg.in/ini.v1 v1.51.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+)
+
+require (
+	github.com/go-redis/redis/v8 v8.11.5
+	github.com/golang-migrate/migrate/v4 v4.15.2
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/google/go-cmp v0.5.9 // indirect
+	github.com/klauspost/compress v1.15.13 // indirect
+	github.com/nats-io/nats-server/v2 v2.9.10 // indirect
+	github.com/nats-io/nkeys v0.3.0 // indirect
+	github.com/nats-io/nuid v1.0.1 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.2 // indirect
+	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
+	golang.org/x/crypto v0.4.0 // indirect
+	golang.org/x/sync v0.1.0 // indirect
+	golang.org/x/text v0.5.0 // indirect
+	google.golang.org/protobuf v1.28.1 // indirect
+)

+ 1928 - 0
sku3d/comm/go.sum

@@ -0,0 +1,1928 @@
+bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
+bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
+cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
+cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
+cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
+cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
+cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
+cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
+cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
+cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/spanner v1.28.0/go.mod h1:7m6mtQZn/hMbMfx62ct5EWrGND4DNqkXyrmBPRS+OJo=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
+github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
+github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
+github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
+github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
+github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
+github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
+github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
+github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
+github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
+github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
+github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
+github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
+github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
+github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc=
+github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
+github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
+github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64/go.mod h1:2qMFB56yOP3KzkB3PbYZ4AlUFg3a88F67TIx5lB/WwY=
+github.com/apache/arrow/go/arrow v0.0.0-20211013220434-5962184e7a30/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
+github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0=
+github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
+github.com/aws/aws-sdk-go-v2/config v1.6.0/go.mod h1:TNtBVmka80lRPk5+S9ZqVfFszOQAGJJ9KbT3EM3CHNU=
+github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw=
+github.com/aws/aws-sdk-go-v2/credentials v1.3.2/go.mod h1:PACKuTJdt6AlXvEq8rFI4eDmoqDFC5DpVKQbWysaDgM=
+github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.4.0/go.mod h1:Mj/U8OpDbcVcoctrYwA2bak8k/HFPdcLzI/vaiXMwuM=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.4.0/go.mod h1:eHwXu2+uE/T6gpnYWwBwqoeqRf9IXyCcolyOWDRAErQ=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.4/go.mod h1:Ex7XQmbFmgFHrjUX6TN3mApKW5Hglyga+F7wZHTtYhA=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.2.0/go.mod h1:Q5jATQc+f1MfZp3PDMhn6ry18hGvE0i8yvbXoKbnZaE=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.2.2/go.mod h1:EASdTcM1lGhUe1/p4gkojHwlGJkeoRjjr1sRCzup3Is=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.2/go.mod h1:NXmNI41bdEsJMrD0v9rUvbGCB5GwdBEpKvUvIY3vTFg=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.5.2/go.mod h1:QuL2Ym8BkrLmN4lUofXYq6000/i5jPjosCNK//t6gak=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.2/go.mod h1:np7TMuJNT83O0oDOSF8i4dF3dvGqA6hPYYo6YYkzgRA=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.12.0/go.mod h1:6J++A5xpo7QDsIeSqPK4UHqMSyPOCopa+zKtqAMhqVQ=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.16.1/go.mod h1:CQe/KvWV1AqRc65KqeJjrLzr5X2ijnFTTVzJW0VBRCI=
+github.com/aws/aws-sdk-go-v2/service/sso v1.3.2/go.mod h1:J21I6kF+d/6XHVk7kp/cx9YVD2TMD2TbLwtRGVcinXo=
+github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.6.1/go.mod h1:hLZ/AnkIKHLuPGjEiyghNEdvJ2PP0MgOxcmv9EBJ4xs=
+github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
+github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
+github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
+github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
+github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
+github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
+github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
+github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
+github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
+github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
+github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
+github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
+github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
+github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
+github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
+github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
+github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
+github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
+github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
+github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
+github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
+github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
+github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
+github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
+github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
+github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
+github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
+github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
+github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
+github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
+github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
+github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
+github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
+github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
+github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
+github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
+github.com/containerd/containerd v1.6.1 h1:oa2uY0/0G+JX4X7hpGCYvkp9FjUancz56kSNnb1sG3o=
+github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE=
+github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
+github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
+github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
+github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
+github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
+github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
+github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
+github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
+github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
+github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
+github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
+github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
+github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
+github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
+github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
+github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
+github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4=
+github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
+github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
+github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
+github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
+github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
+github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
+github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
+github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
+github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
+github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
+github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
+github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
+github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y=
+github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
+github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
+github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=
+github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
+github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
+github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
+github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
+github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
+github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
+github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
+github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
+github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
+github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dhui/dktest v0.3.10 h1:0frpeeoM9pHouHjhLeZDuDTJ0PqjDTrycaHaMmkJAo8=
+github.com/dhui/dktest v0.3.10/go.mod h1:h5Enh0nG3Qbo9WjNFRrwmKUaePEBhXMOygbz3Ww7Sz0=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
+github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
+github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.13+incompatible h1:5s7uxnKZG+b8hYWlPYUi6x1Sjpq2MSt96d15eLZeHyw=
+github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
+github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
+github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
+github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
+github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
+github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
+github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
+github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
+github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
+github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
+github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
+github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-migrate/migrate/v4 v4.15.2 h1:vU+M05vs6jWHKDdmE1Ecwj0BznygFc4QsdRe2E/L7kc=
+github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2ZdbeUNx4sIwiOK96rE9Lw=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
+github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
+github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
+github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
+github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
+github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
+github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
+github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=
+github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
+github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
+github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
+github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
+github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
+github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
+github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
+github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
+github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
+github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
+github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
+github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
+github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
+github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
+github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
+github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
+github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
+github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
+github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
+github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0=
+github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
+github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
+github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
+github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
+github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
+github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
+github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
+github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
+github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
+github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
+github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
+github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
+github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
+github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
+github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
+github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA=
+github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
+github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
+github.com/nats-io/nats-server/v2 v2.9.10 h1:LMC46Oi9E6BUx/xBsaCVZgofliAqKQzRPU6eKWkN8jE=
+github.com/nats-io/nats-server/v2 v2.9.10/go.mod h1:AB6hAnGZDlYfqb7CTAm66ZKMZy9DpfierY1/PbpvI2g=
+github.com/nats-io/nats.go v1.19.0/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA=
+github.com/nats-io/nats.go v1.22.1 h1:XzfqDspY0RNufzdrB8c4hFR+R3dahkxlpWe5+IWJzbE=
+github.com/nats-io/nats.go v1.22.1/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA=
+github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
+github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
+github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
+github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
+github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
+github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
+github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
+github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
+github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
+github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
+github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
+github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
+github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
+github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
+github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
+github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
+github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
+github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
+github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
+github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
+github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
+github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
+github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
+github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
+github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
+github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
+github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
+github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
+github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
+github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
+github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
+gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
+go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
+go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
+go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
+go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
+go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
+go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
+go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8=
+go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
+go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
+go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
+go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
+go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
+go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE=
+go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
+go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
+go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
+go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
+go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
+go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
+go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
+go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
+golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
+golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
+golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210818153620-00dd8d7831e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
+gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
+gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
+gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
+gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
+gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
+google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
+google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
+google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
+google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
+google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
+google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
+google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
+google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
+google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
+google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U=
+google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
+google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 h1:ErU+UA6wxadoU8nWrsy5MZUVBs75K17zUCsUCIfrXCE=
+google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
+google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
+google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
+gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
+k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
+k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
+k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=
+k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
+k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
+k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U=
+k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
+k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
+k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
+k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ=
+k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
+k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
+k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
+k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=
+k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
+k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
+k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
+k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
+k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI=
+k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
+k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
+k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
+k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
+k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
+k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
+modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
+modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo=
+modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
+modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw=
+modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
+modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
+modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
+modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM=
+modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8=
+modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
+modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
+modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY=
+modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k=
+modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs=
+modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
+modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo=
+modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
+modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
+modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

+ 118 - 0
sku3d/comm/lancher-client.go

@@ -0,0 +1,118 @@
+package comm
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/nats-io/nats.go"
+)
+
+// 客户端运行
+var _currAppId = ""
+
+func LancherClientRun(bus *NatsBus, ExePath string, configfile string, iconUrl string) error {
+	currApp, e := ParseConfigFile(configfile)
+	if e != nil {
+		return e
+	}
+
+	id := GetAppPathId(ExePath)
+	_currAppId = id
+
+	currApp.Id = id
+	currApp.LastRuningTime = time.Now()
+	currApp.RegTime = time.Now()
+	currApp.Status = APPSTATE_RUNNING
+	currApp.IconUrl = iconUrl
+
+	pwd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+	currApp.Pwd = pwd
+
+	currApp.ExeFpath = ExePath
+	currApp.Args = strings.Join(os.Args[1:], " ")
+	nc := bus.GetNatsConn()
+	js, e := nc.JetStream()
+	if e != nil {
+		return e
+	}
+
+	payload, e := json.Marshal(currApp)
+	if e != nil {
+		return e
+	}
+
+	kv, e := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: "lancher-apps"})
+	if e != nil {
+		return e
+	}
+	v, e := kv.Get(currApp.Id)
+	if e != nil {
+		if e == nats.ErrKeyNotFound {
+			_, e := kv.Put(currApp.Id, payload)
+			if e != nil {
+				return e
+			}
+			return nil
+
+		} else {
+			return nil
+		}
+	}
+	lastApp := &App{}
+	e = json.Unmarshal(v.Value(), lastApp)
+	if e != nil {
+		return e
+	}
+	lastApp.Name = currApp.Name
+	lastApp.Label = currApp.Label
+	lastApp.Icon = currApp.Icon
+	lastApp.Desc = currApp.Name
+	lastApp.LastRuningTime = time.Now()
+	lastApp.Status = currApp.Status
+	lastApp.IconUrl = currApp.IconUrl
+	lastApp.ExeFpath = currApp.ExeFpath
+	lastApp.Args = currApp.Args
+	lastApp.Pwd = currApp.Pwd
+
+	payload, _ = json.Marshal(lastApp)
+	_, e = kv.Update(currApp.Id, payload, v.Revision())
+	if e != nil {
+		return e
+	}
+	kv.Purge(currApp.Id, nats.LastRevision(v.Revision()))
+	vs, _ := kv.History(currApp.Id)
+	fmt.Println(len(vs))
+
+	return nil
+}
+
+func LancherClientQuitApp(bus *NatsBus) {
+	nc := bus.GetNatsConn()
+	js, e := nc.JetStream()
+	if e != nil {
+		return
+	}
+	kv, e := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: "lancher-apps"})
+	if e != nil {
+		return
+	}
+	v, e := kv.Get(_currAppId)
+	if e != nil {
+		return
+	}
+
+	lastApp := &App{}
+	e = json.Unmarshal(v.Value(), lastApp)
+	if e != nil {
+		return
+	}
+	lastApp.Status = APPSTATE_INSTALLED
+	payload, _ := json.Marshal(lastApp)
+	kv.Update(_currAppId, payload, v.Revision())
+}

+ 186 - 0
sku3d/comm/lancher-server.go

@@ -0,0 +1,186 @@
+package comm
+
+import (
+	"crypto/md5"
+	"encoding/json"
+	"fmt"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/nats-io/nats.go"
+	"github.com/spf13/viper"
+)
+
+const (
+	APPSTATE_RUNNING   = "运行中"
+	APPSTATE_INSTALLED = "已安装"
+)
+
+type App struct {
+	Id      string
+	Name    string
+	Label   string
+	Desc    string
+	Icon    string
+	IconUrl string
+	Version string
+	Type    string
+
+	ExeFpath       string
+	Pwd            string
+	Args           string
+	RegTime        time.Time
+	LastRuningTime time.Time
+	Status         string //运行中
+}
+
+func GetAppId() (string, error) {
+	f, e := os.Executable()
+	if e != nil {
+		return "", e
+	}
+	_, exeName := filepath.Split(f)
+	if strings.Contains(f, "go-build") {
+		fmt.Println()
+		pwd, e := os.Getwd()
+		if e != nil {
+			return "", e
+		}
+		f = path.Join(pwd, exeName)
+	}
+	fmt.Println("app path=>", f)
+	md := md5.Sum([]byte(f))
+	return fmt.Sprintf("%x", md), nil
+}
+
+func GetAppPathId(fpath string) string {
+	md := md5.Sum([]byte(fpath))
+	return fmt.Sprintf("%x", md)
+}
+
+func ParseConfigFile(filepath string) (*App, error) {
+	file, err := os.Open(filepath)
+	if err != nil {
+		return nil, err
+	}
+
+	v := viper.New()
+	v.SetConfigType("yaml")
+	err = v.ReadConfig(file)
+	if err != nil {
+		return nil, err
+	}
+	c := new(App)
+	err = v.Unmarshal(c)
+	return c, err
+}
+
+var _callCancel = func() {}
+
+func CancelServeRun() {
+	_callCancel()
+}
+func ServerRun(bus *NatsBus) error {
+	cancel := WatchChange(bus)
+	if cancel == nil {
+		return fmt.Errorf("启动App监听失败")
+	}
+	_callCancel = cancel
+
+	bus.Subscribe("lancher.apps.list", func(msg *nats.Msg) {
+		out := []*App{}
+		nc := bus.GetNatsConn()
+		js, e := nc.JetStream()
+		if e == nil {
+			kv, e := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: "lancher-apps"})
+			if e == nil {
+				out = getApps(kv)
+			}
+		}
+		payload, _ := json.Marshal(out)
+		msg.Respond(payload)
+	})
+
+	//启动app
+	bus.Subscribe("lancher.start.*", func(msg *nats.Msg) {
+		//获取AppId
+		fmt.Println("starting=>", msg.Subject)
+	})
+	return nil
+}
+
+func WatchChange(bus *NatsBus) func() {
+	nc := bus.GetNatsConn()
+	js, e := nc.JetStream()
+	if e != nil {
+		fmt.Println(e)
+		return nil
+	}
+
+	if e != nil {
+		fmt.Println(e)
+		return nil
+	}
+
+	kv, e := js.CreateKeyValue(&nats.KeyValueConfig{Bucket: "lancher-apps"})
+	if e != nil {
+		return nil
+	}
+	watch, e := kv.WatchAll()
+	if e != nil {
+		return nil
+	}
+	quiteCh := make(chan int, 1)
+
+	go func() {
+		for {
+			conti := true
+			select {
+			case <-quiteCh:
+				conti = false
+			default:
+				v := <-watch.Updates()
+				if v != nil {
+					fmt.Println("wathing update=>", v.Key(), v.Bucket(), v.Operation())
+				}
+				bus.PublishObj("lancher.apps.updated", getApps(kv))
+
+				time.Sleep(time.Second)
+			}
+			if !conti {
+				break
+			}
+		}
+		fmt.Println("waching app quited")
+	}()
+	return func() {
+		quiteCh <- 1
+	}
+}
+
+func getApps(kv nats.KeyValue) []*App {
+	out := []*App{}
+
+	keys, e := kv.Keys()
+	if e != nil {
+		fmt.Println("keys error=>", e)
+		return out
+	}
+	for _, key := range keys {
+		v, _ := kv.Get(key)
+		if v != nil {
+			app := &App{}
+			e := json.Unmarshal(v.Value(), app)
+			if e == nil {
+				fmt.Println("app=>", *app)
+				out = append(out, app)
+			} else {
+				fmt.Println("Unmarshal errr=>", e)
+			}
+		}
+	}
+	return out
+}

+ 28 - 0
sku3d/comm/license.go

@@ -0,0 +1,28 @@
+package comm
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type License struct {
+	Id    primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	State string             `bson:"state,omitempty" json:"state"` //created(已创建) deployed(已部署) invalid(已失效) expired(已过期)
+
+	SaltStr    string `bson:"saltstr,omitempty" json:"saltstr"`       //随机加密盐
+	AesKey     string `bson:"aesKey,omitempty" json:"aesKey"`         //加解密钥
+	RsaPublic  string `bson:"rsaPublic,omitempty" json:"rsaPublic"`   //公钥
+	RsaPrivate string `bson:"rsaPrivate,omitempty" json:"rsaPrivate"` //私钥
+
+	CpuId        string    `bson:"cpuId,omitempty" json:"cpuId"`               //cpuId
+	MacId        string    `bson:"macId,omitempty" json:"macId"`               //MacId
+	AppId        string    `bson:"appId,omitempty" json:"appId"`               //AppId
+	ExpiredAfter time.Time `bson:"expiredAfter,omitempty" json:"expiredAfter"` //expiredAfter
+	StartAt      time.Time `bson:"startAt,omitempty" json:"startAt"`           //startAt
+	Company      string    `bson:"company,omitempty" json:"company"`           //企业信息
+	Title        string    `bson:"title,omitempty" json:"title"`               //licenes名字
+	CreateTime   time.Time `bson:"createTime,omitempty" json:"createTime"`     //创建时间
+	UpdateTime   time.Time `bson:"updateTime,omitempty" json:"updateTime"`     //创建时间
+	DeployedTime time.Time `bson:"deployedTime,omitempty" json:"deployedTime"` //部署时间
+}

+ 193 - 0
sku3d/comm/liscense-deviceId.go

@@ -0,0 +1,193 @@
+//go:build windows
+// +build windows
+
+package comm
+
+import (
+	"context"
+	"crypto/md5"
+	"crypto/rand"
+	"encoding/base64"
+	"encoding/hex"
+	"fmt"
+	"net"
+	"strings"
+	"unsafe"
+
+	"github.com/StackExchange/wmi"
+	"golang.org/x/sys/windows"
+)
+
+func GetPhysicalID() string {
+	var ids []string
+	mac := getMACAddress()
+	if len(mac) < 1 {
+		mac = "mac"
+	}
+	ids = append(ids, mac)
+
+	guid, _ := getMachineGuid()
+	if len(guid) < 1 {
+		guid = "guid"
+	}
+	ids = append(ids, guid)
+
+	cpuinfo, err := getCPUInfo()
+	cpus := []string{}
+	if err == nil {
+		for _, c := range cpuinfo {
+			cpus = append(cpus, c.VendorID+c.PhysicalID)
+		}
+	}
+	cpu := "cpu"
+	if len(cpu) > 0 {
+		cpu = strings.Join(cpus, ",")
+	}
+	ids = append(ids, cpu)
+	idsstr := strings.Join(ids, "|")
+	return idsstr
+}
+
+func getMACAddress() string {
+	netInterfaces, err := net.Interfaces()
+	if err != nil {
+		return ""
+	}
+	for i := 0; i < len(netInterfaces); i++ {
+		//fmt.Println(netInterfaces[i])
+		if (netInterfaces[i].Flags&net.FlagUp) != 0 && (netInterfaces[i].Flags&net.FlagLoopback) == 0 {
+			addrs, _ := netInterfaces[i].Addrs()
+			for _, address := range addrs {
+				ipnet, ok := address.(*net.IPNet)
+				//fmt.Println(ipnet.IP)
+				if ok && ipnet.IP.IsGlobalUnicast() {
+					// 如果IP是全局单拨地址,则返回MAC地址
+					return netInterfaces[i].HardwareAddr.String()
+				}
+			}
+		}
+	}
+	return ""
+}
+
+type cpuInfo struct {
+	CPU        int32  `json:"cpu"`
+	VendorID   string `json:"vendorId"`
+	PhysicalID string `json:"physicalId"`
+}
+
+type win32_Processor struct {
+	Manufacturer string
+	ProcessorID  *string
+}
+
+func getCPUInfo() ([]cpuInfo, error) {
+	var ret []cpuInfo
+	var dst []win32_Processor
+	q := wmi.CreateQuery(&dst, "")
+	fmt.Println(q)
+	if err := wmiQuery(q, &dst); err != nil {
+		return ret, err
+	}
+
+	var procID string
+	for i, l := range dst {
+		procID = ""
+		if l.ProcessorID != nil {
+			procID = *l.ProcessorID
+		}
+
+		cpu := cpuInfo{
+			CPU:        int32(i),
+			VendorID:   l.Manufacturer,
+			PhysicalID: procID,
+		}
+		ret = append(ret, cpu)
+	}
+
+	return ret, nil
+}
+
+// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
+func wmiQuery(query string, dst interface{}, connectServerArgs ...interface{}) error {
+	ctx := context.Background()
+	if _, ok := ctx.Deadline(); !ok {
+		ctxTimeout, cancel := context.WithTimeout(ctx, 3000000000) //超时时间3s
+		defer cancel()
+		ctx = ctxTimeout
+	}
+
+	errChan := make(chan error, 1)
+	go func() {
+		errChan <- wmi.Query(query, dst, connectServerArgs...)
+	}()
+
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	case err := <-errChan:
+		return err
+	}
+}
+
+func getMachineGuid() (string, error) {
+	// there has been reports of issues on 32bit using golang.org/x/sys/windows/registry, see https://github.com/shirou/gopsutil/pull/312#issuecomment-277422612
+	// for rationale of using windows.RegOpenKeyEx/RegQueryValueEx instead of registry.OpenKey/GetStringValue
+	var h windows.Handle
+	err := windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
+	if err != nil {
+		return "", err
+	}
+	defer windows.RegCloseKey(h)
+
+	const windowsRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
+	const uuidLen = 36
+
+	var regBuf [windowsRegBufLen]uint16
+	bufLen := uint32(windowsRegBufLen)
+	var valType uint32
+	err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
+	if err != nil {
+		return "", err
+	}
+
+	hostID := windows.UTF16ToString(regBuf[:])
+	hostIDLen := len(hostID)
+	if hostIDLen != uuidLen {
+		return "", fmt.Errorf("HostID incorrect: %q\n", hostID)
+	}
+
+	return hostID, nil
+}
+
+// 生成32位md5字串
+func GetMd5String(s string, upper bool, half bool) string {
+	h := md5.New()
+	h.Write([]byte(s))
+	result := hex.EncodeToString(h.Sum(nil))
+	if upper == true {
+		result = strings.ToUpper(result)
+	}
+	if half == true {
+		result = result[8:24]
+	}
+	return result
+}
+
+// 利用随机数生成Guid字串
+func UniqueId() string {
+	b := make([]byte, 48)
+	if _, err := rand.Read(b); err != nil {
+		return ""
+	}
+	return GetMd5String(base64.URLEncoding.EncodeToString(b), true, false)
+}
+
+func GetDeviceHash() string {
+	//校验设备
+	id := GetPhysicalID()
+	if len(id) < 1 {
+		return ""
+	}
+	return GetMd5String(id, true, true)
+}

+ 34 - 0
sku3d/comm/liscense-enity.go

@@ -0,0 +1,34 @@
+package comm
+
+import "time"
+
+type LiscenseActiveCode struct {
+	Id       string `bson:"_id,omitempty" json:"_id"`
+	AppName  string `bson:"appName,omitempty" json:"appName"`
+	AppKey   string `bson:"appKey,omitempty" json:"appKey"`
+	AuthId   string `bson:"authId,omitempty" json:"authId"`
+	AuthName string `bson:"authName,omitempty" json:"authName"`
+
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+
+	StartTime  time.Time     `bson:"startTime,omitempty" json:"startTime"`
+	ExpireTime time.Time     `bson:"expireTime,omitempty" json:"expireTime"`
+	Duration   time.Duration `bson:"duration,omitempty" json:"duration"`
+
+	DeviceHashCode string `bson:"deviceHashCode,omitempty" json:"deviceHashCode"`
+}
+
+const (
+	AuthStatusNoActive = "未激活"
+	AuthStatusExpired  = "已过期"
+	AuthStatusOK       = "已激活"
+)
+
+type AuthInfo struct {
+	AuthName   string
+	AuthId     string
+	Duration   time.Duration
+	Expired    time.Time
+	CreateTime time.Time
+	Status     string
+}

+ 298 - 0
sku3d/comm/liscense-main.go

@@ -0,0 +1,298 @@
+//go:build windows
+// +build windows
+
+package comm
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"time"
+
+	"github.com/nats-io/nats.go"
+)
+
+var msgbus *NatsBus
+
+// 开启liscense检测
+func InitLiscenseBus(bus *NatsBus) error {
+	msgbus = bus
+
+	// liscense = NewLiscenseTask()
+	// return liscense.Run(nil)
+
+	return nil
+}
+
+type LiscenseTask struct {
+	Task
+
+	time.Time
+
+	dirty bool
+
+	SK string
+
+	activeObj     *LiscenseActiveCode
+	appPrivateKey []byte
+	appKey        string
+	keyLabel      []byte
+	DataDir       string
+}
+
+type TimeFlag struct {
+	I  string
+	R  int32
+	LT time.Time
+}
+
+func NewLiscenseTask(sk string, privateKey string, label string, appKey string, dataDir string) *LiscenseTask {
+	t := &LiscenseTask{SK: sk, appPrivateKey: []byte(privateKey), keyLabel: []byte(label), appKey: appKey, DataDir: dataDir}
+	t.Task = NewBackgroundTask(t, nil)
+	t.LoadLiscense()
+
+	t.SubscribeActiveMsg()
+
+	return t
+}
+
+type ActiveReq struct {
+	ActiveCode string
+}
+type ActiveResp struct {
+	Error      error
+	Succ       bool
+	AuthName   string
+	StartTime  time.Time
+	ExpireTime time.Time
+	Duration   int
+	ErrorDesc  string
+}
+
+// 加本地liscense
+func (t *LiscenseTask) LoadLiscense() error {
+	datafile := path.Join(t.DataDir, "liscense")
+	d, e := ioutil.ReadFile(datafile)
+	if e != nil {
+		return e
+	}
+
+	c, e := base64.StdEncoding.DecodeString(string(d))
+	if e != nil {
+		return e
+	}
+
+	p, e := decryptOAEP(t.appPrivateKey, c, t.keyLabel)
+	if e != nil {
+		return e
+	}
+	o := &LiscenseActiveCode{}
+	e = json.Unmarshal(p, o)
+	if e != nil {
+		return e
+	}
+	t.activeObj = o
+	return nil
+}
+
+func (t *LiscenseTask) Active(req *ActiveReq) *ActiveResp {
+	//清除已有的激活信息
+	infoPath := path.Join(t.DataDir, "authinfo")
+	os.Remove(infoPath)
+
+	//存储激活码
+	datafile := path.Join(t.DataDir, "liscense")
+
+	e := ioutil.WriteFile(datafile, []byte(req.ActiveCode), os.ModePerm)
+	if e != nil {
+		return &ActiveResp{Succ: false, Error: e}
+	}
+
+	e = t.LoadLiscense()
+	if e != nil {
+		os.Remove(datafile)
+		return &ActiveResp{Succ: false, Error: fmt.Errorf("激活码无效")}
+	}
+
+	//验证当前系统时间
+	if time.Now().Before(t.activeObj.CreateTime) {
+		return &ActiveResp{Succ: false, Error: fmt.Errorf("系统时间无效,无法激活")}
+	}
+
+	//存储最新时间
+	f := t.setTimeFlag()
+	if f == nil {
+		return &ActiveResp{Succ: false, Error: fmt.Errorf("系统时间无效")}
+	}
+
+	if t.IsLiscenseOk() {
+
+		//保存激活码
+		obj := t.activeObj
+		ret := &ActiveResp{Succ: true, AuthName: obj.AuthName, Duration: int(obj.Duration), StartTime: obj.StartTime, ExpireTime: obj.ExpireTime}
+		return ret
+	}
+
+	return &ActiveResp{Succ: false, Error: fmt.Errorf("激活码无效")}
+}
+
+func (t *LiscenseTask) UpdateAuthInfo(status string) {
+	info := &AuthInfo{}
+	info.AuthId = t.activeObj.AuthId
+	info.AuthName = t.activeObj.AuthName
+	info.Expired = t.activeObj.ExpireTime
+	info.CreateTime = t.activeObj.CreateTime
+	info.Duration = t.activeObj.Duration
+	info.Status = status
+
+	infoPath := path.Join(t.DataDir, "authinfo")
+	d, e := json.Marshal(info)
+	if e == nil {
+		ioutil.WriteFile(infoPath, d, os.ModePerm)
+	}
+}
+
+func (t *LiscenseTask) SubscribeActiveMsg() {
+	if msgbus == nil {
+		return
+	}
+
+	msgbus.QueueSubscribe(&SubOption{
+		Sub: "liscense.active." + t.appKey,
+		Obj: func() interface{} { return &ActiveReq{} },
+		Call: func(obj interface{}, msg *nats.Msg) interface{} {
+			req := obj.(*ActiveReq)
+			resp := t.Active(req)
+			if resp.Error != nil {
+				resp.ErrorDesc = resp.Error.Error()
+			} else {
+				msgbus.PublishObj("liscense.active.succ", map[string]string{"appkey": t.appKey})
+			}
+			return resp
+		},
+	})
+}
+
+func (t *LiscenseTask) getTimeFlag() *TimeFlag {
+	//存储最新时间
+	tickfile := path.Join(t.DataDir, "tick")
+	d, e := ioutil.ReadFile(tickfile)
+	if e != nil {
+		return nil
+	}
+	p, e := Decode(t.SK, string(d))
+	if e != nil {
+		return nil
+	}
+	f := &TimeFlag{}
+	e = json.Unmarshal(p, f)
+	if e != nil {
+		return nil
+	}
+	if f.I != t.appKey {
+		return nil
+	}
+	return f
+}
+
+func (t *LiscenseTask) setTimeFlag() *TimeFlag {
+	//存储最新时间
+	tickfile := path.Join(t.DataDir, "tick")
+	f := &TimeFlag{I: t.appKey, R: radomInt(), LT: time.Now()}
+	flag, e := json.Marshal(f)
+	if e != nil {
+		return nil
+	}
+	c, e := Encode(t.SK, flag)
+
+	if e != nil {
+		return nil
+	}
+	e = ioutil.WriteFile(tickfile, []byte(c), os.ModePerm)
+	if e != nil {
+		return nil
+	}
+	return f
+}
+
+func (t *LiscenseTask) UpdateTimeFlag() bool {
+	f1 := t.getTimeFlag()
+	if f1 == nil {
+		t.dirty = true
+		return false
+	}
+	if time.Now().Before(f1.LT) {
+		t.dirty = true
+		return false
+	}
+	t.dirty = false
+	t.setTimeFlag()
+	return true
+}
+
+func (t *LiscenseTask) IsLiscenseOk() bool {
+	ready := true
+	if t.activeObj == nil || t.activeObj.AppKey != t.appKey {
+		ready = false
+	}
+
+	//校验系统时间
+	if t.dirty {
+		ready = false
+	}
+
+	if t.activeObj != nil {
+		hash := GetDeviceHash()
+		if t.activeObj.DeviceHashCode != hash {
+			ready = false
+		}
+	}
+
+	infoPath := path.Join(t.DataDir, "authinfo")
+	if !ready {
+		os.Remove(infoPath)
+		return false
+	}
+
+	isOk := false
+	s := AuthStatusNoActive
+	if t.activeObj.Duration == -1 {
+		isOk = true
+		s = AuthStatusOK
+	} else {
+		n := time.Now()
+		if n.Before(t.activeObj.ExpireTime) && n.After(t.activeObj.StartTime) {
+			isOk = true
+			s = AuthStatusOK
+		} else {
+			s = AuthStatusExpired
+		}
+		if !isOk {
+			os.Remove(infoPath)
+		}
+	}
+	t.UpdateAuthInfo(s)
+
+	return isOk
+}
+
+func (t *LiscenseTask) Loop() bool {
+	t.UpdateTimeFlag()
+
+	time.Sleep(5 * time.Minute) //5分钟更新一下
+
+	return false
+}
+func (t *LiscenseTask) OnAfterRun() error {
+	return nil
+}
+
+func (t *LiscenseTask) OnBeforeRun() error {
+	// if t.activeObj == nil {
+	// 	return fmt.Errorf("应用未激活")
+	// }
+	return nil
+}

+ 48 - 0
sku3d/comm/liscense-main_test.go

@@ -0,0 +1,48 @@
+//go:build windows
+// +build windows
+
+package comm
+
+import "testing"
+
+var queenginePrivateKey = []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAoQUDrzLunGFWKqB0SWBIddSex8FJCVngfXdIteAK+VhxR0SF
+s8rG8/LRPcFg51LyvXs79bUpPUdy/AxZB/nLBjdiV8T2tKKio6OKtwMH3vZ4VOAl
+UJnq3k3opq5sc1tbi7IO0ate3YmUrLwAPnPxfxFeTucYI8o+AWxyK/54DC7rl68R
+bL38/CZ75mzBknnA6m6ATCBoN8zWlk78cwA5tOFwRv3osm6ENflQwZN3bn3fhL2c
+VcwgRnwrxIk+Mh6m3zBR2IHRb8mxazV7IauRCF/N4yp1lQ5Qx8lr7awEd5x0V1yu
+8MmBgibmIi+Kqp/qUCKw+SnGzVCdl4SOh/fNVwIDAQABAoIBABmq4fZ4qP62qXSs
+FT1pIsPdu93d2tWpxRZbAHU1nKo4xf31V1bcuTBEAcTVYaweVeGMlndJhKRnElZJ
+ZZvW3t1D0OJq3GTZFBH/zOn1iripE298N5aMopsDUlXXsiQFaSdwn9oedkXcW53Z
+bjSGaPW3gBAV2IxD/fReJCkNLHfMdjTOlBNuLX+fybda4fR3W0e1K9LyaWcmf1mL
+eX9wiBGDqq+KXvI0Ef4b0LrV8hBB5Qh/2RWokwTBVjQmT+8fTp5iYybpSSWis2/i
+mNbiOrgOZt4b4+nRObQL1/FRLHf48JALZ4IyEHOo0sreGNs76lYYvlIOUxnl7A7T
+DxMbTGECgYEAz6rObj+BVZbg8EaE39vqg1hhPl/nuqXl8mSUCp+SjkeIrmLtlzH0
+N/uVjfnQj8FbZSpBrLnnQwadDmlNmueybt99m8F2WeB1jItzDQ/DeBcrnktGryCK
+cxFj+DOevkGwQQwjQEDhDXRdmBkTDmlICGQt/Z+udsFnzYhdFoYC4vECgYEAxn7b
+Xy0VbHd/AB2/OIyNCpMkuE1BBohSsxX+zARv3NPOnqwxF/mzqIheQFN+qXqCVT5n
+m3jcWDSqCeNFJMM5hmWQUXEvyA8/JEvMtEOTa0uZOG1Ofg0T3fmyeG1/GoPpMpus
+hGIwlEJ9NPNOCl/CV1j/45RgArqaOjgyU6nHpMcCgYBmSsKod8ujd1CRyorFIjSg
+QjaUXPVucS2VeomZugSc5QukqsJRyZ98vAZtCvL6/MmiSXBUPrZ1w5o1cRVzClJQ
+XvImeONtXi1I75/afTtRukgYDwhgV20bRSOzLOoy2XSSdgIfMlYpJHq94wwJ+z4q
+M/6bgb6UbKxPB66i25wsYQKBgQCmvGrz8P/9BAfHhjVZgB790gpXUnWb0il9y60j
+nVqF6dm3SbdXh2YV3K8nY7uTC5itmGUZoRWedmabNbCgJ+mLbocjEmvrpnsIty92
+AQm1aGSFlLQJiFy26oezR28sS8A4xbHN5lkeQRfBwds9NaEfFGCmv71CrZLd8tS7
+mHNegwKBgQCR/dbXkw3jTzQZoVe+Xq4ndBefoxpoRyUR78xtbGo84DZkiD+/0SVp
+FDHTS3P83KWPGB0WxsYDx7oh42vuduQ3Cjg1D34G2QzxhviHVKvlVnsRbhgbG/eK
+rMbuUM6KISochWtXtOxqrYb3n76xzcomOcdYrH33Yzmb7U/lJYG5Pg==
+-----END RSA PRIVATE KEY-----
+`)
+
+func TestMain(t *testing.T) {
+
+	sk := "rxk1ly9+IPbBMJkcyUS53wYrh1owMDAwMDAwMDAwMDA="
+	pk := string(queenginePrivateKey)
+	task := NewLiscenseTask(sk, pk, "queengine-client", "queengine", "../data")
+	// task.Run(nil)
+
+	task.Active(&ActiveReq{
+		ActiveCode: "JhnRb0XwWl+pR6hIUoERhSJKEuoBoccCNShqRRseRhEa136h+r2/dxTuJ5MJD4vTRM32vx+Iq10/UAVuGAUQEuZfVaExRRb6wSw28/AFo5gkh0Os/gqyUj4M7bgULT4x0LGYAaCbGLRyRJ456RasJtk+5rZ7bcYwp5tOPZ920jSPXJg+hWpkjqU5YntjYI+/QzF8YHBYPfNNWqEfcvkJ/y6CwBK9NZx3PQYdHAnYUdG3U5odUSMZJcKKqwGVjAJNJBRA7/OJkqFFT4Mwg4PYzLF6hw2ZRqFNAv8J8FG9+1md+zeLUrwbJiIX865Og5Y71wSc6V9bms5/cqwc2TKTB04bvqigkW1yQWqf/Q7nll1h1fvQrBcUuBoA5lkOLkbksmXrKRbIfl2z3g6FPp918XBI2ImtUbbslaON5HaWDWE1gyQKBEbw8nOC0mgc3psQtO7VkfiHIRy+DCI5RL7ox49C5fHQ9k5fqrvNE+tikYhvc6EEA6IVIuRSKO/Kq8O7aWO7O9zLaHS39PCrGfHtgrmTDqrZQ5hlPhKlvY95iaOK6OgZVUN8F/9b3upvcxHeITLKmSIdV328K3Q0lfu4eeauHJ2B47MtTAyRTTic7rk8v+yhOdVMZfODO46q1kOvDH0MD0QzMACW81kvuPg6asEPnswnyulLuYFqPonuwI8=",
+	})
+}

+ 251 - 0
sku3d/comm/liscense-utils.go

@@ -0,0 +1,251 @@
+package comm
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/pem"
+	"errors"
+	"fmt"
+	"hash"
+	"io"
+	"math"
+
+	"crypto/rand"
+
+	mrand "math/rand"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 实现php的sha1()方法,返回的是byte转换的字符串,如果需要转成16进制,可以使用hex.EncodeToString
+func GetSha1(str string) string {
+	h := sha1.New()
+	io.WriteString(h, str)
+	return string(h.Sum(nil))
+}
+
+// 右边补全字符串实现方法,主要实现php的str_pad()方法
+func StrPadRight(input string, padLength int, padString string) string {
+	output := ""
+	inputLen := len(input)
+	if inputLen >= padLength {
+		return input
+	}
+	ll := padLength - inputLen
+	for i := 1; i <= ll; i = i + len(padString) {
+		output += padString
+	}
+	return input + output
+}
+
+// url安全模式decode字符串
+func UrlSafeB64decode(str string) (result []byte) {
+	str = strings.Replace(str, "-", "+", -1)
+	str = strings.Replace(str, "_", "/", -1)
+	mod4 := len(str) % 4
+	if mod4 != 0 {
+		str = str + "===="[0:mod4]
+	}
+	result, _ = base64.StdEncoding.DecodeString(str)
+	return result
+}
+
+// base64字符串,替换转换为安全模式
+func UrlSafeB64encode(str string) (result string) {
+	str = strings.Replace(str, "+", "-", -1)
+	str = strings.Replace(str, "/", "_", -1)
+	return str
+}
+
+// 使用aes-256-gcm方式解密字符串,主要针对php的openssl_decrypt()方法,注意在php7.1后增加了tag和add参数
+func DecodeAesGcm(encryptData string, hex_key string, hex_add string) ([]byte, error) {
+	tagSize := 16 //nonceSize,tag的长度,用于open时候生成tag,默认12
+	key := []byte(hex_key)
+	add := []byte(hex_add)
+	block, err := aes.NewCipher(key) //生成加解密用的block
+	if err != nil {
+		return []byte(""), err
+	}
+	//根据不同加密算法,也有不同tag长度的方法设定和调用,比如NewGCMWithTagSize、newGCMWithNonceAndTagSize
+	aesgcm, err := cipher.NewGCMWithNonceSize(block, tagSize)
+	if err != nil {
+		return []byte(""), err
+	}
+	decodeEncryptStr := UrlSafeB64decode(encryptData)
+	ciphertext := decodeEncryptStr
+	if len(ciphertext) <= aesgcm.NonceSize() { // 长度应该>iv
+		return []byte(""), errors.New("string: too short") //解密失败
+	}
+	iv := ciphertext[:aesgcm.NonceSize()]        //分离出IV
+	ciphertext = ciphertext[aesgcm.NonceSize():] // 密文,tag是调用open方法时候通过密文和前面new时候传的size来进行截取的
+	plaintext, err := aesgcm.Open(nil, iv, ciphertext, add)
+	return plaintext, err
+}
+
+// 使用aes-256-gcm加密数据,主要针对php的openssl_encrypt()方法,注意在php7.1后增加了tag和add参数
+func EncodeAesGcm(data string, hex_key string, hex_add string) (result string, error error) {
+	tagSize := 16 //nonceSize,tag的长度,用于open时候生成tag,默认12
+	key := []byte(hex_key)
+	add := []byte(hex_add)
+	block, err := aes.NewCipher(key) //生成加解密用的block
+	if err != nil {
+		return result, err
+	}
+	//根据不同加密算法,也有不同tag长度的方法设定和调用,比如NewGCMWithTagSize、newGCMWithNonceAndTagSize
+	aesgcm, err := cipher.NewGCMWithNonceSize(block, tagSize)
+	if err != nil {
+		return result, err
+	}
+	plaintext := []byte(data)
+	iv := make([]byte, tagSize)                       // NonceSize=12
+	rand.Read(iv)                                     //获取随机值
+	ciphertext := aesgcm.Seal(iv, iv, plaintext, add) //加密,密文为:iv+密文+tag
+	result = base64.StdEncoding.EncodeToString(ciphertext)
+	result = UrlSafeB64encode(result)
+	return result, nil // 生成的BS64
+}
+
+func GenSecretKey(appId string, appSecret string) string {
+
+	key := appId + "&" + appSecret
+	sha1_str := GetSha1(key)
+	str10 := "0"
+	pack64, _ := strconv.ParseUint(str10, 10, 32) //对应php的pack()方法,字符串转换为uint类型
+
+	pack32 := uint32(pack64)
+	str_pad := StrPadRight(sha1_str, 32, fmt.Sprintf("%d", pack32))
+	return base64.StdEncoding.EncodeToString([]byte(str_pad))
+}
+
+// 加密
+func Encode(secretKey string, data []byte) (string, error) {
+	decodeSecretKeyByte, _ := base64.StdEncoding.DecodeString(secretKey)
+	c, e := EncodeAesGcm(string(data), string(decodeSecretKeyByte), "")
+	if e != nil {
+		return "", e
+	}
+	return c, nil
+}
+
+// 加密
+func Decode(secretKey string, encryptData string) ([]byte, error) {
+	decodeSecretKeyByte, _ := base64.StdEncoding.DecodeString(secretKey)
+	decodeSecretKey := string(decodeSecretKeyByte)
+	//$data = openssl_decrypt(substr($decodeEncrypt, 16, -16), 'aes-256-gcm', $decodeSecret, 1, substr($decodeEncrypt, 0, 16), substr($decodeEncrypt, -16, 16));
+	p, e := DecodeAesGcm(encryptData, decodeSecretKey, "")
+	if e != nil {
+		return nil, e
+	}
+	return p, nil
+}
+
+func decryptOAEP(pritKey []byte, cipherdata []byte, label []byte) ([]byte, error) {
+
+	block, _ := pem.Decode(pritKey)
+	if block == nil {
+		err := fmt.Errorf("failed to parse certificate PEM")
+		return nil, err
+	}
+
+	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) // ASN.1 PKCS#1 DER encoded form.
+	if err != nil {
+		return nil, err
+	}
+
+	h := sha256.New()
+	return DecryptOAEPLong(h, rand.Reader, priv, cipherdata, label)
+}
+
+func EncryptOAEPLong(hash hash.Hash, random io.Reader, public *rsa.PublicKey, msg []byte, label []byte) ([]byte, error) {
+
+	msgLen := len(msg)
+
+	step := public.Size() - 2*hash.Size() - 2
+
+	var encryptedBytes []byte
+
+	for start := 0; start < msgLen; start += step {
+
+		finish := start + step
+
+		if finish > msgLen {
+
+			finish = msgLen
+
+		}
+
+		encryptedBlockBytes, err := rsa.EncryptOAEP(hash, random, public, msg[start:finish], label)
+
+		if err != nil {
+
+			return nil, err
+
+		}
+
+		encryptedBytes = append(encryptedBytes, encryptedBlockBytes...)
+
+	}
+
+	return encryptedBytes, nil
+
+}
+
+func DecryptOAEPLong(hash hash.Hash, random io.Reader, private *rsa.PrivateKey, msg []byte, label []byte) ([]byte, error) {
+
+	msgLen := len(msg)
+
+	step := private.PublicKey.Size()
+
+	var decryptedBytes []byte
+
+	for start := 0; start < msgLen; start += step {
+
+		finish := start + step
+
+		if finish > msgLen {
+
+			finish = msgLen
+
+		}
+
+		decryptedBlockBytes, err := rsa.DecryptOAEP(hash, random, private, msg[start:finish], label)
+
+		if err != nil {
+
+			return nil, err
+
+		}
+
+		decryptedBytes = append(decryptedBytes, decryptedBlockBytes...)
+
+	}
+	return decryptedBytes, nil
+}
+
+func encryptOAEP(pubkey []byte, msg []byte, label []byte) ([]byte, error) {
+	block, _ := pem.Decode(pubkey)
+	if block == nil {
+		err := fmt.Errorf("failed to parse certificate PEM")
+		return nil, err
+	}
+
+	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		return nil, err
+	}
+	rsaPublicKey := pub.(*rsa.PublicKey)
+
+	h := sha256.New() // sha1.New() or md5.New()
+	return EncryptOAEPLong(h, rand.Reader, rsaPublicKey, msg, label)
+}
+
+func radomInt() int32 {
+	mrand.Seed(time.Now().UnixNano())
+	return mrand.Int31n(math.MaxInt32)
+}

+ 61 - 0
sku3d/comm/liscense-utils_test.go

@@ -0,0 +1,61 @@
+package comm
+
+import (
+	"encoding/hex"
+	"fmt"
+	"math/rand"
+	"testing"
+	"time"
+)
+
+// 长度为62
+var bytes []byte = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890")
+
+func init() {
+	// 保证每次生成的随机数不一样
+	rand.Seed(time.Now().UnixNano())
+}
+
+// 方法一
+func RandStr1(n int) string {
+	result := make([]byte, n)
+	for i := 0; i < n; i++ {
+		result[i] = bytes[rand.Int31()%62]
+	}
+	return string(result)
+}
+
+// 方法二
+func RandStr2(n int) string {
+	result := make([]byte, n/2)
+	rand.Read(result)
+	return hex.EncodeToString(result)
+}
+
+func TestRandom32(t *testing.T) {
+	fmt.Println(RandStr1(32))
+}
+
+func TestAppKey(t *testing.T) {
+	appId := "screen"
+	appKey := "zjIWxbpOxE9lQaRuiO1lLmed7UqWYVjo"
+	secrate := GenSecretKey(appId, appKey)
+
+	t.Error("xxxxx")
+	fmt.Printf("appId %s key: %s => %s \n", appId, appKey, secrate)
+}
+
+func TestEncode(t *testing.T) {
+
+	secrate := "rxk1ly9+IPbBMJkcyUS53wYrh1owMDAwMDAwMDAwMDA="
+	c, e := Encode(secrate, []byte("hello world"))
+	if e != nil {
+		t.Error(e)
+	}
+	fmt.Printf("encoded => %s \n", c)
+	ret, e := Decode(secrate, c)
+	if e != nil {
+		t.Error(e)
+	}
+	fmt.Printf("decode => %s \n", string(ret))
+}

+ 226 - 0
sku3d/comm/mat.go

@@ -0,0 +1,226 @@
+package comm
+
+import (
+	"math"
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type MaterailUv struct {
+	Scale   float32 `bson:"scale" json:"scale"`
+	Rotate  float32 `bson:"rotate" json:"rotate"`
+	OffsetX float32 `bson:"offsetX" json:"offsetX"`
+	OffsetY float32 `bson:"offsetY" json:"offsetY"`
+}
+
+type MaterialConf struct {
+	MatId     primitive.ObjectID `bson:"matId,omitempty" json:"matId"` //材质球Id
+	Thumbnail *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Name      string             `bson:"name,omitempty" json:"name"`
+	CusNum    string             `bson:"cusNum,omitempty" json:"cusNum"` //编号
+
+	NormalMap      *OssType `bson:"normalMap,omitempty" json:"normalMap"`
+	DisplaceMap    *OssType `bson:"displaceMap,omitempty" json:"displaceMap"` //置换贴图
+	DisplaceFactor *float64 `bson:"displaceFactor,omitempty" json:"displaceFactor"`
+	BaseMap        *OssType `bson:"baseMap,omitempty" json:"baseMap"`
+	BaseColor      *Vect3   `bson:"baseColor,omitempty" json:"baseColor"`
+	RoughMap       *OssType `bson:"roughMap,omitempty" json:"roughMap"`
+	RoughFactor    *float64 `bson:"roughFactor,omitempty" json:"roughFactor"`
+	MetalMap       *OssType `bson:"metalMap,omitempty" json:"metalMap"`
+	MetalFactor    *float64 `bson:"metalFactor,omitempty" json:"metalFactor"`
+
+	DiffuseMap   *OssType `bson:"diffuseMap,omitempty" json:"diffuseMap"`
+	DiffuseColor *Vect3   `bson:"diffuseColor,omitempty" json:"diffuseColor"`
+	GlossMap     *OssType `bson:"glossMap,omitempty" json:"glossMap"` //光泽贴图
+	GlossFactor  *float64 `bson:"glossFactor,omitempty" json:"glossFactor"`
+	SpecMap      *OssType `bson:"specMap,omitempty" json:"specMap"`     //高光贴图
+	SpecColor    *Vect3   `bson:"specColor,omitempty" json:"specColor"` //高光贴图
+
+	OpacMap    *OssType `bson:"opacMap,omitempty" json:"opacMap"`       //透明度
+	OpacFactor *float64 `bson:"opacFactor,omitempty" json:"opacFactor"` //透明度贴图
+
+	Type string `bson:"type,omitempty" json:"type"` //spec meta  //高光流 金属流(默认)
+
+	Uvtransform *MaterailUv `bson:"uv,omitempty" json:"uv"`
+
+	CusUvTransform *MaterailUv `bson:"cusUv,omitempty" json:"cusUv"`         //用户定义的uv
+	LogicType      string      `bson:"logicType,omitempty" json:"logicType"` //fabric3d fabric2d color //3d面料 2d图片面料 color //特殊材质球
+	UserData       interface{} `bson:"userData,omitempty" json:"userData"`   //指定要数据放置的字段
+}
+
+type SpecialMat struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"` //材质球Id
+	Thumbnail *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Name      string             `bson:"name,omitempty" json:"name"`
+
+	NormalMap      *OssType `bson:"normalMap,omitempty" json:"normalMap"`
+	DisplaceMap    *OssType `bson:"displaceMap,omitempty" json:"displaceMap"` //置换贴图
+	DisplaceFactor *float64 `bson:"displaceFactor,omitempty" json:"displaceFactor"`
+	BaseMap        *OssType `bson:"baseMap,omitempty" json:"baseMap"`
+	BaseColor      *Vect3   `bson:"baseColor,omitempty" json:"baseColor"`
+	RoughMap       *OssType `bson:"roughMap,omitempty" json:"roughMap"`
+	RoughFactor    *float64 `bson:"roughFactor,omitempty" json:"roughFactor"`
+	MetalMap       *OssType `bson:"metalMap,omitempty" json:"metalMap"`
+	MetalFactor    *float64 `bson:"metalFactor,omitempty" json:"metalFactor"`
+
+	DiffuseMap   *OssType `bson:"diffuseMap,omitempty" json:"diffuseMap"`
+	DiffuseColor *Vect3   `bson:"diffuseColor,omitempty" json:"diffuseColor"`
+	GlossMap     *OssType `bson:"glossMap,omitempty" json:"glossMap"` //光泽贴图
+	GlossFactor  *float64 `bson:"glossFactor,omitempty" json:"glossFactor"`
+	SpecMap      *OssType `bson:"specMap,omitempty" json:"specMap"`     //高光贴图
+	SpecColor    *Vect3   `bson:"specColor,omitempty" json:"specColor"` //高光贴图
+
+	OpacMap    *OssType `bson:"opacMap,omitempty" json:"opacMap"`       //透明度
+	OpacFactor *float64 `bson:"OpacFactor,omitempty" json:"OpacFactor"` //透明度贴图
+
+	Type string `bson:"type,omitempty" json:"type"` //spec meta  //高光流 金属流(默认)
+
+	Category string `bson:"category,omitempty" json:"category"`
+
+	Uvtransform *MaterailUv `bson:"uv,omitempty" json:"uv"`
+
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+}
+
+type LineMat struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"` //材质球Id
+	Thumbnail *OssType           `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Name      string             `bson:"name,omitempty" json:"name"`
+
+	NormalMap      *OssType `bson:"normalMap,omitempty" json:"normalMap"`
+	DisplaceMap    *OssType `bson:"displaceMap,omitempty" json:"displaceMap"` //置换贴图
+	DisplaceFactor *float64 `bson:"displaceFactor,omitempty" json:"displaceFactor"`
+	BaseMap        *OssType `bson:"baseMap,omitempty" json:"baseMap"`
+	BaseColor      *Vect3   `bson:"baseColor,omitempty" json:"baseColor"`
+	RoughMap       *OssType `bson:"roughMap,omitempty" json:"roughMap"`
+	RoughFactor    *float64 `bson:"roughFactor,omitempty" json:"roughFactor"`
+	MetalMap       *OssType `bson:"metalMap,omitempty" json:"metalMap"`
+	MetalFactor    *float64 `bson:"metalFactor,omitempty" json:"metalFactor"`
+
+	DiffuseMap   *OssType `bson:"diffuseMap,omitempty" json:"diffuseMap"`
+	DiffuseColor *Vect3   `bson:"diffuseColor,omitempty" json:"diffuseColor"`
+	GlossMap     *OssType `bson:"glossMap,omitempty" json:"glossMap"` //光泽贴图
+	GlossFactor  *float64 `bson:"glossFactor,omitempty" json:"glossFactor"`
+	SpecMap      *OssType `bson:"specMap,omitempty" json:"specMap"`     //高光贴图
+	SpecColor    *Vect3   `bson:"specColor,omitempty" json:"specColor"` //高光贴图
+
+	OpacMap    *OssType `bson:"opacMap,omitempty" json:"opacMap"`       //透明度
+	OpacFactor *float64 `bson:"OpacFactor,omitempty" json:"OpacFactor"` //透明度贴图
+
+	Type string `bson:"type,omitempty" json:"type"` //spec meta  //高光流 金属流(默认)
+
+	Category string `bson:"category,omitempty" json:"category"`
+
+	Uvtransform *MaterailUv `bson:"uv,omitempty" json:"uv"`
+
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+}
+
+func (m *SpecialMat) Copy2MaterialConf() *MaterialConf {
+	return &MaterialConf{
+		MatId:          m.Id,
+		Thumbnail:      m.Thumbnail,
+		Name:           m.Name,
+		NormalMap:      m.NormalMap,
+		DisplaceMap:    m.DiffuseMap,
+		DisplaceFactor: m.DisplaceFactor,
+		BaseMap:        m.BaseMap,
+		BaseColor:      m.BaseColor,
+		RoughMap:       m.RoughMap,
+		RoughFactor:    m.RoughFactor,
+		MetalMap:       m.MetalMap,
+		MetalFactor:    m.MetalFactor,
+		DiffuseMap:     m.DiffuseMap,
+		DiffuseColor:   m.DiffuseColor,
+		GlossMap:       m.GlossMap,
+		GlossFactor:    m.GlossFactor,
+		SpecMap:        m.SpecMap,
+		SpecColor:      m.SpecColor,
+		OpacMap:        m.OpacMap,
+		OpacFactor:     m.OpacFactor,
+		Type:           m.Type,
+		Uvtransform:    m.Uvtransform,
+	}
+}
+
+const MIN = 0.0001
+
+// MIN 为用户自定义的比较精度
+func IsEqualFloat32(f1, f2 float32) bool {
+	return math.Dim(float64(f1), float64(f2)) < MIN
+}
+func IsEqualFloat64(f1, f2 float64) bool {
+	return math.Dim(f1, f2) < MIN
+}
+
+func IsEqualboolp(b1p, b2p *bool) bool {
+	b1 := false
+	b2 := false
+	if b1p != nil {
+		b1 = *b1p
+	}
+	if b2p != nil {
+		b2 = *b2p
+	}
+	return b1 == b2
+}
+
+func IsEqualFloat64p(f1p, f2p *float64) bool {
+	f1 := 0.0
+	f2 := 0.0
+	if f1p != nil {
+		f1 = *f1p
+	}
+	if f2p != nil {
+		f2 = *f2p
+	}
+	return math.Dim(f1, f2) < MIN
+}
+
+func (mat *MaterialConf) IsEqual(mat2 *MaterialConf) bool {
+	changed := false
+	if mat.BaseMap == nil || mat2.BaseMap == nil {
+		changed = changed || mat.BaseMap != mat2.BaseMap
+	} else {
+		changed = changed || (*mat.BaseMap).Url != (*mat2.BaseMap).Url
+	}
+
+	if mat.NormalMap == nil || mat2.NormalMap == nil {
+		changed = changed || mat.NormalMap != mat2.NormalMap
+	} else {
+		changed = changed || (*mat.NormalMap).Url != (*mat2.NormalMap).Url
+	}
+	if mat.RoughMap == nil || mat2.RoughMap == nil {
+		changed = changed || mat.RoughMap != mat2.RoughMap
+	} else {
+		changed = changed || (*mat.RoughMap).Url != (*mat2.RoughMap).Url
+	}
+
+	if mat.SpecMap == nil || mat2.SpecMap == nil {
+		changed = changed || mat.SpecMap != mat2.SpecMap
+	} else {
+		changed = changed || (*mat.SpecMap).Url != (*mat2.SpecMap).Url
+	}
+
+	changed = changed || !IsEqualFloat64p(mat.RoughFactor, mat2.RoughFactor)
+	changed = changed || !IsEqualFloat64p(mat.MetalFactor, mat2.MetalFactor)
+
+	if mat.MetalMap == nil || mat2.MetalMap == nil {
+		changed = changed || mat.MetalMap != mat2.MetalMap
+	} else {
+		changed = changed || (*mat.MetalMap).Url != (*mat2.MetalMap).Url
+	}
+	if changed {
+		return false
+	}
+
+	if mat.Uvtransform == nil || mat2.Uvtransform == nil {
+		changed = mat2.Uvtransform == nil
+	} else {
+		changed = changed || !IsEqualFloat32(mat.Uvtransform.OffsetX, mat2.Uvtransform.OffsetX) ||
+			!IsEqualFloat32(mat.Uvtransform.Scale, mat2.Uvtransform.Scale) ||
+			!IsEqualFloat32(mat.Uvtransform.Rotate, mat2.Uvtransform.Rotate)
+	}
+	return !changed
+}

+ 40 - 0
sku3d/comm/mesh.go

@@ -0,0 +1,40 @@
+package comm
+
+import (
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"time"
+)
+
+type DesignMesh struct {
+	Osgjsbin  *OssType `bson:"osgjsbin,omitempty" json:"osgjsbin"`
+	Osgjs     *OssType `bson:"osgjs,omitempty" json:"osgjs"`
+	Geometries []string `bson:"geometries,omitempty" json:"geometries"`
+	File       *OssType `bson:"file,omitempty" json:"file"`
+	Shadow     *OssType `bson:"shadow,omitempty" json:"shadow,omitempty"`
+}
+
+//2D 面料
+type Mat2d struct {
+	Image       *OssType     `bson:"image,omitempty" json:"image"`
+	Name        string       `bson:"name,omitempty" json:"name"`
+	Uvtransform Uvtransform `bson:"uv,omitempty" json:"uv"`
+}
+
+type ImageMat struct {
+	Id        primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId    primitive.ObjectID `bson:"userId,omitempty" json:"userId"`
+	TeamId    string             `bson:"teamId,omitempty" json:"teamId"`       //所属团队Id
+	CompanyId string             `bson:"companyId,omitempty" json:"companyId"` //所属团队Id
+
+	Image      *OssType  `bson:"image,omitempty" json:"image"`
+	Thumbnail  *OssType  `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Name       string    `bson:"name,omitempty" json:"name"`
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime"`
+
+	IsPublic *bool `bson:"isPublic,omitempty" json:"isPublic"`
+	Platform *bool `bson:"platform,omitempty" json:"platform"` //是否属于平台
+
+	ColorCards []*Mat2d `bson:"colorCards,omitempty" json:"colorCards"` //2D面料
+	Categories []string `bson:"categories,omitempty" json:"categories"` //目录列表
+	State      int32    `bson:"state,omitempty" json:"state"`
+}

+ 50 - 0
sku3d/comm/migration.go

@@ -0,0 +1,50 @@
+package comm
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/golang-migrate/migrate/v4"
+	"github.com/golang-migrate/migrate/v4/database/mongodb"
+	_ "github.com/golang-migrate/migrate/v4/source/file"
+)
+
+func AppMongoMiration() {
+
+	///data/migrations==mongodb://root:3dshow@3dshow-mongo-alpha:27017/dbname?query&&/data/migrations==mongodb://user:password@host:port/dbname?query
+	config := os.Getenv("MONGO_MIGRATIONS")
+	if len(config) < 1 {
+		return
+	}
+
+	dbs := strings.Split(config, "&&")
+	fmt.Println("mongo_migrations_dbs: ", dbs)
+
+	for _, db := range dbs {
+
+		frags := strings.Split(db, "==")
+		if len(frags) != 2 {
+			panic("mongo-migration config err=>" + db)
+		}
+
+		sourc := frags[0]
+		dbUri := frags[1]
+		p := &mongodb.Mongo{}
+		d, err := p.Open(dbUri)
+		if err != nil {
+			panic("mongo-migration open err=>" + err.Error())
+		}
+		fileSource := fmt.Sprintf("file://%s", sourc)
+		fmt.Println("mongo_migrations_file_source: ", fileSource)
+		mi, err := migrate.NewWithDatabaseInstance(fileSource, "", d)
+		if err != nil {
+			panic("mongo-migration new instance err=>" + err.Error())
+		}
+		err = mi.Up()
+		if err != nil {
+			fmt.Println("mongo-migration up err=>" + err.Error())
+		}
+
+	}
+}

+ 76 - 0
sku3d/comm/nats-config-mongo.go

@@ -0,0 +1,76 @@
+package comm
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"os"
+	"strings"
+
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+type MongoDB struct {
+	Client   *mongo.Client
+	Database *mongo.Database
+}
+
+func (db *MongoDB) GetCollection(name string) *mongo.Collection {
+	return db.Database.Collection(name)
+}
+
+func createMongo(mongodns string) (*MongoDB, error) {
+	start := strings.LastIndex(mongodns, "/")
+	end := strings.LastIndex(mongodns, "?")
+	database := mongodns[start+1 : end]
+	fmt.Println("mongo-format database => ", database)
+	if len(database) < 1 {
+		return nil, errors.New("config is not format of mongodb://root:root@172.17.0.1:37017/comm?authSource=admin")
+	}
+	clientOptions := options.Client().ApplyURI(mongodns)
+	// 连接到MongoDB
+	mgoCli, err := mongo.Connect(context.TODO(), clientOptions)
+	if err != nil {
+		panic(err)
+	}
+
+	// 检查连接
+	err = mgoCli.Ping(context.TODO(), nil)
+	if err != nil {
+		panic(err)
+	}
+	return &MongoDB{Client: mgoCli, Database: mgoCli.Database(database)}, nil
+}
+
+func (bus *NatsBus) NewMongoDBFromConfig(mongoConfKey string, timeout ...interface{}) (*MongoDB, error) {
+	//mongodb://root:root@172.17.0.1:37017/comm?authSource=admin
+	mongodns, err := bus.RequestConfig(mongoConfKey)
+	if err != nil {
+		return nil, err
+	}
+
+	mongoenv := os.Getenv("MONGO")
+	if len(mongoenv) > 0 {
+		mongodns = mongoenv
+	}
+	fmt.Println("config mongodns => ", mongodns)
+	fmt.Println("env mongodns => ", mongodns)
+
+	return createMongo(mongodns)
+}
+
+func (bus *NatsBus) NewMongoDBFromConfigDev(mongoConfKey string, timeout ...interface{}) (*MongoDB, error) {
+	//mongodb://root:root@172.17.0.1:37017/comm?authSource=admin
+	mongodns, err := bus.RequestConfigDev(mongoConfKey)
+	if err != nil {
+		return nil, err
+	}
+	mongoenv := os.Getenv("MONGO")
+	if len(mongoenv) > 0 {
+		mongodns = mongoenv
+	}
+	fmt.Println("config mongodns => ", mongodns)
+	fmt.Println("env mongodns => ", mongodns)
+	return createMongo(mongodns)
+}

+ 68 - 0
sku3d/comm/nats-config-redis.go

@@ -0,0 +1,68 @@
+package comm
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+
+	"github.com/go-redis/redis/v8"
+)
+
+func createRedisClient(rediscnf string) (*redis.Client, error) {
+	redisOps := strings.Split(rediscnf, "#")
+	if len(redisOps) < 2 {
+		return nil, fmt.Errorf("无效redis地址 需要127.0.0.1:16379#0#username#password当前 %s", rediscnf)
+	}
+
+	fmt.Println("redis-format addr => ", redisOps[0])
+
+	db, err := strconv.Atoi(redisOps[1])
+	if err != nil {
+		return nil, errors.New("非法redis数据库 id")
+	}
+
+	connOption := &redis.Options{
+		Addr: redisOps[0],
+		DB:   db, // use default DB
+	}
+
+	if len(redisOps) == 4 {
+		connOption.Username = redisOps[2]
+		connOption.Password = redisOps[3]
+	}
+	return redis.NewClient(connOption), err
+}
+
+func (bus *NatsBus) NewRedisFromConfig(redisConfKey string, timeout ...interface{}) (*redis.Client, error) {
+	//127.0.0.1:16379#0#username#password
+	rediscnf, err := bus.RequestConfig(redisConfKey)
+	if err != nil {
+		panic(err)
+	}
+
+	redisenv := os.Getenv("REDIS")
+	if len(redisenv) > 0 {
+		rediscnf = redisenv
+	}
+	fmt.Println("config rediscnf => ", rediscnf)
+	fmt.Println("env rediscnf => ", redisenv)
+
+	return createRedisClient(rediscnf)
+}
+
+func (bus *NatsBus) NewRedisFromConfigDev(redisConfKey string, timeout ...interface{}) (*redis.Client, error) {
+	//127.0.0.1:16379#0#username#password
+	rediscnf, err := bus.RequestConfigDev(redisConfKey)
+	if err != nil {
+		panic(err)
+	}
+	redisenv := os.Getenv("REDIS")
+	if len(redisenv) > 0 {
+		rediscnf = redisenv
+	}
+	fmt.Println("config rediscnf => ", rediscnf)
+	fmt.Println("env rediscnf => ", redisenv)
+	return createRedisClient(rediscnf)
+}

+ 84 - 0
sku3d/comm/nats-config.go

@@ -0,0 +1,84 @@
+package comm
+
+import "time"
+
+type RequstConfigOption struct {
+	ReqPayload interface{}
+	Timeout    time.Duration
+}
+
+type ReqConfiger struct {
+	Name string
+}
+
+type SetConfiger struct {
+	Name     string
+	Value    string
+	DevValue string
+}
+
+func (q *NatsBus) RequestConfig(key string, timoutOption ...interface{}) (string, error) {
+	data := &ReqConfiger{
+		Name: key,
+	}
+	timeout := 5 * time.Second
+	for _, option := range timoutOption {
+		if _, ok := option.(time.Duration); ok {
+			timeout = option.(time.Duration)
+			break
+		}
+	}
+	result := ""
+	err := q.RequestApi("request.configer", data, timeout, &result)
+	return result, err
+}
+
+func (q *NatsBus) SetConfig(key string, Value string, timoutOption ...interface{}) (string, error) {
+	data := &SetConfiger{
+		Name:  key,
+		Value: Value,
+	}
+	timeout := 5 * time.Second
+	for _, option := range timoutOption {
+		if _, ok := option.(time.Duration); ok {
+			timeout = option.(time.Duration)
+			break
+		}
+	}
+	result := ""
+	err := q.RequestApi("set.configer", data, timeout, &result)
+	return result, err
+}
+
+func (q *NatsBus) SetDevConfig(key string, Value string, timoutOption ...interface{}) (string, error) {
+	data := &SetConfiger{
+		Name:     key,
+		DevValue: Value,
+	}
+	timeout := 5 * time.Second
+	for _, option := range timoutOption {
+		if _, ok := option.(time.Duration); ok {
+			timeout = option.(time.Duration)
+			break
+		}
+	}
+	result := ""
+	err := q.RequestApi("set.configer", data, timeout, &result)
+	return result, err
+}
+
+func (q *NatsBus) RequestConfigDev(key string, timoutOption ...interface{}) (string, error) {
+	data := &ReqConfiger{
+		Name: key,
+	}
+	timeout := 5 * time.Second
+	for _, option := range timoutOption {
+		if _, ok := option.(time.Duration); ok {
+			timeout = option.(time.Duration)
+			break
+		}
+	}
+	result := ""
+	err := q.RequestApi("request.configer.dev", data, timeout, &result)
+	return result, err
+}

+ 30 - 0
sku3d/comm/nats-const-pay.go

@@ -0,0 +1,30 @@
+package comm
+
+import "fmt"
+
+const (
+	CommPayMallCreateQrApi  = "comm.pay.multiPay.createqr"
+	CommPaySku3dCreateQrApi = "comm.pay.singlePay.createqr"
+	// cloud
+	CommPayCloudCreateQrApi = "comm.pay.singlePay.createqr"
+
+	// refund
+	CommPayMallRefundApi = "comm.pay.multiPay.refund"
+	// refundquery
+	CommPayMallRefundQueryApi = "comm.pay.multiPay.refundQuery"
+)
+
+func NewAppPaySuccSubject(app string) string {
+	return fmt.Sprintf("%s-pay-succ#%s.paysucc#%s-paysucc-queue", app, app, app)
+}
+
+func NewAppPayFailSubject(app string) string {
+	return fmt.Sprintf("%s-pay-fail#%s.payfail#%s-payfail-queue", app, app, app)
+}
+
+var CommPayCloudSuccMsgSubject = NewAppPaySuccSubject("cloud")
+var CommPayCloudFailMsgSubject = NewAppPayFailSubject("cloud")
+var CommPaySku3dSuccMsgSubject = NewAppPaySuccSubject("sku3d")
+var CommPaySku3dFailMsgSubject = NewAppPayFailSubject("sku3d")
+var CommPayMallSuccMsgSubject = NewAppPaySuccSubject("mall")
+var CommPayMallFailMsgSubject = NewAppPayFailSubject("mall")

+ 12 - 0
sku3d/comm/nats-const.go

@@ -0,0 +1,12 @@
+package comm
+
+const (
+	PackPublicApi        = "pack.public.api"
+	PackPublicMessageApi = "pack.public.message"
+	PackPullMessageApi   = "pack.pull.message"
+
+	PackCommName       = "comm"
+	PackSku3dName      = "sku3d"
+	PackQueenCloudName = "cloud"
+	PackQueentreeName  = "tree"
+)

+ 149 - 0
sku3d/comm/nats-proxy.go

@@ -0,0 +1,149 @@
+package comm
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/nats-io/nats.go"
+)
+
+type ReplyerApi struct {
+	Subject string
+	Timeout time.Duration
+}
+
+type MessageApi struct {
+	Stream  string
+	Subject string
+}
+
+func CreateBusProxyReplyers(adpters map[string]*NatsBus, localBus *NatsBus) ([]*NatsMsgReplyer, error) {
+
+	out := []*NatsMsgReplyer{}
+
+	for pack, adapter := range adpters {
+		var remoteBus = adapter
+		if remoteBus == nil {
+			continue
+		}
+		//每个包创建一个代理
+		subject := fmt.Sprintf("%s.%s", pack, PackPublicApi)
+		out = append(out, &NatsMsgReplyer{
+			Subject: subject, //包名.PackPublicApi
+			Entity:  func() interface{} { return &ReqPackApi{} },
+			Cb2: func(msg *nats.Msg, payload interface{}) (interface{}, error) {
+				entity, ok := payload.(*ReqPackApi)
+				if !ok {
+					return nil, fmt.Errorf("subject %s need payload type of ReqPackApi", subject)
+				}
+				var ret interface{}
+				timout := 5 * time.Second
+				if entity.Timeout > 0 {
+					timout = entity.Timeout
+				}
+				fmt.Println("proxying api=>", entity.Subject, time.Now().Format("2006-01-02 15:04:05"), entity.Payload, remoteBus.nc.Opts.Url)
+				err := remoteBus.RequestApi(entity.Subject, entity.Payload, timout, &ret)
+				fmt.Println("proxying api back =>", entity.Subject, time.Now().Format("2006-01-02 15:04:05"))
+				return ret, err
+			},
+		})
+
+		//代理转发所有远程暴露消息
+
+		subjectPush := fmt.Sprintf("%s.%s", pack, PackPublicMessageApi)
+		out = append(out, &NatsMsgReplyer{
+			Subject: subjectPush, //包名.PackPublicMessageApi
+			Entity:  func() interface{} { return &ReqStreamApi{} },
+			Cb2: func(msg *nats.Msg, payload interface{}) (interface{}, error) {
+				pld, ok := payload.(*ReqStreamApi)
+				if !ok {
+					return nil, fmt.Errorf("subject %s need payload type of ReqStreamApi", subjectPush)
+				}
+				fmt.Println("proxying message api=>", pld.Subject, time.Now().Format("2006-01-02 15:04:05"))
+				err := remoteBus.PushMessage(pld.Subject, pld.Payload, pld.PubOpts...)
+				fmt.Println("proxying message api back=>", pld.Subject, time.Now().Format("2006-01-02 15:04:05"))
+				return true, err
+			},
+		})
+
+		//代理远程消息的返回转发
+		subjectPull := fmt.Sprintf("%s.%s", pack, PackPullMessageApi) //包名.PackPullMessageApi
+		out = append(out, &NatsMsgReplyer{
+			Subject: subjectPull,
+			Entity:  func() interface{} { return &ReqPullPackMessageApi{} },
+			Cb2: func(msg *nats.Msg, payload interface{}) (interface{}, error) {
+				pld, ok := payload.(*ReqPullPackMessageApi)
+				if !ok {
+					return nil, fmt.Errorf("subject %s need payload type of ReqPullPackMessageApi", subjectPull)
+				}
+				fmt.Println("proxying pull message api=>", pld.Subject, time.Now().Format("2006-01-02 15:04:05"))
+
+				err := remoteBus.CreateRemotePackMessageWatcher(pld, localBus)
+
+				fmt.Println("proxying pull message api back=>", pld.Subject, time.Now().Format("2006-01-02 15:04:05"))
+
+				return true, err
+			},
+		})
+	}
+	return out, nil
+}
+
+func (q *NatsBus) CreateRemotePackMessageWatcher(req *ReqPullPackMessageApi, localBus *NatsBus) error {
+
+	if q.runtimeStartStreamWatcher == nil {
+		return errors.New("remote bus is not running")
+	}
+
+	streamAndTopic := req.Subject
+
+	names := strings.Split(streamAndTopic, "#")
+	if len(names) < 2 || len(names[0]) < 1 || len(names[1]) < 1 {
+		return errors.New("streamAndTopic should like stream#topic")
+	}
+	queueName := ""
+	if len(names) == 2 {
+		v := strings.ReplaceAll(names[1], ".", "-")
+		queueName = v + "-queue"
+	} else {
+		queueName = names[2]
+	}
+
+	find := false
+	for _, w := range q.streamWacther {
+		if w.Stream == names[0] && w.Topic == names[1] && w.Queue == queueName {
+			find = true
+			break
+		}
+	}
+	if find {
+		return fmt.Errorf("pack message Subject %s has registered", streamAndTopic)
+	}
+
+	subjectCallBack := req.SubjectCallBack
+	ackWaitMinute := req.AckWaitMinute
+
+	watcher := &NatsStreamWather{
+		Topic:              names[1],
+		Stream:             names[0],
+		Queue:              queueName,
+		AckWaitMinuteFloat: ackWaitMinute,
+		Cb2: func(msg *nats.Msg) {
+			Resp := &RespPackMessage{}
+			fmt.Println("proxy message coming", names[1], names[0], queueName, "calling=>", subjectCallBack, "timeout minut", ackWaitMinute)
+			err := localBus.RequestApi(subjectCallBack, msg.Data, time.Duration(int64(ackWaitMinute*float64(time.Minute))), Resp)
+			fmt.Println("calling back error =>", err, Resp.CanAck)
+			if Resp.CanAck {
+				msg.Ack()
+			}
+		},
+	}
+
+	q.streamWacther = append(q.streamWacther, watcher)
+
+	q.runtimeStartStreamWatcher(watcher)
+
+	return nil
+}

+ 207 - 0
sku3d/comm/nats-stream.go

@@ -0,0 +1,207 @@
+package comm
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/nats-io/nats.go"
+)
+
+// 投递代理流消息
+func (q *NatsBus) PushPackMessage(streamAndTopic string, payload interface{}, options *RequestOptions) error {
+	pack := "comm"
+	timeout := time.Second * 5
+	pubOpts := []nats.PubOpt{}
+	if options != nil {
+		if len(options.DeployPack) > 0 {
+			pack = options.DeployPack
+		}
+		if options.Timeout > 0 {
+			timeout = options.Timeout
+		}
+
+		if len(options.PubOpts) > 0 {
+			pubOpts = options.PubOpts
+		}
+	}
+	names := strings.Split(streamAndTopic, "#")
+	if len(names) < 2 || len(names[0]) < 1 || len(names[1]) < 1 {
+		return errors.New("streamAndTopic should like stream#topic")
+	}
+
+	req := &ReqStreamApi{Payload: payload, Subject: fmt.Sprintf("%s#%s", names[0], names[1]), PubOpts: pubOpts}
+	payloadData, _ := json.Marshal(req)
+	msg, err := q.nc.Request(fmt.Sprintf("%s.%s", pack, PackPublicMessageApi), payloadData, timeout)
+	if err != nil {
+		return err
+	}
+	result := &NatsResponse{}
+	err = json.Unmarshal(msg.Data, result)
+	if err != nil {
+		return err
+	}
+	if result.ErrorNo != 200 {
+		return errors.New(result.ErrorDesc)
+	}
+	fmt.Println("-----------------------pushPackMessage-------------------------------------")
+	fmt.Println("pack:", pack)
+	fmt.Println("streamAndTopic:", streamAndTopic)
+	fmt.Println("streamName:", names[0])
+	fmt.Println("topic:", names[1])
+
+	return nil
+}
+
+// 消息回复者
+type ReqPullPackMessageApi struct {
+	Subject         string
+	Pack            string
+	AckWaitMinute   float64
+	SubjectCallBack string
+}
+
+func CreatePackMessageCallback(revEntity interface{}, callback PackMessageCallback) PackMessageCallback {
+	return func(entity interface{}) (*RespPackMessage, error) {
+		data := entity.(*[]byte)
+		json.Unmarshal(*data, revEntity)
+		return callback(revEntity)
+	}
+}
+
+// 获取其他部署包的流消息,初始话时调用有效,运行时调用,不能生效
+func (q *NatsBus) PullPackMessage(pack, streamAndTopic string, ackWaitMinute float64, callback PackMessageCallback) error {
+	if len(pack) < 1 {
+		return errors.New("pack 不能为空")
+	}
+	names := strings.Split(streamAndTopic, "#")
+	if len(names) < 2 || len(names[0]) < 1 || len(names[1]) < 1 {
+		return errors.New("streamAndTopic should like stream#topic")
+	}
+	find := false
+	subject := fmt.Sprintf("%s#%s", pack, streamAndTopic)
+	for _, w := range q.replyers {
+		if w.Subject == subject {
+			find = true
+			break
+		}
+	}
+	if find {
+		return fmt.Errorf("pack message Subject %s has registered", streamAndTopic)
+	}
+	if ackWaitMinute < 0.1 { //至少6s
+		ackWaitMinute = 0.1 //
+	}
+
+	//创建消息回调请求响应器
+	watcher := &NatsMsgReplyer{
+		Subject: subject,
+		Entity:  func() interface{} { return &[]byte{} },
+		Timeout: time.Duration(int64(ackWaitMinute * float64(time.Minute))),
+		Cb2: func(msg *nats.Msg, entity interface{}) (interface{}, error) { //true = ack
+			return callback(entity)
+		},
+	}
+	q.replyers = append(q.replyers, watcher)
+
+	//向bus发送PUllPackMessage 请求,由bus代理转发消息到本地Request watcher
+	req := &ReqPullPackMessageApi{
+		Subject:         streamAndTopic,
+		AckWaitMinute:   ackWaitMinute,
+		SubjectCallBack: subject,
+	}
+	q.RequestApi(fmt.Sprintf("%s.%s", pack, PackPullMessageApi), req, time.Second*5, nil)
+	fmt.Println("-----------------------pullPackMessage-------------------------------------")
+	fmt.Println("pack:", pack)
+	fmt.Println("streamAndTopic:", streamAndTopic)
+	fmt.Println("streamName:", names[0])
+	fmt.Println("topic:", names[1])
+
+	return nil
+}
+
+func (q *NatsBus) PullMessage(streamAndTopic string, callback WatcherCallback) error {
+	names := strings.Split(streamAndTopic, "#")
+	if len(names) < 2 || len(names[0]) < 1 || len(names[1]) < 1 {
+		return errors.New("streamAndTopic should like stream#topic")
+	}
+	queueName := ""
+	if len(names) == 2 {
+		v := strings.ReplaceAll(names[1], ".", "-")
+		queueName = v + "-queue"
+	} else {
+		queueName = names[2]
+	}
+
+	find := false
+	for _, w := range q.streamWacther {
+		if w.Stream == names[0] && w.Topic == names[1] && w.Queue == queueName {
+			find = true
+			break
+		}
+	}
+	if find {
+		return fmt.Errorf("pack message Subject %s has registered", streamAndTopic)
+	}
+
+	watcher := &NatsStreamWather{
+		Topic:  names[1],
+		Stream: names[0],
+		Queue:  queueName,
+		Entity: func() interface{} {
+			var enity interface{}
+			return &enity
+		},
+		Cb: callback,
+	}
+	q.streamWacther = append(q.streamWacther, watcher)
+	fmt.Println("-----------------------pullMessage-------------------------------------")
+	fmt.Println("streamAndTopic:", streamAndTopic)
+	fmt.Println("streamName:", names[0])
+	fmt.Println("topic:", names[1])
+
+	return nil
+}
+
+func (q *NatsBus) PushMessage(streamAndTopic string, data interface{}, opts ...nats.PubOpt) error {
+	names := strings.Split(streamAndTopic, "#")
+	if len(names) < 2 || len(names[0]) < 1 || len(names[1]) < 1 {
+		return errors.New("streamAndTopic should like stream#topic")
+	}
+	streamName := names[0]
+	topic := names[1]
+
+	Payload, err := json.Marshal(data)
+	if err != nil {
+		return err
+	}
+	js, err := q.nc.JetStream()
+	if err != nil {
+		return err
+	}
+
+	stream, _ := js.StreamInfo(streamName)
+
+	if stream == nil {
+		_, err = js.AddStream(&nats.StreamConfig{
+			Name:      streamName,
+			Subjects:  []string{topic},
+			Retention: nats.WorkQueuePolicy,
+		})
+		if err != nil {
+			return err
+		}
+		_, err = js.StreamInfo(streamName)
+		if err != nil {
+			return err
+		}
+	}
+	_, err = js.PublishAsync(topic, Payload, opts...)
+	fmt.Println("-----------------------pushMessage-------------------------------------")
+	fmt.Println("streamAndTopic:", streamAndTopic)
+	fmt.Println("streamName:", names[0])
+	fmt.Println("topic:", names[1])
+	return err
+}

+ 704 - 0
sku3d/comm/nats.go

@@ -0,0 +1,704 @@
+package comm
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os"
+	"os/signal"
+	"sync"
+	"syscall"
+	"time"
+
+	"github.com/nats-io/nats.go"
+)
+
+const (
+	ConnStateDisconned = 1
+	ConnStateReconned  = 2
+)
+
+// NATSQueue queue for work
+type NatsBus struct {
+	mu            sync.Mutex
+	nc            *nats.Conn
+	reconnChan    chan int
+	streamWacther []*NatsStreamWather
+	topicWatcher  []*NatsTopicWather
+
+	replyers []*NatsMsgReplyer
+
+	runtimeStartStreamWatcher StartStreamWatcherHandle
+}
+
+type StartStreamWatcherHandle func(conf *NatsStreamWather) error
+
+type NatsStreamWather struct {
+	Stream             string
+	Topic              string
+	Queue              string
+	AckWaitMinute      int64
+	AckWaitMinuteFloat float64
+	Entity             CreateEnity
+	Cb                 WatcherCallback
+	Cb2                WatcherCallback2
+	Exported           bool           //在其他部署包内,是否可以被调用
+	MaxDeliver         int            // 添加 MaxDeliver 配置
+	AckPolicy          nats.AckPolicy // 添加 AckPolicy 配置
+}
+
+// 消息回复者
+type NatsMsgReplyer struct {
+	Subject  string
+	Entity   CreateEnity
+	Exported bool          //在其他部署包内,是否可以被调用
+	Timeout  time.Duration //调用的最大时间
+	Cb       ReplyerHandle
+	Cb2      ReplyerHandle2
+}
+
+type NatsTopicWather struct {
+	Topic string
+	Cb    WatcherCallback
+}
+
+type Stream func(streamName string, topic string, group string)
+type ListenTopic func(topic string)
+type WatcherCallback func(msg *nats.Msg, entity interface{})
+type WatcherCallback2 func(msg *nats.Msg)
+
+type ReplyerHandle func(msg *nats.Msg, entity interface{}) interface{}
+type ReplyerHandle2 func(msg *nats.Msg, entity interface{}) (interface{}, error)
+type ReplyerHandle3 func(msg *nats.Msg) (interface{}, error)
+type CreateEnity func() interface{}
+
+type PackMessageCallback func(entity interface{}) (*RespPackMessage, error)
+type RespPackMessage struct {
+	CanAck bool
+}
+
+// 请求数据
+type NatsResponse struct {
+	ErrorNo   int
+	ErrorDesc string
+	Result    string //json字符串
+}
+
+type RequestOptions struct {
+	Timeout    time.Duration
+	PubOpts    []nats.PubOpt
+	DeployPack string
+}
+
+type ReqPayload struct {
+	Payload    interface{}
+	DeployPack string
+}
+
+type ReqPackApi struct {
+	Payload interface{}
+	Subject string
+	Timeout time.Duration
+}
+
+// req params
+type ReqStreamApi struct {
+	Subject string
+	Payload interface{}
+	PubOpts []nats.PubOpt
+}
+
+type CancelSubscribe func()
+
+type SubscribeCallback func(obj interface{}, msg *nats.Msg) interface{}
+
+type SubOption struct {
+	Sub  string
+	Obj  func() interface{}
+	Call SubscribeCallback
+}
+
+func (q *NatsBus) Subscribe(topic string, msg nats.MsgHandler) (*nats.Subscription, error) {
+	return q.nc.Subscribe(topic, msg)
+}
+
+func (q *NatsBus) SubscribeOnce(Option *SubOption) (CancelSubscribe, error) {
+	var sub *nats.Subscription
+	var cancel = func() {
+		if sub != nil {
+			sub.Unsubscribe()
+			sub = nil
+		}
+	}
+	s, err := q.nc.Subscribe(Option.Sub, func(msg *nats.Msg) {
+		var p interface{}
+		if Option.Obj != nil {
+			p = Option.Obj()
+			err := json.Unmarshal(msg.Data, p)
+			if err != nil {
+				msg.Term()
+				fmt.Println(Option.Sub, " payload parse msg err", err)
+				return
+			}
+		}
+		cancel()
+		out := Option.Call(p, msg)
+		if out != nil {
+			payload, _ := json.Marshal(out)
+			msg.Respond(payload)
+			return
+		}
+	})
+	if err != nil {
+		return nil, err
+	}
+	sub = s
+	return cancel, nil
+}
+
+func (q *NatsBus) QueueSubscribe(Option *SubOption) (CancelSubscribe, error) {
+
+	var sub *nats.Subscription
+	var cancel = func() {
+		if sub != nil {
+			sub.Unsubscribe()
+			sub = nil
+		}
+	}
+	s, err := q.nc.QueueSubscribe(Option.Sub, Option.Sub+".queue", func(msg *nats.Msg) {
+		var p interface{}
+		if Option.Obj != nil {
+			p = Option.Obj()
+			err := json.Unmarshal(msg.Data, p)
+			if err != nil {
+				msg.Term()
+				fmt.Println(Option.Sub, " payload parse msg err", err)
+				return
+			}
+		}
+		out := Option.Call(p, msg)
+		if out != nil {
+			payload, _ := json.Marshal(out)
+			msg.Respond(payload)
+			return
+		}
+	})
+	if err != nil {
+		return nil, err
+	}
+	sub = s
+	return cancel, nil
+}
+
+func (q *NatsBus) Publish(topic string, data []byte) error {
+	return q.nc.Publish(topic, data)
+}
+
+func (q *NatsBus) PublishObj(topic string, obj interface{}) error {
+	data, _ := json.Marshal(obj)
+	return q.nc.Publish(topic, data)
+}
+
+func (q *NatsBus) AddReplyers(w ...*NatsMsgReplyer) {
+	q.replyers = append(q.replyers, w...)
+}
+func (q *NatsBus) AddStreamWacher(w ...*NatsStreamWather) {
+	q.streamWacther = append(q.streamWacther, w...)
+}
+
+// 请求代理api
+
+func (q *NatsBus) RequestPackApi(subject string, Payload interface{}, out interface{}, options *RequestOptions) error {
+
+	pack := "comm"
+	timeout := time.Second * 5
+	if options != nil {
+		if len(options.DeployPack) > 0 {
+			pack = options.DeployPack
+		}
+		if options.Timeout > 0 {
+			timeout = options.Timeout
+		}
+	}
+	payload, _ := json.Marshal(&ReqPackApi{Payload: Payload, Subject: subject, Timeout: timeout})
+	msg, err := q.nc.Request(fmt.Sprintf("%s.%s", pack, PackPublicApi), payload, timeout)
+	if err != nil {
+		return err
+	}
+	result := &NatsResponse{}
+	err = json.Unmarshal(msg.Data, result)
+
+	if err != nil {
+		return err
+	}
+	if result.ErrorNo != 200 {
+		return errors.New(result.ErrorDesc)
+	}
+
+	if out != nil {
+		return json.Unmarshal([]byte(result.Result), out)
+	}
+	return nil
+}
+
+// 请求本地api
+func (q *NatsBus) RequestApi(subject string, data interface{}, timeout time.Duration, out interface{}) error {
+	// Lock so only one goroutine at a time can access the map c.v.
+	q.mu.Lock()
+	defer q.mu.Unlock()
+
+	payload := []byte{}
+	if data != nil {
+		payload, _ = json.Marshal(data)
+	}
+	msg, err := q.nc.Request(subject, payload, timeout)
+	if err != nil {
+		return err
+	}
+	result := &NatsResponse{}
+	err = json.Unmarshal(msg.Data, result)
+	if err != nil {
+		return err
+	}
+	if result.ErrorNo != 200 {
+		return errors.New(result.ErrorDesc)
+	}
+	if out != nil {
+		return json.Unmarshal([]byte(result.Result), out)
+	}
+	return nil
+}
+
+func (q *NatsBus) Request(subject string, data interface{}, timeout time.Duration, out interface{}) error {
+	payload := []byte{}
+	if data != nil {
+		payload, _ = json.Marshal(data)
+	}
+	msg, err := q.nc.Request(subject, payload, timeout)
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(msg.Data, out)
+}
+
+// 退出
+func (q *NatsBus) Quit() {
+	q.nc.Drain()
+	q.nc.Close()
+}
+
+func NewNatsBus2(natsUri string, MaxReconnect int, ReconnDelaySecond int, streamWacther []*NatsStreamWather, replyers []*NatsMsgReplyer) (*NatsBus, error) {
+
+	bus, err := NewNatsBus(natsUri, MaxReconnect, ReconnDelaySecond, streamWacther)
+	if err != nil {
+		return nil, err
+	}
+	bus.replyers = replyers
+
+	return bus, nil
+}
+
+// 可变参数
+func NewNatsBus3(natsUri string, MaxReconnect int, ReconnDelaySecond int, streamWacther []*NatsStreamWather, replyers ...*NatsMsgReplyer) (*NatsBus, error) {
+
+	bus, err := NewNatsBus(natsUri, MaxReconnect, ReconnDelaySecond, streamWacther)
+	if err != nil {
+		return nil, err
+	}
+	bus.replyers = replyers
+	return bus, nil
+}
+
+func NewNatsBus(natsUri string, MaxReconnect int, ReconnDelaySecond int, streamWacther []*NatsStreamWather) (*NatsBus, error) {
+
+	url := natsUri
+	if len(url) < 1 {
+		url = nats.DefaultURL
+	}
+
+	fmt.Println("conning to nats ====> ", url)
+
+	bus := &NatsBus{
+		reconnChan:    make(chan int),
+		streamWacther: streamWacther,
+		topicWatcher:  []*NatsTopicWather{},
+	}
+	nc, err := nats.Connect(url,
+		// nats.Name("render1"),
+		nats.MaxReconnects(MaxReconnect),
+		nats.ReconnectWait(time.Duration(ReconnDelaySecond*int(time.Second))),
+		nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
+			fmt.Printf("%s Got disconnected! Reason: %q\n", time.Now().Format("2006-01-02 15:04:05"), err)
+			bus.reconnChan <- ConnStateDisconned
+		}),
+		nats.ReconnectHandler(func(nc *nats.Conn) {
+			fmt.Printf("%s Got reconnected to %v!\n", time.Now().Format("2006-01-02 15:04:05"), nc.ConnectedUrl())
+			bus.reconnChan <- ConnStateReconned
+		}),
+
+		nats.ClosedHandler(func(nc *nats.Conn) {
+			tip := fmt.Sprintf("%s Connection closed. Reason: %q\n", time.Now().Format("2006-01-02 15:04:05"), nc.LastError())
+			fmt.Println(tip)
+			// panic(tip)
+		}),
+	)
+	if err != nil {
+		fmt.Println("conn to nats failed ====> ", url, err)
+		return nil, err
+	}
+	bus.nc = nc
+	fmt.Printf("%s nats bus Connected: %s\n", time.Now().Format("2006-01-02 15:04:05"), url)
+
+	return bus, nil
+}
+
+// 创建jetStream
+func (q *NatsBus) JetStream() (nats.JetStreamContext, error) {
+	return q.nc.JetStream()
+}
+
+// 创建jetStream
+func (q *NatsBus) CreateStream(streamName string, topic string) (nats.JetStreamContext, error) {
+
+	js, err := q.nc.JetStream()
+	if err != nil {
+		return nil, err
+	}
+	stream, _ := js.StreamInfo(streamName)
+	if stream != nil {
+		return js, nil
+	}
+
+	_, err = js.AddStream(&nats.StreamConfig{
+		Name:         streamName,
+		Subjects:     []string{topic},
+		Retention:    nats.WorkQueuePolicy,
+		MaxConsumers: 5,
+	})
+	if err != nil {
+		fmt.Println("bus CreateStream err=>", err.Error())
+		return nil, err
+	}
+
+	return js, nil
+}
+
+// 监听流数据
+func (q *NatsBus) WatchStream(ctxTerm, ctxConn context.Context, conf *NatsStreamWather) error {
+
+	streamName := conf.Stream
+	topic := conf.Topic
+	queue := conf.Queue
+	fmt.Println("Watching Stream ", conf.Stream, conf.Topic, conf.Queue)
+
+	//创建对应的jet stream 以便模型创建消息不回丢失
+	js, err := q.CreateStream(streamName, topic)
+	if err != nil {
+		fmt.Println("WatchStream  error=>", err.Error())
+		return err
+	}
+
+	// 设置消费者选项,包括 MaxDeliver
+	consumerConfig := &nats.ConsumerConfig{
+		Durable:       queue,
+		AckPolicy:     nats.AckExplicitPolicy, // 强制使用 AckExplicitPolicy
+		MaxDeliver:    conf.MaxDeliver,
+		FilterSubject: topic,
+	}
+
+	if conf.AckWaitMinute > 0 {
+		consumerConfig.AckWait = time.Duration(conf.AckWaitMinute * int64(time.Minute))
+	}
+
+	if conf.AckWaitMinuteFloat > 0 {
+		consumerConfig.AckWait = time.Duration(int64(conf.AckWaitMinuteFloat * float64(time.Minute)))
+	}
+
+	// 检查消费者是否存在
+	consumer, err := js.ConsumerInfo(streamName, queue)
+	if err == nil {
+		// 消费者存在,检查并更新配置
+		needUpdate := consumer.Config.MaxDeliver != conf.MaxDeliver ||
+			consumer.Config.AckWait != consumerConfig.AckWait
+
+		if needUpdate {
+			_, err = js.UpdateConsumer(streamName, consumerConfig)
+			if err != nil {
+				fmt.Println("WatchStream UpdateConsumer err ", conf.Stream, conf.Topic, err)
+				return err
+			}
+		}
+	} else {
+		// 消费者不存在,创建新的
+		_, err = js.AddConsumer(streamName, consumerConfig)
+		if err != nil {
+			fmt.Println("WatchStream AddConsumer err ", conf.Stream, conf.Topic, err)
+			return err
+		}
+	}
+
+	// 使用 pull 订阅模式
+	sub, err := js.PullSubscribe(topic, queue, nats.BindStream(streamName))
+	if err != nil {
+		fmt.Println("WatchStream QueueSubscribeSync err ", conf.Stream, conf.Topic, err)
+		return err
+	}
+	fmt.Println("Watching Stream succ=>", conf.Stream, conf.Topic, conf.Queue)
+
+	var currHandingMsg *nats.Msg = nil
+	for {
+		select {
+		case <-ctxTerm.Done():
+			fmt.Println("Watching Stream Termed", streamName, topic)
+			if currHandingMsg != nil {
+				err := currHandingMsg.Nak()
+				fmt.Println("terminate currMsg ", err)
+			}
+			//中断链接
+			q.Quit()
+			return nil
+		case <-ctxConn.Done():
+			fmt.Println("Watching Stream Conn closed", streamName, topic)
+			if currHandingMsg != nil {
+				err := currHandingMsg.Nak()
+				fmt.Println("terminate currMsg ", err)
+			}
+			return nil
+
+		default:
+			currHandingMsg = nil
+			msgs, err := sub.Fetch(1)
+			if err == nil && len(msgs) > 0 {
+				msg := msgs[0]
+				currHandingMsg = msg
+
+				meta, _ := msg.Metadata()
+
+				fmt.Println("meta for ", meta.Stream, "#", topic, "#", meta.Consumer, "#")
+				fmt.Println("meta NumDelivered", meta.NumDelivered, "NumPending ", meta.NumPending)
+				fmt.Println("meta Sequence.Consumer", meta.Sequence.Consumer, "Sequence.Stream ", meta.Sequence.Stream)
+
+				if conf.Cb2 != nil {
+					conf.Cb2(msg)
+				} else if conf.Cb != nil {
+					var req interface{}
+					if conf.Entity != nil {
+						req = conf.Entity()
+					}
+					err = json.Unmarshal(msg.Data, req)
+					if err != nil {
+						msg.Term()
+						fmt.Println("work msg err=>", err.Error())
+						continue
+					}
+					conf.Cb(msg, req)
+				} else {
+					fmt.Println("error no stream wather callback!!!!")
+				}
+				currHandingMsg = nil
+			}
+		}
+	}
+}
+
+// 监听流数据
+func (q *NatsBus) WatchStream1(ctxTerm, ctxConn context.Context, conf *NatsStreamWather) error {
+
+	streamName := conf.Stream
+	topic := conf.Topic
+	queue := conf.Queue
+	fmt.Println("Watching Stream ", conf.Stream, conf.Topic, conf.Queue)
+
+	//创建对应的jet stream 以便模型创建消息不回丢失
+	js, err := q.CreateStream(streamName, topic)
+	if err != nil {
+		fmt.Println("WatchStream  error=>", err.Error())
+		return err
+	}
+
+	opts := []nats.SubOpt{nats.BindStream(streamName), nats.MaxDeliver(5)} //nats.MaxDeliver(5)
+
+	if conf.AckWaitMinute > 0 {
+		opts = append(opts, nats.AckWait(time.Duration(conf.AckWaitMinute*int64(time.Minute))))
+	}
+
+	if conf.AckWaitMinuteFloat > 0 {
+		opts = append(opts, nats.AckWait(time.Duration(int64(conf.AckWaitMinuteFloat*float64(time.Minute)))))
+	}
+
+	sub, err := js.PullSubscribe(topic, queue, opts...)
+	if err != nil {
+		fmt.Println("WatchStream QueueSubscribeSync err ", conf.Stream, conf.Topic, err)
+		return err
+	}
+	fmt.Println("Watching Stream succ=>", conf.Stream, conf.Topic, conf.Queue)
+
+	var currHandingMsg *nats.Msg = nil
+	for {
+		select {
+		case <-ctxTerm.Done():
+			fmt.Println("Watching Stream Termed", streamName, topic)
+			if currHandingMsg != nil {
+				err := currHandingMsg.Nak()
+				fmt.Println("terminate currMsg ", err)
+			}
+			//中断链接
+			q.Quit()
+			return nil
+		case <-ctxConn.Done():
+			fmt.Println("Watching Stream Conn closed", streamName, topic)
+			if currHandingMsg != nil {
+				err := currHandingMsg.Nak()
+				fmt.Println("terminate currMsg ", err)
+			}
+			return nil
+
+		default:
+			currHandingMsg = nil
+			msgs, err := sub.Fetch(1)
+			if err == nil && len(msgs) > 0 {
+				msg := msgs[0]
+				currHandingMsg = msg
+
+				meta, _ := msg.Metadata()
+
+				fmt.Println("meta for ", meta.Stream, "#", topic, "#", meta.Consumer, "#")
+				fmt.Println("meta NumDelivered", meta.NumDelivered, "NumPending ", meta.NumPending)
+				fmt.Println("meta Sequence.Consumer", meta.Sequence.Consumer, "Sequence.Stream ", meta.Sequence.Stream)
+
+				if conf.Cb2 != nil {
+					conf.Cb2(msg)
+				} else if conf.Cb != nil {
+					var req interface{}
+					if conf.Entity != nil {
+						req = conf.Entity()
+					}
+					err = json.Unmarshal(msg.Data, req)
+					if err != nil {
+						msg.Term()
+						fmt.Println("work msg err=>", err.Error())
+						continue
+					}
+					conf.Cb(msg, req)
+				} else {
+					fmt.Println("error no stream wather callback!!!!")
+				}
+				currHandingMsg = nil
+			}
+		}
+	}
+}
+func (q *NatsBus) GetNatsConn() *nats.Conn {
+	return q.nc
+}
+
+func (q *NatsBus) Run(cb func()) {
+	//ctx, cancel := context.WithCancel(context.Background())
+	//ctx, stop := context.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
+	ctxTerm, cancelTerm := context.WithCancel(context.Background())
+	ctxConn, cancelCnn := context.WithCancel(context.Background())
+
+	defer cancelTerm()
+	defer cancelCnn()
+
+	go func() {
+		exit := make(chan os.Signal, 1)
+		signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
+		<-exit
+		cancelTerm()
+		time.Sleep(1 * time.Second)
+		os.Exit(1)
+	}()
+
+	var QueueSubscribe = func(rplyer *NatsMsgReplyer) {
+		r1 := rplyer
+		queue := fmt.Sprintf("%s.queue", r1.Subject)
+		q.nc.QueueSubscribe(r1.Subject, queue, func(msg *nats.Msg) {
+			defer func() {
+				if err := recover(); err != nil {
+					fmt.Println("catch error ", err)
+				}
+			}()
+
+			fmt.Println("replay for ", r1.Subject)
+			var req interface{}
+			if r1.Entity != nil {
+				req = r1.Entity()
+				err := json.Unmarshal(msg.Data, req)
+				if err != nil {
+					msg.Term()
+					fmt.Println("rplyer work msg err", err.Error())
+					return
+				}
+			}
+
+			if r1.Cb != nil {
+				out := r1.Cb(msg, req)
+				if out != nil {
+					payload, err := json.Marshal(out)
+					if err != nil {
+						fmt.Println("error reply for", r1.Subject, err)
+					}
+					err = msg.Respond(payload)
+					if err != nil {
+						fmt.Println("error reply for", r1.Subject, out)
+					}
+				}
+				return
+			}
+
+			if r1.Cb2 != nil {
+				out, err := r1.Cb2(msg, req)
+				resp := &NatsResponse{ErrorNo: 200}
+				if err != nil {
+					fmt.Println("error reply2 for", r1.Subject, err)
+					resp.ErrorDesc = err.Error()
+					resp.ErrorNo = 400
+				} else {
+					payload, _ := json.Marshal(out)
+					resp.Result = string(payload)
+				}
+				payload, _ := json.Marshal(resp)
+				err = msg.Respond(payload)
+				if err != nil {
+					fmt.Println("error reply2 for", r1.Subject, out)
+				}
+				return
+			}
+		})
+	}
+
+	var startSubscribe = func() {
+		//启动所有watcher
+		for _, item := range q.streamWacther {
+			go q.WatchStream(ctxTerm, ctxConn, item)
+		}
+
+		for _, rplyer := range q.replyers {
+			QueueSubscribe(rplyer)
+		}
+	}
+
+	q.runtimeStartStreamWatcher = func(conf *NatsStreamWather) error {
+		go q.WatchStream(ctxTerm, ctxConn, conf)
+		return nil
+	}
+
+	startSubscribe()
+	if cb != nil {
+		cb()
+	}
+	for {
+		select {
+		case state := <-q.reconnChan:
+			if state == ConnStateDisconned {
+				cancelCnn()
+			} else if state == ConnStateReconned {
+				ctxConn, cancelCnn = context.WithCancel(context.Background())
+
+				startSubscribe()
+			}
+		}
+	}
+}

+ 377 - 0
sku3d/comm/package.go

@@ -0,0 +1,377 @@
+package comm
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type DesignProduct struct { //设计单品信息
+	Id              primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	ProjectId       primitive.ObjectID `bson:"projectId,omitempty" json:"projectId"`
+	FromDbConfId    string             `bson:"fromDbConfId,omitempty" json:"fromDbConfId"` //数据库定义Id
+	FromAssetConfId string             `bson:"fromAssetConfId,omitempty" json:"fromAssetConfId"`
+	FromId          string             `bson:"fromId,omitempty" json:"fromId"`
+	Name            string             `bson:"name,omitempty"  json:"name"`
+	Thumbnail       *OssType           `bson:"thumbnail,omitempty"  json:"thumbnail"`
+
+	StaticMesh *StaticMeshSource `bson:"staticMesh,omitempty" json:"staticMesh"` //静态模型资产
+	UpdateTime time.Time         `bson:"updateTime,omitempty"  json:"updateTime"`
+}
+
+type ProductHeader struct {
+	Id              string    `bson:"id,omitempty"  json:"id"`
+	FromDbConfId    string    `bson:"fromDbConfId,omitempty" json:"fromDbConfId"`       //数据库定义Id
+	FromAssetConfId string    `bson:"fromAssetConfId,omitempty" json:"fromAssetConfId"` //数据包
+	FromId          string    `bson:"fromId,omitempty" json:"fromId"`
+	Name            string    `bson:"name,omitempty"  json:"name"`
+	Thumbnail       *OssType  `bson:"thumbnail,omitempty"  json:"thumbnail"`
+	CreateTime      time.Time `bson:"createTime,omitempty"  json:"createTime"`
+}
+
+type PackageProductCompMat struct {
+	Id      string   `bson:"id" json:"id"`
+	UvMap   *OssType `bson:"uvmap" json:"uvmap"`
+	UvSize  *UvSize  `bson:"uvsize" json:"uvsize"`
+	Name    string   `bson:"name" json:"name"`
+	MatId   string   `bson:"matId" json:"matId"`
+	GroupId string   `bson:"groupId" json:"groupId"` //部件分组Id
+	Index   int      `bson:"index" json:"index"`
+	Visible *bool    `bson:"visible" json:"visible"`
+	Locked  *bool    `bson:"locked" json:"locked"` //是否锁定
+
+	Images     []*ComponentImage `bson:"images" json:"images"`
+	PaperMatId string            `bson:"paperMatId" json:"paperMatId"`                 //纸张id
+	UserData   interface{}       `bson:"userData,omitempty" json:"userData,omitempty"` //自定义数据
+}
+
+type PackageProduct struct {
+	Id         string                   `bson:"id,omitempty" json:"id"`
+	GeomId     string                   `bson:"geomId,omitempty" json:"geomId"`
+	Name       string                   `bson:"name,omitempty" json:"name"`
+	CusNum     string                   `bson:"cusNum,omitempty" json:"cusNum,omitempty"`
+	Type       string                   `bson:"type,omitempty" json:"type,omitempty"` //类型
+	Thumbnail  *OssType                 `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Components []*PackageProductCompMat `bson:"components,omitempty" json:"components"`
+	UserData   interface{}              `bson:"userData,omitempty" json:"userData,omitempty"` //自定义数据
+}
+
+func (p *PackageProduct) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if p.Thumbnail != nil {
+		p.Thumbnail.UpdateSourceUrl(handler)
+	}
+	for _, c := range p.Components {
+		if c.UvMap != nil {
+			c.UvMap.UpdateSourceUrl(handler)
+		}
+	}
+}
+
+type PackageGeom struct {
+	Id          string   `bson:"id,omitempty" json:"id"`
+	Name        string   `bson:"name,omitempty" json:"name"`
+	Thumbnail   *OssType `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Osgjs       *OssType `bson:"osgjs,omitempty" json:"osgjs"`
+	File        *OssType `bson:"file,omitempty" json:"file"` //hdr or fbx
+	Glb         *OssType `bson:"glb,omitempty" json:"glb"`   //hdr or fbx
+	Shadow      *OssType `bson:"shadow,omitempty" json:"shadow,omitempty"`
+	BoundingBox *Obb     `bson:"boundingBox,omitempty" json:"boundingBox,omitempty"`
+
+	GenType string      `bson:"genType,omitempty" json:"genType,omitempty"` //生成方式构建类型staticMesh, paramBox //默认staticMesh类型
+	GenConf interface{} `bson:"genConf,omitempty" json:"genConf,omitempty"` //构建配置
+}
+
+func (g *PackageGeom) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if g.Thumbnail != nil {
+		g.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if g.Osgjs != nil {
+		g.Osgjs.UpdateSourceUrl(handler)
+	}
+	if g.File != nil {
+		g.File.UpdateSourceUrl(handler)
+	}
+	if g.Glb != nil {
+		g.Glb.UpdateSourceUrl(handler)
+	}
+	if g.Shadow != nil {
+		g.Shadow.UpdateSourceUrl(handler)
+	}
+}
+
+type PackageSceneProduct struct {
+	Id        string          `bson:"id,omitempty" json:"id"`
+	ProdId    string          `bson:"prodId,omitempty" json:"prodId"`
+	Transform *ModelTransform `bson:"transform,omitempty" json:"transform,omitempty"`
+	Visible   *bool           `bson:"visible,omitempty" json:"visible,omitempty"`
+	NoShadow  *bool           `bson:"noShadow,omitempty" json:"noShadow,omitempty"` //是否不显示阴影
+	UserData  interface{}     `bson:"userData,omitempty" json:"userData,omitempty"` //自定义数据
+}
+
+type PackageScene struct {
+	Id         string                 `bson:"id,omitempty" json:"id,omitempty"`
+	Name       string                 `bson:"name,omitempty" json:"name,omitempty"`
+	Thumbnail  *OssType               `bson:"thumbnail,omitempty" json:"thumbnail,omitempty"`
+	EnvId      string                 `bson:"envId,omitempty" json:"envId,omitempty"`
+	Products   []*PackageSceneProduct `bson:"products,omitempty" json:"products,omitempty"`
+	Lights     *SceneLight            `bson:"lights,omitempty" json:"lights,omitempty"`
+	Stickers   []*PackSceneSticker    `bson:"stickers,omitempty" json:"stickers,omitempty"`
+	CreateTime time.Time              `bson:"createTime,omitempty"  json:"createTime"`
+	UpdateTime time.Time              `bson:"updateTime,omitempty"  json:"updateTime"`
+	Background *Evn3dBackground       `bson:"background,omitempty"  json:"background"`      //添加背景配置
+	UserData   interface{}            `bson:"userData,omitempty" json:"userData,omitempty"` //自定义数据
+}
+
+func (s *PackageScene) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if s.Thumbnail != nil {
+		s.Thumbnail.UpdateSourceUrl(handler)
+	}
+	for _, tiker := range s.Stickers {
+		if tiker.Img != nil {
+			tiker.Img.UpdateSourceUrl(handler)
+		}
+	}
+	if s.Background != nil && s.Background.Image != nil {
+		s.Background.Image.UpdateSourceUrl(handler)
+	}
+}
+
+type PositionObj2d struct {
+	X int `bson:"x,omitempty"  json:"x"`
+	Y int `bson:"y,omitempty"  json:"y"`
+}
+
+type PackSceneSticker struct {
+	Id       string        `bson:"id,omitempty"  json:"id"`
+	Config   interface{}   `bson:"config,omitempty"  json:"config"`
+	Img      *OssType      `bson:"img,omitempty"  json:"img"`
+	Mirror   []int         `bson:"mirror,omitempty"  json:"mirror"`
+	Position PositionObj2d `bson:"position,omitempty"  json:"position"`
+	Rotation int           `bson:"rotation,omitempty"  json:"rotation"`
+	Scale    []float32     `bson:"scale,omitempty"  json:"scale"`
+	Visible  *bool         `bson:"visible,omitempty"  json:"visible"`
+}
+
+type PackageEnv3d struct {
+	Id         string           `bson:"id,omitempty" json:"id"`
+	Name       string           `bson:"name,omitempty" json:"name"`
+	CusNum     string           `bson:"cusNum,omitempty" json:"cusNum"` //自定义编号
+	Thumbnail  *OssType         `bson:"thumbnail,omitempty" json:"thumbnail"`
+	CreateTime time.Time        `bson:"createTime,omitempty" json:"createTime"`
+	HDR        *OssType         `bson:"hdr,omitempty" json:"hdr"`
+	Config     *Evn3dHdrConf    `bson:"config,omitempty" json:"config"`
+	Options    *Env3dOption     `bson:"options,omitempty" json:"options"`
+	ToneMap    *ToneMap         `bson:"toneMap,omitempty" json:"toneMap"`
+	Background *Evn3dBackground `bson:"background,omitempty" json:"background"`
+
+	UserData interface{} `bson:"userData,omitempty" json:"userData"` //用户数据
+}
+
+func (e *PackageEnv3d) UpdateSourceUrl(handler UpdateUrlHandler) {
+	if e.Thumbnail != nil {
+		e.Thumbnail.UpdateSourceUrl(handler)
+	}
+	if e.HDR != nil {
+		e.HDR.UpdateSourceUrl(handler)
+	}
+	if e.Config != nil {
+		e.Config.UpdateSourceUrl(handler)
+	}
+	if e.Background != nil && e.Background.Image != nil {
+		e.Background.Image.UpdateSourceUrl(handler)
+	}
+}
+
+type PackageMaterial struct {
+	Id        string   `bson:"id,omitempty" json:"id"`
+	Thumbnail *OssType `bson:"thumbnail,omitempty" json:"thumbnail"`
+	Name      string   `bson:"name,omitempty" json:"name"`
+	CusNum    string   `bson:"cusNum,omitempty" json:"cusNum"` //编号
+
+	NormalMap      *OssType `bson:"normalMap,omitempty" json:"normalMap"`
+	DisplaceMap    *OssType `bson:"displaceMap,omitempty" json:"displaceMap"` //置换贴图
+	DisplaceFactor *float64 `bson:"displaceFactor,omitempty" json:"displaceFactor"`
+	BaseMap        *OssType `bson:"baseMap,omitempty" json:"baseMap"`
+	BaseColor      *Vect3   `bson:"baseColor,omitempty" json:"baseColor"`
+	RoughMap       *OssType `bson:"roughMap,omitempty" json:"roughMap"`
+	RoughFactor    *float64 `bson:"roughFactor,omitempty" json:"roughFactor"`
+	MetalMap       *OssType `bson:"metalMap,omitempty" json:"metalMap"`
+	MetalFactor    *float64 `bson:"metalFactor,omitempty" json:"metalFactor"`
+
+	DiffuseMap   *OssType `bson:"diffuseMap,omitempty" json:"diffuseMap"`
+	DiffuseColor *Vect3   `bson:"diffuseColor,omitempty" json:"diffuseColor"`
+	GlossMap     *OssType `bson:"glossMap,omitempty" json:"glossMap"` //光泽贴图
+	GlossFactor  *float64 `bson:"glossFactor,omitempty" json:"glossFactor"`
+	SpecMap      *OssType `bson:"specMap,omitempty" json:"specMap"`     //高光贴图
+	SpecColor    *Vect3   `bson:"specColor,omitempty" json:"specColor"` //高光贴图
+
+	//工艺相关材质球
+	TechMaterial *TechMatConfig    `bson:"techMaterial" json:"techMaterial"`
+	Images       []*ComponentImage `bson:"images" json:"images"`
+
+	OpacMap    *OssType `bson:"opacMap,omitempty" json:"opacMap"`       //透明度
+	OpacFactor *float64 `bson:"opacFactor,omitempty" json:"opacFactor"` //透明度贴图
+
+	Type string `bson:"type,omitempty" json:"type"` //spec meta  //高光流 金属流(默认)
+
+	Uvtransform *MaterailUv `bson:"uv,omitempty" json:"uv"`
+
+	CusUvTransform *MaterailUv `bson:"cusUv,omitempty" json:"cusUv"` //用户定义的uv
+	//LogicType      string      `bson:"logicType,omitempty" json:"logicType"` //fabric3d fabric2d color //3d面料 2d图片面料 color //特殊材质球
+	UvMap    string      `bson:"uvMap,omitempty" json:"uvMap"`       //uv的映射方式 box 或 uv 默认box
+	UserData interface{} `bson:"userData,omitempty" json:"userData"` //指定要数据放置的字段
+}
+
+func (m *PackageMaterial) CopyFromMatConf(c *MaterialConf) {
+	m.Thumbnail = c.Thumbnail
+	m.BaseColor = c.BaseColor
+	m.BaseMap = c.BaseMap
+	m.MetalFactor = c.MetalFactor
+	m.MetalMap = c.MetalMap
+
+	m.RoughFactor = c.RoughFactor
+	m.RoughMap = c.RoughMap
+	m.NormalMap = c.NormalMap
+	m.Name = c.Name
+	if len(c.Type) > 0 {
+		m.Type = c.Type
+	}
+
+	m.OpacFactor = c.OpacFactor
+	m.OpacMap = c.OpacMap
+	m.Uvtransform = c.Uvtransform
+
+	m.CusNum = c.CusNum
+	m.DiffuseColor = c.DiffuseColor
+	m.DiffuseMap = c.DiffuseMap
+	m.DisplaceFactor = c.DisplaceFactor
+	m.DisplaceMap = c.DisplaceMap
+	m.GlossFactor = c.GlossFactor
+	m.GlossMap = c.GlossMap
+	m.SpecColor = c.SpecColor
+	m.SpecMap = c.SpecMap
+}
+
+type Queen3dPackageSource struct {
+	Version string `bson:"version,omitempty" json:"version"`
+
+	ViewMode string `bson:"viewMode,omitempty" json:"viewMode"` //prod or scene //单品或场景
+
+	//材质球
+	Mats []*MatConfigSource `bson:"mats,omitempty" json:"mats"`
+
+	//几何体
+	Geoms []*PackageGeom `bson:"geoms,omitempty" json:"geoms"`
+
+	//环境
+	Env3ds []*PackageEnv3d `bson:"env3ds,omitempty" json:"env3ds"`
+
+	//使用的单品
+	Products []*PackageProduct `bson:"products,omitempty" json:"products"`
+
+	//场景
+	Scenes []*PackageScene `bson:"scenes,omitempty" json:"scenes"`
+
+	UserData interface{} `bson:"userData,omitempty" json:"userData"` //指定要数据放置的字段
+}
+
+// 根据url获取路径和文件名
+func ParseUrl(ourl string) (fullName string, err error) {
+	parsedURL, err := url.Parse(ourl)
+	if err != nil {
+		fmt.Println("Error parsing URL:", err)
+		return "", err
+	}
+	fullName = strings.TrimPrefix(parsedURL.Path, "/")
+	// fileName = path[strings.LastIndex(path, "/")+1:]
+	// fmt.Printf("source file => path:%s, fileName:%s", path, fileName)
+	return fullName, nil
+
+}
+
+func DownloadToSource(url string) (*Queen3dPackageSource, error) {
+	// 下载文件
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println("Error downloading file:", err)
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	// 读取响应体内容
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		fmt.Println("Error reading response body:", err)
+		return nil, err
+	}
+
+	// 解析JSON
+	source := &Queen3dPackageSource{}
+	err = json.Unmarshal(body, source)
+	if err != nil {
+		fmt.Println("Error parsing JSON:", err)
+		return nil, err
+	}
+
+	return source, nil
+}
+
+func DownloadToSourceHdr(url string) (*HdrSource, error) {
+	// 下载文件
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println("Error downloading file:", err)
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	// 读取响应体内容
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		fmt.Println("Error reading response body:", err)
+		return nil, err
+	}
+
+	// 解析JSON
+	source := &HdrSource{}
+	err = json.Unmarshal(body, source)
+	if err != nil {
+		fmt.Println("Error parsing JSON:", err)
+		return nil, err
+	}
+
+	return source, nil
+}
+
+func DownloadToSourceHdrMap(url string) (map[string]interface{}, error) {
+	// 下载文件
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println("Error downloading file:", err)
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	// 读取响应体内容
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		fmt.Println("Error reading response body:", err)
+		return nil, err
+	}
+
+	// 解析JSON
+	source := map[string]interface{}{}
+	err = json.Unmarshal(body, &source)
+	if err != nil {
+		fmt.Println("Error parsing JSON:", err)
+		return nil, err
+	}
+
+	return source, nil
+}

+ 66 - 0
sku3d/comm/pay/const.go

@@ -0,0 +1,66 @@
+package pay
+
+import "fmt"
+
+const (
+	// cloud用户数据库名
+	CLOUD_USER_DB_NAME string = "usercenter"
+	PACK_PAY_NAME      string = "pay"
+
+	// 支付点
+	PayPointListApi   = "pay.point.list"
+	PayPointDetailApi = "pay.point.detail"
+	PayPointAmountApi = "pay.point.amount"
+	PayOrderCreateApi = "pay.order.create"
+	PayOrderDetailApi = "pay.order.detail"
+	PayOrderUpdateApi = "pay.order.update"
+
+	// 退款申请 api
+	PayRefundCreateApi = "pay.refund.create"
+	PayRefundDetailApi = "pay.refund.detail"
+	PayRefundUpdateApi = "pay.refund.update"
+
+	// 支付二维码
+	PaySingleQrApi = "pay.single.crateQr"
+
+	// 支付方式
+	PayModAliPay   = 0
+	PayModWechtPay = 1
+
+	// 支付项目
+	PayProjectQueenShow      = "queenshow"
+	PayProjectQueenShowTrade = "QS"
+
+	// TODO
+	PaySingleRefundApi      = "pay.single.refund"
+	PaySingleRefundQueryApi = "pay.single.refundQuery"
+	PaySingleCloseOrderApi  = "pay.single.closeOrder"
+
+	PayMultiQrApi     = "pay.multi.crateQr"
+	PayMultiRefundApi = "pay.multi.refund"
+
+	// 退款状态
+	REFUND_PROCESSING = iota // 0 退款处理中
+	REFUND_SUCCESS           // 1 退款成功
+	REFUND_CLOSED            // 2 退款关闭
+	REFUND_ABNORMAL          // 3 退款异常
+)
+
+func NewAppPaySuccSubject(app string) string {
+	return fmt.Sprintf("%s-pay-succ#%s.paysucc#%s-paysucc-queue", app, app, app)
+}
+func NewAppPayFailSubject(app string) string {
+	return fmt.Sprintf("%s-pay-fail#%s.payfail#%s-payfail-queue", app, app, app)
+}
+
+var (
+	// 支付订单状态
+	// 待付款
+	WaitPay int32 = 0
+	// 待发货
+	WaitDeliver int32 = 1
+	// 交易完成
+	Transacted int32 = 2
+	// 取消
+	Canceled int32 = 3
+)

+ 51 - 0
sku3d/comm/pay/order.go

@@ -0,0 +1,51 @@
+package pay
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type Order struct {
+	Id         primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
+	PointId    primitive.ObjectID `bson:"pointId,omitempty" json:"pointId,omitempty"`
+	ProductKey string             `bson:"productKey,omitempty" json:"productKey,omitempty"`
+	// 购买人
+	UserId primitive.ObjectID `bson:"UserId,omitempty" json:"UserId,omitempty"`
+	// 支付方式 0 支付宝 1微信
+	PayMod *int32 `bson:"payMod,omitempty" json:"payMod,omitempty"`
+	// 购买数量
+	Quantity *int32 `bson:"quantity,omitempty" json:"quantity,omitempty"`
+	// 购买人数
+	Number *int32   `bson:"number,omitempty" json:"number,omitempty"`
+	Price  *float64 `bson:"price,omitempty" json:"price,omitempty"`
+	Amount *float64 `bson:"amount,omitempty" json:"amount,omitempty"`
+
+	// 优惠后的真实价格
+	RealPrice  *float64 `bson:"realPrice,omitempty" json:"realPrice,omitempty"`
+	RealAmount *float64 `bson:"realAmount,omitempty" json:"realAmount,omitempty"`
+	// 订单状态
+	Status     *int32    `bson:"status,omitempty" json:"status,omitempty"`
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime,omitempty"`
+	UpdateTime time.Time `bson:"updateTime,omitempty" json:"updateTime,omitempty"`
+}
+
+type OrderMsg struct {
+	// orderId
+	Id   primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
+	Name string             `bson:"name,omitempty" json:"name,omitempty"`
+	// 购买数量
+	Quantity *int32 `bson:"quantity,omitempty" json:"quantity,omitempty"`
+	// 购买人数
+	Number *int32 `bson:"number,omitempty" json:"number,omitempty"`
+	// 金额
+	Amount *float64 `bson:"amount,omitempty" json:"amount,omitempty"`
+	// 支付方式 0支付宝 1微信
+	PayMod *int32 `bson:"payMode,omitempty" json:"payMode,omitempty"`
+	// 订单超时,通知微信支付宝关闭订单
+	ExpireTime time.Time `bson:"expireTime,omitempty" json:"expireTime,omitempty"`
+	// 来源 queenshow
+	Project string `bson:"project,omitempty" json:"project,omitempty"`
+	Os      string `bson:"os,omitempty" json:"os,omitempty"`
+	Ip      string `bson:"ip,omitempty" json:"ip,omitempty"`
+}

+ 48 - 0
sku3d/comm/pay/point.go

@@ -0,0 +1,48 @@
+package pay
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type Point struct {
+	Id primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
+	// 支付目标项目名 queenshow
+	Project     string `bson:"project,omitempty" json:"project,omitempty"`
+	ProductName string `bson:"productName,omitempty" json:"productName,omitempty"`
+	ProductKey  string `bson:"productKey,omitempty" json:"productKey,omitempty"`
+	// 0 basic 1 plus 2 plus+
+	// Level *int32 `bson:"level,omitempty" json:"level,omitempty"`
+	// 支付项目包含的服务
+	// Rules []*PayRules `bson:"rules,omitempty" json:"rules,omitempty"`
+	// 支持的支付类型 alipay wechatpay other
+	PayMethods []*PayMethod `bson:"payMethods,omitempty" json:"payMethods,omitempty"`
+	// 价格 支付宝整数代表元 微信整数代表分 业务代码中转换,数据为int32, 如果32.15 price == 3215
+	// 同一为正常数 比如32.15元,支付模块根据不同不同的第三方支付规则进行适配
+	Price *float64 `bson:"price,omitempty" json:"price,omitempty"`
+	// 排序
+	// Sort *int32 `bson:"sort,omitempty" json:"sort,omitempty"`
+	// 备注
+	// Remark     string    `bson:"remark,omitempty" json:"remark,omitempty"`
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime,omitempty"`
+	UpdateTime time.Time `bson:"updateTime,omitempty" json:"updateTime,omitempty"`
+}
+
+//	type PayRules struct {
+//		Key   string
+//		Value string
+//	}
+type PayMethod struct {
+	Key   string // alipay wechatpay
+	Value *int32 // 0 支付宝支付 1 微信支付
+}
+
+type PointAmountReq struct {
+	// PointId    primitive.ObjectID `bson:"pointId,omitempty" json:"pointId,omitempty"`
+	ProductKey string `bson:"productKey,omitempty" json:"productKey,omitempty"`
+	// 购买数量
+	Quantity *int32 `bson:"quantity,omitempty" json:"quantity,omitempty"`
+	// 团购人数
+	Number *int32 `bson:"number,omitempty" json:"number,omitempty"`
+}

+ 51 - 0
sku3d/comm/pay/refund.go

@@ -0,0 +1,51 @@
+package pay
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type ReFund struct {
+	Id     primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
+	UserId primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`
+	// 交易号
+	TradeNo string `bson:"tradeNo,omitempty" json:"tradeNo,omitempty"`
+	// 退款理由
+	Reason string `bson:"reason,omitempty" json:"reason,omitempty"`
+	// 拒绝退款理由
+	DeReason string `bson:"deReason,omitempty" json:"deReason,omitempty"`
+	// 退款金额
+	Amount *float64 `bson:"amount,omitempty" json:"amount,omitempty"`
+	// 原金额
+	Total *float64 `bson:"total,omitempty" json:"total,omitempty"`
+	// 退款手续费用
+	Fee *float64 `bson:"fee,omitempty" json:"fee,omitempty"`
+
+	// 产品相关
+	// 产品编码
+	PointId     primitive.ObjectID `bson:"pointId,omitempty" json:"pointId,omitempty"`
+	PointName   string             `bson:"pointName,omitempty" json:"pointName,omitempty"`
+	PointPrice  *float64           `bson:"pointPrice,omitempty" json:"pointPrice,omitempty"`
+	PointAmount *float64           `bson:"pointAmount,omitempty" json:"rpointAmount,omitempty"`
+	// 退款数量
+	Quantity *int32 `bson:"quantity,omitempty" json:"quantity,omitempty"`
+
+	Status *int32 `bson:"status,omitempty" json:"status,omitempty"`
+	// 支付模式:0支付宝 1微信
+	PayMod     *int32    `bson:"payMode,omitempty" json:"payMode,omitempty"`
+	CreateTime time.Time `bson:"createTime,omitempty" json:"createTime,omitempty"`
+	UpdateTime time.Time `bson:"updateTime,omitempty" json:"updateTime,omitempty"`
+}
+
+type PayRefundReq struct {
+	OrderId primitive.ObjectID `bson:"orderId,omitempty" json:"orderId,omitempty"`
+
+	Reason string `bson:"reason,omitempty" json:"reason,omitempty"`
+}
+
+type RefundDisAgreeReq struct {
+	Id primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
+
+	DeReason string `bson:"deReason,omitempty" json:"deReason,omitempty"`
+}

+ 215 - 0
sku3d/comm/render.go

@@ -0,0 +1,215 @@
+package comm
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type RenderComponent struct {
+	Name        string              `bson:"name,omitempty" json:"name"`
+	FabricId    *primitive.ObjectID `bson:"fabricId,omitempty" json:"fabricId,omitempty"`
+	CardId      *primitive.ObjectID `bson:"cardId,omitempty" json:"cardId,omitempty"`
+	MatType     uint8               `bson:"matType,omitempty" json:"matType"`
+	Imagemat    *ImageMatConf       `bson:"imageMat,omitempty" json:"imageMat,omitempty"`
+	Uvtransform *Uvtransform        `bson:"transform,omitempty" json:"transform"`
+	Color       *Vect3              `bson:"color,omitempty" json:"color"`
+	GroupId     uint64              `bson:"groupId,omitempty" json:"groupId"`
+	Visible     *bool               `bson:"visible,omitempty" json:"visible"`
+	Card        *MaterialConf       `bson:"card,omitempty" json:"card"`
+}
+
+type RenderShoeComp struct {
+	Name   string
+	Config *RenderComponent
+}
+
+type RenderModelComp struct {
+	Id         string              `bson:"id,omitempty" json:"id"`
+	MeshId     *primitive.ObjectID `bson:"meshId,omitempty" json:"meshId,omitempty"`
+	MeshUrl    string              `bson:"meshUrl,omitempty" json:"meshUrl,omitempty"`
+	Type       string              `bson:"type,omitempty" json:"type"`
+	TypeId     *primitive.ObjectID `bson:"typeId,omitempty" json:"typeId,omitempty"`
+	Transform  ModelTransform      `bson:"transform,omitempty" json:"transform,omitempty"`
+	Components []*RenderShoeComp   `bson:"components,omitempty" json:"components,omitempty"`
+}
+
+const (
+	TaskRunState_Created      = 0
+	TaskRunState_Quened       = 1
+	TaskRunState_WorkRuning   = 2
+	TaskRunState_ReWorkRuning = 3
+	TaskRunState_Succ         = 4
+	TaskRunState_Fail         = 5
+)
+
+type TaskRunResult struct {
+	Images []*OssType
+}
+
+type ConvMeshRunResult struct {
+	Osgjs     *OssType
+	Thumbnail *OssType
+	File      *OssType
+}
+
+type RenderSceneLight struct {
+	Type            string  `bson:"type,omitempty" json:"type"`
+	Direction       Vect3   `bson:"direction,omitempty" json:"direction"`
+	Color           Vect3   `bson:"color,omitempty" json:"color"`
+	Intensity       float64 `bson:"intensity,omitempty" json:"intensity"`
+	AngularDiameter float64 `bson:"angularDiameter,omitempty" json:"angularDiameter"`
+}
+
+type RenderTask struct {
+	Id       primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId   primitive.ObjectID `bson:"userId,omitempty" json:"userId"`
+	DesignId primitive.ObjectID `bson:"designId" json:"designId"`
+	WebId    int64              `bson:"webId" json:"webId"`
+
+	RunState   int32          `bson:"runState" json:"runState"`
+	RunPercent int32          `bson:"runPercent" json:"runPercent"`
+	RunResult  *TaskRunResult `bson:"runResult" json:"runResult"`
+	Width      int32          `bson:"width" json:"width"`
+	Height     int32          `bson:"height" json:"height"`
+	CreateTime time.Time      `bson:"createTime" json:"createTime"`
+
+	Scene      *DesignScene        `bson:"scene" json:"scene"`
+	MeshId     string              `bson:"meshId" json:"meshId"`
+	MeshUrl    string              `bson:"meshUrl" json:"meshUrl"`
+	MeshShadow string              `bson:"meshShadow" json:"meshShadow"`
+	Cameras    []*RenderCamera     `bson:"cameras" json:"cameras"`
+	Lights     []*RenderSceneLight `bson:"lights" json:"lights"`
+	ShoeComps  []*RenderShoeComp   `bson:"shoeComps" json:"shoeComps"`
+	ModelComps []*RenderModelComp  `bson:"modelComps" json:"modelComps"`
+
+	MaxSamples int32   `bson:"maxSamples" json:"maxSamples"`
+	Exposure   float32 `bson:"exposure" json:"exposure"`
+	Format     string  `bson:"format" json:"format"` //渲染格式 png jpg
+}
+
+type TreeRenderTask struct {
+	Id      primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
+	UserId  primitive.ObjectID `bson:"userId,omitempty" json:"userId"`
+	AssetId primitive.ObjectID `bson:"assetId" json:"assetId"`
+
+	RunState   int32          `bson:"runState" json:"runState"`
+	RunPercent int32          `bson:"runPercent" json:"runPercent"`
+	RunResult  *TaskRunResult `bson:"runResult" json:"runResult"`
+	Width      int32          `bson:"width" json:"width"`
+	Height     int32          `bson:"height" json:"height"`
+	CreateTime time.Time      `bson:"createTime" json:"createTime"`
+
+	Package *AssetPackage `bson:"pack" json:"pack"`
+	SceneId string        `bson:"sceneId" json:"sceneId"`
+
+	Lights           []*RenderSceneLight `bson:"lights" json:"lights"`
+	Cameras          []*RenderCamera     `bson:"cameras" json:"cameras"`
+	MaxSamples       int32               `bson:"maxSamples" json:"maxSamples"`
+	Exposure         float32             `bson:"exposure" json:"exposure"`
+	Format           string              `bson:"format" json:"format"`                     //渲染格式 png jpg
+	DevicePixelRatio *float32            `bson:"devicePixelRatio" json:"devicePixelRatio"` //渲染格式 png jpg
+}
+
+type RenderSetting struct {
+	MaxSamples int32   `bson:"maxSamples" json:"maxSamples"` //32
+	Exposure   float32 `bson:"exposure" json:"exposure"`     //1.2
+}
+
+type RenderCamera struct {
+	Thumbnail   *OssType       `bson:"thumbnail" json:"thumbnail,omitempty"` //
+	Position    *Vec3Obj       `bson:"position" json:"position,omitempty"`   //
+	Fov         float32        `bson:"fov" json:"fov"`                       //45
+	Direction   *Vec3Obj       `bson:"direction" json:"direction,omitempty"`
+	Viewport    CameraViewPort `bson:"viewport" json:"viewport"`
+	AspectRatio *float32       `bson:"aspectRatio" json:"aspectRatio,omitempty"`
+}
+
+type CameraViewPort struct {
+	Width  int32 `bson:"width" json:"width"`
+	Height int32 `bson:"height" json:"height"`
+}
+
+type SunLight struct {
+	Type            string  `bson:"type" json:"type"`
+	Intensity       float32 `bson:"intensity" json:"intensity"`
+	Direction       Vec3Obj `bson:"direction" json:"direction"`
+	AngularDiameter float32 `bson:"angularDiameter" json:"angularDiameter"`
+	Color           Vect3   `bson:"color" json:"color"`
+}
+
+type RenderEnv struct {
+	Color     Vect3   `bson:"color" json:"color"`
+	Rotation  float32 `bson:"rotation" json:"rotation"`
+	Intensity float32 `bson:"intensity" json:"intensity"`
+	FilePath  string  `bson:"filePath" json:"filePath"`
+	Visiable  bool    `bson:"visiable" json:"visiable"`
+}
+
+type Vec3Obj struct {
+	X float64 `bson:"x" json:"x"`
+	Y float64 `bson:"y" json:"y"`
+	Z float64 `bson:"z" json:"z"`
+}
+type Vec4Obj struct {
+	X float64 `bson:"x" json:"x"`
+	Y float64 `bson:"y" json:"y"`
+	Z float64 `bson:"z" json:"z"`
+	W float64 `bson:"w" json:"w"`
+}
+
+type RenderMatParam struct {
+	Name  string      `bson:"name" json:"name"`
+	Type  string      `bson:"type" json:"type"`
+	Value interface{} `bson:"value" json:"value,omitempty"`
+}
+
+type RenderMat struct {
+	Name         string            `bson:"name" json:"name"`
+	Type         string            `bson:"type" json:"type"`
+	UVProjection string            `bson:"uvProjection" json:"uvProjection"` //"box", "uv"
+	Parameters   []*RenderMatParam `bson:"parameters" json:"parameters"`
+}
+
+type RenderGroup struct {
+	Name      string       `bson:"name" json:"name"`
+	Position  Vec3Obj      `bson:"position" json:"position"`
+	Rotation  Vec4Obj      `bson:"rotation" json:"rotation"`
+	Scale     Vec3Obj      `bson:"scale" json:"scale"`
+	Materials []*RenderMat `bson:"materials" json:"materials"`
+	FilePath  string       `bson:"filePath" json:"filePath"`
+}
+
+type RenderConf struct {
+	Setting    *RenderSetting      `bson:"setting" json:"setting"`
+	Camera     *RenderCamera       `bson:"camera" json:"camera"`
+	Lights     []*RenderSceneLight `bson:"lights" json:"lights"`
+	Enviroment *RenderEnv          `bson:"enviroment" json:"enviroment"`
+	Groups     []*RenderGroup      `bson:"groups" json:"groups"`
+	Shadow     *Shadow             `bson:"shadow" json:"shadow"`
+}
+
+type WorkerRequest struct {
+	Name           string    `json:"name"`
+	UserId         string    `json:"userId"`
+	SessionId      int64     `json:"sessionId"`
+	TaskId         string    `json:"taskId"`
+	MainId         string    `json:"mainId"`
+	TaskCollection string    `json:"taskCollection"`
+	WorkerType     string    `json:"workerType"`
+	CreateTime     time.Time `json:"createTime"`
+}
+
+type WorkerResponse struct {
+	ErrorNo   int    `json:"errorNo"`
+	ErrorDesc string `json:"errorDesc"`
+	Result    struct {
+		WorkingId string `json:"workingId"`
+	} `json:"result"`
+}
+
+type Shadow struct {
+	Size     *Vect2 `bson:"size,omitempty" json:"size,omitempty"`
+	Position *Vect3 `bson:"position,omitempty" json:"position,omitempty"`
+	File     string `bson:"file,omitempty" json:"file,omitempty"`
+}

+ 204 - 0
sku3d/comm/task-background.go

@@ -0,0 +1,204 @@
+package comm
+
+import (
+	"fmt"
+)
+
+type EventListener func(data interface{})
+type GetSignalChannel func() chan interface{}
+
+type Event struct {
+	Name     string
+	Listener EventListener
+	Data     interface{}
+}
+
+type Signal struct {
+	Name     string
+	Get      GetSignalChannel
+	Listener EventListener
+	Data     interface{}
+}
+
+type TaskLifeCirle interface {
+	OnBeforeRun() error
+	OnAfterRun() error
+	Loop() bool
+}
+
+type BackgroundTask struct {
+	LifeCircle TaskLifeCirle
+
+	MaxExceptRestartTimes int //最大异常重启次数
+	ExceptRestartWatting  int //重启等待时间
+
+	runningState  int //运行状态
+	msgChan       chan int
+	eventsMsgChan chan *Event
+
+	_eventMaps map[string]*Event
+
+	_signalMaps map[string]*Signal
+
+	_runData interface{}
+
+	SessionId string
+
+	bus *NatsBus
+
+	data interface{}
+
+	Name string
+}
+
+func NewBackgroundTask(lifeCircle TaskLifeCirle, data interface{}) *BackgroundTask {
+	t := &BackgroundTask{LifeCircle: lifeCircle}
+	t.InitTask(data)
+	return t
+}
+
+func (t *BackgroundTask) GetName() string  { return t.Name }
+func (t *BackgroundTask) SetName(n string) { t.Name = n }
+func (t *BackgroundTask) GetID() string {
+	return t.SessionId
+}
+func (t *BackgroundTask) SetID(id string) {
+	t.SessionId = id
+}
+
+func (t *BackgroundTask) CancelRuning() {
+	go func() {
+		t.msgChan <- MSG_CANCEL_RUNING
+	}()
+}
+
+func (t *BackgroundTask) Restart() {
+	go func() {
+		t.msgChan <- MSG_RESTART_RUNING
+	}()
+}
+
+func (t *BackgroundTask) InitTask(data interface{}) {
+	t.data = data
+	t.msgChan = make(chan int)
+	t.eventsMsgChan = make(chan *Event)
+	t._eventMaps = make(map[string]*Event)
+	t._signalMaps = make(map[string]*Signal)
+
+	t.CreateTaskId()
+}
+
+func (t *BackgroundTask) CreateTaskId() string {
+	t.SessionId = "bgt" + CreateSessionId()
+	return t.SessionId
+}
+
+func (t *BackgroundTask) RegistorEvent(events string, listen EventListener) {
+	// if t._eventMaps == nil {
+	// 	t._eventMaps = make(map[string]*Event)
+	// }
+	// for _, name := range events {
+	// 	t._eventMaps[name] = &Event{Listener: listen, Name: name}
+	// }
+}
+
+func (t *BackgroundTask) RegistorEventAsync(name string, channel GetSignalChannel, listen EventListener) {
+	if t._signalMaps == nil {
+		t._signalMaps = make(map[string]*Signal)
+	}
+	t._signalMaps[name] = &Signal{Listener: listen, Name: name, Get: channel}
+}
+
+func (t *BackgroundTask) EmitEvent(event string, data interface{}) (interface{}, error) {
+	return nil, nil
+}
+
+func (t *BackgroundTask) EmitEventAsync(event string, data interface{}) (chan interface{}, error) {
+
+	return nil, nil
+}
+
+func (t *BackgroundTask) EmitEventSync(event string, data interface{}) (interface{}, error) {
+
+	return nil, nil
+}
+
+func (t *BackgroundTask) EmitSingal(sigal string, data interface{}) {
+
+}
+
+func (t *BackgroundTask) GetRuningState() TaskState {
+	return TaskState(t.runningState)
+}
+
+func (t *BackgroundTask) IsAsync() bool {
+	return true
+}
+
+func (t *BackgroundTask) GetNats() *NatsBus {
+	return t.bus
+}
+
+func (t *BackgroundTask) SetNats(b *NatsBus) {
+	t.bus = b
+}
+
+//data
+
+//state
+
+//action
+
+// swiftTask
+
+func (t *BackgroundTask) Run(overCallback TaskRunCallback) error {
+
+	life := t.LifeCircle
+
+	err := life.OnBeforeRun()
+	if err != nil {
+		return err
+	}
+
+	go func() {
+		t.runningState = BT_STATE_INIT
+		var OnLifeCycleMessage = func(msg int) bool {
+			switch msg {
+			case MSG_CANCEL_RUNING:
+				t.runningState = BT_STATE_QUITED_CACEL //取消执行退出
+				return true
+			case MSG_RESTART_RUNING:
+				t.runningState = BT_STATE_NORMAL_RESTARTING
+			}
+			return false
+		}
+		ticket := 0
+
+		for {
+			quitTask := false
+
+			select {
+			case msg := <-t.msgChan: //msg
+				quitTask = OnLifeCycleMessage(msg)
+
+			default:
+				ticket = ticket + 1
+				fmt.Printf("[%s]background-task tick %d\n", t.Name, ticket)
+				quitTask = life.Loop()
+
+			}
+
+			if quitTask {
+				break
+			}
+		}
+
+		if overCallback != nil {
+			overCallback(nil)
+		}
+
+		life.OnAfterRun()
+
+	}()
+	return nil
+}

+ 334 - 0
sku3d/comm/task-cmd-background.go

@@ -0,0 +1,334 @@
+package comm
+
+import (
+	"bufio"
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"io/fs"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type CmdBackgroundTaskData struct {
+	CmdSuccFlag        string //启动成功标志, 如果不设置,
+	CmdSuccFlag2       string
+	CmdDurationForSucc int //如果没有启动标志,则启动多少时间后,没有报错,则判断启动成功
+	CmdDurationForFail int //有启动标识,启动多少时间后,没有检测到成功标识判为启动失败
+	KillPreExe         bool
+	ExeFile            string //执行文件
+	ExeDir             string //执行目录
+
+	Envs   []string //执行环境变量
+	Params []string //执行参数
+}
+
+type CmdLogFunction func(string)
+
+type CmdBackgroundTask struct {
+	MaxExceptRestartTimes int //最大异常重启次数
+	ExceptRestartWatting  int //重启等待时间
+
+	CmdSuccStarted bool
+
+	LogFunc CmdLogFunction
+
+	RunningState int //运行状态
+	msgChan      chan int
+	SessionId    string
+	bus          *NatsBus
+
+	Data       *CmdBackgroundTaskData
+	InitRunCmd bool
+
+	LifeCircle TaskLifeCirle
+	Name       string
+}
+
+func (t *CmdBackgroundTask) GetName() string  { return t.Name }
+func (t *CmdBackgroundTask) SetName(n string) { t.Name = n }
+
+func (t *CmdBackgroundTask) GetID() string {
+	return t.SessionId
+}
+
+func (t *CmdBackgroundTask) SetID(id string) {
+	t.SessionId = id
+}
+
+func NewCmdBackgroundTask(data *CmdBackgroundTaskData) *CmdBackgroundTask {
+	t := &CmdBackgroundTask{}
+	t.Data = data
+	t.msgChan = make(chan int)
+	t.CreateTaskId()
+	t.InitRunCmd = true
+	return t
+}
+
+func (t *CmdBackgroundTask) CancelRuning() {
+	go func() { //开启新线程投递重启消息,避免再LOOP中调用,导致死锁
+		t.msgChan <- MSG_CANCEL_RUNING
+	}()
+}
+
+func (t *CmdBackgroundTask) Restart() {
+	go func() { //开启新线程投递重启消息,避免再LOOP中调用,导致死锁
+		t.msgChan <- MSG_RESTART_RUNING
+	}()
+}
+
+func (t *CmdBackgroundTask) GetNats() *NatsBus {
+	return t.bus
+}
+
+func (t *CmdBackgroundTask) SetNats(b *NatsBus) {
+	t.bus = b
+}
+
+func (t *CmdBackgroundTask) IsAsync() bool {
+	return true
+}
+
+func (t *CmdBackgroundTask) GetRuningState() TaskState {
+	return TaskState(t.RunningState)
+}
+
+func (t *CmdBackgroundTask) CreateTaskId() string {
+	t.SessionId = "cmd-" + CreateSessionId()
+	return t.SessionId
+}
+
+func (t *CmdBackgroundTask) EmitEvent(event string, data interface{}) (chan interface{}, error) {
+	return nil, nil
+}
+
+func (t *CmdBackgroundTask) Run(overCallback TaskRunCallback) error {
+
+	err := t.LifeCircle.OnBeforeRun()
+	if err != nil {
+		return err
+	}
+
+	var taskLoop = func() {
+		t.RunningState = BT_STATE_INIT
+		loopQuitChan := make(chan error)
+
+		var ctx context.Context
+		var cancel context.CancelFunc
+
+		var StartLoop = func() {
+			t.RunningState = BT_STATE_RUNING
+			defer func() {
+				if r := recover(); r != nil {
+					loopQuitChan <- errors.New(fmt.Sprintf("loop exception :%v", r))
+				}
+			}()
+			ctx, cancel = context.WithCancel(context.Background())
+			loopQuitChan <- t.runCmd(ctx)
+		}
+		if t.InitRunCmd {
+			go StartLoop()
+		}
+
+		loopErrorIndex := 0
+		var OnLoopQuit = func(err error) bool {
+
+			fmt.Println("cmd quit ==>", err)
+
+			if t.RunningState == BT_STATE_QUITED_CACEL {
+				return true
+			}
+
+			if t.RunningState == BT_STATE_NORMAL_RESTARTING { //主动重启中
+				go StartLoop()
+				return false
+			}
+
+			if err != nil { //loop异常退出
+				if t.RunningState == BT_STATE_QUITED_CACEL {
+					return true
+				}
+				loopErrorIndex = loopErrorIndex + 1
+				if loopErrorIndex > t.MaxExceptRestartTimes {
+					t.RunningState = BT_STATE_QUITED_EXCEPTION
+					return true
+				}
+				t.RunningState = BT_STATE_EXCEPTION_RESTARTING
+				if t.ExceptRestartWatting > 0 {
+					time.Sleep(time.Second * time.Duration(t.ExceptRestartWatting))
+				}
+
+				go StartLoop()
+				return false
+			}
+
+			//程序正常终止了
+			t.RunningState = BT_STATE_QUITED_NORMAL
+			return true
+		}
+
+		var OnLifeCycleMessage = func(msg int) bool {
+			switch msg {
+			case MSG_CANCEL_RUNING:
+				t.RunningState = BT_STATE_QUITED_CACEL //取消执行退出
+				if cancel != nil {
+					cancel()
+				} else {
+					return true
+				}
+			case MSG_RESTART_RUNING:
+				t.RunningState = BT_STATE_NORMAL_RESTARTING
+				if cancel != nil {
+					cancel()
+				} else {
+					go StartLoop()
+				}
+			}
+			return false
+		}
+		ticket := 0
+		for {
+			quitTask := false
+			select {
+			case msg := <-t.msgChan: //msg
+				quitTask = OnLifeCycleMessage(msg)
+
+			case err := <-loopQuitChan:
+				quitTask = OnLoopQuit(err)
+			default:
+				ticket = ticket + 1
+				fmt.Printf("[%s]cmd-bg-task tick %d \n", t.Name, ticket)
+				quitTask = t.LifeCircle.Loop()
+
+			}
+
+			if quitTask {
+				break
+			}
+		}
+		t.LifeCircle.OnAfterRun()
+		if overCallback != nil {
+			overCallback(nil)
+		}
+	}
+	go taskLoop()
+
+	return nil
+}
+
+func (t *CmdBackgroundTask) GetExeDir() string {
+	data := t.Data
+
+	if len(data.ExeDir) > 0 {
+		return path.Join(data.ExeDir, fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH))
+	}
+	return filepath.Dir(data.ExeFile)
+}
+
+func (t *CmdBackgroundTask) KillPreExeImpl() {
+
+	//判断exename 是否存在
+	data := t.Data
+	if !data.KillPreExe {
+		return
+	}
+
+	pid := filepath.Join(t.GetExeDir(), "pid")
+	pidstr, _ := ioutil.ReadFile(pid)
+	if len(pidstr) < 1 {
+		return
+	}
+	p, _ := strconv.Atoi(string(pidstr))
+	if p < 1 {
+		return
+	}
+	proc, er := os.FindProcess(p)
+	if er != nil {
+		fmt.Println("find process fail", p, er)
+		return
+	}
+	err := proc.Kill()
+	fmt.Println("kill=>", p, err)
+}
+
+func (t *CmdBackgroundTask) runCmd(ctx context.Context) error {
+	data := t.Data
+
+	exef := data.ExeFile
+	if runtime.GOOS == "windows" {
+		if strings.LastIndex(exef, ".exe") == -1 {
+			exef = exef + ".exe"
+		}
+	}
+	t.KillPreExeImpl()
+
+	cmd := exec.CommandContext(ctx, exef, data.Params...)
+	if len(data.ExeDir) > 0 {
+		cmd.Dir = path.Join(data.ExeDir, fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH))
+	}
+	if len(data.Envs) > 0 {
+		cmd.Env = data.Envs
+	}
+	fmt.Println("start cmd==>", data.ExeDir, data.ExeFile)
+	// cmd.SysProcAttr = xdaemon.NewSysProcAttr()
+	stdoutIn, _ := cmd.StdoutPipe()
+	stderrIn, _ := cmd.StderrPipe()
+
+	reader := bufio.NewReader(stdoutIn)
+	reader2 := bufio.NewReader(stderrIn)
+
+	hasSuccFlag := len(data.CmdSuccFlag) > 0
+
+	go func() {
+		checked := false
+		for {
+			line, err2 := reader.ReadString('\n')
+			if err2 != nil || io.EOF == err2 {
+				break
+			}
+			fmt.Println(line)
+			if t.LogFunc != nil {
+				t.LogFunc(line)
+			}
+			if !checked && hasSuccFlag && ((strings.LastIndex(line, data.CmdSuccFlag) != -1) || (len(data.CmdSuccFlag2) > 0 && strings.LastIndex(line, data.CmdSuccFlag2) != -1)) {
+				checked = true
+				t.CmdSuccStarted = true
+			}
+		}
+	}()
+
+	go func() {
+		checked := false
+		for {
+			line, err2 := reader2.ReadString('\n')
+			if err2 != nil || io.EOF == err2 {
+				break
+			}
+			fmt.Println(line)
+			if !checked && hasSuccFlag && ((strings.LastIndex(line, data.CmdSuccFlag) != -1) || (len(data.CmdSuccFlag2) > 0 && strings.LastIndex(line, data.CmdSuccFlag2) != -1)) {
+				t.CmdSuccStarted = true
+			}
+		}
+	}()
+	exeBase := path.Base(data.ExeFile)
+
+	err := cmd.Start()
+	if err != nil {
+		fmt.Println(exeBase, " start failed", err.Error())
+		return err
+	}
+
+	if data.KillPreExe {
+		ioutil.WriteFile(filepath.Join(t.GetExeDir(), "pid"), []byte(fmt.Sprintf("%d", cmd.Process.Pid)), fs.ModePerm)
+	}
+
+	return cmd.Wait()
+}

+ 283 - 0
sku3d/comm/task-cmd.go

@@ -0,0 +1,283 @@
+package comm
+
+import (
+	"bufio"
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"os/exec"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+)
+
+type CmdTaskData struct {
+	TimeWaitingForRunSucc int    //多少时间后判定为执行成功
+	Timeout               int    //多少时间判定为超时
+	ExeFile               string //执行文件
+	ExeDir                string //执行目录
+	CmdSuccFlag           string
+
+	Envs   []string //执行环境变量
+	Params []string //执行参数
+}
+
+type CmdTask struct {
+	CmdSuccStarted bool
+	LogFunc        CmdLogFunction
+
+	RunningState int //运行状态
+	msgChan      chan int
+	SessionId    string
+	bus          *NatsBus
+
+	Data *CmdTaskData
+
+	LifeCircle TaskLifeCirle
+	Name       string
+}
+
+func (t *CmdTask) GetName() string  { return t.Name }
+func (t *CmdTask) SetName(n string) { t.Name = n }
+
+func (t *CmdTask) GetID() string {
+	return t.SessionId
+}
+func (t *CmdTask) SetID(id string) {
+	t.SessionId = id
+}
+
+func NewCmdTask(data *CmdTaskData) *CmdTask {
+	t := &CmdTask{}
+	t.Data = data
+	t.msgChan = make(chan int)
+	t.CreateTaskId()
+	return t
+}
+
+func (t *CmdTask) CancelRuning() {
+	go func() { //开启新线程投递重启消息,避免再LOOP中调用,导致死锁
+		t.msgChan <- MSG_CANCEL_RUNING
+	}()
+}
+
+func (t *CmdTask) Restart() {
+	panic("不支持重启操作")
+}
+
+func (t *CmdTask) GetNats() *NatsBus {
+	return t.bus
+}
+
+func (t *CmdTask) SetNats(b *NatsBus) {
+	t.bus = b
+}
+
+func (t *CmdTask) IsAsync() bool {
+	return true
+}
+
+func (t *CmdTask) GetRuningState() TaskState {
+	return TaskState(t.RunningState)
+}
+
+func (t *CmdTask) CreateTaskId() string {
+	t.SessionId = "cmd-" + CreateSessionId()
+	return t.SessionId
+}
+
+func (t *CmdTask) Run(overCallback TaskRunCallback) error {
+
+	if t.LifeCircle != nil {
+		err := t.LifeCircle.OnBeforeRun()
+		if err != nil {
+			return err
+		}
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(1)
+	var out error
+
+	var taskRun = func() {
+		t.RunningState = BT_STATE_INIT
+		loopQuitChan := make(chan error)
+
+		var ctx context.Context
+		var cancel context.CancelFunc
+		timeOutCanceled := false
+
+		var StartLoop = func() {
+			t.RunningState = BT_STATE_RUNING
+			defer func() {
+				if r := recover(); r != nil {
+					loopQuitChan <- errors.New(fmt.Sprintf("loop exception :%v", r))
+				}
+			}()
+			ctx, cancel = context.WithCancel(context.Background())
+
+			e := t.runCmd(ctx)
+			if timeOutCanceled {
+				e = nil
+			}
+			loopQuitChan <- e
+		}
+		go StartLoop()
+
+		var OnLoopQuit = func(err error) bool {
+			fmt.Println("cmd quit ==>", err)
+			if t.RunningState == BT_STATE_QUITED_CACEL {
+				err = nil
+			} else {
+				if err != nil { //loop异常退出
+					t.RunningState = BT_STATE_QUITED_EXCEPTION
+					out = err
+				}
+				//程序正常终止了
+				t.RunningState = BT_STATE_QUITED_NORMAL
+			}
+			return true
+		}
+
+		var OnLifeCycleMessage = func(msg int) bool {
+			switch msg {
+			case MSG_CANCEL_RUNING:
+				t.RunningState = BT_STATE_QUITED_CACEL //取消执行退出
+				out = fmt.Errorf("取消执行")
+				if cancel != nil {
+					cancel()
+				} else {
+					return true
+				}
+			}
+			return false
+		}
+		ticket := 0
+		sucFlag := false
+
+		for {
+			quitTask := false
+			select {
+			case msg := <-t.msgChan: //msg
+				quitTask = OnLifeCycleMessage(msg)
+			case err := <-loopQuitChan:
+				quitTask = OnLoopQuit(err)
+			default:
+				ticket = ticket + 1
+				time.Sleep(time.Second)
+
+				if !sucFlag && ticket > t.Data.TimeWaitingForRunSucc {
+					wg.Done()
+					sucFlag = true
+				}
+
+				if !timeOutCanceled && t.Data.Timeout > 0 && ticket > t.Data.Timeout {
+					fmt.Printf("cmd:%s 执行超时,取消执行中", t.Name)
+					out = fmt.Errorf("执行超时")
+					timeOutCanceled = true
+
+					if cancel != nil {
+						cancel()
+					}
+				}
+			}
+
+			if quitTask {
+				break
+			}
+		}
+		if t.LifeCircle != nil {
+			t.LifeCircle.OnAfterRun()
+		}
+
+		if overCallback != nil {
+			overCallback(out)
+		}
+	}
+	go taskRun()
+	wg.Wait()
+
+	return out
+}
+
+func (t *CmdTask) GetExeDir() string {
+	data := t.Data
+
+	if len(data.ExeDir) > 0 {
+		return path.Join(data.ExeDir, fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH))
+	}
+	return filepath.Dir(data.ExeFile)
+}
+
+func (t *CmdTask) runCmd(ctx context.Context) error {
+	data := t.Data
+
+	exef := data.ExeFile
+	if runtime.GOOS == "windows" {
+		if strings.LastIndex(exef, ".exe") == -1 {
+			exef = exef + ".exe"
+		}
+	}
+
+	cmd := exec.CommandContext(ctx, exef, data.Params...)
+	if len(data.ExeDir) > 0 {
+		cmd.Dir = path.Join(data.ExeDir, fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH))
+	}
+	if len(data.Envs) > 0 {
+		cmd.Env = data.Envs
+	}
+	fmt.Println("start cmd==>", data.ExeDir, data.ExeFile)
+	// cmd.SysProcAttr = xdaemon.NewSysProcAttr()
+	stdoutIn, _ := cmd.StdoutPipe()
+	stderrIn, _ := cmd.StderrPipe()
+
+	reader := bufio.NewReader(stdoutIn)
+	reader2 := bufio.NewReader(stderrIn)
+
+	hasSuccFlag := len(data.CmdSuccFlag) > 0
+
+	go func() {
+		checked := false
+		for {
+			line, err2 := reader.ReadString('\n')
+			if err2 != nil || io.EOF == err2 {
+				break
+			}
+			fmt.Println(line)
+			if t.LogFunc != nil {
+				t.LogFunc(line)
+			}
+			if !checked && hasSuccFlag && (strings.LastIndex(line, data.CmdSuccFlag) != -1) {
+				checked = true
+				t.CmdSuccStarted = true
+			}
+		}
+	}()
+
+	go func() {
+		checked := false
+		for {
+			line, err2 := reader2.ReadString('\n')
+			if err2 != nil || io.EOF == err2 {
+				break
+			}
+			fmt.Println(line)
+			if !checked && hasSuccFlag && (strings.LastIndex(line, data.CmdSuccFlag) != -1) {
+				t.CmdSuccStarted = true
+			}
+		}
+	}()
+	exeBase := path.Base(data.ExeFile)
+
+	err := cmd.Start()
+	if err != nil {
+		fmt.Println(exeBase, " start failed", err.Error())
+		return err
+	}
+
+	return cmd.Wait()
+}

+ 230 - 0
sku3d/comm/task-dispatcher-background.go

@@ -0,0 +1,230 @@
+package comm
+
+import (
+	"context"
+	"fmt"
+	"sync"
+	"time"
+)
+
+type LoopCtx struct {
+	RunCtx context.Context
+}
+type CreateLoopCtx struct {
+	RunCtx     context.Context
+	Quit       chan error
+	SetFpsWait func(t time.Duration)
+	RegEvent   func(name string, listener func(v interface{}))
+	RegSignal  func(name string, c chan interface{}, listener func(v interface{}))
+}
+
+const (
+	MSG_CANCEL_RUNING  = 1
+	MSG_RESTART_RUNING = 2
+)
+
+// 此函数为BackgroundTask的主循环, 应该阻塞在此,以响应其事件和持续的输出数据
+// 当前此函数退出时,表面BackgroundTask任务取消执行或重启退出中
+// MainLoop会在独立的线程中被执行
+type MainLoopFunc func(ctx *LoopCtx) error
+type CreateMainLoopFunc func(ctx *CreateLoopCtx) MainLoopFunc
+type TaskCreator func(data interface{}) (t EventTask, createOut interface{}, err error)
+
+type TaskDispatcher struct {
+	runningState int //运行状态
+	msgChan      chan int
+	emiterChan   chan *EmiterRouter
+
+	mtx         sync.Mutex
+	_taskRoutes map[string]TaskCreator
+	_tasks      map[string]EventTask
+
+	_tasksAsyncRuning map[string]bool //正在异步执行的任务
+
+	bus *NatsBus
+}
+
+func (t *TaskDispatcher) GetNats() *NatsBus {
+	return t.bus
+}
+
+func (t *TaskDispatcher) SetNats(b *NatsBus) {
+	t.bus = b
+}
+
+func NewTaskDispatcher() *TaskDispatcher {
+	return &TaskDispatcher{msgChan: make(chan int), emiterChan: make(chan *EmiterRouter), _taskRoutes: map[string]TaskCreator{}, _tasksAsyncRuning: make(map[string]bool)}
+}
+
+func (t *TaskDispatcher) CancelRuning() {
+	t.msgChan <- MSG_CANCEL_RUNING
+}
+
+func (t *TaskDispatcher) IsAsync() bool {
+	return true
+}
+
+func (t *TaskDispatcher) InitTask() {}
+
+func (t *TaskDispatcher) CreateTaskId() string {
+	return "task-dispatcher-id"
+}
+
+func (t *TaskDispatcher) Restart() {
+	t.msgChan <- MSG_RESTART_RUNING
+}
+
+type EmiterRouter struct {
+	Data interface{}
+	Name string
+	Cb   EmitRouterCallback
+}
+
+func (t *TaskDispatcher) GetRuningState() TaskState {
+	return TaskState(t.runningState)
+}
+
+// 发送同步事件
+func EmitEventSync(name string, data interface{}) (interface{}, error) { return nil, nil }
+
+// 发送异步事件
+func EmitEventAsync(name string, data interface{}) (chan interface{}, error) { return nil, nil }
+
+// 添加路由
+func (t *TaskDispatcher) AddRoute(name string, taskCreator TaskCreator) {
+	t.mtx.Lock()
+	defer t.mtx.Unlock()
+
+	if t._taskRoutes == nil {
+		t._taskRoutes = make(map[string]TaskCreator)
+	}
+	t._taskRoutes[name] = taskCreator
+}
+
+// 添加路由
+func (t *TaskDispatcher) GetRouter(name string) TaskCreator {
+	t.mtx.Lock()
+	defer t.mtx.Unlock()
+	return t._taskRoutes[name]
+}
+
+// 移除路由
+func (t *TaskDispatcher) RemoveRoute(name string, listen Task) {
+	t.mtx.Lock()
+	defer t.mtx.Unlock()
+	if t._taskRoutes == nil {
+		return
+	}
+	delete(t._taskRoutes, name)
+}
+
+type EmitRouterCallback func(interface{}, error)
+
+func (t *TaskDispatcher) EmitRouter(name string, data interface{}, cb EmitRouterCallback) {
+	t.emiterChan <- &EmiterRouter{Name: name, Data: data, Cb: cb}
+}
+
+func (t *TaskDispatcher) EmitRouterEvent(routerName string, EventName string, data interface{}) (interface{}, error) {
+
+	if !t._tasksAsyncRuning[routerName] {
+		return nil, fmt.Errorf("%s 没有启动", routerName)
+	}
+
+	if t._tasks[routerName] == nil {
+		return nil, fmt.Errorf("任务 %s 没有注册", routerName)
+	}
+	return t._tasks[routerName].EmitEvent(EventName, data)
+}
+
+func (t *TaskDispatcher) Run(overCallback TaskRunCallback) error {
+	t.runningState = BT_STATE_INIT
+	var OnLifeCycleMessage = func(msg int) bool {
+		quitRuning := true
+		switch msg {
+		case MSG_CANCEL_RUNING:
+			t.runningState = BT_STATE_QUITED_CACEL //取消执行退出
+
+		case MSG_RESTART_RUNING:
+			t.runningState = BT_STATE_NORMAL_RESTARTING
+		default:
+			quitRuning = false
+		}
+		if quitRuning {
+			for name, _ := range t._tasksAsyncRuning {
+				if t._tasks[name] != nil {
+					t._tasks[name].CancelRuning()
+				}
+			}
+		}
+		return false
+	}
+
+	var RunAsyncEvent = func(taskWillRun Task, emiter *EmiterRouter) {
+		t._tasksAsyncRuning[emiter.Name] = true
+		go func() {
+			taskWillRun.Run(nil)
+			t._tasksAsyncRuning[emiter.Name] = false
+		}()
+	}
+
+	ticket := 0
+	// cases := make([]reflect.SelectCase, 0)
+	// cases = append(cases, reflect.SelectCase{
+	// 	Dir:  reflect.SelectRecv,
+	// 	Chan: reflect.ValueOf(t.msgChan),
+	// })
+
+	// cases = append(cases, reflect.SelectCase{
+	// 	Dir:  reflect.SelectRecv,
+	// 	Chan: reflect.ValueOf(t.eventsMsgChan),
+	// })
+
+	// signalArray := []*Signal{}
+	// for _, v := range t._signalMaps {
+	// 	cases = append(cases, reflect.SelectCase{
+	// 		Dir:  reflect.SelectRecv,
+	// 		Chan: reflect.ValueOf(v.Get()),
+	// 	})
+	// 	signalArray = append(signalArray, v)
+	// }
+
+	for {
+		quitTask := false
+
+		// chosen, recv, ok := reflect.Select(cases)
+		// if !ok {
+		// 	ticket = ticket + 1
+		// 	fmt.Println("back-ground-task running: ", ticket)
+		// 	time.Sleep(time.Second * 1)
+		// 	continue
+		// }
+		// fmt.Printf("chosen: %d, recv: %v\n", chosen, recv)
+
+		select {
+		case msg := <-t.msgChan:
+			quitTask = OnLifeCycleMessage(msg)
+
+		case emiter := <-t.emiterChan:
+			creator := t.GetRouter(emiter.Name)
+			if creator != nil {
+				t, out, err := creator(emiter.Data)
+				if emiter.Cb != nil {
+					emiter.Cb(out, err)
+				}
+				RunAsyncEvent(t, emiter)
+			}
+		default:
+			ticket = ticket + 1
+			fmt.Println("backgrount Task runing tick ", ticket)
+		}
+		if quitTask {
+			break
+		}
+	}
+
+	if overCallback != nil {
+		overCallback(nil)
+	}
+
+	return nil
+}

+ 47 - 0
sku3d/comm/task.go

@@ -0,0 +1,47 @@
+package comm
+
+type TaskState int
+
+const (
+	BT_STATE_QUITED_CACEL     = -1 //loop取消退出
+	BT_STATE_QUITED_EXCEPTION = -2 //loop异常退出
+	BT_STATE_QUITED_NORMAL    = -3 //loop退出
+
+	BT_STATE_INIT                 = iota + 1 //初始化中
+	BT_STATE_RUNING                          //正常运行中
+	BT_STATE_EXCEPTION_RESTARTING            //异常重启中
+	BT_STATE_NORMAL_RESTARTING               //主动重启中
+)
+
+type TaskRunCallback func(error)
+
+type Task interface {
+	GetRuningState() TaskState
+
+	CancelRuning()
+
+	Restart()
+
+	InitTask(interface{})
+
+	Run(overCallback TaskRunCallback) error
+
+	IsAsync() bool //是否异步任务 同步任务会等待执行完毕直接返回结果,异步任务,开启新线程后执行,不会等待结果
+
+	GetNats() *NatsBus
+	SetNats(*NatsBus)
+
+	GetName() string
+	SetName(string)
+
+	GetID() string
+	SetID(string)
+}
+
+type EventTask interface {
+	Task
+	RegistorEvent(events string, listen EventListener)
+
+	//发送同步事件
+	EmitEvent(name string, data interface{}) (interface{}, error)
+}

+ 92 - 0
sku3d/comm/utils-other.go

@@ -0,0 +1,92 @@
+package comm
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+func GetFreePort() (int, error) {
+	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
+	if err != nil {
+		return 0, err
+	}
+
+	l, err := net.ListenTCP("tcp", addr)
+	if err != nil {
+		return 0, err
+	}
+	defer l.Close()
+	return l.Addr().(*net.TCPAddr).Port, nil
+}
+
+func GetLocalIpByName(adapterName string) net.IP {
+
+	ifaces, err := net.Interfaces()
+	// handle err
+	if err != nil {
+		log.Println("No network:", err)
+		return nil
+	}
+
+	for _, i := range ifaces {
+		if strings.Contains(i.Name, adapterName) {
+			addrs, err := i.Addrs()
+			if err != nil {
+				log.Println("No IP:", err)
+				return nil
+			}
+
+			for _, addr := range addrs {
+				var ip net.IP
+				switch v := addr.(type) {
+				case *net.IPNet:
+					log.Println("IPNET")
+					ip = v.IP
+				case *net.IPAddr:
+					log.Println("IPAddr")
+					ip = v.IP
+				}
+				if ip[0] == 0 {
+					log.Println("Get device:", i.Name)
+					return ip
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func IsExist(path string) bool {
+	_, err := os.Stat(path)
+	if err != nil {
+		if os.IsExist(err) {
+			return true
+		}
+		if os.IsNotExist(err) {
+			return false
+		}
+		fmt.Println(err)
+		return false
+	}
+	return true
+}
+
+func CreateSessionId() string {
+	t := time.Now()
+	date := t.Format("20060102150405")
+	return fmt.Sprintf("s%s-%d", date, t.Nanosecond())
+}
+
+func GetCurrentDir() string {
+	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
+	if err != nil {
+		return ""
+	}
+	return strings.Replace(dir, "\\", "/", -1)
+}

+ 19 - 0
sku3d/sku3d/Dockerfile

@@ -0,0 +1,19 @@
+FROM registry.cn-chengdu.aliyuncs.com/infish/alpine
+# https://github.com/Docker-Hub-frolvlad/docker-alpine-glibc
+
+RUN echo -e https://mirrors.ustc.edu.cn/alpine/v3.15/main > /etc/apk/repositories \
+  && cat /etc/apk/repositories \
+# 设置时区为上海
+  && apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
+  && echo "Asia/Shanghai" > /etc/timezone \
+  && apk del tzdata \
+  && mkdir -p /root/sku3d
+
+WORKDIR /root/sku3d
+
+ADD ./app.yaml ./app.yaml
+ADD ./sku3dweb-service ./sku3dweb-service
+
+EXPOSE 7902
+
+ENTRYPOINT ["./sku3dweb-service"]

+ 30 - 0
sku3d/sku3d/api/api-admin.go

@@ -0,0 +1,30 @@
+package api
+
+import (
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+func RegAdminActionRouter(router *GinRouter) {
+
+	//用户管理
+	CreateCRUD(router, "/webuser", &CRUDOption{
+		Collection: repo.CollectionUser,
+		NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			u := &model.User{}
+			c.ShouldBindJSON(u)
+			u.CreateTime = time.Now()
+			if len(u.Password) > 0 {
+				u.Password = UtilMd5(u.Password)
+			}
+			return u, nil
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.User{}
+		},
+		SearchProject: []string{"name", "phone", "avatar", "canDeployDesign"},
+	})
+}

+ 137 - 0
sku3d/sku3d/api/api.go

@@ -0,0 +1,137 @@
+package api
+
+import (
+	"context"
+	"fmt"
+	"sku3dweb/conf"
+	"sku3dweb/db"
+	"sku3dweb/db/repo"
+
+	"github.com/gin-contrib/cors"
+	"github.com/gin-contrib/sessions"
+	"github.com/gin-contrib/sessions/cookie"
+	"github.com/gin-gonic/gin"
+	"github.com/go-redis/redis/v8"
+)
+
+const (
+	SKU3DADMIN    = "sku3d-admin"
+	SKU3DUSER     = "sku3d-user"
+	SKU3DDESIGNER = "sku3d-designer"
+)
+
+type Service struct {
+	Gin            *gin.Engine
+	Mongo          *db.MongoDB
+	Redis          *redis.Client
+	Port           int32
+	DebugUserId    string
+	DebugUserPhone string
+	DebugUserRole  string
+	JWT            *UtilsJwt
+	Conf           *conf.AppConf
+}
+
+func (svc *Service) Run() {
+	svc.Gin.Run(fmt.Sprintf(":%d", svc.Port))
+}
+
+type ApiSession struct {
+	Svc  *Service
+	User *JWTUser
+}
+
+func (api *ApiSession) CreateRepoCtx() *repo.RepoSession {
+	return &repo.RepoSession{
+		Ctx:    context.Background(),
+		Client: api.Svc.Mongo,
+		Redis:  api.Svc.Redis,
+	}
+}
+
+func NewHttpService(app *conf.AppConf, dbMongo *db.MongoDB, redisClient *redis.Client) *Service {
+
+	engine := gin.Default()
+
+	store := cookie.NewStore([]byte("spu3d-server"))
+	engine.Use(sessions.Sessions("dcsession", store))
+	engine.Static("/public", "static")
+	config := cors.DefaultConfig()
+	// config.AllowOrigins == []string{"http://google.com", "http://facebook.com"}
+	config.AllowAllOrigins = true
+	config.AllowHeaders = append(config.AllowHeaders, "authorization")
+	engine.Use(cors.New(config))
+
+	jwt := NewUitlsJwt(app)
+
+	s := &Service{Conf: app, Redis: redisClient, JWT: jwt, Gin: engine, Mongo: dbMongo, Port: app.Port, DebugUserId: app.Debug.UserId, DebugUserPhone: app.Debug.UserPhone, DebugUserRole: app.Debug.UserRole}
+
+	RegRouters(s)
+
+	return s
+}
+
+//GinRouter 路由
+type GinRouter struct {
+	group *gin.RouterGroup
+	svc   *Service
+}
+
+func (svc *Service) NewGinRouter(path string) *GinRouter {
+	return &GinRouter{group: svc.Gin.Group(path), svc: svc}
+}
+
+//RouterInterface 路由接口
+type RouterInterface interface {
+	GET(path string, httpHandler Handler)
+	POST(path string, httpHandler Handler)
+}
+
+//GET http Get 请求
+func (g GinRouter) GET(path string, httpHandler Handler) {
+	g.group.GET(path, ResultWrapper(httpHandler, g.svc))
+}
+
+//POST http POST 请求
+func (g GinRouter) POST(path string, httpHandler Handler) {
+	g.group.POST(path, ResultWrapper(httpHandler, g.svc))
+}
+
+//GETJWT http Get 请求
+func (g GinRouter) GETJWT(path string, httpHandler JWTHander) {
+	g.group.GET(path, g.svc.JWT.MiddleFunc(), ResultJWTWrapper(httpHandler, g.svc))
+}
+
+//GETJWTTest http Get 请求
+func (g GinRouter) GETJWTTest(path string, httpHandler JWTHander) {
+	g.group.GET(path, ResultJWTTestWrapper(httpHandler, g.svc))
+}
+
+//POSTJWT http POST 请求
+func (g GinRouter) POSTJWT(path string, httpHandler JWTHander) {
+	g.group.POST(path, g.svc.JWT.MiddleFunc(), ResultJWTWrapper(httpHandler, g.svc))
+}
+
+//DeleteJWT http POST 请求
+func (g GinRouter) DeleteJWT(path string, httpHandler JWTHander) {
+	g.group.DELETE(path, g.svc.JWT.MiddleFunc(), ResultJWTWrapper(httpHandler, g.svc))
+}
+
+//DeleteJWT http POST 请求
+func (g GinRouter) DeleteJWTTEST(path string, httpHandler JWTHander) {
+	g.group.DELETE(path, ResultJWTTestWrapper(httpHandler, g.svc))
+}
+
+//POSTJWTTest 测试
+func (g GinRouter) POSTJWTTest(path string, httpHandler JWTHander) {
+	g.group.POST(path, ResultJWTTestWrapper(httpHandler, g.svc))
+}
+
+// 代参数判断权限
+func (g GinRouter) GETJWTKEY(path string, httpHandler JWTHander, keys ...string) {
+	g.group.GET(path, g.svc.JWT.MiddleFunc(), ResultJWTWrapperKey(httpHandler, g.svc, keys))
+}
+
+func (g GinRouter) POSTJWTKEY(path string, httpHandler JWTHander, keys ...string) {
+	g.group.POST(path, g.svc.JWT.MiddleFunc(), ResultJWTWrapperKey(httpHandler, g.svc, keys))
+}

+ 389 - 0
sku3d/sku3d/api/controller.go

@@ -0,0 +1,389 @@
+package api
+
+import (
+	"crypto/md5"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"runtime"
+	"sku3dweb/log"
+	"strconv"
+
+	jwt "github.com/appleboy/gin-jwt/v2"
+	"github.com/gin-gonic/gin"
+)
+
+// Handler 包装http处理函数
+type Handler func(c *gin.Context, apictx *ApiSession) (interface{}, error)
+
+// JWTHander JWT授权的http处理函数
+type JWTHander func(c *gin.Context, apictx *ApiSession) (interface{}, error)
+
+// ResultWrapper 统一返回结果处理
+func ResultWrapper(handle Handler, svc *Service) gin.HandlerFunc {
+
+	return func(c *gin.Context) {
+
+		defer func() {
+			if r := recover(); r != nil {
+
+				fmt.Println("recover success.")
+				fmt.Println(r)
+
+				buf := make([]byte, 1<<16)
+				runtime.Stack(buf, true)
+				fmt.Println("buf", string(buf))
+
+				c.JSON(http.StatusOK, NewFailResultWithData("系统异常", r))
+			}
+		}()
+
+		data, err := handle(c, &ApiSession{Svc: svc, User: nil})
+		if err != nil {
+			httpErr, ok := err.(HTTPError)
+			if ok {
+				c.JSON(http.StatusOK, NewFailResultWithCode(httpErr.Error(), httpErr.Code))
+				return
+			}
+			c.JSON(http.StatusOK, NewFailResult(err.Error()))
+			return
+		}
+
+		if data != nil {
+			c.JSON(http.StatusOK, NewOkResult(data))
+		}
+	}
+}
+
+// ResultJWTWrapper JWT授权处理handler
+func ResultJWTWrapper(handle JWTHander, svc *Service) gin.HandlerFunc {
+
+	return func(c *gin.Context) {
+
+		defer func() {
+			if r := recover(); r != nil {
+				fmt.Println("recover success.")
+				fmt.Println(r)
+
+				buf := make([]byte, 1<<16)
+				runtime.Stack(buf, true)
+				fmt.Println("buf", string(buf))
+
+				c.JSON(http.StatusOK, NewFailResultWithData("系统异常", r))
+			}
+		}()
+
+		claims := jwt.ExtractClaims(c)
+
+		var usr *JWTUser
+
+		if claims["id"] != nil {
+			id := claims["id"].(string)
+			name := claims["phone"].(string)
+			role := claims["role"].(string)
+			parent := claims["parent"].(string)
+			state := int32(claims["state"].(float64))
+			key := claims["key"].(string)
+			userType := 0
+			if claims["userType"] != nil {
+				userType = int(claims["userType"].(float64))
+			}
+
+			usr = &JWTUser{UserType: userType, ID: id, Phone: name, Parent: parent, Role: role, State: state, Key: key}
+		}
+		var apis = &ApiSession{
+			Svc:  svc,
+			User: usr,
+		}
+		data, err := handle(c, apis)
+
+		if err != nil {
+			fmt.Println(err)
+			httpErr, ok := err.(HTTPError)
+			if ok {
+				c.JSON(http.StatusOK, NewFailResultWithCode(httpErr.Error(), httpErr.Code))
+				return
+			}
+
+			c.JSON(http.StatusOK, NewFailResult(err.Error()))
+			return
+		}
+		if data != nil {
+			c.JSON(http.StatusOK, NewOkResult(data))
+		}
+	}
+}
+
+// ResultJWTWrapper JWT授权处理handler
+func ResultJWTWrapperKey(handle JWTHander, svc *Service, keys []string) gin.HandlerFunc {
+
+	return func(c *gin.Context) {
+
+		defer func() {
+			if r := recover(); r != nil {
+				fmt.Println("recover success.")
+				fmt.Println(r)
+
+				buf := make([]byte, 1<<16)
+				runtime.Stack(buf, true)
+				fmt.Println("buf", string(buf))
+
+				c.JSON(http.StatusOK, NewFailResultWithData("系统异常", r))
+			}
+		}()
+
+		claims := jwt.ExtractClaims(c)
+
+		var usr *JWTUser
+
+		if claims["id"] != nil {
+			id := claims["id"].(string)
+			phone := claims["phone"].(string)
+
+			role := claims["role"].(string)
+			parent := claims["parent"].(string)
+			state := int32(claims["state"].(float64))
+			key := ""
+			if claims["key"] != nil {
+				key = claims["key"].(string)
+			}
+			name := ""
+			if claims["name"] != nil {
+				name = claims["name"].(string)
+			}
+			userType := 0
+			if claims["userType"] != nil {
+				userType = int(claims["userType"].(float64))
+			}
+
+			usr = &JWTUser{UserType: userType, ID: id, Name: name, Phone: phone, Parent: parent, Role: role, State: state, Key: key}
+		}
+
+		var apis = &ApiSession{
+			Svc:  svc,
+			User: usr,
+		}
+
+		flag := false
+		for _, key := range keys {
+			if usr.Key == key {
+				flag = true
+				break
+			}
+		}
+		if !flag {
+			c.JSON(http.StatusForbidden, NewFailResult("您没有权限"))
+			c.Abort()
+			return
+		}
+
+		data, err := handle(c, apis)
+
+		if err != nil {
+			fmt.Println(err)
+			httpErr, ok := err.(HTTPError)
+			if ok {
+				c.JSON(http.StatusOK, NewFailResultWithCode(httpErr.Error(), httpErr.Code))
+				return
+			}
+
+			c.JSON(http.StatusOK, NewFailResult(err.Error()))
+			return
+		}
+		if data != nil {
+			c.JSON(http.StatusOK, NewOkResult(data))
+		}
+	}
+}
+
+// ResultJWTTestWrapper test 默认一个测试用户 JWT授权处理handler
+func ResultJWTTestWrapper(handle JWTHander, svc *Service) gin.HandlerFunc {
+
+	return func(c *gin.Context) {
+
+		defer func() {
+			if r := recover(); r != nil {
+
+				fmt.Println("recover success.")
+				fmt.Println(r)
+
+				buf := make([]byte, 1<<16)
+				runtime.Stack(buf, true)
+				fmt.Println("buf", string(buf))
+
+				c.JSON(http.StatusOK, NewFailResultWithData("error", r))
+			}
+		}()
+
+		var usr *JWTUser = &JWTUser{ID: svc.DebugUserId, Phone: svc.DebugUserPhone, Parent: svc.DebugUserId, Role: svc.DebugUserRole}
+
+		data, err := handle(c, &ApiSession{Svc: svc, User: usr})
+
+		if err != nil {
+			fmt.Println(err)
+			httpErr, ok := err.(HTTPError)
+			if ok {
+				c.JSON(http.StatusOK, NewFailResultWithCode(httpErr.Error(), httpErr.Code))
+				return
+			}
+
+			c.JSON(http.StatusOK, NewFailResult(err.Error()))
+			return
+		}
+		c.JSON(http.StatusOK, NewOkResult(data))
+	}
+}
+
+// ResultSuc 成功
+func ResultSuc(c *gin.Context, data interface{}) {
+	c.JSON(http.StatusOK, NewOkResult(data))
+}
+
+// ResultFail 失败
+func ResultFail(c *gin.Context, msg string) {
+	c.JSON(http.StatusOK, NewFailResult(msg))
+}
+
+// ResultFail401 失败2
+func ResultFail401(c *gin.Context, msg string, data interface{}) {
+	c.JSON(http.StatusOK, &HTTPResult{ErrorNo: 401, Result: data, ErrorDesc: msg})
+}
+
+// UtilMd5 结算md5的值
+func UtilMd5(s string) string {
+	data := []byte(s)
+	has := md5.Sum(data)
+	return fmt.Sprintf("%x", has)
+}
+
+// UtilQueryPageSize 分页数据
+func UtilQueryPageSize(c *gin.Context) (page int64, size int64, query map[string]interface{}) {
+	p := c.Query("page")
+	s := c.Query("size")
+	q := c.Query("query")
+
+	var _page = 1
+
+	if len(p) > 0 {
+		_page, _ = strconv.Atoi(p)
+	}
+	page = int64(_page)
+
+	var _size = 10
+	if len(s) > 0 {
+		_size, _ = strconv.Atoi(s)
+	}
+	size = int64(_size)
+
+	if len(q) > 0 {
+		json.Unmarshal([]byte(q), &query)
+	} else {
+		query = map[string]interface{}{}
+	}
+	return
+}
+
+// HTTPResult 客户端统一返回结构体
+type HTTPResult struct {
+	ErrorNo   int32       `json:"errorNo"`
+	Result    interface{} `json:"result"`
+	ErrorDesc string      `json:"errorDesc"`
+}
+
+func (err HTTPResult) Error() string {
+	return err.ErrorDesc
+}
+
+// HTTPError 统一错误处理
+type HTTPError struct {
+	Code    int32  `json:"code"`
+	Message string `json:"message"`
+}
+
+func (err HTTPError) Error() string {
+	return err.Message
+}
+
+// NewOkResult 创建正确结果
+func NewOkResult(obj interface{}) *HTTPResult {
+	return &HTTPResult{ErrorNo: 200, Result: obj}
+}
+
+// NewFailResult 创建错误结构
+func NewFailResult(desc string) *HTTPResult {
+	return &HTTPResult{ErrorNo: 500, Result: nil, ErrorDesc: desc}
+}
+
+// NewFailResultWithData 创建错误返回结果
+func NewFailResultWithData(desc string, data interface{}) *HTTPResult {
+	return &HTTPResult{ErrorNo: 500, Result: data, ErrorDesc: desc}
+}
+
+// NewFailResultWithCode 创建错误结构
+func NewFailResultWithCode(desc string, code int32) *HTTPResult {
+	return &HTTPResult{ErrorNo: code, Result: nil, ErrorDesc: desc}
+}
+
+// NewError NewError
+func NewError(desc string) HTTPError {
+	return HTTPError{Code: 500, Message: desc}
+}
+
+func NewLogError(desc string) HTTPError {
+	pc, file, line, _ := runtime.Caller(1)
+	log.Errorf("%s:%d=>%s %v", file, line, runtime.FuncForPC(pc).Name(), desc)
+
+	return HTTPError{Code: 500, Message: desc}
+}
+
+func NewLogWithError(err error) HTTPError {
+	pc, file, line, _ := runtime.Caller(1)
+	log.Errorf("%s:%d=>%s %s", file, line, runtime.FuncForPC(pc).Name(), err.Error())
+
+	return HTTPError{Code: 500, Message: err.Error()}
+}
+
+// NewErrorWithCode NewErrorWithCode
+func NewErrorWithCode(desc string, code int32) HTTPError {
+	return HTTPError{Code: code, Message: desc}
+}
+
+// JWTUser jwt登录用户
+type JWTUser struct {
+	ID       string `json:"id"`
+	Parent   string `json:"parent"`
+	Name     string `json:"name"`
+	Phone    string `json:"phone"`
+	Role     string `json:"role"`
+	Perms    string `json:"perms"`
+	State    int32  `json:"state"`
+	Key      string `json:"key"`
+	UserType int    `json:"userType"`
+}
+
+func UtilQueryMap(c *gin.Context) map[string]interface{} {
+	query := map[string]interface{}{}
+	q := c.Query("query")
+	if len(q) > 0 {
+		json.Unmarshal([]byte(q), &query)
+	}
+	return query
+}
+
+func BoolValue(value bool) *bool {
+	return &value
+}
+func Int32Value(value int32) *int32 {
+	return &value
+}
+
+func IntValue(value int) *int {
+	return &value
+}
+
+func Float32Value(value float32) *float32 {
+	return &value
+}
+func Float64Value(value float64) *float64 {
+	return &value
+}

+ 60 - 0
sku3d/sku3d/api/imCategory.go

@@ -0,0 +1,60 @@
+package api
+
+import (
+	"errors"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"sku3dweb/log"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+// CategoryCreate 创建分类
+func CategoryCreate(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	var category model.ImCategory
+	err := c.ShouldBindJSON(&category)
+	if err != nil {
+		return nil, err
+	}
+	category.TypeId = -1
+	category.CreateTime = time.Now()
+	category.UpdateTime = time.Now()
+	return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionImCategory, &category)
+}
+
+// CategoryDelete 删除分类
+func DeleteCategory(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	_id := c.Param("id")
+	id, _ := primitive.ObjectIDFromHex(_id)
+	if id.IsZero() {
+		return nil, errors.New("id错误")
+	}
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionImCategory, _id)
+}
+
+// CategoryList 获取分类列表
+func CategoryList(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionImCategory,
+		Page:        page,
+		Size:        size,
+		Query:       query,
+	})
+}
+
+// CategoryUpdate 更新分类
+func UpdateCategory(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	category := &model.ImCategory{}
+	err := c.ShouldBindJSON(&category)
+	if err != nil {
+		log.Error(err)
+		return nil, err
+	}
+	if category.Id.IsZero() {
+		return nil, errors.New("id错误")
+	}
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionImCategory, category.Id.Hex(), category)
+}

+ 98 - 0
sku3d/sku3d/api/imageMaterial.go

@@ -0,0 +1,98 @@
+package api
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+func CreateMaterial(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	material := &model.ImageMaterial{}
+	err := c.ShouldBindJSON(&material)
+	if err != nil {
+		return nil, err
+	}
+
+	return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionImageMaterial, &material)
+}
+
+func DeleteMaterial(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	_id := c.Param("id")
+	id, _ := primitive.ObjectIDFromHex(_id)
+	if id.IsZero() {
+		return nil, errors.New("id错误")
+	}
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionImageMaterial, _id)
+}
+
+func MaterialList(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+	if v, ok := query["name"]; ok {
+		name := v.(string)
+		if len(name) > 0 {
+			delete(query, "name")
+			query["attributes.name"] = bson.M{"$regex": name, "$options": "$i"}
+		}
+	}
+
+	result, err := repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionImageMaterial,
+		Page:        page,
+		Size:        size,
+		Query:       query,
+	})
+
+	return result, err
+}
+
+func UpdateMaterial(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	material := &model.ImageMaterial{}
+	err := c.ShouldBindJSON(&material)
+	if err != nil {
+		return nil, err
+	}
+	if material.Id.IsZero() {
+		return nil, errors.New("id错误")
+	}
+
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionImageMaterial, material.Id.Hex(), material)
+}
+
+func MaterialInsert(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	// 读取json文件
+	jsonData, err := os.ReadFile("api/datalist/11卡证背景.json")
+	if err != nil {
+		return nil, fmt.Errorf("failed to read JSON file: %v", err)
+	}
+
+	var jsonMaterials []*model.ImageMaterial
+	if err := json.Unmarshal(jsonData, &jsonMaterials); err != nil {
+		return nil, fmt.Errorf("failed to parse JSON: %v", err)
+	}
+	fmt.Println("total:", len(jsonMaterials))
+	count := 0
+	// 转换为model.ImageMaterial结构并插入数据库
+	for _, m := range jsonMaterials {
+		m.Id = primitive.NewObjectID()
+		m.Type = 11
+		id, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionImageMaterial, m)
+		if err != nil {
+			fmt.Println(err)
+		} else {
+			fmt.Println(id)
+			count++
+
+		}
+	}
+
+	return count, nil
+}

+ 169 - 0
sku3d/sku3d/api/jwt.go

@@ -0,0 +1,169 @@
+package api
+
+import (
+	"fmt"
+	"log"
+	"sku3dweb/conf"
+	"time"
+
+	jwt "github.com/appleboy/gin-jwt/v2"
+	"github.com/gin-gonic/gin"
+)
+
+var identityKey = "id"
+
+type UtilsJwt struct {
+	jwt *jwt.GinJWTMiddleware
+}
+
+func (j *UtilsJwt) MiddleFunc() gin.HandlerFunc {
+	return j.jwt.MiddlewareFunc()
+}
+
+func (j *UtilsJwt) JwtCreateToken(data interface{}) (string, time.Time, error) {
+	return j.jwt.TokenGenerator(data)
+}
+
+func NewUitlsJwt(app *conf.AppConf) *UtilsJwt {
+
+	var utils = &UtilsJwt{
+		jwt: nil,
+	}
+
+	var jwtImpl *jwt.GinJWTMiddleware
+
+	jwtImpl, err := jwt.New(&jwt.GinJWTMiddleware{
+		Realm:       "spu3d",
+		Key:         []byte("spu3d secret"),
+		Timeout:     time.Hour * 24,
+		MaxRefresh:  time.Hour * 24,
+		IdentityKey: identityKey,
+		PayloadFunc: func(data interface{}) jwt.MapClaims {
+			if v, ok := data.(*JWTUser); ok {
+				return jwt.MapClaims{
+					"id":       v.ID,
+					"name":     v.Name,
+					"phone":    v.Phone,
+					"role":     v.Role,
+					"parent":   v.Parent,
+					"state":    v.State,
+					"key":      v.Key,
+					"userType": v.UserType,
+				}
+			}
+			return jwt.MapClaims{}
+		},
+		IdentityHandler: func(c *gin.Context) interface{} {
+			claims := jwt.ExtractClaims(c)
+			// lg.Debug().Msgf("token: %v\n", claims)
+
+			u := &JWTUser{
+				ID:     claims["id"].(string), //uint32(claims["id"].(float64)),
+				Phone:  claims["phone"].(string),
+				Role:   claims["role"].(string),
+				Parent: claims["parent"].(string),
+			}
+			if claims["key"] != nil {
+				u.Key = claims["key"].(string)
+			}
+			if claims["state"] != nil {
+				u.State = int32(claims["state"].(float64))
+			}
+
+			if claims["name"] != nil {
+				u.Name = claims["name"].(string)
+			}
+			if claims["userType"] != nil {
+				u.UserType = int(claims["userType"].(float64))
+			}
+			return u
+		},
+
+		Authenticator: func(c *gin.Context) (interface{}, error) {
+			return &JWTUser{Phone: "empty"}, nil
+		},
+
+		Authorizator: func(data interface{}, c *gin.Context) bool {
+			u := data.(*JWTUser)
+			return u.State > 0
+		},
+
+		LoginResponse: func(c *gin.Context, status int, token string, expire time.Time) {
+
+			t, _ := jwtImpl.ParseTokenString(token)
+			fmt.Println("LoginResponse==>", status, token)
+
+			ResultSuc(c, map[string]interface{}{
+				"user":   t.Claims,
+				"token":  token,
+				"expire": expire.Format(time.RFC3339),
+			})
+		},
+
+		LogoutResponse: func(c *gin.Context, status int) {
+			ResultSuc(c, true)
+		},
+
+		RefreshResponse: func(c *gin.Context, status int, token string, expire time.Time) {
+
+			ResultSuc(c, map[string]interface{}{
+				"status": status,
+				"token":  token,
+				"expire": expire.Format(time.RFC3339),
+			})
+		},
+
+		Unauthorized: func(c *gin.Context, status int, token string) {
+			if token == "用户名/密码 不正确" {
+				ResultFail401(c, "账号密码不对", map[string]interface{}{
+					"status": -1,
+					"token":  token,
+				})
+				return
+			}
+
+			fmt.Println("xxxxx")
+			fmt.Println(token, status)
+
+			ResultFail401(c, token, map[string]interface{}{
+				"status": status,
+				"token":  token,
+			})
+		},
+
+		HTTPStatusMessageFunc: func(e error, c *gin.Context) string {
+
+			// if e == jwt.ErrFailedAuthentication {
+			// 	return "用户名/密码 不正确"
+			// }
+			fmt.Println("HTTPStatusMessageFunc", e)
+			return e.Error()
+		},
+
+		// TokenLookup is a string in the form of "<source>:<name>" that is used
+		// to extract token from the request.
+		// Optional. Default value "header:Authorization".
+		// Possible values:
+		// - "header:<name>"
+		// - "query:<name>"
+		// - "cookie:<name>"
+		// - "param:<name>"
+		TokenLookup: "header: Authorization, query: token, cookie: jwt",
+		// TokenLookup: "query:token",
+		// TokenLookup: "cookie:token",
+
+		// TokenHeadName is a string in the header. Default value is "Bearer"
+		TokenHeadName: "Bearer",
+
+		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
+		TimeFunc: time.Now,
+	})
+
+	if err != nil {
+		log.Fatal("JWT Error:" + err.Error())
+	}
+
+	utils.jwt = jwtImpl
+
+	return utils
+}

+ 170 - 0
sku3d/sku3d/api/router.go

@@ -0,0 +1,170 @@
+package api
+
+import (
+	"fmt"
+
+	"github.com/gin-gonic/gin"
+)
+
+// RegRouters 注册路由
+func RegRouters(svc *Service) {
+
+	svc.Gin.Use(Logger())
+
+	//登录
+	spud3dGroup := svc.NewGinRouter("/sku3dsite")
+
+	spud3dGroup.POSTJWT("/login/succ", ServiceLoginSucc)
+
+	//数据存储
+	spud3dGroup.POSTJWTTest("/data/image/policy", ServiceObsCreateImagePolicy)
+	// spud3dGroup.GETJWT("/obs/list", ServiceObsList)
+	spud3dGroup.POSTJWT("/obs/upload", ServiceObsUploadPolicy)
+	// spud3dGroup.POSTJWT("/obs/delete", ServiceObsRemove)
+	// spud3dGroup.POSTJWT("/obs/folder", ServiceObsCreateFolder)
+
+	//minio
+	spud3dGroup.POSTJWT("/minio/policy", MinioCreateUserPolicy)
+
+	//资源路由管理
+	CreateWebRouter(spud3dGroup)
+
+	RegMeshCrud(spud3dGroup)
+
+	RegMatCrud(spud3dGroup)
+
+	//web端
+	// spud3dGroup.POST("/web/login/password", ServiceUserLoginPassword)
+	// spud3dGroup.POST("/web/sms", ServiceSmsSend)
+	// spud3dGroup.POST("/web/login/sms", ServiceUserLoginSms)
+	// spud3dGroup.POSTJWT("/web/login/switch", ServiceLoginSwitchParent)
+
+	// spud3dGroup.POSTJWT("/web/user/loginout", ServiceWebUserLoginout)
+	// spud3dGroup.GETJWT("/web/user/profile", ServiceWebUserProfile)
+	// spud3dGroup.POSTJWT("/web/user/profile", ServiceWebUserUpdateProfile)
+	// spud3dGroup.POSTJWT("/web/user/resetpwd", ServiceWebUserResetPwd)
+
+	// spud3dGroup.POSTJWT("/web/user/editset", ServiceWebUserUpdateEditSet)
+	// spud3dGroup.POSTJWT("/web/user/editset/basecolor", ServiceWebUserUpdateEditSetBaseColor)
+	// spud3dGroup.POSTJWT("/web/user/editset/defaultEnv3d", ServiceWebUserUpdateEditSetDefaultEnv3d)
+
+	// spud3dGroup.POSTJWT("/design/create", ServiceDesignCreate)
+	// spud3dGroup.POSTJWT("/design/resetshoe", ServiceDesignResetShoe)
+	// spud3dGroup.POSTJWT("/design/resetdesign", ServiceDesignResetDesign)
+
+	// spud3dGroup.POSTJWT("/design/update/:id", ServiceDesignUpdate)
+
+	spud3dGroup.GETJWT("/design/list", ServiceDesignList)
+	spud3dGroup.GET("/design/public", ServiceDesignPublicList)
+
+	// spud3dGroup.POSTJWT("/design/delete/:id", ServiceDesignDelete)
+	// spud3dGroup.POSTJWT("/design/scene/create/:id/:scope", ServiceDesignCreateScene)
+	// spud3dGroup.POSTJWT("/design/scene/update/:id/:scope", ServiceDesignUpdateScene)
+	// spud3dGroup.POSTJWT("/design/scene/delete/:id/:scope", ServiceDesignRomoveScene)
+	// spud3dGroup.POSTJWT("/design/rename/:id", ServiceDesignRename)
+
+	// spud3dGroup.POSTJWT("/design/scene/thumbnail/:id", ServiceDesignSceneThumbnail)
+	// spud3dGroup.POSTJWT("/design/public/:id", ServiceDesignPublic)
+	// spud3dGroup.POSTJWT("/design/copy/:id", ServiceDesignCopy)
+
+	spud3dGroup.GET("/design/share", ServiceShareDesign)
+	spud3dGroup.GET("/design/detail/:id", ServiceDesignDetail)
+
+	spud3dGroup.POSTJWT("/design/scene/render", ServiceTaskCreate)
+
+	spud3dGroup.GETJWT("/task/list/:id", ServiceTaskList)
+	spud3dGroup.GETJWT("/task/detail/:id", ServiceTaskDetail)
+	spud3dGroup.POSTJWT("/task/remove/:id", ServiceTaskRemove)
+
+	CreateBackgroundRouter(spud3dGroup)
+
+	// spud3dGroup.POST("/hdr/conv", ServiceConvHdr)
+
+	RegAdminRouter(spud3dGroup)
+
+	RegAdminActionRouter(spud3dGroup)
+
+	CreateCatRouter(spud3dGroup)
+	CreatePermsRouter(spud3dGroup)
+
+	CreateQiyeUserRouter(spud3dGroup)
+	CreateUserUploadRouter(spud3dGroup)
+
+	CreateLibRouter(spud3dGroup)
+
+	CreateUserCamerasRouter(spud3dGroup)
+
+	RegisterTeamLib(spud3dGroup)
+
+	RegCollocateRouter(spud3dGroup)
+
+	RegImageMat(spud3dGroup)
+
+	// 会员
+	Member(spud3dGroup)
+
+	// 会员用户
+	MemberUser(spud3dGroup)
+
+	// 会员申请
+	Order(spud3dGroup)
+	// for _, proxy := range svc.Conf.Proxies {
+	// 	spud3dGroup.group.GET(proxy.Prefix, utils.CreateServiceProxy(proxy))
+	// 	spud3dGroup.group.POST(proxy.Prefix, utils.CreateServiceProxy(proxy))
+	// }
+	Queentree(spud3dGroup)
+
+	CreateUserSettingRouter(spud3dGroup)
+
+	RegTree(spud3dGroup)
+
+	RegTreeAssetOps(spud3dGroup)
+
+	RegTreeAssetRemove(spud3dGroup)
+
+	regAssetUpdator(spud3dGroup)
+
+	RegShareApis(spud3dGroup)
+
+	RegQueenterApis(spud3dGroup)
+
+	// !20241220新增
+	// 分类管理
+	spud3dGroup.POSTJWT("/im/cate/create", CategoryCreate)
+	spud3dGroup.POSTJWT("/im/cate/delete/:id", DeleteCategory)
+	spud3dGroup.GETJWT("/im/cate/list", CategoryList)
+	spud3dGroup.POSTJWT("/im/cate/update", UpdateCategory)
+
+	// 素材管理
+	spud3dGroup.POSTJWT("/im/material/create", CreateMaterial)
+	spud3dGroup.POSTJWT("/im/material/delete/:id", DeleteMaterial)
+	spud3dGroup.GETJWT("/im/material/list", MaterialList)
+	spud3dGroup.POSTJWT("/im/material/update", UpdateMaterial)
+
+	// 图片搜索新增
+	spud3dGroup.POSTJWT("/tree/:assetType/u2m/:id", updateToMilvus)
+	spud3dGroup.POSTJWT("/tree/:assetType/search/image", searchToMilvus)
+}
+
+func Logger() gin.HandlerFunc {
+
+	return func(c *gin.Context) {
+		// 开始时间
+		// start := time.Now()
+		// 处理请求
+		c.Next()
+		// 结束时间
+		// end := time.Now()
+		//执行时间
+		// latency := end.Sub(start)
+
+		path := c.Request.URL.Path
+
+		clientIP := c.ClientIP()
+		// method := c.Request.Method
+		// statusCode := c.Writer.Status()
+
+		out := fmt.Sprintf("%15s=> %s", clientIP, path)
+		fmt.Println(out)
+	}
+}

+ 80 - 0
sku3d/sku3d/api/search-image.go

@@ -0,0 +1,80 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/bus"
+	"sku3dweb/db/model"
+	"sort"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+type ToMilvusReq struct {
+	Scope    string  `json:"scope"`
+	Url      string  `json:"url"`
+	Limit    int     `json:"limit"`
+	MaxScore float64 `json:"maxScore"`
+	MinScore float64 `json:"minScore"`
+}
+
+func updateToMilvus(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	assetType := c.Param("assetType")
+	id := c.Param("id")
+	if len(assetType) < 1 || len(id) < 1 {
+		return nil, fmt.Errorf("参数错误")
+	}
+	var req ToMilvusReq
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		return nil, err
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, req.Scope, assetType)
+	tableName := fmt.Sprintf("%s_%s", srcColl, apictx.User.ID)
+	// 删除旧的图片
+	go deleteImageToMlivus(tableName, id)
+	// 等待删除
+	time.Sleep(50 * time.Millisecond)
+	// 添加新的图片
+	go uploadImageToMlivus(tableName, id, req.Url)
+	return true, nil
+}
+
+func searchToMilvus(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	assetType := c.Param("assetType")
+	var req ToMilvusReq
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		return nil, err
+	}
+	fmt.Printf("search params : %v\n", req)
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, req.Scope, assetType)
+	tableName := fmt.Sprintf("%s_%s", srcColl, apictx.User.ID)
+	lists, err := searchImageToMlivus(tableName, &req)
+	if err != nil {
+		fmt.Println(err)
+		return nil, err
+	}
+	fmt.Printf("image search result: %v\n", lists)
+	handleLists := make([]map[string]interface{}, 0)
+	if len(lists) > 0 {
+		sort.Sort(SortMilvusSearchSlice(lists))
+		lists := FilterMilvusSocreRange(lists, req.MaxScore, req.MinScore)
+
+		return bus.BusTreeSearchImage(&bus.BusTreeSearchImageReq{
+			DbName: Sku3dDbName,
+			Coll:   srcColl,
+			Result: lists,
+		})
+	}
+
+	return handleLists, nil
+}

+ 15 - 0
sku3d/sku3d/api/servcie-test.go

@@ -0,0 +1,15 @@
+package api
+
+import (
+	"fmt"
+
+	"github.com/gin-gonic/gin"
+)
+
+//ServiceTestHttp JWT授权的http处理函数
+func ServiceTestHttp(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	fmt.Println("test")
+
+	return "hello world " + apictx.Svc.DebugUserPhone, nil
+}

+ 193 - 0
sku3d/sku3d/api/service-array.go

@@ -0,0 +1,193 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/repo"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type DetailHandler func(c *gin.Context, apictx *ApiSession, entity interface{}) (interface{}, error)
+
+type ArrayCRUDOption struct {
+	Collection     string
+	ArrayFieldPath string
+	NewModel       CreateModel
+	EmtyModel      CreateModel
+	DocModel       CreateModel
+	SearchProject  []string
+	Query          repo.Map
+	OnUpdate       Update
+	OnDetail       DetailHandler
+
+	NeedsUpdate bool
+	NeedsRemove bool
+}
+
+func CreateArrayCRUD(router *GinRouter, prefix string, option *ArrayCRUDOption) {
+
+	//创建
+	creatpath := fmt.Sprintf("%s/create/:id", prefix)
+	if option.NewModel != nil {
+
+		router.POSTJWT(creatpath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			id := c.Param("id")
+			if len(id) < 1 {
+				return nil, NewError("参数id不能为空")
+			}
+			var entity interface{} = nil
+			entity = option.NewModel(c)
+			if entity == nil {
+				return nil, NewError("数据已存在!")
+			}
+			ret, _ := repo.RepoDocArrayAppend(apictx.CreateRepoCtx(), option.Collection, id, option.ArrayFieldPath, entity)
+			if ret.ModifiedCount == 1 {
+				return entity, nil
+			}
+			return nil, NewError("创建失败!")
+		})
+	}
+
+	//更新
+	if option.NeedsUpdate {
+		updatePath := fmt.Sprintf("%s/update/:id", prefix)
+		router.POSTJWT(updatePath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			docId := c.Param("id")
+			if len(docId) < 1 {
+				return nil, NewError("id不能为空!")
+			}
+			m := option.EmtyModel(c)
+			err := c.ShouldBindJSON(m)
+			if err != nil {
+				fmt.Println(err)
+				return nil, NewError("参数解析错误")
+			}
+
+			if option.OnUpdate != nil {
+				option.OnUpdate(c, apictx, m, docId)
+			}
+
+			//Query:       repo.Map{"scenes.id": scene.Id},
+			//Set:         repo.Map{"scenes.$": scene},
+
+			updateQuery := repo.Map{}
+			for k, v := range option.Query {
+				p := fmt.Sprintf("%s.%s", option.ArrayFieldPath, k)
+				updateQuery[p] = v
+			}
+			updateSet := repo.Map{}
+			spath := fmt.Sprintf("%s.$", option.ArrayFieldPath)
+			updateSet[spath] = m
+
+			updateOption := &repo.ArrayOneUpdateOption{CollectName: option.Collection, Id: docId, Query: updateQuery, Set: updateSet}
+
+			return repo.RepoDocArrayOneUpdate(apictx.CreateRepoCtx(), updateOption)
+		})
+	}
+
+	//数组所有数据
+	SearchPath := fmt.Sprintf("%s/list/:id", prefix)
+
+	pageSearchFn := func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		docId := c.Param("id")
+		if len(docId) < 1 {
+			return nil, NewError("id不能为空!")
+		}
+		query := UtilQueryMap(c)
+		query["_id"], _ = primitive.ObjectIDFromHex(docId)
+
+		ok, docItem := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: option.Collection,
+			Query:       query,
+			Project:     option.SearchProject,
+		})
+		if !ok {
+			return nil, NewError("获取列表失败!")
+		}
+		return docItem, nil
+	}
+
+	router.GETJWT(SearchPath, pageSearchFn)
+
+	//删除
+	if option.NeedsRemove {
+		removePath := fmt.Sprintf("%s/delete/:id", prefix)
+		router.POSTJWT(removePath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			id := c.Param("id")
+			if len(id) < 1 {
+				return nil, NewError("参数不能为空")
+			}
+
+			body := map[string]interface{}{}
+			c.ShouldBindJSON(&body)
+
+			ArrayQuery := repo.Map{}
+			if len(body) < 1 {
+				return nil, NewError("body参数不能为空")
+			}
+			query := bson.M{}
+			for k, v := range body {
+				query[k] = v
+			}
+			ArrayQuery[option.ArrayFieldPath] = query
+
+			options := &repo.ArrayOneRemoveOption{
+				CollectName: option.Collection,
+				Id:          id,
+				ArrayQuery:  ArrayQuery,
+			}
+
+			ret, err := repo.RepoDocArrayOneRemove(apictx.CreateRepoCtx(), options)
+			if ret.ModifiedCount < 1 {
+				return nil, NewError("删除失败!")
+			}
+
+			return ret, err
+		})
+	}
+
+	//详情
+	if option.OnDetail != nil && option.DocModel != nil {
+		DetailPath := fmt.Sprintf("%s/detail/:id", prefix)
+		router.GETJWT(DetailPath, func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+			docId := c.Param("id")
+			if len(docId) < 1 {
+				return nil, NewError("id不能为空!")
+			}
+			query := UtilQueryMap(c)
+
+			filter := repo.Map{}
+			if len(query) > 0 {
+				filter[option.ArrayFieldPath] = query
+			}
+
+			m := option.DocModel(c)
+
+			ok, ret := repo.RepoDocArraySearch(apictx.CreateRepoCtx(), &repo.ArrayOneSearchOption{
+				CollectName: option.Collection,
+				ArrayQuery:  filter,
+				Id:          docId,
+				Field:       option.ArrayFieldPath,
+			})
+
+			if !ok {
+				return nil, NewError("数据不存在!")
+			}
+
+			detail, err := option.OnDetail(c, apictx, ret)
+
+			if err != nil {
+				return nil, err
+			}
+			if detail != nil {
+				return detail, nil
+			}
+			return m, nil
+		})
+	}
+
+}

+ 219 - 0
sku3d/sku3d/api/service-asset-remove.go

@@ -0,0 +1,219 @@
+package api
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"sku3dweb/bus"
+	"sku3dweb/db/model"
+
+	"github.com/gin-gonic/gin"
+	"github.com/xuri/excelize/v2"
+	"infish.cn/comm"
+)
+
+func RegTreeAssetRemove(router *GinRouter) {
+	router.POSTJWT("/tree/user/:assetType/r/:id", removeUserAsset)
+	router.POSTJWT("/tree/team/:assetType/r/:id", removeTeamAsset)
+	router.POSTJWT("/tree/company/:assetType/r/:id", removeCompanyAsset)
+
+	router.GETJWT("/assets/user/download", DownloadUserAsset)
+	router.POSTJWT("/assets/user/download", PostDownloadUserAsset)
+	router.GETJWT("/assets/user/detail", detailUserAsset)
+	router.GETJWT("/assets/team/detail", detailTeamAsset)
+	router.GETJWT("/assets/company/detail", detailCompanyAsset)
+	router.GET("/assets/platform/detail", detailPlatformAsset)
+}
+
+type DownLoadUserAssetRes struct {
+	CompName    string `json:"compName"`
+	MatName     string `json:"matName"`
+	MatNum      string `json:"matNum"`
+	CompanyName string `json:"companyName"`
+}
+
+func PostDownloadUserAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	var req []*DownLoadUserAssetRes
+	err := c.BindJSON(&req)
+	if err != nil {
+		return nil, err
+
+	}
+	if len(req) < 1 {
+		return nil, errors.New("没有数据")
+	}
+	f := excelize.NewFile()
+	execl := NewProductBill(f)
+	execl.Content = req
+	execl.draws()
+	c.Header("Content-Type", "application/octet-stream")
+	c.Header("Content-Disposition", "attachment; filename="+"boom.xlsx")
+	c.Header("Content-Transfer-Encoding", "binary")
+
+	err = f.Write(c.Writer)
+	if err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+// !20240401迁移到 [POST] /assets/user/download,前端解析数据,后端只负责生成excel
+// 验证后删除
+func DownloadUserAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	sceneId := c.Query("sceneId")
+	if len(sceneId) < 1 {
+		return nil, errors.New("sceneId 不能为空")
+	}
+	result, err := detail(c, apictx, "user")
+	if err != nil {
+		return nil, err
+	}
+	j_res, err := json.Marshal(result)
+	if err != nil {
+		fmt.Println(err)
+		return nil, err
+	}
+	asset := comm.AssetPackage{}
+	err = json.Unmarshal(j_res, &asset)
+	if err != nil {
+		fmt.Println(err)
+		return nil, err
+	}
+
+	// 获取场景产品
+	sceneProds := []*comm.PackageSceneProduct{}
+	scenes := asset.Source.Scenes
+	for _, scene := range scenes {
+		if scene.Id == sceneId {
+			sceneProds = scene.Products
+		}
+	}
+	// 获取products
+	products := []*comm.PackageProduct{}
+	for _, product := range asset.Source.Products {
+		for _, sp := range sceneProds {
+			if product.Id == sp.ProdId {
+				products = append(products, product)
+			}
+		}
+
+	}
+	// 获取mats
+	matInfos := []*DownLoadUserAssetRes{}
+	for _, pd := range products {
+		for _, pc := range pd.Components {
+			for _, mat := range asset.Source.Mats {
+				if mat.Id == pc.MatId {
+					supplyName := "-"
+					if userData, ok := pc.UserData.(map[string]interface{}); ok {
+						if matSupply, ok := userData["matSupplyName"]; ok {
+							if sname, ok := matSupply.(string); ok {
+								supplyName = sname
+							}
+						}
+					}
+					matName := "-"
+					matCusNum := "-"
+					if mat.Name != "未命名" && len(mat.Name) > 0 {
+						matName = mat.Name
+					}
+					if mat.CusNum != "未命名" && len(mat.CusNum) > 0 {
+						matCusNum = mat.CusNum
+					}
+					matInfos = append(matInfos, &DownLoadUserAssetRes{
+						CompName:    pc.Name,
+						MatName:     matName,
+						MatNum:      matCusNum,
+						CompanyName: supplyName,
+					})
+
+				}
+
+			}
+
+		}
+	}
+
+	if len(matInfos) < 1 {
+		return nil, errors.New("没有数据")
+	}
+	f := excelize.NewFile()
+	execl := NewProductBill(f)
+	execl.Content = matInfos
+	execl.draws()
+	c.Header("Content-Type", "application/octet-stream")
+	c.Header("Content-Disposition", "attachment; filename="+"boom.xlsx")
+	c.Header("Content-Transfer-Encoding", "binary")
+
+	err = f.Write(c.Writer)
+	if err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
+
+func detail(c *gin.Context, apictx *ApiSession, scope string) (interface{}, error) {
+	assetType := c.Query("type")
+	id := c.Query("id")
+	if len(id) < 1 {
+		return nil, fmt.Errorf("参数错误")
+	}
+	isCompany := false
+	isSku3d := false
+	if scope == SCOPE_PLATFORM {
+		isSku3d = true
+		isCompany = true
+	} else {
+		isSku3d = apictx.User.Parent == model.SKU3D_USERID
+		isCompany = model.USERTYPE_COMPANY == apictx.User.UserType
+	}
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, scope, assetType)
+	fmt.Println(srcColl)
+
+	return bus.BusTreeAssetDetail(Sku3dDbName, srcColl, id)
+}
+
+func detailUserAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return detail(c, apictx, "user")
+}
+
+func detailTeamAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return detail(c, apictx, "team")
+}
+
+func detailCompanyAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return detail(c, apictx, "company")
+}
+func detailPlatformAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return detail(c, apictx, SCOPE_PLATFORM)
+}
+
+func remove(c *gin.Context, apictx *ApiSession, scope string) (interface{}, error) {
+	assetType := c.Param("assetType")
+	id := c.Param("id")
+	if len(assetType) < 1 || len(id) < 1 {
+		return nil, fmt.Errorf("参数错误")
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, scope, assetType)
+
+	return true, bus.BusTreeAssetPackRemove(Sku3dDbName, srcColl, id)
+}
+
+func removeUserAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	return remove(c, apictx, "user")
+}
+
+func removeTeamAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return remove(c, apictx, "team")
+}
+
+func removeCompanyAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return remove(c, apictx, "company")
+}

+ 393 - 0
sku3d/sku3d/api/service-asset-updator.go

@@ -0,0 +1,393 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/bus"
+	"sku3dweb/db/model"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"infish.cn/comm"
+)
+
+func regAssetUpdator(router *GinRouter) {
+
+	router.POSTJWT("/asset/user/:assetType/header", updateUserAssetComm)
+	router.POSTJWT("/asset/team/:assetType/header", updateTeamAssetComm)
+	router.POSTJWT("/asset/company/:assetType/header", updateCompanyAssetComm)
+
+	//目前只支持自己的
+	router.POSTJWT("/asset/user/:assetType/retreat", retreatAssetPack)
+
+	//目前只支持自己的
+	router.POSTJWT("/shadow/:scope/:assetType/:id", createAssetShadow)
+
+	router.POSTJWT("/asset/user/:assetType/create", createAssetPack)
+	// !20240411 迁移到asset/:scope/:assetType/insert scope包括user,team,company
+	// router.POSTJWT("/asset/user/:assetType/insert", insertAssetPack)
+
+	router.POSTJWT("/asset/user/:assetType/insert", insertUserAsset)
+	router.POSTJWT("/asset/team/:assetType/insert", insertTeamAsset)
+	router.POSTJWT("/asset/company/:assetType/insert", insertCompanyAsset)
+
+	router.POSTJWT("/company/publish", companyPublish)
+}
+
+func insertUserAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return insertScopeAsset(c, apictx, "user")
+}
+
+func insertTeamAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return insertScopeAsset(c, apictx, "team")
+}
+
+func insertCompanyAsset(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return insertScopeAsset(c, apictx, "company")
+}
+
+func createAssetPack(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	assetType := c.Param("assetType")
+	if len(assetType) < 1 {
+		return nil, fmt.Errorf("参数错误")
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	req := &bus.UpdateCommReq{Db: Sku3dDbName}
+
+	if isAssetPack(assetType) {
+		req.Pack = &comm.AssetPackage{}
+		err := c.ShouldBindJSON(req.Pack)
+		if err != nil {
+			return nil, err
+		}
+		req.Pack.CreateTime = time.Now()
+		req.Pack.UpdateTime = time.Now()
+		req.Pack.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		req.Pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Pack.OwnerType = "user"
+		if req.Pack.Source != nil {
+			if assetType == "shoe" || assetType == "shoes" {
+				req.Pack.Source.ViewMode = "scene"
+			} else {
+				req.Pack.Source.ViewMode = "prod"
+			}
+
+		}
+
+		transTask := req.Pack.TransTask
+		if comm.TransTaskIsNotEmpty(transTask) {
+			req.Pack.Source = nil
+		}
+
+	} else if isFabric3d(assetType) {
+		req.Mat3d = &comm.AssetMatGroup{}
+		err := c.ShouldBindJSON(req.Mat3d)
+		if err != nil {
+			return nil, err
+		}
+		req.Mat3d.Enable = BoolValue(false)
+		req.Mat3d.AssetState = comm.AssetState_Succ
+		req.Mat3d.CreateTime = time.Now()
+		req.Mat3d.UpdateTime = time.Now()
+		req.Mat3d.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		req.Mat3d.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Mat3d.OwnerType = "user"
+	} else if isAssetImage(assetType) {
+		req.Img = &comm.AssetImage{}
+		err := c.ShouldBindJSON(req.Img)
+		if err != nil {
+			return nil, err
+		}
+		req.Img.Enable = BoolValue(false)
+		req.Img.AssetState = comm.AssetState_Succ
+		req.Img.CreateTime = time.Now()
+		req.Img.UpdateTime = time.Now()
+		req.Img.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		req.Img.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Img.OwnerType = "user"
+	} else if isEnv3d(assetType) {
+		req.Env3d = &comm.AssetEnv3dHdr{}
+		err := c.ShouldBindJSON(req.Env3d)
+		if err != nil {
+			return nil, err
+		}
+		req.Env3d.Enable = BoolValue(false)
+		req.Env3d.AssetState = comm.AssetState_Succ
+		req.Env3d.CreateTime = time.Now()
+		req.Env3d.UpdateTime = time.Now()
+		req.Env3d.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		req.Env3d.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Env3d.OwnerType = "user"
+	} else if isOtherMat(assetType) {
+		req.OtherMat = &comm.AssetMat{}
+		err := c.ShouldBindJSON(req.OtherMat)
+		if err != nil {
+			return nil, err
+		}
+		req.OtherMat.Enable = BoolValue(false)
+		req.OtherMat.AssetState = comm.AssetState_Succ
+		req.OtherMat.CreateTime = time.Now()
+		req.OtherMat.UpdateTime = time.Now()
+		req.OtherMat.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		req.OtherMat.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.OtherMat.OwnerType = "user"
+	}
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, "user", assetType)
+	req.Coll = srcColl
+	fmt.Println("createAssetPack===>:\n", req)
+
+	ret, err := bus.BusTreeAssetCreate(req)
+	fmt.Printf("crate id%v,err:%v", ret, err)
+
+	return ret, err
+}
+
+func insertScopeAsset(c *gin.Context, apictx *ApiSession, scope string) (interface{}, error) {
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+
+	req := &bus.UpdateCommReq{Db: Sku3dDbName}
+	assetType := c.Param("assetType")
+
+	if isAssetPack(assetType) {
+		req.Pack = &comm.AssetPackage{}
+		err := c.ShouldBindJSON(req.Pack)
+		if err != nil {
+			return nil, err
+		}
+		req.Pack.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		// !ownerId 如果是team,company,则是team,parent的id
+		// !前端做了相关处理,这里不需要再处理
+		// req.Pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Pack.OwnerType = scope
+
+		if isShoePack(assetType) && req.Pack.Source != nil {
+			req.Pack.Source.ViewMode = "scene"
+		}
+	} else if isFabric3d(assetType) {
+		req.Mat3d = &comm.AssetMatGroup{}
+		err := c.ShouldBindJSON(req.Mat3d)
+		if err != nil {
+			return nil, err
+		}
+		req.Mat3d.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		// req.Pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Mat3d.OwnerType = scope
+
+	} else if isAssetImage(assetType) {
+		req.Img = &comm.AssetImage{}
+		err := c.ShouldBindJSON(req.Img)
+		if err != nil {
+			return nil, err
+		}
+		req.Img.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		// req.Pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.Img.OwnerType = scope
+
+	} else if isOtherMat(assetType) {
+		req.OtherMat = &comm.AssetMat{}
+		err := c.ShouldBindJSON(req.OtherMat)
+		if err != nil {
+			return nil, err
+		}
+		req.OtherMat.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		// req.Pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+		req.OtherMat.OwnerType = scope
+
+	}
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, scope, assetType)
+	req.Coll = srcColl
+
+	return bus.BusTreeInsertAssetPack(Sku3dDbName, srcColl, &bus.AssetInsertReq{
+		DbName:   Sku3dDbName,
+		Coll:     srcColl,
+		Pack:     req.Pack,
+		Mat3d:    req.Mat3d,
+		Img:      req.Img,
+		OtherMat: req.OtherMat,
+	})
+}
+
+func insertAssetPack(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	assetType := c.Param("assetType")
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	if !isAssetPack(assetType) {
+		return nil, fmt.Errorf("不支持的类型" + assetType)
+	}
+
+	pack := &comm.AssetPackage{}
+	err := c.ShouldBindJSON(pack)
+	if err != nil {
+		return nil, err
+	}
+
+	pack.CreateTime = time.Now()
+	pack.UpdateTime = time.Now()
+	pack.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+	pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent)
+	pack.OwnerType = "user"
+	if isShoePack(assetType) {
+		pack.Source.ViewMode = "scene"
+	} else {
+		pack.Source.ViewMode = "prod"
+	}
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, "user", assetType)
+
+	return bus.BusTreeAssetPackAdd(Sku3dDbName, srcColl, pack)
+}
+
+func retreatAssetPack(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	assetType := c.Param("assetType")
+	scope := "user"
+	if len(assetType) < 1 || len(scope) < 1 {
+		return nil, fmt.Errorf("参数错误")
+	}
+
+	if !isAssetPack(assetType) {
+		return nil, fmt.Errorf("不支持的类型")
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	req := &bus.UpdateCommReq{Db: Sku3dDbName}
+	req.Pack = &comm.AssetPackage{}
+	err := c.ShouldBindJSON(req.Pack)
+	if err != nil {
+		return nil, err
+	}
+
+	req.Pack.Source = &comm.Queen3dPackageSource{} //清空数据,只需要一个Id参数就够了
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, scope, assetType)
+	req.Coll = srcColl
+
+	return req, bus.BusTreeAssetReProcess(req)
+}
+
+func createAssetShadow(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	assetType := c.Param("assetType")
+	scope := c.Param("scope")
+	id := c.Param("id")
+	if len(assetType) < 1 || len(scope) < 1 || len(id) < 1 {
+		return nil, fmt.Errorf("参数错误")
+	}
+	if !isAssetPack(assetType) {
+		return nil, fmt.Errorf("不支持的类型")
+	}
+
+	body := struct {
+		Width int
+		Scale int
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	req := &bus.UpdateCommReq{Db: Sku3dDbName}
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, scope, assetType)
+
+	return req, bus.BusTreeAssetCreateShadow(&bus.CreateShadowReq{
+		Db:    Sku3dDbName,
+		Coll:  srcColl,
+		Id:    id,
+		Width: body.Width,
+		Scale: body.Scale,
+	})
+}
+
+func updateUserAssetComm(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return updateAssetComm(c, apictx, "user")
+}
+
+func updateTeamAssetComm(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return updateAssetComm(c, apictx, "team")
+}
+
+func updateCompanyAssetComm(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return updateAssetComm(c, apictx, "company")
+}
+
+func updateAssetComm(c *gin.Context, apictx *ApiSession, scope string) (interface{}, error) {
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+
+	req := &bus.UpdateCommReq{Db: Sku3dDbName}
+	assetType := c.Param("assetType")
+
+	if isAssetPack(assetType) {
+		req.Pack = &comm.AssetPackage{}
+		err := c.ShouldBindJSON(req.Pack)
+		if err != nil {
+			return nil, err
+		}
+
+		if isShoePack(assetType) && req.Pack.Source != nil {
+			req.Pack.Source.ViewMode = "scene"
+		}
+	} else if isFabric3d(assetType) {
+		req.Mat3d = &comm.AssetMatGroup{}
+		err := c.ShouldBindJSON(req.Mat3d)
+		if err != nil {
+			return nil, err
+		}
+	} else if isAssetImage(assetType) {
+		req.Img = &comm.AssetImage{}
+		err := c.ShouldBindJSON(req.Img)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, scope, assetType)
+	req.Coll = srcColl
+
+	return req, bus.BusTreeAssetUpdate(req)
+}
+
+func companyPublish(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	body := &struct {
+		AssetType string
+		Id        string
+		Publish   bool
+	}{}
+	err := c.ShouldBindJSON(body)
+	if err != nil {
+		return nil, fmt.Errorf("参数错误")
+	}
+
+	isCompany := true
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+
+	req := &bus.UpdateCommReq{Db: Sku3dDbName}
+	assetType := body.AssetType
+	if isAssetPack(assetType) {
+		req.Pack = &comm.AssetPackage{}
+		req.Pack.Id, _ = primitive.ObjectIDFromHex(body.Id)
+		req.Pack.Enable = &body.Publish
+
+	} else if isFabric3d(assetType) {
+		req.Mat3d = &comm.AssetMatGroup{}
+		req.Mat3d.Id, _ = primitive.ObjectIDFromHex(body.Id)
+		req.Mat3d.Enable = &body.Publish
+
+	} else if isAssetImage(assetType) {
+		req.Img = &comm.AssetImage{}
+		req.Img.Id, _ = primitive.ObjectIDFromHex(body.Id)
+		req.Img.Enable = &body.Publish
+	}
+
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, "company", assetType)
+	req.Coll = srcColl
+
+	return req, bus.BusTreeAssetUpdate(req)
+}

+ 317 - 0
sku3d/sku3d/api/service-asset.go

@@ -0,0 +1,317 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/bus"
+	"sku3dweb/db/model"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"infish.cn/comm"
+)
+
+func RegTreeAssetOps(router *GinRouter) {
+	router.POSTJWT("/asset/usersubmit/team", usersubmit2team)
+
+	router.POSTJWT("/asset/usersubmit/company", usersubmit2company)
+
+	router.POSTJWT("/asset/teamsubmit/company", teamsubmit2company)
+
+	router.POSTJWT("/asset/clone/company", clonecompany2self)
+	router.POSTJWT("/asset/clone/team", cloneteam2self)
+
+}
+
+type SubmitBody struct {
+	AssetType string
+	Id        string
+}
+
+// 提交到部门
+func usersubmit2team(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := &SubmitBody{}
+	err := c.ShouldBindJSON(body)
+	if err != nil {
+		return nil, err
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	//from collection 2
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, "user", body.AssetType)
+
+	//target collection
+	targetColl := getAssetTypeCollectin(isCompany, isSku3d, "team", body.AssetType)
+
+	//userInfo
+
+	user, err := bus.GetUserInfo(apictx.User.ID)
+	if err != nil {
+		return nil, err
+	}
+	userInfo := &comm.AssetUserInfo{Name: user.Name, Thumbnail: &comm.OssType{Url: user.Avatar}}
+
+	//获取当前用户的所属部门
+	//ownerType = "team"
+	//ownerId = "xx"
+
+	if isAssetPack(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetPackDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(user.TeamId)
+		pack.OwnerType = "team"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		return bus.BusTreeAssetPackAdd(Sku3dDbName, targetColl, pack)
+
+	} else if isFabric3d(body.AssetType) {
+
+		group, err := bus.BusTreeAssetMatgroupDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+		group.OwnerId, _ = primitive.ObjectIDFromHex(user.TeamId)
+		group.OwnerType = "team"
+		group.UserInfo = userInfo
+		group.Id = primitive.NilObjectID
+
+		return bus.BusTreeAssetMatgroupAdd(Sku3dDbName, targetColl, group)
+	} else if isAssetImage(body.AssetType) {
+
+		group, err := bus.BusTreeAssetImageDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+		group.OwnerId, _ = primitive.ObjectIDFromHex(user.TeamId)
+		group.OwnerType = "team"
+		group.UserInfo = userInfo
+		group.Id = primitive.NilObjectID
+
+		return bus.BusTreeAssetImageAdd(Sku3dDbName, targetColl, group)
+	}
+
+	return nil, fmt.Errorf("不支持的类型")
+}
+
+func usersubmit2company(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := &SubmitBody{}
+	err := c.ShouldBindJSON(body)
+	if err != nil {
+		return nil, err
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	//from collection 2
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, "user", body.AssetType)
+
+	//target collection
+	targetColl := getAssetTypeCollectin(isCompany, isSku3d, "company", body.AssetType)
+
+	//userInfo
+
+	user, err := bus.GetUserInfo(apictx.User.ID)
+	if err != nil {
+		return nil, err
+	}
+	userInfo := &comm.AssetUserInfo{Name: user.Name, Thumbnail: &comm.OssType{Url: user.Avatar}}
+
+	if isAssetPack(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetPackDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent) //所在的企业ID
+		pack.OwnerType = "company"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+
+		return bus.BusTreeAssetPackAdd(Sku3dDbName, targetColl, pack)
+	} else if isFabric3d(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetMatgroupDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent) //所在的企业ID
+		pack.OwnerType = "company"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+
+		return bus.BusTreeAssetMatgroupAdd(Sku3dDbName, targetColl, pack)
+	} else if isAssetImage(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetImageDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent) //所在的企业ID
+		pack.OwnerType = "company"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+
+		return bus.BusTreeAssetImageAdd(Sku3dDbName, targetColl, pack)
+	}
+
+	return nil, fmt.Errorf("不支持的类型")
+}
+
+func teamsubmit2company(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := &SubmitBody{}
+	err := c.ShouldBindJSON(body)
+	if err != nil {
+		return nil, err
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	//from collection 2
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, "team", body.AssetType)
+
+	//target collection
+	targetColl := getAssetTypeCollectin(isCompany, isSku3d, "company", body.AssetType)
+
+	//userInfo
+
+	user, err := bus.GetUserInfo(apictx.User.ID)
+	if err != nil {
+		return nil, err
+	}
+	userInfo := &comm.AssetUserInfo{Name: user.Name, Thumbnail: &comm.OssType{Url: user.Avatar}}
+
+	if isAssetPack(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetPackDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent) //所在的企业ID
+		pack.OwnerType = "company"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+
+		return bus.BusTreeAssetPackAdd(Sku3dDbName, targetColl, pack)
+	} else if isFabric3d(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetMatgroupDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent) //所在的企业ID
+		pack.OwnerType = "company"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+
+		return bus.BusTreeAssetMatgroupAdd(Sku3dDbName, targetColl, pack)
+	} else if isAssetImage(body.AssetType) {
+
+		pack, err := bus.BusTreeAssetImageDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId, _ = primitive.ObjectIDFromHex(apictx.User.Parent) //所在的企业ID
+		pack.OwnerType = "company"
+		pack.UserInfo = userInfo
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+
+		return bus.BusTreeAssetImageAdd(Sku3dDbName, targetColl, pack)
+	}
+
+	return nil, nil
+}
+
+func isAssetPack(name string) bool {
+	return name == "shoes" || name == "last" || name == "heel" || name == "sole" || name == "shoe" || name == "decorate" || name == "shoe2d"
+}
+
+func isShoePack(name string) bool {
+	return name == "shoes" || name == "shoe"
+}
+
+func isFabric3d(name string) bool {
+	return name == "fabric3d" || name == "fabric2d" || name == "fabric" || name == "imagemat"
+}
+
+func isOtherMat(name string) bool {
+	return name == "specialmat" || name == "linemat"
+}
+
+func isEnv3d(name string) bool {
+	return name == "env"
+}
+
+func isAssetImage(name string) bool {
+	return name == "sticker"
+}
+
+func clone2self(c *gin.Context, apictx *ApiSession, fromScope string) (interface{}, error) {
+
+	body := &SubmitBody{}
+	err := c.ShouldBindJSON(body)
+	if err != nil {
+		return nil, err
+	}
+
+	isCompany := model.USERTYPE_COMPANY == apictx.User.UserType
+	isSku3d := apictx.User.Parent == model.SKU3D_USERID
+	//from collection 2
+	srcColl := getAssetTypeCollectin(isCompany, isSku3d, fromScope, body.AssetType)
+
+	//target collection
+	targetColl := getAssetTypeCollectin(isCompany, isSku3d, "user", body.AssetType)
+
+	//userInfo
+
+	user, err := bus.GetUserInfo(apictx.User.ID)
+	if err != nil {
+		return nil, err
+	}
+	userInfo := &comm.AssetUserInfo{Name: user.Name, Thumbnail: &comm.OssType{Url: user.Avatar}}
+
+	if isAssetPack(body.AssetType) {
+		pack, err := bus.BusTreeAssetPackDetail(Sku3dDbName, srcColl, body.Id)
+		if err != nil {
+			return nil, err
+		}
+
+		pack.OwnerId = user.Id
+		pack.OwnerType = "user"
+		pack.UserInfo = userInfo
+		pack.UserId = user.Id
+		pack.Id = primitive.NilObjectID
+		falseVale := false
+		pack.Enable = &falseVale
+		return bus.BusTreeAssetPackAdd(Sku3dDbName, targetColl, pack)
+	}
+	return nil, fmt.Errorf("不支持的类型")
+}
+
+func clonecompany2self(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return clone2self(c, apictx, "company")
+}
+
+func cloneteam2self(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	return clone2self(c, apictx, "team")
+}

+ 36 - 0
sku3d/sku3d/api/service-background.go

@@ -0,0 +1,36 @@
+package api
+
+import (
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+func CreateBackgroundRouter(router *GinRouter) {
+
+	//创建贴图操作
+	CreateCRUD(router, "/background", &CRUDOption{
+		Collection: repo.CollectionBackground,
+		NewModel: func(c *gin.Context) interface{} {
+
+			body := &struct {
+				Type int32
+			}{}
+			c.ShouldBindJSON(&body)
+
+			return &model.CanvasBackground{
+				Type:       body.Type,
+				Order:      0,
+				Color:      &model.Vect3{0.749, 0.749, 0.749},
+				Image:      &model.OssType{Url: "", Size: 0},
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.CanvasBackground{}
+		},
+		SearchProject: []string{"image", "createTime", "type", "order", "color"},
+	})
+}

+ 179 - 0
sku3d/sku3d/api/service-cat.go

@@ -0,0 +1,179 @@
+package api
+
+import (
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+)
+
+func CreateCatRouter(router *GinRouter) {
+	//分类操作
+	CreateCRUD(router, "/category", &CRUDOption{
+		Collection: repo.CollectionCategory,
+		NewModel: func(c *gin.Context) interface{} {
+			cat := &model.Category{
+				CreateTime: time.Now(),
+			}
+			c.ShouldBindJSON(cat)
+			return cat
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.Category{}
+		},
+		SearchProject: []string{"name", "type", "children", "createTime"},
+	})
+
+	router.GET("/qiye/res/category/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		_, _, query := UtilQueryPageSize(c)
+
+		body := &struct {
+			Id string
+		}{Id: query["id"].(string)}
+		if len(body.Id) < 1 {
+			return nil, NewError("id不能为空")
+		}
+
+		options := &repo.DocsSearchOptions{CollectName: repo.CollectionLibCategory, Query: repo.Map{"userId": body.Id}}
+		options.Project = []string{"name", "type", "parent", "createTime", "type", "value", "level", "orderId"}
+		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), options)
+		return map[string]interface{}{"list": list, "total": len(list), "page": 1, "size": len(list)}, nil
+	})
+
+	router.GET("/qiye/lib/category/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		_, _, query := UtilQueryPageSize(c)
+
+		body := &struct {
+			Id string
+		}{Id: query["id"].(string)}
+
+		if len(body.Id) < 1 {
+			return nil, NewError("id不能为空")
+		}
+		options := &repo.DocsSearchOptions{CollectName: repo.CollectionLib2Category, Query: repo.Map{"userId": body.Id}}
+		options.Project = []string{"categoryIds", "type"}
+		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), options)
+
+		return map[string]interface{}{"list": list, "total": len(list), "page": 1, "size": len(list)}, nil
+	})
+
+	router.GET("/platform/res/category/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		options := &repo.DocsSearchOptions{CollectName: repo.CollectionLibCategory, Query: repo.Map{"userId": PLATFORM_USER_ID}}
+		options.Project = []string{"name", "type", "parent", "createTime", "type", "value", "level", "orderId"}
+		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), options)
+		return map[string]interface{}{"list": list, "total": len(list), "page": 1, "size": len(list)}, nil
+	})
+
+	router.GET("/dadong/res/category/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		options := &repo.DocsSearchOptions{CollectName: repo.CollectionLibCategory, Query: repo.Map{"userId": DADONG_USER_ID}}
+		options.Project = []string{"name", "type", "parent", "createTime", "type", "value", "level", "orderId"}
+		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), options)
+		return map[string]interface{}{"list": list, "total": len(list), "page": 1, "size": len(list)}, nil
+	})
+
+	router.GET("/platform/lib/category/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		options := &repo.DocsSearchOptions{CollectName: repo.CollectionLib2Category, Query: repo.Map{"userId": PLATFORM_USER_ID}}
+		options.Project = []string{"categoryIds", "type"}
+		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), options)
+		return map[string]interface{}{"list": list, "total": len(list), "page": 1, "size": len(list)}, nil
+	})
+
+	router.GET("/dadong/lib/category/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		options := &repo.DocsSearchOptions{CollectName: repo.CollectionLib2Category, Query: repo.Map{"userId": DADONG_USER_ID}}
+		options.Project = []string{"categoryIds", "type"}
+		_, list := repo.RepoSeachDocsMap(apictx.CreateRepoCtx(), options)
+		return map[string]interface{}{"list": list, "total": len(list), "page": 1, "size": len(list)}, nil
+	})
+
+	CreateCRUD(router, "/res/category", &CRUDOption{
+		Collection: repo.CollectionLibCategory,
+		NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			cat := &model.ResCategory{}
+			c.ShouldBindJSON(cat)
+			cat.UserId = apictx.User.Parent
+			cat.CreateTime = time.Now()
+			return cat, nil
+		},
+
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.ResCategory{}
+		},
+		JWT: true,
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"userId": apictx.User.Parent}
+		},
+
+		SearchProject: []string{"name", "type", "parent", "createTime", "type", "value", "level", "orderId"},
+
+		Remove: func(c *gin.Context, apictx *ApiSession, id string) (interface{}, error) {
+			team := &model.ResCategory{}
+
+			ok, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{CollectName: repo.CollectionLibCategory, Query: repo.Map{"_id": id}, Project: []string{"orderId", "type"}}, team)
+
+			if !ok {
+				return nil, NewError("删除失败!")
+			}
+
+			minOrderId := team.OrderId //后面有几个零
+			if minOrderId > 0 {
+				var maxStep int64 = 1
+				currValue := minOrderId
+				for {
+					if currValue%10 == 0 {
+						currValue = currValue / 10
+						maxStep = maxStep * 10
+					} else {
+						break
+					}
+				}
+				maxOrderId := minOrderId + maxStep - 1
+				return repo.RepoDeleteDocs(apictx.CreateRepoCtx(), repo.CollectionLibCategory, &bson.M{"orderId": bson.M{"$gte": minOrderId, "$lt": maxOrderId}, "type": team.Type})
+			}
+
+			return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionLibCategory, id)
+		},
+	})
+
+	CreateCRUD(router, "/lib/category", &CRUDOption{
+		Collection: repo.CollectionLib2Category,
+		NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			cat := &model.LibCategory{}
+			c.ShouldBindJSON(cat)
+			cat.UserId = apictx.User.Parent
+			cat.CreateTime = time.Now()
+			return cat, nil
+		},
+
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.LibCategory{}
+		},
+		JWT: true,
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"userId": apictx.User.Parent}
+		},
+		SearchProject: []string{"type", "categoryIds", "createTime"},
+	})
+
+	//team
+
+	// CreateCRUD(router, "/team", &CRUDOption{
+	// 	Collection: repo.CollectionLibCategory,
+	// 	NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	// 		cat := &model.Team{}
+	// 		c.ShouldBindJSON(cat)
+	// 		cat.UserId = apictx.User.Parent
+	// 		cat.CreateTime = time.Now()
+	// 		return cat, nil
+	// 	},
+
+	// 	EmtyModel: func(*gin.Context) interface{} {
+	// 		return &model.Team{}
+	// 	},
+	// 	SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+	// 		return
+	// 	},
+	// 	SearchProject: []string{"name", "userCount", "filters", "createTime"},
+	// })
+}

+ 49 - 0
sku3d/sku3d/api/service-collocate.go

@@ -0,0 +1,49 @@
+package api
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+)
+
+//智能搭配接口
+func RegCollocateRouter(router *GinRouter) {
+
+	//获取项目搭配配置
+	router.GETJWT("/collocate", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		designId := c.Query("designId")
+		scope := c.Query("scope")
+
+		designIdObj, err := primitive.ObjectIDFromHex(designId)
+		if err != nil {
+			return nil, NewError("designId不能为空")
+		}
+		options := &repo.DocSearchOptions{Query: repo.Map{"designId": designIdObj, "scope": scope}, CollectName: repo.CollectionCollocates}
+
+		collecate := &model.Collocate{}
+		_, err = repo.RepoSeachDoc(apictx.CreateRepoCtx(), options, collecate)
+		if collecate.Id == nil {
+			return false, err
+		}
+
+		return collecate, err
+	})
+
+	//更新智能搭配配置
+	router.POSTJWT("/collocate", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		collocate := &model.Collocate{}
+		err := c.ShouldBindJSON(collocate)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(collocate.Comps) == 0 {
+			return nil, NewError("请添加组件")
+		}
+
+		return repo.RepoUpsertSetDoc(apictx.CreateRepoCtx(), repo.CollectionCollocates, repo.Map{"designId": collocate.DesignId, "scope": collocate.Scope}, collocate)
+	})
+
+}

+ 330 - 0
sku3d/sku3d/api/service-conv.go

@@ -0,0 +1,330 @@
+package api
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"os/exec"
+	"path"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"strings"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+func CmdExe(commandName string, params []string) error {
+
+	cmd := exec.Command(commandName, params...)
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		fmt.Println("cmd.StdoutPipe: ", err)
+		return err
+	}
+
+	var errOut bytes.Buffer
+	cmd.Stderr = &errOut
+
+	//创建一个流来读取管道内内容,这里逻辑是通过一行一行的读取的
+	reader := bufio.NewReader(stdout)
+	//实时循环读取输出流中的一行内容
+	go func() {
+		for {
+			line, err2 := reader.ReadString('\n')
+			if err2 != nil || io.EOF == err2 {
+				break
+			}
+			fmt.Println("cmd=》", line)
+		}
+	}()
+
+	err = cmd.Start()
+	if err != nil {
+		return err
+	}
+
+	return cmd.Wait()
+}
+
+func PathExists(path string) (bool, error) {
+
+	_, err := os.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+//ServiceTestHttp JWT授权的http处理函数
+func ServiceConvModel(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := struct {
+		File   *model.OssType
+		DocId  string
+		MeshId string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(body.DocId) < 1 {
+		return nil, NewError("模型Id不能为空")
+	}
+
+	if body.File == nil || len(body.File.Url) < 1 {
+		return nil, NewError("模型链接不能为空")
+	}
+
+	sDir, modelName := path.Split(body.File.Url)
+
+	wkspace := fmt.Sprintf("%d", time.Now().UnixNano())
+	dir := fmt.Sprintf("uploads/%s", wkspace)
+	err = os.MkdirAll(dir, 0777)
+
+	if err != nil {
+		return nil, NewError("创建目录失败!")
+	}
+
+	//1下载文件到本地
+	modelFile := fmt.Sprintf("%s/%s", dir, modelName)
+	out, err := os.Create(modelFile)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+
+	meshUrl := body.File.Url
+	if !strings.HasPrefix(meshUrl, "http") {
+		meshUrl = fmt.Sprintf("https:%s", body.File.Url)
+	}
+	resp, err := http.Get(meshUrl)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	pix, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	_, err = io.Copy(out, bytes.NewReader(pix))
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	out.Close()
+
+	// //调用转换器进行转化
+	// //2 文件转.osg.gles
+
+	relativePath := fmt.Sprintf("%s/%s", wkspace, modelName)
+
+	err = CmdExe("docker", []string{"exec", "osg", "osgconv", "--use-world-frame", "-O", "useExternalBinaryArray mergeAllBinaryFiles", "-o", "0,1,0-0,0,1", fmt.Sprintf(`/data/%s`, relativePath), fmt.Sprintf(`/data/%s.osg.gles`, relativePath)})
+	if err != nil { //获取输出对象,可以从该对象中读取输出结果
+		os.RemoveAll(dir)
+		return nil, err
+	}
+
+	//3 osg.gles 转 .osgjs
+
+	err = CmdExe("docker", []string{"exec", "osg", "osgconv", "-O", "useExternalBinaryArray mergeAllBinaryFiles disableStrictJson", fmt.Sprintf(`/data/%s.osg.gles`, relativePath), fmt.Sprintf(`/data/%s.osgjs`, relativePath)})
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+
+	//resultFile = { name: modelName, main: path.join(dir, `${modelName}.osgjs`), assets: [path.join(dir, `${modelName}.bin`)] }
+
+	osgjs := fmt.Sprintf(`%s/%s.osgjs`, dir, modelName)
+	osgjsbin := fmt.Sprintf(`%s/%s.bin`, dir, modelName)
+
+	fmt.Sprintln(osgjs, osgjsbin)
+
+	ext, _ := PathExists(osgjs)
+	if !ext {
+		os.RemoveAll(dir)
+		return nil, NewError("文件转化失败!")
+	}
+
+	ext, _ = PathExists(osgjsbin)
+	if !ext {
+		os.RemoveAll(dir)
+		return nil, NewError("文件转化失败!")
+	}
+
+	//上传文件到oss的同级目录 ${dir}/result/*.gz
+	resultJs, resultBin, err := UploadObsConvAssets(apictx, sDir, osgjs, osgjsbin)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	mesh := &model.Mesh{File: body.File, Osgjsbin: resultBin, Osgjs: resultJs}
+	saved, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionMesh, body.MeshId, mesh)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+
+	outresult := map[string]interface{}{
+		"osgjs": resultJs,
+		"saved": saved,
+	}
+	os.RemoveAll(dir)
+
+	return outresult, nil
+}
+
+func UploadObsConvAssets(apictx *ApiSession, filePath string, osgjsFilePath string, osgbinFilePath string) (*model.OssType, *model.OssType, error) {
+	seg := strings.Split(filePath, "myhuaweicloud.com/")
+	ossPath := seg[len(seg)-1]
+
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, nil, NewError("创建ObsClient 失败!")
+	}
+
+	defer func() {
+		client.Close()
+	}()
+
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+	resultJs := UploadFile(client, bucketName, osgjsFilePath, ossPath)
+	if resultJs.Size < 1 {
+		return nil, nil, NewError("上传osg js失败!")
+	}
+	resultBin := UploadFile(client, bucketName, osgbinFilePath, ossPath)
+	if resultBin.Size < 1 {
+		return nil, nil, NewError("上传osg bin失败!")
+	}
+	return resultJs, resultBin, nil
+}
+
+func UploadMinioConvAssets(apictx *ApiSession, filePath string, osgjsFilePath string, osgbinFilePath string) (*model.OssType, *model.OssType, error) {
+
+	bucketName := apictx.Svc.Conf.Minio.Bucket
+
+	sepStr := fmt.Sprintf("com/minio/%s/", bucketName)
+
+	seg := strings.Split(filePath, sepStr)
+	ossPath := seg[len(seg)-1]
+
+	client := createMinioClient(apictx.Svc.Conf)
+
+	resultJs, jsSize := MinioUploadLocalFile(client, bucketName, osgjsFilePath, ossPath)
+	if len(resultJs) < 1 {
+		return nil, nil, NewError("上传osg js失败!")
+	}
+
+	resultBin, binSize := MinioUploadLocalFile(client, bucketName, osgbinFilePath, ossPath)
+	if len(resultBin) < 1 {
+		return nil, nil, NewError("上传osg bin失败!")
+	}
+
+	return &model.OssType{
+			Url:  fmt.Sprintf("%s/%s/%s", apictx.Svc.Conf.Minio.PubHost, bucketName, resultJs),
+			Size: jsSize,
+		},
+		&model.OssType{
+			Url:  fmt.Sprintf("%s/%s/%s", apictx.Svc.Conf.Minio.PubHost, bucketName, resultBin),
+			Size: binSize,
+		}, nil
+}
+
+func ConvMeshFile(file *model.OssType, apictx *ApiSession) (*model.OssType, *model.OssType, error) {
+
+	sDir, modelName := path.Split(file.Url)
+
+	wkspace := fmt.Sprintf("%d", time.Now().UnixNano())
+	dir := fmt.Sprintf("uploads/%s", wkspace)
+	err := os.MkdirAll(dir, 0777)
+
+	if err != nil {
+		return nil, nil, NewError("创建目录失败!")
+	}
+
+	//1下载文件到本地
+	modelFile := fmt.Sprintf("%s/%s", dir, modelName)
+	out, err := os.Create(modelFile)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+
+	meshUrl := file.Url
+	if !strings.HasPrefix(meshUrl, "http") {
+		meshUrl = fmt.Sprintf("https:%s", file.Url)
+	}
+	resp, err := http.Get(meshUrl)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+	defer resp.Body.Close()
+
+	pix, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+	_, err = io.Copy(out, bytes.NewReader(pix))
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+	out.Close()
+
+	// //调用转换器进行转化
+	// //2 文件转.osg.gles
+
+	relativePath := fmt.Sprintf("%s/%s", wkspace, modelName)
+
+	err = CmdExe("docker", []string{"exec", "osg", "osgconv", "--use-world-frame", "-O", "useExternalBinaryArray mergeAllBinaryFiles", "-o", "0,1,0-0,0,1", fmt.Sprintf(`/data/%s`, relativePath), fmt.Sprintf(`/data/%s.osg.gles`, relativePath)})
+	if err != nil { //获取输出对象,可以从该对象中读取输出结果
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+
+	//3 osg.gles 转 .osgjs
+
+	err = CmdExe("docker", []string{"exec", "osg", "osgconv", "-O", "useExternalBinaryArray mergeAllBinaryFiles disableStrictJson", fmt.Sprintf(`/data/%s.osg.gles`, relativePath), fmt.Sprintf(`/data/%s.osgjs`, relativePath)})
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+	osgjs := fmt.Sprintf(`%s/%s.osgjs`, dir, modelName)
+	osgjsbin := fmt.Sprintf(`%s/%s.bin`, dir, modelName)
+
+	fmt.Sprintln(osgjs, osgjsbin)
+
+	ext, _ := PathExists(osgjs)
+	if !ext {
+		os.RemoveAll(dir)
+		return nil, nil, NewError("文件转化失败!")
+	}
+
+	ext, _ = PathExists(osgjsbin)
+	if !ext {
+		os.RemoveAll(dir)
+		return nil, nil, NewError("文件转化失败!")
+	}
+
+	//上传文件到oss的同级目录 ${dir}/result/*.gz
+	resultJs, resultBin, err := UploadObsConvAssets(apictx, sDir, osgjs, osgjsbin)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, nil, err
+	}
+
+	os.RemoveAll(dir)
+	return resultJs, resultBin, nil
+}

+ 667 - 0
sku3d/sku3d/api/service-design.go

@@ -0,0 +1,667 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"infish.cn/comm"
+)
+
+func createDefaultDesign(apictx *ApiSession, shoeId string) (*model.Design, error) {
+
+	shoeid, _ := primitive.ObjectIDFromHex(shoeId)
+
+	option := repo.DocSearchOptions{
+		CollectName: repo.CollectionShoes,
+		Query:       repo.Map{"_id": shoeid},
+	}
+	shoe := &model.ShoeMesh{}
+	ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &option, shoe)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, NewError("款式模型已被删除!")
+	}
+	uid := apictx.User.ID
+
+	//查询用户默认配置
+	u := &model.User{}
+	_, err = repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		CollectName: repo.CollectionUser,
+		Query:       repo.Map{"_id": uid}, //"editorSet.themes": bson.M{"$elemMatch": bson.M{"id": theme.Id}}}
+		Project:     []string{"editorSet"},
+	}, u)
+	if err != nil {
+		return nil, err
+	}
+
+	//获取默认环境
+	optionEnv := repo.DocSearchOptions{
+		CollectName: repo.CollectionEvn3d,
+		// Query:       repo.Map{"name": "工作室"},
+	}
+	useDefaultEnvId := false
+	if u.EditorSet != nil && len(u.EditorSet.DefaultEnv3dId) > 0 {
+		optionEnv.Query = repo.Map{"_id": u.EditorSet.DefaultEnv3dId}
+		useDefaultEnvId = true
+	}
+
+	env3d := &model.Env3d{}
+	ok, err = repo.RepoSeachDoc(apictx.CreateRepoCtx(), &optionEnv, env3d)
+	if err != nil {
+		return nil, err
+	}
+	if !ok && useDefaultEnvId {
+		_, err = repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{CollectName: repo.CollectionEvn3d}, env3d)
+		if err != nil {
+			return nil, err
+		}
+	}
+	objUid, _ := primitive.ObjectIDFromHex(uid)
+
+	//获取用户的自定义配置
+	//查询对应的环境是否已经存在
+	var currTheme *model.EnvTheme
+	if u.EditorSet != nil && u.EditorSet.Themes != nil {
+		for _, v := range u.EditorSet.Themes {
+			if v.Id == env3d.Id.Hex() {
+				currTheme = v
+				break
+			}
+		}
+	}
+
+	//queentree 的默认环境球
+	env3d.Id, _ = primitive.ObjectIDFromHex("628c3433633d10f8e365855f")
+	scene := &model.DesignScene{
+		Id:        uint64(time.Now().Unix()),
+		Name:      shoe.Name,
+		Thumbnail: shoe.Thumbnail,
+		Images:    []*model.OssType{},
+		Videos:    []*model.OssType{},
+		Locks:     []string{},
+		Background: &model.SceneBackground{
+			Type:  env3d.Background.Type,
+			Color: env3d.Background.Color,
+			Image: env3d.Background.Image,
+		},
+		Env: &model.DesingSceneEnv3d{
+			Id:        env3d.Id,
+			Name:      env3d.Name,
+			Thumbnail: env3d.Thumbnail,
+			Config:    env3d.Config,
+			HDR:       env3d.HDR,
+
+			Options: &model.Evn3dOption{
+				Rotation: env3d.Options.Rotation,
+				Exposure: env3d.Options.Exposure,
+			},
+			ToneMap: &model.ToneMap{
+				Method:     env3d.ToneMap.Method,
+				Exposure:   env3d.ToneMap.Exposure,
+				Saturation: env3d.ToneMap.Saturation,
+				Contrast:   env3d.ToneMap.Contrast,
+				Brightness: env3d.ToneMap.Brightness,
+			},
+		},
+		Components: []*model.SceneComponent{},
+	}
+
+	if currTheme != nil {
+		scene.Env.Options = currTheme.Options
+		scene.Env.ToneMap = currTheme.ToneMap
+		scene.Background = currTheme.Background
+	}
+
+	design := &model.Design{
+		UserId:     objUid,
+		ShoeId:     shoeid,
+		Name:       shoe.Name,
+		Sharepwd:   "",
+		Thumbnail:  shoe.Thumbnail,
+		CreateTime: time.Now(),
+		Sex:        shoe.Sex,
+		Season:     shoe.Season,
+		Type:       shoe.Type,
+		IsPublic:   BoolValue(false),
+		Scenes:     []*model.DesignScene{scene},
+	}
+
+	return design, nil
+}
+
+func ServiceDesignCreate(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	body := struct {
+		ShoesId string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+	if len(body.ShoesId) < 1 {
+		return nil, NewError("款式Id不能为空!")
+	}
+
+	design, err := createDefaultDesign(apictx, body.ShoesId)
+	if err != nil {
+		return nil, err
+	}
+	did, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, design)
+	if err != nil {
+		return nil, err
+	}
+	return map[string]string{"id": did}, nil
+}
+
+func ServiceDesignList(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+
+	uid, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+	query["userId"] = uid
+
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionDesigns,
+		Page:        page,
+		Size:        size,
+		Query:       query,
+		Project:     []string{"name", "thumbnail", "createTime", "isPublic"},
+		Sort:        bson.M{"createTime": -1},
+	})
+}
+
+func ServiceDesignPublicList(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+	query["isPublic"] = true
+
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionDesigns,
+		Page:        page,
+		Size:        size,
+		Query:       query,
+		Project:     []string{"name", "thumbnail", "createTime", "isPublic"},
+		Sort:        bson.M{"createTime": -1},
+	})
+}
+
+func ServiceDesignPublic(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	body := &struct {
+		IsPublic bool
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+	return repo.RepoUpdateSetDocProps(apictx.CreateRepoCtx(), repo.CollectionDesigns, id, bson.M{"$set": bson.M{"isPublic": body.IsPublic}})
+}
+
+func ServiceDesignCopy(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	scope := c.Query("scope")
+	if len(scope) < 1 {
+		scope = "company"
+	}
+
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+	did, _ := primitive.ObjectIDFromHex(id)
+	uid, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+
+	srcColl := UtilGetDesignCollection(scope)
+
+	option := repo.DocSearchOptions{
+		CollectName: srcColl,
+		Query:       repo.Map{"_id": did},
+	}
+
+	design := &model.Design{}
+	ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &option, design)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, NewError("项目Id无效!")
+	}
+
+	// if apictx.User.ID == design.UserId.Hex() { //当前项目就是用户本身的项目,不进行拷贝
+	// 	return id, nil
+	// }
+
+	//判断当前项目是否已经被拷贝了
+	designSrc := &model.Design{}
+	ok, _ = repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		CollectName: repo.CollectionDesigns,
+		Query:       repo.Map{"fromId": id, "userId": uid},
+		Project:     []string{"_id"},
+	}, designSrc)
+
+	if ok {
+		return designSrc.Id, nil
+	}
+
+	design.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+	design.Id = primitive.NilObjectID
+	design.IsPublic = BoolValue(false)
+	design.CreateTime = time.Now()
+	design.FromId = id
+	design.State = Int32Value(model.LibState_Created)
+
+	return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, design)
+}
+
+func ServiceDesignDelete(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, id)
+}
+
+func ServiceDesignDetail(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+	scope := c.Query("scope")
+	collection := repo.CollectionDesigns
+	if scope == "company" || scope == "team" {
+		collection = fmt.Sprintf("%s_%s", "design", scope)
+	}
+	did, _ := primitive.ObjectIDFromHex(id)
+
+	option := repo.DocSearchOptions{
+		CollectName: collection,
+		Query:       repo.Map{"_id": did},
+		Project:     []string{"name", "thumbnail", "editor", "scenes", "shoeId", "createTime", "state", "sharepwd", "isPublic", "sex", "type", "season", "categories"},
+	}
+	design := &model.Design{}
+	ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &option, design)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, NewError("项目Id无效!")
+	}
+
+	option = repo.DocSearchOptions{
+		CollectName: repo.CollectionShoes,
+		Query:       repo.Map{"_id": design.ShoeId},
+		Project:     []string{"name", "materials", "leftComponents", "meshId"},
+	}
+	shoe := &model.ShoeMesh{}
+	ok, err = repo.RepoSeachDoc(apictx.CreateRepoCtx(), &option, shoe)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, NewError("shoeId无效!")
+	}
+
+	//查询osgjs
+	option = repo.DocSearchOptions{
+		CollectName: repo.CollectionMesh,
+		Query:       repo.Map{"_id": shoe.MeshId},
+		Project:     []string{"osgjs", "materials", "file", "shadow"},
+	}
+	mesh := &model.Mesh{}
+	ok, err = repo.RepoSeachDoc(apictx.CreateRepoCtx(), &option, mesh)
+	if err != nil {
+		return nil, err
+	}
+	if !ok {
+		return nil, NewError("meshId无效!")
+	}
+
+	//获取所有面料
+	fabric := []*model.Fabric{}
+	fabricMap := map[string]*model.Fabric{}
+
+	//获取所有色卡
+	material := []*comm.SpecialMat{}
+	materialMap := map[string]*comm.SpecialMat{}
+
+	//env3d
+	env3d := []map[string]interface{}{}
+	// env3dMap := map[string]bool{}
+
+	//xxx
+	for _, item := range design.Scenes {
+
+		//色卡和面料
+		for _, comp := range item.Components {
+			if comp.MatType == model.MatType_Fabric {
+				fid := comp.FabricId.Hex()
+				f := fabricMap[fid]
+				if f == nil {
+					fb := repo.FabricGetById(apictx.CreateRepoCtx(), comp.FabricScope, fid, []string{"name", "colorCards"})
+					if fb != nil {
+						fabricMap[fid] = fb
+						fabric = append(fabric, fb)
+					}
+					f = fb
+				}
+
+				if comp.Card == nil && f != nil {
+					currCardId := comp.CardId.Hex()
+					for _, item := range f.ColorCards {
+						if item.MatId.Hex() == currCardId {
+							comp.Card = item
+							break
+						}
+					}
+				}
+			}
+
+			if comp.MatType == model.MatType_Effect {
+				fid := comp.CardId.Hex()
+				f := materialMap[fid]
+				if f == nil {
+					mat := repo.SpecialMatGetById(apictx.CreateRepoCtx(), fid)
+					if mat != nil {
+						materialMap[fid] = mat
+						material = append(material, mat)
+					}
+					f = mat
+				}
+				if f != nil && comp.Card == nil {
+					comp.Card = f.Copy2MaterialConf()
+				}
+			}
+		}
+	}
+
+	return map[string]interface{}{
+		"env3d":    env3d,
+		"fabric":   fabric,
+		"material": material,
+		"design":   design,
+		"shoe":     shoe,
+		"mesh":     mesh,
+	}, nil
+}
+
+func ServiceDesignResetShoe(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := &struct {
+		Id     string
+		ShoeId string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+	if len(body.Id) < 1 || len(body.ShoeId) < 1 {
+		return nil, NewError("Id和ShoeId不能为空")
+	}
+
+	design, err := createDefaultDesign(apictx, body.ShoeId)
+	if err != nil {
+		return nil, err
+	}
+	design.State = Int32Value(model.LibState_Created)
+
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, body.Id, design)
+}
+
+func ServiceDesignResetDesign(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := &struct {
+		CurrId    string
+		CurrScope string
+		ToId      string
+		ToScope   string
+	}{}
+
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+	if len(body.CurrId) < 1 || len(body.ToId) < 1 || len(body.CurrScope) < 1 || len(body.ToScope) < 1 {
+		return nil, NewError("参数不合法!")
+	}
+	if body.CurrId == body.ToId {
+		return nil, NewError("两个款式相同不能重置!")
+	}
+
+	toColl := UtilGetDesignCollection(body.ToScope)
+
+	//查询目标设计
+	design := &model.Design{}
+	ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		CollectName: toColl,
+		Query:       repo.Map{"_id": body.ToId},
+	}, design)
+
+	if err != nil || !ok {
+		return nil, NewError("目标款式查询失败!")
+	}
+
+	// design.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+	design.UserId = primitive.NilObjectID
+	design.Id = primitive.NilObjectID
+	design.Sharepwd = ""
+	design.CreateTime = time.Now()
+	design.IsPublic = nil
+	design.Categories = []string{}
+	design.Name = ""
+	design.TeamId = ""    //团队Id 不重置
+	design.CompanyId = "" //企业Id 不重置
+
+	currColl := UtilGetDesignCollection(body.CurrScope)
+
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), currColl, body.CurrId, design)
+}
+
+func ServiceDesignUpdate(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("Id不能为空")
+	}
+	body := &model.Design{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+
+	body.Id = primitive.NilObjectID
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, id, body)
+}
+
+func ServiceDesignCreateScene(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+	did, _ := primitive.ObjectIDFromHex(id)
+
+	fmt.Println(did)
+
+	scene := model.DesignScene{}
+	err := c.ShouldBindJSON(&scene)
+	if err != nil {
+		return nil, err
+	}
+
+	scope := c.Param("scope")
+	coll := repo.CollectionDesigns
+	if scope == "team" || scope == "company" {
+		coll = fmt.Sprintf("%s_%s", "design", scope)
+	}
+
+	return repo.RepoDocArrayAppend(apictx.CreateRepoCtx(), coll, id, "scenes", scene)
+}
+
+func ServiceDesignUpdateScene(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+
+	scope := c.Param("scope")
+
+	scene := model.DesignScene{}
+	err := c.ShouldBindJSON(&scene)
+	if err != nil {
+		return nil, err
+	}
+	if scene.Id < 1 {
+		return nil, NewError("场景Id不能为空!")
+	}
+
+	optSet := repo.Map{}
+	if len(scene.Name) > 0 {
+		optSet["scenes.$.name"] = scene.Name
+	}
+	if scene.Thumbnail != nil {
+		optSet["scenes.$.thumbnail"] = scene.Thumbnail
+	}
+	if scene.Background != nil {
+		optSet["scenes.$.background"] = scene.Background
+	}
+
+	if len(scene.Locks) > 0 {
+		optSet["scenes.$.locks"] = scene.Locks
+	}
+	if scene.Env != nil {
+		optSet["scenes.$.env"] = scene.Env
+	}
+	if len(scene.Components) > 0 {
+		optSet["scenes.$.components"] = scene.Components
+	}
+	if len(scene.Models) > 0 {
+		optSet["scenes.$.models"] = scene.Models
+	}
+
+	if len(scene.Stickers) > 0 {
+		optSet["scenes.$.stickers"] = scene.Stickers
+	}
+
+	if scene.Lights != nil {
+		optSet["scenes.$.lights"] = scene.Lights
+	}
+
+	coll := repo.CollectionDesigns
+	if scope == "team" || scope == "company" {
+		coll = fmt.Sprintf("%s_%s", "design", scope)
+	}
+
+	option := &repo.ArrayOneUpdateOption{
+		CollectName: coll,
+		Id:          id,
+		Query:       repo.Map{"scenes.id": scene.Id},
+		Set:         optSet,
+	}
+
+	ret, err := repo.RepoDocArrayOneUpdate(apictx.CreateRepoCtx(), option)
+
+	return ret, err
+}
+
+func ServiceDesignRomoveScene(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+
+	scene := struct {
+		SceneId uint64
+	}{}
+
+	err := c.ShouldBindJSON(&scene)
+	if err != nil {
+		return nil, err
+	}
+
+	scope := c.Param("scope")
+	coll := repo.CollectionDesigns
+	if scope == "team" || scope == "company" {
+		coll = fmt.Sprintf("%s_%s", "design", scope)
+	}
+
+	option := &repo.ArrayOneRemoveOption{
+		CollectName: coll,
+		Id:          id,
+		ArrayQuery:  repo.Map{"scenes": bson.M{"id": scene.SceneId}},
+	}
+	ret, err := repo.RepoDocArrayOneRemove(apictx.CreateRepoCtx(), option)
+	if ret.ModifiedCount != 1 {
+		return nil, NewError("删除失败")
+	}
+	return ret, err
+}
+
+func ServiceDesignRename(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+	body := struct {
+		Name string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, id, bson.M{"name": body.Name})
+}
+
+func UtilGetDesignCollection(scope string) string {
+	if scope == "team" || scope == "company" {
+		return fmt.Sprintf("%s_%s", "design", scope)
+	}
+	return repo.CollectionDesigns
+}
+
+func UtilGetFabricCollection(scope string) string {
+	if scope == "team" || scope == "company" {
+		return fmt.Sprintf("%s_%s", "fabric", scope)
+	} else if scope == "user" {
+		return repo.CollectionFabric
+	}
+	return "fabric_company"
+}
+
+func UtilGetImgMatCollection(scope string) string {
+	if scope == "team" || scope == "company" {
+		return fmt.Sprintf("%s_%s", "imgmat", scope)
+	}
+	return repo.CollectionImageMat
+}
+
+func ServiceDesignSceneThumbnail(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("id不能为空!")
+	}
+	body := struct {
+		Scope     string
+		SceneId   uint64
+		Thumbnail *model.OssType
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+
+	//更新项目封面
+	//repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionDesigns, id, bson.M{"thumbnail": body.Thumbnail})
+
+	//更新场景封面
+	option := &repo.ArrayOneUpdateOption{
+		CollectName: UtilGetDesignCollection(body.Scope),
+		Id:          id,
+		Query:       repo.Map{"scenes.id": body.SceneId},
+		Set:         repo.Map{"scenes.$.thumbnail": body.Thumbnail},
+	}
+
+	ret, err := repo.RepoDocArrayOneUpdate(apictx.CreateRepoCtx(), option)
+	if ret.ModifiedCount != 1 {
+		return nil, NewError("场景封面更新失败!")
+	}
+	return ret, err
+}

+ 169 - 0
sku3d/sku3d/api/service-hdr.go

@@ -0,0 +1,169 @@
+package api
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"strings"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+//ServiceTestHttp JWT授权的http处理函数
+func ServiceConvHdr(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := struct {
+		Hdr   string
+		DocId string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(body.Hdr) < 1 {
+		return nil, NewError("模型链接不能为空")
+	}
+	if len(body.DocId) < 1 {
+		return nil, NewError("docId不能为空")
+	}
+
+	sDir, modelName := path.Split(body.Hdr)
+
+	seg := strings.Split(sDir, "myhuaweicloud.com/")
+	obsTargetPath := seg[len(seg)-1]
+
+	// sDir = "assets/env3d/db"
+	// obsTargetPath := fmt.Sprintf("%s/%s", sDir, body.DocId)
+
+	wkspace := fmt.Sprintf("%d", time.Now().UnixNano())
+	dir := fmt.Sprintf("uploads/%s", wkspace)
+	err = os.MkdirAll(dir, 0777)
+
+	if err != nil {
+		return nil, NewError("创建目录失败!")
+	}
+
+	//1下载文件到本地
+	localHdrpath := fmt.Sprintf("%s/%s", dir, modelName)
+	out, err := os.Create(localHdrpath)
+
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	hdrUrl := body.Hdr
+	if !strings.HasPrefix(hdrUrl, "http") {
+		hdrUrl = fmt.Sprintf("https:%s", body.Hdr)
+	}
+	resp, err := http.Get(hdrUrl)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	pix, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	_, err = io.Copy(out, bytes.NewReader(pix))
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+	out.Close()
+
+	// //调用转换器进行转化
+	// //2 文件转.osg.gles
+
+	relativePath := fmt.Sprintf("%s/%s", wkspace, modelName)
+	resultPath := fmt.Sprintf("/data/%s/result/", wkspace)
+
+	//let cmd =  `docker run -i -v $(pwd)/uploads:/data dongjianran/envtools process_environment.py --approximateDirectionalLights --write-by-channel --fixedge /data/${hdrName} /data/result/`
+	err = CmdExe("docker", []string{"exec", "hdr", "process_environment.py", "--approximateDirectionalLights", "--write-by-channel", "--fixedge", fmt.Sprintf(`/data/%s`, relativePath), resultPath})
+
+	if err != nil { //获取输出对象,可以从该对象中读取输出结果
+		os.RemoveAll(dir)
+		return nil, err
+	}
+
+	configPath := fmt.Sprintf(`%s/result/config.json`, dir)
+	ext, _ := PathExists(configPath)
+	if !ext {
+		os.RemoveAll(dir)
+		return nil, NewError("文件转化失败!")
+	}
+	//上传文件到oss的同级目录 ${dir}/result/*.gz
+
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, NewError("创建ObsClient 失败!")
+	}
+
+	defer func() {
+		client.Close()
+	}()
+
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+	imageMap := map[string]string{}
+
+	//上传产生的所有文件
+	files, _ := ioutil.ReadDir(fmt.Sprintf(`%s/result`, dir))
+	for _, f := range files {
+		if !f.IsDir() {
+			fmt.Println(f.Name())
+			fpath := fmt.Sprintf(`%s/result/%s`, dir, f.Name())
+			obs := UploadFile(client, bucketName, fpath, obsTargetPath)
+			if obs.Size > 0 {
+				imageMap[f.Name()] = obs.Url
+			}
+		}
+	}
+	bytes, _ := ioutil.ReadFile(configPath)
+
+	//上传hdr文件
+	// hdr := UploadFile(client, bucketName, localHdrpath, obsTargetPath)
+	env3dConf := &model.Evn3dHdrConf{}
+
+	err = json.Unmarshal(bytes, env3dConf)
+	if err != nil {
+		return nil, err
+	}
+
+	thumbnail := model.OssType{}
+
+	for _, v := range env3dConf.Textures {
+		for _, img := range v.Images {
+			img.File = imageMap[img.File]
+		}
+		if v.Type == "thumbnail" && len(thumbnail.Url) < 1 {
+			thumbnail.Url = v.Images[0].File
+			thumbnail.Size = int64(v.Images[0].SizeUncompressed)
+		}
+	}
+
+	env3d := &model.Env3d{
+		// HDR:       hdr,
+		Config:    env3dConf,
+		Thumbnail: &thumbnail,
+	}
+
+	ret, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionEvn3d, body.DocId, env3d)
+	if err != nil {
+		os.RemoveAll(dir)
+		return nil, err
+	}
+
+	os.RemoveAll(dir)
+
+	return ret, nil
+}

+ 95 - 0
sku3d/sku3d/api/service-imgmat.go

@@ -0,0 +1,95 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+func RegImageMat(router *GinRouter) {
+
+	pageSearchFn := func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		page, size, query := UtilQueryPageSize(c)
+
+		scope := c.Param("scope")
+		coll := UtilGetImgMatCollection(scope)
+
+		pageOption := &repo.PageSearchOptions{
+			CollectName: coll,
+			Page:        page,
+			Size:        size,
+			Query:       query,
+			Project:     []string{"name", "thumbnail", "cusNum", "createTime", "colorCards", "image", "categories"},
+			Sort:        bson.M{"createTime": -1},
+		}
+		pageResult, err := repo.RepoPageSearch(apictx.CreateRepoCtx(), pageOption)
+		if err != nil {
+			return nil, err
+		}
+		for _, v := range pageResult.List {
+			if v["userId"] != nil {
+				userId := v["userId"].(primitive.ObjectID).Hex()
+				u, _ := repo.RedisGetUserById(apictx.CreateRepoCtx(), userId)
+				if u != nil {
+					v["user"] = u
+				}
+			}
+		}
+		return pageResult, nil
+	}
+
+	router.GETJWT("/imgmat/list/:scope", pageSearchFn)
+
+	router.GET("/imgmat/detail/:id", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		id := c.Param("id")
+		if len(id) < 1 {
+			return nil, NewError("参数不能为空")
+		}
+		scope := c.Query("scope")
+		out := &model.ImageMat{}
+		uid, _ := primitive.ObjectIDFromHex(id)
+		Collection := UtilGetImgMatCollection(scope)
+		ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: Collection,
+			Query:       repo.Map{"_id": uid},
+			Project:     []string{"name", "image", "thumbnail", "category", "colorCards", "categories"},
+		}, out)
+
+		if err != nil {
+			return nil, err
+		}
+		if !ok {
+			return nil, NewError("没有查询到数据")
+		}
+		return out, nil
+	})
+
+	router.POSTJWT("/imgmat/update/:scope", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		fabric := &model.ImageMat{}
+		err := c.ShouldBindJSON(fabric)
+		if err != nil {
+			fmt.Println(err)
+			return nil, NewError("参数解析错误")
+		}
+
+		collection := UtilGetImgMatCollection(c.Param("scope"))
+		if fabric.Id == primitive.NilObjectID {
+			return nil, NewError("Id不能为空!")
+		}
+		id := fabric.Id.Hex()
+
+		out, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), collection, id, fabric)
+
+		if err != nil {
+			return nil, err
+		}
+		if out.MatchedCount != 1 {
+			return nil, NewError("文件不存在!")
+		}
+		return true, nil
+	})
+}

+ 79 - 0
sku3d/sku3d/api/service-lib.go

@@ -0,0 +1,79 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+)
+
+/**
+db.designs.find({"editor.users":{$not:{$elemMatch:{$nin: [1,2,3]}}}, 'editor.users.0': {$exists: true}}).explain()
+**/
+
+func ParseCategory(filter []string, src []*model.ResCategory) []string {
+	out := []string{}
+
+	if len(filter) < 1 {
+		for _, v := range src {
+			out = append(out, v.Id.Hex())
+		}
+		return out
+	}
+
+	// rootCate := []string{}
+	// for _, v := range src {
+	// 	if v.Level == 0 {
+	// 		rootCate = append(rootCate, v.Id.Hex())
+	// 	}
+	// }
+	return out
+}
+
+func CreateLibRouter(router *GinRouter) {
+
+	//获取款式列表
+	router.GETJWT("/lib/public/design", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		page, size, query := UtilQueryPageSize(c)
+		categories := query["categories"]
+		if categories == nil {
+			return nil, NewError("query.categories不能为空!")
+		}
+		strCategories := []string{}
+
+		cates, ok := categories.([]interface{})
+		if !ok {
+			return nil, NewError("query.categories必须是数组!")
+		}
+		for _, c := range cates {
+			strCategories = append(strCategories, c.(string))
+		}
+
+		designCates := []*model.ResCategory{}
+		err := repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{CollectName: repo.CollectionLibCategory, Query: repo.Map{"type": "design"}}, &designCates)
+		if err != nil {
+			return nil, err
+		}
+
+		//展开当前用户team的资源授权,如果没有则为所有
+		fmt.Sprintln(cates)
+
+		if len(strCategories) > 0 {
+			query["categories"] = bson.M{"$not": bson.M{"$elemMatch": bson.M{"$nin": strCategories}}}
+			query["categories.0"] = bson.M{"$exists": true}
+		}
+		//展开待查询的cates
+		//{$not:{$elemMatch:{$nin: [1,2,3]}}}, 'editor.users.0': {$exists: true}}
+
+		return repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+			CollectName: repo.CollectionDesigns,
+			Page:        page,
+			Size:        size,
+			Query:       query,
+			Project:     []string{"name", "thumbnail", "createTime", "isPublic"},
+		})
+	})
+
+}

+ 118 - 0
sku3d/sku3d/api/service-login.go

@@ -0,0 +1,118 @@
+package api
+
+import (
+	"sku3dweb/bus"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+func ServiceLoginSucc(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	//登录成功
+	setting := &model.UserSetting{}
+
+	ret, err := repo.RepoUpdateSetDocProps(apictx.CreateRepoCtx(), repo.CollectionUserSetting, apictx.User.ID, bson.M{"$set": bson.M{"lastLoginTime": time.Now()}})
+	if err != nil {
+		return nil, err
+	}
+
+	if ret.MatchedCount == 0 {
+		setting.Id, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+		setting.LoginName = apictx.User.Phone
+		setting.CreateTime = time.Now()
+		setting.LastLoginTime = time.Now()
+
+		info, err := bus.GetUserInfo(apictx.User.ID)
+		if err != nil {
+			return nil, err
+		}
+		setting.LoginName = info.Name
+		setting.UserType = *info.UserType
+		setting.Avator = info.Avatar
+
+		return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionUserSetting, setting)
+	}
+
+	return true, nil
+}
+
+func ServiceUserProfile(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	user := apictx.User
+
+	ok, u := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		CollectName: repo.CollectionAdmin,
+		Project:     []string{"name", "phone", "isRoot", "perms", "lastLogin"},
+		Query:       repo.Map{"_id": user.ID},
+	})
+	if !ok {
+		return nil, NewError("用户不存在")
+	}
+	out := map[string]interface{}{
+		"user": u,
+	}
+	return out, nil
+}
+
+func RegAdminRouter(router *GinRouter) {
+
+	router.GET("/admin/user/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		page, size, query := UtilQueryPageSize(c)
+
+		pageOption := &repo.PageSearchOptions{
+			CollectName: repo.CollectionUserSetting,
+			Page:        page,
+			Size:        size,
+			Query:       query,
+			Project:     []string{"loginName", "userType", "avator", "createTime", "updateTime", "enableMall"},
+		}
+
+		return repo.RepoPageSearch(apictx.CreateRepoCtx(), pageOption)
+	})
+
+	// CreateCRUD(router, "/admin/user", &CRUDOption{
+	// 	Collection: repo.CollectionAdmin,
+	// 	NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	// 		body := &struct {
+	// 			Phone    string
+	// 			Password string
+	// 			Perms    string
+	// 			IsRoot   bool
+	// 			Name     string
+	// 		}{}
+	// 		c.ShouldBindJSON(&body)
+
+	// 		ok, _ := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+	// 			CollectName: repo.CollectionAdmin,
+	// 			Query:       repo.Map{"phone": body.Phone},
+	// 			Project:     []string{"_id"},
+	// 		})
+	// 		if ok {
+	// 			return nil, nil
+	// 		}
+
+	// 		return &model.Admin{
+	// 			Name:       body.Name,
+	// 			IsRoot:     &body.IsRoot,
+	// 			Phone:      body.Phone,
+	// 			Password:   UtilMd5(body.Password),
+	// 			Perms:      body.Perms,
+	// 			CreateTime: time.Now(),
+	// 		}, nil
+	// 	},
+	// 	EmtyModel: func(*gin.Context) interface{} {
+	// 		return &model.Admin{}
+	// 	},
+	// 	Update: func(c *gin.Context, apictx *ApiSession, entity interface{}, id string) {
+	// 		m := entity.(*model.Admin)
+	// 		if len(m.Password) > 0 {
+	// 			m.Password = UtilMd5(m.Password)
+	// 		}
+	// 	},
+	// 	SearchProject: []string{"phone", "name", "lastLogin", "perms", "isRoot"},
+	// })
+}

+ 659 - 0
sku3d/sku3d/api/service-mat.go

@@ -0,0 +1,659 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"infish.cn/comm"
+)
+
+// func NewMaterial(header *model.MaterialHeader) *model.MatConfig {
+// 	uv := header.Uvtransform
+
+// 	out := &model.MatConfig{
+// 		CullFace:          "",
+// 		MetalnessWorkFlow: BoolValue(true),
+// 		Channels: &model.MaterialChannels{
+// 			Albedo: &model.MaterailFeature{
+// 				Enable:  BoolValue(true),
+// 				Texture: header.BaseMap,
+// 			},
+// 			NormalMap: &model.MaterailFeature{
+// 				Enable:  BoolValue(true),
+// 				Factor:  Float64Value(1),
+// 				Texture: header.NormalMap,
+// 			},
+// 			Roughness: &model.MaterailFeature{
+// 				Enable:  BoolValue(true),
+// 				Texture: header.RoughMap,
+// 				Factor:  header.RoughFactor,
+// 			},
+// 			MetalnessPBR: &model.MaterailFeature{
+// 				Enable:  BoolValue(true),
+// 				Texture: header.MetalMap,
+// 				Factor:  header.MetalFactor,
+// 			},
+// 			Emissive: &model.Emissive{
+// 				Color:  model.Vect3{0, 0, 0},
+// 				Factor: 1,
+// 				Enable: false,
+// 			},
+// 			BumpMap: &model.MaterailFeature{
+// 				Factor:  Float64Value(1.0),
+// 				Texture: &model.OssType{},
+// 			},
+// 			Opacity: &model.OpacityFeature{
+// 				Type:           "ALPHA_BLEND",
+// 				Factor:         1,
+// 				SampleChannel:  "a",
+// 				RefractionTint: model.Vect3{1, 1, 1},
+// 				IOR:            1,
+// 				Texture:        model.OssType{},
+// 			},
+// 			AO:         &model.AOFeature{},
+// 			SpecularF0: 0.5,
+// 			ClearCoat: &model.ClearCoatFeature{
+// 				Thickness: 5,
+// 				Intensity: 1,
+// 				Color:     model.Vect3{1, 1, 1},
+// 			},
+// 			Displacement: &model.MaterailFeature{
+// 				Texture: header.DisplaceMap,
+// 			},
+// 			CavityMap: &model.MaterailFeature{},
+// 		},
+// 	}
+// 	if uv == nil {
+// 		out.Uvtransform = &model.MaterailUv{
+// 			Scale:   1,
+// 			Rotate:  0,
+// 			OffsetX: 0,
+// 			OffsetY: 0,
+// 		}
+// 	} else {
+// 		out.Uvtransform = uv
+// 	}
+
+// 	if header.Type == "spec" {
+// 		out.MetalnessWorkFlow = BoolValue(false)
+// 	}
+
+// 	if header.MetalFactor == nil {
+// 		out.Channels.MetalnessPBR.Factor = Float64Value(0.0)
+// 	}
+
+// 	if header.RoughFactor == nil {
+// 		out.Channels.Roughness.Factor = Float64Value(0.5)
+// 	}
+// 	return out
+// }
+
+func RegMatCrud(router *GinRouter) {
+
+	//公共2d面料
+	// CreateCRUD(router, "/imgmat", &CRUDOption{
+	// 	Collection: repo.CollectionImageMat,
+	// 	NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	// 		body := &struct {
+	// 			Category   string
+	// 			Category2  string
+	// 			Categories []string
+	// 		}{}
+	// 		c.ShouldBindJSON(&body)
+	// 		nopublic := false
+	// 		platform := true
+
+	// 		uid, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+
+	// 		return &model.ImageMat{
+	// 			UserId:     uid,
+	// 			Category:   body.Category,
+	// 			Category2:  body.Category2,
+	// 			Name:       "2d面料",
+	// 			IsPublic:   &nopublic,
+	// 			Categories: body.Categories,
+	// 			Image: &model.OssType{
+	// 				Url:  "",
+	// 				Size: 0,
+	// 			},
+	// 			Platform:   &platform,
+	// 			CreateTime: time.Now(),
+	// 		}, nil
+	// 	},
+	// 	EmtyModel: func(*gin.Context) interface{} {
+	// 		return &model.ImageMat{}
+	// 	},
+	// 	SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+	// 		return map[string]interface{}{"platform": true}
+	// 	},
+	// 	DetailProject: []string{"name", "image", "isPublic", "categories", "colorCards", "thumbnail"},
+	// 	SearchProject: []string{"name", "image", "isPublic", "createTime", "category", "category2", "categories", "thumbnail"},
+	// })
+
+	router.POSTJWT("/upload/imgmat", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		body := &struct {
+			ColorCards []*model.Mat2d
+			Name       string
+			IsPublic   bool
+			Categories []string
+			Thumbnail  *model.OssType
+		}{}
+		c.ShouldBindJSON(&body)
+
+		if len(body.ColorCards) < 1 {
+			return nil, NewError("色卡不能为空!")
+		}
+		platform := true
+		uid, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+		mat := &model.ImageMat{
+			UserId:     uid,
+			Name:       body.Name,
+			Thumbnail:  body.Thumbnail,
+			IsPublic:   &body.IsPublic,
+			Categories: body.Categories,
+			Platform:   &platform,
+			CreateTime: time.Now(),
+			ColorCards: body.ColorCards,
+			State:      model.LibState_Created,
+		}
+		return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionImageMat, mat)
+	})
+
+	router.POSTJWT("/upload/fabric", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		body := &struct {
+			ColorCards []*comm.MaterialConf
+			Name       string
+			IsPublic   bool
+			Categories []string
+			Thumbnail  *model.OssType
+		}{}
+		c.ShouldBindJSON(&body)
+
+		if len(body.ColorCards) < 1 {
+			return nil, NewError("色卡不能为空!")
+		}
+
+		for _, item := range body.ColorCards {
+			item.MatId = primitive.NewObjectID()
+		}
+
+		uid, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+		mat := &model.Fabric{
+			UserId:     uid,
+			Name:       body.Name,
+			Thumbnail:  body.Thumbnail,
+			Categories: body.Categories,
+			CreateTime: time.Now(),
+			ColorCards: body.ColorCards,
+			State:      model.LibState_Created,
+		}
+
+		return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionFabric, mat)
+	})
+
+	//私有2d面料
+	CreateCRUD(router, "/imgmat/private", &CRUDOption{
+		JWT:        true,
+		Collection: repo.CollectionImageMat,
+		NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+			body := &struct {
+				Name string
+				Url  string
+				Size int32
+			}{}
+			c.ShouldBindJSON(&body)
+			nopublic := true
+			platform := false
+
+			uid, _ := primitive.ObjectIDFromHex(apictx.User.Parent)
+
+			return &model.ImageMat{
+				UserId:   uid,
+				Name:     body.Name,
+				IsPublic: &nopublic,
+				Image: &model.OssType{
+					Url:  body.Url,
+					Size: int64(body.Size),
+				},
+				Platform:   &platform,
+				CreateTime: time.Now(),
+			}, nil
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.ImageMat{}
+		},
+		SearchSort: bson.M{"createTime": -1},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			uid, _ := primitive.ObjectIDFromHex(apictx.User.ID)
+			return map[string]interface{}{"userId": uid}
+		},
+		SearchProject: []string{"name", "image", "isPublic", "createTime", "category", "thumbnail", "colorCards"},
+	})
+
+	//材质球
+	CreateCRUD(router, "/material", &CRUDOption{
+		Collection: repo.CollectionMaterials,
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Category string
+				Keyword  string
+			}{}
+			c.ShouldBindJSON(&body)
+
+			now := time.Now()
+			isPlatform := true
+			isEffect := false
+
+			return &model.Material{
+				Category:   body.Category,
+				Name:       "材质球",
+				IsPlatform: &isPlatform,
+				IsEffect:   &isEffect,
+				Thumbnail:  &model.OssType{Url: "", Size: 0},
+				CreateTime: now,
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.Material{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true, "effect": false}
+		},
+		SearchProject: []string{"name", "thumbnail", "createTime", "category", "data", "isPublic"},
+		DetailProject: []string{"name", "data", "thumbnail"},
+	})
+
+	router.GET("/specialmat/patch", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		out := []*model.Material{}
+
+		err := repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{CollectName: repo.CollectionMaterials, Query: repo.Map{
+			"platform": true,
+			"effect":   true,
+		}}, &out)
+
+		total := 0
+
+		for _, item := range out {
+			m := &comm.SpecialMat{
+				Id:         item.Id,
+				Name:       item.Name,
+				Thumbnail:  &comm.OssType{Url: item.Thumbnail.Url, Size: item.Thumbnail.Size},
+				Category:   item.Category,
+				CreateTime: item.CreateTime,
+				Type:       "meta",
+			}
+			if item.Data == nil {
+				continue
+			}
+
+			if item.Category == "init" {
+				continue
+			}
+
+			channels := item.Data.Channels
+			if channels.Albedo != nil {
+				if channels.Albedo.Texture != nil {
+					m.BaseMap = &comm.OssType{
+						Url:  channels.Albedo.Texture.Url,
+						Size: channels.Albedo.Texture.Size,
+					}
+				}
+				if c := channels.Albedo.Color; c != nil {
+					m.BaseColor = &comm.Vect3{c[0], c[1], c[2]}
+				}
+			}
+
+			if channels.NormalMap != nil {
+				if channels.NormalMap.Texture != nil {
+					m.NormalMap = &comm.OssType{
+						Url:  channels.NormalMap.Texture.Url,
+						Size: channels.NormalMap.Texture.Size,
+					}
+				}
+			}
+
+			if channels.Opacity != nil {
+				if channels.Opacity.Factor > 0 {
+					f := float64(channels.Opacity.Factor)
+					m.OpacFactor = &f
+				}
+				if len(channels.Opacity.Texture.Url) > 0 {
+					m.OpacMap = &comm.OssType{Url: channels.Opacity.Texture.Url, Size: channels.Opacity.Texture.Size}
+				}
+			}
+
+			if channels.Roughness != nil {
+				if channels.Roughness.Texture != nil {
+					m.RoughMap = &comm.OssType{Url: channels.Roughness.Texture.Url, Size: channels.Roughness.Texture.Size}
+				}
+				m.RoughFactor = channels.Roughness.Factor
+			}
+
+			if channels.MetalnessPBR != nil {
+				if channels.MetalnessPBR.Texture != nil {
+					m.MetalMap = &comm.OssType{Url: channels.MetalnessPBR.Texture.Url, Size: channels.MetalnessPBR.Texture.Size}
+				}
+				m.MetalFactor = channels.MetalnessPBR.Factor
+			}
+
+			if item.Data.Uvtransform != nil {
+				m.Uvtransform = &comm.MaterailUv{
+					Scale:   item.Data.Uvtransform.Scale,
+					Rotate:  item.Data.Uvtransform.Rotate,
+					OffsetX: item.Data.Uvtransform.OffsetX,
+					OffsetY: item.Data.Uvtransform.OffsetY,
+				}
+			} else {
+				m.Uvtransform = &comm.MaterailUv{
+					Scale:   1,
+					Rotate:  0,
+					OffsetX: 0,
+					OffsetY: 0,
+				}
+			}
+
+			i, e := repo.RepoAddDoc(apictx.CreateRepoCtx(), "specialmats", m)
+			fmt.Println(i, e)
+			total += 1
+		}
+
+		return total, err
+	})
+
+	//特殊材质球
+	CreateCRUD(router, "/specialmat", &CRUDOption{
+		Collection: "specialmats",
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Category  string
+				Keyword   string
+				Name      string
+				Thumbnail *comm.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			return &comm.SpecialMat{
+				Category:   body.Category,
+				Name:       body.Name,
+				Thumbnail:  body.Thumbnail,
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &comm.SpecialMat{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{}
+		},
+		DetailProject:    []string{"_id"},
+		DetailProjectAll: true,
+	})
+
+	// 针车线
+	CreateCRUD(router, "/linemat", &CRUDOption{
+		Collection: "linemats",
+		NewModel: func(c *gin.Context) interface{} {
+			body := &comm.LineMat{}
+			c.ShouldBindJSON(body)
+			body.CreateTime = time.Now()
+			return body
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &comm.LineMat{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{}
+		},
+		DetailProject:    []string{"_id"},
+		DetailProjectAll: true,
+	})
+
+	//面料
+	CreateCRUD(router, "/fabric", &CRUDOption{
+		Collection: repo.CollectionFabric,
+		// NewModel2: func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		// 	body := &struct {
+		// 		Category   string
+		// 		Category2  string
+		// 		Keyword    string
+		// 		Categories []string
+		// 	}{}
+		// 	c.ShouldBindJSON(&body)
+
+		// 	now := time.Now()
+		// 	public := false
+		// 	isPlatform := true
+
+		// 	return &model.Fabric{
+		// 		Category:   body.Category,
+		// 		Category2:  body.Category2,
+		// 		ColorCards: []*model.MaterialHeader{},
+		// 		Categories: body.Categories,
+		// 		Name:       "面料1",
+		// 		Thumbnail:  &model.OssType{Url: "", Size: 0},
+		// 		CreateTime: now,
+		// 		IsPublic:   &public,
+		// 		Price:      0,
+		// 		Platform:   &isPlatform,
+		// 	}, nil
+		// },
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.Fabric{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true}
+		},
+		noUpdate: true,
+
+		SearchProject: []string{"name", "thumbnail", "createTime", "category", "category2", "isPublic", "colorCards", "price", "categories"},
+		// DetailProject: []string{"name", "price", "thumbnail", "category", "category2", "colorCards", "categories"},
+	})
+
+	pageSearchFn := func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		page, size, query := UtilQueryPageSize(c)
+
+		scope := c.Param("scope")
+		coll := UtilGetFabricCollection(scope)
+
+		pageOption := &repo.PageSearchOptions{
+			CollectName: coll,
+			Page:        page,
+			Size:        size,
+			Query:       query,
+			Project:     []string{"name", "thumbnail", "cusNum", "createTime", "category", "colorCards", "price", "categories"},
+			Sort:        bson.M{"createTime": -1},
+		}
+		pageResult, err := repo.RepoPageSearch(apictx.CreateRepoCtx(), pageOption)
+		if err != nil {
+			return nil, err
+		}
+		for _, v := range pageResult.List {
+			if v["userId"] != nil {
+				userId := v["userId"].(primitive.ObjectID).Hex()
+				u, _ := repo.RedisGetUserById(apictx.CreateRepoCtx(), userId)
+				if u != nil {
+					v["user"] = u
+				}
+			}
+		}
+		return pageResult, nil
+	}
+
+	router.GETJWT("/fabric/list/:scope", pageSearchFn)
+
+	router.GET("/fabric/detail/:id", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		id := c.Param("id")
+		if len(id) < 1 {
+			return nil, NewError("参数不能为空")
+		}
+		scope := c.Query("scope")
+		out := &model.Fabric{}
+		uid, _ := primitive.ObjectIDFromHex(id)
+		Collection := UtilGetFabricCollection(scope)
+		ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: Collection,
+			Query:       repo.Map{"_id": uid},
+			Project:     []string{"name", "price", "thumbnail", "category", "colorCards", "categories"},
+		}, out)
+
+		if err != nil {
+			return nil, err
+		}
+		if !ok {
+			return nil, NewError("没有查询到数据")
+		}
+		return out, nil
+	})
+
+	router.POSTJWT("/fabric/update/:scope", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		fabric := &model.Fabric{}
+		err := c.ShouldBindJSON(fabric)
+		if err != nil {
+			fmt.Println(err)
+			return nil, NewError("参数解析错误")
+		}
+
+		collection := UtilGetFabricCollection(c.Param("scope"))
+		if fabric.Id == primitive.NilObjectID {
+			return nil, NewError("Id不能为空!")
+		}
+		id := fabric.Id.Hex()
+		if len(fabric.ColorCards) > 0 {
+			for _, item := range fabric.ColorCards {
+				if item.MatId == primitive.NilObjectID {
+					item.MatId = primitive.NewObjectID()
+				}
+			}
+		}
+
+		// if len(fabric.ColorCards) > 0 { //更新色卡, 更新对应的材质球Id
+		// 	oldFabric := &model.Fabric{}
+		// 	ok, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{CollectName: collection, Query: repo.Map{"_id": id}, Project: []string{"colorCards"}}, oldFabric)
+		// 	if ok {
+		// 		// oldLen := len(oldFabric.ColorCards)
+		// 		oldMatIds := map[string]bool{}
+
+		// 		findCard := func(id string) *model.MaterialHeader {
+		// 			for _, c := range oldFabric.ColorCards {
+		// 				if c.MatId.Hex() == id {
+		// 					return c
+		// 				}
+		// 			}
+		// 			return nil
+		// 		}
+
+		// 		for _, mat := range fabric.ColorCards {
+
+		// 			//没有ID 新增
+		// 			if mat.MatId == primitive.NilObjectID {
+		// 				matItem := &model.Material{
+		// 					Category:   "fabric",
+		// 					Name:       mat.Name,
+		// 					IsPlatform: BoolValue(false),
+		// 					IsEffect:   BoolValue(false),
+		// 					Thumbnail:  mat.Thumbnail,
+		// 					Data:       NewMaterial(mat),
+		// 					CreateTime: time.Now(),
+		// 				}
+		// 				ret, _ := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionMaterials, matItem)
+		// 				mat.MatId, _ = primitive.ObjectIDFromHex(ret)
+		// 				continue
+		// 			}
+		// 			//有判断是更新还是删除
+		// 			old := findCard(mat.MatId.Hex())
+		// 			if old != nil { //找到了判断是否更新
+		// 				oldMatIds[old.MatId.Hex()] = true
+		// 				noChanged := mat.IsEqual(old)
+		// 				if noChanged {
+		// 					continue
+		// 				}
+		// 				//更新
+		// 				material := NewMaterial(mat)
+		// 				matRet, _ := repo.RepoUpdateSetDocProps(apictx.CreateRepoCtx(), repo.CollectionMaterials, mat.MatId.Hex(), bson.M{"$set": bson.M{"data": material}})
+		// 				fmt.Sprintln(matRet)
+		// 			}
+		// 		}
+		// 		//删除旧的
+		// 		for _, oldCard := range oldFabric.ColorCards {
+		// 			if oldCard.MatId == primitive.NilObjectID {
+		// 				continue
+		// 			}
+
+		// 			id := oldCard.MatId.Hex()
+		// 			if !oldMatIds[id] {
+		// 				removeRet, _ := repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionMaterials, id)
+		// 				fmt.Sprintln("==removeRet===")
+		// 				fmt.Sprintln(removeRet)
+		// 			}
+		// 		}
+		// 	}
+		// }
+		return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), collection, id, fabric)
+		// if err != nil {
+		// 	return nil, err
+		// }
+		// if out.MatchedCount != 1 {
+		// 	return nil, NewError("文件不存在!")
+		// }
+		// if out.ModifiedCount != 1 {
+		// 	return nil, NewError("更新失败!")
+		// }
+	})
+
+	//私有2d面料
+	router.GETJWT("/fabric/private/list", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		page, size, query := UtilQueryPageSize(c)
+
+		query["userId"] = apictx.User.ID
+		pageOption := &repo.PageSearchOptions{
+			CollectName: repo.CollectionFabric,
+			Page:        page,
+			Size:        size,
+			Query:       query,
+			Project:     []string{"name", "createTime", "categories", "thumbnail", "colorCards"},
+		}
+		pageOption.Sort = bson.M{"createTime": -1}
+
+		pageResult, err := repo.RepoPageSearch(apictx.CreateRepoCtx(), pageOption)
+		if err != nil {
+			return nil, err
+		}
+		return pageResult, nil
+	})
+
+	router.GET("/dadongfabricpatch", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		//db.fabric_company.find({"name":{"$regex":"dd-"}})
+		var fabrics = []*model.Fabric{}
+
+		err := repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: "fabric_company",
+			Query:       repo.Map{"name": bson.M{"$regex": "dd-"}},
+		}, &fabrics)
+
+		total := 0
+
+		for _, item := range fabrics {
+
+			item.Categories = []string{"62469386cd23c798d0d20a4a"}
+			item.UserId, _ = primitive.ObjectIDFromHex(DADONG_USER_ID)
+			item.Id = primitive.NilObjectID
+			item.CompanyId = DADONG_USER_ID
+			item.State = model.LibState_Publish2Qiye
+			_, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), "fabric_company", item)
+			if err == nil {
+				total += 1
+			}
+		}
+
+		return total, err
+	})
+}

+ 270 - 0
sku3d/sku3d/api/service-member-user.go

@@ -0,0 +1,270 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/bus"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+// 会员用户
+func MemberUser(r *GinRouter) {
+
+	// 查询个人会员信息(管理员, 用户)
+	r.GETJWTKEY("/memberuser/info", GetMemberUserInfo, SKU3DADMIN, SKU3DUSER)
+
+	// 查看会员用户(管理员)
+	r.GETJWTKEY("/memberuser/detail/:id", GetMemberUser, SKU3DADMIN)
+
+	// Deprecated 查看会员用户列表(管理员, 用户)
+	r.GETJWTKEY("/memberuser/list", GetMemberUsers, SKU3DADMIN, SKU3DUSER)
+
+	// Deprecated 添加会员用户(管理员, 用户)
+	r.POSTJWTKEY("/memberuser/create", CreateMemberUser, SKU3DADMIN, SKU3DUSER)
+
+	// Deprecated 修改会员用户(管理员, 用户)
+	r.POSTJWTKEY("/memberuser/update", UpdateMemberUser, SKU3DADMIN, SKU3DUSER)
+
+	// Deprecated 删除会员用户(管理员, 用户)
+	r.POSTJWTKEY("/memberuser/delete/:id", DeleteMemberUser, SKU3DADMIN)
+
+	// 购买会员
+	r.POSTJWT("/memberuser/pay", Pay)
+
+}
+
+func Pay(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	var body struct {
+		OrderId primitive.ObjectID
+		PayMod  int
+	}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return false, err
+	}
+
+	ctx := apictx.CreateRepoCtx()
+
+	// 2、查询订单是否存在
+	var order model.Order
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionOrder,
+		Query:       repo.Map{"_id": body.OrderId},
+	}
+	found, _ := repo.RepoSeachDoc(ctx, option, &order)
+	if !found {
+		return nil, NewError("不存在该会员")
+	}
+	if *order.Status != model.WatiPay {
+		return nil, NewError("请重新创建订单")
+	}
+
+	// 3、更新订单支付模式
+	order.PayMod = &body.PayMod
+	_, err = repo.RepoUpdateSetDoc(ctx, repo.CollectionOrder, order.Id.Hex(), &order)
+	if err != nil {
+		return nil, NewError("支付方式修改失败")
+	}
+
+	var orderName string
+	if order.Member.Day == nil {
+		orderName = fmt.Sprintf("会员类型:%s,使用人数:%d", order.Member.Name, *order.Member.UserNum)
+	} else {
+		orderName = fmt.Sprintf("会员类型:%s,天数:%d,使用人数:%d", order.Member.Name, *order.Member.Day, *order.Member.UserNum)
+	}
+
+	return bus.GetQrCode(&model.OrderMsg{Id: order.Id.Hex(), Name: orderName, PayMode: &body.PayMod, Amount: order.Amount, ModName: "SKU3D", ExpireTime: order.CreateTime.Add(time.Minute * 15)})
+}
+
+func GetMemberUserInfo(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	pidStr := apictx.User.Parent
+	pid, err := primitive.ObjectIDFromHex(pidStr)
+	if err != nil {
+		return false, err
+	}
+
+	// 1.获取免费版
+	var member model.Member
+	ctx := apictx.CreateRepoCtx()
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionMember,
+		Query:       repo.Map{"level": model.FREE},
+	}
+	found, _ := repo.RepoSeachDoc(ctx, option, &member)
+	if !found {
+		return []interface{}{}, err
+	}
+
+	// 1.获取会员用户,免费版亦或是企业版
+	var memberusers []*model.MemberUser
+
+	option1 := &repo.DocSearchOptions{
+		CollectName: repo.CollectionMemberUser,
+		Query:       repo.Map{"parentId": pid},
+	}
+	err = repo.RepoSeachDocs(ctx, option1, &memberusers)
+	if err != nil {
+		return []interface{}{}, err
+	}
+
+	// 2、没有免费版则插入
+	if len(memberusers) == 0 {
+		var memberuser model.MemberUser
+		memberuser.ParentId = &pid
+		memberuser.MemberId = member.Id
+		memberuser.CreateTime = time.Now()
+		memberuser.UpdateTime = time.Now()
+		memberuser.DeadTime = time.Now().Add(time.Hour * 24 * 15)
+		idStr, err := repo.RepoAddDoc(ctx, repo.CollectionMemberUser, memberuser)
+		if err != nil {
+			return []interface{}{}, err
+		}
+		id, _ := primitive.ObjectIDFromHex(idStr)
+		memberuser.Id = &id
+		memberusers = []*model.MemberUser{&memberuser}
+	}
+
+	// 3.返回前端需要的状态
+	var mvos []interface{}
+	for _, v := range memberusers {
+		// 查询订单,加一个status字段
+		var mvo struct {
+			*model.MemberUser
+			Status *int `json:"status,omitempty"`
+		}
+		mvo.MemberUser = v
+
+		// 存在则为 免费已申请 和 会员已创建
+		var order model.Order
+		query := repo.Map{}
+
+		query["userId"] = pid
+		// 过滤出子账号可以获取父账号的订单
+		// 过滤出申请中和等待支付的
+		if v.MemberId.Hex() == member.Id.Hex() {
+			query["status"] = model.Applying
+		} else {
+			if apictx.User.Parent != "" {
+				pid, err := primitive.ObjectIDFromHex(apictx.User.Parent)
+				if err != nil {
+					return false, err
+				}
+				query["userId"] = pid
+			}
+			query["status"] = model.WatiPay
+		}
+		option := &repo.DocSearchOptions{
+			CollectName: repo.CollectionOrder,
+			Query:       query,
+		}
+
+		// 查出申请中、正在购买
+		flag, _ := repo.RepoSeachDoc(ctx, option, &order)
+		if flag {
+			mvo.Status = order.Status
+		}
+
+		// 判断是否为试用免费版
+		// 免费 && 创建时间+15天>现在 && 过期时间-创建时间<=15天
+
+		if mvo.MemberId.Hex() == member.Id.Hex() && mvo.Status == nil && time.Until(v.CreateTime.Add(time.Hour*24*15)) >= 0 && v.DeadTime.Sub(v.CreateTime) <= time.Hour*24*15 {
+			mvo.Status = &model.Trial
+		}
+
+		mvos = append(mvos, mvo)
+	}
+
+	return mvos, nil
+}
+
+func GetMemberUser(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	idStr := c.Param("id")
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return false, err
+	}
+
+	var memberuser model.MemberUser
+
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionMemberUser,
+		Query:       repo.Map{"_id": id},
+	}
+
+	// 1.存在返回,不存在插入免费映射表
+	ctx := apictx.CreateRepoCtx()
+	found, _ := repo.RepoSeachDoc(ctx, option, &memberuser)
+	if !found {
+		return false, NewError("免费版不存在")
+	}
+
+	// 2.获取企业会员用户
+	// 	option1 := repo.DocSearchAggreOptions{
+	// 		CollectName: repo.CollectionMemberUser,
+	// 		Query: []bson.M{{"$match":bson.M{"userIds":  id}}},
+	// }
+
+	// 	var mu []*model.MemberUser
+	// 	_, err = repo.RepoSeachDocAggre(ctx, &option1, &mu)
+	// 	if err != nil {
+	// 		return false, err
+	// 	}
+	// 	mu = append(mu, &memberuser)
+
+	return memberuser, nil
+}
+
+func GetMemberUsers(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+
+	option := &repo.PageSearchOptions{
+		CollectName: repo.CollectionMemberUser,
+		Query:       query,
+		Page:        page,
+		Size:        size,
+		Sort:        bson.M{"createTime": -1},
+	}
+
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), option)
+}
+
+func CreateMemberUser(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	var memberuser model.MemberUser
+	err := c.ShouldBindJSON(&memberuser)
+	if err != nil {
+		return false, nil
+	}
+
+	ctx := apictx.CreateRepoCtx()
+
+	memberuser.CreateTime = time.Now()
+	memberuser.UpdateTime = time.Now()
+
+	return repo.RepoAddDoc(ctx, repo.CollectionMemberUser, &memberuser)
+}
+
+func UpdateMemberUser(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	var memberuser model.MemberUser
+	err := c.ShouldBindJSON(&memberuser)
+	if err != nil {
+		return false, nil
+	}
+
+	memberuser.UpdateTime = time.Now()
+
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionMemberUser, memberuser.Id.Hex(), &memberuser)
+}
+
+func DeleteMemberUser(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	idStr := c.Param("id")
+
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionMemberUser, idStr)
+}

+ 111 - 0
sku3d/sku3d/api/service-member.go

@@ -0,0 +1,111 @@
+package api
+
+import (
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+//会员
+func Member(r *GinRouter) {
+
+	// 查看会员
+	r.GETJWTKEY("/member/detail/:id", GetMember, SKU3DADMIN)
+
+	// 查看会员列表
+	r.GETJWT("/member/list", GetMembers)
+
+	// 添加会员
+	r.POSTJWTKEY("/member/create", CreateMember, SKU3DADMIN)
+
+	// 修改会员
+	r.POSTJWTKEY("/member/update", UpdateMember, SKU3DADMIN)
+
+	// 删除会员
+	r.POSTJWTKEY("/member/delete/:id", DeleteMember, SKU3DADMIN)
+
+}
+
+func GetMember(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	idStr := c.Param("id")
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return false, err
+	}
+
+	var member model.Member
+
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionMember,
+		Query:       repo.Map{"_id": id},
+	}
+
+	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), option, &member)
+	if !found {
+		return false, err
+	}
+
+	return member, nil
+}
+
+func GetMembers(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+
+	option := &repo.PageSearchOptions{
+		CollectName: repo.CollectionMember,
+		Query:       query,
+		Page:        page,
+		Size:        size,
+		Sort:        bson.M{"createTime": -1},
+	}
+
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), option)
+}
+
+func CreateMember(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	var member model.Member
+	err := c.ShouldBindJSON(&member)
+	if err != nil {
+		return false, nil
+	}
+
+	ctx := apictx.CreateRepoCtx()
+
+	// option := &repo.DocSearchOptions{
+	// 	CollectName: repo.CollectionMember,
+	// 	Query: repo.Map{"key":member.Key},
+	// }
+	// found, _ := repo.RepoSeachDocMap(ctx, option)
+	// if found{
+	// 	return false,NewError("已存在key")
+	// }
+
+	member.CreateTime = time.Now()
+	member.UpdateTime = time.Now()
+
+	return repo.RepoAddDoc(ctx, repo.CollectionMember, &member)
+}
+
+func UpdateMember(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	var member model.Member
+	err := c.ShouldBindJSON(&member)
+	if err != nil {
+		return false, nil
+	}
+
+	member.UpdateTime = time.Now()
+
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionMember, member.Id.Hex(), &member)
+}
+
+func DeleteMember(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	idStr := c.Param("id")
+
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionMember, idStr)
+}

+ 412 - 0
sku3d/sku3d/api/service-mesh.go

@@ -0,0 +1,412 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+func RegMeshCrud(router *GinRouter) {
+
+	//创建Mesh
+	CreateCRUD(router, "/mesh", &CRUDOption{
+		Collection: repo.CollectionMesh,
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Category  string
+				Name      string
+				Thumbnail *model.OssType
+				File      *model.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			out := &model.Mesh{
+				Name:       body.Name,
+				Category:   body.Category,
+				CreateTime: time.Now(),
+				Geometries: []string{},
+				File:       body.File,
+				Thumbnail:  body.Thumbnail,
+			}
+
+			return out
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.Mesh{}
+		},
+		//DetailProject: []string{"osgjs", "name", "file", "materials"},
+		SearchProject: []string{"name", "createTime", "geometries", "thumbnail", "osgjs", "category", "osgjsbin", "file", "geometries"},
+	})
+
+	router.GET("/mesh/detail/:id", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		id := c.Param("id")
+		if len(id) < 1 {
+			return nil, NewError("id不能为空!")
+		}
+		out := &model.Mesh{}
+
+		uid, _ := primitive.ObjectIDFromHex(id)
+
+		ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: repo.CollectionMesh,
+			Query:       repo.Map{"_id": uid},
+		}, out)
+
+		if err != nil {
+			return nil, err
+		}
+		if !ok {
+			return nil, NewError("没有查询到数据")
+		}
+		return out, nil
+	})
+
+	//转换模型
+	// router.POST("/mesh/conv", ServiceConvModel)
+
+	//鞋型编辑
+	CreateCRUD(router, "/shoe", &CRUDOption{
+		Collection: repo.CollectionShoes,
+
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Sex       uint8
+				Type      string
+				Season    uint8
+				Name      string
+				Thumbnail *model.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			nopublic := false
+			Platform := true
+
+			return &model.ShoeMesh{
+				Name:       body.Name,
+				Sex:        body.Sex,
+				Type:       body.Type,
+				Season:     body.Season,
+				IsPublic:   &nopublic,
+				Thumbnail:  body.Thumbnail,
+				Platform:   &Platform,
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.ShoeMesh{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true}
+		},
+		SearchProject: []string{"name", "thumbnail", "isPublic", "createTime", "sex", "type", "season", "meshId"},
+	})
+
+	//鞋楦
+	CreateCRUD(router, "/last", &CRUDOption{
+		Collection: repo.CollectionLasts,
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Sex       uint8
+				Type      string
+				Season    uint8
+				Name      string
+				Thumbnail *model.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			nopublic := false
+			Platform := true
+
+			return &model.LastMesh{
+				Name:       body.Name,
+				Sex:        body.Sex,
+				Type:       body.Type,
+				Season:     body.Season,
+				IsPublic:   &nopublic,
+				Thumbnail:  body.Thumbnail,
+				Platform:   &Platform,
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.LastMesh{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true}
+		},
+		SearchProject: []string{"name", "thumbnail", "isPublic", "createTime", "sex", "type", "season", "meshId"},
+	})
+
+	//鞋跟
+	CreateCRUD(router, "/heel", &CRUDOption{
+		Collection: repo.CollectionHeels,
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Sex       uint8
+				Type      string
+				Season    uint8
+				Name      string
+				Thumbnail *model.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			nopublic := false
+			Platform := true
+
+			return &model.HeelMesh{
+				Name:       body.Name,
+				Sex:        body.Sex,
+				Type:       body.Type,
+				Season:     body.Season,
+				IsPublic:   &nopublic,
+				Thumbnail:  body.Thumbnail,
+				Platform:   &Platform,
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.HeelMesh{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true}
+		},
+		SearchProject: []string{"name", "thumbnail", "isPublic", "createTime", "sex", "type", "season", "meshId"},
+	})
+
+	//大底
+	CreateCRUD(router, "/sole", &CRUDOption{
+		Collection: repo.CollectionSoles,
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Sex       uint8
+				Type      string
+				Season    uint8
+				Name      string
+				Thumbnail *model.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			nopublic := false
+			Platform := true
+
+			return &model.SoleMesh{
+				Name:       body.Name,
+				Sex:        body.Sex,
+				Type:       body.Type,
+				Season:     body.Season,
+				IsPublic:   &nopublic,
+				Thumbnail:  body.Thumbnail,
+				Platform:   &Platform,
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.SoleMesh{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true}
+		},
+		SearchProject: []string{"name", "thumbnail", "isPublic", "createTime", "sex", "type", "season", "meshId"},
+	})
+
+	//配饰
+	CreateCRUD(router, "/decorate", &CRUDOption{
+		Collection: repo.CollectionDecorates,
+		NewModel: func(c *gin.Context) interface{} {
+			body := &struct {
+				Sex       uint8
+				Type      string
+				Season    uint8
+				Name      string
+				Thumbnail *model.OssType
+			}{}
+			c.ShouldBindJSON(&body)
+
+			nopublic := false
+			Platform := true
+
+			return &model.DecorateMesh{
+				Name:       body.Name,
+				Sex:        body.Sex,
+				Type:       body.Type,
+				Season:     body.Season,
+				IsPublic:   &nopublic,
+				Thumbnail:  body.Thumbnail,
+				Platform:   &Platform,
+				CreateTime: time.Now(),
+			}
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.DecorateMesh{}
+		},
+		SearchFilter: func(c *gin.Context, apictx *ApiSession, query map[string]interface{}) map[string]interface{} {
+			return map[string]interface{}{"platform": true}
+		},
+		SearchProject: []string{"name", "thumbnail", "isPublic", "createTime", "sex", "type", "season", "meshId"},
+	})
+
+	router.POSTJWT("/lib/update", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		body := &struct {
+			Id         string `json:"_id"`
+			Type       string
+			Name       string
+			CusNum     string
+			Thumbnail  *model.OssType
+			State      int32
+			Categories []string
+			Scope      string
+		}{}
+		c.ShouldBindJSON(&body)
+
+		collections := map[string]string{"imgmat": repo.CollectionImageMat, "fabric": repo.CollectionFabric, "sticker": repo.CollectionStickers, "design": repo.CollectionDesigns, "last": repo.CollectionLasts, "heel": repo.CollectionHeels, "sole": repo.CollectionSoles, "decorate": repo.CollectionDecorates}
+		if len(collections[body.Type]) < 1 {
+			return nil, NewError("库类型不支持!")
+		}
+
+		if len(body.Id) < 1 {
+			return nil, NewError("Id不能为空!")
+		}
+
+		collect := collections[body.Type]
+		if body.Scope == "team" || body.Scope == "company" {
+			collect = fmt.Sprintf("%s_%s", body.Type, body.Scope)
+		}
+
+		fmt.Println("update=>", body)
+		data := bson.M{"state": body.State}
+		if body.Thumbnail != nil {
+			data["thumbnail"] = body.Thumbnail
+		}
+		if body.Categories != nil && len(body.Categories) > 0 {
+			data["categories"] = body.Categories
+		}
+
+		if len(body.Name) > 0 {
+			data["name"] = body.Name
+		}
+
+		if len(body.CusNum) > 0 {
+			data["cusNum"] = body.CusNum
+		}
+
+		if body.Scope == "company" && body.State == model.LibState_Publish2Qiye && len(body.Name) < 1 { //更新发布状态
+			data["createTime"] = time.Now()
+		}
+
+		return repo.RepoUpdateSetDocProps(apictx.CreateRepoCtx(), collect, body.Id, bson.M{"$set": data})
+	})
+
+	router.GET("/lib/mesh/osg", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+		id := c.Query("id")
+		if len(id) < 1 {
+			return nil, NewError("id不能为空!")
+		}
+		meshType := c.Query("type")
+		if meshType == "soles" {
+			meshType = "sole"
+		}
+		scope := c.Query("scope")
+		// if len(scope) < 1 {
+		// 	return nil, NewError("预览模型的scope不能为空!")
+		// }
+
+		collections := map[string]string{"shoes": repo.CollectionShoes, "last": repo.CollectionLasts, "heel": repo.CollectionHeels, "sole": repo.CollectionSoles, "decorate": repo.CollectionDecorates}
+		CollectName := collections[meshType]
+		if len(CollectName) < 1 {
+			return nil, NewError("库类型不支持!")
+		}
+
+		if scope == "team" || scope == "company" {
+			if meshType != "shoes" {
+				CollectName = fmt.Sprintf("%s_%s", meshType, scope)
+			}
+		}
+
+		if meshType == "shoes" {
+			designCollection := repo.CollectionDesigns
+			if scope == "team" || scope == "company" {
+				designCollection = fmt.Sprintf("design_%s", scope)
+			}
+			ok, shoeRet := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: designCollection,
+				Query:       repo.Map{"_id": id},
+				Project:     []string{"shoeId"},
+			})
+			if !ok {
+				return nil, NewError("模型查询失败!")
+			}
+			id = shoeRet["shoeId"].(primitive.ObjectID).Hex()
+		}
+
+		ok, meshRet := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: CollectName,
+			Query:       repo.Map{"_id": id},
+			Project:     []string{"meshId"},
+		})
+		if !ok {
+			return nil, NewError("模型查询失败!")
+		}
+		meshId, _ := meshRet["meshId"].(primitive.ObjectID)
+
+		ok, meshDetail := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: repo.CollectionMesh,
+			Query:       repo.Map{"_id": meshId},
+			Project:     []string{"osgjs", "name", "file", "shadow"},
+		})
+
+		if !ok {
+			return nil, NewError("模型查询失败!")
+		}
+
+		return meshDetail, nil
+	})
+
+	// router.POSTJWT("/mesh/shadow/create/:id", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	// 	designId := c.Param("id")
+	// 	if len(designId) < 1 {
+	// 		return nil, NewError("id不能为空!")
+	// 	}
+
+	// 	ok, shoeRet := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+	// 		CollectName: repo.CollectionDesigns,
+	// 		Query:       repo.Map{"_id": designId},
+	// 		Project:     []string{"shoeId"},
+	// 	})
+
+	// 	if !ok {
+	// 		return nil, NewError("模型查询失败!")
+	// 	}
+	// 	id := shoeRet["shoeId"].(primitive.ObjectID).Hex()
+
+	// 	ok, meshShoeRet := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+	// 		CollectName: repo.CollectionShoes,
+	// 		Query:       repo.Map{"_id": id},
+	// 		Project:     []string{"meshId"},
+	// 	})
+	// 	if !ok {
+	// 		return nil, NewError("模型查询失败!")
+	// 	}
+	// 	meshId, _ := meshShoeRet["meshId"].(primitive.ObjectID)
+
+	// 	meshObj := &model.Mesh{}
+
+	// 	ok, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+	// 		CollectName: repo.CollectionMesh,
+	// 		Query:       repo.Map{"_id": meshId.Hex()},
+	// 		Project:     []string{"file"},
+	// 	}, meshObj)
+
+	// 	if !ok || err != nil {
+	// 		return nil, NewError("模型查询失败!")
+	// 	}
+
+	// 	return true, bus.NatsCenter.ShadowProcessReq(meshId.Hex(), meshObj.File.Url, apictx.User.ID)
+	// })
+
+}

+ 124 - 0
sku3d/sku3d/api/service-minio.go

@@ -0,0 +1,124 @@
+package api
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"path"
+	"sku3dweb/conf"
+	"strings"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/minio/minio-go/v7"
+	"github.com/minio/minio-go/v7/pkg/credentials"
+)
+
+func createMinioClient(conf *conf.AppConf) *minio.Client {
+
+	endpoint := conf.Minio.Endpoint
+
+	accessKeyID := conf.Minio.AccessKey
+	secretAccessKey := conf.Minio.AccessSec
+	useSSL := false
+
+	// Initialize minio client object.
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+		return nil
+	}
+	return minioClient
+}
+
+func MinioCreateUserPolicy(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	body := struct {
+		Key string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, NewError("参数解析错误!")
+	}
+	if len(body.Key) < 1 {
+		return nil, NewError("Object Key不能为空")
+	}
+	// Initialize policy condition config.
+	policy := minio.NewPostPolicy()
+
+	// Apply upload policy restrictions:
+	policy.SetBucket(apictx.Svc.Conf.Minio.Bucket)
+
+	fkey := body.Key //fmt.Sprintf("u/%s/%s", apictx.User.Parent, body.Key)
+	policy.SetKey(fkey)
+	policy.SetExpires(time.Now().UTC().Add(time.Second * 600)) // expires in 10 days
+
+	// Only allow 'png' images.
+	// policy.SetContentType("image/png")
+
+	// Only allow content size in range 1KB to 1MB.
+	policy.SetContentLengthRange(0, 5*1024*1024)
+
+	// Add a user metadata using the key "custom" and value "user"
+	// policy.SetUserMetadata("custom", "user")
+
+	client := createMinioClient(apictx.Svc.Conf)
+	url, err := client.PresignedPutObject(
+		context.Background(), apictx.Svc.Conf.Minio.Bucket, fkey, time.Second*600,
+	)
+	//_, formdata, err := client.PresignedPostPolicy(context.Background(), policy)
+
+	outUrl := fmt.Sprintf("%s://%s%s?%s", url.Scheme, url.Host, url.Path, url.RawQuery)
+
+	if err != nil {
+		return nil, err
+	}
+
+	out := map[string]interface{}{
+		"uploadUrl": outUrl,
+		"url":       fmt.Sprintf("%s/%s/%s", apictx.Svc.Conf.Minio.PubHost, apictx.Svc.Conf.Minio.Bucket, fkey),
+	}
+	return out, nil
+}
+
+func MinioUploadFile(client *minio.Client, bucketName string, fpath string, key string) (string, int64) {
+	info, err := client.FPutObject(context.Background(), bucketName, key, fpath, minio.PutObjectOptions{})
+	fmt.Println(info)
+	if err != nil {
+		return "", 0
+	}
+	return info.Key, info.Size
+}
+
+func MinioUploadLocalFile(client *minio.Client, bucketName string, fpath string, minioDir string) (string, int64) {
+
+	_, name := path.Split(fpath)
+	keyFormat := "%s/%s"
+	if strings.HasSuffix(minioDir, "/") {
+		keyFormat = "%s%s"
+	}
+	minioKey := fmt.Sprintf(keyFormat, minioDir, name)
+
+	opts := minio.PutObjectOptions{}
+
+	fpathExt := path.Ext(fpath)
+	isGzFile := fpathExt == ".gz"
+	if isGzFile {
+		opts.ContentType = "text/plain"
+		opts.ContentEncoding = "gzip"
+	}
+
+	info, err := client.FPutObject(context.Background(), bucketName, minioKey, fpath, opts)
+	fmt.Println(info)
+	if err != nil {
+		info, err = client.FPutObject(context.Background(), bucketName, minioKey, fpath, opts)
+		if err != nil {
+			return "", 0
+		}
+	}
+
+	return minioKey, info.Size
+}

+ 374 - 0
sku3d/sku3d/api/service-obs.go

@@ -0,0 +1,374 @@
+package api
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"sku3dweb/db/model"
+	"sku3dweb/log"
+	"strings"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
+)
+
+//infishCn
+//RA1D7CFVCVKUFX1FHHPB
+//cDrR2NgAF2KaA4MRkcK19r93P3P6hLKOPhHvPu9p
+var (
+	AccessKeyId = "RA1D7CFVCVKUFX1FHHPB"
+	Endpoint    = "obs.cn-east-3.myhuaweicloud.com"
+)
+
+func CreateObsClient() (*obs.ObsClient, error) {
+	var sk = "cDrR2NgAF2KaA4MRkcK19r93P3P6hLKOPhHvPu9p"
+	// 创建ObsClient结构体
+	return obs.New(AccessKeyId, sk, Endpoint)
+}
+
+//u/xxx/img/up/timps.png
+func ServiceObsCreateImagePolicy(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	body := struct {
+		Ext string
+	}{}
+
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, NewError("参数解析错误!")
+	}
+	if len(body.Ext) < 1 {
+		return nil, NewError("上传文件的扩展名不能为空")
+	}
+
+	fkey := fmt.Sprintf("u/%s/images/uploads/%v.%s", apictx.User.Parent, time.Now().UnixNano(), body.Ext)
+
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, NewError("创建ObsClient 失败!")
+	}
+
+	defer func() {
+		client.Close()
+	}()
+
+	// 除key,policy,signature外,表单上传时的其他参数,支持的值:
+	// 	acl
+	// 	cache-control
+	// 	content-type
+	// 	content-disposition
+	// 	content-encoding
+	// 	expires
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+
+	result, err := client.CreateBrowserBasedSignature(&obs.CreateBrowserBasedSignatureInput{
+		Bucket:  bucketName,
+		Key:     fkey,
+		Expires: 300,
+		FormParams: map[string]string{
+			"x-obs-acl": "public-read",
+		},
+	})
+
+	if err != nil {
+		return nil, NewLogWithError(err)
+	}
+
+	out := map[string]interface{}{
+		"accessKeyId":  AccessKeyId,
+		"originPolicy": result.OriginPolicy,
+		"policy":       result.Policy,
+		"signature":    result.Signature,
+		"host":         fmt.Sprintf("//%s.%s", bucketName, Endpoint),
+		"key":          fkey,
+	}
+	return out, nil
+}
+
+func ServiceObsList(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	prefix := c.Query("prefix")
+
+	if len(prefix) < 1 {
+		return nil, NewError("prefix不能为空")
+	}
+
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, NewError("创建ObsClient 失败!")
+	}
+
+	defer func() {
+		client.Close()
+	}()
+
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+	result, err := client.ListObjects(&obs.ListObjectsInput{
+		Bucket: bucketName,
+		ListObjsInput: obs.ListObjsInput{
+			Prefix:    prefix,
+			Delimiter: "/",
+		},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	fmt.Println("result=>", prefix)
+	fmt.Println(result.CommonPrefixes, result.IsTruncated)
+	out := []*model.ObsItem{}
+
+	imgExts := map[string]bool{".jpeg": true, ".jpg": true, ".gif": true, ".png": true, ".svg": true, ".bmp": true}
+
+	for k, v := range result.Contents {
+		fmt.Println(k, v.Key, v.LastModified, v.Size)
+		if prefix == v.Key {
+			continue
+		}
+		isDirSuffix := strings.HasSuffix(v.Key, "/")
+		isDir := (isDirSuffix && v.Size == 0)
+		isImage := false
+
+		originKey := strings.ToLower(v.Key)
+		if !isDir && imgExts[path.Ext(originKey)] {
+			isImage = true
+		}
+
+		keys := strings.Split(v.Key, "/")
+		name := ""
+		if isDir {
+			name = keys[len(keys)-2]
+		} else {
+			name = keys[len(keys)-1]
+		}
+
+		url := ""
+		if !isDir {
+			url = fmt.Sprintf("//%s.%s/%s", bucketName, Endpoint, v.Key)
+		}
+
+		out = append(out, &model.ObsItem{
+			Name:         name,
+			Size:         uint64(v.Size),
+			IsImage:      isImage,
+			Url:          url,
+			IsDir:        isDir,
+			LastModified: v.LastModified,
+		})
+	}
+
+	for _, v := range result.CommonPrefixes {
+		keys := strings.Split(v, "/")
+		name := keys[len(keys)-2]
+
+		out = append(out, &model.ObsItem{
+			Name:    name,
+			IsImage: false,
+			IsDir:   true,
+		})
+	}
+
+	outMap := map[string]interface{}{
+		"list":        out,
+		"isTruncated": result.IsTruncated,
+	}
+	return outMap, nil
+}
+
+//管理后台上传文件
+func ServiceObsUploadPolicy(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	body := struct {
+		Key string
+	}{}
+
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, NewError("参数解析错误!")
+	}
+	if len(body.Key) < 1 {
+		return nil, NewError("上传文件的key不能为空")
+	}
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, NewError("创建ObsClient 失败!")
+	}
+	defer func() {
+		client.Close()
+	}()
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+
+	result, err := client.CreateBrowserBasedSignature(&obs.CreateBrowserBasedSignatureInput{
+		Bucket:  bucketName,
+		Key:     body.Key,
+		Expires: 600,
+		FormParams: map[string]string{
+			"x-obs-acl": "public-read",
+		},
+	})
+
+	if err != nil {
+		return nil, NewLogWithError(err)
+	}
+
+	out := map[string]interface{}{
+		"accessKeyId":  AccessKeyId,
+		"originPolicy": result.OriginPolicy,
+		"policy":       result.Policy,
+		"signature":    result.Signature,
+		"host":         fmt.Sprintf("https://%s.%s", bucketName, Endpoint),
+		"key":          body.Key,
+	}
+	return out, nil
+}
+
+func ServiceObsRemove(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	body := struct {
+		Key string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, NewError("参数解析错误!")
+	}
+	if len(body.Key) < 1 {
+		return nil, NewError("文件的key不能为空")
+	}
+
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, NewError("创建ObsClient 失败!")
+	}
+
+	defer func() {
+		client.Close()
+	}()
+
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+	result, err := client.DeleteObject(&obs.DeleteObjectInput{
+		Bucket: bucketName,
+		Key:    body.Key,
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+func ServiceObsCreateFolder(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := struct {
+		Key string
+	}{}
+	err := c.ShouldBindJSON(&body)
+	if err != nil {
+		return nil, NewError("参数解析错误!")
+	}
+	if len(body.Key) < 1 {
+		return nil, NewError("文件的key不能为空")
+	}
+
+	client, err := CreateObsClient()
+	if err != nil {
+		return nil, NewError("创建ObsClient 失败!")
+	}
+
+	defer func() {
+		client.Close()
+	}()
+
+	bucketName := apictx.Svc.Conf.Obs.Bucket
+	result, err := client.PutObject(&obs.PutObjectInput{
+		PutObjectBasicInput: obs.PutObjectBasicInput{
+			ObjectOperationInput: obs.ObjectOperationInput{
+				Bucket: bucketName,
+				Key:    body.Key,
+				ACL:    obs.AclPublicRead,
+			},
+		},
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+func UploadFile(obsClient *obs.ObsClient, bucketName string, fpath string, obsDir string) *model.OssType {
+
+	_, obsname := path.Split(fpath)
+	keyFormat := "%s/%s"
+	if strings.HasSuffix(obsDir, "/") {
+		keyFormat = "%s%s"
+	}
+	obsKey := fmt.Sprintf(keyFormat, obsDir, obsname)
+
+	input := &obs.PutFileInput{}
+	input.Bucket = bucketName
+	input.Key = obsKey
+	input.SourceFile = fpath
+
+	fpathExt := path.Ext(fpath)
+	isGzFile := fpathExt == ".gz"
+	if isGzFile {
+		input.ContentType = "text/plain"
+		input.Metadata = map[string]string{"Content-Encoding": "gzip"}
+	}
+	result, err := obsClient.PutFile(input)
+
+	isUploadOk := true
+
+	if err != nil {
+		log.Errorf("upload obs fail %s", fpath)
+		log.Error(err)
+		isUploadOk = false
+	}
+
+	if result.StatusCode != 200 {
+		isUploadOk = false
+	}
+
+	if !isUploadOk {
+		result, err = obsClient.PutFile(input)
+
+		if err != nil {
+			log.Errorf("upload obs fail2 %s", fpath)
+			log.Error(err)
+			return &model.OssType{}
+		}
+		if result.StatusCode != 200 {
+			return &model.OssType{}
+		}
+	}
+
+	if isGzFile {
+		metaRet, err := obsClient.SetObjectMetadata(&obs.SetObjectMetadataInput{
+			Bucket:          bucketName,
+			Key:             obsKey,
+			ContentEncoding: "gzip",
+			ContentType:     "text/plain",
+		})
+		fmt.Println(metaRet, err)
+
+		if err != nil {
+			metaRet, err = obsClient.SetObjectMetadata(&obs.SetObjectMetadataInput{
+				Bucket:          bucketName,
+				Key:             obsKey,
+				ContentEncoding: "gzip",
+				ContentType:     "text/plain",
+			})
+			fmt.Println(metaRet, err)
+		}
+	}
+
+	fi, err := os.Stat(fpath)
+	size := int64(1)
+	if err == nil {
+		size = fi.Size()
+	}
+
+	return &model.OssType{Url: fmt.Sprintf("//%s.%s/%s", bucketName, Endpoint, obsKey), Size: int64(size)}
+}

+ 290 - 0
sku3d/sku3d/api/service-order.go

@@ -0,0 +1,290 @@
+package api
+
+import (
+	"fmt"
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+//订单
+func Order(r *GinRouter) {
+
+	// 查看订单
+	r.GETJWTKEY("/order/detail/:id", GetOrder, SKU3DUSER, SKU3DADMIN)
+
+	// 查看订单列表
+	r.GETJWTKEY("/order/list", GetOrders, SKU3DUSER, SKU3DADMIN)
+
+	// 添加
+	r.POSTJWTKEY("/order/create", CreateOrder, SKU3DUSER, SKU3DADMIN)
+
+	// 修改订单
+	r.POSTJWTKEY("/order/update", UpdateOrder, SKU3DUSER, SKU3DADMIN)
+
+	// 删除订单
+	r.POSTJWTKEY("/order/delete/:id", DeleteOrder, SKU3DUSER, SKU3DADMIN)
+}
+
+func GetOrder(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	idStr := c.Param("id")
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return false, err
+	}
+
+	query := repo.Map{"_id": id}
+	// 非管理员只能看自己的订单
+	if apictx.User.Key == SKU3DUSER {
+		pid, err := primitive.ObjectIDFromHex(apictx.User.Parent)
+		if err != nil {
+			return false, err
+		}
+		query["userId"] = pid
+	}
+
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionOrder,
+		Query:       query,
+	}
+
+	found, m := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), option)
+	if !found {
+		return false, NewError("没有找到该订单")
+	}
+
+	return m, nil
+}
+
+func GetOrders(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+
+	option := &repo.PageSearchOptions{
+		CollectName: repo.CollectionOrder,
+		Query:       query,
+		Page:        page,
+		Size:        size,
+		Sort:        bson.M{"createTime": -1},
+	}
+
+	// 非管理员只能看自己的订单
+	if apictx.User.Key == SKU3DUSER {
+		pid, err := primitive.ObjectIDFromHex(apictx.User.Parent)
+		if err != nil {
+			return false, err
+		}
+		query["userId"] = pid
+	}
+
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), option)
+}
+
+func CreateOrder(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	var order model.Order
+	err := c.ShouldBindJSON(&order)
+	if err != nil {
+		fmt.Println(err)
+		return false, err
+	}
+
+	ctx := apictx.CreateRepoCtx()
+
+	pid, err := primitive.ObjectIDFromHex(apictx.User.Parent)
+	if err != nil {
+		return false, err
+	}
+
+	query := repo.Map{"userId": pid}
+
+	// 申请免费版,或者购买企业版
+	if order.Apply != nil && order.Member == nil {
+		query["status"] = model.Applying
+		option := &repo.DocSearchOptions{
+			CollectName: repo.CollectionOrder,
+			Query:       query,
+		}
+		found, _ := repo.RepoSeachDocMap(ctx, option)
+		if found {
+			return false, NewError("已申请")
+		}
+		order.Status = &model.Applying
+
+	} else if order.Apply == nil && order.Member != nil {
+		query["status"] = model.WatiPay
+		option := &repo.DocSearchOptions{
+			CollectName: repo.CollectionOrder,
+			Query:       query,
+		}
+		found, _ := repo.RepoSeachDocMap(ctx, option)
+		if found {
+			return false, NewError("请先完成已有支付")
+		}
+		order.Status = &model.WatiPay
+
+		// 查询member信息
+		var m model.Member
+
+		option1 := &repo.DocSearchOptions{
+			CollectName: repo.CollectionMember,
+			Query:       repo.Map{"_id": order.Member.Id},
+		}
+		found, _ = repo.RepoSeachDoc(ctx, option1, &m)
+		if !found {
+			return false, NewError("不存在该会员信息")
+		}
+
+		m.Day = order.Member.Day
+		order.Member.Member = &m
+
+		// 直接购买/续费
+		var total int
+		if order.Member.Day != nil { //直接购买默认是5个席位
+			// 席数*价格*年
+			order.Member.UserNum = IntValue(5)
+			m.UserNum = order.Member.UserNum
+			total = 2000
+		} else {
+			// 添加席位
+			// 查询多少过期时间
+			var mu model.MemberUser
+			option := &repo.DocSearchOptions{
+				CollectName: repo.CollectionMemberUser,
+				Query:       repo.Map{"parentId": pid, "memberId": m.Id},
+				Project:     []string{"deadTime"},
+			}
+			found, _ := repo.RepoSeachDoc(ctx, option, &mu)
+			if !found {
+				return false, NewError("未购买过企业会员")
+			}
+
+			// 剩余天数
+			day := int(time.Until(mu.DeadTime) / (time.Hour * 24))
+
+			if day <= 0 {
+				day = 1
+			}
+
+			// 计算一天需要多少钱
+			dayPrice := *order.Member.Price / 365
+
+			total = (*order.Member.UserNum) * dayPrice * day
+		}
+		order.Amount = &total
+
+	} else {
+		return false, NewError("请求错误")
+	}
+
+	if apictx.User.Parent != "" {
+		pid, err := primitive.ObjectIDFromHex(apictx.User.Parent)
+		if err != nil {
+			return false, err
+		}
+		order.UserId = &pid
+	} else {
+		order.UserId = &pid
+	}
+
+	order.UserName = apictx.User.Name
+	order.CreateTime = time.Now()
+	order.UpdateTime = time.Now()
+
+	return repo.RepoAddDoc(ctx, repo.CollectionOrder, &order)
+}
+
+func UpdateOrder(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+
+	var order model.Order
+	err := c.ShouldBindJSON(&order)
+	if err != nil {
+		return false, nil
+	}
+	if order.Id == nil {
+		return false, NewError("Id不能为空")
+	}
+
+	ctx := apictx.CreateRepoCtx()
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionOrder,
+		Query:       repo.Map{"_id": order.Id},
+		Project:     []string{"userId"},
+	}
+	found, _ := repo.RepoSeachDoc(ctx, option, &order)
+	if !found {
+		return false, NewError("不存在该订单")
+	}
+
+	if apictx.User.Key == SKU3DUSER {
+		order.Status = &model.Applying
+	}
+
+	// 免费版加一年时间
+	if *order.Status == model.ApplySuccess {
+		// 获取免费版
+		var member model.Member
+		option := &repo.DocSearchOptions{
+			CollectName: repo.CollectionMember,
+			Query:       repo.Map{"level": model.FREE},
+		}
+		found, _ := repo.RepoSeachDoc(ctx, option, &member)
+		if !found {
+			return false, err
+		}
+
+		// 更新增加时间
+		data := repo.Map{"deadTime": time.Now().Add(365 * time.Hour * 24)}
+		res, err := repo.RepoUpdateOneDoc(apictx.CreateRepoCtx(), repo.CollectionMemberUser, bson.M{"parentId": order.UserId, "memberId": member.Id}, data)
+		fmt.Println(res, err)
+		order.Status = &model.ApplySuccess
+	}
+
+	order.UpdateTime = time.Now()
+
+	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionOrder, order.Id.Hex(), &order)
+}
+
+func DeleteOrder(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	idStr := c.Param("id")
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return false, nil
+	}
+
+	// 1、查询是否存在
+	query := repo.Map{"_id": id}
+	if apictx.User.Key == SKU3DUSER {
+		userId, err := primitive.ObjectIDFromHex(apictx.User.ID)
+		if err != nil {
+			return false, err
+		}
+		query["userId"] = userId
+	}
+
+	var order model.Order
+	ctx := apictx.CreateRepoCtx()
+	option := &repo.DocSearchOptions{
+		CollectName: repo.CollectionOrder,
+		Query:       query,
+	}
+	found, _ := repo.RepoSeachDoc(ctx, option, &order)
+	if !found {
+		return false, NewError("不存在该订单")
+	}
+
+	// 2、关闭订单
+	// om := &model.OrderMsg{Id: order.Id.Hex(), PayMode: order.PayMod, ModName: "SKU3D"}
+	// res, err := bus.CloseOrder(om)
+	// if err != nil {
+	// 	return false, err
+	// }
+	// if res.Code != model.SUCCESS.Code {
+	// 	return false, NewError(res.Message)
+	// }
+
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionOrder, idStr)
+}

+ 5 - 0
sku3d/sku3d/api/service-patch.go

@@ -0,0 +1,5 @@
+package api
+
+func RegPatchCrud(router *GinRouter) {
+
+}

+ 118 - 0
sku3d/sku3d/api/service-perm.go

@@ -0,0 +1,118 @@
+package api
+
+import (
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson"
+)
+
+func CreatePermsRouter(router *GinRouter) {
+
+	//分类操作
+	CreateCRUD(router, "/perms", &CRUDOption{
+		Collection: repo.CollectionPerms,
+		NewModel: func(c *gin.Context) interface{} {
+			cat := &model.Perms{}
+			c.ShouldBindJSON(cat)
+			return cat
+		},
+		EmtyModel: func(*gin.Context) interface{} {
+			return &model.Category{}
+		},
+		SearchProject: []string{"group", "children"},
+	})
+
+	//更新个人设计权限
+	router.POST("/feature/person", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		entity := &model.SystemPersonFeatures{}
+		c.ShouldBindJSON(entity)
+		entity.Type = model.Feature_Person
+		entity.UpdateTime = time.Now()
+		return repo.RepoUpsertSetDoc(apictx.CreateRepoCtx(), repo.CollectionSettings, bson.M{"type": model.Feature_Person}, entity)
+	})
+
+	router.GET("/feature/person", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		entity := &model.SystemPersonFeatures{}
+		_, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: repo.CollectionSettings,
+			Query:       repo.Map{"type": model.Feature_Person},
+		}, entity)
+		if err != nil {
+			return nil, err
+		}
+		return entity, nil
+	})
+
+	//更新企业功能设计
+	router.POST("/feature/company", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		entity := &model.SystemCompanyFeatures{}
+		c.ShouldBindJSON(entity)
+		entity.Type = model.Feature_Company
+		entity.UpdateTime = time.Now()
+		return repo.RepoUpsertSetDoc(apictx.CreateRepoCtx(), repo.CollectionSettings, bson.M{"type": model.Feature_Company}, entity)
+	})
+	router.GET("/feature/company", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		entity := &model.SystemCompanyFeatures{}
+		_, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: repo.CollectionSettings,
+			Query:       repo.Map{"type": model.Feature_Company},
+		}, entity)
+		if err != nil {
+			return nil, err
+		}
+		return entity, nil
+	})
+
+	//更新其他功能设计
+	router.POST("/feature/other", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		entity := &model.SystemOtherFeatures{}
+		c.ShouldBindJSON(entity)
+		entity.Type = model.Feature_Other
+		entity.UpdateTime = time.Now()
+		return repo.RepoUpsertSetDoc(apictx.CreateRepoCtx(), repo.CollectionSettings, bson.M{"type": model.Feature_Other}, entity)
+	})
+	router.GET("/feature/other", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		entity := &model.SystemOtherFeatures{}
+		_, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: repo.CollectionSettings,
+			Query:       repo.Map{"type": model.Feature_Other},
+		}, entity)
+		if err != nil {
+			return nil, err
+		}
+		return entity, nil
+	})
+
+	router.GET("/features", func(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+		var entities []*model.SystemOtherFeatures = []*model.SystemOtherFeatures{}
+		err := repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+			CollectName: repo.CollectionSettings,
+			Query:       repo.Map{"type": bson.M{"$in": []string{model.Feature_Other, model.Feature_Company, model.Feature_Person}}},
+		}, &entities)
+
+		if err != nil {
+			return nil, err
+		}
+
+		// out := map[string][]string{}
+		// for _, item := range entities {
+		// 	perms := []string{}
+		// 	if item.Value != nil && item.Value.Features != nil {
+		// 		for _, group := range item.Value.Features {
+		// 			if *group.Enable && len(group.Children) > 0 {
+		// 				for _, ite := range group.Children {
+		// 					if len(ite.Value) > 0 && *ite.Enable {
+		// 						perms = append(perms, ite.Value)
+		// 					}
+		// 				}
+		// 			}
+		// 		}
+		// 	}
+		// 	out[item.Type] = perms
+		// }
+		return entities, nil
+	})
+}

+ 59 - 0
sku3d/sku3d/api/service-project.go

@@ -0,0 +1,59 @@
+package api
+
+import (
+	"sku3dweb/db/model"
+	"sku3dweb/db/repo"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+func ServiceProjectCreate(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	body := &struct {
+		Name string
+	}{}
+	err := c.ShouldBindJSON(body)
+	if err != nil {
+		return nil, NewError("参数解析错误")
+	}
+	if len(body.Name) < 1 {
+		return nil, NewError("名称不能为空")
+	}
+
+	project := &model.Project{
+		Name:       body.Name,
+		CreateTime: time.Now(),
+		Products:   []*model.Product{},
+		Scenes:     []*model.Scene{},
+	}
+	project.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID)
+
+	return repo.AddProject(apictx.CreateRepoCtx(), project)
+}
+
+func ServiceProjectList(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	page, size, query := UtilQueryPageSize(c)
+
+	if query == nil {
+		query = map[string]interface{}{}
+	}
+
+	query["userId"], _ = primitive.ObjectIDFromHex(apictx.User.ID)
+
+	return repo.RepoPageSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionProject,
+		Page:        page,
+		Size:        size,
+		Query:       query,
+		Project:     []string{"name", "thumbnail", "createTime"},
+	})
+}
+
+func ServiceProjectDelete(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	if len(id) < 1 {
+		return nil, NewError("参数不能为空")
+	}
+	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionProject, id)
+}

Some files were not shown because too many files changed in this diff