cxd 9 сар өмнө
commit
2cdde249ea
100 өөрчлөгдсөн 7497 нэмэгдсэн , 0 устгасан
  1. 14 0
      .gitignore
  2. 0 0
      DCbackend/__init__.py
  3. 16 0
      DCbackend/asgi.py
  4. 302 0
      DCbackend/settings.py
  5. 23 0
      DCbackend/urls.py
  6. 41 0
      DCbackend/utils/AliyunOss.py
  7. 8 0
      DCbackend/utils/ErrorMsg.py
  8. 193 0
      DCbackend/utils/ExportDocument.py
  9. 18 0
      DCbackend/utils/MyEncoder.py
  10. 0 0
      DCbackend/utils/__init__.py
  11. 178 0
      DCbackend/utils/common.py
  12. 61 0
      DCbackend/utils/token.py
  13. 16 0
      DCbackend/wsgi.py
  14. 20 0
      Dockerfile
  15. 72 0
      LICENSE
  16. 3 0
      README.md
  17. 32 0
      backend/Controller/AdminRoleController.py
  18. 31 0
      backend/Controller/AuthMenuController.py
  19. 28 0
      backend/Controller/BrandController.py
  20. 40 0
      backend/Controller/CustomerController.py
  21. 28 0
      backend/Controller/DocumentCategoryController.py
  22. 33 0
      backend/Controller/DocumentController.py
  23. 20 0
      backend/Controller/ExportController.py
  24. 28 0
      backend/Controller/FormulaController.py
  25. 28 0
      backend/Controller/ImageCategoryController.py
  26. 28 0
      backend/Controller/ImageController.py
  27. 8 0
      backend/Controller/IndexController.py
  28. 60 0
      backend/Controller/KbmController.py
  29. 31 0
      backend/Controller/KbmDocumentTypeController.py
  30. 22 0
      backend/Controller/MilvusController.py
  31. 40 0
      backend/Controller/MinioController.py
  32. 9 0
      backend/Controller/OperationController.py
  33. 28 0
      backend/Controller/ParamsController.py
  34. 34 0
      backend/Controller/ProductCategoryController.py
  35. 29 0
      backend/Controller/ProductController.py
  36. 28 0
      backend/Controller/ProjectCategoryController.py
  37. 28 0
      backend/Controller/ProjectController.py
  38. 23 0
      backend/Controller/SMSController.py
  39. 52 0
      backend/Controller/SourceDataController.py
  40. 41 0
      backend/Controller/SuperAdminController.py
  41. 28 0
      backend/Controller/SupplierController.py
  42. 41 0
      backend/Controller/TemplateCategoryController.py
  43. 8 0
      backend/Controller/TemplateCategoryUserController.py
  44. 32 0
      backend/Controller/TemplateController.py
  45. 10 0
      backend/Controller/UploadController.py
  46. 70 0
      backend/Controller/UserController.py
  47. 7 0
      backend/Controller/UserOperationController.py
  48. 27 0
      backend/Controller/WechatComplaintController.py
  49. 85 0
      backend/Controller/WechatController.py
  50. 0 0
      backend/Controller/__init__.py
  51. 7 0
      backend/Form/AdminRoleForm.py
  52. 9 0
      backend/Form/AuthMenuForm.py
  53. 9 0
      backend/Form/AuthUserMenuForm.py
  54. 9 0
      backend/Form/BrandForm.py
  55. 9 0
      backend/Form/DocumentCategoryForm.py
  56. 9 0
      backend/Form/DocumentForm.py
  57. 9 0
      backend/Form/FormulaForm.py
  58. 9 0
      backend/Form/ImageCategoryForm.py
  59. 9 0
      backend/Form/ImageForm.py
  60. 9 0
      backend/Form/KbmDocumentTypeForm.py
  61. 12 0
      backend/Form/KnowledgebaseForm.py
  62. 9 0
      backend/Form/ParamsForm.py
  63. 9 0
      backend/Form/ProductCategoryForm.py
  64. 12 0
      backend/Form/ProductForm.py
  65. 9 0
      backend/Form/ProjectCategoryForm.py
  66. 9 0
      backend/Form/ProjectForm.py
  67. 9 0
      backend/Form/SourceDataForm.py
  68. 9 0
      backend/Form/SuperAdminForm.py
  69. 9 0
      backend/Form/SuperSettingsForm.py
  70. 9 0
      backend/Form/SupplierForm.py
  71. 9 0
      backend/Form/TemplateCategoryForm.py
  72. 9 0
      backend/Form/TemplateForm.py
  73. 9 0
      backend/Form/UserForm.py
  74. 9 0
      backend/Form/WechatComplaintForm.py
  75. 9 0
      backend/Form/WechatUserForm.py
  76. 0 0
      backend/Form/__init__.py
  77. 242 0
      backend/Service/AdminRoleService.py
  78. 260 0
      backend/Service/AdminService.py
  79. 189 0
      backend/Service/AuthMenuService.py
  80. 78 0
      backend/Service/BrandService.py
  81. 53 0
      backend/Service/CustomerService.py
  82. 76 0
      backend/Service/DocumentCategoryService.py
  83. 248 0
      backend/Service/DocumentService.py
  84. 309 0
      backend/Service/ExportService.py
  85. 85 0
      backend/Service/FormulaService.py
  86. 78 0
      backend/Service/ImageCategoryService.py
  87. 88 0
      backend/Service/ImageService.py
  88. 23 0
      backend/Service/IndexService.py
  89. 115 0
      backend/Service/KbmDocumentTypeService.py
  90. 1669 0
      backend/Service/KbmService.py
  91. 182 0
      backend/Service/MilvusService.py
  92. 600 0
      backend/Service/MinioService.py
  93. 16 0
      backend/Service/OperationService.py
  94. 94 0
      backend/Service/ParamsService.py
  95. 124 0
      backend/Service/ProductCategoryService.py
  96. 165 0
      backend/Service/ProductService.py
  97. 80 0
      backend/Service/ProjectCategoryService.py
  98. 116 0
      backend/Service/ProjectService.py
  99. 173 0
      backend/Service/SMSService.py
  100. 203 0
      backend/Service/SourceDataService.py

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+.venv
+.idea
+# 忽略 __pycache__ 目录
+__pycache__/
+
+# 忽略 migrations 目录
+backend/__pycache__
+uploads/
+
+# 忽略 __pycache__ 目录
+__pycache__/
+
+# 忽略 .pyc 文件
+*.pyc

+ 0 - 0
DCbackend/__init__.py


+ 16 - 0
DCbackend/asgi.py

@@ -0,0 +1,16 @@
+"""
+ASGI config for DCbackend project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DCbackend.settings')
+
+application = get_asgi_application()

+ 302 - 0
DCbackend/settings.py

@@ -0,0 +1,302 @@
+"""
+Django settings for DCbackend project.
+
+Generated by 'django-admin startproject' using Django 5.0.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.0/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/5.0/ref/settings/
+"""
+import os
+import sys
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-6lnki%2*#2a6t=!#@tt9k75v05mp(6gba)0hegycf_u_hriu8s'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = ['*']
+
+# Application definition
+
+INSTALLED_APPS = [
+    'backend.apps.BackendConfig',
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'corsheaders'
+
+]
+
+MIDDLEWARE = [
+    'corsheaders.middleware.CorsMiddleware',
+    # 'backend.middleware.Authorization',
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware'
+]
+
+CORS_ALLOW_METHODS = (
+    'DELETE',
+    'GET',
+    'OPTIONS',
+    'PATCH',
+    'POST',
+    'PUT',
+    'VIEW',
+)
+
+
+CORS_ALLOWED_ORIGINS = [
+    'http://127.0.0.1',
+    'http://localhost',
+    'http://120.46.190.49',
+]
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_ALLOW_CREDENTIALS = True
+ROOT_URLCONF = 'DCbackend.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [BASE_DIR / 'templates'],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'DCbackend.wsgi.application'
+
+# Database
+# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
+IPINFO = '183.195.216.54'
+#正式知识库redis
+# REDIS_HOST = 'docapi.shqlsy.com'  # 或者你的 Redis 服务器地址
+REDIS_HOST = IPINFO  # 或者你的 Redis 服务器地址
+REDIS_PORT = 6379
+REDIS_PASSWORD = 'infini_rag_flow'  # 替换为你的实际 Redis 密码
+REDIS_DB = 0
+
+#49服务器 redis
+RAYCOS_REDIS_HOST = '120.46.190.49'  # 或者你的 Redis 服务器地址
+RAYCOS_REDIS_PORT = 6379
+RAYCOS_REDIS_PASSWORD = 'Iayq@742295'  # 替换为你的实际 Redis 密码
+RAYCOS_REDIS_DB = 0
+
+#58rabbitmq配置
+RABBITMQ_HOST = IPINFO
+RABBITMQ_PORT = 5672
+RABBITMQ_USER = 'admin'
+RABBITMQ_PASSWORD = 'Gd8XCWEef72GerxE'
+RABBITMQ_QUEUE_NAME = 'document_processing_queue'
+# 正式数据库配置
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': 'yao_data',
+        'USER': 'yao_data',
+        'PASSWORD': 'Gd8XCWEef72GerxE',
+        'HOST': '183.195.216.54',
+        'PORT': '3306',
+    }
+}
+
+# 正式缓存配置
+CACHES = {
+    "default": {
+        "BACKEND": "django_redis.cache.RedisCache",
+        "LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}",
+        "OPTIONS": {
+            "CLIENT_CLASS": "django_redis.client.DefaultClient",
+            "PASSWORD": REDIS_PASSWORD,
+        }
+    }
+}
+# #49数据库配置
+# DATABASES = {
+#     'default': {
+#         'ENGINE': 'django.db.backends.mysql',
+#         'NAME': 'ql_data',
+#         'USER': 'root',
+#         'PASSWORD': 'Gd8XCWEef72GerxE',
+#         'HOST': '120.46.190.49',
+#         'PORT': '3306',
+#     }
+# }
+# #49缓存配置
+# CACHES = {
+#     "default": {
+#         "BACKEND": "django_redis.cache.RedisCache",
+#         "LOCATION": f"redis://{RAYCOS_REDIS_HOST}:{RAYCOS_REDIS_PORT}/{RAYCOS_REDIS_DB}",
+#         "OPTIONS": {
+#             "CLIENT_CLASS": "django_redis.client.DefaultClient",
+#             "PASSWORD": RAYCOS_REDIS_PASSWORD,
+#         }
+#     }
+# }
+# #49rabbitmq配置
+# RABBITMQ_HOST = '120.46.190.49'
+# RABBITMQ_PORT = 5672
+# RABBITMQ_USER = 'admin'
+# RABBITMQ_PASSWORD = 'Gd8XCWEef72GerxE'
+# RABBITMQ_QUEUE_NAME = 'document_processing_queue'
+
+
+# Password validation
+# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+# Internationalization
+# https://docs.djangoproject.com/en/5.0/topics/i18n/
+
+LANGUAGE_CODE = 'zh-hans'
+
+TIME_ZONE = 'Asia/Shanghai'
+
+USE_I18N = True
+
+USE_TZ = True
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/5.0/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+
+# 文件上传部分配置
+MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
+ALLOWED_FILE_TYPES = ['jpg', 'jpeg', 'png', 'gif', 'pdf']
+MAX_UPLOAD_SIZE = 104857600  # 10MB
+# 增加最大上传大小到 10MB (或者你需要的大小)
+DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024  # 10 MB
+
+# 如果你还想增加最大上传文件大小,也可以设置这个
+FILE_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024  # 10 MB
+
+# 填写自己的 access信息
+oss_key_id = 'LTAI5tESB5tEYccrrUjkG7c5'
+oss_key_secret = 'l9CZU6pkWDdQVGbHxwZGdx9mu9cv2U'
+oss_bucket = ' leaseraycos'
+oss_endPoint = 'oss-cn-shanghai.aliyuncs.com'
+oss_url = "https://leaseraycos.oss-cn-shanghai.aliyuncs.com"
+region_id = "cn-shanghai"
+SignName = "上海瑞赢旷视科技"
+
+EMAIL_ACCOUNT = "smtpdm.aliyun.com"
+
+# MinIO 配置
+MINIO_ENDPOINT = f"{IPINFO}:9000"
+MINIO_ACCESS_KEY = "minioadmin"
+MINIO_SECRET_KEY = "minioadmin"
+MINIO_SECURE = False
+
+# Milvus 配置
+MILVUS_HOST = IPINFO
+MILVUS_PORT = "19530"
+MILVUS_USER ="root"
+MILVUS_PASSWORD ="Aplus123"
+VECTOR_DIMENSION = 768
+
+#微信参数
+WECHAT_APPID = "wxa874c6c38e498eb4"
+WECHAT_SECRET = "ebb128f45ad2069b736ef0312b5e656f"
+
+
+
+# 根据操作系统设置日志目录
+if sys.platform.startswith('linux'):
+    LOG_DIR = Path('/www/wwwroot/log/ql_api')
+elif sys.platform == 'win32':
+    LOG_DIR = Path('D:\\ruiyingkuangshi\\log')
+else:
+    LOG_DIR = BASE_DIR / 'logs'  # 默认情况
+
+# 确保日志目录存在
+LOG_DIR.mkdir(parents=True, exist_ok=True)
+
+# 日志文件路径
+LOG_FILE = LOG_DIR / 'debug.log'
+
+# 日志配置
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
+            'style': '{',
+        },
+        'simple': {
+            'format': '{levelname} {message}',
+            'style': '{',
+        },
+    },
+    'handlers': {
+        'file': {
+            'level': 'DEBUG',
+            'class': 'logging.handlers.RotatingFileHandler',
+            'filename': str(LOG_FILE),  # 转换为字符串
+            'maxBytes': 1024 * 1024 * 200,  # 200 MB
+            'backupCount': 5,
+            'formatter': 'verbose',
+        },
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'simple',
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['file', 'console'],
+            'level': 'INFO',
+            'propagate': False,
+        },
+        '': {  # 根记录器
+            'handlers': ['file', 'console'],
+            'level': 'DEBUG',
+        },
+    },
+}

+ 23 - 0
DCbackend/urls.py

@@ -0,0 +1,23 @@
+"""
+URL configuration for DCbackend project.
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/5.0/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, include
+
+
+urlpatterns = [
+    path('', include("backend.urls")),
+]

+ 41 - 0
DCbackend/utils/AliyunOss.py

@@ -0,0 +1,41 @@
+import os
+import sys
+import oss2
+
+# 本地文件夹路径目录
+basedir = 'D:\\'
+# 阿里云存储目录,要保证和下面的project_name相同
+projectList = 'document'
+# 本地文件夹
+dirList = [basedir]
+
+
+class AliyunOss(object):
+
+    def __init__(self):
+        self.access_key_id ='LTAI5tESB5tEYccrrUjkG7c5'  # 从阿里云查询到的 AccessKey 的ID
+        self.access_key_secret = "l9CZU6pkWDdQVGbHxwZGdx9mu9cv2U"  # 从阿里云查询到的 AccessKey 的Secret
+        self.auth = oss2.Auth(self.access_key_id, self.access_key_secret)
+        self.bucket_name = "leaseraycos"  # 阿里云上创建好的Bucket的名称
+        self.endpoint = "oss-cn-shanghai.aliyuncs.com"  # 阿里云从Bucket中查询到的endpoint
+        self.bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
+
+    def put_object_from_file(self, name, file):
+        """
+        :param name: 在阿里云Bucket中要保存的文件名
+        :param file: 本地图片的文件名
+        :return:
+        """
+        self.bucket.put_object_from_file(name, file)
+        return "https://{}.{}/{}".format(self.bucket_name, self.endpoint, name)
+
+
+def uploadFile(file):
+    # remoteName为oss中路径+文件名,file为本地上传文件名称
+    remoteName = projectList + file.replace(basedir, '').replace('\\', '/')
+    print('uploading..', file, 'remoteName', remoteName)
+    result = AliyunOss().put_object_from_file(remoteName, file)
+    # 文件上传成功http状态输出200
+    # print('http status: {0}'.format(result.status))
+    print(result)
+

+ 8 - 0
DCbackend/utils/ErrorMsg.py

@@ -0,0 +1,8 @@
+ErrorDesc = [
+    (1002, "登录失败,请提供正确的用户名和密码"),
+    (2002, "请使用正确的方法获取数据"),
+]
+
+
+def getMessage(code):
+    return ErrorDesc[code]

+ 193 - 0
DCbackend/utils/ExportDocument.py

@@ -0,0 +1,193 @@
+import os
+from datetime import datetime
+from bs4 import BeautifulSoup
+from docx import Document
+from docx.shared import Pt
+from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
+from docx.shared import RGBColor
+from docx.oxml.ns import qn
+from docx.oxml import OxmlElement
+from DCbackend.settings import MEDIA_ROOT
+# 添加标题和书签
+def add_title_with_bookmark(doc, text, style, bookmark_id):
+    paragraph = doc.add_paragraph(text, style=style)
+
+    run = paragraph.add_run()
+    tag = run._r
+    start = OxmlElement('w:bookmarkStart')
+    start.set(qn('w:id'), str(bookmark_id))
+    start.set(qn('w:name'), text)
+    tag.append(start)
+
+    tr = OxmlElement('w:r')
+    tr.text = ''
+    tag.append(tr)
+
+    end = OxmlElement('w:bookmarkEnd')
+    end.set(qn('w:id'), str(bookmark_id))
+    end.set(qn('w:name'), text)
+    tag.append(end)
+
+
+
+
+# 获取文档文件名称
+def getDocxFileName():
+    date = datetime.now().strftime("%Y%m%d%H%M%S")
+    fileName = "output.docx"
+    filename = f"{date}_{fileName}"
+    path = os.path.join(MEDIA_ROOT, filename)
+    return path
+
+
+
+def saveDocFile(title,htmlContent):
+    doc = Document()
+
+
+    # 标记目录的位置
+    soup = BeautifulSoup(htmlContent, 'html.parser')
+    paragraphs = soup.find_all(['h1'])
+    # 将HTML内容转换为Word文档
+    bookMarkId = 0
+    if paragraphs:
+        doc.add_paragraph(title)
+        doc.add_paragraph("目录")
+        catalog_p = doc.add_paragraph('')
+
+        for p in paragraphs:
+            text = p.get_text()
+            style = p.name
+            if style.startswith('h1'):
+                # 添加标题
+                bookMarkId=bookMarkId+1
+                add_title_with_bookmark(doc, text, style='Heading 1', bookmark_id=bookMarkId)
+            else:
+                doc.add_paragraph(text)
+    else:
+        paragraphs = soup.find_all(['div'])
+        for paragraph in paragraphs:
+            doc.add_paragraph(paragraph.getText())
+
+    if bookMarkId>0:
+        # 开始写入目录
+        # 目录开头增加值域,方便手动更新整个目录。自动生成的目录标题包含在值域中。
+        toc_paragraph = catalog_p.insert_paragraph_before()  # 在标记的目录位置前添加段落
+        r1 = toc_paragraph.add_run()
+        toc_field = OxmlElement('w:fldChar')
+        toc_field.set(qn('w:fldCharType'), 'begin')
+        r1._r.append(toc_field)
+
+        r2 = toc_paragraph.add_run()
+        toc_field = OxmlElement('w:instrText')
+        toc_field.set(qn('xml:space'), 'preserve')
+        toc_field.text = 'TOC \\o "1-3" \\h \\z '
+        r2._r.append(toc_field)
+
+        r3 = toc_paragraph.add_run()
+        toc_field = OxmlElement('w:fldChar')
+        toc_field.set(qn('w:fldCharType'), 'separate')
+        r3._r.append(toc_field)
+
+        # 自动生成目录内容,不包含页码
+        for paragraph in doc.paragraphs:
+            if 'Heading' in paragraph.style.name:
+
+                b = paragraph._element.findall('.//' + qn('w:bookmarkStart'))
+                bookmark_name = b[0].get(qn('w:name'))
+                text = paragraph.text
+                level = int(paragraph.style.name[-1])
+                # print(text, bookmark_name)
+
+                toc_paragraph = catalog_p.insert_paragraph_before(style='Normal')
+                # 二级标题设置缩进
+                if level == 2:
+                    toc_paragraph.paragraph_format.first_line_indent = Pt(24)
+
+                # 设置制表符,可显示页码前的"…………"
+                tabs = OxmlElement('w:tabs')
+                tab1 = OxmlElement('w:tab')
+                tab1.set(qn('w:val'), "left")
+                tab1.set(qn('w:leader'), "dot")
+                tab1.set(qn('w:pos'), "8400")
+                tabs.append(tab1)
+                toc_paragraph._p.pPr.append(tabs)
+                # toc_paragraph若未设定style,toc_paragraph._p没有pPr属性,需注释前一句代码,使用以下语句
+                # pPr = OxmlElement('w:pPr')
+                # pPr.append(tabs)
+                # toc_paragraph._p.append(pPr)
+
+                hyperlink = OxmlElement('w:hyperlink')
+                hyperlink.set(qn('w:anchor'), bookmark_name)
+                hyperlink.set(qn('w:history'), '1')
+
+                hr1 = OxmlElement('w:r')
+                rPr = OxmlElement('w:rPr')
+                rStyle = OxmlElement('w:rStyle')
+                rStyle.set(qn('w:val'), "a4")
+                rPr.append(rStyle)
+                hr1.text = text
+                hr1.append(rPr)
+                hyperlink.append(hr1)
+
+                hr2 = OxmlElement('w:r')
+                rPr = OxmlElement('w:rPr')
+                fldChar = OxmlElement('w:fldChar')
+                fldChar.set(qn('w:fldCharType'), 'begin')
+                hr2.append(rPr)
+                hr2.append(fldChar)
+                hyperlink.append(hr2)
+
+                hr3 = OxmlElement('w:r')
+                rPr = OxmlElement('w:rPr')
+                instrText = OxmlElement('w:instrText')
+                instrText.set(qn('xml:space'), 'preserve')
+                instrText.text = ' PAGEREF {} \h '.format(bookmark_name)
+                hr3.append(rPr)
+                hr3.append(instrText)
+                hyperlink.append(hr3)
+
+                hr4 = OxmlElement('w:r')
+                rPr = OxmlElement('w:rPr')
+                fldChar = OxmlElement('w:fldChar')
+                fldChar.set(qn('w:fldCharType'), 'separate')
+                hr4.append(rPr)
+                hr4.append(fldChar)
+                hyperlink.append(hr4)
+
+                hrt = OxmlElement('w:r')
+                tab = OxmlElement('w:tab')
+                hrt.append(tab)
+                hyperlink.append(hrt)
+
+                hr5 = OxmlElement('w:r')
+                rPr = OxmlElement('w:rPr')
+                hr5.text = ''
+                hr5.append(rPr)
+                hyperlink.append(hr5)
+
+                hr6 = OxmlElement('w:r')
+                rPr = OxmlElement('w:rPr')
+                fldChar = OxmlElement('w:fldChar')
+                fldChar.set(qn('w:fldCharType'), 'end')
+                hr6.append(rPr)
+                hr6.append(fldChar)
+                hyperlink.append(hr6)
+
+                toc_paragraph._p.append(hyperlink)
+
+        # 目录结尾的值域
+        toc_paragraph = catalog_p.insert_paragraph_before()
+        r4 = toc_paragraph.add_run()
+        toc_field = OxmlElement('w:fldChar')
+        toc_field.set(qn('w:fldCharType'), 'end')
+        r4._r.append(toc_field)
+        # 分页
+        break_page_p = catalog_p.insert_paragraph_before()
+        break_page_p.add_run().add_break(WD_BREAK.PAGE)
+
+    fileName=getDocxFileName()
+
+    doc.save(fileName)
+
+    return fileName

+ 18 - 0
DCbackend/utils/MyEncoder.py

@@ -0,0 +1,18 @@
+import datetime
+import json
+
+
+class MyEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, datetime.datetime):
+            return obj.strftime("%Y-%m-%d %H:%M:%S")
+        if isinstance(obj, bytes):
+            return str(obj, encoding='utf-8')
+        if isinstance(obj, int):
+            return int(obj)
+        elif isinstance(obj, float):
+            return float(obj)
+        elif isinstance(obj, bool):
+            return str(obj, encoding='utf-8')
+        else:
+            return super(MyEncoder, self).default(obj)

+ 0 - 0
DCbackend/utils/__init__.py


+ 178 - 0
DCbackend/utils/common.py

@@ -0,0 +1,178 @@
+import json
+import os
+from datetime import datetime
+from xml.dom.minidom import Document
+
+import requests
+
+from django.core import paginator
+from django.http import HttpResponse, JsonResponse
+from DCbackend.settings import MEDIA_ROOT, oss_key_id, oss_key_secret, oss_endPoint, oss_bucket, oss_url
+
+import os
+import oss2
+
+from DCbackend.utils.MyEncoder import MyEncoder
+
+
+# def success(data):
+#     result = {"status": 200, "message": "success", "data": data}
+#     return HttpResponse(json.dumps(result, cls=MyEncoder, indent=4), content_type="application/json")
+def success(data, message="success"):
+    result = {
+        "status": 200,
+        "message": message,
+        "data": data
+    }
+    return HttpResponse(
+        json.dumps(result, cls=MyEncoder, indent=4),
+        content_type="application/json"
+    )
+
+def fail(message):
+    return HttpResponse(JsonResponse({"status": 999, "message": message}), content_type="application/json")
+
+
+def fail(message=None):
+    if message is None:
+        message = []
+    return HttpResponse(JsonResponse({"status": 999, "message": message}), content_type="application/json")
+
+
+def pageData(page, pageSize, totalPage, totalRecord, dataList):
+    return {"page": page, "pageSize": pageSize, "totalPage": totalPage, "totalRecord": totalRecord,
+            "dataList": dataList}
+
+
+def handle_uploaded_file(file):
+    date = datetime.now().strftime("%Y%m%d%H%M%S")
+    filename = f"{date}_{file.name}"
+    if not os.path.exists(MEDIA_ROOT):
+        os.mkdir(MEDIA_ROOT)
+    path = os.path.join(MEDIA_ROOT, filename)
+
+    with open(path, 'wb+') as destination:
+        for chunk in file.chunks():
+            destination.write(chunk)
+    return path
+
+
+# 上传文件到 OSS
+# 这里的file_path就是上面我们保存到local_url容器里面的dst
+def oss_upload_file(file_path):
+    # 通过basename方法得到路径最后的文件名,也就是图片名(火影忍者.jpg)
+    file_name = os.path.basename(file_path)
+
+    # 构造oss中的路径,我这里就是:anime/火影忍者.jpg
+    # 创建 OSS 链接
+    auth = oss2.Auth(oss_key_id, oss_key_secret)
+    bucket = oss2.Bucket(auth, oss_endPoint, oss_bucket)
+
+    oss_path = "document/" + file_name
+    # 上传文件,以二进制方式(rb)打开图片上传
+    with open(file_path, 'rb') as file_obj:
+        result = bucket.put_object(oss_path, file_obj)
+    return oss_url + "/" + oss_path
+    # return result
+
+
+def getHttpPost():
+    url = "https://www.h3yun.com/OpenApi/Invoke"
+    payload = {
+        "ActionName": "LoadBizObjects",
+        "SchemaCode": "isaxp2no91kwgggtzmnfsmf90",
+        "Filter": "{\"FromRowNum\":   0,\"RequireCount\": false,\"ReturnItems\": [],   \"SortByCollection\": [],\"ToRowNum\": 500,   \"Matcher\": { \"Type\": \"And\",   \"Matchers\": []}}"
+    }
+    headers = {
+        "EngineCode": "bddua5a6od64p2nu",
+        "EngineSecret": "Az24wdw8f1dgZw5j7UeGivgcJuRLuaxqQ0banHM6YLkD/oa2uNIH8g==",
+        "Content-Type": "application/json",
+        'content-type': "application/json"
+    }
+    response = requests.request("POST", url, json=payload, headers=headers)
+    if response.status_code == 200:
+        data = response.json()
+        content = json.dumps(data, cls=MyEncoder, indent=4)
+        filename = "customer.txt"
+        if not os.path.exists(MEDIA_ROOT):
+            os.mkdir(MEDIA_ROOT)
+        path = os.path.join(MEDIA_ROOT, filename)
+        with open(path, "w", encoding='utf-8') as file:
+            file.write(content)
+
+        with open(path, "r", encoding='utf-8') as openFile:
+            fileContent = openFile.read()
+            fileContent = fileContent.replace('\'', '\"')
+            fileContent = fileContent.replace('true', 'True')
+            fileContent = fileContent.replace('false', 'False')
+            fileContent = fileContent.replace('null', '""')
+            item = eval(fileContent)
+            result = []
+            if item['Successful']:
+                for it in item['ReturnData']['BizObjectArray']:
+                    result.append({
+                        "objectId": it["ObjectId"],
+                        "Name": it['Name'],
+                        "creator": it["OwnerId"],
+                        "SeqNo": it['SeqNo'],
+                        "mobile": it["F0000005"],
+                        'customerName': it['F0000007'],
+                        'enName': it['F0000002'],
+                        'CreatedTime': it['CreatedTime'],
+                        'ModifiedTime': it['ModifiedTime'],
+                        'step': it['F0000010'],
+                        'address': it['F0000003'],
+                        'contact': it['F0000008'],
+                    })
+            # js = json.loads(json.dumps(temp,cls=MyEncoder, indent=4))
+            return result
+        return
+    else:
+        return False
+
+
+def formatDateTime(time):
+    if time == "":
+        return datetime.now()
+    date_object = datetime.datetime.strptime(time, "%Y/%m/%d %H:%M:%S")
+    formatted_date = date_object.strftime("%Y-%m-%d %H:%M:%S")
+    return formatted_date
+
+
+def saveDocFile(fileName, htmlText):
+    date = datetime.now().strftime("%Y%m%d%H%M%S")
+    filename = f"{date}_{fileName}"
+    # if not os.path.exists(MEDIA_ROOT):
+    #     os.mkdir(MEDIA_ROOT)
+    path = os.path.join(MEDIA_ROOT, filename)
+    # output = pypandoc.convert_text(htmlText, 'docx', format='html')
+    # doc = Document()
+    # doc.add_paragraph(output)
+    # doc.save(path)
+    return path
+
+
+# 获取文档文件名称
+def getDocxFileName(ext="docx"):
+    date = datetime.now().strftime("%Y%m%d%H%M%S")
+    fileName = "output." + ext
+    filename = f"{date}_{fileName}"
+    path = os.path.join(MEDIA_ROOT, filename)
+    return path
+
+
+def downloadFile(id, fileUrl):
+    local_filename = os.path.join(MEDIA_ROOT, str(id)+".xlsx")
+    if os.path.exists(local_filename):
+        return local_filename
+
+    response = requests.get(fileUrl)
+
+    # Check if the request was successful
+    if response.status_code == 200:
+        # Open the local file in binary write mode and write the content of the remote file to it
+        with open(local_filename, 'wb') as file:
+            file.write(response.content)
+        return local_filename
+    else:
+        return ""

+ 61 - 0
DCbackend/utils/token.py

@@ -0,0 +1,61 @@
+import time
+from django.core import signing
+import hashlib
+
+HEADER = {'typ': 'JWP', 'alg': 'default'}
+KEY = "J&Y"
+SALT = "www.jmd-mall.cn"
+
+
+def encrypt(obj):
+    """加密:signing 加密 and Base64 编码"""
+    value = signing.dumps(obj, key=KEY, salt=SALT)
+    value = signing.b64_encode(value.encode()).decode()
+    return value
+
+
+def decrypt(src):
+    """解密:Base64 解码 and signing 解密"""
+    src = signing.b64_decode(src.encode()).decode()
+    raw = signing.loads(src, key=KEY, salt=SALT)
+    return raw
+
+
+def create_token(username):
+    """生成token信息"""
+    # 1. 加密头信息
+    header = encrypt(HEADER)
+    # 2. 构造Payload(有效期14天)
+    payload = {"username": username, "iat": time.time(),
+               "exp": time.time() + 1209600.0}
+    payload = encrypt(payload)
+    # 3. MD5 生成签名
+    md5 = hashlib.md5()
+    md5.update(("%s.%s" % (header, payload)).encode())
+    signature = md5.hexdigest()
+    token = "%s.%s.%s" % (header, payload, signature)
+    return token
+
+
+def get_payload(token):
+    """解析 token 获取 payload 数据"""
+    payload = str(token).split('.')[1]
+    payload = decrypt(payload)
+    return payload
+
+
+def get_username(token):
+    """解析 token 获取 username"""
+    payload = get_payload(token)
+    return payload['username']
+
+
+def get_exp_time(token):
+    """解析 token 获取过期时间"""
+    payload = get_payload(token)
+    return payload['exp']
+
+
+def check_token(username, token):
+    """验证 token:检查 username 和 token 是否一致且未过期"""
+    return get_username(token) == username and get_exp_time(token) > time.time()

+ 16 - 0
DCbackend/wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for DCbackend project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DCbackend.settings')
+
+application = get_wsgi_application()

+ 20 - 0
Dockerfile

@@ -0,0 +1,20 @@
+# 使用 Docker 官方的 Python 3.12 镜像
+FROM python:3.12
+
+# 创建一个新用户 pythonuser
+RUN adduser pythonuser
+
+# 切换到 pythonuser 用户
+USER pythonuser
+
+# 设置工作目录
+WORKDIR /docker
+
+# 复制项目文件到工作目录,并设置文件所有者为 pythonuser
+COPY --chown=pythonuser:pythonuser . /docker
+
+# 复制 base.py 文件到指定位置
+COPY ./base.py /usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py
+
+# 启动 Django 开发服务器
+CMD ["python", "manage.py", "runserver", "0.0.0.0:8084"]

+ 72 - 0
LICENSE

@@ -0,0 +1,72 @@
+Apache License 
+Version 2.0, January 2004 
+http://www.apache.org/licenses/
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License"); 
+you may not use this file except in compliance with the License. 
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software 
+distributed under the License is distributed on an "AS IS" BASIS, 
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+See the License for the specific language governing permissions and 
+limitations under the License.

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+# api
+
+接口

+ 32 - 0
backend/Controller/AdminRoleController.py

@@ -0,0 +1,32 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.AdminRoleService import AdminRoleService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return AdminRoleService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return AdminRoleService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return AdminRoleService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return AdminRoleService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return AdminRoleService.Search(reqeust)
+
+@require_http_methods(['POST'])
+def selectInfo(reqeust):
+    return AdminRoleService.selectInfo(reqeust)

+ 31 - 0
backend/Controller/AuthMenuController.py

@@ -0,0 +1,31 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.AuthMenuService import AuthMenuService
+
+@require_http_methods(['POST'])
+def menuList(reqeust):
+    return AuthMenuService.menuList(reqeust)
+
+@require_http_methods(['POST'])
+def search(reqeust):
+    return AuthMenuService.search(reqeust)
+
+@require_http_methods(['POST'])
+def create(reqeust):
+    return AuthMenuService.create(reqeust)
+
+@require_http_methods(['POST'])
+def update(reqeust):
+    return AuthMenuService.update(reqeust)
+
+@require_http_methods(['POST'])
+def delete(reqeust):
+    return AuthMenuService.delete(reqeust)
+
+@require_http_methods(['POST'])
+def info(reqeust):
+    return AuthMenuService.info(reqeust)
+
+@require_http_methods(['POST'])
+def listpage(reqeust):
+    return AuthMenuService.listpage(reqeust)

+ 28 - 0
backend/Controller/BrandController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.BrandService import BrandService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return BrandService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return BrandService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return BrandService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return BrandService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return BrandService.Search(reqeust)

+ 40 - 0
backend/Controller/CustomerController.py

@@ -0,0 +1,40 @@
+import json
+
+from django.views.decorators.http import require_http_methods
+from django.utils.timezone import make_aware
+
+from DCbackend.utils import MyEncoder
+from DCbackend.utils.common import getHttpPost, success, formatDateTime
+from backend.Service.CustomerService import CustomerService
+from backend.Service.ProjectService import ProjectService
+from backend.models import Customer
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return CustomerService.Search(reqeust)
+
+
+@require_http_methods(['POST'])
+def sync(reqeust):
+    saveData()
+    return success(True)
+
+
+def saveData():
+    data = getHttpPost()
+    for item in data:
+        customer = Customer.objects.filter(code=item['objectId']).first()
+        if not customer:  # 如果存在,则直接更新
+            customer = Customer()
+        customer.code = item['objectId']
+        customer.name = item['Name']
+        customer.mobile = item['mobile']
+        customer.en_name=item['enName']
+        customer.contact = item['contact']
+        customer.address = item['address']
+        customer.other = item['creator']
+        customer.create_time = formatDateTime(item['CreatedTime'])
+        customer.status = 5
+        customer.save()
+    return True

+ 28 - 0
backend/Controller/DocumentCategoryController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.DocumentCategoryService import DocumentCategoryService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return DocumentCategoryService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return DocumentCategoryService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return DocumentCategoryService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return DocumentCategoryService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return DocumentCategoryService.Search(reqeust)

+ 33 - 0
backend/Controller/DocumentController.py

@@ -0,0 +1,33 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.DocumentService import DocumentService
+from backend.Service.TemplateService import TemplateService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return DocumentService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return DocumentService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return DocumentService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return DocumentService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return DocumentService.Search(reqeust)
+
+@require_http_methods(['POST'])
+def pageDocument(reqeust):
+    return DocumentService.pageDocument(reqeust)

+ 20 - 0
backend/Controller/ExportController.py

@@ -0,0 +1,20 @@
+# from django.views.decorators.http import require_http_methods
+#
+# from backend.Service.ExportService import ExportService
+#
+# @require_http_methods(['POST'])
+# def htmlPDF(request):
+#     return ExportService.htmlPDF(request)
+#
+# @require_http_methods(['POST'])
+# def htmlPDFWord(request):
+#     return ExportService.htmlPDFWord(request)
+#
+#
+# @require_http_methods(['POST'])
+# def htmlWord(request):
+#     return ExportService.htmlWord(request)
+#
+#
+#
+#

+ 28 - 0
backend/Controller/FormulaController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.FormulaService import FormulaService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return FormulaService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return FormulaService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return FormulaService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return FormulaService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return FormulaService.Search(reqeust)

+ 28 - 0
backend/Controller/ImageCategoryController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ImageCategoryService import ImageCategoryService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ImageCategoryService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ImageCategoryService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ImageCategoryService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ImageCategoryService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ImageCategoryService.Search(reqeust)

+ 28 - 0
backend/Controller/ImageController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ImageService import ImageService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ImageService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ImageService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ImageService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ImageService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ImageService.Search(reqeust)

+ 8 - 0
backend/Controller/IndexController.py

@@ -0,0 +1,8 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.IndexService import IndexService
+
+
+@require_http_methods(['POST'])
+def statistics(reqeust):
+    return IndexService.statistics(reqeust)

+ 60 - 0
backend/Controller/KbmController.py

@@ -0,0 +1,60 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.KbmService import KbmService
+
+#查询所有bucket信息
+@require_http_methods(['POST'])
+def selectBucketInfo(request):
+    return KbmService.selectBucketInfo(request)
+
+#获取bucket内信息
+@require_http_methods(['POST'])
+def getFileInfo(request):
+    return KbmService.getFileInfo(request)
+
+#修改名称
+@require_http_methods(['POST'])
+def updateName(request):
+    return KbmService.updateName(request)
+#逻辑删除
+@require_http_methods(['POST'])
+def deleteDocument(request):
+    return KbmService.deleteDocument(request)
+
+#获取文件的url
+@require_http_methods(['POST'])
+def getUrl(request):
+    return KbmService.getUrl(request)
+#分片文件
+@require_http_methods(['POST'])
+def analysis(request):
+    return KbmService.analysis(request)
+
+@require_http_methods(['POST'])
+def searchTaskInfo(request):
+    return KbmService.searchTaskInfo(request)
+
+@require_http_methods(['POST'])
+def deleteBucket(request):
+    return KbmService.deleteBucket(request)
+
+@require_http_methods(['POST'])
+def getRunStatus(request):
+    return KbmService.getRunStatus(request)
+
+@require_http_methods(['POST'])
+def batchAnalysis(request):
+    return KbmService.batchAnalysis(request)
+
+@require_http_methods(['POST'])
+def getSuffixName(request):
+    return KbmService.getSuffixName(request)
+
+
+@require_http_methods(['POST'])
+def batchMove(request):
+    return KbmService.batchMove(request)
+
+@require_http_methods(['POST'])
+def updateKbm(request):
+    return KbmService.updateKbm(request)

+ 31 - 0
backend/Controller/KbmDocumentTypeController.py

@@ -0,0 +1,31 @@
+from django.views.decorators.http import require_http_methods
+
+from django.views.decorators.http import require_http_methods
+from backend.Service.KbmDocumentTypeService import KbmDocumentTypeService
+
+@require_http_methods(['POST'])
+def insertType(request):
+    return KbmDocumentTypeService.insertType(request)
+
+@require_http_methods(['POST'])
+def updateType(request):
+    return KbmDocumentTypeService.updateType(request)
+
+@require_http_methods(['POST'])
+def selectTypeList(request):
+    return KbmDocumentTypeService.selectTypeList(request)
+
+@require_http_methods(['POST'])
+def delete(request):
+    return KbmDocumentTypeService.delete(request)
+
+@require_http_methods(['POST'])
+def Info(request):
+    return KbmDocumentTypeService.Info(request)
+
+@require_http_methods(['POST'])
+def selectType0(request):
+    return KbmDocumentTypeService.selectType0(request)
+
+
+

+ 22 - 0
backend/Controller/MilvusController.py

@@ -0,0 +1,22 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.MilvusService import MilvusService
+
+@require_http_methods(['POST'])
+def list_all_collections(request):
+    return MilvusService.list_all_collections(request)
+
+@require_http_methods(['POST'])
+def delete_collection(request):
+    return MilvusService.delete_collection(request)
+
+@require_http_methods(['POST'])
+def delete_milvus_data(request):
+    return MilvusService.delete_milvus_data(request)
+
+
+@require_http_methods(['POST'])
+def getMinioURl(request):
+    return MilvusService.getMinioURl(request)
+
+

+ 40 - 0
backend/Controller/MinioController.py

@@ -0,0 +1,40 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.MinioService import MinioService
+
+
+@require_http_methods(['POST'])
+def post(reqeust):
+    return MinioService.post(reqeust)
+
+@require_http_methods(['POST'])
+def nameGetUrl(reqeust):
+    return MinioService.nameGetUrl(reqeust)
+
+@require_http_methods(['POST'])
+def deleteFile(reqeust):
+    return MinioService.deleteFile(reqeust)
+
+@require_http_methods(['POST'])
+def listBuckets(reqeust):
+    return MinioService.listBuckets(reqeust)
+
+@require_http_methods(['POST'])
+def createBucket(reqeust):
+    return MinioService.createBucket(reqeust)
+
+@require_http_methods(['POST'])
+def getBucketContents(reqeust):
+    return MinioService.getBucketContents(reqeust)
+
+@require_http_methods(['POST'])
+def renameFile(reqeust):
+    return MinioService.renameFile(reqeust)
+
+@require_http_methods(['POST'])
+def deleteBucket(reqeust):
+    return MinioService.deleteBucket(reqeust)
+
+@require_http_methods(['POST'])
+def readPdfSlice(reqeust):
+    return MinioService.readPdfSlice(reqeust)

+ 9 - 0
backend/Controller/OperationController.py

@@ -0,0 +1,9 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.OperationService import OperationService
+
+
+#
+@require_http_methods(['POST'])
+def selectOperation(reqeust):
+    return OperationService.selectOperation(reqeust)

+ 28 - 0
backend/Controller/ParamsController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ParamsService import ParamsService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ParamsService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ParamsService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ParamsService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ParamsService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ParamsService.Search(reqeust)

+ 34 - 0
backend/Controller/ProductCategoryController.py

@@ -0,0 +1,34 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ProductCategoryService import ProductCategoryService
+from backend.Service.ProjectCategoryService import ProjectCategoryService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ProductCategoryService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ProductCategoryService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ProductCategoryService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ProductCategoryService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ProductCategoryService.Search(reqeust)
+
+
+@require_http_methods(['POST'])
+def All(reqeust):
+    return ProductCategoryService.getAllCategories()

+ 29 - 0
backend/Controller/ProductController.py

@@ -0,0 +1,29 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.FormulaService import FormulaService
+from backend.Service.ProductService import ProductService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ProductService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ProductService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ProductService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ProductService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ProductService.Search(reqeust)

+ 28 - 0
backend/Controller/ProjectCategoryController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ProjectCategoryService import ProjectCategoryService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ProjectCategoryService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ProjectCategoryService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ProjectCategoryService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ProjectCategoryService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ProjectCategoryService.Search(reqeust)

+ 28 - 0
backend/Controller/ProjectController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ProjectService import ProjectService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return ProjectService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return ProjectService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return ProjectService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return ProjectService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return ProjectService.Search(reqeust)

+ 23 - 0
backend/Controller/SMSController.py

@@ -0,0 +1,23 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.SMSService import SMSService
+
+
+@require_http_methods(['POST'])
+def tailPhone(request):
+    return SMSService.tailPhone(request)
+@require_http_methods(['POST'])
+def codeCheckout(request):
+    return SMSService.codeCheckout(request)
+@require_http_methods(['POST'])
+def updatePassword(request):
+    return SMSService.updatePassword(request)
+
+@require_http_methods(['POST'])
+def getTemplates(request):
+    return SMSService.getTemplates(request)
+
+@require_http_methods(['POST'])
+def getSMScount(request):
+    return SMSService.getSMScount(request)
+

+ 52 - 0
backend/Controller/SourceDataController.py

@@ -0,0 +1,52 @@
+from django.core.paginator import Paginator
+from django.http import JsonResponse
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.SourceDataService import SourceDataService
+from backend.models import SourceCategory
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return SourceDataService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return SourceDataService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return SourceDataService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return SourceDataService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return SourceDataService.Search(reqeust)
+
+
+@require_http_methods(['POST'])
+def find(reqeust):
+    return SourceDataService.findData(reqeust)
+
+@require_http_methods(['POST'])
+def Categories(request):
+    return SourceDataService.Categories(request)
+
+@require_http_methods(['POST'])
+def Catcreat(request):
+    return SourceDataService.Catcreat(request)
+
+@require_http_methods(['POST'])
+def info(request):
+    return SourceDataService.info(request)
+
+@require_http_methods(['POST'])
+def updatetype(request):
+    return SourceDataService.updatetype(request)

+ 41 - 0
backend/Controller/SuperAdminController.py

@@ -0,0 +1,41 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.SuperAdminService import SuperAdminService
+
+@require_http_methods(['POST'])
+def createPassword(request):
+    return SuperAdminService.createPassword(request)
+
+@require_http_methods(['POST'])
+def createAccount(request):
+    return SuperAdminService.createAccount(request)
+
+@require_http_methods(['POST'])
+def superLogin(request):
+    return SuperAdminService.superLogin(request)
+
+@require_http_methods(['POST'])
+def selcetSetting(request):
+    return SuperAdminService.selcetSetting(request)
+
+@require_http_methods(['POST'])
+def insertSetting(request):
+    return SuperAdminService.insertSetting(request)
+
+@require_http_methods(['POST'])
+def updateSettings(request):
+    return SuperAdminService.updateSettings(request)
+
+@require_http_methods(['POST'])
+def deleteSettings(request):
+    return SuperAdminService.deleteSettings(request)
+
+@require_http_methods(['POST'])
+def getSuperMenu(request):
+    return SuperAdminService.getSuperMenu(request)
+
+
+
+
+
+

+ 28 - 0
backend/Controller/SupplierController.py

@@ -0,0 +1,28 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.SupplierService import SupplierService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return SupplierService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return SupplierService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return SupplierService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return SupplierService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return SupplierService.Search(reqeust)

+ 41 - 0
backend/Controller/TemplateCategoryController.py

@@ -0,0 +1,41 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.SupplierService import SupplierService
+from backend.Service.TemplateCategoryService import TemplateCategoryService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return TemplateCategoryService.Create(reqeust)
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return TemplateCategoryService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return TemplateCategoryService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return TemplateCategoryService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return TemplateCategoryService.Search(reqeust)
+
+@require_http_methods(['POST'])
+def allInfo(reqeust):
+    return TemplateCategoryService.allInfo(reqeust)
+@require_http_methods(['POST'])
+def allList(reqeust):
+    return TemplateCategoryService.allList(reqeust)
+
+@require_http_methods(['POST'])
+def treeCategory(reqeust):
+    return TemplateCategoryService.treeCategory(reqeust)
+
+

+ 8 - 0
backend/Controller/TemplateCategoryUserController.py

@@ -0,0 +1,8 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.TemplateCategoryUserService import TemplateCategoryUserService
+
+
+@require_http_methods(['POST'])
+def search(reqeust):
+    return TemplateCategoryUserService.search(reqeust)

+ 32 - 0
backend/Controller/TemplateController.py

@@ -0,0 +1,32 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.TemplateService import TemplateService
+
+
+@require_http_methods(['POST'])
+def Create(reqeust):
+    return TemplateService.Create(reqeust)
+
+
+@require_http_methods(['POST'])
+def Update(reqeust):
+    return TemplateService.Update(reqeust)
+
+
+@require_http_methods(['POST'])
+def Delete(reqeust):
+    return TemplateService.Delete(reqeust)
+
+
+@require_http_methods(['POST'])
+def Info(reqeust):
+    return TemplateService.Info(reqeust)
+
+
+@require_http_methods(['POST'])
+def Search(reqeust):
+    return TemplateService.Search(reqeust)
+
+@require_http_methods(['POST'])
+def pageTemplate(reqeust):
+    return TemplateService.pageTemplate(reqeust)

+ 10 - 0
backend/Controller/UploadController.py

@@ -0,0 +1,10 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.ProjectService import ProjectService
+from backend.Service.UploadService import UploadService
+
+
+@require_http_methods(['POST'])
+def File(reqeust):
+    return UploadService.File(reqeust)
+

+ 70 - 0
backend/Controller/UserController.py

@@ -0,0 +1,70 @@
+from django.shortcuts import render
+from django.views.decorators.http import require_http_methods
+
+from DCbackend.utils.common import fail
+from backend.Service.AdminService import AdminService
+
+
+# Create your views here.
+
+def index(request):
+    return fail("接口地址,请勿直接访问")
+
+
+# 用户登录
+@require_http_methods(['POST'])
+def login(request):
+    return AdminService.login(request)
+
+@require_http_methods(['POST'])
+def login1(request):
+    return AdminService.login1(request)
+# 用户登录
+@require_http_methods(['POST'])
+def logout(request):
+    return AdminService.logout(request)
+
+
+# 用户登录
+@require_http_methods(['POST'])
+def currentUser(request):
+    return AdminService.getAdminByToken(request)
+
+
+# 用户登录
+@require_http_methods(['POST'])
+def getUserInfo(request):
+    return AdminService.getAdminInfo(request)
+
+
+# 创建用户
+@require_http_methods(['POST'])
+def create(request):
+    return AdminService.create(request)
+
+
+# 搜索列表
+@require_http_methods(['POST'])
+def search(request):
+    return AdminService.search(request)
+
+
+# 搜索列表
+@require_http_methods(['POST'])
+def update(request):
+    return AdminService.update(request)
+
+# 搜索列表
+@require_http_methods(['POST'])
+def updateProfile(request):
+    return AdminService.updateProfile(request)
+
+
+# 搜索列表
+@require_http_methods(['POST'])
+def changePassword(request):
+    return AdminService.changePassword(request)
+
+@require_http_methods(['POST'])
+def delete(request):
+    return AdminService.delete(request)

+ 7 - 0
backend/Controller/UserOperationController.py

@@ -0,0 +1,7 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.UserOperationService import UserOperationService
+
+@require_http_methods(['POST'])
+def selectUserOperation(request):
+    return UserOperationService.selectUserOperation(request)

+ 27 - 0
backend/Controller/WechatComplaintController.py

@@ -0,0 +1,27 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.WechatComplaintService import WechatComplaintService
+
+
+@require_http_methods(['POST'])
+def insertComplaint(request):
+    return WechatComplaintService.insertComplaint(request)
+
+@require_http_methods(['POST'])
+def deleteComplaint(request):
+    return WechatComplaintService.deleteComplaint(request)
+
+@require_http_methods(['POST'])
+def getComplaintList(request):
+    return WechatComplaintService.getComplaintList(request)
+
+@require_http_methods(['POST'])
+def getComplaintInfo(request):
+    return WechatComplaintService.getComplaintInfo(request)
+
+@require_http_methods(['POST'])
+def updateComplaint(request):
+    return WechatComplaintService.updateComplaint(request)
+
+
+

+ 85 - 0
backend/Controller/WechatController.py

@@ -0,0 +1,85 @@
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.WechatService import WechatService
+from django.views.decorators.http import require_http_methods
+
+from backend.Service.WechatService import WechatService
+
+@require_http_methods(['POST'])
+def insertInfo(request):
+    return WechatService.insertInfo(request)
+
+@require_http_methods(['POST'])
+def getInfoList(request):
+    return WechatService.getInfoList(request)
+
+@require_http_methods(['POST'])
+def updateUserInfo(request):
+    return WechatService.updateUserInfo(request)
+
+@require_http_methods(['POST'])
+def updateNikeName(request):
+    return WechatService.updateNikeName(request)
+
+
+@require_http_methods(['POST'])
+def toExamine(request):
+    return WechatService.to_examine(request)
+
+
+@require_http_methods(['POST'])
+def deleteUserInfo(request):
+    return WechatService.deleteUserInfo(request)
+
+@require_http_methods(['POST'])
+def get_access_token(request):
+    return WechatService.get_access_token(request)
+
+@require_http_methods(['POST'])
+def wechatLogin(request):
+    return WechatService.wechatLogin(request)
+
+@require_http_methods(['POST'])
+def getUserPhoneNumber(request):
+    return WechatService.getUserPhoneNumber(request)
+
+@require_http_methods(['POST'])
+def getUserInfo(request):
+    return WechatService.getUserInfo(request)
+
+@require_http_methods(['POST'])
+def getKbmUrl(request):
+    return WechatService.getKbmUrl(request)
+@require_http_methods(['POST'])
+def getOpenPid(request):
+    return WechatService.getOpenPid(request)
+
+@require_http_methods(['POST'])
+def updateBackground(request):
+    return WechatService.updateBackground(request)
+
+
+
+
+
+
+
+#
+@require_http_methods(['POST'])
+def pcSMS(request):
+    return WechatService.pcSMS(request)
+
+@require_http_methods(['POST'])
+def codeCheckout(request):
+    return WechatService.codeCheckout(request)
+
+@require_http_methods(['POST'])
+def pcLoging(request):
+    return WechatService.pcLoging(request)
+
+@require_http_methods(['POST'])
+def pcInnerAi(request):
+    return WechatService.pcInnerAi(request)
+
+
+

+ 0 - 0
backend/Controller/__init__.py


+ 7 - 0
backend/Form/AdminRoleForm.py

@@ -0,0 +1,7 @@
+from django import forms
+
+from backend.models import AdminRole
+class AdminRoleForm(forms.ModelForm):
+    class Meta:
+        model = AdminRole
+        fields = ['name',  'intro','is_admin','status']

+ 9 - 0
backend/Form/AuthMenuForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import AuthMenu
+
+
+class AuthMenuForm(forms.ModelForm):
+    class Meta:
+        model = AuthMenu
+        fields = ['code',  'name', 'parent_id', 'sort', 'status', 'path', 'type', "create_time"]

+ 9 - 0
backend/Form/AuthUserMenuForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import AuthUserMenu
+
+
+class AuthUserMenuForm(forms.ModelForm):
+    class Meta:
+        model = AuthUserMenu
+        fields = ['rola_id', 'parent_id']

+ 9 - 0
backend/Form/BrandForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Brand
+
+
+class BrandForm(forms.ModelForm):
+    class Meta:
+        model = Brand
+        fields = ['intro', 'name', 'status']

+ 9 - 0
backend/Form/DocumentCategoryForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import DocumentCategory
+
+
+class DocumentCategoryForm(forms.ModelForm):
+    class Meta:
+        model = DocumentCategory
+        fields = ['name', 'intro', 'status']

+ 9 - 0
backend/Form/DocumentForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Admin, Document
+
+
+class DocumentForm(forms.ModelForm):
+    class Meta:
+        model = Document
+        fields = ['title', 'category_id', 'data', 'status', 'is_template','user_id']

+ 9 - 0
backend/Form/FormulaForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Brand, Formula
+
+
+class FormulaForm(forms.ModelForm):
+    class Meta:
+        model = Formula
+        fields = ['name', 'formula', 'use_status', 'intro', 'params', 'status', 'point']

+ 9 - 0
backend/Form/ImageCategoryForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Supplier, ImageCategory
+
+
+class ImageCategoryForm(forms.ModelForm):
+    class Meta:
+        model = ImageCategory
+        fields = ['intro', 'name', 'status']

+ 9 - 0
backend/Form/ImageForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Brand, Image
+
+
+class ImageForm(forms.ModelForm):
+    class Meta:
+        model = Image
+        fields = ['category_id', 'name', 'width', 'height', 'path', 'status']

+ 9 - 0
backend/Form/KbmDocumentTypeForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import KbmDocumentType
+
+
+class KbmDocumentTypeForm(forms.ModelForm):
+    class Meta:
+        model = KbmDocumentType
+        fields = ['name', 'kb_id','status','parent_id']

+ 12 - 0
backend/Form/KnowledgebaseForm.py

@@ -0,0 +1,12 @@
+from django import forms
+
+from backend.models import Knowledgebase
+
+
+class KnowledgebaseForm(forms.ModelForm):
+    class Meta:
+        model = Knowledgebase
+        fields = ['create_time', 'update_time', 'role_id', 'name', 'description' ,
+                  'language', 'embd_id', 'permission' ,'created_by', 'doc_num',
+                  'token_num', 'chunk_num', 'similarity_threshold', 'vector_similarity_weight', 'parser_id', 'parser_config', 'status','location']
+

+ 9 - 0
backend/Form/ParamsForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Brand, Params
+
+
+class ParamsForm(forms.ModelForm):
+    class Meta:
+        model = Params
+        fields = ['code', 'type', 'name', 'value', 'value_type', 'value_item', 'intro', 'status']

+ 9 - 0
backend/Form/ProductCategoryForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Admin, ProductCategory
+
+
+class ProductCategoryForm(forms.ModelForm):
+    class Meta:
+        model = ProductCategory
+        fields = ['name', 'parent_id', 'attrs', 'status']

+ 12 - 0
backend/Form/ProductForm.py

@@ -0,0 +1,12 @@
+from django import forms
+
+from backend.models import Product
+
+
+class ProductForm(forms.ModelForm):
+
+
+    class Meta:
+        model = Product
+        fields = ['category_id', 'brand_id', 'supplier_id', 'name', 'intro', 'price', 'product_area', 'is_safe',
+                  'safe_time', 'is_package', 'package_intro', 'qualifications','status','attrs']

+ 9 - 0
backend/Form/ProjectCategoryForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import ProjectCategory
+
+
+class ProjectCategoryForm(forms.ModelForm):
+    class Meta:
+        model = ProjectCategory
+        fields = ['code', 'name', 'status']

+ 9 - 0
backend/Form/ProjectForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import ProjectCategory, Project
+
+
+class ProjectForm(forms.ModelForm):
+    class Meta:
+        model = Project
+        fields = ['name','intro', 'customer_id','category_id', 'status']

+ 9 - 0
backend/Form/SourceDataForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import SourceData
+
+
+class SourceDataForm(forms.ModelForm):
+    class Meta:
+        model = SourceData
+        fields = ['code', 'name', 'source_data', 'status', 'category_id']

+ 9 - 0
backend/Form/SuperAdminForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import SuperAdmin
+
+
+class SuperAdminForm(forms.ModelForm):
+    class Meta:
+        model = SuperAdmin
+        fields = ['username', 'password', 'mobile', 'email','real_name','code','job','contact', 'role_id', 'status']

+ 9 - 0
backend/Form/SuperSettingsForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import SuperSettings
+
+
+class SuperSettingsForm(forms.ModelForm):
+    class Meta:
+        model = SuperSettings
+        fields = ['logo', 'name', 'sidebar_color', 'internal_color','button_color','log_color','log_color_url','head_color']

+ 9 - 0
backend/Form/SupplierForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Supplier
+
+
+class SupplierForm(forms.ModelForm):
+    class Meta:
+        model = Supplier
+        fields = ['code', 'name','intro', 'status']

+ 9 - 0
backend/Form/TemplateCategoryForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Brand, Image, TemplateCategory
+
+
+class TemplateCategoryForm(forms.ModelForm):
+    class Meta:
+        model = TemplateCategory
+        fields = ['name', 'intro', 'status', 'parent_id']

+ 9 - 0
backend/Form/TemplateForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Brand, Template
+
+
+class TemplateForm(forms.ModelForm):
+    class Meta:
+        model = Template
+        fields = ['category_id','code', 'name', 'intro', 'lay_id', 'type', 'content','attrs', 'status']

+ 9 - 0
backend/Form/UserForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import Admin
+
+
+class UserForm(forms.ModelForm):
+    class Meta:
+        model = Admin
+        fields = ['username', 'password', 'mobile', 'email','real_name','code','job','contact', 'role_id', 'status']

+ 9 - 0
backend/Form/WechatComplaintForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import WechatComplaint
+
+
+class WechatComplaintForm(forms.ModelForm):
+    class Meta:
+        model = WechatComplaint
+        fields = ['name', 'phone', 'corporate_name','content','status']

+ 9 - 0
backend/Form/WechatUserForm.py

@@ -0,0 +1,9 @@
+from django import forms
+
+from backend.models import WechatUser
+
+
+class WechatUserForm(forms.ModelForm):
+    class Meta:
+        model = WechatUser
+        fields = ['name', 'nikeName', 'phone', 'openid','age','level','status','user_id', 'id_card','mini_url','examine','source']

+ 0 - 0
backend/Form/__init__.py


+ 242 - 0
backend/Service/AdminRoleService.py

@@ -0,0 +1,242 @@
+import json
+from enum import Enum
+
+from django.core.paginator import Paginator
+from django.db import transaction
+from django.forms import model_to_dict
+from oss2.exceptions import status
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.AdminRoleForm import AdminRoleForm
+from backend.Service.AuthMenuService import AuthMenuService
+from backend.models import AdminRole, AuthUserMenu, TemplateCateGoryUser, Admin, DocumentCategoryUser, DocumentCategory, \
+    SourceDataCategoryUser, SourceCategory, UserOperation, Operation
+from django.db.models import F, Case, When, IntegerField, Exists, OuterRef
+
+class AdminRoleService:
+
+
+    @staticmethod
+    def Create(request):
+        name = request.POST.get("name")
+        adminRola = AdminRole.objects.filter(name=name).exclude(status=4).exists()
+        if adminRola:
+            return fail("已存在同名,请修改后重试。")
+
+        form = AdminRoleForm(request.POST)
+        authList = request.POST.get("authList")
+        template_cate_auth = request.POST.get("template_cate_auth")
+        doc_cate_auth = request.POST.get("doc_cate_auth")
+        source_cate_auth = request.POST.get("source_cate_auth")
+        if form.is_valid():
+
+            admin_role = form.save()
+            # 获取保存后生成的 ID
+            generated_id = admin_role.id
+
+            if generated_id is None:
+                return fail("保存失败:无法获取生成的ID")
+            # 保存菜单信息
+            if authList:
+                auth_list = json.loads(authList)
+                for menu in auth_list:
+                    AuthUserMenu.objects.create(
+                        rola_id=generated_id,
+                        menu_id=menu.get("value")
+                    )
+
+            if template_cate_auth:
+                template_cate_auth_list = json.loads(template_cate_auth)
+                for auth in template_cate_auth_list:
+                    UserOperation.objects.create(
+                        role_id=generated_id,
+                        operation_id=auth.get("operation_id")
+                    )
+            if doc_cate_auth:
+                doc_cate_auth_list = json.loads(doc_cate_auth)
+                for auth in doc_cate_auth_list:
+                    UserOperation.objects.create(
+                        role_id=generated_id,
+                        operation_id=auth.get("operation_id")
+                    )
+            if source_cate_auth:
+                source_cate_auth_list = json.loads(source_cate_auth)
+                for auth in source_cate_auth_list:
+                    UserOperation.objects.create(
+                        role_id=generated_id,
+                        operation_id=auth.get("operation_id")
+                    )
+
+            return success({"id": generated_id})
+
+        return fail(form.errors)
+
+
+
+    @staticmethod
+    @transaction.atomic
+    def Update(request):
+        try:
+            data = AdminRole.objects.filter(id=request.POST.get("id")).first()
+            authList = request.POST.get("authList")
+            template_cate_auth = request.POST.get("template_cate_auth")
+            doc_cate_auth = request.POST.get("doc_cate_auth")
+            source_cate_auth = request.POST.get("source_cate_auth")
+            if data is not None:
+                data.name = request.POST.get("name")
+                data.intro = request.POST.get("intro")
+                data.status = request.POST.get("status")
+                data.is_admin = request.POST.get("is_admin")
+                data.save()
+
+            generated_id = request.POST.get("id")
+                # 删除旧的菜单信息
+            AuthUserMenu.objects.filter(rola_id=generated_id).delete()
+
+            UserOperation.objects.filter(role_id=generated_id).delete()
+
+
+            # 保存菜单信息
+            if authList:
+                auth_list = json.loads(authList)
+                for menu in auth_list:
+                    AuthUserMenu.objects.create(
+                        rola_id=generated_id,
+                        menu_id=menu.get("value")
+                    )
+
+            if template_cate_auth:
+                template_cate_auth_list = json.loads(template_cate_auth)
+                for auth in template_cate_auth_list:
+                    UserOperation.objects.create(
+                        role_id=generated_id,
+                        operation_id=auth.get("operation_id")
+                    )
+            if doc_cate_auth:
+                doc_cate_auth_list = json.loads(doc_cate_auth)
+                for auth in doc_cate_auth_list:
+                    UserOperation.objects.create(
+                        role_id=generated_id,
+                        operation_id=auth.get("operation_id")
+                    )
+            if source_cate_auth:
+                source_cate_auth_list = json.loads(source_cate_auth)
+                for auth in source_cate_auth_list:
+                    UserOperation.objects.create(
+                        role_id=generated_id,
+                        operation_id=auth.get("operation_id")
+                    )
+
+                return success("成功")
+            else:
+                return fail("更新参数信息失败")
+        except Exception as e:
+            return fail(str(e))
+
+    @staticmethod
+    def Info(request):
+        info = AdminRole.objects.filter(id=request.POST.get("id")).first()
+        return success(AdminRoleService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        AdminRole.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: AdminRole):
+        return {
+            "id": item.id,
+            'name': item.name,
+            'intro': item.intro,
+            'is_admin': item.is_admin,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        isAdmin = request.POST.get("is_admin")
+        if isAdmin:
+            where['is_admin'] = isAdmin
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(AdminRole.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = AdminRoleService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+
+
+    @staticmethod
+    def selectInfo(request):
+        id = request.POST.get("id")
+        if id is None:
+            return fail("id为空")
+
+        #查询角色信息
+        info = AdminRole.objects.filter(id=id).first()
+        info_dist = model_to_dict(info) if info else None
+        #查询菜单
+        menuList = AuthMenuService.menuInfo(id) or []
+
+        role_id = id
+
+        # 使用通用函数获取不同 content_type_id 的操作列表
+        # 1模板分类
+        template_category_users_list = get_operations_with_type(1, role_id)
+        # 2文档分类
+        doc_cate_auth = get_operations_with_type(2, role_id)
+        # 3原始数据分类
+        source_cate_auth = get_operations_with_type(3, role_id)
+
+        data = {
+            "info": info_dist,
+            "menuList": menuList,
+            "templateCategoryUsers": template_category_users_list,
+            "doc_cate_auth": doc_cate_auth,
+            "source_cate_auth": source_cate_auth
+        }
+        return success(data)
+
+
+def get_operations_with_type(content_type_id, role_id):
+    operations = Operation.objects.filter(content_type_id=content_type_id)
+
+    user_operation_exists = UserOperation.objects.filter(
+        role_id=role_id,
+        operation_id=OuterRef('pk')
+    )
+
+    operations = operations.annotate(
+        type=Case(
+            When(Exists(user_operation_exists), then=1),
+            default=0,
+            output_field=IntegerField()
+        )
+    ).values('id', 'content_type_id', 'name', 'type', 'codename')
+
+    # 执行查询并获取结果
+    result = list(operations)
+
+
+    return result
+

+ 260 - 0
backend/Service/AdminService.py

@@ -0,0 +1,260 @@
+import json
+import uuid
+from datetime import timedelta
+
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.paginator import Paginator
+from django.db import transaction
+from django.shortcuts import get_object_or_404
+
+from DCbackend.utils.common import success, fail, pageData
+from DCbackend.utils.token import encrypt, decrypt
+from backend.Form.UserForm import UserForm
+from backend.Service.AdminRoleService import AdminRoleService
+from backend.Service.AuthMenuService import AuthMenuService
+from backend.models import Admin, AdminRole
+from django.core.cache import cache
+
+class AdminService:
+
+    # 获取管理员信息
+    @staticmethod
+    def getAdminInfo(request):
+        adminId = request.POST.get('id')
+        adminInfo = Admin.objects.filter(id=adminId).first()
+        if adminInfo is not None:
+            return success(AdminService.getAdminDetail(adminInfo))
+        else:
+            return fail("管理员信息不存在")
+        # return success(Admin.objects.filter(pk=request.POST.get('id', 0)).first())
+
+    # 输出
+    @staticmethod
+    def getAdminDetail(admin: Admin):
+        role = AdminRole.objects.filter(id=admin.role_id).first()
+        roleInfo = {}
+
+        if role:
+            roleInfo = AdminRoleService.getDetail(role)
+
+            #菜單
+            results = AuthMenuService.menuInfo(admin.role_id)
+
+        data = {
+            'id': admin.id,
+            'username': admin.username,
+            'email': admin.email,
+            'mobile': admin.mobile,
+            'realName': admin.real_name,
+            'role_id': admin.role_id,
+            'roleInfo': roleInfo,
+            'token': admin.token,
+            'createTime': admin.create_time,
+            'status': admin.status,
+            'avatar': admin.header,
+            'job': admin.job,
+            'code': admin.code,
+            'contact': admin.contact,
+            'introduction': '',
+            'roles': "[admin]",
+            'authList': results
+        }
+        return data
+
+    # 创建管理员
+    @staticmethod
+    def create(request):
+        form = UserForm(request.POST)
+        if form.is_valid():
+            form.instance.password = encrypt(form.instance.password)
+            form.save()
+            return success("保存成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def update(request):
+        data = Admin.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            if request.POST.get("password"):
+                data.password = encrypt(request.POST.get("password"))
+            data.email = request.POST.get("email")
+            data.mobile = request.POST.get("mobile")
+            data.status = request.POST.get("status")
+            data.real_name = request.POST.get("real_name")
+            data.role_id = request.POST.get("role_id")
+            data.code = request.POST.get("code")
+            data.header = request.POST.get("header")
+            data.job = request.POST.get("job")
+            data.contact = request.POST.get("contact")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新用户信息失败")
+
+    @staticmethod
+    def updateProfile(request):
+        data = AdminService.verify_token(request.POST.get("token"))
+        if data is not None:
+            data.email = request.POST.get("email")
+            data.real_name = request.POST.get("realName")
+            data.code = request.POST.get("code")
+            data.header = request.POST.get("header")
+            data.job = request.POST.get("job")
+            data.contact = request.POST.get("contact")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新用户信息失败")
+
+    @staticmethod
+    def changePassword(request):
+        oldPassword = request.POST.get("oldPassword")
+        newPassword = request.POST.get("newPassword")
+        confirmPassword = request.POST.get("confirmPassword")
+        if newPassword!=confirmPassword:
+            return fail("两次填写的密码不相同")
+        data = AdminService.verify_token(request.POST.get("token"))
+        if data is not None:
+            if oldPassword != decrypt(data.password):
+                return fail("原密码不正确")
+            data.password=encrypt(newPassword)
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新用户信息失败")
+
+    # 通过token获取管理员信息
+    @staticmethod
+    def getAdminByToken(request):
+        adminInfo = AdminService.verify_token(request.POST.get("token"))
+        if adminInfo is not None:
+            return success(AdminService.getAdminDetail(adminInfo))
+        else:
+            return fail("用户信息不存在")
+
+    # 账户登录
+    @staticmethod
+    def login1(request):
+        username = request.POST.get("username", "")
+        password = request.POST.get("password", "")
+        adminInfo = Admin.objects.filter(username=username).first()
+        if adminInfo is not None:
+            if password == decrypt(adminInfo.password):
+                adminInfo.token = uuid.uuid4().hex
+                adminInfo.save()
+                return success({"token": adminInfo.token})
+            else:
+                return fail("登录密码不正确")
+        else:
+            return fail("登录失败,管理员用户名不存在")
+
+    @staticmethod
+    def login(request):
+        username = request.POST.get("username", "")
+        password = request.POST.get("password", "")
+        adminInfo = Admin.objects.filter(username=username).first()
+
+        if adminInfo is not None:
+            if password == decrypt(adminInfo.password):
+                # 检查是否存在有效的 token
+                existing_token = AdminService.get_existing_token(adminInfo.id)
+
+                if existing_token:
+                    # 如果存在有效的 token,直接返回
+                    return success({"token": existing_token})
+                else:
+                    # 如果不存在有效的 token,生成新的
+                    new_token = uuid.uuid4().hex
+                    cache.set(f"admin_token:{new_token}", adminInfo.id, timeout=60 * 60 * 24)
+                    cache.set(f"admin_id_to_token:{adminInfo.id}", new_token, timeout=60 * 60 * 24)
+                    return success({"token": new_token})
+            else:
+                return fail("登录密码不正确")
+        else:
+            return fail("登录失败,管理员用户名不存在")
+
+    @staticmethod
+    def verify_token(token):
+        admin_id = cache.get(f"admin_token:{token}")
+        if admin_id:
+            return Admin.objects.filter(id=admin_id).first()
+        return None
+
+    @staticmethod
+    def get_existing_token(admin_id):
+        # 尝试从缓存中获取该管理员的 token
+        token = cache.get(f"admin_id_to_token:{admin_id}")
+        if token:
+            # 如果找到 token,检查它是否仍然有效
+            if cache.get(f"admin_token:{token}"):
+                return token
+        return None
+    # 账户登出
+    @staticmethod
+    def logout(request):
+        token = request.POST.get("token")
+        adminInfo = AdminService.verify_token(token)
+        if adminInfo is not None:
+            # Invalidate the token in the cache
+            cache.delete(f"admin_token:{token}")
+            cache.delete(f"admin_id_to_token:{adminInfo.id}")
+            return success({"result": True})
+        else:
+            return fail("登出失败,token无效")
+
+    # 搜索管理员列表
+    @staticmethod
+    def search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+        username = request.POST.get("username", '')
+        if username:
+            where["username__contains"] = username
+
+        mobile = request.POST.get("mobile", '')
+        if mobile:
+            where["mobile__contains"] = mobile
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Admin.objects.filter(**where).exclude(status=4).order_by("-id"), pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            adminInfo = AdminService.getAdminDetail(item)
+            dataList.append(adminInfo)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+
+    @staticmethod
+    @transaction.atomic
+    def delete(request):
+        user_id = request.POST.get("id")
+
+        if not user_id:
+            return fail("User ID is required")
+
+        try:
+            # 尝试获取用户对象
+            user = get_object_or_404(Admin, id=user_id)
+
+            # 更新用户状态为逻辑删除(status=4)
+            updated_count = Admin.objects.filter(id=user_id).update(status=4)
+
+            if updated_count == 0:
+                return fail("指定的用户不存在")
+
+            return success("用户已成功删除")
+
+        except ObjectDoesNotExist:
+            return fail("指定的用户不存在")
+        except Exception as e:
+            return fail(f"删除用户时发生错误: {str(e)}")

+ 189 - 0
backend/Service/AuthMenuService.py

@@ -0,0 +1,189 @@
+import json
+
+from django.core.paginator import Paginator
+from django.db.models import Q
+
+from backend.Form.AuthMenuForm import AuthMenuForm
+from DCbackend.utils.common import success, fail, pageData
+from backend.models import AuthMenu, Admin, AuthUserMenu
+from django.core.cache import cache
+
+class AuthMenuService:
+    @staticmethod
+    def verify_token(token):
+        admin_id = cache.get(f"admin_token:{token}")
+        if admin_id:
+            return Admin.objects.filter(id=admin_id).first()
+        return None
+    # 根据token查询菜单
+    @staticmethod
+    def menuList(request):
+        adminInfo = AuthMenuService.verify_token(request.POST.get("token"))
+        if adminInfo is not None:
+            rolaId = adminInfo.role_id
+            return success(AuthMenuService.menuInfo(rolaId))
+        else:
+            return fail()
+
+    @staticmethod
+    def menuInfo(roleId):
+
+        userMenus = AuthUserMenu.objects.filter(rola_id=roleId)
+        results = []
+        if userMenus.exists():
+
+            for menu in userMenus:
+                menuid = menu.menu_id
+                menu = AuthMenu.objects.filter(id=menuid).first()
+
+                if menu is not None:
+                    results.append({
+                        "id": menuid,
+                        "name": menu.name,
+                        "code": menu.code,
+                        "parent_id": menu.parent_id,
+                        "sort": menu.sort,
+                        "status": menu.status,
+                        "path": menu.path,
+                        "type": menu.type
+                    })
+        return results;
+
+    #角色权限返回菜单
+    @staticmethod
+    def search(request):
+        parent_menus = AuthMenu.objects.filter(parent_id=0).order_by("-id")
+        # 创建一个字典来存储一级菜单及其对应的二级菜单
+        dataList = []
+
+        for parent in parent_menus:
+            # 查询每个一级菜单项的子菜单项
+            child_menus = AuthMenu.objects.filter(parent_id=parent.id).order_by("-id")
+            # 将一级菜单及其子菜单存储在字典中
+            for child in child_menus:
+                dataList.append({
+                    "id": child.id,
+                    "name": child.name,
+                    "code": child.code,
+                    "parent_id": child.parent_id,
+                    "sort": child.sort,
+                    "status": child.status,
+                    "path": child.path,
+                    "type": child.type
+                })
+        return success(dataList)
+
+    #创建菜单
+    @staticmethod
+    def create(request):
+        form = AuthMenuForm(request.POST)
+        if form.is_valid():
+            menu = form.save(commit=False)  # 创建模型实例但不保存到数据库
+            if menu.parent_id is None:
+                menu.parent_id = 0
+            menu.save()  # 现在保存到数据库
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def update(request):
+        data = AuthMenu.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.code = request.POST.get("code")
+            data.parent_id = request.POST.get("parent_id")
+            data.status = request.POST.get("status")
+            data.sort = request.POST.get("sort")
+            data.path = request.POST.get("path")
+            data.type = request.POST.get("type")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新源数据失败")
+    @staticmethod
+    def delete(request):
+        AuthMenu.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def info(request):
+        info = AuthMenu.objects.filter(id=request.POST.get("id")).first()
+        return success(AuthMenuService.getDetail(info))
+
+
+    #分页查询菜单
+    @staticmethod
+    def listpage(request):
+        # 从POST请求中获取数据
+        page = int(request.POST.get('page', 1))
+        page_size = int(request.POST.get('page_size', 10))
+
+        # 获取额外的筛选参数
+        name = request.POST.get('name', '')
+        code = request.POST.get('code', '')
+        type = request.POST.get('type', '')
+        status = request.POST.get('status', '')
+
+        # 查询所有父级菜单
+        parent_menus = AuthMenu.objects.filter(parent_id=0).order_by("-id")
+
+        dataList = []
+
+        for parent in parent_menus:
+            # 构建子菜单的多条件查询
+            child_query = Q(parent_id=parent.id)
+
+            # 根据提供的参数添加筛选条件
+            if name:
+                child_query &= Q(name__icontains=name)
+            if code:
+                child_query &= Q(code__icontains=code)
+            if type:
+                child_query &= Q(type__icontains=type)
+            if status:
+                child_query &= Q(status__icontains=status)
+
+
+            # 使用构建的条件查询子菜单
+            child_menus = AuthMenu.objects.filter(child_query).order_by("-id")
+
+            for child in child_menus:
+                dataList.append({
+                    "id": child.id,
+                    "name": child.name,
+                    "code": child.code,
+                    "parent_id": child.parent_id,
+                    "sort": child.sort,
+                    "status": child.status,
+                    "path": child.path,
+                    "create_time": child.create_time,
+                    "type": child.type
+                })
+
+        # 对结果进行分页
+        paginator = Paginator(dataList, page_size)
+        page_obj = paginator.get_page(page)
+
+        # 构建响应数据
+        response_data = {
+            "total": paginator.count,
+            "page": page,
+            "page_size": page_size,
+            "data": list(page_obj)
+        }
+
+        return success(response_data)
+
+    @staticmethod
+    def getDetail(item: AuthMenu):
+        return {
+            "id": item.id,
+            'code': item.code,
+            'name': item.name,
+            'parent_id': item.parent_id,
+            'status': item.status,
+            'sort': item.sort,
+            'path': item.path,
+            'type': item.type
+        }

+ 78 - 0
backend/Service/BrandService.py

@@ -0,0 +1,78 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.BrandForm import BrandForm
+from backend.models import ProjectCategory, Brand
+
+
+class BrandService:
+
+    @staticmethod
+    def Create(request):
+        form = BrandForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = Brand.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.intro = request.POST.get("intro")
+            data.name = request.POST.get("name")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新品牌信息失败")
+
+    @staticmethod
+    def Info(request):
+        info = Brand.objects.filter(id=request.POST.get("id")).first()
+        return success(BrandService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Brand.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Brand):
+        return {
+            "id": item.id,
+            'intro': item.intro,
+            'name': item.name,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Brand.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = BrandService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 53 - 0
backend/Service/CustomerService.py

@@ -0,0 +1,53 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, pageData
+from backend.models import Customer
+
+
+class CustomerService:
+
+    @staticmethod
+    def getDetail(item: Customer):
+        return {
+            "id": item.id,
+            'code': item.code,
+            'name': item.name,
+            'enName': item.en_name,
+            'mobile': item.mobile,
+            'contact': item.contact,
+            'address': item.address,
+            'other': item.other,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        code = request.POST.get("code", '')
+        if code:
+            where["code__icontains"] = code
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Customer.objects.filter(**where).exclude(status=4).order_by("-id"), pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = CustomerService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 76 - 0
backend/Service/DocumentCategoryService.py

@@ -0,0 +1,76 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.DocumentCategoryForm import DocumentCategoryForm
+from backend.models import Template, DocumentCategory
+
+
+class DocumentCategoryService:
+
+    @staticmethod
+    def Create(request):
+        form = DocumentCategoryForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = DocumentCategory.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.intro = request.POST.get("intro")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新分类失败")
+
+    @staticmethod
+    def Info(request):
+        info = DocumentCategory.objects.filter(id=request.POST.get("id")).first()
+        return success(DocumentCategoryService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        DocumentCategory.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: DocumentCategory):
+        return {
+            "id": item.id,
+            'name': item.name,
+            'intro': item.intro,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(DocumentCategory.objects.filter(**where).exclude(status=4).order_by("-id"), pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = DocumentCategoryService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 248 - 0
backend/Service/DocumentService.py

@@ -0,0 +1,248 @@
+import json
+
+
+from django.core.paginator import Paginator
+
+
+from DCbackend.utils.common import success, fail, pageData, saveDocFile
+from backend.Form.DocumentForm import DocumentForm
+from backend.Service.AdminService import AdminService
+from backend.Service.DocumentCategoryService import DocumentCategoryService
+from backend.models import Supplier, Document, DocumentCategory, ProductDocument, ProjectDocument, Admin, AdminRole
+
+
+#
+# from docx import Document
+# from bs4 import BeautifulSoup
+
+class DocumentService:
+
+    @staticmethod
+    def Create(request):
+        try:
+
+            form = DocumentForm(request.POST)
+            if form.is_valid():
+                is_template = request.POST.get("is_template")
+                category_id = request.POST.get("category_id")
+
+                if int(is_template) == 0 and category_id == None:
+                    return fail("保存文档时,分类不能为空")
+
+                form.save()
+
+
+                document_id = form.instance.id
+                links = json.loads(request.POST.get("links"))
+                for link in links:
+                    pd = ProductDocument()
+                    pd.document_id = document_id
+                    pd.product_id = link
+                    pd.status = 5
+                    pd.save()
+                projects = json.loads(request.POST.get("projects"))
+                for p in projects:
+                    pr = ProjectDocument()
+                    pr.project_id = p
+                    pr.doc_id = document_id
+                    pr.status = 5
+                    pr.save()
+                return success(document_id)
+            else:
+                return fail(form.errors)
+        except Exception as ex:
+            return fail(str(ex))
+    @staticmethod
+    def Update(request):
+        data = Document.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.title = request.POST.get("title")
+            data.data = request.POST.get("data")
+            data.status = request.POST.get("status")
+            data.save()
+            links = json.loads(request.POST.get("links"))
+            ProductDocument.objects.filter(document_id=data.id).update(status=4)
+            for link in links:
+                pd = ProductDocument()
+                pd.document_id = data.id
+                pd.product_id = link
+                pd.status = 5
+                pd.save()
+            projects = json.loads(request.POST.get("projects"))
+            ProjectDocument.objects.filter(doc_id=data.id).update(status=4)
+            for p in projects:
+                pr = ProjectDocument()
+                pr.project_id = p
+                pr.doc_id = data.id
+                pr.status = 5
+                pr.save()
+
+            return success(data.id)
+        else:
+            return fail("更新文档信息失败")
+
+    @staticmethod
+    def Info(request):
+        info = Document.objects.filter(id=request.POST.get("id")).first()
+        return success(DocumentService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Document.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Document):
+        where = {}
+        where["document_id"] = item.id
+        dpList = ProductDocument.objects.filter(**where).exclude(status=4).all()
+        linkProduct = []
+        for it in dpList:
+            # temp = {
+            #     'id': it.id,
+            #     'product_id': it.product_id,
+            #     'document_id': it.document_id,
+            # }
+            linkProduct.append(it.product_id)
+
+        prList = ProjectDocument.objects.filter(doc_id=item.id).exclude(status=4).all()
+        linkProject = []
+        for it in prList:
+            # temp = {
+            #     'id': it.id,
+            #     'project_id': it.project_id,
+            #     'doc_id': it.doc_id,
+            # }
+            linkProject.append(it.project_id)
+
+        isTemplate = item.is_template
+        username = ''
+        if item.is_template is False:
+            adminInfo = Admin.objects.filter(id=item.user_id).first()
+            username = adminInfo.username
+        return {
+            "id": item.id,
+            'title': item.title,
+            'data': item.data,
+            'user_id': item.user_id,
+            'linkProduct': linkProduct,
+            'links': '',
+            'linkProject': linkProject,
+            'projects': '',
+            'category_id': item.category_id,
+            'createTime': item.create_time,
+            'status': item.status,
+            'is_template': item.is_template,
+            'user_name': username
+        }
+
+    @staticmethod
+    def Search(request):
+
+
+
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+        where = {}
+
+        admin = AdminService.verify_token(request.POST.get("token"))
+        if admin:
+            role_id = admin.role_id
+            role = AdminRole.objects.filter(id=role_id).first()
+            if role.is_admin != 1:
+                where["user_id"] = admin.id
+        else:
+            return fail("token信息异常")
+
+        categoryId = request.POST.get("category_id")
+        if categoryId:
+            where["category_id"] = categoryId
+
+        title = request.POST.get("title", '')
+        if title:
+            where["title__icontains"] = title
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Document.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = DocumentService.getDetail(item)
+            category = DocumentCategory.objects.filter(id=item.category_id).first()
+            info['category'] = DocumentCategoryService.getDetail(category)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+
+
+    @staticmethod
+    def pageDocument(request):
+        where = {}
+        admin = AdminService.verify_token(request.POST.get("token"))
+        if admin:
+            role_id = admin.role_id
+            role = AdminRole.objects.filter(id=role_id).first()
+            if role.is_admin != 1:
+                where["user_id"] = admin.id
+        else:
+            return fail("token过期请重新登录")
+
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 10
+
+
+        categoryId = request.POST.get("category_id")
+        if categoryId:
+            where["category_id"] = categoryId
+
+        title = request.POST.get("title", '')
+        if title:
+            where["title__icontains"] = title
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Document.objects.filter(**where).exclude(status=4).order_by("-id").values('id', 'title', 'category_id','create_time', 'status','is_template','user_id'),pageSize)
+
+        page_obj = paginator.get_page(page)
+
+        # 获取所有需要的 category_id
+        category_ids = set(info['category_id'] for info in page_obj if info['category_id'] is not None)
+
+        # 一次性查询所有需要的 DocumentCategory
+        categories = {
+            cat['id']: cat['name']
+            for cat in DocumentCategory.objects.filter(id__in=category_ids).exclude(status=4).values('id', 'name')
+        }
+
+        # 获取所有需要的 user_id
+        user_ids = set(info['user_id'] for info in page_obj if info['user_id'] is not None)
+
+        # 一次性查询所有需要的 Admin
+        admin_info = {
+            admin['id']: admin['username']
+            for admin in Admin.objects.filter(id__in=user_ids).values('id', 'username')
+        }
+
+        dataList = []
+        for info in page_obj:
+            info_dict = dict(info)  # 转换为可变的字典
+            category_id = info_dict['category_id']
+            user_id = info_dict['user_id']
+            info_dict['type_name'] = categories.get(category_id, '')
+            info_dict['user_name'] = admin_info.get(user_id, '')
+            dataList.append(info_dict)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+

+ 309 - 0
backend/Service/ExportService.py

@@ -0,0 +1,309 @@
+# import io
+# import re
+# import subprocess
+# import sys
+#
+# import mammoth
+# import pdfkit
+# from bs4 import BeautifulSoup
+# from django.http import  JsonResponse
+# from pdf2docx import Converter
+# from DCbackend.utils import common
+# from DCbackend.utils.common import success, fail
+# import os
+# from datetime import datetime
+# class ExportService:
+#
+#     @staticmethod
+#     def Export(request):
+#         html_content = request.POST.get("content", "<h1>Test Content</h1>")
+#
+#         # 预处理 HTML 内容,将特定格式转换为标题
+#         def process_content(content):
+#             # 使用正则表达式匹配 A{{Directory1}}, A.1{{Directory2}} 等格式
+#             pattern = r'([A-Z](?:\.\d+)*){{Directory(\d+)}}'
+#
+#             def replace_with_header(match):
+#                 prefix = match.group(1)
+#                 level = int(match.group(2))
+#                 # 确保标题级别在1到6之间
+#                 level = max(1, min(level, 6))
+#                 return f'<h{level}>{prefix}'
+#
+#             # 替换匹配的内容为HTML标题
+#             processed_content = re.sub(pattern, replace_with_header, content)
+#             return processed_content
+#
+#         processed_html_content = process_content(html_content)
+#
+#         output_dir = 'uploads'
+#         input_file = 'input.html'
+#
+#         # 使用当前时间生成唯一的文件名
+#         current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
+#         output_file = f'exported_document_{current_time}.docx'
+#
+#         input_path = os.path.join(output_dir, input_file)
+#         output_path = os.path.join(output_dir, output_file)
+#
+#         print(f"准备创建输入文件: {input_path}")
+#
+#         try:
+#             # 确保输出目录存在
+#             os.makedirs(output_dir, exist_ok=True)
+#
+#             # 写入处理后的 HTML 内容到输入文件
+#             with open(input_path, 'w', encoding='utf-8') as f:
+#                 f.write(processed_html_content)
+#
+#             print(f"输入文件成功创建: {input_path}")
+#
+#             if sys.platform.startswith('win'):
+#                 pandoc_path = r'D:\Download\pandoc-3.1.12.2\pandoc.exe'
+#             else:
+#                 pandoc_path = '/usr/local/bin/pandoc'
+#             command = [
+#                 pandoc_path,
+#                 "-f", "html",
+#                 "-t", "docx",
+#                 "--toc",  # 生成目录
+#                 "--toc-depth=6",  # 目录深度,设置为6以包含所有可能的级别
+#                 "--number-sections",  # 为标题添加编号
+#                 "-o", output_path,
+#                 input_path
+#             ]
+#
+#             print("开始执行 Pandoc 转换")
+#             result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+#
+#             if result.returncode == 0:
+#                 print(f"Pandoc 转换成功,输出文件: {output_path}")
+#                 if os.path.exists(output_path):
+#                     oss_url = common.oss_upload_file(output_path)
+#                     file_size = os.path.getsize(output_path)
+#                     return success({
+#                         "file_name": output_file,
+#                         "file_path": oss_url,
+#                         "file_size": file_size
+#                     })
+#                 else:
+#                     return JsonResponse({"error": "DOCX 文件创建失败,无法找到文件"}, status=500)
+#             else:
+#                 print(f"Pandoc 转换失败。错误信息: {result.stderr.decode()}")
+#                 return JsonResponse({"error": f"Pandoc 转换失败: {result.stderr.decode()}"}, status=500)
+#
+#         except Exception as e:
+#             print(f"处理过程中发生错误: {str(e)}")
+#             return JsonResponse({"error": f"处理过程中发生错误: {str(e)}"}, status=500)
+#         finally:
+#             # 清理输入文件
+#             if os.path.exists(input_path):
+#                 os.remove(input_path)
+#                 print(f"已删除输入文件: {input_path}")
+#
+#     @staticmethod
+#     def htmlPDFWord(request):
+#         html_content = request.POST.get("content", "<h1>Test Content</h1>")
+#
+#         # 创建一个临时目录来存储生成的文件
+#         output_dir = 'temp_files'
+#         os.makedirs(output_dir, exist_ok=True)
+#
+#         # 生成唯一的文件名
+#         current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
+#         pdf_file = f'exported_document_{current_time}.pdf'
+#         docx_file = f'exported_document_{current_time}.docx'
+#         pdf_path = os.path.join(output_dir, pdf_file)
+#         docx_path = os.path.join(output_dir, docx_file)
+#
+#         try:
+#             # 配置pdfkit选项
+#             options = {
+#                 'page-size': 'A4',
+#                 'margin-top': '0.75in',
+#                 'margin-right': '0.75in',
+#                 'margin-bottom': '0.75in',
+#                 'margin-left': '0.75in',
+#                 'encoding': "UTF-8",
+#             }
+#
+#             # 指定wkhtmltopdf的路径
+#             if sys.platform.startswith('win'):
+#                 config = pdfkit.configuration(wkhtmltopdf=r'D:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe')
+#             else:
+#                 config = pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf')
+#
+#             # 将HTML转换为PDF
+#             pdfkit.from_string(html_content, pdf_path, options=options, configuration=config)
+#
+#             # 将PDF转换为DOCX
+#             cv = Converter(pdf_path)
+#             cv.convert(docx_path)
+#             cv.close()
+#
+#             # 上传到阿里云OSS
+#             oss_url = common.oss_upload_file(docx_path)
+#
+#             # 获取文件大小
+#             file_size = os.path.getsize(docx_path)
+#
+#             # 返回成功响应,包含文件信息
+#             return success({
+#                 "file_name": docx_file,
+#                 "file_path": oss_url,
+#                 "file_size": file_size
+#             })
+#
+#         except Exception as e:
+#             return fail(f"文档生成或上传失败: {str(e)}")
+#
+#         finally:
+#             # 清理临时文件
+#             if os.path.exists(pdf_path):
+#                 os.remove(pdf_path)
+#             if os.path.exists(docx_path):
+#                 os.remove(docx_path)
+#             # 尝试删除临时目录
+#             try:
+#                 os.rmdir(output_dir)
+#             except OSError:
+#                 # 如果目录不为空,就忽略错误
+#                 pass
+#
+#     @staticmethod
+#     def htmlPDF(request):
+#         html_content = request.POST.get("content", "<h1>Test Content</h1>")
+#
+#         # 创建一个临时目录来存储生成的PDF
+#         output_dir = 'temp_pdf'
+#         os.makedirs(output_dir, exist_ok=True)
+#
+#         # 生成唯一的文件名
+#         current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
+#         output_file = f'exported_document_{current_time}.pdf'
+#         output_path = os.path.join(output_dir, output_file)
+#
+#         try:
+#             # 配置pdfkit选项
+#             # 调整选项以放大内容
+#             options = {
+#                 'page-size': 'A4',
+#                 'margin-top': '0.75in',
+#                 'margin-right': '0.75in',
+#                 'margin-bottom': '0.75in',
+#                 'margin-left': '0.75in',
+#                 'encoding': "UTF-8",
+#                 'dpi': 300,  # 增加 DPI 以提高清晰度
+#             }
+#
+#             # 指定wkhtmltopdf的路径
+#             if sys.platform.startswith('win'):
+#                 config = pdfkit.configuration(wkhtmltopdf=r'D:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe')
+#             else:
+#                 config = pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf')
+#
+#             # 将HTML转换为PDF
+#             pdfkit.from_string(html_content, output_path, options=options, configuration=config)
+#
+#             # 上传到阿里云OSS
+#             oss_url = common.oss_upload_file(output_path)
+#
+#             # 获取文件大小
+#             file_size = os.path.getsize(output_path)
+#
+#             # 删除临时文件
+#             os.remove(output_path)
+#
+#             # 返回成功响应,包含文件信息
+#             return success({
+#                 "file_name": output_file,
+#                 "file_path": oss_url,
+#                 "file_size": file_size
+#             })
+#
+#         except Exception as e:
+#             # 如果出现错误,确保删除任何可能创建的临时文件
+#             if os.path.exists(output_path):
+#                 os.remove(output_path)
+#             return fail(f"PDF生成或上传失败: {str(e)}")
+#
+#         finally:
+#             # 尝试删除临时目录
+#             try:
+#                 os.rmdir(output_dir)
+#             except OSError:
+#                 # 如果目录不为空,就忽略错误
+#                 pass
+#
+#     @staticmethod
+#     def htmlWord(request):
+#         html_content = request.POST.get("content", "<h1>Test Content</h1>")
+#
+#         # 创建一个内存中的字节流来存储Word文档
+#         output = io.BytesIO()
+#
+#         # 使用mammoth将HTML转换为Word文档
+#         style_map = """
+#             p => p:fresh
+#             h1 => h1:fresh
+#             h2 => h2:fresh
+#             h3 => h3:fresh
+#             h4 => h4:fresh
+#             h5 => h5:fresh
+#             h6 => h6:fresh
+#             strong => b
+#             em => i
+#             ul => ul
+#             ol => ol
+#             li => li
+#             """
+#         mammoth.convert_html_to_docx(html_content, output, style_map=style_map)
+#
+#         # 生成唯一的文件名
+#         current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
+#         output_file = f'exported_document_{current_time}.docx'
+#         output_dir = 'temp_word'
+#         os.makedirs(output_dir, exist_ok=True)
+#         output_path = os.path.join(output_dir, output_file)
+#
+#         try:
+#             # 将内存中的文档保存到文件
+#             with open(output_path, "wb") as f:
+#                 f.write(output.getvalue())
+#
+#             # 上传到阿里云OSS
+#             oss_url = common.oss_upload_file(output_path)
+#
+#             # 获取文件大小
+#             file_size = os.path.getsize(output_path)
+#
+#             return success({
+#                 "file_name": output_file,
+#                 "file_path": oss_url,
+#                 "file_size": file_size
+#             })
+#
+#         except Exception as e:
+#             return fail(f"Word文档生成或上传失败: {str(e)}")
+#
+#         finally:
+#             # 清理临时文件
+#             if os.path.exists(output_path):
+#                 os.remove(output_path)
+#             # 尝试删除临时目录
+#             try:
+#                 os.rmdir(output_dir)
+#             except OSError:
+#                 # 如果目录不为空,就忽略错误
+#                 pass
+#
+#
+#
+#
+#
+#
+#
+#
+#
+#
+#

+ 85 - 0
backend/Service/FormulaService.py

@@ -0,0 +1,85 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.BrandForm import BrandForm
+from backend.Form.FormulaForm import FormulaForm
+from backend.models import ProjectCategory, Brand, Formula
+
+
+class FormulaService:
+
+    @staticmethod
+    def Create(request):
+        form = FormulaForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = Formula.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.formula = request.POST.get("formula")
+            data.use_status = request.POST.get("use_status")
+            data.intro = request.POST.get("intro")
+            data.params = request.POST.get("params")
+            data.status = request.POST.get("status")
+            data.point = request.POST.get("point")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新品牌信息失败")
+
+    @staticmethod
+    def Info(request):
+        info = Formula.objects.filter(id=request.POST.get("id")).first()
+        return success(FormulaService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Formula.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Formula):
+        return {
+            "id": item.id,
+            'name': item.name,
+            'formula': item.formula,
+            'intro': item.intro,
+            'use_status': item.use_status,
+            'createTime': item.create_time,
+            'params': item.params,
+            'point': item.point,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Formula.objects.filter(**where).exclude(status=4).order_by("-id"), pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = FormulaService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 78 - 0
backend/Service/ImageCategoryService.py

@@ -0,0 +1,78 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.ImageCategoryForm import ImageCategoryForm
+from backend.Form.SupplierForm import SupplierForm
+from backend.models import Supplier, ImageCategory
+
+
+class ImageCategoryService:
+
+    @staticmethod
+    def Create(request):
+        form = ImageCategoryForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = ImageCategory.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.intro = request.POST.get("intro")
+            data.name = request.POST.get("name")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新供应商信息失败")
+
+    @staticmethod
+    def Info(request):
+        info = ImageCategory.objects.filter(id=request.POST.get("id")).first()
+        return success(ImageCategoryService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        ImageCategory.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: ImageCategory):
+        return {
+            "id": item.id,
+            'intro': item.intro,
+            'name': item.name,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(ImageCategory.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = ImageCategoryService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 88 - 0
backend/Service/ImageService.py

@@ -0,0 +1,88 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.ImageForm import ImageForm
+from backend.Form.ParamsForm import ParamsForm
+from backend.models import Params, Image
+
+
+class ImageService:
+
+    @staticmethod
+    def Create(request):
+        form = ImageForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = Image.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.category_id = request.POST.get("category_id")
+            data.name = request.POST.get("name")
+            data.width = request.POST.get("width")
+            data.height = request.POST.get("height")
+            data.path = request.POST.get("path")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新图片信息失败")
+
+    @staticmethod
+    def Info(request):
+        info = Image.objects.filter(id=request.POST.get("id")).first()
+        return success(ImageService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Image.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Image):
+        return {
+            "id": item.id,
+            'categoryId': item.category_id,
+            'name': item.name,
+            'width': item.width,
+            'height': item.height,
+            'path': item.path,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        categoryId = request.POST.get("categoryId", '')
+        if categoryId:
+            where["category_id"] = categoryId
+
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Image.objects.filter(**where).exclude(status=4).order_by("-id"),pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = ImageService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 23 - 0
backend/Service/IndexService.py

@@ -0,0 +1,23 @@
+from django.core.paginator import Paginator
+from oss2.exceptions import status
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.models import Admin, Document, Template, Product
+
+
+class IndexService:
+    #首页统计数量
+    @staticmethod
+    def statistics(request):
+        admin_count = Admin.objects.exclude(status=4).count()
+        document_coutn  = Document.objects.exclude(status=4).count()
+        template_count = Template.objects.exclude(status=4).count()
+        product_count = Product.objects.exclude(status=4).count()
+
+        count = {
+            'admin_count': admin_count,
+            'document_coutn': document_coutn,
+            'template_count': template_count,
+            'product_count': product_count
+        }
+        return success(count)

+ 115 - 0
backend/Service/KbmDocumentTypeService.py

@@ -0,0 +1,115 @@
+from backend.Form.KbmDocumentTypeForm import KbmDocumentTypeForm
+from backend.models import KbmDocumentType, DocumentKbm
+from base import logger
+from django.core.paginator import Paginator
+from django.db import transaction
+
+from DCbackend import settings
+from DCbackend.utils.common import success, fail, pageData
+#知识库文件分类
+class KbmDocumentTypeService:
+    @staticmethod
+    @transaction.atomic
+    def insertType(request):
+        kb_id = request.POST.get("kb_id")
+        if not kb_id:
+            return fail("知识库id为空")
+        form = KbmDocumentTypeForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("保存成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    @transaction.atomic
+    def updateType(request):
+        data = KbmDocumentType.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.kb_id = request.POST.get("kb_id")
+            data.status = request.POST.get("status")
+            data.parent_id = request.POST.get("parent_id")
+            data.save()
+            return success("更新成功")
+        else:
+            return fail("更新参数信息失败")
+
+    @staticmethod
+    def selectTypeList(request):
+        page = request.POST.get("page",1)
+        pageSize = request.POST.get("pageSize",10)
+        kb_id = request.POST.get("kb_id")
+        if not kb_id:
+            return fail("知识库id为空")
+
+        where = {}
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        where['kb_id'] = kb_id
+
+        paginator = Paginator(KbmDocumentType.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+
+        dataList = []
+        for item in page_obj:
+            count = DocumentKbm.objects.filter(doc_type_id=item.id).count()
+            info = KbmDocumentTypeService.getDetail(item, count)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+
+    @staticmethod
+    def getDetail(item, count=None):
+        info = {
+            "id": item.id,
+            'name': item.name,
+            'kb_id': item.kb_id,
+            'create_time': item.create_time,
+            'status': item.status
+        }
+        if count is not None:
+            info['document_count'] = count
+        return info
+
+    @staticmethod
+    @transaction.atomic
+    def delete(request):
+        id = request.POST.get("id")
+        if not id:
+            return fail("id为空")
+        count = DocumentKbm.objects.filter(doc_type_id=id).exclude(status=4).count()
+        if count > 0:
+            return fail("该分类下存在文件,请删除后重试")
+
+        KbmDocumentType.objects.filter(id=id).update(status=4)
+
+        return success("删除成功")
+
+    @staticmethod
+    def Info(request):
+        info = KbmDocumentType.objects.filter(id=request.POST.get("id")).first()
+        return success(KbmDocumentTypeService.getDetail(info),"查询成功")
+
+
+    @staticmethod
+    def selectType0(request):
+        type_id = request.POST.get("type_id")
+        kb_id = request.POST.get("kb_id")
+        if not type_id:
+            return fail("type_id为空")
+
+        count = DocumentKbm.objects.filter(doc_type_id=type_id,kb_id=kb_id).exclude(status=4).count()
+
+        return success(count)
+
+
+
+
+

+ 1669 - 0
backend/Service/KbmService.py

@@ -0,0 +1,1669 @@
+import logging
+import platform
+import shutil
+import subprocess
+import tempfile
+import threading
+import uuid
+import warnings
+from datetime import timedelta, datetime, timezone
+from io import BytesIO
+from time import sleep
+from typing import Collection
+
+import PyPDF2
+import chardet
+import cv2
+import fitz
+import pandas as pd
+import redis
+import requests
+from PIL import Image
+from bs4 import BeautifulSoup
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
+from django.db import transaction
+from django.shortcuts import get_object_or_404
+from minio import Minio
+import re
+import os
+
+import markdown
+from paddleocr import PaddleOCR
+from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType, utility
+from requests import RequestException
+from scipy import interpolate
+from tabulate import tabulate
+from DCbackend.settings import MILVUS_HOST, MILVUS_PORT, VECTOR_DIMENSION, MILVUS_USER, MILVUS_PASSWORD
+from base import logger
+
+os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
+from scipy.spatial.distance import cosine
+from django.utils import timezone
+import traceback
+from DCbackend.utils.common import success, fail
+from backend.Service.MinioService import MinioService
+from backend.models import Knowledgebase, DocumentKbm, File2document, File, Task, TaskSublist, KbmDocumentType
+import pytesseract
+import numpy as np
+import json
+import time
+import pika
+from django.db.models import Count, Case, When, IntegerField, Q, Max
+from django.conf import settings
+
+
+class DocumentQueue:
+    QUEUE_KEY = "document_process_queue"
+
+
+
+minio_client = Minio(
+    settings.MINIO_ENDPOINT,
+    access_key=settings.MINIO_ACCESS_KEY,
+    secret_key=settings.MINIO_SECRET_KEY,
+    secure=settings.MINIO_SECURE
+)
+
+if os.name == 'nt':  # Windows
+    pytesseract.pytesseract.tesseract_cmd = r'D:\Program Files\OCR\tesseract.exe'
+else:  # macOS 或 Linux
+    pytesseract.pytesseract.tesseract_cmd = r'/usr/bin/tesseract'
+
+
+
+class KbmService:
+    bert_model = None
+    bert_tokenizer = None
+    #大模型地址
+    API_URL = "http://127.0.0.1:11434/api/embeddings"
+
+    #通用rabbitmq 放入队列
+    def send_to_rabbitmq(queue_name, message):
+        """
+        将消息发送到指定的RabbitMQ队列
+        """
+        try:
+            connection = pika.BlockingConnection(pika.ConnectionParameters(
+                host=settings.RABBITMQ_HOST,
+                port=settings.RABBITMQ_PORT,
+                credentials=pika.PlainCredentials(
+                    settings.RABBITMQ_USER,
+                    settings.RABBITMQ_PASSWORD
+                )
+            ))
+            channel = connection.channel()
+
+            channel.queue_declare(queue=queue_name, durable=True)
+
+            channel.basic_publish(
+                exchange='',
+                routing_key=queue_name,
+                body=json.dumps(message),
+                properties=pika.BasicProperties(
+                    delivery_mode=2,  # 使消息持久化
+                )
+            )
+
+            connection.close()
+            logger.info(f"消息已发送到队列 {queue_name}")
+            return True
+        except Exception as e:
+            logger.error(f"发送消息到RabbitMQ时出错: {str(e)}")
+            return False
+
+    @staticmethod
+    def selectBucketInfo(request):
+        # user_id = request.POST.get("user_id")
+        knowledgebases = Knowledgebase.objects.filter().exclude(status=4).order_by('-create_time').values('id', 'create_time', 'name', 'doc_num', 'description')
+
+        result = []
+        for kb in knowledgebases:
+            # 使用一次查询获取所有计数
+            counts = DocumentKbm.objects.filter(kb_id=kb['id']).aggregate(
+                word_count=Count(Case(When(type__in=['doc', 'docx'], then=1), output_field=IntegerField())),
+                pdf_count=Count(Case(When(type='pdf', then=1), output_field=IntegerField())),
+                excel_count=Count(Case(When(type__in=['xls', 'xlsx'], then=1), output_field=IntegerField()))
+            )
+
+            kb_data = kb.copy()  # 创建 kb 的副本,以避免修改原始数据
+            kb_data.update(counts)
+            result.append(kb_data)
+
+        return success(result)
+
+    @staticmethod
+    def getFileInfo(request):
+        try:
+            bucket_id = request.POST.get("bucket_id")
+            page = request.POST.get("page", 1)
+            per_page = request.POST.get("pageSize", 10)
+            object_name = request.POST.get("object_name", "")
+            run = request.POST.get("run", "")
+            type = request.POST.get("type", "")
+            doc_type_id = request.POST.get("doc_type_id")
+            if not bucket_id:
+                return fail("bucket_id为空")
+
+
+            # 确保 page 和 per_page 是整数
+            page = int(page)
+            per_page = int(per_page)
+
+            # # 查询文档并排序
+            # documents = DocumentKbm.objects.filter(
+            #     Q(kb_id=bucket_id) &
+            #     Q(name__icontains=object_name)&
+            #     Q(run__icontains=run)&
+            #     Q(type__icontains=type)&
+            #     Q(doc_type_id=doc_type_id)&
+            #     ~Q(status=4)
+            # ).order_by('-create_time')
+
+            # 构建基本查询
+            query = Q(kb_id=bucket_id) & Q(name__icontains=object_name) & Q(run__icontains=run) & Q(type__icontains=type) & ~Q(status=4)
+
+            # 如果 doc_type_id 有值,则添加到查询条件
+            if doc_type_id:
+                query &= Q(doc_type_id=doc_type_id)
+
+            # 查询文档并排序
+            documents = DocumentKbm.objects.filter(query).order_by('-create_time')
+
+            # 创建分页器
+            paginator = Paginator(documents, per_page)
+
+            try:
+                # 获取指定页的结果
+                documents_page = paginator.page(page)
+            except PageNotAnInteger:
+                # 如果页码不是整数,返回第一页
+                documents_page = paginator.page(1)
+            except EmptyPage:
+                # 如果页码超出范围,返回最后一页
+                documents_page = paginator.page(paginator.num_pages)
+
+            # 将查询结果转换为列表
+            result = list(documents_page.object_list.values())
+            for info in result:
+                document_id = info['id']
+                max_page = TaskSublist.objects.filter(doc_id=document_id).aggregate(Max('page_number'))['page_number__max']
+                info['max_page'] = max_page if max_page is not None else 0
+
+            pagination_info = {
+                'total_count': paginator.count,
+                'total_pages': paginator.num_pages,
+                'total_size': per_page,
+                'current_page': documents_page.number,
+                'has_next': documents_page.has_next(),
+                'has_previous': documents_page.has_previous()
+            }
+            data = {
+                'pagination': pagination_info,
+                'documents': result
+            }
+
+            return success(data)
+        except Exception as e:
+            return fail("获取信息失败")
+
+    @staticmethod
+    @transaction.atomic
+    def updateName(request):
+        try:
+            new_name = request.POST.get("new_name")
+            document_id = request.POST.get("document_id")
+
+            if not new_name or not document_id:
+                return fail("新名称和文件ID不能为空")
+
+            # 获取 DocumentKbm 实例并更新
+            document = get_object_or_404(DocumentKbm, id=document_id)
+            location = document.location
+
+            # 获取原始文件的扩展名
+            _, original_extension = os.path.splitext(location)
+
+            # 检查新名称是否包含扩展名,如果没有则添加原始扩展名
+            _, new_extension = os.path.splitext(new_name)
+            if not new_extension:
+                new_name = f"{new_name}{original_extension}"
+
+            document.name = new_name
+            document.save()
+
+            # 获取关联的 File2document 和 File
+            file2doc = File2document.objects.filter(document_id=document_id).first()
+            if file2doc:
+                file = get_object_or_404(File, id=file2doc.file_id)
+                file.name = new_name
+                file.save()
+            else:
+                # 记录一个警告,因为没有找到关联的 File
+                logger.error(f"Warning: No associated File found for DocumentKbm with id {document_id}")
+
+            return success("文件名更新成功")
+        except ObjectDoesNotExist:
+            return fail("指定的文件或关联文件不存在")
+        except Exception as e:
+            return fail(f"更新文件名失败: {str(e)}")
+
+
+    @staticmethod
+    @transaction.atomic
+    def deleteDocument(request):
+
+        document_id = request.POST.get("document_id")
+
+        document = get_object_or_404(DocumentKbm, id=document_id)
+        document.status = 4
+        document.save()
+
+        file2doc = File2document.objects.filter(document_id=document_id).first()
+        if file2doc:
+            file = get_object_or_404(File, id=file2doc.file_id)
+            file.status = 4
+            file.save()
+
+        kb_id = document.kb_id
+        new_count = DocumentKbm.objects.filter(kb_id=kb_id).exclude(status=4).count()
+        Knowledgebase.objects.filter(id=kb_id).update(doc_num=new_count)
+        try:
+            # 清理milvus
+            # 连接到 Milvus
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT)
+
+            kmb = Knowledgebase.objects.filter(id=kb_id).first()
+            collection = Collection(kmb.location)
+
+            tasks = TaskSublist.objects.filter(doc_id=document.id)
+            for task in tasks:
+                expr = f'id in [{task.milvus_id}]'
+                collection.delete(expr)
+            return success("删除成功")
+        except Exception as e:
+            return fail(f"删除milvus集合时发生错误: {str(e)}")
+
+        finally:
+            # 断开 Milvus 连接
+            connections.disconnect("default")
+
+    @staticmethod
+    def getUrl(request):
+        try:
+            document_id = request.POST.get("document_id")
+            if not document_id:
+                return fail("文档ID不能为空")
+
+            # 获取 DocumentKbm 对象
+            document = get_object_or_404(DocumentKbm, id=document_id)
+
+            object_name = document.location
+
+            # 获取对应的 Knowledgebase 对象
+            knowledgebase = get_object_or_404(Knowledgebase, id=document.kb_id)
+            bucket_name = knowledgebase.location
+
+            return MinioService.geturl(object_name, bucket_name)
+
+        except ObjectDoesNotExist:
+            return fail("指定的文档或知识库不存在")
+        except Exception as e:
+            return fail(f"获取URL失败: {str(e)}")
+
+
+    #新rabbitmq队列
+    @staticmethod
+    def analysis(request):
+        document_id = request.POST.get("document_id")
+        start_page = int(request.POST.get('start_page', 1))
+        end_page = int(request.POST.get('end_page', -1))
+        max_tokens = int(request.POST.get('max_tokens', 2048))
+        if max_tokens == 0:
+            max_tokens = 2048
+
+        logger.info(f"开始处理文档 ID: {document_id}")
+
+        try:
+            document = DocumentKbm.objects.get(id=document_id)
+            if int(document.run) in [1, 5]:  # 1: 处理中, 5: 等待处理
+                logger.info(f"文档 {document_id} 已有队列")
+                return success("文档正在处理中或已经处理完成")
+
+            # 准备消息
+            message = {
+                'document_id': document_id,
+                'start_page': start_page,
+                'end_page': end_page,
+                'max_tokens': max_tokens
+            }
+
+            # 发送消息到队列
+            if KbmService.send_to_rabbitmq(settings.RABBITMQ_QUEUE_NAME, message):
+                # 更新文档状态为等待处理
+                document.run = 5  # 5表示等待处理
+                document.save()
+                logger.info(f"文档 {document_id} 状态已更新为等待处理")
+                return success("文档已添加到处理队列")
+            else:
+                document.run = 4
+                document.save()
+                return fail("添加文档到处理队列失败")
+
+        except DocumentKbm.DoesNotExist:
+            logger.error(f"文档 {document_id} 不存在")
+            document.run = 4
+            document.save()
+            return fail("文档不存在")
+        except Exception as e:
+            logger.error(f"处理文档 {document_id} 时出错: {str(e)}")
+            document.run = 4
+            document.save()
+            return fail("处理文档时出错")
+
+    semaphore = threading.Semaphore(4)
+    # @staticmethod
+    # def process_queue():
+    #     logger.info("开始监测RabbitMQ队列")
+    #     connection = pika.BlockingConnection(pika.ConnectionParameters(
+    #         host=settings.RABBITMQ_HOST,
+    #         port=settings.RABBITMQ_PORT,
+    #         credentials=pika.PlainCredentials(
+    #             settings.RABBITMQ_USER,
+    #             settings.RABBITMQ_PASSWORD
+    #         )
+    #     ))
+    #     channel = connection.channel()
+    #     channel.queue_declare(queue=settings.RABBITMQ_QUEUE_NAME, durable=True)
+    #
+    #     def callback(ch, method, properties, body):
+    #         with KbmService.semaphore:
+    #             try:
+    #                 job = json.loads(body)
+    #                 document_id = job['document_id']
+    #                 start_page = job['start_page']
+    #                 end_page = job['end_page']
+    #                 max_tokens = job['max_tokens']
+    #
+    #                 logger.info(f"开始执行解析文档 {document_id}")
+    #                 KbmService.async_analysis(document_id, start_page, end_page, max_tokens)
+    #
+    #                 # 处理成功,确认消息
+    #                 ch.basic_ack(delivery_tag=method.delivery_tag)
+    #             except Exception as e:
+    #                 logger.error(f"处理队列消息时发生错误: {str(e)}")
+    #                 # 处理失败,拒绝消息并重新入队
+    #                 ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
+    #
+    #     # 设置预取计数为4,与最大并发数相匹配
+    #     channel.basic_qos(prefetch_count=4)
+    #     channel.basic_consume(queue=settings.RABBITMQ_QUEUE_NAME, on_message_callback=callback)
+    #
+    #     logger.info('等待队列消息。要退出请按 CTRL+C')
+    #     channel.start_consuming()
+    connection = None
+    channel = None
+    should_stop = False
+
+    @staticmethod
+    def check_and_process_queue():
+        KbmService.should_stop = False
+        while not KbmService.should_stop:
+            try:
+                if KbmService.queue_has_messages():
+                    KbmService.process_queue()
+                else:
+                    logger.info("队列为空,等待下一次检查...")
+                    time.sleep(60)  # 等待60秒后再次检查
+            except Exception as e:
+                logger.error(f"检查队列时发生错误: {str(e)}")
+                time.sleep(60)  # 发生错误时,等待60秒后重试
+
+    @staticmethod
+    def queue_has_messages():
+        try:
+            connection = KbmService.create_connection()
+            channel = connection.channel()
+            queue = channel.queue_declare(queue=settings.RABBITMQ_QUEUE_NAME, passive=True)
+            message_count = queue.method.message_count
+            connection.close()
+            return message_count > 0
+        except Exception as e:
+            logger.error(f"检查队列消息数量时发生错误: {str(e)}")
+            return False
+
+    @staticmethod
+    def create_connection():
+        return pika.BlockingConnection(pika.ConnectionParameters(
+            host=settings.RABBITMQ_HOST,
+            port=settings.RABBITMQ_PORT,
+            credentials=pika.PlainCredentials(
+                settings.RABBITMQ_USER,
+                settings.RABBITMQ_PASSWORD
+            )
+        ))
+
+    @staticmethod
+    def process_queue():
+        logger.info("队列中有消息,开始处理...")
+        KbmService.connection = KbmService.create_connection()
+        KbmService.channel = KbmService.connection.channel()
+        KbmService.channel.queue_declare(queue=settings.RABBITMQ_QUEUE_NAME, durable=True)
+        KbmService.channel.basic_qos(prefetch_count=4)
+        KbmService.channel.basic_consume(queue=settings.RABBITMQ_QUEUE_NAME, on_message_callback=KbmService.callback)
+
+        try:
+            KbmService.channel.start_consuming()
+        except KeyboardInterrupt:
+            KbmService.should_stop = True
+        finally:
+            KbmService.close_connection()
+
+    @staticmethod
+    def callback(ch, method, properties, body):
+        with KbmService.semaphore:
+            try:
+                job = json.loads(body)
+                document_id = job['document_id']
+                start_page = job['start_page']
+                end_page = job['end_page']
+                max_tokens = job['max_tokens']
+
+                logger.info(f"开始执行解析文档 {document_id}")
+                KbmService.async_analysis(document_id, start_page, end_page, max_tokens)
+                ch.basic_ack(delivery_tag=method.delivery_tag)
+            except Exception as e:
+                logger.error(f"处理队列消息时发生错误: {str(e)}")
+                ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
+
+            # 检查是否还有更多消息
+            if not KbmService.queue_has_messages():
+                logger.info("队列处理完毕,停止消费...")
+                ch.stop_consuming()
+
+    @staticmethod
+    def close_connection():
+        if KbmService.channel:
+            try:
+                KbmService.channel.close()
+            except Exception:
+                pass
+        if KbmService.connection:
+            try:
+                KbmService.connection.close()
+            except Exception:
+                pass
+        KbmService.channel = None
+        KbmService.connection = None
+
+    @staticmethod
+    def stop_service():
+        KbmService.should_stop = True
+        if KbmService.channel:
+            KbmService.channel.stop_consuming()
+        KbmService.close_connection()
+    @staticmethod
+    def get_embedding_excel(text, target_dim=768):
+        """
+        获取文本的嵌入向量
+        """
+        try:
+            if not text or not text.strip():
+                logging.warning("Empty text provided for embedding. Returning zero vector.")
+                return np.zeros(target_dim).tolist()
+
+            # 确保文本被正确编码
+            encoded_text = text.encode('utf-8').decode('utf-8')
+
+            payload = {
+                "model": "nomic-embed-text:latest",
+                "prompt": encoded_text
+            }
+            headers = {"Content-Type": "application/json"}
+
+            response = requests.post(KbmService.API_URL, json=payload, headers=headers)
+            logger.info(f"response::::{response}")
+            response.raise_for_status()
+            embedding_data = response.json()
+
+            if 'embedding' not in embedding_data:
+                raise ValueError(f"API 响应中没有找到嵌入向量. 响应内容: {embedding_data}")
+
+            embedding = embedding_data['embedding']
+            original_embedding = np.array(embedding)
+
+            if len(original_embedding) == target_dim:
+                return original_embedding.tolist()
+
+            # 如果原始维度不等于目标维度,进行插值
+            original_indices = np.arange(len(original_embedding))
+            new_indices = np.linspace(0, len(original_embedding) - 1, target_dim)
+            f = interpolate.interp1d(original_indices, original_embedding)
+            extended_embedding = f(new_indices)
+
+            return extended_embedding.tolist()
+
+        except requests.exceptions.RequestException as e:
+            logging.error(f"API 请求错误: {str(e)}")
+            raise
+        except ValueError as e:
+            logging.error(f"值错误: {str(e)}")
+            raise
+        except Exception as e:
+            logging.error(f"获取文本嵌入时发生意外错误: {str(e)}")
+            raise
+
+    @classmethod
+    def get_embedding_pdf(cls, text, target_dim=768, max_retries=3, backoff_factor=0.3):
+        """
+        获取文本的嵌入向量,并填充或截断到目标维度,包含重试机制
+        """
+        for attempt in range(max_retries):
+            try:
+                payload = {
+                    "model": "nomic-embed-text:latest",
+                    "prompt": text
+                }
+                headers = {
+                    "Content-Type": "application/json"
+                }
+
+                response = requests.post(cls.API_URL, json=payload, headers=headers, timeout=30)
+                sleep(0.3)
+                response.raise_for_status()
+                result = response.json()
+
+                embedding = result.get('embedding')
+
+                if embedding is None:
+                    raise ValueError("API 响应中没有找到嵌入向量")
+
+                embedding_array = np.array(embedding)
+                current_dim = embedding_array.shape[0]
+
+                if current_dim < target_dim:
+                    padded_embedding = np.pad(embedding_array, (0, target_dim - current_dim), 'constant')
+                    logging.info(f"向量已从 {current_dim} 维填充到 {target_dim} 维")
+                    return padded_embedding
+                elif current_dim > target_dim:
+                    truncated_embedding = embedding_array[:target_dim]
+                    logging.info(f"向量已从 {current_dim} 维截断到 {target_dim} 维")
+                    return truncated_embedding
+                else:
+                    return embedding_array
+
+            except RequestException as e:
+                logging.error(f"API 请求错误 (尝试 {attempt + 1}/{max_retries}): {e}")
+                if attempt == max_retries - 1:
+                    raise
+                time.sleep(backoff_factor * (2 ** attempt))
+            except ValueError as e:
+                logging.error(f"解析响应错误: {e}")
+                raise
+            except Exception as e:
+                logging.error(f"获取文本嵌入时发生未知错误: {e}")
+                raise
+
+        raise Exception("达到最大重试次数,无法获取嵌入")
+
+    @staticmethod
+    def split_text_by_semantic(text, max_tokens, bucket_name, similarity_threshold=0.5, batch_size=1000):
+        logging.info("开始分割文本并保存到向量数据库")
+
+        try:
+            #object1   object1为后续可能添加的字段  因为无法直接修改名称  备用
+            source = "知识库"
+            object1 ="some_object1"
+            object2 ="some_object2"
+            # 连接到Milvus
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT,user=MILVUS_USER,password=MILVUS_PASSWORD)
+            collection_name = f"{bucket_name}"
+            collection = KbmService._get_or_create_collection(collection_name)
+
+            sentences = KbmService._split_sentences(text)
+            if not sentences:
+                logging.warning("没有找到有效的句子,将文本按最大令牌数分割")
+                sentences = [text[i:i + max_tokens] for i in range(0, len(text), max_tokens)]
+
+            chunks = []
+            current_chunk = sentences[0]
+            current_embedding = KbmService.get_embedding_pdf(current_chunk, target_dim=VECTOR_DIMENSION)
+            batch_data = []
+
+            for sentence in sentences[1:]:
+                sentence_embedding = KbmService.get_embedding_pdf(sentence, target_dim=VECTOR_DIMENSION)
+                similarity = 1 - cosine(current_embedding, sentence_embedding)
+
+                if len(current_chunk) + len(sentence) <= max_tokens and similarity >= similarity_threshold:
+                    current_chunk += sentence
+                    current_embedding = (current_embedding + sentence_embedding) / 2
+                else:
+                    batch_data.append((current_chunk, current_embedding))
+                    if len(batch_data) >= batch_size:
+                        ids = KbmService._insert_batch(collection, batch_data,source,object1,object2)
+                        sleep(1)
+                        logger.info("减少milvus压力睡眠1秒")
+                        if ids is not None:
+                            chunks.extend([{'content': chunk, 'milvus_id': id} for (chunk, _), id in zip(batch_data, ids)])
+                        else:
+                            logging.error("向 Milvus 插入批量数据失败,这批数据将被跳过")
+                        batch_data = []
+                    current_chunk = sentence
+                    current_embedding = sentence_embedding
+
+            # 处理最后一个chunk和剩余的batch数据
+            if current_chunk:
+                batch_data.append((current_chunk, current_embedding))
+            if batch_data:
+                ids = KbmService._insert_batch(collection, batch_data,source,object1,object2)
+                sleep(1)
+                logger.info("减少milvus压力睡眠1秒")
+                if ids is not None:
+                    chunks.extend([{'content': chunk, 'milvus_id': id} for (chunk, _), id in zip(batch_data, ids)])
+                else:
+                    logging.error("向 Milvus 插入批量数据失败,这批数据将被跳过")
+
+            KbmService._create_index_and_load(collection)
+
+            logging.info(f"成功将{len(chunks)}个文本块分割并保存到Milvus")
+            return chunks
+
+        except Exception as e:
+            logging.error(f"处理文本时发生错误: {str(e)}")
+            raise
+        finally:
+            connections.disconnect("default")
+
+    @staticmethod
+    def _get_or_create_collection(collection_name):
+        if not utility.has_collection(collection_name):
+            fields = [
+                FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
+                FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=65000),
+                FieldSchema(name="object1", dtype=DataType.VARCHAR, max_length=65000),
+                FieldSchema(name="object2", dtype=DataType.VARCHAR, max_length=65000),
+                FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=65000),
+                FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=VECTOR_DIMENSION)
+            ]
+            schema = CollectionSchema(fields, "Semantic text chunks collection")
+            return Collection(name=collection_name, schema=schema)
+        return Collection(name=collection_name)
+
+    @staticmethod
+    def _split_sentences(text):
+        sentences = re.split('([。!?])', text)
+        sentences = [''.join(i) for i in zip(sentences[0::2], sentences[1::2] + [''])]
+        return [s.strip() for s in sentences if s.strip()]
+
+    @staticmethod
+    def _insert_batch(collection, batch_data, source, object1, object2):
+        try:
+            entities = [
+                [source] * len(batch_data),  # source
+                [object1]* len(batch_data),  # object1
+                [object2]* len(batch_data),  # object2
+                [chunk for chunk, _ in batch_data],
+                [embedding.tolist() for _, embedding in batch_data]
+            ]
+            # 插入数据并获取插入操作的结果
+            insert_result = collection.insert(entities)
+
+            # 获取插入的 ID
+            inserted_ids = insert_result.primary_keys
+            return inserted_ids
+
+            logging.info(f"成功插入{len(batch_data)}个文本块到Milvus")
+        except Exception as e:
+            logging.error(f"批量插入数据时发生错误: {str(e)}")
+
+    @staticmethod
+    def _create_index_and_load(collection):
+        index_params = {
+            "index_type": "IVF_FLAT",
+            "metric_type": "L2",
+            "params": {"nlist": 768}
+        }
+        collection.create_index("embedding", index_params)
+        collection.load()
+
+
+
+
+
+    @staticmethod
+    def async_analysis(document_id, start_page, end_page, max_tokens):
+        start_time = time.time()
+        excel_status = 1
+        try:
+            logger.info(f"开始处理文档 {document_id}")
+
+            # 更新文档状态为处理中
+            DocumentKbm.objects.filter(id=document_id).update(run=1)
+
+            document = get_object_or_404(DocumentKbm, id=document_id)
+            object_name = document.location
+            file_extension = object_name.split('.')[-1].lower()
+
+            knowledgebase = get_object_or_404(Knowledgebase, id=document.kb_id)
+            bucket_name = knowledgebase.location
+
+            KbmService.clearPreviousData(document_id, bucket_name)
+
+            response = minio_client.get_object(bucket_name, object_name)
+            file_content = BytesIO(response.read())
+
+            if file_extension in ['xls', 'xlsx']:
+                result, excel_status = KbmService.process_excel(file_content, document_id, max_tokens, bucket_name)
+            elif file_extension == 'pdf':
+                result = KbmService.process_pdf(file_content, document_id, max_tokens,bucket_name)
+            # elif file_extension == 'txt':
+            #     result = KbmService.process_txt(file_content, document_id, max_tokens, bucket_name)
+            elif file_extension == 'md':
+                result = KbmService.process_markdown(file_content, document_id, max_tokens, bucket_name)
+            elif file_extension in ['doc', 'docx']:
+                pdf_content = KbmService.convert_doc_to_pdf(file_content)
+                result = KbmService.process_pdf(pdf_content, document_id, max_tokens, bucket_name)
+            else:
+                raise ValueError(f"Unsupported file type: {file_extension}")
+
+            KbmService.saveTask(document_id, len(result))
+            end_time = time.time()
+            execution_time = round(end_time - start_time, 2)
+            KbmService.updateDocument(max_tokens, len(result), document_id, execution_time)
+
+            # 更新文档状态
+            if excel_status == 6:
+                DocumentKbm.objects.filter(id=document_id).update(run=6)  # Excel 特殊情况
+                logger.info(f"文档 {document_id} 更新完成,状态设置为6(Excel特殊情况)")
+            else:
+                DocumentKbm.objects.filter(id=document_id).update(run=3)  # 假设3表示成功状态
+                logger.info(f"文档 {document_id} 更新完成,状态设置为3(成功)")
+        except Exception as e:
+            logger.error(f"Analysis failed for document {document_id}: {str(e)}")
+            logger.error("Exception traceback:")
+            traceback.print_exc()
+            # 更新文档状态为失败
+            DocumentKbm.objects.filter(id=document_id).update(run=4)
+
+        logger.info(f"文档 {document_id} 处理完成")
+
+    @staticmethod
+    def convert_doc_to_pdf(file_content):
+        try:
+            # 创建临时文件
+            with tempfile.NamedTemporaryFile(delete=False, suffix='.docx') as temp_input:
+                temp_input.write(file_content.getvalue())
+                temp_input_path = temp_input.name
+
+            temp_output_dir = tempfile.mkdtemp()
+
+            # 查找 LibreOffice 路径
+            libreoffice_path = KbmService.get_libreoffice_path()
+            if not libreoffice_path:
+                raise FileNotFoundError("找不到 LibreOffice 可执行文件")
+
+            # 转换为 PDF
+            pdf_path = KbmService.run_libreoffice_conversion(libreoffice_path, temp_input_path, temp_output_dir)
+
+            # 读取 PDF 内容
+            with open(pdf_path, 'rb') as pdf_file:
+                pdf_content = pdf_file.read()
+
+
+            # 读取并返回 PDF 内容
+            with open(pdf_path, 'rb') as pdf_file:
+                return BytesIO(pdf_file.read())
+
+        except Exception as e:
+            logging.error(f"将文档转换为 PDF 时出错: {str(e)}", exc_info=True)
+            return BytesIO()
+
+        finally:
+            KbmService.cleanup_temp_files(temp_input_path, temp_output_dir)
+
+    @staticmethod
+    def get_libreoffice_path():
+        system = platform.system()
+        if system == "Windows":
+            libreoffice_paths = [r"D:\Program Files\libreoffice\program\soffice.exe"]
+            return next((path for path in libreoffice_paths if os.path.exists(path)), None)
+        else:  # Linux 或 macOS
+            for path in ['/usr/bin/libreoffice', '/usr/bin/soffice', '/opt/libreoffice/program/soffice']:
+                if os.path.exists(path):
+                    return path
+            return shutil.which('libreoffice') or shutil.which('soffice')
+
+    @staticmethod
+    def run_libreoffice_conversion(libreoffice_path, input_path, output_dir):
+        cmd = [
+            libreoffice_path,
+            '--headless',
+            '--convert-to', 'pdf:writer_pdf_Export:{"PageSize":{"Width":21000,"Height":29700}}',
+            '--outdir', output_dir,
+            input_path
+        ]
+        try:
+            env = os.environ.copy()
+            env['HOME'] = '/mnt/ql_api/tmp'  # 设置一个临时的 HOME 目录
+            env['LC_ALL'] = 'C'  # 设置一个标准的语言环境
+            sleep(0.3)
+            result = subprocess.run(cmd, check=True, capture_output=True, text=True, timeout=60, env=env)
+            logging.info(f"LibreOffice 转换输出: {result.stdout}")
+
+            pdf_filename = os.path.splitext(os.path.basename(input_path))[0] + '.pdf'
+            pdf_path = os.path.join(output_dir, pdf_filename)
+
+            if not os.path.exists(pdf_path):
+                raise FileNotFoundError(f"PDF 文件未生成。输出目录内容: {os.listdir(output_dir)}")
+
+            # 使用 PyPDF2 检查页数
+            with open(pdf_path, 'rb') as pdf_file:
+                pdf_reader = PyPDF2.PdfReader(pdf_file)
+                page_count = len(pdf_reader.pages)
+                logging.info(f"生成的 PDF 文件页数: {page_count}")
+
+            pdf_size = os.path.getsize(pdf_path)
+            if pdf_size < 1000:
+                logging.warning(f"生成的 PDF 文件大小异常小: {pdf_size} bytes")
+
+            return pdf_path
+        except subprocess.TimeoutExpired:
+            raise TimeoutError("LibreOffice 转换超时")
+        except subprocess.CalledProcessError as e:
+            raise RuntimeError(f"LibreOffice 转换失败: {e.output}")
+
+    @staticmethod
+    def cleanup_temp_files(temp_input_path, temp_output_dir):
+        if os.path.exists(temp_input_path):
+            os.remove(temp_input_path)
+        if os.path.exists(temp_output_dir):
+            shutil.rmtree(temp_output_dir)
+    @staticmethod
+    def process_excel(file_content, document_id, max_tokens, bucket_name):
+        result = []
+        collection_name = f"{bucket_name}"
+        status=1
+        # object1   object1为后续可能添加的字段  因为无法直接修改名称  备用
+        source = "知识库"
+        object1 = "some_object1"
+        object2 = "some_object2"
+
+        def warning_catcher(message, category, filename, lineno, file=None, line=None):
+            nonlocal status
+            if category == UserWarning:
+                if "File contains an invalid specification for 0" in str(message) or \
+                        "Defined names for sheet index 0 cannot be located" in str(message):
+                    status = 6
+            logger.error(f"Warning: {message}")
+
+        warnings.showwarning = warning_catcher
+        try:
+            excel_file = pd.ExcelFile(file_content)
+        except Exception as e:
+            logging.error(f"Error reading Excel file: {str(e)}")
+            return result
+
+
+        try:
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT,user=MILVUS_USER,password=MILVUS_PASSWORD)
+            collection = KbmService._get_or_create_collection(collection_name)
+
+            for sheet_name in excel_file.sheet_names:
+                df = pd.read_excel(excel_file, sheet_name=sheet_name)
+                if df.empty:
+                    logging.warning(f"Sheet '{sheet_name}' is empty. Skipping.")
+                    continue
+
+                logging.info(f"Processing sheet '{sheet_name}' with shape {df.shape}")
+
+                markdown_content = KbmService._excel_to_markdown(df, sheet_name)
+                chunks = KbmService._split_markdown(markdown_content, max_tokens)
+
+                for chunk_number, chunk_content in enumerate(chunks, start=1):
+                    try:
+                        if not chunk_content.strip():
+                            logging.warning(f"Empty chunk {chunk_number} in sheet '{sheet_name}'. Skipping.")
+                            continue
+
+                        embedding = KbmService.get_embedding_excel(chunk_content, target_dim=VECTOR_DIMENSION)
+
+                        if isinstance(embedding, (list, np.ndarray)) and len(embedding) == VECTOR_DIMENSION:
+                            sleep(1)
+                            logger.info("减少milvus压力睡眠1秒")
+                            milvus_id = KbmService._insert_data(collection, chunk_content, embedding,source,object1,object2)
+                        else:
+                            logging.error(f"Invalid embedding format for chunk {chunk_number} of sheet {sheet_name}.")
+                            continue
+
+                        KbmService.saveTaskSublist(
+                            document_id=document_id,
+                            name=f"sheet_{sheet_name}",
+                            page_number=1,
+                            chunk_number=chunk_number,
+                            content=chunk_content,
+                            milvus_id=milvus_id
+                        )
+
+                        result.append({
+                            'page_number': 1,
+                            'chunk_number': chunk_number,
+                        })
+
+                    except Exception as e:
+                        logging.error(f"Error processing chunk {chunk_number} of sheet {sheet_name}: {str(e)}")
+                        logging.error(f"Chunk content: {chunk_content}")
+                        logging.exception("Detailed error information:")
+
+        except Exception as e:
+            logger.error(f"Error processing Excel file: {str(e)}")
+            logger.error("Detailed error information:")
+            import traceback
+            logger.error(traceback.format_exc())
+            status = 6  # 设置状态为6,表示处理出错
+            raise
+
+        finally:
+            connections.disconnect("default")
+
+        return result, status
+    #excel转markdown
+    @staticmethod
+    def _excel_to_markdown(df, sheet_name):
+        if df.empty:
+            return f"# {sheet_name}\n\n表格为空"
+
+        headers = df.columns.tolist()
+        data = df.values.tolist()
+
+        # 将所有数据转换为字符串
+        data = [[str(cell) for cell in row] for row in data]
+
+        markdown = f"# {sheet_name}\n\n"
+        markdown += tabulate(data, headers=headers, tablefmt="pipe", showindex=False)
+        return markdown
+    #excel分割策略
+    @staticmethod
+    def _split_json(json_str, max_tokens):
+        # 简单的分割策略,可以根据需要优化
+        data = json.loads(json_str)
+        chunks = []
+        current_chunk = []
+        current_size = 0
+
+        for item in data:
+            item_str = json.dumps(item)
+            item_size = len(item_str)
+            if current_size + item_size > max_tokens and current_chunk:
+                chunks.append(json.dumps(current_chunk))
+                current_chunk = []
+                current_size = 0
+            current_chunk.append(item)
+            current_size += item_size
+
+        if current_chunk:
+            chunks.append(json.dumps(current_chunk))
+
+        return chunks
+
+    @staticmethod
+    def _split_markdown(markdown_content, max_tokens):
+        chunks = []
+        current_chunk = ""
+        lines = markdown_content.split('\n')
+
+        for line in lines:
+            if len(current_chunk) + len(line) + 1 > max_tokens:
+                if current_chunk:
+                    chunks.append(current_chunk.strip())
+                current_chunk = line
+            else:
+                current_chunk += '\n' + line if current_chunk else line
+
+        if current_chunk:
+            chunks.append(current_chunk.strip())
+
+        return chunks
+    #milvus excel插入格式
+    @staticmethod
+    def _insert_data(collection, content, embedding,source,object1,object2):
+        try:
+            data = [
+                [source],  # content field
+                [object1],  # content field
+                [object2],  # content field
+                [content],  # content field
+                [embedding]  # embedding field
+            ]
+            insert_result = collection.insert(data)
+            logging.info(f"Inserted 1 record into Milvus")
+            return insert_result.primary_keys[0]  # 返回插入的 ID
+        except Exception as e:
+            logging.error(f"Error inserting data into Milvus: {str(e)}")
+            raise
+    #创建milvus索引
+    @staticmethod
+    def _create_index_if_not_exists(collection):
+        if not collection.has_index():
+            index_params = {
+                "index_type": "IVF_FLAT",
+                "metric_type": "L2",
+                "params": {"nlist": 768}
+            }
+            collection.create_index("embedding", index_params)
+
+    @staticmethod
+    def process_pdf(file_content, document_id, max_tokens, bucket_name):
+        logger.info(f"开始解析pdf")
+        doc = fitz.open(stream=file_content, filetype="pdf")
+        total_pages = len(doc)
+
+        # 提取整个文档的文本
+        full_text = ""
+        image_texts = []
+        page_images = []
+
+        for page_num in range(total_pages):
+            page = doc[page_num]
+            full_text += page.get_text()
+
+            # OCR识别图片
+            for img in page.get_images():
+                xref = img[0]
+                base_image = doc.extract_image(xref)
+                image_data = base_image["image"]
+                logger.info("开始ocr")
+                image_text = KbmService.extract_text_from_image(image_data)
+                sleep(1)
+                logger.info("识别完成,防止调用频繁睡眠1秒")
+                if image_text:
+                    image_texts.append(image_text)
+
+            # 渲染页面为图像并保存
+            page_image = KbmService.render_page_to_image(page)
+            image_name = KbmService.save_image_to_minio(page_image, bucket_name)
+            page_images.append((page_num + 1, image_name))
+        # 将图片文本添加到全文中
+        if image_texts:
+            full_text += " ".join(image_texts)
+
+        # 对整个文本进行语义分割
+        text_chunks = KbmService.split_text_by_semantic(full_text, max_tokens, bucket_name)
+        logger.info("分割完成")
+
+        result = []
+        for i, chunk in enumerate(text_chunks):
+            # 为每个chunk分配一个页面图像
+            page_number, image_name = page_images[min(i, len(page_images) - 1)]
+
+            KbmService.saveTaskSublist(
+                document_id=document_id,
+                name=image_name,
+                page_number=page_number,
+                chunk_number=i + 1,
+                content=chunk['content'],
+                milvus_id=chunk['milvus_id']
+            )
+            result.append({
+                'page_number': page_number,
+                'chunk_number': i + 1,
+            })
+
+        doc.close()
+        logger.info("解析结束")
+        return result
+    # @staticmethod
+    # def process_pdf(file_content, document_id, start_page, end_page, max_tokens, bucket_name):
+    #     print(f"开始解析pdf:::: {str(file_content)}")
+    #     doc = fitz.open(stream=file_content, filetype="pdf")
+    #     total_pages = len(doc)
+    #
+    #     start_page = max(1, start_page) - 1
+    #     end_page = min(total_pages, end_page if end_page > 0 else total_pages)
+    #     result = []
+    #     for page_num in range(start_page, end_page):
+    #         page = doc[page_num]
+    #         text = page.get_text()
+    #
+    #         image_texts = []
+    #         for img in page.get_images():
+    #             xref = img[0]
+    #             base_image = doc.extract_image(xref)
+    #             image_data = base_image["image"]
+    #             #OCR识别图片
+    #             print("开始ocr")
+    #             image_text = KbmService.extract_text_from_image(image_data)
+    #             sleep(1)
+    #             print("识别完成,防止调用频繁睡眠1秒")
+    #             if image_text:
+    #                 image_texts.append(image_text)
+    #         if image_texts:
+    #             text += "".join(image_texts)
+    #
+    #         # 使用语义分割替代简单的文本分割
+    #         text_chunks = KbmService.split_text_by_semantic(text, max_tokens,bucket_name)
+    #         print("分割完成")
+    #         page_image = KbmService.render_page_to_image(page)
+    #         print("转化图片")
+    #         image_name = KbmService.save_image_to_minio(page_image, bucket_name)
+    #         print("上传minio")
+    #         for i, chunk in enumerate(text_chunks):
+    #             KbmService.saveTaskSublist(
+    #                 document_id=document_id,
+    #                 name=image_name,
+    #                 page_number=page_num + 1,
+    #                 chunk_number=i + 1,
+    #                 content=chunk['content'],
+    #                 milvus_id=chunk['milvus_id']
+    #             )
+    #             result.append({
+    #                 'page_number': page_num + 1,
+    #                 'chunk_number': i + 1,
+    #             })
+    #
+    #     doc.close()
+    #     print("解析结束")
+    #     return result
+    #解析markdown
+    @staticmethod
+    def process_markdown(file_content, document_id, max_tokens, bucket_name):
+        logging.info(f"开始解析 Markdown,document_id: {document_id}, max_tokens: {max_tokens}")
+        try:
+            # 检测文件编码
+            raw_content = file_content.read()
+            detected = chardet.detect(raw_content)
+            encoding = detected['encoding']
+            logging.info(f"检测到的文件编码: {encoding}")
+
+            # 解码文件内容
+            text = raw_content.decode(encoding)
+            logging.info(f"Markdown 文件总字符数: {len(text)}")
+            logging.debug(f"Markdown 文件前100个字符: {text[:100]}")
+
+            # 将 Markdown 转换为 HTML
+            html = markdown.markdown(text)
+
+            # 使用 BeautifulSoup 提取纯文本
+            soup = BeautifulSoup(html, 'html.parser')
+            plain_text = soup.get_text()
+
+            logging.info(f"提取的纯文本总字符数: {len(plain_text)}")
+            logging.debug(f"提取的纯文本前100个字符: {plain_text[:100]}")
+
+            if not plain_text.strip():
+                logging.warning("Markdown 文件内容为空")
+                return []
+
+            # 使用 split_text_by_semantic 方法分割文本
+            text_chunks = KbmService.split_text_by_semantic(plain_text, max_tokens, bucket_name)
+            logging.info(f"分割后的文本块数: {len(text_chunks)}")
+
+            result = []
+            for i, chunk in enumerate(text_chunks, 1):
+                KbmService.saveTaskSublist(
+                    document_id=document_id,
+                    name="markdown_content",
+                    page_number=1,
+                    chunk_number=i,
+                    content=chunk['content'],
+                    milvus_id=chunk['milvus_id']
+                )
+                result.append({
+                    'page_number': 1,
+                    'chunk_number': i,
+                })
+
+            logging.info(f"Markdown 处理完成,总共生成 {len(result)} 个文本块")
+            return result
+
+        except Exception as e:
+            logging.error(f"处理 Markdown 时发生错误: {str(e)}")
+            logging.exception("详细错误信息:")
+            return []
+
+    @staticmethod
+    def split_text(text, max_tokens):
+        words = text.split()
+        chunks = []
+        current_chunk = []
+        current_token_count = 0
+
+        for word in words:
+            word_tokens = KbmService.estimate_tokens(word)
+            if current_token_count + word_tokens > max_tokens and current_chunk:
+                chunks.append(' '.join(current_chunk))
+                current_chunk = []
+                current_token_count = 0
+            current_chunk.append(word)
+            current_token_count += word_tokens
+
+        if current_chunk:
+            chunks.append(' '.join(current_chunk))
+
+        return chunks
+
+    @staticmethod
+    def estimate_tokens(text):
+        return len(re.findall(r'\w+', text)) * 1.3
+    #OCR图片识别
+    ocr = PaddleOCR(use_angle_cls=True, lang="ch", use_gpu=False, det_db_thresh=0.3, det_db_box_thresh=0.3)
+
+    @staticmethod
+    def extract_text_from_image(image_data):
+        try:
+            if isinstance(image_data, BytesIO):
+                image_data = image_data.getvalue()
+
+            # 读取图像
+            nparr = np.frombuffer(image_data, np.uint8)
+            image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
+
+            # 图像预处理
+            preprocessed = KbmService.preprocess_image(image)
+
+            # 使用PaddleOCR进行识别
+            result = KbmService.ocr.ocr(preprocessed, cls=True)
+
+            # 提取文本并去除所有空格
+            if result and isinstance(result[0], list):
+                text = ''.join(
+                    [line[1][0].replace(' ', '') for line in result[0] if line[1][1] > 0.5])  # 只保留置信度大于0.5的结果,并去除空格
+            else:
+                text = ""
+
+            # 后处理
+            text = KbmService.post_process_text(text)
+
+            if text and len(text) > 2:  # 假设有意义的文本至少有3个字符
+                logging.info(f"提取的文本长度: {len(text)}")
+                return text
+            else:
+                logging.info("提取的内容似乎是图像,而不是文本")
+                return "图片"
+
+        except Exception as e:
+            logging.error(f"从图像提取文本时出错: {str(e)}")
+            return "图片"
+
+    @staticmethod
+    def preprocess_image(image):
+        # 转换为灰度图像
+        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
+
+        # 自适应阈值处理
+        binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
+
+        # 对二值图像进行膨胀操作,使文字更粗
+        kernel = np.ones((2, 2), np.uint8)
+        dilated = cv2.dilate(binary, kernel, iterations=1)
+
+        return dilated
+
+    def post_process_text(text):
+        # 将连续的冒号或点替换为空格
+        text = re.sub(r'[:.]+', ' ', text)
+
+        # 保留中文字符、英文字母、数字、常用标点
+        text = re.sub(r'[^\u4e00-\u9fff\u3000-\u303fa-zA-Z0-9.,!?;:()"\'\s]', '', text)
+
+        # 删除连续的数字(3个或更多)
+        text = re.sub(r'\d{3,}', '', text)
+
+        # 处理多余的空白字符
+        text = re.sub(r'\s+', ' ', text).strip()
+
+        # 删除单独的数字,但保留章节编号和有意义的数字
+        text = re.sub(r'\b(?<![第章])\d+(?!\d)\b', '', text)
+
+        # 清理多余的空格
+        text = re.sub(r'\s+', ' ', text).strip()
+
+        return text
+
+
+    @staticmethod
+    def render_page_to_image(page, scale=2):
+        pix = page.get_pixmap(matrix=fitz.Matrix(scale, scale))
+        img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
+        buffered = BytesIO()
+        img.save(buffered, format="PNG")
+        buffered.seek(0)
+        return buffered
+
+    @staticmethod
+    def save_image_to_minio(image_data, bucket_name):
+        image_name = f"page_image_{uuid.uuid4()}.png"
+        minio_client.put_object(bucket_name, image_name, image_data, length=image_data.getbuffer().nbytes)
+        return image_name
+    @staticmethod
+    @transaction.atomic
+    def saveTask(document_id, total_chunks):
+        Task.objects.update_or_create(
+            doc_id=document_id,  # 查找条件
+            defaults={'to_page': total_chunks}  # 要更新或创建的字段
+        )
+
+    @staticmethod
+    @transaction.atomic
+    def saveTaskSublist(document_id, name, page_number=None, chunk_number=None, content=None,milvus_id=None):
+        try:
+            # 确保 content 是 Unicode 字符串
+            if content is not None:
+                if isinstance(content, bytes):
+                    content = content.decode('utf-8')
+                else:
+                    content = str(content)
+
+            TaskSublist.objects.create(
+                doc_id=document_id,
+                name=name,
+                page_number=str(page_number) if page_number is not None else '0',
+                chunk_number=str(chunk_number) if chunk_number is not None else '0',
+                content=content,
+                milvus_id=milvus_id
+            )
+            logging.info(f"Successfully saved TaskSublist for document {document_id}, chunk {chunk_number}")
+        except Exception as e:
+            logging.error(f"Error saving TaskSublist: {str(e)}")
+            # 可以选择在这里重新抛出异常,或者进行其他错误处理
+
+    @staticmethod
+    @transaction.atomic
+    def clearPreviousData(document_id,bucket_name):
+        try:
+            # 获取与文档相关的所有 TaskSublist,milvus 记录
+            task_sublists = TaskSublist.objects.filter(doc_id=document_id)
+            # 获取集合对象
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT)
+
+            milvus_collection_exists = utility.has_collection(bucket_name)
+            if milvus_collection_exists:
+                collection = Collection(bucket_name)
+            # 连接到 Milvus
+
+            # 从 MinIO 中删除相关的图片
+            for task in task_sublists:
+                try:
+                    # 执行删除minio
+                    minio_client.remove_object(bucket_name, task.name)
+
+                    # 执行删除milvus
+                    if milvus_collection_exists and task.milvus_id:
+                        expr = f'id in [{task.milvus_id}]'
+                        collection.delete(expr)
+                except Exception as e:
+                    logger.error(f"Error deleting object from MinIO: {e}")
+
+            # 从数据库中删除 TaskSublist 记录
+            task_sublists.delete()
+        except Exception as e:
+            logging.error(f"Error deleting object from MinIO: {str(e)}")
+        finally:
+            connections.disconnect("default")
+
+
+
+    @staticmethod
+    @transaction.atomic
+    def updateDocument(max_tokens, total_chunks, document_id,execution_time):
+        try:
+            # 检查是否存在相关的 TaskSublist
+            count = TaskSublist.objects.filter(doc_id=document_id).count()
+
+            # 根据 TaskSublist 的存在与否设置运行状态
+            progress_status = 1 if count > 0 else -1
+
+
+            # 更新 DocumentKbm 对象
+            updated = DocumentKbm.objects.filter(id=document_id).update(
+                token_num=max_tokens,
+                chunk_num=total_chunks,
+                progress=progress_status,
+                process_begin_at=timezone.now(),
+                process_duation= execution_time
+            )
+
+            if updated:
+                return True, f"Document {document_id} updated successfully."
+            else:
+                return False, f"Document {document_id} not found."
+
+        except Exception as e:
+            # 如果发生任何错误,事务会自动回滚
+            return False, f"Error updating document: {str(e)}"
+    #异步调用
+
+
+    @staticmethod
+    def searchTaskInfo(request):
+        document_id = request.POST.get("document_id")
+        page = request.POST.get('page', 1)
+        page_size = request.POST.get('page_size', 10)  # 每页显示的项目数,默认为10
+
+        taskSublists = TaskSublist.objects.filter(doc_id=document_id).order_by('id')
+
+        document = get_object_or_404(DocumentKbm, id=document_id)
+        location = document.location
+
+        knowledgebase = get_object_or_404(Knowledgebase, id=document.kb_id)
+        bucket_name = knowledgebase.location
+
+        documentUrl = minio_client.presigned_get_object(
+            bucket_name=bucket_name,
+            object_name=location,
+            expires=timedelta(days=1)  # URL有效期为1天
+        )
+
+        # 创建分页器
+        paginator = Paginator(taskSublists, page_size)
+
+        try:
+            tasks_page = paginator.page(page)
+        except PageNotAnInteger:
+            # 如果页码不是整数,返回第一页
+            tasks_page = paginator.page(1)
+        except EmptyPage:
+            # 如果页码超出范围,返回最后一页
+            tasks_page = paginator.page(paginator.num_pages)
+
+        task_results = []
+        for task in tasks_page:
+            try:
+                # 生成MinIO对象的预签名URL
+                url = minio_client.presigned_get_object(
+                    bucket_name=bucket_name,
+                    object_name=task.name,
+                    expires=timedelta(days=1)  # URL有效期为1天
+                )
+
+                task_results.append({
+                    'id': task.id,
+                    'doc_id': task.doc_id,
+                    'name': task.name,
+                    'page_number': task.page_number,
+                    'chunk_number': task.chunk_number,
+                    'content': task.content,
+                    'url': url
+                })
+            except Exception as e:
+                logger.error(f"Error generating URL for object {task.name}: {str(e)}")
+                # 如果生成URL失败,我们仍然添加其他信息,但URL为None
+                task_results.append({
+                    'id': task.id,
+                    'doc_id': task.doc_id,
+                    'name': task.name,
+                    'page_number': task.page_number,
+                    'chunk_number': task.chunk_number,
+                    'content': task.content,
+                    'url': None
+                })
+
+        # 创建包含 documentUrl 和分页信息的最终结果
+        result = {
+            'documentUrl': documentUrl,
+            'tasks': task_results,
+            'pagination': {
+                'current_page': tasks_page.number,
+                'num_pages': paginator.num_pages,
+                'per_page': page_size,
+                'total_count': paginator.count,
+                'has_next': tasks_page.has_next(),
+                'has_previous': tasks_page.has_previous(),
+            }
+        }
+
+        return success(result)
+
+    @staticmethod
+    @transaction.atomic
+    def deleteBucket(request):
+        bucket_id = request.POST.get("bucket_id")
+
+        if not bucket_id:
+            return fail("Bucket ID 为空")
+
+        try:
+            # 检查是否存在未删除的文档
+            active_docs_count = DocumentKbm.objects.filter(kb_id=bucket_id).exclude(status=4).count()
+
+            if active_docs_count > 0:
+                return fail(f"无法删除知识库,还有 {active_docs_count} 个未删除的文档")
+
+            # 如果没有未删除的文档,则更新知识库状态
+            updated_count = Knowledgebase.objects.filter(id=bucket_id).update(status=4,name=bucket_id, location=bucket_id)
+
+            if updated_count == 0:
+                return fail("指定的知识库不存在")
+
+            return success("知识库已成功删除")
+
+        except Exception as e:
+            return fail(f"删除知识库时发生错误: {str(e)}")
+
+    @staticmethod
+    def getRunStatus(request):
+        document_id = request.POST.get("document_id")
+        run = DocumentKbm.objects.filter(id = document_id).values("run").first()
+        return success(run)
+    @staticmethod
+    def batchAnalysis(request):
+        ids_str = request.POST.get("ids")
+        start_page = int(request.POST.get('start_page', 1))
+        end_page = int(request.POST.get('end_page', -1))
+        max_tokens = int(request.POST.get('max_tokens', 2048))
+        try:
+            # 尝试将字符串解析为 JSON 列表
+            ids = json.loads(ids_str)
+
+            if not isinstance(ids, list):
+                return fail("无效输入:'ids'应该是一个列表")
+            results = []
+            for document_id in ids:
+                sleep(0.1)
+                logger.info("缓解压力沉睡0.1秒")
+                # 为每个 document_id 创建一个新的请求对象
+                analysis_request = type('AnalysisRequest', (), {})()
+                analysis_request.POST = {
+                    'document_id': document_id,
+                    'start_page': start_page,
+                    'end_page': end_page,
+                    'max_tokens': max_tokens
+                }
+
+                # 调用 analysis 方法
+                response = KbmService.analysis(analysis_request)
+                message = response.get('message')
+
+            return success(message,"已添加到队列")
+
+        except Exception as e:
+
+            return fail(f"An error occurred: {str(e)}")
+
+    # 假设这是您支持的文件后缀名列表
+    SUPPORTED_SUFFIXES = [
+        'txt', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'md'
+    ]
+
+    @staticmethod
+    def getSuffixName(request):
+        try:
+            # 获取数据库中的所有不重复的 type 值
+            db_types = DocumentKbm.objects.values_list('type', flat=True).distinct()
+
+            # 将数据库中的类型转换为集合
+            db_types_set = set(db_types)
+
+            # 将 SUPPORTED_SUFFIXES 转换为集合
+            supported_set = set(KbmService.SUPPORTED_SUFFIXES)
+
+            # 合并两个集合,自动去除重复项
+            combined_set = supported_set.union(db_types_set)
+
+            # 将结果转换回列表
+            combined_suffixes = list(combined_set)
+
+            # 对结果进行排序(可选)
+            combined_suffixes.sort()
+
+            return success(combined_suffixes)
+
+        except Exception as e:
+            return fail(f"获取文件后缀名时发生错误: {str(e)}")
+
+    @staticmethod
+    @transaction.atomic
+    def batchMove(request):
+        ids = json.loads(request.POST.get("ids"))
+        doc_type_id = request.POST.get("doc_type_id")
+        if not doc_type_id:
+            return fail("分类id为空")
+        if  not ids:
+            return fail("未传出文件id")
+
+        type = KbmDocumentType.objects.filter(id=doc_type_id).exclude(status=4).first()
+        if not type:
+            return fail("当前分类不存在")
+        DocumentKbm.objects.filter(id__in=ids).update(doc_type_id=doc_type_id)
+
+        return success("批量移动成功")
+
+    @staticmethod
+    @transaction.atomic
+    def moveDocument(doc_id, doc_type_id):
+        # 这个方法可以保持不变,因为它已经是单次更新操作
+        return DocumentKbm.objects.filter(id=doc_id).update(doc_type_id=doc_type_id)
+
+    @staticmethod
+    @transaction.atomic
+    def updateKbm(request):
+        id = request.POST.get("id")
+        if not id:
+            return fail("id为空")
+        kmb = Knowledgebase.objects.filter(id=id).first()
+        name = request.POST.get("name")
+        if not name:
+            return fail("名称不能为空")
+        if kmb:
+            kmb.name = name
+            kmb.description = request.POST.get("description","")
+            kmb.save()
+            return success("修改成功")
+        else:
+            return fail("修改失败")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 182 - 0
backend/Service/MilvusService.py

@@ -0,0 +1,182 @@
+import json
+from datetime import timedelta
+
+from django.http import JsonResponse
+from minio import Minio
+from pymilvus import Collection, FieldSchema, CollectionSchema, DataType, utility, connections
+
+from DCbackend import settings
+from DCbackend.settings import MILVUS_HOST, MILVUS_PORT, MILVUS_USER, MILVUS_PASSWORD
+from DCbackend.utils.common import success, fail, pageData
+from backend.models import TaskSublist, DocumentKbm, Knowledgebase
+from base import logger
+
+minio_client = Minio(
+    settings.MINIO_ENDPOINT,
+    access_key=settings.MINIO_ACCESS_KEY,
+    secret_key=settings.MINIO_SECRET_KEY,
+    secure=settings.MINIO_SECURE
+)
+class MilvusService:
+
+    @staticmethod
+    def list_all_collections(request):
+        """
+        列出 Milvus 中的所有集合
+        """
+        try:
+            # 连接到 Milvus
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT,user=MILVUS_USER,password=MILVUS_PASSWORD)
+
+            # 获取所有集合名称
+            collection_names = utility.list_collections()
+
+            # 获取每个集合的详细信息
+            collections_info = []
+            for name in collection_names:
+                collection = Collection(name)
+                schema = collection.schema
+                schema_dict = {
+                    "fields": [
+                        {
+                            "name": field.name,
+                            "dtype": str(field.dtype),
+                            "is_primary": field.is_primary,
+                            "auto_id": field.auto_id,
+                            "description": field.description,
+                            "max_length": field.max_length if hasattr(field, 'max_length') else None,
+                            "dim": field.dim if field.dtype == DataType.FLOAT_VECTOR else None
+                        } for field in schema.fields
+                    ],
+                    "description": schema.description
+                }
+                info = {
+                    "name": name,
+                    "entities": collection.num_entities,
+                    "schema": schema_dict
+                }
+                collections_info.append(info)
+
+            return success(collections_info)  # 假设 success 函数可以处理字典列表
+
+        except Exception as e:
+            print(f"列出集合时发生错误: {str(e)}")
+            raise
+
+        finally:
+            # 断开 Milvus 连接
+            connections.disconnect("default")
+
+    @staticmethod
+    def delete_collection(request):
+        """
+        删除 Milvus 中的指定集合
+
+        :param request: HTTP 请求对象
+        :return: JsonResponse 对象
+        """
+        collection_name = request.POST.get("collection_name")
+        if not collection_name:
+            return fail("集合名称未提供")
+
+        try:
+            # 连接到 Milvus
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT,user=MILVUS_USER,password=MILVUS_PASSWORD)
+
+            # 检查集合是否存在
+            if not utility.has_collection(collection_name):
+                return fail(f"集合 '{collection_name}' 不存在")
+
+            # 删除集合
+            utility.drop_collection(collection_name)
+
+            return success( f"集合 '{collection_name}' 已成功删除")
+
+        except Exception as e:
+            return fail(f"删除集合时发生错误: {str(e)}")
+
+        finally:
+            # 断开 Milvus 连接
+            connections.disconnect("default")
+
+
+    @staticmethod
+    def delete_milvus_data(request):
+        """
+        从 Milvus 中删除指定 ID 的数据
+
+        :param request: HTTP 请求对象
+        :return: JsonResponse 对象
+        """
+        collection_name = request.POST.get("collection_name")
+        id_to_delete = request.POST.get("id")
+
+        if not collection_name:
+            return fail("集合名称未提供")
+        if not id_to_delete:
+            return fail("要删除的 ID 未提供")
+
+        try:
+            # 连接到 Milvus
+            connections.connect("default", host=MILVUS_HOST, port=MILVUS_PORT,user=MILVUS_USER,password=MILVUS_PASSWORD)
+
+            # 检查集合是否存在
+            if not utility.has_collection(collection_name):
+                return fail(f"集合 '{collection_name}' 不存在")
+
+            # 获取集合对象
+            collection = Collection(collection_name)
+
+            # 执行删除操作
+            expr = f'id in [{id_to_delete}]'
+            delete_result = collection.delete(expr)
+
+            if delete_result.delete_count > 0:
+                return success(f"ID 为 {id_to_delete} 的数据已成功从集合 '{collection_name}' 中删除")
+            else:
+                return fail(f"未找到 ID 为 {id_to_delete} 的数据")
+
+        except Exception as e:
+            return fail(f"删除数据时发生错误: {str(e)}")
+
+        finally:
+            # 断开 Milvus 连接
+            connections.disconnect("default")
+
+    @staticmethod
+    def getMinioURl(request):
+        data = json.loads(request.body)
+        id = data.get("id")
+        logger.info(f"request:{request}")
+        if not id:
+            return fail("id为空")
+
+        task = TaskSublist.objects.filter(milvus_id=id).first()
+        if not task:
+            return fail("无此数据")
+        document = DocumentKbm.objects.filter(id = task.doc_id).first()
+        if not document:
+            return fail("未找到文件")
+        kmb = Knowledgebase.objects.filter(id=document.kb_id).first()
+
+
+        object_name = document.location
+        bucket_name = kmb.location
+        if not object_name:
+            return fail('Object name is required')
+
+        url = minio_client.presigned_get_object(
+            bucket_name,
+            object_name,
+            expires=timedelta(days=1)
+        )
+        bucket_info = {
+            'object_name': object_name,
+            'url': url,
+        }
+        return success(bucket_info)
+
+
+
+
+

+ 600 - 0
backend/Service/MinioService.py

@@ -0,0 +1,600 @@
+import base64
+import os
+from datetime import timedelta, datetime
+from io import BytesIO
+
+import chardet
+import fitz
+import unicodedata
+from cfgv import ValidationError
+from django.core.files.base import ContentFile
+from django.db import transaction
+from minio import Minio, S3Error
+from minio.commonconfig import CopySource
+
+from DCbackend import settings
+from DCbackend.utils.common import success, fail
+from backend.models import Admin, Knowledgebase, File, DocumentKbm, File2document
+from pypinyin import lazy_pinyin, Style
+import re
+# 初始化 MinIO 客户端
+minio_client = Minio(
+    settings.MINIO_ENDPOINT,
+    access_key=settings.MINIO_ACCESS_KEY,
+    secret_key=settings.MINIO_SECRET_KEY,
+    secure=settings.MINIO_SECURE
+)
+def contains_chinese(text):
+    """检查字符串是否包含中文字符"""
+    return any('\u4e00' <= char <= '\u9fff' for char in text)
+
+def convert_to_pinyin(text):
+    """
+    将中文字符转换为不带声调的拼音,保留字母和数字,其他字符替换为连字符,
+    并确保生成的名称符合 bucket 命名规则
+    """
+    result = []
+    for char in text:
+        if '\u4e00' <= char <= '\u9fff':
+            # 中文字符
+            pinyin = lazy_pinyin(char, style=Style.NORMAL)
+            result.extend(pinyin)
+        elif char.isalnum():
+            # 字母和数字
+            result.append(char.lower())
+        else:
+            # 其他字符替换为连字符
+            result.append('-')
+
+    # 合并字符并去除声调
+    bucket_name = ''.join(result)
+    bucket_name = unicodedata.normalize('NFKD', bucket_name).encode('ASCII', 'ignore').decode('ASCII')
+
+    # 合并连续的连字符
+    bucket_name = re.sub(r'-+', '-', bucket_name)
+
+    # 确保名称以字母或数字开头和结尾
+    bucket_name = bucket_name.strip('-')
+
+    # 如果名称为空,使用默认名称
+    if not bucket_name:
+        bucket_name = 'default-bucket'
+
+    # 确保名称长度在 3-63 之间
+    if len(bucket_name) < 3:
+        bucket_name = bucket_name.ljust(3, 'a')
+    elif len(bucket_name) > 63:
+        bucket_name = bucket_name[:63]
+
+    return bucket_name.lower()
+
+class MinioService:
+    @staticmethod
+    def listBuckets(request):
+        #查询所有bucket信息
+        buckets = minio_client.list_buckets()
+        # 将 Bucket 对象转换为可序列化的字典,包含所有可用信息
+        serializable_buckets = []
+        for bucket in buckets:
+            # 处理创建日期
+            if isinstance(bucket.creation_date, datetime):
+                # 添加8小时
+                adjusted_date = bucket.creation_date + timedelta(hours=8)
+                formatted_date = adjusted_date.strftime("%Y-%m-%d %H:%M:%S")
+            else:
+                formatted_date = str(bucket.creation_date)
+
+            bucket_info = {
+                'bucket_name': bucket.name,
+                'creation_date': formatted_date,
+            }
+            #根据桶名查询数量
+            try:
+                objects = minio_client.list_objects(bucket.name, recursive=True)
+                file_count = sum(1 for _ in objects)
+                bucket_info['file_count'] = file_count
+            except Exception as e:
+                bucket_info['file_count'] = f"Error: {str(e)}"
+            serializable_buckets.append(bucket_info)
+
+        return success(serializable_buckets)
+
+    @staticmethod
+    def createBucket(request):
+        """创建新的 bucket"""
+        try:
+            user_id = request.POST.get("user_id")
+            # 从请求中获取 bucket 名称
+            bucket_name = request.POST.get('bucket_name')
+            description = request.POST.get('description', "")
+
+            if not bucket_name:
+                return fail('Bucket name is required')
+
+            minio_name = bucket_name
+            if contains_chinese(bucket_name):
+                minio_name = convert_to_pinyin(bucket_name)
+
+            # 检查 bucket 是否已存在
+            if minio_client.bucket_exists(minio_name):
+                return fail(f'Bucket "{minio_name}" 已存在')
+
+            # 创建 bucket
+            minio_client.make_bucket(minio_name)
+            # 同步数据库
+            return MinioService.saveBucketDb(bucket_name, user_id, minio_name, description)
+
+        except S3Error as e:
+            return fail(f'Failed to create bucket: {str(e)}')
+        except Exception as e:
+            return fail(f'An unexpected error occurred: {str(e)}')
+
+    @staticmethod
+    def saveBucketDb(bucket_name, user_id,minio_name,description):
+        try:
+            admin = Admin.objects.get(id=user_id)
+        except Admin.DoesNotExist:
+            return fail("用户信息不存在")
+
+        try:
+            # 假设 Knowledgebase 是您的模型类名
+            db = Knowledgebase(
+                role_id=admin.role_id,
+                name=bucket_name,
+                location=minio_name,
+                description=description,
+                created_by=user_id
+            )
+            db.save()
+
+            file = File.objects.filter(
+                role_id=admin.role_id,
+                created_by=user_id,
+                name='/',
+                source_type=''
+            ).exclude(status=4).first()
+            if file is None:
+                # 如果记录不存在,创建新记录
+                file = File.objects.create(
+                    role_id=admin.role_id,
+                    name='/',
+                    source_type='',
+                    created_by= user_id,
+                    location= '',
+                    type= "folder",
+                    status= 5,
+                )
+                # 创建后更新parent_id
+                file.parent_id = file.id
+                file.save()
+
+                kbm = File.objects.create(
+                    role_id=admin.role_id,
+                    name='.knowledgebase',  # 根据需要修改名称
+                    source_type='knowledgebase',
+                    created_by=user_id,
+                    location='',
+                    type="folder",
+                    status=5,
+                    parent_id=file.id  # 设置parent_id为主记录的id
+                )
+
+                bucket = File.objects.create(
+                    role_id=admin.role_id,
+                    name=bucket_name,  # 根据需要修改名称
+                    source_type='knowledgebase',
+                    created_by=user_id,
+                    location='',
+                    type="folder",
+                    status=5,
+                    parent_id=kbm.id
+                )
+            else:
+                kbm = File.objects.get(
+                    role_id=admin.role_id,
+                    created_by=user_id,
+                    name='.knowledgebase',
+                    type='folder',
+                    source_type='knowledgebase'
+                )
+                bucket = File.objects.create(
+                    role_id=admin.role_id,
+                    name=bucket_name,  # 根据需要修改名称
+                    source_type='knowledgebase',
+                    created_by=user_id,
+                    location='',
+                    type="folder",
+                    status=5,
+                    parent_id=kbm.id
+                )
+
+        except ValidationError as e:
+            return fail(f"验证错误: {e}")
+        except Exception as e:
+            return fail(f"保存失败: {str(e)}")
+
+        return success(f'Bucket "{bucket_name}" 创建成功')
+
+    @staticmethod
+    def is_valid_bucket_name(bucket_name):
+        """检查 bucket 名称是否合法"""
+        import re
+        # bucket 名称必须在 3-63 个字符之间,只能包含小写字母、数字和连字符
+        pattern = r'^[a-z0-9][a-z0-9\-]{1,61}[a-z0-9]$'
+        return re.match(pattern, bucket_name) is not None
+
+
+    #获取指定buck内文件信息
+    @staticmethod
+    def getBucketContents(request):
+        """获取指定 bucket 内的所有文件信息"""
+        try:
+            # 从请求中获取 bucket 名称
+            bucket_name = request.POST.get('bucket_name')
+
+            # 使用 get 方法获取 page 和 page_size,如果不存在则使用默认值
+            page = int(request.POST.get('page', 1))
+            page_size = int(request.POST.get('page_size', 10))
+            print(page, page_size)
+            if not bucket_name:
+                return fail('请求参数为空')
+
+            # 检查 bucket 是否存在
+            if not minio_client.bucket_exists(bucket_name):
+                return fail(f'Bucket "{bucket_name}" 不存在')
+
+            # 获取 bucket 内的所有对象
+            objects = minio_client.list_objects(bucket_name, recursive=True)
+
+            # 整理文件信息
+            file_info = list(objects)  # 转换为列表以获得准确的长度
+
+            # 计算总数和总页数
+            total_count = len(file_info)
+            total_pages = (total_count + page_size - 1) // page_size
+
+            # 确保页码在有效范围内
+            page = max(1, min(page, total_pages))
+
+            # 计算切片的起始和结束索引
+            start_index = (page - 1) * page_size
+            end_index = min(start_index + page_size, total_count)
+
+            # 获取当前页的数据
+            paginated_files = [
+                {
+                    'object_name': obj.object_name,
+                    'size': obj.size,
+                    'last_modified': obj.last_modified,
+                    'version_id': obj.version_id,
+                    'etag': obj.etag
+                }
+                for obj in file_info[start_index:end_index]
+            ]
+
+            return success({
+                'bucket_name': bucket_name,
+                'files': paginated_files,
+                'page': page,
+                'page_size': page_size,
+                'total_pages': total_pages,
+                'total_count': total_count
+            })
+        except S3Error as e:
+            return fail(f'Failed to get bucket contents: {str(e)}')
+        except Exception as e:
+            return fail(f'An unexpected error occurred: {str(e)}')
+
+    @staticmethod
+    @transaction.atomic()
+    def post(request):
+        """上传文件"""
+        uploaded_file = request.FILES['file']  # 获取上传的文件
+        bucket_id = request.POST.get('bucket_id')  # BUCKET的名称
+        user_id = request.POST.get('user_id')  # BUCKET的名称
+        file_path = request.POST.get('file_path', '')
+        doc_type_id = request.POST.get('doc_type_id',0)
+
+        if not uploaded_file:
+            return fail('没有需要上传的文件')
+        if not bucket_id:
+            return fail('bucket_id为空')
+
+        # 使用 file_path 和文件名构造对象名称
+        object_name = f"{file_path.strip('/')}/{uploaded_file.name}".lstrip('/')
+        count = DocumentKbm.objects.filter(name=object_name, kb_id=bucket_id).exclude(status=4).count()
+        if count > 0:
+            return fail("已有重复文件,请删除后重试")
+
+        try:
+            # 读取文件内容
+            file_content = uploaded_file.read()
+
+            # 使用 MinIO 客户端上传文件
+            knowledgebase = Knowledgebase.objects.get(id=bucket_id)
+            bucket_name = knowledgebase.location
+
+            minio_client.put_object(
+                bucket_name,
+                object_name,
+                ContentFile(file_content),
+                length=len(file_content),
+                content_type=uploaded_file.content_type
+            )
+            MinioService.saveDocumentKbm(knowledgebase, uploaded_file, user_id, object_name,doc_type_id)
+
+            return success("保存成功")
+        except Exception as e:
+            return fail(str(e))
+
+    def is_image_file(extension):
+        image_extensions = [
+            'jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp',
+            'svg', 'raw', 'heif', 'heic', 'indd',
+            'ai', 'eps', 'psd', 'xcf', 'cr2', 'nef', 'orf', 'sr2',
+            'jfif', 'exif', 'ico', 'tga'
+        ]
+        return extension.lower().strip('.') in image_extensions
+
+    def is_text_file(extension):
+        text_extensions = [
+            'txt', 'pdf', 'doc', 'docx', 'rtf', 'odt', 'xls', 'xlsx',
+            'csv', 'tsv', 'json', 'xml', 'html', 'htm', 'md', 'markdown',
+            'tex', 'log', 'ini', 'cfg', 'conf', 'py', 'js', 'css', 'scss',
+            'less', 'sql', 'php', 'java', 'c', 'cpp', 'h', 'hpp', 'sh',
+            'bat', 'ps1', 'rb', 'yaml', 'yml', 'toml', 'rst', 'asciidoc',
+            'ppt', 'pptx', 'odp', 'key', 'pages', 'numbers'
+        ]
+        return extension.lower().strip('.') in text_extensions
+
+    @staticmethod
+    @transaction.atomic
+    def saveDocumentKbm(knowledgebase,uploaded_file,user_id,object_name,doc_type_id):
+        try:
+            size = uploaded_file.size
+            _, file_extension = os.path.splitext(uploaded_file.name)
+            # file_extension 现在包含了文件的后缀名,包括点号(例如 ".txt")
+            # 如果您不想要点号,可以这样做:
+            file_extension = file_extension[1:] if file_extension else ''
+            # 根据文件类型设置 parser_id
+            if MinioService.is_image_file(file_extension):
+                parser_id = 'picture'
+            elif MinioService.is_text_file(file_extension):
+                parser_id = 'naive'
+
+            documentKbm = DocumentKbm.objects.create(
+                kb_id=knowledgebase.id,
+                parser_id=parser_id,
+                parser_config='{"pages": [[1, 1000000]]}',
+                type = file_extension,
+                created_by=user_id,
+                name=object_name,
+                location=object_name,
+                size=size,
+                doc_type_id=doc_type_id
+            )
+            doc_id = documentKbm.id
+
+            admin = Admin.objects.get(id=user_id)
+            file = File.objects.filter(
+                role_id=admin.role_id,
+                created_by=user_id,
+                name=knowledgebase.name,
+                source_type='knowledgebase',
+                type='folder'
+            ).exclude(status=4).first()
+
+            fileDB = File.objects.create(
+                role_id=admin.role_id,
+                name=object_name,
+                source_type='knowledgebase',
+                created_by=user_id,
+                location=object_name,
+                type=file_extension,
+                status=5,
+                size=size,
+                parent_id= file.id
+            )
+            file_id = fileDB.id
+
+            file2document = File2document.objects.create(
+                file_id=file_id,
+                document_id=doc_id
+            )
+
+
+
+            child_count = File.objects.filter(parent_id=file.id).exclude(status=4).count()
+
+            knowledgebase.doc_num = child_count
+            knowledgebase.save()
+
+
+        except Exception as e:
+
+            return fail(str(e))
+
+
+
+    # 根据名称获取地址
+    @staticmethod
+    def nameGetUrl(request):
+        object_name = request.POST.get('object_name')
+        bucket_name = request.POST.get('bucket_name')
+        return MinioService.geturl(object_name,bucket_name)
+
+
+
+
+    @staticmethod
+    def geturl(name,bucket_name):
+        """获取文件地址"""
+        object_name = name
+        if not object_name:
+            return fail('Object name is required')
+        try:
+            # 生成一个预签名 URL,有效期为1小时
+            url = minio_client.presigned_get_object(
+                bucket_name,
+                object_name,
+                expires=timedelta(hours=1)
+            )
+            return success({'url': url})
+        except Exception as e:
+            return fail(str(e))
+
+
+    @staticmethod
+    def deleteFile(request):
+        """根据名称删除文件"""
+        bucket_name = request.POST.get('bucket_name')
+        object_name = request.POST.get('object_name')
+
+        if not object_name:
+            return fail('需要删除的文件未找到')
+
+        try:
+            minio_client.remove_object(bucket_name, object_name)
+            return success('删除成功')
+        except S3Error as e:
+            return fail(f'Failed to delete file: {str(e)}')
+        except Exception as e:
+            return fail(f'An unexpected error occurred: {str(e)}')
+
+    @staticmethod
+    def renameFile(request):
+        """重命名 bucket 中的文件"""
+        bucket_name = request.POST.get('bucket_name')
+        old_name = request.POST.get('object_name')
+        new_name = request.POST.get('new_name')
+
+        if not old_name or not new_name:
+            return fail('原文件名或新文件名未提供')
+
+        # 检查新文件名是否包含后缀,如果没有则添加原文件的后缀
+        if '.' not in new_name:
+            old_extension = old_name.split('.')[-1] if '.' in old_name else ''
+            new_name = f"{new_name}.{old_extension}" if old_extension else new_name
+
+        try:
+            # 复制对象到新名称
+            result = minio_client.copy_object(
+                bucket_name, new_name,
+                CopySource(bucket_name, old_name)
+            )
+
+            # 如果复制成功,删除原对象
+            if result:
+                minio_client.remove_object(bucket_name, old_name)
+                return success("修改昵称成功")
+            else:
+                return fail('文件重命名失败')
+        except S3Error as e:
+            return fail(f'重命名文件失败: {str(e)}')
+        except Exception as e:
+            return fail(f'发生意外错误: {str(e)}')
+
+
+    @staticmethod
+    def delete_bucket(request):
+        """删除 bucket"""
+        bucket_name = request.POST.get('bucket_name')
+
+        if not bucket_name:
+            return fail({'bucket_name 为空'})
+
+        try:
+            # 开始事务
+            with transaction.atomic():
+                # 1. 从 MinIO 删除 bucket
+                try:
+                    # 首先删除 bucket 中的所有对象
+                    objects = minio_client.list_objects(bucket_name, recursive=True)
+                    for obj in objects:
+                        minio_client.remove_object(bucket_name, obj.object_name)
+
+                    # 然后删除 bucket
+                    minio_client.remove_bucket(bucket_name)
+                except S3Error as e:
+                    return fail(f'MinIO 错误: {str(e)}')
+
+                # 2. 从数据库中删除相应记录
+                try:
+                    bucket = Knowledgebase.objects.get(name=bucket_name)
+                    bucket.delete()
+                except Knowledgebase.DoesNotExist:
+                    return fail('数据库中不存在该 bucket 记录')
+
+            return fail(f'Bucket "{bucket_name}" 已成功删除')
+
+        except Exception as e:
+            return fail(f'删除 bucket 时发生错误: {str(e)}')
+
+    @staticmethod
+    def deleteBucket(request):
+        return MinioService.delete_bucket(request)
+
+    #切片
+    @staticmethod
+    def readPdfSlice(request):
+        """读取 PDF 切片,包括文本和图片"""
+        bucket_name = request.POST.get('bucket_name')
+        object_name = request.POST.get('object_name')
+        start_page = int(request.POST.get('start_page', 1))
+        end_page = int(request.POST.get('end_page', -1))
+
+        if not bucket_name or not object_name:
+            return fail('bucket_name 或 object_name 为空')
+
+        try:
+            # 从 MinIO 获取 PDF 文件
+            response = minio_client.get_object(bucket_name, object_name)
+            pdf_content = BytesIO(response.read())
+
+            # 使用 PyMuPDF 读取 PDF
+            doc = fitz.open(stream=pdf_content, filetype="pdf")
+            total_pages = len(doc)
+
+            # 调整页面范围
+            start_page = max(1, start_page) - 1  # 转换为从 0 开始的索引
+            end_page = min(total_pages, end_page if end_page > 0 else total_pages)
+
+            # 读取指定页面范围的内容
+            result = []
+            for page_num in range(start_page, end_page):
+                page = doc[page_num]
+                text = page.get_text()
+
+                # 提取图片
+                images = []
+                for img in page.get_images():
+                    xref = img[0]
+                    base_image = doc.extract_image(xref)
+                    image_data = base_image["image"]
+                    image_format = base_image["ext"]
+                    image_base64 = base64.b64encode(image_data).decode('utf-8')
+                    images.append({
+                        'format': image_format,
+                        'data': image_base64
+                    })
+
+                result.append({
+                    'page_number': page_num + 1,
+                    'content': text,
+                    'images': images
+                })
+
+            # 关闭连接
+            doc.close()
+            pdf_content.close()
+            response.close()
+
+            info = {
+                'total_pages': total_pages,
+                'sliced_content': result
+            }
+            return success(info)
+
+        except Exception as e:
+            return fail(str(e))
+

+ 16 - 0
backend/Service/OperationService.py

@@ -0,0 +1,16 @@
+from django.core.paginator import Paginator
+from django.forms import model_to_dict
+
+
+from DCbackend.utils.common import success
+from backend.models import Operation
+
+
+class OperationService:
+    @staticmethod
+    def selectOperation(request):
+        content_type_id = request.POST.get("content_type_id")
+
+        operation = Operation.objects.filter(content_type_id=content_type_id)
+        operation_list = [model_to_dict(operation) for operation in operation]
+        return success(operation_list)

+ 94 - 0
backend/Service/ParamsService.py

@@ -0,0 +1,94 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.ParamsForm import ParamsForm
+from backend.models import Params
+
+
+class ParamsService:
+
+    @staticmethod
+    def Create(request):
+        form = ParamsForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = Params.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.code = request.POST.get("code")
+            data.type = request.POST.get("type")
+            data.intro = request.POST.get("intro")
+            data.name = request.POST.get("name")
+            data.value_type = request.POST.get("value_type")
+            data.value_item = request.POST.get("value_item")
+            data.value = request.POST.get("value")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新参数信息失败")
+
+    @staticmethod
+    def Info(request):
+        info = Params.objects.filter(id=request.POST.get("id")).first()
+        return success(ParamsService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Params.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Params):
+        return {
+            "id": item.id,
+            "type": item.type,
+            'code': item.code,
+            'name': item.name,
+            'intro': item.intro,
+            'value_type': item.value_type,
+            'value_item': item.value_item,
+            'value': item.value,
+            'createTime': item.create_time,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+        code = request.POST.get("code", '')
+        if code:
+            where["code__icontains"] = code
+
+        type = request.POST.get("type", '')
+        if type:
+            where["type"] = type
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Params.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = ParamsService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 124 - 0
backend/Service/ProductCategoryService.py

@@ -0,0 +1,124 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.BrandForm import BrandForm
+from backend.Form.ProductCategoryForm import ProductCategoryForm
+from backend.Form.ProductForm import ProductForm
+from backend.models import ProjectCategory, Brand, Product, ProductCategory
+
+
+class ProductCategoryService:
+
+    @staticmethod
+    def Create(request):
+        form = ProductCategoryForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = ProductCategory.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.parent_id = request.POST.get("parent_id")
+            data.attrs = request.POST.get("attrs")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新产品分类失败")
+
+    @staticmethod
+    def Info(request):
+        info = ProductCategory.objects.filter(id=request.POST.get("id")).first()
+        return success(ProductCategoryService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        ProductCategory.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: ProductCategory):
+        parentData = {}
+        if item.parent_id > 0:
+            parentItem = ProductCategory.objects.filter(id=item.parent_id).first()
+            parentData = ProductCategoryService.getDetail(parentItem)
+
+        return {
+            "id": item.id,
+            'parent_id': item.parent_id,
+            'parentData': parentData,
+            'name': item.name,
+            'attrs': item.attrs,
+            'createTime': item.create_time,
+            'children':[],
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        parent_id = request.POST.get("parent_id")
+        if parent_id:
+            where["parent_id"] = parent_id
+        else:
+            where['parent_id'] = 0
+
+        name = request.POST.get("name")
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(ProductCategory.objects.filter(**where).exclude(status=4).order_by("-id"),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = ProductCategoryService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+
+    @staticmethod
+    def getAllCategories():
+        top = ProductCategory.objects.filter(status=5, parent_id=0).order_by("id").all()
+        dataList = []
+        for item in top:
+            itemData={
+                "id": item.id,
+                'children': ProductCategoryService.getSubCategory(item),
+                'value': item.id,
+                'label': item.name,
+                'attrs': item.attrs,
+            }
+            dataList.append(itemData)
+        return success(dataList)
+
+    @staticmethod
+    def getSubCategory(topCategory: ProductCategory):
+        items = ProductCategory.objects.filter(status=5, parent_id=topCategory.id).order_by("id").all()
+        dataList = []
+        for item in items:
+            itemData = {
+                "id": item.id,
+                'label': item.name,
+                'value': item.id,
+                'attrs':item.attrs,
+            }
+
+            dataList.append(itemData)
+        return dataList

+ 165 - 0
backend/Service/ProductService.py

@@ -0,0 +1,165 @@
+import json
+
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.BrandForm import BrandForm
+from backend.Form.ProductForm import ProductForm
+from backend.Service.BrandService import BrandService
+from backend.Service.ProductCategoryService import ProductCategoryService
+from backend.Service.SupplierService import SupplierService
+from backend.models import ProjectCategory, Brand, Product, ProductCategory, Supplier, ProductDocument
+
+
+class ProductService:
+
+    @staticmethod
+    def Create(request):
+        form = ProductForm(request.POST)
+        if form.is_valid():
+            form.save()
+            links = json.loads(request.POST.get("links"))
+            for link in links:
+                pd = ProductDocument()
+                pd.document_id = link
+                pd.product_id = form.instance.id
+                pd.status = 5
+                pd.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = Product.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.category_id = request.POST.get("category_id")
+            data.brand_id = request.POST.get("brand_id")
+            data.supplier_id = request.POST.get("supplier_id")
+            data.name = request.POST.get("name")
+            data.intro = request.POST.get("intro")
+            data.price = request.POST.get("price")
+            data.product_area = request.POST.get("product_area")
+            data.is_safe = request.POST.get("is_safe")
+            data.safe_time = request.POST.get("safe_time")
+            data.is_package = request.POST.get("is_package")
+            data.package_intro = request.POST.get("package_intro")
+            data.qualifications = request.POST.get("qualifications")
+            data.status = request.POST.get("status")
+            data.attrs = request.POST.get("attrs")
+            data.save()
+
+            links = json.loads(request.POST.get("links"))
+
+            ProductDocument.objects.filter(product_id=data.id).update(status=4)
+            for link in links:
+                pd = ProductDocument()
+                pd.document_id = link
+                pd.product_id = data.id
+                pd.status = 5
+                pd.save()
+
+            return success("成功")
+        else:
+            return fail("更新产品信息")
+
+
+    @staticmethod
+    def Info(request):
+        info = Product.objects.filter(id=request.POST.get("id")).first()
+        return success(ProductService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Product.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Product):
+
+        category = ProductCategory.objects.filter(id=item.category_id).first()
+        categoryData = ProductCategoryService.getDetail(category)
+
+        brand = Brand.objects.filter(id=item.brand_id).first()
+        brandData = BrandService.getDetail(brand)
+
+        supplier = Supplier.objects.filter(id=item.supplier_id).first()
+        supplierData = SupplierService.getDetail(supplier)
+        where = {}
+        where["product_id"] = item.id
+        dpList = ProductDocument.objects.filter(**where).exclude(status=4).all()
+        linkProduct = []
+        for it in dpList:
+            # temp = {
+            #     'id': it.id,
+            #     'product_id': it.product_id,
+            #     'document_id': it.document_id,
+            # }
+            linkProduct.append( it.document_id)
+
+        return {
+            "id": item.id,
+            'category_id': item.category_id,
+            'brand_id': item.brand_id,
+            'supplier_id': item.supplier_id,
+            'linkDocument': linkProduct,
+            'category': categoryData,
+            'supplier': supplierData,
+            'brand': brandData,
+            'name': item.name,
+            'intro': item.intro,
+            'price': item.price,
+            'product_area': item.product_area,
+            'is_safe': item.is_safe,
+            'safe_time': item.safe_time,
+            'is_package': item.is_package,
+            'package_intro': item.package_intro,
+            'qualifications': item.qualifications,
+            'createTime': item.create_time,
+            'attrs': item.attrs,
+            'status': item.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        category_id = request.POST.get("category_id")
+        if category_id:
+            where["category_id"] = category_id
+
+        brand_id = request.POST.get("brand_id")
+        if brand_id:
+            where["brand_id"] = brand_id
+
+        ids = request.POST.get("ids")
+        if ids:
+            idRange = json.loads(ids)
+            where['id__in'] = idRange
+
+        supplier_id = request.POST.get("supplier_id")
+        if supplier_id:
+            where["supplier_id"] = supplier_id
+
+        name = request.POST.get("name")
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Product.objects.filter(**where).exclude(status=4).order_by("-id"), pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            info = ProductService.getDetail(item)
+            dataList.append(info)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 80 - 0
backend/Service/ProjectCategoryService.py

@@ -0,0 +1,80 @@
+from django.core.paginator import Paginator
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.ProjectCategoryForm import ProjectCategoryForm
+from backend.models import ProjectCategory, DefaultStatus
+
+
+class ProjectCategoryService:
+
+    @staticmethod
+    def Create(request):
+        form = ProjectCategoryForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = ProjectCategory.objects.filter(id=request.POST.get("id")).first();
+        if data is not None:
+            data.code = request.POST.get("code")
+            data.name = request.POST.get("name")
+            data.status = request.POST.get("status")
+            data.save()
+            return success("成功")
+        else:
+            return fail("保存分类失败")
+
+    @staticmethod
+    def Info(request):
+        info = ProjectCategory.objects.filter(id=request.POST.get("id")).first()
+        return success(ProjectCategoryService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        ProjectCategory.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(category: ProjectCategory):
+
+        return {
+            "id": category.id,
+            'code': category.code,
+            'name': category.name,
+            'createTime': category.create_time,
+            'status': category.status
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 10
+
+        where = {}
+        code = request.POST.get("code", '')
+        if code:
+            where["code__icontains"] = code
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(ProjectCategory.objects.filter(**where).exclude(status=4).order_by("-id"),pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            adminInfo = ProjectCategoryService.getDetail(item)
+            dataList.append(adminInfo)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 116 - 0
backend/Service/ProjectService.py

@@ -0,0 +1,116 @@
+from django.core.paginator import Paginator
+import json
+
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.ProjectCategoryForm import ProjectCategoryForm
+from backend.Form.ProjectForm import ProjectForm
+from backend.Service.CustomerService import CustomerService
+from backend.Service.ProjectCategoryService import ProjectCategoryService
+from backend.models import ProjectCategory, DefaultStatus, Project, Customer, ProjectDocument
+
+
+class ProjectService:
+
+    @staticmethod
+    def Create(request):
+        form = ProjectForm(request.POST)
+        if form.is_valid():
+            form.save()
+            project_id = form.instance.id
+            links = json.loads(request.POST.get("links"))
+            for p in links:
+                pr = ProjectDocument()
+                pr.project_id = project_id
+                pr.doc_id = p
+                pr.status = 5
+                pr.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = Project.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.intro = request.POST.get("intro")
+            data.customer_id = request.POST.get("customer_id")
+            data.category_id = request.POST.get("category_id")
+            data.status = request.POST.get("status")
+            data.save()
+
+            project_id = data.id
+            ProjectDocument.objects.filter(project_id=project_id).update(status=4)
+            links = json.loads(request.POST.get("links"))
+            for p in links:
+                pr = ProjectDocument()
+                pr.project_id = project_id
+                pr.doc_id = p
+                pr.status = 5
+                pr.save()
+            return success("成功")
+        else:
+            return fail("保存分类失败")
+
+    @staticmethod
+    def Info(request):
+        info = Project.objects.filter(id=request.POST.get("id")).first()
+        return success(ProjectService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        Project.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: Project):
+        category = ProjectCategory.objects.filter(id=item.category_id).first()
+        if category:
+            categoryInfo = ProjectCategoryService.getDetail(category)
+        c = Customer.objects.filter(id=item.customer_id).first()
+        if c:
+            customerInfo = CustomerService.getDetail(c)
+
+        prList = ProjectDocument.objects.filter(project_id=item.id).exclude(status=4).all()
+        linkDoc = []
+        for it in prList:
+            linkDoc.append(it.doc_id)
+        return {
+            "id": item.id,
+            'name': item.name,
+            'intro': item.intro,
+            'customerId': item.customer_id,
+            'categoryId': item.category_id,
+            'createTime': item.create_time,
+            'category': categoryInfo,
+            'status': item.status,
+            'customer': customerInfo,
+            'linkDoc':linkDoc,
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(Project.objects.filter(**where).exclude(status=4).order_by("-id"), pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            adminInfo = ProjectService.getDetail(item)
+            dataList.append(adminInfo)
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))

+ 173 - 0
backend/Service/SMSService.py

@@ -0,0 +1,173 @@
+import json
+import ssl
+
+from aliyunsdkcore.client import AcsClient
+from aliyunsdkcore.request import CommonRequest
+import random
+
+from DCbackend import settings
+from DCbackend.utils.common import success, fail
+# 在你的视图函数中使用这个方法
+from django.http import JsonResponse
+
+from DCbackend.utils.token import encrypt
+from backend.models import Admin
+
+
+class SMSService:
+    # 发送短信
+    @staticmethod
+    def send_sms(phone_number, code):
+        client = AcsClient(settings.oss_key_id, settings.oss_key_secret, settings.region_id)
+
+        request = CommonRequest()
+        request.set_accept_format('json')
+        request.set_domain('dysmsapi.aliyuncs.com')
+        request.set_method('POST')
+        request.set_protocol_type('https')
+        request.set_version('2017-05-25')  # 更正版本号
+        request.set_action_name('SendSms')
+
+        request.add_query_param('RegionId', settings.region_id)
+        request.add_query_param('PhoneNumbers', phone_number)
+        request.add_query_param('SignName', settings.SignName)
+        request.add_query_param('TemplateCode', "SMS_270860270")
+        request.add_query_param('TemplateParam', json.dumps({"code": code}))
+
+
+        response = client.do_action_with_exception(request)
+        print(str(response, encoding='utf-8'))
+        return json.loads(response)
+
+    #发送验证码
+    @staticmethod
+    def tailPhone(request):
+        phone = request.POST.get("phone")
+        verification_code = ''.join(random.choices('0123456789', k=6))
+        result = SMSService.send_sms(phone, verification_code)
+        if result and result.get('Code') == 'OK':
+            adminInfo = Admin.objects.filter(mobile=phone).first()
+            if adminInfo:
+                # 如果查询到了数据
+                adminInfo.verification_code = verification_code
+                adminInfo.save()
+                return success("验证码发送成功")
+            else:
+                return fail("查无此人")
+        else:
+            return fail("验证码发送失败")
+    #校验验证码
+    @staticmethod
+    def codeCheckout(request):
+        phone = request.POST.get("phone")
+        verification_code = request.POST.get("verification_code")
+        admin_exists  = Admin.objects.filter(mobile=phone,verification_code = verification_code).exists()
+        if admin_exists:
+            return success("验证成功")
+        else:
+            return fail("验证失败,请检查验证码和手机号")
+    #修改密码
+    @staticmethod
+    def updatePassword(request):
+        newPassword1 = request.POST.get("new_password1")
+        newPassword2 = request.POST.get("new_password2")
+        phone = request.POST.get("phone")
+
+        if newPassword1 == newPassword2:
+            newPassword = newPassword1
+            adminInfo = Admin.objects.filter(mobile=phone).first()
+            if adminInfo:
+                adminInfo.password = encrypt(newPassword)
+                adminInfo.verification_code = ""
+                adminInfo.save()
+                return success("修改成功")
+            else:
+                return fail("修改失败")
+        else:
+            return fail("两次密码不相同")
+
+
+
+
+    #获取模版列表
+    @staticmethod
+    def get_aliyun_sms_templates(access_key_id, access_key_secret, region_id=settings.region_id, page_size=10, page_index=1):
+        """
+        获取阿里云短信模板列表
+        :param access_key_id: 阿里云访问密钥ID
+        :param access_key_secret: 阿里云访问密钥密码
+        :param region_id: 地域ID,默认为上海
+        :param page_size: 每页显示的模板数量
+        :param page_index: 页码
+        :return: 短信模板列表
+        """
+        client = AcsClient(access_key_id, access_key_secret, region_id)
+
+        request = CommonRequest()
+        request.set_accept_format('json')
+        request.set_domain('dysmsapi.aliyuncs.com')
+        request.set_method('POST')
+        request.set_protocol_type('https')
+        request.set_version('2017-05-25')
+        request.set_action_name('QuerySmsTemplateList')  # 修改为正确的Action名称
+
+        request.add_query_param('PageSize', page_size)
+        request.add_query_param('PageIndex', page_index)
+
+        response = client.do_action_with_exception(request)
+        response_json = json.loads(response)
+
+        return response_json
+
+    @staticmethod
+    def getTemplates(request):
+        page_size = int(request.GET.get('page_size', 10))
+        page_index = int(request.GET.get('page_index', 1))
+
+        templates = SMSService.get_aliyun_sms_templates(
+            settings.oss_key_id,
+            settings.oss_key_secret,
+            page_size=page_size,
+            page_index=page_index
+        )
+        return JsonResponse(templates)
+
+
+    @staticmethod
+    def getSMScount(request):
+
+        return success(SMSService.get_aliyun_sms_remaining())
+
+
+    @staticmethod
+    def get_aliyun_sms_remaining():
+        # 创建AcsClient实例
+        client = AcsClient(settings.oss_key_id, settings.oss_key_secret, settings.region_id)
+
+        # 创建request,并设置参数
+        request = CommonRequest()
+        request.set_accept_format('json')
+        request.set_domain('dysmsapi.aliyuncs.com')
+        request.set_method('POST')
+        request.set_protocol_type('https')
+        request.set_version('2017-05-25')
+        request.set_action_name('QuerySendDetails')
+
+        # 设置业务参数
+        request.add_query_param('PhoneNumber', '1234567890')  # 示例手机号
+        request.add_query_param('SendDate', '20230101')  # 示例日期
+        request.add_query_param('PageSize', '1')
+        request.add_query_param('CurrentPage', '1')
+
+        # 发起请求并获取响应
+        response = client.do_action_with_exception(request)
+
+        # 解析响应
+        import json
+        result = json.loads(response)
+
+        # 获取剩余短信数量
+        remaining_count = result.get('TotalCount', 0)
+
+        return result
+

+ 203 - 0
backend/Service/SourceDataService.py

@@ -0,0 +1,203 @@
+import json
+
+from django.core.paginator import Paginator
+from django.http import JsonResponse
+
+from DCbackend.utils import common
+from DCbackend.utils.common import success, fail, pageData
+from backend.Form.SourceDataForm import SourceDataForm
+from backend.models import SourceData, SourceCategory
+
+
+class SourceDataService:
+
+    @staticmethod
+    def Create(request):
+        form = SourceDataForm(request.POST)
+        if form.is_valid():
+            form.save()
+            return success("成功")
+        else:
+            return fail(form.errors)
+
+    @staticmethod
+    def Update(request):
+        data = SourceData.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.code = request.POST.get("code")
+            data.name = request.POST.get("name")
+            data.source_data = request.POST.get("source_data")
+            data.status = request.POST.get("status")
+            data.category_id = request.POST.get("category_id")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新源数据成功")
+
+    @staticmethod
+    def Info(request):
+        info = SourceData.objects.filter(id=request.POST.get("id")).first()
+        return success(SourceDataService.getDetail(info))
+
+    @staticmethod
+    def Delete(request):
+        SourceData.objects.filter(id=request.POST.get("id")).update(status=4)
+        return success("成功")
+
+    @staticmethod
+    def getDetail(item: SourceData):
+        return {
+            "id": item.id,
+            'code': item.code,
+            'name': item.name,
+            'source_data': item.source_data,
+            'createTime': item.create_time,
+            'status': item.status,
+            'category_id': item.category_id
+        }
+
+    @staticmethod
+    def getDetailList(item):
+        return {
+            "id": item['id'],
+            'code': item['code'],
+            'name': item['name'],
+            'createTime': item['create_time'].strftime('%Y-%m-%d %H:%M:%S') if item['create_time'] else None,
+            'status': item['status'],
+            'category_id': item['category_id']
+        }
+
+    @staticmethod
+    def Search(request):
+        page = request.POST.get("page")
+        pageSize = request.POST.get("pageSize")
+        if page is None:
+            page = 1
+        if pageSize is None:
+            pageSize = 1
+
+        where = {}
+
+        code = request.POST.get("code", '')
+        if code:
+            where["code__icontains"] = code
+
+        name = request.POST.get("name", '')
+        if name:
+            where["name__icontains"] = name
+
+        status = request.POST.get("status")
+        if status:
+            where['status'] = status
+
+        paginator = Paginator(SourceData.objects.filter(**where).exclude(status=4).order_by("-id").values('id','code','name','status','category_id','create_time'),
+                              pageSize)  # 每页显示10条数据
+        page_obj = paginator.get_page(page)
+        dataList = []
+        for item in page_obj:
+            dataInfo = SourceDataService.getDetailList(item)
+            dataList.append(dataInfo)
+
+        return success(pageData(page, pageSize, paginator.num_pages, paginator.count, dataList))
+
+    @staticmethod
+    def findData(request):
+        row = request.POST.get("row")
+        col = request.POST.get("col")
+        tbCode = request.POST.get("code")
+
+        tbData = SourceData.objects.filter(code=tbCode).first()
+        if not tbData:
+            return fail("数据表标签不正确")
+
+        jsonData = json.loads(tbData.source_data)
+        sheetData=jsonData['exportJson']['sheets'][0]['data']
+        rowData=sheetData[int(row)] 
+        result=''
+        if 'v' in rowData[int(col)]:
+            result = rowData[int(col)]['v']
+        elif 'ct' in rowData[int(col)]:
+             if 's' in rowData[int(col)]['ct']:
+                 for sub in rowData[int(col)]['ct']['s']:
+                     result+=str(sub['v'])
+
+        return success({"result": result})
+
+    @staticmethod
+    def Categories(request):
+        query = request.POST.get('query', '')
+        status = request.POST.get('status', '')
+        page = request.POST.get('page', 1)
+
+        sourcecategories = SourceCategory.objects.all()
+
+        if query:
+            sourcecategories = sourcecategories.filter(name__icontains=query)
+        if status:
+            sourcecategories = sourcecategories.filter(status=status)
+
+        paginator = Paginator(sourcecategories, 10)  # 每页显示10条
+        page_obj = paginator.get_page(page)
+        sourcecategories = paginator.get_page(page)
+
+        data = {
+            'status': '200',
+            'message': 'success',
+            'data': {
+                'sourcecategories': [
+                    {
+                        'id': category.id,
+                        'name': category.name,
+                        'status': category.status,
+                        'created_at': category.created_at.strftime('%Y-%m-%d %H:%M:%S')
+                    } for category in page_obj
+                ],
+                'total': paginator.count,
+                'page': page,
+                'total_pages': paginator.num_pages
+            }
+        }
+
+        return JsonResponse(data)
+
+    @staticmethod
+    def Catcreat(request):
+        name = request.POST.get('name')
+        status = request.POST.get('status', 'active')
+
+        sourcecategory = SourceCategory.objects.create(name=name, status=status)
+
+        data = {
+            'id': sourcecategory.id,
+            'name': sourcecategory.name,
+            'status': sourcecategory.status,
+            'created_at': sourcecategory.created_at.strftime('%Y-%m-%d %H:%M:%S')
+        }
+        return success(data)
+
+    @staticmethod
+    def info(request):
+        info = SourceCategory.objects.filter(id=request.POST.get("id")).first()
+
+        return success(SourceDataService.getcategory(info))
+
+    @staticmethod
+    def updatetype(request):
+        data = SourceCategory.objects.filter(id=request.POST.get("id")).first()
+        if data is not None:
+            data.name = request.POST.get("name")
+            data.status = request.POST.get("status")
+            data.created_at = request.POST.get("created_at")
+            data.save()
+            return success("成功")
+        else:
+            return fail("更新源数据成功")
+
+    @staticmethod
+    def getcategory(item: SourceCategory):
+        return {
+            "id": item.id,
+            'name': item.name,
+            'status': item.status,
+            'created_at': item.created_at
+        }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно